experimental-a2 0.8.1 → 0.10.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/actor-client.d.ts +46 -0
  3. package/dist/actor-client.d.ts.map +1 -0
  4. package/dist/actor-client.js +54 -0
  5. package/dist/actor-client.js.map +1 -0
  6. package/dist/actor-react.d.ts +54 -0
  7. package/dist/actor-react.d.ts.map +1 -0
  8. package/dist/actor-react.js +79 -0
  9. package/dist/actor-react.js.map +1 -0
  10. package/dist/actor-shared-DI7J5upy.js +127 -0
  11. package/dist/actor-shared-DI7J5upy.js.map +1 -0
  12. package/dist/actor-shared-USo5MyuF.d.ts +136 -0
  13. package/dist/actor-shared-USo5MyuF.d.ts.map +1 -0
  14. package/dist/actor.browser.d.ts +1 -0
  15. package/dist/actor.browser.js +13 -0
  16. package/dist/actor.browser.js.map +1 -0
  17. package/dist/actor.d.ts +176 -0
  18. package/dist/actor.d.ts.map +1 -0
  19. package/dist/actor.js +437 -0
  20. package/dist/actor.js.map +1 -0
  21. package/dist/ai-server.d.ts +2 -2
  22. package/dist/ai-server.js +2 -2
  23. package/dist/ai.d.ts +2 -2
  24. package/dist/client.d.ts +14 -8
  25. package/dist/client.d.ts.map +1 -1
  26. package/dist/client.js +238 -58
  27. package/dist/client.js.map +1 -1
  28. package/dist/{errors-BQuJpe82.js → errors-DCk6ch5n.js} +16 -2
  29. package/dist/{errors-BQuJpe82.js.map → errors-DCk6ch5n.js.map} +1 -1
  30. package/dist/{idempotent-replay-DuqEkYA7.js → idempotent-replay-DVOlyYbx.js} +2 -2
  31. package/dist/{idempotent-replay-DuqEkYA7.js.map → idempotent-replay-DVOlyYbx.js.map} +1 -1
  32. package/dist/index.d.ts +16 -3
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +2 -2
  35. package/dist/react.d.ts +1 -1
  36. package/dist/{contract-jIfaR085.d.ts → reducer-DJKWm3cp.d.ts} +39 -39
  37. package/dist/reducer-DJKWm3cp.d.ts.map +1 -0
  38. package/dist/scheduler-qstash.d.ts +2 -2
  39. package/dist/scheduler-qstash.js +2 -2
  40. package/dist/scheduler-vercel.d.ts +2 -2
  41. package/dist/scheduler-vercel.js +1 -1
  42. package/dist/{server-DjPhHnbI.d.ts → server-DgCrSuhB.d.ts} +5 -3
  43. package/dist/server-DgCrSuhB.d.ts.map +1 -0
  44. package/dist/{server-B2XNevQA.js → server-DlLyvaSH.js} +140 -81
  45. package/dist/server-DlLyvaSH.js.map +1 -0
  46. package/dist/server.d.ts +3 -3
  47. package/dist/server.js +1 -1
  48. package/dist/{store-RJO35BMj.d.ts → store-DGHeBtIQ.d.ts} +2 -2
  49. package/dist/{store-RJO35BMj.d.ts.map → store-DGHeBtIQ.d.ts.map} +1 -1
  50. package/dist/store-memory.d.ts +1 -1
  51. package/dist/store-memory.js +2 -2
  52. package/dist/store-postgres.d.ts +1 -1
  53. package/dist/store-postgres.js +2 -2
  54. package/dist/{store-redis-core-DT01r4GZ.js → store-redis-core-z-ykbyMg.js} +3 -3
  55. package/dist/{store-redis-core-DT01r4GZ.js.map → store-redis-core-z-ykbyMg.js.map} +1 -1
  56. package/dist/store-redis-http.d.ts +1 -1
  57. package/dist/store-redis-http.js +2 -2
  58. package/dist/store-redis.d.ts +1 -1
  59. package/dist/store-redis.js +2 -2
  60. package/dist/store-sqlite.d.ts +1 -1
  61. package/dist/store-sqlite.js +2 -2
  62. package/dist/{wire-B6te_wns.js → wire--yji6mO3.js} +2 -2
  63. package/dist/{wire-B6te_wns.js.map → wire--yji6mO3.js.map} +1 -1
  64. package/docs/actors/01-introduction.mdx +189 -0
  65. package/docs/actors/02-concurrency.mdx +154 -0
  66. package/docs/actors/03-timers.mdx +120 -0
  67. package/docs/actors/04-routes.mdx +352 -0
  68. package/docs/actors/meta.ts +1 -0
  69. package/docs/concepts/meta.ts +1 -0
  70. package/docs/guides/10-transports.mdx +72 -22
  71. package/docs/guides/meta.ts +1 -0
  72. package/docs/index.mdx +3 -0
  73. package/docs/reference/01-api.mdx +30 -12
  74. package/docs/reference/02-errors.mdx +33 -0
  75. package/docs/reference/meta.ts +1 -0
  76. package/examples/playground/app/page.tsx +10 -1
  77. package/examples/playground/app/vault/[vaultId]/route.ts +19 -0
  78. package/examples/playground/app/vault/page.tsx +12 -0
  79. package/examples/playground/app/vault/server.ts +9 -0
  80. package/examples/playground/app/vault/vault-client.tsx +124 -0
  81. package/examples/playground/app/vault/vault.test.ts +147 -0
  82. package/examples/playground/app/vault/vault.ts +119 -0
  83. package/examples/playground/package.json +1 -1
  84. package/package.json +7 -1
  85. package/src/actor-client.ts +132 -0
  86. package/src/actor-react.ts +143 -0
  87. package/src/actor-shared.ts +356 -0
  88. package/src/actor.browser.ts +12 -0
  89. package/src/actor.ts +914 -0
  90. package/src/client.ts +341 -88
  91. package/src/errors.ts +15 -0
  92. package/src/index.ts +1 -1
  93. package/src/server-fetch.ts +51 -23
  94. package/src/server.ts +13 -3
  95. package/src/session-socket.ts +216 -81
  96. package/dist/contract-jIfaR085.d.ts.map +0 -1
  97. package/dist/server-B2XNevQA.js.map +0 -1
  98. package/dist/server-DjPhHnbI.d.ts.map +0 -1
