experimental-a2 0.16.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +352 -0
- package/dist/{actor-ohPC-81x.d.ts → actor-DBbe5xeE.d.ts} +21 -11
- package/dist/actor-DBbe5xeE.d.ts.map +1 -0
- package/dist/actor-client.d.ts +2 -5
- package/dist/actor-client.d.ts.map +1 -1
- package/dist/actor-client.js +13 -4
- package/dist/actor-client.js.map +1 -1
- package/dist/actor-react.d.ts +5 -3
- package/dist/actor-react.d.ts.map +1 -1
- package/dist/actor-react.js +20 -10
- package/dist/actor-react.js.map +1 -1
- package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
- package/dist/actor-shared-t7w8xIKP.js.map +1 -0
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +42 -23
- package/dist/actor.js.map +1 -1
- package/dist/{ai-DgOBltJ_.js → ai-UrWfKeuv.js} +621 -216
- package/dist/ai-UrWfKeuv.js.map +1 -0
- package/dist/ai-client.d.ts +27 -0
- package/dist/ai-client.d.ts.map +1 -0
- package/dist/ai-client.js +29 -0
- package/dist/ai-client.js.map +1 -0
- package/dist/ai-react.d.ts +96 -0
- package/dist/ai-react.d.ts.map +1 -0
- package/dist/ai-react.js +142 -0
- package/dist/ai-react.js.map +1 -0
- package/dist/ai-server.browser.js +2 -2
- package/dist/ai-server.browser.js.map +1 -1
- package/dist/ai-server.d.ts +80 -36
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +432 -258
- package/dist/ai-server.js.map +1 -1
- package/dist/{ai-CrEf6p_W.d.ts → ai-vpGtcaCB.d.ts} +114 -100
- package/dist/ai-vpGtcaCB.d.ts.map +1 -0
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/{client-BYzHjkwU.js → client-COaiaDub.js} +447 -80
- package/dist/client-COaiaDub.js.map +1 -0
- package/dist/{client-CzyacQpJ.d.ts → client-qRnRUzBS.d.ts} +14 -13
- package/dist/client-qRnRUzBS.d.ts.map +1 -0
- package/dist/client.d.ts +4 -2
- package/dist/client.js +1 -1
- package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
- package/dist/contract-BYKASlCV.js.map +1 -0
- package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
- package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-BrLso5x_.d.ts} +7 -3
- package/dist/initial-snapshot-BrLso5x_.d.ts.map +1 -0
- package/dist/{internal-Dq2qYxou.js → internal-CbVvEwad.js} +23 -2
- package/dist/internal-CbVvEwad.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/react-BbJ-g6Uu.d.ts +86 -0
- package/dist/react-BbJ-g6Uu.d.ts.map +1 -0
- package/dist/react.d.ts +2 -122
- package/dist/react.js +107 -100
- package/dist/react.js.map +1 -1
- package/dist/reducer-BAXc8JyF.js +87 -0
- package/dist/reducer-BAXc8JyF.js.map +1 -0
- package/dist/{reducer-otzHjuJj.d.ts → reducer-BYu6DT7h.d.ts} +45 -29
- package/dist/reducer-BYu6DT7h.d.ts.map +1 -0
- package/dist/reducer-runtime-DQwz_J59.js +28 -0
- package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +3 -3
- package/dist/scheduler-qstash.d.ts.map +1 -1
- package/dist/scheduler-qstash.js +33 -10
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
- package/dist/scheduler-task-X1SToDjP.js.map +1 -0
- package/dist/scheduler-vercel.d.ts +4 -9
- package/dist/scheduler-vercel.d.ts.map +1 -1
- package/dist/scheduler-vercel.js +25 -44
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-DbD7IJVK.d.ts → server-CGM8WzRG.d.ts} +87 -26
- package/dist/server-CGM8WzRG.d.ts.map +1 -0
- package/dist/{server-BD5ckxZb.js → server-Rj64fNvX.js} +854 -202
- package/dist/server-Rj64fNvX.js.map +1 -0
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
- package/dist/session-clear-DGI4jTQ5.js.map +1 -0
- package/dist/session-clear-Dshpuhff.d.ts +12 -0
- package/dist/session-clear-Dshpuhff.d.ts.map +1 -0
- package/dist/{store-DyZM6fS5.d.ts → store-Gpkq7WBo.d.ts} +44 -3
- package/dist/store-Gpkq7WBo.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +182 -69
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-CmxUbV93.js → store-polling-4_y8lETm.js} +21 -4
- package/dist/store-polling-4_y8lETm.js.map +1 -0
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +399 -108
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-Q1C-k2dd.js} +3 -3
- package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-Q1C-k2dd.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.d.ts.map +1 -1
- package/dist/store-redis-http.js +293 -11
- package/dist/store-redis-http.js.map +1 -1
- package/dist/store-redis-notify-tqWNnMRu.js +2374 -0
- package/dist/store-redis-notify-tqWNnMRu.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +11 -11
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +166 -32
- package/dist/store-sqlite.js.map +1 -1
- package/dist/stream-buffer-Cx7AAPGq.js +23 -0
- package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
- package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-cZH-cibj.d.ts} +2 -2
- package/dist/telemetry-cZH-cibj.d.ts.map +1 -0
- package/dist/{wire-BO5wWCb1.js → wire-DjfHOvdW.js} +132 -10
- package/dist/wire-DjfHOvdW.js.map +1 -0
- package/docs/01-quickstart.mdx +2 -2
- package/docs/actors/01-introduction.mdx +4 -0
- package/docs/actors/04-routes.mdx +14 -1
- package/docs/concepts/01-contracts.mdx +95 -0
- package/docs/concepts/04-state.mdx +3 -3
- package/docs/guides/01-timers.mdx +22 -14
- package/docs/guides/02-cancellation.mdx +1 -1
- package/docs/guides/03-react.mdx +268 -328
- package/docs/guides/04-local-first.mdx +32 -73
- package/docs/guides/05-production.mdx +40 -27
- package/docs/guides/06-ai-agents.mdx +671 -581
- package/docs/guides/09-presence.mdx +3 -7
- package/docs/guides/10-transports.mdx +5 -5
- package/docs/index.mdx +10 -18
- package/docs/reference/01-api.mdx +656 -194
- package/docs/reference/02-errors.mdx +2 -1
- package/examples/playground/DEPLOY.md +52 -6
- package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
- package/examples/playground/app/a0/[appId]/page.tsx +48 -0
- package/examples/playground/app/a0/a0.css +818 -0
- package/examples/playground/app/a0/app-list.tsx +211 -0
- package/examples/playground/app/a0/app-view.tsx +55 -0
- package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
- package/examples/playground/app/a0/apps/[appId]/route.ts +23 -0
- package/examples/playground/app/a0/apps.test.ts +116 -0
- package/examples/playground/app/a0/apps.ts +87 -0
- package/examples/playground/app/a0/chat.tsx +364 -0
- package/examples/playground/app/a0/columns.tsx +109 -0
- package/examples/playground/app/a0/context-menu.tsx +116 -0
- package/examples/playground/app/a0/cookies.ts +46 -0
- package/examples/playground/app/a0/crash-on-request.ts +72 -0
- package/examples/playground/app/a0/crashes/route.ts +3 -0
- package/examples/playground/app/a0/crashes.test.ts +33 -0
- package/examples/playground/app/a0/crashes.ts +28 -0
- package/examples/playground/app/a0/devtools.tsx +164 -0
- package/examples/playground/app/a0/events/route.ts +6 -0
- package/examples/playground/app/a0/flags.ts +30 -0
- package/examples/playground/app/a0/layout.tsx +38 -0
- package/examples/playground/app/a0/list/route.ts +10 -0
- package/examples/playground/app/a0/model.test.ts +56 -0
- package/examples/playground/app/a0/model.ts +58 -0
- package/examples/playground/app/a0/page.tsx +14 -0
- package/examples/playground/app/a0/preview-pane.tsx +145 -0
- package/examples/playground/app/a0/preview.test.ts +53 -0
- package/examples/playground/app/a0/preview.ts +26 -0
- package/examples/playground/app/a0/sandbox-log.ts +69 -0
- package/examples/playground/app/a0/sandbox.ts +191 -0
- package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
- package/examples/playground/app/a0/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/server.ts +158 -0
- package/examples/playground/app/a0/session.ts +47 -0
- package/examples/playground/app/a0/shell.tsx +150 -0
- package/examples/playground/app/a0/short/events/route.ts +9 -0
- package/examples/playground/app/a0/splitter.tsx +120 -0
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
- package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
- package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
- package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
- package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
- package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
- package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
- package/examples/playground/app/agent/events/route.ts +5 -2
- package/examples/playground/app/agent/ingress.test.ts +98 -0
- package/examples/playground/app/agent/model-input.test.tsx +58 -0
- package/examples/playground/app/agent/model-input.tsx +115 -0
- package/examples/playground/app/agent/model.test.ts +73 -0
- package/examples/playground/app/agent/model.ts +58 -26
- package/examples/playground/app/agent/new-agent-session.tsx +37 -19
- package/examples/playground/app/agent/page.tsx +4 -4
- package/examples/playground/app/agent/server.ts +15 -14
- package/examples/playground/app/agent/session.ts +5 -6
- package/examples/playground/app/agent/telemetry.test.ts +168 -0
- package/examples/playground/app/agent/telemetry.ts +30 -0
- package/examples/playground/app/agent/trace.test.ts +23 -0
- package/examples/playground/app/agent/trace.ts +18 -0
- package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +11 -11
- package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
- package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
- package/examples/playground/app/canvas/model.ts +53 -53
- package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
- package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
- package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
- package/examples/playground/app/chat/[chatId]/session.ts +2 -19
- package/examples/playground/app/chat/agent/route.ts +9 -0
- package/examples/playground/app/chat/model.test.ts +46 -119
- package/examples/playground/app/chat/model.ts +82 -128
- package/examples/playground/app/chat/scheduler/route.ts +2 -2
- package/examples/playground/app/chat/server.test.ts +108 -0
- package/examples/playground/app/chat/server.ts +69 -73
- package/examples/playground/app/components/activity-feed.tsx +30 -1
- package/examples/playground/app/components/activity-history.test.tsx +35 -0
- package/examples/playground/app/components/demo-icons.tsx +89 -0
- package/examples/playground/app/components/demos.tsx +33 -0
- package/examples/playground/app/components/fast-link.test.tsx +58 -0
- package/examples/playground/app/components/fast-link.tsx +55 -0
- package/examples/playground/app/components/site-header.tsx +96 -0
- package/examples/playground/app/counter/counter-client.tsx +3 -3
- package/examples/playground/app/counter/model.test.ts +3 -3
- package/examples/playground/app/counter/model.ts +11 -21
- package/examples/playground/app/counter/page.tsx +2 -15
- package/examples/playground/app/counter/session.ts +2 -2
- package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
- package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
- package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
- package/examples/playground/app/documents/[documentId]/session.ts +2 -2
- package/examples/playground/app/documents/model.ts +27 -27
- package/examples/playground/app/globals.css +420 -207
- package/examples/playground/app/initial-state.test.tsx +54 -0
- package/examples/playground/app/layout.tsx +7 -23
- package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
- package/examples/playground/app/orders/[orderId]/session.ts +2 -2
- package/examples/playground/app/orders/model.ts +49 -49
- package/examples/playground/app/page.tsx +13 -102
- package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
- package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
- package/examples/playground/app/recovery/model.ts +30 -30
- package/examples/playground/instrumentation.ts +5 -0
- package/examples/playground/package.json +10 -2
- package/examples/playground/scripts/a0-template.ts +77 -0
- package/examples/playground/vercel.json +18 -0
- package/package.json +3 -1
- package/src/actor-client.ts +16 -4
- package/src/actor-react.ts +25 -11
- package/src/actor-shared.ts +21 -6
- package/src/actor.ts +76 -31
- package/src/ai-agent-actions.ts +219 -0
- package/src/ai-agent.ts +2343 -0
- package/src/ai-app-state.ts +91 -0
- package/src/ai-client-state.ts +6 -3
- package/src/ai-client.ts +62 -0
- package/src/ai-context-schema.ts +25 -5
- package/src/ai-context.ts +90 -40
- package/src/ai-control-server.ts +92 -72
- package/src/ai-coordinator.ts +23 -22
- package/src/ai-message-base.ts +209 -0
- package/src/ai-message-projection.ts +262 -64
- package/src/ai-progress-batches.ts +26 -0
- package/src/ai-projector.ts +95 -14
- package/src/ai-react.ts +146 -0
- package/src/ai-server.browser.ts +2 -2
- package/src/ai-server.ts +20 -1964
- package/src/ai-stored-state.ts +48 -163
- package/src/ai.ts +339 -155
- package/src/bootstrap-stream.ts +72 -0
- package/src/bounded-cache.ts +55 -0
- package/src/client-render-session.ts +49 -0
- package/src/client.ts +688 -151
- package/src/contract.ts +92 -42
- package/src/deterministic-id.ts +37 -0
- package/src/equal-json.ts +32 -0
- package/src/errors.ts +13 -1
- package/src/index.ts +5 -1
- package/src/initial-snapshot.ts +39 -0
- package/src/internal.ts +45 -0
- package/src/postgres-notification-scope.ts +14 -3
- package/src/react.ts +171 -206
- package/src/redis-http-reply.ts +256 -0
- package/src/redis-http-stream.ts +73 -0
- package/src/redis-http-subscriptions.ts +13 -5
- package/src/reducer-runtime.ts +52 -0
- package/src/reducer.ts +92 -10
- package/src/scheduler-qstash.ts +49 -20
- package/src/scheduler-task.ts +34 -2
- package/src/scheduler-vercel.ts +32 -84
- package/src/server-fetch.ts +139 -35
- package/src/server.ts +957 -237
- package/src/session-clear.ts +23 -0
- package/src/session-socket.ts +154 -49
- package/src/sse.ts +71 -4
- package/src/store-memory.ts +220 -78
- package/src/store-polling.ts +18 -4
- package/src/store-postgres.ts +461 -114
- package/src/store-redis-core.ts +1260 -434
- package/src/store-redis-http.ts +18 -2
- package/src/store-redis-notify.ts +260 -85
- package/src/store-redis.ts +19 -9
- package/src/store-sqlite.ts +259 -45
- package/src/store.ts +46 -1
- package/src/stream-buffer.ts +43 -0
- package/src/telemetry.ts +13 -1
- package/src/wire.ts +241 -13
- package/dist/actor-ohPC-81x.d.ts.map +0 -1
- package/dist/actor-shared-B5tJfzt-.js.map +0 -1
- package/dist/ai-CrEf6p_W.d.ts.map +0 -1
- package/dist/ai-DgOBltJ_.js.map +0 -1
- package/dist/client-BYzHjkwU.js.map +0 -1
- package/dist/client-CzyacQpJ.d.ts.map +0 -1
- package/dist/contract-CKRg_E4q.js.map +0 -1
- package/dist/errors-DCk6ch5n.js.map +0 -1
- package/dist/errors-DvhSXnxk.d.ts.map +0 -1
- package/dist/internal-Dq2qYxou.js.map +0 -1
- package/dist/react.d.ts.map +0 -1
- package/dist/reducer-DEMjEY_O.js +0 -29
- package/dist/reducer-DEMjEY_O.js.map +0 -1
- package/dist/reducer-otzHjuJj.d.ts.map +0 -1
- package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
- package/dist/server-BD5ckxZb.js.map +0 -1
- package/dist/server-DbD7IJVK.d.ts.map +0 -1
- package/dist/store-DyZM6fS5.d.ts.map +0 -1
- package/dist/store-polling-CmxUbV93.js.map +0 -1
- package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
- package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
- package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
- package/dist/validate-XKT4FSNn.js +0 -30
- package/dist/validate-XKT4FSNn.js.map +0 -1
- package/dist/wire-BO5wWCb1.js.map +0 -1
- package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
- package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
- package/examples/playground/app/agent/compaction-settings.ts +0 -49
- package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
- package/examples/playground/app/agent/compaction-timeline.ts +0 -208
package/dist/store-memory.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as idempotentReplay } from "./idempotent-replay-
|
|
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 } from "./internal-CbVvEwad.js";
|
|
3
|
+
import { t as idempotentReplay } from "./idempotent-replay-JdgMQRRm.js";
|
|
4
4
|
import { n as SYSTEM_CLOCK, t as RANDOM_IDS } from "./store-N8PXxDAS.js";
|
|
5
5
|
//#region src/store-memory.ts
|
|
6
|
+
const RECLAIM_BATCH = 256;
|
|
7
|
+
const headOf = (log) => log ? log.offset + log.rows.length : 0;
|
|
6
8
|
const toStored = (row) => ({
|
|
7
9
|
id: row.id,
|
|
8
10
|
type: row.type,
|
|
@@ -57,13 +59,27 @@ function memory(options = {}) {
|
|
|
57
59
|
if (!subs) return;
|
|
58
60
|
for (const listener of subs) for (const row of rows) listener(row);
|
|
59
61
|
};
|
|
60
|
-
const
|
|
61
|
-
let
|
|
62
|
-
if (!
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
const logOf = (sessionId) => {
|
|
63
|
+
let log = sessions.get(sessionId);
|
|
64
|
+
if (!log) {
|
|
65
|
+
log = {
|
|
66
|
+
rows: [],
|
|
67
|
+
offset: 0,
|
|
68
|
+
clearIndex: 0
|
|
69
|
+
};
|
|
70
|
+
sessions.set(sessionId, log);
|
|
65
71
|
}
|
|
66
|
-
return
|
|
72
|
+
return log;
|
|
73
|
+
};
|
|
74
|
+
const rowAt = (sessionId, index) => {
|
|
75
|
+
const log = sessions.get(sessionId);
|
|
76
|
+
return log && index > log.offset ? log.rows[index - log.offset - 1] : void 0;
|
|
77
|
+
};
|
|
78
|
+
const clearIndexOf = (sessionId) => sessions.get(sessionId)?.clearIndex ?? 0;
|
|
79
|
+
/** The row owning `id`, ignoring this session's rows below its clear. */
|
|
80
|
+
const ownerOf = (sessionId, id) => {
|
|
81
|
+
const row = byEventId.get(id);
|
|
82
|
+
return row?.sessionId === sessionId && row.index < clearIndexOf(sessionId) ? void 0 : row;
|
|
67
83
|
};
|
|
68
84
|
const checkpointKey = (sessionId, reducerName, index) => `${snapshotKey(sessionId, reducerName)}\u0000${index}`;
|
|
69
85
|
const releaseSnapshotPins = (sessionId, eventIndex) => {
|
|
@@ -123,14 +139,23 @@ function memory(options = {}) {
|
|
|
123
139
|
if (next.failedAt === null) state.ready.set(next.index, next);
|
|
124
140
|
};
|
|
125
141
|
const find = (sessionId, index) => {
|
|
126
|
-
const row =
|
|
142
|
+
const row = rowAt(sessionId, index);
|
|
127
143
|
if (!row) throw new TypeError(`no event at index ${index} in session '${sessionId}'`);
|
|
128
144
|
return row;
|
|
129
145
|
};
|
|
130
|
-
const appendResult = (sessionId, events) =>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
const appendResult = (sessionId, events) => {
|
|
147
|
+
const log = sessions.get(sessionId);
|
|
148
|
+
return {
|
|
149
|
+
events,
|
|
150
|
+
hasPending: (log?.rows ?? []).some((row) => row.index > log.clearIndex && row.processedAt === null)
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
/** The attempt-currency fence shared by handler appends and clears. */
|
|
154
|
+
const assertCurrentCause = (sessionId, cause) => {
|
|
155
|
+
if (cause.index <= clearIndexOf(sessionId)) throw supersededAttemptError(sessionId, cause);
|
|
156
|
+
const parent = find(sessionId, cause.index);
|
|
157
|
+
if (parent.attemptCount !== cause.attempt || parent.failedAt !== null) throw supersededAttemptError(sessionId, cause);
|
|
158
|
+
};
|
|
134
159
|
const claim = (row, holder, now, expiresAt) => {
|
|
135
160
|
row.attemptCount += 1;
|
|
136
161
|
row.firstClaimedAt ??= new Date(now);
|
|
@@ -142,14 +167,14 @@ function memory(options = {}) {
|
|
|
142
167
|
const checkBatchIds = (sessionId, events) => {
|
|
143
168
|
const supplied = events.filter((candidate) => candidate.id !== void 0);
|
|
144
169
|
if (new Set(supplied.map((candidate) => candidate.id)).size !== supplied.length) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "batch contains the same event id more than once");
|
|
145
|
-
const existing = supplied.map((candidate) =>
|
|
170
|
+
const existing = supplied.map((candidate) => ownerOf(sessionId, candidate.id)).filter((row) => row !== void 0);
|
|
146
171
|
const foreign = existing.find((row) => row.sessionId !== sessionId);
|
|
147
172
|
if (foreign) throw new A2Error("PARTIAL_DUPLICATE_BATCH", `event id '${foreign.id}' already exists in another session`);
|
|
148
173
|
return existing;
|
|
149
174
|
};
|
|
150
175
|
const insert = (sessionId, events, now, cause) => {
|
|
151
|
-
const
|
|
152
|
-
const base =
|
|
176
|
+
const log = logOf(sessionId);
|
|
177
|
+
const base = headOf(log);
|
|
153
178
|
const inserted = events.map((candidate, offset) => ({
|
|
154
179
|
id: candidate.id ?? generateId(),
|
|
155
180
|
type: candidate.type,
|
|
@@ -173,59 +198,128 @@ function memory(options = {}) {
|
|
|
173
198
|
lastError: null,
|
|
174
199
|
failedAt: null
|
|
175
200
|
}));
|
|
176
|
-
for (const row of inserted) if (
|
|
201
|
+
for (const row of inserted) if (ownerOf(sessionId, row.id)) throw new A2Error("PARTIAL_DUPLICATE_BATCH", `event id '${row.id}' already exists`);
|
|
177
202
|
for (const row of inserted) {
|
|
178
|
-
rows.push(row);
|
|
203
|
+
log.rows.push(row);
|
|
179
204
|
byEventId.set(row.id, row);
|
|
180
205
|
enqueue(sessionId, row);
|
|
181
206
|
}
|
|
182
207
|
return inserted;
|
|
183
208
|
};
|
|
209
|
+
/** Every cached fold of this session below its clear, head or historical. */
|
|
210
|
+
const dropCheckpointsBelow = (sessionId, clearIndex) => {
|
|
211
|
+
const prefix = `${sessionId}\u0000`;
|
|
212
|
+
for (const [key, snapshot] of snapshots) {
|
|
213
|
+
if (!key.startsWith(prefix) || snapshot.index >= clearIndex) continue;
|
|
214
|
+
snapshots.delete(key);
|
|
215
|
+
snapshotPinCounts.delete(`${key}\u0000${snapshot.index}`);
|
|
216
|
+
}
|
|
217
|
+
for (const [key, snapshot] of historicalSnapshots) {
|
|
218
|
+
if (!key.startsWith(prefix) || snapshot.index >= clearIndex) continue;
|
|
219
|
+
historicalSnapshots.delete(key);
|
|
220
|
+
snapshotPinCounts.delete(key);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
184
223
|
const readStateOf = (sessionId, reducerName, throughIndex, snapshotThroughIndex) => {
|
|
185
224
|
const key = snapshotKey(sessionId, reducerName);
|
|
225
|
+
const clearIndex = clearIndexOf(sessionId);
|
|
186
226
|
const snapshotFrontier = snapshotThroughIndex ?? throughIndex;
|
|
187
|
-
const
|
|
227
|
+
const stored = snapshots.get(key);
|
|
228
|
+
const head = stored && stored.index >= clearIndex ? stored : void 0;
|
|
188
229
|
let candidate = head && (snapshotFrontier === void 0 || head.index <= snapshotFrontier) ? head : null;
|
|
189
230
|
if (snapshotFrontier !== void 0) {
|
|
190
231
|
const prefix = `${key}\u0000`;
|
|
191
|
-
for (const [historicalKey, snapshot] of historicalSnapshots) if (historicalKey.startsWith(prefix) && snapshot.index <= snapshotFrontier && (!candidate || snapshot.index > candidate.index)) candidate = snapshot;
|
|
232
|
+
for (const [historicalKey, snapshot] of historicalSnapshots) if (historicalKey.startsWith(prefix) && snapshot.index >= clearIndex && snapshot.index <= snapshotFrontier && (!candidate || snapshot.index > candidate.index)) candidate = snapshot;
|
|
192
233
|
}
|
|
193
234
|
const snapshot = candidate ? {
|
|
194
235
|
index: candidate.index,
|
|
195
236
|
state: structuredClone(candidate.state)
|
|
196
237
|
} : null;
|
|
197
|
-
const afterIndex = snapshot?.index ?? 0;
|
|
238
|
+
const afterIndex = Math.max(snapshot?.index ?? 0, clearIndex - 1);
|
|
198
239
|
return {
|
|
199
240
|
headIndex: head?.index ?? null,
|
|
200
241
|
snapshot,
|
|
201
|
-
events: (sessions.get(sessionId) ?? []).filter((row) => row.index > afterIndex && (throughIndex === void 0 || row.index <= throughIndex)).map(toEvent)
|
|
242
|
+
events: (sessions.get(sessionId)?.rows ?? []).filter((row) => row.index > afterIndex && (throughIndex === void 0 || row.index <= throughIndex)).map(toEvent),
|
|
243
|
+
...clearIndex > 0 ? { clearIndex } : {}
|
|
202
244
|
};
|
|
203
245
|
};
|
|
204
246
|
return {
|
|
205
|
-
async append(sessionId, events) {
|
|
247
|
+
async append(sessionId, events, appendOptions) {
|
|
206
248
|
if (events.length === 0) return appendResult(sessionId, []);
|
|
207
249
|
const existing = checkBatchIds(sessionId, events);
|
|
208
250
|
if (existing.length > 0) {
|
|
209
251
|
if (existing.length === events.length) return appendResult(sessionId, idempotentReplay(events, existing.map(toStored)));
|
|
210
252
|
throw new A2Error("PARTIAL_DUPLICATE_BATCH", `batch mixes ${existing.length} already-appended and ${events.length - existing.length} fresh events`);
|
|
211
253
|
}
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const parent = rows[event.cause.index - 1];
|
|
216
|
-
if (!parent) throw new TypeError(`no event at index ${event.cause.index} in session '${sessionId}'`);
|
|
217
|
-
if (parent.attemptCount !== event.cause.attempt || parent.failedAt !== null) throw new A2Error("SUPERSEDED_ATTEMPT", `attempt ${event.cause.attempt} no longer owns event ${event.cause.index} in session '${sessionId}'`);
|
|
218
|
-
}
|
|
254
|
+
const originIndex = appendOptions?.originIndex;
|
|
255
|
+
if (originIndex !== void 0 && originIndex <= clearIndexOf(sessionId)) throw sessionClearedError(`scheduled work from event ${originIndex} was cleared in session '${sessionId}'`);
|
|
256
|
+
for (const event of events) if (event.cause) assertCurrentCause(sessionId, event.cause);
|
|
219
257
|
const now = clock.now();
|
|
220
258
|
const inserted = insert(sessionId, events, now);
|
|
221
259
|
notifyStreams(sessionId, inserted);
|
|
222
260
|
return appendResult(sessionId, inserted.map(toStored));
|
|
223
261
|
},
|
|
262
|
+
async clear({ sessionId, ifLastIndex, cause }) {
|
|
263
|
+
if (cause) assertCurrentCause(sessionId, cause);
|
|
264
|
+
const log = sessions.get(sessionId);
|
|
265
|
+
const head = headOf(log);
|
|
266
|
+
const clearIndex = log?.clearIndex ?? 0;
|
|
267
|
+
if (ifLastIndex !== void 0 && clearIndex === ifLastIndex + 1) return {
|
|
268
|
+
cleared: true,
|
|
269
|
+
index: head,
|
|
270
|
+
clearIndex
|
|
271
|
+
};
|
|
272
|
+
if (ifLastIndex !== void 0 && ifLastIndex !== head) return {
|
|
273
|
+
cleared: false,
|
|
274
|
+
index: head,
|
|
275
|
+
clearIndex
|
|
276
|
+
};
|
|
277
|
+
if (head === 0 || clearIndex === head) return {
|
|
278
|
+
cleared: true,
|
|
279
|
+
index: head,
|
|
280
|
+
clearIndex
|
|
281
|
+
};
|
|
282
|
+
const [marker] = insert(sessionId, [{
|
|
283
|
+
type: CLEAR_EVENT_TYPE,
|
|
284
|
+
payload: null,
|
|
285
|
+
settled: true
|
|
286
|
+
}], clock.now());
|
|
287
|
+
logOf(sessionId).clearIndex = marker.index;
|
|
288
|
+
dispatch.delete(sessionId);
|
|
289
|
+
notifyStreams(sessionId, [marker]);
|
|
290
|
+
return {
|
|
291
|
+
cleared: true,
|
|
292
|
+
index: marker.index,
|
|
293
|
+
clearIndex: marker.index
|
|
294
|
+
};
|
|
295
|
+
},
|
|
296
|
+
async reclaim(sessionId) {
|
|
297
|
+
const log = sessions.get(sessionId);
|
|
298
|
+
if (!log || log.offset >= log.clearIndex - 1) return {
|
|
299
|
+
deleted: 0,
|
|
300
|
+
done: true
|
|
301
|
+
};
|
|
302
|
+
const removed = log.rows.splice(0, Math.min(RECLAIM_BATCH, log.clearIndex - 1 - log.offset));
|
|
303
|
+
log.offset += removed.length;
|
|
304
|
+
for (const row of removed) {
|
|
305
|
+
if (byEventId.get(row.id) === row) byEventId.delete(row.id);
|
|
306
|
+
releaseSnapshotPins(sessionId, row.index);
|
|
307
|
+
}
|
|
308
|
+
const done = log.offset === log.clearIndex - 1;
|
|
309
|
+
if (done) dropCheckpointsBelow(sessionId, log.clearIndex);
|
|
310
|
+
return {
|
|
311
|
+
deleted: removed.length,
|
|
312
|
+
done
|
|
313
|
+
};
|
|
314
|
+
},
|
|
224
315
|
async read(sessionId, opts) {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
316
|
+
const log = sessions.get(sessionId);
|
|
317
|
+
if (!log) return [];
|
|
318
|
+
const head = headOf(log);
|
|
319
|
+
const after = Math.max(opts?.afterIndex ?? 0, log.clearIndex - 1, log.offset);
|
|
320
|
+
const through = Math.min(opts?.throughIndex ?? head, head);
|
|
321
|
+
if (through <= after) return [];
|
|
322
|
+
return log.rows.slice(after - log.offset, through - log.offset).map(toStored);
|
|
229
323
|
},
|
|
230
324
|
async claimAvailable({ sessionId, holder, ttlMs, expiresAtMs, excludeIndexes }) {
|
|
231
325
|
const claimedAt = clock.now();
|
|
@@ -247,20 +341,29 @@ function memory(options = {}) {
|
|
|
247
341
|
retryAt
|
|
248
342
|
} : { outcome: "settled" };
|
|
249
343
|
eligible.sort((a, b) => a.index - b.index);
|
|
344
|
+
const clearIndex = clearIndexOf(sessionId);
|
|
250
345
|
return {
|
|
251
346
|
outcome: "claimed",
|
|
252
|
-
events: eligible.map((row) =>
|
|
347
|
+
events: eligible.map((row) => {
|
|
348
|
+
const claimed = claim(row, holder, claimedAt, expiresAt);
|
|
349
|
+
if (clearIndex > 0) claimed.clearIndex = clearIndex;
|
|
350
|
+
return claimed;
|
|
351
|
+
})
|
|
253
352
|
};
|
|
254
353
|
},
|
|
255
354
|
async renewClaims({ sessionId, holder, claims, ttlMs, expiresAtMs }) {
|
|
256
355
|
const now = clock.now();
|
|
257
356
|
const expiresAt = new Date(expiresAtMs ?? now.getTime() + ttlMs);
|
|
258
|
-
const
|
|
357
|
+
const clearIndex = clearIndexOf(sessionId);
|
|
259
358
|
const renewed = [];
|
|
260
359
|
const superseded = [];
|
|
261
|
-
for (const
|
|
262
|
-
|
|
263
|
-
|
|
360
|
+
for (const { index, attempt } of claims.toSorted((a, b) => a.index - b.index)) {
|
|
361
|
+
if (index <= clearIndex) {
|
|
362
|
+
superseded.push(index);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const row = rowAt(sessionId, index);
|
|
366
|
+
if (!row) continue;
|
|
264
367
|
if (row.attemptCount > attempt) {
|
|
265
368
|
superseded.push(row.index);
|
|
266
369
|
continue;
|
|
@@ -276,6 +379,7 @@ function memory(options = {}) {
|
|
|
276
379
|
};
|
|
277
380
|
},
|
|
278
381
|
async completeAttempt({ sessionId, index, attempt, events }) {
|
|
382
|
+
if (index <= clearIndexOf(sessionId)) return { outcome: "superseded" };
|
|
279
383
|
const parent = find(sessionId, index);
|
|
280
384
|
const cause = {
|
|
281
385
|
index,
|
|
@@ -310,6 +414,10 @@ function memory(options = {}) {
|
|
|
310
414
|
};
|
|
311
415
|
},
|
|
312
416
|
async failAttempt({ sessionId, index, attempt, error, maxFailures }) {
|
|
417
|
+
if (index <= clearIndexOf(sessionId)) return {
|
|
418
|
+
outcome: "superseded",
|
|
419
|
+
failureCount: 0
|
|
420
|
+
};
|
|
313
421
|
const row = find(sessionId, index);
|
|
314
422
|
if (row.processedAt || row.attemptCount !== attempt || row.claimHolder === null && row.lastFailedAttempt !== attempt) return {
|
|
315
423
|
outcome: "superseded",
|
|
@@ -352,11 +460,13 @@ function memory(options = {}) {
|
|
|
352
460
|
},
|
|
353
461
|
async putSnapshots(sessionId, reducerName, writes) {
|
|
354
462
|
const key = snapshotKey(sessionId, reducerName);
|
|
463
|
+
const clearIndex = clearIndexOf(sessionId);
|
|
355
464
|
for (const write of writes.toSorted((a, b) => a.index - b.index)) {
|
|
465
|
+
if (write.index < clearIndex) continue;
|
|
356
466
|
const checkpoint = checkpointKey(sessionId, reducerName, write.index);
|
|
357
467
|
for (const eventIndex of write.pinEventIndexes ?? []) {
|
|
358
|
-
const event =
|
|
359
|
-
if (!event || event.processedAt !== null || event.failedAt !== null) continue;
|
|
468
|
+
const event = rowAt(sessionId, eventIndex);
|
|
469
|
+
if (!event || event.index <= clearIndex || event.processedAt !== null || event.failedAt !== null) continue;
|
|
360
470
|
const pinKey = eventPinKey(sessionId, eventIndex);
|
|
361
471
|
let pinned = snapshotPins.get(pinKey);
|
|
362
472
|
if (!pinned) {
|
|
@@ -370,7 +480,7 @@ function memory(options = {}) {
|
|
|
370
480
|
}
|
|
371
481
|
const current = snapshots.get(key);
|
|
372
482
|
if (!current || write.index >= current.index) {
|
|
373
|
-
if (current && write.index > current.index) {
|
|
483
|
+
if (current && write.index > current.index && current.index >= clearIndex) {
|
|
374
484
|
const currentCheckpoint = checkpointKey(sessionId, reducerName, current.index);
|
|
375
485
|
if ((snapshotPinCounts.get(currentCheckpoint) ?? 0) > 0) historicalSnapshots.set(currentCheckpoint, {
|
|
376
486
|
index: current.index,
|
|
@@ -473,12 +583,10 @@ function memory(options = {}) {
|
|
|
473
583
|
stream(sessionId, opts) {
|
|
474
584
|
const startAfter = opts?.startAfter ?? 0;
|
|
475
585
|
return { [Symbol.asyncIterator]() {
|
|
476
|
-
let last = startAfter;
|
|
477
|
-
const buffer = [];
|
|
586
|
+
let last = Math.max(startAfter, clearIndexOf(sessionId) - 1);
|
|
478
587
|
let wake = null;
|
|
479
588
|
let closed = false;
|
|
480
|
-
const onRow = (
|
|
481
|
-
buffer.push(row);
|
|
589
|
+
const onRow = () => {
|
|
482
590
|
wake?.();
|
|
483
591
|
};
|
|
484
592
|
let subs = streamSubscribers.get(sessionId);
|
|
@@ -487,38 +595,43 @@ function memory(options = {}) {
|
|
|
487
595
|
streamSubscribers.set(sessionId, subs);
|
|
488
596
|
}
|
|
489
597
|
subs.add(onRow);
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
598
|
+
const stop = () => {
|
|
599
|
+
if (closed) return;
|
|
600
|
+
closed = true;
|
|
493
601
|
subs.delete(onRow);
|
|
494
602
|
if (subs.size === 0) streamSubscribers.delete(sessionId);
|
|
603
|
+
wake?.();
|
|
495
604
|
};
|
|
496
605
|
return {
|
|
497
606
|
async next() {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
return {
|
|
504
|
-
value: toStored(row),
|
|
505
|
-
done: false
|
|
607
|
+
try {
|
|
608
|
+
for (;;) {
|
|
609
|
+
if (closed) return {
|
|
610
|
+
value: void 0,
|
|
611
|
+
done: true
|
|
506
612
|
};
|
|
613
|
+
const log = sessions.get(sessionId);
|
|
614
|
+
const row = log?.rows[Math.max(0, last - log.offset)];
|
|
615
|
+
if (row) {
|
|
616
|
+
const value = toStored(row);
|
|
617
|
+
last = row.index;
|
|
618
|
+
return {
|
|
619
|
+
value,
|
|
620
|
+
done: false
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
await new Promise((resolve) => {
|
|
624
|
+
wake = resolve;
|
|
625
|
+
});
|
|
626
|
+
wake = null;
|
|
507
627
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
};
|
|
512
|
-
await new Promise((resolve) => {
|
|
513
|
-
wake = resolve;
|
|
514
|
-
});
|
|
515
|
-
wake = null;
|
|
628
|
+
} catch (error) {
|
|
629
|
+
stop();
|
|
630
|
+
throw error;
|
|
516
631
|
}
|
|
517
632
|
},
|
|
518
633
|
async return() {
|
|
519
|
-
|
|
520
|
-
unsubscribe();
|
|
521
|
-
wake?.();
|
|
634
|
+
stop();
|
|
522
635
|
return {
|
|
523
636
|
value: void 0,
|
|
524
637
|
done: true
|
package/dist/store-memory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-memory.js","names":[],"sources":["../src/store-memory.ts"],"sourcesContent":["/**\n * experimental-a2/store-memory — in-memory store backend (the test default).\n *\n * Implements the A2Store interface with zero dependencies. See\n * specs/a2-implementation.md §3 for the contract; the conformance suite\n * in test/conformance is the executable version of it.\n */\n\nimport type { PresencePatch } from './contract.ts'\nimport { A2Error } from './errors.ts'\nimport { idempotentReplay } from './idempotent-replay.ts'\nimport { nullProtoRecord } from './internal.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\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, Row[]>()\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 rowsOf = (sessionId: string): Row[] => {\n let rows = sessions.get(sessionId)\n if (!rows) {\n rows = []\n sessions.set(sessionId, rows)\n }\n return rows\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 = sessions.get(sessionId)?.[index - 1]\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 events,\n hasPending: (sessions.get(sessionId) ?? []).some(\n (row) => row.processedAt === null,\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) => byEventId.get(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 rows = rowsOf(sessionId)\n const base = rows.length === 0 ? 0 : rows[rows.length - 1]!.index\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 (byEventId.has(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 rows.push(row)\n byEventId.set(row.id, row)\n enqueue(sessionId, row)\n }\n return inserted\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 snapshotFrontier = snapshotThroughIndex ?? throughIndex\n const head = snapshots.get(key)\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 <= 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 = snapshot?.index ?? 0\n return {\n headIndex: head?.index ?? null,\n snapshot,\n events: (sessions.get(sessionId) ?? [])\n .filter(\n (row) =>\n row.index > afterIndex &&\n (throughIndex === undefined || row.index <= throughIndex),\n )\n .map(toEvent),\n }\n }\n\n return {\n async append(sessionId, events) {\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 // Attempt-currency fence: a fresh handler append commits only while\n // its causal attempt is still the parent's latest (store.ts `append`).\n const rows = sessions.get(sessionId) ?? []\n for (const event of events) {\n if (!event.cause) continue\n const parent = rows[event.cause.index - 1]\n if (!parent) {\n throw new TypeError(\n `no event at index ${event.cause.index} in session '${sessionId}'`,\n )\n }\n if (\n parent.attemptCount !== event.cause.attempt ||\n parent.failedAt !== null\n ) {\n throw new A2Error(\n 'SUPERSEDED_ATTEMPT',\n `attempt ${event.cause.attempt} no longer owns event ${event.cause.index} in session '${sessionId}'`,\n )\n }\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 read(sessionId, opts) {\n const rows = sessions.get(sessionId) ?? []\n const start = Math.max(0, opts?.afterIndex ?? 0)\n const end = Math.max(0, opts?.throughIndex ?? rows.length)\n return rows.slice(start, end).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 return {\n outcome: 'claimed',\n events: eligible.map((row) => claim(row, holder, claimedAt, expiresAt)),\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 requested = new Map(claims.map((c) => [c.index, c.attempt]))\n const renewed: number[] = []\n const superseded: number[] = []\n for (const row of sessions.get(sessionId) ?? []) {\n const attempt = requested.get(row.index)\n if (attempt === undefined) 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 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 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 for (const write of writes.toSorted((a, b) => a.index - b.index)) {\n const checkpoint = checkpointKey(sessionId, reducerName, write.index)\n for (const eventIndex of write.pinEventIndexes ?? []) {\n const event = sessions.get(sessionId)?.[eventIndex - 1]\n if (!event || event.processedAt !== null || event.failedAt !== null) {\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 (current && write.index > current.index) {\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 = startAfter\n const buffer: Row[] = []\n let wake: (() => void) | null = null\n let closed = false\n\n const onRow = (row: Row): void => {\n buffer.push(row)\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 // Seed with history — synchronously, in the same tick as the\n // subscription, so nothing can slip between the two.\n const existing = sessions.get(sessionId) ?? []\n buffer.unshift(...existing.filter((row) => row.index > startAfter))\n\n const unsubscribe = (): void => {\n subs.delete(onRow)\n if (subs.size === 0) streamSubscribers.delete(sessionId)\n }\n\n return {\n async next(): Promise<IteratorResult<Event>> {\n for (;;) {\n while (buffer.length > 0) {\n const row = buffer.shift()!\n if (row.index <= last) continue\n last = row.index\n return { value: toStored(row), done: false }\n }\n if (closed) return { value: undefined, done: true }\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 },\n async return(): Promise<IteratorResult<Event>> {\n closed = true\n unsubscribe()\n wake?.()\n return { value: undefined, done: true }\n },\n }\n },\n }\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"store-memory.js","names":[],"sources":["../src/store-memory.ts"],"sourcesContent":["/**\n * experimental-a2/store-memory — in-memory store backend (the test default).\n *\n * Implements the A2Store interface with zero dependencies. See\n * specs/a2-implementation.md §3 for the contract; the conformance suite\n * in test/conformance is the executable version of it.\n */\n\nimport type { PresencePatch } from './contract.ts'\nimport { A2Error, supersededAttemptError } from './errors.ts'\nimport { idempotentReplay } from './idempotent-replay.ts'\nimport { nullProtoRecord } from './internal.ts'\nimport { CLEAR_EVENT_TYPE, sessionClearedError } from './session-clear.ts'\nimport {\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":"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"}
|