Sync / foldkit-durable

Services, codecs, errors, metrics

As a service. Journal.define<Operation, Snapshot, Principal>('app/TodoJournal') fixes the key and the type parameters together and returns tag and layer:

ts
const TodoJournal = Journal.define<Operation, Snapshot, Principal>('app/TodoJournal')

const served = Effect.gen(function* () {
  const journal = yield* TodoJournal.tag
  return yield* journal.load(DocumentId.make('todos'))
}).pipe(Effect.provide(TodoJournal.layer(options)))

Two definitions are two services; each tag is satisfied only by its own layer. Journal.layer(options) is the default-key form.

Codecs. operation and snapshot take an Effect Schema.Codec, or a pair of throwing encode/decode functions; Codec.fromSchema spells out the conversion. Encoded operations must be JSON. A schema that needs services to decode is refused, because the codec runs inside the transaction.

Ids are branded Schema values: DocumentId.make, OpId.make, ActorId.make, Sequence.make, Cursor.make.

Errors are tagged, so Effect.catchTag narrows them: JournalError (storage), UnsupportedJournalVersionError, InvalidOperationError, OperationRejectedError, IdentityConflictError, InvalidCursorError, CompactedCursorError, InvalidCompactionError, EffectFailedError.

Metrics. Journal.metrics counts appends, compactions, owner effect runs, and coalesced effect runs.

Migrations. Tables are created and upgraded with a transactional SQLite user_version migration; an interrupted one is retried safely. Your own operation, snapshot, and effect payloads are not migrated.