experimental-a2 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/{actor-CqibLgjQ.d.ts → actor-Bqyb7gwf.d.ts} +3 -3
- package/dist/{actor-CqibLgjQ.d.ts.map → actor-Bqyb7gwf.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +2 -2
- package/dist/actor-react.d.ts +2 -2
- package/dist/actor-react.js +5 -5
- package/dist/{actor-shared-DFET5q16.js → actor-shared-B4OB6Qo7.js} +3 -3
- package/dist/{actor-shared-DFET5q16.js.map → actor-shared-B4OB6Qo7.js.map} +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +4 -4
- package/dist/{ai-DAu77CDr.d.ts → ai-BZUu5-b7.d.ts} +2 -2
- package/dist/{ai-DAu77CDr.d.ts.map → ai-BZUu5-b7.d.ts.map} +1 -1
- package/dist/{ai-EEfKyc-z.js → ai-CCCNxd3L.js} +3 -3
- package/dist/{ai-EEfKyc-z.js.map → ai-CCCNxd3L.js.map} +1 -1
- package/dist/ai-client.d.ts +2 -2
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.d.ts +1 -1
- package/dist/{ai-harness-DTjkAr2T.d.ts → ai-harness-C8aV869s.d.ts} +2 -2
- package/dist/{ai-harness-DTjkAr2T.d.ts.map → ai-harness-C8aV869s.d.ts.map} +1 -1
- package/dist/ai-pi.d.ts +1 -1
- package/dist/ai-react.d.ts +4 -4
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +3 -3
- package/dist/ai-server.js +4 -4
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +1 -1
- package/dist/{client-CUMbS_38.d.ts → client-CCJASjUM.d.ts} +2 -2
- package/dist/{client-CUMbS_38.d.ts.map → client-CCJASjUM.d.ts.map} +1 -1
- package/dist/{client-BQS4i_V9.js → client-sq-djxhD.js} +4 -4
- package/dist/{client-BQS4i_V9.js.map → client-sq-djxhD.js.map} +1 -1
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1 -1
- package/dist/{contract-CWukt01d.js → contract-DqfBSIqJ.js} +2 -2
- package/dist/{contract-CWukt01d.js.map → contract-DqfBSIqJ.js.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/{initial-snapshot-D3Lve43o.d.ts → initial-snapshot-BA6xwv10.d.ts} +2 -2
- package/dist/initial-snapshot-BA6xwv10.d.ts.map +1 -0
- package/dist/{react-BoHvC3v9.d.ts → react-KC4eW7yG.d.ts} +3 -3
- package/dist/{react-BoHvC3v9.d.ts.map → react-KC4eW7yG.d.ts.map} +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{reducer-CjmvZSAb.js → reducer-Bswr11ar.js} +2 -2
- package/dist/{reducer-CjmvZSAb.js.map → reducer-Bswr11ar.js.map} +1 -1
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +2 -2
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +1 -1
- package/dist/{server-qgez0Xny.d.ts → server-B9gu0qi1.d.ts} +9 -3
- package/dist/server-B9gu0qi1.d.ts.map +1 -0
- package/dist/{server-DtFrWxi3.js → server-DX7mM6nm.js} +22 -14
- package/dist/server-DX7mM6nm.js.map +1 -0
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1 -1
- package/dist/{session-clear-wHd5GCpZ.js → session-clear-XJYpmUpg.js} +14 -2
- package/dist/session-clear-XJYpmUpg.js.map +1 -0
- package/dist/{session-clear-D1PFe7xl.d.ts → session-clear-vfonou9S.d.ts} +2 -2
- package/dist/session-clear-vfonou9S.d.ts.map +1 -0
- package/dist/{store-D0MhBvg6.d.ts → store-DEzcHZOA.d.ts} +11 -1
- package/dist/store-DEzcHZOA.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/{store-codec-DwrWlgQx.js → store-codec-pAG2oPgQ.js} +4 -4
- package/dist/store-codec-pAG2oPgQ.js.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +11 -4
- package/dist/store-memory.js.map +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +26 -7
- package/dist/store-postgres.js.map +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +2 -2
- package/dist/{store-redis-notify-CBDVJxA_.js → store-redis-notify-ys_-VrSP.js} +27 -16
- package/dist/store-redis-notify-ys_-VrSP.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +2 -2
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +11 -4
- package/dist/store-sqlite.js.map +1 -1
- package/dist/store-valkey.d.ts +1 -1
- package/dist/store-valkey.js +2 -2
- package/dist/{wire-CV-Z7wz-.js → wire-BFE9sIQf.js} +3 -2
- package/dist/wire-BFE9sIQf.js.map +1 -0
- package/docs/contract/03-sessions.mdx +51 -0
- package/docs/reference/02-server.mdx +44 -4
- package/docs/reference/06-errors.mdx +1 -0
- package/examples/playground/app/a2slack/consumers/search.ts +1 -1
- package/examples/playground/app/a2slack/db/migrations/0005_a2slack_feed_v2.sql +7 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0005_snapshot.json +1165 -0
- package/examples/playground/app/a2slack/db/migrations/meta/_journal.json +8 -1
- package/examples/playground/app/a2slack/db/queries.ts +1 -1
- package/examples/playground/app/a2slack/drills/a2slack.drill.ts +17 -14
- package/examples/playground/app/a2slack/search-index.ts +1 -1
- package/examples/playground/app/a2slack/server.ts +2 -2
- package/examples/playground/lib/store.ts +2 -0
- package/examples/playground/package.json +1 -1
- package/package.json +1 -1
- package/src/errors.ts +39 -0
- package/src/idempotent-replay.ts +4 -3
- package/src/server.ts +32 -9
- package/src/store-memory.ts +26 -4
- package/src/store-postgres.ts +36 -6
- package/src/store-redis-core.ts +40 -15
- package/src/store-sqlite.ts +25 -4
- package/src/store.ts +10 -0
- package/src/wire.ts +1 -0
- package/dist/initial-snapshot-D3Lve43o.d.ts.map +0 -1
- package/dist/server-DtFrWxi3.js.map +0 -1
- package/dist/server-qgez0Xny.d.ts.map +0 -1
- package/dist/session-clear-D1PFe7xl.d.ts.map +0 -1
- package/dist/session-clear-wHd5GCpZ.js.map +0 -1
- package/dist/store-D0MhBvg6.d.ts.map +0 -1
- package/dist/store-codec-DwrWlgQx.js.map +0 -1
- package/dist/store-redis-notify-CBDVJxA_.js.map +0 -1
- package/dist/wire-CV-Z7wz-.js.map +0 -1
package/dist/store-memory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-memory.js","names":[],"sources":["../src/store-memory.ts"],"sourcesContent":["/**\n * experimental-a2/store-memory — in-memory store backend (the test default).\n *\n * Implements the A2Store interface with zero dependencies. See\n * specs/a2-implementation.md §3 for the contract; the conformance suite\n * in test/conformance is the executable version of it.\n */\n\nimport type { PresencePatch } from './contract.ts'\nimport { A2Error, supersededAttemptError } from './errors.ts'\nimport { idempotentReplay } from './idempotent-replay.ts'\nimport { nullProtoRecord } from './internal.ts'\nimport { CLEAR_EVENT_TYPE, sessionClearedError } from './session-clear.ts'\nimport { idleLogCursor } from './store-codec.ts'\nimport {\n RANDOM_IDS,\n SYSTEM_CLOCK,\n type A2Store,\n type AppendEvent,\n type Clock,\n type Event,\n type EventCause,\n type HeadDispatch,\n type HeadEvent,\n type HeadRef,\n type IdSource,\n type PresenceRow,\n type StoreConsumer,\n type StoredCursor,\n type StoredEvent,\n type StoreStateRead,\n} from './store.ts'\n\nexport type MemoryStoreOptions = {\n /** Injectable clock — every stored timestamp comes from here. */\n clock?: Clock\n /** Injectable id source for generated event ids. */\n ids?: IdSource\n}\n\ntype Row = {\n id: string\n type: string\n payload: unknown\n index: number\n sessionId: string\n createdAt: Date\n cause: EventCause | null\n}\n\ntype Dispatch = Omit<HeadDispatch, 'consumer' | 'lane'>\n\ntype Completion = { attempt: number; returnedEventIds: string[] }\n\n/**\n * One (consumer, lane) record. A log cursor reads the whole log by type, a\n * lane cursor queues its lane's events, and a concurrent cursor (lane null)\n * covers one event.\n */\ntype Cursor = {\n consumer: string\n lane: string | null\n kind: 'log' | 'lane' | 'event'\n position: number\n head: number | null\n dispatch: Dispatch\n /** Lane cursors: the lane's event indexes in log order. */\n queue: number[]\n /** Lane cursors: where the head sits in `queue`. */\n queued: number\n /** Completions by head index; a log cursor keeps only its latest. */\n completions: Map<number, Completion>\n /** Log cursors: the types it takes, null for every type. */\n types: readonly string[] | null\n}\n\n/** A null `value` is a tombstone: it keeps a delete's `at` until it expires. */\ntype PresenceEntry = {\n value: unknown\n seen: number\n at: Date\n expiresAt: Date\n}\n\n/** Contiguous rows; `reclaim` removes from the front below `clearIndex`. */\ntype SessionLog = {\n rows: Row[]\n /** Index of the event before `rows[0]`. */\n offset: number\n /** Latest clear marker, or 0. */\n clearIndex: number\n /** The session's unique index on event ids, like the SQL schemas'. */\n byEventId: Map<string, Row>\n}\n\nconst RECLAIM_BATCH = 256\n\nconst headOf = (log: SessionLog | undefined): number =>\n log ? log.offset + log.rows.length : 0\n\nconst freshDispatch = (): Dispatch => ({\n firstClaimedAt: null,\n lastClaimedAt: null,\n attemptCount: 0,\n claimHolder: null,\n claimExpiresAt: null,\n failureCount: 0,\n lastFailedAt: null,\n lastFailedAttempt: null,\n lastError: null,\n failedAt: null,\n})\n\nconst date = (value: Date | null): Date | null =>\n value ? new Date(value) : null\n\nconst toStored = (row: Row): StoredEvent => ({\n id: row.id,\n type: row.type,\n payload: structuredClone(row.payload),\n index: row.index,\n sessionId: row.sessionId,\n createdAt: new Date(row.createdAt),\n cause: row.cause ? { ...row.cause } : null,\n})\n\nconst toEvent = (row: Row): Event => ({\n id: row.id,\n type: row.type,\n payload: structuredClone(row.payload),\n index: row.index,\n sessionId: row.sessionId,\n createdAt: new Date(row.createdAt),\n})\n\nconst toHead = (cursor: Cursor, row: Row): HeadEvent => ({\n ...toStored(row),\n consumer: cursor.consumer,\n lane: cursor.lane,\n firstClaimedAt: date(cursor.dispatch.firstClaimedAt),\n lastClaimedAt: date(cursor.dispatch.lastClaimedAt),\n attemptCount: cursor.dispatch.attemptCount,\n claimHolder: cursor.dispatch.claimHolder,\n claimExpiresAt: date(cursor.dispatch.claimExpiresAt),\n failureCount: cursor.dispatch.failureCount,\n lastFailedAt: date(cursor.dispatch.lastFailedAt),\n lastFailedAttempt: cursor.dispatch.lastFailedAttempt,\n lastError: cursor.dispatch.lastError,\n failedAt: date(cursor.dispatch.failedAt),\n})\n\nconst toCursor = (cursor: Cursor): StoredCursor => ({\n consumer: cursor.consumer,\n lane: cursor.lane,\n position: cursor.position,\n head: cursor.head,\n firstClaimedAt: date(cursor.dispatch.firstClaimedAt),\n lastClaimedAt: date(cursor.dispatch.lastClaimedAt),\n attemptCount: cursor.dispatch.attemptCount,\n claimHolder: cursor.dispatch.claimHolder,\n claimExpiresAt: date(cursor.dispatch.claimExpiresAt),\n failureCount: cursor.dispatch.failureCount,\n lastFailedAt: date(cursor.dispatch.lastFailedAt),\n lastFailedAttempt: cursor.dispatch.lastFailedAttempt,\n lastError: cursor.dispatch.lastError,\n failedAt: date(cursor.dispatch.failedAt),\n})\n\nconst compareText = (a: string, b: string): number =>\n a < b ? -1 : a > b ? 1 : 0\n\n/** Consumer, then lane, with concurrent cursors last by head. */\nconst byCursor = (a: StoredCursor, b: StoredCursor): number =>\n compareText(a.consumer, b.consumer) ||\n (a.lane === null\n ? b.lane === null\n ? (a.head ?? 0) - (b.head ?? 0)\n : 1\n : b.lane === null\n ? -1\n : compareText(a.lane, b.lane))\n\nconst cursorKey = (\n consumer: string,\n lane: string | null,\n index: number,\n): string =>\n lane === null ? `${consumer}\\u0000#${index}` : `${consumer}\\u0000L${lane}`\n\nconst takes = (types: readonly string[] | null, type: string): boolean =>\n type !== CLEAR_EVENT_TYPE && (types === null || types.includes(type))\n\nconst byHead = (a: HeadEvent, b: HeadEvent): number =>\n a.index - b.index || compareText(a.consumer, b.consumer)\n\nconst snapshotKey = (sessionId: string, reducerName: string): string =>\n `${sessionId}\\u0000${reducerName}`\n\nconst headPinKey = (sessionId: string, ref: HeadRef): string =>\n `${sessionId}\\u0000${cursorKey(ref.consumer, ref.lane, ref.index)}\\u0000${ref.index}`\n\n/** Whether a cursor belongs to a consumer the caller serves; omitted, every one. */\nconst servedBy = (\n consumers: readonly StoreConsumer[] | undefined,\n): ((cursor: Cursor) => boolean) => {\n if (!consumers) return () => true\n const names = new Set(consumers.map((consumer) => consumer.name))\n return (cursor) => names.has(cursor.consumer)\n}\n\n/** A cursor's current head. */\nconst refOf = (cursor: Cursor): HeadRef => ({\n consumer: cursor.consumer,\n lane: cursor.lane,\n index: cursor.head!,\n})\n\nexport function memory(options: MemoryStoreOptions = {}): A2Store {\n const clock = options.clock ?? SYSTEM_CLOCK\n const generateId = options.ids ?? RANDOM_IDS\n\n const sessions = new Map<string, SessionLog>()\n /** Session id → cursor key → cursor. */\n const cursors = new Map<string, Map<string, Cursor>>()\n /** Keyed by `${sessionId}\\u0000${reducerName}` — a pure cache. */\n const snapshots = new Map<\n string,\n { index: number; state: unknown; updatedAt: Date }\n >()\n const historicalSnapshots = new Map<\n string,\n { index: number; state: unknown; updatedAt: Date }\n >()\n const snapshotPins = new Map<string, Set<string>>()\n const snapshotPinCounts = new Map<string, number>()\n const streamSubscribers = new Map<string, Set<(row: Row) => void>>()\n /** ns → participant → field → latest surviving write. */\n const presenceRows = new Map<\n string,\n Map<string, Map<string, PresenceEntry>>\n >()\n const presenceSubscribers = new Map<\n string,\n Set<(patch: PresencePatch) => void>\n >()\n\n const notifyStreams = (sessionId: string, rows: Row[]): void => {\n const subs = streamSubscribers.get(sessionId)\n if (!subs) return\n for (const listener of subs) {\n for (const row of rows) listener(row)\n }\n }\n\n const logOf = (sessionId: string): SessionLog => {\n let log = sessions.get(sessionId)\n if (!log) {\n log = { rows: [], offset: 0, clearIndex: 0, byEventId: new Map() }\n sessions.set(sessionId, log)\n }\n return log\n }\n\n const rowAt = (sessionId: string, index: number): Row | undefined => {\n const log = sessions.get(sessionId)\n return log && index > log.offset\n ? log.rows[index - log.offset - 1]\n : undefined\n }\n\n const clearIndexOf = (sessionId: string): number =>\n sessions.get(sessionId)?.clearIndex ?? 0\n\n /** The session's row owning `id`, ignoring rows below its clear. */\n const ownerOf = (sessionId: string, id: string): Row | undefined => {\n const log = sessions.get(sessionId)\n const row = log?.byEventId.get(id)\n return log && row && row.index >= log.clearIndex ? row : undefined\n }\n\n const cursorsOf = (sessionId: string): Map<string, Cursor> => {\n let map = cursors.get(sessionId)\n if (!map) {\n map = new Map()\n cursors.set(sessionId, map)\n }\n return map\n }\n\n const cursorAt = (sessionId: string, ref: HeadRef): Cursor | undefined =>\n cursors.get(sessionId)?.get(cursorKey(ref.consumer, ref.lane, ref.index))\n\n const checkpointKey = (\n sessionId: string,\n reducerName: string,\n index: number,\n ): string => `${snapshotKey(sessionId, reducerName)}\\u0000${index}`\n\n const releaseSnapshotPins = (sessionId: string, ref: HeadRef): void => {\n const pinKey = headPinKey(sessionId, ref)\n const checkpoints = snapshotPins.get(pinKey)\n if (!checkpoints) return\n for (const key of checkpoints) {\n const next = (snapshotPinCounts.get(key) ?? 1) - 1\n if (next === 0) {\n snapshotPinCounts.delete(key)\n historicalSnapshots.delete(key)\n } else {\n snapshotPinCounts.set(key, next)\n }\n }\n snapshotPins.delete(pinKey)\n }\n\n /** The next event after `after` that a log cursor takes, or null. */\n const nextLogHead = (\n sessionId: string,\n after: number,\n types: readonly string[] | null,\n ): number | null => {\n const log = sessions.get(sessionId)\n if (!log) return null\n for (\n let offset = Math.max(0, after - log.offset);\n offset < log.rows.length;\n offset += 1\n ) {\n const row = log.rows[offset]!\n if (takes(types, row.type)) return row.index\n }\n return null\n }\n\n /** Moves a cursor past its head: the next head starts with fresh bookkeeping. */\n const advance = (sessionId: string, cursor: Cursor): void => {\n const done = cursor.head!\n releaseSnapshotPins(sessionId, refOf(cursor))\n cursor.position = done\n cursor.dispatch = freshDispatch()\n if (cursor.kind === 'event') {\n cursor.head = null\n } else if (cursor.kind === 'lane') {\n cursor.queued += 1\n cursor.head = cursor.queue[cursor.queued] ?? null\n } else {\n cursor.head = nextLogHead(sessionId, done, cursor.types)\n }\n }\n\n const hasPending = (\n sessionId: string,\n consumers: readonly StoreConsumer[] | undefined,\n ): boolean => {\n const served = servedBy(consumers)\n for (const cursor of cursors.get(sessionId)?.values() ?? []) {\n if (\n cursor.head !== null &&\n cursor.dispatch.failedAt === null &&\n served(cursor)\n ) {\n return true\n }\n }\n return false\n }\n\n /** The attempt-currency fence shared by consumer appends and clears. */\n const assertCurrentCause = (sessionId: string, cause: EventCause): void => {\n if (cause.index <= clearIndexOf(sessionId)) {\n throw supersededAttemptError(sessionId, cause)\n }\n const cursor = cursorAt(sessionId, cause)\n if (\n !cursor ||\n cursor.head !== cause.index ||\n cursor.dispatch.attemptCount !== cause.attempt ||\n cursor.dispatch.failedAt !== null\n ) {\n throw supersededAttemptError(sessionId, cause)\n }\n }\n\n const deadLetter = (sessionId: string, cursor: Cursor, now: Date): void => {\n cursor.dispatch.failedAt = new Date(now)\n releaseSnapshotPins(sessionId, refOf(cursor))\n }\n\n const claim = ({\n sessionId,\n cursor,\n holder,\n now,\n expiresAt,\n maxFailures,\n }: {\n sessionId: string\n cursor: Cursor\n holder: string\n now: Date\n expiresAt: Date\n maxFailures: number | undefined\n }): HeadEvent => {\n const dispatch = cursor.dispatch\n const row = rowAt(sessionId, cursor.head!)!\n // A claim still held here lapsed: its attempt never finished.\n if (dispatch.claimHolder !== null) {\n dispatch.failureCount += 1\n if (maxFailures !== undefined && dispatch.failureCount >= maxFailures) {\n // Advancing the attempt fences the lapsed one; nothing runs.\n dispatch.attemptCount += 1\n dispatch.claimHolder = null\n dispatch.claimExpiresAt = null\n deadLetter(sessionId, cursor, now)\n return toHead(cursor, row)\n }\n }\n dispatch.attemptCount += 1\n dispatch.firstClaimedAt ??= new Date(now)\n dispatch.lastClaimedAt = new Date(now)\n dispatch.claimHolder = holder\n dispatch.claimExpiresAt = new Date(expiresAt)\n return toHead(cursor, row)\n }\n\n const checkBatchIds = (\n sessionId: string,\n events: readonly (AppendEvent & { id?: string })[],\n ): Row[] => {\n const supplied = events.filter(\n (candidate): candidate is AppendEvent & { id: string } =>\n candidate.id !== undefined,\n )\n const suppliedIds = new Set(supplied.map((candidate) => candidate.id))\n if (suppliedIds.size !== supplied.length) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'batch contains the same event id more than once',\n )\n }\n return supplied\n .map((candidate) => ownerOf(sessionId, candidate.id))\n .filter((row): row is Row => row !== undefined)\n }\n\n /** Records the types a declared log consumer takes on its cursor. */\n const refreshLogTypes = (\n sessionId: string,\n consumers: readonly StoreConsumer[] | undefined,\n ): void => {\n const map = cursors.get(sessionId)\n if (!map) return\n for (const consumer of consumers ?? []) {\n if (!consumer.log) continue\n const cursor = map.get(cursorKey(consumer.name, consumer.log.lane, 0))\n if (cursor?.kind === 'log') cursor.types = consumer.log.types\n }\n }\n\n /** Starts the cursor work an inserted batch carries. */\n const enqueue = (\n sessionId: string,\n rows: readonly Row[],\n events: readonly AppendEvent[],\n base: number,\n consumers: readonly StoreConsumer[] | undefined,\n ): void => {\n const map = cursorsOf(sessionId)\n rows.forEach((row, offset) => {\n for (const target of events[offset]!.targets ?? []) {\n const key = cursorKey(target.consumer, target.lane, row.index)\n if (target.lane === null) {\n map.set(key, {\n consumer: target.consumer,\n lane: null,\n kind: 'event',\n position: row.index - 1,\n head: row.index,\n dispatch: freshDispatch(),\n queue: [],\n queued: 0,\n completions: new Map(),\n types: null,\n })\n continue\n }\n let cursor = map.get(key)\n if (!cursor) {\n cursor = {\n consumer: target.consumer,\n lane: target.lane,\n kind: 'lane',\n position: clearIndexOf(sessionId),\n head: null,\n dispatch: freshDispatch(),\n queue: [],\n queued: 0,\n completions: new Map(),\n types: null,\n }\n map.set(key, cursor)\n }\n cursor.queue.push(row.index)\n cursor.head ??= row.index\n }\n })\n const clearIndex = clearIndexOf(sessionId)\n for (const consumer of consumers ?? []) {\n const log = consumer.log\n if (!log) continue\n const first = rows.find((row) => takes(log.types, row.type))\n if (!first) continue\n const key = cursorKey(consumer.name, log.lane, 0)\n const cursor = map.get(key)\n if (!cursor) {\n const position = log.backfill ? clearIndex : base\n map.set(key, {\n consumer: consumer.name,\n lane: log.lane,\n kind: 'log',\n position,\n head: log.backfill\n ? nextLogHead(sessionId, position, log.types)\n : first.index,\n dispatch: freshDispatch(),\n queue: [],\n queued: 0,\n completions: new Map(),\n types: log.types,\n })\n } else {\n cursor.types = log.types\n // Not `first`: a deployment that doesn't serve this consumer may\n // have appended events it takes since it went idle.\n cursor.head ??= nextLogHead(sessionId, cursor.position, log.types)\n }\n }\n }\n\n const insert = (\n sessionId: string,\n events: readonly AppendEvent[],\n now: Date,\n consumers: readonly StoreConsumer[] | undefined,\n cause?: EventCause,\n ): Row[] => {\n const log = logOf(sessionId)\n const base = headOf(log)\n const inserted = events.map((candidate, offset): Row => ({\n id: candidate.id ?? generateId(),\n type: candidate.type,\n payload: structuredClone(candidate.payload),\n index: base + 1 + offset,\n sessionId,\n createdAt: new Date(now),\n cause: cause\n ? { ...cause }\n : candidate.cause\n ? { ...candidate.cause }\n : null,\n }))\n for (const row of inserted) {\n if (ownerOf(sessionId, row.id)) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n `event id '${row.id}' already exists`,\n )\n }\n }\n for (const row of inserted) {\n log.rows.push(row)\n log.byEventId.set(row.id, row)\n }\n enqueue(sessionId, inserted, events, base, consumers)\n return inserted\n }\n\n /** Every cached fold of this session below its clear, head or historical. */\n const dropCheckpointsBelow = (\n sessionId: string,\n clearIndex: number,\n ): void => {\n const prefix = `${sessionId}\\u0000`\n for (const [key, snapshot] of snapshots) {\n if (!key.startsWith(prefix) || snapshot.index >= clearIndex) continue\n snapshots.delete(key)\n snapshotPinCounts.delete(`${key}\\u0000${snapshot.index}`)\n }\n for (const [key, snapshot] of historicalSnapshots) {\n if (!key.startsWith(prefix) || snapshot.index >= clearIndex) continue\n historicalSnapshots.delete(key)\n snapshotPinCounts.delete(key)\n }\n }\n\n const readStateOf = (\n sessionId: string,\n reducerName: string,\n throughIndex?: number,\n snapshotThroughIndex?: number,\n ): StoreStateRead => {\n const key = snapshotKey(sessionId, reducerName)\n const clearIndex = clearIndexOf(sessionId)\n const snapshotFrontier = snapshotThroughIndex ?? throughIndex\n const stored = snapshots.get(key)\n const head = stored && stored.index >= clearIndex ? stored : undefined\n let candidate =\n head && (snapshotFrontier === undefined || head.index <= snapshotFrontier)\n ? head\n : null\n if (snapshotFrontier !== undefined) {\n const prefix = `${key}\\u0000`\n for (const [historicalKey, snapshot] of historicalSnapshots) {\n if (\n historicalKey.startsWith(prefix) &&\n snapshot.index >= clearIndex &&\n snapshot.index <= snapshotFrontier &&\n (!candidate || snapshot.index > candidate.index)\n ) {\n candidate = snapshot\n }\n }\n }\n const snapshot = candidate\n ? { index: candidate.index, state: structuredClone(candidate.state) }\n : null\n const afterIndex = Math.max(snapshot?.index ?? 0, clearIndex - 1)\n return {\n headIndex: head?.index ?? null,\n snapshot,\n events: (sessions.get(sessionId)?.rows ?? [])\n .filter(\n (row) =>\n row.index > afterIndex &&\n (throughIndex === undefined || row.index <= throughIndex),\n )\n .map(toEvent),\n ...(clearIndex > 0 ? { clearIndex } : {}),\n }\n }\n\n /** The dead-lettered head a redrive names, or undefined. */\n const deadHeadOf = (\n sessionId: string,\n consumer: string,\n index: number,\n ): Cursor | undefined => {\n if (index <= clearIndexOf(sessionId)) return undefined\n for (const cursor of cursors.get(sessionId)?.values() ?? []) {\n if (\n cursor.consumer === consumer &&\n cursor.head === index &&\n cursor.dispatch.failedAt !== null\n ) {\n return cursor\n }\n }\n return undefined\n }\n\n return {\n async append(sessionId, events, appendOptions) {\n const consumers = appendOptions?.consumers\n if (events.length === 0) {\n return { events: [], hasPending: hasPending(sessionId, consumers) }\n }\n\n // Batch-scoped idempotency (a2-implementation.md §3): all ids\n // already present → lost-ack retry, return the original rows;\n // some present → the caller mixed sent and fresh events.\n const existing = checkBatchIds(sessionId, events)\n if (existing.length > 0) {\n if (existing.length === events.length) {\n return {\n events: idempotentReplay(events, existing.map(toStored)),\n hasPending: hasPending(sessionId, consumers),\n }\n }\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n `batch mixes ${existing.length} already-appended and ${events.length - existing.length} fresh events`,\n )\n }\n\n const originIndex = appendOptions?.originIndex\n if (originIndex !== undefined && originIndex <= clearIndexOf(sessionId)) {\n throw sessionClearedError(\n `scheduled work from event ${originIndex} was cleared in session '${sessionId}'`,\n )\n }\n // Attempt-currency fence: a fresh consumer append commits only while\n // its causal attempt still owns the cursor's head (store.ts `append`).\n for (const event of events) {\n if (event.cause) assertCurrentCause(sessionId, event.cause)\n }\n\n const inserted = insert(sessionId, events, clock.now(), consumers)\n notifyStreams(sessionId, inserted)\n return {\n events: inserted.map(toStored),\n hasPending: hasPending(sessionId, consumers),\n }\n },\n\n async clear({ sessionId, ifLastIndex, cause }) {\n if (cause) assertCurrentCause(sessionId, cause)\n const log = sessions.get(sessionId)\n const head = headOf(log)\n const clearIndex = log?.clearIndex ?? 0\n if (ifLastIndex !== undefined && clearIndex === ifLastIndex + 1) {\n return { cleared: true, index: head, clearIndex }\n }\n if (ifLastIndex !== undefined && ifLastIndex !== head) {\n return { cleared: false, index: head, clearIndex }\n }\n if (head === 0 || clearIndex === head) {\n return { cleared: true, index: head, clearIndex }\n }\n const [marker] = insert(\n sessionId,\n [{ type: CLEAR_EVENT_TYPE, payload: null }],\n clock.now(),\n undefined,\n )\n logOf(sessionId).clearIndex = marker!.index\n // Every event is at or below the marker: no head survives, and log\n // cursors count from the marker.\n const map = cursors.get(sessionId)\n for (const [key, cursor] of map ?? []) {\n if (cursor.head !== null) releaseSnapshotPins(sessionId, refOf(cursor))\n if (cursor.kind !== 'log') {\n map!.delete(key)\n continue\n }\n cursor.position = marker!.index\n cursor.head = null\n cursor.dispatch = freshDispatch()\n cursor.completions.clear()\n }\n notifyStreams(sessionId, [marker!])\n return { cleared: true, index: marker!.index, clearIndex: marker!.index }\n },\n\n async reclaim(sessionId) {\n const log = sessions.get(sessionId)\n if (!log || log.offset >= log.clearIndex - 1) {\n return { deleted: 0, done: true }\n }\n const removed = log.rows.splice(\n 0,\n Math.min(RECLAIM_BATCH, log.clearIndex - 1 - log.offset),\n )\n log.offset += removed.length\n for (const row of removed) {\n // A reused id belongs to its new row now.\n if (log.byEventId.get(row.id) === row) log.byEventId.delete(row.id)\n }\n const done = log.offset === log.clearIndex - 1\n // Writes below the marker are dropped, so one sweep at the end suffices.\n if (done) dropCheckpointsBelow(sessionId, log.clearIndex)\n return { deleted: removed.length, done }\n },\n\n async wakeLog({ sessionId, consumer }) {\n const { log } = consumer\n const key = cursorKey(consumer.name, log.lane, 0)\n const head = headOf(sessions.get(sessionId))\n let cursor = cursors.get(sessionId)?.get(key)\n if (!cursor) {\n const marker = clearIndexOf(sessionId)\n if (head === marker) {\n return {\n cursor: idleLogCursor(consumer.name, log.lane, marker),\n head,\n }\n }\n const position = log.backfill ? marker : head\n cursor = {\n consumer: consumer.name,\n lane: log.lane,\n kind: 'log',\n position,\n head: log.backfill\n ? nextLogHead(sessionId, position, log.types)\n : null,\n dispatch: freshDispatch(),\n queue: [],\n queued: 0,\n completions: new Map(),\n types: log.types,\n }\n cursorsOf(sessionId).set(key, cursor)\n } else {\n cursor.types = log.types\n cursor.head ??= nextLogHead(sessionId, cursor.position, log.types)\n }\n return { cursor: toCursor(cursor), head }\n },\n\n async cursors(sessionId) {\n return [...(cursors.get(sessionId)?.values() ?? [])]\n .filter((cursor) => cursor.kind !== 'event' || cursor.head !== null)\n .map(toCursor)\n .toSorted(byCursor)\n },\n\n async read(sessionId, opts) {\n const log = sessions.get(sessionId)\n if (!log) return []\n const head = headOf(log)\n const after = Math.max(\n opts?.afterIndex ?? 0,\n log.clearIndex - 1,\n log.offset,\n )\n const through = Math.min(opts?.throughIndex ?? head, head)\n if (through <= after) return []\n return log.rows\n .slice(after - log.offset, through - log.offset)\n .map(toStored)\n },\n\n async claimAvailable({\n sessionId,\n holder,\n ttlMs,\n expiresAtMs,\n exclude,\n consumers,\n maxFailures,\n through,\n }) {\n refreshLogTypes(sessionId, consumers)\n const claimedAt = clock.now()\n const now = claimedAt.getTime()\n const expiresAt = new Date(expiresAtMs ?? now + ttlMs)\n const excluded = new Set(\n (exclude ?? []).map((ref) =>\n cursorKey(ref.consumer, ref.lane, ref.index),\n ),\n )\n const served = servedBy(consumers)\n const eligible: Cursor[] = []\n let retryAt: Date | null = null\n\n for (const [key, cursor] of cursors.get(sessionId) ?? []) {\n if (\n cursor.head === null ||\n (through !== undefined && cursor.head > through) ||\n cursor.dispatch.failedAt !== null ||\n !served(cursor) ||\n excluded.has(key)\n ) {\n continue\n }\n const expiry = cursor.dispatch.claimExpiresAt\n if (expiry && expiry.getTime() > now) {\n if (!retryAt || expiry < retryAt) retryAt = new Date(expiry)\n continue\n }\n eligible.push(cursor)\n }\n\n if (eligible.length === 0) {\n return retryAt ? { outcome: 'busy', retryAt } : { outcome: 'settled' }\n }\n const clearIndex = clearIndexOf(sessionId)\n const claimed = eligible\n .map((cursor) => {\n const head = claim({\n sessionId,\n cursor,\n holder,\n now: claimedAt,\n expiresAt,\n maxFailures,\n })\n if (clearIndex > 0) head.clearIndex = clearIndex\n return head\n })\n .toSorted(byHead)\n return {\n outcome: 'claimed',\n events: claimed,\n head: headOf(sessions.get(sessionId)),\n }\n },\n\n async renewClaims({ sessionId, holder, claims, ttlMs, expiresAtMs }) {\n const now = clock.now()\n const expiresAt = new Date(expiresAtMs ?? now.getTime() + ttlMs)\n const clearIndex = clearIndexOf(sessionId)\n const renewed: HeadRef[] = []\n const superseded: HeadRef[] = []\n const ordered = claims.toSorted(\n (a, b) => a.index - b.index || compareText(a.consumer, b.consumer),\n )\n for (const { attempt, ...ref } of ordered) {\n if (ref.index <= clearIndex) {\n superseded.push(ref)\n continue\n }\n const cursor = cursorAt(sessionId, ref)\n if (!cursor) continue\n const dispatch = cursor.dispatch\n // An event the cursor has not reached yet holds no claim to renew.\n if (cursor.head !== ref.index && cursor.position < ref.index) continue\n if (cursor.head !== ref.index || dispatch.attemptCount > attempt) {\n superseded.push(ref)\n continue\n }\n if (\n dispatch.failedAt === null &&\n dispatch.claimHolder === holder &&\n dispatch.claimExpiresAt !== null &&\n dispatch.claimExpiresAt > now\n ) {\n dispatch.claimExpiresAt = new Date(expiresAt)\n renewed.push(ref)\n }\n }\n return { renewed, superseded }\n },\n\n async completeAttempt({\n sessionId,\n consumer,\n lane,\n index,\n attempt,\n events,\n consumers,\n }) {\n if (index <= clearIndexOf(sessionId)) return { outcome: 'superseded' }\n const ref = { consumer, lane, index }\n const cursor = cursorAt(sessionId, ref)\n if (!cursor) {\n if (!rowAt(sessionId, index)) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n return { outcome: 'superseded' }\n }\n refreshLogTypes(sessionId, consumers)\n\n if (cursor.head !== index) {\n const completion = cursor.completions.get(index)\n if (!completion || completion.attempt !== attempt) {\n return { outcome: 'superseded' }\n }\n const requestedIds = events.map((event) => event.id)\n if (\n completion.returnedEventIds.length !== requestedIds.length ||\n completion.returnedEventIds.some(\n (id, offset) => id !== requestedIds[offset],\n )\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'completed attempt does not match the returned event batch',\n )\n }\n const existing = checkBatchIds(sessionId, events)\n if (\n existing.length !== events.length ||\n existing.some(\n (row) =>\n row.cause?.consumer !== consumer ||\n row.cause.lane !== lane ||\n row.cause.index !== index ||\n row.cause.attempt !== attempt,\n )\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'completed attempt does not match the returned event batch',\n )\n }\n return {\n outcome: 'completed',\n events:\n events.length === 0\n ? []\n : idempotentReplay(events, existing.map(toStored)),\n }\n }\n const dispatch = cursor.dispatch\n if (\n dispatch.attemptCount !== attempt ||\n dispatch.failedAt !== null ||\n dispatch.claimHolder === null\n ) {\n return { outcome: 'superseded' }\n }\n\n const existing = checkBatchIds(sessionId, events)\n if (existing.length > 0) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'returned event batch contains already-appended ids',\n )\n }\n const inserted = insert(sessionId, events, clock.now(), consumers, {\n consumer,\n lane,\n index,\n attempt,\n })\n if (cursor.kind === 'log') cursor.completions.clear()\n cursor.completions.set(index, {\n attempt,\n returnedEventIds: events.map((event) => event.id),\n })\n advance(sessionId, cursor)\n notifyStreams(sessionId, inserted)\n return { outcome: 'completed', events: inserted.map(toStored) }\n },\n\n async failAttempt({\n sessionId,\n consumer,\n lane,\n index,\n attempt,\n error,\n maxFailures,\n retryAfterMs,\n }) {\n if (index <= clearIndexOf(sessionId)) {\n return { outcome: 'superseded', failureCount: 0 }\n }\n const cursor = cursorAt(sessionId, { consumer, lane, index })\n if (!cursor) {\n if (!rowAt(sessionId, index)) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n return { outcome: 'superseded', failureCount: 0 }\n }\n const dispatch = cursor.dispatch\n if (\n cursor.head !== index ||\n dispatch.attemptCount !== attempt ||\n (dispatch.claimHolder === null &&\n dispatch.lastFailedAttempt !== attempt)\n ) {\n return {\n outcome: 'superseded',\n failureCount: cursor.head === index ? dispatch.failureCount : 0,\n }\n }\n if (\n dispatch.lastFailedAttempt === attempt &&\n dispatch.claimHolder === null\n ) {\n return {\n outcome: dispatch.failedAt ? 'dead_lettered' : 'failed',\n failureCount: dispatch.failureCount,\n }\n }\n if (dispatch.failedAt) {\n return { outcome: 'dead_lettered', failureCount: dispatch.failureCount }\n }\n dispatch.failureCount += 1\n dispatch.lastError = error\n const failedAt = clock.now()\n dispatch.lastFailedAt = new Date(failedAt)\n dispatch.lastFailedAttempt = attempt\n dispatch.claimHolder = null\n dispatch.claimExpiresAt =\n retryAfterMs === undefined || dispatch.failureCount >= maxFailures\n ? null\n : new Date(failedAt.getTime() + retryAfterMs)\n if (dispatch.failureCount >= maxFailures) {\n deadLetter(sessionId, cursor, failedAt)\n return {\n outcome: 'dead_lettered',\n failureCount: dispatch.failureCount,\n }\n }\n return { outcome: 'failed', failureCount: dispatch.failureCount }\n },\n\n async deadLetters(sessionId) {\n const clearIndex = clearIndexOf(sessionId)\n const dead: HeadEvent[] = []\n for (const cursor of cursors.get(sessionId)?.values() ?? []) {\n if (\n cursor.head === null ||\n cursor.head <= clearIndex ||\n cursor.dispatch.failedAt === null\n ) {\n continue\n }\n dead.push(toHead(cursor, rowAt(sessionId, cursor.head)!))\n }\n return dead.toSorted(byHead)\n },\n\n async redrive({ sessionId, consumer, index, action, consumers }) {\n const cursor = deadHeadOf(sessionId, consumer, index)\n if (!cursor) {\n return { redriven: false, hasPending: hasPending(sessionId, consumers) }\n }\n refreshLogTypes(sessionId, consumers)\n if (action === 'skip') {\n advance(sessionId, cursor)\n } else {\n cursor.dispatch.failedAt = null\n cursor.dispatch.failureCount = 0\n }\n return { redriven: true, hasPending: hasPending(sessionId, consumers) }\n },\n\n async readState(sessionId, reducerName, stateOptions) {\n return readStateOf(\n sessionId,\n reducerName,\n stateOptions?.throughIndex,\n stateOptions?.snapshotThroughIndex,\n )\n },\n\n async readStates(requests, reducerName) {\n return requests.map((request) =>\n readStateOf(\n request.sessionId,\n reducerName,\n request.throughIndex,\n request.snapshotThroughIndex,\n ),\n )\n },\n\n async putSnapshots(sessionId, reducerName, writes) {\n const key = snapshotKey(sessionId, reducerName)\n const clearIndex = clearIndexOf(sessionId)\n for (const write of writes.toSorted((a, b) => a.index - b.index)) {\n if (write.index < clearIndex) continue\n const checkpoint = checkpointKey(sessionId, reducerName, write.index)\n for (const pin of write.pins ?? []) {\n const cursor = cursorAt(sessionId, pin)\n if (\n !cursor ||\n pin.index <= clearIndex ||\n cursor.head !== pin.index ||\n cursor.dispatch.failedAt !== null\n ) {\n continue\n }\n const pinKey = headPinKey(sessionId, pin)\n let pinned = snapshotPins.get(pinKey)\n if (!pinned) {\n pinned = new Set()\n snapshotPins.set(pinKey, pinned)\n }\n if (!pinned.has(checkpoint)) {\n pinned.add(checkpoint)\n snapshotPinCounts.set(\n checkpoint,\n (snapshotPinCounts.get(checkpoint) ?? 0) + 1,\n )\n }\n }\n\n const current = snapshots.get(key)\n if (!current || write.index >= current.index) {\n if (\n current &&\n write.index > current.index &&\n current.index >= clearIndex\n ) {\n const currentCheckpoint = checkpointKey(\n sessionId,\n reducerName,\n current.index,\n )\n if ((snapshotPinCounts.get(currentCheckpoint) ?? 0) > 0) {\n historicalSnapshots.set(currentCheckpoint, {\n index: current.index,\n state: structuredClone(current.state),\n updatedAt: new Date(current.updatedAt),\n })\n }\n }\n snapshots.set(key, {\n index: write.index,\n state: structuredClone(write.state),\n updatedAt: clock.now(),\n })\n historicalSnapshots.delete(checkpoint)\n } else if (\n write.index < current.index &&\n (snapshotPinCounts.get(checkpoint) ?? 0) > 0\n ) {\n historicalSnapshots.set(checkpoint, {\n index: write.index,\n state: structuredClone(write.state),\n updatedAt: clock.now(),\n })\n }\n }\n },\n\n presence: {\n async set(ns, participant, values, meta) {\n let participants = presenceRows.get(ns)\n if (!participants) {\n participants = new Map()\n presenceRows.set(ns, participants)\n }\n let fields = participants.get(participant)\n if (!fields) {\n fields = new Map()\n participants.set(participant, fields)\n }\n\n // Expiry anchors on the storage's own clock — the sender's\n // `at` orders writes but never extends or shortens a lifetime.\n const now = clock.now()\n const expiresAtMs = now.getTime() + meta.ttlMs\n // Field-keyed and caller-named — null-prototype, like every\n // presence map (see `nullProtoRecord`).\n const applied: Record<string, unknown> = nullProtoRecord()\n let appliedCount = 0\n for (const [field, value] of Object.entries(values)) {\n // Field-wise LWW by `at` against unexpired values and\n // tombstones alike; ties go to the incoming write, so\n // same-stamp sets keep set-then-read intuition.\n const existing = fields.get(field)\n if (existing && existing.expiresAt > now && existing.at > meta.at)\n continue\n fields.set(field, {\n value: structuredClone(value),\n seen: meta.seen,\n at: new Date(meta.at),\n expiresAt: new Date(expiresAtMs),\n })\n applied[field] = structuredClone(value)\n appliedCount += 1\n }\n if (fields.size === 0) participants.delete(participant)\n if (participants.size === 0) presenceRows.delete(ns)\n\n // Only applied fields broadcast — a losing write repaints nothing,\n // so subscribers stay consistent with what read() returns.\n if (appliedCount === 0) return\n const subs = presenceSubscribers.get(ns)\n if (!subs) return\n const patch: PresencePatch = {\n participant,\n values: applied,\n seen: meta.seen,\n at: new Date(meta.at),\n }\n for (const listener of subs) listener(patch)\n },\n\n async read(ns) {\n const participants = presenceRows.get(ns)\n if (!participants) return []\n const now = clock.now()\n const rows: PresenceRow[] = []\n for (const [participant, fields] of participants) {\n for (const [field, entry] of fields) {\n // Expiry is enforced lazily on read — no timers; a silent\n // participant's rows vanish the next time anyone looks.\n if (entry.expiresAt <= now) {\n fields.delete(field)\n continue\n }\n if (entry.value === null) continue\n rows.push({\n participant,\n field,\n value: structuredClone(entry.value),\n seen: entry.seen,\n at: new Date(entry.at),\n expiresAt: new Date(entry.expiresAt),\n })\n }\n if (fields.size === 0) participants.delete(participant)\n }\n if (participants.size === 0) presenceRows.delete(ns)\n return rows\n },\n\n subscribe(ns, onPatch) {\n let subs = presenceSubscribers.get(ns)\n if (!subs) {\n subs = new Set()\n presenceSubscribers.set(ns, subs)\n }\n subs.add(onPatch)\n return () => {\n subs.delete(onPatch)\n if (subs.size === 0) presenceSubscribers.delete(ns)\n }\n },\n },\n\n stream(sessionId, opts) {\n const startAfter = opts?.startAfter ?? 0\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n let last = Math.max(startAfter, clearIndexOf(sessionId) - 1)\n let wake: (() => void) | null = null\n let closed = false\n\n const onRow = (): void => {\n wake?.()\n }\n let subs = streamSubscribers.get(sessionId)\n if (!subs) {\n subs = new Set()\n streamSubscribers.set(sessionId, subs)\n }\n subs.add(onRow)\n const stop = (): void => {\n if (closed) return\n closed = true\n subs.delete(onRow)\n if (subs.size === 0) streamSubscribers.delete(sessionId)\n wake?.()\n }\n\n return {\n async next(): Promise<IteratorResult<Event>> {\n try {\n for (;;) {\n if (closed) return { value: undefined, done: true }\n const log = sessions.get(sessionId)\n // Rows reclaimed behind a running stream are skipped.\n const row = log?.rows[Math.max(0, last - log.offset)]\n if (row) {\n const value = toStored(row)\n last = row.index\n return { value, done: false }\n }\n // oxlint-disable-next-line no-await-in-loop -- wait-for-wake\n await new Promise<void>((resolve) => {\n wake = resolve\n })\n wake = null\n }\n } catch (error) {\n stop()\n throw error\n }\n },\n async return(): Promise<IteratorResult<Event>> {\n stop()\n return { value: undefined, done: true }\n },\n }\n },\n }\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"store-memory.js","names":[],"sources":["../src/store-memory.ts"],"sourcesContent":["/**\n * experimental-a2/store-memory — in-memory store backend (the test default).\n *\n * Implements the A2Store interface with zero dependencies. See\n * specs/a2-implementation.md §3 for the contract; the conformance suite\n * in test/conformance is the executable version of it.\n */\n\nimport type { PresencePatch } from './contract.ts'\nimport {\n A2Error,\n duplicateBatchError,\n lastIndexMismatchError,\n supersededAttemptError,\n} from './errors.ts'\nimport { idempotentReplay } from './idempotent-replay.ts'\nimport { nullProtoRecord } from './internal.ts'\nimport { CLEAR_EVENT_TYPE, sessionClearedError } from './session-clear.ts'\nimport { idleLogCursor } from './store-codec.ts'\nimport {\n RANDOM_IDS,\n SYSTEM_CLOCK,\n type A2Store,\n type AppendEvent,\n type Clock,\n type Event,\n type EventCause,\n type HeadDispatch,\n type HeadEvent,\n type HeadRef,\n type IdSource,\n type PresenceRow,\n type StoreConsumer,\n type StoredCursor,\n type StoredEvent,\n type StoreStateRead,\n} from './store.ts'\n\nexport type MemoryStoreOptions = {\n /** Injectable clock — every stored timestamp comes from here. */\n clock?: Clock\n /** Injectable id source for generated event ids. */\n ids?: IdSource\n}\n\ntype Row = {\n id: string\n type: string\n payload: unknown\n index: number\n sessionId: string\n createdAt: Date\n cause: EventCause | null\n}\n\ntype Dispatch = Omit<HeadDispatch, 'consumer' | 'lane'>\n\ntype Completion = { attempt: number; returnedEventIds: string[] }\n\n/**\n * One (consumer, lane) record. A log cursor reads the whole log by type, a\n * lane cursor queues its lane's events, and a concurrent cursor (lane null)\n * covers one event.\n */\ntype Cursor = {\n consumer: string\n lane: string | null\n kind: 'log' | 'lane' | 'event'\n position: number\n head: number | null\n dispatch: Dispatch\n /** Lane cursors: the lane's event indexes in log order. */\n queue: number[]\n /** Lane cursors: where the head sits in `queue`. */\n queued: number\n /** Completions by head index; a log cursor keeps only its latest. */\n completions: Map<number, Completion>\n /** Log cursors: the types it takes, null for every type. */\n types: readonly string[] | null\n}\n\n/** A null `value` is a tombstone: it keeps a delete's `at` until it expires. */\ntype PresenceEntry = {\n value: unknown\n seen: number\n at: Date\n expiresAt: Date\n}\n\n/** Contiguous rows; `reclaim` removes from the front below `clearIndex`. */\ntype SessionLog = {\n rows: Row[]\n /** Index of the event before `rows[0]`. */\n offset: number\n /** Latest clear marker, or 0. */\n clearIndex: number\n /** The session's unique index on event ids, like the SQL schemas'. */\n byEventId: Map<string, Row>\n}\n\nconst RECLAIM_BATCH = 256\n\nconst headOf = (log: SessionLog | undefined): number =>\n log ? log.offset + log.rows.length : 0\n\nconst freshDispatch = (): Dispatch => ({\n firstClaimedAt: null,\n lastClaimedAt: null,\n attemptCount: 0,\n claimHolder: null,\n claimExpiresAt: null,\n failureCount: 0,\n lastFailedAt: null,\n lastFailedAttempt: null,\n lastError: null,\n failedAt: null,\n})\n\nconst date = (value: Date | null): Date | null =>\n value ? new Date(value) : null\n\nconst toStored = (row: Row): StoredEvent => ({\n id: row.id,\n type: row.type,\n payload: structuredClone(row.payload),\n index: row.index,\n sessionId: row.sessionId,\n createdAt: new Date(row.createdAt),\n cause: row.cause ? { ...row.cause } : null,\n})\n\nconst toEvent = (row: Row): Event => ({\n id: row.id,\n type: row.type,\n payload: structuredClone(row.payload),\n index: row.index,\n sessionId: row.sessionId,\n createdAt: new Date(row.createdAt),\n})\n\nconst toHead = (cursor: Cursor, row: Row): HeadEvent => ({\n ...toStored(row),\n consumer: cursor.consumer,\n lane: cursor.lane,\n firstClaimedAt: date(cursor.dispatch.firstClaimedAt),\n lastClaimedAt: date(cursor.dispatch.lastClaimedAt),\n attemptCount: cursor.dispatch.attemptCount,\n claimHolder: cursor.dispatch.claimHolder,\n claimExpiresAt: date(cursor.dispatch.claimExpiresAt),\n failureCount: cursor.dispatch.failureCount,\n lastFailedAt: date(cursor.dispatch.lastFailedAt),\n lastFailedAttempt: cursor.dispatch.lastFailedAttempt,\n lastError: cursor.dispatch.lastError,\n failedAt: date(cursor.dispatch.failedAt),\n})\n\nconst toCursor = (cursor: Cursor): StoredCursor => ({\n consumer: cursor.consumer,\n lane: cursor.lane,\n position: cursor.position,\n head: cursor.head,\n firstClaimedAt: date(cursor.dispatch.firstClaimedAt),\n lastClaimedAt: date(cursor.dispatch.lastClaimedAt),\n attemptCount: cursor.dispatch.attemptCount,\n claimHolder: cursor.dispatch.claimHolder,\n claimExpiresAt: date(cursor.dispatch.claimExpiresAt),\n failureCount: cursor.dispatch.failureCount,\n lastFailedAt: date(cursor.dispatch.lastFailedAt),\n lastFailedAttempt: cursor.dispatch.lastFailedAttempt,\n lastError: cursor.dispatch.lastError,\n failedAt: date(cursor.dispatch.failedAt),\n})\n\nconst compareText = (a: string, b: string): number =>\n a < b ? -1 : a > b ? 1 : 0\n\n/** Consumer, then lane, with concurrent cursors last by head. */\nconst byCursor = (a: StoredCursor, b: StoredCursor): number =>\n compareText(a.consumer, b.consumer) ||\n (a.lane === null\n ? b.lane === null\n ? (a.head ?? 0) - (b.head ?? 0)\n : 1\n : b.lane === null\n ? -1\n : compareText(a.lane, b.lane))\n\nconst cursorKey = (\n consumer: string,\n lane: string | null,\n index: number,\n): string =>\n lane === null ? `${consumer}\\u0000#${index}` : `${consumer}\\u0000L${lane}`\n\nconst takes = (types: readonly string[] | null, type: string): boolean =>\n type !== CLEAR_EVENT_TYPE && (types === null || types.includes(type))\n\nconst byHead = (a: HeadEvent, b: HeadEvent): number =>\n a.index - b.index || compareText(a.consumer, b.consumer)\n\nconst snapshotKey = (sessionId: string, reducerName: string): string =>\n `${sessionId}\\u0000${reducerName}`\n\nconst headPinKey = (sessionId: string, ref: HeadRef): string =>\n `${sessionId}\\u0000${cursorKey(ref.consumer, ref.lane, ref.index)}\\u0000${ref.index}`\n\n/** Whether a cursor belongs to a consumer the caller serves; omitted, every one. */\nconst servedBy = (\n consumers: readonly StoreConsumer[] | undefined,\n): ((cursor: Cursor) => boolean) => {\n if (!consumers) return () => true\n const names = new Set(consumers.map((consumer) => consumer.name))\n return (cursor) => names.has(cursor.consumer)\n}\n\n/** A cursor's current head. */\nconst refOf = (cursor: Cursor): HeadRef => ({\n consumer: cursor.consumer,\n lane: cursor.lane,\n index: cursor.head!,\n})\n\nexport function memory(options: MemoryStoreOptions = {}): A2Store {\n const clock = options.clock ?? SYSTEM_CLOCK\n const generateId = options.ids ?? RANDOM_IDS\n\n const sessions = new Map<string, SessionLog>()\n /** Session id → cursor key → cursor. */\n const cursors = new Map<string, Map<string, Cursor>>()\n /** Keyed by `${sessionId}\\u0000${reducerName}` — a pure cache. */\n const snapshots = new Map<\n string,\n { index: number; state: unknown; updatedAt: Date }\n >()\n const historicalSnapshots = new Map<\n string,\n { index: number; state: unknown; updatedAt: Date }\n >()\n const snapshotPins = new Map<string, Set<string>>()\n const snapshotPinCounts = new Map<string, number>()\n const streamSubscribers = new Map<string, Set<(row: Row) => void>>()\n /** ns → participant → field → latest surviving write. */\n const presenceRows = new Map<\n string,\n Map<string, Map<string, PresenceEntry>>\n >()\n const presenceSubscribers = new Map<\n string,\n Set<(patch: PresencePatch) => void>\n >()\n\n const notifyStreams = (sessionId: string, rows: Row[]): void => {\n const subs = streamSubscribers.get(sessionId)\n if (!subs) return\n for (const listener of subs) {\n for (const row of rows) listener(row)\n }\n }\n\n const logOf = (sessionId: string): SessionLog => {\n let log = sessions.get(sessionId)\n if (!log) {\n log = { rows: [], offset: 0, clearIndex: 0, byEventId: new Map() }\n sessions.set(sessionId, log)\n }\n return log\n }\n\n const rowAt = (sessionId: string, index: number): Row | undefined => {\n const log = sessions.get(sessionId)\n return log && index > log.offset\n ? log.rows[index - log.offset - 1]\n : undefined\n }\n\n const clearIndexOf = (sessionId: string): number =>\n sessions.get(sessionId)?.clearIndex ?? 0\n\n /** The session's row owning `id`, ignoring rows below its clear. */\n const ownerOf = (sessionId: string, id: string): Row | undefined => {\n const log = sessions.get(sessionId)\n const row = log?.byEventId.get(id)\n return log && row && row.index >= log.clearIndex ? row : undefined\n }\n\n const cursorsOf = (sessionId: string): Map<string, Cursor> => {\n let map = cursors.get(sessionId)\n if (!map) {\n map = new Map()\n cursors.set(sessionId, map)\n }\n return map\n }\n\n const cursorAt = (sessionId: string, ref: HeadRef): Cursor | undefined =>\n cursors.get(sessionId)?.get(cursorKey(ref.consumer, ref.lane, ref.index))\n\n const checkpointKey = (\n sessionId: string,\n reducerName: string,\n index: number,\n ): string => `${snapshotKey(sessionId, reducerName)}\\u0000${index}`\n\n const releaseSnapshotPins = (sessionId: string, ref: HeadRef): void => {\n const pinKey = headPinKey(sessionId, ref)\n const checkpoints = snapshotPins.get(pinKey)\n if (!checkpoints) return\n for (const key of checkpoints) {\n const next = (snapshotPinCounts.get(key) ?? 1) - 1\n if (next === 0) {\n snapshotPinCounts.delete(key)\n historicalSnapshots.delete(key)\n } else {\n snapshotPinCounts.set(key, next)\n }\n }\n snapshotPins.delete(pinKey)\n }\n\n /** The next event after `after` that a log cursor takes, or null. */\n const nextLogHead = (\n sessionId: string,\n after: number,\n types: readonly string[] | null,\n ): number | null => {\n const log = sessions.get(sessionId)\n if (!log) return null\n for (\n let offset = Math.max(0, after - log.offset);\n offset < log.rows.length;\n offset += 1\n ) {\n const row = log.rows[offset]!\n if (takes(types, row.type)) return row.index\n }\n return null\n }\n\n /** Moves a cursor past its head: the next head starts with fresh bookkeeping. */\n const advance = (sessionId: string, cursor: Cursor): void => {\n const done = cursor.head!\n releaseSnapshotPins(sessionId, refOf(cursor))\n cursor.position = done\n cursor.dispatch = freshDispatch()\n if (cursor.kind === 'event') {\n cursor.head = null\n } else if (cursor.kind === 'lane') {\n cursor.queued += 1\n cursor.head = cursor.queue[cursor.queued] ?? null\n } else {\n cursor.head = nextLogHead(sessionId, done, cursor.types)\n }\n }\n\n const hasPending = (\n sessionId: string,\n consumers: readonly StoreConsumer[] | undefined,\n ): boolean => {\n const served = servedBy(consumers)\n for (const cursor of cursors.get(sessionId)?.values() ?? []) {\n if (\n cursor.head !== null &&\n cursor.dispatch.failedAt === null &&\n served(cursor)\n ) {\n return true\n }\n }\n return false\n }\n\n /** The attempt-currency fence shared by consumer appends and clears. */\n const assertCurrentCause = (sessionId: string, cause: EventCause): void => {\n if (cause.index <= clearIndexOf(sessionId)) {\n throw supersededAttemptError(sessionId, cause)\n }\n const cursor = cursorAt(sessionId, cause)\n if (\n !cursor ||\n cursor.head !== cause.index ||\n cursor.dispatch.attemptCount !== cause.attempt ||\n cursor.dispatch.failedAt !== null\n ) {\n throw supersededAttemptError(sessionId, cause)\n }\n }\n\n const deadLetter = (sessionId: string, cursor: Cursor, now: Date): void => {\n cursor.dispatch.failedAt = new Date(now)\n releaseSnapshotPins(sessionId, refOf(cursor))\n }\n\n const claim = ({\n sessionId,\n cursor,\n holder,\n now,\n expiresAt,\n maxFailures,\n }: {\n sessionId: string\n cursor: Cursor\n holder: string\n now: Date\n expiresAt: Date\n maxFailures: number | undefined\n }): HeadEvent => {\n const dispatch = cursor.dispatch\n const row = rowAt(sessionId, cursor.head!)!\n // A claim still held here lapsed: its attempt never finished.\n if (dispatch.claimHolder !== null) {\n dispatch.failureCount += 1\n if (maxFailures !== undefined && dispatch.failureCount >= maxFailures) {\n // Advancing the attempt fences the lapsed one; nothing runs.\n dispatch.attemptCount += 1\n dispatch.claimHolder = null\n dispatch.claimExpiresAt = null\n deadLetter(sessionId, cursor, now)\n return toHead(cursor, row)\n }\n }\n dispatch.attemptCount += 1\n dispatch.firstClaimedAt ??= new Date(now)\n dispatch.lastClaimedAt = new Date(now)\n dispatch.claimHolder = holder\n dispatch.claimExpiresAt = new Date(expiresAt)\n return toHead(cursor, row)\n }\n\n const checkBatchIds = (\n sessionId: string,\n events: readonly (AppendEvent & { id?: string })[],\n ): Row[] => {\n const supplied = events.filter(\n (candidate): candidate is AppendEvent & { id: string } =>\n candidate.id !== undefined,\n )\n const suppliedIds = new Set(supplied.map((candidate) => candidate.id))\n if (suppliedIds.size !== supplied.length) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'batch contains the same event id more than once',\n )\n }\n return supplied\n .map((candidate) => ownerOf(sessionId, candidate.id))\n .filter((row): row is Row => row !== undefined)\n }\n\n /** Records the types a declared log consumer takes on its cursor. */\n const refreshLogTypes = (\n sessionId: string,\n consumers: readonly StoreConsumer[] | undefined,\n ): void => {\n const map = cursors.get(sessionId)\n if (!map) return\n for (const consumer of consumers ?? []) {\n if (!consumer.log) continue\n const cursor = map.get(cursorKey(consumer.name, consumer.log.lane, 0))\n if (cursor?.kind === 'log') cursor.types = consumer.log.types\n }\n }\n\n /** Starts the cursor work an inserted batch carries. */\n const enqueue = (\n sessionId: string,\n rows: readonly Row[],\n events: readonly AppendEvent[],\n base: number,\n consumers: readonly StoreConsumer[] | undefined,\n ): void => {\n const map = cursorsOf(sessionId)\n rows.forEach((row, offset) => {\n for (const target of events[offset]!.targets ?? []) {\n const key = cursorKey(target.consumer, target.lane, row.index)\n if (target.lane === null) {\n map.set(key, {\n consumer: target.consumer,\n lane: null,\n kind: 'event',\n position: row.index - 1,\n head: row.index,\n dispatch: freshDispatch(),\n queue: [],\n queued: 0,\n completions: new Map(),\n types: null,\n })\n continue\n }\n let cursor = map.get(key)\n if (!cursor) {\n cursor = {\n consumer: target.consumer,\n lane: target.lane,\n kind: 'lane',\n position: clearIndexOf(sessionId),\n head: null,\n dispatch: freshDispatch(),\n queue: [],\n queued: 0,\n completions: new Map(),\n types: null,\n }\n map.set(key, cursor)\n }\n cursor.queue.push(row.index)\n cursor.head ??= row.index\n }\n })\n const clearIndex = clearIndexOf(sessionId)\n for (const consumer of consumers ?? []) {\n const log = consumer.log\n if (!log) continue\n const first = rows.find((row) => takes(log.types, row.type))\n if (!first) continue\n const key = cursorKey(consumer.name, log.lane, 0)\n const cursor = map.get(key)\n if (!cursor) {\n const position = log.backfill ? clearIndex : base\n map.set(key, {\n consumer: consumer.name,\n lane: log.lane,\n kind: 'log',\n position,\n head: log.backfill\n ? nextLogHead(sessionId, position, log.types)\n : first.index,\n dispatch: freshDispatch(),\n queue: [],\n queued: 0,\n completions: new Map(),\n types: log.types,\n })\n } else {\n cursor.types = log.types\n // Not `first`: a deployment that doesn't serve this consumer may\n // have appended events it takes since it went idle.\n cursor.head ??= nextLogHead(sessionId, cursor.position, log.types)\n }\n }\n }\n\n const insert = (\n sessionId: string,\n events: readonly AppendEvent[],\n now: Date,\n consumers: readonly StoreConsumer[] | undefined,\n cause?: EventCause,\n ): Row[] => {\n const log = logOf(sessionId)\n const base = headOf(log)\n const inserted = events.map((candidate, offset): Row => ({\n id: candidate.id ?? generateId(),\n type: candidate.type,\n payload: structuredClone(candidate.payload),\n index: base + 1 + offset,\n sessionId,\n createdAt: new Date(now),\n cause: cause\n ? { ...cause }\n : candidate.cause\n ? { ...candidate.cause }\n : null,\n }))\n for (const row of inserted) {\n if (ownerOf(sessionId, row.id)) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n `event id '${row.id}' already exists`,\n )\n }\n }\n for (const row of inserted) {\n log.rows.push(row)\n log.byEventId.set(row.id, row)\n }\n enqueue(sessionId, inserted, events, base, consumers)\n return inserted\n }\n\n /** Every cached fold of this session below its clear, head or historical. */\n const dropCheckpointsBelow = (\n sessionId: string,\n clearIndex: number,\n ): void => {\n const prefix = `${sessionId}\\u0000`\n for (const [key, snapshot] of snapshots) {\n if (!key.startsWith(prefix) || snapshot.index >= clearIndex) continue\n snapshots.delete(key)\n snapshotPinCounts.delete(`${key}\\u0000${snapshot.index}`)\n }\n for (const [key, snapshot] of historicalSnapshots) {\n if (!key.startsWith(prefix) || snapshot.index >= clearIndex) continue\n historicalSnapshots.delete(key)\n snapshotPinCounts.delete(key)\n }\n }\n\n const readStateOf = (\n sessionId: string,\n reducerName: string,\n throughIndex?: number,\n snapshotThroughIndex?: number,\n ): StoreStateRead => {\n const key = snapshotKey(sessionId, reducerName)\n const clearIndex = clearIndexOf(sessionId)\n const snapshotFrontier = snapshotThroughIndex ?? throughIndex\n const stored = snapshots.get(key)\n const head = stored && stored.index >= clearIndex ? stored : undefined\n let candidate =\n head && (snapshotFrontier === undefined || head.index <= snapshotFrontier)\n ? head\n : null\n if (snapshotFrontier !== undefined) {\n const prefix = `${key}\\u0000`\n for (const [historicalKey, snapshot] of historicalSnapshots) {\n if (\n historicalKey.startsWith(prefix) &&\n snapshot.index >= clearIndex &&\n snapshot.index <= snapshotFrontier &&\n (!candidate || snapshot.index > candidate.index)\n ) {\n candidate = snapshot\n }\n }\n }\n const snapshot = candidate\n ? { index: candidate.index, state: structuredClone(candidate.state) }\n : null\n const afterIndex = Math.max(snapshot?.index ?? 0, clearIndex - 1)\n return {\n headIndex: head?.index ?? null,\n snapshot,\n events: (sessions.get(sessionId)?.rows ?? [])\n .filter(\n (row) =>\n row.index > afterIndex &&\n (throughIndex === undefined || row.index <= throughIndex),\n )\n .map(toEvent),\n ...(clearIndex > 0 ? { clearIndex } : {}),\n }\n }\n\n /** The dead-lettered head a redrive names, or undefined. */\n const deadHeadOf = (\n sessionId: string,\n consumer: string,\n index: number,\n ): Cursor | undefined => {\n if (index <= clearIndexOf(sessionId)) return undefined\n for (const cursor of cursors.get(sessionId)?.values() ?? []) {\n if (\n cursor.consumer === consumer &&\n cursor.head === index &&\n cursor.dispatch.failedAt !== null\n ) {\n return cursor\n }\n }\n return undefined\n }\n\n return {\n async append(sessionId, events, appendOptions) {\n const consumers = appendOptions?.consumers\n if (events.length === 0) {\n return { events: [], hasPending: hasPending(sessionId, consumers) }\n }\n\n const ifLastIndex = appendOptions?.ifLastIndex\n const conditional =\n ifLastIndex === undefined\n ? undefined\n : { sessionId, ifLastIndex, head: headOf(sessions.get(sessionId)) }\n\n // Batch-scoped idempotency (a2-implementation.md §3): all ids\n // already present → lost-ack retry, return the original rows;\n // some present → the caller mixed sent and fresh events.\n const existing = checkBatchIds(sessionId, events)\n if (existing.length > 0) {\n if (existing.length === events.length) {\n return {\n events: idempotentReplay(\n events,\n existing.map(toStored),\n conditional,\n ),\n hasPending: hasPending(sessionId, consumers),\n }\n }\n throw duplicateBatchError(\n `batch mixes ${existing.length} already-appended and ${events.length - existing.length} fresh events`,\n conditional,\n )\n }\n\n const originIndex = appendOptions?.originIndex\n if (originIndex !== undefined && originIndex <= clearIndexOf(sessionId)) {\n throw sessionClearedError(\n `scheduled work from event ${originIndex} was cleared in session '${sessionId}'`,\n )\n }\n // Attempt-currency fence: a fresh consumer append commits only while\n // its causal attempt still owns the cursor's head (store.ts `append`).\n for (const event of events) {\n if (event.cause) assertCurrentCause(sessionId, event.cause)\n }\n if (conditional && conditional.head !== conditional.ifLastIndex) {\n throw lastIndexMismatchError(\n sessionId,\n conditional.ifLastIndex,\n conditional.head,\n )\n }\n\n const inserted = insert(sessionId, events, clock.now(), consumers)\n notifyStreams(sessionId, inserted)\n return {\n events: inserted.map(toStored),\n hasPending: hasPending(sessionId, consumers),\n }\n },\n\n async clear({ sessionId, ifLastIndex, cause }) {\n if (cause) assertCurrentCause(sessionId, cause)\n const log = sessions.get(sessionId)\n const head = headOf(log)\n const clearIndex = log?.clearIndex ?? 0\n if (ifLastIndex !== undefined && clearIndex === ifLastIndex + 1) {\n return { cleared: true, index: head, clearIndex }\n }\n if (ifLastIndex !== undefined && ifLastIndex !== head) {\n return { cleared: false, index: head, clearIndex }\n }\n if (head === 0 || clearIndex === head) {\n return { cleared: true, index: head, clearIndex }\n }\n const [marker] = insert(\n sessionId,\n [{ type: CLEAR_EVENT_TYPE, payload: null }],\n clock.now(),\n undefined,\n )\n logOf(sessionId).clearIndex = marker!.index\n // Every event is at or below the marker: no head survives, and log\n // cursors count from the marker.\n const map = cursors.get(sessionId)\n for (const [key, cursor] of map ?? []) {\n if (cursor.head !== null) releaseSnapshotPins(sessionId, refOf(cursor))\n if (cursor.kind !== 'log') {\n map!.delete(key)\n continue\n }\n cursor.position = marker!.index\n cursor.head = null\n cursor.dispatch = freshDispatch()\n cursor.completions.clear()\n }\n notifyStreams(sessionId, [marker!])\n return { cleared: true, index: marker!.index, clearIndex: marker!.index }\n },\n\n async reclaim(sessionId) {\n const log = sessions.get(sessionId)\n if (!log || log.offset >= log.clearIndex - 1) {\n return { deleted: 0, done: true }\n }\n const removed = log.rows.splice(\n 0,\n Math.min(RECLAIM_BATCH, log.clearIndex - 1 - log.offset),\n )\n log.offset += removed.length\n for (const row of removed) {\n // A reused id belongs to its new row now.\n if (log.byEventId.get(row.id) === row) log.byEventId.delete(row.id)\n }\n const done = log.offset === log.clearIndex - 1\n // Writes below the marker are dropped, so one sweep at the end suffices.\n if (done) dropCheckpointsBelow(sessionId, log.clearIndex)\n return { deleted: removed.length, done }\n },\n\n async wakeLog({ sessionId, consumer }) {\n const { log } = consumer\n const key = cursorKey(consumer.name, log.lane, 0)\n const head = headOf(sessions.get(sessionId))\n let cursor = cursors.get(sessionId)?.get(key)\n if (!cursor) {\n const marker = clearIndexOf(sessionId)\n if (head === marker) {\n return {\n cursor: idleLogCursor(consumer.name, log.lane, marker),\n head,\n }\n }\n const position = log.backfill ? marker : head\n cursor = {\n consumer: consumer.name,\n lane: log.lane,\n kind: 'log',\n position,\n head: log.backfill\n ? nextLogHead(sessionId, position, log.types)\n : null,\n dispatch: freshDispatch(),\n queue: [],\n queued: 0,\n completions: new Map(),\n types: log.types,\n }\n cursorsOf(sessionId).set(key, cursor)\n } else {\n cursor.types = log.types\n cursor.head ??= nextLogHead(sessionId, cursor.position, log.types)\n }\n return { cursor: toCursor(cursor), head }\n },\n\n async cursors(sessionId) {\n return [...(cursors.get(sessionId)?.values() ?? [])]\n .filter((cursor) => cursor.kind !== 'event' || cursor.head !== null)\n .map(toCursor)\n .toSorted(byCursor)\n },\n\n async read(sessionId, opts) {\n const log = sessions.get(sessionId)\n if (!log) return []\n const head = headOf(log)\n const after = Math.max(\n opts?.afterIndex ?? 0,\n log.clearIndex - 1,\n log.offset,\n )\n const through = Math.min(opts?.throughIndex ?? head, head)\n if (through <= after) return []\n return log.rows\n .slice(after - log.offset, through - log.offset)\n .map(toStored)\n },\n\n async claimAvailable({\n sessionId,\n holder,\n ttlMs,\n expiresAtMs,\n exclude,\n consumers,\n maxFailures,\n through,\n }) {\n refreshLogTypes(sessionId, consumers)\n const claimedAt = clock.now()\n const now = claimedAt.getTime()\n const expiresAt = new Date(expiresAtMs ?? now + ttlMs)\n const excluded = new Set(\n (exclude ?? []).map((ref) =>\n cursorKey(ref.consumer, ref.lane, ref.index),\n ),\n )\n const served = servedBy(consumers)\n const eligible: Cursor[] = []\n let retryAt: Date | null = null\n\n for (const [key, cursor] of cursors.get(sessionId) ?? []) {\n if (\n cursor.head === null ||\n (through !== undefined && cursor.head > through) ||\n cursor.dispatch.failedAt !== null ||\n !served(cursor) ||\n excluded.has(key)\n ) {\n continue\n }\n const expiry = cursor.dispatch.claimExpiresAt\n if (expiry && expiry.getTime() > now) {\n if (!retryAt || expiry < retryAt) retryAt = new Date(expiry)\n continue\n }\n eligible.push(cursor)\n }\n\n if (eligible.length === 0) {\n return retryAt ? { outcome: 'busy', retryAt } : { outcome: 'settled' }\n }\n const clearIndex = clearIndexOf(sessionId)\n const claimed = eligible\n .map((cursor) => {\n const head = claim({\n sessionId,\n cursor,\n holder,\n now: claimedAt,\n expiresAt,\n maxFailures,\n })\n if (clearIndex > 0) head.clearIndex = clearIndex\n return head\n })\n .toSorted(byHead)\n return {\n outcome: 'claimed',\n events: claimed,\n head: headOf(sessions.get(sessionId)),\n }\n },\n\n async renewClaims({ sessionId, holder, claims, ttlMs, expiresAtMs }) {\n const now = clock.now()\n const expiresAt = new Date(expiresAtMs ?? now.getTime() + ttlMs)\n const clearIndex = clearIndexOf(sessionId)\n const renewed: HeadRef[] = []\n const superseded: HeadRef[] = []\n const ordered = claims.toSorted(\n (a, b) => a.index - b.index || compareText(a.consumer, b.consumer),\n )\n for (const { attempt, ...ref } of ordered) {\n if (ref.index <= clearIndex) {\n superseded.push(ref)\n continue\n }\n const cursor = cursorAt(sessionId, ref)\n if (!cursor) continue\n const dispatch = cursor.dispatch\n // An event the cursor has not reached yet holds no claim to renew.\n if (cursor.head !== ref.index && cursor.position < ref.index) continue\n if (cursor.head !== ref.index || dispatch.attemptCount > attempt) {\n superseded.push(ref)\n continue\n }\n if (\n dispatch.failedAt === null &&\n dispatch.claimHolder === holder &&\n dispatch.claimExpiresAt !== null &&\n dispatch.claimExpiresAt > now\n ) {\n dispatch.claimExpiresAt = new Date(expiresAt)\n renewed.push(ref)\n }\n }\n return { renewed, superseded }\n },\n\n async completeAttempt({\n sessionId,\n consumer,\n lane,\n index,\n attempt,\n events,\n consumers,\n }) {\n if (index <= clearIndexOf(sessionId)) return { outcome: 'superseded' }\n const ref = { consumer, lane, index }\n const cursor = cursorAt(sessionId, ref)\n if (!cursor) {\n if (!rowAt(sessionId, index)) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n return { outcome: 'superseded' }\n }\n refreshLogTypes(sessionId, consumers)\n\n if (cursor.head !== index) {\n const completion = cursor.completions.get(index)\n if (!completion || completion.attempt !== attempt) {\n return { outcome: 'superseded' }\n }\n const requestedIds = events.map((event) => event.id)\n if (\n completion.returnedEventIds.length !== requestedIds.length ||\n completion.returnedEventIds.some(\n (id, offset) => id !== requestedIds[offset],\n )\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'completed attempt does not match the returned event batch',\n )\n }\n const existing = checkBatchIds(sessionId, events)\n if (\n existing.length !== events.length ||\n existing.some(\n (row) =>\n row.cause?.consumer !== consumer ||\n row.cause.lane !== lane ||\n row.cause.index !== index ||\n row.cause.attempt !== attempt,\n )\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'completed attempt does not match the returned event batch',\n )\n }\n return {\n outcome: 'completed',\n events:\n events.length === 0\n ? []\n : idempotentReplay(events, existing.map(toStored)),\n }\n }\n const dispatch = cursor.dispatch\n if (\n dispatch.attemptCount !== attempt ||\n dispatch.failedAt !== null ||\n dispatch.claimHolder === null\n ) {\n return { outcome: 'superseded' }\n }\n\n const existing = checkBatchIds(sessionId, events)\n if (existing.length > 0) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'returned event batch contains already-appended ids',\n )\n }\n const inserted = insert(sessionId, events, clock.now(), consumers, {\n consumer,\n lane,\n index,\n attempt,\n })\n if (cursor.kind === 'log') cursor.completions.clear()\n cursor.completions.set(index, {\n attempt,\n returnedEventIds: events.map((event) => event.id),\n })\n advance(sessionId, cursor)\n notifyStreams(sessionId, inserted)\n return { outcome: 'completed', events: inserted.map(toStored) }\n },\n\n async failAttempt({\n sessionId,\n consumer,\n lane,\n index,\n attempt,\n error,\n maxFailures,\n retryAfterMs,\n }) {\n if (index <= clearIndexOf(sessionId)) {\n return { outcome: 'superseded', failureCount: 0 }\n }\n const cursor = cursorAt(sessionId, { consumer, lane, index })\n if (!cursor) {\n if (!rowAt(sessionId, index)) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n return { outcome: 'superseded', failureCount: 0 }\n }\n const dispatch = cursor.dispatch\n if (\n cursor.head !== index ||\n dispatch.attemptCount !== attempt ||\n (dispatch.claimHolder === null &&\n dispatch.lastFailedAttempt !== attempt)\n ) {\n return {\n outcome: 'superseded',\n failureCount: cursor.head === index ? dispatch.failureCount : 0,\n }\n }\n if (\n dispatch.lastFailedAttempt === attempt &&\n dispatch.claimHolder === null\n ) {\n return {\n outcome: dispatch.failedAt ? 'dead_lettered' : 'failed',\n failureCount: dispatch.failureCount,\n }\n }\n if (dispatch.failedAt) {\n return { outcome: 'dead_lettered', failureCount: dispatch.failureCount }\n }\n dispatch.failureCount += 1\n dispatch.lastError = error\n const failedAt = clock.now()\n dispatch.lastFailedAt = new Date(failedAt)\n dispatch.lastFailedAttempt = attempt\n dispatch.claimHolder = null\n dispatch.claimExpiresAt =\n retryAfterMs === undefined || dispatch.failureCount >= maxFailures\n ? null\n : new Date(failedAt.getTime() + retryAfterMs)\n if (dispatch.failureCount >= maxFailures) {\n deadLetter(sessionId, cursor, failedAt)\n return {\n outcome: 'dead_lettered',\n failureCount: dispatch.failureCount,\n }\n }\n return { outcome: 'failed', failureCount: dispatch.failureCount }\n },\n\n async deadLetters(sessionId) {\n const clearIndex = clearIndexOf(sessionId)\n const dead: HeadEvent[] = []\n for (const cursor of cursors.get(sessionId)?.values() ?? []) {\n if (\n cursor.head === null ||\n cursor.head <= clearIndex ||\n cursor.dispatch.failedAt === null\n ) {\n continue\n }\n dead.push(toHead(cursor, rowAt(sessionId, cursor.head)!))\n }\n return dead.toSorted(byHead)\n },\n\n async redrive({ sessionId, consumer, index, action, consumers }) {\n const cursor = deadHeadOf(sessionId, consumer, index)\n if (!cursor) {\n return { redriven: false, hasPending: hasPending(sessionId, consumers) }\n }\n refreshLogTypes(sessionId, consumers)\n if (action === 'skip') {\n advance(sessionId, cursor)\n } else {\n cursor.dispatch.failedAt = null\n cursor.dispatch.failureCount = 0\n }\n return { redriven: true, hasPending: hasPending(sessionId, consumers) }\n },\n\n async readState(sessionId, reducerName, stateOptions) {\n return readStateOf(\n sessionId,\n reducerName,\n stateOptions?.throughIndex,\n stateOptions?.snapshotThroughIndex,\n )\n },\n\n async readStates(requests, reducerName) {\n return requests.map((request) =>\n readStateOf(\n request.sessionId,\n reducerName,\n request.throughIndex,\n request.snapshotThroughIndex,\n ),\n )\n },\n\n async putSnapshots(sessionId, reducerName, writes) {\n const key = snapshotKey(sessionId, reducerName)\n const clearIndex = clearIndexOf(sessionId)\n for (const write of writes.toSorted((a, b) => a.index - b.index)) {\n if (write.index < clearIndex) continue\n const checkpoint = checkpointKey(sessionId, reducerName, write.index)\n for (const pin of write.pins ?? []) {\n const cursor = cursorAt(sessionId, pin)\n if (\n !cursor ||\n pin.index <= clearIndex ||\n cursor.head !== pin.index ||\n cursor.dispatch.failedAt !== null\n ) {\n continue\n }\n const pinKey = headPinKey(sessionId, pin)\n let pinned = snapshotPins.get(pinKey)\n if (!pinned) {\n pinned = new Set()\n snapshotPins.set(pinKey, pinned)\n }\n if (!pinned.has(checkpoint)) {\n pinned.add(checkpoint)\n snapshotPinCounts.set(\n checkpoint,\n (snapshotPinCounts.get(checkpoint) ?? 0) + 1,\n )\n }\n }\n\n const current = snapshots.get(key)\n if (!current || write.index >= current.index) {\n if (\n current &&\n write.index > current.index &&\n current.index >= clearIndex\n ) {\n const currentCheckpoint = checkpointKey(\n sessionId,\n reducerName,\n current.index,\n )\n if ((snapshotPinCounts.get(currentCheckpoint) ?? 0) > 0) {\n historicalSnapshots.set(currentCheckpoint, {\n index: current.index,\n state: structuredClone(current.state),\n updatedAt: new Date(current.updatedAt),\n })\n }\n }\n snapshots.set(key, {\n index: write.index,\n state: structuredClone(write.state),\n updatedAt: clock.now(),\n })\n historicalSnapshots.delete(checkpoint)\n } else if (\n write.index < current.index &&\n (snapshotPinCounts.get(checkpoint) ?? 0) > 0\n ) {\n historicalSnapshots.set(checkpoint, {\n index: write.index,\n state: structuredClone(write.state),\n updatedAt: clock.now(),\n })\n }\n }\n },\n\n presence: {\n async set(ns, participant, values, meta) {\n let participants = presenceRows.get(ns)\n if (!participants) {\n participants = new Map()\n presenceRows.set(ns, participants)\n }\n let fields = participants.get(participant)\n if (!fields) {\n fields = new Map()\n participants.set(participant, fields)\n }\n\n // Expiry anchors on the storage's own clock — the sender's\n // `at` orders writes but never extends or shortens a lifetime.\n const now = clock.now()\n const expiresAtMs = now.getTime() + meta.ttlMs\n // Field-keyed and caller-named — null-prototype, like every\n // presence map (see `nullProtoRecord`).\n const applied: Record<string, unknown> = nullProtoRecord()\n let appliedCount = 0\n for (const [field, value] of Object.entries(values)) {\n // Field-wise LWW by `at` against unexpired values and\n // tombstones alike; ties go to the incoming write, so\n // same-stamp sets keep set-then-read intuition.\n const existing = fields.get(field)\n if (existing && existing.expiresAt > now && existing.at > meta.at)\n continue\n fields.set(field, {\n value: structuredClone(value),\n seen: meta.seen,\n at: new Date(meta.at),\n expiresAt: new Date(expiresAtMs),\n })\n applied[field] = structuredClone(value)\n appliedCount += 1\n }\n if (fields.size === 0) participants.delete(participant)\n if (participants.size === 0) presenceRows.delete(ns)\n\n // Only applied fields broadcast — a losing write repaints nothing,\n // so subscribers stay consistent with what read() returns.\n if (appliedCount === 0) return\n const subs = presenceSubscribers.get(ns)\n if (!subs) return\n const patch: PresencePatch = {\n participant,\n values: applied,\n seen: meta.seen,\n at: new Date(meta.at),\n }\n for (const listener of subs) listener(patch)\n },\n\n async read(ns) {\n const participants = presenceRows.get(ns)\n if (!participants) return []\n const now = clock.now()\n const rows: PresenceRow[] = []\n for (const [participant, fields] of participants) {\n for (const [field, entry] of fields) {\n // Expiry is enforced lazily on read — no timers; a silent\n // participant's rows vanish the next time anyone looks.\n if (entry.expiresAt <= now) {\n fields.delete(field)\n continue\n }\n if (entry.value === null) continue\n rows.push({\n participant,\n field,\n value: structuredClone(entry.value),\n seen: entry.seen,\n at: new Date(entry.at),\n expiresAt: new Date(entry.expiresAt),\n })\n }\n if (fields.size === 0) participants.delete(participant)\n }\n if (participants.size === 0) presenceRows.delete(ns)\n return rows\n },\n\n subscribe(ns, onPatch) {\n let subs = presenceSubscribers.get(ns)\n if (!subs) {\n subs = new Set()\n presenceSubscribers.set(ns, subs)\n }\n subs.add(onPatch)\n return () => {\n subs.delete(onPatch)\n if (subs.size === 0) presenceSubscribers.delete(ns)\n }\n },\n },\n\n stream(sessionId, opts) {\n const startAfter = opts?.startAfter ?? 0\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n let last = Math.max(startAfter, clearIndexOf(sessionId) - 1)\n let wake: (() => void) | null = null\n let closed = false\n\n const onRow = (): void => {\n wake?.()\n }\n let subs = streamSubscribers.get(sessionId)\n if (!subs) {\n subs = new Set()\n streamSubscribers.set(sessionId, subs)\n }\n subs.add(onRow)\n const stop = (): void => {\n if (closed) return\n closed = true\n subs.delete(onRow)\n if (subs.size === 0) streamSubscribers.delete(sessionId)\n wake?.()\n }\n\n return {\n async next(): Promise<IteratorResult<Event>> {\n try {\n for (;;) {\n if (closed) return { value: undefined, done: true }\n const log = sessions.get(sessionId)\n // Rows reclaimed behind a running stream are skipped.\n const row = log?.rows[Math.max(0, last - log.offset)]\n if (row) {\n const value = toStored(row)\n last = row.index\n return { value, done: false }\n }\n // oxlint-disable-next-line no-await-in-loop -- wait-for-wake\n await new Promise<void>((resolve) => {\n wake = resolve\n })\n wake = null\n }\n } catch (error) {\n stop()\n throw error\n }\n },\n async return(): Promise<IteratorResult<Event>> {\n stop()\n return { value: undefined, done: true }\n },\n }\n },\n }\n },\n }\n}\n"],"mappings":"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"}
|
package/dist/store-postgres.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as Clock, s as IdSource, t as A2Store } from "./store-
|
|
1
|
+
import { r as Clock, s as IdSource, t as A2Store } from "./store-DEzcHZOA.js";
|
|
2
2
|
//#region src/store-postgres.d.ts
|
|
3
3
|
/** The result shape this backend reads: just rows. */
|
|
4
4
|
type PostgresQueryResult = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-postgres.d.ts","names":[],"sources":["../src/store-postgres.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"store-postgres.d.ts","names":[],"sources":["../src/store-postgres.ts"],"mappings":";;;KA0FY;EAAwB,MAAM;;;KAG9B;EACV,MAAM,cAAc,qBAAqB,QAAQ;EACjD;;;;;;;;KASU;EACV,MAAM,cAAc,qBAAqB,QAAQ;EACjD,YAAY,QAAQ;EACpB,QAAQ;;KAGE;;EAEV;;EAEA;;EAEA,SAAS;;EAET,QAAQ;;EAER,MAAM;;KAGI,gBAAgB;;;EAG1B,SAAS;;iBAuuCK,SAAS,UAAS,uBAA4B"}
|
package/dist/store-postgres.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { i as A2Error, r as sessionClearedError, s as
|
|
1
|
+
import { c as lastIndexMismatchError, i as A2Error, l as supersededAttemptError, r as sessionClearedError, s as duplicateBatchError, t as CLEAR_EVENT_TYPE } from "./session-clear-XJYpmUpg.js";
|
|
2
2
|
import { i as NOTIFY_TIMINGS, y as nullProtoRecord } from "./internal-CvsrprnU.js";
|
|
3
3
|
import { n as pollingStream, t as defaultSleep } from "./store-polling-BZt2Rf3D.js";
|
|
4
4
|
import { r as platformWaitUntil } from "./platform-B4TnJtWu.js";
|
|
5
5
|
import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
|
|
6
|
-
import { a as idempotentReplay, i as idleLogCursor, n as decodeCause, r as decodeReturnedEventIds, t as cursorKeyOf } from "./store-codec-
|
|
6
|
+
import { a as idempotentReplay, i as idleLogCursor, n as decodeCause, r as decodeReturnedEventIds, t as cursorKeyOf } from "./store-codec-pAG2oPgQ.js";
|
|
7
7
|
import { n as SYSTEM_CLOCK, t as RANDOM_IDS } from "./store-N8PXxDAS.js";
|
|
8
8
|
import { t as pollPresence } from "./store-presence-polling-JuAy2Ck2.js";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
@@ -867,7 +867,8 @@ const INSERTED_AFTER = `(
|
|
|
867
867
|
* every event's id belongs to this session already, the batch is a retry: it
|
|
868
868
|
* stops with the committed rows. Some owned aborts, as does scheduled work
|
|
869
869
|
* from at or below the clear marker (`origin`). Rows below the marker own no
|
|
870
|
-
* id.
|
|
870
|
+
* id. Both id outcomes carry the head, which a conditional append's conflict
|
|
871
|
+
* reports when it moved.
|
|
871
872
|
*/
|
|
872
873
|
const APPEND_CHECKS = `
|
|
873
874
|
with boundary as materialized (
|
|
@@ -885,12 +886,12 @@ const APPEND_CHECKS = `
|
|
|
885
886
|
)
|
|
886
887
|
select case
|
|
887
888
|
when (select count(*) from owned) = ${param.bigint("count")} then
|
|
888
|
-
jsonb_build_object('stop', 'replay',
|
|
889
|
+
jsonb_build_object('stop', 'replay', 'head', boundary.base,
|
|
889
890
|
'has_pending', boundary.has_pending,
|
|
890
891
|
'events', (select jsonb_agg(to_jsonb(owned) order by owned.idx)
|
|
891
892
|
from owned))
|
|
892
893
|
when exists (select 1 from owned) then
|
|
893
|
-
jsonb_build_object('abort', 'mixed', 'owned',
|
|
894
|
+
jsonb_build_object('abort', 'mixed', 'head', boundary.base, 'owned',
|
|
894
895
|
(select count(*) from owned))
|
|
895
896
|
when ${param.bigint("origin")} <= boundary.clear_index then
|
|
896
897
|
jsonb_build_object('abort', 'cleared')
|
|
@@ -922,6 +923,15 @@ const FENCE = `
|
|
|
922
923
|
from cause where cause.ord not in (select ord from held)
|
|
923
924
|
order by cause.ord limit 1`;
|
|
924
925
|
/**
|
|
926
|
+
* A conditional append's head check, under the session lock and after the
|
|
927
|
+
* fences: it aborts with the head unless the head is `ifLastIndex`.
|
|
928
|
+
*/
|
|
929
|
+
const IF_LAST_INDEX = `
|
|
930
|
+
select jsonb_build_object('abort', 'moved', 'index', head.idx)
|
|
931
|
+
from (select coalesce(max(idx), 0) as idx from a2_events
|
|
932
|
+
where session_id = ${SESSION}) head
|
|
933
|
+
where head.idx <> ${param.bigint("ifLastIndex")}`;
|
|
934
|
+
/**
|
|
925
935
|
* Frees the `ids` this session's rows below its clear marker hold, so the
|
|
926
936
|
* batch can take them. It never waits on a row: a reclaim deleting one may
|
|
927
937
|
* hold it, and one it skips aborts.
|
|
@@ -1885,12 +1895,19 @@ function postgres(options = {}) {
|
|
|
1885
1895
|
const causes = [...new Map(events.flatMap((event) => event.cause ? [[jsonb(event.cause), event.cause]] : [])).values()];
|
|
1886
1896
|
const work = enqueue(events, consumers);
|
|
1887
1897
|
const originIndex = appendOptions?.originIndex;
|
|
1898
|
+
const ifLastIndex = appendOptions?.ifLastIndex;
|
|
1899
|
+
const conditionalAt = (head) => ifLastIndex === void 0 ? void 0 : {
|
|
1900
|
+
sessionId,
|
|
1901
|
+
ifLastIndex,
|
|
1902
|
+
head: asNumber(head)
|
|
1903
|
+
};
|
|
1888
1904
|
const checked = (await run({
|
|
1889
1905
|
sessionId,
|
|
1890
1906
|
statements: [
|
|
1891
1907
|
LOCK_SESSION,
|
|
1892
1908
|
APPEND_CHECKS,
|
|
1893
1909
|
causes.length > 0 && FENCE,
|
|
1910
|
+
ifLastIndex !== void 0 && IF_LAST_INDEX,
|
|
1894
1911
|
suppliedIds.length > 0 && REUSE_IDS,
|
|
1895
1912
|
INSERT_EVENTS,
|
|
1896
1913
|
...work.statements
|
|
@@ -1901,6 +1918,7 @@ function postgres(options = {}) {
|
|
|
1901
1918
|
ids: suppliedIds,
|
|
1902
1919
|
count: events.length,
|
|
1903
1920
|
origin: originIndex ?? null,
|
|
1921
|
+
ifLastIndex: ifLastIndex ?? null,
|
|
1904
1922
|
causes: causes.map((cause, offset) => ({
|
|
1905
1923
|
ord: offset + 1,
|
|
1906
1924
|
consumer: cause.consumer,
|
|
@@ -1915,17 +1933,18 @@ function postgres(options = {}) {
|
|
|
1915
1933
|
switch (reason["abort"]) {
|
|
1916
1934
|
case "mixed": {
|
|
1917
1935
|
const owned = asNumber(reason["owned"]);
|
|
1918
|
-
return
|
|
1936
|
+
return duplicateBatchError(`batch mixes ${owned} already-appended and ${events.length - owned} fresh events`, conditionalAt(reason["head"]));
|
|
1919
1937
|
}
|
|
1920
1938
|
case "cleared": return sessionClearedError(`scheduled work from event ${originIndex} was cleared in session '${sessionId}'`);
|
|
1921
1939
|
case "superseded": return supersededAttemptError(sessionId, causes[asNumber(reason["ord"]) - 1]);
|
|
1940
|
+
case "moved": return lastIndexMismatchError(sessionId, ifLastIndex, asNumber(reason["index"]));
|
|
1922
1941
|
case "held": return new A2Error("STORE_UNAVAILABLE", `an event id being reused is still held by cleared work in session '${sessionId}'`);
|
|
1923
1942
|
default: return new A2Error("STORE_UNAVAILABLE", "postgres store operation failed");
|
|
1924
1943
|
}
|
|
1925
1944
|
}
|
|
1926
1945
|
})).find((row) => row["stop"] === "replay" || "base" in row);
|
|
1927
1946
|
if (checked["stop"] === "replay") return {
|
|
1928
|
-
events: idempotentReplay(events, checked["events"].map(toStored)),
|
|
1947
|
+
events: idempotentReplay(events, checked["events"].map(toStored), conditionalAt(checked["head"])),
|
|
1929
1948
|
hasPending: checked["has_pending"] === true
|
|
1930
1949
|
};
|
|
1931
1950
|
return {
|