Sync / foldkit-durable

JournalOptions

interface · packages/durable/src/journal.ts:82

interface JournalOptions<
  Operation,
  Snapshot,
  Principal,
  OperationEncoded = unknown,
  SnapshotEncoded = unknown,
> {
  readonly file: string | Config.Config<string>
  readonly operation: CodecInput<Operation, OperationEncoded>
  readonly snapshot: CodecInput<Snapshot, SnapshotEncoded>
  readonly empty: () => Snapshot
  readonly reduce: (snapshot: Snapshot, operation: Operation) => Snapshot
  readonly stamp?: (
    operation: Operation,
    commit: {
      readonly sequence: Sequence
      readonly actorId: ActorId
    },
  ) => Operation
  readonly opId: (operation: Operation) => OpId
  readonly actorId: (principal: Principal) => ActorId
  readonly validate?: (
    request: ValidationRequest<Operation, Snapshot, Principal>,
  ) => void | Effect.Effect<void, InvalidOperationError>
  readonly authorize?: (
    request: AuthorizationRequest<Operation, Snapshot, Principal>,
  ) =>
    AuthorizationDecision | Effect.Effect<AuthorizationDecision, JournalError>
  readonly snapshotEvery?: number
  readonly replicaId?: (operation: Operation) => string
  readonly d1?: true | undefined
  readonly legacyReplicaId?: (opId: OpId) => string
}

Members

JournalOptions.file

property · packages/durable/src/journal.ts:93

readonly file: string | Config.Config<string>

A node:sqlite path, or :memory:. A Config lets an application supply the path as a layer instead of a literal.

JournalOptions.operation

property · packages/durable/src/journal.ts:95

readonly operation: CodecInput<Operation, OperationEncoded>

An Effect Schema.Codec, or a pair of throwing encode/decode functions.

JournalOptions.snapshot

property · packages/durable/src/journal.ts:96

readonly snapshot: CodecInput<Snapshot, SnapshotEncoded>

JournalOptions.empty

property · packages/durable/src/journal.ts:97

readonly empty: () => Snapshot

JournalOptions.reduce

property · packages/durable/src/journal.ts:99

readonly reduce: (snapshot: Snapshot, operation: Operation) => Snapshot

Deterministic and fast: it runs inside the append transaction.

JournalOptions.stamp

property · packages/durable/src/journal.ts:107

readonly stamp?: (
  operation: Operation,
  commit: {
    readonly sequence: Sequence
    readonly actorId: ActorId
  },
) => Operation

Writes what the commit decided into the operation: its sequence and actor, which the client that sent it cannot know. Runs inside the append transaction after validate and authorize, before reduce, and must keep the operation's identity. The stamped operation is what is stored, reduced, read and recovered; a retry is still recognized by what was sent.

JournalOptions.opId

property · packages/durable/src/journal.ts:112

readonly opId: (operation: Operation) => OpId

Stable identity; a repeat is answered idempotently.

JournalOptions.actorId

property · packages/durable/src/journal.ts:114

readonly actorId: (principal: Principal) => ActorId

The trusted actor recorded for the commit.

JournalOptions.validate

property · packages/durable/src/journal.ts:122

readonly validate?: (
  request: ValidationRequest<Operation, Snapshot, Principal>,
) => void | Effect.Effect<void, InvalidOperationError>

Structural checks, run inside the append transaction before authorize. Throw, or return an Effect that fails with InvalidOperationError. Like authorize it holds the write lock, so it has no service requirement and must stay local to the snapshot. On D1 (d1: true) there is no lock: it sees the latest committed snapshot, and a concurrent commit retries.

JournalOptions.authorize

property · packages/durable/src/journal.ts:133

readonly authorize?: (
  request: AuthorizationRequest<Operation, Snapshot, Principal>,
) =>
  AuthorizationDecision | Effect.Effect<AuthorizationDecision, JournalError>

Policy decision, run inside the append transaction. Return a boolean, a refusal carrying its reason, or an Effect when the decision needs to suspend. A service requirement is not available: the decision runs while the write lock is held, so keep it local to the snapshot and fail with JournalError. On D1 (d1: true) there is no lock; route appends through one writer when the policy must see every commit in order.

JournalOptions.snapshotEvery

property · packages/durable/src/journal.ts:143

readonly snapshotEvery?: number

How many commits pass between writes of the snapshot; load replays the operations since the last one. Default 1, a write per commit. A larger value trades a short replay on load for not encoding a large snapshot per append. The journal keeps the snapshot in memory either way, so an append decodes it only after another connection changed the document.

JournalOptions.replicaId

property · packages/durable/src/journal.ts:151

readonly replicaId?: (operation: Operation) => string

The replica an operation came from, when operations carry one. The first commit from a replica binds it to the committing actor, per document, and an operation from that replica by any other actor is refused (OperationRejectedError) before validate runs, so the replica a committed operation names is one its actor holds. The first actor to use an id claims it, so ids should be unguessable or assigned per actor.

JournalOptions.d1

property · packages/durable/src/journal.ts:162

readonly d1?: true | undefined

Pass d1: true when the SqlClient is Cloudflare D1. D1 has no transactions, refuses PRAGMA user_version and VACUUM, and runs each statement on its own: the schema version lives in a durable_meta table, commits retry on constraint conflicts, vacuum is a no-op (D1 auto-vacuums), and reads are separate statements. Route appends through one writer (a Durable Object): concurrent writers stay safe through the uniqueness constraints, but validate and authorize then see the latest committed snapshot rather than one held under a write lock.

JournalOptions.legacyReplicaId

property · packages/durable/src/journal.ts:169

readonly legacyReplicaId?: (opId: OpId) => string

Recovers a replica from the id of a compacted operation written before schema 6. Required to enable replicaId on such a journal: its payload is gone, so the operation cannot be decoded. The journal refuses to open rather than let another actor claim an unrecognized replica.