@@ -61,43 +61,6 @@ declare namespace StandardSchemaV1 {
61
61
  export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
62
62
  }
63
63
  //#endregion
64
- //#region src/reducer.d.ts
65
- /**
66
- * A pure fold over a session's events — `(state, event) => state` —
67
- * derived from a contract via `contract.reducer(options).fold(fn)`.
68
- *
69
- * The name is the reducer's identity. Cached folds (server snapshots,
70
- * the browser cache) are keyed by it, which makes it the invalidation
71
- * knob too: changed the fold's logic? Change the name. Snapshots under
72
- * the old name are ignored and the next read refolds from raw events.
73
- * See docs/concepts/04-state.mdx.
74
- */
75
- type Reducer<D extends EventDefs = EventDefs, S = unknown, P extends PresenceDefs = Record<never, never>> = {
76
- /** Identity and cache key. Rename when the fold's logic changes. */
77
- readonly name: string;
78
- /** The contract's vocabulary — types every event in the fold. */
79
- readonly events: Readonly<D>;
80
- /**
81
- * The contract's presence vocabulary — carried because the reducer
82
- * is the client's typed handle on the contract. The fold never sees
83
- * presence.
84
- */
85
- readonly presence: Readonly<P>;
86
- readonly fold: (state: S, event: ContractEvent<D>) => S;
87
- readonly initialState: S;
88
- /**
89
- * When present: `initialState` was validated against it at
90
- * definition, and any cached fold a2 rehydrates (server snapshots,
91
- * the browser cache) is checked against it — failing snapshots are
92
- * discarded and refolded, catching shape drift a stale name can't.
93
- */
94
- readonly stateSchema: StandardSchemaV1<unknown, S> | undefined;
95
- };
96
- /** The second step of `contract.reducer` — see the method's doc. */
97
- type ReducerBuilder<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = {
98
- fold(fold: (state: S, event: ContractEvent<D>) => S): Reducer<D, S, P>;
99
- };
100
- //#endregion
101
64
  //#region src/contract.d.ts
102
65
  /** The event vocabulary shape: event name → payload schema. */
103
66
  type EventDefs = Record<string, StandardSchemaV1>;
@@ -215,5 +178,42 @@ declare function contract<D extends EventDefs, P extends PresenceDefs = Record<n
215
178
  presence?: P;
216
179
  }): Contract<D, P>;
217
180
  //#endregion
