@nice-code/action 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_wsAcceptorCarrier = require("../../wsAcceptorCarrier-DHRbsY1X.cjs");
3
+ let _nice_code_util = require("@nice-code/util");
4
+ //#region src/platform/cloudflare/index.ts
5
+ /**
6
+ * Build a hibernatable-WebSocket acceptor carrier for a Durable Object in one call — the `send`, the
7
+ * `WebSocketPair` upgrade, and the hibernation attachment hooks all derived from the DO's `ctx`. Hand it
8
+ * straight to `serveChannel`'s `carriers`, and forward the DO's socket events to the returned handle:
9
+ * ```ts
10
+ * const ws = durableObjectWsCarrier(this.ctx);
11
+ * const server = serveChannel(runtime, channel, {
12
+ * clientEnv,
13
+ * storage: durableObjectStorage(this.ctx, { keyPrefix: "ws:" }),
14
+ * handlers: [localHandler],
15
+ * carriers: [ws, httpAcceptorCarrier()],
16
+ * });
17
+ * // webSocketMessage(c, m) => ws.receive(c, m);
18
+ * // webSocketClose/Error(c) => ws.drop(c);
19
+ * ```
20
+ */
21
+ function durableObjectWsCarrier(ctx, options = {}) {
22
+ return require_wsAcceptorCarrier.wsAcceptorCarrier({
23
+ secure: options.secure,
24
+ send: (ws, frame) => ws.send(frame),
25
+ upgrade: () => {
26
+ const pair = new WebSocketPair();
27
+ const client = pair[0];
28
+ const server = pair[1];
29
+ ctx.acceptWebSocket(server);
30
+ return new Response(null, {
31
+ status: 101,
32
+ webSocket: client
33
+ });
34
+ },
35
+ hibernation: {
36
+ getConnections: () => ctx.getWebSockets(),
37
+ getAttachment: (ws) => ws.deserializeAttachment(),
38
+ setAttachment: (ws, binding) => ws.serializeAttachment(binding)
39
+ }
40
+ });
41
+ }
42
+ /**
43
+ * Wrap a Durable Object's storage as a {@link StorageAdapter} for `serveChannel`'s `storage` — sugar over
44
+ * `createDurableObjectStorageAdapter({ durableObjectStorage: ctx.storage, … })` so a DO needs one import.
45
+ */
46
+ function durableObjectStorage(ctx, options = {}) {
47
+ return (0, _nice_code_util.createDurableObjectStorageAdapter)({
48
+ durableObjectStorage: ctx.storage,
49
+ keyPrefix: options.keyPrefix
50
+ });
51
+ }
52
+ //#endregion
53
+ exports.durableObjectStorage = durableObjectStorage;
54
+ exports.durableObjectWsCarrier = durableObjectWsCarrier;
55
+
56
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["wsAcceptorCarrier"],"sources":["../../../src/platform/cloudflare/index.ts"],"sourcesContent":["import {\r\n createDurableObjectStorageAdapter,\r\n type StorageAdapter,\r\n type TCreateDurableObjectStorageOptions,\r\n} from \"@nice-code/util\";\r\nimport type { IChannelServer } from \"../../ActionRuntime/Channel/serveChannel\";\r\nimport type { IDuplexAcceptorCarrier } from \"../../ActionRuntime/Transport/Carrier/AcceptorCarrier.types\";\r\nimport { wsAcceptorCarrier } from \"../../ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier\";\r\n\r\n/**\r\n * Cloudflare-specific helpers for `@nice-code/action`, imported from `@nice-code/action/platform/cloudflare`.\r\n * They collapse the Durable Object boilerplate (the `WebSocketPair` upgrade, hibernation attachment wiring,\r\n * and DO-storage adapter) into one-liners you hand to `serveChannel`. The core library stays\r\n * platform-agnostic — nothing here is reachable from the main entry.\r\n *\r\n * The Workers runtime surface this module needs is declared *structurally* (and the two globals it\r\n * constructs are declared module-locally below) rather than pulled from `@cloudflare/workers-types`, so the\r\n * library's own DOM-lib build never clashes with that package's global `Response`/`WebSocket` redefinitions.\r\n * A real `DurableObjectState` and its hibernatable `WebSocket`s satisfy these shapes.\r\n */\r\n\r\ntype TDurableObjectStorage = TCreateDurableObjectStorageOptions[\"durableObjectStorage\"];\r\n\r\n/** The slice of a Durable Object's hibernatable WebSocket these helpers touch. */\r\nexport interface IDurableObjectWebSocket {\r\n send(data: string | ArrayBuffer | Uint8Array): void;\r\n serializeAttachment(value: unknown): void;\r\n // Mirrors the Workers runtime's `any` return so a typed connection binding round-trips without a cast.\r\n deserializeAttachment(): any;\r\n}\r\n\r\n/** The slice of a Durable Object's `state` (its `ctx`) these helpers touch. */\r\nexport interface IDurableObjectContext {\r\n storage: TDurableObjectStorage;\r\n getWebSockets(): IDurableObjectWebSocket[];\r\n acceptWebSocket(ws: IDurableObjectWebSocket): void;\r\n}\r\n\r\n/** An `IChannelServer` whose connections are a Durable Object's hibernatable WebSockets — the type to\r\n * store the result of `serveChannel(...)` in when serving over {@link durableObjectWsCarrier}. */\r\nexport type TDurableObjectChannelServer = IChannelServer<IDurableObjectWebSocket>;\r\n\r\n// Workers runtime globals, declared module-locally (the runtime provides them at deploy time). Declaring\r\n// them here keeps them out of the package's global scope, so the DOM lib's `Response`/`WebSocket` stand\r\n// elsewhere. The `Response` constructor type still returns the DOM `Response` — only its init gains the\r\n// Workers-only `webSocket` field.\r\ndeclare const WebSocketPair: {\r\n new (): { 0: IDurableObjectWebSocket; 1: IDurableObjectWebSocket };\r\n};\r\ndeclare const Response: {\r\n new (\r\n body: BodyInit | null,\r\n init?: {\r\n status?: number;\r\n statusText?: string;\r\n headers?: HeadersInit;\r\n webSocket?: IDurableObjectWebSocket;\r\n },\r\n ): Response;\r\n};\r\n\r\nexport interface IDurableObjectWsCarrierOptions {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint —\r\n * then `serveChannel` needs no `storage` for this carrier.\r\n */\r\n secure?: boolean;\r\n}\r\n\r\n/**\r\n * Build a hibernatable-WebSocket acceptor carrier for a Durable Object in one call — the `send`, the\r\n * `WebSocketPair` upgrade, and the hibernation attachment hooks all derived from the DO's `ctx`. Hand it\r\n * straight to `serveChannel`'s `carriers`, and forward the DO's socket events to the returned handle:\r\n * ```ts\r\n * const ws = durableObjectWsCarrier(this.ctx);\r\n * const server = serveChannel(runtime, channel, {\r\n * clientEnv,\r\n * storage: durableObjectStorage(this.ctx, { keyPrefix: \"ws:\" }),\r\n * handlers: [localHandler],\r\n * carriers: [ws, httpAcceptorCarrier()],\r\n * });\r\n * // webSocketMessage(c, m) => ws.receive(c, m);\r\n * // webSocketClose/Error(c) => ws.drop(c);\r\n * ```\r\n */\r\nexport function durableObjectWsCarrier(\r\n ctx: IDurableObjectContext,\r\n options: IDurableObjectWsCarrierOptions = {},\r\n): IDuplexAcceptorCarrier<IDurableObjectWebSocket> {\r\n return wsAcceptorCarrier<IDurableObjectWebSocket>({\r\n secure: options.secure,\r\n send: (ws, frame) => ws.send(frame),\r\n upgrade: () => {\r\n const pair = new WebSocketPair();\r\n const client = pair[0];\r\n const server = pair[1];\r\n // Hibernatable WebSocket — the DO can sleep between messages.\r\n ctx.acceptWebSocket(server);\r\n return new Response(null, { status: 101, webSocket: client });\r\n },\r\n hibernation: {\r\n getConnections: () => ctx.getWebSockets(),\r\n getAttachment: (ws) => ws.deserializeAttachment(),\r\n setAttachment: (ws, binding) => ws.serializeAttachment(binding),\r\n },\r\n });\r\n}\r\n\r\nexport interface IDurableObjectStorageOptions {\r\n /** Namespace prefix for every key (e.g. `\"demo-ws:\"`), so several adapters can share one DO storage. */\r\n keyPrefix?: string;\r\n}\r\n\r\n/**\r\n * Wrap a Durable Object's storage as a {@link StorageAdapter} for `serveChannel`'s `storage` — sugar over\r\n * `createDurableObjectStorageAdapter({ durableObjectStorage: ctx.storage, … })` so a DO needs one import.\r\n */\r\nexport function durableObjectStorage(\r\n ctx: IDurableObjectContext,\r\n options: IDurableObjectStorageOptions = {},\r\n): StorageAdapter {\r\n return createDurableObjectStorageAdapter({\r\n durableObjectStorage: ctx.storage,\r\n keyPrefix: options.keyPrefix,\r\n });\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqFA,SAAgB,uBACd,KACA,UAA0C,CAAC,GACM;CACjD,OAAOA,0BAAAA,kBAA2C;EAChD,QAAQ,QAAQ;EAChB,OAAO,IAAI,UAAU,GAAG,KAAK,KAAK;EAClC,eAAe;GACb,MAAM,OAAO,IAAI,cAAc;GAC/B,MAAM,SAAS,KAAK;GACpB,MAAM,SAAS,KAAK;GAEpB,IAAI,gBAAgB,MAAM;GAC1B,OAAO,IAAI,SAAS,MAAM;IAAE,QAAQ;IAAK,WAAW;GAAO,CAAC;EAC9D;EACA,aAAa;GACX,sBAAsB,IAAI,cAAc;GACxC,gBAAgB,OAAO,GAAG,sBAAsB;GAChD,gBAAgB,IAAI,YAAY,GAAG,oBAAoB,OAAO;EAChE;CACF,CAAC;AACH;;;;;AAWA,SAAgB,qBACd,KACA,UAAwC,CAAC,GACzB;CAChB,QAAA,GAAA,gBAAA,kCAAA,CAAyC;EACvC,sBAAsB,IAAI;EAC1B,WAAW,QAAQ;CACrB,CAAC;AACH"}
@@ -0,0 +1,67 @@
1
+ import { Et as IDuplexAcceptorCarrier, St as IChannelServer } from "../../ActionPayload.types-snDlSIF-.cjs";
2
+ import { StorageAdapter, TCreateDurableObjectStorageOptions } from "@nice-code/util";
3
+
4
+ //#region src/platform/cloudflare/index.d.ts
5
+ /**
6
+ * Cloudflare-specific helpers for `@nice-code/action`, imported from `@nice-code/action/platform/cloudflare`.
7
+ * They collapse the Durable Object boilerplate (the `WebSocketPair` upgrade, hibernation attachment wiring,
8
+ * and DO-storage adapter) into one-liners you hand to `serveChannel`. The core library stays
9
+ * platform-agnostic — nothing here is reachable from the main entry.
10
+ *
11
+ * The Workers runtime surface this module needs is declared *structurally* (and the two globals it
12
+ * constructs are declared module-locally below) rather than pulled from `@cloudflare/workers-types`, so the
13
+ * library's own DOM-lib build never clashes with that package's global `Response`/`WebSocket` redefinitions.
14
+ * A real `DurableObjectState` and its hibernatable `WebSocket`s satisfy these shapes.
15
+ */
16
+ type TDurableObjectStorage = TCreateDurableObjectStorageOptions["durableObjectStorage"];
17
+ /** The slice of a Durable Object's hibernatable WebSocket these helpers touch. */
18
+ interface IDurableObjectWebSocket {
19
+ send(data: string | ArrayBuffer | Uint8Array): void;
20
+ serializeAttachment(value: unknown): void;
21
+ deserializeAttachment(): any;
22
+ }
23
+ /** The slice of a Durable Object's `state` (its `ctx`) these helpers touch. */
24
+ interface IDurableObjectContext {
25
+ storage: TDurableObjectStorage;
26
+ getWebSockets(): IDurableObjectWebSocket[];
27
+ acceptWebSocket(ws: IDurableObjectWebSocket): void;
28
+ }
29
+ /** An `IChannelServer` whose connections are a Durable Object's hibernatable WebSockets — the type to
30
+ * store the result of `serveChannel(...)` in when serving over {@link durableObjectWsCarrier}. */
31
+ type TDurableObjectChannelServer = IChannelServer<IDurableObjectWebSocket>;
32
+ interface IDurableObjectWsCarrierOptions {
33
+ /**
34
+ * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint —
35
+ * then `serveChannel` needs no `storage` for this carrier.
36
+ */
37
+ secure?: boolean;
38
+ }
39
+ /**
40
+ * Build a hibernatable-WebSocket acceptor carrier for a Durable Object in one call — the `send`, the
41
+ * `WebSocketPair` upgrade, and the hibernation attachment hooks all derived from the DO's `ctx`. Hand it
42
+ * straight to `serveChannel`'s `carriers`, and forward the DO's socket events to the returned handle:
43
+ * ```ts
44
+ * const ws = durableObjectWsCarrier(this.ctx);
45
+ * const server = serveChannel(runtime, channel, {
46
+ * clientEnv,
47
+ * storage: durableObjectStorage(this.ctx, { keyPrefix: "ws:" }),
48
+ * handlers: [localHandler],
49
+ * carriers: [ws, httpAcceptorCarrier()],
50
+ * });
51
+ * // webSocketMessage(c, m) => ws.receive(c, m);
52
+ * // webSocketClose/Error(c) => ws.drop(c);
53
+ * ```
54
+ */
55
+ declare function durableObjectWsCarrier(ctx: IDurableObjectContext, options?: IDurableObjectWsCarrierOptions): IDuplexAcceptorCarrier<IDurableObjectWebSocket>;
56
+ interface IDurableObjectStorageOptions {
57
+ /** Namespace prefix for every key (e.g. `"demo-ws:"`), so several adapters can share one DO storage. */
58
+ keyPrefix?: string;
59
+ }
60
+ /**
61
+ * Wrap a Durable Object's storage as a {@link StorageAdapter} for `serveChannel`'s `storage` — sugar over
62
+ * `createDurableObjectStorageAdapter({ durableObjectStorage: ctx.storage, … })` so a DO needs one import.
63
+ */
64
+ declare function durableObjectStorage(ctx: IDurableObjectContext, options?: IDurableObjectStorageOptions): StorageAdapter;
65
+ //#endregion
66
+ export { IDurableObjectContext, IDurableObjectStorageOptions, IDurableObjectWebSocket, IDurableObjectWsCarrierOptions, TDurableObjectChannelServer, durableObjectStorage, durableObjectWsCarrier };
67
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1,67 @@
1
+ import { Et as IDuplexAcceptorCarrier, St as IChannelServer } from "../../ActionPayload.types-BchJrBIX.mjs";
2
+ import { StorageAdapter, TCreateDurableObjectStorageOptions } from "@nice-code/util";
3
+
4
+ //#region src/platform/cloudflare/index.d.ts
5
+ /**
6
+ * Cloudflare-specific helpers for `@nice-code/action`, imported from `@nice-code/action/platform/cloudflare`.
7
+ * They collapse the Durable Object boilerplate (the `WebSocketPair` upgrade, hibernation attachment wiring,
8
+ * and DO-storage adapter) into one-liners you hand to `serveChannel`. The core library stays
9
+ * platform-agnostic — nothing here is reachable from the main entry.
10
+ *
11
+ * The Workers runtime surface this module needs is declared *structurally* (and the two globals it
12
+ * constructs are declared module-locally below) rather than pulled from `@cloudflare/workers-types`, so the
13
+ * library's own DOM-lib build never clashes with that package's global `Response`/`WebSocket` redefinitions.
14
+ * A real `DurableObjectState` and its hibernatable `WebSocket`s satisfy these shapes.
15
+ */
16
+ type TDurableObjectStorage = TCreateDurableObjectStorageOptions["durableObjectStorage"];
17
+ /** The slice of a Durable Object's hibernatable WebSocket these helpers touch. */
18
+ interface IDurableObjectWebSocket {
19
+ send(data: string | ArrayBuffer | Uint8Array): void;
20
+ serializeAttachment(value: unknown): void;
21
+ deserializeAttachment(): any;
22
+ }
23
+ /** The slice of a Durable Object's `state` (its `ctx`) these helpers touch. */
24
+ interface IDurableObjectContext {
25
+ storage: TDurableObjectStorage;
26
+ getWebSockets(): IDurableObjectWebSocket[];
27
+ acceptWebSocket(ws: IDurableObjectWebSocket): void;
28
+ }
29
+ /** An `IChannelServer` whose connections are a Durable Object's hibernatable WebSockets — the type to
30
+ * store the result of `serveChannel(...)` in when serving over {@link durableObjectWsCarrier}. */
31
+ type TDurableObjectChannelServer = IChannelServer<IDurableObjectWebSocket>;
32
+ interface IDurableObjectWsCarrierOptions {
33
+ /**
34
+ * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint —
35
+ * then `serveChannel` needs no `storage` for this carrier.
36
+ */
37
+ secure?: boolean;
38
+ }
39
+ /**
40
+ * Build a hibernatable-WebSocket acceptor carrier for a Durable Object in one call — the `send`, the
41
+ * `WebSocketPair` upgrade, and the hibernation attachment hooks all derived from the DO's `ctx`. Hand it
42
+ * straight to `serveChannel`'s `carriers`, and forward the DO's socket events to the returned handle:
43
+ * ```ts
44
+ * const ws = durableObjectWsCarrier(this.ctx);
45
+ * const server = serveChannel(runtime, channel, {
46
+ * clientEnv,
47
+ * storage: durableObjectStorage(this.ctx, { keyPrefix: "ws:" }),
48
+ * handlers: [localHandler],
49
+ * carriers: [ws, httpAcceptorCarrier()],
50
+ * });
51
+ * // webSocketMessage(c, m) => ws.receive(c, m);
52
+ * // webSocketClose/Error(c) => ws.drop(c);
53
+ * ```
54
+ */
55
+ declare function durableObjectWsCarrier(ctx: IDurableObjectContext, options?: IDurableObjectWsCarrierOptions): IDuplexAcceptorCarrier<IDurableObjectWebSocket>;
56
+ interface IDurableObjectStorageOptions {
57
+ /** Namespace prefix for every key (e.g. `"demo-ws:"`), so several adapters can share one DO storage. */
58
+ keyPrefix?: string;
59
+ }
60
+ /**
61
+ * Wrap a Durable Object's storage as a {@link StorageAdapter} for `serveChannel`'s `storage` — sugar over
62
+ * `createDurableObjectStorageAdapter({ durableObjectStorage: ctx.storage, … })` so a DO needs one import.
63
+ */
64
+ declare function durableObjectStorage(ctx: IDurableObjectContext, options?: IDurableObjectStorageOptions): StorageAdapter;
65
+ //#endregion
66
+ export { IDurableObjectContext, IDurableObjectStorageOptions, IDurableObjectWebSocket, IDurableObjectWsCarrierOptions, TDurableObjectChannelServer, durableObjectStorage, durableObjectWsCarrier };
67
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,54 @@
1
+ import { t as wsAcceptorCarrier } from "../../wsAcceptorCarrier-CXGlQU_f.mjs";
2
+ import { createDurableObjectStorageAdapter } from "@nice-code/util";
3
+ //#region src/platform/cloudflare/index.ts
4
+ /**
5
+ * Build a hibernatable-WebSocket acceptor carrier for a Durable Object in one call — the `send`, the
6
+ * `WebSocketPair` upgrade, and the hibernation attachment hooks all derived from the DO's `ctx`. Hand it
7
+ * straight to `serveChannel`'s `carriers`, and forward the DO's socket events to the returned handle:
8
+ * ```ts
9
+ * const ws = durableObjectWsCarrier(this.ctx);
10
+ * const server = serveChannel(runtime, channel, {
11
+ * clientEnv,
12
+ * storage: durableObjectStorage(this.ctx, { keyPrefix: "ws:" }),
13
+ * handlers: [localHandler],
14
+ * carriers: [ws, httpAcceptorCarrier()],
15
+ * });
16
+ * // webSocketMessage(c, m) => ws.receive(c, m);
17
+ * // webSocketClose/Error(c) => ws.drop(c);
18
+ * ```
19
+ */
20
+ function durableObjectWsCarrier(ctx, options = {}) {
21
+ return wsAcceptorCarrier({
22
+ secure: options.secure,
23
+ send: (ws, frame) => ws.send(frame),
24
+ upgrade: () => {
25
+ const pair = new WebSocketPair();
26
+ const client = pair[0];
27
+ const server = pair[1];
28
+ ctx.acceptWebSocket(server);
29
+ return new Response(null, {
30
+ status: 101,
31
+ webSocket: client
32
+ });
33
+ },
34
+ hibernation: {
35
+ getConnections: () => ctx.getWebSockets(),
36
+ getAttachment: (ws) => ws.deserializeAttachment(),
37
+ setAttachment: (ws, binding) => ws.serializeAttachment(binding)
38
+ }
39
+ });
40
+ }
41
+ /**
42
+ * Wrap a Durable Object's storage as a {@link StorageAdapter} for `serveChannel`'s `storage` — sugar over
43
+ * `createDurableObjectStorageAdapter({ durableObjectStorage: ctx.storage, … })` so a DO needs one import.
44
+ */
45
+ function durableObjectStorage(ctx, options = {}) {
46
+ return createDurableObjectStorageAdapter({
47
+ durableObjectStorage: ctx.storage,
48
+ keyPrefix: options.keyPrefix
49
+ });
50
+ }
51
+ //#endregion
52
+ export { durableObjectStorage, durableObjectWsCarrier };
53
+
54
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/platform/cloudflare/index.ts"],"sourcesContent":["import {\r\n createDurableObjectStorageAdapter,\r\n type StorageAdapter,\r\n type TCreateDurableObjectStorageOptions,\r\n} from \"@nice-code/util\";\r\nimport type { IChannelServer } from \"../../ActionRuntime/Channel/serveChannel\";\r\nimport type { IDuplexAcceptorCarrier } from \"../../ActionRuntime/Transport/Carrier/AcceptorCarrier.types\";\r\nimport { wsAcceptorCarrier } from \"../../ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier\";\r\n\r\n/**\r\n * Cloudflare-specific helpers for `@nice-code/action`, imported from `@nice-code/action/platform/cloudflare`.\r\n * They collapse the Durable Object boilerplate (the `WebSocketPair` upgrade, hibernation attachment wiring,\r\n * and DO-storage adapter) into one-liners you hand to `serveChannel`. The core library stays\r\n * platform-agnostic — nothing here is reachable from the main entry.\r\n *\r\n * The Workers runtime surface this module needs is declared *structurally* (and the two globals it\r\n * constructs are declared module-locally below) rather than pulled from `@cloudflare/workers-types`, so the\r\n * library's own DOM-lib build never clashes with that package's global `Response`/`WebSocket` redefinitions.\r\n * A real `DurableObjectState` and its hibernatable `WebSocket`s satisfy these shapes.\r\n */\r\n\r\ntype TDurableObjectStorage = TCreateDurableObjectStorageOptions[\"durableObjectStorage\"];\r\n\r\n/** The slice of a Durable Object's hibernatable WebSocket these helpers touch. */\r\nexport interface IDurableObjectWebSocket {\r\n send(data: string | ArrayBuffer | Uint8Array): void;\r\n serializeAttachment(value: unknown): void;\r\n // Mirrors the Workers runtime's `any` return so a typed connection binding round-trips without a cast.\r\n deserializeAttachment(): any;\r\n}\r\n\r\n/** The slice of a Durable Object's `state` (its `ctx`) these helpers touch. */\r\nexport interface IDurableObjectContext {\r\n storage: TDurableObjectStorage;\r\n getWebSockets(): IDurableObjectWebSocket[];\r\n acceptWebSocket(ws: IDurableObjectWebSocket): void;\r\n}\r\n\r\n/** An `IChannelServer` whose connections are a Durable Object's hibernatable WebSockets — the type to\r\n * store the result of `serveChannel(...)` in when serving over {@link durableObjectWsCarrier}. */\r\nexport type TDurableObjectChannelServer = IChannelServer<IDurableObjectWebSocket>;\r\n\r\n// Workers runtime globals, declared module-locally (the runtime provides them at deploy time). Declaring\r\n// them here keeps them out of the package's global scope, so the DOM lib's `Response`/`WebSocket` stand\r\n// elsewhere. The `Response` constructor type still returns the DOM `Response` — only its init gains the\r\n// Workers-only `webSocket` field.\r\ndeclare const WebSocketPair: {\r\n new (): { 0: IDurableObjectWebSocket; 1: IDurableObjectWebSocket };\r\n};\r\ndeclare const Response: {\r\n new (\r\n body: BodyInit | null,\r\n init?: {\r\n status?: number;\r\n statusText?: string;\r\n headers?: HeadersInit;\r\n webSocket?: IDurableObjectWebSocket;\r\n },\r\n ): Response;\r\n};\r\n\r\nexport interface IDurableObjectWsCarrierOptions {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint —\r\n * then `serveChannel` needs no `storage` for this carrier.\r\n */\r\n secure?: boolean;\r\n}\r\n\r\n/**\r\n * Build a hibernatable-WebSocket acceptor carrier for a Durable Object in one call — the `send`, the\r\n * `WebSocketPair` upgrade, and the hibernation attachment hooks all derived from the DO's `ctx`. Hand it\r\n * straight to `serveChannel`'s `carriers`, and forward the DO's socket events to the returned handle:\r\n * ```ts\r\n * const ws = durableObjectWsCarrier(this.ctx);\r\n * const server = serveChannel(runtime, channel, {\r\n * clientEnv,\r\n * storage: durableObjectStorage(this.ctx, { keyPrefix: \"ws:\" }),\r\n * handlers: [localHandler],\r\n * carriers: [ws, httpAcceptorCarrier()],\r\n * });\r\n * // webSocketMessage(c, m) => ws.receive(c, m);\r\n * // webSocketClose/Error(c) => ws.drop(c);\r\n * ```\r\n */\r\nexport function durableObjectWsCarrier(\r\n ctx: IDurableObjectContext,\r\n options: IDurableObjectWsCarrierOptions = {},\r\n): IDuplexAcceptorCarrier<IDurableObjectWebSocket> {\r\n return wsAcceptorCarrier<IDurableObjectWebSocket>({\r\n secure: options.secure,\r\n send: (ws, frame) => ws.send(frame),\r\n upgrade: () => {\r\n const pair = new WebSocketPair();\r\n const client = pair[0];\r\n const server = pair[1];\r\n // Hibernatable WebSocket — the DO can sleep between messages.\r\n ctx.acceptWebSocket(server);\r\n return new Response(null, { status: 101, webSocket: client });\r\n },\r\n hibernation: {\r\n getConnections: () => ctx.getWebSockets(),\r\n getAttachment: (ws) => ws.deserializeAttachment(),\r\n setAttachment: (ws, binding) => ws.serializeAttachment(binding),\r\n },\r\n });\r\n}\r\n\r\nexport interface IDurableObjectStorageOptions {\r\n /** Namespace prefix for every key (e.g. `\"demo-ws:\"`), so several adapters can share one DO storage. */\r\n keyPrefix?: string;\r\n}\r\n\r\n/**\r\n * Wrap a Durable Object's storage as a {@link StorageAdapter} for `serveChannel`'s `storage` — sugar over\r\n * `createDurableObjectStorageAdapter({ durableObjectStorage: ctx.storage, … })` so a DO needs one import.\r\n */\r\nexport function durableObjectStorage(\r\n ctx: IDurableObjectContext,\r\n options: IDurableObjectStorageOptions = {},\r\n): StorageAdapter {\r\n return createDurableObjectStorageAdapter({\r\n durableObjectStorage: ctx.storage,\r\n keyPrefix: options.keyPrefix,\r\n });\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqFA,SAAgB,uBACd,KACA,UAA0C,CAAC,GACM;CACjD,OAAO,kBAA2C;EAChD,QAAQ,QAAQ;EAChB,OAAO,IAAI,UAAU,GAAG,KAAK,KAAK;EAClC,eAAe;GACb,MAAM,OAAO,IAAI,cAAc;GAC/B,MAAM,SAAS,KAAK;GACpB,MAAM,SAAS,KAAK;GAEpB,IAAI,gBAAgB,MAAM;GAC1B,OAAO,IAAI,SAAS,MAAM;IAAE,QAAQ;IAAK,WAAW;GAAO,CAAC;EAC9D;EACA,aAAa;GACX,sBAAsB,IAAI,cAAc;GACxC,gBAAgB,OAAO,GAAG,sBAAsB;GAChD,gBAAgB,IAAI,YAAY,GAAG,oBAAoB,OAAO;EAChE;CACF,CAAC;AACH;;;;;AAWA,SAAgB,qBACd,KACA,UAAwC,CAAC,GACzB;CAChB,OAAO,kCAAkC;EACvC,sBAAsB,IAAI;EAC1B,WAAW,QAAQ;CACrB,CAAC;AACH"}
@@ -1,4 +1,4 @@
1
- import { Fr as ActionSchema, Lr as TInferActionError, Mr as TInferOutputFromSchema, Tr as IActionDomain, Yt as ActionCore, jr as TInferInputFromSchema } from "../ActionPayload.types-CO_hXlBc.cjs";
1
+ import { Lr as IActionDomain, Ur as TInferInputFromSchema, Wr as TInferOutputFromSchema, Yr as TInferActionError, qr as ActionSchema, sn as ActionCore } from "../ActionPayload.types-snDlSIF-.cjs";
2
2
  import { QueryKey, UseMutationOptions, UseMutationResult, UseQueryOptions, UseQueryResult } from "@tanstack/react-query";
