experimental-a2 0.19.0 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +1 -1
- package/dist/{actor-C7b-1arn.d.ts → actor-HnGmrOZZ.d.ts} +5 -5
- package/dist/{actor-C7b-1arn.d.ts.map → actor-HnGmrOZZ.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +2 -2
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +5 -5
- package/dist/{actor-shared-t7w8xIKP.js → actor-shared-DFET5q16.js} +3 -3
- package/dist/{actor-shared-t7w8xIKP.js.map → actor-shared-DFET5q16.js.map} +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +54 -43
- package/dist/actor.js.map +1 -1
- package/dist/{ai-Cz3hqVhk.d.ts → ai-CvEnqVfo.d.ts} +8 -8
- package/dist/{ai-Cz3hqVhk.d.ts.map → ai-CvEnqVfo.d.ts.map} +1 -1
- package/dist/{ai-mMiWul1T.js → ai-CzE672Q3.js} +9 -9
- package/dist/ai-CzE672Q3.js.map +1 -0
- package/dist/ai-client.d.ts +2 -2
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.d.ts +1 -1
- package/dist/ai-fx.d.ts.map +1 -1
- package/dist/ai-fx.js +36 -9
- package/dist/ai-fx.js.map +1 -1
- package/dist/{ai-harness-CV6rXbB3.d.ts → ai-harness-Bfk-Lycn.d.ts} +2 -2
- package/dist/ai-harness-Bfk-Lycn.d.ts.map +1 -0
- package/dist/{ai-harness-BuuC-gJ3.js → ai-harness-dQeJaR1t.js} +17 -4
- package/dist/ai-harness-dQeJaR1t.js.map +1 -0
- package/dist/ai-pi.d.ts +1 -1
- package/dist/ai-pi.js +4 -4
- package/dist/ai-pi.js.map +1 -1
- package/dist/ai-react.d.ts +5 -5
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +14 -17
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +84 -75
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/{client-BhHLoNKF.js → client-9IeTsiAs.js} +5 -5
- package/dist/{client-BhHLoNKF.js.map → client-9IeTsiAs.js.map} +1 -1
- package/dist/{client-h_DKFAjj.d.ts → client-CUMbS_38.d.ts} +3 -3
- package/dist/{client-h_DKFAjj.d.ts.map → client-CUMbS_38.d.ts.map} +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/{contract-BYKASlCV.js → contract-CWukt01d.js} +2 -2
- package/dist/contract-CWukt01d.js.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/{initial-snapshot-Bx-c0pDP.d.ts → initial-snapshot-D3Lve43o.d.ts} +4 -4
- package/dist/initial-snapshot-D3Lve43o.d.ts.map +1 -0
- package/dist/{internal-CscSY7K_.js → internal-Bc4PvfHk.js} +10 -3
- package/dist/internal-Bc4PvfHk.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/otel.js +1 -1
- package/dist/otel.js.map +1 -1
- package/dist/{react-BBXgHWFc.d.ts → react-BoHvC3v9.d.ts} +4 -4
- package/dist/{react-BBXgHWFc.d.ts.map → react-BoHvC3v9.d.ts.map} +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{reducer-CqA59Q6U.d.ts → reducer-CYPHsSmd.d.ts} +2 -2
- package/dist/{reducer-CqA59Q6U.d.ts.map → reducer-CYPHsSmd.d.ts.map} +1 -1
- package/dist/{reducer-BAXc8JyF.js → reducer-CjmvZSAb.js} +2 -2
- package/dist/{reducer-BAXc8JyF.js.map → reducer-CjmvZSAb.js.map} +1 -1
- package/dist/runtime-consumer-BWmht60z.js +12 -0
- package/dist/runtime-consumer-BWmht60z.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.d.ts.map +1 -1
- package/dist/scheduler-qstash.js +7 -7
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/scheduler-task-X1SToDjP.js.map +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.d.ts.map +1 -1
- package/dist/scheduler-vercel.js +5 -4
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-DYAATFuT.d.ts → server-BshFZ0eL.d.ts} +108 -41
- package/dist/server-BshFZ0eL.d.ts.map +1 -0
- package/dist/{server-D-Y4pqwu.js → server-DvIB8kj5.js} +423 -135
- package/dist/server-DvIB8kj5.js.map +1 -0
- package/dist/server.browser.js +1 -1
- package/dist/server.browser.js.map +1 -1
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/{session-clear-C-QwTXV1.d.ts → session-clear-Bk_29eEw.d.ts} +2 -2
- package/dist/session-clear-Bk_29eEw.d.ts.map +1 -0
- package/dist/{session-clear-DGI4jTQ5.js → session-clear-wHd5GCpZ.js} +5 -4
- package/dist/session-clear-wHd5GCpZ.js.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-Z2qjw50n.d.ts +523 -0
- package/dist/store-Z2qjw50n.d.ts.map +1 -0
- package/dist/store-codec-DwrWlgQx.js +65 -0
- package/dist/store-codec-DwrWlgQx.js.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +393 -210
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-7WJV0YWP.js → store-polling-DYNyxCev.js} +2 -2
- package/dist/{store-polling-7WJV0YWP.js.map → store-polling-DYNyxCev.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +1595 -673
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-B1ixeqIS.js → store-presence-polling-CzQ0wdtM.js} +3 -3
- package/dist/{store-presence-polling-B1ixeqIS.js.map → store-presence-polling-CzQ0wdtM.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +4 -4
- package/dist/store-redis-notify-IiVdlYHc.js +2919 -0
- package/dist/store-redis-notify-IiVdlYHc.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +2 -2
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +517 -287
- package/dist/store-sqlite.js.map +1 -1
- package/dist/{telemetry-Cyfqo6Qt.d.ts → telemetry-CWwsh-Gx.d.ts} +3 -3
- package/dist/telemetry-CWwsh-Gx.d.ts.map +1 -0
- package/dist/{wire-BKxH1vbP.js → wire-CZGqNZRx.js} +5 -4
- package/dist/wire-CZGqNZRx.js.map +1 -0
- package/docs/01-quickstart.mdx +28 -19
- package/docs/02-examples.mdx +1 -1
- package/docs/actors/01-introduction.mdx +9 -1
- package/docs/actors/02-concurrency.mdx +1 -1
- package/docs/actors/meta.ts +1 -1
- package/docs/agents/01-introduction.mdx +1 -1
- package/docs/agents/02-tools.mdx +13 -12
- package/docs/agents/03-conversations.mdx +31 -21
- package/docs/agents/04-harnesses.mdx +7 -3
- package/docs/agents/meta.ts +1 -1
- package/docs/client/03-presence.mdx +18 -14
- package/docs/client/meta.ts +1 -1
- package/docs/contract/01-events.mdx +2 -2
- package/docs/contract/02-state.mdx +18 -14
- package/docs/contract/03-sessions.mdx +32 -25
- package/docs/index.mdx +31 -26
- package/docs/reference/01-core.mdx +12 -12
- package/docs/reference/02-server.mdx +247 -192
- package/docs/reference/04-ai.mdx +29 -20
- package/docs/reference/05-telemetry.mdx +146 -0
- package/docs/reference/06-errors.mdx +24 -18
- package/docs/reference/meta.ts +1 -1
- package/docs/schedulers/01-overview.mdx +112 -0
- package/docs/schedulers/02-vercel-queues.mdx +87 -0
- package/docs/schedulers/03-qstash.mdx +123 -0
- package/docs/schedulers/meta.ts +1 -0
- package/docs/server/01-consumers.mdx +415 -0
- package/docs/server/02-timers.mdx +33 -23
- package/docs/server/03-cancellation.mdx +52 -44
- package/docs/server/04-routes.mdx +4 -1
- package/docs/server/05-sql-projections.mdx +391 -0
- package/docs/server/06-guarantees.mdx +60 -50
- package/docs/stores/01-overview.mdx +37 -0
- package/docs/stores/02-redis.mdx +57 -0
- package/docs/stores/03-redis-http.mdx +56 -0
- package/docs/stores/04-postgres.mdx +172 -0
- package/docs/stores/05-sqlite.mdx +36 -0
- package/docs/stores/06-memory.mdx +22 -0
- package/docs/stores/meta.ts +1 -0
- package/docs/upgrades/01-overview.mdx +30 -0
- package/docs/upgrades/79-0-21.mdx +165 -0
- package/docs/upgrades/80-0-20.mdx +43 -0
- package/docs/upgrades/81-0-19.mdx +33 -0
- package/docs/upgrades/82-0-18.mdx +33 -0
- package/docs/upgrades/83-0-17.mdx +69 -0
- package/docs/upgrades/meta.ts +3 -0
- package/examples/playground/DEPLOY.md +48 -11
- package/examples/playground/app/a0/apps/[appId]/route.ts +6 -10
- package/examples/playground/app/a0/chat.test.tsx +421 -330
- package/examples/playground/app/a0/sandbox-log.ts +17 -15
- package/examples/playground/app/a0/server.ts +4 -4
- package/examples/playground/app/a0/shell.tsx +1 -1
- package/examples/playground/app/a0/uploads/route.test.ts +59 -0
- package/examples/playground/app/a2slack/a2slack.css +1067 -0
- package/examples/playground/app/a2slack/c/[channel]/page.tsx +20 -0
- package/examples/playground/app/a2slack/consumers/agent.ts +63 -0
- package/examples/playground/app/a2slack/consumers/feed.ts +164 -0
- package/examples/playground/app/a2slack/consumers/notify.ts +116 -0
- package/examples/playground/app/a2slack/consumers/search.ts +37 -0
- package/examples/playground/app/a2slack/consumers/triage.ts +31 -0
- package/examples/playground/app/a2slack/consumers/welcome.ts +25 -0
- package/examples/playground/app/a2slack/db/a2.ts +206 -0
- package/examples/playground/app/a2slack/db/index.ts +73 -0
- package/examples/playground/app/a2slack/db/migrations/0000_extensions.sql +4 -0
- package/examples/playground/app/a2slack/db/migrations/0001_workspace.sql +74 -0
- package/examples/playground/app/a2slack/db/migrations/0002_a2.sql +215 -0
- package/examples/playground/app/a2slack/db/migrations/0003_a2_batch.sql +67 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0000_snapshot.json +18 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0001_snapshot.json +500 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0002_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0003_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/_journal.json +34 -0
- package/examples/playground/app/a2slack/db/queries.ts +263 -0
- package/examples/playground/app/a2slack/db/schema.ts +145 -0
- package/examples/playground/app/a2slack/drills/a2slack.drill.ts +708 -0
- package/examples/playground/app/a2slack/drills/harness.ts +244 -0
- package/examples/playground/app/a2slack/events/route.ts +36 -0
- package/examples/playground/app/a2slack/identity.ts +44 -0
- package/examples/playground/app/a2slack/inbox/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/data/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/page.tsx +101 -0
- package/examples/playground/app/a2slack/lag.ts +73 -0
- package/examples/playground/app/a2slack/layout.tsx +43 -0
- package/examples/playground/app/a2slack/messages/route.ts +17 -0
- package/examples/playground/app/a2slack/model.test.ts +208 -0
- package/examples/playground/app/a2slack/model.ts +369 -0
- package/examples/playground/app/a2slack/models.test.ts +72 -0
- package/examples/playground/app/a2slack/page.tsx +10 -0
- package/examples/playground/app/a2slack/scheduler/route.ts +5 -0
- package/examples/playground/app/a2slack/search/route.ts +40 -0
- package/examples/playground/app/a2slack/search-index.ts +108 -0
- package/examples/playground/app/a2slack/server.test.ts +315 -0
- package/examples/playground/app/a2slack/server.ts +55 -0
- package/examples/playground/app/a2slack/sessions.ts +50 -0
- package/examples/playground/app/a2slack/thread/route.ts +8 -0
- package/examples/playground/app/a2slack/ui/ChannelView.tsx +87 -0
- package/examples/playground/app/a2slack/ui/Composer.tsx +117 -0
- package/examples/playground/app/a2slack/ui/DotsShadow.tsx +42 -0
- package/examples/playground/app/a2slack/ui/Feed.tsx +157 -0
- package/examples/playground/app/a2slack/ui/InboxContext.tsx +63 -0
- package/examples/playground/app/a2slack/ui/InboxView.tsx +82 -0
- package/examples/playground/app/a2slack/ui/Menu.tsx +128 -0
- package/examples/playground/app/a2slack/ui/MessageRow.tsx +314 -0
- package/examples/playground/app/a2slack/ui/NameGate.tsx +35 -0
- package/examples/playground/app/a2slack/ui/SearchContext.tsx +146 -0
- package/examples/playground/app/a2slack/ui/SearchResults.tsx +97 -0
- package/examples/playground/app/a2slack/ui/Shell.tsx +34 -0
- package/examples/playground/app/a2slack/ui/Sidebar.tsx +199 -0
- package/examples/playground/app/a2slack/ui/ThreadPanel.tsx +134 -0
- package/examples/playground/app/a2slack/ui/useInbox.ts +71 -0
- package/examples/playground/app/agent/[agentId]/agent-queue.test.tsx +116 -90
- package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +107 -76
- package/examples/playground/app/agent/ingress.test.ts +41 -17
- package/examples/playground/app/agent/model-input.test.tsx +92 -43
- package/examples/playground/app/agent/server.ts +3 -3
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.test.tsx +2 -2
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +1 -1
- package/examples/playground/app/chat/server.test.ts +38 -65
- package/examples/playground/app/chat/server.ts +45 -35
- package/examples/playground/app/components/activity-history.test.tsx +60 -18
- package/examples/playground/app/components/demo-icons.tsx +7 -0
- package/examples/playground/app/components/demos.tsx +7 -0
- package/examples/playground/app/components/fast-link.test.tsx +3 -5
- package/examples/playground/app/initial-state.test.tsx +86 -45
- package/examples/playground/app/orders/[orderId]/order-client.tsx +2 -2
- package/examples/playground/app/orders/page.tsx +1 -1
- package/examples/playground/app/orders/server.ts +49 -30
- package/examples/playground/app/recovery/[recoveryId]/recovery-client.tsx +14 -15
- package/examples/playground/app/recovery/page.tsx +4 -4
- package/examples/playground/app/recovery/server.ts +23 -32
- package/examples/playground/drizzle.config.ts +14 -0
- package/examples/playground/next.config.ts +6 -1
- package/examples/playground/package.json +13 -1
- package/examples/playground/scripts/migrate.mjs +31 -0
- package/examples/playground/test/agent.ts +35 -0
- package/examples/playground/test/model.ts +149 -0
- package/examples/playground/test/serve.ts +184 -0
- package/examples/playground/test/server-only.ts +1 -0
- package/examples/playground/vercel.json +9 -0
- package/examples/playground/vitest.config.ts +27 -0
- package/examples/playground/vitest.drills.config.ts +32 -0
- package/package.json +7 -2
- package/sql/postgres/drizzle-schema.ts +206 -0
- package/sql/postgres/notifications.sql +24 -0
- package/sql/postgres/schema.sql +276 -0
- package/src/actor.ts +101 -71
- package/src/ai-agent.ts +154 -115
- package/src/ai-control-server.ts +8 -8
- package/src/ai-fx.ts +41 -9
- package/src/ai-harness.ts +28 -2
- package/src/ai-pi.ts +2 -2
- package/src/ai-server.ts +2 -2
- package/src/ai.ts +7 -7
- package/src/contract.ts +1 -1
- package/src/deterministic-id.ts +26 -3
- package/src/errors.ts +9 -6
- package/src/idempotent-replay.ts +0 -1
- package/src/index.ts +1 -1
- package/src/internal.ts +17 -6
- package/src/otel.ts +1 -1
- package/src/postgres-notifications.ts +0 -22
- package/src/postgres-resources.ts +6 -9
- package/src/postgres-schema.ts +121 -0
- package/src/push-envelope.ts +20 -0
- package/src/runtime-consumer.ts +10 -0
- package/src/scheduler-qstash.ts +12 -5
- package/src/scheduler-task.ts +2 -2
- package/src/scheduler-vercel.ts +10 -4
- package/src/server.browser.ts +1 -1
- package/src/server.ts +839 -238
- package/src/session-socket.ts +1 -1
- package/src/store-codec.ts +66 -0
- package/src/store-memory.ts +581 -255
- package/src/store-postgres.ts +1984 -943
- package/src/store-redis-core.ts +1624 -1152
- package/src/store-redis-notify.ts +2 -0
- package/src/store-sqlite.ts +891 -486
- package/src/store.ts +227 -97
- package/src/telemetry.ts +2 -1
- package/src/wire.ts +1 -0
- package/dist/ai-harness-BuuC-gJ3.js.map +0 -1
- package/dist/ai-harness-CV6rXbB3.d.ts.map +0 -1
- package/dist/ai-mMiWul1T.js.map +0 -1
- package/dist/contract-BYKASlCV.js.map +0 -1
- package/dist/idempotent-replay-JdgMQRRm.js +0 -21
- package/dist/idempotent-replay-JdgMQRRm.js.map +0 -1
- package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +0 -1
- package/dist/internal-CscSY7K_.js.map +0 -1
- package/dist/server-D-Y4pqwu.js.map +0 -1
- package/dist/server-DYAATFuT.d.ts.map +0 -1
- package/dist/session-clear-C-QwTXV1.d.ts.map +0 -1
- package/dist/session-clear-DGI4jTQ5.js.map +0 -1
- package/dist/store-CyLUy86v.d.ts +0 -404
- package/dist/store-CyLUy86v.d.ts.map +0 -1
- package/dist/store-codec-DTG0Ftek.js +0 -10
- package/dist/store-codec-DTG0Ftek.js.map +0 -1
- package/dist/store-redis-notify-CM0AfLY0.js +0 -2544
- package/dist/store-redis-notify-CM0AfLY0.js.map +0 -1
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +0 -1
- package/dist/wire-BKxH1vbP.js.map +0 -1
- package/docs/reference/05-adapters.mdx +0 -351
- package/docs/server/01-handlers.mdx +0 -200
- package/docs/server/05-production.mdx +0 -323
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.21.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 65ab0df: Under `fx()` and `pi()`, steering that hands off before any of the turn's model calls finished, as after a pause during the first one, now joins the turn's prompt. The turn's first model call sees it, and under `fx()` its files go with the prompt instead of failing the turn. Before, the engine got it only after that call, as steering, so the model answered without it once.
|
|
8
|
+
|
|
9
|
+
## 0.21.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- a80ad79: `session.consume(consumer, { through?, signal? })` runs a consumer with a string lane on a session, in the calling invocation, until it has handled every event it takes through `through` (default: the head), and returns `{ index }`. Use it to read your writes before querying a table a consumer keeps, to start a backfill right away with `{ through: 0 }`, or in one consumer to wait for another's table to reach its trigger. The server's own consumer shares its cursor with background work, and `consume` waits for whichever invocation holds it; a consumer the server doesn't run is pulled only by `consume` and keeps its place between calls. A dead-lettered head throws `CONSUMER_DEAD_LETTERED`. Custom stores implement `wakeLog`, which creates or wakes one log consumer's cursor without an append, and writes nothing for a session with nothing after its latest clear. A consumer with `backfill` can't use `abortOn`: defining one throws a `TypeError`, since checking cancellation while replaying history would read the rest of the log for every event.
|
|
14
|
+
- 2cbb952: Consumers: `consumer({ contract, name, types?, lane?, backfill?, abortOn? }).run(fn)` defines a named reader of every session's log, registered with `createServer({ consumers })`. Several consumers can react to one event, each with its own durable cursor, lanes, retries, and dead letters; a consumer with a string lane runs everything it takes in log order and can start from the beginning of the log with `backfill: true`. Dead letters now carry their `consumer`, and `session.redrive()` takes `{ consumer, index }`. The store contract moves dispatch bookkeeping from event rows to cursors: custom adapters implement `cursors()` and the cursor-keyed claim, completion, failure, and redrive methods. Let earlier deployments finish their work first: on Postgres, then apply the new `sql/postgres/schema.sql` while none of them is running. On Postgres and Redis every event is kept, and work an earlier release's handlers hadn't finished is dropped; Redis reads an earlier release's sessions under the same `keyPrefix`, and its old dispatch keys stay until you delete them. SQLite starts fresh: a file from an earlier release throws `STORE_NOT_CONFIGURED`, so delete it or pass another `path`. Upgrade steps for each store: https://www.a2.dev/upgrades/0-21.
|
|
15
|
+
- 59a655d: `handlers` are gone: consumers are the one way to react to events. `createServer({ handlers })`, the `handler()` helper, and the `Handler`, `HandlerEntry`, and `Lane` types are removed; write each reaction in `createServer({ consumers })`, inline as `{ name, types?, lane?, run }` on the server's contract, or as `consumer({ contract, name, types?, lane? }).run(fn)` to define one on its own. An inline `run` narrows `event` to its `types`; `agent({ consumers })` takes both forms too. A handler table whose types shared a lane becomes one consumer that takes those types, with a lane function returning that lane. `agent({ handlers })` is now `agent({ consumers })`. `HandlerContext` is now `ConsumerContext`, `HandlerAppend` is `ConsumerAppend`, `ToolHandlerContext` is `ToolConsumerContext`, `handlerContext()` is `consumerContext()`, and `runWithHandlerContext` is `runWithConsumerContext`. A consumer of an AI event type runs beside the agent's own, so `agent()` no longer rejects app code on those types. `session.redrive()` takes only `{ consumer, index }`. Actors (`actor({ handlers })` keeps its name) and the AI runtime run on A2's own consumers, named under the reserved `a2.` prefix: `a2.actor` and `a2.actor.concurrent`, and `a2.ai.metadata`, `a2.ai.control`, `a2.ai.model`, and `a2.ai.tools`. Their event ids are unchanged. The `a2.append.source` telemetry attribute reads `consumer` where it read `handler`. Every rename, old to new: https://www.a2.dev/upgrades/0-21.
|
|
16
|
+
|
|
17
|
+
Lag telemetry: every `a2.event` span carries `a2.event.age_ms`, how old the event was when its run started, and `a2.event.lag`, how many events the session had appended after it when it was claimed. Group them by `a2.event.consumer` to see how far each consumer is behind. Claim results from a store may now report the session's `head`, which every built-in store does.
|
|
18
|
+
|
|
19
|
+
`server.fetch` refuses a pushed event `id` longer than 256 characters, or shaped like a UUIDv5, with `INVALID_PAYLOAD`. A2 derives the ids of what consumers append, return, and schedule as UUIDv5s from inputs a caller can know, so a caller who pushed one first could take its place: dead-letter the consumer, or turn a scheduled event into a no-op. Client ids from `crypto.randomUUID()` (v4) and `idempotentId()` (v8) are unaffected.
|
|
20
|
+
|
|
21
|
+
- 59a655d: The Postgres store checks nothing up front. It no longer reads a schema version before its first query, and `schema.sql` drops the `a2_schema` table. A query that finds one of A2's tables or columns missing, or that its role can't use, throws `STORE_NOT_CONFIGURED`, naming the files to apply or the grants to make. A store with `listenConnectionString` still looks for its triggers, since a missing one fails no query: its first subscription throws `STORE_NOT_CONFIGURED` until `notifications.sql` is applied. When to apply the new `schema.sql`: https://www.a2.dev/upgrades/0-21.
|
|
22
|
+
|
|
23
|
+
`sql/postgres/drizzle-schema.ts` shows A2's tables as Drizzle definitions: an example of one way to fit them into a Drizzle project, so your own migrations create them. `schema.sql` also works as a migration as it ships: a breakpoint comment ends each statement, so a tool that sends one statement per query, like Drizzle on PGlite, applies it unchanged.
|
|
24
|
+
|
|
25
|
+
- af3cb3e: The Postgres store runs each append, claim, completion, `session.consume` poll, and redrive in one round trip, through a new function in `schema.sql`, `a2_batch`. A session's lock is held only while the database runs the call, not across round trips from your app, so one busy session keeps up on a database a few milliseconds away. A completion that moves its lane or log cursor to the next event takes no session lock at all, and neither does a `consume` poll that finds nothing new.
|
|
26
|
+
|
|
27
|
+
Apply the new `schema.sql` before you deploy. It adds the function and changes no table, and the deployment that is running keeps working against it. Deployed first, writes throw `STORE_NOT_CONFIGURED` naming `schema.sql`, and the next call after you apply it succeeds. With Drizzle, add the function as a custom migration (`drizzle-kit generate --custom`), copied from `schema.sql`: Drizzle can't declare functions. The function needs `read committed`, Postgres's default isolation level; under a stricter default it throws `STORE_NOT_CONFIGURED`.
|
|
28
|
+
|
|
29
|
+
Two fixes ride along. A drain whose last runs finish while it is claiming now claims again, instead of stopping with their cursors' next events unclaimed until the next append or scheduler watchdog. And on Postgres, a claim that waited on a cursor while its completion moved it to a newer event now returns that event; before, it held the head without running it, until a takeover counted the claim as a failed attempt.
|
|
30
|
+
|
|
31
|
+
A payload Postgres can't store, one with a NUL character (`\u0000`), now throws `INVALID_PAYLOAD` with nothing written, instead of `STORE_UNAVAILABLE`, which said to retry.
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- 59a655d: Docs: an Upgrades section, `docs/upgrades/`, with one page per release that needs steps from you, from 0.17 through 0.21. `docs/server/05-sql-projections.mdx` keeps tables in your own database from A2's logs: it claims each event with one upsert on a position row, first in the transaction, and covers reading your writes, live views, SQLite, lag, and rebuilds.
|
|
36
|
+
|
|
37
|
+
## 0.20.0
|
|
38
|
+
|
|
39
|
+
### Minor Changes
|
|
40
|
+
|
|
41
|
+
- 123c993: The Postgres store no longer creates its tables. Before you deploy this release, apply the SQL it needs, `node_modules/experimental-a2/sql/postgres/schema.sql`, once per database, however you run SQL against it. It creates only what is missing and leaves existing data alone, so it is safe on a database an earlier release set up, and the deployment that is running now keeps working against it, without waiting on its writes. It also records a schema version in a new `a2_schema` table. Apply it as the role your app connects with; if another role applies it, grant your app's role `select` on `a2_schema`. With `listenConnectionString`, also apply `sql/postgres/notifications.sql`, which installs the triggers A2 used to install for you.
|
|
42
|
+
|
|
43
|
+
If you deploy first, the store throws `STORE_NOT_CONFIGURED` on its first query, naming the file to apply, and the next call after you apply it succeeds without a redeploy. Until then, nothing is written: appends fail, and scheduled work waits. Vercel Queues keeps redelivering it, and the QStash scheduler now treats `STORE_NOT_CONFIGURED` like a store outage, handing each task to a later message (30 seconds, backing off to one day) instead of letting QStash's retries run out into its DLQ. That also covers a missing driver package, which used to dead-letter.
|
|
44
|
+
|
|
45
|
+
Each cold start now reads the schema version instead of running DDL. The DDL took a share lock on `a2_events` even when every table existed, so a cold start waited behind any open write to it, and writes waited behind the cold start. The store also no longer needs permission to create tables, so a runtime role with only read and write access works. SQLite still creates its tables on open.
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- 123c993: Docs: stores and schedulers each get their own section, with one page per adapter, such as `docs/stores/04-postgres.mdx` and `docs/schedulers/03-qstash.mdx`. `docs/server/05-production.mdx` is gone: resolving dead letters moved to `docs/server/01-handlers.mdx`, and traces to `docs/reference/05-telemetry.mdx`, which replaces `docs/reference/05-adapters.mdx`.
|
|
50
|
+
- 5228ecb: A returned batch the store refuses, such as one that reuses an event id, now dead-letters the event on that first attempt, like a `NonRetriableError`, with the store's error as its `lastError`: a retry would return the same ids. Before, the refused completion left the claim to lapse, so the event was retried on every watchdog wake and dead-lettered with no error. A scheduler delivery whose drain throws after arming its successor watchdog now acknowledges once that successor is durable, with the error on its `a2.drain` span (`a2.drain.outcome: failed`). Before, it threw: Vercel Queues redelivered the message alongside the successor, each redelivery arming another, and QStash retried it or, for a store outage, published a second, outage successor. `server.drain()` still throws.
|
|
51
|
+
- 272e603: `fx()` model calls now carry the same AI Gateway defaults as the AI SDK harness: `caching: 'auto'` in the request's Gateway options, under any Gateway options FX sets itself such as its fast tier, and the session's stable `x-session-affinity` header. Before, FX asked for no caching and sent its own affinity, which changed every time A2 rebuilt the kernel, so a response's model calls rarely reused the provider's prompt cache. The rest of each request goes as FX wrote it.
|
|
52
|
+
|
|
3
53
|
## 0.19.0
|
|
4
54
|
|
|
5
55
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { s as Contract, t as Reducer, u as EventDefs, v as StandardSchemaV1 } from "./reducer-
|
|
2
|
-
import { t as A2Store } from "./store-
|
|
3
|
-
import {
|
|
4
|
-
import { i as A2Telemetry } from "./telemetry-
|
|
1
|
+
import { s as Contract, t as Reducer, u as EventDefs, v as StandardSchemaV1 } from "./reducer-CYPHsSmd.js";
|
|
2
|
+
import { t as A2Store } from "./store-Z2qjw50n.js";
|
|
3
|
+
import { g as ScheduleTiming, n as A2Server, t as A2Scheduler, y as Session } from "./server-BshFZ0eL.js";
|
|
4
|
+
import { i as A2Telemetry } from "./telemetry-CWwsh-Gx.js";
|
|
5
5
|
//#region src/actor-shared.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* A handler answered by throwing `NonRetriableError`: the event
|
|
@@ -317,4 +317,4 @@ type ActorDefinition<D extends ActorProtocol> = {
|
|
|
317
317
|
declare function actor<D extends ActorProtocol>(options: ActorOptions<D>): ActorDefinition<D>;
|
|
318
318
|
//#endregion
|
|
319
319
|
export { ActorProtocol as _, ActorHandle as a, ActorScheduleSend as b, ActorOptions as c, concurrent as d, ActorConcurrentContext as f, ActorPresenceValues as g, ActorPresenceOf as h, ActorFetchOptions as i, ActorSendOptions as l, ActorPresenceMap as m, ActorCallResult as n, ActorHandler as o, ActorContext as p, ActorDefinition as r, ActorOperation as s, ActorCallOptions as t, actor as u, ActorRefusedError as v, ActorSend as x, ActorScheduleOptions as y };
|
|
320
|
-
//# sourceMappingURL=actor-
|
|
320
|
+
//# sourceMappingURL=actor-HnGmrOZZ.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actor-
|
|
1
|
+
{"version":3,"file":"actor-HnGmrOZZ.d.ts","names":[],"sources":["../src/actor-shared.ts","../src/actor.ts"],"mappings":";;;;;;;;;;;cAiCa,0BAA0B;;WAE5B;;WAEA;EACT,YAAY,eAAe,mBAAmB;;;;;;;;;KAepC;EACV;EACA;;;;;;;EAOA;;;KAIU,gBAAgB,UAAU,iBAAiB;EACrD,gBAAgB;IAEd;;KAIQ,oBAAoB,QAC7B,WAAW,cAAc,EAAE;;;;;;KAQlB,iBAAiB;GAExB,yBAAA,WAAW;IAAgB,OAAO,EAAE;IAAI;IAAc,IAAI;;;;;;;;;;;;KAanD,UAAU,iBACV,WAAW,eAAe,EAAE,MACjC,QAAQ,EAAE,eACV,OAAO,EAAE;;;;;;;;;KAWJ,uBAAuB;EAAmB;;;;;;;;;;;;KAY1C,kBAAkB,iBAClB,WAAW,eAAe,EAAE,MACjC,OAAO,EAAE,gBAAgB,SAAS,yBAAyB,iBAC3D,OAAO,EAAE,IAAI,SAAS,yBAAyB;;KAI1C,aAAa,UAAU;;EAEjC,OAAO;;EAEP;;EAEA;;EAEA,QAAQ;;EAER,MAAM,UAAU;;EAEhB,UAAU,kBAAkB;;;;;;;;KASlB,uBAAuB,UAAU;;EAE3C;;EAEA;;EAEA,QAAQ;;EAER,SAAS;IAAU,OAAO;IAAY;;;EAEtC,MAAM,UAAU;;EAEhB,UAAU,kBAAkB;;KAGlB,kBAAkB;EAC5B,OAAO;;EAEP;;EAEA;;EAEA;;;KAWU,qBAAqB;EAC/B,kBAAkB,iBAAiB,kBAAkB;;;;;;;;;;;;;;KClE3C,aAAa,UAAU,eAAe,gBAAgB,iBAC5D,KAAK,aAAa,IAAI,OAAO,YAAY,cAAc;EAEvD;EACA,SACE,KAAK,uBAAuB,IAC5B,OAAO,YAAY,cACT;;;;;;;;;;;;;;;;iBAiBF,WAAW,KAAK,GAC9B,SAAS,KAAK,KAAK,OAAO,aAAa;EACpC;EAAkB,SAAS,KAAK,KAAK,OAAO,aAAa;;KAIlD,aAAa,UAAU;;EAEjC;;EAEA,OAAO;;;;;EAKP,aAAa,WAAW,cAAc,aAAa,GAAG;EACtD,QAAQ;EACR,YAAY;EACZ,YAAY;MACR,gBAAgB;EAChB;;;;;;;;;;;;EAYA;;;;;;;KAQM;WACG;WAAwB;;WAExB;WACA;WACA;;WAGA;WACA;WACA;WACA;WACA;;WAGA;WACA;WACA;WACA,QAAQ,SAAS;;KAGpB,kBAAkB,UAAU;;EAEtC,aAAa,WAAW,6BAA6B;;;;;;;;;;;;EAYrD,QAAQ,OAAO;;KAGL;;EAEV;;KAGU,mBAAmB;;EAE7B;;KAGU,gBAAgB;;EAE1B,OAAO;;EAEP;;;;;EAKA;;KAGG,WACH,UAAU,eACV,gBAAgB,0BACH,YAAY,MAErB,QAAQ,YAAY,IACpB,UAAU,qBACP,QAAQ,gBAAgB,gBAE3B,OAAO,YAAY,IACnB,UAAU,qBACP,QAAQ,gBAAgB;KAE5B,WACH,UAAU,eACV,gBAAgB,0BACH,YAAY,MAErB,QAAQ,YAAY,IACpB,UAAU,qBACP;EAAU;EAAY;MAEzB,OAAO,YAAY,IACnB,UAAU,qBACP;EAAU;EAAY;;;KAGnB,YAAY,UAAU;WACvB;;;;;;;;WAQA,kBAAkB,WAAW,cAAc,WAAW,GAAG;;WAEzD,kBAAkB,WAAW,cAAc,WAAW,GAAG;;WAEzD,aAAa,QAAQ,gBAAgB;;;;;;;;;;;WAWrC,QACP,SAAS,SACT,UAAU,kBAAkB,OACzB,QAAQ;;WAEJ,SAAS,QAAQ;;KAGhB,gBAAgB,UAAU;WAC3B;;;;;;WAMA,WAAW;;WAEX,UAAU,SAAS;;WAEnB,QAAQ,SAAS;;WAEjB,SAAS,QAAQ,qBAAqB,aAAa;EAC5D,MAAM,aAAa,YAAY;;;;;;;iBAsDjB,MAAM,UAAU,eAC9B,SAAS,aAAa,KACrB,gBAAgB"}
|
package/dist/actor-client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as ActorCallResult, v as ActorRefusedError } from "./actor-
|
|
1
|
+
import { n as ActorCallResult, v as ActorRefusedError } from "./actor-HnGmrOZZ.js";
|
|
2
2
|
//#region src/actor-client.d.ts
|
|
3
3
|
declare class ActorRequestError extends Error {
|
|
4
4
|
readonly status: number | undefined;
|
package/dist/actor-client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as A2Error } from "./session-clear-
|
|
2
|
-
import { i as ActorRefusedError } from "./actor-shared-
|
|
1
|
+
import { i as A2Error } from "./session-clear-wHd5GCpZ.js";
|
|
2
|
+
import { i as ActorRefusedError } from "./actor-shared-DFET5q16.js";
|
|
3
3
|
//#region src/actor-client.ts
|
|
4
4
|
/**
|
|
5
5
|
* experimental-a2/actor/client — typed calls to an actor over HTTP.
|
package/dist/actor-react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { c as ContractEvent, u as EventDefs } from "./reducer-
|
|
2
|
-
import { g as ActorPresenceValues, m as ActorPresenceMap } from "./actor-
|
|
1
|
+
import { c as ContractEvent, u as EventDefs } from "./reducer-CYPHsSmd.js";
|
|
2
|
+
import { g as ActorPresenceValues, m as ActorPresenceMap } from "./actor-HnGmrOZZ.js";
|
|
3
3
|
import { ActorClientCall, ActorStateOf, AnyActorDefinition } from "./actor-client.js";
|
|
4
|
-
import { a as Connection } from "./client-
|
|
4
|
+
import { a as Connection } from "./client-CUMbS_38.js";
|
|
5
5
|
//#region src/actor-react.d.ts
|
|
6
6
|
/** The server-to-client handoff: `await def.actor(id).state()`. */
|
|
7
7
|
type ActorSnapshot<S> = {
|
package/dist/actor-react.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { n as isSessionCleared } from "./session-clear-
|
|
3
|
-
import { r as foldsClearMarkers } from "./reducer-
|
|
4
|
-
import { n as internalContract } from "./contract-
|
|
5
|
-
import { t as createClient } from "./client-
|
|
2
|
+
import { n as isSessionCleared } from "./session-clear-wHd5GCpZ.js";
|
|
3
|
+
import { r as foldsClearMarkers } from "./reducer-CjmvZSAb.js";
|
|
4
|
+
import { n as internalContract } from "./contract-CWukt01d.js";
|
|
5
|
+
import { t as createClient } from "./client-9IeTsiAs.js";
|
|
6
6
|
import { useSession } from "./react.js";
|
|
7
|
-
import { l as openPresenceDefs } from "./actor-shared-
|
|
7
|
+
import { l as openPresenceDefs } from "./actor-shared-DFET5q16.js";
|
|
8
8
|
import { createActorClient } from "./actor-client.js";
|
|
9
9
|
import { useMemo } from "react";
|
|
10
10
|
//#region src/actor-react.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as reducer } from "./reducer-
|
|
2
|
-
import { n as internalContract } from "./contract-
|
|
1
|
+
import { a as reducer } from "./reducer-CjmvZSAb.js";
|
|
2
|
+
import { n as internalContract } from "./contract-CWukt01d.js";
|
|
3
3
|
//#region src/actor-shared.ts
|
|
4
4
|
/**
|
|
5
5
|
* Internal shared vocabulary of the actor module — the reserved event
|
|
@@ -129,4 +129,4 @@ function actorReducer(options) {
|
|
|
129
129
|
//#endregion
|
|
130
130
|
export { actorFailedSchema as a, isJsonTree as c, ActorRefusedError as i, openPresenceDefs as l, ACTOR_LANE as n, actorReducer as o, ACTOR_STATE_EVENT as r, actorStateSchema as s, ACTOR_FAILED_EVENT as t };
|
|
131
131
|
|
|
132
|
-
//# sourceMappingURL=actor-shared-
|
|
132
|
+
//# sourceMappingURL=actor-shared-DFET5q16.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actor-shared-t7w8xIKP.js","names":["createContract"],"sources":["../src/actor-shared.ts"],"sourcesContent":["import { reducer } from './reducer.ts'\n/**\n * Internal shared vocabulary of the actor module — the reserved event\n * envelopes, the protocol and context types, the state fold. Not an\n * entry point: the public surfaces are experimental-a2/actor (server),\n * /actor/client, and /actor/react.\n *\n * An actor is a durable mailbox with memory: one session per instance,\n * events processed one at a time (a lane) unless a handler opts out\n * with `concurrent: true`, state materialized as `a2.actor.state`\n * events committed atomically with each handler's completion.\n */\n\nimport { internalContract as createContract } from './contract.ts'\nimport type { Reducer } from './reducer.ts'\nimport type { ScheduleTiming } from './server.ts'\nimport type { StandardSchemaV1 } from './standard-schema.ts'\n\n/** Event type of a committed state change — one per completed serial handler. */\nexport const ACTOR_STATE_EVENT = 'a2.actor.state'\n/** Event type of a refusal — a handler that answered by throwing NonRetriableError. */\nexport const ACTOR_FAILED_EVENT = 'a2.actor.failed'\n/** The per-instance lane every serial handler shares — the state's write lock. */\nexport const ACTOR_LANE = 'a2.actor'\n/** Reducer identity — bump when the fold's meaning changes. */\nexport const ACTOR_REDUCER_NAME = 'a2.actor.v1'\n\n/**\n * A handler answered by throwing `NonRetriableError`: the event\n * settled, the state did not change. Isomorphic — the server raises it\n * from calls and `fetch()` serializes it as a 409; the client's `call`\n * proxy revives it from that response.\n */\nexport class ActorRefusedError extends Error {\n /** The refused event's name. */\n readonly event: string\n /** The refused event's message id. */\n readonly messageId: string\n constructor(event: string, messageId: string, message: string) {\n super(message)\n this.name = 'ActorRefusedError'\n this.event = event\n this.messageId = messageId\n }\n}\n\n/**\n * The declaration an actor is defined over: its state shape and its\n * event vocabulary, as types. Core A2 contracts are schemas because\n * the wire is untrusted; actor protocols are types because your\n * server is trusted — same contract-first design language, dialed to\n * the trust level.\n */\nexport type ActorProtocol = {\n state: object\n events: object\n /**\n * Optional third vocabulary: presence field → value type. Ephemeral\n * audience state (who is here, cursors), replicated to subscribers\n * and never stored in the log. Declaring it requires `presence:\n * true` in the actor options — the wire bit types cannot carry.\n */\n presence?: object\n}\n\n/** The protocol's presence vocabulary, `never` when undeclared. */\nexport type ActorPresenceOf<D extends ActorProtocol> = D extends {\n presence: infer P extends object\n}\n ? P\n : never\n\n/** A `setPresence` patch: changed fields, `null` clears one. */\nexport type ActorPresenceValues<P> = {\n [F in keyof P & string]?: P[F] | null\n}\n\n/**\n * The replicated presence map: participant → field → latest value,\n * last write wins per field. Values are peer-authored — render them\n * like user input.\n */\nexport type ActorPresenceMap<P> = {\n [participant: string]: {\n [F in keyof P & string]?: { value: P[F]; seen: number; at: Date }\n }\n}\n\n/**\n * The typed self-send surface: one method per declared event —\n * `ctx.send.transfer({ ref, amount })`. Buffered, not immediate: every\n * send requested during a handler commits atomically with that\n * handler's completion (a serial handler's state commit, a concurrent\n * handler's settlement), riding core's returned-events semantics — so\n * a reserve-and-trigger can never half-happen, and re-runs converge on\n * the same deterministic message ids.\n */\nexport type ActorSend<E> = {\n readonly [K in keyof E]: {} extends E[K]\n ? (input?: E[K]) => void\n : (input: E[K]) => void\n}\n\n/**\n * Timing for a scheduled event: exactly `{ delay: '5d' }` or\n * `{ at: Date }` (core §6 semantics — relative delays anchor to the\n * triggering message's durable `createdAt`, so re-runs resolve the\n * same due time). `name` overrides the timer's identity — it defaults\n * to the target event name, scoped to the triggering message, so one\n * handler run gets one timer per target event unless named apart.\n */\nexport type ActorScheduleOptions = ScheduleTiming & { name?: string }\n\n/**\n * The typed durable-timer surface: one method per declared event —\n * `ctx.schedule.refund({ ref: ctx.id }, { delay: '1h' })`. Unlike\n * `send`, scheduling is immediate, not buffered: it awaits provider\n * acceptance at call time (a timer is provider-side, not a log row),\n * so a handler that schedules and then refuses has still armed the\n * timer. That is safe by the guarded-delivery idiom — the stale timer\n * fires into an idempotent no-op — but it is the one exception to\n * \"refusals are total\". Requires a configured `scheduler`.\n */\nexport type ActorScheduleSend<E> = {\n readonly [K in keyof E]: {} extends E[K]\n ? (input: E[K] | undefined, options: ActorScheduleOptions) => Promise<void>\n : (input: E[K], options: ActorScheduleOptions) => Promise<void>\n}\n\n/** What a serial handler receives alongside its typed input. */\nexport type ActorContext<D extends ActorProtocol> = {\n /** Mutable draft, committed atomically with the handler's completion. */\n state: D['state']\n /** The message id — stable across re-runs; the idempotency key for external I/O. */\n id: string\n /** Durable 1-based dispatch ordinal of this message. */\n attempt: number\n /** The ordinary A2 handler signal — fires on claim expiry or supersession. */\n signal: AbortSignal\n /** Typed buffered self-send — committed atomically with the state commit. */\n send: ActorSend<D['events']>\n /** Typed durable timers — immediate provider handoff, see ActorScheduleSend. */\n schedule: ActorScheduleSend<D['events']>\n}\n\n/**\n * What a `concurrent: true` handler receives. No draft — concurrent\n * handlers run off the lane, in parallel, so state is a snapshot read\n * (honest about staleness) and mutations happen by sending events\n * whose serial handlers decide against fresh state.\n */\nexport type ActorConcurrentContext<D extends ActorProtocol> = {\n /** The message id — stable across re-runs; the idempotency key for external I/O. */\n id: string\n /** Durable 1-based dispatch ordinal of this message. */\n attempt: number\n /** The ordinary A2 handler signal — fires on claim expiry or supersession. */\n signal: AbortSignal\n /** Snapshot read — observational; the world moves while this runs. */\n state(): Promise<{ state: D['state']; index: number }>\n /** Typed buffered self-send — committed atomically with settlement. */\n send: ActorSend<D['events']>\n /** Typed durable timers — immediate provider handoff, see ActorScheduleSend. */\n schedule: ActorScheduleSend<D['events']>\n}\n\nexport type ActorStatePayload<S> = {\n state: S\n /** The event whose handler committed this state. */\n event: string\n /** The message (invocation event) id this state answers. */\n message: string\n /** The session's latest clear marker when this state was read. */\n clearIndex?: number\n}\n\nexport type ActorFailedPayload = {\n /** The message (invocation event) id this refusal answers. */\n message: string\n event: string\n error: string\n}\n\n/** The event vocabulary a client needs to follow an actor's state. */\nexport type ActorClientEventDefs<S> = {\n 'a2.actor.state': StandardSchemaV1<ActorStatePayload<S>>\n}\n\ntype SchemaResult<T> = StandardSchemaV1.Result<T>\n\nconst issue = <T>(message: string): SchemaResult<T> => ({\n issues: [{ message }],\n})\n\nconst schema = <T>(\n label: string,\n parse: (value: unknown) => SchemaResult<T>,\n): StandardSchemaV1<T> => ({\n '~standard': {\n version: 1,\n vendor: 'a2',\n validate(value) {\n try {\n return parse(value)\n } catch (error) {\n return issue(\n `${label}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n },\n },\n})\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/** True for plain JSON trees — what the log can store without loss. */\nexport function isJsonTree(\n value: unknown,\n seen: Set<object> = new Set(),\n): boolean {\n if (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'boolean'\n ) {\n return true\n }\n if (typeof value === 'number') return Number.isFinite(value)\n if (typeof value !== 'object') return false\n if (seen.has(value)) return false\n seen.add(value)\n const valid = Array.isArray(value)\n ? value.every((item) => isJsonTree(item, seen))\n : Object.getPrototypeOf(value) === Object.prototype &&\n Object.values(value).every(\n (item) => item === undefined || isJsonTree(item, seen),\n )\n seen.delete(value)\n return valid\n}\n\n/** Per-field ceiling for presence values — presence is a cursor, not a document. */\nexport const PRESENCE_VALUE_MAX_BYTES = 8_192\n\n/**\n * The open presence vocabulary: the '*' catch-all validates any field\n * name against the JSON floor. The protocol types the vocabulary at\n * compile time; the route's `authorize` sees every set before\n * acceptance; renderers treat values as user input. Meaning has three\n * guards — this schema only owns the bytes.\n */\nexport const openPresenceDefs: Readonly<\n Record<string, StandardSchemaV1<unknown>>\n> = Object.freeze({\n '*': {\n '~standard': {\n version: 1,\n vendor: 'a2',\n validate: (value: unknown) => {\n if (!isJsonTree(value)) {\n return {\n issues: [{ message: 'presence values must be plain JSON trees' }],\n }\n }\n if (JSON.stringify(value).length > PRESENCE_VALUE_MAX_BYTES) {\n return {\n issues: [\n {\n message: `presence values are capped at ${PRESENCE_VALUE_MAX_BYTES} bytes`,\n },\n ],\n }\n }\n return { value }\n },\n },\n } satisfies StandardSchemaV1<unknown>,\n})\n\n/**\n * The `a2.actor.state` envelope: provenance plus the state, held to a\n * plain JSON tree (a `Date` or `Map` in state fails loudly at commit,\n * never silently coerces in the log).\n */\nexport function actorStateSchema<S>(): StandardSchemaV1<ActorStatePayload<S>> {\n return schema(ACTOR_STATE_EVENT, (value) => {\n if (\n !isRecord(value) ||\n typeof value['event'] !== 'string' ||\n typeof value['message'] !== 'string'\n ) {\n return issue(`invalid ${ACTOR_STATE_EVENT} payload`)\n }\n const state = value['state']\n if (!isJsonTree(state)) {\n return issue('the actor state must be a plain JSON tree')\n }\n const clearIndex = value['clearIndex']\n if (\n clearIndex !== undefined &&\n (!Number.isSafeInteger(clearIndex) || (clearIndex as number) < 1)\n ) {\n return issue('the actor clearIndex must be a positive safe integer')\n }\n return {\n value: {\n state: state as S,\n event: value['event'],\n message: value['message'],\n ...(clearIndex === undefined\n ? {}\n : { clearIndex: clearIndex as number }),\n },\n }\n })\n}\n\nexport const actorFailedSchema: StandardSchemaV1<ActorFailedPayload> = schema(\n ACTOR_FAILED_EVENT,\n (value) => {\n if (\n !isRecord(value) ||\n typeof value['message'] !== 'string' ||\n typeof value['event'] !== 'string' ||\n typeof value['error'] !== 'string'\n ) {\n return issue(`invalid ${ACTOR_FAILED_EVENT} payload`)\n }\n return {\n value: {\n message: value['message'],\n event: value['event'],\n error: value['error'],\n },\n }\n },\n)\n\nexport type ActorReducerOptions<S> = {\n /** The actor's name — must match the server-side definition. */\n name: string\n /** The initial state — a plain JSON tree, the fold's seed. */\n state: S\n}\n\n/**\n * The library fold over an actor's state commits — last write wins.\n * Pure library code: a browser bundle folds an actor's live state with\n * no user code, so the actor definition itself stays server-only\n * (import the server module's type for typing, this reducer for data).\n */\nexport function actorReducer<S>(\n options: ActorReducerOptions<S>,\n): Reducer<ActorClientEventDefs<S>, S> {\n if (!isJsonTree(options.state)) {\n throw new TypeError(\n `actor '${options.name}': the initial state must be a plain JSON tree`,\n )\n }\n const events: ActorClientEventDefs<S> = {\n [ACTOR_STATE_EVENT]: actorStateSchema(),\n }\n return reducer({\n contract: createContract({ name: options.name, events }),\n name: ACTOR_REDUCER_NAME,\n initialState: options.state,\n }).fold((state, event) =>\n event.type === ACTOR_STATE_EVENT ? event.payload.state : state,\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"actor-shared-DFET5q16.js","names":["createContract"],"sources":["../src/actor-shared.ts"],"sourcesContent":["import { reducer } from './reducer.ts'\n/**\n * Internal shared vocabulary of the actor module — the reserved event\n * envelopes, the protocol and context types, the state fold. Not an\n * entry point: the public surfaces are experimental-a2/actor (server),\n * /actor/client, and /actor/react.\n *\n * An actor is a durable mailbox with memory: one session per instance,\n * events processed one at a time (a lane) unless a handler opts out\n * with `concurrent: true`, state materialized as `a2.actor.state`\n * events committed atomically with each handler's completion.\n */\n\nimport { internalContract as createContract } from './contract.ts'\nimport type { Reducer } from './reducer.ts'\nimport type { ScheduleTiming } from './server.ts'\nimport type { StandardSchemaV1 } from './standard-schema.ts'\n\n/** Event type of a committed state change — one per completed serial handler. */\nexport const ACTOR_STATE_EVENT = 'a2.actor.state'\n/** Event type of a refusal — a handler that answered by throwing NonRetriableError. */\nexport const ACTOR_FAILED_EVENT = 'a2.actor.failed'\n/** The per-instance lane every serial handler shares — the state's write lock. */\nexport const ACTOR_LANE = 'a2.actor'\n/** Reducer identity — bump when the fold's meaning changes. */\nexport const ACTOR_REDUCER_NAME = 'a2.actor.v1'\n\n/**\n * A handler answered by throwing `NonRetriableError`: the event\n * settled, the state did not change. Isomorphic — the server raises it\n * from calls and `fetch()` serializes it as a 409; the client's `call`\n * proxy revives it from that response.\n */\nexport class ActorRefusedError extends Error {\n /** The refused event's name. */\n readonly event: string\n /** The refused event's message id. */\n readonly messageId: string\n constructor(event: string, messageId: string, message: string) {\n super(message)\n this.name = 'ActorRefusedError'\n this.event = event\n this.messageId = messageId\n }\n}\n\n/**\n * The declaration an actor is defined over: its state shape and its\n * event vocabulary, as types. Core A2 contracts are schemas because\n * the wire is untrusted; actor protocols are types because your\n * server is trusted — same contract-first design language, dialed to\n * the trust level.\n */\nexport type ActorProtocol = {\n state: object\n events: object\n /**\n * Optional third vocabulary: presence field → value type. Ephemeral\n * audience state (who is here, cursors), replicated to subscribers\n * and never stored in the log. Declaring it requires `presence:\n * true` in the actor options — the wire bit types cannot carry.\n */\n presence?: object\n}\n\n/** The protocol's presence vocabulary, `never` when undeclared. */\nexport type ActorPresenceOf<D extends ActorProtocol> = D extends {\n presence: infer P extends object\n}\n ? P\n : never\n\n/** A `setPresence` patch: changed fields, `null` clears one. */\nexport type ActorPresenceValues<P> = {\n [F in keyof P & string]?: P[F] | null\n}\n\n/**\n * The replicated presence map: participant → field → latest value,\n * last write wins per field. Values are peer-authored — render them\n * like user input.\n */\nexport type ActorPresenceMap<P> = {\n [participant: string]: {\n [F in keyof P & string]?: { value: P[F]; seen: number; at: Date }\n }\n}\n\n/**\n * The typed self-send surface: one method per declared event —\n * `ctx.send.transfer({ ref, amount })`. Buffered, not immediate: every\n * send requested during a handler commits atomically with that\n * handler's completion (a serial handler's state commit, a concurrent\n * handler's settlement), riding core's returned-events semantics — so\n * a reserve-and-trigger can never half-happen, and re-runs converge on\n * the same deterministic message ids.\n */\nexport type ActorSend<E> = {\n readonly [K in keyof E]: {} extends E[K]\n ? (input?: E[K]) => void\n : (input: E[K]) => void\n}\n\n/**\n * Timing for a scheduled event: exactly `{ delay: '5d' }` or\n * `{ at: Date }` (core §6 semantics — relative delays anchor to the\n * triggering message's durable `createdAt`, so re-runs resolve the\n * same due time). `name` overrides the timer's identity — it defaults\n * to the target event name, scoped to the triggering message, so one\n * handler run gets one timer per target event unless named apart.\n */\nexport type ActorScheduleOptions = ScheduleTiming & { name?: string }\n\n/**\n * The typed durable-timer surface: one method per declared event —\n * `ctx.schedule.refund({ ref: ctx.id }, { delay: '1h' })`. Unlike\n * `send`, scheduling is immediate, not buffered: it awaits provider\n * acceptance at call time (a timer is provider-side, not a log row),\n * so a handler that schedules and then refuses has still armed the\n * timer. That is safe by the guarded-delivery idiom — the stale timer\n * fires into an idempotent no-op — but it is the one exception to\n * \"refusals are total\". Requires a configured `scheduler`.\n */\nexport type ActorScheduleSend<E> = {\n readonly [K in keyof E]: {} extends E[K]\n ? (input: E[K] | undefined, options: ActorScheduleOptions) => Promise<void>\n : (input: E[K], options: ActorScheduleOptions) => Promise<void>\n}\n\n/** What a serial handler receives alongside its typed input. */\nexport type ActorContext<D extends ActorProtocol> = {\n /** Mutable draft, committed atomically with the handler's completion. */\n state: D['state']\n /** The message id — stable across re-runs; the idempotency key for external I/O. */\n id: string\n /** Durable 1-based dispatch ordinal of this message. */\n attempt: number\n /** The ordinary A2 handler signal — fires on claim expiry or supersession. */\n signal: AbortSignal\n /** Typed buffered self-send — committed atomically with the state commit. */\n send: ActorSend<D['events']>\n /** Typed durable timers — immediate provider handoff, see ActorScheduleSend. */\n schedule: ActorScheduleSend<D['events']>\n}\n\n/**\n * What a `concurrent: true` handler receives. No draft — concurrent\n * handlers run off the lane, in parallel, so state is a snapshot read\n * (honest about staleness) and mutations happen by sending events\n * whose serial handlers decide against fresh state.\n */\nexport type ActorConcurrentContext<D extends ActorProtocol> = {\n /** The message id — stable across re-runs; the idempotency key for external I/O. */\n id: string\n /** Durable 1-based dispatch ordinal of this message. */\n attempt: number\n /** The ordinary A2 handler signal — fires on claim expiry or supersession. */\n signal: AbortSignal\n /** Snapshot read — observational; the world moves while this runs. */\n state(): Promise<{ state: D['state']; index: number }>\n /** Typed buffered self-send — committed atomically with settlement. */\n send: ActorSend<D['events']>\n /** Typed durable timers — immediate provider handoff, see ActorScheduleSend. */\n schedule: ActorScheduleSend<D['events']>\n}\n\nexport type ActorStatePayload<S> = {\n state: S\n /** The event whose handler committed this state. */\n event: string\n /** The message (invocation event) id this state answers. */\n message: string\n /** The session's latest clear marker when this state was read. */\n clearIndex?: number\n}\n\nexport type ActorFailedPayload = {\n /** The message (invocation event) id this refusal answers. */\n message: string\n event: string\n error: string\n}\n\n/** The event vocabulary a client needs to follow an actor's state. */\nexport type ActorClientEventDefs<S> = {\n 'a2.actor.state': StandardSchemaV1<ActorStatePayload<S>>\n}\n\ntype SchemaResult<T> = StandardSchemaV1.Result<T>\n\nconst issue = <T>(message: string): SchemaResult<T> => ({\n issues: [{ message }],\n})\n\nconst schema = <T>(\n label: string,\n parse: (value: unknown) => SchemaResult<T>,\n): StandardSchemaV1<T> => ({\n '~standard': {\n version: 1,\n vendor: 'a2',\n validate(value) {\n try {\n return parse(value)\n } catch (error) {\n return issue(\n `${label}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n },\n },\n})\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/** True for plain JSON trees — what the log can store without loss. */\nexport function isJsonTree(\n value: unknown,\n seen: Set<object> = new Set(),\n): boolean {\n if (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'boolean'\n ) {\n return true\n }\n if (typeof value === 'number') return Number.isFinite(value)\n if (typeof value !== 'object') return false\n if (seen.has(value)) return false\n seen.add(value)\n const valid = Array.isArray(value)\n ? value.every((item) => isJsonTree(item, seen))\n : Object.getPrototypeOf(value) === Object.prototype &&\n Object.values(value).every(\n (item) => item === undefined || isJsonTree(item, seen),\n )\n seen.delete(value)\n return valid\n}\n\n/** Per-field ceiling for presence values — presence is a cursor, not a document. */\nexport const PRESENCE_VALUE_MAX_BYTES = 8_192\n\n/**\n * The open presence vocabulary: the '*' catch-all validates any field\n * name against the JSON floor. The protocol types the vocabulary at\n * compile time; the route's `authorize` sees every set before\n * acceptance; renderers treat values as user input. Meaning has three\n * guards — this schema only owns the bytes.\n */\nexport const openPresenceDefs: Readonly<\n Record<string, StandardSchemaV1<unknown>>\n> = Object.freeze({\n '*': {\n '~standard': {\n version: 1,\n vendor: 'a2',\n validate: (value: unknown) => {\n if (!isJsonTree(value)) {\n return {\n issues: [{ message: 'presence values must be plain JSON trees' }],\n }\n }\n if (JSON.stringify(value).length > PRESENCE_VALUE_MAX_BYTES) {\n return {\n issues: [\n {\n message: `presence values are capped at ${PRESENCE_VALUE_MAX_BYTES} bytes`,\n },\n ],\n }\n }\n return { value }\n },\n },\n } satisfies StandardSchemaV1<unknown>,\n})\n\n/**\n * The `a2.actor.state` envelope: provenance plus the state, held to a\n * plain JSON tree (a `Date` or `Map` in state fails loudly at commit,\n * never silently coerces in the log).\n */\nexport function actorStateSchema<S>(): StandardSchemaV1<ActorStatePayload<S>> {\n return schema(ACTOR_STATE_EVENT, (value) => {\n if (\n !isRecord(value) ||\n typeof value['event'] !== 'string' ||\n typeof value['message'] !== 'string'\n ) {\n return issue(`invalid ${ACTOR_STATE_EVENT} payload`)\n }\n const state = value['state']\n if (!isJsonTree(state)) {\n return issue('the actor state must be a plain JSON tree')\n }\n const clearIndex = value['clearIndex']\n if (\n clearIndex !== undefined &&\n (!Number.isSafeInteger(clearIndex) || (clearIndex as number) < 1)\n ) {\n return issue('the actor clearIndex must be a positive safe integer')\n }\n return {\n value: {\n state: state as S,\n event: value['event'],\n message: value['message'],\n ...(clearIndex === undefined\n ? {}\n : { clearIndex: clearIndex as number }),\n },\n }\n })\n}\n\nexport const actorFailedSchema: StandardSchemaV1<ActorFailedPayload> = schema(\n ACTOR_FAILED_EVENT,\n (value) => {\n if (\n !isRecord(value) ||\n typeof value['message'] !== 'string' ||\n typeof value['event'] !== 'string' ||\n typeof value['error'] !== 'string'\n ) {\n return issue(`invalid ${ACTOR_FAILED_EVENT} payload`)\n }\n return {\n value: {\n message: value['message'],\n event: value['event'],\n error: value['error'],\n },\n }\n },\n)\n\nexport type ActorReducerOptions<S> = {\n /** The actor's name — must match the server-side definition. */\n name: string\n /** The initial state — a plain JSON tree, the fold's seed. */\n state: S\n}\n\n/**\n * The library fold over an actor's state commits — last write wins.\n * Pure library code: a browser bundle folds an actor's live state with\n * no user code, so the actor definition itself stays server-only\n * (import the server module's type for typing, this reducer for data).\n */\nexport function actorReducer<S>(\n options: ActorReducerOptions<S>,\n): Reducer<ActorClientEventDefs<S>, S> {\n if (!isJsonTree(options.state)) {\n throw new TypeError(\n `actor '${options.name}': the initial state must be a plain JSON tree`,\n )\n }\n const events: ActorClientEventDefs<S> = {\n [ACTOR_STATE_EVENT]: actorStateSchema(),\n }\n return reducer({\n contract: createContract({ name: options.name, events }),\n name: ACTOR_REDUCER_NAME,\n initialState: options.state,\n }).fold((state, event) =>\n event.type === ACTOR_STATE_EVENT ? event.payload.state : state,\n )\n}\n"],"mappings":"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"}
|
package/dist/actor.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as ActorProtocol, a as ActorHandle, b as ActorScheduleSend, c as ActorOptions, d as concurrent, f as ActorConcurrentContext, g as ActorPresenceValues, h as ActorPresenceOf, i as ActorFetchOptions, l as ActorSendOptions, m as ActorPresenceMap, n as ActorCallResult, o as ActorHandler, p as ActorContext, r as ActorDefinition, s as ActorOperation, t as ActorCallOptions, u as actor, v as ActorRefusedError, x as ActorSend, y as ActorScheduleOptions } from "./actor-
|
|
1
|
+
import { _ as ActorProtocol, a as ActorHandle, b as ActorScheduleSend, c as ActorOptions, d as concurrent, f as ActorConcurrentContext, g as ActorPresenceValues, h as ActorPresenceOf, i as ActorFetchOptions, l as ActorSendOptions, m as ActorPresenceMap, n as ActorCallResult, o as ActorHandler, p as ActorContext, r as ActorDefinition, s as ActorOperation, t as ActorCallOptions, u as actor, v as ActorRefusedError, x as ActorSend, y as ActorScheduleOptions } from "./actor-HnGmrOZZ.js";
|
|
2
2
|
export { ActorCallOptions, ActorCallResult, type ActorConcurrentContext, type ActorContext, ActorDefinition, ActorFetchOptions, ActorHandle, ActorHandler, ActorOperation, ActorOptions, type ActorPresenceMap, type ActorPresenceOf, type ActorPresenceValues, type ActorProtocol, ActorRefusedError, type ActorScheduleOptions, type ActorScheduleSend, type ActorSend, ActorSendOptions, actor, concurrent };
|
package/dist/actor.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { a as NonRetriableError, i as A2Error, n as isSessionCleared, r as sessionClearedError } from "./session-clear-
|
|
2
|
-
import { n as internalContract } from "./contract-
|
|
3
|
-
import {
|
|
4
|
-
import { c as parsePresenceSibling,
|
|
5
|
-
import {
|
|
1
|
+
import { a as NonRetriableError, i as A2Error, n as isSessionCleared, r as sessionClearedError } from "./session-clear-wHd5GCpZ.js";
|
|
2
|
+
import { n as internalContract } from "./contract-CWukt01d.js";
|
|
3
|
+
import { b as serverInternals } from "./internal-Bc4PvfHk.js";
|
|
4
|
+
import { c as parsePresenceSibling, n as createServer, o as setServerFetchHooks, s as sseResponse } from "./server-DvIB8kj5.js";
|
|
5
|
+
import { t as runtimeConsumer } from "./runtime-consumer-BWmht60z.js";
|
|
6
|
+
import { a as actorFailedSchema, c as isJsonTree, i as ActorRefusedError, l as openPresenceDefs, n as ACTOR_LANE, o as actorReducer, r as ACTOR_STATE_EVENT, s as actorStateSchema, t as ACTOR_FAILED_EVENT } from "./actor-shared-DFET5q16.js";
|
|
6
7
|
//#region src/actor.ts
|
|
7
8
|
/**
|
|
8
9
|
* experimental-a2/actor — durable actors over the log. Server-only
|
|
@@ -148,10 +149,11 @@ function actor(options) {
|
|
|
148
149
|
});
|
|
149
150
|
return send;
|
|
150
151
|
};
|
|
151
|
-
const
|
|
152
|
+
const concurrentRuns = /* @__PURE__ */ new Map();
|
|
153
|
+
const serialRuns = /* @__PURE__ */ new Map();
|
|
152
154
|
for (const [eventName, def] of handlersByEvent) {
|
|
153
155
|
if (def.kind === "concurrent") {
|
|
154
|
-
|
|
156
|
+
concurrentRuns.set(eventName, async (ctx) => {
|
|
155
157
|
const sends = [];
|
|
156
158
|
try {
|
|
157
159
|
await def.handle({
|
|
@@ -181,48 +183,45 @@ function actor(options) {
|
|
|
181
183
|
};
|
|
182
184
|
}
|
|
183
185
|
return sends;
|
|
184
|
-
};
|
|
186
|
+
});
|
|
185
187
|
continue;
|
|
186
188
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
await def.handle({
|
|
195
|
-
state: draft,
|
|
196
|
-
id: ctx.event.id,
|
|
197
|
-
attempt: ctx.attempt,
|
|
198
|
-
signal: ctx.signal,
|
|
199
|
-
send: makeSend(sends),
|
|
200
|
-
schedule: makeSchedule((taskName, timing, event) => ctx.session.schedule(taskName, timing, event))
|
|
201
|
-
}, ctx.event.payload);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
if (!(error instanceof NonRetriableError)) throw error;
|
|
204
|
-
const payload = {
|
|
205
|
-
message: ctx.event.id,
|
|
206
|
-
event: eventName,
|
|
207
|
-
error: describeError(error)
|
|
208
|
-
};
|
|
209
|
-
return {
|
|
210
|
-
type: ACTOR_FAILED_EVENT,
|
|
211
|
-
payload
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
const payload = {
|
|
189
|
+
serialRuns.set(eventName, async (ctx) => {
|
|
190
|
+
const { state, clearIndex } = await ctx.session.state(serverReducer, { through: "latest" });
|
|
191
|
+
const draft = structuredClone(state);
|
|
192
|
+
const sends = [];
|
|
193
|
+
try {
|
|
194
|
+
await def.handle({
|
|
215
195
|
state: draft,
|
|
216
|
-
|
|
196
|
+
id: ctx.event.id,
|
|
197
|
+
attempt: ctx.attempt,
|
|
198
|
+
signal: ctx.signal,
|
|
199
|
+
send: makeSend(sends),
|
|
200
|
+
schedule: makeSchedule((taskName, timing, event) => ctx.session.schedule(taskName, timing, event))
|
|
201
|
+
}, ctx.event.payload);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (!(error instanceof NonRetriableError)) throw error;
|
|
204
|
+
const payload = {
|
|
217
205
|
message: ctx.event.id,
|
|
218
|
-
|
|
206
|
+
event: eventName,
|
|
207
|
+
error: describeError(error)
|
|
219
208
|
};
|
|
220
|
-
return
|
|
221
|
-
type:
|
|
209
|
+
return {
|
|
210
|
+
type: ACTOR_FAILED_EVENT,
|
|
222
211
|
payload
|
|
223
|
-
}
|
|
212
|
+
};
|
|
224
213
|
}
|
|
225
|
-
|
|
214
|
+
const payload = {
|
|
215
|
+
state: draft,
|
|
216
|
+
event: eventName,
|
|
217
|
+
message: ctx.event.id,
|
|
218
|
+
...clearIndex === void 0 ? {} : { clearIndex }
|
|
219
|
+
};
|
|
220
|
+
return [...sends, {
|
|
221
|
+
type: ACTOR_STATE_EVENT,
|
|
222
|
+
payload
|
|
223
|
+
}];
|
|
224
|
+
});
|
|
226
225
|
}
|
|
227
226
|
const presenceEnabled = options.presence === true;
|
|
228
227
|
const contract = presenceEnabled ? internalContract({
|
|
@@ -233,9 +232,21 @@ function actor(options) {
|
|
|
233
232
|
name,
|
|
234
233
|
events
|
|
235
234
|
});
|
|
235
|
+
const serialTypes = [...serialRuns.keys()];
|
|
236
|
+
const concurrentTypes = [...concurrentRuns.keys()];
|
|
237
|
+
const consumers = [...serialTypes.length === 0 ? [] : [runtimeConsumer({
|
|
238
|
+
contract,
|
|
239
|
+
name: "a2.actor",
|
|
240
|
+
types: serialTypes,
|
|
241
|
+
lane: () => ACTOR_LANE
|
|
242
|
+
}).run((ctx) => serialRuns.get(ctx.event.type)(ctx))], ...concurrentTypes.length === 0 ? [] : [runtimeConsumer({
|
|
243
|
+
contract,
|
|
244
|
+
name: "a2.actor.concurrent",
|
|
245
|
+
types: concurrentTypes
|
|
246
|
+
}).run((ctx) => concurrentRuns.get(ctx.event.type)(ctx))]];
|
|
236
247
|
const server = createServer({
|
|
237
248
|
contract,
|
|
238
|
-
|
|
249
|
+
consumers,
|
|
239
250
|
...options.store ? { store: options.store } : {},
|
|
240
251
|
...options.scheduler ? { scheduler: options.scheduler } : {},
|
|
241
252
|
...options.telemetry ? { telemetry: options.telemetry } : {}
|