218
- export { EventDefs as a, PresencePatch as c, WithPresence as d, contract as f, StandardSchemaV1 as h, EventBatchFactory as i, PresenceSnapshot as l, ReducerBuilder as m, Contract as n, PresenceDefs as o, Reducer as p, ContractEvent as r, PresenceMap as s, AppendInput as t, ReducerOptions as u };
219
- //# sourceMappingURL=contract-jIfaR085.d.ts.map
181
+ //#region src/reducer.d.ts
182
+ /**
183
+ * A pure fold over a session's events — `(state, event) => state` —
184
+ * derived from a contract via `contract.reducer(options).fold(fn)`.
185
+ *
186
+ * The name is the reducer's identity. Cached folds (server snapshots,
187
+ * the browser cache) are keyed by it, which makes it the invalidation
188
+ * knob too: changed the fold's logic? Change the name. Snapshots under
189
+ * the old name are ignored and the next read refolds from raw events.
190
+ * See docs/concepts/04-state.mdx.
191
+ */
192
+ type Reducer<D extends EventDefs = EventDefs, S = unknown, P extends PresenceDefs = Record<never, never>> = {
193
+ /** Identity and cache key. Rename when the fold's logic changes. */
194
+ readonly name: string;
195
+ /** The contract's vocabulary — types every event in the fold. */
196
+ readonly events: Readonly<D>;
197
+ /**
198
+ * The contract's presence vocabulary — carried because the reducer
199
+ * is the client's typed handle on the contract. The fold never sees
200
+ * presence.
201
+ */
202
+ readonly presence: Readonly<P>;
203
+ readonly fold: (state: S, event: ContractEvent<D>) => S;
204
+ readonly initialState: S;
205
+ /**
206
+ * When present: `initialState` was validated against it at
207
+ * definition, and any cached fold a2 rehydrates (server snapshots,
208
+ * the browser cache) is checked against it — failing snapshots are
209
+ * discarded and refolded, catching shape drift a stale name can't.
210
+ */
211
+ readonly stateSchema: StandardSchemaV1<unknown, S> | undefined;
212
+ };
213
+ /** The second step of `contract.reducer` — see the method's doc. */
214
+ type ReducerBuilder<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = {
215
+ fold(fold: (state: S, event: ContractEvent<D>) => S): Reducer<D, S, P>;
216
+ };
217
+ //#endregion
218
+ export { ContractEvent as a, PresenceDefs as c, PresenceSnapshot as d, ReducerOptions as f, StandardSchemaV1 as h, Contract as i, PresenceMap as l, contract as m, ReducerBuilder as n, EventBatchFactory as o, WithPresence as p, AppendInput as r, EventDefs as s, Reducer as t, PresencePatch as u };
219
+ //# sourceMappingURL=reducer-DJKWm3cp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer-DJKWm3cp.d.ts","names":[],"sources":["../src/standard-schema.ts","../src/contract.ts","../src/reducer.ts"],"mappings":";;;;;;;;UAQiB,iBAAiB,iBAAiB,SAAS;;WAEjD,aAAa,iBAAiB,MAAM,OAAO;;kBAG7B;;mBAEN,MAAM,iBAAiB,SAAS;;aAEtC;;aAEA;;aAEA,WACP,mBACG,OAAO,UAAU,QAAQ,OAAO;;aAE5B,QAAQ,MAAM,OAAO;;;cAIpB,OAAO,UAAU,cAAc,UAAU;;mBAGpC,cAAc;;aAEpB,OAAO;;aAEP;;;mBAIM;;aAEN,QAAQ,cAAc;;;mBAIhB;;aAEN;;aAEA,OAAO,cAAc,cAAc;;;mBAI7B;;aAEN,KAAK;;;mBAIC,MAAM,iBAAiB,SAAS;;aAEtC,OAAO;;aAEP,QAAQ;;;cAIP,WAAW,eAAe,oBAAoB,YACxD;;cAIU,YAAY,eAAe,oBAAoB,YACzD;;;;;KC1DQ,YAAY,eAAe;;KAG3B,eAAe,eAAe;;KAG9B,cACV,UAAU,WACV,gBAAgB,mBAAmB,iBAElC,WAAW;EACV;EACA,MAAM;EACN,SAAS,iBAAiB,YAAY,EAAE;EACxC;EACA;EACA,WAAW;KAEb;;KAGU,YAAY,UAAU,gBAC/B,WAAW;EACV,MAAM;EACN,SAAS,iBAAiB,WAAW,EAAE;EACvC;WAEI;KAEI,kBAAkB,UAAU,oBAChC,mBAAmB,YAAY,SAElC,QAAQ,MACR;;;;;;KAOO,cAAc,UAAU,eAAe;EACjD;EACA,WACG,WAAW,cAAc,iBAAiB,YAAY,EAAE;EAE3D;EACA,IAAI;;;;;;;;KASM,YAAY,UAAU,eAAe;GAE5C,yBAAA,WAAW;IACV,OAAO,iBAAiB,YAAY,EAAE;IACtC;IACA,IAAI;;;;;;;;KAUE,iBAAiB,UAAU,eAAe;EACpD,UAAU,YAAY;;;;;;;KAQZ,aACV,UAAU,cACV,iBACQ,4BAA4B;KAE1B,eAAe,MAAM;;;;;EAK/B;;;;;;EAMA,cAAc;;;;;;EAMd,cAAc,iBAAiB,MAAM;;KAG3B,SACV,UAAU,YAAY,WACtB,UAAU,eAAe;;WAGhB;;WAEA,QAAQ,SAAS;;WAEjB,UAAU,SAAS;;WAEnB,OAAO,kBAAkB;;;;;;;;;;;EAWlC,QAAQ,MAAM,IAAI,MAChB,SAAS,eAAe,MAAM,KAC7B,eAAe,GAAG,GAAG;;;;;;;iBAQV,SACd,UAAU,WACV,UAAU,eAAe,sBACzB;;EAEA;;EAEA,QAAQ;;;;;;EAMR,WAAW;IACT,SAAS,GAAG;;;;;;;;;;;;;KCxJJ,QACV,UAAU,YAAY,WACtB,aACA,UAAU,eAAe;;WAGhB;;WAEA,QAAQ,SAAS;;;;;;WAMjB,UAAU,SAAS;WACnB,OAAO,OAAO,GAAG,OAAO,cAAc,OAAO;WAC7C,cAAc;;;;;;;WAOd,aAAa,0BAA0B;;;KAItC,eACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,KAAK,OAAO,OAAO,GAAG,OAAO,cAAc,OAAO,IAAI,QAAQ,GAAG,GAAG"}
@@ -1,5 +1,5 @@
1
- import { r as Clock } from "./store-RJO35BMj.js";
2
- import { T as SchedulerTask, t as A2Scheduler } from "./server-DjPhHnbI.js";
1
+ import { r as Clock } from "./store-DGHeBtIQ.js";
2
+ import { T as SchedulerTask, t as A2Scheduler } from "./server-DgCrSuhB.js";
3
3
  //#region src/scheduler-qstash.d.ts