3
3
 
4
4
  //#region src/react-query/hooks/useActionMutation.d.ts
@@ -1,4 +1,4 @@
1
- import { Fr as ActionSchema, Lr as TInferActionError, Mr as TInferOutputFromSchema, Tr as IActionDomain, Yt as ActionCore, jr as TInferInputFromSchema } from "../ActionPayload.types-fieMKAgt.mjs";
1
+ import { Lr as IActionDomain, Ur as TInferInputFromSchema, Wr as TInferOutputFromSchema, Yr as TInferActionError, qr as ActionSchema, sn as ActionCore } from "../ActionPayload.types-BchJrBIX.mjs";
2
2
  import { QueryKey, UseMutationOptions, UseMutationResult, UseQueryOptions, UseQueryResult } from "@tanstack/react-query";
3
3
 
4
4
  //#region src/react-query/hooks/useActionMutation.d.ts
@@ -0,0 +1,80 @@
1
+ //#region src/ActionRuntime/Transport/Transport.types.ts
2
+ /**
3
+ * Carrier-shape class identity: the one carrier-invariant trait the selection layer reasons about —
4
+ * whether a carrier can push unsolicited frames (`duplex`: WS/WebRTC/BLE/in-memory) or only answers a
5
+ * request with a single correlated reply (`exchange`: HTTP). The concrete carrier kind ("ws", "webrtc",
6
+ * "http", …) is a free-form {@link ITransportRouteInfo.carrierLabel} for display, not a class tag.
7
+ */
8
+ let ETransportShape = /* @__PURE__ */ function(ETransportShape) {
9
+ ETransportShape["duplex"] = "duplex";
10
+ ETransportShape["exchange"] = "exchange";
11
+ return ETransportShape;
12
+ }({});
13
+ /**
14
+ *
15
+ * TRANSPORT READINESS RESPONSE
16
+ *
17
+ */
18
+ let ETransportStatus = /* @__PURE__ */ function(ETransportStatus) {
19
+ ETransportStatus["uninitialized"] = "uninitialized";
20
+ ETransportStatus["unsupported"] = "unsupported";
21
+ ETransportStatus["initializing"] = "initializing";
22
+ ETransportStatus["ready"] = "ready";
23
+ ETransportStatus["failed"] = "failed";
24
+ return ETransportStatus;
25
+ }({});
26
+ //#endregion
27
+ //#region src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.ts
28
+ /**
29
+ * Build the inert lifecycle slot a duplex carrier factory spreads into its handle: `receive`/`drop` throw
30
+ * (or no-op) until `serveChannel` calls `_activate` with the carrier's live router. Shared by every duplex
31
+ * carrier factory (`wsAcceptorCarrier`, a WebRTC carrier, the Cloudflare DO helper, …) so the
32
+ * stateful-handle wiring lives in exactly one place.
33
+ */
34
+ function createDuplexCarrierLifecycle() {
35
+ let router;
36
+ return {
37
+ receive(connection, frame) {
38
+ if (router == null) throw new Error("acceptor carrier not served yet — pass it to serveChannel() before feeding frames");
39
+ router.receive(connection, frame);
40
+ },
41
+ drop(connection) {
42
+ router?.drop(connection);
43
+ },
44
+ _activate(liveRouter) {
45
+ router = liveRouter;
46
+ }
47
+ };
48
+ }
49
+ /**
50
+ * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch
51
+ * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex
52
+ * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.
53
+ */
54
+ function isExchangeAcceptorCarrier(carrier) {
55
+ return carrier.shape === "exchange";
56
+ }
57
+ //#endregion
58
+ //#region src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.ts
59
+ /**
60
+ * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to
61
+ * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to
62
+ * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,
63
+ * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.
64
+ */
65
+ function wsAcceptorCarrier(options) {
66
+ return {
67
+ ...createDuplexCarrierLifecycle(),
68
+ shape: "duplex",
69
+ carrierLabel: options.carrierLabel ?? "ws",
70
+ secure: options.secure,
71
+ send: options.send,
72
+ upgrade: options.upgrade,
73
+ isUpgrade: options.isUpgrade ?? ((request) => request.headers.get("Upgrade") === "websocket"),
74
+ hibernation: options.hibernation
75
+ };
76
+ }
77
+ //#endregion
78
+ export { ETransportStatus as i, isExchangeAcceptorCarrier as n, ETransportShape as r, wsAcceptorCarrier as t };
79
+
80
+ //# sourceMappingURL=wsAcceptorCarrier-CXGlQU_f.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsAcceptorCarrier-CXGlQU_f.mjs","names":[],"sources":["../src/ActionRuntime/Transport/Transport.types.ts","../src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.ts"],"sourcesContent":["import type { NiceError } from \"@nice-code/error\";\r\nimport type { ClientCryptoKeyLink } from \"@nice-code/util\";\r\nimport type {\r\n IActionPayload_Request_JsonObject,\r\n IActionPayload_Result_JsonObject,\r\n TActionPayload_Any_Instance,\r\n TActionPayload_Any_JsonObject,\r\n} from \"../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport type { ActionPayload_Request } from \"../../ActionDefinition/Action/Payload/ActionPayload_Request\";\r\nimport type { ActionPayload_Result } from \"../../ActionDefinition/Action/Payload/ActionPayload_Result\";\r\nimport type { RunningAction } from \"../../ActionDefinition/Action/RunningAction\";\r\nimport type { IRuntimeCoordinate, RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\n// Carrier-neutral handshake (the file keeps its WS-era name until Phase 3 renames it to crypto/actionHandshake).\r\nimport type { ESecurityLevel } from \"./crypto/actionHandshake\";\r\nimport type { TransportConnection } from \"./TransportConnection\";\r\n\r\n/**\r\n * Carrier-shape class identity: the one carrier-invariant trait the selection layer reasons about —\r\n * whether a carrier can push unsolicited frames (`duplex`: WS/WebRTC/BLE/in-memory) or only answers a\r\n * request with a single correlated reply (`exchange`: HTTP). The concrete carrier kind (\"ws\", \"webrtc\",\r\n * \"http\", …) is a free-form {@link ITransportRouteInfo.carrierLabel} for display, not a class tag.\r\n */\r\nexport enum ETransportShape {\r\n duplex = \"duplex\",\r\n exchange = \"exchange\",\r\n}\r\n\r\n/**\r\n * Serializable, display-only description of how an action was routed through a transport. Stored on\r\n * the action's route items and shown in the devtools external-handler chips. Keep this free of\r\n * sensitive data (e.g. auth headers) — route items travel over the wire.\r\n */\r\nexport interface ITransportRouteInfo {\r\n /** Free-form carrier kind for the devtools chip — e.g. \"ws\", \"webrtc\", \"http\". */\r\n carrierLabel: string;\r\n /** Short label for chips, e.g. \"POST /resolve_action\" or \"ws host/resolve_action/ws\". */\r\n summary?: string;\r\n url?: string;\r\n method?: string;\r\n detail?: Record<string, string | number | boolean>;\r\n}\r\n\r\nexport interface IUpdateActionRunConfig_Output {\r\n timeout?: number;\r\n}\r\n\r\nexport type TUpdateActionRunConfig = (\r\n input: ITransportRouteActionParams & { timeout: number },\r\n) => IUpdateActionRunConfig_Output;\r\n\r\nexport interface IActionTransportReadyData_Base {\r\n updateRunConfig?: TUpdateActionRunConfig;\r\n}\r\n\r\n/**\r\n * Client-side secure-channel config for a connector link — carrier-neutral (the secure session never\r\n * cared about the carrier). When present (and `securityLevel !== none`), the connection runs the\r\n * handshake during initialization and, at the `encrypted` level, encrypts every frame. The acceptor\r\n * side adds a negotiable level set + verify-key resolver on top of this (see Phase 3's `ISecureConfig`).\r\n */\r\nexport interface ISecureClientConfig {\r\n /** The level this client requests; the peer must allow it. */\r\n securityLevel: ESecurityLevel;\r\n /** This client's crypto identity (verify + exchange key pairs, optionally persisted). */\r\n link: ClientCryptoKeyLink;\r\n /** This client's runtime coordinate — its authenticated identity to the peer. */\r\n localCoordinate: IRuntimeCoordinate;\r\n /** Wire dictionary version; the peer rejects the handshake on a mismatch. */\r\n dictionaryVersion: string;\r\n}\r\n\r\n/**\r\n *\r\n * TRANSPORT READINESS RESPONSE\r\n *\r\n */\r\nexport enum ETransportStatus {\r\n uninitialized = \"uninitialized\",\r\n unsupported = \"unsupported\",\r\n initializing = \"initializing\",\r\n ready = \"ready\",\r\n failed = \"failed\",\r\n}\r\n\r\nexport interface ITransportStatusInfo_Base<S extends ETransportStatus> {\r\n status: S;\r\n}\r\n\r\nexport interface ITransportStatusInfo_Failed\r\n extends ITransportStatusInfo_Base<ETransportStatus.failed> {\r\n error: NiceError;\r\n timeFailed: number;\r\n}\r\n\r\nexport interface ITransportStatusInfo_Unsupported\r\n extends ITransportStatusInfo_Base<ETransportStatus.unsupported> {}\r\n\r\nexport interface ITransportStatusInfo_Ready<READY extends IActionTransportReadyData_Base>\r\n extends ITransportStatusInfo_Base<ETransportStatus.ready> {\r\n readyData: READY;\r\n}\r\n\r\nexport type TTransportInitializationFinishedInfo<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Ready<READY>\r\n | ITransportStatusInfo_Failed\r\n | ITransportStatusInfo_Unsupported;\r\n\r\nexport interface ITransportStatusInfo_Initializing<READY extends IActionTransportReadyData_Base>\r\n extends ITransportStatusInfo_Base<ETransportStatus.initializing> {\r\n timeStarted: number;\r\n initializationPromise: Promise<TTransportInitializationFinishedInfo<READY>>;\r\n}\r\n\r\nexport type TTransportStatusInfo<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Base<ETransportStatus.uninitialized>\r\n | ITransportStatusInfo_Unsupported\r\n | ITransportStatusInfo_Initializing<READY>\r\n | ITransportStatusInfo_Ready<READY>\r\n | ITransportStatusInfo_Failed;\r\n\r\nexport type TTransportStatusInfo_GetTransport_Output<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Base<ETransportStatus.uninitialized>\r\n | ITransportStatusInfo_Unsupported\r\n | Omit<ITransportStatusInfo_Initializing<READY>, \"timeStarted\">\r\n | ITransportStatusInfo_Ready<READY>\r\n | Omit<ITransportStatusInfo_Failed, \"timeFailed\">;\r\n\r\n/**\r\n *\r\n * TRANSPORT ROUTING\r\n *\r\n */\r\n\r\nexport interface ITransportRouteClientParams {\r\n localClient: RuntimeCoordinate;\r\n externalClient: RuntimeCoordinate;\r\n}\r\n\r\nexport interface ITransportRouteActionParams extends ITransportRouteClientParams {\r\n action: TActionPayload_Any_Instance<any, any>;\r\n}\r\n\r\nexport interface ITransportMethod_SendActionData_Input extends ITransportRouteActionParams {\r\n runningAction: RunningAction<any, any>;\r\n timeout: number;\r\n}\r\n\r\nexport interface ITransportDispatchAction<P> extends ITransportMethod_SendActionData_Input {\r\n params: P;\r\n}\r\n\r\nexport type TSendActionDataMethod = (input: ITransportMethod_SendActionData_Input) => void;\r\n\r\nexport type TSendReturnDataMethod = (\r\n payload: TActionPayload_Any_Instance<any, any>,\r\n /**\r\n * The local/external client pair this payload is being returned over. Bidirectional transports use\r\n * it to build the full route params their outgoing formatter expects (e.g. binary packing). Optional\r\n * so existing return-data implementations keep type-checking.\r\n */\r\n clients?: ITransportRouteClientParams,\r\n) => void;\r\n\r\nexport interface IActionTransportReadyData_Methods extends IActionTransportReadyData_Base {\r\n sendActionData: TSendActionDataMethod;\r\n /**\r\n * Optional — implement on bidirectional transports (WebSocket, Custom) to enable return-path\r\n * routing. When present, the runtime uses this to dispatch results and progress payloads directly\r\n * back to `originClient` without going through the original request transport.\r\n */\r\n sendReturnData?: TSendReturnDataMethod;\r\n addOnDisconnectListener?: (callback: () => void) => void;\r\n /**\r\n * Optional — implement on transports holding a long-lived connection (WebSocket, Custom) to close it\r\n * deliberately. Called by `ConnectorHandler.clearTransportCache()` so a teardown actually\r\n * releases the underlying socket instead of leaving it open until GC.\r\n */\r\n disconnect?: () => void;\r\n}\r\n\r\nexport interface IActionTransportReady {\r\n methods: IActionTransportReadyData_Methods;\r\n transport: TransportConnection;\r\n}\r\n\r\nexport type TTransportCache = Map<string, IActionTransportReady | Promise<IActionTransportReady>>;\r\n\r\nexport type TOnResolveIncomingRequest = (request: ActionPayload_Request<any>) => void;\r\nexport type TOnResolveIncomingRequestJson = (\r\n request: IActionPayload_Request_JsonObject<any>,\r\n) => void;\r\n\r\nexport type TOnResolveIncomingResponse = (response: ActionPayload_Result<any>) => void;\r\nexport type TOnResolveIncomingResponseJson = (\r\n response: IActionPayload_Result_JsonObject<any>,\r\n) => void;\r\n\r\nexport type TOnResolveAnyIncomingActionData = (\r\n actionData: ActionPayload_Request<any> | ActionPayload_Result<any>,\r\n) => void;\r\n\r\nexport type TOnResolveAnyIncomingActionData_Json = (\r\n actionData: TActionPayload_Any_JsonObject<any>,\r\n) => void;\r\n\r\nexport interface IActionTransportResolvers {\r\n onIncomingActionDataJson: TOnResolveAnyIncomingActionData_Json;\r\n}\r\n\r\nexport type TGetTransportFn<\r\n IN extends ITransportRouteActionParams,\r\n READY extends IActionTransportReadyData_Base,\r\n> = (input: IN) => TTransportStatusInfo_GetTransport_Output<READY>;\r\n\r\nexport interface IActionTransportInitialized<\r\n IN extends ITransportRouteActionParams,\r\n READY extends IActionTransportReadyData_Base,\r\n> {\r\n getTransportCacheKey?: (input: IN) => string[];\r\n getTransport: TGetTransportFn<IN, READY>;\r\n}\r\n\r\nexport interface IActionTransportDef<\r\n TYPE extends ETransportShape,\r\n INIT extends IActionTransportInitialized<any, any>,\r\n> {\r\n type: TYPE;\r\n initialize: () => INIT;\r\n}\r\n","import type { IAcceptorConnectionBinding } from \"../../Handler/PeerLink/Acceptor/AcceptorHandler\";\r\nimport type { IDuplexConnectionRouter } from \"../../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\r\nimport { ETransportShape } from \"../Transport.types\";\r\nimport type { TFrame } from \"./Carrier.types\";\r\n\r\n/**\r\n * Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}\r\n * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an\r\n * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral\r\n * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block\r\n * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates\r\n * it.\r\n *\r\n * Two shapes mirror the connector side:\r\n *\r\n * - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can\r\n * push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step\r\n * (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.\r\n * - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push\r\n * (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to\r\n * upgrade.\r\n */\r\n\r\n/**\r\n * Persistence + replay hooks for a duplex carrier whose connections outlive process eviction (e.g. a\r\n * Durable Object's hibernatable WebSockets). Optional — omit for a transport that never hibernates. The\r\n * same surface {@link createHibernatableWsServerAdapter} consumes, minus the handler it's bound to.\r\n */\r\nexport interface IAcceptorHibernation<TConn> {\r\n /** All currently-live connections — replayed on build to rebuild bindings after a wake. */\r\n getConnections: () => TConn[];\r\n /** Read a connection's persisted binding (e.g. `(ws) => ws.deserializeAttachment()`). */\r\n getAttachment: (connection: TConn) => IAcceptorConnectionBinding | undefined;\r\n /** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */\r\n setAttachment: (connection: TConn, binding: IAcceptorConnectionBinding) => void;\r\n}\r\n\r\n/**\r\n * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each\r\n * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a\r\n * server with *several* duplex carriers routes each connection's traffic to the right one — you hold the\r\n * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if\r\n * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)\r\n */\r\nexport interface IDuplexCarrierLifecycle<TConn> {\r\n /** Feed one inbound frame from a live connection into the server. Throws until the carrier is served. */\r\n receive(connection: TConn, frame: TFrame): void;\r\n /** Forget a connection on close/error. No-op until the carrier is served. */\r\n drop(connection: TConn): void;\r\n /** @internal `serveChannel` binds this carrier's live connection router here. */\r\n _activate(router: IDuplexConnectionRouter<TConn>): void;\r\n}\r\n\r\n/**\r\n * Build the inert lifecycle slot a duplex carrier factory spreads into its handle: `receive`/`drop` throw\r\n * (or no-op) until `serveChannel` calls `_activate` with the carrier's live router. Shared by every duplex\r\n * carrier factory (`wsAcceptorCarrier`, a WebRTC carrier, the Cloudflare DO helper, …) so the\r\n * stateful-handle wiring lives in exactly one place.\r\n */\r\nexport function createDuplexCarrierLifecycle<TConn>(): IDuplexCarrierLifecycle<TConn> {\r\n let router: IDuplexConnectionRouter<TConn> | undefined;\r\n return {\r\n receive(connection, frame) {\r\n if (router == null) {\r\n throw new Error(\r\n \"acceptor carrier not served yet — pass it to serveChannel() before feeding frames\",\r\n );\r\n }\r\n router.receive(connection, frame);\r\n },\r\n drop(connection) {\r\n router?.drop(connection);\r\n },\r\n _activate(liveRouter) {\r\n router = liveRouter;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live\r\n * connection, how to perform the transport-specific upgrade that admits one, which requests are such\r\n * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and\r\n * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see\r\n * {@link IDuplexCarrierLifecycle}).\r\n */\r\nexport interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {\r\n /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */\r\n readonly shape: ETransportShape.duplex;\r\n /**\r\n * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex\r\n * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no\r\n * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain\r\n * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a\r\n * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that\r\n * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /**\r\n * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.\r\n * Only consulted when {@link upgrade} is present.\r\n */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /** Optional persistence + replay for connections that survive eviction (Durable Object hibernation). */\r\n hibernation?: IAcceptorHibernation<TConn>;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By\r\n * default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose\r\n * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint\r\n * that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's\r\n * `plainTransport({ carrier: httpCarrier(...) })`. So a server can pair a secure duplex (WebSocket) with a\r\n * plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.\r\n */\r\nexport interface IExchangeAcceptorCarrier {\r\n /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */\r\n readonly shape: ETransportShape.exchange;\r\n /**\r\n * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain\r\n * endpoint: the body is the raw action wire and the result is the response body — no handshake, token,\r\n * or encryption. A plain endpoint ignores the central crypto identity entirely.\r\n */\r\n secure?: boolean;\r\n /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\r\n isActionPath?: (url: URL) => boolean;\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\r\n * permissive `*` set; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\r\n useErrorStatus?: boolean;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\nexport type TAcceptorCarrier<TConn = unknown> =\r\n | IDuplexAcceptorCarrier<TConn>\r\n | IExchangeAcceptorCarrier;\r\n\r\n/**\r\n * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch\r\n * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex\r\n * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.\r\n */\r\nexport function isExchangeAcceptorCarrier<TConn>(\r\n carrier: TAcceptorCarrier<TConn>,\r\n): carrier is IExchangeAcceptorCarrier {\r\n return carrier.shape === ETransportShape.exchange;\r\n}\r\n","import { ETransportShape } from \"../../../Transport.types\";\r\nimport {\r\n createDuplexCarrierLifecycle,\r\n type IAcceptorHibernation,\r\n type IDuplexAcceptorCarrier,\r\n} from \"../../AcceptorCarrier.types\";\r\nimport type { TFrame } from \"../../Carrier.types\";\r\n\r\nexport interface IWsAcceptorCarrierOptions<TConn> {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint\r\n * — connections speak the channel's wire codec with a self-asserted identity, no handshake/pins/encryption\r\n * (and `serveChannel` then needs no `storage` for this carrier).\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific WebSocket upgrade, returning its raw response (e.g. a Durable Object's\r\n * `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit if sockets are fed in out of band.\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /** Persistence + replay hooks for hibernatable sockets (e.g. a Durable Object). */\r\n hibernation?: IAcceptorHibernation<TConn>;\r\n /** Override the devtools carrier-kind label (defaults to `\"ws\"`). */\r\n carrierLabel?: string;\r\n}\r\n\r\n/**\r\n * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to\r\n * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to\r\n * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,\r\n * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.\r\n */\r\nexport function wsAcceptorCarrier<TConn = WebSocket>(\r\n options: IWsAcceptorCarrierOptions<TConn>,\r\n): IDuplexAcceptorCarrier<TConn> {\r\n return {\r\n ...createDuplexCarrierLifecycle<TConn>(),\r\n shape: ETransportShape.duplex,\r\n carrierLabel: options.carrierLabel ?? \"ws\",\r\n secure: options.secure,\r\n send: options.send,\r\n upgrade: options.upgrade,\r\n isUpgrade: options.isUpgrade ?? ((request) => request.headers.get(\"Upgrade\") === \"websocket\"),\r\n hibernation: options.hibernation,\r\n };\r\n}\r\n"],"mappings":";;;;;;;AAsBA,IAAY,kBAAL,yBAAA,iBAAA;CACL,gBAAA,YAAA;CACA,gBAAA,cAAA;;AACF,EAAA,CAAA,CAAA;;;;;;AAmDA,IAAY,mBAAL,yBAAA,kBAAA;CACL,iBAAA,mBAAA;CACA,iBAAA,iBAAA;CACA,iBAAA,kBAAA;CACA,iBAAA,WAAA;CACA,iBAAA,YAAA;;AACF,EAAA,CAAA,CAAA;;;;;;;;;ACvBA,SAAgB,+BAAsE;CACpF,IAAI;CACJ,OAAO;EACL,QAAQ,YAAY,OAAO;GACzB,IAAI,UAAU,MACZ,MAAM,IAAI,MACR,mFACF;GAEF,OAAO,QAAQ,YAAY,KAAK;EAClC;EACA,KAAK,YAAY;GACf,QAAQ,KAAK,UAAU;EACzB;EACA,UAAU,YAAY;GACpB,SAAS;EACX;CACF;AACF;;;;;;AA6EA,SAAgB,0BACd,SACqC;CACrC,OAAO,QAAQ,UAAA;AACjB;;;;;;;;;AC1HA,SAAgB,kBACd,SAC+B;CAC/B,OAAO;EACL,GAAG,6BAAoC;EACvC,OAAA;EACA,cAAc,QAAQ,gBAAgB;EACtC,QAAQ,QAAQ;EAChB,MAAM,QAAQ;EACd,SAAS,QAAQ;EACjB,WAAW,QAAQ,eAAe,YAAY,QAAQ,QAAQ,IAAI,SAAS,MAAM;EACjF,aAAa,QAAQ;CACvB;AACF"}
@@ -0,0 +1,103 @@
1
+ //#region src/ActionRuntime/Transport/Transport.types.ts
2
+ /**
3
+ * Carrier-shape class identity: the one carrier-invariant trait the selection layer reasons about —
4
+ * whether a carrier can push unsolicited frames (`duplex`: WS/WebRTC/BLE/in-memory) or only answers a
5
+ * request with a single correlated reply (`exchange`: HTTP). The concrete carrier kind ("ws", "webrtc",
6
+ * "http", …) is a free-form {@link ITransportRouteInfo.carrierLabel} for display, not a class tag.
7
+ */
8
+ let ETransportShape = /* @__PURE__ */ function(ETransportShape) {
9
+ ETransportShape["duplex"] = "duplex";
10
+ ETransportShape["exchange"] = "exchange";
11
+ return ETransportShape;
12
+ }({});
13
+ /**
14
+ *
15
+ * TRANSPORT READINESS RESPONSE
16
+ *
17
+ */
18
+ let ETransportStatus = /* @__PURE__ */ function(ETransportStatus) {
19
+ ETransportStatus["uninitialized"] = "uninitialized";
20
+ ETransportStatus["unsupported"] = "unsupported";
21
+ ETransportStatus["initializing"] = "initializing";
22
+ ETransportStatus["ready"] = "ready";
23
+ ETransportStatus["failed"] = "failed";
24
+ return ETransportStatus;
25
+ }({});
26
+ //#endregion
27
+ //#region src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.ts
28
+ /**
29
+ * Build the inert lifecycle slot a duplex carrier factory spreads into its handle: `receive`/`drop` throw
30
+ * (or no-op) until `serveChannel` calls `_activate` with the carrier's live router. Shared by every duplex
31
+ * carrier factory (`wsAcceptorCarrier`, a WebRTC carrier, the Cloudflare DO helper, …) so the
32
+ * stateful-handle wiring lives in exactly one place.
33
+ */
34
+ function createDuplexCarrierLifecycle() {
35
+ let router;
36
+ return {
37
+ receive(connection, frame) {
38
+ if (router == null) throw new Error("acceptor carrier not served yet — pass it to serveChannel() before feeding frames");
39
+ router.receive(connection, frame);
40
+ },
41
+ drop(connection) {
42
+ router?.drop(connection);
43
+ },
44
+ _activate(liveRouter) {
45
+ router = liveRouter;
46
+ }
47
+ };
48
+ }
49
+ /**
50
+ * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch
51
+ * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex
52
+ * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.
53
+ */
54
+ function isExchangeAcceptorCarrier(carrier) {
55
+ return carrier.shape === "exchange";
56
+ }
57
+ //#endregion
58
+ //#region src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.ts
59
+ /**
60
+ * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to
61
+ * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to
62
+ * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,
63
+ * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.
64
+ */
65
+ function wsAcceptorCarrier(options) {
66
+ return {
67
+ ...createDuplexCarrierLifecycle(),
68
+ shape: "duplex",
69
+ carrierLabel: options.carrierLabel ?? "ws",
70
+ secure: options.secure,
71
+ send: options.send,
72
+ upgrade: options.upgrade,
73
+ isUpgrade: options.isUpgrade ?? ((request) => request.headers.get("Upgrade") === "websocket"),
74
+ hibernation: options.hibernation
75
+ };
76
+ }
77
+ //#endregion
78
+ Object.defineProperty(exports, "ETransportShape", {
79
+ enumerable: true,
80
+ get: function() {
81
+ return ETransportShape;
82
+ }
83
+ });
84
+ Object.defineProperty(exports, "ETransportStatus", {
85
+ enumerable: true,
86
+ get: function() {
87
+ return ETransportStatus;
88
+ }
89
+ });
90
+ Object.defineProperty(exports, "isExchangeAcceptorCarrier", {
91
+ enumerable: true,
92
+ get: function() {
93
+ return isExchangeAcceptorCarrier;
94
+ }
95
+ });
96
+ Object.defineProperty(exports, "wsAcceptorCarrier", {
97
+ enumerable: true,
98
+ get: function() {
99
+ return wsAcceptorCarrier;
100
+ }
101
+ });
102
+
103
+ //# sourceMappingURL=wsAcceptorCarrier-DHRbsY1X.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsAcceptorCarrier-DHRbsY1X.cjs","names":[],"sources":["../src/ActionRuntime/Transport/Transport.types.ts","../src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.ts"],"sourcesContent":["import type { NiceError } from \"@nice-code/error\";\r\nimport type { ClientCryptoKeyLink } from \"@nice-code/util\";\r\nimport type {\r\n IActionPayload_Request_JsonObject,\r\n IActionPayload_Result_JsonObject,\r\n TActionPayload_Any_Instance,\r\n TActionPayload_Any_JsonObject,\r\n} from \"../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport type { ActionPayload_Request } from \"../../ActionDefinition/Action/Payload/ActionPayload_Request\";\r\nimport type { ActionPayload_Result } from \"../../ActionDefinition/Action/Payload/ActionPayload_Result\";\r\nimport type { RunningAction } from \"../../ActionDefinition/Action/RunningAction\";\r\nimport type { IRuntimeCoordinate, RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\n// Carrier-neutral handshake (the file keeps its WS-era name until Phase 3 renames it to crypto/actionHandshake).\r\nimport type { ESecurityLevel } from \"./crypto/actionHandshake\";\r\nimport type { TransportConnection } from \"./TransportConnection\";\r\n\r\n/**\r\n * Carrier-shape class identity: the one carrier-invariant trait the selection layer reasons about —\r\n * whether a carrier can push unsolicited frames (`duplex`: WS/WebRTC/BLE/in-memory) or only answers a\r\n * request with a single correlated reply (`exchange`: HTTP). The concrete carrier kind (\"ws\", \"webrtc\",\r\n * \"http\", …) is a free-form {@link ITransportRouteInfo.carrierLabel} for display, not a class tag.\r\n */\r\nexport enum ETransportShape {\r\n duplex = \"duplex\",\r\n exchange = \"exchange\",\r\n}\r\n\r\n/**\r\n * Serializable, display-only description of how an action was routed through a transport. Stored on\r\n * the action's route items and shown in the devtools external-handler chips. Keep this free of\r\n * sensitive data (e.g. auth headers) — route items travel over the wire.\r\n */\r\nexport interface ITransportRouteInfo {\r\n /** Free-form carrier kind for the devtools chip — e.g. \"ws\", \"webrtc\", \"http\". */\r\n carrierLabel: string;\r\n /** Short label for chips, e.g. \"POST /resolve_action\" or \"ws host/resolve_action/ws\". */\r\n summary?: string;\r\n url?: string;\r\n method?: string;\r\n detail?: Record<string, string | number | boolean>;\r\n}\r\n\r\nexport interface IUpdateActionRunConfig_Output {\r\n timeout?: number;\r\n}\r\n\r\nexport type TUpdateActionRunConfig = (\r\n input: ITransportRouteActionParams & { timeout: number },\r\n) => IUpdateActionRunConfig_Output;\r\n\r\nexport interface IActionTransportReadyData_Base {\r\n updateRunConfig?: TUpdateActionRunConfig;\r\n}\r\n\r\n/**\r\n * Client-side secure-channel config for a connector link — carrier-neutral (the secure session never\r\n * cared about the carrier). When present (and `securityLevel !== none`), the connection runs the\r\n * handshake during initialization and, at the `encrypted` level, encrypts every frame. The acceptor\r\n * side adds a negotiable level set + verify-key resolver on top of this (see Phase 3's `ISecureConfig`).\r\n */\r\nexport interface ISecureClientConfig {\r\n /** The level this client requests; the peer must allow it. */\r\n securityLevel: ESecurityLevel;\r\n /** This client's crypto identity (verify + exchange key pairs, optionally persisted). */\r\n link: ClientCryptoKeyLink;\r\n /** This client's runtime coordinate — its authenticated identity to the peer. */\r\n localCoordinate: IRuntimeCoordinate;\r\n /** Wire dictionary version; the peer rejects the handshake on a mismatch. */\r\n dictionaryVersion: string;\r\n}\r\n\r\n/**\r\n *\r\n * TRANSPORT READINESS RESPONSE\r\n *\r\n */\r\nexport enum ETransportStatus {\r\n uninitialized = \"uninitialized\",\r\n unsupported = \"unsupported\",\r\n initializing = \"initializing\",\r\n ready = \"ready\",\r\n failed = \"failed\",\r\n}\r\n\r\nexport interface ITransportStatusInfo_Base<S extends ETransportStatus> {\r\n status: S;\r\n}\r\n\r\nexport interface ITransportStatusInfo_Failed\r\n extends ITransportStatusInfo_Base<ETransportStatus.failed> {\r\n error: NiceError;\r\n timeFailed: number;\r\n}\r\n\r\nexport interface ITransportStatusInfo_Unsupported\r\n extends ITransportStatusInfo_Base<ETransportStatus.unsupported> {}\r\n\r\nexport interface ITransportStatusInfo_Ready<READY extends IActionTransportReadyData_Base>\r\n extends ITransportStatusInfo_Base<ETransportStatus.ready> {\r\n readyData: READY;\r\n}\r\n\r\nexport type TTransportInitializationFinishedInfo<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Ready<READY>\r\n | ITransportStatusInfo_Failed\r\n | ITransportStatusInfo_Unsupported;\r\n\r\nexport interface ITransportStatusInfo_Initializing<READY extends IActionTransportReadyData_Base>\r\n extends ITransportStatusInfo_Base<ETransportStatus.initializing> {\r\n timeStarted: number;\r\n initializationPromise: Promise<TTransportInitializationFinishedInfo<READY>>;\r\n}\r\n\r\nexport type TTransportStatusInfo<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Base<ETransportStatus.uninitialized>\r\n | ITransportStatusInfo_Unsupported\r\n | ITransportStatusInfo_Initializing<READY>\r\n | ITransportStatusInfo_Ready<READY>\r\n | ITransportStatusInfo_Failed;\r\n\r\nexport type TTransportStatusInfo_GetTransport_Output<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Base<ETransportStatus.uninitialized>\r\n | ITransportStatusInfo_Unsupported\r\n | Omit<ITransportStatusInfo_Initializing<READY>, \"timeStarted\">\r\n | ITransportStatusInfo_Ready<READY>\r\n | Omit<ITransportStatusInfo_Failed, \"timeFailed\">;\r\n\r\n/**\r\n *\r\n * TRANSPORT ROUTING\r\n *\r\n */\r\n\r\nexport interface ITransportRouteClientParams {\r\n localClient: RuntimeCoordinate;\r\n externalClient: RuntimeCoordinate;\r\n}\r\n\r\nexport interface ITransportRouteActionParams extends ITransportRouteClientParams {\r\n action: TActionPayload_Any_Instance<any, any>;\r\n}\r\n\r\nexport interface ITransportMethod_SendActionData_Input extends ITransportRouteActionParams {\r\n runningAction: RunningAction<any, any>;\r\n timeout: number;\r\n}\r\n\r\nexport interface ITransportDispatchAction<P> extends ITransportMethod_SendActionData_Input {\r\n params: P;\r\n}\r\n\r\nexport type TSendActionDataMethod = (input: ITransportMethod_SendActionData_Input) => void;\r\n\r\nexport type TSendReturnDataMethod = (\r\n payload: TActionPayload_Any_Instance<any, any>,\r\n /**\r\n * The local/external client pair this payload is being returned over. Bidirectional transports use\r\n * it to build the full route params their outgoing formatter expects (e.g. binary packing). Optional\r\n * so existing return-data implementations keep type-checking.\r\n */\r\n clients?: ITransportRouteClientParams,\r\n) => void;\r\n\r\nexport interface IActionTransportReadyData_Methods extends IActionTransportReadyData_Base {\r\n sendActionData: TSendActionDataMethod;\r\n /**\r\n * Optional — implement on bidirectional transports (WebSocket, Custom) to enable return-path\r\n * routing. When present, the runtime uses this to dispatch results and progress payloads directly\r\n * back to `originClient` without going through the original request transport.\r\n */\r\n sendReturnData?: TSendReturnDataMethod;\r\n addOnDisconnectListener?: (callback: () => void) => void;\r\n /**\r\n * Optional — implement on transports holding a long-lived connection (WebSocket, Custom) to close it\r\n * deliberately. Called by `ConnectorHandler.clearTransportCache()` so a teardown actually\r\n * releases the underlying socket instead of leaving it open until GC.\r\n */\r\n disconnect?: () => void;\r\n}\r\n\r\nexport interface IActionTransportReady {\r\n methods: IActionTransportReadyData_Methods;\r\n transport: TransportConnection;\r\n}\r\n\r\nexport type TTransportCache = Map<string, IActionTransportReady | Promise<IActionTransportReady>>;\r\n\r\nexport type TOnResolveIncomingRequest = (request: ActionPayload_Request<any>) => void;\r\nexport type TOnResolveIncomingRequestJson = (\r\n request: IActionPayload_Request_JsonObject<any>,\r\n) => void;\r\n\r\nexport type TOnResolveIncomingResponse = (response: ActionPayload_Result<any>) => void;\r\nexport type TOnResolveIncomingResponseJson = (\r\n response: IActionPayload_Result_JsonObject<any>,\r\n) => void;\r\n\r\nexport type TOnResolveAnyIncomingActionData = (\r\n actionData: ActionPayload_Request<any> | ActionPayload_Result<any>,\r\n) => void;\r\n\r\nexport type TOnResolveAnyIncomingActionData_Json = (\r\n actionData: TActionPayload_Any_JsonObject<any>,\r\n) => void;\r\n\r\nexport interface IActionTransportResolvers {\r\n onIncomingActionDataJson: TOnResolveAnyIncomingActionData_Json;\r\n}\r\n\r\nexport type TGetTransportFn<\r\n IN extends ITransportRouteActionParams,\r\n READY extends IActionTransportReadyData_Base,\r\n> = (input: IN) => TTransportStatusInfo_GetTransport_Output<READY>;\r\n\r\nexport interface IActionTransportInitialized<\r\n IN extends ITransportRouteActionParams,\r\n READY extends IActionTransportReadyData_Base,\r\n> {\r\n getTransportCacheKey?: (input: IN) => string[];\r\n getTransport: TGetTransportFn<IN, READY>;\r\n}\r\n\r\nexport interface IActionTransportDef<\r\n TYPE extends ETransportShape,\r\n INIT extends IActionTransportInitialized<any, any>,\r\n> {\r\n type: TYPE;\r\n initialize: () => INIT;\r\n}\r\n","import type { IAcceptorConnectionBinding } from \"../../Handler/PeerLink/Acceptor/AcceptorHandler\";\r\nimport type { IDuplexConnectionRouter } from \"../../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\r\nimport { ETransportShape } from \"../Transport.types\";\r\nimport type { TFrame } from \"./Carrier.types\";\r\n\r\n/**\r\n * Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}\r\n * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an\r\n * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral\r\n * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block\r\n * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates\r\n * it.\r\n *\r\n * Two shapes mirror the connector side:\r\n *\r\n * - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can\r\n * push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step\r\n * (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.\r\n * - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push\r\n * (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to\r\n * upgrade.\r\n */\r\n\r\n/**\r\n * Persistence + replay hooks for a duplex carrier whose connections outlive process eviction (e.g. a\r\n * Durable Object's hibernatable WebSockets). Optional — omit for a transport that never hibernates. The\r\n * same surface {@link createHibernatableWsServerAdapter} consumes, minus the handler it's bound to.\r\n */\r\nexport interface IAcceptorHibernation<TConn> {\r\n /** All currently-live connections — replayed on build to rebuild bindings after a wake. */\r\n getConnections: () => TConn[];\r\n /** Read a connection's persisted binding (e.g. `(ws) => ws.deserializeAttachment()`). */\r\n getAttachment: (connection: TConn) => IAcceptorConnectionBinding | undefined;\r\n /** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */\r\n setAttachment: (connection: TConn, binding: IAcceptorConnectionBinding) => void;\r\n}\r\n\r\n/**\r\n * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each\r\n * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a\r\n * server with *several* duplex carriers routes each connection's traffic to the right one — you hold the\r\n * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if\r\n * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)\r\n */\r\nexport interface IDuplexCarrierLifecycle<TConn> {\r\n /** Feed one inbound frame from a live connection into the server. Throws until the carrier is served. */\r\n receive(connection: TConn, frame: TFrame): void;\r\n /** Forget a connection on close/error. No-op until the carrier is served. */\r\n drop(connection: TConn): void;\r\n /** @internal `serveChannel` binds this carrier's live connection router here. */\r\n _activate(router: IDuplexConnectionRouter<TConn>): void;\r\n}\r\n\r\n/**\r\n * Build the inert lifecycle slot a duplex carrier factory spreads into its handle: `receive`/`drop` throw\r\n * (or no-op) until `serveChannel` calls `_activate` with the carrier's live router. Shared by every duplex\r\n * carrier factory (`wsAcceptorCarrier`, a WebRTC carrier, the Cloudflare DO helper, …) so the\r\n * stateful-handle wiring lives in exactly one place.\r\n */\r\nexport function createDuplexCarrierLifecycle<TConn>(): IDuplexCarrierLifecycle<TConn> {\r\n let router: IDuplexConnectionRouter<TConn> | undefined;\r\n return {\r\n receive(connection, frame) {\r\n if (router == null) {\r\n throw new Error(\r\n \"acceptor carrier not served yet — pass it to serveChannel() before feeding frames\",\r\n );\r\n }\r\n router.receive(connection, frame);\r\n },\r\n drop(connection) {\r\n router?.drop(connection);\r\n },\r\n _activate(liveRouter) {\r\n router = liveRouter;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live\r\n * connection, how to perform the transport-specific upgrade that admits one, which requests are such\r\n * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and\r\n * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see\r\n * {@link IDuplexCarrierLifecycle}).\r\n */\r\nexport interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {\r\n /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */\r\n readonly shape: ETransportShape.duplex;\r\n /**\r\n * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex\r\n * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no\r\n * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain\r\n * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a\r\n * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that\r\n * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /**\r\n * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.\r\n * Only consulted when {@link upgrade} is present.\r\n */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /** Optional persistence + replay for connections that survive eviction (Durable Object hibernation). */\r\n hibernation?: IAcceptorHibernation<TConn>;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By\r\n * default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose\r\n * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint\r\n * that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's\r\n * `plainTransport({ carrier: httpCarrier(...) })`. So a server can pair a secure duplex (WebSocket) with a\r\n * plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.\r\n */\r\nexport interface IExchangeAcceptorCarrier {\r\n /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */\r\n readonly shape: ETransportShape.exchange;\r\n /**\r\n * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain\r\n * endpoint: the body is the raw action wire and the result is the response body — no handshake, token,\r\n * or encryption. A plain endpoint ignores the central crypto identity entirely.\r\n */\r\n secure?: boolean;\r\n /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\r\n isActionPath?: (url: URL) => boolean;\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\r\n * permissive `*` set; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\r\n useErrorStatus?: boolean;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\nexport type TAcceptorCarrier<TConn = unknown> =\r\n | IDuplexAcceptorCarrier<TConn>\r\n | IExchangeAcceptorCarrier;\r\n\r\n/**\r\n * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch\r\n * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex\r\n * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.\r\n */\r\nexport function isExchangeAcceptorCarrier<TConn>(\r\n carrier: TAcceptorCarrier<TConn>,\r\n): carrier is IExchangeAcceptorCarrier {\r\n return carrier.shape === ETransportShape.exchange;\r\n}\r\n","import { ETransportShape } from \"../../../Transport.types\";\r\nimport {\r\n createDuplexCarrierLifecycle,\r\n type IAcceptorHibernation,\r\n type IDuplexAcceptorCarrier,\r\n} from \"../../AcceptorCarrier.types\";\r\nimport type { TFrame } from \"../../Carrier.types\";\r\n\r\nexport interface IWsAcceptorCarrierOptions<TConn> {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint\r\n * — connections speak the channel's wire codec with a self-asserted identity, no handshake/pins/encryption\r\n * (and `serveChannel` then needs no `storage` for this carrier).\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific WebSocket upgrade, returning its raw response (e.g. a Durable Object's\r\n * `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit if sockets are fed in out of band.\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /** Persistence + replay hooks for hibernatable sockets (e.g. a Durable Object). */\r\n hibernation?: IAcceptorHibernation<TConn>;\r\n /** Override the devtools carrier-kind label (defaults to `\"ws\"`). */\r\n carrierLabel?: string;\r\n}\r\n\r\n/**\r\n * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to\r\n * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to\r\n * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,\r\n * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.\r\n */\r\nexport function wsAcceptorCarrier<TConn = WebSocket>(\r\n options: IWsAcceptorCarrierOptions<TConn>,\r\n): IDuplexAcceptorCarrier<TConn> {\r\n return {\r\n ...createDuplexCarrierLifecycle<TConn>(),\r\n shape: ETransportShape.duplex,\r\n carrierLabel: options.carrierLabel ?? \"ws\",\r\n secure: options.secure,\r\n send: options.send,\r\n upgrade: options.upgrade,\r\n isUpgrade: options.isUpgrade ?? ((request) => request.headers.get(\"Upgrade\") === \"websocket\"),\r\n hibernation: options.hibernation,\r\n };\r\n}\r\n"],"mappings":";;;;;;;AAsBA,IAAY,kBAAL,yBAAA,iBAAA;CACL,gBAAA,YAAA;CACA,gBAAA,cAAA;;AACF,EAAA,CAAA,CAAA;;;;;;AAmDA,IAAY,mBAAL,yBAAA,kBAAA;CACL,iBAAA,mBAAA;CACA,iBAAA,iBAAA;CACA,iBAAA,kBAAA;CACA,iBAAA,WAAA;CACA,iBAAA,YAAA;;AACF,EAAA,CAAA,CAAA;;;;;;;;;ACvBA,SAAgB,+BAAsE;CACpF,IAAI;CACJ,OAAO;EACL,QAAQ,YAAY,OAAO;GACzB,IAAI,UAAU,MACZ,MAAM,IAAI,MACR,mFACF;GAEF,OAAO,QAAQ,YAAY,KAAK;EAClC;EACA,KAAK,YAAY;GACf,QAAQ,KAAK,UAAU;EACzB;EACA,UAAU,YAAY;GACpB,SAAS;EACX;CACF;AACF;;;;;;AA6EA,SAAgB,0BACd,SACqC;CACrC,OAAO,QAAQ,UAAA;AACjB;;;;;;;;;AC1HA,SAAgB,kBACd,SAC+B;CAC/B,OAAO;EACL,GAAG,6BAAoC;EACvC,OAAA;EACA,cAAc,QAAQ,gBAAgB;EACtC,QAAQ,QAAQ;EAChB,MAAM,QAAQ;EACd,SAAS,QAAQ;EACjB,WAAW,QAAQ,eAAe,YAAY,QAAQ,QAAQ,IAAI,SAAS,MAAM;EACjF,aAAa,QAAQ;CACvB;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nice-code/action",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "main": "./build/index.cjs",
5
5
  "module": "./build/index.mjs",
