@statewalker/cellb 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +46 -3
  2. package/bin/cellb.mjs +20 -0
  3. package/dist/browser.d.ts +2 -0
  4. package/dist/browser.js +1 -0
  5. package/dist/browser.js.map +1 -1
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +62 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/isolate/browser.d.ts +2 -1
  10. package/dist/isolate/browser.js +2 -1
  11. package/dist/isolate/browser.js.map +1 -1
  12. package/dist/isolate/isolate.js +5 -3
  13. package/dist/isolate/isolate.js.map +1 -1
  14. package/dist/isolate/protocol.d.ts +4 -2
  15. package/dist/isolate/protocol.js +4 -4
  16. package/dist/isolate/protocol.js.map +1 -1
  17. package/dist/node.d.ts +3 -0
  18. package/dist/node.js +2 -0
  19. package/dist/node.js.map +1 -1
  20. package/dist/project/browser-host.d.ts +6 -12
  21. package/dist/project/browser-host.js +2 -25
  22. package/dist/project/browser-host.js.map +1 -1
  23. package/dist/project/browser-load.d.ts +22 -0
  24. package/dist/project/browser-load.js +63 -0
  25. package/dist/project/browser-load.js.map +1 -0
  26. package/dist/project/build.d.ts +14 -0
  27. package/dist/project/build.js +53 -0
  28. package/dist/project/build.js.map +1 -0
  29. package/dist/project/built-project.d.ts +88 -0
  30. package/dist/project/built-project.js +118 -0
  31. package/dist/project/built-project.js.map +1 -0
  32. package/dist/project/node-load.d.ts +14 -0
  33. package/dist/project/node-load.js +187 -0
  34. package/dist/project/node-load.js.map +1 -0
  35. package/dist/project/wrangler.d.ts +9 -0
  36. package/dist/project/wrangler.js +24 -0
  37. package/dist/project/wrangler.js.map +1 -1
  38. package/dist/ws/server.d.ts +24 -6
  39. package/dist/ws/server.js +52 -16
  40. package/dist/ws/server.js.map +1 -1
  41. package/package.json +6 -1
@@ -1 +1 @@
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 } from \"./frames.js\";\n\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), 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 ends accepted in 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 constructor(port: MessagePort) {\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 }\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 acceptedEnds.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 acceptedEnds.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 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._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 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 // 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 holder\n // gets them first, then the rest (ServerWebSocket.detach).\n try {\n return ws.detach();\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"]}
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.4.0",
3
+ "version": "0.5.1",
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,6 +58,7 @@
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",
61
+ "@statewalker/webrun-files-node": "^0.10.3",
57
62
  "@statewalker/webrun-http-browser": "^0.12.1",
58
63
  "@statewalker/webrun-http-streams": "^0.4.0",
59
64
  "@statewalker/webrun-rpc": "^0.10.0",