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
package/docs/index.mdx
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Introduction
|
|
3
|
-
description: A2
|
|
3
|
+
description: A2 syncs your immutable event log, from server to browser.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
7
|
pnpm add experimental-a2
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Handlers react to them, and usually return the next one. The browser follows
|
|
14
|
-
the same log and folds the same state as the server. That's the whole model;
|
|
15
|
-
the rest of this page is it happening.
|
|
16
|
-
|
|
17
|
-
Events record facts, reducers compute the current view, and handlers perform
|
|
18
|
-
the reactions.
|
|
10
|
+
```bash
|
|
11
|
+
npx skills add vercel-labs/a2
|
|
12
|
+
```
|
|
19
13
|
|
|
20
|
-
## Define a contract
|
|
14
|
+
## Define a shared contract
|
|
21
15
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
off it later.
|
|
16
|
+
Name the events once. The server implements the contract, and the browser
|
|
17
|
+
types itself off it.
|
|
25
18
|
|
|
26
19
|
```ts contracts.ts
|
|
27
20
|
import { z } from 'zod'
|
|
@@ -30,152 +23,143 @@ import * as a2 from 'experimental-a2'
|
|
|
30
23
|
export const orders = a2.contract({
|
|
31
24
|
name: 'orders',
|
|
32
25
|
events: {
|
|
33
|
-
created: z.object({
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
'order.created': z.object({
|
|
27
|
+
shopId: z.string(),
|
|
28
|
+
items: z.array(z.string()),
|
|
29
|
+
}),
|
|
30
|
+
'shop.notified': z.object({ shopId: z.string() }),
|
|
31
|
+
'courier.assigned': z.object({ courierId: z.string() }),
|
|
32
|
+
'order.delivered': z.object({ signedBy: z.string() }),
|
|
33
|
+
'order.cancelled': z.object({ reason: z.string() }),
|
|
34
|
+
},
|
|
35
|
+
reducer: {
|
|
36
|
+
name: 'order-status',
|
|
37
|
+
initialState: { status: 'new' },
|
|
38
|
+
fold: (state, event) => ({ status: event.type }),
|
|
36
39
|
},
|
|
37
40
|
})
|
|
38
41
|
```
|
|
39
42
|
|
|
40
|
-
##
|
|
43
|
+
## Implement the server
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
Bind the contract to a store, a scheduler, and the handlers that react to its
|
|
46
|
+
events.
|
|
44
47
|
|
|
45
48
|
```ts server/orders.ts
|
|
46
49
|
import { createServer } from 'experimental-a2/server'
|
|
50
|
+
import { vercelQueues } from 'experimental-a2/scheduler-vercel'
|
|
51
|
+
import { redis } from 'experimental-a2/store-redis'
|
|
47
52
|
import { orders } from '@/contracts'
|
|
48
53
|
|
|
49
54
|
export const ordersServer = createServer({
|
|
50
55
|
contract: orders,
|
|
56
|
+
store: redis({ url: process.env.REDIS_URL }),
|
|
57
|
+
scheduler: vercelQueues(),
|
|
51
58
|
handlers: {
|
|
52
|
-
created: async ({ event }) => {
|
|
59
|
+
'order.created': async ({ event }) => {
|
|
53
60
|
// your side effect; event.id makes a stable idempotency key:
|
|
54
61
|
// await notifyShop(event.payload, { idempotencyKey: event.id })
|
|
55
|
-
|
|
62
|
+
const { shopId } = event.payload
|
|
63
|
+
return { type: 'shop.notified', payload: { shopId } }
|
|
56
64
|
},
|
|
57
65
|
},
|
|
58
66
|
})
|
|
59
67
|
```
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
A session is one instance of the contract, with its own ordered log. It is the
|
|
64
|
-
thing A2 keeps durable and live. The id is yours:
|
|
65
|
-
|
|
66
|
-
- `session('order-8194')`: an order moving through fulfillment
|
|
67
|
-
- `session('chat-a8c1')`: an agent chat streaming its reply
|
|
68
|
-
- `session('cart-julian')`: a shopping cart
|
|
69
|
-
- `session('doc-roadmap')`: a document with three people typing in it
|
|
70
|
-
|
|
71
|
-
Anything whose story spans more than one request. Put events in the same
|
|
72
|
-
session when they form one history or view. Handlers are concurrent unless
|
|
73
|
-
they share a lane. Any server code can open
|
|
74
|
-
one and append: a route handler, a webhook, a cron job.
|
|
69
|
+
One route serves every session to the browser:
|
|
75
70
|
|
|
76
71
|
```ts app/api/orders/route.ts
|
|
77
72
|
import { ordersServer } from '@/server/orders'
|
|
78
73
|
|
|
79
|
-
export
|
|
80
|
-
|
|
81
|
-
await ordersServer.session(body.orderId).append({
|
|
82
|
-
type: 'created',
|
|
83
|
-
payload: { items: body.items },
|
|
84
|
-
})
|
|
85
|
-
return Response.json({ ok: true })
|
|
86
|
-
}
|
|
74
|
+
export const GET = ordersServer.fetch
|
|
75
|
+
export const POST = ordersServer.fetch
|
|
87
76
|
```
|
|
88
77
|
|
|
89
|
-
|
|
90
|
-
`append` returns; the return confirms durability, not handler completion.
|
|
91
|
-
A queue-backed scheduler wakes pending work after a crash or timeout. The same
|
|
92
|
-
scheduler lets a session append a typed event later with
|
|
93
|
-
`session.schedule(name, { delay: '30s' }, event)`.
|
|
94
|
-
|
|
95
|
-
## State is a fold
|
|
78
|
+
## Implement the client
|
|
96
79
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
80
|
+
The browser follows a session over SSE or a
|
|
81
|
+
[WebSocket](/client/transports) and folds the same state. `push` applies
|
|
82
|
+
instantly and rolls back if the server says no.
|
|
100
83
|
|
|
101
|
-
```
|
|
84
|
+
```tsx app/orders/[orderId]/order.tsx
|
|
85
|
+
'use client'
|
|
86
|
+
import { createClient } from 'experimental-a2/client'
|
|
87
|
+
import { useSession } from 'experimental-a2/react'
|
|
102
88
|
import { orders } from '@/contracts'
|
|
103
89
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return { status: 'created' }
|
|
110
|
-
case 'shop.notified':
|
|
111
|
-
return { status: 'notified' }
|
|
112
|
-
case 'cancelled':
|
|
113
|
-
return { status: 'cancelled' }
|
|
114
|
-
default:
|
|
115
|
-
return state
|
|
116
|
-
}
|
|
117
|
-
})
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
```ts
|
|
121
|
-
// anywhere on the server:
|
|
122
|
-
import { ordersServer } from '@/server/orders'
|
|
123
|
-
import { orderStatus } from '@/reducer'
|
|
90
|
+
const ordersClient = createClient({
|
|
91
|
+
contract: orders,
|
|
92
|
+
// SSE; for a WebSocket: { type: 'ws', url: '/api/orders' }
|
|
93
|
+
api: '/api/orders',
|
|
94
|
+
})
|
|
124
95
|
|
|
125
|
-
|
|
126
|
-
|
|
96
|
+
export function Order({ orderId }: { orderId: string }) {
|
|
97
|
+
const { state, push } = useSession(ordersClient, orderId)
|
|
98
|
+
return (
|
|
99
|
+
<button
|
|
100
|
+
onClick={() =>
|
|
101
|
+
void push({ type: 'order.cancelled', payload: { reason: 'customer' } })
|
|
102
|
+
}
|
|
103
|
+
>
|
|
104
|
+
{state.status} · cancel
|
|
105
|
+
</button>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
127
108
|
```
|
|
128
109
|
|
|
129
|
-
##
|
|
130
|
-
|
|
131
|
-
The reducer is isomorphic, so the browser runs the same fold the server
|
|
132
|
-
does. A session streams to the client live and takes optimistic writes,
|
|
133
|
-
all typed by the one contract:
|
|
110
|
+
## It's just an event log
|
|
134
111
|
|
|
135
|
-
```ts
|
|
136
|
-
|
|
137
|
-
import {
|
|
138
|
-
import { orderStatus } from '@/reducer'
|
|
112
|
+
```ts
|
|
113
|
+
// anywhere on the server: a route, a webhook, a cron job
|
|
114
|
+
import { ordersServer } from '@/server/orders'
|
|
139
115
|
|
|
140
|
-
|
|
141
|
-
|
|
116
|
+
const order = ordersServer.session('order-8194')
|
|
117
|
+
await order.append({
|
|
118
|
+
type: 'courier.assigned',
|
|
119
|
+
payload: { courierId: 'courier-7' },
|
|
142
120
|
})
|
|
121
|
+
const events = await order.history()
|
|
122
|
+
// order.created, shop.notified, courier.assigned
|
|
143
123
|
```
|
|
144
124
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
125
|
+
A session is the ordered list of what happened. State is a fold over it, on
|
|
126
|
+
both sides of the wire. Everything else A2 keeps is a cache it can rebuild
|
|
127
|
+
from the log.
|
|
148
128
|
|
|
149
|
-
|
|
150
|
-
const { state, connection, push } = useSession()
|
|
151
|
-
return (
|
|
152
|
-
<div>
|
|
153
|
-
{state.status}
|
|
154
|
-
{connection.status === 'live' ? ' · live' : ''}
|
|
155
|
-
<button onClick={() => void push({ type: 'cancelled', payload: {} })}>
|
|
156
|
-
Cancel order
|
|
157
|
-
</button>
|
|
158
|
-
</div>
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
```
|
|
129
|
+
## With strong guarantees
|
|
162
130
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
131
|
+
- Appends are effectively-once.
|
|
132
|
+
- The log stays ordered.
|
|
133
|
+
- Handlers are at-least-once.
|
|
134
|
+
- Returned events commit with the handler's completion.
|
|
135
|
+
- Handlers run concurrently, unless they share a lane.
|
|
136
|
+
- Failures stay contained.
|
|
137
|
+
- A failing handler never makes `append` throw.
|
|
167
138
|
|
|
168
|
-
|
|
139
|
+
[Guarantees](/server/guarantees) has the details and the limits.
|
|
169
140
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
141
|
+
## Go further...
|
|
142
|
+
|
|
143
|
+
<CardGroup cols={2}>
|
|
144
|
+
<Card title="Handlers" href="/server/handlers" icon="zap">
|
|
145
|
+
Retries, lanes, and idempotency.
|
|
146
|
+
</Card>
|
|
147
|
+
<Card title="State" href="/contract/state" icon="layers">
|
|
148
|
+
Reducers and the current view.
|
|
149
|
+
</Card>
|
|
150
|
+
<Card title="History" href="/contract/state#history" icon="history">
|
|
151
|
+
The raw log, oldest first.
|
|
152
|
+
</Card>
|
|
153
|
+
<Card title="AI" href="/agents/introduction" icon="bot">
|
|
154
|
+
Agents with tools and approvals.
|
|
155
|
+
</Card>
|
|
156
|
+
<Card title="Timers" href="/server/timers" icon="clock">
|
|
157
|
+
Schedule an event for later.
|
|
158
|
+
</Card>
|
|
159
|
+
<Card title="Production" href="/server/production" icon="rocket">
|
|
160
|
+
A store and a scheduler.
|
|
161
|
+
</Card>
|
|
162
|
+
</CardGroup>
|
|
179
163
|
|
|
180
164
|
## FAQ
|
|
181
165
|
|
|
@@ -187,7 +171,7 @@ a durable event log. Events settle in their append transaction, with no drain,
|
|
|
187
171
|
claim, or queue message. A contract can also mix handled and history-only event
|
|
188
172
|
types. A2 decides per event type.
|
|
189
173
|
|
|
190
|
-
See [Events without handlers](/
|
|
174
|
+
See [Events without handlers](/server/handlers#events-without-handlers).
|
|
191
175
|
|
|
192
176
|
</details>
|
|
193
177
|
|
|
@@ -197,7 +181,7 @@ See [Events without handlers](/concepts/handlers#events-without-handlers).
|
|
|
197
181
|
Configure Vercel Queues or QStash, then call
|
|
198
182
|
`session.schedule(name, { delay: '30s' }, event)` or use `{ at: Date }`. The
|
|
199
183
|
same scheduler route handles recovery and the delayed append. Names and omitted
|
|
200
|
-
event ids stay stable across retries. See [Timers](/
|
|
184
|
+
event ids stay stable across retries. See [Timers](/server/timers).
|
|
201
185
|
|
|
202
186
|
</details>
|
|
203
187
|
|
|
@@ -206,7 +190,7 @@ event ids stay stable across retries. See [Timers](/guides/timers).
|
|
|
206
190
|
|
|
207
191
|
Cancellation is an event too: same route, same auth, same log. Handlers
|
|
208
192
|
opt in with `abortOn` and receive a live `AbortSignal`; reducers cut the
|
|
209
|
-
derived view exactly. See [Cancellation](/
|
|
193
|
+
derived view exactly. See [Cancellation](/server/cancellation).
|
|
210
194
|
|
|
211
195
|
</details>
|
|
212
196
|
|
|
@@ -214,8 +198,9 @@ derived view exactly. See [Cancellation](/guides/cancellation).
|
|
|
214
198
|
<summary>What happens when a handler keeps failing?</summary>
|
|
215
199
|
|
|
216
200
|
It retries with backoff, then dead-letters after ten caught failures. Later
|
|
217
|
-
events in the same lane wait; unlaned events and
|
|
218
|
-
|
|
201
|
+
events in the same lane wait until you retry or skip it; unlaned events and
|
|
202
|
+
other lanes continue.
|
|
203
|
+
[Guarantees](/server/guarantees) has the exact guarantees and the
|
|
219
204
|
recovery story behind them.
|
|
220
205
|
|
|
221
206
|
</details>
|
|
@@ -227,7 +212,7 @@ A durable store backend, such as Postgres or Redis. Add a queue-backed scheduler
|
|
|
227
212
|
when the contract has handlers or calls `session.schedule`. A history-only
|
|
228
213
|
contract with no timers needs no reaction infrastructure. Development needs
|
|
229
214
|
neither: SQLite appears under `.a2/` and state survives restarts. See
|
|
230
|
-
[
|
|
215
|
+
[Production](/server/production).
|
|
231
216
|
|
|
232
217
|
</details>
|
|
233
218
|
|
|
@@ -243,10 +228,10 @@ does not replicate database tables or subscribe to queries across sessions.
|
|
|
243
228
|
<details>
|
|
244
229
|
<summary>Where does my application database fit?</summary>
|
|
245
230
|
|
|
246
|
-
A2 does not replace it.
|
|
231
|
+
A2 does not replace it. Keep collections,
|
|
247
232
|
relationships, constraints, search, and reporting in your application
|
|
248
233
|
database. Split ownership instead of writing the same fact through both paths.
|
|
249
|
-
See [
|
|
234
|
+
See [Sessions and your database](/contract/sessions#sessions-and-your-database).
|
|
250
235
|
|
|
251
236
|
</details>
|
|
252
237
|
|
|
@@ -256,35 +241,6 @@ See [A2 and your database](/guides/application-data).
|
|
|
256
241
|
Yes. Every installation includes a standalone Next.js playground at
|
|
257
242
|
`node_modules/experimental-a2/examples/playground`. It contains complete
|
|
258
243
|
contracts, reducers, servers, route handlers, clients, and schedulers using only
|
|
259
|
-
public package entry points. See [Complete examples](/
|
|
244
|
+
public package entry points. See [Complete examples](/examples).
|
|
260
245
|
|
|
261
246
|
</details>
|
|
262
|
-
|
|
263
|
-
## Where to next
|
|
264
|
-
|
|
265
|
-
<CardGroup cols={2}>
|
|
266
|
-
<Card title="Quickstart" href="/quickstart" icon="rocket">
|
|
267
|
-
A contract, a handler, and a durable append in five minutes.
|
|
268
|
-
</Card>
|
|
269
|
-
<Card title="Contracts and sessions" href="/concepts/contracts" icon="folder">
|
|
270
|
-
The two nouns you'll use everywhere.
|
|
271
|
-
</Card>
|
|
272
|
-
<Card title="Durability" href="/concepts/durability" icon="wrench">
|
|
273
|
-
What A2 guarantees, and exactly how.
|
|
274
|
-
</Card>
|
|
275
|
-
<Card title="Live UI with React" href="/guides/react" icon="lightbulb">
|
|
276
|
-
Stream a session to the browser with optimistic writes.
|
|
277
|
-
</Card>
|
|
278
|
-
<Card title="Durable AI agents" href="/guides/ai-agents" icon="bot">
|
|
279
|
-
Run AI SDK agents with durable messages, progress, tools, and approvals.
|
|
280
|
-
</Card>
|
|
281
|
-
<Card title="A2 and your database" href="/guides/application-data" icon="database">
|
|
282
|
-
Decide what belongs in a session and what belongs in ordinary tables.
|
|
283
|
-
</Card>
|
|
284
|
-
<Card title="Actors" href="/actors/introduction" icon="boxes">
|
|
285
|
-
Durable objects on the log: typed state, one message at a time.
|
|
286
|
-
</Card>
|
|
287
|
-
<Card title="Complete examples" href="/guides/examples" icon="code">
|
|
288
|
-
Read or copy the standalone Next.js playground included with the package.
|
|
289
|
-
</Card>
|
|
290
|
-
</CardGroup>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Core
|
|
3
|
+
description: "Every entry point, and the isomorphic `experimental-a2` core: contracts, reducers, IDs, and the event shape."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Entry points
|
|
7
|
+
|
|
8
|
+
| Entry point | Ships | Peer dependency |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| `experimental-a2` | `contract`, `A2Error`: isomorphic core | none |
|
|
11
|
+
| `experimental-a2/server` | `createServer`: implement a contract | none |
|
|
12
|
+
| `experimental-a2/client` | `createClient`: framework-agnostic session client | none |
|
|
13
|
+
| `experimental-a2/react` | `createReact` | `react` |
|
|
14
|
+
| `experimental-a2/ai` | `agentContract`, `handlerContext`, `inputs`, and the AI reducer: isomorphic | `ai` |
|
|
15
|
+
| `experimental-a2/ai/server` | `agent`: an agent's loop as an A2 server | `ai` |
|
|
16
|
+
| `experimental-a2/ai/fx` | `fx`: the FX harness for `agent()` | `ai`, `libfx` |
|
|
17
|
+
| `experimental-a2/ai/pi` | `pi`: the Pi harness for `agent()` | `ai`, `@earendil-works/pi-coding-agent`, `@earendil-works/pi-ai` |
|
|
18
|
+
| `experimental-a2/ai/client` | `createAgentClient`: contract-free agent client | `ai` |
|
|
19
|
+
| `experimental-a2/ai/react` | `useAgent` | `ai`, `react` |
|
|
20
|
+
| [`experimental-a2/store-postgres`](/reference/adapters#experimental-a2store-postgres) | `postgres`: Postgres store backend | `pg` (or inject a client) |
|
|
21
|
+
| [`experimental-a2/store-redis`](/reference/adapters#experimental-a2store-redis) | `redis`: Redis Streams store backend, push-native streaming | `ioredis` (or inject a client) |
|
|
22
|
+
| [`experimental-a2/store-redis-http`](/reference/adapters#experimental-a2store-redis-http) | `redisHttp`: the same Redis store over provider REST APIs (Upstash) | none |
|
|
23
|
+
| `experimental-a2/store-sqlite` | SQLite store backend | none |
|
|
24
|
+
| `experimental-a2/store-memory` | in-memory store backend | none |
|
|
25
|
+
| `experimental-a2/scheduler-vercel` | `vercelQueues`: recovery and timers | `@vercel/queue` |
|
|
26
|
+
| `experimental-a2/scheduler-qstash` | `qstash`: signed recovery and timers | `@upstash/qstash` |
|
|
27
|
+
| `experimental-a2/cache-indexeddb` | `indexedDb` browser cache | none |
|
|
28
|
+
| `experimental-a2/otel` | `otel` telemetry adapter | `@opentelemetry/api` |
|
|
29
|
+
|
|
30
|
+
`experimental-a2` imports none of the backends, and a browser-bundle test in CI
|
|
31
|
+
enforces it. Importing a backend's entry point, such as
|
|
32
|
+
`experimental-a2/store-postgres`, is what pulls that backend in; core never puts
|
|
33
|
+
one in your client bundle.
|
|
34
|
+
|
|
35
|
+
## `experimental-a2`
|
|
36
|
+
|
|
37
|
+
### `a2.contract(options)`
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
a2.contract(options: {
|
|
41
|
+
name: string
|
|
42
|
+
events: Record<string, StandardSchemaV1>
|
|
43
|
+
presence?: Record<string, StandardSchemaV1>
|
|
44
|
+
reducer?: { name: string; initialState: S; stateSchema?: StandardSchemaV1; fold: (state: S, event: Event) => S }
|
|
45
|
+
}): Contract
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Defines one kind of lifecycle. The result is a plain, importable,
|
|
49
|
+
**isomorphic** value: the server implements it, reducers derive from it, and
|
|
50
|
+
the browser types its pushes off it. See [Events](/contract/events).
|
|
51
|
+
|
|
52
|
+
| Option | Meaning |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `name` | Unique per app; identifies the contract in storage and queue messages. May not contain the control character U+001F. |
|
|
55
|
+
| `events` | Event type to payload schema. Types may not start with `a2.`, which A2 reserves for its own events, such as `a2.cleared`. |
|
|
56
|
+
| `presence` | Optional. Field to schema for one participant's ephemeral state. Replicated last-writer-wins, never stored in the log, invisible to reducers. See [Presence](/client/presence). |
|
|
57
|
+
| `reducer` | Optional. The one reducer the contract publishes to clients; an inline definition includes `fold`. See [`reducer(options)`](#reduceroptions). |
|
|
58
|
+
|
|
59
|
+
Schemas are [Standard Schema](https://standardschema.dev): Zod, Valibot,
|
|
60
|
+
ArkType, anything that implements it. Validators must be synchronous; async
|
|
61
|
+
ones are rejected at definition time. The validated output is what's stored.
|
|
62
|
+
|
|
63
|
+
### `contract.batch(...events)`
|
|
64
|
+
|
|
65
|
+
Returns the same events as a readonly tuple, preserving each literal type in a
|
|
66
|
+
heterogeneous handler-returned array. A2 validates them when the handler
|
|
67
|
+
completes. See [Handlers](/server/handlers#return-what-happens-after-success).
|
|
68
|
+
|
|
69
|
+
### `reducer(options)`
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
reducer(options: {
|
|
73
|
+
contract: Contract
|
|
74
|
+
name: string // identity; keys cached snapshots
|
|
75
|
+
initialState: S // the seed (schema input, when stateSchema is set)
|
|
76
|
+
stateSchema?: StandardSchemaV1 // declares S, checks initialState, guards cached folds
|
|
77
|
+
}): { fold(fn: (state: S, event: Event) => S): Reducer<S> }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Builds a standalone reducer for server reads or for the contract's `reducer`
|
|
81
|
+
field. See [State](/contract/state#reducers).
|
|
82
|
+
|
|
83
|
+
- The first call fixes the state and event types, so `.fold()` receives
|
|
84
|
+
concrete ones. Literal unions in your state survive with zero annotations.
|
|
85
|
+
- `name` keys cached snapshots, so two reducers never fight over a cache
|
|
86
|
+
entry. Rename it when the fold's logic changes: a rename (or a `stateSchema`
|
|
87
|
+
mismatch, when present) invalidates, and the next read refolds from raw
|
|
88
|
+
events.
|
|
89
|
+
|
|
90
|
+
### `A2Error`
|
|
91
|
+
|
|
92
|
+
The shared base for A2 failures, discriminated by `code`. The session client
|
|
93
|
+
exposes its `A2ClientError` subclass. See [Errors](/reference/errors).
|
|
94
|
+
|
|
95
|
+
### `idempotentId(...parts)`
|
|
96
|
+
|
|
97
|
+
`idempotentId(...parts: string[]): Promise<string>` derives a stable UUIDv8
|
|
98
|
+
from an ordered tuple of strings. It performs no I/O and runs on the server and
|
|
99
|
+
in the browser.
|
|
100
|
+
|
|
101
|
+
```ts shared/request-id.ts
|
|
102
|
+
import { idempotentId } from 'experimental-a2'
|
|
103
|
+
|
|
104
|
+
export const id = await idempotentId('orders', 'shop-7', 'request-42')
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- Pass the result as an event's `id` when retries should converge on the same
|
|
108
|
+
event.
|
|
109
|
+
- The same tuple produces the same ID across runtimes and A2 versions.
|
|
110
|
+
- String boundaries, order, and case matter; strings are not normalized. Empty
|
|
111
|
+
strings and an empty tuple are valid.
|
|
112
|
+
- Every argument has the same role; a prefix such as `'orders'` is a namespace
|
|
113
|
+
by convention. Include enough context to distinguish operations across
|
|
114
|
+
contracts and sessions: event IDs are globally unique within a store.
|
|
115
|
+
- A2 preserves explicit IDs and does not change its automatic ID generation.
|
|
116
|
+
Changing an existing recipe changes retry identity, so retries of already
|
|
117
|
+
committed requests must keep their original IDs.
|
|
118
|
+
|
|
119
|
+
### `SessionCleared`
|
|
120
|
+
|
|
121
|
+
An `Event` whose `type` is `'a2.cleared'` and whose `payload` is `null`: the
|
|
122
|
+
marker a [clear](/reference/server#sessionclearoptions) leaves in
|
|
123
|
+
`session.stream()`. It never reaches handlers or `history()`. Comparing
|
|
124
|
+
`event.type` with that string narrows a stream item to `SessionCleared`. The
|
|
125
|
+
session client handles the marker itself, so it matters only when you read the
|
|
126
|
+
server stream yourself.
|
|
127
|
+
|
|
128
|
+
## Event
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
type Event = {
|
|
132
|
+
id: string
|
|
133
|
+
type: string
|
|
134
|
+
payload: unknown // typed by the contract's schema for `type`
|
|
135
|
+
index: number // position in the session's log, from 1
|
|
136
|
+
sessionId: string
|
|
137
|
+
createdAt: Date
|
|
138
|
+
}
|
|
139
|
+
```
|