experimental-a2 0.21.1 → 0.22.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 +21 -0
- package/dist/{actor-HnGmrOZZ.d.ts → actor-CqibLgjQ.d.ts} +13 -5
- package/dist/actor-CqibLgjQ.d.ts.map +1 -0
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-react.d.ts +1 -1
- package/dist/actor-react.js +1 -1
- package/dist/actor-shared-DFET5q16.js.map +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +3 -3
- package/dist/{ai-CvEnqVfo.d.ts → ai-DAu77CDr.d.ts} +2 -2
- package/dist/{ai-CvEnqVfo.d.ts.map → ai-DAu77CDr.d.ts.map} +1 -1
- package/dist/{ai-CzE672Q3.js → ai-EEfKyc-z.js} +2 -2
- package/dist/{ai-CzE672Q3.js.map → ai-EEfKyc-z.js.map} +1 -1
- package/dist/ai-client.d.ts +1 -1
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.d.ts +4 -2
- package/dist/ai-fx.d.ts.map +1 -1
- package/dist/ai-fx.js +211 -61
- package/dist/ai-fx.js.map +1 -1
- package/dist/{ai-harness-Bfk-Lycn.d.ts → ai-harness-DTjkAr2T.d.ts} +2 -2
- package/dist/{ai-harness-Bfk-Lycn.d.ts.map → ai-harness-DTjkAr2T.d.ts.map} +1 -1
- package/dist/ai-harness-dQeJaR1t.js.map +1 -1
- package/dist/ai-pi.d.ts +1 -1
- package/dist/ai-pi.js +1 -1
- package/dist/ai-react.d.ts +1 -1
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +3 -3
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +10 -4
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +1 -1
- package/dist/{client-9IeTsiAs.js → client-BQS4i_V9.js} +27 -4
- package/dist/client-BQS4i_V9.js.map +1 -0
- package/dist/client-CUMbS_38.d.ts.map +1 -1
- package/dist/client.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/{internal-Bc4PvfHk.js → internal-CvsrprnU.js} +32 -15
- package/dist/{internal-Bc4PvfHk.js.map → internal-CvsrprnU.js.map} +1 -1
- package/dist/react.js +1 -1
- package/dist/{runtime-consumer-BWmht60z.js → runtime-consumer-CIeL33wj.js} +2 -2
- package/dist/{runtime-consumer-BWmht60z.js.map → runtime-consumer-CIeL33wj.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 +2 -2
- package/dist/{server-DvIB8kj5.js → server-DtFrWxi3.js} +30 -8
- package/dist/server-DtFrWxi3.js.map +1 -0
- package/dist/{server-BshFZ0eL.d.ts → server-qgez0Xny.d.ts} +3 -3
- package/dist/{server-BshFZ0eL.d.ts.map → server-qgez0Xny.d.ts.map} +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1 -1
- package/dist/{session-clear-Bk_29eEw.d.ts → session-clear-D1PFe7xl.d.ts} +2 -2
- package/dist/session-clear-D1PFe7xl.d.ts.map +1 -0
- package/dist/{store-Z2qjw50n.d.ts → store-D0MhBvg6.d.ts} +15 -6
- package/dist/store-D0MhBvg6.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +15 -17
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-DYNyxCev.js → store-polling-BZt2Rf3D.js} +2 -2
- package/dist/{store-polling-DYNyxCev.js.map → store-polling-BZt2Rf3D.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 +31 -40
- package/dist/store-postgres.js.map +1 -1
- package/dist/store-presence-polling-JuAy2Ck2.js +111 -0
- package/dist/store-presence-polling-JuAy2Ck2.js.map +1 -0
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +3 -3
- package/dist/{store-redis-notify-IiVdlYHc.js → store-redis-notify-CsJw_hfD.js} +538 -397
- package/dist/store-redis-notify-CsJw_hfD.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +3 -4
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +16 -18
- package/dist/store-sqlite.js.map +1 -1
- package/dist/stream-buffer-DrTyiIuo.js +57 -0
- package/dist/stream-buffer-DrTyiIuo.js.map +1 -0
- package/dist/{wire-CZGqNZRx.js → wire-CV-Z7wz-.js} +3 -3
- package/dist/{wire-CZGqNZRx.js.map → wire-CV-Z7wz-.js.map} +1 -1
- package/docs/actors/01-introduction.mdx +5 -2
- package/docs/agents/03-conversations.mdx +9 -8
- package/docs/agents/04-harnesses.mdx +7 -4
- package/docs/reference/01-core.mdx +3 -2
- package/docs/reference/02-server.mdx +5 -1
- package/docs/reference/04-ai.mdx +13 -4
- package/docs/reference/06-errors.mdx +1 -1
- package/docs/server/01-consumers.mdx +4 -1
- package/docs/server/06-guarantees.mdx +7 -4
- package/docs/stores/02-redis.mdx +16 -10
- package/docs/stores/03-redis-http.mdx +6 -4
- package/docs/stores/04-postgres.mdx +2 -2
- package/docs/upgrades/01-overview.mdx +1 -0
- package/docs/upgrades/78-0-22.mdx +115 -0
- package/examples/playground/app/a0/sandbox.ts +58 -13
- package/examples/playground/app/a2slack/db/a2.ts +7 -4
- package/examples/playground/app/a2slack/db/migrations/0004_a2_session_event_ids.sql +2 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0004_snapshot.json +1165 -0
- package/examples/playground/app/a2slack/db/migrations/meta/_journal.json +8 -1
- package/examples/playground/app/chat/server.ts +2 -2
- package/examples/playground/lib/store.ts +1 -1
- package/examples/playground/package.json +1 -1
- package/package.json +1 -1
- package/sql/postgres/drizzle-schema.ts +7 -4
- package/sql/postgres/schema.sql +24 -3
- package/src/actor-shared.ts +10 -2
- package/src/ai-agent.ts +13 -0
- package/src/ai-fx.ts +321 -77
- package/src/ai-harness.ts +7 -0
- package/src/client.ts +19 -2
- package/src/deterministic-id.ts +1 -1
- package/src/internal.ts +42 -11
- package/src/presence-recovery.ts +18 -11
- package/src/presence-tombstones.ts +52 -0
- package/src/push-envelope.ts +2 -2
- package/src/server.ts +49 -15
- package/src/store-memory.ts +24 -32
- package/src/store-postgres.ts +43 -53
- package/src/store-presence-polling.ts +59 -34
- package/src/store-redis-core.ts +595 -439
- package/src/store-redis-notify.ts +74 -26
- package/src/store-redis.ts +2 -3
- package/src/store-sqlite.ts +34 -40
- package/src/store.ts +14 -5
- package/dist/actor-HnGmrOZZ.d.ts.map +0 -1
- package/dist/client-9IeTsiAs.js.map +0 -1
- package/dist/server-DvIB8kj5.js.map +0 -1
- package/dist/session-clear-Bk_29eEw.d.ts.map +0 -1
- package/dist/store-Z2qjw50n.d.ts.map +0 -1
- package/dist/store-presence-polling-CzQ0wdtM.js +0 -94
- package/dist/store-presence-polling-CzQ0wdtM.js.map +0 -1
- package/dist/store-redis-notify-IiVdlYHc.js.map +0 -1
- package/dist/stream-buffer-Cx7AAPGq.js +0 -23
- package/dist/stream-buffer-Cx7AAPGq.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\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}\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 /** Global unique index on event ids, like the SQL schema's. */\n const byEventId = new Map<string, Row>()\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 }\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 row owning `id`, ignoring this session's rows below its clear. */\n const ownerOf = (sessionId: string, id: string): Row | undefined => {\n const row = byEventId.get(id)\n return row?.sessionId === sessionId && row.index < clearIndexOf(sessionId)\n ? undefined\n : row\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 const existing = supplied\n .map((candidate) => ownerOf(sessionId, candidate.id))\n .filter((row): row is Row => row !== undefined)\n const foreign = existing.find((row) => row.sessionId !== sessionId)\n if (foreign) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n `event id '${foreign.id}' already exists in another session`,\n )\n }\n return existing\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 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 (byEventId.get(row.id) === row) 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 expiresAtMs = clock.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`; 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.at > meta.at) continue\n if (value === null) {\n fields.delete(field)\n } else {\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 }\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 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 { 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,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as POLL_TIMINGS } from "./internal-CvsrprnU.js";
|
|
2
2
|
//#region src/stream-activity.ts
|
|
3
3
|
const streamActivities = /* @__PURE__ */ new WeakMap();
|
|
4
4
|
function createStreamActivity(wake) {
|
|
@@ -149,4 +149,4 @@ function pollingStream(readAfter, options) {
|
|
|
149
149
|
//#endregion
|
|
150
150
|
export { streamActivities as i, pollingStream as n, createStreamActivity as r, defaultSleep as t };
|
|
151
151
|
|
|
152
|
-
//# sourceMappingURL=store-polling-
|
|
152
|
+
//# sourceMappingURL=store-polling-BZt2Rf3D.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-polling-
|
|
1
|
+
{"version":3,"file":"store-polling-BZt2Rf3D.js","names":[],"sources":["../src/stream-activity.ts","../src/store-polling.ts"],"sourcesContent":["export type StreamActivity = {\n readonly idle: boolean\n setIdle(idle: boolean): void\n activate(): void\n subscribe(listener: (idle: boolean) => void): () => void\n}\n\nexport const streamActivities: WeakMap<object, StreamActivity> = new WeakMap()\n\nexport function createStreamActivity(wake: () => void): StreamActivity {\n let idle = false\n const listeners = new Set<(idle: boolean) => void>()\n const setIdle = (next: boolean): void => {\n if (idle === next) return\n idle = next\n for (const listener of listeners) listener(idle)\n }\n return {\n get idle() {\n return idle\n },\n setIdle,\n activate() {\n setIdle(false)\n wake()\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n }\n}\n","/**\n * The shared polling stream — how poll-based backends (sqlite,\n * postgres) implement `A2Store.stream`.\n *\n * The cadence is activity-adaptive: while events are flowing, polls\n * run at a tight floor (25ms) so streams feel pushed — a token\n * generation reads smoothly instead of clumping at the idle interval;\n * every empty poll doubles the gap until the idle ceiling. The cadence\n * is owned (POLL_TIMINGS in internal.ts), not a public option: the\n * floor already keeps active streams smooth, so tuning it is a cost\n * decision a2 makes, not one it delegates. Correctness never depends\n * on the cadence — each poll is a catch-up read (`index > last`, in\n * order), so nothing can be missed, only arrive a beat later.\n */\n\nimport { POLL_TIMINGS } from './internal.ts'\nimport type { Event } from './store.ts'\nimport { createStreamActivity, streamActivities } from './stream-activity.ts'\n\n/** One cancellable sleep — the injection seam for cadence tests. */\nexport type Sleeper = { promise: Promise<void>; cancel: () => void }\n\n/** Unref'd cancellable timer — shared with the redis safety read. */\nexport const defaultSleep = (ms: number): Sleeper => {\n let timer: ReturnType<typeof setTimeout> | undefined\n let wake: (() => void) | undefined\n const promise = new Promise<void>((resolve) => {\n wake = resolve // assigned synchronously by the executor\n timer = setTimeout(resolve, ms)\n // Don't hold the process open for an idle poll.\n ;(timer as { unref?: () => void }).unref?.()\n })\n return {\n promise,\n cancel: () => {\n clearTimeout(timer)\n wake?.()\n },\n }\n}\n\n// Read the seam lazily — tests shrink POLL_TIMINGS in their setup.\nconst ceiling = (): number => POLL_TIMINGS.idleCeilingMs\nconst floor = (): number =>\n Math.min(POLL_TIMINGS.activeFloorMs, POLL_TIMINGS.idleCeilingMs)\n\nexport type PollingStreamOptions = {\n /** Deliver events strictly after this index (exclusive). */\n startAfter?: number\n /** Injectable sleep — tests assert the cadence without wall-clock waits. */\n sleep?: (ms: number) => Sleeper\n}\n\n/**\n * Build the `stream()` iterable from a backend's catch-up read. The\n * iterable ends when the consumer calls `return()` (a disconnecting\n * SSE client), waking any pending sleep immediately.\n */\nexport function pollingStream(\n readAfter: (afterIndex: number) => Event[] | Promise<Event[]>,\n options: PollingStreamOptions,\n): AsyncIterable<Event> {\n const startAfter = options.startAfter ?? 0\n const sleep = options.sleep ?? defaultSleep\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n let last = startAfter\n let buffer: Array<Event | undefined> = []\n let position = 0\n let closed = false\n let pending: Sleeper | null = null\n let interval = floor()\n let activityVersion = 0\n const activity = createStreamActivity(() => {\n if (closed) return\n activityVersion += 1\n interval = floor()\n pending?.cancel()\n })\n\n const iterator: AsyncIterator<Event> = {\n async next(): Promise<IteratorResult<Event>> {\n for (;;) {\n if (closed) return { value: undefined, done: true }\n try {\n if (position === buffer.length) {\n // oxlint-disable-next-line no-await-in-loop -- poll loop\n const rows = await readAfter(last)\n if (closed) return { value: undefined, done: true }\n buffer = rows\n position = 0\n }\n } catch (error) {\n if (closed) return { value: undefined, done: true }\n throw error\n }\n if (closed) return { value: undefined, done: true }\n const row = buffer[position]\n if (row) {\n // Activity: tighten back to the floor.\n activity.activate()\n last = row.index\n buffer[position] = undefined\n position += 1\n if (position === buffer.length) {\n buffer = []\n position = 0\n }\n return { value: row, done: false }\n }\n // Empty poll: wait, then back off toward the idle ceiling.\n activity.setIdle(interval === ceiling())\n const version = activityVersion\n pending = sleep(interval)\n // oxlint-disable-next-line no-await-in-loop -- poll loop\n await pending.promise\n pending = null\n if (version === activityVersion) {\n interval = Math.min(interval * 2, ceiling())\n }\n }\n },\n async return(): Promise<IteratorResult<Event>> {\n closed = true\n buffer = []\n position = 0\n pending?.cancel()\n return { value: undefined, done: true }\n },\n }\n streamActivities.set(iterator, activity)\n return iterator\n },\n }\n}\n"],"mappings":";;AAOA,MAAa,mCAAoD,IAAI,QAAQ;AAE7E,SAAgB,qBAAqB,MAAkC;CACrE,IAAI,OAAO;CACX,MAAM,4BAAY,IAAI,IAA6B;CACnD,MAAM,WAAW,SAAwB;EACvC,IAAI,SAAS,MAAM;EACnB,OAAO;EACP,KAAK,MAAM,YAAY,WAAW,SAAS,IAAI;CACjD;CACA,OAAO;EACL,IAAI,OAAO;GACT,OAAO;EACT;EACA;EACA,WAAW;GACT,QAAQ,KAAK;GACb,KAAK;EACP;EACA,UAAU,UAAU;GAClB,UAAU,IAAI,QAAQ;GACtB,aAAa,UAAU,OAAO,QAAQ;EACxC;CACF;AACF;;;;;;;;;;;;;;;;;;ACRA,MAAa,gBAAgB,OAAwB;CACnD,IAAI;CACJ,IAAI;CAOJ,OAAO;EACL,SAAA,IAPkB,SAAe,YAAY;GAC7C,OAAO;GACP,QAAQ,WAAW,SAAS,EAAE;GAE7B,MAAkC,QAAQ;EAC7C,CAEQ;EACN,cAAc;GACZ,aAAa,KAAK;GAClB,OAAO;EACT;CACF;AACF;AAGA,MAAM,gBAAwB,aAAa;AAC3C,MAAM,cACJ,KAAK,IAAI,aAAa,eAAe,aAAa,aAAa;;;;;;AAcjE,SAAgB,cACd,WACA,SACsB;CACtB,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,QAAQ,QAAQ,SAAS;CAE/B,OAAO,EACL,CAAC,OAAO,iBAAuC;EAC7C,IAAI,OAAO;EACX,IAAI,SAAmC,CAAC;EACxC,IAAI,WAAW;EACf,IAAI,SAAS;EACb,IAAI,UAA0B;EAC9B,IAAI,WAAW,MAAM;EACrB,IAAI,kBAAkB;EACtB,MAAM,WAAW,2BAA2B;GAC1C,IAAI,QAAQ;GACZ,mBAAmB;GACnB,WAAW,MAAM;GACjB,SAAS,OAAO;EAClB,CAAC;EAED,MAAM,WAAiC;GACrC,MAAM,OAAuC;IAC3C,SAAS;KACP,IAAI,QAAQ,OAAO;MAAE,OAAO,KAAA;MAAW,MAAM;KAAK;KAClD,IAAI;MACF,IAAI,aAAa,OAAO,QAAQ;OAE9B,MAAM,OAAO,MAAM,UAAU,IAAI;OACjC,IAAI,QAAQ,OAAO;QAAE,OAAO,KAAA;QAAW,MAAM;OAAK;OAClD,SAAS;OACT,WAAW;MACb;KACF,SAAS,OAAO;MACd,IAAI,QAAQ,OAAO;OAAE,OAAO,KAAA;OAAW,MAAM;MAAK;MAClD,MAAM;KACR;KACA,IAAI,QAAQ,OAAO;MAAE,OAAO,KAAA;MAAW,MAAM;KAAK;KAClD,MAAM,MAAM,OAAO;KACnB,IAAI,KAAK;MAEP,SAAS,SAAS;MAClB,OAAO,IAAI;MACX,OAAO,YAAY,KAAA;MACnB,YAAY;MACZ,IAAI,aAAa,OAAO,QAAQ;OAC9B,SAAS,CAAC;OACV,WAAW;MACb;MACA,OAAO;OAAE,OAAO;OAAK,MAAM;MAAM;KACnC;KAEA,SAAS,QAAQ,aAAa,QAAQ,CAAC;KACvC,MAAM,UAAU;KAChB,UAAU,MAAM,QAAQ;KAExB,MAAM,QAAQ;KACd,UAAU;KACV,IAAI,YAAY,iBACd,WAAW,KAAK,IAAI,WAAW,GAAG,QAAQ,CAAC;IAE/C;GACF;GACA,MAAM,SAAyC;IAC7C,SAAS;IACT,SAAS,CAAC;IACV,WAAW;IACX,SAAS,OAAO;IAChB,OAAO;KAAE,OAAO,KAAA;KAAW,MAAM;IAAK;GACxC;EACF;EACA,iBAAiB,IAAI,UAAU,QAAQ;EACvC,OAAO;CACT,EACF;AACF"}
|
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-D0MhBvg6.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":";;;KAqFY;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;;
|
|
1
|
+
{"version":3,"file":"store-postgres.d.ts","names":[],"sources":["../src/store-postgres.ts"],"mappings":";;;KAqFY;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;;iBA4tCK,SAAS,UAAS,uBAA4B"}
|
package/dist/store-postgres.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { i as A2Error, r as sessionClearedError, s as supersededAttemptError, t as CLEAR_EVENT_TYPE } from "./session-clear-wHd5GCpZ.js";
|
|
2
|
-
import {
|
|
3
|
-
import { n as pollingStream, t as defaultSleep } from "./store-polling-
|
|
2
|
+
import { i as NOTIFY_TIMINGS, y as nullProtoRecord } from "./internal-CvsrprnU.js";
|
|
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
6
|
import { a as idempotentReplay, i as idleLogCursor, n as decodeCause, r as decodeReturnedEventIds, t as cursorKeyOf } from "./store-codec-DwrWlgQx.js";
|
|
7
7
|
import { n as SYSTEM_CLOCK, t as RANDOM_IDS } from "./store-N8PXxDAS.js";
|
|
8
|
-
import { t as pollPresence } from "./store-presence-polling-
|
|
8
|
+
import { t as pollPresence } from "./store-presence-polling-JuAy2Ck2.js";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
10
|
//#region src/postgres-notifications.ts
|
|
11
11
|
const postgresNotificationChannel = ({ kind, sessionId }) => `a2_${kind}_${createHash("md5").update(sessionId).digest("hex")}`;
|
|
@@ -863,9 +863,9 @@ const INSERTED_AFTER = `(
|
|
|
863
863
|
from a2_events where session_id = ${SESSION})`;
|
|
864
864
|
/**
|
|
865
865
|
* An append's checks, under the session lock: the session's head and pending
|
|
866
|
-
* state, and what its supplied `ids` say.
|
|
867
|
-
* this session already, the batch is a retry: it
|
|
868
|
-
* rows. Some owned
|
|
866
|
+
* state, and what its supplied `ids` say. Ids are unique per session. When
|
|
867
|
+
* every event's id belongs to this session already, the batch is a retry: it
|
|
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
870
|
* id.
|
|
871
871
|
*/
|
|
@@ -879,15 +879,11 @@ const APPEND_CHECKS = `
|
|
|
879
879
|
select event.session_id, event.idx, event.event_type, event.payload,
|
|
880
880
|
event.event_id, event.created_at, event.cause
|
|
881
881
|
from a2_events event, boundary
|
|
882
|
-
where event.
|
|
883
|
-
and
|
|
884
|
-
|
|
882
|
+
where event.session_id = ${SESSION}
|
|
883
|
+
and event.event_id = any(${param.texts("ids")})
|
|
884
|
+
and event.idx >= boundary.clear_index
|
|
885
885
|
)
|
|
886
886
|
select case
|
|
887
|
-
when exists (select 1 from owned where session_id <> ${SESSION}) then
|
|
888
|
-
jsonb_build_object('abort', 'foreign', 'event_id', (
|
|
889
|
-
select event_id from owned where session_id <> ${SESSION}
|
|
890
|
-
order by idx limit 1))
|
|
891
887
|
when (select count(*) from owned) = ${param.bigint("count")} then
|
|
892
888
|
jsonb_build_object('stop', 'replay',
|
|
893
889
|
'has_pending', boundary.has_pending,
|
|
@@ -1334,9 +1330,9 @@ const RETURN_CHECKS = `
|
|
|
1334
1330
|
jsonb_build_object('stop', 'superseded')
|
|
1335
1331
|
when exists (
|
|
1336
1332
|
select 1 from a2_events event
|
|
1337
|
-
where event.
|
|
1338
|
-
and
|
|
1339
|
-
|
|
1333
|
+
where event.session_id = ${SESSION}
|
|
1334
|
+
and event.event_id = any(${param.texts("ids")})
|
|
1335
|
+
and event.idx >= ${clearIndexOf(SESSION)}
|
|
1340
1336
|
) then jsonb_build_object('abort', 'owned')
|
|
1341
1337
|
else jsonb_build_object('base', (
|
|
1342
1338
|
select coalesce(max(idx), 0) from a2_events
|
|
@@ -1847,7 +1843,7 @@ function postgres(options = {}) {
|
|
|
1847
1843
|
events: []
|
|
1848
1844
|
};
|
|
1849
1845
|
const { rows: existing } = await query(`select session_id, idx, event_type, payload, event_id, created_at, cause
|
|
1850
|
-
from a2_events where event_id = any($
|
|
1846
|
+
from a2_events where session_id = $1 and event_id = any($2::text[])`, [sessionId, ids]);
|
|
1851
1847
|
const byId = new Map(existing.map((row) => [row["event_id"], row]));
|
|
1852
1848
|
const ordered = ids.map((id) => byId.get(id));
|
|
1853
1849
|
if (ordered.some((row) => !row) && await clearedAt({
|
|
@@ -1857,7 +1853,7 @@ function postgres(options = {}) {
|
|
|
1857
1853
|
})) return superseded;
|
|
1858
1854
|
if (ordered.some((row) => {
|
|
1859
1855
|
const cause = row ? decodeCause(row["cause"]) : null;
|
|
1860
|
-
return !row ||
|
|
1856
|
+
return !row || cause?.consumer !== consumer || cause.lane !== lane || cause.index !== index || cause.attempt !== attempt;
|
|
1861
1857
|
})) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "returned event retry does not match the committed batch");
|
|
1862
1858
|
return {
|
|
1863
1859
|
outcome: "completed",
|
|
@@ -1917,7 +1913,6 @@ function postgres(options = {}) {
|
|
|
1917
1913
|
},
|
|
1918
1914
|
abort: (reason) => {
|
|
1919
1915
|
switch (reason["abort"]) {
|
|
1920
|
-
case "foreign": return new A2Error("PARTIAL_DUPLICATE_BATCH", `event id '${String(reason["event_id"])}' already exists in another session`);
|
|
1921
1916
|
case "mixed": {
|
|
1922
1917
|
const owned = asNumber(reason["owned"]);
|
|
1923
1918
|
return new A2Error("PARTIAL_DUPLICATE_BATCH", `batch mixes ${owned} already-appended and ${events.length - owned} fresh events`);
|
|
@@ -2527,33 +2522,29 @@ function postgres(options = {}) {
|
|
|
2527
2522
|
if (entries.length === 0) return;
|
|
2528
2523
|
await wrap(async () => {
|
|
2529
2524
|
const atMs = meta.at.getTime();
|
|
2530
|
-
const
|
|
2525
|
+
const nowMs = clock.now().getTime();
|
|
2526
|
+
const expiresAtMs = nowMs + meta.ttlMs;
|
|
2531
2527
|
const applied = nullProtoRecord();
|
|
2532
2528
|
const write = async (query) => {
|
|
2533
|
-
for (const [field, value] of entries) if ((await
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
on conflict (ns, participant, field) do update set
|
|
2544
|
-
value = excluded.value,
|
|
2545
|
-
seen = excluded.seen,
|
|
2546
|
-
at = excluded.at,
|
|
2547
|
-
expires_at = excluded.expires_at
|
|
2548
|
-
where a2_presence.at <= excluded.at${notifications ? " returning field" : ""}`, [
|
|
2529
|
+
for (const [field, value] of entries) if ((await query(`insert into a2_presence
|
|
2530
|
+
(ns, participant, field, value, seen, at, expires_at)
|
|
2531
|
+
values ($1, $2, $3, $4::jsonb, $5, $6, $7)
|
|
2532
|
+
on conflict (ns, participant, field) do update set
|
|
2533
|
+
value = excluded.value,
|
|
2534
|
+
seen = excluded.seen,
|
|
2535
|
+
at = excluded.at,
|
|
2536
|
+
expires_at = excluded.expires_at
|
|
2537
|
+
where a2_presence.at <= excluded.at
|
|
2538
|
+
or a2_presence.expires_at <= $8${notifications ? " returning field" : ""}`, [
|
|
2549
2539
|
ns,
|
|
2550
2540
|
participant,
|
|
2551
2541
|
field,
|
|
2552
2542
|
jsonb(value),
|
|
2553
2543
|
meta.seen,
|
|
2554
2544
|
atMs,
|
|
2555
|
-
expiresAtMs
|
|
2556
|
-
|
|
2545
|
+
expiresAtMs,
|
|
2546
|
+
nowMs
|
|
2547
|
+
])).rows.length) applied[field] = structuredClone(value);
|
|
2557
2548
|
};
|
|
2558
2549
|
if (notifications) await withTx(write);
|
|
2559
2550
|
else {
|
|
@@ -2583,7 +2574,7 @@ function postgres(options = {}) {
|
|
|
2583
2574
|
)
|
|
2584
2575
|
select participant, field, value, seen, at, expires_at
|
|
2585
2576
|
from a2_presence
|
|
2586
|
-
where ns = $1 and expires_at > $2
|
|
2577
|
+
where ns = $1 and expires_at > $2 and value <> 'null'::jsonb
|
|
2587
2578
|
order by participant, field`, [ns, now]);
|
|
2588
2579
|
return rows.map((row) => ({
|
|
2589
2580
|
participant: row["participant"],
|
|
@@ -2800,7 +2791,7 @@ function postgres(options = {}) {
|
|
|
2800
2791
|
const subscription = presenceSubscriptions.get(ns);
|
|
2801
2792
|
await subscription?.ready;
|
|
2802
2793
|
await subscription?.poll.ready;
|
|
2803
|
-
return read(ns);
|
|
2794
|
+
return subscription ? subscription.poll.snapshot(() => read(ns)) : read(ns);
|
|
2804
2795
|
};
|
|
2805
2796
|
presence.subscribe = (ns, onPatch) => {
|
|
2806
2797
|
if (closed) throw new A2Error("STORE_UNAVAILABLE", "the store was closed");
|