Sync / foldkit-durable

Install

bash
pnpm add foldkit-durable

effect is a peer dependency, @effect/sql-sqlite-node comes with the package, and Node 22 is required for node:sqlite.

In a browser, or any runtime without node:sqlite, import foldkit-durable/core instead: the same journal, as a layer that needs the SqlClient it runs over, with no file. @effect/sql-sqlite-wasm gives SQLite compiled to WebAssembly:

ts
import * as WasmClient from '@effect/sql-sqlite-wasm/SqliteClient'
import { Effect, Layer } from 'effect'
import { DocumentId, Journal } from 'foldkit-durable/core'

const Todos = Journal.define<Operation, Snapshot, Principal>('app/Todos')
// `options` as for `Journal.make`, without `file`.
const live = Todos.layer(options).pipe(Layer.provide(WasmClient.layerMemory({})))

const program = Effect.gen(function* () {
  const journal = yield* Todos.tag
  return yield* journal.cursor(DocumentId.make('todos'))
}).pipe(Effect.provide(live))

The database is provided to the layer, so it opens and closes with the journal: there is no moment the journal holds a closed connection. To share one connection with the application's own tables, provide the driver once to both (Layer.mergeAll(Todos.layer(options), Tables).pipe(Layer.provide( driver))). Outside Effect, keep the layer in a ManagedRuntime that lives as long as the code using it (runtime.runPromise(Effect.service(Todos.tag))).

core reaches neither Node's driver nor a node: module; payloads are hashed in plain JavaScript, as node:crypto hashed them, so a database written before still recognizes its retransmissions.

On Cloudflare D1, provide @effect/sql-d1's client and pass d1: true (see On Cloudflare D1).