@statewalker/cellb 0.1.1 → 0.3.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/README.md +51 -25
- package/dist/adapters/wasm-sqlite.d.ts +11 -0
- package/dist/adapters/wasm-sqlite.js +26 -3
- package/dist/adapters/wasm-sqlite.js.map +1 -1
- package/dist/bindings/assets.js +2 -2
- package/dist/bindings/assets.js.map +1 -1
- package/dist/bindings/r2.js +2 -2
- package/dist/bindings/r2.js.map +1 -1
- package/dist/browser.d.ts +0 -1
- package/dist/browser.js +0 -1
- package/dist/browser.js.map +1 -1
- package/dist/bucket/celld-cells.js +1 -1
- package/dist/bucket/celld-cells.js.map +1 -1
- package/dist/bucket/s3-object-store.js +1 -1
- package/dist/bucket/s3-object-store.js.map +1 -1
- package/dist/core/handles.d.ts +5 -2
- package/dist/core/handles.js +12 -1
- package/dist/core/handles.js.map +1 -1
- package/dist/core/id.js +3 -3
- package/dist/core/id.js.map +1 -1
- package/dist/core/namespace.d.ts +1 -1
- package/dist/core/namespace.js +2 -1
- package/dist/core/namespace.js.map +1 -1
- package/dist/core/object-host.d.ts +23 -0
- package/dist/core/object-host.js +87 -6
- package/dist/core/object-host.js.map +1 -1
- package/dist/core/runtime.d.ts +10 -0
- package/dist/core/runtime.js +43 -2
- package/dist/core/runtime.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/isolate/browser.js +1 -1
- package/dist/isolate/browser.js.map +1 -1
- package/dist/isolate/host.d.ts +5 -1
- package/dist/isolate/host.js +29 -2
- package/dist/isolate/host.js.map +1 -1
- package/dist/isolate/isolate-worker.js +11 -1
- package/dist/isolate/isolate-worker.js.map +1 -1
- package/dist/isolate/isolate.js +20 -0
- package/dist/isolate/isolate.js.map +1 -1
- package/dist/isolate/node-entry.js +11 -0
- package/dist/isolate/node-entry.js.map +1 -1
- package/dist/isolate/protocol.d.ts +5 -0
- package/dist/isolate/protocol.js +114 -2
- package/dist/isolate/protocol.js.map +1 -1
- package/dist/node-compat/crypto.js +3 -3
- package/dist/node-compat/crypto.js.map +1 -1
- package/dist/peer/celld-peer.d.ts +17 -0
- package/dist/peer/celld-peer.js +49 -7
- package/dist/peer/celld-peer.js.map +1 -1
- package/dist/project/browser-host.js +3 -2
- package/dist/project/browser-host.js.map +1 -1
- package/dist/project/node-project.js +2 -1
- package/dist/project/node-project.js.map +1 -1
- package/dist/project/site-loader.d.ts +45 -0
- package/dist/project/site-loader.js +97 -0
- package/dist/project/site-loader.js.map +1 -0
- package/dist/project/site.d.ts +0 -36
- package/dist/project/site.js +11 -99
- package/dist/project/site.js.map +1 -1
- package/dist/project/wrangler.js +10 -0
- package/dist/project/wrangler.js.map +1 -1
- package/dist/router/cell-router.js +1 -1
- package/dist/router/cell-router.js.map +1 -1
- package/dist/site.d.ts +1 -0
- package/dist/site.js +6 -0
- package/dist/site.js.map +1 -0
- package/dist/transport/port.d.ts +3 -4
- package/dist/transport/port.js +21 -79
- package/dist/transport/port.js.map +1 -1
- package/dist/ws/client.d.ts +23 -8
- package/dist/ws/client.js +50 -189
- package/dist/ws/client.js.map +1 -1
- package/dist/ws/frames.d.ts +31 -8
- package/dist/ws/frames.js +54 -7
- package/dist/ws/frames.js.map +1 -1
- package/dist/ws/server.d.ts +4 -0
- package/dist/ws/server.js +29 -5
- package/dist/ws/server.js.map +1 -1
- package/package.json +29 -10
package/dist/ws/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/ws/client.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n byteSize,\n CellsCloseEvent,\n checkClose,\n DEFAULT_HEARTBEAT_MS,\n type Frame,\n toFrameData,\n} from \"./frames.js\";\n\n/** Opens the transport of a socket: resolves with the MessagePort once the server accepted (101). */\nexport type WebSocketConnector = (\n url: string,\n protocols: string[],\n) => Promise<{ port: MessagePort; protocol?: string }>;\n\ntype Handler = ((ev: any) => void) | null;\n\n/**\n * A WHATWG-WebSocket-compatible class whose transport is a MessagePort obtained\n * from `connect` (page → ServiceWorker → owning shard worker, or in-process).\n * Installed for user code through webrun-modules `~deps` (provided `WebSocket`),\n * so the real `globalThis.WebSocket` is left untouched.\n */\n/** RFC 7230 token (subprotocol names). */\nconst TOKEN = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n\nexport interface CellsWebSocketOptions {\n /** Liveness ping period (ms); 0 disables. The peer is declared dead after 2 missed periods. */\n heartbeatMs?: number;\n /**\n * Interval timers for the heartbeat. On a page's main thread pass worker-timers'\n * `setInterval`/`clearInterval`: background-tab throttling of main-thread timers would\n * otherwise delay pings and make the server drop a live socket. Workers and Node do not\n * throttle, so the built-in timers are the default.\n */\n timers?: { setInterval(fn: () => void, ms: number): unknown; clearInterval(id: unknown): void };\n}\n\n/**\n * A WHATWG `WebSocket` class whose sockets reach a Worker through `connect`. Typed as the\n * platform's `WebSocket` (the class implements that interface; its own state is private).\n */\nexport function createWebSocketClass(\n connect: WebSocketConnector,\n options: CellsWebSocketOptions = {},\n): typeof WebSocket {\n const heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;\n const timers = options.timers ?? {\n setInterval: (fn: () => void, ms: number) => setInterval(fn, ms),\n clearInterval: (id: unknown) => clearInterval(id as ReturnType<typeof setInterval>),\n };\n class CellsWebSocket extends EventTarget {\n static readonly CONNECTING = 0;\n static readonly OPEN = 1;\n static readonly CLOSING = 2;\n static readonly CLOSED = 3;\n readonly CONNECTING = 0;\n readonly OPEN = 1;\n readonly CLOSING = 2;\n readonly CLOSED = 3;\n\n readonly url: string;\n readyState = 0;\n protocol = \"\";\n extensions = \"\";\n #buffered = 0;\n /** Sizes of the messages posted and not acknowledged yet, in order (the port keeps it). */\n #unacked: number[] = [];\n /** Bytes sent and not yet received by the server end (it acknowledges each message). */\n get bufferedAmount(): number {\n return this.#buffered;\n }\n #binaryType: \"blob\" | \"arraybuffer\" = \"blob\";\n get binaryType(): \"blob\" | \"arraybuffer\" {\n return this.#binaryType;\n }\n /** Only \"blob\" and \"arraybuffer\" are taken; any other value is ignored (WHATWG). */\n set binaryType(v: \"blob\" | \"arraybuffer\") {\n if (v === \"blob\" || v === \"arraybuffer\") this.#binaryType = v;\n }\n onopen: Handler = null;\n onmessage: Handler = null;\n onclose: Handler = null;\n onerror: Handler = null;\n #port: MessagePort | undefined;\n #pendingClose: { code?: number; reason?: string } | undefined;\n #beat: unknown;\n #lastPong = 0;\n\n #startHeartbeat(): void {\n if (!heartbeatMs) return;\n this.#lastPong = Date.now();\n this.#beat = timers.setInterval(() => {\n if (this.readyState >= 3) return this.#stopHeartbeat();\n if (Date.now() - this.#lastPong > 2 * heartbeatMs) return this.#fail(); // peer gone\n this.#port?.postMessage({ t: \"p\", hb: heartbeatMs } satisfies Frame);\n }, heartbeatMs);\n this.#port?.postMessage({ t: \"p\", hb: heartbeatMs } satisfies Frame);\n }\n #stopHeartbeat(): void {\n if (this.#beat !== undefined) timers.clearInterval(this.#beat);\n this.#beat = undefined;\n }\n\n constructor(url: string | URL, protocols: string | string[] = []) {\n super();\n // WHATWG \"new WebSocket(url, protocols)\" validation, in spec order.\n let u: URL;\n try {\n u = new URL(String(url), (globalThis as any).location?.href ?? \"http://localhost/\");\n } catch {\n throw new DOMException(`The URL '${String(url)}' is invalid.`, \"SyntaxError\");\n }\n if (u.protocol === \"http:\") u.protocol = \"ws:\";\n if (u.protocol === \"https:\") u.protocol = \"wss:\";\n if (u.protocol !== \"ws:\" && u.protocol !== \"wss:\")\n throw new DOMException(`The URL's scheme must be either 'ws' or 'wss'.`, \"SyntaxError\");\n if (u.hash || String(url).includes(\"#\"))\n throw new DOMException(\"The URL contains a fragment identifier.\", \"SyntaxError\");\n const list = typeof protocols === \"string\" ? [protocols] : [...protocols];\n const seen = new Set<string>();\n for (const p of list) {\n if (!TOKEN.test(p))\n throw new DOMException(`The subprotocol '${p}' is invalid.`, \"SyntaxError\");\n if (seen.has(p.toLowerCase()))\n throw new DOMException(`The subprotocol '${p}' is duplicated.`, \"SyntaxError\");\n seen.add(p.toLowerCase());\n }\n this.url = u.toString();\n connect(this.url, list).then(\n ({ port, protocol }) => this.#opened(port, protocol ?? \"\"),\n () => this.#fail(),\n );\n }\n\n #emit(ev: Event, handler: Handler): void {\n handler?.call(this, ev);\n this.dispatchEvent(ev);\n }\n\n #opened(port: MessagePort, protocol: string): void {\n if (this.readyState === 3) {\n port.close();\n return;\n }\n this.#port = port;\n this.protocol = protocol;\n port.onmessage = (e) => this.#frame(e.data as Frame);\n port.start?.();\n if (this.#pendingClose) {\n // close() was called while connecting\n this.readyState = 2;\n port.postMessage({\n t: \"c\",\n code: this.#pendingClose.code ?? 1000,\n reason: this.#pendingClose.reason ?? \"\",\n } satisfies Frame);\n return;\n }\n this.readyState = 1;\n this.#startHeartbeat();\n this.#emit(new Event(\"open\"), this.onopen);\n }\n\n #fail(): void {\n if (this.readyState === 3) return;\n this.#stopHeartbeat();\n this.#port?.close();\n this.readyState = 3;\n this.#emit(new Event(\"error\"), this.onerror);\n this.#emit(\n new CellsCloseEvent(\"close\", { code: 1006, reason: \"\", wasClean: false }),\n this.onclose,\n );\n }\n\n #frame(f: Frame): void {\n if (f.t === \"q\") {\n this.#lastPong = Date.now();\n return;\n }\n if (f.t === \"p\") return;\n if (f.t === \"a\") {\n this.#buffered -= this.#unacked.shift() ?? 0;\n return;\n }\n if (f.t === \"m\") {\n if (this.readyState !== 1) return;\n let data: unknown = f.d;\n if (typeof data !== \"string\" && this.binaryType === \"blob\")\n data = new Blob([data as ArrayBuffer]);\n this.#emit(\n new MessageEvent(\"message\", { data, origin: new URL(this.url).origin }),\n this.onmessage,\n );\n } else {\n const echo = this.readyState === 1;\n if (echo)\n this.#port!.postMessage({ t: \"c\", code: f.code, reason: f.reason } satisfies Frame); // close handshake\n this.readyState = 3;\n this.#stopHeartbeat();\n this.#port!.close();\n this.#emit(\n new CellsCloseEvent(\"close\", { code: f.code, reason: f.reason, wasClean: true }),\n this.onclose,\n );\n }\n }\n\n send(data: string | ArrayBuffer | ArrayBufferView | Blob): void {\n if (this.readyState === 0)\n throw new DOMException(\"Still in CONNECTING state.\", \"InvalidStateError\");\n if (this.readyState !== 1) return; // spec: silently discarded after closing\n if (data instanceof Blob) {\n this.#buffered += data.size;\n void data.arrayBuffer().then((b) => this.readyState === 1 && this.#post(b));\n return;\n }\n const d = toFrameData(data);\n this.#buffered += byteSize(d);\n this.#post(d);\n }\n\n #post(d: string | ArrayBuffer): void {\n this.#unacked.push(byteSize(d));\n this.#port!.postMessage({ t: \"m\", d } satisfies Frame, typeof d === \"string\" ? [] : [d]);\n }\n\n close(code?: number, reason?: string): void {\n checkClose(code, reason);\n if (this.readyState >= 2) return;\n if (this.readyState === 0) {\n this.#pendingClose = { code, reason };\n this.readyState = 2;\n return;\n }\n this.readyState = 2;\n this.#port!.postMessage({ t: \"c\", code: code ?? 1005, reason: reason ?? \"\" } satisfies Frame);\n }\n }\n return CellsWebSocket as unknown as typeof WebSocket;\n}\n\nexport type CellsWebSocketClass = ReturnType<typeof createWebSocketClass>;\n"]}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/ws/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,IAAI,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAavG,wGAAwG;AACxG,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAsBtC;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAA2B,EAC3B,OAAO,GAA0B,EAAE;IAEnC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3B,MAAM,QAAQ,GAAG,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACtF,MAAM,UAAU,GACd,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC;QAClC,CAAC,CAAC,EAAW,EAAE,EAAE,CAAC,YAAY,CAAC,EAAmC,CAAC,CAAC,CAAC;IACvE,MAAM,IAAI,GAAG,wBAAwB,CACnC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;QACvB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACzD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC;IAC5C,CAAC,EACD;QACE,WAAW,EAAE,OAAO,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW;QAChF,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,MAAM,EAAE,MAAM,IAAI;YAChB,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,CAAC;YACnD,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;YAC/C,UAAU,EAAE,QAAQ;YACpB,YAAY,EAAE,UAAU;SACzB;KACF,CACF,CAAC;IACF,kEAAkE;IAClE,MAAM,cAAe,SAAQ,IAAI;QAC/B,YAAY,GAAiB,EAAE,SAAS,GAAsB,EAAE;YAC9D,MAAM,IAAI,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;YAC1E,8DAA8D;YAC9D,+CAA+C;YAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;gBAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;oBACf,MAAM,IAAI,YAAY,CAAC,oBAAoB,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;gBACjF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAChB,CAAC;YACD,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;KACF;IACD,OAAO,cAA6C,CAAC;AACvD,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CAAC,GAAiB;IACjC,IAAK,UAAqC,CAAC,QAAQ;QAAE,OAAO,GAAG,CAAC;IAChE,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,mBAAmB,CAAC,CAAC,IAAI,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC","sourcesContent":["import { createWebSocketClass as createPortWebSocketClass } from \"@statewalker/webrun-http-browser/sw\";\n\n/** Opens the transport of a socket: resolves with the MessagePort once the server accepted (101). */\nexport type WebSocketConnector = (\n url: string,\n protocols: string[],\n) => Promise<{ port: MessagePort; protocol?: string }>;\n\ntype IntervalTimers = {\n setInterval(fn: () => void, ms: number): unknown;\n clearInterval(id: unknown): void;\n};\n\n/** The longest period a timer takes (2³¹−1 ms, about 24.8 days): `heartbeatMs: 0` pings that rarely. */\nconst LONGEST_PERIOD_MS = 2 ** 31 - 1;\n\nexport interface CellsWebSocketOptions {\n /**\n * Liveness ping period (ms, default 10 s); 0 disables the heartbeat. The peer is declared dead\n * after 2 periods without a pong.\n */\n heartbeatMs?: number;\n /**\n * Timers for the heartbeat (and, when given, the close handshake's timeout). On a page's main\n * thread pass worker-timers' functions: background-tab throttling of main-thread timers would\n * otherwise delay pings and make the server drop a live socket. Workers and Node do not\n * throttle, so the built-in timers are the default.\n */\n timers?: IntervalTimers & {\n setTimeout?(fn: () => void, ms: number): unknown;\n clearTimeout?(id: unknown): void;\n };\n /** How long `close()` waits for the server's echo before closing with 1006 (default 5 s). */\n closeTimeoutMs?: number;\n}\n\n/**\n * A WHATWG `WebSocket` class whose sockets reach a Worker through `connect` (page →\n * ServiceWorker → the shard worker that owns the object, or in-process). It is\n * `@statewalker/webrun-http-browser`'s client (`createWebSocketClass`): the socket's frames on its\n * MessagePort are that package's (version `WS_PORT_PROTOCOL_VERSION`), which the server ends of\n * cellb's WebSocketPairs speak. Installed for user code through webrun-modules `~deps` (provided\n * `WebSocket`), so the real `globalThis.WebSocket` is left untouched. Typed as the platform's\n * `WebSocket` (the class implements that interface).\n */\nexport function createWebSocketClass(\n connect: WebSocketConnector,\n options: CellsWebSocketOptions = {},\n): typeof WebSocket {\n const { timers } = options;\n const setTimer = timers?.setTimeout?.bind(timers) ?? ((fn, ms) => setTimeout(fn, ms));\n const clearTimer =\n timers?.clearTimeout?.bind(timers) ??\n ((id: unknown) => clearTimeout(id as ReturnType<typeof setTimeout>));\n const Base = createPortWebSocketClass(\n async (url, protocols) => {\n const { port, protocol } = await connect(url, protocols);\n return { port, protocol: protocol ?? \"\" };\n },\n {\n heartbeatMs: options.heartbeatMs === 0 ? LONGEST_PERIOD_MS : options.heartbeatMs,\n closeTimeoutMs: options.closeTimeoutMs,\n timers: timers && {\n setInterval: (fn, ms) => timers.setInterval(fn, ms),\n clearInterval: (id) => timers.clearInterval(id),\n setTimeout: setTimer,\n clearTimeout: clearTimer,\n },\n },\n );\n // Two of cellb's own client's rules the package's does not apply.\n class CellsWebSocket extends Base {\n constructor(url: string | URL, protocols: string | string[] = []) {\n const list = typeof protocols === \"string\" ? [protocols] : [...protocols];\n // Subprotocols are duplicates case-insensitively (WHATWG; WPT\n // Create-protocols-repeated-case-insensitive).\n const seen = new Set<string>();\n for (const p of list) {\n const key = p.toLowerCase();\n if (seen.has(key))\n throw new DOMException(`The subprotocol '${p}' is duplicated.`, \"SyntaxError\");\n seen.add(key);\n }\n super(resolved(url), list);\n }\n }\n return CellsWebSocket as unknown as typeof WebSocket;\n}\n\n/**\n * A relative url resolved against the page, or, where there is none (Node, a worker without\n * `location`), against `http://localhost/`, as cellb's client always did. An url that does not\n * parse is passed on, for the class to refuse with its SyntaxError.\n */\nfunction resolved(url: string | URL): string | URL {\n if ((globalThis as { location?: unknown }).location) return url;\n try {\n return new URL(String(url), \"http://localhost/\").href;\n } catch {\n return url;\n }\n}\n\nexport type CellsWebSocketClass = ReturnType<typeof createWebSocketClass>;\n"]}
|
package/dist/ws/frames.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Wire protocol of a WebSocket carried by a MessagePort (both directions):
|
|
3
|
+
* `@statewalker/webrun-http-browser`'s frames (version `WS_PORT_PROTOCOL_VERSION`), spoken by its
|
|
4
|
+
* client (`createWebSocketClass`) and by the ends of cellb's WebSocketPairs.
|
|
5
|
+
*
|
|
6
|
+
* m a message (an ArrayBuffer is transferred)
|
|
7
|
+
* c close, initiating or echo
|
|
8
|
+
* p ping (client → server), carries the client's heartbeat period
|
|
9
|
+
* q pong (server → client)
|
|
10
|
+
* a ack: the receiver dispatched `n` more bytes of messages (the sender's `bufferedAmount`)
|
|
11
|
+
*
|
|
12
|
+
* MessagePorts never report that the other side died (no `close` event in browsers), so both
|
|
13
|
+
* ends run a heartbeat: the client pings every `hb` ms and closes with 1006 when pongs stop for
|
|
14
|
+
* 2·hb; the server closes with 1006 when pings stop for 3·hb. This is what makes a socket notice a
|
|
15
|
+
* closed tab. A hidden tab's client announces a longer period (its timers are throttled).
|
|
16
|
+
*/
|
|
2
17
|
export type Frame = {
|
|
3
18
|
t: "m";
|
|
4
19
|
d: string | ArrayBuffer;
|
|
@@ -13,14 +28,8 @@ export type Frame = {
|
|
|
13
28
|
t: "q";
|
|
14
29
|
} | {
|
|
15
30
|
t: "a";
|
|
31
|
+
n: number;
|
|
16
32
|
};
|
|
17
|
-
/**
|
|
18
|
-
* MessagePorts never report that the other side died (no `close` event in
|
|
19
|
-
* browsers), so both ends run a heartbeat: the client pings every `hb` ms and
|
|
20
|
-
* closes with 1006 when pongs stop for 2·hb; the server closes with 1006 when
|
|
21
|
-
* pings stop for 3·hb. This is what makes a socket notice a closed tab.
|
|
22
|
-
*/
|
|
23
|
-
export declare const DEFAULT_HEARTBEAT_MS = 10000;
|
|
24
33
|
/** What a message adds to the sender's `bufferedAmount` (WHATWG: UTF-8 bytes of a string). */
|
|
25
34
|
export declare function byteSize(d: string | ArrayBuffer): number;
|
|
26
35
|
export declare function toFrameData(data: unknown): string | ArrayBuffer;
|
|
@@ -36,3 +45,17 @@ export declare class CellsCloseEvent extends Event {
|
|
|
36
45
|
wasClean?: boolean;
|
|
37
46
|
});
|
|
38
47
|
}
|
|
48
|
+
/** One side of a frame relay: a MessagePort, or a port of a multiplexer. */
|
|
49
|
+
export interface FrameEnd {
|
|
50
|
+
post(f: Frame): void;
|
|
51
|
+
listen(fn: (f: Frame) => void): void;
|
|
52
|
+
close(): void;
|
|
53
|
+
}
|
|
54
|
+
export declare const portEnd: (port: MessagePort) => FrameEnd;
|
|
55
|
+
/**
|
|
56
|
+
* Relays a socket's frames between `a` and `b`. Once a close frame has passed each way (the
|
|
57
|
+
* closing handshake) both ends are closed, after a moment for the last frames to arrive.
|
|
58
|
+
* Returns a function that ends the relay at once, telling `a` the socket closed with `code`;
|
|
59
|
+
* `onDone` runs when the relay ends either way.
|
|
60
|
+
*/
|
|
61
|
+
export declare function relayFrames(a: FrameEnd, b: FrameEnd, onDone?: () => void): (code: number, reason: string) => void;
|
package/dist/ws/frames.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* closes with 1006 when pongs stop for 2·hb; the server closes with 1006 when
|
|
5
|
-
* pings stop for 3·hb. This is what makes a socket notice a closed tab.
|
|
6
|
-
*/
|
|
7
|
-
export const DEFAULT_HEARTBEAT_MS = 10_000;
|
|
1
|
+
// Declared here, not imported from webrun-http-browser/sw: its declarations name ServiceWorker
|
|
2
|
+
// types, and cellb's must compile with the DOM lib alone (scripts/pack-check.mjs). The two are
|
|
3
|
+
// kept the same by test/p07w-websocket.test.ts.
|
|
8
4
|
/** What a message adds to the sender's `bufferedAmount` (WHATWG: UTF-8 bytes of a string). */
|
|
9
5
|
export function byteSize(d) {
|
|
10
6
|
return typeof d === "string" ? new TextEncoder().encode(d).byteLength : d.byteLength;
|
|
@@ -36,4 +32,55 @@ export class CellsCloseEvent extends Event {
|
|
|
36
32
|
this.wasClean = init.wasClean ?? false;
|
|
37
33
|
}
|
|
38
34
|
}
|
|
35
|
+
export const portEnd = (port) => ({
|
|
36
|
+
post: (f) => port.postMessage(f),
|
|
37
|
+
listen: (fn) => {
|
|
38
|
+
port.onmessage = (e) => fn(e.data);
|
|
39
|
+
},
|
|
40
|
+
close: () => port.close(),
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Relays a socket's frames between `a` and `b`. Once a close frame has passed each way (the
|
|
44
|
+
* closing handshake) both ends are closed, after a moment for the last frames to arrive.
|
|
45
|
+
* Returns a function that ends the relay at once, telling `a` the socket closed with `code`;
|
|
46
|
+
* `onDone` runs when the relay ends either way.
|
|
47
|
+
*/
|
|
48
|
+
export function relayFrames(a, b, onDone) {
|
|
49
|
+
let fromA = false;
|
|
50
|
+
let fromB = false;
|
|
51
|
+
let done = false;
|
|
52
|
+
const finish = () => {
|
|
53
|
+
if (done)
|
|
54
|
+
return;
|
|
55
|
+
done = true;
|
|
56
|
+
onDone?.();
|
|
57
|
+
const t = setTimeout(() => {
|
|
58
|
+
a.close();
|
|
59
|
+
b.close();
|
|
60
|
+
}, 1_000);
|
|
61
|
+
t.unref?.();
|
|
62
|
+
};
|
|
63
|
+
a.listen((f) => {
|
|
64
|
+
b.post(f);
|
|
65
|
+
if (f.t !== "c")
|
|
66
|
+
return;
|
|
67
|
+
fromA = true;
|
|
68
|
+
if (fromB)
|
|
69
|
+
finish();
|
|
70
|
+
});
|
|
71
|
+
b.listen((f) => {
|
|
72
|
+
a.post(f);
|
|
73
|
+
if (f.t !== "c")
|
|
74
|
+
return;
|
|
75
|
+
fromB = true;
|
|
76
|
+
if (fromA)
|
|
77
|
+
finish();
|
|
78
|
+
});
|
|
79
|
+
return (code, reason) => {
|
|
80
|
+
if (done)
|
|
81
|
+
return;
|
|
82
|
+
a.post({ t: "c", code, reason });
|
|
83
|
+
finish();
|
|
84
|
+
};
|
|
85
|
+
}
|
|
39
86
|
//# sourceMappingURL=frames.js.map
|
package/dist/ws/frames.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frames.js","sourceRoot":"","sources":["../../src/ws/frames.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"frames.js","sourceRoot":"","sources":["../../src/ws/frames.ts"],"names":[],"mappings":"AAsBA,+FAA+F;AAC/F,+FAA+F;AAC/F,gDAAgD;AAEhD,8FAA8F;AAC9F,MAAM,UAAU,QAAQ,CAAC,CAAuB;IAC9C,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,IAAI,YAAY,WAAW;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtD,IAAI,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAgB,CAAC;IAC9F,MAAM,IAAI,SAAS,CACjB,gFAAgF,CACjF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAa,EAAE,MAAe;IACvD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC;QACrE,MAAM,IAAI,YAAY,CACpB,iEAAiE,IAAI,cAAc,EACnF,oBAAoB,CACrB,CAAC;IACJ,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,GAAG,GAAG;QAC3E,MAAM,IAAI,YAAY,CACpB,4DAA4D,EAC5D,aAAa,CACd,CAAC;AACN,CAAC;AAED,0FAA0F;AAC1F,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAS;IACb,MAAM,CAAS;IACf,QAAQ,CAAU;IAC3B,YAAY,IAAY,EAAE,IAAI,GAA2D,EAAE;QACzF,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;IACzC,CAAC;CACF;AASD,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAiB,EAAY,EAAE,CAAC,CAAC;IACvD,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IAChC,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE;QACb,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAa,CAAC,CAAC;IAC9C,CAAC;IACD,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;CAC1B,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,CAAW,EACX,CAAW,EACX,MAAmB;IAEnB,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,IAAI,IAAI;YAAE,OAAO;QACjB,IAAI,GAAG,IAAI,CAAC;QACZ,MAAM,EAAE,EAAE,CAAC;QACX,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE;YACxB,CAAC,CAAC,KAAK,EAAE,CAAC;YACV,CAAC,CAAC,KAAK,EAAE,CAAC;QACZ,CAAC,EAAE,KAAK,CAAC,CAAC;QACT,CAAwB,CAAC,KAAK,EAAE,EAAE,CAAC;IACtC,CAAC,CAAC;IACF,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACb,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACV,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,OAAO;QACxB,KAAK,GAAG,IAAI,CAAC;QACb,IAAI,KAAK;YAAE,MAAM,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACb,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACV,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,OAAO;QACxB,KAAK,GAAG,IAAI,CAAC;QACb,IAAI,KAAK;YAAE,MAAM,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;QACtB,IAAI,IAAI;YAAE,OAAO;QACjB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACjC,MAAM,EAAE,CAAC;IACX,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Wire protocol of a WebSocket carried by a MessagePort (both directions):\n * `@statewalker/webrun-http-browser`'s frames (version `WS_PORT_PROTOCOL_VERSION`), spoken by its\n * client (`createWebSocketClass`) and by the ends of cellb's WebSocketPairs.\n *\n * m a message (an ArrayBuffer is transferred)\n * c close, initiating or echo\n * p ping (client → server), carries the client's heartbeat period\n * q pong (server → client)\n * a ack: the receiver dispatched `n` more bytes of messages (the sender's `bufferedAmount`)\n *\n * MessagePorts never report that the other side died (no `close` event in browsers), so both\n * ends run a heartbeat: the client pings every `hb` ms and closes with 1006 when pongs stop for\n * 2·hb; the server closes with 1006 when pings stop for 3·hb. This is what makes a socket notice a\n * closed tab. A hidden tab's client announces a longer period (its timers are throttled).\n */\nexport type Frame =\n | { t: \"m\"; d: string | ArrayBuffer }\n | { t: \"c\"; code: number; reason: string }\n | { t: \"p\"; hb: number }\n | { t: \"q\" }\n | { t: \"a\"; n: number };\n// Declared here, not imported from webrun-http-browser/sw: its declarations name ServiceWorker\n// types, and cellb's must compile with the DOM lib alone (scripts/pack-check.mjs). The two are\n// kept the same by test/p07w-websocket.test.ts.\n\n/** What a message adds to the sender's `bufferedAmount` (WHATWG: UTF-8 bytes of a string). */\nexport function byteSize(d: string | ArrayBuffer): number {\n return typeof d === \"string\" ? new TextEncoder().encode(d).byteLength : d.byteLength;\n}\n\nexport function toFrameData(data: unknown): string | ArrayBuffer {\n if (typeof data === \"string\") return data;\n if (data instanceof ArrayBuffer) return data.slice(0);\n if (ArrayBuffer.isView(data))\n return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer;\n throw new TypeError(\n \"WebSocket.send: unsupported data type (string, ArrayBuffer or ArrayBufferView)\",\n );\n}\n\nexport function checkClose(code?: number, reason?: string): void {\n if (code !== undefined && code !== 1000 && (code < 3000 || code > 4999))\n throw new DOMException(\n `The close code must be either 1000, or between 3000 and 4999. ${code} is neither.`,\n \"InvalidAccessError\",\n );\n if (reason !== undefined && new TextEncoder().encode(reason).byteLength > 123)\n throw new DOMException(\n \"The close reason must not be greater than 123 UTF-8 bytes.\",\n \"SyntaxError\",\n );\n}\n\n/** CloseEvent is not a global in every runtime (Node 22): a compatible Event subclass. */\nexport class CellsCloseEvent extends Event {\n readonly code: number;\n readonly reason: string;\n readonly wasClean: boolean;\n constructor(type: string, init: { code?: number; reason?: string; wasClean?: boolean } = {}) {\n super(type);\n this.code = init.code ?? 1005;\n this.reason = init.reason ?? \"\";\n this.wasClean = init.wasClean ?? false;\n }\n}\n\n/** One side of a frame relay: a MessagePort, or a port of a multiplexer. */\nexport interface FrameEnd {\n post(f: Frame): void;\n listen(fn: (f: Frame) => void): void;\n close(): void;\n}\n\nexport const portEnd = (port: MessagePort): FrameEnd => ({\n post: (f) => port.postMessage(f),\n listen: (fn) => {\n port.onmessage = (e) => fn(e.data as Frame);\n },\n close: () => port.close(),\n});\n\n/**\n * Relays a socket's frames between `a` and `b`. Once a close frame has passed each way (the\n * closing handshake) both ends are closed, after a moment for the last frames to arrive.\n * Returns a function that ends the relay at once, telling `a` the socket closed with `code`;\n * `onDone` runs when the relay ends either way.\n */\nexport function relayFrames(\n a: FrameEnd,\n b: FrameEnd,\n onDone?: () => void,\n): (code: number, reason: string) => void {\n let fromA = false;\n let fromB = false;\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n onDone?.();\n const t = setTimeout(() => {\n a.close();\n b.close();\n }, 1_000);\n (t as { unref?(): void }).unref?.();\n };\n a.listen((f) => {\n b.post(f);\n if (f.t !== \"c\") return;\n fromA = true;\n if (fromB) finish();\n });\n b.listen((f) => {\n a.post(f);\n if (f.t !== \"c\") return;\n fromB = true;\n if (fromA) finish();\n });\n return (code, reason) => {\n if (done) return;\n a.post({ t: \"c\", code, reason });\n finish();\n };\n}\n"]}
|
package/dist/ws/server.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type Frame } from "./frames.js";
|
|
1
2
|
declare const kPort: unique symbol;
|
|
3
|
+
declare const kTakeQueued: unique symbol;
|
|
2
4
|
/**
|
|
3
5
|
* Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair):
|
|
4
6
|
* events are delivered only after `accept()`; `send`/`close` as in workerd.
|
|
@@ -18,6 +20,8 @@ export declare class CfWebSocket extends EventTarget {
|
|
|
18
20
|
get binaryType(): "blob" | "arraybuffer";
|
|
19
21
|
set binaryType(v: "blob" | "arraybuffer");
|
|
20
22
|
[kPort]: MessagePort | undefined;
|
|
23
|
+
/** The frames that arrived before accept(), removed (takeClientPort hands them over). */
|
|
24
|
+
[kTakeQueued](): Frame[];
|
|
21
25
|
constructor(port: MessagePort);
|
|
22
26
|
/** The other end of the WebSocketPair (the runtime checks how the server end was accepted). */
|
|
23
27
|
peer?: CfWebSocket;
|
package/dist/ws/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { CellsCloseEvent, checkClose, toFrameData } from "./frames.js";
|
|
1
|
+
import { byteSize, CellsCloseEvent, checkClose, portEnd, relayFrames, toFrameData, } from "./frames.js";
|
|
2
2
|
const kPort = Symbol("cellb.ws.port");
|
|
3
|
+
const kTakeQueued = Symbol("cellb.ws.takeQueued");
|
|
3
4
|
/** Server ends accepted in this realm and not yet closed (see `CfWebSocket.goAway`). */
|
|
4
5
|
const acceptedEnds = new Set();
|
|
5
6
|
/**
|
|
@@ -28,6 +29,10 @@ export class CfWebSocket extends EventTarget {
|
|
|
28
29
|
[kPort];
|
|
29
30
|
#accepted = false;
|
|
30
31
|
#queued = [];
|
|
32
|
+
/** The frames that arrived before accept(), removed (takeClientPort hands them over). */
|
|
33
|
+
[kTakeQueued]() {
|
|
34
|
+
return this.#queued.splice(0);
|
|
35
|
+
}
|
|
31
36
|
#attachment;
|
|
32
37
|
#lastPing = 0;
|
|
33
38
|
#watch;
|
|
@@ -44,11 +49,19 @@ export class CfWebSocket extends EventTarget {
|
|
|
44
49
|
this.#queued.push(f);
|
|
45
50
|
};
|
|
46
51
|
}
|
|
47
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Heartbeat: answer the client's ping; close 1006 when pings stop for 3 periods. The watch
|
|
54
|
+
* starts with the first ping (a pair's ends used in this realm never ping) and keeps that
|
|
55
|
+
* period. A later ping may announce a longer one (webrun-http-browser's client does when its
|
|
56
|
+
* page turns hidden), but a tab that closes turns hidden on its way out: following it would
|
|
57
|
+
* leave a dead tab's socket open for minutes. Pages keep pinging on time with unthrottled
|
|
58
|
+
* timers (`timers`).
|
|
59
|
+
*/
|
|
48
60
|
#ping(hb) {
|
|
49
61
|
this.#lastPing = Date.now();
|
|
50
62
|
this[kPort]?.postMessage({ t: "q" });
|
|
51
|
-
|
|
63
|
+
// A period no timer takes (2³¹−1 ms and up: `heartbeatMs: 0`) means no heartbeat.
|
|
64
|
+
if (this.#watch || !hb || hb >= 2 ** 31 - 1)
|
|
52
65
|
return;
|
|
53
66
|
this.#watch = setInterval(() => {
|
|
54
67
|
if (this.readyState === 3)
|
|
@@ -95,7 +108,8 @@ export class CfWebSocket extends EventTarget {
|
|
|
95
108
|
if (f.t === "m") {
|
|
96
109
|
const data = typeof f.d !== "string" && this.#binaryType === "blob" ? new Blob([f.d]) : f.d;
|
|
97
110
|
this.dispatchEvent(new MessageEvent("message", { data }));
|
|
98
|
-
|
|
111
|
+
// The sender's bufferedAmount drains by the bytes dispatched.
|
|
112
|
+
this[kPort]?.postMessage({ t: "a", n: byteSize(f.d) });
|
|
99
113
|
return;
|
|
100
114
|
}
|
|
101
115
|
if (f.t !== "c")
|
|
@@ -209,7 +223,17 @@ export function takeClientPort(ws) {
|
|
|
209
223
|
ws[kPort] = undefined;
|
|
210
224
|
port.onmessage = null;
|
|
211
225
|
ws.readyState = 3; // handed over: this end must not answer pings any more
|
|
212
|
-
|
|
226
|
+
// Frames the server sent before the response was handed over (partyserver greets in
|
|
227
|
+
// onConnect, before it returns the 101) reached this end and wait in its queue: the new
|
|
228
|
+
// holder gets them first, then the rest, through a channel that relays both ways.
|
|
229
|
+
const queued = ws[kTakeQueued]();
|
|
230
|
+
if (!queued.length)
|
|
231
|
+
return port;
|
|
232
|
+
const relay = new MessageChannel();
|
|
233
|
+
for (const f of queued)
|
|
234
|
+
relay.port2.postMessage(f);
|
|
235
|
+
relayFrames(portEnd(relay.port2), portEnd(port)); // closed with the socket's handshake
|
|
236
|
+
return relay.port1;
|
|
213
237
|
}
|
|
214
238
|
/**
|
|
215
239
|
* `Response` that also accepts Cloudflare's `{ status: 101, webSocket }` init —
|
package/dist/ws/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ws/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAc,WAAW,EAAE,MAAM,aAAa,CAAC;AAEnF,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;AACtC,wFAAwF;AACxF,MAAM,YAAY,GAAG,IAAI,GAAG,EAAe,CAAC;AAE5C;;;;GAIG;AACH,MAAM,OAAO,WAAY,SAAQ,WAAW;IAC1C,MAAM,CAAU,sBAAsB,GAAG,CAAC,CAAC;IAC3C,MAAM,CAAU,gBAAgB,GAAG,CAAC,CAAC;IACrC,MAAM,CAAU,mBAAmB,GAAG,CAAC,CAAC;IACxC,MAAM,CAAU,kBAAkB,GAAG,CAAC,CAAC;IACvC,UAAU,GAAG,CAAC,CAAC;IACf,GAAG,GAAkB,IAAI,CAAC;IAE1B,QAAQ,GAAkB,IAAI,CAAC;IAC/B,UAAU,GAAkB,IAAI,CAAC;IACjC,WAAW,GAA2B,aAAa,CAAC;IACpD,4EAA4E;IAC5E,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IACD,IAAI,UAAU,CAAC,CAAyB;QACtC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,aAAa;YAAE,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,CAAC,KAAK,CAAC,CAA0B;IACjC,SAAS,GAAG,KAAK,CAAC;IAClB,OAAO,GAAY,EAAE,CAAC;IACtB,WAAW,CAAU;IAErB,SAAS,GAAG,CAAC,CAAC;IACd,MAAM,CAA6C;IAEnD,YAAY,IAAiB;QAC3B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,EAAE;YACrB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAa,CAAC;YAC1B,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gCAAgC;YAC1E,IAAI,IAAI,CAAC,SAAS;gBAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;;gBAC9B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC;IAED,qFAAqF;IACrF,KAAK,CAAC,EAAU;QACd,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,EAAE,GAAG,EAAkB,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE;YAAE,OAAO;QAC/B,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;YAC7B,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;gBAAE,OAAO,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7D,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,GAAG,CAAC,GAAG,EAAE;gBAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC;IAED,gFAAgF;IAChF,KAAK;QACH,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3B,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,OAAO;QAClC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,aAAa,CAAC,IAAI,eAAe,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,+FAA+F;IAC/F,IAAI,CAAe;IACnB,kFAAkF;IAClF,YAAY,GAAG,KAAK,CAAC;IACrB,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;YACd,MAAM,IAAI,SAAS,CAAC,mEAAmE,CAAC,CAAC;QAC3F,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QACtB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,mBAAmB,CAAC,CAAU;QAC5B,IAAI,CAAC,WAAW,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,qBAAqB;QACnB,OAAO,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,CAAQ;QACb,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5F,IAAI,CAAC,aAAa,CAAC,IAAI,YAAY,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC1D,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,EAAE,GAAG,EAAkB,CAAC,CAAC,CAAC,qCAAqC;YAC3F,OAAO;QACT,CAAC;QACD,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,OAAO;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,KAAK,CAAC,CAAC;QACtC,IAAI,OAAO;YACT,IAAI,CAAC,KAAK,CAAE,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAkB,CAAC,CAAC;QACvF,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAE,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,aAAa,CAChB,IAAI,eAAe,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CACjF,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,0BAA0B;QAC/C,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,IAAI,EAAE,CAAC,UAAU,IAAI,CAAC;gBAAE,SAAS;YACjC,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC;YAClB,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAkB,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED,IAAI,CAAC,IAA4C;QAC/C,yFAAyF;QACzF,wCAAwC;QACxC,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;QAC7F,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAE,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAkB,EAAE,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED,KAAK,CAAC,IAAa,EAAE,MAAe;QAClC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC;YAAE,OAAO;QACjC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,KAAK,CAAE,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,EAAkB,CAAC,CAAC;IACjG,CAAC;CACF;AAED,iGAAiG;AACjG,MAAM,aAAa,GACjB,UACD,CAAC,SAAS,CAAC;AAEZ;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE;IACpC,SAAS,SAAS,CAAgB,GAAW,EAAE,SAA6B;QAC1E,IAAI,CAAC,IAAI,MAAM;YACb,MAAM,IAAI,SAAS,CAAC,iEAAiE,CAAC,CAAC;QACzF,IAAI,CAAC,aAAa;YAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QAC1F,OAAO,IAAI,aAAa,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC3C,CAAC;IACD,SAAS,CAAC,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;IAC5C,MAAM,MAAM,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAW,CAAC;IAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,eAAe,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,EAAE;QACnD,KAAK,EAAE,CAAC,CAAU,EAAE,EAAE,CACpB,CAAC,YAAY,WAAW,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,CAAC,YAAY,aAAa,CAAC;KAC1F,CAAC,CAAC;IACH,OAAO,SAMN,CAAC;AACJ,CAAC,CAAC,EAAE,CAAC;AAEL,2FAA2F;AAC3F,MAAM,OAAO,aAAa;IACxB,CAAC,CAAc;IACf,CAAC,CAAc;IACf;QACE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;QAC9C,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACvB,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,qEAAqE;IACrE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QAChB,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;IAChB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,qBAAsB,SAAQ,aAAa;IACtD;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC;QAC5B,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC;IAC9B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAAe;IAC5C,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;IACvB,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;IACpE,EAAE,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IACtB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACtB,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,uDAAuD;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,wFAAwF;AACxF,MAAM,YAAY,GAAG,QAAQ,CAAC;AAE9B,iFAAiF;AACjF,MAAM,cAAc,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAC9F,YAAY,CACb,CAAC;AAEF,MAAM,OAAO,aAAc,SAAQ,QAAQ;IAChC,OAAO,CAAS;IAChB,SAAS,CAAqB;IACvC,YACE,IAAsB,EACtB,IAAI,GAAsD,EAAE;QAE5D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC;QACpC,4FAA4F;QAC5F,0FAA0F;QAC1F,6EAA6E;QAC7E,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;YAC3B,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;YACnC,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;QACxC,IAAI,cAAc,EAAE,CAAC;YACnB,0FAA0F;YAC1F,0FAA0F;YAC1F,uFAAuF;YACvF,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,YAAY,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzF,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,EAAE;gBAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;YACtE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE;gBACrC,KAAK,EAAE,OAAO;gBACd,YAAY,EAAE,IAAI;gBAClB,UAAU,EAAE,IAAI;aACjB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,IAAI,MAAM;QACR,mFAAmF;QACnF,wEAAwE;QACxE,OAAO,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;IACvD,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAU;QACpC,OAAO,CAAC,YAAY,YAAY,CAAC;IACnC,CAAC;IAED,0FAA0F;IAC1F,yFAAyF;IACzF,iEAAiE;IACjE,MAAM,CAAU,IAAI,CAAC,IAAa,EAAE,IAAI,GAAiB,EAAE;QACzD,IAAI,CAAC,cAAc;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAClF,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,CAAU,QAAQ,CAAC,GAAiB,EAAE,MAAM,GAAG,GAAG;QACtD,IAAI,CAAC,cAAc;YAAE,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC7C,MAAM,IAAI,UAAU,CAAC,kBAAkB,MAAM,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,GAAI,UAA8C,CAAC,QAAQ,EAAE,IAAI,CAAC;QAC5E,OAAO,IAAI,aAAa,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC7F,CAAC;CACF;AAED,6EAA6E;AAC7E,MAAM,OAAO,4BAA4B;IAC9B,OAAO,CAAS;IAChB,QAAQ,CAAS;IAC1B,YAAY,OAAe,EAAE,QAAgB;QAC3C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF","sourcesContent":["import { CellsCloseEvent, checkClose, type Frame, toFrameData } from \"./frames.js\";\n\nconst kPort = Symbol(\"cellb.ws.port\");\n/** Server ends accepted in this realm and not yet closed (see `CfWebSocket.goAway`). */\nconst acceptedEnds = new Set<CfWebSocket>();\n\n/**\n * Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair):\n * events are delivered only after `accept()`; `send`/`close` as in workerd.\n * Hibernation (`ctx.acceptWebSocket`) is a later prototype.\n */\nexport class CfWebSocket extends EventTarget {\n static readonly READY_STATE_CONNECTING = 0;\n static readonly READY_STATE_OPEN = 1;\n static readonly READY_STATE_CLOSING = 2;\n static readonly READY_STATE_CLOSED = 3;\n readyState = 1;\n url: string | null = null;\n\n protocol: string | null = null;\n extensions: string | null = null;\n #binaryType: \"blob\" | \"arraybuffer\" = \"arraybuffer\";\n /** How binary messages arrive; other values are ignored, as in browsers. */\n get binaryType(): \"blob\" | \"arraybuffer\" {\n return this.#binaryType;\n }\n set binaryType(v: \"blob\" | \"arraybuffer\") {\n if (v === \"blob\" || v === \"arraybuffer\") this.#binaryType = v;\n }\n [kPort]: MessagePort | undefined;\n #accepted = false;\n #queued: Frame[] = [];\n #attachment: unknown;\n\n #lastPing = 0;\n #watch: ReturnType<typeof setInterval> | undefined;\n\n constructor(port: MessagePort) {\n super();\n this[kPort] = port;\n port.onmessage = (e) => {\n const f = e.data as Frame;\n if (f.t === \"p\") return this.#ping(f.hb); // answered even before accept()\n if (this.#accepted) this.#frame(f);\n else this.#queued.push(f);\n };\n }\n\n /** Heartbeat: answer the client's ping; close 1006 when pings stop for 3 periods. */\n #ping(hb: number): void {\n this.#lastPing = Date.now();\n this[kPort]?.postMessage({ t: \"q\" } satisfies Frame);\n if (this.#watch || !hb) return;\n this.#watch = setInterval(() => {\n if (this.readyState === 3) return clearInterval(this.#watch);\n if (Date.now() - this.#lastPing > 3 * hb) this.#gone();\n }, hb);\n }\n\n /** The client stopped pinging (tab closed, worker killed): abnormal closure. */\n #gone(): void {\n clearInterval(this.#watch);\n if (this.readyState === 3) return;\n this.readyState = 3;\n acceptedEnds.delete(this);\n this[kPort]?.close();\n this.dispatchEvent(new CellsCloseEvent(\"close\", { code: 1006, reason: \"\", wasClean: false }));\n }\n\n /** The other end of the WebSocketPair (the runtime checks how the server end was accepted). */\n peer?: CfWebSocket;\n /** Accepted through ctx.acceptWebSocket() (hibernatable) rather than accept(). */\n hibernatable = false;\n get accepted(): boolean {\n return this.#accepted;\n }\n\n accept(): void {\n if (this.#accepted) return;\n if (!this[kPort])\n throw new TypeError(\"This WebSocket end was handed to a client; it cannot be accepted.\");\n this.#accepted = true;\n acceptedEnds.add(this);\n this[kPort].start?.();\n for (const f of this.#queued.splice(0)) this.#frame(f);\n }\n\n serializeAttachment(v: unknown): void {\n this.#attachment = structuredClone(v);\n }\n deserializeAttachment(): unknown {\n return structuredClone(this.#attachment);\n }\n\n #frame(f: Frame): void {\n if (f.t === \"m\") {\n const data = typeof f.d !== \"string\" && this.#binaryType === \"blob\" ? new Blob([f.d]) : f.d;\n this.dispatchEvent(new MessageEvent(\"message\", { data }));\n this[kPort]?.postMessage({ t: \"a\" } satisfies Frame); // the sender's bufferedAmount drains\n return;\n }\n if (f.t !== \"c\") return;\n const wasOpen = this.readyState === 1;\n if (wasOpen)\n this[kPort]!.postMessage({ t: \"c\", code: f.code, reason: f.reason } satisfies Frame);\n this.readyState = 3;\n acceptedEnds.delete(this);\n clearInterval(this.#watch);\n this[kPort]!.close();\n this.dispatchEvent(\n new CellsCloseEvent(\"close\", { code: f.code, reason: f.reason, wasClean: true }),\n );\n }\n\n /**\n * Closes every server end accepted in this realm with 1001 (going away). A shard worker that\n * hands its cells to another tab does it before it stops, so that clients reconnect at once\n * instead of noticing through the heartbeat; Worker code cannot send 1001 itself (`close`).\n */\n static goAway(reason = \"moving to another worker\"): void {\n for (const ws of acceptedEnds) {\n acceptedEnds.delete(ws);\n if (ws.readyState >= 2) continue;\n ws.readyState = 2;\n ws[kPort]?.postMessage({ t: \"c\", code: 1001, reason } satisfies Frame);\n }\n }\n\n send(data: string | ArrayBuffer | ArrayBufferView): void {\n // celld lets either end of a pair send before accept() (workerd refuses); frames wait in\n // the port until the other end listens.\n if (this.readyState !== 1) throw new TypeError(\"Can't call WebSocket send() after close().\");\n const d = toFrameData(data);\n this[kPort]!.postMessage({ t: \"m\", d } satisfies Frame, typeof d === \"string\" ? [] : [d]);\n }\n\n close(code?: number, reason?: string): void {\n checkClose(code, reason);\n if (this.readyState >= 2) return;\n this.readyState = 2;\n this[kPort]!.postMessage({ t: \"c\", code: code ?? 1005, reason: reason ?? \"\" } satisfies Frame);\n }\n}\n\n/** The host's own WebSocket (Node's, or the browser's): the client sockets Worker code opens. */\nconst HostWebSocket = (\n globalThis as { WebSocket?: new (url: string, p?: string | string[]) => object }\n).WebSocket;\n\n/**\n * The `WebSocket` global of Worker code. On workerd one class covers both ends of a\n * WebSocketPair and the client sockets `new WebSocket(url)` opens, and libraries rely on that:\n * the Agents SDK (partyserver) reads attachments with\n * `WebSocket.prototype.deserializeAttachment.call(ws)` and compares `readyState` with\n * `WebSocket.READY_STATE_OPEN`. So its prototype is the server end's (`CfWebSocket`), its\n * statics are workerd's, `instanceof` holds for both kinds, and `new WebSocket(url)` still opens\n * the host's client socket.\n */\nexport const WorkersWebSocket = (() => {\n function WebSocket(this: unknown, url: string, protocols?: string | string[]): object {\n if (!new.target)\n throw new TypeError(\"Failed to construct 'WebSocket': Please use the 'new' operator.\");\n if (!HostWebSocket) throw new Error(\"WebSocket clients are not available in this realm.\");\n return new HostWebSocket(url, protocols);\n }\n WebSocket.prototype = CfWebSocket.prototype;\n const states = { CONNECTING: 0, OPEN: 1, CLOSING: 2, CLOSED: 3 } as const;\n for (const [name, value] of Object.entries(states)) {\n Object.defineProperty(WebSocket, name, { value, enumerable: true });\n Object.defineProperty(WebSocket, `READY_STATE_${name}`, { value, enumerable: true });\n }\n Object.defineProperty(WebSocket, Symbol.hasInstance, {\n value: (v: unknown) =>\n v instanceof CfWebSocket || (HostWebSocket !== undefined && v instanceof HostWebSocket),\n });\n return WebSocket as unknown as typeof CfWebSocket & {\n new (url: string, protocols?: string | string[]): CfWebSocket;\n readonly CONNECTING: 0;\n readonly OPEN: 1;\n readonly CLOSING: 2;\n readonly CLOSED: 3;\n };\n})();\n\n/** `new WebSocketPair()` → `{ 0: client, 1: server }`, entangled over a MessageChannel. */\nexport class WebSocketPair {\n 0: CfWebSocket;\n 1: CfWebSocket;\n constructor() {\n const { port1, port2 } = new MessageChannel();\n this[0] = new CfWebSocket(port1);\n this[1] = new CfWebSocket(port2);\n this[0].peer = this[1];\n this[1].peer = this[0];\n }\n /** `const [client, server] = new WebSocketPair()`, as in workerd. */\n *[Symbol.iterator](): IterableIterator<CfWebSocket> {\n yield this[0];\n yield this[1];\n }\n}\n\n/**\n * `WebSocketPair` under the `websocket_standard_binary_type` compatibility flag: both ends\n * start with `binaryType` \"blob\", as browsers' WebSockets do. Without it they start with\n * \"arraybuffer\", workerd's older default.\n */\nexport class StandardWebSocketPair extends WebSocketPair {\n constructor() {\n super();\n this[0].binaryType = \"blob\";\n this[1].binaryType = \"blob\";\n }\n}\n\n/**\n * Detaches the transport of the CLIENT end returned in a 101 response, so the\n * runtime can hand the raw MessagePort to the real client (transferable).\n */\nexport function takeClientPort(ws: CfWebSocket): MessagePort {\n const port = ws[kPort];\n if (!port) throw new TypeError(\"WebSocket end already handed over\");\n ws[kPort] = undefined;\n port.onmessage = null;\n ws.readyState = 3; // handed over: this end must not answer pings any more\n return port;\n}\n\n/**\n * `Response` that also accepts Cloudflare's `{ status: 101, webSocket }` init —\n * the platform `Response` rejects status 101. Provided to user code via ~deps.\n */\n/** The platform `Response`, as this module sees it (it is external to user bundles). */\nconst HostResponse = Response;\n\n/** Browsers drop `Set-Cookie` from every Response they build (Node keeps it). */\nconst dropsSetCookie = !new HostResponse(null, { headers: { \"set-cookie\": \"a=b\" } }).headers.has(\n \"set-cookie\",\n);\n\nexport class CellsResponse extends Response {\n readonly #status: number;\n readonly webSocket: CfWebSocket | null;\n constructor(\n body?: BodyInit | null,\n init: ResponseInit & { webSocket?: CfWebSocket | null } = {},\n ) {\n const upgrade = init.status === 101;\n // The init's members read one by one, as Fetch reads a dictionary: an init that is itself a\n // Response (`new Response(body, response)`, as opencode's SDK does) keeps its headers and\n // statusText, which a spread would drop (they are getters on its prototype).\n super(upgrade ? null : body, {\n status: upgrade ? 200 : init.status,\n statusText: init.statusText,\n headers: init.headers,\n });\n this.#status = init.status ?? 200;\n this.webSocket = init.webSocket ?? null;\n if (dropsSetCookie) {\n // In a browser the platform's headers drop `Set-Cookie`, which Workers set (Astro appends\n // its cookies to the response): the response keeps its headers in a standalone `Headers`,\n // the ones the platform derived (content-type) plus the Set-Cookie values of the init.\n const headers = new Headers(super.headers);\n const given = init.headers instanceof Headers ? init.headers : new Headers(init.headers);\n for (const c of given.getSetCookie()) headers.append(\"set-cookie\", c);\n Object.defineProperty(this, \"headers\", {\n value: headers,\n configurable: true,\n enumerable: true,\n });\n }\n }\n get status(): number {\n // Static helpers (CellsResponse.json(), .redirect(), .error()) may build instances\n // without running this constructor: fall back to the platform's status.\n return #status in this ? this.#status : super.status;\n }\n\n /**\n * Every platform `Response` is a Workers `Response`, as on workerd: `Response.json()`,\n * `Response.redirect()` and `Response.error()` (the platform's static helpers, which build\n * platform instances, not instances of this subclass) and `fetch()` results pass\n * `instanceof Response` in Worker code. Astro checks every endpoint's result that way.\n */\n static [Symbol.hasInstance](v: unknown): boolean {\n return v instanceof HostResponse;\n }\n\n // In a browser the platform's static helpers build platform responses, whose headers drop\n // `Set-Cookie`: these build instances of this class there (`Response.json()` is how most\n // APIs answer, and their cookies are appended to it afterwards).\n static override json(data: unknown, init: ResponseInit = {}): Response {\n if (!dropsSetCookie) return super.json(data, init);\n const headers = new Headers(init.headers);\n if (!headers.has(\"content-type\")) headers.set(\"content-type\", \"application/json\");\n return new CellsResponse(JSON.stringify(data), { ...init, headers });\n }\n\n static override redirect(url: string | URL, status = 302): Response {\n if (!dropsSetCookie) return super.redirect(url, status);\n if (![301, 302, 303, 307, 308].includes(status))\n throw new RangeError(`Invalid status ${status}`);\n const base = (globalThis as { location?: { href: string } }).location?.href;\n return new CellsResponse(null, { status, headers: { location: new URL(url, base).href } });\n }\n}\n\n/** Workers `WebSocketRequestResponsePair` (ctx.setWebSocketAutoResponse). */\nexport class WebSocketRequestResponsePair {\n readonly request: string;\n readonly response: string;\n constructor(request: string, response: string) {\n this.request = request;\n this.response = response;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ws/server.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n byteSize,\n CellsCloseEvent,\n checkClose,\n type Frame,\n portEnd,\n relayFrames,\n toFrameData,\n} from \"./frames.js\";\n\nconst kPort = Symbol(\"cellb.ws.port\");\nconst kTakeQueued = Symbol(\"cellb.ws.takeQueued\");\n/** Server ends accepted in this realm and not yet closed (see `CfWebSocket.goAway`). */\nconst acceptedEnds = new Set<CfWebSocket>();\n\n/**\n * Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair):\n * events are delivered only after `accept()`; `send`/`close` as in workerd.\n * Hibernation (`ctx.acceptWebSocket`) is a later prototype.\n */\nexport class CfWebSocket extends EventTarget {\n static readonly READY_STATE_CONNECTING = 0;\n static readonly READY_STATE_OPEN = 1;\n static readonly READY_STATE_CLOSING = 2;\n static readonly READY_STATE_CLOSED = 3;\n readyState = 1;\n url: string | null = null;\n\n protocol: string | null = null;\n extensions: string | null = null;\n #binaryType: \"blob\" | \"arraybuffer\" = \"arraybuffer\";\n /** How binary messages arrive; other values are ignored, as in browsers. */\n get binaryType(): \"blob\" | \"arraybuffer\" {\n return this.#binaryType;\n }\n set binaryType(v: \"blob\" | \"arraybuffer\") {\n if (v === \"blob\" || v === \"arraybuffer\") this.#binaryType = v;\n }\n [kPort]: MessagePort | undefined;\n #accepted = false;\n #queued: Frame[] = [];\n /** The frames that arrived before accept(), removed (takeClientPort hands them over). */\n [kTakeQueued](): Frame[] {\n return this.#queued.splice(0);\n }\n #attachment: unknown;\n\n #lastPing = 0;\n #watch: ReturnType<typeof setInterval> | undefined;\n\n constructor(port: MessagePort) {\n super();\n this[kPort] = port;\n port.onmessage = (e) => {\n const f = e.data as Frame;\n if (f.t === \"p\") return this.#ping(f.hb); // answered even before accept()\n if (this.#accepted) this.#frame(f);\n else this.#queued.push(f);\n };\n }\n\n /**\n * Heartbeat: answer the client's ping; close 1006 when pings stop for 3 periods. The watch\n * starts with the first ping (a pair's ends used in this realm never ping) and keeps that\n * period. A later ping may announce a longer one (webrun-http-browser's client does when its\n * page turns hidden), but a tab that closes turns hidden on its way out: following it would\n * leave a dead tab's socket open for minutes. Pages keep pinging on time with unthrottled\n * timers (`timers`).\n */\n #ping(hb: number): void {\n this.#lastPing = Date.now();\n this[kPort]?.postMessage({ t: \"q\" } satisfies Frame);\n // A period no timer takes (2³¹−1 ms and up: `heartbeatMs: 0`) means no heartbeat.\n if (this.#watch || !hb || hb >= 2 ** 31 - 1) return;\n this.#watch = setInterval(() => {\n if (this.readyState === 3) return clearInterval(this.#watch);\n if (Date.now() - this.#lastPing > 3 * hb) this.#gone();\n }, hb);\n }\n\n /** The client stopped pinging (tab closed, worker killed): abnormal closure. */\n #gone(): void {\n clearInterval(this.#watch);\n if (this.readyState === 3) return;\n this.readyState = 3;\n acceptedEnds.delete(this);\n this[kPort]?.close();\n this.dispatchEvent(new CellsCloseEvent(\"close\", { code: 1006, reason: \"\", wasClean: false }));\n }\n\n /** The other end of the WebSocketPair (the runtime checks how the server end was accepted). */\n peer?: CfWebSocket;\n /** Accepted through ctx.acceptWebSocket() (hibernatable) rather than accept(). */\n hibernatable = false;\n get accepted(): boolean {\n return this.#accepted;\n }\n\n accept(): void {\n if (this.#accepted) return;\n if (!this[kPort])\n throw new TypeError(\"This WebSocket end was handed to a client; it cannot be accepted.\");\n this.#accepted = true;\n acceptedEnds.add(this);\n this[kPort].start?.();\n for (const f of this.#queued.splice(0)) this.#frame(f);\n }\n\n serializeAttachment(v: unknown): void {\n this.#attachment = structuredClone(v);\n }\n deserializeAttachment(): unknown {\n return structuredClone(this.#attachment);\n }\n\n #frame(f: Frame): void {\n if (f.t === \"m\") {\n const data = typeof f.d !== \"string\" && this.#binaryType === \"blob\" ? new Blob([f.d]) : f.d;\n this.dispatchEvent(new MessageEvent(\"message\", { data }));\n // The sender's bufferedAmount drains by the bytes dispatched.\n this[kPort]?.postMessage({ t: \"a\", n: byteSize(f.d) } satisfies Frame);\n return;\n }\n if (f.t !== \"c\") return;\n const wasOpen = this.readyState === 1;\n if (wasOpen)\n this[kPort]!.postMessage({ t: \"c\", code: f.code, reason: f.reason } satisfies Frame);\n this.readyState = 3;\n acceptedEnds.delete(this);\n clearInterval(this.#watch);\n this[kPort]!.close();\n this.dispatchEvent(\n new CellsCloseEvent(\"close\", { code: f.code, reason: f.reason, wasClean: true }),\n );\n }\n\n /**\n * Closes every server end accepted in this realm with 1001 (going away). A shard worker that\n * hands its cells to another tab does it before it stops, so that clients reconnect at once\n * instead of noticing through the heartbeat; Worker code cannot send 1001 itself (`close`).\n */\n static goAway(reason = \"moving to another worker\"): void {\n for (const ws of acceptedEnds) {\n acceptedEnds.delete(ws);\n if (ws.readyState >= 2) continue;\n ws.readyState = 2;\n ws[kPort]?.postMessage({ t: \"c\", code: 1001, reason } satisfies Frame);\n }\n }\n\n send(data: string | ArrayBuffer | ArrayBufferView): void {\n // celld lets either end of a pair send before accept() (workerd refuses); frames wait in\n // the port until the other end listens.\n if (this.readyState !== 1) throw new TypeError(\"Can't call WebSocket send() after close().\");\n const d = toFrameData(data);\n this[kPort]!.postMessage({ t: \"m\", d } satisfies Frame, typeof d === \"string\" ? [] : [d]);\n }\n\n close(code?: number, reason?: string): void {\n checkClose(code, reason);\n if (this.readyState >= 2) return;\n this.readyState = 2;\n this[kPort]!.postMessage({ t: \"c\", code: code ?? 1005, reason: reason ?? \"\" } satisfies Frame);\n }\n}\n\n/** The host's own WebSocket (Node's, or the browser's): the client sockets Worker code opens. */\nconst HostWebSocket = (\n globalThis as { WebSocket?: new (url: string, p?: string | string[]) => object }\n).WebSocket;\n\n/**\n * The `WebSocket` global of Worker code. On workerd one class covers both ends of a\n * WebSocketPair and the client sockets `new WebSocket(url)` opens, and libraries rely on that:\n * the Agents SDK (partyserver) reads attachments with\n * `WebSocket.prototype.deserializeAttachment.call(ws)` and compares `readyState` with\n * `WebSocket.READY_STATE_OPEN`. So its prototype is the server end's (`CfWebSocket`), its\n * statics are workerd's, `instanceof` holds for both kinds, and `new WebSocket(url)` still opens\n * the host's client socket.\n */\nexport const WorkersWebSocket = (() => {\n function WebSocket(this: unknown, url: string, protocols?: string | string[]): object {\n if (!new.target)\n throw new TypeError(\"Failed to construct 'WebSocket': Please use the 'new' operator.\");\n if (!HostWebSocket) throw new Error(\"WebSocket clients are not available in this realm.\");\n return new HostWebSocket(url, protocols);\n }\n WebSocket.prototype = CfWebSocket.prototype;\n const states = { CONNECTING: 0, OPEN: 1, CLOSING: 2, CLOSED: 3 } as const;\n for (const [name, value] of Object.entries(states)) {\n Object.defineProperty(WebSocket, name, { value, enumerable: true });\n Object.defineProperty(WebSocket, `READY_STATE_${name}`, { value, enumerable: true });\n }\n Object.defineProperty(WebSocket, Symbol.hasInstance, {\n value: (v: unknown) =>\n v instanceof CfWebSocket || (HostWebSocket !== undefined && v instanceof HostWebSocket),\n });\n return WebSocket as unknown as typeof CfWebSocket & {\n new (url: string, protocols?: string | string[]): CfWebSocket;\n readonly CONNECTING: 0;\n readonly OPEN: 1;\n readonly CLOSING: 2;\n readonly CLOSED: 3;\n };\n})();\n\n/** `new WebSocketPair()` → `{ 0: client, 1: server }`, entangled over a MessageChannel. */\nexport class WebSocketPair {\n 0: CfWebSocket;\n 1: CfWebSocket;\n constructor() {\n const { port1, port2 } = new MessageChannel();\n this[0] = new CfWebSocket(port1);\n this[1] = new CfWebSocket(port2);\n this[0].peer = this[1];\n this[1].peer = this[0];\n }\n /** `const [client, server] = new WebSocketPair()`, as in workerd. */\n *[Symbol.iterator](): IterableIterator<CfWebSocket> {\n yield this[0];\n yield this[1];\n }\n}\n\n/**\n * `WebSocketPair` under the `websocket_standard_binary_type` compatibility flag: both ends\n * start with `binaryType` \"blob\", as browsers' WebSockets do. Without it they start with\n * \"arraybuffer\", workerd's older default.\n */\nexport class StandardWebSocketPair extends WebSocketPair {\n constructor() {\n super();\n this[0].binaryType = \"blob\";\n this[1].binaryType = \"blob\";\n }\n}\n\n/**\n * Detaches the transport of the CLIENT end returned in a 101 response, so the\n * runtime can hand the raw MessagePort to the real client (transferable).\n */\nexport function takeClientPort(ws: CfWebSocket): MessagePort {\n const port = ws[kPort];\n if (!port) throw new TypeError(\"WebSocket end already handed over\");\n ws[kPort] = undefined;\n port.onmessage = null;\n ws.readyState = 3; // handed over: this end must not answer pings any more\n // Frames the server sent before the response was handed over (partyserver greets in\n // onConnect, before it returns the 101) reached this end and wait in its queue: the new\n // holder gets them first, then the rest, through a channel that relays both ways.\n const queued = ws[kTakeQueued]();\n if (!queued.length) return port;\n const relay = new MessageChannel();\n for (const f of queued) relay.port2.postMessage(f);\n relayFrames(portEnd(relay.port2), portEnd(port)); // closed with the socket's handshake\n return relay.port1;\n}\n\n/**\n * `Response` that also accepts Cloudflare's `{ status: 101, webSocket }` init —\n * the platform `Response` rejects status 101. Provided to user code via ~deps.\n */\n/** The platform `Response`, as this module sees it (it is external to user bundles). */\nconst HostResponse = Response;\n\n/** Browsers drop `Set-Cookie` from every Response they build (Node keeps it). */\nconst dropsSetCookie = !new HostResponse(null, { headers: { \"set-cookie\": \"a=b\" } }).headers.has(\n \"set-cookie\",\n);\n\nexport class CellsResponse extends Response {\n readonly #status: number;\n readonly webSocket: CfWebSocket | null;\n constructor(\n body?: BodyInit | null,\n init: ResponseInit & { webSocket?: CfWebSocket | null } = {},\n ) {\n const upgrade = init.status === 101;\n // The init's members read one by one, as Fetch reads a dictionary: an init that is itself a\n // Response (`new Response(body, response)`, as opencode's SDK does) keeps its headers and\n // statusText, which a spread would drop (they are getters on its prototype).\n super(upgrade ? null : body, {\n status: upgrade ? 200 : init.status,\n statusText: init.statusText,\n headers: init.headers,\n });\n this.#status = init.status ?? 200;\n this.webSocket = init.webSocket ?? null;\n if (dropsSetCookie) {\n // In a browser the platform's headers drop `Set-Cookie`, which Workers set (Astro appends\n // its cookies to the response): the response keeps its headers in a standalone `Headers`,\n // the ones the platform derived (content-type) plus the Set-Cookie values of the init.\n const headers = new Headers(super.headers);\n const given = init.headers instanceof Headers ? init.headers : new Headers(init.headers);\n for (const c of given.getSetCookie()) headers.append(\"set-cookie\", c);\n Object.defineProperty(this, \"headers\", {\n value: headers,\n configurable: true,\n enumerable: true,\n });\n }\n }\n get status(): number {\n // Static helpers (CellsResponse.json(), .redirect(), .error()) may build instances\n // without running this constructor: fall back to the platform's status.\n return #status in this ? this.#status : super.status;\n }\n\n /**\n * Every platform `Response` is a Workers `Response`, as on workerd: `Response.json()`,\n * `Response.redirect()` and `Response.error()` (the platform's static helpers, which build\n * platform instances, not instances of this subclass) and `fetch()` results pass\n * `instanceof Response` in Worker code. Astro checks every endpoint's result that way.\n */\n static [Symbol.hasInstance](v: unknown): boolean {\n return v instanceof HostResponse;\n }\n\n // In a browser the platform's static helpers build platform responses, whose headers drop\n // `Set-Cookie`: these build instances of this class there (`Response.json()` is how most\n // APIs answer, and their cookies are appended to it afterwards).\n static override json(data: unknown, init: ResponseInit = {}): Response {\n if (!dropsSetCookie) return super.json(data, init);\n const headers = new Headers(init.headers);\n if (!headers.has(\"content-type\")) headers.set(\"content-type\", \"application/json\");\n return new CellsResponse(JSON.stringify(data), { ...init, headers });\n }\n\n static override redirect(url: string | URL, status = 302): Response {\n if (!dropsSetCookie) return super.redirect(url, status);\n if (![301, 302, 303, 307, 308].includes(status))\n throw new RangeError(`Invalid status ${status}`);\n const base = (globalThis as { location?: { href: string } }).location?.href;\n return new CellsResponse(null, { status, headers: { location: new URL(url, base).href } });\n }\n}\n\n/** Workers `WebSocketRequestResponsePair` (ctx.setWebSocketAutoResponse). */\nexport class WebSocketRequestResponsePair {\n readonly request: string;\n readonly response: string;\n constructor(request: string, response: string) {\n this.request = request;\n this.response = response;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/cellb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "cellb: isomorphic Durable Objects runtime (celld/Cloudflare-compatible surface) for browsers and servers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"types": "./dist/browser.d.ts",
|
|
27
27
|
"default": "./dist/browser.js"
|
|
28
28
|
},
|
|
29
|
+
"./site": {
|
|
30
|
+
"types": "./dist/site.d.ts",
|
|
31
|
+
"default": "./dist/site.js"
|
|
32
|
+
},
|
|
29
33
|
"./host-modules": {
|
|
30
34
|
"types": "./dist/project/host-modules.d.ts",
|
|
31
35
|
"default": "./dist/project/host-modules.js"
|
|
@@ -45,18 +49,15 @@
|
|
|
45
49
|
"access": "public"
|
|
46
50
|
},
|
|
47
51
|
"dependencies": {
|
|
48
|
-
"@noble/hashes": "^
|
|
52
|
+
"@noble/hashes": "^2.4.0",
|
|
49
53
|
"@sqlite.org/sqlite-wasm": "^3.53.4-build1",
|
|
50
54
|
"@statewalker/webrun-files": "^0.10.0",
|
|
51
55
|
"@statewalker/webrun-files-browser": "^0.10.0",
|
|
52
56
|
"@statewalker/webrun-files-mem": "^0.10.0",
|
|
53
|
-
"@statewalker/webrun-http-browser": "^0.7.
|
|
54
|
-
"@statewalker/webrun-http-streams": "^0.3.
|
|
55
|
-
"@statewalker/webrun-
|
|
56
|
-
"@statewalker/webrun-
|
|
57
|
-
"@statewalker/webrun-site-builder": "^0.2.0",
|
|
58
|
-
"@statewalker/webrun-site-host": "^0.3.0",
|
|
59
|
-
"@statewalker/webrun-streams": "^0.2.0",
|
|
57
|
+
"@statewalker/webrun-http-browser": "^0.7.1",
|
|
58
|
+
"@statewalker/webrun-http-streams": "^0.3.1",
|
|
59
|
+
"@statewalker/webrun-rpc": "^0.5.1",
|
|
60
|
+
"@statewalker/webrun-streams": "^0.2.3",
|
|
60
61
|
"acorn": "^8.19.0",
|
|
61
62
|
"assert": "^2.1.0",
|
|
62
63
|
"buffer": "^6.0.3",
|
|
@@ -68,9 +69,21 @@
|
|
|
68
69
|
"ws": "^8.22.0"
|
|
69
70
|
},
|
|
70
71
|
"peerDependencies": {
|
|
72
|
+
"@statewalker/webrun-modules": "^0.3.5",
|
|
73
|
+
"@statewalker/webrun-site-builder": "^0.2.0",
|
|
74
|
+
"@statewalker/webrun-site-host": "^0.3.0",
|
|
71
75
|
"esbuild": ">=0.25"
|
|
72
76
|
},
|
|
73
77
|
"peerDependenciesMeta": {
|
|
78
|
+
"@statewalker/webrun-modules": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"@statewalker/webrun-site-builder": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"@statewalker/webrun-site-host": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
74
87
|
"esbuild": {
|
|
75
88
|
"optional": true
|
|
76
89
|
}
|
|
@@ -78,12 +91,16 @@
|
|
|
78
91
|
"devDependencies": {
|
|
79
92
|
"@playwright/test": "^1.63.0",
|
|
80
93
|
"@sparticuz/chromium": "^153.0.0",
|
|
81
|
-
"@statewalker/webrun-
|
|
94
|
+
"@statewalker/webrun-modules": "^0.3.5",
|
|
95
|
+
"@statewalker/webrun-site-builder": "^0.2.0",
|
|
96
|
+
"@statewalker/webrun-site-host": "^0.3.0",
|
|
82
97
|
"@types/node": "^26.6.4",
|
|
83
98
|
"@types/ws": "^8.18.2",
|
|
84
99
|
"esbuild": "^0.28.0",
|
|
85
100
|
"html-rewriter-wasm": "^0.4.1",
|
|
86
101
|
"miniflare": "4.20260730.0",
|
|
102
|
+
"partyserver": "0.5.10",
|
|
103
|
+
"partywhen": "0.1.5",
|
|
87
104
|
"sqlite-vec-wasm-demo": "0.1.6",
|
|
88
105
|
"typescript": "^7.0.2",
|
|
89
106
|
"vite": "^8.3.2",
|
|
@@ -93,6 +110,8 @@
|
|
|
93
110
|
"scripts": {
|
|
94
111
|
"build": "node scripts/build.mjs",
|
|
95
112
|
"test": "vitest run",
|
|
113
|
+
"bench": "node scripts/build.mjs && node --expose-gc bench/node-bench.mjs",
|
|
114
|
+
"bench:browser": "PW_CHROMIUM=\"${PW_CHROMIUM:-$(node scripts/npm-chromium.mjs)}\" playwright test --config playwright.probes.config.ts --reporter=line bench",
|
|
96
115
|
"test:unit": "vitest run test/",
|
|
97
116
|
"test:e2e": "vitest run e2e/",
|
|
98
117
|
"typecheck": "tsc --noEmit && tsc --noEmit -p e2e-browser/pages/tsconfig.json",
|