experimental-a2 0.16.1 → 0.18.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 +373 -0
- package/README.md +1 -1
- package/dist/{actor-ohPC-81x.d.ts → actor-Bjce9_5U.d.ts} +21 -11
- package/dist/actor-Bjce9_5U.d.ts.map +1 -0
- package/dist/actor-client.d.ts +2 -5
- package/dist/actor-client.d.ts.map +1 -1
- package/dist/actor-client.js +13 -4
- package/dist/actor-client.js.map +1 -1
- package/dist/actor-react.d.ts +5 -3
- package/dist/actor-react.d.ts.map +1 -1
- package/dist/actor-react.js +20 -10
- package/dist/actor-react.js.map +1 -1
- package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
- package/dist/actor-shared-t7w8xIKP.js.map +1 -0
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +42 -23
- package/dist/actor.js.map +1 -1
- package/dist/{ai-DgOBltJ_.js → ai-BpOGm-zB.js} +621 -216
- package/dist/ai-BpOGm-zB.js.map +1 -0
- package/dist/{ai-CrEf6p_W.d.ts → ai-DrUaMyRt.d.ts} +114 -100
- package/dist/ai-DrUaMyRt.d.ts.map +1 -0
- package/dist/ai-client.d.ts +27 -0
- package/dist/ai-client.d.ts.map +1 -0
- package/dist/ai-client.js +29 -0
- package/dist/ai-client.js.map +1 -0
- package/dist/ai-fx.browser.d.ts +1 -0
- package/dist/ai-fx.browser.js +6 -0
- package/dist/ai-fx.browser.js.map +1 -0
- package/dist/ai-fx.d.ts +10 -0
- package/dist/ai-fx.d.ts.map +1 -0
- package/dist/ai-fx.js +634 -0
- package/dist/ai-fx.js.map +1 -0
- package/dist/ai-harness-BuLh_EYi.js +273 -0
- package/dist/ai-harness-BuLh_EYi.js.map +1 -0
- package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
- package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
- package/dist/ai-pi.browser.d.ts +1 -0
- package/dist/ai-pi.browser.js +6 -0
- package/dist/ai-pi.browser.js.map +1 -0
- package/dist/ai-pi.d.ts +17 -0
- package/dist/ai-pi.d.ts.map +1 -0
- package/dist/ai-pi.js +904 -0
- package/dist/ai-pi.js.map +1 -0
- package/dist/ai-react.d.ts +96 -0
- package/dist/ai-react.d.ts.map +1 -0
- package/dist/ai-react.js +142 -0
- package/dist/ai-react.js.map +1 -0
- package/dist/ai-server.browser.js +2 -2
- package/dist/ai-server.browser.js.map +1 -1
- package/dist/ai-server.d.ts +109 -43
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +526 -283
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/bounded-cache-CPHWF-8h.js +41 -0
- package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
- package/dist/{client-BYzHjkwU.js → client-BReKqwZC.js} +447 -80
- package/dist/client-BReKqwZC.js.map +1 -0
- package/dist/{client-CzyacQpJ.d.ts → client-h_DKFAjj.d.ts} +14 -13
- package/dist/client-h_DKFAjj.d.ts.map +1 -0
- package/dist/client.d.ts +4 -2
- package/dist/client.js +1 -1
- package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
- package/dist/contract-BYKASlCV.js.map +1 -0
- package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
- package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +7 -3
- package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +1 -0
- package/dist/{internal-Dq2qYxou.js → internal-B2T3U4Rq.js} +29 -2
- package/dist/internal-B2T3U4Rq.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/react-BBXgHWFc.d.ts +86 -0
- package/dist/react-BBXgHWFc.d.ts.map +1 -0
- package/dist/react.d.ts +2 -122
- package/dist/react.js +107 -100
- package/dist/react.js.map +1 -1
- package/dist/reducer-BAXc8JyF.js +87 -0
- package/dist/reducer-BAXc8JyF.js.map +1 -0
- package/dist/{reducer-otzHjuJj.d.ts → reducer-CqA59Q6U.d.ts} +46 -30
- package/dist/reducer-CqA59Q6U.d.ts.map +1 -0
- package/dist/reducer-runtime-DQwz_J59.js +28 -0
- package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +3 -3
- package/dist/scheduler-qstash.d.ts.map +1 -1
- package/dist/scheduler-qstash.js +33 -10
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
- package/dist/scheduler-task-X1SToDjP.js.map +1 -0
- package/dist/scheduler-vercel.d.ts +4 -9
- package/dist/scheduler-vercel.d.ts.map +1 -1
- package/dist/scheduler-vercel.js +25 -44
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-BD5ckxZb.js → server-Dw_k_7kO.js} +904 -219
- package/dist/server-Dw_k_7kO.js.map +1 -0
- package/dist/{server-DbD7IJVK.d.ts → server-En7cbi3v.d.ts} +116 -26
- package/dist/server-En7cbi3v.d.ts.map +1 -0
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/session-clear-Bo-XTFEy.d.ts +12 -0
- package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
- package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
- package/dist/session-clear-DGI4jTQ5.js.map +1 -0
- package/dist/{store-DyZM6fS5.d.ts → store-Djdv0bnZ.d.ts} +73 -3
- package/dist/store-Djdv0bnZ.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +205 -66
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-CmxUbV93.js → store-polling-B5FcwJfo.js} +21 -4
- package/dist/store-polling-B5FcwJfo.js.map +1 -0
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +450 -108
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-BAwS8RWn.js} +3 -3
- package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-BAwS8RWn.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.d.ts.map +1 -1
- package/dist/store-redis-http.js +293 -11
- package/dist/store-redis-http.js.map +1 -1
- package/dist/store-redis-notify-BRITti_o.js +2493 -0
- package/dist/store-redis-notify-BRITti_o.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +11 -11
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +197 -33
- package/dist/store-sqlite.js.map +1 -1
- package/dist/stream-buffer-Cx7AAPGq.js +23 -0
- package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
- package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
- package/dist/{wire-BO5wWCb1.js → wire-rSUglYo8.js} +132 -10
- package/dist/wire-rSUglYo8.js.map +1 -0
- package/docs/01-quickstart.mdx +50 -84
- package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
- package/docs/actors/01-introduction.mdx +48 -82
- package/docs/actors/02-concurrency.mdx +43 -70
- package/docs/actors/03-timers.mdx +28 -42
- package/docs/actors/04-routes.mdx +110 -137
- package/docs/actors/meta.ts +1 -1
- package/docs/agents/01-introduction.mdx +257 -0
- package/docs/agents/02-tools.mdx +247 -0
- package/docs/agents/03-conversations.mdx +538 -0
- package/docs/agents/04-harnesses.mdx +136 -0
- package/docs/agents/meta.ts +1 -0
- package/docs/client/01-react.mdx +294 -0
- package/docs/client/02-transports.mdx +86 -0
- package/docs/client/03-presence.mdx +224 -0
- package/docs/client/meta.ts +1 -0
- package/docs/contract/01-events.mdx +57 -0
- package/docs/contract/02-state.mdx +142 -0
- package/docs/contract/03-sessions.mdx +175 -0
- package/docs/index.mdx +112 -156
- package/docs/reference/01-core.mdx +139 -0
- package/docs/reference/02-server.mdx +855 -0
- package/docs/reference/03-client.mdx +301 -0
- package/docs/reference/04-ai.mdx +722 -0
- package/docs/reference/05-adapters.mdx +351 -0
- package/docs/reference/06-errors.mdx +138 -0
- package/docs/reference/meta.ts +1 -1
- package/docs/server/01-handlers.mdx +200 -0
- package/docs/server/02-timers.mdx +130 -0
- package/docs/server/03-cancellation.mdx +141 -0
- package/docs/server/04-routes.mdx +243 -0
- package/docs/server/05-production.mdx +322 -0
- package/docs/server/06-guarantees.mdx +123 -0
- package/examples/playground/DEPLOY.md +53 -7
- package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
- package/examples/playground/app/a0/[appId]/page.tsx +53 -0
- package/examples/playground/app/a0/a0.css +818 -0
- package/examples/playground/app/a0/app-list.tsx +218 -0
- package/examples/playground/app/a0/app-view.tsx +63 -0
- package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
- package/examples/playground/app/a0/apps/[appId]/route.ts +28 -0
- package/examples/playground/app/a0/apps.test.ts +128 -0
- package/examples/playground/app/a0/apps.ts +102 -0
- package/examples/playground/app/a0/chat.tsx +410 -0
- package/examples/playground/app/a0/columns.tsx +109 -0
- package/examples/playground/app/a0/context-menu.tsx +116 -0
- package/examples/playground/app/a0/cookies.ts +46 -0
- package/examples/playground/app/a0/crash-on-request.ts +72 -0
- package/examples/playground/app/a0/crashes/route.ts +3 -0
- package/examples/playground/app/a0/crashes.test.ts +33 -0
- package/examples/playground/app/a0/crashes.ts +28 -0
- package/examples/playground/app/a0/devtools.tsx +164 -0
- package/examples/playground/app/a0/events/route.ts +6 -0
- package/examples/playground/app/a0/flags.ts +30 -0
- package/examples/playground/app/a0/fx/events/route.ts +9 -0
- package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
- package/examples/playground/app/a0/layout.tsx +38 -0
- package/examples/playground/app/a0/list/route.ts +10 -0
- package/examples/playground/app/a0/model.test.ts +73 -0
- package/examples/playground/app/a0/model.ts +95 -0
- package/examples/playground/app/a0/no-photon.cjs +4 -0
- package/examples/playground/app/a0/no-quickjs.cjs +3 -0
- package/examples/playground/app/a0/page.tsx +14 -0
- package/examples/playground/app/a0/pi/events/route.ts +9 -0
- package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
- package/examples/playground/app/a0/preview-pane.tsx +145 -0
- package/examples/playground/app/a0/preview.test.ts +53 -0
- package/examples/playground/app/a0/preview.ts +26 -0
- package/examples/playground/app/a0/sandbox-log.ts +75 -0
- package/examples/playground/app/a0/sandbox-session.ts +152 -0
- package/examples/playground/app/a0/sandbox.ts +191 -0
- package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
- package/examples/playground/app/a0/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/server.ts +213 -0
- package/examples/playground/app/a0/session.ts +59 -0
- package/examples/playground/app/a0/shell.tsx +158 -0
- package/examples/playground/app/a0/short/events/route.ts +9 -0
- package/examples/playground/app/a0/splitter.tsx +120 -0
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
- package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
- package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
- package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
- package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
- package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
- package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
- package/examples/playground/app/agent/events/route.ts +5 -2
- package/examples/playground/app/agent/ingress.test.ts +101 -0
- package/examples/playground/app/agent/model-input.test.tsx +58 -0
- package/examples/playground/app/agent/model-input.tsx +115 -0
- package/examples/playground/app/agent/model.test.ts +73 -0
- package/examples/playground/app/agent/model.ts +58 -26
- package/examples/playground/app/agent/new-agent-session.tsx +37 -19
- package/examples/playground/app/agent/page.tsx +4 -4
- package/examples/playground/app/agent/server.ts +19 -15
- package/examples/playground/app/agent/session.ts +5 -6
- package/examples/playground/app/agent/telemetry.test.ts +189 -0
- package/examples/playground/app/agent/telemetry.ts +30 -0
- package/examples/playground/app/agent/trace.test.ts +23 -0
- package/examples/playground/app/agent/trace.ts +18 -0
- package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +12 -12
- package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
- package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
- package/examples/playground/app/canvas/model.ts +53 -53
- package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
- package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
- package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
- package/examples/playground/app/chat/[chatId]/session.ts +2 -19
- package/examples/playground/app/chat/agent/route.ts +9 -0
- package/examples/playground/app/chat/model.test.ts +46 -119
- package/examples/playground/app/chat/model.ts +82 -128
- package/examples/playground/app/chat/scheduler/route.ts +2 -2
- package/examples/playground/app/chat/server.test.ts +112 -0
- package/examples/playground/app/chat/server.ts +113 -117
- package/examples/playground/app/components/activity-feed.tsx +30 -1
- package/examples/playground/app/components/activity-history.test.tsx +35 -0
- package/examples/playground/app/components/demo-icons.tsx +89 -0
- package/examples/playground/app/components/demos.tsx +33 -0
- package/examples/playground/app/components/fast-link.test.tsx +58 -0
- package/examples/playground/app/components/fast-link.tsx +55 -0
- package/examples/playground/app/components/site-header.tsx +96 -0
- package/examples/playground/app/counter/counter-client.tsx +3 -3
- package/examples/playground/app/counter/model.test.ts +3 -3
- package/examples/playground/app/counter/model.ts +11 -21
- package/examples/playground/app/counter/page.tsx +2 -15
- package/examples/playground/app/counter/session.ts +2 -2
- package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
- package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
- package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
- package/examples/playground/app/documents/[documentId]/session.ts +2 -2
- package/examples/playground/app/documents/model.ts +27 -27
- package/examples/playground/app/globals.css +420 -207
- package/examples/playground/app/initial-state.test.tsx +54 -0
- package/examples/playground/app/layout.tsx +7 -23
- package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
- package/examples/playground/app/orders/[orderId]/session.ts +2 -2
- package/examples/playground/app/orders/model.ts +49 -49
- package/examples/playground/app/page.tsx +13 -102
- package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
- package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
- package/examples/playground/app/recovery/model.ts +30 -30
- package/examples/playground/instrumentation.ts +5 -0
- package/examples/playground/next.config.ts +17 -5
- package/examples/playground/package.json +13 -2
- package/examples/playground/scripts/a0-template.ts +77 -0
- package/examples/playground/vercel.json +36 -0
- package/package.json +29 -3
- package/src/actor-client.ts +16 -4
- package/src/actor-react.ts +25 -11
- package/src/actor-shared.ts +21 -6
- package/src/actor.ts +76 -31
- package/src/ai-agent-actions.ts +219 -0
- package/src/ai-agent.ts +2517 -0
- package/src/ai-app-state.ts +91 -0
- package/src/ai-client-state.ts +6 -3
- package/src/ai-client.ts +62 -0
- package/src/ai-context-schema.ts +25 -5
- package/src/ai-context.ts +90 -40
- package/src/ai-control-server.ts +92 -72
- package/src/ai-coordinator.ts +23 -22
- package/src/ai-fx.browser.ts +5 -0
- package/src/ai-fx.ts +833 -0
- package/src/ai-harness.ts +518 -0
- package/src/ai-message-base.ts +209 -0
- package/src/ai-message-projection.ts +262 -64
- package/src/ai-model-metadata.ts +11 -6
- package/src/ai-pi.browser.ts +5 -0
- package/src/ai-pi.ts +1263 -0
- package/src/ai-progress-batches.ts +26 -0
- package/src/ai-projector.ts +95 -14
- package/src/ai-react.ts +146 -0
- package/src/ai-sdk-step.ts +2 -2
- package/src/ai-server.browser.ts +2 -2
- package/src/ai-server.ts +23 -1965
- package/src/ai-stored-state.ts +48 -163
- package/src/ai.ts +339 -155
- package/src/bootstrap-stream.ts +72 -0
- package/src/bounded-cache.ts +55 -0
- package/src/client-render-session.ts +49 -0
- package/src/client.ts +688 -151
- package/src/contract.ts +92 -42
- package/src/deterministic-id.ts +37 -0
- package/src/equal-json.ts +32 -0
- package/src/errors.ts +13 -1
- package/src/index.ts +5 -1
- package/src/initial-snapshot.ts +39 -0
- package/src/internal.ts +52 -0
- package/src/libfx.d.ts +34 -0
- package/src/postgres-notification-scope.ts +14 -3
- package/src/react.ts +171 -206
- package/src/redis-http-reply.ts +256 -0
- package/src/redis-http-stream.ts +73 -0
- package/src/redis-http-subscriptions.ts +13 -5
- package/src/reducer-runtime.ts +52 -0
- package/src/reducer.ts +93 -11
- package/src/scheduler-qstash.ts +49 -20
- package/src/scheduler-task.ts +34 -2
- package/src/scheduler-vercel.ts +32 -84
- package/src/server-fetch.ts +139 -35
- package/src/server.ts +1133 -255
- package/src/session-clear.ts +23 -0
- package/src/session-socket.ts +154 -49
- package/src/sse.ts +71 -4
- package/src/store-memory.ts +252 -75
- package/src/store-polling.ts +18 -4
- package/src/store-postgres.ts +541 -114
- package/src/store-redis-core.ts +1450 -493
- package/src/store-redis-http.ts +18 -2
- package/src/store-redis-notify.ts +262 -85
- package/src/store-redis.ts +19 -9
- package/src/store-sqlite.ts +319 -45
- package/src/store.ts +79 -1
- package/src/stream-buffer.ts +43 -0
- package/src/telemetry.ts +14 -1
- package/src/wire.ts +241 -13
- package/dist/actor-ohPC-81x.d.ts.map +0 -1
- package/dist/actor-shared-B5tJfzt-.js.map +0 -1
- package/dist/ai-CrEf6p_W.d.ts.map +0 -1
- package/dist/ai-DgOBltJ_.js.map +0 -1
- package/dist/client-BYzHjkwU.js.map +0 -1
- package/dist/client-CzyacQpJ.d.ts.map +0 -1
- package/dist/contract-CKRg_E4q.js.map +0 -1
- package/dist/errors-DCk6ch5n.js.map +0 -1
- package/dist/errors-DvhSXnxk.d.ts.map +0 -1
- package/dist/internal-Dq2qYxou.js.map +0 -1
- package/dist/react.d.ts.map +0 -1
- package/dist/reducer-DEMjEY_O.js +0 -29
- package/dist/reducer-DEMjEY_O.js.map +0 -1
- package/dist/reducer-otzHjuJj.d.ts.map +0 -1
- package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
- package/dist/server-BD5ckxZb.js.map +0 -1
- package/dist/server-DbD7IJVK.d.ts.map +0 -1
- package/dist/store-DyZM6fS5.d.ts.map +0 -1
- package/dist/store-polling-CmxUbV93.js.map +0 -1
- package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
- package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
- package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
- package/dist/validate-XKT4FSNn.js +0 -30
- package/dist/validate-XKT4FSNn.js.map +0 -1
- package/dist/wire-BO5wWCb1.js.map +0 -1
- package/docs/concepts/01-contracts.mdx +0 -142
- package/docs/concepts/02-handlers.mdx +0 -284
- package/docs/concepts/03-durability.mdx +0 -314
- package/docs/concepts/04-state.mdx +0 -209
- package/docs/guides/01-timers.mdx +0 -185
- package/docs/guides/02-cancellation.mdx +0 -161
- package/docs/guides/03-react.mdx +0 -384
- package/docs/guides/04-local-first.mdx +0 -88
- package/docs/guides/05-production.mdx +0 -521
- package/docs/guides/06-ai-agents.mdx +0 -1318
- package/docs/guides/08-application-data.mdx +0 -113
- package/docs/guides/09-presence.mdx +0 -266
- package/docs/guides/10-transports.mdx +0 -221
- package/docs/reference/01-api.mdx +0 -1896
- package/docs/reference/02-errors.mdx +0 -149
- package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
- package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
- package/examples/playground/app/agent/compaction-settings.ts +0 -49
- package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
- package/examples/playground/app/agent/compaction-timeline.ts +0 -208
- /package/docs/{concepts → contract}/meta.ts +0 -0
- /package/docs/{guides → server}/meta.ts +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { i as A2Error, n as isSessionCleared } from "./session-clear-DGI4jTQ5.js";
|
|
2
|
+
import { n as foldEvent, s as validateSync } from "./reducer-BAXc8JyF.js";
|
|
3
|
+
import { r as projectClientState, t as decodeState } from "./reducer-runtime-DQwz_J59.js";
|
|
4
|
+
import { _ as nullProtoRecord, a as PRESENCE_TIMINGS, l as STREAM_TIMINGS, o as RESERVED_PARTICIPANT_IDS } from "./internal-B2T3U4Rq.js";
|
|
5
|
+
import { _ as isWirePresenceSnapshot, a as errorFromWire, c as eventFromCompactWire, f as isCompactWireEvent, g as isWirePresencePatch, h as isWireEvent, l as eventFromWire, p as isStateSnapshotFrame, v as parseSocketFrame, x as presenceSnapshotFromWire, y as presencePatchFromWire } from "./wire-rSUglYo8.js";
|
|
6
6
|
//#region src/client-errors.ts
|
|
7
7
|
var A2ClientError = class extends A2Error {
|
|
8
8
|
status;
|
|
@@ -29,6 +29,37 @@ function asClientError(error, options) {
|
|
|
29
29
|
return new A2ClientError(options?.code ?? "UNKNOWN", error instanceof Error ? error.message : "client operation failed", { cause: error });
|
|
30
30
|
}
|
|
31
31
|
//#endregion
|
|
32
|
+
//#region src/initial-snapshot.ts
|
|
33
|
+
function isInitialPromise(initial) {
|
|
34
|
+
return initial !== null && typeof initial === "object" && "then" in initial && typeof initial.then === "function";
|
|
35
|
+
}
|
|
36
|
+
function observeInitial(options) {
|
|
37
|
+
let listener = options.settled;
|
|
38
|
+
const deliver = (result) => {
|
|
39
|
+
const current = listener;
|
|
40
|
+
listener = void 0;
|
|
41
|
+
current?.(result);
|
|
42
|
+
};
|
|
43
|
+
Promise.resolve(options.promise).then((value) => deliver({ value }), (error) => deliver({ error }));
|
|
44
|
+
return () => {
|
|
45
|
+
listener = void 0;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/client-render-session.ts
|
|
50
|
+
const factories = /* @__PURE__ */ new WeakMap();
|
|
51
|
+
function registerRenderSessionFactory(options) {
|
|
52
|
+
factories.set(options.client, options);
|
|
53
|
+
}
|
|
54
|
+
function resolveInitialForReact(options) {
|
|
55
|
+
factories.get(options.client)?.resolveInitial(options);
|
|
56
|
+
}
|
|
57
|
+
function createRenderSession(options) {
|
|
58
|
+
const factory = factories.get(options.client);
|
|
59
|
+
if (!factory) throw new TypeError("React requires a client returned by createClient");
|
|
60
|
+
return factory.factory(options.sessionId, options.settings);
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
32
63
|
//#region src/client.ts
|
|
33
64
|
/**
|
|
34
65
|
* experimental-a2/client — the framework-agnostic session client.
|
|
@@ -44,6 +75,13 @@ function asClientError(error, options) {
|
|
|
44
75
|
* The client is a replica, never an access path — the routes it talks
|
|
45
76
|
* to authorize every read and write.
|
|
46
77
|
*/
|
|
78
|
+
function assertSessionSeed(options) {
|
|
79
|
+
const initial = options?.initial;
|
|
80
|
+
if (initial == null || isInitialPromise(initial)) return;
|
|
81
|
+
if (initial === null || typeof initial !== "object" || !Object.hasOwn(initial, "state") || !Object.hasOwn(initial, "index") || !("index" in initial) || typeof initial.index !== "number" || !Number.isSafeInteger(initial.index) || initial.index < 0) throw new TypeError("initial must contain state and a non-negative safe integer index");
|
|
82
|
+
const clearIndex = initial.clearIndex;
|
|
83
|
+
if (clearIndex !== void 0 && (typeof clearIndex !== "number" || !Number.isSafeInteger(clearIndex) || clearIndex < 0 || clearIndex > initial.index)) throw new TypeError("initial.clearIndex must be a non-negative safe integer no greater than its index");
|
|
84
|
+
}
|
|
47
85
|
const PUSH_ATTEMPTS = 3;
|
|
48
86
|
const RETRYABLE_PUSH_CODES = /* @__PURE__ */ new Set([
|
|
49
87
|
"STORE_UNAVAILABLE",
|
|
@@ -68,9 +106,14 @@ const httpError = async (response) => {
|
|
|
68
106
|
});
|
|
69
107
|
};
|
|
70
108
|
const httpTransport = (routes, fetchImpl, presence) => ({
|
|
71
|
-
async *connect({ sessionId, startAfter, signal }) {
|
|
109
|
+
async *connect({ sessionId, startAfter, reducer: reducerName, signal }) {
|
|
72
110
|
const streamUrl = sessionUrl(routes.stream, sessionId);
|
|
73
|
-
const res = await fetchImpl(`${streamUrl}${streamUrl.includes("?") ? "&" : "?"}
|
|
111
|
+
const res = await fetchImpl(`${streamUrl}${streamUrl.includes("?") ? "&" : "?"}${new URLSearchParams({
|
|
112
|
+
sessionId,
|
|
113
|
+
reducer: reducerName,
|
|
114
|
+
wire: "2",
|
|
115
|
+
...startAfter === void 0 ? { bootstrap: "1" } : { index: String(startAfter) }
|
|
116
|
+
})}`, {
|
|
74
117
|
headers: { accept: "text/event-stream" },
|
|
75
118
|
signal
|
|
76
119
|
});
|
|
@@ -99,10 +142,20 @@ const httpTransport = (routes, fetchImpl, presence) => ({
|
|
|
99
142
|
if (!data) continue;
|
|
100
143
|
if (eventName === void 0) {
|
|
101
144
|
const parsed = JSON.parse(data);
|
|
102
|
-
if (
|
|
145
|
+
if (Array.isArray(parsed)) {
|
|
146
|
+
if (!parsed.every(isCompactWireEvent)) throw new A2ClientError("INVALID_PAYLOAD", "invalid event batch");
|
|
147
|
+
yield {
|
|
148
|
+
kind: "events",
|
|
149
|
+
events: parsed.map((row) => eventFromCompactWire(row, sessionId))
|
|
150
|
+
};
|
|
151
|
+
} else if (isWireEvent(parsed)) yield {
|
|
103
152
|
kind: "event",
|
|
104
153
|
event: parsed
|
|
105
154
|
};
|
|
155
|
+
} else if (eventName === "a2-snapshot") {
|
|
156
|
+
const parsed = JSON.parse(data);
|
|
157
|
+
if (!isStateSnapshotFrame(parsed)) throw new A2ClientError("INVALID_PAYLOAD", "invalid bootstrap snapshot");
|
|
158
|
+
yield parsed;
|
|
106
159
|
} else if (presence && eventName === "presence-snapshot") {
|
|
107
160
|
const parsed = JSON.parse(data);
|
|
108
161
|
if (isWirePresenceSnapshot(parsed)) yield {
|
|
@@ -144,7 +197,11 @@ const httpTransport = (routes, fetchImpl, presence) => ({
|
|
|
144
197
|
if (!res.ok) throw await httpError(res);
|
|
145
198
|
const rows = await res.json();
|
|
146
199
|
if (!Array.isArray(rows) || !rows.every(isWireEvent)) throw new A2ClientError("STORE_UNAVAILABLE", "history response was not a list of events");
|
|
147
|
-
|
|
200
|
+
const clearIndex = Number(res.headers.get("a2-clear-index") ?? NaN);
|
|
201
|
+
return Number.isSafeInteger(clearIndex) && clearIndex > 0 ? {
|
|
202
|
+
events: rows,
|
|
203
|
+
clearIndex
|
|
204
|
+
} : { events: rows };
|
|
148
205
|
},
|
|
149
206
|
sendPresence(envelope) {
|
|
150
207
|
(async () => {
|
|
@@ -214,16 +271,21 @@ const sessionWsTransport = (url, webSocketImpl, presence) => {
|
|
|
214
271
|
const connections = /* @__PURE__ */ new Map();
|
|
215
272
|
const ready = socketReadyWaiters();
|
|
216
273
|
let nextReq = 1;
|
|
217
|
-
const socketUrl = (sessionId, startAfter) => {
|
|
274
|
+
const socketUrl = (sessionId, startAfter, reducerName) => {
|
|
218
275
|
const resolved = webSocketUrl(sessionUrl(url, sessionId));
|
|
219
276
|
resolved.searchParams.set("sessionId", sessionId);
|
|
220
|
-
resolved.searchParams.
|
|
277
|
+
resolved.searchParams.delete("index");
|
|
278
|
+
resolved.searchParams.delete("bootstrap");
|
|
279
|
+
resolved.searchParams.set("reducer", reducerName);
|
|
280
|
+
resolved.searchParams.set("wire", "2");
|
|
281
|
+
if (startAfter === void 0) resolved.searchParams.set("bootstrap", "1");
|
|
282
|
+
else resolved.searchParams.set("index", String(startAfter));
|
|
221
283
|
return resolved.href;
|
|
222
284
|
};
|
|
223
285
|
return {
|
|
224
|
-
async *connect({ sessionId, startAfter, signal }) {
|
|
286
|
+
async *connect({ sessionId, startAfter, reducer: reducerName, signal }) {
|
|
225
287
|
if (webSocketImpl === void 0) throw new TypeError("no WebSocket implementation available — pass one via createClient({ webSocket }) where the global is missing");
|
|
226
|
-
const socket = new webSocketImpl(socketUrl(sessionId, startAfter));
|
|
288
|
+
const socket = new webSocketImpl(socketUrl(sessionId, startAfter, reducerName));
|
|
227
289
|
const connection = {
|
|
228
290
|
socket,
|
|
229
291
|
pending: /* @__PURE__ */ new Map()
|
|
@@ -241,7 +303,10 @@ const sessionWsTransport = (url, webSocketImpl, presence) => {
|
|
|
241
303
|
if (ended) return;
|
|
242
304
|
ended = true;
|
|
243
305
|
failure = error;
|
|
244
|
-
if (connections.get(sessionId) === connection)
|
|
306
|
+
if (connections.get(sessionId) === connection) {
|
|
307
|
+
connections.delete(sessionId);
|
|
308
|
+
ready.notify(sessionId);
|
|
309
|
+
}
|
|
245
310
|
rejectPendingAcks(connection.pending);
|
|
246
311
|
notify();
|
|
247
312
|
};
|
|
@@ -254,9 +319,18 @@ const sessionWsTransport = (url, webSocketImpl, presence) => {
|
|
|
254
319
|
socket.addEventListener("message", (event) => {
|
|
255
320
|
if (connections.get(sessionId) !== connection || ended) return;
|
|
256
321
|
if (typeof event.data !== "string") return;
|
|
257
|
-
|
|
322
|
+
let frame;
|
|
323
|
+
try {
|
|
324
|
+
frame = parseSocketFrame(event.data);
|
|
325
|
+
} catch (error) {
|
|
326
|
+
finish(asClientError(error));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
258
329
|
if (frame === null) return;
|
|
259
330
|
switch (frame.kind) {
|
|
331
|
+
case "snapshot":
|
|
332
|
+
queue.push(frame);
|
|
333
|
+
break;
|
|
260
334
|
case "ack": {
|
|
261
335
|
const waiter = connection.pending.get(frame.req);
|
|
262
336
|
if (waiter === void 0) return;
|
|
@@ -276,6 +350,13 @@ const sessionWsTransport = (url, webSocketImpl, presence) => {
|
|
|
276
350
|
event: frame.event
|
|
277
351
|
});
|
|
278
352
|
break;
|
|
353
|
+
case "events":
|
|
354
|
+
if (frame.sessionId !== void 0) return;
|
|
355
|
+
queue.push({
|
|
356
|
+
kind: "events",
|
|
357
|
+
events: frame.events.map((row) => eventFromCompactWire(row, sessionId))
|
|
358
|
+
});
|
|
359
|
+
break;
|
|
279
360
|
case "presence":
|
|
280
361
|
if (presence && frame.sessionId === void 0) queue.push({
|
|
281
362
|
kind: "presence",
|
|
@@ -378,6 +459,14 @@ const wakeChannel = (channel) => {
|
|
|
378
459
|
channel.wake?.();
|
|
379
460
|
channel.wake = null;
|
|
380
461
|
};
|
|
462
|
+
const answersAttempt = (frame, channel) => frame.attempt === void 0 || frame.attempt === channel.attempt;
|
|
463
|
+
const subscriptionStart = (index, reducer) => index === void 0 ? {
|
|
464
|
+
bootstrap: true,
|
|
465
|
+
reducer
|
|
466
|
+
} : {
|
|
467
|
+
index,
|
|
468
|
+
reducer
|
|
469
|
+
};
|
|
381
470
|
const subscribeFrame = (sessions) => {
|
|
382
471
|
return JSON.stringify({
|
|
383
472
|
kind: "subscribe",
|
|
@@ -394,6 +483,7 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
394
483
|
let socketOpen = false;
|
|
395
484
|
let watchdog;
|
|
396
485
|
let nextReq = 1;
|
|
486
|
+
let nextAttempt = 1;
|
|
397
487
|
const trySend = (data) => {
|
|
398
488
|
if (socket === null || !socketOpen) return;
|
|
399
489
|
try {
|
|
@@ -429,6 +519,7 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
429
519
|
const detach = (channel) => {
|
|
430
520
|
if (channels.get(channel.sessionId) !== channel) return;
|
|
431
521
|
channels.delete(channel.sessionId);
|
|
522
|
+
ready.notify(channel.sessionId);
|
|
432
523
|
if (!channel.dropped) {
|
|
433
524
|
const frame = {
|
|
434
525
|
kind: "unsubscribe",
|
|
@@ -456,12 +547,17 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
456
547
|
};
|
|
457
548
|
const route = (frame) => {
|
|
458
549
|
switch (frame.kind) {
|
|
550
|
+
case "snapshot": {
|
|
551
|
+
const channel = channels.get(frame.sessionId);
|
|
552
|
+
if (channel && channel.live && !channel.ended) deliver(channel, frame);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
459
555
|
case "ping":
|
|
460
556
|
for (const channel of channels.values()) if (channel.live && !channel.ended) deliver(channel, { kind: "ping" });
|
|
461
557
|
return;
|
|
462
558
|
case "subscribed": {
|
|
463
559
|
const channel = channels.get(frame.sessionId);
|
|
464
|
-
if (channel === void 0 || channel.ended || channel.live) return;
|
|
560
|
+
if (channel === void 0 || channel.ended || channel.live || !answersAttempt(frame, channel)) return;
|
|
465
561
|
channel.live = true;
|
|
466
562
|
deliver(channel, { kind: "ping" });
|
|
467
563
|
ready.notify(frame.sessionId);
|
|
@@ -469,7 +565,7 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
469
565
|
}
|
|
470
566
|
case "unsubscribed": {
|
|
471
567
|
const channel = channels.get(frame.sessionId);
|
|
472
|
-
if (channel === void 0) return;
|
|
568
|
+
if (channel === void 0 || !answersAttempt(frame, channel)) return;
|
|
473
569
|
channel.dropped = true;
|
|
474
570
|
settleChannel(channel, frame.error ? asClientError(frame.error) : frame.reason === void 0 ? null : new A2ClientError("CONNECTION_FAILED", frame.reason));
|
|
475
571
|
return;
|
|
@@ -490,6 +586,16 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
490
586
|
});
|
|
491
587
|
return;
|
|
492
588
|
}
|
|
589
|
+
case "events": {
|
|
590
|
+
const { sessionId } = frame;
|
|
591
|
+
if (sessionId === void 0) return;
|
|
592
|
+
const channel = channels.get(sessionId);
|
|
593
|
+
if (channel !== void 0 && channel.live && !channel.ended) deliver(channel, {
|
|
594
|
+
kind: "events",
|
|
595
|
+
events: frame.events.map((row) => eventFromCompactWire(row, sessionId))
|
|
596
|
+
});
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
493
599
|
case "presence": {
|
|
494
600
|
if (!presence || frame.sessionId === void 0) return;
|
|
495
601
|
const channel = channels.get(frame.sessionId);
|
|
@@ -513,7 +619,9 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
513
619
|
const ensureSocket = () => {
|
|
514
620
|
if (socket !== null) return;
|
|
515
621
|
if (webSocketImpl === void 0) throw new TypeError("no WebSocket implementation available — pass one via createClient({ webSocket }) where the global is missing");
|
|
516
|
-
const
|
|
622
|
+
const target = webSocketUrl(url);
|
|
623
|
+
target.searchParams.set("wire", "2");
|
|
624
|
+
const created = new webSocketImpl(target.href);
|
|
517
625
|
socket = created;
|
|
518
626
|
socketOpen = false;
|
|
519
627
|
armWatchdog(created);
|
|
@@ -523,7 +631,8 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
523
631
|
armWatchdog(created);
|
|
524
632
|
const sessions = [...channels.values()].filter((channel) => !channel.ended).map((channel) => ({
|
|
525
633
|
id: channel.sessionId,
|
|
526
|
-
|
|
634
|
+
attempt: channel.attempt,
|
|
635
|
+
...subscriptionStart(channel.startAfter, channel.reducer)
|
|
527
636
|
}));
|
|
528
637
|
if (sessions.length > 0) trySend(subscribeFrame(sessions));
|
|
529
638
|
});
|
|
@@ -531,7 +640,13 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
531
640
|
if (socket !== created) return;
|
|
532
641
|
if (typeof event.data !== "string") return;
|
|
533
642
|
armWatchdog(created);
|
|
534
|
-
|
|
643
|
+
let frame;
|
|
644
|
+
try {
|
|
645
|
+
frame = parseSocketFrame(event.data);
|
|
646
|
+
} catch (error) {
|
|
647
|
+
failSocket(asClientError(error));
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
535
650
|
if (frame === null) return;
|
|
536
651
|
route(frame);
|
|
537
652
|
});
|
|
@@ -548,10 +663,12 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
548
663
|
});
|
|
549
664
|
};
|
|
550
665
|
return {
|
|
551
|
-
async *connect({ sessionId, startAfter, signal }) {
|
|
666
|
+
async *connect({ sessionId, startAfter, reducer: reducerName, signal }) {
|
|
552
667
|
const channel = {
|
|
553
668
|
sessionId,
|
|
669
|
+
attempt: nextAttempt++,
|
|
554
670
|
startAfter,
|
|
671
|
+
reducer: reducerName,
|
|
555
672
|
queue: [],
|
|
556
673
|
live: false,
|
|
557
674
|
dropped: false,
|
|
@@ -564,7 +681,8 @@ const multiplexedWsTransport = (url, webSocketImpl, presence) => {
|
|
|
564
681
|
ensureSocket();
|
|
565
682
|
if (socketOpen) trySend(subscribeFrame([{
|
|
566
683
|
id: sessionId,
|
|
567
|
-
|
|
684
|
+
attempt: channel.attempt,
|
|
685
|
+
...subscriptionStart(startAfter, reducerName)
|
|
568
686
|
}]));
|
|
569
687
|
} catch (error) {
|
|
570
688
|
if (channels.get(sessionId) === channel) channels.delete(sessionId);
|
|
@@ -660,8 +778,10 @@ const transportFor = (api, fetchImpl, webSocketImpl, presence) => {
|
|
|
660
778
|
return sessionWsTransport(api.url, webSocketImpl, presence);
|
|
661
779
|
};
|
|
662
780
|
function createClient(options) {
|
|
663
|
-
const {
|
|
664
|
-
const
|
|
781
|
+
const { contract, api } = options;
|
|
782
|
+
const reducer = contract?.reducer;
|
|
783
|
+
if (!reducer) throw new TypeError("createClient requires a contract with a public reducer");
|
|
784
|
+
const presenceDefs = contract.presence;
|
|
665
785
|
const declaresPresence = Object.keys(presenceDefs).length > 0;
|
|
666
786
|
const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
667
787
|
const clientFetch = async (input, init) => {
|
|
@@ -684,6 +804,8 @@ function createClient(options) {
|
|
|
684
804
|
entry.timer = setTimeout(() => {
|
|
685
805
|
if (sessions.get(sessionId) !== entry) return;
|
|
686
806
|
if (entry.runtime.canEvict()) {
|
|
807
|
+
entry.runtime.close();
|
|
808
|
+
clearTimeout(entry.timer);
|
|
687
809
|
sessions.delete(sessionId);
|
|
688
810
|
return;
|
|
689
811
|
}
|
|
@@ -695,10 +817,21 @@ function createClient(options) {
|
|
|
695
817
|
scheduleGc(sessionId);
|
|
696
818
|
};
|
|
697
819
|
const makeSession = (sessionId, sessionOptions) => {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
820
|
+
assertSessionSeed(sessionOptions);
|
|
821
|
+
const supplied = sessionOptions?.initial;
|
|
822
|
+
const initial = isInitialPromise(supplied) ? void 0 : supplied;
|
|
823
|
+
let hydrated = initial != null;
|
|
824
|
+
let bootstrapGeneration;
|
|
825
|
+
let stateIndex = initial?.index ?? 0;
|
|
826
|
+
let streamIndex = initial?.index;
|
|
827
|
+
let foldedState = initial == null ? reducer.initialState : initial.state;
|
|
828
|
+
/** The latest clear marker this session has learned of, or 0. */
|
|
829
|
+
let clearIndex = initial?.clearIndex ?? 0;
|
|
830
|
+
let initialVersion = 0;
|
|
831
|
+
const seenInitials = /* @__PURE__ */ new WeakSet();
|
|
832
|
+
if (initial != null) seenInitials.add(initial);
|
|
833
|
+
let preload;
|
|
834
|
+
const serverEvents = (sessionOptions?.initialEvents ?? []).filter((event) => event.index <= stateIndex && event.index > clearIndex).toSorted((a, b) => a.index - b.index);
|
|
702
835
|
let pending = [];
|
|
703
836
|
let participant = sessionOptions?.participant ?? options.participant;
|
|
704
837
|
let presenceState = nullProtoRecord();
|
|
@@ -720,6 +853,7 @@ function createClient(options) {
|
|
|
720
853
|
let inFlightPushes = 0;
|
|
721
854
|
let pushWireTail = Promise.resolve();
|
|
722
855
|
let deferredHydration;
|
|
856
|
+
let resumeInitialError;
|
|
723
857
|
let hydrationNotification;
|
|
724
858
|
const emit = () => {
|
|
725
859
|
for (const listener of [...listeners]) listener();
|
|
@@ -728,10 +862,16 @@ function createClient(options) {
|
|
|
728
862
|
snapshot = null;
|
|
729
863
|
emit();
|
|
730
864
|
};
|
|
731
|
-
const notifyHydrated = () => {
|
|
865
|
+
const notifyHydrated = (resume) => {
|
|
866
|
+
resumeInitialError ??= resume;
|
|
732
867
|
snapshot = null;
|
|
733
868
|
clearTimeout(hydrationNotification);
|
|
734
|
-
hydrationNotification = setTimeout(
|
|
869
|
+
hydrationNotification = setTimeout(() => {
|
|
870
|
+
const failure = resumeInitialError;
|
|
871
|
+
resumeInitialError = void 0;
|
|
872
|
+
if (failure && active && status === "paused" && lastError === failure) reconnectStream();
|
|
873
|
+
else emit();
|
|
874
|
+
}, 0);
|
|
735
875
|
hydrationNotification.unref?.();
|
|
736
876
|
};
|
|
737
877
|
const mergeServerEvents = (events, throughIndex) => {
|
|
@@ -739,7 +879,7 @@ function createClient(options) {
|
|
|
739
879
|
const byIndex = new Map(serverEvents.map((event) => [event.index, event]));
|
|
740
880
|
let changed = false;
|
|
741
881
|
for (const event of events) {
|
|
742
|
-
if (event.index > throughIndex) continue;
|
|
882
|
+
if (event.index > throughIndex || event.index <= clearIndex) continue;
|
|
743
883
|
if (byIndex.get(event.index)?.id === event.id) continue;
|
|
744
884
|
byIndex.set(event.index, event);
|
|
745
885
|
changed = true;
|
|
@@ -748,10 +888,25 @@ function createClient(options) {
|
|
|
748
888
|
serverEvents.splice(0, serverEvents.length, ...[...byIndex.values()].toSorted((a, b) => a.index - b.index));
|
|
749
889
|
return true;
|
|
750
890
|
};
|
|
891
|
+
/**
|
|
892
|
+
* Raise the latest known clear marker (a2-implementation.md §17.4):
|
|
893
|
+
* drop the served history it ended and every push acked below it.
|
|
894
|
+
* The boundary never moves backward.
|
|
895
|
+
*/
|
|
896
|
+
const applyClearBoundary = (index) => {
|
|
897
|
+
if (index <= clearIndex) return false;
|
|
898
|
+
clearIndex = index;
|
|
899
|
+
const kept = serverEvents.filter((event) => event.index > index);
|
|
900
|
+
serverEvents.splice(0, serverEvents.length, ...kept);
|
|
901
|
+
pending = pending.filter((entry) => entry.acked === void 0 || entry.acked.index > index);
|
|
902
|
+
return true;
|
|
903
|
+
};
|
|
751
904
|
const overlayEvents = () => {
|
|
752
905
|
const acked = pending.filter((p) => p.acked).map((p) => p.acked).toSorted((a, b) => a.index - b.index);
|
|
753
906
|
const maxKnown = Math.max(stateIndex, acked.at(-1)?.index ?? stateIndex);
|
|
754
|
-
const
|
|
907
|
+
const waiting = pending.filter((p) => !p.acked);
|
|
908
|
+
const held = waiting.findIndex((p) => p.heldUntilAck);
|
|
909
|
+
const unacked = (held === -1 ? waiting : waiting.slice(0, held)).map((p, i) => ({
|
|
755
910
|
id: p.id,
|
|
756
911
|
type: p.type,
|
|
757
912
|
payload: p.payload,
|
|
@@ -781,14 +936,14 @@ function createClient(options) {
|
|
|
781
936
|
const oldestLoaded = serverEvents[0]?.index ?? null;
|
|
782
937
|
return {
|
|
783
938
|
loading: historyLoads > 0,
|
|
784
|
-
complete: oldestLoaded === 1 || stateIndex ===
|
|
939
|
+
complete: hydrated && (oldestLoaded === clearIndex + 1 || stateIndex === clearIndex),
|
|
785
940
|
oldestLoaded
|
|
786
941
|
};
|
|
787
942
|
};
|
|
788
943
|
const buildSnapshot = () => {
|
|
789
944
|
const overlay = overlayEvents();
|
|
790
945
|
let state = foldedState;
|
|
791
|
-
for (const event of overlay) if (event.index > stateIndex) state = reducer.fold(state, event);
|
|
946
|
+
for (const event of hydrated ? overlay : []) if (event.index > stateIndex) state = reducer.fold(state, event);
|
|
792
947
|
const displayedOverlay = overlay.filter((event) => {
|
|
793
948
|
if (event.index > stateIndex) return true;
|
|
794
949
|
let low = 0;
|
|
@@ -803,7 +958,11 @@ function createClient(options) {
|
|
|
803
958
|
return true;
|
|
804
959
|
});
|
|
805
960
|
return {
|
|
806
|
-
state:
|
|
961
|
+
state: projectClientState({
|
|
962
|
+
reducer,
|
|
963
|
+
state
|
|
964
|
+
}),
|
|
965
|
+
hydrated,
|
|
807
966
|
events: [...serverEvents, ...displayedOverlay],
|
|
808
967
|
index: stateIndex,
|
|
809
968
|
history: historyState(),
|
|
@@ -939,30 +1098,65 @@ function createClient(options) {
|
|
|
939
1098
|
queuePresenceSend(validated, at);
|
|
940
1099
|
touch(sessionId);
|
|
941
1100
|
};
|
|
942
|
-
/** Pushes awaiting stream confirmation — resolved
|
|
943
|
-
*
|
|
1101
|
+
/** Pushes awaiting stream confirmation — resolved the moment the
|
|
1102
|
+
* stream, by event or bootstrap snapshot, passes their batch. */
|
|
944
1103
|
let confirmWatchers = [];
|
|
1104
|
+
const resolveConfirmed = () => {
|
|
1105
|
+
const delivered = streamIndex ?? 0;
|
|
1106
|
+
const due = confirmWatchers.filter((watcher) => watcher.index <= delivered);
|
|
1107
|
+
if (due.length === 0) return;
|
|
1108
|
+
confirmWatchers = confirmWatchers.filter((watcher) => watcher.index > delivered);
|
|
1109
|
+
for (const watcher of due) watcher.resolve();
|
|
1110
|
+
};
|
|
1111
|
+
const retireCoveredPending = () => {
|
|
1112
|
+
pending = pending.filter((entry) => !entry.acked || entry.acked.index > Math.min(stateIndex, streamIndex ?? stateIndex));
|
|
1113
|
+
};
|
|
1114
|
+
const adoptBaseline = (baseline) => {
|
|
1115
|
+
hydrated = true;
|
|
1116
|
+
stateIndex = baseline.index;
|
|
1117
|
+
foldedState = baseline.state;
|
|
1118
|
+
for (const entry of pending) if (entry.sent && !entry.acked) entry.heldUntilAck = true;
|
|
1119
|
+
retireCoveredPending();
|
|
1120
|
+
};
|
|
1121
|
+
const bootstrapState = (frame) => {
|
|
1122
|
+
let state;
|
|
1123
|
+
try {
|
|
1124
|
+
state = decodeState({
|
|
1125
|
+
reducer,
|
|
1126
|
+
state: frame.stateUndefined ? void 0 : frame.state
|
|
1127
|
+
});
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
throw asClientError(error, { code: "INVALID_PAYLOAD" });
|
|
1130
|
+
}
|
|
1131
|
+
if (!reducer.stateSchema) return state;
|
|
1132
|
+
const result = validateSync(reducer.stateSchema, state, "bootstrap stateSchema");
|
|
1133
|
+
if (result.issues) throw new A2ClientError("INVALID_PAYLOAD", "bootstrap state failed stateSchema", { details: result.issues });
|
|
1134
|
+
return result.value;
|
|
1135
|
+
};
|
|
945
1136
|
/** The single ingest point: every server-confirmed event, in log
|
|
946
1137
|
* order, from the stream. */
|
|
947
|
-
const ingest = (
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1138
|
+
const ingest = (events) => {
|
|
1139
|
+
let delivered = false;
|
|
1140
|
+
for (const event of events) {
|
|
1141
|
+
if (event.index <= (streamIndex ?? 0)) continue;
|
|
1142
|
+
delivered = true;
|
|
1143
|
+
streamIndex = event.index;
|
|
1144
|
+
const cleared = isSessionCleared(event);
|
|
1145
|
+
if (event.index > stateIndex) {
|
|
1146
|
+
stateIndex = event.index;
|
|
1147
|
+
if (!cleared) serverEvents.push(event);
|
|
1148
|
+
foldedState = foldEvent(reducer, foldedState, event);
|
|
1149
|
+
} else if (!cleared) mergeServerEvents([event], stateIndex);
|
|
1150
|
+
if (cleared) applyClearBoundary(event.index);
|
|
1151
|
+
pending = pending.filter((p) => p.id !== event.id);
|
|
960
1152
|
}
|
|
1153
|
+
if (!delivered) return;
|
|
1154
|
+
resolveConfirmed();
|
|
961
1155
|
notify();
|
|
962
1156
|
touch(sessionId);
|
|
963
1157
|
};
|
|
964
1158
|
const validated = (events) => events.map((event) => {
|
|
965
|
-
const schema = Object.hasOwn(
|
|
1159
|
+
const schema = Object.hasOwn(contract.events, event.type) ? contract.events[event.type] : void 0;
|
|
966
1160
|
if (!schema) throw new A2ClientError("UNKNOWN_EVENT_TYPE", `no event type '${String(event.type)}' in the reducer's vocabulary`);
|
|
967
1161
|
const result = validateSync(schema, event.payload, `event '${String(event.type)}'`);
|
|
968
1162
|
if (result.issues) throw new A2ClientError("INVALID_PAYLOAD", `invalid payload for event '${String(event.type)}'`, { details: result.issues });
|
|
@@ -998,7 +1192,7 @@ function createClient(options) {
|
|
|
998
1192
|
return Object.defineProperty(ack, "confirmed", { get() {
|
|
999
1193
|
confirmed ??= ack.then((acked) => new Promise((resolve) => {
|
|
1000
1194
|
const last = acked.at(-1)?.index ?? 0;
|
|
1001
|
-
if (
|
|
1195
|
+
if ((streamIndex ?? 0) >= last) {
|
|
1002
1196
|
resolve(acked);
|
|
1003
1197
|
return;
|
|
1004
1198
|
}
|
|
@@ -1010,6 +1204,20 @@ function createClient(options) {
|
|
|
1010
1204
|
return confirmed;
|
|
1011
1205
|
} });
|
|
1012
1206
|
};
|
|
1207
|
+
const initialSettled = (epoch) => new Promise((resolve, reject) => {
|
|
1208
|
+
const check = () => {
|
|
1209
|
+
const failure = leaseEpoch !== epoch || status === "closed" ? new DOMException("session closed", "AbortError") : !hydrated && preload?.error;
|
|
1210
|
+
if (failure) {
|
|
1211
|
+
listeners.delete(check);
|
|
1212
|
+
reject(failure);
|
|
1213
|
+
} else if (hydrated || !preload) {
|
|
1214
|
+
listeners.delete(check);
|
|
1215
|
+
resolve();
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
listeners.add(check);
|
|
1219
|
+
check();
|
|
1220
|
+
});
|
|
1013
1221
|
const push = (...events) => {
|
|
1014
1222
|
touch(sessionId);
|
|
1015
1223
|
return withConfirmed((async () => {
|
|
@@ -1027,10 +1235,19 @@ function createClient(options) {
|
|
|
1027
1235
|
});
|
|
1028
1236
|
pending.push(...entries);
|
|
1029
1237
|
inFlightPushes += 1;
|
|
1238
|
+
let acknowledgedIndex;
|
|
1239
|
+
const pushEpoch = leaseEpoch;
|
|
1240
|
+
const releaseConnection = !hydrated || transport.awaitReady || active ? runtime.connect() : void 0;
|
|
1030
1241
|
try {
|
|
1031
1242
|
refreshIdleStream();
|
|
1032
1243
|
notify();
|
|
1033
1244
|
await waitForPriorPush;
|
|
1245
|
+
if (transport.awaitReady) {
|
|
1246
|
+
await initialSettled(pushEpoch);
|
|
1247
|
+
await transport.awaitReady(sessionId, STREAM_TIMINGS.stallTimeoutMs);
|
|
1248
|
+
}
|
|
1249
|
+
if (leaseEpoch !== pushEpoch) throw new DOMException("session closed", "AbortError");
|
|
1250
|
+
for (const entry of entries) entry.sent = true;
|
|
1034
1251
|
const acked = await post({
|
|
1035
1252
|
sessionId,
|
|
1036
1253
|
events: entries.map(({ id, type, payload }) => ({
|
|
@@ -1039,11 +1256,12 @@ function createClient(options) {
|
|
|
1039
1256
|
payload
|
|
1040
1257
|
}))
|
|
1041
1258
|
});
|
|
1259
|
+
acknowledgedIndex = acked.at(-1)?.index;
|
|
1042
1260
|
for (const event of acked) {
|
|
1043
1261
|
const entry = pending.find((p) => p.id === event.id);
|
|
1044
1262
|
if (entry) entry.acked = event;
|
|
1045
1263
|
}
|
|
1046
|
-
|
|
1264
|
+
retireCoveredPending();
|
|
1047
1265
|
if ((acked.at(-1)?.index ?? 0) > (streamIndex ?? stateIndex)) refreshIdleStream();
|
|
1048
1266
|
notify();
|
|
1049
1267
|
return acked;
|
|
@@ -1053,12 +1271,20 @@ function createClient(options) {
|
|
|
1053
1271
|
notify();
|
|
1054
1272
|
throw err;
|
|
1055
1273
|
} finally {
|
|
1274
|
+
if (releaseConnection && pushEpoch === leaseEpoch && acknowledgedIndex !== void 0 && acknowledgedIndex > (streamIndex ?? 0)) confirmWatchers.push({
|
|
1275
|
+
index: acknowledgedIndex,
|
|
1276
|
+
resolve: releaseConnection,
|
|
1277
|
+
lease: true
|
|
1278
|
+
});
|
|
1279
|
+
else releaseConnection?.();
|
|
1056
1280
|
releasePush();
|
|
1057
1281
|
inFlightPushes -= 1;
|
|
1058
1282
|
if (inFlightPushes === 0 && deferredHydration) {
|
|
1059
1283
|
const next = deferredHydration;
|
|
1060
1284
|
deferredHydration = void 0;
|
|
1061
|
-
|
|
1285
|
+
if (next.version === void 0 || next.version === initialVersion) {
|
|
1286
|
+
if (applyInitial(next)) notifyHydrated();
|
|
1287
|
+
}
|
|
1062
1288
|
}
|
|
1063
1289
|
touch(sessionId);
|
|
1064
1290
|
}
|
|
@@ -1095,18 +1321,19 @@ function createClient(options) {
|
|
|
1095
1321
|
const run = historyTail.then(async () => {
|
|
1096
1322
|
const upper = before ?? serverEvents[0]?.index ?? stateIndex + 1;
|
|
1097
1323
|
const lte = Math.min(upper - 1, stateIndex);
|
|
1098
|
-
const gte = Math.max(1, upper - (limit ?? DEFAULT_HISTORY_LIMIT));
|
|
1324
|
+
const gte = Math.max(clearIndex + 1, upper - (limit ?? DEFAULT_HISTORY_LIMIT));
|
|
1099
1325
|
if (gte > lte) return [];
|
|
1100
1326
|
const missing = missingBetween(gte, lte);
|
|
1101
1327
|
if (missing === null) return eventsBetween(gte, lte);
|
|
1102
1328
|
historyLoads += 1;
|
|
1103
1329
|
notify();
|
|
1104
1330
|
try {
|
|
1105
|
-
const
|
|
1331
|
+
const page = await readHistory({
|
|
1106
1332
|
sessionId,
|
|
1107
1333
|
...missing
|
|
1108
1334
|
});
|
|
1109
|
-
|
|
1335
|
+
if (page.clearIndex !== void 0) applyClearBoundary(page.clearIndex);
|
|
1336
|
+
mergeServerEvents(page.events.map((row) => eventFromWire(row)), stateIndex);
|
|
1110
1337
|
return eventsBetween(gte, lte);
|
|
1111
1338
|
} catch (error) {
|
|
1112
1339
|
throw asClientError(error);
|
|
@@ -1158,14 +1385,46 @@ function createClient(options) {
|
|
|
1158
1385
|
};
|
|
1159
1386
|
let wasLive = false;
|
|
1160
1387
|
try {
|
|
1388
|
+
while (!hydrated && preload && !preload.error) await waitForInitial(controller.signal);
|
|
1389
|
+
if (generation !== run || controller.signal.aborted) break;
|
|
1390
|
+
if (!hydrated && preload?.error) {
|
|
1391
|
+
lastError = preload.error;
|
|
1392
|
+
status = "paused";
|
|
1393
|
+
notify();
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
if (hydrated && streamIndex === void 0) streamIndex = stateIndex;
|
|
1397
|
+
resolveConfirmed();
|
|
1398
|
+
if (generation !== run) break;
|
|
1399
|
+
bootstrapGeneration = streamIndex === void 0 ? run : void 0;
|
|
1161
1400
|
const frames = transport.connect({
|
|
1162
1401
|
sessionId,
|
|
1163
|
-
startAfter: streamIndex
|
|
1402
|
+
startAfter: streamIndex,
|
|
1403
|
+
reducer: reducer.name,
|
|
1164
1404
|
signal: controller.signal
|
|
1165
1405
|
});
|
|
1166
1406
|
for await (const frame of frames) {
|
|
1167
1407
|
if (generation !== run) break;
|
|
1168
1408
|
armStall();
|
|
1409
|
+
if (frame.kind === "snapshot") {
|
|
1410
|
+
if (frame.sessionId !== sessionId || frame.name !== reducer.name || !isStateSnapshotFrame(frame)) throw new A2ClientError("INVALID_PAYLOAD", "bootstrap snapshot belongs to another session or reducer");
|
|
1411
|
+
if (bootstrapGeneration !== run) throw new A2ClientError("INVALID_PAYLOAD", "unexpected bootstrap snapshot");
|
|
1412
|
+
const baseline = !hydrated || frame.index >= stateIndex ? {
|
|
1413
|
+
state: bootstrapState(frame),
|
|
1414
|
+
index: frame.index
|
|
1415
|
+
} : void 0;
|
|
1416
|
+
streamIndex = frame.index;
|
|
1417
|
+
bootstrapGeneration = void 0;
|
|
1418
|
+
applyClearBoundary(frame.clearIndex ?? 0);
|
|
1419
|
+
if (baseline) adoptBaseline(baseline);
|
|
1420
|
+
resolveConfirmed();
|
|
1421
|
+
notify();
|
|
1422
|
+
if (generation !== run) break;
|
|
1423
|
+
}
|
|
1424
|
+
if (!hydrated || streamIndex === void 0) {
|
|
1425
|
+
if (frame.kind === "event" || frame.kind === "events") throw new A2ClientError("INVALID_PAYLOAD", "received an event before the bootstrap snapshot");
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1169
1428
|
if (!wasLive) {
|
|
1170
1429
|
backoff = STREAM_TIMINGS.reconnectBaseMs;
|
|
1171
1430
|
attempt = 0;
|
|
@@ -1182,7 +1441,10 @@ function createClient(options) {
|
|
|
1182
1441
|
}
|
|
1183
1442
|
if (frame.kind === "event") {
|
|
1184
1443
|
streamIdle = false;
|
|
1185
|
-
ingest(eventFromWire(frame.event));
|
|
1444
|
+
ingest([eventFromWire(frame.event)]);
|
|
1445
|
+
} else if (frame.kind === "events") {
|
|
1446
|
+
streamIdle = false;
|
|
1447
|
+
ingest(frame.events);
|
|
1186
1448
|
} else if (frame.kind === "activity") {
|
|
1187
1449
|
streamIdle = frame.idle;
|
|
1188
1450
|
if (streamIdle && (inFlightPushes > 0 || pending.some((entry) => (entry.acked?.index ?? 0) > (streamIndex ?? stateIndex)))) refreshIdleStream();
|
|
@@ -1196,6 +1458,7 @@ function createClient(options) {
|
|
|
1196
1458
|
timeoutMs: STREAM_TIMINGS.stallTimeoutMs
|
|
1197
1459
|
}) : asClientError(err);
|
|
1198
1460
|
} finally {
|
|
1461
|
+
if (bootstrapGeneration === run) bootstrapGeneration = void 0;
|
|
1199
1462
|
clearTimeout(stall);
|
|
1200
1463
|
}
|
|
1201
1464
|
if (generation !== run) break;
|
|
@@ -1262,6 +1525,71 @@ function createClient(options) {
|
|
|
1262
1525
|
if (active && generation === run) runStream(run);
|
|
1263
1526
|
previous?.abort();
|
|
1264
1527
|
};
|
|
1528
|
+
const applyInitial = ({ settings: next, version }) => {
|
|
1529
|
+
const nextIndex = next.initial?.index ?? 0;
|
|
1530
|
+
if (hydrated && nextIndex > stateIndex && inFlightPushes > 0) {
|
|
1531
|
+
if (!deferredHydration || nextIndex > (deferredHydration.settings.initial?.index ?? 0)) deferredHydration = {
|
|
1532
|
+
settings: next,
|
|
1533
|
+
...version === void 0 ? {} : { version }
|
|
1534
|
+
};
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
const boundaryChanged = applyClearBoundary(next.initial?.clearIndex ?? 0);
|
|
1538
|
+
const historyChanged = mergeServerEvents(next.initialEvents, Math.min(nextIndex, stateIndex)) || boundaryChanged;
|
|
1539
|
+
if (next.initial != null && bootstrapGeneration === void 0) streamIndex ??= nextIndex;
|
|
1540
|
+
if (next.initial == null || hydrated && nextIndex <= stateIndex) return historyChanged;
|
|
1541
|
+
adoptBaseline(next.initial);
|
|
1542
|
+
mergeServerEvents(next.initialEvents, stateIndex);
|
|
1543
|
+
return true;
|
|
1544
|
+
};
|
|
1545
|
+
const invalidateInitial = () => {
|
|
1546
|
+
initialVersion++;
|
|
1547
|
+
preload?.cancel();
|
|
1548
|
+
preload = void 0;
|
|
1549
|
+
if (deferredHydration?.version !== void 0) deferredHydration = void 0;
|
|
1550
|
+
};
|
|
1551
|
+
const settleInitial = (result) => {
|
|
1552
|
+
const entry = preload;
|
|
1553
|
+
if (!entry || entry.source !== result.source || entry.version !== initialVersion) return;
|
|
1554
|
+
entry.cancel();
|
|
1555
|
+
const cold = !hydrated;
|
|
1556
|
+
let changed = false;
|
|
1557
|
+
if ("error" in result) {
|
|
1558
|
+
if (cold) entry.error = asClientError(result.error);
|
|
1559
|
+
else preload = void 0;
|
|
1560
|
+
} else try {
|
|
1561
|
+
assertSessionSeed({ initial: result.value });
|
|
1562
|
+
if (isInitialPromise(result.value)) throw new TypeError("an initial Promise must resolve to a snapshot");
|
|
1563
|
+
preload = void 0;
|
|
1564
|
+
changed = applyInitial({
|
|
1565
|
+
settings: {
|
|
1566
|
+
...entry.settings,
|
|
1567
|
+
initial: result.value
|
|
1568
|
+
},
|
|
1569
|
+
version: entry.version
|
|
1570
|
+
});
|
|
1571
|
+
} catch (error) {
|
|
1572
|
+
if (cold) {
|
|
1573
|
+
preload = entry;
|
|
1574
|
+
entry.error = asClientError(error, { code: "INVALID_PAYLOAD" });
|
|
1575
|
+
} else preload = void 0;
|
|
1576
|
+
}
|
|
1577
|
+
if (cold || changed) notifyHydrated();
|
|
1578
|
+
};
|
|
1579
|
+
const waitForInitial = (signal) => new Promise((resolve, reject) => {
|
|
1580
|
+
let settled = false;
|
|
1581
|
+
const finish = () => {
|
|
1582
|
+
if (settled || !signal.aborted && !hydrated && preload && !preload.error) return;
|
|
1583
|
+
settled = true;
|
|
1584
|
+
listeners.delete(finish);
|
|
1585
|
+
signal.removeEventListener("abort", finish);
|
|
1586
|
+
if (signal.aborted) reject(signal.reason);
|
|
1587
|
+
else resolve();
|
|
1588
|
+
};
|
|
1589
|
+
listeners.add(finish);
|
|
1590
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
1591
|
+
finish();
|
|
1592
|
+
});
|
|
1265
1593
|
const runtime = {
|
|
1266
1594
|
sessionId,
|
|
1267
1595
|
...declaresPresence ? { setPresence } : {},
|
|
@@ -1302,43 +1630,71 @@ function createClient(options) {
|
|
|
1302
1630
|
};
|
|
1303
1631
|
},
|
|
1304
1632
|
close() {
|
|
1633
|
+
invalidateInitial();
|
|
1634
|
+
resumeInitialError = void 0;
|
|
1305
1635
|
leases = 0;
|
|
1306
1636
|
leaseEpoch += 1;
|
|
1637
|
+
confirmWatchers = confirmWatchers.filter((watcher) => !watcher.lease);
|
|
1307
1638
|
stopStream();
|
|
1308
1639
|
},
|
|
1640
|
+
resolveInitial: settleInitial,
|
|
1309
1641
|
hydrate(next) {
|
|
1642
|
+
assertSessionSeed(next);
|
|
1310
1643
|
touch(sessionId);
|
|
1311
1644
|
if (!next) return;
|
|
1312
1645
|
participant ??= next.participant;
|
|
1313
|
-
const
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1646
|
+
const input = next.initial;
|
|
1647
|
+
const resume = !hydrated && status === "paused" && preload?.error === lastError ? preload.error : void 0;
|
|
1648
|
+
if (isInitialPromise(input)) {
|
|
1649
|
+
if (seenInitials.has(input)) {
|
|
1650
|
+
if (preload?.source === input && next.initialEvents !== void 0) preload.settings.initialEvents = next.initialEvents;
|
|
1651
|
+
if (mergeServerEvents(next.initialEvents, stateIndex)) notifyHydrated();
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
invalidateInitial();
|
|
1655
|
+
seenInitials.add(input);
|
|
1656
|
+
const { initial: _initial, ...settings } = next;
|
|
1657
|
+
const entry = {
|
|
1658
|
+
source: input,
|
|
1659
|
+
version: initialVersion,
|
|
1660
|
+
settings,
|
|
1661
|
+
cancel() {}
|
|
1662
|
+
};
|
|
1663
|
+
preload = entry;
|
|
1664
|
+
entry.cancel = observeInitial({
|
|
1665
|
+
promise: input,
|
|
1666
|
+
settled: (result) => settleInitial({
|
|
1667
|
+
source: input,
|
|
1668
|
+
...result
|
|
1669
|
+
})
|
|
1670
|
+
});
|
|
1671
|
+
if (resume) notifyHydrated(resume);
|
|
1322
1672
|
return;
|
|
1323
1673
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1674
|
+
if (input != null) {
|
|
1675
|
+
const repeated = seenInitials.has(input);
|
|
1676
|
+
seenInitials.add(input);
|
|
1677
|
+
if (repeated || hydrated && input.index <= stateIndex) {
|
|
1678
|
+
if (deferredHydration?.settings.initial === input && next.initialEvents !== void 0) deferredHydration.settings.initialEvents = next.initialEvents;
|
|
1679
|
+
const boundaryChanged = applyClearBoundary(input.clearIndex ?? 0);
|
|
1680
|
+
if (mergeServerEvents(next.initialEvents, Math.min(input.index, stateIndex)) || boundaryChanged) notifyHydrated();
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1332
1683
|
}
|
|
1333
|
-
|
|
1684
|
+
if (input !== void 0) invalidateInitial();
|
|
1685
|
+
if (applyInitial({ settings: {
|
|
1686
|
+
...next,
|
|
1687
|
+
initial: input
|
|
1688
|
+
} }) || resume || input === null) notifyHydrated(resume);
|
|
1334
1689
|
},
|
|
1335
1690
|
canEvict() {
|
|
1336
1691
|
return listeners.size === 0 && !active && inFlightPushes === 0 && historyLoads === 0 && confirmWatchers.length === 0 && presenceTimer === void 0;
|
|
1337
1692
|
}
|
|
1338
1693
|
};
|
|
1694
|
+
if (isInitialPromise(supplied)) runtime.hydrate(sessionOptions);
|
|
1339
1695
|
return runtime;
|
|
1340
1696
|
};
|
|
1341
|
-
|
|
1697
|
+
const client = { session(sessionId, sessionOptions) {
|
|
1342
1698
|
const existing = sessions.get(sessionId);
|
|
1343
1699
|
if (existing) {
|
|
1344
1700
|
existing.runtime.hydrate(sessionOptions);
|
|
@@ -1350,8 +1706,19 @@ function createClient(options) {
|
|
|
1350
1706
|
touch(sessionId);
|
|
1351
1707
|
return runtime;
|
|
1352
1708
|
} };
|
|
1709
|
+
registerRenderSessionFactory({
|
|
1710
|
+
client,
|
|
1711
|
+
factory: (sessionId, settings) => makeSession(sessionId, settings),
|
|
1712
|
+
resolveInitial: ({ session, source, value }) => {
|
|
1713
|
+
session.resolveInitial({
|
|
1714
|
+
source,
|
|
1715
|
+
value
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
return client;
|
|
1353
1720
|
}
|
|
1354
1721
|
//#endregion
|
|
1355
|
-
export { A2ClientError as n, createClient as t };
|
|
1722
|
+
export { A2ClientError as a, isInitialPromise as i, createRenderSession as n, resolveInitialForReact as r, createClient as t };
|
|
1356
1723
|
|
|
1357
|
-
//# sourceMappingURL=client-
|
|
1724
|
+
//# sourceMappingURL=client-BReKqwZC.js.map
|