4
4
  type QStashStoreOutage = {
5
5
  version: 1;
@@ -1,6 +1,6 @@
1
- import { t as A2Error } from "./errors-BQuJpe82.js";
1
+ import { t as A2Error } from "./errors-DCk6ch5n.js";
2
2
  import { d as classifySchedulerSendFailure, g as serverInternals, t as DRAIN_TIMINGS, u as assertSchedulerTargets } from "./internal-DRXJ56EI.js";
3
- import { i as platformVercelOidcToken, n as deliverSchedulerAppend } from "./server-B2XNevQA.js";
3
+ import { n as deliverSchedulerAppend, o as platformVercelOidcToken } from "./server-DlLyvaSH.js";
4
4
  import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
5
5
  import { n as SYSTEM_CLOCK } from "./store-N8PXxDAS.js";
6
6
  import { t as normalizeSchedulerTask } from "./scheduler-task-BpzhPnRS.js";
@@ -1,5 +1,5 @@
1
- import { r as Clock } from "./store-RJO35BMj.js";
2
- import { T as SchedulerTask, t as A2Scheduler } from "./server-DjPhHnbI.js";
1
+ import { r as Clock } from "./store-DGHeBtIQ.js";
2
+ import { T as SchedulerTask, t as A2Scheduler } from "./server-DgCrSuhB.js";
3
3
  //#region src/scheduler-vercel.d.ts
4
4
  /**
5
5
  * The "not settled yet" signal for a stalled handler — an expected state,
@@ -1,5 +1,5 @@
1
1
  import { d as classifySchedulerSendFailure, g as serverInternals, t as DRAIN_TIMINGS, u as assertSchedulerTargets } from "./internal-DRXJ56EI.js";
2
- import { n as deliverSchedulerAppend } from "./server-B2XNevQA.js";
2
+ import { n as deliverSchedulerAppend } from "./server-DlLyvaSH.js";
3
3
  import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
4
4
  import { n as SYSTEM_CLOCK } from "./store-N8PXxDAS.js";
5
5
  import { t as normalizeSchedulerTask } from "./scheduler-task-BpzhPnRS.js";
@@ -1,5 +1,5 @@
1
- import { a as EventDefs, c as PresencePatch, d as WithPresence, l as PresenceSnapshot, n as Contract, o as PresenceDefs, p as Reducer, r as ContractEvent, s as PresenceMap, t as AppendInput } from "./contract-jIfaR085.js";
2
- import { t as A2Store } from "./store-RJO35BMj.js";
1
+ import { a as ContractEvent, c as PresenceDefs, d as PresenceSnapshot, i as Contract, l as PresenceMap, p as WithPresence, r as AppendInput, s as EventDefs, t as Reducer, u as PresencePatch } from "./reducer-DJKWm3cp.js";
2
+ import { t as A2Store } from "./store-DGHeBtIQ.js";
3
3
  import { i as A2Telemetry } from "./telemetry-CpeclqB2.js";
4
4
  //#region src/session-socket.d.ts
5
5
  /**
@@ -56,6 +56,8 @@ type UpgradeWebSocket = (attach: (socket: A2Socket) => void) => Response | Promi
56
56
  type ServerFetchOptions<D extends EventDefs = EventDefs, P extends PresenceDefs = Record<never, never>> = {
57
57
  authorize?: (operation: A2Operation<D, P>) => boolean | Promise<boolean>;
58
58
  upgradeWebSocket?: UpgradeWebSocket;
59
+ /** Allow one upgraded connection to select more than one session. */
60
+ multiplexWebSocket?: boolean;
59
61
  };
