Sync / foldkit-durable
On Cloudflare D1 (d1: true)
D1 speaks SQLite but refuses transactions, PRAGMA user_version, and
VACUUM, so the journal adapts when the options say d1: true:
ts
import * as D1Client from '@effect/sql-d1/D1Client'
import { Layer } from 'effect'
import { Journal } from 'foldkit-durable/core'
// `TodoSync` is the Sync contract from Sixty seconds, with `foldkit-sync`
// above; `env.DB` is the Worker's D1 binding.
const Todos = Journal.define<Operation, Snapshot, Principal>('app/Todos')
const live = Todos.layer({ ...TodoSync.journalContract(), d1: true }).pipe(
Layer.provide(D1Client.layer({ db: env.DB })),
)What changes, and what does not:
- The schema version lives in a
durable_metatable instead ofPRAGMA user_version; migrations are the same steps, each safe to rerun. - A commit retries on a uniqueness conflict instead of holding a write
lock: whoever loses the sequence race re-reads the cursor and replays the
decision, so concurrent appends still land gap-free.
validateandauthorizesee the latest committed snapshot. appendAllcommits each operation on its own rather than atomically.- Reads are separate statements: a snapshot replays only the prefix its cursor names, and a tail a compaction cut short mid-read is refused rather than returned short.
vacuumsucceeds without doing anything: D1 auto-vacuums, and there is no file or WAL to reclaim.- Route appends through one writer (a Durable Object): concurrent writers stay safe through the constraints, but exact read-your-commit consistency is a single writer's property.
Journal.make (the node:sqlite file entry) refuses d1: true: it is for
a D1 SqlClient through foldkit-durable/core, not a file.