@statewalker/cellb 0.3.0 → 0.5.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 +46 -3
- package/bin/cellb.mjs +20 -0
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +1 -0
- package/dist/browser.js.map +1 -1
- package/dist/bucket/celld-cells.js +25 -3
- package/dist/bucket/celld-cells.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -0
- package/dist/isolate/browser.d.ts +2 -1
- package/dist/isolate/browser.js +2 -1
- package/dist/isolate/browser.js.map +1 -1
- package/dist/isolate/isolate.js +5 -3
- package/dist/isolate/isolate.js.map +1 -1
- package/dist/isolate/protocol.d.ts +4 -2
- package/dist/isolate/protocol.js +4 -4
- package/dist/isolate/protocol.js.map +1 -1
- package/dist/node.d.ts +3 -0
- package/dist/node.js +2 -0
- package/dist/node.js.map +1 -1
- package/dist/project/browser-host.d.ts +6 -12
- package/dist/project/browser-host.js +2 -25
- package/dist/project/browser-host.js.map +1 -1
- package/dist/project/browser-load.d.ts +22 -0
- package/dist/project/browser-load.js +63 -0
- package/dist/project/browser-load.js.map +1 -0
- package/dist/project/browser-shards.d.ts +8 -9
- package/dist/project/browser-shards.js +88 -52
- package/dist/project/browser-shards.js.map +1 -1
- package/dist/project/browser-tabs.d.ts +14 -4
- package/dist/project/browser-tabs.js +193 -208
- package/dist/project/browser-tabs.js.map +1 -1
- package/dist/project/build.d.ts +14 -0
- package/dist/project/build.js +53 -0
- package/dist/project/build.js.map +1 -0
- package/dist/project/built-project.d.ts +88 -0
- package/dist/project/built-project.js +118 -0
- package/dist/project/built-project.js.map +1 -0
- package/dist/project/node-load.d.ts +14 -0
- package/dist/project/node-load.js +187 -0
- package/dist/project/node-load.js.map +1 -0
- package/dist/project/wrangler.d.ts +9 -0
- package/dist/project/wrangler.js +24 -0
- package/dist/project/wrangler.js.map +1 -1
- package/dist/transport/cells-http.d.ts +6 -0
- package/dist/transport/cells-http.js +22 -1
- package/dist/transport/cells-http.js.map +1 -1
- package/dist/transport/cells-link.d.ts +2 -0
- package/dist/transport/cells-link.js +5 -0
- package/dist/transport/cells-link.js.map +1 -1
- package/dist/transport/port.js +4 -0
- package/dist/transport/port.js.map +1 -1
- package/dist/ws/server.d.ts +39 -23
- package/dist/ws/server.js +99 -140
- package/dist/ws/server.js.map +1 -1
- package/package.json +9 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cells-link.js","sourceRoot":"","sources":["../../src/transport/cells-link.ts"],"names":[],"mappings":"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","sourcesContent":["import { deserializeError } from \"@statewalker/webrun-streams\";\nimport { toAnotherNode } from \"../core/handles.js\";\nimport type { DurableObjectId } from \"../core/id.js\";\nimport type { ObjectEndpoint } from \"../core/namespace.js\";\nimport type { ValueCodec } from \"../ports.js\";\nimport { carryWebSocketHeaders } from \"../ws/upgrade.js\";\n\n/** Control headers that mark internal requests on a cells-http link (see cells-http.ts). */\nexport const H = {\n kind: \"x-cellb\", // \"describe\" | \"fetch\" | \"rpc\" | \"error\" (response)\n binding: \"x-cellb-binding\",\n target: \"x-cellb-target\", // \"name:<n>\" | \"id:<hex>\"\n method: \"x-cellb-method\",\n proto: \"x-cellb-proto\", // original scheme (the HTTP/1.1 wire drops it)\n ws: \"x-cellb-ws\", // request: id under which the caller awaits a WebSocket port\n wsAccepted: \"x-cellb-ws-accepted\", // response: the port was handed over (control channel)\n upgrade: \"x-cellb-upgrade\", // `Upgrade` is hop-by-hop: dropped by the HTTP/1.1 wire, carried here\n wsProtocol: \"x-cellb-ws-protocol\", // `Sec-WebSocket-Protocol` is forbidden in browser Requests\n shard: \"x-cellb-shard\", // the shard that owns the target, as the sending shard computed it\n // Ingress from a browser (a ServiceWorker): `Cookie` is forbidden in browser Requests and\n // `Set-Cookie` in browser Responses, so the page's cookies travel here, and the Worker's\n // Set-Cookie values come back URI-encoded and comma-separated (browser-cookies.ts).\n cookie: \"x-cellb-cookie\",\n setCookie: \"x-cellb-set-cookie\",\n} as const;\n\nexport type Send = (req: Request) => Promise<Response>;\n\n/** A `send` that carries the scheme and turns `x-cellb: error` responses back into exceptions. */\nexport function postVia(send: Send): Send {\n return async (req) => {\n const res = await send(withScheme(req));\n if (res.headers.get(H.kind) === \"error\") throw deserializeError(await res.json());\n return res;\n };\n}\n\n/** Stub endpoint whose object lives behind `post` (another worker, tab or peer). */\nexport function httpEndpoint(\n post: Send,\n codec: ValueCodec,\n binding: string,\n className: string,\n id: DurableObjectId,\n): ObjectEndpoint {\n // By name when the id came from one (only idFromName() sets `name`): keeps ctx.id.name\n // on the far side, which derives the same id with the same namespace key.\n const target = id.name !== undefined ? `name:${id.name}` : `id:${id}`;\n const base = { [H.binding]: binding, [H.target]: target };\n return {\n // Not the caller's abort signal: an aborted caller does not cancel a local object's\n // fetch() either (the object answers, nobody reads), so neither does a remote one.\n fetch: (req) => post(addHeaders(req, { [H.kind]: \"fetch\", ...base }, { detach: true })),\n async call(method, args) {\n const res = await post(\n control(\"rpc\", { ...base, [H.method]: method }, codec.encode(toAnotherNode(args))),\n );\n return codec.decode(new Uint8Array(await res.arrayBuffer()));\n },\n };\n}\n\nexport function control(kind: string, headers: Record<string, string>, body?: Uint8Array): Request {\n return new Request(\"http://cellb.internal/\", {\n method: body ? \"POST\" : \"GET\",\n headers: { [H.kind]: kind, ...headers },\n body: body as BodyInit | undefined,\n });\n}\n\n/**\n * Re-creates a request with extra headers, streaming its body through. The request itself is the\n * source, not `body: req.body`: Firefox has no `Request.body`, so that would drop the body there.\n */\nfunction addHeaders(\n req: Request,\n extra: Record<string, string>,\n opts: { detach?: boolean } = {},\n): Request {\n const headers = new Headers(req.headers);\n for (const [k, v] of Object.entries(extra)) headers.set(k, v);\n return keepUpgrade(req, new Request(req, opts.detach ? { headers, signal: null } : { headers }));\n}\n\n/** Browsers drop the forbidden `Upgrade` header from every rebuilt Request: carry it over. */\nexport function keepUpgrade(from: Request, to: Request): Request {\n return carryWebSocketHeaders(from.headers, to);\n}\n\nexport function withScheme(req: Request): Request {\n return addHeaders(req, { [H.proto]: new URL(req.url).protocol.replace(\":\", \"\") });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cells-link.js","sourceRoot":"","sources":["../../src/transport/cells-link.ts"],"names":[],"mappings":"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","sourcesContent":["import { deserializeError } from \"@statewalker/webrun-streams\";\nimport { toAnotherNode } from \"../core/handles.js\";\nimport type { DurableObjectId } from \"../core/id.js\";\nimport type { ObjectEndpoint } from \"../core/namespace.js\";\nimport type { ValueCodec } from \"../ports.js\";\nimport { carryWebSocketHeaders } from \"../ws/upgrade.js\";\n\n/** Control headers that mark internal requests on a cells-http link (see cells-http.ts). */\nexport const H = {\n kind: \"x-cellb\", // \"describe\" | \"fetch\" | \"rpc\" | \"error\" (response)\n binding: \"x-cellb-binding\",\n target: \"x-cellb-target\", // \"name:<n>\" | \"id:<hex>\"\n method: \"x-cellb-method\",\n proto: \"x-cellb-proto\", // original scheme (the HTTP/1.1 wire drops it)\n ws: \"x-cellb-ws\", // request: id under which the caller awaits a WebSocket port\n wsAccepted: \"x-cellb-ws-accepted\", // response: the port was handed over (control channel)\n upgrade: \"x-cellb-upgrade\", // `Upgrade` is hop-by-hop: dropped by the HTTP/1.1 wire, carried here\n wsProtocol: \"x-cellb-ws-protocol\", // `Sec-WebSocket-Protocol` is forbidden in browser Requests\n shard: \"x-cellb-shard\", // the shard that owns the target, as the sending shard computed it\n // A call between shards through a ServiceWorker goes to `http://cellb.internal/<project>/`\n // (the router's key for it): the url the caller asked for travels here.\n url: \"x-cellb-url\",\n // stripBase: the prefix the router keeps in the url (its key) for the Worker to cut off.\n base: \"x-cellb-base\",\n // Ingress from a browser (a ServiceWorker): `Cookie` is forbidden in browser Requests and\n // `Set-Cookie` in browser Responses, so the page's cookies travel here, and the Worker's\n // Set-Cookie values come back URI-encoded and comma-separated (browser-cookies.ts).\n cookie: \"x-cellb-cookie\",\n setCookie: \"x-cellb-set-cookie\",\n} as const;\n\nexport type Send = (req: Request) => Promise<Response>;\n\n/** A `send` that carries the scheme and turns `x-cellb: error` responses back into exceptions. */\nexport function postVia(send: Send): Send {\n return async (req) => {\n const res = await send(withScheme(req));\n if (res.headers.get(H.kind) === \"error\") throw deserializeError(await res.json());\n return res;\n };\n}\n\n/** Stub endpoint whose object lives behind `post` (another worker, tab or peer). */\nexport function httpEndpoint(\n post: Send,\n codec: ValueCodec,\n binding: string,\n className: string,\n id: DurableObjectId,\n): ObjectEndpoint {\n // By name when the id came from one (only idFromName() sets `name`): keeps ctx.id.name\n // on the far side, which derives the same id with the same namespace key.\n const target = id.name !== undefined ? `name:${id.name}` : `id:${id}`;\n const base = { [H.binding]: binding, [H.target]: target };\n return {\n // Not the caller's abort signal: an aborted caller does not cancel a local object's\n // fetch() either (the object answers, nobody reads), so neither does a remote one.\n fetch: (req) => post(addHeaders(req, { [H.kind]: \"fetch\", ...base }, { detach: true })),\n async call(method, args) {\n const res = await post(\n control(\"rpc\", { ...base, [H.method]: method }, codec.encode(toAnotherNode(args))),\n );\n return codec.decode(new Uint8Array(await res.arrayBuffer()));\n },\n };\n}\n\nexport function control(kind: string, headers: Record<string, string>, body?: Uint8Array): Request {\n return new Request(\"http://cellb.internal/\", {\n method: body ? \"POST\" : \"GET\",\n headers: { [H.kind]: kind, ...headers },\n body: body as BodyInit | undefined,\n });\n}\n\n/**\n * Re-creates a request with extra headers, streaming its body through. The request itself is the\n * source, not `body: req.body`: Firefox has no `Request.body`, so that would drop the body there.\n */\nfunction addHeaders(\n req: Request,\n extra: Record<string, string>,\n opts: { detach?: boolean } = {},\n): Request {\n const headers = new Headers(req.headers);\n for (const [k, v] of Object.entries(extra)) headers.set(k, v);\n return keepUpgrade(req, new Request(req, opts.detach ? { headers, signal: null } : { headers }));\n}\n\n/** Browsers drop the forbidden `Upgrade` header from every rebuilt Request: carry it over. */\nexport function keepUpgrade(from: Request, to: Request): Request {\n return carryWebSocketHeaders(from.headers, to);\n}\n\nexport function withScheme(req: Request): Request {\n return addHeaders(req, { [H.proto]: new URL(req.url).protocol.replace(\":\", \"\") });\n}\n"]}
|
package/dist/transport/port.js
CHANGED
|
@@ -6,6 +6,10 @@ import { withScheme } from "./cells-link.js";
|
|
|
6
6
|
* (p31-port-leak.test.ts). 4096 per link, cellb's ceiling before the links were the package's (its
|
|
7
7
|
* default is 1024): a streaming response holds its port for its whole life.
|
|
8
8
|
* `globalThis.__cellbMaxPorts` overrides it: tests that check nothing leaks set it low.
|
|
9
|
+
*
|
|
10
|
+
* Since webrun-http-browser 0.12 a body's chunks are pipelined under the link's flow-control
|
|
11
|
+
* window (256 KiB by default) instead of going one confirmed chunk at a time: a reader that stops
|
|
12
|
+
* holds about one window of its body, so the bound is about `maxPorts` windows per link.
|
|
9
13
|
*/
|
|
10
14
|
const maxPorts = () => globalThis.__cellbMaxPorts ?? 4096;
|
|
11
15
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/transport/port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAGrF,OAAO,EAEL,gBAAgB,EAEhB,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAa,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAExD
|
|
1
|
+
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/transport/port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAGrF,OAAO,EAEL,gBAAgB,EAEhB,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAa,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;;;;;GASG;AACH,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAE,UAA2C,CAAC,eAAe,IAAI,IAAI,CAAC;AAQ5F;;;;GAIG;AACH,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,IAA8B,CAAC;AAElE;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAc,EACd,OAAoB,EACpB,IAAuB;IAEvB,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;AAChG,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAc,EACd,IAAuB;IAEvB,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IAClE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,oBAAoB,CAAC,IAAc,EAAE,OAAa;IAChE,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAc;IAEd,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC3E,OAAO;QACL,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACzC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;KAC1B,CAAC;AACJ,CAAC","sourcesContent":["import { connectHttpLink, serveHttpLink } from \"@statewalker/webrun-http-browser/sw\";\nimport type { overPipe } from \"@statewalker/webrun-rpc\";\nimport type { CellRuntime } from \"../core/runtime.js\";\nimport {\n type CellsHttpOptions,\n cellsHttpHandler,\n type RemoteRuntime,\n remoteRuntime,\n} from \"./cells-http.js\";\nimport { type Send, withScheme } from \"./cells-link.js\";\n\n/**\n * Multiplexed ports per link: one per exchange in flight, given back when the exchange ends\n * (p31-port-leak.test.ts). 4096 per link, cellb's ceiling before the links were the package's (its\n * default is 1024): a streaming response holds its port for its whole life.\n * `globalThis.__cellbMaxPorts` overrides it: tests that check nothing leaks set it low.\n *\n * Since webrun-http-browser 0.12 a body's chunks are pipelined under the link's flow-control\n * window (256 KiB by default) instead of going one confirmed chunk at a time: a reader that stops\n * holds about one window of its body, so the bound is about `maxPorts` windows per link.\n */\nconst maxPorts = () => (globalThis as { __cellbMaxPorts?: number }).__cellbMaxPorts ?? 4096;\n\n/**\n * A DEDICATED message pipe (MessagePort, or a worker_threads port). Don't pass a\n * Worker object that also carries other messages — transfer a MessageChannel port.\n */\nexport type CellPort = Parameters<typeof overPipe>[0];\n\n/**\n * The links are webrun-http-browser's (`connectHttpLink` / `serveHttpLink`): HTTP/1.1 with\n * streaming bodies over webrun-rpc calls, one multiplexed port per exchange. They take a\n * MessagePort; a worker_threads port has the same `postMessage` / `addEventListener` surface.\n */\nconst asPort = (port: CellPort) => port as unknown as MessagePort;\n\n/**\n * Cell-host side: serves the runtime's HTTP surface over a port. Resolves to an async\n * disposer.\n */\nexport function serveRuntime(\n port: CellPort,\n runtime: CellRuntime,\n opts?: CellsHttpOptions,\n): Promise<() => Promise<void>> {\n return serveHttpLink(asPort(port), cellsHttpHandler(runtime, opts), { maxPorts: maxPorts() });\n}\n\n/** Front side (page, SW bridge, another tab): a RemoteRuntime over a port. */\nexport async function connectRuntime(\n port: CellPort,\n opts?: CellsHttpOptions,\n): Promise<RemoteRuntime & { close(): Promise<void> }> {\n const link = await connectHttpLink(asPort(port), { maxPorts: maxPorts() });\n const remote = await remoteRuntime((req) => link.send(req), opts);\n return Object.assign(remote, { close: () => link.close() });\n}\n\n/** Serves any HTTP handler over a port (responder side of a link). */\nexport function serveHandlerOverPort(port: CellPort, handler: Send): Promise<() => Promise<void>> {\n return serveHttpLink(asPort(port), handler, { maxPorts: maxPorts() });\n}\n\n/** A `send` over a port (initiator side of a link); the scheme rides in a header. */\nexport async function linkOverPort(\n port: CellPort,\n): Promise<{ send: Send; close(): Promise<void> }> {\n const link = await connectHttpLink(asPort(port), { maxPorts: maxPorts() });\n return {\n send: (req) => link.send(withScheme(req)),\n close: () => link.close(),\n };\n}\n"]}
|
package/dist/ws/server.d.ts
CHANGED
|
@@ -1,44 +1,47 @@
|
|
|
1
|
+
import { ServerWebSocket } from "@statewalker/webrun-http-browser/sw";
|
|
1
2
|
import { type Frame } from "./frames.js";
|
|
2
|
-
declare const kPort: unique symbol;
|
|
3
|
-
declare const kTakeQueued: unique symbol;
|
|
4
3
|
/**
|
|
5
|
-
* Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair)
|
|
6
|
-
*
|
|
4
|
+
* Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair), on
|
|
5
|
+
* webrun-http-browser's `ServerWebSocket` and its frames, with workerd's semantics:
|
|
6
|
+
*
|
|
7
|
+
* - events only after `accept()` (frames that arrive earlier wait), and either end may `send()`
|
|
8
|
+
* before it (celld does; the frames wait in the port);
|
|
9
|
+
* - `close()` with no code sends 1005 and waits for the echo however long; the codes Worker code
|
|
10
|
+
* may send are 1000 and 3000–4999; messages in flight when `close()` was called still arrive;
|
|
11
|
+
* - the heartbeat watch starts with the client's first ping and keeps that period: a pair's ends
|
|
12
|
+
* used in one realm never ping, and a tab that closes turns hidden (announcing a longer period)
|
|
13
|
+
* on its way out;
|
|
14
|
+
* - attachments, `peer`, `hibernatable`, and `goAway` for the server ends of this realm.
|
|
15
|
+
*
|
|
7
16
|
* Hibernation (`ctx.acceptWebSocket`) is a later prototype.
|
|
8
17
|
*/
|
|
9
|
-
export declare class CfWebSocket extends
|
|
18
|
+
export declare class CfWebSocket extends ServerWebSocket {
|
|
10
19
|
#private;
|
|
11
20
|
static readonly READY_STATE_CONNECTING = 0;
|
|
12
21
|
static readonly READY_STATE_OPEN = 1;
|
|
13
22
|
static readonly READY_STATE_CLOSING = 2;
|
|
14
23
|
static readonly READY_STATE_CLOSED = 3;
|
|
15
|
-
readyState: number;
|
|
16
|
-
url: string | null;
|
|
17
|
-
protocol: string | null;
|
|
18
24
|
extensions: string | null;
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
set binaryType(v: "blob" | "arraybuffer");
|
|
22
|
-
[kPort]: MessagePort | undefined;
|
|
23
|
-
/** The frames that arrived before accept(), removed (takeClientPort hands them over). */
|
|
24
|
-
[kTakeQueued](): Frame[];
|
|
25
|
-
constructor(port: MessagePort);
|
|
25
|
+
/** `queued`: frames the end received before `port` reached it, delivered first (a 101's). */
|
|
26
|
+
constructor(port: MessagePort, queued?: Frame[]);
|
|
26
27
|
/** The other end of the WebSocketPair (the runtime checks how the server end was accepted). */
|
|
27
28
|
peer?: CfWebSocket;
|
|
28
29
|
/** Accepted through ctx.acceptWebSocket() (hibernatable) rather than accept(). */
|
|
29
30
|
hibernatable: boolean;
|
|
30
|
-
get accepted(): boolean;
|
|
31
31
|
accept(): void;
|
|
32
|
+
send(data: string | ArrayBuffer | ArrayBufferView): void;
|
|
33
|
+
protected checkClose(code: number | undefined, reason: string | undefined): void;
|
|
34
|
+
protected _onClosed(): void;
|
|
32
35
|
serializeAttachment(v: unknown): void;
|
|
33
36
|
deserializeAttachment(): unknown;
|
|
34
37
|
/**
|
|
35
|
-
* Closes every server end
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
+
* Closes every server end of this realm with 1001 (going away): the accepted ones, and those
|
|
39
|
+
* whose client end was handed over but which were never accepted. A shard worker that hands
|
|
40
|
+
* its cells to another tab does it before it stops, and an isolate before it is terminated,
|
|
41
|
+
* so that clients reconnect at once instead of noticing through the heartbeat; Worker code
|
|
42
|
+
* cannot send 1001 itself (`close`).
|
|
38
43
|
*/
|
|
39
44
|
static goAway(reason?: string): void;
|
|
40
|
-
send(data: string | ArrayBuffer | ArrayBufferView): void;
|
|
41
|
-
close(code?: number, reason?: string): void;
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
47
|
* The `WebSocket` global of Worker code. On workerd one class covers both ends of a
|
|
@@ -72,9 +75,23 @@ export declare class WebSocketPair {
|
|
|
72
75
|
export declare class StandardWebSocketPair extends WebSocketPair {
|
|
73
76
|
constructor();
|
|
74
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Detaches the CLIENT end returned in a 101 response: its own port, and the frames the server
|
|
80
|
+
* sent before the response was handed over (partyserver greets in onConnect, before it returns
|
|
81
|
+
* the 101), which reached this end and wait in its queue. A holder that replays them
|
|
82
|
+
* (`new CfWebSocket(port, frames)`) needs no relay. Only when this end itself sent frames not
|
|
83
|
+
* yet acknowledged does `detach()` keep a relay (it absorbs their acknowledgements); the frames
|
|
84
|
+
* then travel through it.
|
|
85
|
+
*/
|
|
86
|
+
export declare function takeClientEnd(ws: CfWebSocket): {
|
|
87
|
+
port: MessagePort;
|
|
88
|
+
frames: Frame[];
|
|
89
|
+
};
|
|
75
90
|
/**
|
|
76
91
|
* Detaches the transport of the CLIENT end returned in a 101 response, so the
|
|
77
|
-
* runtime can hand the raw MessagePort to the real client (transferable).
|
|
92
|
+
* runtime can hand the raw MessagePort to the real client (transferable). Frames the server
|
|
93
|
+
* sent before the hand-over go to the new holder first, then the rest (ServerWebSocket.detach
|
|
94
|
+
* relays them).
|
|
78
95
|
*/
|
|
79
96
|
export declare function takeClientPort(ws: CfWebSocket): MessagePort;
|
|
80
97
|
export declare class CellsResponse extends Response {
|
|
@@ -100,4 +117,3 @@ export declare class WebSocketRequestResponsePair {
|
|
|
100
117
|
readonly response: string;
|
|
101
118
|
constructor(request: string, response: string);
|
|
102
119
|
}
|
|
103
|
-
export {};
|
package/dist/ws/server.js
CHANGED
|
@@ -1,102 +1,75 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const kTakeQueued = Symbol("cellb.ws.takeQueued");
|
|
4
|
-
/** Server ends accepted in this realm and not yet closed (see `CfWebSocket.goAway`). */
|
|
5
|
-
const acceptedEnds = new Set();
|
|
1
|
+
import { ServerWebSocket } from "@statewalker/webrun-http-browser/sw";
|
|
2
|
+
import { checkClose } from "./frames.js";
|
|
6
3
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Server ends of this realm that a client may be waiting on, not yet closed: the accepted ones,
|
|
5
|
+
* and those whose client end was handed over (see `CfWebSocket.goAway`).
|
|
6
|
+
*/
|
|
7
|
+
const serverEnds = new Set();
|
|
8
|
+
/**
|
|
9
|
+
* Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair), on
|
|
10
|
+
* webrun-http-browser's `ServerWebSocket` and its frames, with workerd's semantics:
|
|
11
|
+
*
|
|
12
|
+
* - events only after `accept()` (frames that arrive earlier wait), and either end may `send()`
|
|
13
|
+
* before it (celld does; the frames wait in the port);
|
|
14
|
+
* - `close()` with no code sends 1005 and waits for the echo however long; the codes Worker code
|
|
15
|
+
* may send are 1000 and 3000–4999; messages in flight when `close()` was called still arrive;
|
|
16
|
+
* - the heartbeat watch starts with the client's first ping and keeps that period: a pair's ends
|
|
17
|
+
* used in one realm never ping, and a tab that closes turns hidden (announcing a longer period)
|
|
18
|
+
* on its way out;
|
|
19
|
+
* - attachments, `peer`, `hibernatable`, and `goAway` for the server ends of this realm.
|
|
20
|
+
*
|
|
9
21
|
* Hibernation (`ctx.acceptWebSocket`) is a later prototype.
|
|
10
22
|
*/
|
|
11
|
-
export class CfWebSocket extends
|
|
23
|
+
export class CfWebSocket extends ServerWebSocket {
|
|
12
24
|
static READY_STATE_CONNECTING = 0;
|
|
13
25
|
static READY_STATE_OPEN = 1;
|
|
14
26
|
static READY_STATE_CLOSING = 2;
|
|
15
27
|
static READY_STATE_CLOSED = 3;
|
|
16
|
-
readyState = 1;
|
|
17
|
-
url = null;
|
|
18
|
-
protocol = null;
|
|
19
28
|
extensions = null;
|
|
20
|
-
#binaryType = "arraybuffer";
|
|
21
|
-
/** How binary messages arrive; other values are ignored, as in browsers. */
|
|
22
|
-
get binaryType() {
|
|
23
|
-
return this.#binaryType;
|
|
24
|
-
}
|
|
25
|
-
set binaryType(v) {
|
|
26
|
-
if (v === "blob" || v === "arraybuffer")
|
|
27
|
-
this.#binaryType = v;
|
|
28
|
-
}
|
|
29
|
-
[kPort];
|
|
30
|
-
#accepted = false;
|
|
31
|
-
#queued = [];
|
|
32
|
-
/** The frames that arrived before accept(), removed (takeClientPort hands them over). */
|
|
33
|
-
[kTakeQueued]() {
|
|
34
|
-
return this.#queued.splice(0);
|
|
35
|
-
}
|
|
36
29
|
#attachment;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
*/
|
|
60
|
-
#ping(hb) {
|
|
61
|
-
this.#lastPing = Date.now();
|
|
62
|
-
this[kPort]?.postMessage({ t: "q" });
|
|
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)
|
|
65
|
-
return;
|
|
66
|
-
this.#watch = setInterval(() => {
|
|
67
|
-
if (this.readyState === 3)
|
|
68
|
-
return clearInterval(this.#watch);
|
|
69
|
-
if (Date.now() - this.#lastPing > 3 * hb)
|
|
70
|
-
this.#gone();
|
|
71
|
-
}, hb);
|
|
72
|
-
}
|
|
73
|
-
/** The client stopped pinging (tab closed, worker killed): abnormal closure. */
|
|
74
|
-
#gone() {
|
|
75
|
-
clearInterval(this.#watch);
|
|
76
|
-
if (this.readyState === 3)
|
|
77
|
-
return;
|
|
78
|
-
this.readyState = 3;
|
|
79
|
-
acceptedEnds.delete(this);
|
|
80
|
-
this[kPort]?.close();
|
|
81
|
-
this.dispatchEvent(new CellsCloseEvent("close", { code: 1006, reason: "", wasClean: false }));
|
|
30
|
+
/** `queued`: frames the end received before `port` reached it, delivered first (a 101's). */
|
|
31
|
+
constructor(port, queued = []) {
|
|
32
|
+
super(port, {
|
|
33
|
+
watch: "first-ping",
|
|
34
|
+
heartbeatPeriod: "first",
|
|
35
|
+
sendBeforeAccept: true,
|
|
36
|
+
deliverWhileClosing: true,
|
|
37
|
+
defaultCloseCode: 1005,
|
|
38
|
+
closeTimeoutMs: Number.POSITIVE_INFINITY,
|
|
39
|
+
});
|
|
40
|
+
// workerd's server ends have no url or subprotocol of their own.
|
|
41
|
+
Object.defineProperty(this, "url", { value: null, writable: true, configurable: true });
|
|
42
|
+
Object.defineProperty(this, "protocol", { value: null, writable: true, configurable: true });
|
|
43
|
+
// Ahead of anything from the port, which reaches this end in a later task.
|
|
44
|
+
if (queued.length)
|
|
45
|
+
this._queue.unshift(...queued);
|
|
82
46
|
}
|
|
83
47
|
/** The other end of the WebSocketPair (the runtime checks how the server end was accepted). */
|
|
84
48
|
peer;
|
|
85
49
|
/** Accepted through ctx.acceptWebSocket() (hibernatable) rather than accept(). */
|
|
86
50
|
hibernatable = false;
|
|
87
|
-
get accepted() {
|
|
88
|
-
return this.#accepted;
|
|
89
|
-
}
|
|
90
51
|
accept() {
|
|
91
|
-
if (this
|
|
52
|
+
if (this.accepted)
|
|
92
53
|
return;
|
|
93
|
-
|
|
54
|
+
try {
|
|
55
|
+
super.accept();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
94
58
|
throw new TypeError("This WebSocket end was handed to a client; it cannot be accepted.");
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
59
|
+
}
|
|
60
|
+
serverEnds.add(this);
|
|
61
|
+
}
|
|
62
|
+
send(data) {
|
|
63
|
+
if (this.readyState !== 1)
|
|
64
|
+
throw new TypeError("Can't call WebSocket send() after close().");
|
|
65
|
+
super.send(data);
|
|
66
|
+
}
|
|
67
|
+
checkClose(code, reason) {
|
|
68
|
+
checkClose(code, reason);
|
|
69
|
+
}
|
|
70
|
+
_onClosed() {
|
|
71
|
+
serverEnds.delete(this);
|
|
72
|
+
super._onClosed();
|
|
100
73
|
}
|
|
101
74
|
serializeAttachment(v) {
|
|
102
75
|
this.#attachment = structuredClone(v);
|
|
@@ -104,54 +77,21 @@ export class CfWebSocket extends EventTarget {
|
|
|
104
77
|
deserializeAttachment() {
|
|
105
78
|
return structuredClone(this.#attachment);
|
|
106
79
|
}
|
|
107
|
-
#frame(f) {
|
|
108
|
-
if (f.t === "m") {
|
|
109
|
-
const data = typeof f.d !== "string" && this.#binaryType === "blob" ? new Blob([f.d]) : f.d;
|
|
110
|
-
this.dispatchEvent(new MessageEvent("message", { data }));
|
|
111
|
-
// The sender's bufferedAmount drains by the bytes dispatched.
|
|
112
|
-
this[kPort]?.postMessage({ t: "a", n: byteSize(f.d) });
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
if (f.t !== "c")
|
|
116
|
-
return;
|
|
117
|
-
const wasOpen = this.readyState === 1;
|
|
118
|
-
if (wasOpen)
|
|
119
|
-
this[kPort].postMessage({ t: "c", code: f.code, reason: f.reason });
|
|
120
|
-
this.readyState = 3;
|
|
121
|
-
acceptedEnds.delete(this);
|
|
122
|
-
clearInterval(this.#watch);
|
|
123
|
-
this[kPort].close();
|
|
124
|
-
this.dispatchEvent(new CellsCloseEvent("close", { code: f.code, reason: f.reason, wasClean: true }));
|
|
125
|
-
}
|
|
126
80
|
/**
|
|
127
|
-
* Closes every server end
|
|
128
|
-
*
|
|
129
|
-
*
|
|
81
|
+
* Closes every server end of this realm with 1001 (going away): the accepted ones, and those
|
|
82
|
+
* whose client end was handed over but which were never accepted. A shard worker that hands
|
|
83
|
+
* its cells to another tab does it before it stops, and an isolate before it is terminated,
|
|
84
|
+
* so that clients reconnect at once instead of noticing through the heartbeat; Worker code
|
|
85
|
+
* cannot send 1001 itself (`close`).
|
|
130
86
|
*/
|
|
131
87
|
static goAway(reason = "moving to another worker") {
|
|
132
|
-
for (const ws of
|
|
133
|
-
|
|
88
|
+
for (const ws of serverEnds) {
|
|
89
|
+
serverEnds.delete(ws);
|
|
134
90
|
if (ws.readyState >= 2)
|
|
135
91
|
continue;
|
|
136
|
-
ws.
|
|
137
|
-
ws[kPort]?.postMessage({ t: "c", code: 1001, reason });
|
|
92
|
+
ws._startClose(1001, reason);
|
|
138
93
|
}
|
|
139
94
|
}
|
|
140
|
-
send(data) {
|
|
141
|
-
// celld lets either end of a pair send before accept() (workerd refuses); frames wait in
|
|
142
|
-
// the port until the other end listens.
|
|
143
|
-
if (this.readyState !== 1)
|
|
144
|
-
throw new TypeError("Can't call WebSocket send() after close().");
|
|
145
|
-
const d = toFrameData(data);
|
|
146
|
-
this[kPort].postMessage({ t: "m", d }, typeof d === "string" ? [] : [d]);
|
|
147
|
-
}
|
|
148
|
-
close(code, reason) {
|
|
149
|
-
checkClose(code, reason);
|
|
150
|
-
if (this.readyState >= 2)
|
|
151
|
-
return;
|
|
152
|
-
this.readyState = 2;
|
|
153
|
-
this[kPort].postMessage({ t: "c", code: code ?? 1005, reason: reason ?? "" });
|
|
154
|
-
}
|
|
155
95
|
}
|
|
156
96
|
/** The host's own WebSocket (Node's, or the browser's): the client sockets Worker code opens. */
|
|
157
97
|
const HostWebSocket = globalThis.WebSocket;
|
|
@@ -212,28 +152,47 @@ export class StandardWebSocketPair extends WebSocketPair {
|
|
|
212
152
|
this[1].binaryType = "blob";
|
|
213
153
|
}
|
|
214
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Detaches the CLIENT end returned in a 101 response: its own port, and the frames the server
|
|
157
|
+
* sent before the response was handed over (partyserver greets in onConnect, before it returns
|
|
158
|
+
* the 101), which reached this end and wait in its queue. A holder that replays them
|
|
159
|
+
* (`new CfWebSocket(port, frames)`) needs no relay. Only when this end itself sent frames not
|
|
160
|
+
* yet acknowledged does `detach()` keep a relay (it absorbs their acknowledgements); the frames
|
|
161
|
+
* then travel through it.
|
|
162
|
+
*/
|
|
163
|
+
export function takeClientEnd(ws) {
|
|
164
|
+
const frames = ws.bufferedAmount === 0 ? ws._queue.splice(0) : [];
|
|
165
|
+
let port;
|
|
166
|
+
try {
|
|
167
|
+
port = ws.detach(); // with its queue emptied: the end's own port
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
ws._queue.unshift(...frames);
|
|
171
|
+
throw new TypeError("WebSocket end already handed over");
|
|
172
|
+
}
|
|
173
|
+
// Its server end now has a client waiting, accepted or not: `goAway` must reach it.
|
|
174
|
+
const server = ws.peer;
|
|
175
|
+
if (server && server.readyState < 2)
|
|
176
|
+
serverEnds.add(server);
|
|
177
|
+
return { port, frames };
|
|
178
|
+
}
|
|
215
179
|
/**
|
|
216
180
|
* Detaches the transport of the CLIENT end returned in a 101 response, so the
|
|
217
|
-
* runtime can hand the raw MessagePort to the real client (transferable).
|
|
181
|
+
* runtime can hand the raw MessagePort to the real client (transferable). Frames the server
|
|
182
|
+
* sent before the hand-over go to the new holder first, then the rest (ServerWebSocket.detach
|
|
183
|
+
* relays them).
|
|
218
184
|
*/
|
|
219
185
|
export function takeClientPort(ws) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
ws.readyState = 3; // handed over: this end must not answer pings any more
|
|
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)
|
|
186
|
+
try {
|
|
187
|
+
const port = ws.detach();
|
|
188
|
+
const server = ws.peer;
|
|
189
|
+
if (server && server.readyState < 2)
|
|
190
|
+
serverEnds.add(server);
|
|
231
191
|
return port;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return relay.port1;
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
throw new TypeError("WebSocket end already handed over");
|
|
195
|
+
}
|
|
237
196
|
}
|
|
238
197
|
/**
|
|
239
198
|
* `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":"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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"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ws/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { ServerWebSocket } from \"@statewalker/webrun-http-browser/sw\";\nimport { checkClose, type Frame } from \"./frames.js\";\n\n/**\n * Server ends of this realm that a client may be waiting on, not yet closed: the accepted ones,\n * and those whose client end was handed over (see `CfWebSocket.goAway`).\n */\nconst serverEnds = new Set<CfWebSocket>();\n\n/** What `ServerWebSocket` keeps of the frames that reached an end before `accept()`. */\ntype QueueOf = { _queue: Frame[] };\n\n/**\n * Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair), on\n * webrun-http-browser's `ServerWebSocket` and its frames, with workerd's semantics:\n *\n * - events only after `accept()` (frames that arrive earlier wait), and either end may `send()`\n * before it (celld does; the frames wait in the port);\n * - `close()` with no code sends 1005 and waits for the echo however long; the codes Worker code\n * may send are 1000 and 3000–4999; messages in flight when `close()` was called still arrive;\n * - the heartbeat watch starts with the client's first ping and keeps that period: a pair's ends\n * used in one realm never ping, and a tab that closes turns hidden (announcing a longer period)\n * on its way out;\n * - attachments, `peer`, `hibernatable`, and `goAway` for the server ends of this realm.\n *\n * Hibernation (`ctx.acceptWebSocket`) is a later prototype.\n */\nexport class CfWebSocket extends ServerWebSocket {\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 extensions: string | null = null;\n #attachment: unknown;\n\n /** `queued`: frames the end received before `port` reached it, delivered first (a 101's). */\n constructor(port: MessagePort, queued: Frame[] = []) {\n super(port, {\n watch: \"first-ping\",\n heartbeatPeriod: \"first\",\n sendBeforeAccept: true,\n deliverWhileClosing: true,\n defaultCloseCode: 1005,\n closeTimeoutMs: Number.POSITIVE_INFINITY,\n });\n // workerd's server ends have no url or subprotocol of their own.\n Object.defineProperty(this, \"url\", { value: null, writable: true, configurable: true });\n Object.defineProperty(this, \"protocol\", { value: null, writable: true, configurable: true });\n // Ahead of anything from the port, which reaches this end in a later task.\n if (queued.length) (this as unknown as QueueOf)._queue.unshift(...queued);\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\n override accept(): void {\n if (this.accepted) return;\n try {\n super.accept();\n } catch {\n throw new TypeError(\"This WebSocket end was handed to a client; it cannot be accepted.\");\n }\n serverEnds.add(this);\n }\n\n override send(data: string | ArrayBuffer | ArrayBufferView): void {\n if (this.readyState !== 1) throw new TypeError(\"Can't call WebSocket send() after close().\");\n super.send(data);\n }\n\n protected override checkClose(code: number | undefined, reason: string | undefined): void {\n checkClose(code, reason);\n }\n\n protected override _onClosed(): void {\n serverEnds.delete(this);\n super._onClosed();\n }\n\n serializeAttachment(v: unknown): void {\n this.#attachment = structuredClone(v);\n }\n deserializeAttachment(): unknown {\n return structuredClone(this.#attachment);\n }\n\n /**\n * Closes every server end of this realm with 1001 (going away): the accepted ones, and those\n * whose client end was handed over but which were never accepted. A shard worker that hands\n * its cells to another tab does it before it stops, and an isolate before it is terminated,\n * so that clients reconnect at once instead of noticing through the heartbeat; Worker code\n * cannot send 1001 itself (`close`).\n */\n static goAway(reason = \"moving to another worker\"): void {\n for (const ws of serverEnds) {\n serverEnds.delete(ws);\n if (ws.readyState >= 2) continue;\n ws._startClose(1001, reason);\n }\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 CLIENT end returned in a 101 response: its own port, and the frames the server\n * sent before the response was handed over (partyserver greets in onConnect, before it returns\n * the 101), which reached this end and wait in its queue. A holder that replays them\n * (`new CfWebSocket(port, frames)`) needs no relay. Only when this end itself sent frames not\n * yet acknowledged does `detach()` keep a relay (it absorbs their acknowledgements); the frames\n * then travel through it.\n */\nexport function takeClientEnd(ws: CfWebSocket): { port: MessagePort; frames: Frame[] } {\n const frames = ws.bufferedAmount === 0 ? (ws as unknown as QueueOf)._queue.splice(0) : [];\n let port: MessagePort;\n try {\n port = ws.detach(); // with its queue emptied: the end's own port\n } catch {\n (ws as unknown as QueueOf)._queue.unshift(...frames);\n throw new TypeError(\"WebSocket end already handed over\");\n }\n // Its server end now has a client waiting, accepted or not: `goAway` must reach it.\n const server = ws.peer;\n if (server && server.readyState < 2) serverEnds.add(server);\n return { port, frames };\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). Frames the server\n * sent before the hand-over go to the new holder first, then the rest (ServerWebSocket.detach\n * relays them).\n */\nexport function takeClientPort(ws: CfWebSocket): MessagePort {\n try {\n const port = ws.detach();\n const server = ws.peer;\n if (server && server.readyState < 2) serverEnds.add(server);\n return port;\n } catch {\n throw new TypeError(\"WebSocket end already handed over\");\n }\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.5.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",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=22.13"
|
|
15
15
|
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"cellb": "./bin/cellb.mjs"
|
|
18
|
+
},
|
|
16
19
|
"exports": {
|
|
17
20
|
".": {
|
|
18
21
|
"types": "./dist/index.d.ts",
|
|
@@ -41,6 +44,7 @@
|
|
|
41
44
|
},
|
|
42
45
|
"files": [
|
|
43
46
|
"dist",
|
|
47
|
+
"bin",
|
|
44
48
|
"vendor",
|
|
45
49
|
"README.md",
|
|
46
50
|
"LICENSE"
|
|
@@ -54,9 +58,10 @@
|
|
|
54
58
|
"@statewalker/webrun-files": "^0.10.0",
|
|
55
59
|
"@statewalker/webrun-files-browser": "^0.10.0",
|
|
56
60
|
"@statewalker/webrun-files-mem": "^0.10.0",
|
|
57
|
-
"@statewalker/webrun-
|
|
58
|
-
"@statewalker/webrun-http-
|
|
59
|
-
"@statewalker/webrun-
|
|
61
|
+
"@statewalker/webrun-files-node": "^0.10.3",
|
|
62
|
+
"@statewalker/webrun-http-browser": "^0.12.1",
|
|
63
|
+
"@statewalker/webrun-http-streams": "^0.4.0",
|
|
64
|
+
"@statewalker/webrun-rpc": "^0.10.0",
|
|
60
65
|
"@statewalker/webrun-streams": "^0.2.3",
|
|
61
66
|
"acorn": "^8.19.0",
|
|
62
67
|
"assert": "^2.1.0",
|