Sync / foldkit-durable

Using Durable directly

Without Sync, a Journal needs an operation codec, a snapshot codec, an empty snapshot, and a pure reducer:

ts
import { Effect, Schema } from 'effect'
import { ActorId, Cursor, DocumentId, Journal, OpId } from 'foldkit-durable'

const Operation = Schema.Struct({ opId: Schema.String, title: Schema.String })
const Snapshot = Schema.Struct({ todos: Schema.Array(Schema.String) })
type Principal = { readonly actorId: string }

const program = Effect.gen(function* () {
  const journal = yield* Journal.make({
    file: 'journal.sqlite',
    operation: Operation,
    snapshot: Snapshot,
    empty: () => ({ todos: [] }),
    reduce: (snapshot, operation) => ({ todos: [...snapshot.todos, operation.title] }),
    opId: operation => OpId.make(operation.opId),
    actorId: (principal: Principal) => ActorId.make(principal.actorId),
  })

  const todos = DocumentId.make('todos')
  yield* journal.append(todos, { opId: 'tab-1:1', title: 'Milk' }, { actorId: 'alice' })
  const { snapshot, cursor } = yield* journal.load(todos)
  const since = yield* journal.read(todos, Cursor.make(0))
  return { snapshot, cursor, since }
}).pipe(Effect.scoped)

The operation schema decides what append accepts: its encoded side, so a transforming schema is checked at the call site rather than degrading the boundary to unknown. A payload that does not decode is an InvalidOperationError.