60
62
  //#endregion
61
63
  //#region src/scheduler-task.d.ts
@@ -279,4 +281,4 @@ type ServerOptions<D extends EventDefs, P extends PresenceDefs = Record<never, n
279
281
  declare function createServer<D extends EventDefs, P extends PresenceDefs = Record<never, never>>(options: ServerOptions<D, P>): A2Server<D, P>;
280
282
  //#endregion
281
283
  export { UpgradeWebSocket as A, SchedulerAppendTask as C, A2PushEvent as D, A2Operation as E, A2PushPresence as O, ScheduledEvent as S, SchedulerTask as T, SessionPresence as _, Handler as a, createServer as b, HandlerEntry as c, ScheduleDelay as d, ScheduleTiming as f, SessionDispatch as g, SessionAppend as h, DrainableServer as i, A2Socket as j, ServerFetchOptions as k, Lane as l, Session as m, A2Server as n, HandlerAppend as o, ServerOptions as p, AbortSpec as r, HandlerContext as s, A2Scheduler as t, LaneContext as u, SessionSchedule as v, SchedulerDrainTask as w, deliverSchedulerAppend as x, StateOptions as y };
282
- //# sourceMappingURL=server-DjPhHnbI.d.ts.map
284
+ //# sourceMappingURL=server-DgCrSuhB.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-DgCrSuhB.d.ts","names":[],"sources":["../src/session-socket.ts","../src/server-fetch.ts","../src/scheduler-task.ts","../src/server.ts"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { n as validateSync } from "./validate-XKT4FSNn.js";
2
- import { n as asStoreUnavailable, t as A2Error } from "./errors-BQuJpe82.js";
2
+ import { n as NonRetriableError, r as asStoreUnavailable, t as A2Error } from "./errors-DCk6ch5n.js";
3
3
  import { c as STREAM_TIMINGS, g as serverInternals, h as nullProtoRecord, i as POLL_TIMINGS, m as markSchedulerSendFailure, o as RESERVED_PARTICIPANT_IDS, s as SOCKET_TIMINGS, t as DRAIN_TIMINGS } from "./internal-DRXJ56EI.js";
4
4
  import { t as defaultSleep } from "./store-polling-6DW7F1DT.js";
5
- import { _ as socketErrorAckFor, a as errorToWire, b as socketUnsubscribedFor, g as socketAckFor, h as presenceSnapshotToWire, i as errorStatus, n as asA2Error, p as presencePatchToWire, s as eventToWire, t as SOCKET_PING_FRAME, v as socketFrameFor, y as socketSubscribedFor } from "./wire-B6te_wns.js";
5
+ import { _ as socketErrorAckFor, a as errorToWire, b as socketUnsubscribedFor, g as socketAckFor, h as presenceSnapshotToWire, i as errorStatus, n as asA2Error, p as presencePatchToWire, s as eventToWire, t as SOCKET_PING_FRAME, v as socketFrameFor, y as socketSubscribedFor } from "./wire--yji6mO3.js";
6
6
  import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
7
7
  //#region src/platform.ts
8
8
  const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
