experimental-a2 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +1 -1
- package/dist/{actor-C7b-1arn.d.ts → actor-CqibLgjQ.d.ts} +15 -7
- package/dist/actor-CqibLgjQ.d.ts.map +1 -0
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +2 -2
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +5 -5
- package/dist/{actor-shared-t7w8xIKP.js → actor-shared-DFET5q16.js} +3 -3
- package/dist/actor-shared-DFET5q16.js.map +1 -0
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +54 -43
- package/dist/actor.js.map +1 -1
- package/dist/{ai-Cz3hqVhk.d.ts → ai-DAu77CDr.d.ts} +8 -8
- package/dist/{ai-Cz3hqVhk.d.ts.map → ai-DAu77CDr.d.ts.map} +1 -1
- package/dist/{ai-mMiWul1T.js → ai-EEfKyc-z.js} +9 -9
- package/dist/ai-EEfKyc-z.js.map +1 -0
- package/dist/ai-client.d.ts +2 -2
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.d.ts +4 -2
- package/dist/ai-fx.d.ts.map +1 -1
- package/dist/ai-fx.js +214 -67
- package/dist/ai-fx.js.map +1 -1
- package/dist/{ai-harness-CV6rXbB3.d.ts → ai-harness-DTjkAr2T.d.ts} +2 -2
- package/dist/{ai-harness-CV6rXbB3.d.ts.map → ai-harness-DTjkAr2T.d.ts.map} +1 -1
- package/dist/{ai-harness-Clbg5ei7.js → ai-harness-dQeJaR1t.js} +7 -3
- package/dist/ai-harness-dQeJaR1t.js.map +1 -0
- package/dist/ai-pi.d.ts +1 -1
- package/dist/ai-pi.js +3 -3
- package/dist/ai-pi.js.map +1 -1
- package/dist/ai-react.d.ts +5 -5
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +14 -17
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +86 -73
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/{client-BhHLoNKF.js → client-BQS4i_V9.js} +29 -6
- package/dist/client-BQS4i_V9.js.map +1 -0
- package/dist/{client-h_DKFAjj.d.ts → client-CUMbS_38.d.ts} +3 -3
- package/dist/{client-h_DKFAjj.d.ts.map → client-CUMbS_38.d.ts.map} +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/{contract-BYKASlCV.js → contract-CWukt01d.js} +2 -2
- package/dist/contract-CWukt01d.js.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/{initial-snapshot-Bx-c0pDP.d.ts → initial-snapshot-D3Lve43o.d.ts} +4 -4
- package/dist/initial-snapshot-D3Lve43o.d.ts.map +1 -0
- package/dist/{internal-CscSY7K_.js → internal-CvsrprnU.js} +40 -16
- package/dist/internal-CvsrprnU.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/otel.js +1 -1
- package/dist/otel.js.map +1 -1
- package/dist/{react-BBXgHWFc.d.ts → react-BoHvC3v9.d.ts} +4 -4
- package/dist/{react-BBXgHWFc.d.ts.map → react-BoHvC3v9.d.ts.map} +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{reducer-CqA59Q6U.d.ts → reducer-CYPHsSmd.d.ts} +2 -2
- package/dist/{reducer-CqA59Q6U.d.ts.map → reducer-CYPHsSmd.d.ts.map} +1 -1
- package/dist/{reducer-BAXc8JyF.js → reducer-CjmvZSAb.js} +2 -2
- package/dist/{reducer-BAXc8JyF.js.map → reducer-CjmvZSAb.js.map} +1 -1
- package/dist/runtime-consumer-CIeL33wj.js +12 -0
- package/dist/runtime-consumer-CIeL33wj.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +3 -3
- package/dist/scheduler-task-X1SToDjP.js.map +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +3 -3
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-qyb5aJJG.js → server-DtFrWxi3.js} +412 -116
- package/dist/server-DtFrWxi3.js.map +1 -0
- package/dist/{server-DYAATFuT.d.ts → server-qgez0Xny.d.ts} +108 -41
- package/dist/server-qgez0Xny.d.ts.map +1 -0
- package/dist/server.browser.js +1 -1
- package/dist/server.browser.js.map +1 -1
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/{session-clear-C-QwTXV1.d.ts → session-clear-D1PFe7xl.d.ts} +2 -2
- package/dist/session-clear-D1PFe7xl.d.ts.map +1 -0
- package/dist/{session-clear-DGI4jTQ5.js → session-clear-wHd5GCpZ.js} +5 -4
- package/dist/session-clear-wHd5GCpZ.js.map +1 -0
- package/dist/store-D0MhBvg6.d.ts +532 -0
- package/dist/store-D0MhBvg6.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-codec-DwrWlgQx.js +65 -0
- package/dist/store-codec-DwrWlgQx.js.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +406 -225
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-7WJV0YWP.js → store-polling-BZt2Rf3D.js} +2 -2
- package/dist/{store-polling-7WJV0YWP.js.map → store-polling-BZt2Rf3D.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +1598 -587
- package/dist/store-postgres.js.map +1 -1
- package/dist/store-presence-polling-JuAy2Ck2.js +111 -0
- package/dist/store-presence-polling-JuAy2Ck2.js.map +1 -0
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +4 -4
- package/dist/store-redis-notify-CsJw_hfD.js +3060 -0
- package/dist/store-redis-notify-CsJw_hfD.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +4 -5
- 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 +527 -299
- package/dist/store-sqlite.js.map +1 -1
- package/dist/stream-buffer-DrTyiIuo.js +57 -0
- package/dist/stream-buffer-DrTyiIuo.js.map +1 -0
- package/dist/{telemetry-Cyfqo6Qt.d.ts → telemetry-CWwsh-Gx.d.ts} +3 -3
- package/dist/telemetry-CWwsh-Gx.d.ts.map +1 -0
- package/dist/{wire-BKxH1vbP.js → wire-CV-Z7wz-.js} +6 -5
- package/dist/wire-CV-Z7wz-.js.map +1 -0
- package/docs/01-quickstart.mdx +21 -15
- package/docs/02-examples.mdx +1 -1
- package/docs/actors/01-introduction.mdx +13 -2
- package/docs/actors/02-concurrency.mdx +1 -1
- package/docs/agents/01-introduction.mdx +1 -1
- package/docs/agents/02-tools.mdx +12 -11
- package/docs/agents/03-conversations.mdx +36 -25
- package/docs/agents/04-harnesses.mdx +14 -7
- package/docs/client/03-presence.mdx +18 -14
- package/docs/contract/01-events.mdx +2 -2
- package/docs/contract/02-state.mdx +18 -14
- package/docs/contract/03-sessions.mdx +32 -25
- package/docs/index.mdx +29 -24
- package/docs/reference/01-core.mdx +7 -6
- package/docs/reference/02-server.mdx +250 -191
- package/docs/reference/04-ai.mdx +33 -23
- package/docs/reference/05-telemetry.mdx +27 -13
- package/docs/reference/06-errors.mdx +25 -19
- package/docs/reference/meta.ts +1 -1
- package/docs/schedulers/01-overview.mdx +8 -8
- package/docs/schedulers/02-vercel-queues.mdx +2 -2
- package/docs/schedulers/03-qstash.mdx +4 -4
- package/docs/server/01-consumers.mdx +418 -0
- package/docs/server/02-timers.mdx +31 -21
- package/docs/server/03-cancellation.mdx +52 -44
- package/docs/server/04-routes.mdx +4 -1
- package/docs/server/05-sql-projections.mdx +391 -0
- package/docs/server/06-guarantees.mdx +61 -51
- package/docs/stores/01-overview.mdx +3 -3
- package/docs/stores/02-redis.mdx +24 -10
- package/docs/stores/03-redis-http.mdx +8 -6
- package/docs/stores/04-postgres.mdx +65 -14
- package/docs/stores/05-sqlite.mdx +5 -3
- package/docs/upgrades/01-overview.mdx +31 -0
- package/docs/upgrades/78-0-22.mdx +115 -0
- package/docs/upgrades/79-0-21.mdx +165 -0
- package/docs/upgrades/80-0-20.mdx +43 -0
- package/docs/upgrades/81-0-19.mdx +33 -0
- package/docs/upgrades/82-0-18.mdx +33 -0
- package/docs/upgrades/83-0-17.mdx +69 -0
- package/docs/upgrades/meta.ts +3 -0
- package/examples/playground/DEPLOY.md +39 -7
- package/examples/playground/app/a0/chat.test.tsx +2 -2
- package/examples/playground/app/a0/sandbox-log.ts +17 -15
- package/examples/playground/app/a0/sandbox.ts +58 -13
- package/examples/playground/app/a0/server.ts +4 -4
- package/examples/playground/app/a2slack/a2slack.css +1067 -0
- package/examples/playground/app/a2slack/c/[channel]/page.tsx +20 -0
- package/examples/playground/app/a2slack/consumers/agent.ts +63 -0
- package/examples/playground/app/a2slack/consumers/feed.ts +164 -0
- package/examples/playground/app/a2slack/consumers/notify.ts +116 -0
- package/examples/playground/app/a2slack/consumers/search.ts +37 -0
- package/examples/playground/app/a2slack/consumers/triage.ts +31 -0
- package/examples/playground/app/a2slack/consumers/welcome.ts +25 -0
- package/examples/playground/app/a2slack/db/a2.ts +209 -0
- package/examples/playground/app/a2slack/db/index.ts +73 -0
- package/examples/playground/app/a2slack/db/migrations/0000_extensions.sql +4 -0
- package/examples/playground/app/a2slack/db/migrations/0001_workspace.sql +74 -0
- package/examples/playground/app/a2slack/db/migrations/0002_a2.sql +215 -0
- package/examples/playground/app/a2slack/db/migrations/0003_a2_batch.sql +67 -0
- package/examples/playground/app/a2slack/db/migrations/0004_a2_session_event_ids.sql +2 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0000_snapshot.json +18 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0001_snapshot.json +500 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0002_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0003_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0004_snapshot.json +1165 -0
- package/examples/playground/app/a2slack/db/migrations/meta/_journal.json +41 -0
- package/examples/playground/app/a2slack/db/queries.ts +263 -0
- package/examples/playground/app/a2slack/db/schema.ts +145 -0
- package/examples/playground/app/a2slack/drills/a2slack.drill.ts +708 -0
- package/examples/playground/app/a2slack/drills/harness.ts +244 -0
- package/examples/playground/app/a2slack/events/route.ts +36 -0
- package/examples/playground/app/a2slack/identity.ts +44 -0
- package/examples/playground/app/a2slack/inbox/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/data/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/page.tsx +101 -0
- package/examples/playground/app/a2slack/lag.ts +73 -0
- package/examples/playground/app/a2slack/layout.tsx +43 -0
- package/examples/playground/app/a2slack/messages/route.ts +17 -0
- package/examples/playground/app/a2slack/model.test.ts +208 -0
- package/examples/playground/app/a2slack/model.ts +369 -0
- package/examples/playground/app/a2slack/models.test.ts +72 -0
- package/examples/playground/app/a2slack/page.tsx +10 -0
- package/examples/playground/app/a2slack/scheduler/route.ts +5 -0
- package/examples/playground/app/a2slack/search/route.ts +40 -0
- package/examples/playground/app/a2slack/search-index.ts +108 -0
- package/examples/playground/app/a2slack/server.test.ts +315 -0
- package/examples/playground/app/a2slack/server.ts +55 -0
- package/examples/playground/app/a2slack/sessions.ts +50 -0
- package/examples/playground/app/a2slack/thread/route.ts +8 -0
- package/examples/playground/app/a2slack/ui/ChannelView.tsx +87 -0
- package/examples/playground/app/a2slack/ui/Composer.tsx +117 -0
- package/examples/playground/app/a2slack/ui/DotsShadow.tsx +42 -0
- package/examples/playground/app/a2slack/ui/Feed.tsx +157 -0
- package/examples/playground/app/a2slack/ui/InboxContext.tsx +63 -0
- package/examples/playground/app/a2slack/ui/InboxView.tsx +82 -0
- package/examples/playground/app/a2slack/ui/Menu.tsx +128 -0
- package/examples/playground/app/a2slack/ui/MessageRow.tsx +314 -0
- package/examples/playground/app/a2slack/ui/NameGate.tsx +35 -0
- package/examples/playground/app/a2slack/ui/SearchContext.tsx +146 -0
- package/examples/playground/app/a2slack/ui/SearchResults.tsx +97 -0
- package/examples/playground/app/a2slack/ui/Shell.tsx +34 -0
- package/examples/playground/app/a2slack/ui/Sidebar.tsx +199 -0
- package/examples/playground/app/a2slack/ui/ThreadPanel.tsx +134 -0
- package/examples/playground/app/a2slack/ui/useInbox.ts +71 -0
- package/examples/playground/app/agent/server.ts +3 -3
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +1 -1
- package/examples/playground/app/chat/server.ts +45 -35
- package/examples/playground/app/components/demo-icons.tsx +7 -0
- package/examples/playground/app/components/demos.tsx +7 -0
- package/examples/playground/app/orders/[orderId]/order-client.tsx +2 -2
- package/examples/playground/app/orders/page.tsx +1 -1
- package/examples/playground/app/orders/server.ts +49 -30
- package/examples/playground/app/recovery/[recoveryId]/recovery-client.tsx +14 -15
- package/examples/playground/app/recovery/page.tsx +4 -4
- package/examples/playground/app/recovery/server.ts +23 -32
- package/examples/playground/drizzle.config.ts +14 -0
- package/examples/playground/lib/store.ts +1 -1
- package/examples/playground/next.config.ts +6 -1
- package/examples/playground/package.json +12 -1
- package/examples/playground/scripts/migrate.mjs +31 -0
- package/examples/playground/vercel.json +9 -0
- package/examples/playground/vitest.drills.config.ts +32 -0
- package/package.json +5 -2
- package/sql/postgres/drizzle-schema.ts +209 -0
- package/sql/postgres/schema.sql +227 -67
- package/src/actor-shared.ts +10 -2
- package/src/actor.ts +101 -71
- package/src/ai-agent.ts +162 -111
- package/src/ai-control-server.ts +8 -8
- package/src/ai-fx.ts +324 -82
- package/src/ai-harness.ts +17 -2
- package/src/ai-pi.ts +2 -2
- package/src/ai-server.ts +2 -2
- package/src/ai.ts +7 -7
- package/src/client.ts +19 -2
- package/src/contract.ts +1 -1
- package/src/deterministic-id.ts +27 -4
- package/src/errors.ts +8 -5
- package/src/idempotent-replay.ts +0 -1
- package/src/index.ts +1 -1
- package/src/internal.ts +57 -16
- package/src/otel.ts +1 -1
- package/src/postgres-schema.ts +98 -57
- package/src/presence-recovery.ts +18 -11
- package/src/presence-tombstones.ts +52 -0
- package/src/push-envelope.ts +20 -0
- package/src/runtime-consumer.ts +10 -0
- package/src/scheduler-task.ts +2 -2
- package/src/scheduler-vercel.ts +1 -1
- package/src/server.browser.ts +1 -1
- package/src/server.ts +819 -218
- package/src/session-socket.ts +1 -1
- package/src/store-codec.ts +66 -0
- package/src/store-memory.ts +604 -286
- package/src/store-postgres.ts +2006 -857
- package/src/store-presence-polling.ts +59 -34
- package/src/store-redis-core.ts +1961 -1333
- package/src/store-redis-notify.ts +76 -26
- package/src/store-redis.ts +2 -3
- package/src/store-sqlite.ts +915 -516
- package/src/store.ts +241 -102
- package/src/telemetry.ts +2 -1
- package/src/wire.ts +1 -0
- package/dist/actor-C7b-1arn.d.ts.map +0 -1
- package/dist/actor-shared-t7w8xIKP.js.map +0 -1
- package/dist/ai-harness-Clbg5ei7.js.map +0 -1
- package/dist/ai-mMiWul1T.js.map +0 -1
- package/dist/client-BhHLoNKF.js.map +0 -1
- package/dist/contract-BYKASlCV.js.map +0 -1
- package/dist/idempotent-replay-JdgMQRRm.js +0 -21
- package/dist/idempotent-replay-JdgMQRRm.js.map +0 -1
- package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +0 -1
- package/dist/internal-CscSY7K_.js.map +0 -1
- package/dist/server-DYAATFuT.d.ts.map +0 -1
- package/dist/server-qyb5aJJG.js.map +0 -1
- package/dist/session-clear-C-QwTXV1.d.ts.map +0 -1
- package/dist/session-clear-DGI4jTQ5.js.map +0 -1
- package/dist/store-CyLUy86v.d.ts +0 -404
- package/dist/store-CyLUy86v.d.ts.map +0 -1
- package/dist/store-codec-DTG0Ftek.js +0 -10
- package/dist/store-codec-DTG0Ftek.js.map +0 -1
- package/dist/store-presence-polling-B1ixeqIS.js +0 -94
- package/dist/store-presence-polling-B1ixeqIS.js.map +0 -1
- package/dist/store-redis-notify-CM0AfLY0.js +0 -2544
- package/dist/store-redis-notify-CM0AfLY0.js.map +0 -1
- package/dist/stream-buffer-Cx7AAPGq.js +0 -23
- package/dist/stream-buffer-Cx7AAPGq.js.map +0 -1
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +0 -1
- package/dist/wire-BKxH1vbP.js.map +0 -1
- package/docs/server/01-handlers.mdx +0 -227
package/dist/store-sqlite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-sqlite.js","names":[],"sources":["../src/store-sqlite.ts"],"sourcesContent":["/**\n * experimental-a2/store-sqlite — sqlite store backend (the dev default, `.a2/dev.db`).\n *\n * Built on `node:sqlite` (Node ≥ 22.13) so it ships with zero\n * dependencies. Implements the A2Store interface; the conformance suite\n * in test/conformance is the executable contract.\n *\n * The spec's `index` column is stored as `idx` (a2-implementation.md\n * §11 — `index` collides with a reserved word) and mapped back at the\n * API boundary. Timestamps are epoch milliseconds written from the\n * injected clock — never SQL `now()` — so tests can time-travel against\n * real storage.\n */\n\nimport { mkdirSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { DatabaseSync } from 'node:sqlite'\nimport { A2Error, supersededAttemptError } from './errors.ts'\nimport { CLEAR_EVENT_TYPE, sessionClearedError } from './session-clear.ts'\nimport { idempotentReplay } from './idempotent-replay.ts'\nimport { decodeReturnedEventIds } from './store-codec.ts'\nimport { pollingStream } from './store-polling.ts'\nimport {\n RANDOM_IDS,\n SYSTEM_CLOCK,\n type A2Store,\n type Clock,\n type Event,\n type EventCause,\n type IdSource,\n type StoredEvent,\n} from './store.ts'\n\nexport type SqliteStoreOptions = {\n /** Database file path. Defaults to `.a2/dev.db`; `:memory:` works. */\n path?: string\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\nexport type SqliteStore = A2Store & {\n /** Close the underlying database handle. */\n close(): void\n}\n\nconst SCHEMA = `\ncreate table if not exists a2_events (\n session_id text not null,\n idx integer not null,\n event_type text not null,\n payload text not null,\n event_id text not null,\n created_at integer not null,\n cause text,\n lane text,\n lane_ready integer not null default 0,\n processed_at integer,\n processed_by_attempt integer,\n returned_event_ids text,\n first_claimed_at integer,\n last_claimed_at integer,\n attempt_count integer not null default 0,\n failure_count integer not null default 0,\n last_failed_at integer,\n last_failed_attempt integer,\n last_error text,\n failed_at integer,\n claim_holder text,\n claim_expires_at integer,\n primary key (session_id, idx)\n) strict;\n\ncreate unique index if not exists a2_events_event_id on a2_events (event_id);\ncreate index if not exists a2_events_unprocessed\n on a2_events (session_id, idx) where processed_at is null;\ncreate index if not exists a2_events_lane_pending\n on a2_events (session_id, lane, idx) where processed_at is null;\ncreate index if not exists a2_events_dispatch_ready\n on a2_events (session_id, idx)\n where lane_ready = 1 and processed_at is null and failed_at is null;\ncreate index if not exists a2_events_claim_expiry\n on a2_events (session_id, claim_expires_at)\n where lane_ready = 1 and processed_at is null and failed_at is null;\n\ncreate table if not exists a2_snapshots (\n session_id text not null,\n reducer_name text not null,\n up_to_index integer not null,\n state text not null,\n updated_at integer not null,\n primary key (session_id, reducer_name)\n) strict;\n\ncreate table if not exists a2_snapshot_history (\n session_id text not null,\n reducer_name text not null,\n up_to_index integer not null,\n state text not null,\n updated_at integer not null,\n primary key (session_id, reducer_name, up_to_index)\n) strict;\n\ncreate table if not exists a2_snapshot_pins (\n session_id text not null,\n event_index integer not null,\n reducer_name text not null,\n up_to_index integer not null,\n primary key (session_id, event_index, reducer_name, up_to_index)\n) strict;\n\ncreate index if not exists a2_snapshot_pins_checkpoint\n on a2_snapshot_pins (session_id, reducer_name, up_to_index);\n\ncreate table if not exists a2_clears (\n session_id text not null primary key,\n clear_index integer not null\n) strict;\n\ncreate table if not exists a2_presence (\n ns text not null,\n participant text not null,\n field text not null,\n value text not null,\n seen integer not null,\n at integer not null,\n expires_at integer not null,\n primary key (ns, participant, field)\n) strict;\n`\n\n/**\n * Switching journal modes takes an exclusive lock, and — unlike normal\n * statements — the switch can return SQLITE_BUSY without consulting the\n * busy handler while several connections race it (instances cold-booting\n * against one file). Bounded synchronous retry; the window is boot-only\n * and tiny. Found by the multi-process torture test.\n */\nconst setWalJournalMode = (db: DatabaseSync): void => {\n for (let attempt = 0; ; attempt += 1) {\n try {\n db.exec('pragma journal_mode = wal')\n return\n } catch (err) {\n const busy = (err as { errcode?: number }).errcode === 5\n if (!busy || attempt >= 100) throw err\n // Synchronous 5ms sleep — sqlite() is a sync constructor.\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5)\n }\n }\n}\n\nconst wrap = <T>(fn: () => T): T => {\n try {\n return fn()\n } catch (err) {\n if (err instanceof A2Error || err instanceof TypeError) throw err\n throw new A2Error('STORE_UNAVAILABLE', 'sqlite store operation failed', {\n cause: err,\n })\n }\n}\n\nconst toDate = (ms: number | bigint | null): Date | null =>\n ms === null ? null : new Date(Number(ms))\n\nconst toCause = (json: string | null): EventCause | null => {\n if (json === null) return null\n const value = JSON.parse(json) as {\n index?: unknown\n attempt?: unknown\n batchSize?: unknown\n }\n if (\n !Number.isInteger(value.index) ||\n Number(value.index) < 1 ||\n !Number.isInteger(value.attempt) ||\n Number(value.attempt) < 1\n ) {\n throw new TypeError('stored event has an invalid cause')\n }\n if (\n value.batchSize !== undefined &&\n (!Number.isInteger(value.batchSize) || Number(value.batchSize) < 1)\n ) {\n throw new TypeError('stored event has an invalid cause')\n }\n return {\n index: Number(value.index),\n attempt: Number(value.attempt),\n ...(value.batchSize === undefined\n ? {}\n : { batchSize: Number(value.batchSize) }),\n }\n}\n\nconst toStored = (row: EventRow): StoredEvent => ({\n id: row.event_id,\n type: row.event_type,\n payload: JSON.parse(row.payload),\n index: Number(row.idx),\n sessionId: row.session_id,\n createdAt: new Date(Number(row.created_at)),\n cause: toCause(row.cause),\n lane: row.lane,\n processedAt: toDate(row.processed_at),\n processedByAttempt:\n row.processed_by_attempt === null ? null : Number(row.processed_by_attempt),\n returnedEventIds:\n row.returned_event_ids === null\n ? null\n : decodeReturnedEventIds(row.returned_event_ids),\n firstClaimedAt: toDate(row.first_claimed_at),\n lastClaimedAt: toDate(row.last_claimed_at),\n attemptCount: Number(row.attempt_count),\n failureCount: Number(row.failure_count),\n lastFailedAt: toDate(row.last_failed_at),\n lastFailedAttempt:\n row.last_failed_attempt === null ? null : Number(row.last_failed_attempt),\n lastError: row.last_error,\n failedAt: toDate(row.failed_at),\n claimHolder: row.claim_holder,\n claimExpiresAt: toDate(row.claim_expires_at),\n})\n\nconst toEvent = (row: EventRow): Event => ({\n id: row.event_id,\n type: row.event_type,\n payload: JSON.parse(row.payload),\n index: Number(row.idx),\n sessionId: row.session_id,\n createdAt: new Date(Number(row.created_at)),\n})\n\ntype EventRow = {\n session_id: string\n idx: number | bigint\n event_type: string\n payload: string\n event_id: string\n created_at: number | bigint\n cause: string | null\n lane: string | null\n processed_at: number | bigint | null\n processed_by_attempt: number | bigint | null\n returned_event_ids: string | null\n first_claimed_at: number | bigint | null\n last_claimed_at: number | bigint | null\n attempt_count: number | bigint\n failure_count: number | bigint\n last_failed_at: number | bigint | null\n last_failed_attempt: number | bigint | null\n last_error: string | null\n failed_at: number | bigint | null\n claim_holder: string | null\n claim_expires_at: number | bigint | null\n}\n\ntype PresenceDbRow = {\n participant: string\n field: string\n value: string\n seen: number | bigint\n at: number | bigint\n expires_at: number | bigint\n}\n\ntype StateReadRow = Partial<EventRow> & {\n row_kind: 'snapshot' | 'event'\n clear_index: number | bigint | null\n head_index: number | bigint | null\n snapshot_index: number | bigint | null\n snapshot_state: string | null\n}\n\ntype SnapshotHeadRow = {\n up_to_index: number | bigint\n state: string\n updated_at: number | bigint\n}\n\ntype AppendStatusRow = {\n max: number | bigint\n has_pending: number | bigint\n}\n\ntype ExistingEventRow = EventRow & {\n session_has_pending: number | bigint\n}\n\nconst RECLAIM_BATCH = 256\n\nexport function sqlite(options: SqliteStoreOptions = {}): SqliteStore {\n const path = options.path ?? '.a2/dev.db'\n const clock = options.clock ?? SYSTEM_CLOCK\n const generateId = options.ids ?? RANDOM_IDS\n\n if (path !== ':memory:') {\n // The dev-default backend only ever runs outside production, but\n // bundlers can't know that: without the ignore, Turbopack sees a\n // dynamic fs path and traces the entire project into the output.\n mkdirSync(dirname(resolve(/* turbopackIgnore: true */ path)), {\n recursive: true,\n })\n }\n const db = new DatabaseSync(path)\n // busy_timeout before anything that takes locks: concurrent\n // cold-opens (several instances booting at once) race the WAL switch\n // and schema creation.\n db.exec('pragma busy_timeout = 5000')\n setWalJournalMode(db)\n db.exec('pragma synchronous = normal')\n db.exec(SCHEMA)\n\n const insertEvent = db.prepare(\n `insert into a2_events\n (session_id, idx, event_type, payload, event_id, created_at,\n cause, lane, lane_ready, processed_at)\n select ?, ?, ?, ?, ?, ?, ?, ?,\n case\n when ? is null then 1\n when not exists (\n select 1 from a2_events\n where session_id = ? and lane = ? and processed_at is null\n and idx > ?\n ) then 1\n else 0\n end,\n ?`,\n )\n const promoteLaneHead = db.prepare(\n `update a2_events set lane_ready = 1\n where session_id = ?\n and idx = (\n select idx from a2_events\n where session_id = ? and lane = ? and processed_at is null\n and idx > ?\n order by idx limit 1\n )`,\n )\n const appendStatus = db.prepare(\n `select coalesce(\n (select max(idx) from a2_events where session_id = ?),\n 0\n ) as max,\n exists(\n select 1 from a2_events\n where session_id = ? and processed_at is null and idx > ?\n ) as has_pending`,\n )\n const upsertPresence = db.prepare(\n `insert into a2_presence (ns, participant, field, value, seen, at, expires_at)\n values (?, ?, ?, ?, ?, ?, ?)\n on conflict (ns, participant, field) do update set\n value = excluded.value,\n seen = excluded.seen,\n at = excluded.at,\n expires_at = excluded.expires_at\n where a2_presence.at <= excluded.at`,\n )\n const deletePresence = db.prepare(\n `delete from a2_presence\n where ns = ? and participant = ? and field = ? and at <= ?`,\n )\n const sweepPresence = db.prepare(\n 'delete from a2_presence where ns = ? and expires_at <= ?',\n )\n const selectPresence = db.prepare(\n `select participant, field, value, seen, at, expires_at\n from a2_presence\n where ns = ? and expires_at > ?\n order by participant, field`,\n )\n const selectByIds = (count: number) =>\n db.prepare(\n `select stored.*,\n exists(\n select 1 from a2_events pending\n where pending.session_id = ?\n and pending.processed_at is null\n and pending.idx > ?\n ) as session_has_pending\n from a2_events stored\n where stored.event_id in (${Array.from({ length: count }, () => '?').join(', ')})\n order by stored.idx`,\n )\n\n const selectClearIndex = db.prepare(\n 'select clear_index from a2_clears where session_id = ?',\n )\n const clearIndexOf = (sessionId: string): number =>\n Number(\n (\n selectClearIndex.get(sessionId) as\n { clear_index: number | bigint } | undefined\n )?.clear_index ?? 0,\n )\n const deleteEventById = db.prepare(\n 'delete from a2_events where session_id = ? and event_id = ?',\n )\n\n /**\n * The rows owning `ids`. This session's rows below its clear marker do\n * not own their ids: they are deleted here so the ids can be reused.\n */\n const liveOwnersOf = (\n sessionId: string,\n clearIndex: number,\n ids: readonly string[],\n ): ExistingEventRow[] => {\n if (ids.length === 0) return []\n const rows = selectByIds(ids.length).all(\n sessionId,\n clearIndex,\n ...ids,\n ) as unknown as ExistingEventRow[]\n return rows.filter((row) => {\n if (row.session_id !== sessionId || Number(row.idx) >= clearIndex)\n return true\n deleteEventById.run(sessionId, row.event_id)\n return false\n })\n }\n\n const selectCauseParent = db.prepare(\n 'select attempt_count, failed_at from a2_events where session_id = ? and idx = ?',\n )\n /** The attempt-currency fence shared by handler appends and clears. */\n const assertCurrentCause = ({\n sessionId,\n cause,\n clearIndex,\n }: {\n sessionId: string\n cause: EventCause\n clearIndex: number\n }): void => {\n if (cause.index <= clearIndex)\n throw supersededAttemptError(sessionId, cause)\n const parent = selectCauseParent.get(sessionId, cause.index) as\n { attempt_count: number | bigint; failed_at: unknown } | undefined\n if (!parent) {\n throw new TypeError(\n `no event at index ${cause.index} in session '${sessionId}'`,\n )\n }\n if (\n Number(parent.attempt_count) !== cause.attempt ||\n parent.failed_at !== null\n ) {\n throw supersededAttemptError(sessionId, cause)\n }\n }\n\n const tx = <T>(fn: () => T): T => {\n db.exec('begin immediate')\n try {\n const result = fn()\n db.exec('commit')\n return result\n } catch (err) {\n try {\n db.exec('rollback')\n } catch {\n // Rollback can fail if the transaction never started; the\n // original error is what matters.\n }\n throw err\n }\n }\n\n /** A dead-lettered event's snapshot pins, and history no pin keeps. */\n const releasePins = (sessionId: string, index: number): void => {\n db.prepare(\n 'delete from a2_snapshot_pins where session_id = ? and event_index = ?',\n ).run(sessionId, index)\n db.prepare(\n `delete from a2_snapshot_history\n where session_id = ?\n and not exists (\n select 1 from a2_snapshot_pins pin\n where pin.session_id = a2_snapshot_history.session_id\n and pin.reducer_name = a2_snapshot_history.reducer_name\n and pin.up_to_index = a2_snapshot_history.up_to_index\n )`,\n ).run(sessionId)\n }\n return {\n async append(sessionId, events, appendOptions) {\n if (events.length === 0) {\n return wrap(() => {\n const status = appendStatus.get(\n sessionId,\n sessionId,\n clearIndexOf(sessionId),\n ) as AppendStatusRow\n return {\n events: [],\n hasPending: Number(status.has_pending) === 1,\n }\n })\n }\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n const supplied = events.filter((e) => e.id !== undefined)\n const suppliedIds = supplied.map((e) => e.id as string)\n if (new Set(suppliedIds).size !== suppliedIds.length) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'batch contains the same event id more than once',\n )\n }\n if (suppliedIds.length > 0) {\n const existing = liveOwnersOf(sessionId, clearIndex, suppliedIds)\n if (existing.length > 0) {\n const foreign = existing.find(\n (row) => row.session_id !== sessionId,\n )\n if (foreign) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n `event id '${foreign.event_id}' already exists in another session`,\n )\n }\n if (existing.length === events.length) {\n return {\n events: idempotentReplay(events, existing.map(toStored)),\n hasPending: Number(existing[0]!.session_has_pending) === 1,\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\n const originIndex = appendOptions?.originIndex\n if (originIndex !== undefined && originIndex <= clearIndex) {\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\n // while its causal attempt is still the parent's latest\n // (store.ts `append`). `begin immediate` serializes this read\n // against a concurrent claim.\n for (const e of events) {\n if (e.cause)\n assertCurrentCause({ sessionId, cause: e.cause, clearIndex })\n }\n\n const status = appendStatus.get(\n sessionId,\n sessionId,\n clearIndex,\n ) as AppendStatusRow\n const base = Number(status.max)\n const now = clock.now().getTime()\n const inserted: StoredEvent[] = events.map((e, i) => {\n const id = e.id ?? generateId()\n const index = base + 1 + i\n insertEvent.run(\n sessionId,\n index,\n e.type,\n JSON.stringify(e.payload) ?? 'null',\n id,\n now,\n e.cause ? JSON.stringify(e.cause) : null,\n e.lane ?? null,\n e.lane ?? null,\n sessionId,\n e.lane ?? null,\n clearIndex,\n e.settled ? now : null,\n )\n return {\n id,\n type: e.type,\n payload: structuredClone(e.payload),\n index,\n sessionId,\n createdAt: new Date(now),\n cause: e.cause ? { ...e.cause } : null,\n lane: e.lane ?? null,\n processedAt: e.settled ? new Date(now) : null,\n processedByAttempt: null,\n returnedEventIds: null,\n firstClaimedAt: null,\n lastClaimedAt: null,\n attemptCount: 0,\n failureCount: 0,\n lastFailedAt: null,\n lastFailedAttempt: null,\n lastError: null,\n failedAt: null,\n claimHolder: null,\n claimExpiresAt: null,\n }\n })\n return {\n events: inserted,\n hasPending:\n Number(status.has_pending) === 1 ||\n events.some((event) => event.settled !== true),\n }\n }),\n )\n },\n\n async read(sessionId, opts) {\n return wrap(() => {\n // Reads start at the latest clear marker.\n const conditions = [\n 'session_id = ?',\n 'idx >= coalesce((select clear_index from a2_clears where session_id = ?), 0)',\n ]\n const params: (string | number)[] = [sessionId, sessionId]\n if (opts?.afterIndex !== undefined) {\n conditions.push('idx > ?')\n params.push(opts.afterIndex)\n }\n if (opts?.throughIndex !== undefined) {\n conditions.push('idx <= ?')\n params.push(opts.throughIndex)\n }\n const rows = db\n .prepare(\n `select * from a2_events where ${conditions.join(' and ')} order by idx`,\n )\n .all(...params) as unknown as EventRow[]\n return rows.map(toStored)\n })\n },\n\n async claimAvailable({\n sessionId,\n holder,\n ttlMs,\n expiresAtMs,\n excludeIndexes = [],\n maxFailures,\n }) {\n return wrap(() =>\n tx(() => {\n const now = clock.now().getTime()\n const expiresAt = expiresAtMs ?? now + ttlMs\n const clearIndex = clearIndexOf(sessionId)\n const exclusion =\n excludeIndexes.length === 0\n ? ''\n : `and event.idx not in (${excludeIndexes.map(() => '?').join(', ')})`\n const eligible = db\n .prepare(\n `select event.idx, event.claim_holder, event.failure_count\n from a2_events event\n where event.session_id = ?\n and event.processed_at is null\n and event.failed_at is null\n and event.lane_ready = 1\n and event.idx > ?\n and (event.claim_expires_at is null or event.claim_expires_at <= ?)\n ${exclusion}\n order by event.idx`,\n )\n .all(\n sessionId,\n clearIndex,\n now,\n ...excludeIndexes,\n ) as unknown as Array<{\n idx: number | bigint\n claim_holder: string | null\n failure_count: number | bigint\n }>\n if (eligible.length > 0) {\n const update = db.prepare(\n `update a2_events set\n attempt_count = attempt_count + 1,\n failure_count = failure_count\n + case when claim_holder is null then 0 else 1 end,\n first_claimed_at = coalesce(first_claimed_at, ?),\n last_claimed_at = ?,\n claim_holder = ?,\n claim_expires_at = ?\n where session_id = ? and idx = ?\n returning *`,\n )\n // Advancing the attempt fences the lapsed one; nothing runs.\n const deadLetter = () =>\n db.prepare(\n `update a2_events set\n attempt_count = attempt_count + 1,\n failure_count = failure_count + 1,\n failed_at = ?,\n claim_holder = null,\n claim_expires_at = null\n where session_id = ? and idx = ?\n returning *`,\n )\n const events = eligible.map((row) => {\n // A lapsed claim's takeover that spends the last failed attempt.\n const exhausted =\n row.claim_holder !== null &&\n maxFailures !== undefined &&\n Number(row.failure_count) + 1 >= maxFailures\n const claimed = (\n exhausted\n ? deadLetter().get(now, sessionId, row.idx)\n : update.get(now, now, holder, expiresAt, sessionId, row.idx)\n ) as EventRow\n if (exhausted) releasePins(sessionId, Number(row.idx))\n const stored = toStored(claimed)\n if (clearIndex > 0) stored.clearIndex = clearIndex\n return stored\n })\n return { outcome: 'claimed', events }\n }\n\n const active = db\n .prepare(\n `select min(claim_expires_at) as retry_at\n from a2_events event\n where session_id = ?\n and processed_at is null\n and failed_at is null\n and lane_ready = 1\n and idx > ?\n and claim_expires_at > ?\n ${exclusion}`,\n )\n .get(sessionId, clearIndex, now, ...excludeIndexes) as {\n retry_at: number | bigint | null\n }\n return active.retry_at === null\n ? { outcome: 'settled' }\n : { outcome: 'busy', retryAt: new Date(Number(active.retry_at)) }\n }),\n )\n },\n\n async renewClaims({ sessionId, holder, claims, ttlMs, expiresAtMs }) {\n if (claims.length === 0) return { renewed: [], superseded: [] }\n return wrap(() =>\n tx(() => {\n const now = clock.now().getTime()\n const expiresAt = expiresAtMs ?? now + ttlMs\n const renewed: number[] = []\n const superseded: number[] = []\n const currentAttempt = db.prepare(\n 'select attempt_count from a2_events where session_id = ? and idx = ?',\n )\n const extend = db.prepare(\n `update a2_events set claim_expires_at = ?\n where session_id = ?\n and idx = ?\n and attempt_count = ?\n and processed_at is null\n and failed_at is null\n and claim_holder = ?\n and claim_expires_at > ?`,\n )\n const clearIndex = clearIndexOf(sessionId)\n for (const claim of claims) {\n if (claim.index <= clearIndex) {\n superseded.push(claim.index)\n continue\n }\n const row = currentAttempt.get(sessionId, claim.index) as\n { attempt_count: number | bigint } | undefined\n if (!row) continue\n if (Number(row.attempt_count) > claim.attempt) {\n superseded.push(claim.index)\n continue\n }\n const updated = extend.run(\n expiresAt,\n sessionId,\n claim.index,\n claim.attempt,\n holder,\n now,\n )\n if (Number(updated.changes) === 1) renewed.push(claim.index)\n }\n return {\n renewed: renewed.toSorted((a, b) => a - b),\n superseded: superseded.toSorted((a, b) => a - b),\n }\n }),\n )\n },\n\n async completeAttempt({ sessionId, index, attempt, events }) {\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n if (index <= clearIndex) return { outcome: 'superseded' }\n const parent = db\n .prepare('select * from a2_events where session_id = ? and idx = ?')\n .get(sessionId, index) as EventRow | undefined\n if (!parent) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n const ids = events.map((event) => event.id)\n if (new Set(ids).size !== ids.length) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'returned event batch contains the same event id more than once',\n )\n }\n\n if (parent.processed_at !== null) {\n if (Number(parent.processed_by_attempt) !== attempt) {\n return { outcome: 'superseded' }\n }\n const returnedIds =\n parent.returned_event_ids === null\n ? null\n : decodeReturnedEventIds(parent.returned_event_ids)\n if (\n returnedIds === null ||\n returnedIds.length !== ids.length ||\n returnedIds.some((id, offset) => id !== ids[offset])\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'completed attempt does not match the returned event batch',\n )\n }\n const existing =\n ids.length === 0\n ? []\n : (selectByIds(ids.length).all(\n sessionId,\n clearIndex,\n ...ids,\n ) as unknown as ExistingEventRow[])\n const byId = new Map(existing.map((row) => [row.event_id, row]))\n const ordered = ids.map((id) => byId.get(id))\n if (\n ordered.some(\n (row) =>\n !row ||\n row.session_id !== sessionId ||\n row.cause === null ||\n toCause(row.cause)?.index !== index ||\n toCause(row.cause)?.attempt !== attempt,\n )\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'returned event retry does not match the committed batch',\n )\n }\n return {\n outcome: 'completed',\n events: idempotentReplay(\n events,\n (ordered as ExistingEventRow[]).map(toStored),\n ),\n }\n }\n if (\n Number(parent.attempt_count) !== attempt ||\n parent.claim_holder === null ||\n parent.failed_at !== null\n ) {\n return { outcome: 'superseded' }\n }\n\n if (liveOwnersOf(sessionId, clearIndex, ids).length > 0) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'returned event batch contains an already-appended event id',\n )\n }\n\n const status = appendStatus.get(\n sessionId,\n sessionId,\n clearIndex,\n ) as AppendStatusRow\n const now = clock.now().getTime()\n db.prepare(\n `update a2_events set\n processed_at = ?,\n processed_by_attempt = ?,\n returned_event_ids = ?,\n lane_ready = 0,\n claim_holder = null,\n claim_expires_at = null\n where session_id = ? and idx = ?`,\n ).run(now, attempt, JSON.stringify(ids), sessionId, index)\n db.prepare(\n 'delete from a2_snapshot_pins where session_id = ? and event_index = ?',\n ).run(sessionId, index)\n db.prepare(\n `delete from a2_snapshot_history\n where session_id = ?\n and not exists (\n select 1 from a2_snapshot_pins pin\n where pin.session_id = a2_snapshot_history.session_id\n and pin.reducer_name = a2_snapshot_history.reducer_name\n and pin.up_to_index = a2_snapshot_history.up_to_index\n )`,\n ).run(sessionId)\n if (parent.lane !== null) {\n promoteLaneHead.run(sessionId, sessionId, parent.lane, clearIndex)\n }\n const inserted = events.map((event, offset) => {\n const id = event.id\n const childIndex = Number(status.max) + offset + 1\n const cause = { index, attempt }\n insertEvent.run(\n sessionId,\n childIndex,\n event.type,\n JSON.stringify(event.payload) ?? 'null',\n id,\n now,\n JSON.stringify(cause),\n event.lane ?? null,\n event.lane ?? null,\n sessionId,\n event.lane ?? null,\n clearIndex,\n event.settled ? now : null,\n )\n return toStored(\n db\n .prepare(\n 'select * from a2_events where session_id = ? and idx = ?',\n )\n .get(sessionId, childIndex) as EventRow,\n )\n })\n return { outcome: 'completed', events: inserted }\n }),\n )\n },\n\n async failAttempt({\n sessionId,\n index,\n attempt,\n error,\n maxFailures,\n retryAfterMs,\n }) {\n return wrap(() =>\n tx(() => {\n if (index <= clearIndexOf(sessionId)) {\n return { outcome: 'superseded', failureCount: 0 }\n }\n const current = db\n .prepare('select * from a2_events where session_id = ? and idx = ?')\n .get(sessionId, index) as EventRow | undefined\n if (!current) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n const failureCount = Number(current.failure_count)\n if (\n current.processed_at !== null ||\n Number(current.attempt_count) !== attempt\n ) {\n return { outcome: 'superseded', failureCount }\n }\n if (current.failed_at !== null) {\n return { outcome: 'dead_lettered', failureCount }\n }\n if (\n current.claim_holder === null &&\n current.last_failed_attempt !== null &&\n Number(current.last_failed_attempt) === attempt\n ) {\n return { outcome: 'failed', failureCount }\n }\n if (current.claim_holder === null) {\n return { outcome: 'superseded', failureCount }\n }\n\n const now = clock.now().getTime()\n const nextFailureCount = failureCount + 1\n const deadLettered = nextFailureCount >= maxFailures\n db.prepare(\n `update a2_events set\n failure_count = ?,\n last_error = ?,\n last_failed_at = ?,\n last_failed_attempt = ?,\n failed_at = ?,\n claim_holder = null,\n claim_expires_at = ?\n where session_id = ? and idx = ?`,\n ).run(\n nextFailureCount,\n error,\n now,\n attempt,\n deadLettered ? now : null,\n deadLettered || retryAfterMs === undefined\n ? null\n : now + retryAfterMs,\n sessionId,\n index,\n )\n if (deadLettered) releasePins(sessionId, index)\n return {\n outcome: deadLettered ? 'dead_lettered' : 'failed',\n failureCount: nextFailureCount,\n }\n }),\n )\n },\n\n async deadLetters(sessionId) {\n return wrap(() => {\n const rows = db\n .prepare(\n `select * from a2_events\n where session_id = ?\n and processed_at is null\n and failed_at is not null\n and idx > coalesce(\n (select clear_index from a2_clears where session_id = ?), 0\n )\n order by idx`,\n )\n .all(sessionId, sessionId) as unknown as EventRow[]\n return rows.map(toStored)\n })\n },\n\n async redrive({ sessionId, index, action }) {\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n const row =\n index > clearIndex\n ? (db\n .prepare(\n 'select lane, processed_at, failed_at from a2_events where session_id = ? and idx = ?',\n )\n .get(sessionId, index) as\n | Pick<EventRow, 'lane' | 'processed_at' | 'failed_at'>\n | undefined)\n : undefined\n const redriven =\n row !== undefined &&\n row.processed_at === null &&\n row.failed_at !== null\n if (redriven && action === 'skip') {\n db.prepare(\n `update a2_events set processed_at = ?, lane_ready = 0\n where session_id = ? and idx = ?`,\n ).run(clock.now().getTime(), sessionId, index)\n if (row.lane !== null) {\n promoteLaneHead.run(sessionId, sessionId, row.lane, clearIndex)\n }\n } else if (redriven) {\n db.prepare(\n `update a2_events set failed_at = null, failure_count = 0\n where session_id = ? and idx = ?`,\n ).run(sessionId, index)\n }\n const status = appendStatus.get(\n sessionId,\n sessionId,\n clearIndex,\n ) as AppendStatusRow\n return { redriven, hasPending: Number(status.has_pending) === 1 }\n }),\n )\n },\n\n async readState(sessionId, reducerName, stateOptions) {\n return wrap(() => {\n const throughIndex = stateOptions?.throughIndex ?? null\n const snapshotThroughIndex =\n stateOptions?.snapshotThroughIndex ?? throughIndex\n const rows = db\n .prepare(\n `with boundary as materialized (\n select coalesce(\n (select clear_index from a2_clears where session_id = ?), 0\n ) as clear_index\n ), head as materialized (\n select up_to_index, state, updated_at\n from a2_snapshots\n where session_id = ? and reducer_name = ?\n and up_to_index >= (select clear_index from boundary)\n ), snapshot as materialized (\n select up_to_index, state from (\n select up_to_index, state from head\n where (? is null or up_to_index <= ?)\n union all\n select up_to_index, state\n from a2_snapshot_history\n where session_id = ? and reducer_name = ?\n and up_to_index >= (select clear_index from boundary)\n and (? is null or up_to_index <= ?)\n ) order by up_to_index desc limit 1\n )\n select 0 as row_order, 'snapshot' as row_kind,\n (select clear_index from boundary) as clear_index,\n (select up_to_index from head) as head_index,\n (select up_to_index from snapshot) as snapshot_index,\n (select state from snapshot) as snapshot_state,\n null as session_id, null as idx, null as event_type,\n null as payload, null as event_id, null as created_at,\n null as cause, null as lane, null as lane_ready,\n null as processed_at,\n null as processed_by_attempt, null as returned_event_ids,\n null as first_claimed_at,\n null as last_claimed_at, null as attempt_count,\n null as failure_count, null as last_failed_at,\n null as last_failed_attempt, null as last_error,\n null as failed_at, null as claim_holder,\n null as claim_expires_at\n union all\n select 1 as row_order, 'event' as row_kind,\n null as clear_index,\n (select up_to_index from head) as head_index,\n null as snapshot_index, null as snapshot_state,\n event.*\n from a2_events as event\n where event.session_id = ?\n and event.idx > max(\n coalesce((select up_to_index from snapshot), 0),\n (select clear_index from boundary) - 1\n )\n and (? is null or event.idx <= ?)\n order by row_order, idx`,\n )\n .all(\n sessionId,\n sessionId,\n reducerName,\n snapshotThroughIndex,\n snapshotThroughIndex,\n sessionId,\n reducerName,\n snapshotThroughIndex,\n snapshotThroughIndex,\n sessionId,\n throughIndex,\n throughIndex,\n ) as unknown as StateReadRow[]\n const snapshot = rows.find((row) => row.row_kind === 'snapshot')\n const clearIndex = Number(snapshot?.clear_index ?? 0)\n return {\n ...(clearIndex > 0 ? { clearIndex } : {}),\n headIndex:\n snapshot?.head_index === null || snapshot?.head_index === undefined\n ? null\n : Number(snapshot.head_index),\n snapshot:\n snapshot?.snapshot_index !== null &&\n snapshot?.snapshot_index !== undefined\n ? {\n index: Number(snapshot.snapshot_index),\n state: JSON.parse(snapshot.snapshot_state!),\n }\n : null,\n events: rows\n .filter(\n (row): row is StateReadRow & EventRow => row.row_kind === 'event',\n )\n .map(toEvent),\n }\n })\n },\n\n async putSnapshots(sessionId, reducerName, writes) {\n await wrap(() =>\n tx(() => {\n const selectHead = db.prepare(\n `select up_to_index, state, updated_at from a2_snapshots\n where session_id = ? and reducer_name = ?`,\n )\n const unfinished = db.prepare(\n `select 1 from a2_events\n where session_id = ? and idx = ? and idx > ?\n and processed_at is null and failed_at is null`,\n )\n const insertPin = db.prepare(\n `insert or ignore into a2_snapshot_pins\n (session_id, event_index, reducer_name, up_to_index)\n values (?, ?, ?, ?)`,\n )\n const hasPin = db.prepare(\n `select 1 from a2_snapshot_pins\n where session_id = ? and reducer_name = ? and up_to_index = ?\n limit 1`,\n )\n const putHistory = db.prepare(\n `insert into a2_snapshot_history\n (session_id, reducer_name, up_to_index, state, updated_at)\n values (?, ?, ?, ?, ?)\n on conflict (session_id, reducer_name, up_to_index) do update set\n state = excluded.state, updated_at = excluded.updated_at`,\n )\n const putHead = db.prepare(\n `insert into a2_snapshots\n (session_id, reducer_name, up_to_index, state, updated_at)\n values (?, ?, ?, ?, ?)\n on conflict (session_id, reducer_name) do update set\n up_to_index = excluded.up_to_index,\n state = excluded.state,\n updated_at = excluded.updated_at\n where excluded.up_to_index >= a2_snapshots.up_to_index`,\n )\n const deleteHistory = db.prepare(\n `delete from a2_snapshot_history\n where session_id = ? and reducer_name = ? and up_to_index = ?`,\n )\n const clearIndex = clearIndexOf(sessionId)\n for (const write of writes.toSorted((a, b) => a.index - b.index)) {\n // A fold from before the latest clear describes a lifetime that is gone.\n if (write.index < clearIndex) continue\n for (const eventIndex of write.pinEventIndexes ?? []) {\n if (unfinished.get(sessionId, eventIndex, clearIndex)) {\n insertPin.run(sessionId, eventIndex, reducerName, write.index)\n }\n }\n const head = selectHead.get(sessionId, reducerName) as\n SnapshotHeadRow | undefined\n const now = clock.now().getTime()\n if (!head || write.index >= Number(head.up_to_index)) {\n if (\n head &&\n write.index > Number(head.up_to_index) &&\n Number(head.up_to_index) >= clearIndex &&\n hasPin.get(sessionId, reducerName, Number(head.up_to_index))\n ) {\n putHistory.run(\n sessionId,\n reducerName,\n Number(head.up_to_index),\n head.state,\n Number(head.updated_at),\n )\n }\n putHead.run(\n sessionId,\n reducerName,\n write.index,\n JSON.stringify(write.state) ?? 'null',\n now,\n )\n deleteHistory.run(sessionId, reducerName, write.index)\n } else if (\n write.index < Number(head.up_to_index) &&\n hasPin.get(sessionId, reducerName, write.index)\n ) {\n putHistory.run(\n sessionId,\n reducerName,\n write.index,\n JSON.stringify(write.state) ?? 'null',\n now,\n )\n }\n }\n }),\n )\n },\n\n presence: {\n async set(ns, participant, values, meta) {\n const entries = Object.entries(values)\n if (entries.length === 0) return\n wrap(() =>\n tx(() => {\n const atMs = meta.at.getTime()\n // Expiry anchors on the storage clock — the sender's `at`\n // orders writes but never anchors their lifetime.\n const expiresAtMs = clock.now().getTime() + meta.ttlMs\n for (const [field, value] of entries) {\n // Field-wise LWW: a stored row with a strictly newer `at`\n // survives; ties go to the incoming write (memory parity).\n if (value === null) {\n deletePresence.run(ns, participant, field, atMs)\n } else {\n upsertPresence.run(\n ns,\n participant,\n field,\n JSON.stringify(value) ?? 'null',\n meta.seen,\n atMs,\n expiresAtMs,\n )\n }\n }\n }),\n )\n },\n\n async read(ns) {\n return wrap(() => {\n const now = clock.now().getTime()\n // The sweep is opportunistic cleanup; the select's filter\n // alone keeps expired rows out of this result.\n sweepPresence.run(ns, now)\n const rows = selectPresence.all(ns, now) as unknown as PresenceDbRow[]\n return rows.map((row) => ({\n participant: row.participant,\n field: row.field,\n value: JSON.parse(row.value) as unknown,\n seen: Number(row.seen),\n at: new Date(Number(row.at)),\n expiresAt: new Date(Number(row.expires_at)),\n }))\n })\n },\n },\n\n stream(sessionId, opts) {\n const readAfter = (afterIndex: number): StoredEvent[] =>\n wrap(() => {\n // A cursor below the latest marker resumes at it.\n const rows = db\n .prepare(\n `select * from a2_events\n where session_id = ? and idx > ?\n and idx >= coalesce((select clear_index from a2_clears where session_id = ?), 0)\n order by idx`,\n )\n .all(sessionId, afterIndex, sessionId) as unknown as EventRow[]\n return rows.map(toStored)\n })\n return pollingStream(\n readAfter,\n opts?.startAfter !== undefined ? { startAfter: opts.startAfter } : {},\n )\n },\n\n async clear({ sessionId, ifLastIndex, cause }) {\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n if (cause) assertCurrentCause({ sessionId, cause, clearIndex })\n const head = Number(\n (\n appendStatus.get(\n sessionId,\n sessionId,\n clearIndex,\n ) as AppendStatusRow\n ).max,\n )\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 = head + 1\n const now = clock.now().getTime()\n insertEvent.run(\n sessionId,\n marker,\n CLEAR_EVENT_TYPE,\n 'null',\n generateId(),\n now,\n null,\n null,\n null,\n sessionId,\n null,\n clearIndex,\n now,\n )\n db.prepare(\n `insert into a2_clears (session_id, clear_index) values (?, ?)\n on conflict (session_id) do update set clear_index = excluded.clear_index`,\n ).run(sessionId, marker)\n return { cleared: true, index: marker, clearIndex: marker }\n }),\n )\n },\n\n async reclaim(sessionId) {\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n if (clearIndex === 0) return { deleted: 0, done: true }\n const last = db\n .prepare(\n `select max(idx) as last from (\n select idx from a2_events\n where session_id = ? and idx < ?\n order by idx limit ?\n )`,\n )\n .get(sessionId, clearIndex, RECLAIM_BATCH) as {\n last: number | bigint | null\n }\n let deleted = 0\n if (last.last !== null) {\n const through = Number(last.last)\n deleted = Number(\n db\n .prepare(\n 'delete from a2_events where session_id = ? and idx <= ?',\n )\n .run(sessionId, through).changes,\n )\n db.prepare(\n 'delete from a2_snapshot_pins where session_id = ? and event_index <= ?',\n ).run(sessionId, through)\n }\n const done =\n db\n .prepare(\n 'select 1 from a2_events where session_id = ? and idx < ? limit 1',\n )\n .get(sessionId, clearIndex) === undefined\n // Writes serialize with the clear, so none lands below the marker later.\n if (done) {\n for (const table of [\n 'a2_snapshot_pins',\n 'a2_snapshot_history',\n 'a2_snapshots',\n ]) {\n db.prepare(\n `delete from ${table} where session_id = ? and up_to_index < ?`,\n ).run(sessionId, clearIndex)\n }\n }\n return { deleted, done }\n }),\n )\n },\n\n close() {\n db.close()\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"store-sqlite.js","names":[],"sources":["../src/store-sqlite.ts"],"sourcesContent":["/**\n * experimental-a2/store-sqlite — sqlite store backend (the dev default, `.a2/dev.db`).\n *\n * Built on `node:sqlite` (Node ≥ 22.13) so it ships with zero\n * dependencies. Implements the A2Store interface; the conformance suite\n * in test/conformance is the executable contract.\n *\n * The spec's `index` column is stored as `idx` (a2-implementation.md\n * §11 — `index` collides with a reserved word) and mapped back at the\n * API boundary. Timestamps are epoch milliseconds written from the\n * injected clock — never SQL `now()` — so tests can time-travel against\n * real storage.\n */\n\nimport { mkdirSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { DatabaseSync } from 'node:sqlite'\nimport { A2Error, supersededAttemptError } from './errors.ts'\nimport { CLEAR_EVENT_TYPE, sessionClearedError } from './session-clear.ts'\nimport { idempotentReplay } from './idempotent-replay.ts'\nimport {\n cursorKeyOf,\n decodeCause,\n decodeReturnedEventIds,\n idleLogCursor,\n} from './store-codec.ts'\nimport { pollingStream } from './store-polling.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 HeadEvent,\n type HeadRef,\n type IdSource,\n type StoreConsumer,\n type StoredCursor,\n type StoredEvent,\n} from './store.ts'\n\nexport type SqliteStoreOptions = {\n /** Database file path. Defaults to `.a2/dev.db`; `:memory:` works. */\n path?: string\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\nexport type SqliteStore = A2Store & {\n /** Close the underlying database handle. */\n close(): void\n}\n\n/** The cursor layout; databases from before it are rejected, not migrated. */\nconst LAYOUT = 'cursors-1'\n\nconst SCHEMA = `\ncreate table if not exists a2_meta (\n key text not null primary key,\n value text not null\n) strict;\n\ncreate table if not exists a2_events (\n session_id text not null,\n idx integer not null,\n event_type text not null,\n payload text not null,\n event_id text not null,\n created_at integer not null,\n cause text,\n primary key (session_id, idx)\n) strict;\n\ncreate unique index if not exists a2_events_session_event_id\n on a2_events (session_id, event_id);\ndrop index if exists a2_events_event_id;\ncreate index if not exists a2_events_types on a2_events (session_id, event_type, idx);\n\ncreate table if not exists a2_cursors (\n session_id text not null,\n consumer text not null,\n cursor_key text not null,\n lane text,\n kind text not null,\n position integer not null,\n head integer,\n types text,\n attempt_count integer not null default 0,\n first_claimed_at integer,\n last_claimed_at integer,\n claim_holder text,\n claim_expires_at integer,\n failure_count integer not null default 0,\n last_failed_at integer,\n last_failed_attempt integer,\n last_error text,\n failed_at integer,\n completed_idx integer,\n completed_attempt integer,\n returned_event_ids text,\n primary key (session_id, consumer, cursor_key)\n) strict;\n\ncreate index if not exists a2_cursors_heads\n on a2_cursors (session_id, head) where head is not null;\n\ncreate table if not exists a2_lane_events (\n session_id text not null,\n consumer text not null,\n lane text not null,\n idx integer not null,\n done integer not null default 0,\n completed_attempt integer,\n returned_event_ids text,\n primary key (session_id, consumer, lane, idx)\n) strict;\n\ncreate table if not exists a2_snapshots (\n session_id text not null,\n reducer_name text not null,\n up_to_index integer not null,\n state text not null,\n updated_at integer not null,\n primary key (session_id, reducer_name)\n) strict;\n\ncreate table if not exists a2_snapshot_history (\n session_id text not null,\n reducer_name text not null,\n up_to_index integer not null,\n state text not null,\n updated_at integer not null,\n primary key (session_id, reducer_name, up_to_index)\n) strict;\n\ncreate table if not exists a2_snapshot_pins (\n session_id text not null,\n consumer text not null,\n cursor_key text not null,\n event_index integer not null,\n reducer_name text not null,\n up_to_index integer not null,\n primary key (session_id, consumer, cursor_key, event_index, reducer_name, up_to_index)\n) strict;\n\ncreate index if not exists a2_snapshot_pins_checkpoint\n on a2_snapshot_pins (session_id, reducer_name, up_to_index);\n\ncreate table if not exists a2_clears (\n session_id text not null primary key,\n clear_index integer not null\n) strict;\n\ncreate table if not exists a2_presence (\n ns text not null,\n participant text not null,\n field text not null,\n value text not null,\n seen integer not null,\n at integer not null,\n expires_at integer not null,\n primary key (ns, participant, field)\n) strict;\n`\n\n/**\n * Switching journal modes takes an exclusive lock, and — unlike normal\n * statements — the switch can return SQLITE_BUSY without consulting the\n * busy handler while several connections race it (instances cold-booting\n * against one file). Bounded synchronous retry; the window is boot-only\n * and tiny. Found by the multi-process torture test.\n */\nconst setWalJournalMode = (db: DatabaseSync): void => {\n for (let attempt = 0; ; attempt += 1) {\n try {\n db.exec('pragma journal_mode = wal')\n return\n } catch (err) {\n const busy = (err as { errcode?: number }).errcode === 5\n if (!busy || attempt >= 100) throw err\n // Synchronous 5ms sleep — sqlite() is a sync constructor.\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5)\n }\n }\n}\n\nconst wrap = <T>(fn: () => T): T => {\n try {\n return fn()\n } catch (err) {\n if (err instanceof A2Error || err instanceof TypeError) throw err\n throw new A2Error('STORE_UNAVAILABLE', 'sqlite store operation failed', {\n cause: err,\n })\n }\n}\n\nconst toDate = (ms: number | bigint | null): Date | null =>\n ms === null ? null : new Date(Number(ms))\n\nconst toNumber = (value: number | bigint | null): number | null =>\n value === null ? null : Number(value)\n\ntype EventRow = {\n session_id: string\n idx: number | bigint\n event_type: string\n payload: string\n event_id: string\n created_at: number | bigint\n cause: string | null\n}\n\ntype CursorRow = {\n session_id: string\n consumer: string\n cursor_key: string\n lane: string | null\n kind: 'log' | 'lane' | 'event'\n position: number | bigint\n head: number | bigint | null\n types: string | null\n attempt_count: number | bigint\n first_claimed_at: number | bigint | null\n last_claimed_at: number | bigint | null\n claim_holder: string | null\n claim_expires_at: number | bigint | null\n failure_count: number | bigint\n last_failed_at: number | bigint | null\n last_failed_attempt: number | bigint | null\n last_error: string | null\n failed_at: number | bigint | null\n completed_idx: number | bigint | null\n completed_attempt: number | bigint | null\n returned_event_ids: string | null\n}\n\n/** A cursor joined with its head event's columns, prefixed `e_`. */\ntype HeadRow = CursorRow & {\n e_idx: number | bigint\n e_event_type: string\n e_payload: string\n e_event_id: string\n e_created_at: number | bigint\n e_cause: string | null\n}\n\nconst toStored = (row: EventRow): StoredEvent => ({\n id: row.event_id,\n type: row.event_type,\n payload: JSON.parse(row.payload),\n index: Number(row.idx),\n sessionId: row.session_id,\n createdAt: new Date(Number(row.created_at)),\n cause: decodeCause(row.cause),\n})\n\nconst toEvent = (row: EventRow): Event => ({\n id: row.event_id,\n type: row.event_type,\n payload: JSON.parse(row.payload),\n index: Number(row.idx),\n sessionId: row.session_id,\n createdAt: new Date(Number(row.created_at)),\n})\n\nconst toCursor = (row: CursorRow): StoredCursor => ({\n consumer: row.consumer,\n lane: row.lane,\n position: Number(row.position),\n head: toNumber(row.head),\n firstClaimedAt: toDate(row.first_claimed_at),\n lastClaimedAt: toDate(row.last_claimed_at),\n attemptCount: Number(row.attempt_count),\n claimHolder: row.claim_holder,\n claimExpiresAt: toDate(row.claim_expires_at),\n failureCount: Number(row.failure_count),\n lastFailedAt: toDate(row.last_failed_at),\n lastFailedAttempt: toNumber(row.last_failed_attempt),\n lastError: row.last_error,\n failedAt: toDate(row.failed_at),\n})\n\nconst toHead = (row: HeadRow): HeadEvent => {\n const { position: _position, head: _head, ...dispatch } = toCursor(row)\n return {\n ...toStored({\n session_id: row.session_id,\n idx: row.e_idx,\n event_type: row.e_event_type,\n payload: row.e_payload,\n event_id: row.e_event_id,\n created_at: row.e_created_at,\n cause: row.e_cause,\n }),\n ...dispatch,\n }\n}\n\ntype PresenceDbRow = {\n participant: string\n field: string\n value: string\n seen: number | bigint\n at: number | bigint\n expires_at: number | bigint\n}\n\ntype StateReadRow = Partial<EventRow> & {\n row_kind: 'snapshot' | 'event'\n clear_index: number | bigint | null\n head_index: number | bigint | null\n snapshot_index: number | bigint | null\n snapshot_state: string | null\n}\n\ntype SnapshotHeadRow = {\n up_to_index: number | bigint\n state: string\n updated_at: number | bigint\n}\n\nconst RECLAIM_BATCH = 256\n\n/** The head columns every claim, dead letter, and listing joins in. */\nconst HEAD_SELECT = `\n select cursor.*,\n event.idx as e_idx, event.event_type as e_event_type,\n event.payload as e_payload, event.event_id as e_event_id,\n event.created_at as e_created_at, event.cause as e_cause\n from a2_cursors cursor\n join a2_events event\n on event.session_id = cursor.session_id and event.idx = cursor.head`\n\n/** A fresh head's dispatch columns, for `set` clauses. */\nconst FRESH_DISPATCH = `\n attempt_count = 0, first_claimed_at = null, last_claimed_at = null,\n claim_holder = null, claim_expires_at = null, failure_count = 0,\n last_failed_at = null, last_failed_attempt = null, last_error = null,\n failed_at = null`\n\nconst placeholders = (count: number): string =>\n Array.from({ length: count }, () => '?').join(', ')\n\nconst compareText = (a: string, b: string): number =>\n a < b ? -1 : a > b ? 1 : 0\n\nexport function sqlite(options: SqliteStoreOptions = {}): SqliteStore {\n const path = options.path ?? '.a2/dev.db'\n const clock = options.clock ?? SYSTEM_CLOCK\n const generateId = options.ids ?? RANDOM_IDS\n\n if (path !== ':memory:') {\n // The dev-default backend only ever runs outside production, but\n // bundlers can't know that: without the ignore, Turbopack sees a\n // dynamic fs path and traces the entire project into the output.\n mkdirSync(dirname(resolve(/* turbopackIgnore: true */ path)), {\n recursive: true,\n })\n }\n const db = new DatabaseSync(path)\n // busy_timeout before anything that takes locks: concurrent\n // cold-opens (several instances booting at once) race the WAL switch\n // and schema creation.\n db.exec('pragma busy_timeout = 5000')\n setWalJournalMode(db)\n db.exec('pragma synchronous = normal')\n const legacy = db\n .prepare(\"select 1 from pragma_table_info('a2_events') where name = ?\")\n .get('processed_at')\n if (legacy) {\n db.close()\n throw new A2Error(\n 'STORE_NOT_CONFIGURED',\n `the sqlite database at '${path}' has the A2 layout from before consumers; delete it, or point the store at a fresh database file`,\n )\n }\n db.exec(SCHEMA)\n db.prepare(\n \"insert into a2_meta (key, value) values ('layout', ?) on conflict (key) do nothing\",\n ).run(LAYOUT)\n\n const insertEvent = db.prepare(\n `insert into a2_events\n (session_id, idx, event_type, payload, event_id, created_at, cause)\n values (?, ?, ?, ?, ?, ?, ?)`,\n )\n const selectHead = db.prepare(\n 'select coalesce(max(idx), 0) as max from a2_events where session_id = ?',\n )\n const headOf = (sessionId: string): number =>\n Number((selectHead.get(sessionId) as { max: number | bigint }).max)\n const selectEvent = db.prepare(\n 'select 1 from a2_events where session_id = ? and idx = ?',\n )\n const selectCursor = db.prepare(\n 'select * from a2_cursors where session_id = ? and consumer = ? and cursor_key = ?',\n )\n const cursorOf = (sessionId: string, ref: HeadRef): CursorRow | undefined =>\n selectCursor.get(\n sessionId,\n ref.consumer,\n cursorKeyOf(ref.lane, ref.index),\n ) as CursorRow | undefined\n const insertEventCursor = db.prepare(\n `insert into a2_cursors\n (session_id, consumer, cursor_key, lane, kind, position, head)\n values (?, ?, ?, null, 'event', ?, ?)`,\n )\n const insertLaneEvent = db.prepare(\n `insert into a2_lane_events (session_id, consumer, lane, idx)\n values (?, ?, ?, ?)`,\n )\n // A lane cursor whose head lies at or below the clear marker is idle: the\n // marker superseded everything it had queued.\n const queueLaneHead = db.prepare(\n `insert into a2_cursors\n (session_id, consumer, cursor_key, lane, kind, position, head)\n values (?, ?, ?, ?, 'lane', ?, ?)\n on conflict (session_id, consumer, cursor_key) do update set\n head = excluded.head,\n position = max(a2_cursors.position, excluded.position),\n ${FRESH_DISPATCH}\n where a2_cursors.head is null or a2_cursors.head <= excluded.position`,\n )\n const openLogCursor = db.prepare(\n `insert into a2_cursors\n (session_id, consumer, cursor_key, lane, kind, position, head, types)\n values (?, ?, ?, ?, 'log', ?, ?, ?)`,\n )\n const wakeLogCursor = db.prepare(\n `update a2_cursors set types = ?, head = ?\n where session_id = ? and consumer = ? and cursor_key = ?`,\n )\n const refreshTypes = db.prepare(\n `update a2_cursors set types = ?\n where session_id = ? and consumer = ? and kind = 'log'\n and types is not ?`,\n )\n const nextLogHeadAll = db.prepare(\n `select min(idx) as next from a2_events\n where session_id = ? and idx > ? and event_type != ?`,\n )\n const nextLogHeadOf = db.prepare(\n `select min(idx) as next from a2_events\n where session_id = ? and idx > ? and event_type != ?\n and event_type in (select value from json_each(?))`,\n )\n const nextLogHead = (\n sessionId: string,\n after: number,\n types: string | null,\n ): number | null =>\n toNumber(\n (\n (types === null\n ? nextLogHeadAll.get(sessionId, after, CLEAR_EVENT_TYPE)\n : nextLogHeadOf.get(sessionId, after, CLEAR_EVENT_TYPE, types)) as {\n next: number | bigint | null\n }\n ).next,\n )\n const nextLaneHead = db.prepare(\n `select min(idx) as next from a2_lane_events\n where session_id = ? and consumer = ? and lane = ? and done = 0\n and idx > ?`,\n )\n const selectLaneEvent = db.prepare(\n `select completed_attempt, returned_event_ids from a2_lane_events\n where session_id = ? and consumer = ? and lane = ? and idx = ?`,\n )\n const finishLaneEvent = db.prepare(\n `update a2_lane_events\n set done = 1, completed_attempt = ?, returned_event_ids = ?\n where session_id = ? and consumer = ? and lane = ? and idx = ?`,\n )\n const moveCursor = db.prepare(\n `update a2_cursors set position = ?, head = ?, ${FRESH_DISPATCH}\n where session_id = ? and consumer = ? and cursor_key = ?`,\n )\n const recordCompletion = db.prepare(\n `update a2_cursors\n set completed_idx = ?, completed_attempt = ?, returned_event_ids = ?\n where session_id = ? and consumer = ? and cursor_key = ?`,\n )\n // A JSON null value is a tombstone: a delete's `at`, kept until it\n // expires so an older set arriving after it still loses.\n const upsertPresence = db.prepare(\n `insert into a2_presence (ns, participant, field, value, seen, at, expires_at)\n values (?, ?, ?, ?, ?, ?, ?)\n on conflict (ns, participant, field) do update set\n value = excluded.value,\n seen = excluded.seen,\n at = excluded.at,\n expires_at = excluded.expires_at\n where a2_presence.at <= excluded.at or a2_presence.expires_at <= ?`,\n )\n const sweepPresence = db.prepare(\n 'delete from a2_presence where ns = ? and expires_at <= ?',\n )\n const selectPresence = db.prepare(\n `select participant, field, value, seen, at, expires_at\n from a2_presence\n where ns = ? and expires_at > ? and value != 'null'\n order by participant, field`,\n )\n const selectByIds = (count: number) =>\n db.prepare(\n `select * from a2_events\n where session_id = ? and event_id in (${placeholders(count)})\n order by idx`,\n )\n\n const selectClearIndex = db.prepare(\n 'select clear_index from a2_clears where session_id = ?',\n )\n const clearIndexOf = (sessionId: string): number =>\n Number(\n (\n selectClearIndex.get(sessionId) as\n { clear_index: number | bigint } | undefined\n )?.clear_index ?? 0,\n )\n const deleteEventById = db.prepare(\n 'delete from a2_events where session_id = ? and event_id = ?',\n )\n\n /** Served consumers as a SQL condition on `cursor.consumer`. */\n const servedClause = (\n consumers: readonly StoreConsumer[] | undefined,\n ): { sql: string; params: string[] } =>\n consumers === undefined\n ? { sql: '', params: [] }\n : consumers.length === 0\n ? { sql: 'and 0', params: [] }\n : {\n sql: `and cursor.consumer in (${placeholders(consumers.length)})`,\n params: consumers.map((consumer) => consumer.name),\n }\n\n const hasPending = (\n sessionId: string,\n clearIndex: number,\n consumers: readonly StoreConsumer[] | undefined,\n ): boolean => {\n const served = servedClause(consumers)\n return (\n db\n .prepare(\n `select 1 from a2_cursors cursor\n where cursor.session_id = ? and cursor.head > ?\n and cursor.failed_at is null ${served.sql}\n limit 1`,\n )\n .get(sessionId, clearIndex, ...served.params) !== undefined\n )\n }\n\n /**\n * The session's rows owning `ids`. Rows below its clear marker do not\n * own their ids: they are deleted here so the ids can be reused.\n */\n const liveOwnersOf = (\n sessionId: string,\n clearIndex: number,\n ids: readonly string[],\n ): EventRow[] => {\n if (ids.length === 0) return []\n const rows = selectByIds(ids.length).all(\n sessionId,\n ...ids,\n ) as unknown as EventRow[]\n return rows.filter((row) => {\n if (Number(row.idx) >= clearIndex) return true\n deleteEventById.run(sessionId, row.event_id)\n return false\n })\n }\n\n /** The attempt-currency fence shared by consumer appends and clears. */\n const assertCurrentCause = ({\n sessionId,\n cause,\n clearIndex,\n }: {\n sessionId: string\n cause: EventCause\n clearIndex: number\n }): void => {\n if (cause.index <= clearIndex)\n throw supersededAttemptError(sessionId, cause)\n const cursor = cursorOf(sessionId, cause)\n if (\n !cursor ||\n toNumber(cursor.head) !== cause.index ||\n Number(cursor.attempt_count) !== cause.attempt ||\n cursor.failed_at !== null\n ) {\n throw supersededAttemptError(sessionId, cause)\n }\n }\n\n const tx = <T>(fn: () => T): T => {\n db.exec('begin immediate')\n try {\n const result = fn()\n db.exec('commit')\n return result\n } catch (err) {\n try {\n db.exec('rollback')\n } catch {\n // Rollback can fail if the transaction never started; the\n // original error is what matters.\n }\n throw err\n }\n }\n\n /** A finished or dead head's snapshot pins, and history no pin keeps. */\n const releasePins = (\n sessionId: string,\n consumer: string,\n cursorKey: string,\n index: number,\n ): void => {\n db.prepare(\n `delete from a2_snapshot_pins\n where session_id = ? and consumer = ? and cursor_key = ? and event_index = ?`,\n ).run(sessionId, consumer, cursorKey, index)\n db.prepare(\n `delete from a2_snapshot_history\n where session_id = ?\n and not exists (\n select 1 from a2_snapshot_pins pin\n where pin.session_id = a2_snapshot_history.session_id\n and pin.reducer_name = a2_snapshot_history.reducer_name\n and pin.up_to_index = a2_snapshot_history.up_to_index\n )`,\n ).run(sessionId)\n }\n\n /** Moves a cursor past its head; the next head starts fresh. */\n const advance = (sessionId: string, cursor: CursorRow): void => {\n const done = Number(cursor.head)\n releasePins(sessionId, cursor.consumer, cursor.cursor_key, done)\n let next: number | null\n if (cursor.kind === 'event') {\n next = null\n } else if (cursor.kind === 'lane') {\n next = toNumber(\n (\n nextLaneHead.get(\n sessionId,\n cursor.consumer,\n cursor.lane,\n Math.max(done, clearIndexOf(sessionId)),\n ) as { next: number | bigint | null }\n ).next,\n )\n } else {\n const { types } = selectCursor.get(\n sessionId,\n cursor.consumer,\n cursor.cursor_key,\n ) as CursorRow\n next = nextLogHead(sessionId, done, types)\n }\n moveCursor.run(done, next, sessionId, cursor.consumer, cursor.cursor_key)\n }\n\n /**\n * Brings a log consumer's cursor up to date and records its types. A\n * missing cursor opens at the clear marker with `backfill`, otherwise at\n * `base`; an idle one takes the next event of its types after its\n * position; one with a head keeps it, claim and all. Returns its key.\n */\n const wakeLogAt = ({\n sessionId,\n consumer,\n log,\n base,\n clearIndex,\n }: {\n sessionId: string\n consumer: string\n log: NonNullable<StoreConsumer['log']>\n base: number\n clearIndex: number\n }): string => {\n const types = log.types === null ? null : JSON.stringify(log.types)\n const key = cursorKeyOf(log.lane, 0)\n const existing = selectCursor.get(sessionId, consumer, key) as\n CursorRow | undefined\n if (existing) {\n wakeLogCursor.run(\n types,\n existing.head === null\n ? // A deployment that doesn't serve this consumer may have\n // appended events it takes since it went idle.\n nextLogHead(sessionId, Number(existing.position), types)\n : Number(existing.head),\n sessionId,\n consumer,\n key,\n )\n return key\n }\n const position = log.backfill ? clearIndex : base\n openLogCursor.run(\n sessionId,\n consumer,\n key,\n log.lane,\n position,\n nextLogHead(sessionId, position, types),\n types,\n )\n return key\n }\n\n /** Starts the cursor work an inserted batch carries. */\n const enqueue = ({\n sessionId,\n events,\n base,\n clearIndex,\n consumers,\n }: {\n sessionId: string\n events: readonly AppendEvent[]\n base: number\n clearIndex: number\n consumers: readonly StoreConsumer[] | undefined\n }): void => {\n events.forEach((event, offset) => {\n const index = base + 1 + offset\n for (const target of event.targets ?? []) {\n if (target.lane === null) {\n insertEventCursor.run(\n sessionId,\n target.consumer,\n cursorKeyOf(null, index),\n index - 1,\n index,\n )\n continue\n }\n insertLaneEvent.run(sessionId, target.consumer, target.lane, index)\n queueLaneHead.run(\n sessionId,\n target.consumer,\n cursorKeyOf(target.lane, index),\n target.lane,\n clearIndex,\n index,\n )\n }\n })\n // The batch is inserted already, so a cursor it wakes finds the batch's\n // first event of its types, or an earlier one.\n for (const consumer of consumers ?? []) {\n const log = consumer.log\n if (!log) continue\n const carries = events.some(\n (event) =>\n event.type !== CLEAR_EVENT_TYPE &&\n (log.types === null || log.types.includes(event.type)),\n )\n if (!carries) continue\n wakeLogAt({ sessionId, consumer: consumer.name, log, base, clearIndex })\n }\n }\n\n const refreshLogTypes = (\n sessionId: string,\n consumers: readonly StoreConsumer[] | undefined,\n ): void => {\n for (const consumer of consumers ?? []) {\n if (!consumer.log) continue\n const types =\n consumer.log.types === null ? null : JSON.stringify(consumer.log.types)\n refreshTypes.run(types, sessionId, consumer.name, types)\n }\n }\n\n const insertBatch = ({\n sessionId,\n events,\n clearIndex,\n consumers,\n cause,\n }: {\n sessionId: string\n events: readonly AppendEvent[]\n clearIndex: number\n consumers: readonly StoreConsumer[] | undefined\n cause?: EventCause\n }): StoredEvent[] => {\n const base = headOf(sessionId)\n const now = clock.now().getTime()\n const inserted = events.map((event, offset): StoredEvent => {\n const id = event.id ?? generateId()\n const index = base + 1 + offset\n const eventCause = cause ?? event.cause ?? null\n insertEvent.run(\n sessionId,\n index,\n event.type,\n JSON.stringify(event.payload) ?? 'null',\n id,\n now,\n eventCause ? JSON.stringify(eventCause) : null,\n )\n return {\n id,\n type: event.type,\n payload: structuredClone(event.payload),\n index,\n sessionId,\n createdAt: new Date(now),\n cause: eventCause ? { ...eventCause } : null,\n }\n })\n enqueue({ sessionId, events, base, clearIndex, consumers })\n return inserted\n }\n\n return {\n async append(sessionId, events, appendOptions) {\n const consumers = appendOptions?.consumers\n if (events.length === 0) {\n return wrap(() => ({\n events: [],\n hasPending: hasPending(sessionId, clearIndexOf(sessionId), consumers),\n }))\n }\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n const supplied = events.filter((e) => e.id !== undefined)\n const suppliedIds = supplied.map((e) => e.id as string)\n if (new Set(suppliedIds).size !== suppliedIds.length) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'batch contains the same event id more than once',\n )\n }\n if (suppliedIds.length > 0) {\n const existing = liveOwnersOf(sessionId, clearIndex, suppliedIds)\n if (existing.length > 0) {\n if (existing.length === events.length) {\n return {\n events: idempotentReplay(events, existing.map(toStored)),\n hasPending: hasPending(sessionId, clearIndex, consumers),\n }\n }\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n `batch mixes ${existing.length} already-appended and ${events.length - existing.length} fresh events`,\n )\n }\n }\n\n const originIndex = appendOptions?.originIndex\n if (originIndex !== undefined && originIndex <= clearIndex) {\n throw sessionClearedError(\n `scheduled work from event ${originIndex} was cleared in session '${sessionId}'`,\n )\n }\n // Attempt-currency fence: a fresh consumer append commits only\n // while its causal attempt still owns the cursor's head\n // (store.ts `append`). `begin immediate` serializes this read\n // against a concurrent claim.\n for (const e of events) {\n if (e.cause)\n assertCurrentCause({ sessionId, cause: e.cause, clearIndex })\n }\n\n const inserted = insertBatch({\n sessionId,\n events,\n clearIndex,\n consumers,\n })\n return {\n events: inserted,\n hasPending: hasPending(sessionId, clearIndex, consumers),\n }\n }),\n )\n },\n\n async wakeLog({ sessionId, consumer }) {\n return wrap(() => {\n // Nothing after the latest clear: a missing cursor stays missing,\n // and nothing takes the write lock.\n const { lane } = consumer.log\n const marker = clearIndexOf(sessionId)\n if (\n headOf(sessionId) === marker &&\n !selectCursor.get(sessionId, consumer.name, cursorKeyOf(lane, 0))\n ) {\n return {\n cursor: idleLogCursor(consumer.name, lane, marker),\n head: marker,\n }\n }\n return tx(() => {\n const head = headOf(sessionId)\n const key = wakeLogAt({\n sessionId,\n consumer: consumer.name,\n log: consumer.log,\n base: head,\n clearIndex: clearIndexOf(sessionId),\n })\n const cursor = selectCursor.get(sessionId, consumer.name, key)\n return { cursor: toCursor(cursor as CursorRow), head }\n })\n })\n },\n\n async cursors(sessionId) {\n return wrap(() => {\n const clearIndex = clearIndexOf(sessionId)\n const rows = db\n .prepare(\n `select * from a2_cursors\n where session_id = ?\n and (\n kind = 'log'\n or head > ?\n or (kind = 'lane' and head is null and position > ?)\n )\n order by consumer, lane is null, lane, head`,\n )\n .all(sessionId, clearIndex, clearIndex) as unknown as CursorRow[]\n return rows.map(toCursor)\n })\n },\n\n async read(sessionId, opts) {\n return wrap(() => {\n // Reads start at the latest clear marker.\n const conditions = [\n 'session_id = ?',\n 'idx >= coalesce((select clear_index from a2_clears where session_id = ?), 0)',\n ]\n const params: (string | number)[] = [sessionId, sessionId]\n if (opts?.afterIndex !== undefined) {\n conditions.push('idx > ?')\n params.push(opts.afterIndex)\n }\n if (opts?.throughIndex !== undefined) {\n conditions.push('idx <= ?')\n params.push(opts.throughIndex)\n }\n const rows = db\n .prepare(\n `select * from a2_events where ${conditions.join(' and ')} order by idx`,\n )\n .all(...params) as unknown as EventRow[]\n return rows.map(toStored)\n })\n },\n\n async claimAvailable({\n sessionId,\n holder,\n ttlMs,\n expiresAtMs,\n exclude = [],\n consumers,\n maxFailures,\n through,\n }) {\n return wrap(() =>\n tx(() => {\n refreshLogTypes(sessionId, consumers)\n const now = clock.now().getTime()\n const expiresAt = expiresAtMs ?? now + ttlMs\n const clearIndex = clearIndexOf(sessionId)\n const served = servedClause(consumers)\n const excludedKeys = exclude.map(\n (ref) => `${ref.consumer}\\u0000${cursorKeyOf(ref.lane, ref.index)}`,\n )\n const exclusion =\n excludedKeys.length === 0\n ? ''\n : `and cursor.consumer || char(0) || cursor.cursor_key\n not in (${placeholders(excludedKeys.length)})`\n const bounded = through === undefined ? '' : 'and cursor.head <= ?'\n const filter = `\n where cursor.session_id = ?\n and cursor.head > ?\n and cursor.failed_at is null\n ${bounded}\n ${served.sql}\n ${exclusion}`\n const params = [\n sessionId,\n clearIndex,\n ...(through === undefined ? [] : [through]),\n ...served.params,\n ...excludedKeys,\n now,\n ]\n const eligible = db\n .prepare(\n `select cursor.consumer, cursor.cursor_key,\n cursor.claim_holder, cursor.failure_count\n from a2_cursors cursor\n ${filter}\n and (cursor.claim_expires_at is null\n or cursor.claim_expires_at <= ?)\n order by cursor.head, cursor.consumer`,\n )\n .all(...params) as unknown as Array<\n Pick<\n CursorRow,\n 'consumer' | 'cursor_key' | 'claim_holder' | 'failure_count'\n >\n >\n if (eligible.length > 0) {\n const claimHead = db.prepare(\n `update a2_cursors set\n attempt_count = attempt_count + 1,\n failure_count = failure_count\n + case when claim_holder is null then 0 else 1 end,\n first_claimed_at = coalesce(first_claimed_at, ?),\n last_claimed_at = ?,\n claim_holder = ?,\n claim_expires_at = ?\n where session_id = ? and consumer = ? and cursor_key = ?`,\n )\n // Advancing the attempt fences the lapsed one; nothing runs.\n const deadLetterHead = db.prepare(\n `update a2_cursors set\n attempt_count = attempt_count + 1,\n failure_count = failure_count + 1,\n failed_at = ?,\n claim_holder = null,\n claim_expires_at = null\n where session_id = ? and consumer = ? and cursor_key = ?`,\n )\n const selectHeadRow = db.prepare(\n `${HEAD_SELECT}\n where cursor.session_id = ? and cursor.consumer = ?\n and cursor.cursor_key = ?`,\n )\n const events = eligible.map((row) => {\n // A lapsed claim's takeover that spends the last failed attempt.\n const exhausted =\n row.claim_holder !== null &&\n maxFailures !== undefined &&\n Number(row.failure_count) + 1 >= maxFailures\n if (exhausted) {\n deadLetterHead.run(now, sessionId, row.consumer, row.cursor_key)\n } else {\n claimHead.run(\n now,\n now,\n holder,\n expiresAt,\n sessionId,\n row.consumer,\n row.cursor_key,\n )\n }\n const claimed = toHead(\n selectHeadRow.get(\n sessionId,\n row.consumer,\n row.cursor_key,\n ) as HeadRow,\n )\n if (exhausted) {\n releasePins(\n sessionId,\n row.consumer,\n row.cursor_key,\n claimed.index,\n )\n }\n if (clearIndex > 0) claimed.clearIndex = clearIndex\n return claimed\n })\n return { outcome: 'claimed', events, head: headOf(sessionId) }\n }\n\n const active = db\n .prepare(\n `select min(cursor.claim_expires_at) as retry_at\n from a2_cursors cursor\n ${filter}\n and cursor.claim_expires_at > ?`,\n )\n .get(...params) as { retry_at: number | bigint | null }\n return active.retry_at === null\n ? { outcome: 'settled' }\n : { outcome: 'busy', retryAt: new Date(Number(active.retry_at)) }\n }),\n )\n },\n\n async renewClaims({ sessionId, holder, claims, ttlMs, expiresAtMs }) {\n if (claims.length === 0) return { renewed: [], superseded: [] }\n return wrap(() =>\n tx(() => {\n const now = clock.now().getTime()\n const expiresAt = expiresAtMs ?? now + ttlMs\n const renewed: HeadRef[] = []\n const superseded: HeadRef[] = []\n const extend = db.prepare(\n `update a2_cursors set claim_expires_at = ?\n where session_id = ? and consumer = ? and cursor_key = ?\n and head = ?\n and attempt_count = ?\n and failed_at is null\n and claim_holder = ?\n and claim_expires_at > ?`,\n )\n const clearIndex = clearIndexOf(sessionId)\n const ordered = claims.toSorted(\n (a, b) => a.index - b.index || compareText(a.consumer, b.consumer),\n )\n for (const { attempt, ...ref } of ordered) {\n if (ref.index <= clearIndex) {\n superseded.push(ref)\n continue\n }\n const cursor = cursorOf(sessionId, ref)\n if (!cursor) continue\n const head = toNumber(cursor.head)\n // An event the cursor has not reached yet holds no claim to renew.\n if (head !== ref.index && Number(cursor.position) < ref.index)\n continue\n if (head !== ref.index || Number(cursor.attempt_count) > attempt) {\n superseded.push(ref)\n continue\n }\n const updated = extend.run(\n expiresAt,\n sessionId,\n ref.consumer,\n cursor.cursor_key,\n ref.index,\n attempt,\n holder,\n now,\n )\n if (Number(updated.changes) === 1) renewed.push(ref)\n }\n return { renewed, superseded }\n }),\n )\n },\n\n async completeAttempt({\n sessionId,\n consumer,\n lane,\n index,\n attempt,\n events,\n consumers,\n }) {\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n if (index <= clearIndex) return { outcome: 'superseded' }\n const cursor = cursorOf(sessionId, { consumer, lane, index })\n if (!cursor) {\n if (!selectEvent.get(sessionId, index)) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n return { outcome: 'superseded' }\n }\n refreshLogTypes(sessionId, consumers)\n const ids = events.map((event) => event.id)\n if (new Set(ids).size !== ids.length) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'returned event batch contains the same event id more than once',\n )\n }\n\n if (toNumber(cursor.head) !== index) {\n let completion:\n { attempt: number | null; ids: string | null } | undefined\n if (cursor.kind === 'lane') {\n const row = selectLaneEvent.get(\n sessionId,\n consumer,\n lane,\n index,\n ) as\n | {\n completed_attempt: number | bigint | null\n returned_event_ids: string | null\n }\n | undefined\n completion = row && {\n attempt: toNumber(row.completed_attempt),\n ids: row.returned_event_ids,\n }\n } else if (toNumber(cursor.completed_idx) === index) {\n completion = {\n attempt: toNumber(cursor.completed_attempt),\n ids: cursor.returned_event_ids,\n }\n }\n if (!completion || completion.attempt !== attempt) {\n return { outcome: 'superseded' }\n }\n const returnedIds =\n completion.ids === null\n ? null\n : decodeReturnedEventIds(completion.ids)\n if (\n returnedIds === null ||\n returnedIds.length !== ids.length ||\n returnedIds.some((id, offset) => id !== ids[offset])\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'completed attempt does not match the returned event batch',\n )\n }\n const existing =\n ids.length === 0\n ? []\n : (selectByIds(ids.length).all(\n sessionId,\n ...ids,\n ) as unknown as EventRow[])\n const byId = new Map(existing.map((row) => [row.event_id, row]))\n const ordered = ids.map((id) => byId.get(id))\n if (\n ordered.some((row) => {\n const cause = row ? decodeCause(row.cause) : null\n return (\n !row ||\n cause?.consumer !== consumer ||\n cause.lane !== lane ||\n cause.index !== index ||\n cause.attempt !== attempt\n )\n })\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'returned event retry does not match the committed batch',\n )\n }\n return {\n outcome: 'completed',\n events:\n events.length === 0\n ? []\n : idempotentReplay(\n events,\n (ordered as EventRow[]).map(toStored),\n ),\n }\n }\n if (\n Number(cursor.attempt_count) !== attempt ||\n cursor.claim_holder === null ||\n cursor.failed_at !== null\n ) {\n return { outcome: 'superseded' }\n }\n\n if (liveOwnersOf(sessionId, clearIndex, ids).length > 0) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'returned event batch contains an already-appended event id',\n )\n }\n\n const inserted = insertBatch({\n sessionId,\n events,\n clearIndex,\n consumers,\n cause: { consumer, lane, index, attempt },\n })\n const returned = JSON.stringify(ids)\n if (cursor.kind === 'lane') {\n finishLaneEvent.run(\n attempt,\n returned,\n sessionId,\n consumer,\n lane,\n index,\n )\n } else {\n recordCompletion.run(\n index,\n attempt,\n returned,\n sessionId,\n consumer,\n cursor.cursor_key,\n )\n }\n advance(sessionId, cursor)\n return { outcome: 'completed', events: inserted }\n }),\n )\n },\n\n async failAttempt({\n sessionId,\n consumer,\n lane,\n index,\n attempt,\n error,\n maxFailures,\n retryAfterMs,\n }) {\n return wrap(() =>\n tx(() => {\n if (index <= clearIndexOf(sessionId)) {\n return { outcome: 'superseded', failureCount: 0 }\n }\n const current = cursorOf(sessionId, { consumer, lane, index })\n if (!current) {\n if (!selectEvent.get(sessionId, index)) {\n throw new TypeError(\n `no event at index ${index} in session '${sessionId}'`,\n )\n }\n return { outcome: 'superseded', failureCount: 0 }\n }\n if (toNumber(current.head) !== index) {\n return { outcome: 'superseded', failureCount: 0 }\n }\n const failureCount = Number(current.failure_count)\n if (Number(current.attempt_count) !== attempt) {\n return { outcome: 'superseded', failureCount }\n }\n if (\n current.claim_holder === null &&\n current.last_failed_attempt !== null &&\n Number(current.last_failed_attempt) === attempt\n ) {\n return {\n outcome: current.failed_at === null ? 'failed' : 'dead_lettered',\n failureCount,\n }\n }\n if (current.failed_at !== null) {\n return { outcome: 'dead_lettered', failureCount }\n }\n if (current.claim_holder === null) {\n return { outcome: 'superseded', failureCount }\n }\n\n const now = clock.now().getTime()\n const nextFailureCount = failureCount + 1\n const deadLettered = nextFailureCount >= maxFailures\n db.prepare(\n `update a2_cursors set\n failure_count = ?,\n last_error = ?,\n last_failed_at = ?,\n last_failed_attempt = ?,\n failed_at = ?,\n claim_holder = null,\n claim_expires_at = ?\n where session_id = ? and consumer = ? and cursor_key = ?`,\n ).run(\n nextFailureCount,\n error,\n now,\n attempt,\n deadLettered ? now : null,\n deadLettered || retryAfterMs === undefined\n ? null\n : now + retryAfterMs,\n sessionId,\n consumer,\n current.cursor_key,\n )\n if (deadLettered) {\n releasePins(sessionId, consumer, current.cursor_key, index)\n }\n return {\n outcome: deadLettered ? 'dead_lettered' : 'failed',\n failureCount: nextFailureCount,\n }\n }),\n )\n },\n\n async deadLetters(sessionId) {\n return wrap(() => {\n const rows = db\n .prepare(\n `${HEAD_SELECT}\n where cursor.session_id = ?\n and cursor.failed_at is not null\n and cursor.head > coalesce(\n (select clear_index from a2_clears where session_id = ?), 0\n )\n order by cursor.head, cursor.consumer`,\n )\n .all(sessionId, sessionId) as unknown as HeadRow[]\n return rows.map(toHead)\n })\n },\n\n async redrive({ sessionId, consumer, index, action, consumers }) {\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n const cursor =\n index > clearIndex\n ? (db\n .prepare(\n `select * from a2_cursors\n where session_id = ? and consumer = ? and head = ?\n and failed_at is not null`,\n )\n .get(sessionId, consumer, index) as CursorRow | undefined)\n : undefined\n if (cursor) {\n refreshLogTypes(sessionId, consumers)\n if (action === 'skip') {\n if (cursor.kind === 'lane') {\n finishLaneEvent.run(\n null,\n null,\n sessionId,\n consumer,\n cursor.lane,\n index,\n )\n }\n advance(sessionId, cursor)\n } else {\n db.prepare(\n `update a2_cursors set failed_at = null, failure_count = 0\n where session_id = ? and consumer = ? and cursor_key = ?`,\n ).run(sessionId, consumer, cursor.cursor_key)\n }\n }\n return {\n redriven: cursor !== undefined,\n hasPending: hasPending(sessionId, clearIndex, consumers),\n }\n }),\n )\n },\n\n async readState(sessionId, reducerName, stateOptions) {\n return wrap(() => {\n const throughIndex = stateOptions?.throughIndex ?? null\n const snapshotThroughIndex =\n stateOptions?.snapshotThroughIndex ?? throughIndex\n const rows = db\n .prepare(\n `with boundary as materialized (\n select coalesce(\n (select clear_index from a2_clears where session_id = ?), 0\n ) as clear_index\n ), head as materialized (\n select up_to_index, state, updated_at\n from a2_snapshots\n where session_id = ? and reducer_name = ?\n and up_to_index >= (select clear_index from boundary)\n ), snapshot as materialized (\n select up_to_index, state from (\n select up_to_index, state from head\n where (? is null or up_to_index <= ?)\n union all\n select up_to_index, state\n from a2_snapshot_history\n where session_id = ? and reducer_name = ?\n and up_to_index >= (select clear_index from boundary)\n and (? is null or up_to_index <= ?)\n ) order by up_to_index desc limit 1\n )\n select 0 as row_order, 'snapshot' as row_kind,\n (select clear_index from boundary) as clear_index,\n (select up_to_index from head) as head_index,\n (select up_to_index from snapshot) as snapshot_index,\n (select state from snapshot) as snapshot_state,\n null as session_id, null as idx, null as event_type,\n null as payload, null as event_id, null as created_at,\n null as cause\n union all\n select 1 as row_order, 'event' as row_kind,\n null as clear_index,\n (select up_to_index from head) as head_index,\n null as snapshot_index, null as snapshot_state,\n event.session_id, event.idx, event.event_type,\n event.payload, event.event_id, event.created_at,\n event.cause\n from a2_events as event\n where event.session_id = ?\n and event.idx > max(\n coalesce((select up_to_index from snapshot), 0),\n (select clear_index from boundary) - 1\n )\n and (? is null or event.idx <= ?)\n order by row_order, idx`,\n )\n .all(\n sessionId,\n sessionId,\n reducerName,\n snapshotThroughIndex,\n snapshotThroughIndex,\n sessionId,\n reducerName,\n snapshotThroughIndex,\n snapshotThroughIndex,\n sessionId,\n throughIndex,\n throughIndex,\n ) as unknown as StateReadRow[]\n const snapshot = rows.find((row) => row.row_kind === 'snapshot')\n const clearIndex = Number(snapshot?.clear_index ?? 0)\n return {\n ...(clearIndex > 0 ? { clearIndex } : {}),\n headIndex:\n snapshot?.head_index === null || snapshot?.head_index === undefined\n ? null\n : Number(snapshot.head_index),\n snapshot:\n snapshot?.snapshot_index !== null &&\n snapshot?.snapshot_index !== undefined\n ? {\n index: Number(snapshot.snapshot_index),\n state: JSON.parse(snapshot.snapshot_state!),\n }\n : null,\n events: rows\n .filter(\n (row): row is StateReadRow & EventRow => row.row_kind === 'event',\n )\n .map(toEvent),\n }\n })\n },\n\n async putSnapshots(sessionId, reducerName, writes) {\n await wrap(() =>\n tx(() => {\n const selectSnapshotHead = db.prepare(\n `select up_to_index, state, updated_at from a2_snapshots\n where session_id = ? and reducer_name = ?`,\n )\n const unfinished = db.prepare(\n `select 1 from a2_cursors\n where session_id = ? and consumer = ? and cursor_key = ?\n and head = ? and head > ? and failed_at is null`,\n )\n const insertPin = db.prepare(\n `insert or ignore into a2_snapshot_pins\n (session_id, consumer, cursor_key, event_index, reducer_name, up_to_index)\n values (?, ?, ?, ?, ?, ?)`,\n )\n const hasPin = db.prepare(\n `select 1 from a2_snapshot_pins\n where session_id = ? and reducer_name = ? and up_to_index = ?\n limit 1`,\n )\n const putHistory = db.prepare(\n `insert into a2_snapshot_history\n (session_id, reducer_name, up_to_index, state, updated_at)\n values (?, ?, ?, ?, ?)\n on conflict (session_id, reducer_name, up_to_index) do update set\n state = excluded.state, updated_at = excluded.updated_at`,\n )\n const putHead = db.prepare(\n `insert into a2_snapshots\n (session_id, reducer_name, up_to_index, state, updated_at)\n values (?, ?, ?, ?, ?)\n on conflict (session_id, reducer_name) do update set\n up_to_index = excluded.up_to_index,\n state = excluded.state,\n updated_at = excluded.updated_at\n where excluded.up_to_index >= a2_snapshots.up_to_index`,\n )\n const deleteHistory = db.prepare(\n `delete from a2_snapshot_history\n where session_id = ? and reducer_name = ? and up_to_index = ?`,\n )\n const clearIndex = clearIndexOf(sessionId)\n for (const write of writes.toSorted((a, b) => a.index - b.index)) {\n // A fold from before the latest clear describes a lifetime that is gone.\n if (write.index < clearIndex) continue\n for (const pin of write.pins ?? []) {\n const cursorKey = cursorKeyOf(pin.lane, pin.index)\n if (\n unfinished.get(\n sessionId,\n pin.consumer,\n cursorKey,\n pin.index,\n clearIndex,\n )\n ) {\n insertPin.run(\n sessionId,\n pin.consumer,\n cursorKey,\n pin.index,\n reducerName,\n write.index,\n )\n }\n }\n const head = selectSnapshotHead.get(sessionId, reducerName) as\n SnapshotHeadRow | undefined\n const now = clock.now().getTime()\n if (!head || write.index >= Number(head.up_to_index)) {\n if (\n head &&\n write.index > Number(head.up_to_index) &&\n Number(head.up_to_index) >= clearIndex &&\n hasPin.get(sessionId, reducerName, Number(head.up_to_index))\n ) {\n putHistory.run(\n sessionId,\n reducerName,\n Number(head.up_to_index),\n head.state,\n Number(head.updated_at),\n )\n }\n putHead.run(\n sessionId,\n reducerName,\n write.index,\n JSON.stringify(write.state) ?? 'null',\n now,\n )\n deleteHistory.run(sessionId, reducerName, write.index)\n } else if (\n write.index < Number(head.up_to_index) &&\n hasPin.get(sessionId, reducerName, write.index)\n ) {\n putHistory.run(\n sessionId,\n reducerName,\n write.index,\n JSON.stringify(write.state) ?? 'null',\n now,\n )\n }\n }\n }),\n )\n },\n\n presence: {\n async set(ns, participant, values, meta) {\n const entries = Object.entries(values)\n if (entries.length === 0) return\n wrap(() =>\n tx(() => {\n const atMs = meta.at.getTime()\n // Expiry anchors on the storage clock — the sender's `at`\n // orders writes but never anchors their lifetime.\n const nowMs = clock.now().getTime()\n const expiresAtMs = nowMs + meta.ttlMs\n for (const [field, value] of entries) {\n // Field-wise LWW against values and tombstones alike: an\n // unexpired stored row with a strictly newer `at` survives;\n // ties go to the incoming write (memory parity).\n upsertPresence.run(\n ns,\n participant,\n field,\n JSON.stringify(value) ?? 'null',\n meta.seen,\n atMs,\n expiresAtMs,\n nowMs,\n )\n }\n }),\n )\n },\n\n async read(ns) {\n return wrap(() => {\n const now = clock.now().getTime()\n // The sweep is opportunistic cleanup; the select's filter\n // alone keeps expired rows out of this result.\n sweepPresence.run(ns, now)\n const rows = selectPresence.all(ns, now) as unknown as PresenceDbRow[]\n return rows.map((row) => ({\n participant: row.participant,\n field: row.field,\n value: JSON.parse(row.value) as unknown,\n seen: Number(row.seen),\n at: new Date(Number(row.at)),\n expiresAt: new Date(Number(row.expires_at)),\n }))\n })\n },\n },\n\n stream(sessionId, opts) {\n const readAfter = (afterIndex: number): StoredEvent[] =>\n wrap(() => {\n // A cursor below the latest marker resumes at it.\n const rows = db\n .prepare(\n `select * from a2_events\n where session_id = ? and idx > ?\n and idx >= coalesce((select clear_index from a2_clears where session_id = ?), 0)\n order by idx`,\n )\n .all(sessionId, afterIndex, sessionId) as unknown as EventRow[]\n return rows.map(toStored)\n })\n return pollingStream(\n readAfter,\n opts?.startAfter !== undefined ? { startAfter: opts.startAfter } : {},\n )\n },\n\n async clear({ sessionId, ifLastIndex, cause }) {\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n if (cause) assertCurrentCause({ sessionId, cause, clearIndex })\n const head = headOf(sessionId)\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 = head + 1\n insertEvent.run(\n sessionId,\n marker,\n CLEAR_EVENT_TYPE,\n 'null',\n generateId(),\n clock.now().getTime(),\n null,\n )\n db.prepare(\n `insert into a2_clears (session_id, clear_index) values (?, ?)\n on conflict (session_id) do update set clear_index = excluded.clear_index`,\n ).run(sessionId, marker)\n // One row per log consumer: they count from the marker. Lane and\n // concurrent cursors at or below it are superseded by the marker\n // alone, and reclaim deletes them.\n db.prepare(\n `update a2_cursors\n set position = ?, head = null, ${FRESH_DISPATCH},\n completed_idx = null, completed_attempt = null,\n returned_event_ids = null\n where session_id = ? and kind = 'log'`,\n ).run(marker, sessionId)\n db.prepare(\n 'delete from a2_snapshot_pins where session_id = ? and event_index < ?',\n ).run(sessionId, marker)\n return { cleared: true, index: marker, clearIndex: marker }\n }),\n )\n },\n\n async reclaim(sessionId) {\n return wrap(() =>\n tx(() => {\n const clearIndex = clearIndexOf(sessionId)\n if (clearIndex === 0) return { deleted: 0, done: true }\n const last = db\n .prepare(\n `select max(idx) as last from (\n select idx from a2_events\n where session_id = ? and idx < ?\n order by idx limit ?\n )`,\n )\n .get(sessionId, clearIndex, RECLAIM_BATCH) as {\n last: number | bigint | null\n }\n let deleted = 0\n if (last.last !== null) {\n const through = Number(last.last)\n deleted = Number(\n db\n .prepare(\n 'delete from a2_events where session_id = ? and idx <= ?',\n )\n .run(sessionId, through).changes,\n )\n db.prepare(\n 'delete from a2_lane_events where session_id = ? and idx <= ?',\n ).run(sessionId, through)\n db.prepare(\n `delete from a2_cursors\n where session_id = ? and kind = 'event' and position < ?`,\n ).run(sessionId, through)\n }\n const done =\n db\n .prepare(\n 'select 1 from a2_events where session_id = ? and idx < ? limit 1',\n )\n .get(sessionId, clearIndex) === undefined\n // Writes serialize with the clear, so none lands below the marker later.\n if (done) {\n db.prepare(\n `delete from a2_cursors\n where session_id = ? and kind != 'log'\n and (head is null or head <= ?) and position <= ?`,\n ).run(sessionId, clearIndex, clearIndex)\n for (const table of [\n 'a2_snapshot_pins',\n 'a2_snapshot_history',\n 'a2_snapshots',\n ]) {\n db.prepare(\n `delete from ${table} where session_id = ? and up_to_index < ?`,\n ).run(sessionId, clearIndex)\n }\n }\n return { deleted, done }\n }),\n )\n },\n\n close() {\n db.close()\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { o as PRESENCE_TIMINGS } from "./internal-CvsrprnU.js";
|
|
2
|
+
//#region src/presence-tombstones.ts
|
|
3
|
+
const key = ({ participant, field }) => JSON.stringify([participant, field]);
|
|
4
|
+
function presenceTombstones() {
|
|
5
|
+
const stamps = /* @__PURE__ */ new Map();
|
|
6
|
+
const prune = () => {
|
|
7
|
+
const cutoff = Date.now() - PRESENCE_TIMINGS.tombstoneMs;
|
|
8
|
+
for (const [id, stamp] of stamps) {
|
|
9
|
+
if (stamp.keptAtMs > cutoff) return;
|
|
10
|
+
stamps.delete(id);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
outranks(write) {
|
|
15
|
+
if (stamps.size === 0) return false;
|
|
16
|
+
prune();
|
|
17
|
+
const stamp = stamps.get(key(write));
|
|
18
|
+
return stamp !== void 0 && stamp.atMs > write.at.getTime();
|
|
19
|
+
},
|
|
20
|
+
keep(deletion) {
|
|
21
|
+
prune();
|
|
22
|
+
const id = key(deletion);
|
|
23
|
+
stamps.delete(id);
|
|
24
|
+
stamps.set(id, {
|
|
25
|
+
atMs: deletion.at.getTime(),
|
|
26
|
+
keptAtMs: Date.now()
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
drop(field) {
|
|
30
|
+
if (stamps.size > 0) stamps.delete(key(field));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/stream-buffer.ts
|
|
36
|
+
const isSerializedEvent = (event) => "payloadJson" in event;
|
|
37
|
+
const parseSerializedEvent = (event) => ({
|
|
38
|
+
id: event.id,
|
|
39
|
+
type: event.type,
|
|
40
|
+
payload: JSON.parse(event.payloadJson),
|
|
41
|
+
index: event.index,
|
|
42
|
+
sessionId: event.sessionId,
|
|
43
|
+
createdAt: new Date(event.createdAtMs)
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* The next event a stream iterator already holds, removed without
|
|
47
|
+
* awaiting its `next()`, which then continues after it; `undefined` when
|
|
48
|
+
* it holds none. Call only while no `next()` is pending. The SSE encoder
|
|
49
|
+
* writes these with the event `next()` just returned, so a buffered
|
|
50
|
+
* backlog leaves in few writes.
|
|
51
|
+
*/
|
|
52
|
+
const bufferedEvents = /* @__PURE__ */ new WeakMap();
|
|
53
|
+
const takeBuffered = (iterator) => bufferedEvents.get(iterator)?.();
|
|
54
|
+
//#endregion
|
|
55
|
+
export { presenceTombstones as a, takeBuffered as i, isSerializedEvent as n, parseSerializedEvent as r, bufferedEvents as t };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=stream-buffer-DrTyiIuo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-buffer-DrTyiIuo.js","names":[],"sources":["../src/presence-tombstones.ts","../src/stream-buffer.ts"],"sourcesContent":["import { PRESENCE_TIMINGS } from './internal.ts'\n\ntype PresenceField = { participant: string; field: string }\ntype PresenceWrite = PresenceField & { at: Date }\n\n/**\n * The deletion stamps a presence applier keeps per (participant, field),\n * so a null keeps beating older sets that arrive after it. Each lives\n * `PRESENCE_TIMINGS.tombstoneMs` of local receipt time and is pruned\n * lazily on use, without timers.\n */\nexport type PresenceTombstones = {\n /** Whether the field's deletion is strictly newer than the write. */\n outranks(write: PresenceWrite): boolean\n /** Keep the deletion; the caller has already ordered it past any prior one. */\n keep(deletion: PresenceWrite): void\n /** Forget the field's deletion once a write at or past it applies. */\n drop(field: PresenceField): void\n}\n\nconst key = ({ participant, field }: PresenceField): string =>\n JSON.stringify([participant, field])\n\nexport function presenceTombstones(): PresenceTombstones {\n // Insertion order is receipt order, so pruning stops at the first\n // tombstone still inside the window.\n const stamps = new Map<string, { atMs: number; keptAtMs: number }>()\n const prune = (): void => {\n const cutoff = Date.now() - PRESENCE_TIMINGS.tombstoneMs\n for (const [id, stamp] of stamps) {\n if (stamp.keptAtMs > cutoff) return\n stamps.delete(id)\n }\n }\n return {\n outranks(write) {\n if (stamps.size === 0) return false\n prune()\n const stamp = stamps.get(key(write))\n return stamp !== undefined && stamp.atMs > write.at.getTime()\n },\n keep(deletion) {\n prune()\n const id = key(deletion)\n stamps.delete(id)\n stamps.set(id, { atMs: deletion.at.getTime(), keptAtMs: Date.now() })\n },\n drop(field) {\n if (stamps.size > 0) stamps.delete(key(field))\n },\n }\n}\n","import type { Event } from './store.ts'\n\n/**\n * An event as a store returned it, its payload still the JSON text A2's\n * store wrote with `JSON.stringify`. For payloads built from ordinary objects,\n * parsing and re-serializing that text yields the same text, so the SSE lane\n * can write it into a frame unchanged.\n */\nexport type SerializedEvent = {\n id: string\n type: string\n payloadJson: string\n index: number\n sessionId: string\n createdAtMs: number\n}\n\nexport type HeldEvent = Event | SerializedEvent\n\nexport const isSerializedEvent = (event: object): event is SerializedEvent =>\n 'payloadJson' in event\n\nexport const parseSerializedEvent = (event: SerializedEvent): Event => ({\n id: event.id,\n type: event.type,\n payload: JSON.parse(event.payloadJson),\n index: event.index,\n sessionId: event.sessionId,\n createdAt: new Date(event.createdAtMs),\n})\n\n/**\n * The next event a stream iterator already holds, removed without\n * awaiting its `next()`, which then continues after it; `undefined` when\n * it holds none. Call only while no `next()` is pending. The SSE encoder\n * writes these with the event `next()` just returned, so a buffered\n * backlog leaves in few writes.\n */\nexport const bufferedEvents: WeakMap<object, () => HeldEvent | undefined> =\n new WeakMap()\n\nexport const takeBuffered = (iterator: object): HeldEvent | undefined =>\n bufferedEvents.get(iterator)?.()\n"],"mappings":";;AAoBA,MAAM,OAAO,EAAE,aAAa,YAC1B,KAAK,UAAU,CAAC,aAAa,KAAK,CAAC;AAErC,SAAgB,qBAAyC;CAGvD,MAAM,yBAAS,IAAI,IAAgD;CACnE,MAAM,cAAoB;EACxB,MAAM,SAAS,KAAK,IAAI,IAAI,iBAAiB;EAC7C,KAAK,MAAM,CAAC,IAAI,UAAU,QAAQ;GAChC,IAAI,MAAM,WAAW,QAAQ;GAC7B,OAAO,OAAO,EAAE;EAClB;CACF;CACA,OAAO;EACL,SAAS,OAAO;GACd,IAAI,OAAO,SAAS,GAAG,OAAO;GAC9B,MAAM;GACN,MAAM,QAAQ,OAAO,IAAI,IAAI,KAAK,CAAC;GACnC,OAAO,UAAU,KAAA,KAAa,MAAM,OAAO,MAAM,GAAG,QAAQ;EAC9D;EACA,KAAK,UAAU;GACb,MAAM;GACN,MAAM,KAAK,IAAI,QAAQ;GACvB,OAAO,OAAO,EAAE;GAChB,OAAO,IAAI,IAAI;IAAE,MAAM,SAAS,GAAG,QAAQ;IAAG,UAAU,KAAK,IAAI;GAAE,CAAC;EACtE;EACA,KAAK,OAAO;GACV,IAAI,OAAO,OAAO,GAAG,OAAO,OAAO,IAAI,KAAK,CAAC;EAC/C;CACF;AACF;;;AChCA,MAAa,qBAAqB,UAChC,iBAAiB;AAEnB,MAAa,wBAAwB,WAAmC;CACtE,IAAI,MAAM;CACV,MAAM,MAAM;CACZ,SAAS,KAAK,MAAM,MAAM,WAAW;CACrC,OAAO,MAAM;CACb,WAAW,MAAM;CACjB,WAAW,IAAI,KAAK,MAAM,WAAW;AACvC;;;;;;;;AASA,MAAa,iCACX,IAAI,QAAQ;AAEd,MAAa,gBAAgB,aAC3B,eAAe,IAAI,QAAQ,CAAC,GAAG"}
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
/** Attribute values a2 emits. */
|
|
9
9
|
type A2AttributeValue = string | number | boolean;
|
|
10
10
|
/** The spans a2 emits today. The catalogue grows with the surface. */
|
|
11
|
-
type A2SpanName = "a2.append" | "a2.drain" | "a2.event" | "a2.claim" | "a2.commit" | "a2.state" | "a2.snapshot" | "a2.redrive" | "a2.ai.control" | "a2.ai.ack" | "a2.ai.generate" | "a2.ai.context" | "a2.ai.compaction" | "a2.ai.tool";
|
|
11
|
+
type A2SpanName = "a2.append" | "a2.drain" | "a2.event" | "a2.claim" | "a2.commit" | "a2.state" | "a2.snapshot" | "a2.redrive" | "a2.consume" | "a2.ai.control" | "a2.ai.ack" | "a2.ai.generate" | "a2.ai.context" | "a2.ai.compaction" | "a2.ai.tool";
|
|
12
12
|
type A2SpanHandle = {
|
|
13
13
|
/** Attach or update an attribute mid-span (e.g. the outcome). */
|
|
14
14
|
setAttribute(key: string, value: A2AttributeValue): void;
|
|
15
15
|
/**
|
|
16
16
|
* Mark the span failed without a2's control flow throwing — used for
|
|
17
|
-
*
|
|
17
|
+
* consumer failures, which a2 swallows by design (they go into the
|
|
18
18
|
* retry machinery, not up the stack) but telemetry must still see.
|
|
19
19
|
*/
|
|
20
20
|
recordError(error: unknown): void;
|
|
@@ -30,4 +30,4 @@ type A2Telemetry = {
|
|
|
30
30
|
};
|
|
31
31
|
//#endregion
|
|
32
32
|
export { A2Telemetry as i, A2SpanHandle as n, A2SpanName as r, A2AttributeValue as t };
|
|
33
|
-
//# sourceMappingURL=telemetry-
|
|
33
|
+
//# sourceMappingURL=telemetry-CWwsh-Gx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-CWwsh-Gx.d.ts","names":[],"sources":["../src/telemetry.ts"],"mappings":";;;;;;;;KAQY;;KAGA;KAiBA;;EAEV,aAAa,aAAa,OAAO;;;;;;EAMjC,YAAY;;KAGF;;;;;;;EAOV,KAAK,GACH,MAAM,YACN,YAAY,eAAe,mBAC3B,KAAK,MAAM,iBAAiB,QAAQ,KACnC,QAAQ"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as A2Error } from "./session-clear-
|
|
2
|
-
import {
|
|
3
|
-
import { i as takeBuffered, n as isSerializedEvent } from "./stream-buffer-
|
|
1
|
+
import { i as A2Error } from "./session-clear-wHd5GCpZ.js";
|
|
2
|
+
import { y as nullProtoRecord } from "./internal-CvsrprnU.js";
|
|
3
|
+
import { i as takeBuffered, n as isSerializedEvent } from "./stream-buffer-DrTyiIuo.js";
|
|
4
4
|
//#region src/wire.ts
|
|
5
5
|
/**
|
|
6
6
|
* The wire format shared by server.fetch and experimental-a2/client
|
|
@@ -180,7 +180,8 @@ const ERROR_STATUS = {
|
|
|
180
180
|
STORE_UNAVAILABLE: 503,
|
|
181
181
|
STORE_NOT_CONFIGURED: 500,
|
|
182
182
|
UNKNOWN_PRESENCE_FIELD: 400,
|
|
183
|
-
PRESENCE_NOT_SUPPORTED: 500
|
|
183
|
+
PRESENCE_NOT_SUPPORTED: 500,
|
|
184
|
+
CONSUMER_DEAD_LETTERED: 500
|
|
184
185
|
};
|
|
185
186
|
function errorStatus(code) {
|
|
186
187
|
return ERROR_STATUS[isServerErrorCode(code) ? code : "STORE_UNAVAILABLE"];
|
|
@@ -406,4 +407,4 @@ const sessionTag = (frame) => {
|
|
|
406
407
|
//#endregion
|
|
407
408
|
export { stateSnapshotToWire as A, serializedEventToWireJson as C, socketFrameFor as D, socketEventsFor as E, socketSubscribedFor as O, presenceSnapshotToWire as S, socketErrorAckFor as T, isWirePresenceSnapshot as _, errorFromWire as a, presencePatchToWire as b, eventFromCompactWire as c, heldEventBatch as d, isCompactWireEvent as f, isWirePresencePatch as g, isWireEvent as h, compactEventJson as i, wireVersion as j, socketUnsubscribedFor as k, eventFromWire as l, isStreamEvent as m, SOCKET_PING_FRAME as n, errorStatus as o, isStateSnapshotFrame as p, asA2Error as r, errorToWire as s, EVENT_WRITE_CHARS as t, eventToWire as u, parseSocketFrame as v, socketAckFor as w, presenceSnapshotFromWire as x, presencePatchFromWire as y };
|
|
408
409
|
|
|
409
|
-
//# sourceMappingURL=wire-
|
|
410
|
+
//# sourceMappingURL=wire-CV-Z7wz-.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire-CV-Z7wz-.js","names":[],"sources":["../src/wire.ts"],"sourcesContent":["/**\n * The wire format shared by server.fetch and experimental-a2/client\n * (browser side): events as JSON with ISO timestamps, and the A2Error\n * envelope. Internal module — both entry points re-export what's\n * public.\n */\n\nimport { A2Error, type A2ErrorCode, type A2ServerErrorCode } from './errors.ts'\nimport { nullProtoRecord } from './internal.ts'\nimport type { Event } from './store.ts'\nimport {\n isSerializedEvent,\n takeBuffered,\n type HeldEvent,\n type SerializedEvent,\n} from './stream-buffer.ts'\nimport type {\n PresenceMap,\n PresencePatch,\n PresenceSnapshot,\n} from './contract.ts'\n\nexport type WireEvent = {\n id: string\n type: string\n payload: unknown\n index: number\n sessionId: string\n /** ISO 8601 — revived to a Date on the client so reducers see the\n * same shape on both sides of the wire. */\n createdAt: string\n}\n\nexport function eventToWire(event: Event): WireEvent {\n return {\n id: event.id,\n type: event.type,\n payload: event.payload,\n index: event.index,\n sessionId: event.sessionId,\n createdAt: event.createdAt.toISOString(),\n }\n}\n\n// Consecutive frames usually repeat a stream's session, an event type, and\n// the timestamp of events appended together; each keeps its last encoding.\nconst lastEncoding = <T>(encode: (value: T) => string) => {\n let last: T | undefined\n let text = ''\n return (value: T): string => {\n if (value !== last) {\n text = encode(value)\n last = value\n }\n return text\n }\n}\nconst isoJson = lastEncoding((ms: number) =>\n JSON.stringify(new Date(ms).toISOString()),\n)\nconst sessionJson = lastEncoding((id: string) => JSON.stringify(id))\nconst typeJson = lastEncoding((type: string) => JSON.stringify(type))\n\n/**\n * `JSON.stringify(eventToWire(event))` for the parsed event, built around the\n * stored payload text instead of parsing and re-serializing it.\n */\nexport function serializedEventToWireJson(event: SerializedEvent): string {\n return `{\"id\":${JSON.stringify(event.id)},\"type\":${typeJson(event.type)},\"payload\":${event.payloadJson},\"index\":${event.index},\"sessionId\":${sessionJson(event.sessionId)},\"createdAt\":${isoJson(event.createdAtMs)}}`\n}\n\n/**\n * Wire version 2: what a client opts into with `wire=2` on the stream URL.\n * Live events travel in batches, without the `sessionId` the connection\n * already names, with `createdAt` as epoch milliseconds. Any other value,\n * or none, selects version 1.\n */\nexport type WireVersion = 1 | 2\n\nexport function wireVersion(value: string | null): WireVersion {\n return value === '2' ? 2 : 1\n}\n\n/**\n * Events already held by a stream join one write while it is shorter than\n * this many characters, so a write can exceed it by the event that crosses it.\n */\nexport const EVENT_WRITE_CHARS: number = 64 * 1024\n\n/** Stream items that are events, as opposed to snapshot or presence frames. */\nexport function isStreamEvent(item: object): item is HeldEvent {\n return !('kind' in item) && !('snapshot' in item) && !('participant' in item)\n}\n\n/**\n * `first` and the events `iterator` already holds, encoded, until the batch\n * reaches `EVENT_WRITE_CHARS`; `last` is the batch's final event.\n */\nexport function heldEventBatch(options: {\n first: HeldEvent\n iterator: object\n encode: (event: HeldEvent) => string\n}): { events: string[]; last: HeldEvent } {\n const { iterator, encode } = options\n let last = options.first\n const events = [encode(last)]\n let chars = events[0]!.length\n while (chars < EVENT_WRITE_CHARS) {\n const held = takeBuffered(iterator)\n if (!held) break\n const json = encode(held)\n events.push(json)\n chars += json.length + 1\n last = held\n }\n return { events, last }\n}\n\n/** A version 2 live event: `WireEvent` minus `sessionId`, ms timestamp. */\nexport type CompactWireEvent = {\n id: string\n type: string\n payload: unknown\n index: number\n createdAt: number\n}\n\n/** One version 2 event as JSON, splicing a held row's stored payload. */\nexport function compactEventJson(event: HeldEvent): string {\n if (!isSerializedEvent(event))\n return JSON.stringify({\n id: event.id,\n type: event.type,\n payload: event.payload,\n index: event.index,\n createdAt: event.createdAt.getTime(),\n })\n return `{\"id\":${JSON.stringify(event.id)},\"type\":${typeJson(event.type)},\"payload\":${event.payloadJson},\"index\":${event.index},\"createdAt\":${event.createdAtMs}}`\n}\n\nexport function isCompactWireEvent(value: unknown): value is CompactWireEvent {\n if (value === null || typeof value !== 'object') return false\n const v = value as Record<string, unknown>\n return (\n typeof v['id'] === 'string' &&\n typeof v['type'] === 'string' &&\n typeof v['index'] === 'number' &&\n typeof v['createdAt'] === 'number' &&\n Number.isFinite(v['createdAt'])\n )\n}\n\nexport function eventFromCompactWire(\n wire: CompactWireEvent,\n sessionId: string,\n): Event {\n return {\n id: wire.id,\n type: wire.type,\n payload: wire.payload,\n index: wire.index,\n sessionId,\n createdAt: new Date(wire.createdAt),\n }\n}\n\nexport function eventFromWire(wire: WireEvent): Event {\n return {\n id: wire.id,\n type: wire.type,\n payload: wire.payload,\n index: wire.index,\n sessionId: wire.sessionId,\n createdAt: new Date(wire.createdAt),\n }\n}\n\nexport function isWireEvent(value: unknown): value is WireEvent {\n if (value === null || typeof value !== 'object') return false\n const v = value as Record<string, unknown>\n return (\n typeof v['id'] === 'string' &&\n typeof v['type'] === 'string' &&\n typeof v['index'] === 'number' &&\n typeof v['sessionId'] === 'string' &&\n typeof v['createdAt'] === 'string'\n )\n}\n\n// ── the presence wire pair ───────────────────────────────────────────\n\n/** The `event: presence` SSE frame body: one patch as JSON. */\nexport type WirePresencePatch = {\n participant: string\n values: Record<string, unknown>\n seen: number\n /** ISO 8601 — revived to a Date on the client. */\n at: string\n}\n\n/**\n * The `event: presence-snapshot` SSE frame body: the in-memory\n * `PresenceSnapshot` shape verbatim, with each per-field `at`\n * serialized to ISO 8601.\n */\nexport type WirePresenceSnapshot = {\n snapshot: {\n [participant: string]: {\n [field: string]: { value: unknown; seen: number; at: string }\n }\n }\n}\n\nexport function presencePatchToWire(patch: PresencePatch): WirePresencePatch {\n return {\n participant: patch.participant,\n values: patch.values,\n seen: patch.seen,\n at: patch.at.toISOString(),\n }\n}\n\nexport function presencePatchFromWire(wire: WirePresencePatch): PresencePatch {\n return {\n participant: wire.participant,\n values: wire.values,\n seen: wire.seen,\n at: new Date(wire.at),\n }\n}\n\n// Participant and field keys come off the wire, so every object keyed\n// by them is built null-prototype — see `nullProtoRecord`.\nexport function presenceSnapshotToWire(\n snapshot: PresenceSnapshot,\n): WirePresenceSnapshot {\n const wire: WirePresenceSnapshot['snapshot'] = nullProtoRecord()\n for (const [participant, fields] of Object.entries(snapshot.snapshot)) {\n const wireFields: WirePresenceSnapshot['snapshot'][string] =\n nullProtoRecord()\n for (const [field, entry] of Object.entries(fields)) {\n if (entry === undefined) continue\n wireFields[field] = {\n value: entry.value,\n seen: entry.seen,\n at: entry.at.toISOString(),\n }\n }\n wire[participant] = wireFields\n }\n return { snapshot: wire }\n}\n\nexport function presenceSnapshotFromWire(\n wire: WirePresenceSnapshot,\n): PresenceSnapshot {\n const map: PresenceMap = nullProtoRecord()\n for (const [participant, fields] of Object.entries(wire.snapshot)) {\n const revived: PresenceMap[string] = nullProtoRecord()\n for (const [field, entry] of Object.entries(fields)) {\n revived[field] = {\n value: entry.value,\n seen: entry.seen,\n at: new Date(entry.at),\n }\n }\n map[participant] = revived\n }\n return { snapshot: map }\n}\n\nexport function isWirePresencePatch(\n value: unknown,\n): value is WirePresencePatch {\n if (value === null || typeof value !== 'object') return false\n const v = value as Record<string, unknown>\n return (\n typeof v['participant'] === 'string' &&\n v['values'] !== null &&\n typeof v['values'] === 'object' &&\n !Array.isArray(v['values']) &&\n typeof v['seen'] === 'number' &&\n typeof v['at'] === 'string'\n )\n}\n\nexport function isWirePresenceSnapshot(\n value: unknown,\n): value is WirePresenceSnapshot {\n if (value === null || typeof value !== 'object') return false\n const v = value as Record<string, unknown>\n return (\n v['snapshot'] !== null &&\n typeof v['snapshot'] === 'object' &&\n !Array.isArray(v['snapshot'])\n )\n}\n\n// ── the A2Error envelope ─────────────────────────────────────────────\n\nexport type WireError = {\n error: { code: A2ServerErrorCode; message: string; details?: unknown }\n}\n\nconst ERROR_STATUS: Record<A2ServerErrorCode, number> = {\n INVALID_PAYLOAD: 400,\n FORBIDDEN: 403,\n UNKNOWN_EVENT_TYPE: 400,\n PARTIAL_DUPLICATE_BATCH: 400,\n SUPERSEDED_ATTEMPT: 409,\n SESSION_CLEARED: 409,\n CLAIM_EXPIRED: 409,\n STORE_UNAVAILABLE: 503,\n STORE_NOT_CONFIGURED: 500,\n UNKNOWN_PRESENCE_FIELD: 400,\n PRESENCE_NOT_SUPPORTED: 500,\n CONSUMER_DEAD_LETTERED: 500,\n}\n\nexport function errorStatus(code: A2ErrorCode): number {\n return ERROR_STATUS[isServerErrorCode(code) ? code : 'STORE_UNAVAILABLE']\n}\n\nexport function errorToWire(error: A2Error): WireError {\n const failure = asA2Error(error)\n const body: WireError = {\n error: {\n code: failure.code as A2ServerErrorCode,\n message: failure.message,\n },\n }\n if (failure.details !== undefined) body.error.details = failure.details\n return body\n}\n\n/** Wrap an arbitrary thrown value for the wire: known server codes pass through,\n * anything else becomes STORE_UNAVAILABLE — from the client's\n * perspective an unknown server failure is retryable-once, not a\n * protocol contract. */\nexport function asA2Error(error: unknown): A2Error {\n return error instanceof A2Error && isServerErrorCode(error.code)\n ? error\n : new A2Error('STORE_UNAVAILABLE', 'internal error', { cause: error })\n}\n\n/** Rebuild an A2Error from a wire body; null if the body isn't one. */\nexport function errorFromWire(body: unknown): A2Error | null {\n if (body === null || typeof body !== 'object') return null\n const err = (body as { error?: unknown }).error\n if (err === null || typeof err !== 'object') return null\n const { code, message, details } = err as Record<string, unknown>\n if (typeof code !== 'string' || !isServerErrorCode(code)) return null\n return new A2Error(code, String(message ?? code), {\n details,\n })\n}\n\nconst isServerErrorCode = (code: string): code is A2ServerErrorCode =>\n Object.hasOwn(ERROR_STATUS, code)\n\n// ── the ws frame layer ───────────────────────────────────────────────\n// The SSE lanes reframed for a socket (specs/a2-api.md §13): every\n// message is one JSON text frame, and a frame is its wire payload plus\n// a `kind` discriminant (plus `req` where a reply must correlate).\n// Unknown kinds are skipped by both sides — the same\n// forward-compatibility rule as named SSE frames.\n//\n// The multiplexed superset rides the same frames: `subscribe` /\n// `unsubscribe` up-frames open and close per-session lanes on one\n// socket, `sessionId` tags route everything else. Single-session\n// frames (no tags) keep parsing unchanged — a route opts into\n// multiplexing by choosing the multi-session server handler, never by\n// breaking the old protocol.\n\n/**\n * Server → client, parsed: a live-stream item, a push ack, a\n * subscription lifecycle notice, or the heartbeat. `parseSocketFrame`\n * yields these; frames of unknown kind (or ones failing their shape\n * guard) come back `null`. `sessionId` is present on frames from a\n * multiplexed socket and absent on a single-session one.\n */\nexport type StateSnapshotFrame = {\n kind: 'snapshot'\n sessionId: string\n name: string\n index: number\n state: unknown\n stateUndefined?: true\n /** The session's latest clear marker, when it has been cleared. */\n clearIndex?: number\n}\n\nexport function stateSnapshotToWire(\n frame: StateSnapshotFrame,\n): StateSnapshotFrame {\n return frame.state === undefined\n ? { ...frame, state: null, stateUndefined: true }\n : frame\n}\n\nexport function isStateSnapshotFrame(\n value: unknown,\n): value is StateSnapshotFrame {\n if (value === null || typeof value !== 'object') return false\n const frame = value as Record<string, unknown>\n return (\n frame['kind'] === 'snapshot' &&\n typeof frame['sessionId'] === 'string' &&\n typeof frame['name'] === 'string' &&\n frame['name'].length > 0 &&\n typeof frame['index'] === 'number' &&\n Number.isSafeInteger(frame['index']) &&\n frame['index'] >= 0 &&\n Object.hasOwn(frame, 'state') &&\n (!Object.hasOwn(frame, 'stateUndefined') ||\n (frame['stateUndefined'] === true && frame['state'] === null)) &&\n (!Object.hasOwn(frame, 'clearIndex') ||\n (typeof frame['clearIndex'] === 'number' &&\n Number.isSafeInteger(frame['clearIndex']) &&\n frame['clearIndex'] > 0 &&\n frame['clearIndex'] <= frame['index']))\n )\n}\n\nexport type SubscriptionStart =\n | { index: number; bootstrap?: never; reducer?: string }\n | { bootstrap: true; reducer: string; index?: never }\n\nexport type SocketDownFrame =\n | StateSnapshotFrame\n | { kind: 'event'; event: WireEvent }\n | { kind: 'events'; events: CompactWireEvent[]; sessionId?: string }\n | { kind: 'presence'; patch: WirePresencePatch; sessionId?: string }\n | {\n kind: 'presence-snapshot'\n snapshot: WirePresenceSnapshot\n sessionId?: string\n }\n | { kind: 'ack'; req: number; events: WireEvent[]; sessionId?: string }\n | { kind: 'ack'; req: number; error: A2Error; sessionId?: string }\n | { kind: 'subscribed'; sessionId: string; attempt?: number }\n | {\n kind: 'unsubscribed'\n sessionId: string\n attempt?: number\n reason?: string\n error?: A2Error\n }\n | { kind: 'ping' }\n\n/**\n * Client → server: the push envelope's two planes. `sessionId` is\n * implied by the socket on a single-session connection and required by\n * the multiplexed handler. Plain JSON on the wire; the server parses\n * and validates them through the same seams as `parsePushBody`.\n */\nexport type SocketPushFrame = {\n kind: 'push'\n /** Client-local ack correlator — opaque to the server. */\n req: number\n events: Array<{ type: string; payload: unknown; id?: string }>\n sessionId?: string\n}\n\nexport type SocketPresenceFrame = {\n kind: 'presence'\n participant: string\n values: Record<string, unknown>\n seen?: number\n at?: number\n sessionId?: string\n}\n\n/** One session lane in a subscribe frame. The server echoes `attempt`\n * on that lane's `subscribed`/`unsubscribed` answers, so a client that\n * re-subscribed before an answer arrived can tell the answers apart. */\nexport type SocketSubscribeEntry = {\n id: string\n attempt?: number\n} & SubscriptionStart\n\n/** Open per-session lanes on a multiplexed socket. `index` is each\n * session's exclusive resume frontier — `stream({ startAfter })`. */\nexport type SocketSubscribeFrame = {\n kind: 'subscribe'\n sessions: SocketSubscribeEntry[]\n}\n\nexport type SocketUnsubscribeFrame = {\n kind: 'unsubscribe'\n sessions: string[]\n}\n\nexport type SocketUpFrame =\n | SocketPushFrame\n | SocketPresenceFrame\n | SocketSubscribeFrame\n | SocketUnsubscribeFrame\n\nexport const SOCKET_PING_FRAME: string = JSON.stringify({ kind: 'ping' })\n\n/** One stream item as a socket frame — `sseResponse`'s framing over\n * the same codecs, with `kind` instead of an SSE event name. On a\n * multiplexed socket presence frames carry the `sessionId` tag; events\n * already carry theirs in the wire event. */\nexport function socketFrameFor(\n item: Event | PresencePatch | PresenceSnapshot | StateSnapshotFrame,\n sessionId?: string,\n): string {\n const tag = sessionId === undefined ? {} : { sessionId }\n if ('kind' in item) return JSON.stringify(stateSnapshotToWire(item))\n if ('snapshot' in item) {\n return JSON.stringify({\n kind: 'presence-snapshot',\n ...presenceSnapshotToWire(item),\n ...tag,\n })\n }\n if ('participant' in item) {\n return JSON.stringify({\n kind: 'presence',\n ...presencePatchToWire(item),\n ...tag,\n })\n }\n return JSON.stringify({ kind: 'event', ...eventToWire(item) })\n}\n\n/** A version 2 batch frame from compact event JSON; a multiplexed\n * socket names the batch's session once. */\nexport function socketEventsFor(events: string[], sessionId?: string): string {\n const tag =\n sessionId === undefined ? '' : `\"sessionId\":${JSON.stringify(sessionId)},`\n return `{\"kind\":\"events\",${tag}\"events\":[${events.join(',')}]}`\n}\n\nexport function socketAckFor(\n req: number,\n events: Event[],\n sessionId?: string,\n): string {\n return JSON.stringify({\n kind: 'ack',\n req,\n events: events.map(eventToWire),\n ...(sessionId === undefined ? {} : { sessionId }),\n })\n}\n\nexport function socketErrorAckFor(\n req: number,\n error: A2Error,\n sessionId?: string,\n): string {\n return JSON.stringify({\n kind: 'ack',\n req,\n ...errorToWire(error),\n ...(sessionId === undefined ? {} : { sessionId }),\n })\n}\n\nexport function socketSubscribedFor(options: {\n sessionId: string\n attempt?: number | undefined\n}): string {\n const { sessionId, attempt } = options\n return JSON.stringify({\n kind: 'subscribed',\n sessionId,\n ...(attempt === undefined ? {} : { attempt }),\n })\n}\n\n/** The subscription-over notice: with a `reason` the server rejected\n * or lost the session's stream, without one it ended cleanly. */\nexport function socketUnsubscribedFor(options: {\n sessionId: string\n attempt?: number | undefined\n reason?: string\n error?: A2Error\n}): string {\n const { sessionId, attempt, reason, error } = options\n return JSON.stringify({\n kind: 'unsubscribed',\n sessionId,\n ...(attempt === undefined ? {} : { attempt }),\n ...(reason === undefined ? {} : { reason }),\n ...(error === undefined ? {} : errorToWire(error)),\n })\n}\n\nconst isAttempt = (value: unknown): value is number =>\n typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\n\nexport function parseSocketFrame(data: string): SocketDownFrame | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(data)\n } catch {\n return null\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return null\n }\n const frame = parsed as Record<string, unknown>\n switch (frame['kind']) {\n case 'snapshot':\n if (!isStateSnapshotFrame(parsed))\n throw new A2Error('INVALID_PAYLOAD', 'invalid bootstrap snapshot')\n return parsed\n case 'ping':\n return { kind: 'ping' }\n case 'event': {\n if (!isWireEvent(parsed)) return null\n const { id, type, payload, index, sessionId, createdAt } = parsed\n return {\n kind: 'event',\n event: { id, type, payload, index, sessionId, createdAt },\n }\n }\n case 'events': {\n // Dropping a batch would leave a gap the frontier then skips past.\n const events = frame['events']\n if (!Array.isArray(events) || !events.every(isCompactWireEvent))\n throw new A2Error('INVALID_PAYLOAD', 'invalid event batch')\n return { kind: 'events', events, ...sessionTag(frame) }\n }\n case 'presence': {\n if (!isWirePresencePatch(parsed)) return null\n const { participant, values, seen, at } = parsed\n return {\n kind: 'presence',\n patch: { participant, values, seen, at },\n ...sessionTag(frame),\n }\n }\n case 'presence-snapshot': {\n if (!isWirePresenceSnapshot(parsed)) return null\n return {\n kind: 'presence-snapshot',\n snapshot: { snapshot: parsed.snapshot },\n ...sessionTag(frame),\n }\n }\n case 'subscribed': {\n const { sessionId, attempt } = frame\n if (typeof sessionId !== 'string') return null\n if (attempt !== undefined && !isAttempt(attempt)) return null\n return {\n kind: 'subscribed',\n sessionId,\n ...(attempt === undefined ? {} : { attempt }),\n }\n }\n case 'unsubscribed': {\n const { sessionId, attempt } = frame\n if (typeof sessionId !== 'string') return null\n if (attempt !== undefined && !isAttempt(attempt)) return null\n const reason = frame['reason']\n const error =\n errorFromWire(parsed) ??\n ('error' in frame\n ? new A2Error(\n 'STORE_UNAVAILABLE',\n 'unintelligible subscription error',\n )\n : null)\n return {\n kind: 'unsubscribed',\n sessionId,\n ...(attempt === undefined ? {} : { attempt }),\n ...(typeof reason === 'string' ? { reason } : {}),\n ...(error === null ? {} : { error }),\n }\n }\n case 'ack': {\n const req = frame['req']\n if (typeof req !== 'number') return null\n const tag = sessionTag(frame)\n const events = frame['events']\n if (Array.isArray(events) && events.every(isWireEvent)) {\n return { kind: 'ack', req, events, ...tag }\n }\n const error = errorFromWire(parsed)\n if (error) return { kind: 'ack', req, error, ...tag }\n // A correlatable ack must never be dropped: its waiter would hang\n // forever behind a healthy socket (pings keep the watchdog fed).\n // An outcome this client cannot interpret — an error code from a\n // newer server, an event shape that fails a guard — degrades to\n // a lost ack: retryable, and the client-generated ids make the\n // retry idempotent even if the append actually committed.\n return {\n kind: 'ack',\n req,\n error: new A2Error('STORE_UNAVAILABLE', 'unintelligible ack'),\n ...tag,\n }\n }\n default:\n return null\n }\n}\n\nconst sessionTag = (frame: Record<string, unknown>): { sessionId?: string } => {\n const sessionId = frame['sessionId']\n return typeof sessionId === 'string' ? { sessionId } : {}\n}\n"],"mappings":";;;;;;;;;;AAiCA,SAAgB,YAAY,OAAyB;CACnD,OAAO;EACL,IAAI,MAAM;EACV,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,OAAO,MAAM;EACb,WAAW,MAAM;EACjB,WAAW,MAAM,UAAU,YAAY;CACzC;AACF;AAIA,MAAM,gBAAmB,WAAiC;CACxD,IAAI;CACJ,IAAI,OAAO;CACX,QAAQ,UAAqB;EAC3B,IAAI,UAAU,MAAM;GAClB,OAAO,OAAO,KAAK;GACnB,OAAO;EACT;EACA,OAAO;CACT;AACF;AACA,MAAM,UAAU,cAAc,OAC5B,KAAK,UAAU,IAAI,KAAK,EAAE,CAAC,CAAC,YAAY,CAAC,CAC3C;AACA,MAAM,cAAc,cAAc,OAAe,KAAK,UAAU,EAAE,CAAC;AACnE,MAAM,WAAW,cAAc,SAAiB,KAAK,UAAU,IAAI,CAAC;;;;;AAMpE,SAAgB,0BAA0B,OAAgC;CACxE,OAAO,SAAS,KAAK,UAAU,MAAM,EAAE,EAAE,UAAU,SAAS,MAAM,IAAI,EAAE,aAAa,MAAM,YAAY,WAAW,MAAM,MAAM,eAAe,YAAY,MAAM,SAAS,EAAE,eAAe,QAAQ,MAAM,WAAW,EAAE;AACtN;AAUA,SAAgB,YAAY,OAAmC;CAC7D,OAAO,UAAU,MAAM,IAAI;AAC7B;;;;;AAMA,MAAa,oBAA4B;;AAGzC,SAAgB,cAAc,MAAiC;CAC7D,OAAO,EAAE,UAAU,SAAS,EAAE,cAAc,SAAS,EAAE,iBAAiB;AAC1E;;;;;AAMA,SAAgB,eAAe,SAIW;CACxC,MAAM,EAAE,UAAU,WAAW;CAC7B,IAAI,OAAO,QAAQ;CACnB,MAAM,SAAS,CAAC,OAAO,IAAI,CAAC;CAC5B,IAAI,QAAQ,OAAO,EAAE,CAAE;CACvB,OAAO,QAAQ,mBAAmB;EAChC,MAAM,OAAO,aAAa,QAAQ;EAClC,IAAI,CAAC,MAAM;EACX,MAAM,OAAO,OAAO,IAAI;EACxB,OAAO,KAAK,IAAI;EAChB,SAAS,KAAK,SAAS;EACvB,OAAO;CACT;CACA,OAAO;EAAE;EAAQ;CAAK;AACxB;;AAYA,SAAgB,iBAAiB,OAA0B;CACzD,IAAI,CAAC,kBAAkB,KAAK,GAC1B,OAAO,KAAK,UAAU;EACpB,IAAI,MAAM;EACV,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,OAAO,MAAM;EACb,WAAW,MAAM,UAAU,QAAQ;CACrC,CAAC;CACH,OAAO,SAAS,KAAK,UAAU,MAAM,EAAE,EAAE,UAAU,SAAS,MAAM,IAAI,EAAE,aAAa,MAAM,YAAY,WAAW,MAAM,MAAM,eAAe,MAAM,YAAY;AACjK;AAEA,SAAgB,mBAAmB,OAA2C;CAC5E,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,IAAI;CACV,OACE,OAAO,EAAE,UAAU,YACnB,OAAO,EAAE,YAAY,YACrB,OAAO,EAAE,aAAa,YACtB,OAAO,EAAE,iBAAiB,YAC1B,OAAO,SAAS,EAAE,YAAY;AAElC;AAEA,SAAgB,qBACd,MACA,WACO;CACP,OAAO;EACL,IAAI,KAAK;EACT,MAAM,KAAK;EACX,SAAS,KAAK;EACd,OAAO,KAAK;EACZ;EACA,WAAW,IAAI,KAAK,KAAK,SAAS;CACpC;AACF;AAEA,SAAgB,cAAc,MAAwB;CACpD,OAAO;EACL,IAAI,KAAK;EACT,MAAM,KAAK;EACX,SAAS,KAAK;EACd,OAAO,KAAK;EACZ,WAAW,KAAK;EAChB,WAAW,IAAI,KAAK,KAAK,SAAS;CACpC;AACF;AAEA,SAAgB,YAAY,OAAoC;CAC9D,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,IAAI;CACV,OACE,OAAO,EAAE,UAAU,YACnB,OAAO,EAAE,YAAY,YACrB,OAAO,EAAE,aAAa,YACtB,OAAO,EAAE,iBAAiB,YAC1B,OAAO,EAAE,iBAAiB;AAE9B;AA0BA,SAAgB,oBAAoB,OAAyC;CAC3E,OAAO;EACL,aAAa,MAAM;EACnB,QAAQ,MAAM;EACd,MAAM,MAAM;EACZ,IAAI,MAAM,GAAG,YAAY;CAC3B;AACF;AAEA,SAAgB,sBAAsB,MAAwC;CAC5E,OAAO;EACL,aAAa,KAAK;EAClB,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,IAAI,IAAI,KAAK,KAAK,EAAE;CACtB;AACF;AAIA,SAAgB,uBACd,UACsB;CACtB,MAAM,OAAyC,gBAAgB;CAC/D,KAAK,MAAM,CAAC,aAAa,WAAW,OAAO,QAAQ,SAAS,QAAQ,GAAG;EACrE,MAAM,aACJ,gBAAgB;EAClB,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,MAAM,GAAG;GACnD,IAAI,UAAU,KAAA,GAAW;GACzB,WAAW,SAAS;IAClB,OAAO,MAAM;IACb,MAAM,MAAM;IACZ,IAAI,MAAM,GAAG,YAAY;GAC3B;EACF;EACA,KAAK,eAAe;CACtB;CACA,OAAO,EAAE,UAAU,KAAK;AAC1B;AAEA,SAAgB,yBACd,MACkB;CAClB,MAAM,MAAmB,gBAAgB;CACzC,KAAK,MAAM,CAAC,aAAa,WAAW,OAAO,QAAQ,KAAK,QAAQ,GAAG;EACjE,MAAM,UAA+B,gBAAgB;EACrD,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,MAAM,GAChD,QAAQ,SAAS;GACf,OAAO,MAAM;GACb,MAAM,MAAM;GACZ,IAAI,IAAI,KAAK,MAAM,EAAE;EACvB;EAEF,IAAI,eAAe;CACrB;CACA,OAAO,EAAE,UAAU,IAAI;AACzB;AAEA,SAAgB,oBACd,OAC4B;CAC5B,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,IAAI;CACV,OACE,OAAO,EAAE,mBAAmB,YAC5B,EAAE,cAAc,QAChB,OAAO,EAAE,cAAc,YACvB,CAAC,MAAM,QAAQ,EAAE,SAAS,KAC1B,OAAO,EAAE,YAAY,YACrB,OAAO,EAAE,UAAU;AAEvB;AAEA,SAAgB,uBACd,OAC+B;CAC/B,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,IAAI;CACV,OACE,EAAE,gBAAgB,QAClB,OAAO,EAAE,gBAAgB,YACzB,CAAC,MAAM,QAAQ,EAAE,WAAW;AAEhC;AAQA,MAAM,eAAkD;CACtD,iBAAiB;CACjB,WAAW;CACX,oBAAoB;CACpB,yBAAyB;CACzB,oBAAoB;CACpB,iBAAiB;CACjB,eAAe;CACf,mBAAmB;CACnB,sBAAsB;CACtB,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;AAC1B;AAEA,SAAgB,YAAY,MAA2B;CACrD,OAAO,aAAa,kBAAkB,IAAI,IAAI,OAAO;AACvD;AAEA,SAAgB,YAAY,OAA2B;CACrD,MAAM,UAAU,UAAU,KAAK;CAC/B,MAAM,OAAkB,EACtB,OAAO;EACL,MAAM,QAAQ;EACd,SAAS,QAAQ;CACnB,EACF;CACA,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,MAAM,UAAU,QAAQ;CAChE,OAAO;AACT;;;;;AAMA,SAAgB,UAAU,OAAyB;CACjD,OAAO,iBAAiB,WAAW,kBAAkB,MAAM,IAAI,IAC3D,QACA,IAAI,QAAQ,qBAAqB,kBAAkB,EAAE,OAAO,MAAM,CAAC;AACzE;;AAGA,SAAgB,cAAc,MAA+B;CAC3D,IAAI,SAAS,QAAQ,OAAO,SAAS,UAAU,OAAO;CACtD,MAAM,MAAO,KAA6B;CAC1C,IAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU,OAAO;CACpD,MAAM,EAAE,MAAM,SAAS,YAAY;CACnC,IAAI,OAAO,SAAS,YAAY,CAAC,kBAAkB,IAAI,GAAG,OAAO;CACjE,OAAO,IAAI,QAAQ,MAAM,OAAO,WAAW,IAAI,GAAG,EAChD,QACF,CAAC;AACH;AAEA,MAAM,qBAAqB,SACzB,OAAO,OAAO,cAAc,IAAI;AAkClC,SAAgB,oBACd,OACoB;CACpB,OAAO,MAAM,UAAU,KAAA,IACnB;EAAE,GAAG;EAAO,OAAO;EAAM,gBAAgB;CAAK,IAC9C;AACN;AAEA,SAAgB,qBACd,OAC6B;CAC7B,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,QAAQ;CACd,OACE,MAAM,YAAY,cAClB,OAAO,MAAM,iBAAiB,YAC9B,OAAO,MAAM,YAAY,YACzB,MAAM,OAAO,CAAC,SAAS,KACvB,OAAO,MAAM,aAAa,YAC1B,OAAO,cAAc,MAAM,QAAQ,KACnC,MAAM,YAAY,KAClB,OAAO,OAAO,OAAO,OAAO,MAC3B,CAAC,OAAO,OAAO,OAAO,gBAAgB,KACpC,MAAM,sBAAsB,QAAQ,MAAM,aAAa,UACzD,CAAC,OAAO,OAAO,OAAO,YAAY,KAChC,OAAO,MAAM,kBAAkB,YAC9B,OAAO,cAAc,MAAM,aAAa,KACxC,MAAM,gBAAgB,KACtB,MAAM,iBAAiB,MAAM;AAErC;AA6EA,MAAa,oBAA4B,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;;;;;AAMxE,SAAgB,eACd,MACA,WACQ;CACR,MAAM,MAAM,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;CACvD,IAAI,UAAU,MAAM,OAAO,KAAK,UAAU,oBAAoB,IAAI,CAAC;CACnE,IAAI,cAAc,MAChB,OAAO,KAAK,UAAU;EACpB,MAAM;EACN,GAAG,uBAAuB,IAAI;EAC9B,GAAG;CACL,CAAC;CAEH,IAAI,iBAAiB,MACnB,OAAO,KAAK,UAAU;EACpB,MAAM;EACN,GAAG,oBAAoB,IAAI;EAC3B,GAAG;CACL,CAAC;CAEH,OAAO,KAAK,UAAU;EAAE,MAAM;EAAS,GAAG,YAAY,IAAI;CAAE,CAAC;AAC/D;;;AAIA,SAAgB,gBAAgB,QAAkB,WAA4B;CAG5E,OAAO,oBADL,cAAc,KAAA,IAAY,KAAK,eAAe,KAAK,UAAU,SAAS,EAAE,GAC3C,YAAY,OAAO,KAAK,GAAG,EAAE;AAC9D;AAEA,SAAgB,aACd,KACA,QACA,WACQ;CACR,OAAO,KAAK,UAAU;EACpB,MAAM;EACN;EACA,QAAQ,OAAO,IAAI,WAAW;EAC9B,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;CACjD,CAAC;AACH;AAEA,SAAgB,kBACd,KACA,OACA,WACQ;CACR,OAAO,KAAK,UAAU;EACpB,MAAM;EACN;EACA,GAAG,YAAY,KAAK;EACpB,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;CACjD,CAAC;AACH;AAEA,SAAgB,oBAAoB,SAGzB;CACT,MAAM,EAAE,WAAW,YAAY;CAC/B,OAAO,KAAK,UAAU;EACpB,MAAM;EACN;EACA,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;CAC7C,CAAC;AACH;;;AAIA,SAAgB,sBAAsB,SAK3B;CACT,MAAM,EAAE,WAAW,SAAS,QAAQ,UAAU;CAC9C,OAAO,KAAK,UAAU;EACpB,MAAM;EACN;EACA,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;EAC3C,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EACzC,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,YAAY,KAAK;CAClD,CAAC;AACH;AAEA,MAAM,aAAa,UACjB,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,SAAS;AAEvE,SAAgB,iBAAiB,MAAsC;CACrE,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,QAAQ;EACN,OAAO;CACT;CACA,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GACvE,OAAO;CAET,MAAM,QAAQ;CACd,QAAQ,MAAM,SAAd;EACE,KAAK;GACH,IAAI,CAAC,qBAAqB,MAAM,GAC9B,MAAM,IAAI,QAAQ,mBAAmB,4BAA4B;GACnE,OAAO;EACT,KAAK,QACH,OAAO,EAAE,MAAM,OAAO;EACxB,KAAK,SAAS;GACZ,IAAI,CAAC,YAAY,MAAM,GAAG,OAAO;GACjC,MAAM,EAAE,IAAI,MAAM,SAAS,OAAO,WAAW,cAAc;GAC3D,OAAO;IACL,MAAM;IACN,OAAO;KAAE;KAAI;KAAM;KAAS;KAAO;KAAW;IAAU;GAC1D;EACF;EACA,KAAK,UAAU;GAEb,MAAM,SAAS,MAAM;GACrB,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,CAAC,OAAO,MAAM,kBAAkB,GAC5D,MAAM,IAAI,QAAQ,mBAAmB,qBAAqB;GAC5D,OAAO;IAAE,MAAM;IAAU;IAAQ,GAAG,WAAW,KAAK;GAAE;EACxD;EACA,KAAK,YAAY;GACf,IAAI,CAAC,oBAAoB,MAAM,GAAG,OAAO;GACzC,MAAM,EAAE,aAAa,QAAQ,MAAM,OAAO;GAC1C,OAAO;IACL,MAAM;IACN,OAAO;KAAE;KAAa;KAAQ;KAAM;IAAG;IACvC,GAAG,WAAW,KAAK;GACrB;EACF;EACA,KAAK;GACH,IAAI,CAAC,uBAAuB,MAAM,GAAG,OAAO;GAC5C,OAAO;IACL,MAAM;IACN,UAAU,EAAE,UAAU,OAAO,SAAS;IACtC,GAAG,WAAW,KAAK;GACrB;EAEF,KAAK,cAAc;GACjB,MAAM,EAAE,WAAW,YAAY;GAC/B,IAAI,OAAO,cAAc,UAAU,OAAO;GAC1C,IAAI,YAAY,KAAA,KAAa,CAAC,UAAU,OAAO,GAAG,OAAO;GACzD,OAAO;IACL,MAAM;IACN;IACA,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;GAC7C;EACF;EACA,KAAK,gBAAgB;GACnB,MAAM,EAAE,WAAW,YAAY;GAC/B,IAAI,OAAO,cAAc,UAAU,OAAO;GAC1C,IAAI,YAAY,KAAA,KAAa,CAAC,UAAU,OAAO,GAAG,OAAO;GACzD,MAAM,SAAS,MAAM;GACrB,MAAM,QACJ,cAAc,MAAM,MACnB,WAAW,QACR,IAAI,QACF,qBACA,mCACF,IACA;GACN,OAAO;IACL,MAAM;IACN;IACA,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;IAC3C,GAAI,OAAO,WAAW,WAAW,EAAE,OAAO,IAAI,CAAC;IAC/C,GAAI,UAAU,OAAO,CAAC,IAAI,EAAE,MAAM;GACpC;EACF;EACA,KAAK,OAAO;GACV,MAAM,MAAM,MAAM;GAClB,IAAI,OAAO,QAAQ,UAAU,OAAO;GACpC,MAAM,MAAM,WAAW,KAAK;GAC5B,MAAM,SAAS,MAAM;GACrB,IAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,MAAM,WAAW,GACnD,OAAO;IAAE,MAAM;IAAO;IAAK;IAAQ,GAAG;GAAI;GAE5C,MAAM,QAAQ,cAAc,MAAM;GAClC,IAAI,OAAO,OAAO;IAAE,MAAM;IAAO;IAAK;IAAO,GAAG;GAAI;GAOpD,OAAO;IACL,MAAM;IACN;IACA,OAAO,IAAI,QAAQ,qBAAqB,oBAAoB;IAC5D,GAAG;GACL;EACF;EACA,SACE,OAAO;CACX;AACF;AAEA,MAAM,cAAc,UAA2D;CAC7E,MAAM,YAAY,MAAM;CACxB,OAAO,OAAO,cAAc,WAAW,EAAE,UAAU,IAAI,CAAC;AAC1D"}
|