experimental-a2 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +360 -0
- package/dist/{actor-ohPC-81x.d.ts → actor-DBbe5xeE.d.ts} +21 -11
- package/dist/actor-DBbe5xeE.d.ts.map +1 -0
- package/dist/actor-client.d.ts +2 -5
- package/dist/actor-client.d.ts.map +1 -1
- package/dist/actor-client.js +13 -4
- package/dist/actor-client.js.map +1 -1
- package/dist/actor-react.d.ts +5 -3
- package/dist/actor-react.d.ts.map +1 -1
- package/dist/actor-react.js +20 -10
- package/dist/actor-react.js.map +1 -1
- package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
- package/dist/actor-shared-t7w8xIKP.js.map +1 -0
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +42 -23
- package/dist/actor.js.map +1 -1
- package/dist/{ai-CrLYwNEx.js → ai-UrWfKeuv.js} +966 -541
- package/dist/ai-UrWfKeuv.js.map +1 -0
- package/dist/ai-client.d.ts +27 -0
- package/dist/ai-client.d.ts.map +1 -0
- package/dist/ai-client.js +29 -0
- package/dist/ai-client.js.map +1 -0
- package/dist/ai-react.d.ts +96 -0
- package/dist/ai-react.d.ts.map +1 -0
- package/dist/ai-react.js +142 -0
- package/dist/ai-react.js.map +1 -0
- package/dist/ai-server.browser.js +2 -2
- package/dist/ai-server.browser.js.map +1 -1
- package/dist/ai-server.d.ts +80 -36
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +466 -265
- package/dist/ai-server.js.map +1 -1
- package/dist/{ai-HJ9fHfYI.d.ts → ai-vpGtcaCB.d.ts} +115 -101
- package/dist/ai-vpGtcaCB.d.ts.map +1 -0
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/{client-BYzHjkwU.js → client-COaiaDub.js} +447 -80
- package/dist/client-COaiaDub.js.map +1 -0
- package/dist/{client-CzyacQpJ.d.ts → client-qRnRUzBS.d.ts} +14 -13
- package/dist/client-qRnRUzBS.d.ts.map +1 -0
- package/dist/client.d.ts +4 -2
- package/dist/client.js +1 -1
- package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
- package/dist/contract-BYKASlCV.js.map +1 -0
- package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
- package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-BrLso5x_.d.ts} +7 -3
- package/dist/initial-snapshot-BrLso5x_.d.ts.map +1 -0
- package/dist/{internal-Dq2qYxou.js → internal-CbVvEwad.js} +23 -2
- package/dist/internal-CbVvEwad.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/react-BbJ-g6Uu.d.ts +86 -0
- package/dist/react-BbJ-g6Uu.d.ts.map +1 -0
- package/dist/react.d.ts +2 -122
- package/dist/react.js +107 -100
- package/dist/react.js.map +1 -1
- package/dist/reducer-BAXc8JyF.js +87 -0
- package/dist/reducer-BAXc8JyF.js.map +1 -0
- package/dist/{reducer-otzHjuJj.d.ts → reducer-BYu6DT7h.d.ts} +45 -29
- package/dist/reducer-BYu6DT7h.d.ts.map +1 -0
- package/dist/reducer-runtime-DQwz_J59.js +28 -0
- package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +3 -3
- package/dist/scheduler-qstash.d.ts.map +1 -1
- package/dist/scheduler-qstash.js +33 -10
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
- package/dist/scheduler-task-X1SToDjP.js.map +1 -0
- package/dist/scheduler-vercel.d.ts +4 -9
- package/dist/scheduler-vercel.d.ts.map +1 -1
- package/dist/scheduler-vercel.js +25 -44
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-DbD7IJVK.d.ts → server-CGM8WzRG.d.ts} +87 -26
- package/dist/server-CGM8WzRG.d.ts.map +1 -0
- package/dist/{server-BD5ckxZb.js → server-Rj64fNvX.js} +854 -202
- package/dist/server-Rj64fNvX.js.map +1 -0
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
- package/dist/session-clear-DGI4jTQ5.js.map +1 -0
- package/dist/session-clear-Dshpuhff.d.ts +12 -0
- package/dist/session-clear-Dshpuhff.d.ts.map +1 -0
- package/dist/{store-DyZM6fS5.d.ts → store-Gpkq7WBo.d.ts} +44 -3
- package/dist/store-Gpkq7WBo.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +182 -69
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-CmxUbV93.js → store-polling-4_y8lETm.js} +21 -4
- package/dist/store-polling-4_y8lETm.js.map +1 -0
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +399 -108
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-Q1C-k2dd.js} +3 -3
- package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-Q1C-k2dd.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.d.ts.map +1 -1
- package/dist/store-redis-http.js +293 -11
- package/dist/store-redis-http.js.map +1 -1
- package/dist/store-redis-notify-tqWNnMRu.js +2374 -0
- package/dist/store-redis-notify-tqWNnMRu.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +11 -11
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +166 -32
- package/dist/store-sqlite.js.map +1 -1
- package/dist/stream-buffer-Cx7AAPGq.js +23 -0
- package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
- package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-cZH-cibj.d.ts} +2 -2
- package/dist/telemetry-cZH-cibj.d.ts.map +1 -0
- package/dist/{wire-BO5wWCb1.js → wire-DjfHOvdW.js} +132 -10
- package/dist/wire-DjfHOvdW.js.map +1 -0
- package/docs/01-quickstart.mdx +2 -2
- package/docs/actors/01-introduction.mdx +4 -0
- package/docs/actors/04-routes.mdx +14 -1
- package/docs/concepts/01-contracts.mdx +95 -0
- package/docs/concepts/04-state.mdx +3 -3
- package/docs/guides/01-timers.mdx +22 -14
- package/docs/guides/02-cancellation.mdx +1 -1
- package/docs/guides/03-react.mdx +268 -328
- package/docs/guides/04-local-first.mdx +32 -73
- package/docs/guides/05-production.mdx +40 -27
- package/docs/guides/06-ai-agents.mdx +680 -580
- package/docs/guides/09-presence.mdx +3 -7
- package/docs/guides/10-transports.mdx +5 -5
- package/docs/index.mdx +10 -18
- package/docs/reference/01-api.mdx +662 -195
- package/docs/reference/02-errors.mdx +2 -1
- package/examples/playground/DEPLOY.md +52 -6
- package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
- package/examples/playground/app/a0/[appId]/page.tsx +48 -0
- package/examples/playground/app/a0/a0.css +818 -0
- package/examples/playground/app/a0/app-list.tsx +211 -0
- package/examples/playground/app/a0/app-view.tsx +55 -0
- package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
- package/examples/playground/app/a0/apps/[appId]/route.ts +23 -0
- package/examples/playground/app/a0/apps.test.ts +116 -0
- package/examples/playground/app/a0/apps.ts +87 -0
- package/examples/playground/app/a0/chat.tsx +364 -0
- package/examples/playground/app/a0/columns.tsx +109 -0
- package/examples/playground/app/a0/context-menu.tsx +116 -0
- package/examples/playground/app/a0/cookies.ts +46 -0
- package/examples/playground/app/a0/crash-on-request.ts +72 -0
- package/examples/playground/app/a0/crashes/route.ts +3 -0
- package/examples/playground/app/a0/crashes.test.ts +33 -0
- package/examples/playground/app/a0/crashes.ts +28 -0
- package/examples/playground/app/a0/devtools.tsx +164 -0
- package/examples/playground/app/a0/events/route.ts +6 -0
- package/examples/playground/app/a0/flags.ts +30 -0
- package/examples/playground/app/a0/layout.tsx +38 -0
- package/examples/playground/app/a0/list/route.ts +10 -0
- package/examples/playground/app/a0/model.test.ts +56 -0
- package/examples/playground/app/a0/model.ts +58 -0
- package/examples/playground/app/a0/page.tsx +14 -0
- package/examples/playground/app/a0/preview-pane.tsx +145 -0
- package/examples/playground/app/a0/preview.test.ts +53 -0
- package/examples/playground/app/a0/preview.ts +26 -0
- package/examples/playground/app/a0/sandbox-log.ts +69 -0
- package/examples/playground/app/a0/sandbox.ts +191 -0
- package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
- package/examples/playground/app/a0/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/server.ts +158 -0
- package/examples/playground/app/a0/session.ts +47 -0
- package/examples/playground/app/a0/shell.tsx +150 -0
- package/examples/playground/app/a0/short/events/route.ts +9 -0
- package/examples/playground/app/a0/splitter.tsx +120 -0
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
- package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
- package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
- package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
- package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
- package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
- package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
- package/examples/playground/app/agent/events/route.ts +5 -2
- package/examples/playground/app/agent/ingress.test.ts +98 -0
- package/examples/playground/app/agent/model-input.test.tsx +58 -0
- package/examples/playground/app/agent/model-input.tsx +115 -0
- package/examples/playground/app/agent/model.test.ts +73 -0
- package/examples/playground/app/agent/model.ts +58 -26
- package/examples/playground/app/agent/new-agent-session.tsx +37 -19
- package/examples/playground/app/agent/page.tsx +4 -4
- package/examples/playground/app/agent/server.ts +15 -14
- package/examples/playground/app/agent/session.ts +5 -6
- package/examples/playground/app/agent/telemetry.test.ts +168 -0
- package/examples/playground/app/agent/telemetry.ts +30 -0
- package/examples/playground/app/agent/trace.test.ts +23 -0
- package/examples/playground/app/agent/trace.ts +18 -0
- package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +11 -11
- package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
- package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
- package/examples/playground/app/canvas/model.ts +53 -53
- package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
- package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
- package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
- package/examples/playground/app/chat/[chatId]/session.ts +2 -19
- package/examples/playground/app/chat/agent/route.ts +9 -0
- package/examples/playground/app/chat/model.test.ts +46 -119
- package/examples/playground/app/chat/model.ts +82 -128
- package/examples/playground/app/chat/scheduler/route.ts +2 -2
- package/examples/playground/app/chat/server.test.ts +108 -0
- package/examples/playground/app/chat/server.ts +69 -73
- package/examples/playground/app/components/activity-feed.tsx +30 -1
- package/examples/playground/app/components/activity-history.test.tsx +35 -0
- package/examples/playground/app/components/demo-icons.tsx +89 -0
- package/examples/playground/app/components/demos.tsx +33 -0
- package/examples/playground/app/components/fast-link.test.tsx +58 -0
- package/examples/playground/app/components/fast-link.tsx +55 -0
- package/examples/playground/app/components/site-header.tsx +96 -0
- package/examples/playground/app/counter/counter-client.tsx +3 -3
- package/examples/playground/app/counter/model.test.ts +3 -3
- package/examples/playground/app/counter/model.ts +11 -21
- package/examples/playground/app/counter/page.tsx +2 -15
- package/examples/playground/app/counter/session.ts +2 -2
- package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
- package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
- package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
- package/examples/playground/app/documents/[documentId]/session.ts +2 -2
- package/examples/playground/app/documents/model.ts +27 -27
- package/examples/playground/app/globals.css +420 -207
- package/examples/playground/app/initial-state.test.tsx +54 -0
- package/examples/playground/app/layout.tsx +7 -23
- package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
- package/examples/playground/app/orders/[orderId]/session.ts +2 -2
- package/examples/playground/app/orders/model.ts +49 -49
- package/examples/playground/app/page.tsx +13 -102
- package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
- package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
- package/examples/playground/app/recovery/model.ts +30 -30
- package/examples/playground/instrumentation.ts +5 -0
- package/examples/playground/package.json +10 -2
- package/examples/playground/scripts/a0-template.ts +77 -0
- package/examples/playground/vercel.json +18 -0
- package/package.json +3 -1
- package/src/actor-client.ts +16 -4
- package/src/actor-react.ts +25 -11
- package/src/actor-shared.ts +21 -6
- package/src/actor.ts +76 -31
- package/src/ai-agent-actions.ts +219 -0
- package/src/ai-agent.ts +2343 -0
- package/src/ai-app-state.ts +91 -0
- package/src/ai-client-state.ts +39 -2
- 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 +94 -55
- package/src/ai-control.ts +2 -0
- package/src/ai-coordinator.ts +23 -22
- package/src/ai-message-base.ts +209 -0
- package/src/ai-message-projection.ts +382 -58
- package/src/ai-progress-batches.ts +26 -0
- package/src/ai-projector.ts +95 -14
- package/src/ai-react.ts +146 -0
- package/src/ai-server.browser.ts +2 -2
- package/src/ai-server.ts +20 -1951
- package/src/ai-stored-state.ts +48 -163
- package/src/ai.ts +343 -276
- package/src/bootstrap-stream.ts +72 -0
- package/src/bounded-cache.ts +55 -0
- package/src/client-render-session.ts +49 -0
- package/src/client.ts +688 -151
- package/src/contract.ts +92 -42
- package/src/deterministic-id.ts +37 -0
- package/src/equal-json.ts +32 -0
- package/src/errors.ts +13 -1
- package/src/index.ts +5 -1
- package/src/initial-snapshot.ts +39 -0
- package/src/internal.ts +45 -0
- package/src/postgres-notification-scope.ts +14 -3
- package/src/react.ts +171 -206
- package/src/redis-http-reply.ts +256 -0
- package/src/redis-http-stream.ts +73 -0
- package/src/redis-http-subscriptions.ts +13 -5
- package/src/reducer-runtime.ts +52 -0
- package/src/reducer.ts +92 -10
- package/src/scheduler-qstash.ts +49 -20
- package/src/scheduler-task.ts +34 -2
- package/src/scheduler-vercel.ts +32 -84
- package/src/server-fetch.ts +139 -35
- package/src/server.ts +957 -237
- package/src/session-clear.ts +23 -0
- package/src/session-socket.ts +154 -49
- package/src/sse.ts +71 -4
- package/src/store-memory.ts +220 -78
- package/src/store-polling.ts +18 -4
- package/src/store-postgres.ts +461 -114
- package/src/store-redis-core.ts +1260 -434
- package/src/store-redis-http.ts +18 -2
- package/src/store-redis-notify.ts +260 -85
- package/src/store-redis.ts +19 -9
- package/src/store-sqlite.ts +259 -45
- package/src/store.ts +46 -1
- package/src/stream-buffer.ts +43 -0
- package/src/telemetry.ts +13 -1
- package/src/wire.ts +241 -13
- package/dist/actor-ohPC-81x.d.ts.map +0 -1
- package/dist/actor-shared-B5tJfzt-.js.map +0 -1
- package/dist/ai-CrLYwNEx.js.map +0 -1
- package/dist/ai-HJ9fHfYI.d.ts.map +0 -1
- package/dist/client-BYzHjkwU.js.map +0 -1
- package/dist/client-CzyacQpJ.d.ts.map +0 -1
- package/dist/contract-CKRg_E4q.js.map +0 -1
- package/dist/errors-DCk6ch5n.js.map +0 -1
- package/dist/errors-DvhSXnxk.d.ts.map +0 -1
- package/dist/internal-Dq2qYxou.js.map +0 -1
- package/dist/react.d.ts.map +0 -1
- package/dist/reducer-DEMjEY_O.js +0 -29
- package/dist/reducer-DEMjEY_O.js.map +0 -1
- package/dist/reducer-otzHjuJj.d.ts.map +0 -1
- package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
- package/dist/server-BD5ckxZb.js.map +0 -1
- package/dist/server-DbD7IJVK.d.ts.map +0 -1
- package/dist/store-DyZM6fS5.d.ts.map +0 -1
- package/dist/store-polling-CmxUbV93.js.map +0 -1
- package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
- package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
- package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
- package/dist/validate-XKT4FSNn.js +0 -30
- package/dist/validate-XKT4FSNn.js.map +0 -1
- package/dist/wire-BO5wWCb1.js.map +0 -1
- package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
- package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
- package/examples/playground/app/agent/compaction-settings.ts +0 -49
- package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
- package/examples/playground/app/agent/compaction-timeline.ts +0 -208
package/src/react.ts
CHANGED
|
@@ -1,27 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
/**
|
|
3
|
-
* experimental-a2/react — React bindings over experimental-a2/client.
|
|
4
|
-
*
|
|
5
|
-
* `createReact({ client })` is a factory (like createContext): call it
|
|
6
|
-
* once in a `'use client'` module with a shared A2 client and export the
|
|
7
|
-
* bound pair. Server components import `SessionProvider` from that module
|
|
8
|
-
* as a client reference; client components import `useSession` from the
|
|
9
|
-
* same file. The shared client also makes imperative pushes and provider
|
|
10
|
-
* renders resolve the same live session. Everything is typed by the
|
|
11
|
-
* reducer value — no type arguments, and nothing here ever touches the
|
|
12
|
-
* machine module.
|
|
13
|
-
*
|
|
14
|
-
* The standalone `useSession(client, sessionId, options?)` is the
|
|
15
|
-
* provider-less primitive for client-first apps: identity-mapped
|
|
16
|
-
* handles, one leased stream per session, and an app-owned hydration
|
|
17
|
-
* input — the hook holds the stream until the app hands it a
|
|
18
|
-
* `{ state, index }` fold.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
2
|
import {
|
|
22
3
|
createContext,
|
|
23
4
|
createElement,
|
|
24
5
|
useContext,
|
|
6
|
+
use,
|
|
25
7
|
useEffect,
|
|
26
8
|
useMemo,
|
|
27
9
|
useSyncExternalStore,
|
|
@@ -37,10 +19,11 @@ import {
|
|
|
37
19
|
type PushResult,
|
|
38
20
|
type SessionClient,
|
|
39
21
|
type SessionClientPresence,
|
|
40
|
-
type
|
|
22
|
+
type SessionOptions,
|
|
41
23
|
} from './client.ts'
|
|
42
24
|
import type {
|
|
43
25
|
AppendInput,
|
|
26
|
+
Contract,
|
|
44
27
|
ContractEvent,
|
|
45
28
|
EventDefs,
|
|
46
29
|
PresenceDefs,
|
|
@@ -49,44 +32,24 @@ import type {
|
|
|
49
32
|
WithPresence,
|
|
50
33
|
} from './contract.ts'
|
|
51
34
|
import type { Reducer } from './reducer.ts'
|
|
35
|
+
import {
|
|
36
|
+
createRenderSession,
|
|
37
|
+
resolveInitialForReact,
|
|
38
|
+
} from './client-render-session.ts'
|
|
39
|
+
import { isInitialPromise, type InitialSnapshot } from './initial-snapshot.ts'
|
|
52
40
|
|
|
53
41
|
export type UseSessionOptions<S> = {
|
|
54
|
-
/** Overrides the client's `participant` for this session — for apps
|
|
55
|
-
* whose identity is only known at mount time. Bound once per
|
|
56
|
-
* session handle. */
|
|
57
42
|
participant?: string
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* its own route. Atomic on purpose — the fold and its frontier
|
|
62
|
-
* travel together or not at all. While `undefined` (the app's fetch
|
|
63
|
-
* has not landed), the hook holds the stream; when the value
|
|
64
|
-
* arrives, the session hydrates through the ordinary re-hydration
|
|
65
|
-
* seam and the stream connects at that frontier. To fold from the log's start
|
|
66
|
-
* deliberately, hand the fold's true starting point:
|
|
67
|
-
* `{ state: reducer.initialState, index: 0 }` — state at index 0 is
|
|
68
|
-
* the reducer's seed by definition, so the explicit replay needs no
|
|
69
|
-
* special vocabulary. There is no pending/ready echo on the result:
|
|
70
|
-
* whether the fold has been handed over is this option — the
|
|
71
|
-
* caller's own input — so the data layer's loading state IS the
|
|
72
|
-
* pending state.
|
|
73
|
-
*/
|
|
74
|
-
hydrate?: { state: S; index: number } | undefined
|
|
43
|
+
initial?: InitialSnapshot<S>
|
|
44
|
+
enabled?: boolean
|
|
45
|
+
suspend?: boolean
|
|
75
46
|
}
|
|
76
47
|
|
|
77
48
|
export type SessionProviderProps<D extends EventDefs, S> = {
|
|
78
|
-
/** Which session to subscribe to. */
|
|
79
49
|
sessionId: string
|
|
80
|
-
/** Base path of the route exposing GET (stream) and POST (push). */
|
|
81
50
|
api: string
|
|
82
|
-
|
|
83
|
-
initialState: S
|
|
84
|
-
/** The fold's frontier — the stream resumes exactly there. */
|
|
85
|
-
initialIndex: number
|
|
86
|
-
/** Server-rendered event history through `initialIndex`. */
|
|
51
|
+
initial?: InitialSnapshot<S>
|
|
87
52
|
initialEvents?: ContractEvent<D>[]
|
|
88
|
-
/** Overrides the client's `participant` for this session — for apps
|
|
89
|
-
* whose identity is only known at mount time. */
|
|
90
53
|
participant?: string
|
|
91
54
|
children?: ReactNode
|
|
92
55
|
}
|
|
@@ -96,11 +59,6 @@ export type BoundSessionProviderProps<D extends EventDefs, S> = Omit<
|
|
|
96
59
|
'api'
|
|
97
60
|
>
|
|
98
61
|
|
|
99
|
-
/**
|
|
100
|
-
* The presence members of the hook result — intersected in via
|
|
101
|
-
* `WithPresence`, so they exist exactly when the contract declares
|
|
102
|
-
* presence fields (the reducer is the client's typed handle on it).
|
|
103
|
-
*/
|
|
104
62
|
export type UseSessionPresence<P extends PresenceDefs> = {
|
|
105
63
|
/** The replicated presence map, this client included (local echo). */
|
|
106
64
|
presence: PresenceMap<P>
|
|
@@ -121,6 +79,8 @@ export type UseSessionResult<
|
|
|
121
79
|
> = WithPresence<P, UseSessionPresence<P>> & {
|
|
122
80
|
/** The live view, folded through the shared reducer. */
|
|
123
81
|
state: S
|
|
82
|
+
/** A state/index snapshot has been applied; independent of connection status. */
|
|
83
|
+
hydrated: boolean
|
|
124
84
|
/** The raw event feed — observed, seeded, or backscrolled. */
|
|
125
85
|
events: ContractEvent<D>[]
|
|
126
86
|
/** The stream frontier — `lastSeenIndex` for cancellation. */
|
|
@@ -149,219 +109,224 @@ export type UseSessionResult<
|
|
|
149
109
|
history: HistoryState
|
|
150
110
|
}
|
|
151
111
|
|
|
112
|
+
export type BoundSessionOptions<
|
|
113
|
+
D extends EventDefs,
|
|
114
|
+
S,
|
|
115
|
+
P extends PresenceDefs,
|
|
116
|
+
> = UseSessionOptions<S> & {
|
|
117
|
+
sessionId?: string
|
|
118
|
+
client?: A2Client<D, S, P>
|
|
119
|
+
}
|
|
120
|
+
|
|
152
121
|
export type A2React<
|
|
153
122
|
D extends EventDefs,
|
|
154
123
|
S,
|
|
155
124
|
P extends PresenceDefs = Record<never, never>,
|
|
156
|
-
V = S,
|
|
157
125
|
> = {
|
|
158
126
|
SessionProvider: (props: SessionProviderProps<D, S>) => ReactElement
|
|
159
|
-
useSession: (
|
|
127
|
+
useSession: (
|
|
128
|
+
options?: BoundSessionOptions<D, S, P>,
|
|
129
|
+
) => UseSessionResult<D, S, P>
|
|
160
130
|
}
|
|
161
131
|
|
|
162
132
|
export type BoundA2React<
|
|
163
133
|
D extends EventDefs,
|
|
164
134
|
S,
|
|
165
135
|
P extends PresenceDefs = Record<never, never>,
|
|
166
|
-
V = S,
|
|
167
136
|
> = {
|
|
168
137
|
SessionProvider: (props: BoundSessionProviderProps<D, S>) => ReactElement
|
|
169
|
-
useSession: (
|
|
138
|
+
useSession: (
|
|
139
|
+
options?: BoundSessionOptions<D, S, P>,
|
|
140
|
+
) => UseSessionResult<D, S, P>
|
|
170
141
|
}
|
|
171
142
|
|
|
172
143
|
export function createReact<
|
|
173
144
|
D extends EventDefs,
|
|
174
145
|
S,
|
|
175
146
|
P extends PresenceDefs = Record<never, never>,
|
|
176
|
-
|
|
177
|
-
>(options: { client: A2Client<D, S, P, V> }): BoundA2React<D, S, P, V>
|
|
147
|
+
>(options: { client: A2Client<D, S, P> }): BoundA2React<D, S, P>
|
|
178
148
|
export function createReact<
|
|
179
149
|
D extends EventDefs,
|
|
180
150
|
S,
|
|
181
151
|
P extends PresenceDefs = Record<never, never>,
|
|
182
|
-
|
|
183
|
-
>(options: { reducer: Reducer<D, S, P, V> }): A2React<D, S, P, V>
|
|
152
|
+
>(options: { contract: Contract<D, P, Reducer<D, S, P>> }): A2React<D, S, P>
|
|
184
153
|
export function createReact<
|
|
185
154
|
D extends EventDefs,
|
|
186
155
|
S,
|
|
187
156
|
P extends PresenceDefs = Record<never, never>,
|
|
188
|
-
V = S,
|
|
189
157
|
>(
|
|
190
|
-
options:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
...(participant === undefined ? {} : { participant }),
|
|
215
|
-
})
|
|
216
|
-
: (() => {
|
|
217
|
-
const clients = new Map<string, A2Client<D, S, P, V>>()
|
|
218
|
-
return (
|
|
219
|
-
sessionId: string,
|
|
220
|
-
api: string | undefined,
|
|
221
|
-
initialState: S,
|
|
222
|
-
initialIndex: number,
|
|
223
|
-
initialEvents: ContractEvent<D>[] | undefined,
|
|
224
|
-
participant: string | undefined,
|
|
225
|
-
) => {
|
|
226
|
-
if (api === undefined) {
|
|
227
|
-
throw new TypeError(
|
|
228
|
-
'SessionProvider requires api when createReact binds a reducer',
|
|
229
|
-
)
|
|
230
|
-
}
|
|
231
|
-
let client = clients.get(api)
|
|
232
|
-
if (!client) {
|
|
233
|
-
client = createClient({ reducer: options.reducer, api })
|
|
234
|
-
clients.set(api, client)
|
|
235
|
-
}
|
|
236
|
-
return client.session(sessionId, {
|
|
237
|
-
initialState,
|
|
238
|
-
initialIndex,
|
|
239
|
-
...(initialEvents === undefined ? {} : { initialEvents }),
|
|
240
|
-
...(participant === undefined ? {} : { participant }),
|
|
241
|
-
})
|
|
242
|
-
}
|
|
243
|
-
})()
|
|
158
|
+
options:
|
|
159
|
+
| { client: A2Client<D, S, P> }
|
|
160
|
+
| { contract: Contract<D, P, Reducer<D, S, P>> },
|
|
161
|
+
): BoundA2React<D, S, P> | A2React<D, S, P> {
|
|
162
|
+
type Scope = {
|
|
163
|
+
client: A2Client<D, S, P>
|
|
164
|
+
sessionId: string
|
|
165
|
+
settings: SessionOptions<D, S>
|
|
166
|
+
}
|
|
167
|
+
const Context = createContext<Scope | null>(null)
|
|
168
|
+
const clients = new Map<string, A2Client<D, S, P>>()
|
|
169
|
+
const resolveClient = (api: string | undefined): A2Client<D, S, P> => {
|
|
170
|
+
if ('client' in options) return options.client
|
|
171
|
+
if (api === undefined)
|
|
172
|
+
throw new TypeError(
|
|
173
|
+
'SessionProvider requires api when createReact binds a contract',
|
|
174
|
+
)
|
|
175
|
+
let client = clients.get(api)
|
|
176
|
+
if (!client) {
|
|
177
|
+
client = createClient({ contract: options.contract, api })
|
|
178
|
+
clients.set(api, client)
|
|
179
|
+
}
|
|
180
|
+
return client
|
|
181
|
+
}
|
|
244
182
|
|
|
245
183
|
function SessionProvider(
|
|
246
184
|
props: SessionProviderProps<D, S> | BoundSessionProviderProps<D, S>,
|
|
247
185
|
): ReactElement {
|
|
248
|
-
const {
|
|
249
|
-
sessionId,
|
|
250
|
-
initialState,
|
|
251
|
-
initialIndex,
|
|
252
|
-
initialEvents,
|
|
253
|
-
participant,
|
|
254
|
-
} = props
|
|
186
|
+
const { sessionId, initial, initialEvents, participant } = props
|
|
255
187
|
const api = 'api' in props ? props.api : undefined
|
|
256
|
-
const
|
|
257
|
-
() =>
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
[sessionId, api,
|
|
188
|
+
const scope = useMemo(
|
|
189
|
+
() => ({
|
|
190
|
+
client: resolveClient(api),
|
|
191
|
+
sessionId,
|
|
192
|
+
settings: {
|
|
193
|
+
...(initial === undefined ? {} : { initial }),
|
|
194
|
+
...(initialEvents === undefined ? {} : { initialEvents }),
|
|
195
|
+
...(participant === undefined ? {} : { participant }),
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
[sessionId, api, initial, initialEvents, participant],
|
|
267
199
|
)
|
|
268
|
-
|
|
269
|
-
return createElement(Context.Provider, { value: client }, props.children)
|
|
200
|
+
return createElement(Context.Provider, { value: scope }, props.children)
|
|
270
201
|
}
|
|
271
202
|
|
|
272
|
-
function useBoundSession(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
203
|
+
function useBoundSession(
|
|
204
|
+
overrides: BoundSessionOptions<D, S, P> = {},
|
|
205
|
+
): UseSessionResult<D, S, P> {
|
|
206
|
+
const scope = useContext(Context)
|
|
207
|
+
const client =
|
|
208
|
+
overrides.client ??
|
|
209
|
+
scope?.client ??
|
|
210
|
+
('client' in options ? options.client : undefined)
|
|
211
|
+
const sessionId = overrides.sessionId ?? scope?.sessionId
|
|
212
|
+
if (!client || sessionId === undefined)
|
|
213
|
+
throw new TypeError(
|
|
214
|
+
'provide client and sessionId through SessionProvider or hook options',
|
|
277
215
|
)
|
|
278
|
-
|
|
279
|
-
|
|
216
|
+
const inherits = client === scope?.client && sessionId === scope?.sessionId
|
|
217
|
+
const inherited = inherits ? scope.settings : undefined
|
|
218
|
+
const { initial, participant } = overrides
|
|
219
|
+
const settings = useMemo(
|
|
220
|
+
() => ({
|
|
221
|
+
...inherited,
|
|
222
|
+
...(initial === undefined ? {} : { initial }),
|
|
223
|
+
...(participant === undefined ? {} : { participant }),
|
|
224
|
+
}),
|
|
225
|
+
[inherited, initial, participant],
|
|
226
|
+
)
|
|
227
|
+
return useLiveSession({
|
|
228
|
+
client,
|
|
229
|
+
sessionId,
|
|
230
|
+
settings,
|
|
231
|
+
enabled: overrides.enabled ?? true,
|
|
232
|
+
suspend: overrides.suspend ?? false,
|
|
233
|
+
})
|
|
280
234
|
}
|
|
281
235
|
|
|
282
236
|
return { SessionProvider, useSession: useBoundSession }
|
|
283
237
|
}
|
|
284
238
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const
|
|
239
|
+
function useLiveSession<
|
|
240
|
+
D extends EventDefs,
|
|
241
|
+
S,
|
|
242
|
+
P extends PresenceDefs,
|
|
243
|
+
>(options: {
|
|
244
|
+
client: A2Client<D, S, P>
|
|
245
|
+
sessionId: string
|
|
246
|
+
settings: SessionOptions<D, S>
|
|
247
|
+
enabled: boolean
|
|
248
|
+
suspend: boolean
|
|
249
|
+
}): UseSessionResult<D, S, P> {
|
|
250
|
+
const { client, sessionId, settings, enabled, suspend } = options
|
|
251
|
+
const browserSession = useMemo(() => {
|
|
252
|
+
if (typeof window === 'undefined') {
|
|
253
|
+
if (isInitialPromise(settings.initial))
|
|
254
|
+
void settings.initial.catch(() => {})
|
|
255
|
+
return undefined
|
|
256
|
+
}
|
|
257
|
+
return client.session(sessionId, settings)
|
|
258
|
+
}, [client, sessionId, settings])
|
|
259
|
+
const promise = isInitialPromise(settings.initial)
|
|
260
|
+
? settings.initial
|
|
261
|
+
: undefined
|
|
262
|
+
const resolved = promise && suspend ? use(promise) : undefined
|
|
263
|
+
const renderInitial = promise ? resolved : settings.initial
|
|
264
|
+
const renderSession = useMemo(() => {
|
|
265
|
+
let session: SessionClient<D, S, P> | undefined
|
|
266
|
+
return () => {
|
|
267
|
+
session ??= createRenderSession({
|
|
268
|
+
client,
|
|
269
|
+
sessionId,
|
|
270
|
+
settings: { ...settings, initial: renderInitial },
|
|
271
|
+
})
|
|
272
|
+
return session
|
|
273
|
+
}
|
|
274
|
+
}, [client, sessionId, settings, renderInitial])
|
|
275
|
+
const session = browserSession ?? renderSession()
|
|
276
|
+
if (browserSession && promise && suspend)
|
|
277
|
+
resolveInitialForReact({
|
|
278
|
+
client,
|
|
279
|
+
session: browserSession,
|
|
280
|
+
source: promise,
|
|
281
|
+
value: resolved,
|
|
282
|
+
})
|
|
283
|
+
const serverSnapshot = useMemo(
|
|
284
|
+
() => () => renderSession().getSnapshot(),
|
|
285
|
+
[renderSession],
|
|
286
|
+
)
|
|
287
|
+
const snapshot = useSyncExternalStore(
|
|
310
288
|
session.subscribe,
|
|
311
289
|
session.getSnapshot,
|
|
312
|
-
|
|
290
|
+
serverSnapshot,
|
|
313
291
|
)
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
if (enabled) return session.connect()
|
|
294
|
+
}, [session, enabled])
|
|
314
295
|
return useMemo(() => {
|
|
315
|
-
// Runtime mirror of the WithPresence surface: the client attaches
|
|
316
|
-
// setPresence (and the snapshot carries presence) exactly when the
|
|
317
|
-
// reducer declares presence fields — no runtime-inert members.
|
|
318
296
|
const { setPresence } = session as Partial<SessionClientPresence<P>>
|
|
319
297
|
const { presence } = snapshot as Partial<{ presence: PresenceMap<P> }>
|
|
320
298
|
return {
|
|
321
|
-
|
|
322
|
-
events: snapshot.events,
|
|
323
|
-
index: snapshot.index,
|
|
324
|
-
connection: snapshot.connection,
|
|
299
|
+
...snapshot,
|
|
325
300
|
push: session.push,
|
|
326
301
|
reconnect: session.reconnect,
|
|
327
302
|
loadHistory: session.loadHistory,
|
|
328
|
-
history: snapshot.history,
|
|
329
303
|
...(setPresence && presence ? { presence, setPresence } : {}),
|
|
330
|
-
} as UseSessionResult<D,
|
|
331
|
-
}, [
|
|
304
|
+
} as UseSessionResult<D, S, P>
|
|
305
|
+
}, [session, snapshot])
|
|
332
306
|
}
|
|
333
307
|
|
|
334
|
-
/**
|
|
335
|
-
* The standalone, provider-less hook — for apps whose components reach
|
|
336
|
-
* sessions ad hoc (a sidebar of channel sessions, a user session read
|
|
337
|
-
* from a menu). Takes the shared client and a session id; the handle
|
|
338
|
-
* is identity-mapped, so every hook and provider mounting the same
|
|
339
|
-
* session shares one runtime and one leased stream. Hydration is
|
|
340
|
-
* app-owned: pass `hydrate` when your fetch lands; until then the
|
|
341
|
-
* stream is held, and your data layer's loading state is the pending
|
|
342
|
-
* state.
|
|
343
|
-
*/
|
|
344
308
|
export function useSession<
|
|
345
309
|
D extends EventDefs,
|
|
346
310
|
S,
|
|
347
311
|
P extends PresenceDefs = Record<never, never>,
|
|
348
|
-
V = S,
|
|
349
312
|
>(
|
|
350
|
-
client: A2Client<D, S, P
|
|
313
|
+
client: A2Client<D, S, P>,
|
|
351
314
|
sessionId: string,
|
|
352
|
-
options
|
|
353
|
-
): UseSessionResult<D,
|
|
354
|
-
const {
|
|
355
|
-
const
|
|
356
|
-
() =>
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
...(participant === undefined ? {} : { participant }),
|
|
362
|
-
}),
|
|
363
|
-
[client, sessionId, hydrate, participant],
|
|
315
|
+
options: UseSessionOptions<NoInfer<S>> = {},
|
|
316
|
+
): UseSessionResult<D, S, P> {
|
|
317
|
+
const { initial, participant } = options
|
|
318
|
+
const settings = useMemo(
|
|
319
|
+
() => ({
|
|
320
|
+
...(initial === undefined ? {} : { initial }),
|
|
321
|
+
...(participant === undefined ? {} : { participant }),
|
|
322
|
+
}),
|
|
323
|
+
[initial, participant],
|
|
364
324
|
)
|
|
365
|
-
|
|
366
|
-
|
|
325
|
+
return useLiveSession({
|
|
326
|
+
client,
|
|
327
|
+
sessionId,
|
|
328
|
+
settings,
|
|
329
|
+
enabled: options.enabled ?? true,
|
|
330
|
+
suspend: options.suspend ?? false,
|
|
331
|
+
})
|
|
367
332
|
}
|