@@ -116,10 +116,9 @@ async function parsePushBody(req) {
116
116
  /**
117
117
  * The A2 socket protocol's server half (specs/a2-api.md §13): the
118
118
  * per-socket shell — teardown, backpressure, heartbeat, the malformed
119
- * budget, the in-flight bound — and `sessionsSocket`, the multiplexed
120
- * frame handler riding it. Internal module: `handle`'s upgrade lane is
121
- * the sanctioned mount; only `A2Socket` (the structural socket the
122
- * platform hands over) is re-exported as public surface.
119
+ * budget, and the in-flight bound. `sessionSocket` binds one session to
120
+ * one socket; `sessionsSocket` is the opt-in multiplexed handler. Only
121
+ * `A2Socket`, the structural socket the platform hands over, is public.
123
122
  */
124
123
  const messageText = (data) => {
125
124
  if (typeof data === "string") return data;
@@ -222,10 +221,101 @@ function socketShell(socket, release, onFrame, deadlineAt) {
222
221
  track
223
222
  };
224
223
  }
224
+ const socketSubscription = (target, startAfter, presence) => ({
225
+ target,
226
+ iterator: (presence ? target.stream({
227
+ startAfter,
228
+ presence: true
229
+ }) : target.stream({ startAfter }))[Symbol.asyncIterator](),
230
+ stopped: false
231
+ });
225
232
  const stopSubscription = (subscription) => {
226
233
  subscription.stopped = true;
227
234
  subscription.iterator.return?.()?.catch(() => {});
228
235
  };
236
+ const pumpSubscription = async (shell, subscription, frameFor) => {
237
+ for (;;) {
238
+ while (!shell.torn() && !subscription.stopped && shell.buffered() > SOCKET_TIMINGS.highWaterMarkBytes) await new Promise((wake) => setTimeout(wake, SOCKET_TIMINGS.resumePollMs));
239
+ if (shell.torn() || subscription.stopped) return;
240
+ const { value, done } = await subscription.iterator.next();
241
+ if (shell.torn() || subscription.stopped || done) return;
242
+ shell.send(frameFor(value));
243
+ }
244
+ };
245
+ const handleSocketPush = async (shell, target, sessionId, frame, req, ackSessionId, gatePush) => {
246
+ try {
247
+ const events = parsePushEvents(frame["events"]);
248
+ const denial = await gatePush?.(sessionId, events, void 0) ?? null;
249
+ if (denial !== null) {
250
+ shell.send(socketErrorAckFor(req, denial, ackSessionId));
251
+ return;
252
+ }
253
+ const appended = await target.append(...events);
254
+ shell.send(socketAckFor(req, appended, ackSessionId));
255
+ } catch (error) {
256
+ shell.send(socketErrorAckFor(req, asA2Error(error), ackSessionId));
257
+ }
258
+ };
259
+ const applySocketPresence = async (shell, target, sessionId, frame, gatePush) => {
260
+ if (typeof target.setPresence !== "function") {
261
+ shell.malformedFrame("presence frame on a presence-less session");
262
+ return;
263
+ }
264
+ let patch;
265
+ try {
266
+ patch = parsePresenceSibling(frame);
267
+ } catch {
268
+ shell.malformedFrame("invalid presence frame");
269
+ return;
270
+ }
271
+ if (!patch) return;
272
+ try {
273
+ if ((await gatePush?.(sessionId, [], patch) ?? null) !== null) return;
274
+ await target.setPresence(patch);
275
+ } catch (error) {
276
+ const failure = asA2Error(error);
277
+ if (failure.code === "INVALID_PAYLOAD" || failure.code === "UNKNOWN_PRESENCE_FIELD") {
278
+ shell.malformedFrame("invalid presence frame");
279
+ return;
280
+ }
281
+ shell.shutdown(1011, "internal error");
282
+ }
283
+ };
284
+ function sessionSocket(sessionId, session, socket, options) {
285
+ const target = session;
286
+ const subscription = socketSubscription(target, options?.startAfter ?? 0, options?.presence === true);
287
+ const handleFrame = (frame) => {
288
+ switch (frame["kind"]) {
289
+ case "push": {
290
+ const req = frame["req"];
291
+ if (typeof req !== "number") {
292
+ shell.malformedFrame("push frame without a numeric req");
293
+ return;
294
+ }
295
+ if (frame["sessionId"] !== void 0) {
296
+ shell.send(socketErrorAckFor(req, new A2Error("INVALID_PAYLOAD", "sessionId is not allowed on a single-session socket")));
297
+ return;
298
+ }
299
+ if (shell.overCap()) {
300
+ shell.send(socketErrorAckFor(req, new A2Error("STORE_UNAVAILABLE", "push shed: too many in flight on this socket")));
301
+ return;
302
+ }
303
+ shell.track(handleSocketPush(shell, target, sessionId, frame, req, void 0, options?.gatePush));
304
+ return;
305
+ }
306
+ case "presence":
307
+ if (frame["sessionId"] !== void 0) {
308
+ shell.malformedFrame("sessionId is not allowed on a single-session socket");
309
+ return;
310
+ }
311
+ if (!shell.overCap()) shell.track(applySocketPresence(shell, target, sessionId, frame, options?.gatePush));
312
+ return;
313
+ default: return;
314
+ }
315
+ };
316
+ const shell = socketShell(socket, () => stopSubscription(subscription), handleFrame, options?.deadline);
317
+ pumpSubscription(shell, subscription, socketFrameFor).then(() => shell.shutdown(1e3), () => shell.shutdown(1011, "stream failed"));
318
+ }
229
319
  const parseSubscribeEntries = (value) => {
230
320
  if (!Array.isArray(value) || value.length === 0) return null;
231
321
  const entries = [];
@@ -259,22 +349,9 @@ function sessionsSocket(resolve, socket, options) {
259
349
  const retire = (id, subscription) => {
260
350
  if (subscriptions.get(id) === subscription) subscriptions.delete(id);
261
351
  };
262
- const pump = async (id, subscription) => {
263
- for (;;) {
264
- for (;;) {
265
- if (shell.torn() || subscription.stopped || shell.buffered() <= SOCKET_TIMINGS.highWaterMarkBytes) break;
266
- await new Promise((wake) => setTimeout(wake, SOCKET_TIMINGS.resumePollMs));
267
- }
268
- if (shell.torn() || subscription.stopped) return;
269
- const { value, done } = await subscription.iterator.next();
270
- if (shell.torn() || subscription.stopped) return;
271
- if (done) return;
272
- shell.send(socketFrameFor(value, id));
273
- }
274
- };
275
352
  const runSubscription = async (id, subscription) => {
276
353
  try {
277
- await pump(id, subscription);
354
+ await pumpSubscription(shell, subscription, (item) => socketFrameFor(item, id));
278
355
  if (shell.torn() || subscription.stopped) return;
279
356
  retire(id, subscription);
280
357
  shell.send(socketUnsubscribedFor(id));
@@ -300,14 +377,7 @@ function sessionsSocket(resolve, socket, options) {
300
377
  const target = session;
301
378
  const existing = subscriptions.get(id);
302
379
  if (existing) stopSubscription(existing);
303
- const subscription = {
304
- target,
305
- iterator: (presence ? target.stream({
306
- startAfter: index,
307
- presence: true
308
- }) : target.stream({ startAfter: index }))[Symbol.asyncIterator](),
309
- stopped: false
310
- };
380
+ const subscription = socketSubscription(target, index, presence);
311
381
  subscriptions.set(id, subscription);
312
382
  shell.send(socketSubscribedFor(id));
313
383
  runSubscription(id, subscription);
@@ -322,45 +392,12 @@ function sessionsSocket(resolve, socket, options) {
322
392
  shell.send(socketErrorAckFor(req, new A2Error("STORE_UNAVAILABLE", "session is not subscribed on this socket"), sessionId));
323
393
  return;
324
394
  }
325
- try {
326
- const events = parsePushEvents(frame["events"]);
327
- const denial = await options?.gatePush?.(sessionId, events, void 0) ?? null;
328
- if (denial !== null) {
329
- shell.send(socketErrorAckFor(req, denial, sessionId));
330
- return;
331
- }
332
- const appended = await subscription.target.append(...events);
333
- shell.send(socketAckFor(req, appended, sessionId));
334
- } catch (err) {
335
- shell.send(socketErrorAckFor(req, asA2Error(err), sessionId));
336
- }
395
+ await handleSocketPush(shell, subscription.target, sessionId, frame, req, sessionId, options?.gatePush);
337
396
  };
338
397
  const handlePresence = async (frame, sessionId) => {
339
398
  const subscription = subscriptions.get(sessionId);
340
399
  if (subscription === void 0) return;
341
- if (typeof subscription.target.setPresence !== "function") {
342
- shell.malformedFrame("presence frame on a presence-less session");
343
- return;
344
- }
345
- let patch;
346
- try {
347
- patch = parsePresenceSibling(frame);
348
- } catch {
349
- shell.malformedFrame("invalid presence frame");
350
- return;
351
- }
352
- if (!patch) return;
353
- try {
354
- if ((await options?.gatePush?.(sessionId, [], patch) ?? null) !== null) return;
355
- await subscription.target.setPresence(patch);
356
- } catch (error) {
357
- const failure = asA2Error(error);
358
- if (failure.code === "INVALID_PAYLOAD" || failure.code === "UNKNOWN_PRESENCE_FIELD") {
359
- shell.malformedFrame("invalid presence frame");
360
- return;
361
- }
362
- shell.shutdown(1011, "internal error");
363
- }
400
+ await applySocketPresence(shell, subscription.target, sessionId, frame, options?.gatePush);
364
401
  };
365
402
  const handleFrame = (frame) => {
366
403
  switch (frame["kind"]) {
@@ -591,7 +628,20 @@ function createServerFetch(server) {
591
628
  const validateIngress = async (context) => {
592
629
  await serverFetchHooks.get(fetch)?.validateIngress?.(context);
593
630
  };
594
- const attach = (socket) => {
631
+ const gateSocketPush = async (sessionId, events, pushedPresence) => {
632
+ try {
633
+ await authorize(freezePush(sessionId, events, pushedPresence, "websocket"));
634
+ await validateIngress({
635
+ sessionId,
636
+ events,
637
+ ...pushedPresence === void 0 ? {} : { presence: pushedPresence }
638
+ });
639
+ return null;
640
+ } catch (error) {
641
+ return asA2Error(error);
642
+ }
643
+ };
644
+ const attachMultiplexed = (socket) => {
595
645
  const deadline = invocationDeadlineMs();
596
646
  sessionsSocket(async (sessionId, startAfter) => {
597
647
  return await authorized({
@@ -603,25 +653,34 @@ function createServerFetch(server) {
603
653
  }, socket, {
604
654
  presence,
605
655
  ...deadline === null ? {} : { deadline },
606
- gatePush: async (sessionId, events, pushedPresence) => {
607
- try {
608
- await authorize(freezePush(sessionId, events, pushedPresence, "websocket"));
609
- await validateIngress({
610
- sessionId,
611
- events,
612
- ...pushedPresence === void 0 ? {} : { presence: pushedPresence }
613
- });
614
- return null;
615
- } catch (error) {
616
- return asA2Error(error);
617
- }
618
- }
656
+ gatePush: gateSocketPush
619
657
  });
620
658
  };
621
659
  try {
622
660
  if (request.method === "GET") {
623
661
  if (request.headers.get("upgrade")?.toLowerCase() === "websocket") {
624
662
  if (options?.upgradeWebSocket === void 0) return new Response("WebSocket upgrade requested, but server.fetch() has no upgrade implementation", { status: 426 });
663
+ if (options.multiplexWebSocket === true) return await options.upgradeWebSocket(attachMultiplexed);
664
+ const { searchParams } = new URL(request.url);
665
+ const sessionId = searchParams.get("sessionId");
666
+ if (sessionId === null || sessionId.length === 0) throw new A2Error("INVALID_PAYLOAD", "missing sessionId");
667
+ const startAfter = parseResumeIndex(searchParams.get("index"));
668
+ await authorize({
669
+ type: "stream",
670
+ sessionId,
671
+ startAfter,
672
+ transport: "websocket"
673
+ });
674
+ const target = server.session(sessionId);
675
+ const attach = (socket) => {
676
+ const deadline = invocationDeadlineMs();
677
+ sessionSocket(sessionId, target, socket, {
678
+ startAfter,
679
+ presence,
680
+ ...deadline === null ? {} : { deadline },
681
+ gatePush: gateSocketPush
682
+ });
683
+ };
625
684
  return await options.upgradeWebSocket(attach);
626
685
  }
627
686
  const { searchParams } = new URL(request.url);
@@ -1364,7 +1423,7 @@ function createServer(options) {
1364
1423
  const validated = nullProtoRecord();
1365
1424
  for (const [field, value] of Object.entries(patch.values)) {
1366
1425
  if (value === void 0) continue;
1367
- const schema = Object.hasOwn(presenceDefs, field) ? presenceDefs[field] : void 0;
1426
+ const schema = Object.hasOwn(presenceDefs, field) ? presenceDefs[field] : RESERVED_PARTICIPANT_IDS.has(field) ? void 0 : presenceDefs["*"];
1368
1427
  if (!schema) throw new A2Error("UNKNOWN_PRESENCE_FIELD", `contract '${name}' has no presence field '${field}'`);
1369
1428
  if (value === null) {
1370
1429
  validated[field] = null;
@@ -1870,7 +1929,7 @@ function createServer(options) {
1870
1929
  index: event.index,
1871
1930
  attempt: event.attemptCount,
1872
1931
  error: describeError(err),
1873
- maxFailures: MAX_FAILURES
1932
+ maxFailures: err instanceof NonRetriableError ? 1 : MAX_FAILURES
1874
1933
  });
1875
1934
  eventSpan.setAttribute("a2.event.outcome", failure.outcome);
1876
1935
  return {
@@ -2157,6 +2216,6 @@ function cloneInitial(initial) {
2157
2216
  }
2158
2217
  }
2159
2218
  //#endregion
2160
- export { platformVercelOidcToken as i, deliverSchedulerAppend as n, setServerFetchHooks as r, createServer as t };
2219
+ export { parsePresenceSibling as a, sseResponse as i, deliverSchedulerAppend as n, platformVercelOidcToken as o, setServerFetchHooks as r, createServer as t };
2161
2220
 
2162
- //# sourceMappingURL=server-B2XNevQA.js.map
2221
+ //# sourceMappingURL=server-DlLyvaSH.js.map