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.
- package/CHANGELOG.md +42 -0
- package/dist/actor-client.d.ts +46 -0
- package/dist/actor-client.d.ts.map +1 -0
- package/dist/actor-client.js +54 -0
- package/dist/actor-client.js.map +1 -0
- package/dist/actor-react.d.ts +54 -0
- package/dist/actor-react.d.ts.map +1 -0
- package/dist/actor-react.js +79 -0
- package/dist/actor-react.js.map +1 -0
- package/dist/actor-shared-DI7J5upy.js +127 -0
- package/dist/actor-shared-DI7J5upy.js.map +1 -0
- package/dist/actor-shared-USo5MyuF.d.ts +136 -0
- package/dist/actor-shared-USo5MyuF.d.ts.map +1 -0
- package/dist/actor.browser.d.ts +1 -0
- package/dist/actor.browser.js +13 -0
- package/dist/actor.browser.js.map +1 -0
- package/dist/actor.d.ts +176 -0
- package/dist/actor.d.ts.map +1 -0
- package/dist/actor.js +437 -0
- package/dist/actor.js.map +1 -0
- package/dist/ai-server.d.ts +2 -2
- package/dist/ai-server.js +2 -2
- package/dist/ai.d.ts +2 -2
- package/dist/client.d.ts +14 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +238 -58
- package/dist/client.js.map +1 -1
- package/dist/{errors-BQuJpe82.js → errors-DCk6ch5n.js} +16 -2
- package/dist/{errors-BQuJpe82.js.map → errors-DCk6ch5n.js.map} +1 -1
- package/dist/{idempotent-replay-DuqEkYA7.js → idempotent-replay-DVOlyYbx.js} +2 -2
- package/dist/{idempotent-replay-DuqEkYA7.js.map → idempotent-replay-DVOlyYbx.js.map} +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/react.d.ts +1 -1
- package/dist/{contract-jIfaR085.d.ts → reducer-DJKWm3cp.d.ts} +39 -39
- package/dist/reducer-DJKWm3cp.d.ts.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +2 -2
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +1 -1
- package/dist/{server-DjPhHnbI.d.ts → server-DgCrSuhB.d.ts} +5 -3
- package/dist/server-DgCrSuhB.d.ts.map +1 -0
- package/dist/{server-B2XNevQA.js → server-DlLyvaSH.js} +140 -81
- package/dist/server-DlLyvaSH.js.map +1 -0
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/{store-RJO35BMj.d.ts → store-DGHeBtIQ.d.ts} +2 -2
- package/dist/{store-RJO35BMj.d.ts.map → store-DGHeBtIQ.d.ts.map} +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.js +2 -2
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.js +2 -2
- package/dist/{store-redis-core-DT01r4GZ.js → store-redis-core-z-ykbyMg.js} +3 -3
- package/dist/{store-redis-core-DT01r4GZ.js.map → store-redis-core-z-ykbyMg.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +2 -2
- 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.js +2 -2
- package/dist/{wire-B6te_wns.js → wire--yji6mO3.js} +2 -2
- package/dist/{wire-B6te_wns.js.map → wire--yji6mO3.js.map} +1 -1
- package/docs/actors/01-introduction.mdx +189 -0
- package/docs/actors/02-concurrency.mdx +154 -0
- package/docs/actors/03-timers.mdx +120 -0
- package/docs/actors/04-routes.mdx +352 -0
- package/docs/actors/meta.ts +1 -0
- package/docs/concepts/meta.ts +1 -0
- package/docs/guides/10-transports.mdx +72 -22
- package/docs/guides/meta.ts +1 -0
- package/docs/index.mdx +3 -0
- package/docs/reference/01-api.mdx +30 -12
- package/docs/reference/02-errors.mdx +33 -0
- package/docs/reference/meta.ts +1 -0
- package/examples/playground/app/page.tsx +10 -1
- package/examples/playground/app/vault/[vaultId]/route.ts +19 -0
- package/examples/playground/app/vault/page.tsx +12 -0
- package/examples/playground/app/vault/server.ts +9 -0
- package/examples/playground/app/vault/vault-client.tsx +124 -0
- package/examples/playground/app/vault/vault.test.ts +147 -0
- package/examples/playground/app/vault/vault.ts +119 -0
- package/examples/playground/package.json +1 -1
- package/package.json +7 -1
- package/src/actor-client.ts +132 -0
- package/src/actor-react.ts +143 -0
- package/src/actor-shared.ts +356 -0
- package/src/actor.browser.ts +12 -0
- package/src/actor.ts +914 -0
- package/src/client.ts +341 -88
- package/src/errors.ts +15 -0
- package/src/index.ts +1 -1
- package/src/server-fetch.ts +51 -23
- package/src/server.ts +13 -3
- package/src/session-socket.ts +216 -81
- package/dist/contract-jIfaR085.d.ts.map +0 -1
- package/dist/server-B2XNevQA.js.map +0 -1
- 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
|
-
|
|
219
|
-
|
|
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-
|
|
2
|
-
import { T as SchedulerTask, t as A2Scheduler } from "./server-
|
|
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;
|
package/dist/scheduler-qstash.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as A2Error } from "./errors-
|
|
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 {
|
|
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-
|
|
2
|
-
import { T as SchedulerTask, t as A2Scheduler } from "./server-
|
|
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,
|
package/dist/scheduler-vercel.js
CHANGED
|
@@ -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-
|
|
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
|
|
2
|
-
import { t as A2Store } from "./store-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
120
|
-
*
|
|
121
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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 {
|
|
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-
|
|
2221
|
+
//# sourceMappingURL=server-DlLyvaSH.js.map
|