Sync / foldkit-durable
Sixty seconds, with foldkit-sync
If the journal backs a Sync contract, do not restate the shared schema or the
reducer. The contract already knows the slice, the durable Messages, the
initial value, and how they replay through update:
ts
import { Effect } from 'effect'
import { ActorId, DocumentId, Journal, OpId } from 'foldkit-durable'
import { TodoSync } from './sync.js'
type Principal = { readonly actorId: string }
const program = Effect.gen(function* () {
const journal = yield* Journal.make({
// From the Sync contract: operation and snapshot codecs, the empty
// snapshot, the reducer, each operation's replica, and any `authorize`.
...TodoSync.journalContract(),
file: 'todos.sqlite',
// Sync and Durable brand their ids separately; re-brand at the boundary.
opId: operation => OpId.make(operation.opId),
actorId: (principal: Principal) => ActorId.make(principal.actorId),
})
return yield* journal.load(DocumentId.make('todos'))
}).pipe(Effect.scoped)Journal.make is scoped: the SQLite connection closes with the scope, so keep
it open for the server's lifetime. file is a path, :memory: for tests, or a
Config<string>.
What the two packages each own:
Foldkit application Model · Message · update
|
foldkit-sync what is shared, what is durable, how it replays
| journalContract()
foldkit-durable what committed, in what order, what state follows
|
SQLiteThe journal does not speak Sync's exchange. Your server's handler does, with
append for each pending operation, read for what follows the cursor, and
load for a checkpoint. The guide
shows that handler whole; the rest of this README explains each call.