6
6
  "types": "./build/index.d.cts",
@@ -16,6 +16,17 @@
16
16
  "default": "./build/index.cjs"
17
17
  }
18
18
  },
19
+ "./platform/cloudflare": {
20
+ "source": "./src/platform/cloudflare/index.ts",
21
+ "import": {
22
+ "types": "./build/platform/cloudflare/index.d.mts",
23
+ "default": "./build/platform/cloudflare/index.mjs"
24
+ },
25
+ "require": {
26
+ "types": "./build/platform/cloudflare/index.d.cts",
27
+ "default": "./build/platform/cloudflare/index.cjs"
28
+ }
29
+ },
19
30
  "./devtools/browser": {
20
31
  "source": "./src/devtools/browser/index.ts",
21
32
  "import": {
@@ -75,9 +86,9 @@
75
86
  },
76
87
  "type": "module",
77
88
  "dependencies": {
78
- "@nice-code/common-errors": "0.16.0",
79
- "@nice-code/error": "0.16.0",
80
- "@nice-code/util": "0.16.0",
89
+ "@nice-code/common-errors": "0.18.0",
90
+ "@nice-code/error": "0.18.0",
91
+ "@nice-code/util": "0.18.0",
81
92
  "@standard-schema/spec": "^1.1.0",
82
93
  "@tanstack/react-virtual": "^3.13.26",
83
94
  "http-status-codes": "^2.3.0",