experimental-a2 0.17.0 → 0.18.1

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.
Files changed (221) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +1 -1
  3. package/dist/{actor-DBbe5xeE.d.ts → actor-C7b-1arn.d.ts} +5 -5
  4. package/dist/{actor-DBbe5xeE.d.ts.map → actor-C7b-1arn.d.ts.map} +1 -1
  5. package/dist/actor-client.d.ts +1 -1
  6. package/dist/actor-react.d.ts +3 -3
  7. package/dist/actor-react.js +1 -1
  8. package/dist/actor.d.ts +1 -1
  9. package/dist/actor.js +2 -2
  10. package/dist/{ai-vpGtcaCB.d.ts → ai-BMLlI2Ff.d.ts} +3 -3
  11. package/dist/{ai-vpGtcaCB.d.ts.map → ai-BMLlI2Ff.d.ts.map} +1 -1
  12. package/dist/{ai-UrWfKeuv.js → ai-D6mGlO8j.js} +2 -2
  13. package/dist/{ai-UrWfKeuv.js.map → ai-D6mGlO8j.js.map} +1 -1
  14. package/dist/ai-client.d.ts +2 -2
  15. package/dist/ai-client.js +2 -2
  16. package/dist/ai-fx.browser.d.ts +1 -0
  17. package/dist/ai-fx.browser.js +6 -0
  18. package/dist/ai-fx.browser.js.map +1 -0
  19. package/dist/ai-fx.d.ts +10 -0
  20. package/dist/ai-fx.d.ts.map +1 -0
  21. package/dist/ai-fx.js +692 -0
  22. package/dist/ai-fx.js.map +1 -0
  23. package/dist/ai-harness-B2suP2Qh.d.ts +26 -0
  24. package/dist/ai-harness-B2suP2Qh.d.ts.map +1 -0
  25. package/dist/ai-harness-BuLh_EYi.js +273 -0
  26. package/dist/ai-harness-BuLh_EYi.js.map +1 -0
  27. package/dist/ai-pi.browser.d.ts +1 -0
  28. package/dist/ai-pi.browser.js +6 -0
  29. package/dist/ai-pi.browser.js.map +1 -0
  30. package/dist/ai-pi.d.ts +17 -0
  31. package/dist/ai-pi.d.ts.map +1 -0
  32. package/dist/ai-pi.js +950 -0
  33. package/dist/ai-pi.js.map +1 -0
  34. package/dist/ai-react.d.ts +5 -5
  35. package/dist/ai-react.js +2 -2
  36. package/dist/ai-server.d.ts +47 -25
  37. package/dist/ai-server.d.ts.map +1 -1
  38. package/dist/ai-server.js +113 -40
  39. package/dist/ai-server.js.map +1 -1
  40. package/dist/ai-tool-input-DmSqMUQi.js +40 -0
  41. package/dist/ai-tool-input-DmSqMUQi.js.map +1 -0
  42. package/dist/ai.d.ts +1 -1
  43. package/dist/ai.js +1 -1
  44. package/dist/bounded-cache-CPHWF-8h.js +41 -0
  45. package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
  46. package/dist/{client-COaiaDub.js → client-BhHLoNKF.js} +3 -3
  47. package/dist/{client-COaiaDub.js.map → client-BhHLoNKF.js.map} +1 -1
  48. package/dist/{client-qRnRUzBS.d.ts → client-h_DKFAjj.d.ts} +3 -3
  49. package/dist/{client-qRnRUzBS.d.ts.map → client-h_DKFAjj.d.ts.map} +1 -1
  50. package/dist/client.d.ts +3 -3
  51. package/dist/client.js +1 -1
  52. package/dist/index.d.ts +5 -5
  53. package/dist/{initial-snapshot-BrLso5x_.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +2 -2
  54. package/dist/{initial-snapshot-BrLso5x_.d.ts.map → initial-snapshot-Bx-c0pDP.d.ts.map} +1 -1
  55. package/dist/{internal-CbVvEwad.js → internal-CscSY7K_.js} +11 -3
  56. package/dist/internal-CscSY7K_.js.map +1 -0
  57. package/dist/otel.d.ts +1 -1
  58. package/dist/{react-BbJ-g6Uu.d.ts → react-BBXgHWFc.d.ts} +4 -4
  59. package/dist/{react-BbJ-g6Uu.d.ts.map → react-BBXgHWFc.d.ts.map} +1 -1
  60. package/dist/react.d.ts +1 -1
  61. package/dist/react.js +1 -1
  62. package/dist/reducer-BAXc8JyF.js.map +1 -1
  63. package/dist/{reducer-BYu6DT7h.d.ts → reducer-CqA59Q6U.d.ts} +2 -2
  64. package/dist/{reducer-BYu6DT7h.d.ts.map → reducer-CqA59Q6U.d.ts.map} +1 -1
  65. package/dist/scheduler-qstash.d.ts +2 -2
  66. package/dist/scheduler-qstash.js +3 -3
  67. package/dist/scheduler-qstash.js.map +1 -1
  68. package/dist/scheduler-vercel.d.ts +7 -7
  69. package/dist/scheduler-vercel.d.ts.map +1 -1
  70. package/dist/scheduler-vercel.js +9 -10
  71. package/dist/scheduler-vercel.js.map +1 -1
  72. package/dist/{server-Rj64fNvX.js → server-D-Y4pqwu.js} +126 -67
  73. package/dist/server-D-Y4pqwu.js.map +1 -0
  74. package/dist/{server-CGM8WzRG.d.ts → server-DYAATFuT.d.ts} +42 -7
  75. package/dist/server-DYAATFuT.d.ts.map +1 -0
  76. package/dist/server.d.ts +4 -4
  77. package/dist/server.js +1 -1
  78. package/dist/{session-clear-Dshpuhff.d.ts → session-clear-C-QwTXV1.d.ts} +2 -2
  79. package/dist/session-clear-C-QwTXV1.d.ts.map +1 -0
  80. package/dist/{store-Gpkq7WBo.d.ts → store-CyLUy86v.d.ts} +53 -5
  81. package/dist/store-CyLUy86v.d.ts.map +1 -0
  82. package/dist/store-N8PXxDAS.js.map +1 -1
  83. package/dist/store-memory.d.ts +1 -1
  84. package/dist/store-memory.js +56 -11
  85. package/dist/store-memory.js.map +1 -1
  86. package/dist/{store-polling-4_y8lETm.js → store-polling-7WJV0YWP.js} +2 -2
  87. package/dist/{store-polling-4_y8lETm.js.map → store-polling-7WJV0YWP.js.map} +1 -1
  88. package/dist/store-postgres.d.ts +1 -1
  89. package/dist/store-postgres.d.ts.map +1 -1
  90. package/dist/store-postgres.js +106 -25
  91. package/dist/store-postgres.js.map +1 -1
  92. package/dist/{store-presence-polling-Q1C-k2dd.js → store-presence-polling-B1ixeqIS.js} +3 -3
  93. package/dist/{store-presence-polling-Q1C-k2dd.js.map → store-presence-polling-B1ixeqIS.js.map} +1 -1
  94. package/dist/store-redis-http.d.ts +1 -1
  95. package/dist/store-redis-http.js +3 -3
  96. package/dist/{store-redis-notify-tqWNnMRu.js → store-redis-notify-CM0AfLY0.js} +271 -101
  97. package/dist/store-redis-notify-CM0AfLY0.js.map +1 -0
  98. package/dist/store-redis.d.ts +1 -1
  99. package/dist/store-redis.js +1 -1
  100. package/dist/store-sqlite.d.ts +1 -1
  101. package/dist/store-sqlite.js +66 -21
  102. package/dist/store-sqlite.js.map +1 -1
  103. package/dist/{telemetry-cZH-cibj.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
  104. package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
  105. package/dist/{wire-DjfHOvdW.js → wire-BKxH1vbP.js} +2 -2
  106. package/dist/{wire-DjfHOvdW.js.map → wire-BKxH1vbP.js.map} +1 -1
  107. package/docs/01-quickstart.mdx +49 -83
  108. package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
  109. package/docs/actors/01-introduction.mdx +48 -86
  110. package/docs/actors/02-concurrency.mdx +43 -70
  111. package/docs/actors/03-timers.mdx +28 -42
  112. package/docs/actors/04-routes.mdx +110 -150
  113. package/docs/actors/meta.ts +1 -1
  114. package/docs/agents/01-introduction.mdx +257 -0
  115. package/docs/agents/02-tools.mdx +247 -0
  116. package/docs/agents/03-conversations.mdx +538 -0
  117. package/docs/agents/04-harnesses.mdx +136 -0
  118. package/docs/agents/meta.ts +1 -0
  119. package/docs/client/01-react.mdx +294 -0
  120. package/docs/client/02-transports.mdx +86 -0
  121. package/docs/client/03-presence.mdx +224 -0
  122. package/docs/client/meta.ts +1 -0
  123. package/docs/contract/01-events.mdx +57 -0
  124. package/docs/contract/02-state.mdx +142 -0
  125. package/docs/contract/03-sessions.mdx +175 -0
  126. package/docs/index.mdx +109 -145
  127. package/docs/reference/01-core.mdx +139 -0
  128. package/docs/reference/02-server.mdx +860 -0
  129. package/docs/reference/03-client.mdx +301 -0
  130. package/docs/reference/04-ai.mdx +731 -0
  131. package/docs/reference/05-adapters.mdx +351 -0
  132. package/docs/reference/06-errors.mdx +138 -0
  133. package/docs/reference/meta.ts +1 -1
  134. package/docs/server/01-handlers.mdx +200 -0
  135. package/docs/server/02-timers.mdx +130 -0
  136. package/docs/server/03-cancellation.mdx +142 -0
  137. package/docs/server/04-routes.mdx +243 -0
  138. package/docs/server/05-production.mdx +323 -0
  139. package/docs/server/06-guarantees.mdx +132 -0
  140. package/examples/playground/DEPLOY.md +3 -5
  141. package/examples/playground/app/a0/[appId]/page.tsx +12 -13
  142. package/examples/playground/app/a0/app-list.tsx +9 -2
  143. package/examples/playground/app/a0/app-view.tsx +9 -1
  144. package/examples/playground/app/a0/apps/[appId]/route.ts +10 -5
  145. package/examples/playground/app/a0/apps.test.ts +12 -0
  146. package/examples/playground/app/a0/apps.ts +22 -7
  147. package/examples/playground/app/a0/chat.tsx +47 -1
  148. package/examples/playground/app/a0/devtools.tsx +2 -8
  149. package/examples/playground/app/a0/fx/events/route.ts +9 -0
  150. package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
  151. package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
  152. package/examples/playground/app/a0/model.test.ts +18 -1
  153. package/examples/playground/app/a0/model.ts +47 -10
  154. package/examples/playground/app/a0/no-photon.cjs +4 -0
  155. package/examples/playground/app/a0/no-quickjs.cjs +3 -0
  156. package/examples/playground/app/a0/pi/events/route.ts +9 -0
  157. package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
  158. package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
  159. package/examples/playground/app/a0/sandbox-log.ts +10 -4
  160. package/examples/playground/app/a0/sandbox-session.ts +152 -0
  161. package/examples/playground/app/a0/scheduler/route.ts +2 -2
  162. package/examples/playground/app/a0/server.ts +98 -54
  163. package/examples/playground/app/a0/session.ts +17 -5
  164. package/examples/playground/app/a0/shell.tsx +11 -3
  165. package/examples/playground/app/agent/ingress.test.ts +6 -3
  166. package/examples/playground/app/agent/server.ts +9 -6
  167. package/examples/playground/app/agent/telemetry.test.ts +71 -50
  168. package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +1 -1
  169. package/examples/playground/app/chat/server.test.ts +30 -26
  170. package/examples/playground/app/chat/server.ts +46 -46
  171. package/examples/playground/next.config.ts +17 -5
  172. package/examples/playground/package.json +4 -1
  173. package/examples/playground/vercel.json +18 -0
  174. package/package.json +27 -3
  175. package/src/ai-agent.ts +271 -90
  176. package/src/ai-fx.browser.ts +5 -0
  177. package/src/ai-fx.ts +912 -0
  178. package/src/ai-harness.ts +518 -0
  179. package/src/ai-model-metadata.ts +11 -6
  180. package/src/ai-pi.browser.ts +5 -0
  181. package/src/ai-pi.ts +1322 -0
  182. package/src/ai-sdk-step.ts +2 -2
  183. package/src/ai-server.ts +3 -1
  184. package/src/ai-tool-input.ts +45 -0
  185. package/src/internal.ts +13 -0
  186. package/src/libfx.d.ts +34 -0
  187. package/src/reducer.ts +1 -1
  188. package/src/scheduler-qstash.ts +5 -5
  189. package/src/scheduler-vercel.ts +14 -15
  190. package/src/server.ts +233 -29
  191. package/src/store-memory.ts +92 -16
  192. package/src/store-postgres.ts +156 -25
  193. package/src/store-redis-core.ts +322 -110
  194. package/src/store-redis-notify.ts +2 -0
  195. package/src/store-sqlite.ts +121 -28
  196. package/src/store.ts +55 -3
  197. package/src/telemetry.ts +1 -0
  198. package/dist/internal-CbVvEwad.js.map +0 -1
  199. package/dist/server-CGM8WzRG.d.ts.map +0 -1
  200. package/dist/server-Rj64fNvX.js.map +0 -1
  201. package/dist/session-clear-Dshpuhff.d.ts.map +0 -1
  202. package/dist/store-Gpkq7WBo.d.ts.map +0 -1
  203. package/dist/store-redis-notify-tqWNnMRu.js.map +0 -1
  204. package/dist/telemetry-cZH-cibj.d.ts.map +0 -1
  205. package/docs/concepts/01-contracts.mdx +0 -237
  206. package/docs/concepts/02-handlers.mdx +0 -284
  207. package/docs/concepts/03-durability.mdx +0 -314
  208. package/docs/concepts/04-state.mdx +0 -209
  209. package/docs/guides/01-timers.mdx +0 -193
  210. package/docs/guides/02-cancellation.mdx +0 -161
  211. package/docs/guides/03-react.mdx +0 -324
  212. package/docs/guides/04-local-first.mdx +0 -47
  213. package/docs/guides/05-production.mdx +0 -534
  214. package/docs/guides/06-ai-agents.mdx +0 -1408
  215. package/docs/guides/08-application-data.mdx +0 -113
  216. package/docs/guides/09-presence.mdx +0 -262
  217. package/docs/guides/10-transports.mdx +0 -221
  218. package/docs/reference/01-api.mdx +0 -2358
  219. package/docs/reference/02-errors.mdx +0 -150
  220. /package/docs/{concepts → contract}/meta.ts +0 -0
  221. /package/docs/{guides → server}/meta.ts +0 -0
@@ -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 {\n RANDOM_IDS,\n SYSTEM_CLOCK,\n type A2Store,\n type AppendEvent,\n type Clock,\n type Event,\n type EventCause,\n type IdSource,\n type PresenceRow,\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 lane: string | null\n processedAt: Date | null\n processedByAttempt: number | null\n returnedEventIds: string[] | null\n firstClaimedAt: Date | null\n lastClaimedAt: Date | null\n attemptCount: number\n claimHolder: string | null\n claimExpiresAt: Date | null\n failureCount: number\n lastFailedAt: Date | null\n lastFailedAttempt: number | null\n lastError: string | null\n failedAt: Date | null\n}\n\ntype PresenceEntry = {\n value: unknown\n seen: number\n at: Date\n expiresAt: Date\n}\n\ntype LaneQueue = { rows: Row[]; head: number }\ntype SessionDispatch = {\n ready: Map<number, Row>\n lanes: Map<string, LaneQueue>\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 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 lane: row.lane,\n processedAt: row.processedAt ? new Date(row.processedAt) : null,\n processedByAttempt: row.processedByAttempt,\n returnedEventIds: row.returnedEventIds ? [...row.returnedEventIds] : null,\n firstClaimedAt: row.firstClaimedAt ? new Date(row.firstClaimedAt) : null,\n lastClaimedAt: row.lastClaimedAt ? new Date(row.lastClaimedAt) : null,\n attemptCount: row.attemptCount,\n claimHolder: row.claimHolder,\n claimExpiresAt: row.claimExpiresAt ? new Date(row.claimExpiresAt) : null,\n failureCount: row.failureCount,\n lastFailedAt: row.lastFailedAt ? new Date(row.lastFailedAt) : null,\n lastFailedAttempt: row.lastFailedAttempt,\n lastError: row.lastError,\n failedAt: row.failedAt ? new Date(row.failedAt) : 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 snapshotKey = (sessionId: string, reducerName: string): string =>\n `${sessionId}\\u0000${reducerName}`\n\nconst eventPinKey = (sessionId: string, index: number): string =>\n `${sessionId}\\u0000${index}`\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 const dispatch = new Map<string, SessionDispatch>()\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 checkpointKey = (\n sessionId: string,\n reducerName: string,\n index: number,\n ): string => `${snapshotKey(sessionId, reducerName)}\\u0000${index}`\n\n const releaseSnapshotPins = (sessionId: string, eventIndex: number): void => {\n const pinKey = eventPinKey(sessionId, eventIndex)\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 const dispatchOf = (sessionId: string): SessionDispatch => {\n let state = dispatch.get(sessionId)\n if (!state) {\n state = { ready: new Map(), lanes: new Map() }\n dispatch.set(sessionId, state)\n }\n return state\n }\n\n const enqueue = (sessionId: string, row: Row): void => {\n if (row.processedAt !== null) return\n const state = dispatchOf(sessionId)\n if (row.lane === null) {\n state.ready.set(row.index, row)\n return\n }\n let queue = state.lanes.get(row.lane)\n if (!queue) {\n queue = { rows: [], head: 0 }\n state.lanes.set(row.lane, queue)\n }\n queue.rows.push(row)\n if (queue.rows.length === 1) state.ready.set(row.index, row)\n }\n\n const settle = (sessionId: string, row: Row): void => {\n const state = dispatchOf(sessionId)\n state.ready.delete(row.index)\n if (row.lane === null) return\n const queue = state.lanes.get(row.lane)!\n if (queue.rows[queue.head] !== row) return\n queue.head += 1\n if (queue.head === queue.rows.length) {\n state.lanes.delete(row.lane)\n return\n }\n const next = queue.rows[queue.head]!\n if (next.failedAt === null) state.ready.set(next.index, next)\n }\n\n const find = (sessionId: string, index: number): Row => {\n const row = rowAt(sessionId, index)\n if (!row) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n return row\n }\n\n const appendResult = (sessionId: string, events: StoredEvent[]) => {\n const log = sessions.get(sessionId)\n return {\n events,\n hasPending: (log?.rows ?? []).some(\n (row) => row.index > log!.clearIndex && row.processedAt === null,\n ),\n }\n }\n\n /** The attempt-currency fence shared by handler 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 parent = find(sessionId, cause.index)\n if (parent.attemptCount !== cause.attempt || parent.failedAt !== null) {\n throw supersededAttemptError(sessionId, cause)\n }\n }\n\n const claim = (\n row: Row,\n holder: string,\n now: Date,\n expiresAt: Date,\n ): StoredEvent => {\n row.attemptCount += 1\n row.firstClaimedAt ??= new Date(now)\n row.lastClaimedAt = new Date(now)\n row.claimHolder = holder\n row.claimExpiresAt = new Date(expiresAt)\n return toStored(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 const insert = (\n sessionId: string,\n events: readonly AppendEvent[],\n now: Date,\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 lane: candidate.lane ?? null,\n processedAt: candidate.settled ? new Date(now) : null,\n processedByAttempt: null,\n returnedEventIds: null,\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 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 enqueue(sessionId, row)\n }\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 return {\n async append(sessionId, events, appendOptions) {\n if (events.length === 0) return appendResult(sessionId, [])\n\n // Batch-scoped idempotency (a2-implementation.md §3): all ids\n // already present → lost-ack retry, return the original rows;\n // some present → the caller mixed sent and fresh events.\n const existing = checkBatchIds(sessionId, events)\n if (existing.length > 0) {\n if (existing.length === events.length) {\n return appendResult(\n sessionId,\n idempotentReplay(events, existing.map(toStored)),\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 handler append commits only while\n // its causal attempt is still the parent's latest (store.ts `append`).\n for (const event of events) {\n if (event.cause) assertCurrentCause(sessionId, event.cause)\n }\n\n const now = clock.now()\n const inserted = insert(sessionId, events, now)\n notifyStreams(sessionId, inserted)\n return appendResult(sessionId, inserted.map(toStored))\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, settled: true }],\n clock.now(),\n )\n logOf(sessionId).clearIndex = marker!.index\n // Every row is at or below the marker, so no dispatch state survives.\n dispatch.delete(sessionId)\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 releaseSnapshotPins(sessionId, row.index)\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 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 excludeIndexes,\n }) {\n const claimedAt = clock.now()\n const now = claimedAt.getTime()\n const expiresAt = new Date(expiresAtMs ?? now + ttlMs)\n const excluded = new Set(excludeIndexes ?? [])\n const eligible: Row[] = []\n let retryAt: Date | null = null\n\n for (const row of dispatchOf(sessionId).ready.values()) {\n if (row.failedAt !== null || excluded.has(row.index)) continue\n if (row.claimExpiresAt && row.claimExpiresAt.getTime() > now) {\n if (!retryAt || row.claimExpiresAt < retryAt) {\n retryAt = new Date(row.claimExpiresAt)\n }\n continue\n }\n eligible.push(row)\n }\n\n if (eligible.length === 0) {\n return retryAt ? { outcome: 'busy', retryAt } : { outcome: 'settled' }\n }\n eligible.sort((a, b) => a.index - b.index)\n const clearIndex = clearIndexOf(sessionId)\n return {\n outcome: 'claimed',\n events: eligible.map((row) => {\n const claimed = claim(row, holder, claimedAt, expiresAt)\n if (clearIndex > 0) claimed.clearIndex = clearIndex\n return claimed\n }),\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: number[] = []\n const superseded: number[] = []\n for (const { index, attempt } of claims.toSorted(\n (a, b) => a.index - b.index,\n )) {\n if (index <= clearIndex) {\n superseded.push(index)\n continue\n }\n const row = rowAt(sessionId, index)\n if (!row) continue\n if (row.attemptCount > attempt) {\n superseded.push(row.index)\n continue\n }\n if (\n row.processedAt === null &&\n row.failedAt === null &&\n row.claimHolder === holder &&\n row.claimExpiresAt !== null &&\n row.claimExpiresAt > now\n ) {\n row.claimExpiresAt = new Date(expiresAt)\n renewed.push(row.index)\n }\n }\n return { renewed, superseded }\n },\n\n async completeAttempt({ sessionId, index, attempt, events }) {\n if (index <= clearIndexOf(sessionId)) return { outcome: 'superseded' }\n const parent = find(sessionId, index)\n const cause = { index, attempt }\n if (parent.processedAt !== null) {\n if (parent.processedByAttempt !== attempt) {\n return { outcome: 'superseded' }\n }\n const requestedIds = events.map((event) => event.id)\n if (\n parent.returnedEventIds === null ||\n parent.returnedEventIds.length !== requestedIds.length ||\n parent.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?.index !== index || 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: idempotentReplay(events, existing.map(toStored)),\n }\n }\n if (\n parent.attemptCount !== attempt ||\n parent.failedAt !== null ||\n parent.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 now = clock.now()\n const inserted = insert(sessionId, events, now, cause)\n parent.processedAt = new Date(now)\n parent.processedByAttempt = attempt\n parent.returnedEventIds = events.map((event) => event.id)\n parent.claimHolder = null\n parent.claimExpiresAt = null\n settle(sessionId, parent)\n releaseSnapshotPins(sessionId, index)\n notifyStreams(sessionId, inserted)\n return { outcome: 'completed', events: inserted.map(toStored) }\n },\n\n async failAttempt({ sessionId, index, attempt, error, maxFailures }) {\n if (index <= clearIndexOf(sessionId)) {\n return { outcome: 'superseded', failureCount: 0 }\n }\n const row = find(sessionId, index)\n if (\n row.processedAt ||\n row.attemptCount !== attempt ||\n (row.claimHolder === null && row.lastFailedAttempt !== attempt)\n ) {\n return { outcome: 'superseded', failureCount: row.failureCount }\n }\n if (row.lastFailedAttempt === attempt && row.claimHolder === null) {\n return {\n outcome: row.failedAt ? 'dead_lettered' : 'failed',\n failureCount: row.failureCount,\n }\n }\n if (row.failedAt) {\n return { outcome: 'dead_lettered', failureCount: row.failureCount }\n }\n row.failureCount += 1\n row.lastError = error\n const failedAt = clock.now()\n row.lastFailedAt = new Date(failedAt)\n row.lastFailedAttempt = attempt\n row.claimHolder = null\n row.claimExpiresAt = null\n if (row.failureCount >= maxFailures) {\n row.failedAt = new Date(failedAt)\n dispatchOf(sessionId).ready.delete(row.index)\n releaseSnapshotPins(sessionId, index)\n return {\n outcome: 'dead_lettered',\n failureCount: row.failureCount,\n }\n }\n return { outcome: 'failed', failureCount: row.failureCount }\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 eventIndex of write.pinEventIndexes ?? []) {\n const event = rowAt(sessionId, eventIndex)\n if (\n !event ||\n event.index <= clearIndex ||\n event.processedAt !== null ||\n event.failedAt !== null\n ) {\n continue\n }\n const pinKey = eventPinKey(sessionId, eventIndex)\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":";;;;;AAgFA,MAAM,gBAAgB;AAEtB,MAAM,UAAU,QACd,MAAM,IAAI,SAAS,IAAI,KAAK,SAAS;AAEvC,MAAM,YAAY,SAA2B;CAC3C,IAAI,IAAI;CACR,MAAM,IAAI;CACV,SAAS,gBAAgB,IAAI,OAAO;CACpC,OAAO,IAAI;CACX,WAAW,IAAI;CACf,WAAW,IAAI,KAAK,IAAI,SAAS;CACjC,OAAO,IAAI,QAAQ,EAAE,GAAG,IAAI,MAAM,IAAI;CACtC,MAAM,IAAI;CACV,aAAa,IAAI,cAAc,IAAI,KAAK,IAAI,WAAW,IAAI;CAC3D,oBAAoB,IAAI;CACxB,kBAAkB,IAAI,mBAAmB,CAAC,GAAG,IAAI,gBAAgB,IAAI;CACrE,gBAAgB,IAAI,iBAAiB,IAAI,KAAK,IAAI,cAAc,IAAI;CACpE,eAAe,IAAI,gBAAgB,IAAI,KAAK,IAAI,aAAa,IAAI;CACjE,cAAc,IAAI;CAClB,aAAa,IAAI;CACjB,gBAAgB,IAAI,iBAAiB,IAAI,KAAK,IAAI,cAAc,IAAI;CACpE,cAAc,IAAI;CAClB,cAAc,IAAI,eAAe,IAAI,KAAK,IAAI,YAAY,IAAI;CAC9D,mBAAmB,IAAI;CACvB,WAAW,IAAI;CACf,UAAU,IAAI,WAAW,IAAI,KAAK,IAAI,QAAQ,IAAI;AACpD;AAEA,MAAM,WAAW,SAAqB;CACpC,IAAI,IAAI;CACR,MAAM,IAAI;CACV,SAAS,gBAAgB,IAAI,OAAO;CACpC,OAAO,IAAI;CACX,WAAW,IAAI;CACf,WAAW,IAAI,KAAK,IAAI,SAAS;AACnC;AAEA,MAAM,eAAe,WAAmB,gBACtC,GAAG,UAAU,QAAQ;AAEvB,MAAM,eAAe,WAAmB,UACtC,GAAG,UAAU,QAAQ;AAEvB,SAAgB,OAAO,UAA8B,CAAC,GAAY;CAChE,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,aAAa,QAAQ,OAAO;CAElC,MAAM,2BAAW,IAAI,IAAwB;CAC7C,MAAM,2BAAW,IAAI,IAA6B;;CAElD,MAAM,4BAAY,IAAI,IAAiB;;CAEvC,MAAM,4BAAY,IAAI,IAGpB;CACF,MAAM,sCAAsB,IAAI,IAG9B;CACF,MAAM,+BAAe,IAAI,IAAyB;CAClD,MAAM,oCAAoB,IAAI,IAAoB;CAClD,MAAM,oCAAoB,IAAI,IAAqC;;CAEnE,MAAM,+BAAe,IAAI,IAGvB;CACF,MAAM,sCAAsB,IAAI,IAG9B;CAEF,MAAM,iBAAiB,WAAmB,SAAsB;EAC9D,MAAM,OAAO,kBAAkB,IAAI,SAAS;EAC5C,IAAI,CAAC,MAAM;EACX,KAAK,MAAM,YAAY,MACrB,KAAK,MAAM,OAAO,MAAM,SAAS,GAAG;CAExC;CAEA,MAAM,SAAS,cAAkC;EAC/C,IAAI,MAAM,SAAS,IAAI,SAAS;EAChC,IAAI,CAAC,KAAK;GACR,MAAM;IAAE,MAAM,CAAC;IAAG,QAAQ;IAAG,YAAY;GAAE;GAC3C,SAAS,IAAI,WAAW,GAAG;EAC7B;EACA,OAAO;CACT;CAEA,MAAM,SAAS,WAAmB,UAAmC;EACnE,MAAM,MAAM,SAAS,IAAI,SAAS;EAClC,OAAO,OAAO,QAAQ,IAAI,SACtB,IAAI,KAAK,QAAQ,IAAI,SAAS,KAC9B,KAAA;CACN;CAEA,MAAM,gBAAgB,cACpB,SAAS,IAAI,SAAS,CAAC,EAAE,cAAc;;CAGzC,MAAM,WAAW,WAAmB,OAAgC;EAClE,MAAM,MAAM,UAAU,IAAI,EAAE;EAC5B,OAAO,KAAK,cAAc,aAAa,IAAI,QAAQ,aAAa,SAAS,IACrE,KAAA,IACA;CACN;CAEA,MAAM,iBACJ,WACA,aACA,UACW,GAAG,YAAY,WAAW,WAAW,EAAE,QAAQ;CAE5D,MAAM,uBAAuB,WAAmB,eAA6B;EAC3E,MAAM,SAAS,YAAY,WAAW,UAAU;EAChD,MAAM,cAAc,aAAa,IAAI,MAAM;EAC3C,IAAI,CAAC,aAAa;EAClB,KAAK,MAAM,OAAO,aAAa;GAC7B,MAAM,QAAQ,kBAAkB,IAAI,GAAG,KAAK,KAAK;GACjD,IAAI,SAAS,GAAG;IACd,kBAAkB,OAAO,GAAG;IAC5B,oBAAoB,OAAO,GAAG;GAChC,OACE,kBAAkB,IAAI,KAAK,IAAI;EAEnC;EACA,aAAa,OAAO,MAAM;CAC5B;CAEA,MAAM,cAAc,cAAuC;EACzD,IAAI,QAAQ,SAAS,IAAI,SAAS;EAClC,IAAI,CAAC,OAAO;GACV,QAAQ;IAAE,uBAAO,IAAI,IAAI;IAAG,uBAAO,IAAI,IAAI;GAAE;GAC7C,SAAS,IAAI,WAAW,KAAK;EAC/B;EACA,OAAO;CACT;CAEA,MAAM,WAAW,WAAmB,QAAmB;EACrD,IAAI,IAAI,gBAAgB,MAAM;EAC9B,MAAM,QAAQ,WAAW,SAAS;EAClC,IAAI,IAAI,SAAS,MAAM;GACrB,MAAM,MAAM,IAAI,IAAI,OAAO,GAAG;GAC9B;EACF;EACA,IAAI,QAAQ,MAAM,MAAM,IAAI,IAAI,IAAI;EACpC,IAAI,CAAC,OAAO;GACV,QAAQ;IAAE,MAAM,CAAC;IAAG,MAAM;GAAE;GAC5B,MAAM,MAAM,IAAI,IAAI,MAAM,KAAK;EACjC;EACA,MAAM,KAAK,KAAK,GAAG;EACnB,IAAI,MAAM,KAAK,WAAW,GAAG,MAAM,MAAM,IAAI,IAAI,OAAO,GAAG;CAC7D;CAEA,MAAM,UAAU,WAAmB,QAAmB;EACpD,MAAM,QAAQ,WAAW,SAAS;EAClC,MAAM,MAAM,OAAO,IAAI,KAAK;EAC5B,IAAI,IAAI,SAAS,MAAM;EACvB,MAAM,QAAQ,MAAM,MAAM,IAAI,IAAI,IAAI;EACtC,IAAI,MAAM,KAAK,MAAM,UAAU,KAAK;EACpC,MAAM,QAAQ;EACd,IAAI,MAAM,SAAS,MAAM,KAAK,QAAQ;GACpC,MAAM,MAAM,OAAO,IAAI,IAAI;GAC3B;EACF;EACA,MAAM,OAAO,MAAM,KAAK,MAAM;EAC9B,IAAI,KAAK,aAAa,MAAM,MAAM,MAAM,IAAI,KAAK,OAAO,IAAI;CAC9D;CAEA,MAAM,QAAQ,WAAmB,UAAuB;EACtD,MAAM,MAAM,MAAM,WAAW,KAAK;EAClC,IAAI,CAAC,KACH,MAAM,IAAI,UACR,qBAAqB,MAAM,eAAe,UAAU,EACtD;EAEF,OAAO;CACT;CAEA,MAAM,gBAAgB,WAAmB,WAA0B;EACjE,MAAM,MAAM,SAAS,IAAI,SAAS;EAClC,OAAO;GACL;GACA,aAAa,KAAK,QAAQ,CAAC,EAAA,CAAG,MAC3B,QAAQ,IAAI,QAAQ,IAAK,cAAc,IAAI,gBAAgB,IAC9D;EACF;CACF;;CAGA,MAAM,sBAAsB,WAAmB,UAA4B;EACzE,IAAI,MAAM,SAAS,aAAa,SAAS,GACvC,MAAM,uBAAuB,WAAW,KAAK;EAE/C,MAAM,SAAS,KAAK,WAAW,MAAM,KAAK;EAC1C,IAAI,OAAO,iBAAiB,MAAM,WAAW,OAAO,aAAa,MAC/D,MAAM,uBAAuB,WAAW,KAAK;CAEjD;CAEA,MAAM,SACJ,KACA,QACA,KACA,cACgB;EAChB,IAAI,gBAAgB;EACpB,IAAI,mBAAmB,IAAI,KAAK,GAAG;EACnC,IAAI,gBAAgB,IAAI,KAAK,GAAG;EAChC,IAAI,cAAc;EAClB,IAAI,iBAAiB,IAAI,KAAK,SAAS;EACvC,OAAO,SAAS,GAAG;CACrB;CAEA,MAAM,iBACJ,WACA,WACU;EACV,MAAM,WAAW,OAAO,QACrB,cACC,UAAU,OAAO,KAAA,CACrB;EAEA,IAAI,IADoB,IAAI,SAAS,KAAK,cAAc,UAAU,EAAE,CACtD,CAAC,CAAC,SAAS,SAAS,QAChC,MAAM,IAAI,QACR,2BACA,iDACF;EAEF,MAAM,WAAW,SACd,KAAK,cAAc,QAAQ,WAAW,UAAU,EAAE,CAAC,CAAC,CACpD,QAAQ,QAAoB,QAAQ,KAAA,CAAS;EAChD,MAAM,UAAU,SAAS,MAAM,QAAQ,IAAI,cAAc,SAAS;EAClE,IAAI,SACF,MAAM,IAAI,QACR,2BACA,aAAa,QAAQ,GAAG,oCAC1B;EAEF,OAAO;CACT;CAEA,MAAM,UACJ,WACA,QACA,KACA,UACU;EACV,MAAM,MAAM,MAAM,SAAS;EAC3B,MAAM,OAAO,OAAO,GAAG;EACvB,MAAM,WAAW,OAAO,KAAK,WAAW,YAAiB;GACvD,IAAI,UAAU,MAAM,WAAW;GAC/B,MAAM,UAAU;GAChB,SAAS,gBAAgB,UAAU,OAAO;GAC1C,OAAO,OAAO,IAAI;GAClB;GACA,WAAW,IAAI,KAAK,GAAG;GACvB,OAAO,QACH,EAAE,GAAG,MAAM,IACX,UAAU,QACR,EAAE,GAAG,UAAU,MAAM,IACrB;GACN,MAAM,UAAU,QAAQ;GACxB,aAAa,UAAU,UAAU,IAAI,KAAK,GAAG,IAAI;GACjD,oBAAoB;GACpB,kBAAkB;GAClB,gBAAgB;GAChB,eAAe;GACf,cAAc;GACd,aAAa;GACb,gBAAgB;GAChB,cAAc;GACd,cAAc;GACd,mBAAmB;GACnB,WAAW;GACX,UAAU;EACZ,EAAE;EACF,KAAK,MAAM,OAAO,UAChB,IAAI,QAAQ,WAAW,IAAI,EAAE,GAC3B,MAAM,IAAI,QACR,2BACA,aAAa,IAAI,GAAG,iBACtB;EAGJ,KAAK,MAAM,OAAO,UAAU;GAC1B,IAAI,KAAK,KAAK,GAAG;GACjB,UAAU,IAAI,IAAI,IAAI,GAAG;GACzB,QAAQ,WAAW,GAAG;EACxB;EACA,OAAO;CACT;;CAGA,MAAM,wBACJ,WACA,eACS;EACT,MAAM,SAAS,GAAG,UAAU;EAC5B,KAAK,MAAM,CAAC,KAAK,aAAa,WAAW;GACvC,IAAI,CAAC,IAAI,WAAW,MAAM,KAAK,SAAS,SAAS,YAAY;GAC7D,UAAU,OAAO,GAAG;GACpB,kBAAkB,OAAO,GAAG,IAAI,QAAQ,SAAS,OAAO;EAC1D;EACA,KAAK,MAAM,CAAC,KAAK,aAAa,qBAAqB;GACjD,IAAI,CAAC,IAAI,WAAW,MAAM,KAAK,SAAS,SAAS,YAAY;GAC7D,oBAAoB,OAAO,GAAG;GAC9B,kBAAkB,OAAO,GAAG;EAC9B;CACF;CAEA,MAAM,eACJ,WACA,aACA,cACA,yBACmB;EACnB,MAAM,MAAM,YAAY,WAAW,WAAW;EAC9C,MAAM,aAAa,aAAa,SAAS;EACzC,MAAM,mBAAmB,wBAAwB;EACjD,MAAM,SAAS,UAAU,IAAI,GAAG;EAChC,MAAM,OAAO,UAAU,OAAO,SAAS,aAAa,SAAS,KAAA;EAC7D,IAAI,YACF,SAAS,qBAAqB,KAAA,KAAa,KAAK,SAAS,oBACrD,OACA;EACN,IAAI,qBAAqB,KAAA,GAAW;GAClC,MAAM,SAAS,GAAG,IAAI;GACtB,KAAK,MAAM,CAAC,eAAe,aAAa,qBACtC,IACE,cAAc,WAAW,MAAM,KAC/B,SAAS,SAAS,cAClB,SAAS,SAAS,qBACjB,CAAC,aAAa,SAAS,QAAQ,UAAU,QAE1C,YAAY;EAGlB;EACA,MAAM,WAAW,YACb;GAAE,OAAO,UAAU;GAAO,OAAO,gBAAgB,UAAU,KAAK;EAAE,IAClE;EACJ,MAAM,aAAa,KAAK,IAAI,UAAU,SAAS,GAAG,aAAa,CAAC;EAChE,OAAO;GACL,WAAW,MAAM,SAAS;GAC1B;GACA,SAAS,SAAS,IAAI,SAAS,CAAC,EAAE,QAAQ,CAAC,EAAA,CACxC,QACE,QACC,IAAI,QAAQ,eACX,iBAAiB,KAAA,KAAa,IAAI,SAAS,aAChD,CAAC,CACA,IAAI,OAAO;GACd,GAAI,aAAa,IAAI,EAAE,WAAW,IAAI,CAAC;EACzC;CACF;CAEA,OAAO;EACL,MAAM,OAAO,WAAW,QAAQ,eAAe;GAC7C,IAAI,OAAO,WAAW,GAAG,OAAO,aAAa,WAAW,CAAC,CAAC;GAK1D,MAAM,WAAW,cAAc,WAAW,MAAM;GAChD,IAAI,SAAS,SAAS,GAAG;IACvB,IAAI,SAAS,WAAW,OAAO,QAC7B,OAAO,aACL,WACA,iBAAiB,QAAQ,SAAS,IAAI,QAAQ,CAAC,CACjD;IAEF,MAAM,IAAI,QACR,2BACA,eAAe,SAAS,OAAO,wBAAwB,OAAO,SAAS,SAAS,OAAO,cACzF;GACF;GAEA,MAAM,cAAc,eAAe;GACnC,IAAI,gBAAgB,KAAA,KAAa,eAAe,aAAa,SAAS,GACpE,MAAM,oBACJ,6BAA6B,YAAY,2BAA2B,UAAU,EAChF;GAIF,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,OAAO,mBAAmB,WAAW,MAAM,KAAK;GAG5D,MAAM,MAAM,MAAM,IAAI;GACtB,MAAM,WAAW,OAAO,WAAW,QAAQ,GAAG;GAC9C,cAAc,WAAW,QAAQ;GACjC,OAAO,aAAa,WAAW,SAAS,IAAI,QAAQ,CAAC;EACvD;EAEA,MAAM,MAAM,EAAE,WAAW,aAAa,SAAS;GAC7C,IAAI,OAAO,mBAAmB,WAAW,KAAK;GAC9C,MAAM,MAAM,SAAS,IAAI,SAAS;GAClC,MAAM,OAAO,OAAO,GAAG;GACvB,MAAM,aAAa,KAAK,cAAc;GACtC,IAAI,gBAAgB,KAAA,KAAa,eAAe,cAAc,GAC5D,OAAO;IAAE,SAAS;IAAM,OAAO;IAAM;GAAW;GAElD,IAAI,gBAAgB,KAAA,KAAa,gBAAgB,MAC/C,OAAO;IAAE,SAAS;IAAO,OAAO;IAAM;GAAW;GAEnD,IAAI,SAAS,KAAK,eAAe,MAC/B,OAAO;IAAE,SAAS;IAAM,OAAO;IAAM;GAAW;GAElD,MAAM,CAAC,UAAU,OACf,WACA,CAAC;IAAE,MAAM;IAAkB,SAAS;IAAM,SAAS;GAAK,CAAC,GACzD,MAAM,IAAI,CACZ;GACA,MAAM,SAAS,CAAC,CAAC,aAAa,OAAQ;GAEtC,SAAS,OAAO,SAAS;GACzB,cAAc,WAAW,CAAC,MAAO,CAAC;GAClC,OAAO;IAAE,SAAS;IAAM,OAAO,OAAQ;IAAO,YAAY,OAAQ;GAAM;EAC1E;EAEA,MAAM,QAAQ,WAAW;GACvB,MAAM,MAAM,SAAS,IAAI,SAAS;GAClC,IAAI,CAAC,OAAO,IAAI,UAAU,IAAI,aAAa,GACzC,OAAO;IAAE,SAAS;IAAG,MAAM;GAAK;GAElC,MAAM,UAAU,IAAI,KAAK,OACvB,GACA,KAAK,IAAI,eAAe,IAAI,aAAa,IAAI,IAAI,MAAM,CACzD;GACA,IAAI,UAAU,QAAQ;GACtB,KAAK,MAAM,OAAO,SAAS;IAEzB,IAAI,UAAU,IAAI,IAAI,EAAE,MAAM,KAAK,UAAU,OAAO,IAAI,EAAE;IAC1D,oBAAoB,WAAW,IAAI,KAAK;GAC1C;GACA,MAAM,OAAO,IAAI,WAAW,IAAI,aAAa;GAE7C,IAAI,MAAM,qBAAqB,WAAW,IAAI,UAAU;GACxD,OAAO;IAAE,SAAS,QAAQ;IAAQ;GAAK;EACzC;EAEA,MAAM,KAAK,WAAW,MAAM;GAC1B,MAAM,MAAM,SAAS,IAAI,SAAS;GAClC,IAAI,CAAC,KAAK,OAAO,CAAC;GAClB,MAAM,OAAO,OAAO,GAAG;GACvB,MAAM,QAAQ,KAAK,IACjB,MAAM,cAAc,GACpB,IAAI,aAAa,GACjB,IAAI,MACN;GACA,MAAM,UAAU,KAAK,IAAI,MAAM,gBAAgB,MAAM,IAAI;GACzD,IAAI,WAAW,OAAO,OAAO,CAAC;GAC9B,OAAO,IAAI,KACR,MAAM,QAAQ,IAAI,QAAQ,UAAU,IAAI,MAAM,CAAC,CAC/C,IAAI,QAAQ;EACjB;EAEA,MAAM,eAAe,EACnB,WACA,QACA,OACA,aACA,kBACC;GACD,MAAM,YAAY,MAAM,IAAI;GAC5B,MAAM,MAAM,UAAU,QAAQ;GAC9B,MAAM,YAAY,IAAI,KAAK,eAAe,MAAM,KAAK;GACrD,MAAM,WAAW,IAAI,IAAI,kBAAkB,CAAC,CAAC;GAC7C,MAAM,WAAkB,CAAC;GACzB,IAAI,UAAuB;GAE3B,KAAK,MAAM,OAAO,WAAW,SAAS,CAAC,CAAC,MAAM,OAAO,GAAG;IACtD,IAAI,IAAI,aAAa,QAAQ,SAAS,IAAI,IAAI,KAAK,GAAG;IACtD,IAAI,IAAI,kBAAkB,IAAI,eAAe,QAAQ,IAAI,KAAK;KAC5D,IAAI,CAAC,WAAW,IAAI,iBAAiB,SACnC,UAAU,IAAI,KAAK,IAAI,cAAc;KAEvC;IACF;IACA,SAAS,KAAK,GAAG;GACnB;GAEA,IAAI,SAAS,WAAW,GACtB,OAAO,UAAU;IAAE,SAAS;IAAQ;GAAQ,IAAI,EAAE,SAAS,UAAU;GAEvE,SAAS,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;GACzC,MAAM,aAAa,aAAa,SAAS;GACzC,OAAO;IACL,SAAS;IACT,QAAQ,SAAS,KAAK,QAAQ;KAC5B,MAAM,UAAU,MAAM,KAAK,QAAQ,WAAW,SAAS;KACvD,IAAI,aAAa,GAAG,QAAQ,aAAa;KACzC,OAAO;IACT,CAAC;GACH;EACF;EAEA,MAAM,YAAY,EAAE,WAAW,QAAQ,QAAQ,OAAO,eAAe;GACnE,MAAM,MAAM,MAAM,IAAI;GACtB,MAAM,YAAY,IAAI,KAAK,eAAe,IAAI,QAAQ,IAAI,KAAK;GAC/D,MAAM,aAAa,aAAa,SAAS;GACzC,MAAM,UAAoB,CAAC;GAC3B,MAAM,aAAuB,CAAC;GAC9B,KAAK,MAAM,EAAE,OAAO,aAAa,OAAO,UACrC,GAAG,MAAM,EAAE,QAAQ,EAAE,KACxB,GAAG;IACD,IAAI,SAAS,YAAY;KACvB,WAAW,KAAK,KAAK;KACrB;IACF;IACA,MAAM,MAAM,MAAM,WAAW,KAAK;IAClC,IAAI,CAAC,KAAK;IACV,IAAI,IAAI,eAAe,SAAS;KAC9B,WAAW,KAAK,IAAI,KAAK;KACzB;IACF;IACA,IACE,IAAI,gBAAgB,QACpB,IAAI,aAAa,QACjB,IAAI,gBAAgB,UACpB,IAAI,mBAAmB,QACvB,IAAI,iBAAiB,KACrB;KACA,IAAI,iBAAiB,IAAI,KAAK,SAAS;KACvC,QAAQ,KAAK,IAAI,KAAK;IACxB;GACF;GACA,OAAO;IAAE;IAAS;GAAW;EAC/B;EAEA,MAAM,gBAAgB,EAAE,WAAW,OAAO,SAAS,UAAU;GAC3D,IAAI,SAAS,aAAa,SAAS,GAAG,OAAO,EAAE,SAAS,aAAa;GACrE,MAAM,SAAS,KAAK,WAAW,KAAK;GACpC,MAAM,QAAQ;IAAE;IAAO;GAAQ;GAC/B,IAAI,OAAO,gBAAgB,MAAM;IAC/B,IAAI,OAAO,uBAAuB,SAChC,OAAO,EAAE,SAAS,aAAa;IAEjC,MAAM,eAAe,OAAO,KAAK,UAAU,MAAM,EAAE;IACnD,IACE,OAAO,qBAAqB,QAC5B,OAAO,iBAAiB,WAAW,aAAa,UAChD,OAAO,iBAAiB,MACrB,IAAI,WAAW,OAAO,aAAa,OACtC,GAEA,MAAM,IAAI,QACR,2BACA,2DACF;IAEF,MAAM,WAAW,cAAc,WAAW,MAAM;IAChD,IACE,SAAS,WAAW,OAAO,UAC3B,SAAS,MACN,QACC,IAAI,OAAO,UAAU,SAAS,IAAI,MAAM,YAAY,OACxD,GAEA,MAAM,IAAI,QACR,2BACA,2DACF;IAEF,OAAO;KACL,SAAS;KACT,QAAQ,iBAAiB,QAAQ,SAAS,IAAI,QAAQ,CAAC;IACzD;GACF;GACA,IACE,OAAO,iBAAiB,WACxB,OAAO,aAAa,QACpB,OAAO,gBAAgB,MAEvB,OAAO,EAAE,SAAS,aAAa;GAIjC,IADiB,cAAc,WAAW,MAC/B,CAAC,CAAC,SAAS,GACpB,MAAM,IAAI,QACR,2BACA,oDACF;GAEF,MAAM,MAAM,MAAM,IAAI;GACtB,MAAM,WAAW,OAAO,WAAW,QAAQ,KAAK,KAAK;GACrD,OAAO,cAAc,IAAI,KAAK,GAAG;GACjC,OAAO,qBAAqB;GAC5B,OAAO,mBAAmB,OAAO,KAAK,UAAU,MAAM,EAAE;GACxD,OAAO,cAAc;GACrB,OAAO,iBAAiB;GACxB,OAAO,WAAW,MAAM;GACxB,oBAAoB,WAAW,KAAK;GACpC,cAAc,WAAW,QAAQ;GACjC,OAAO;IAAE,SAAS;IAAa,QAAQ,SAAS,IAAI,QAAQ;GAAE;EAChE;EAEA,MAAM,YAAY,EAAE,WAAW,OAAO,SAAS,OAAO,eAAe;GACnE,IAAI,SAAS,aAAa,SAAS,GACjC,OAAO;IAAE,SAAS;IAAc,cAAc;GAAE;GAElD,MAAM,MAAM,KAAK,WAAW,KAAK;GACjC,IACE,IAAI,eACJ,IAAI,iBAAiB,WACpB,IAAI,gBAAgB,QAAQ,IAAI,sBAAsB,SAEvD,OAAO;IAAE,SAAS;IAAc,cAAc,IAAI;GAAa;GAEjE,IAAI,IAAI,sBAAsB,WAAW,IAAI,gBAAgB,MAC3D,OAAO;IACL,SAAS,IAAI,WAAW,kBAAkB;IAC1C,cAAc,IAAI;GACpB;GAEF,IAAI,IAAI,UACN,OAAO;IAAE,SAAS;IAAiB,cAAc,IAAI;GAAa;GAEpE,IAAI,gBAAgB;GACpB,IAAI,YAAY;GAChB,MAAM,WAAW,MAAM,IAAI;GAC3B,IAAI,eAAe,IAAI,KAAK,QAAQ;GACpC,IAAI,oBAAoB;GACxB,IAAI,cAAc;GAClB,IAAI,iBAAiB;GACrB,IAAI,IAAI,gBAAgB,aAAa;IACnC,IAAI,WAAW,IAAI,KAAK,QAAQ;IAChC,WAAW,SAAS,CAAC,CAAC,MAAM,OAAO,IAAI,KAAK;IAC5C,oBAAoB,WAAW,KAAK;IACpC,OAAO;KACL,SAAS;KACT,cAAc,IAAI;IACpB;GACF;GACA,OAAO;IAAE,SAAS;IAAU,cAAc,IAAI;GAAa;EAC7D;EAEA,MAAM,UAAU,WAAW,aAAa,cAAc;GACpD,OAAO,YACL,WACA,aACA,cAAc,cACd,cAAc,oBAChB;EACF;EAEA,MAAM,WAAW,UAAU,aAAa;GACtC,OAAO,SAAS,KAAK,YACnB,YACE,QAAQ,WACR,aACA,QAAQ,cACR,QAAQ,oBACV,CACF;EACF;EAEA,MAAM,aAAa,WAAW,aAAa,QAAQ;GACjD,MAAM,MAAM,YAAY,WAAW,WAAW;GAC9C,MAAM,aAAa,aAAa,SAAS;GACzC,KAAK,MAAM,SAAS,OAAO,UAAU,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,GAAG;IAChE,IAAI,MAAM,QAAQ,YAAY;IAC9B,MAAM,aAAa,cAAc,WAAW,aAAa,MAAM,KAAK;IACpE,KAAK,MAAM,cAAc,MAAM,mBAAmB,CAAC,GAAG;KACpD,MAAM,QAAQ,MAAM,WAAW,UAAU;KACzC,IACE,CAAC,SACD,MAAM,SAAS,cACf,MAAM,gBAAgB,QACtB,MAAM,aAAa,MAEnB;KAEF,MAAM,SAAS,YAAY,WAAW,UAAU;KAChD,IAAI,SAAS,aAAa,IAAI,MAAM;KACpC,IAAI,CAAC,QAAQ;MACX,yBAAS,IAAI,IAAI;MACjB,aAAa,IAAI,QAAQ,MAAM;KACjC;KACA,IAAI,CAAC,OAAO,IAAI,UAAU,GAAG;MAC3B,OAAO,IAAI,UAAU;MACrB,kBAAkB,IAChB,aACC,kBAAkB,IAAI,UAAU,KAAK,KAAK,CAC7C;KACF;IACF;IAEA,MAAM,UAAU,UAAU,IAAI,GAAG;IACjC,IAAI,CAAC,WAAW,MAAM,SAAS,QAAQ,OAAO;KAC5C,IACE,WACA,MAAM,QAAQ,QAAQ,SACtB,QAAQ,SAAS,YACjB;MACA,MAAM,oBAAoB,cACxB,WACA,aACA,QAAQ,KACV;MACA,KAAK,kBAAkB,IAAI,iBAAiB,KAAK,KAAK,GACpD,oBAAoB,IAAI,mBAAmB;OACzC,OAAO,QAAQ;OACf,OAAO,gBAAgB,QAAQ,KAAK;OACpC,WAAW,IAAI,KAAK,QAAQ,SAAS;MACvC,CAAC;KAEL;KACA,UAAU,IAAI,KAAK;MACjB,OAAO,MAAM;MACb,OAAO,gBAAgB,MAAM,KAAK;MAClC,WAAW,MAAM,IAAI;KACvB,CAAC;KACD,oBAAoB,OAAO,UAAU;IACvC,OAAO,IACL,MAAM,QAAQ,QAAQ,UACrB,kBAAkB,IAAI,UAAU,KAAK,KAAK,GAE3C,oBAAoB,IAAI,YAAY;KAClC,OAAO,MAAM;KACb,OAAO,gBAAgB,MAAM,KAAK;KAClC,WAAW,MAAM,IAAI;IACvB,CAAC;GAEL;EACF;EAEA,UAAU;GACR,MAAM,IAAI,IAAI,aAAa,QAAQ,MAAM;IACvC,IAAI,eAAe,aAAa,IAAI,EAAE;IACtC,IAAI,CAAC,cAAc;KACjB,+BAAe,IAAI,IAAI;KACvB,aAAa,IAAI,IAAI,YAAY;IACnC;IACA,IAAI,SAAS,aAAa,IAAI,WAAW;IACzC,IAAI,CAAC,QAAQ;KACX,yBAAS,IAAI,IAAI;KACjB,aAAa,IAAI,aAAa,MAAM;IACtC;IAIA,MAAM,cAAc,MAAM,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK;IAGjD,MAAM,UAAmC,gBAAgB;IACzD,IAAI,eAAe;IACnB,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,MAAM,GAAG;KAGnD,MAAM,WAAW,OAAO,IAAI,KAAK;KACjC,IAAI,YAAY,SAAS,KAAK,KAAK,IAAI;KACvC,IAAI,UAAU,MACZ,OAAO,OAAO,KAAK;UAEnB,OAAO,IAAI,OAAO;MAChB,OAAO,gBAAgB,KAAK;MAC5B,MAAM,KAAK;MACX,IAAI,IAAI,KAAK,KAAK,EAAE;MACpB,WAAW,IAAI,KAAK,WAAW;KACjC,CAAC;KAEH,QAAQ,SAAS,gBAAgB,KAAK;KACtC,gBAAgB;IAClB;IACA,IAAI,OAAO,SAAS,GAAG,aAAa,OAAO,WAAW;IACtD,IAAI,aAAa,SAAS,GAAG,aAAa,OAAO,EAAE;IAInD,IAAI,iBAAiB,GAAG;IACxB,MAAM,OAAO,oBAAoB,IAAI,EAAE;IACvC,IAAI,CAAC,MAAM;IACX,MAAM,QAAuB;KAC3B;KACA,QAAQ;KACR,MAAM,KAAK;KACX,IAAI,IAAI,KAAK,KAAK,EAAE;IACtB;IACA,KAAK,MAAM,YAAY,MAAM,SAAS,KAAK;GAC7C;GAEA,MAAM,KAAK,IAAI;IACb,MAAM,eAAe,aAAa,IAAI,EAAE;IACxC,IAAI,CAAC,cAAc,OAAO,CAAC;IAC3B,MAAM,MAAM,MAAM,IAAI;IACtB,MAAM,OAAsB,CAAC;IAC7B,KAAK,MAAM,CAAC,aAAa,WAAW,cAAc;KAChD,KAAK,MAAM,CAAC,OAAO,UAAU,QAAQ;MAGnC,IAAI,MAAM,aAAa,KAAK;OAC1B,OAAO,OAAO,KAAK;OACnB;MACF;MACA,KAAK,KAAK;OACR;OACA;OACA,OAAO,gBAAgB,MAAM,KAAK;OAClC,MAAM,MAAM;OACZ,IAAI,IAAI,KAAK,MAAM,EAAE;OACrB,WAAW,IAAI,KAAK,MAAM,SAAS;MACrC,CAAC;KACH;KACA,IAAI,OAAO,SAAS,GAAG,aAAa,OAAO,WAAW;IACxD;IACA,IAAI,aAAa,SAAS,GAAG,aAAa,OAAO,EAAE;IACnD,OAAO;GACT;GAEA,UAAU,IAAI,SAAS;IACrB,IAAI,OAAO,oBAAoB,IAAI,EAAE;IACrC,IAAI,CAAC,MAAM;KACT,uBAAO,IAAI,IAAI;KACf,oBAAoB,IAAI,IAAI,IAAI;IAClC;IACA,KAAK,IAAI,OAAO;IAChB,aAAa;KACX,KAAK,OAAO,OAAO;KACnB,IAAI,KAAK,SAAS,GAAG,oBAAoB,OAAO,EAAE;IACpD;GACF;EACF;EAEA,OAAO,WAAW,MAAM;GACtB,MAAM,aAAa,MAAM,cAAc;GACvC,OAAO,EACL,CAAC,OAAO,iBAAuC;IAC7C,IAAI,OAAO,KAAK,IAAI,YAAY,aAAa,SAAS,IAAI,CAAC;IAC3D,IAAI,OAA4B;IAChC,IAAI,SAAS;IAEb,MAAM,cAAoB;KACxB,OAAO;IACT;IACA,IAAI,OAAO,kBAAkB,IAAI,SAAS;IAC1C,IAAI,CAAC,MAAM;KACT,uBAAO,IAAI,IAAI;KACf,kBAAkB,IAAI,WAAW,IAAI;IACvC;IACA,KAAK,IAAI,KAAK;IACd,MAAM,aAAmB;KACvB,IAAI,QAAQ;KACZ,SAAS;KACT,KAAK,OAAO,KAAK;KACjB,IAAI,KAAK,SAAS,GAAG,kBAAkB,OAAO,SAAS;KACvD,OAAO;IACT;IAEA,OAAO;KACL,MAAM,OAAuC;MAC3C,IAAI;OACF,SAAS;QACP,IAAI,QAAQ,OAAO;SAAE,OAAO,KAAA;SAAW,MAAM;QAAK;QAClD,MAAM,MAAM,SAAS,IAAI,SAAS;QAElC,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI,GAAG,OAAO,IAAI,MAAM;QACnD,IAAI,KAAK;SACP,MAAM,QAAQ,SAAS,GAAG;SAC1B,OAAO,IAAI;SACX,OAAO;UAAE;UAAO,MAAM;SAAM;QAC9B;QAEA,MAAM,IAAI,SAAe,YAAY;SACnC,OAAO;QACT,CAAC;QACD,OAAO;OACT;MACF,SAAS,OAAO;OACd,KAAK;OACL,MAAM;MACR;KACF;KACA,MAAM,SAAyC;MAC7C,KAAK;MACL,OAAO;OAAE,OAAO,KAAA;OAAW,MAAM;MAAK;KACxC;IACF;GACF,EACF;EACF;CACF;AACF"}
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 {\n RANDOM_IDS,\n SYSTEM_CLOCK,\n type A2Store,\n type AppendEvent,\n type Clock,\n type Event,\n type EventCause,\n type IdSource,\n type PresenceRow,\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 lane: string | null\n processedAt: Date | null\n processedByAttempt: number | null\n returnedEventIds: string[] | null\n firstClaimedAt: Date | null\n lastClaimedAt: Date | null\n attemptCount: number\n claimHolder: string | null\n claimExpiresAt: Date | null\n failureCount: number\n lastFailedAt: Date | null\n lastFailedAttempt: number | null\n lastError: string | null\n failedAt: Date | null\n}\n\ntype PresenceEntry = {\n value: unknown\n seen: number\n at: Date\n expiresAt: Date\n}\n\ntype LaneQueue = { rows: Row[]; head: number }\ntype SessionDispatch = {\n ready: Map<number, Row>\n lanes: Map<string, LaneQueue>\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 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 lane: row.lane,\n processedAt: row.processedAt ? new Date(row.processedAt) : null,\n processedByAttempt: row.processedByAttempt,\n returnedEventIds: row.returnedEventIds ? [...row.returnedEventIds] : null,\n firstClaimedAt: row.firstClaimedAt ? new Date(row.firstClaimedAt) : null,\n lastClaimedAt: row.lastClaimedAt ? new Date(row.lastClaimedAt) : null,\n attemptCount: row.attemptCount,\n claimHolder: row.claimHolder,\n claimExpiresAt: row.claimExpiresAt ? new Date(row.claimExpiresAt) : null,\n failureCount: row.failureCount,\n lastFailedAt: row.lastFailedAt ? new Date(row.lastFailedAt) : null,\n lastFailedAttempt: row.lastFailedAttempt,\n lastError: row.lastError,\n failedAt: row.failedAt ? new Date(row.failedAt) : 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 snapshotKey = (sessionId: string, reducerName: string): string =>\n `${sessionId}\\u0000${reducerName}`\n\nconst eventPinKey = (sessionId: string, index: number): string =>\n `${sessionId}\\u0000${index}`\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 const dispatch = new Map<string, SessionDispatch>()\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 checkpointKey = (\n sessionId: string,\n reducerName: string,\n index: number,\n ): string => `${snapshotKey(sessionId, reducerName)}\\u0000${index}`\n\n const releaseSnapshotPins = (sessionId: string, eventIndex: number): void => {\n const pinKey = eventPinKey(sessionId, eventIndex)\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 const dispatchOf = (sessionId: string): SessionDispatch => {\n let state = dispatch.get(sessionId)\n if (!state) {\n state = { ready: new Map(), lanes: new Map() }\n dispatch.set(sessionId, state)\n }\n return state\n }\n\n const enqueue = (sessionId: string, row: Row): void => {\n if (row.processedAt !== null) return\n const state = dispatchOf(sessionId)\n if (row.lane === null) {\n state.ready.set(row.index, row)\n return\n }\n let queue = state.lanes.get(row.lane)\n if (!queue) {\n queue = { rows: [], head: 0 }\n state.lanes.set(row.lane, queue)\n }\n queue.rows.push(row)\n if (queue.rows.length === 1) state.ready.set(row.index, row)\n }\n\n const settle = (sessionId: string, row: Row): void => {\n const state = dispatchOf(sessionId)\n state.ready.delete(row.index)\n if (row.lane === null) return\n const queue = state.lanes.get(row.lane)!\n if (queue.rows[queue.head] !== row) return\n queue.head += 1\n if (queue.head === queue.rows.length) {\n state.lanes.delete(row.lane)\n return\n }\n const next = queue.rows[queue.head]!\n if (next.failedAt === null) state.ready.set(next.index, next)\n }\n\n const find = (sessionId: string, index: number): Row => {\n const row = rowAt(sessionId, index)\n if (!row) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n return row\n }\n\n const hasPending = (sessionId: string): boolean => {\n const log = sessions.get(sessionId)\n return (log?.rows ?? []).some(\n (row) => row.index > log!.clearIndex && row.processedAt === null,\n )\n }\n\n const appendResult = (sessionId: string, events: StoredEvent[]) => ({\n events,\n hasPending: hasPending(sessionId),\n })\n\n /** The attempt-currency fence shared by handler 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 parent = find(sessionId, cause.index)\n if (parent.attemptCount !== cause.attempt || parent.failedAt !== null) {\n throw supersededAttemptError(sessionId, cause)\n }\n }\n\n const claim = ({\n sessionId,\n row,\n holder,\n now,\n expiresAt,\n maxFailures,\n }: {\n sessionId: string\n row: Row\n holder: string\n now: Date\n expiresAt: Date\n maxFailures: number | undefined\n }): StoredEvent => {\n // A claim still held here lapsed: its attempt never finished.\n if (row.claimHolder !== null) {\n row.failureCount += 1\n if (maxFailures !== undefined && row.failureCount >= maxFailures) {\n // Advancing the attempt fences the lapsed one; nothing runs.\n row.attemptCount += 1\n row.failedAt = new Date(now)\n row.claimHolder = null\n row.claimExpiresAt = null\n dispatchOf(sessionId).ready.delete(row.index)\n releaseSnapshotPins(sessionId, row.index)\n return toStored(row)\n }\n }\n row.attemptCount += 1\n row.firstClaimedAt ??= new Date(now)\n row.lastClaimedAt = new Date(now)\n row.claimHolder = holder\n row.claimExpiresAt = new Date(expiresAt)\n return toStored(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 const insert = (\n sessionId: string,\n events: readonly AppendEvent[],\n now: Date,\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 lane: candidate.lane ?? null,\n processedAt: candidate.settled ? new Date(now) : null,\n processedByAttempt: null,\n returnedEventIds: null,\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 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 enqueue(sessionId, row)\n }\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 return {\n async append(sessionId, events, appendOptions) {\n if (events.length === 0) return appendResult(sessionId, [])\n\n // Batch-scoped idempotency (a2-implementation.md §3): all ids\n // already present → lost-ack retry, return the original rows;\n // some present → the caller mixed sent and fresh events.\n const existing = checkBatchIds(sessionId, events)\n if (existing.length > 0) {\n if (existing.length === events.length) {\n return appendResult(\n sessionId,\n idempotentReplay(events, existing.map(toStored)),\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 handler append commits only while\n // its causal attempt is still the parent's latest (store.ts `append`).\n for (const event of events) {\n if (event.cause) assertCurrentCause(sessionId, event.cause)\n }\n\n const now = clock.now()\n const inserted = insert(sessionId, events, now)\n notifyStreams(sessionId, inserted)\n return appendResult(sessionId, inserted.map(toStored))\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, settled: true }],\n clock.now(),\n )\n logOf(sessionId).clearIndex = marker!.index\n // Every row is at or below the marker, so no dispatch state survives.\n dispatch.delete(sessionId)\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 releaseSnapshotPins(sessionId, row.index)\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 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 excludeIndexes,\n maxFailures,\n }) {\n const claimedAt = clock.now()\n const now = claimedAt.getTime()\n const expiresAt = new Date(expiresAtMs ?? now + ttlMs)\n const excluded = new Set(excludeIndexes ?? [])\n const eligible: Row[] = []\n let retryAt: Date | null = null\n\n for (const row of dispatchOf(sessionId).ready.values()) {\n if (row.failedAt !== null || excluded.has(row.index)) continue\n if (row.claimExpiresAt && row.claimExpiresAt.getTime() > now) {\n if (!retryAt || row.claimExpiresAt < retryAt) {\n retryAt = new Date(row.claimExpiresAt)\n }\n continue\n }\n eligible.push(row)\n }\n\n if (eligible.length === 0) {\n return retryAt ? { outcome: 'busy', retryAt } : { outcome: 'settled' }\n }\n eligible.sort((a, b) => a.index - b.index)\n const clearIndex = clearIndexOf(sessionId)\n return {\n outcome: 'claimed',\n events: eligible.map((row) => {\n const claimed = claim({\n sessionId,\n row,\n holder,\n now: claimedAt,\n expiresAt,\n maxFailures,\n })\n if (clearIndex > 0) claimed.clearIndex = clearIndex\n return claimed\n }),\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: number[] = []\n const superseded: number[] = []\n for (const { index, attempt } of claims.toSorted(\n (a, b) => a.index - b.index,\n )) {\n if (index <= clearIndex) {\n superseded.push(index)\n continue\n }\n const row = rowAt(sessionId, index)\n if (!row) continue\n if (row.attemptCount > attempt) {\n superseded.push(row.index)\n continue\n }\n if (\n row.processedAt === null &&\n row.failedAt === null &&\n row.claimHolder === holder &&\n row.claimExpiresAt !== null &&\n row.claimExpiresAt > now\n ) {\n row.claimExpiresAt = new Date(expiresAt)\n renewed.push(row.index)\n }\n }\n return { renewed, superseded }\n },\n\n async completeAttempt({ sessionId, index, attempt, events }) {\n if (index <= clearIndexOf(sessionId)) return { outcome: 'superseded' }\n const parent = find(sessionId, index)\n const cause = { index, attempt }\n if (parent.processedAt !== null) {\n if (parent.processedByAttempt !== attempt) {\n return { outcome: 'superseded' }\n }\n const requestedIds = events.map((event) => event.id)\n if (\n parent.returnedEventIds === null ||\n parent.returnedEventIds.length !== requestedIds.length ||\n parent.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?.index !== index || 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: idempotentReplay(events, existing.map(toStored)),\n }\n }\n if (\n parent.attemptCount !== attempt ||\n parent.failedAt !== null ||\n parent.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 now = clock.now()\n const inserted = insert(sessionId, events, now, cause)\n parent.processedAt = new Date(now)\n parent.processedByAttempt = attempt\n parent.returnedEventIds = events.map((event) => event.id)\n parent.claimHolder = null\n parent.claimExpiresAt = null\n settle(sessionId, parent)\n releaseSnapshotPins(sessionId, index)\n notifyStreams(sessionId, inserted)\n return { outcome: 'completed', events: inserted.map(toStored) }\n },\n\n async failAttempt({\n sessionId,\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 row = find(sessionId, index)\n if (\n row.processedAt ||\n row.attemptCount !== attempt ||\n (row.claimHolder === null && row.lastFailedAttempt !== attempt)\n ) {\n return { outcome: 'superseded', failureCount: row.failureCount }\n }\n if (row.lastFailedAttempt === attempt && row.claimHolder === null) {\n return {\n outcome: row.failedAt ? 'dead_lettered' : 'failed',\n failureCount: row.failureCount,\n }\n }\n if (row.failedAt) {\n return { outcome: 'dead_lettered', failureCount: row.failureCount }\n }\n row.failureCount += 1\n row.lastError = error\n const failedAt = clock.now()\n row.lastFailedAt = new Date(failedAt)\n row.lastFailedAttempt = attempt\n row.claimHolder = null\n row.claimExpiresAt =\n retryAfterMs === undefined || row.failureCount >= maxFailures\n ? null\n : new Date(failedAt.getTime() + retryAfterMs)\n if (row.failureCount >= maxFailures) {\n row.failedAt = new Date(failedAt)\n dispatchOf(sessionId).ready.delete(row.index)\n releaseSnapshotPins(sessionId, index)\n return {\n outcome: 'dead_lettered',\n failureCount: row.failureCount,\n }\n }\n return { outcome: 'failed', failureCount: row.failureCount }\n },\n\n async deadLetters(sessionId) {\n const log = sessions.get(sessionId)\n return (log?.rows ?? [])\n .filter(\n (row) =>\n row.index > log!.clearIndex &&\n row.processedAt === null &&\n row.failedAt !== null,\n )\n .map(toStored)\n },\n\n async redrive({ sessionId, index, action }) {\n const row =\n index > clearIndexOf(sessionId) ? rowAt(sessionId, index) : undefined\n if (!row || row.processedAt !== null || row.failedAt === null) {\n return { redriven: false, hasPending: hasPending(sessionId) }\n }\n if (action === 'skip') {\n row.processedAt = clock.now()\n settle(sessionId, row)\n } else {\n row.failedAt = null\n row.failureCount = 0\n const state = dispatchOf(sessionId)\n const queue = row.lane === null ? undefined : state.lanes.get(row.lane)\n if (row.lane === null || queue?.rows[queue.head] === row) {\n state.ready.set(row.index, row)\n }\n }\n return { redriven: true, hasPending: hasPending(sessionId) }\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 eventIndex of write.pinEventIndexes ?? []) {\n const event = rowAt(sessionId, eventIndex)\n if (\n !event ||\n event.index <= clearIndex ||\n event.processedAt !== null ||\n event.failedAt !== null\n ) {\n continue\n }\n const pinKey = eventPinKey(sessionId, eventIndex)\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,4 +1,4 @@
1
- import { i as POLL_TIMINGS } from "./internal-CbVvEwad.js";
1
+ import { i as POLL_TIMINGS } from "./internal-CscSY7K_.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-4_y8lETm.js.map
152
+ //# sourceMappingURL=store-polling-7WJV0YWP.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-polling-4_y8lETm.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"}
1
+ {"version":3,"file":"store-polling-7WJV0YWP.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"}
@@ -1,4 +1,4 @@
1
- import { r as Clock, s as IdSource, t as A2Store } from "./store-Gpkq7WBo.js";
1
+ import { r as Clock, s as IdSource, t as A2Store } from "./store-CyLUy86v.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":";;;KAuDY;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;;iBAqQK,SAAS,UAAS,uBAA4B"}
1
+ {"version":3,"file":"store-postgres.d.ts","names":[],"sources":["../src/store-postgres.ts"],"mappings":";;;KAuDY;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;;iBAiSK,SAAS,UAAS,uBAA4B"}
@@ -1,12 +1,12 @@
1
1
  import { i as A2Error, r as sessionClearedError, s as supersededAttemptError, t as CLEAR_EVENT_TYPE } from "./session-clear-DGI4jTQ5.js";
2
- import { g as nullProtoRecord, r as NOTIFY_TIMINGS } from "./internal-CbVvEwad.js";
3
- import { n as pollingStream, t as defaultSleep } from "./store-polling-4_y8lETm.js";
2
+ import { _ as nullProtoRecord, r as NOTIFY_TIMINGS } from "./internal-CscSY7K_.js";
3
+ import { n as pollingStream, t as defaultSleep } from "./store-polling-7WJV0YWP.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 { t as idempotentReplay } from "./idempotent-replay-JdgMQRRm.js";
7
7
  import { n as SYSTEM_CLOCK, t as RANDOM_IDS } from "./store-N8PXxDAS.js";
8
8
  import { t as decodeReturnedEventIds } from "./store-codec-DTG0Ftek.js";
9
- import { t as pollPresence } from "./store-presence-polling-Q1C-k2dd.js";
9
+ import { t as pollPresence } from "./store-presence-polling-B1ixeqIS.js";
10
10
  import { createHash } from "node:crypto";
11
11
  //#region src/postgres-notifications.ts
12
12
  const postgresNotificationChannel = ({ kind, sessionId }) => `a2_${kind}_${createHash("md5").update(sessionId).digest("hex")}`;
@@ -694,6 +694,19 @@ const createListener = ({ pg, connectionString }) => postgresNotifications(async
694
694
  }
695
695
  };
696
696
  });
697
+ /** A dead-lettered event's snapshot pins, and history no pin keeps. */
698
+ const releasePins = async ({ query, sessionId, index }) => {
699
+ await query(`delete from a2_snapshot_pins
700
+ where session_id = $1 and event_index = $2`, [sessionId, index]);
701
+ await query(`delete from a2_snapshot_history history
702
+ where history.session_id = $1
703
+ and not exists (
704
+ select 1 from a2_snapshot_pins pin
705
+ where pin.session_id = history.session_id
706
+ and pin.reducer_name = history.reducer_name
707
+ and pin.up_to_index = history.up_to_index
708
+ )`, [sessionId]);
709
+ };
697
710
  function postgres(options = {}) {
698
711
  const clock = options.clock ?? SYSTEM_CLOCK;
699
712
  const generateId = options.ids ?? RANDOM_IDS;
@@ -1095,10 +1108,12 @@ function postgres(options = {}) {
1095
1108
  return rows.map(toEvent);
1096
1109
  });
1097
1110
  },
1098
- async claimAvailable({ sessionId, holder, ttlMs, expiresAtMs, excludeIndexes = [] }) {
1111
+ async claimAvailable({ sessionId, holder, ttlMs, expiresAtMs, excludeIndexes = [], maxFailures }) {
1099
1112
  return wrap(() => withTx(async (query) => {
1100
1113
  const now = clock.now().getTime();
1101
1114
  const expiresAt = expiresAtMs ?? now + ttlMs;
1115
+ const exhausted = `(claimed.claim_holder is not null
1116
+ and claimed.failure_count + 1 >= $6::int)`;
1102
1117
  const { rows } = await query(`with eligible as materialized (
1103
1118
  select event.idx
1104
1119
  from a2_events event
@@ -1113,10 +1128,16 @@ function postgres(options = {}) {
1113
1128
  )
1114
1129
  update a2_events claimed set
1115
1130
  attempt_count = claimed.attempt_count + 1,
1116
- first_claimed_at = coalesce(claimed.first_claimed_at, $2),
1117
- last_claimed_at = $2,
1118
- claim_holder = $3,
1119
- claim_expires_at = $4
1131
+ failure_count = claimed.failure_count
1132
+ + case when claimed.claim_holder is null then 0 else 1 end,
1133
+ failed_at = case when ${exhausted} then $2::bigint else claimed.failed_at end,
1134
+ first_claimed_at = case when ${exhausted}
1135
+ then claimed.first_claimed_at
1136
+ else coalesce(claimed.first_claimed_at, $2::bigint) end,
1137
+ last_claimed_at = case when ${exhausted}
1138
+ then claimed.last_claimed_at else $2::bigint end,
1139
+ claim_holder = case when ${exhausted} then null else $3::text end,
1140
+ claim_expires_at = case when ${exhausted} then null else $4::bigint end
1120
1141
  from eligible
1121
1142
  where claimed.session_id = $1 and claimed.idx = eligible.idx
1122
1143
  returning claimed.*, ${clearIndexOf("$1")} as clear_index`, [
@@ -1124,8 +1145,14 @@ function postgres(options = {}) {
1124
1145
  now,
1125
1146
  holder,
1126
1147
  expiresAt,
1127
- [...excludeIndexes]
1148
+ [...excludeIndexes],
1149
+ maxFailures ?? null
1128
1150
  ]);
1151
+ for (const row of rows) if (row["failed_at"] !== null) await releasePins({
1152
+ query,
1153
+ sessionId,
1154
+ index: asNumber(row["idx"])
1155
+ });
1129
1156
  if (rows.length > 0) return {
1130
1157
  outcome: "claimed",
1131
1158
  events: rows.map((row) => {
@@ -1142,7 +1169,12 @@ function postgres(options = {}) {
1142
1169
  and failed_at is null
1143
1170
  and lane_ready
1144
1171
  and idx > ${clearIndexOf("$1")}
1145
- and claim_expires_at > $2`, [sessionId, now]);
1172
+ and claim_expires_at > $2
1173
+ and not (idx = any($3::bigint[]))`, [
1174
+ sessionId,
1175
+ now,
1176
+ [...excludeIndexes]
1177
+ ]);
1146
1178
  const retryAt = activeRows[0]?.["retry_at"];
1147
1179
  return retryAt === null || retryAt === void 0 ? { outcome: "settled" } : {
1148
1180
  outcome: "busy",
@@ -1336,7 +1368,8 @@ function postgres(options = {}) {
1336
1368
  };
1337
1369
  }));
1338
1370
  },
1339
- async failAttempt({ sessionId, index, attempt, error, maxFailures }) {
1371
+ async failAttempt({ sessionId, index, attempt, error, maxFailures, retryAfterMs }) {
1372
+ const now = clock.now().getTime();
1340
1373
  return wrap(() => withTx(async (query) => {
1341
1374
  const { rows } = await query(`with current as materialized (
1342
1375
  select e.* from a2_events e
@@ -1354,7 +1387,10 @@ function postgres(options = {}) {
1354
1387
  else e.failed_at
1355
1388
  end,
1356
1389
  claim_holder = null,
1357
- claim_expires_at = null
1390
+ claim_expires_at = case
1391
+ when e.failure_count + 1 >= $5 then null
1392
+ else $7::bigint
1393
+ end
1358
1394
  from current
1359
1395
  where e.session_id = $1
1360
1396
  and e.idx = $2
@@ -1393,7 +1429,8 @@ function postgres(options = {}) {
1393
1429
  attempt,
1394
1430
  error,
1395
1431
  maxFailures,
1396
- clock.now().getTime()
1432
+ now,
1433
+ retryAfterMs === void 0 ? null : now + retryAfterMs
1397
1434
  ]);
1398
1435
  const row = rows[0];
1399
1436
  if (row?.["outcome"] === "cleared") return {
@@ -1411,24 +1448,68 @@ function postgres(options = {}) {
1411
1448
  };
1412
1449
  throw new TypeError(`no event at index ${index} in session '${sessionId}'`);
1413
1450
  }
1414
- if (row["outcome"] === "dead_lettered") {
1415
- await query(`delete from a2_snapshot_pins
1416
- where session_id = $1 and event_index = $2`, [sessionId, index]);
1417
- await query(`delete from a2_snapshot_history history
1418
- where history.session_id = $1
1419
- and not exists (
1420
- select 1 from a2_snapshot_pins pin
1421
- where pin.session_id = history.session_id
1422
- and pin.reducer_name = history.reducer_name
1423
- and pin.up_to_index = history.up_to_index
1424
- )`, [sessionId]);
1425
- }
1451
+ if (row["outcome"] === "dead_lettered") await releasePins({
1452
+ query,
1453
+ sessionId,
1454
+ index
1455
+ });
1426
1456
  return {
1427
1457
  outcome: row["outcome"],
1428
1458
  failureCount: asNumber(row["failure_count"])
1429
1459
  };
1430
1460
  }));
1431
1461
  },
1462
+ async deadLetters(sessionId) {
1463
+ return wrap(async () => {
1464
+ const { rows } = await (await client()).query(`select * from a2_events
1465
+ where session_id = $1
1466
+ and processed_at is null
1467
+ and failed_at is not null
1468
+ and idx > ${clearIndexOf("$1")}
1469
+ order by idx`, [sessionId]);
1470
+ return rows.map(toStored);
1471
+ });
1472
+ },
1473
+ async redrive({ sessionId, index, action }) {
1474
+ return wrap(() => withTx(async (query) => {
1475
+ if (action === "skip") await query("select pg_advisory_xact_lock(hashtext($1))", [sessionId]);
1476
+ const { rows } = await query(`select lane from a2_events
1477
+ where session_id = $1 and idx = $2
1478
+ and idx > ${clearIndexOf("$1")}
1479
+ and processed_at is null
1480
+ and failed_at is not null
1481
+ for update`, [sessionId, index]);
1482
+ const row = rows[0];
1483
+ if (row && action === "retry") await query(`update a2_events set failed_at = null, failure_count = 0
1484
+ where session_id = $1 and idx = $2`, [sessionId, index]);
1485
+ else if (row) {
1486
+ await query(`update a2_events set processed_at = $3, lane_ready = false
1487
+ where session_id = $1 and idx = $2`, [
1488
+ sessionId,
1489
+ index,
1490
+ clock.now().getTime()
1491
+ ]);
1492
+ const lane = row["lane"] ?? null;
1493
+ if (lane !== null) await query(`update a2_events set lane_ready = true
1494
+ where session_id = $1
1495
+ and idx = (
1496
+ select idx from a2_events
1497
+ where session_id = $1 and lane = $2 and processed_at is null
1498
+ and idx > ${clearIndexOf("$1")}
1499
+ order by idx limit 1
1500
+ )`, [sessionId, lane]);
1501
+ }
1502
+ const { rows: status } = await query(`select exists(
1503
+ select 1 from a2_events
1504
+ where session_id = $1 and processed_at is null
1505
+ and idx > ${clearIndexOf("$1")}
1506
+ ) as has_pending`, [sessionId]);
1507
+ return {
1508
+ redriven: row !== void 0,
1509
+ hasPending: status[0]?.["has_pending"] === true
1510
+ };
1511
+ }));
1512
+ },
1432
1513
  async readState(sessionId, reducerName, stateOptions) {
1433
1514
  return wrap(async () => (await readStateBatch([{
1434
1515
  sessionId,