@statewalker/cellb 0.2.0 → 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 +7 -5
- package/dist/isolate/host.d.ts +1 -1
- package/dist/isolate/host.js +17 -6
- package/dist/isolate/host.js.map +1 -1
- package/dist/isolate/isolate.js +8 -0
- package/dist/isolate/isolate.js.map +1 -1
- package/dist/isolate/protocol.d.ts +3 -2
- package/dist/isolate/protocol.js +47 -51
- package/dist/isolate/protocol.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/wrangler.js +1 -1
- package/dist/project/wrangler.js.map +1 -1
- 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 +17 -8
- package/dist/ws/frames.js +3 -7
- package/dist/ws/frames.js.map +1 -1
- package/dist/ws/server.js +13 -4
- package/dist/ws/server.js.map +1 -1
- package/package.json +5 -6
package/README.md
CHANGED
|
@@ -226,10 +226,12 @@ docs/ topic pages
|
|
|
226
226
|
- **The gates use no `AsyncLocalStorage`**, which browsers lack.
|
|
227
227
|
- **A browser origin has a fixed number of shards**, because an OPFS pool is exclusive per worker
|
|
228
228
|
([docs/browser-host.md](docs/browser-host.md)).
|
|
229
|
-
- **WebSockets travel over MessagePorts**, because a ServiceWorker cannot accept them.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
229
|
+
- **WebSockets travel over MessagePorts**, because a ServiceWorker cannot accept them. The client
|
|
230
|
+
(`createWebSocketClass`) and the frames on the port are `webrun-http-browser`'s; the ends of a
|
|
231
|
+
`WebSocketPair` are cellb's, with workerd's semantics. A port has no send buffer to measure, so
|
|
232
|
+
the server end acknowledges the bytes of each message it dispatched and a client's
|
|
233
|
+
`bufferedAmount` counts the bytes not acknowledged yet: the WHATWG signal a sender throttles on.
|
|
234
|
+
workerd's server-side WebSocket has no `bufferedAmount`, and cellb's has none.
|
|
233
235
|
- **Each loaded Worker gets a realm of its own**, a thread or a Dedicated Worker: as in celld's
|
|
234
236
|
isolates, loaded code must not see the host's globals or env.
|
|
235
237
|
- **`idFromName` is synchronous**, with `@noble/hashes` instead of `crypto.subtle`, which is
|
|
@@ -257,7 +259,7 @@ Queues, Workflows, …) in [docs/services/](docs/services/). What is isolated, a
|
|
|
257
259
|
- `@statewalker/webrun-rpc`, `webrun-http-streams`, `webrun-streams` — HTTP with streaming bodies
|
|
258
260
|
over MessagePorts.
|
|
259
261
|
- `@statewalker/webrun-files` — the FilesApi for logs, backups and KV blobs;
|
|
260
|
-
`webrun-http-browser` —
|
|
262
|
+
`webrun-http-browser` — the HTTP links over MessagePorts, the WebSocket client and its frames.
|
|
261
263
|
- Optional peers, for `@statewalker/cellb/site` only: `webrun-modules` (compiles a project's
|
|
262
264
|
sources in the browser; it brings lightningcss-wasm and @jspm/core, about 30 MB),
|
|
263
265
|
`webrun-site-builder` (the in-browser HTTP server), and `webrun-site-host`, which the
|
package/dist/isolate/host.d.ts
CHANGED
package/dist/isolate/host.js
CHANGED
|
@@ -22,7 +22,7 @@ export function isolatedModuleLoader(opts) {
|
|
|
22
22
|
terminate: () => isolate.terminate(),
|
|
23
23
|
};
|
|
24
24
|
isolates.add(running);
|
|
25
|
-
const { call, fetch
|
|
25
|
+
const { call, fetch } = await hostPort(isolate.port, async (m) => {
|
|
26
26
|
if (m.type === "durable") {
|
|
27
27
|
const st = opts.storage?.() ?? {};
|
|
28
28
|
if (m.op === "load")
|
|
@@ -44,7 +44,9 @@ export function isolatedModuleLoader(opts) {
|
|
|
44
44
|
console[m.level](...m.args);
|
|
45
45
|
}
|
|
46
46
|
}, fetcher);
|
|
47
|
-
|
|
47
|
+
// Before it ends, the isolate tells its sockets' clients it is going away (1001): their
|
|
48
|
+
// frames go straight to the isolate (protocol.ts), so only it can.
|
|
49
|
+
running.goAway = () => call({ type: "goAway" });
|
|
48
50
|
const exports = await call({
|
|
49
51
|
type: "load",
|
|
50
52
|
...where,
|
|
@@ -79,11 +81,20 @@ export function isolatedModuleLoader(opts) {
|
|
|
79
81
|
};
|
|
80
82
|
return Object.assign(load, {
|
|
81
83
|
close() {
|
|
82
|
-
|
|
83
|
-
isolate.closeSockets?.(); // its sockets' clients hear 1001 before the isolate ends
|
|
84
|
-
isolate.terminate();
|
|
85
|
-
}
|
|
84
|
+
const ending = [...isolates];
|
|
86
85
|
isolates.clear();
|
|
86
|
+
// Each isolate closes its sockets (1001), within a second; then it ends.
|
|
87
|
+
return Promise.all(ending.map(async (isolate) => {
|
|
88
|
+
let timer;
|
|
89
|
+
await Promise.race([
|
|
90
|
+
isolate.goAway?.().catch(() => { }),
|
|
91
|
+
new Promise((r) => {
|
|
92
|
+
timer = setTimeout(r, 1_000);
|
|
93
|
+
}),
|
|
94
|
+
]);
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
isolate.terminate();
|
|
97
|
+
})).then(() => { });
|
|
87
98
|
},
|
|
88
99
|
});
|
|
89
100
|
}
|
package/dist/isolate/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/isolate/host.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type LogSink,\n type ModuleLoader,\n outboundFetch,\n type WorkerCode,\n} from \"../bindings/worker-loader.js\";\nimport { ISOLATED, type IsolatedClass } from \"../core/handles.js\";\nimport type { CellStorage } from \"../ports.js\";\nimport { hostPort, type PortLike } from \"./protocol.js\";\n\n/**\n * Worker Loader whose code runs in a separate realm per loaded Worker (an isolate). Returns\n * a module facade: each export is an object whose `fetch()` and methods cross the port.\n * Durable Object classes cannot cross (their storage API is synchronous): use an in-realm\n * loader for `getDurableObjectClass` (see `workerLoader(load, { classes })`).\n */\nexport function isolatedModuleLoader(opts: {\n /**\n * The host's storage, so facets of loaded code are logged and restored like the root's\n * own (celld: one stream per facet under the root, the root's epoch).\n */\n storage?: () => CellStorage | undefined;\n spawn(id: string): { port: PortLike; terminate(): void };\n prepare(id: string, code: WorkerCode): Promise<{ source?: string; url?: string }>;\n}): IsolatedModuleLoader {\n // Every isolate this loader started: `close()` ends them (a project runtime's close does).\n const isolates = new Set<{ terminate(): void; closeSockets?(): void }>();\n const load = async (id: string, code: WorkerCode, logs?: LogSink) => {\n // The env crosses as any call does: bindings and other stubs by reference.\n const env: Record<string, unknown> = { ...code.env };\n // The loaded code's global fetch(): blocked in the isolate, or sent back here to the Fetcher.\n const fetcher = outboundFetch(code);\n const outbound = code.globalOutbound === null ? \"blocked\" : fetcher ? \"fetcher\" : undefined;\n const where = await opts.prepare(id, code);\n const isolate = opts.spawn(id);\n const running: { terminate(): void; closeSockets?(): void } = {\n terminate: () => isolate.terminate(),\n };\n isolates.add(running);\n const { call, fetch, closeSockets } = await hostPort(\n isolate.port,\n async (m) => {\n if (m.type === \"durable\") {\n const st = opts.storage?.() ?? {};\n if (m.op === \"load\") return st.backup?.load?.(m.cellKey);\n if (m.op === \"committed\" && st.durability) {\n const info = m.info;\n const token = info.image;\n await st.durability.committed(m.cellKey, {\n ...info,\n image:\n typeof token === \"number\"\n ? () => call<Uint8Array | undefined>({ type: \"image\", token })\n : token,\n });\n }\n return;\n }\n if (m.type === \"log\") {\n logs?.(m.level, m.args);\n (console as any)[m.level](...m.args);\n }\n },\n fetcher,\n );\n running.closeSockets = closeSockets;\n const exports = await call<Record<string, string>>({\n type: \"load\",\n ...where,\n env,\n outbound,\n });\n const facade = (name: string) =>\n new Proxy(\n {},\n {\n get(_t, prop) {\n if (prop === \"then\" || typeof prop === \"symbol\") return undefined;\n // Called as a plain-object entrypoint (entrypointStub): `ctx` comes last, after `env`\n // (the isolate has its own); its props cross with the call.\n if (prop === \"fetch\")\n return (req: Request, _env: unknown, ctx?: { props?: unknown }) =>\n fetch({ name, props: ctx?.props }, req);\n return (...args: unknown[]) => {\n const ctx = args.pop() as { props?: unknown };\n args.pop();\n return call({ type: \"call\", name, method: prop, args, props: ctx.props });\n };\n },\n },\n );\n const module: Record<string | symbol, unknown> = Object.fromEntries(\n Object.keys(exports).map((k) => [k, facade(k)]),\n );\n // Durable Object classes of the loaded code run in the isolate too (facets).\n module[ISOLATED] = (className: string): IsolatedClass => ({\n fetch: (cellKey, req) => fetch({ className, cellKey }, req),\n call: (cellKey, method, args) =>\n call({ type: \"object\", op: \"call\", className, cellKey, method, args }),\n restart: (cellKey) => call({ type: \"object\", op: \"restart\", className, cellKey }),\n delete: (cellKey) => call({ type: \"object\", op: \"delete\", className, cellKey }),\n attach: (cellKey, epoch, idName) =>\n call({ type: \"object\", op: \"attach\", className, cellKey, epoch, idName }),\n });\n return module as Record<string, unknown>;\n };\n return Object.assign(load, {\n close() {\n for (const isolate of isolates) {\n isolate.closeSockets?.(); // its sockets' clients hear 1001 before the isolate ends\n isolate.terminate();\n }\n isolates.clear();\n },\n });\n}\n\n/** A Worker Loader's module loader whose isolates end with `close()`. */\nexport type IsolatedModuleLoader = ModuleLoader & { close(): void };\n"]}
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/isolate/host.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type LogSink,\n type ModuleLoader,\n outboundFetch,\n type WorkerCode,\n} from \"../bindings/worker-loader.js\";\nimport { ISOLATED, type IsolatedClass } from \"../core/handles.js\";\nimport type { CellStorage } from \"../ports.js\";\nimport { hostPort, type PortLike } from \"./protocol.js\";\n\n/**\n * Worker Loader whose code runs in a separate realm per loaded Worker (an isolate). Returns\n * a module facade: each export is an object whose `fetch()` and methods cross the port.\n * Durable Object classes cannot cross (their storage API is synchronous): use an in-realm\n * loader for `getDurableObjectClass` (see `workerLoader(load, { classes })`).\n */\nexport function isolatedModuleLoader(opts: {\n /**\n * The host's storage, so facets of loaded code are logged and restored like the root's\n * own (celld: one stream per facet under the root, the root's epoch).\n */\n storage?: () => CellStorage | undefined;\n spawn(id: string): { port: PortLike; terminate(): void };\n prepare(id: string, code: WorkerCode): Promise<{ source?: string; url?: string }>;\n}): IsolatedModuleLoader {\n // Every isolate this loader started: `close()` ends them (a project runtime's close does).\n const isolates = new Set<{ terminate(): void; goAway?(): Promise<unknown> }>();\n const load = async (id: string, code: WorkerCode, logs?: LogSink) => {\n // The env crosses as any call does: bindings and other stubs by reference.\n const env: Record<string, unknown> = { ...code.env };\n // The loaded code's global fetch(): blocked in the isolate, or sent back here to the Fetcher.\n const fetcher = outboundFetch(code);\n const outbound = code.globalOutbound === null ? \"blocked\" : fetcher ? \"fetcher\" : undefined;\n const where = await opts.prepare(id, code);\n const isolate = opts.spawn(id);\n const running: { terminate(): void; goAway?(): Promise<unknown> } = {\n terminate: () => isolate.terminate(),\n };\n isolates.add(running);\n const { call, fetch } = await hostPort(\n isolate.port,\n async (m) => {\n if (m.type === \"durable\") {\n const st = opts.storage?.() ?? {};\n if (m.op === \"load\") return st.backup?.load?.(m.cellKey);\n if (m.op === \"committed\" && st.durability) {\n const info = m.info;\n const token = info.image;\n await st.durability.committed(m.cellKey, {\n ...info,\n image:\n typeof token === \"number\"\n ? () => call<Uint8Array | undefined>({ type: \"image\", token })\n : token,\n });\n }\n return;\n }\n if (m.type === \"log\") {\n logs?.(m.level, m.args);\n (console as any)[m.level](...m.args);\n }\n },\n fetcher,\n );\n // Before it ends, the isolate tells its sockets' clients it is going away (1001): their\n // frames go straight to the isolate (protocol.ts), so only it can.\n running.goAway = () => call({ type: \"goAway\" });\n const exports = await call<Record<string, string>>({\n type: \"load\",\n ...where,\n env,\n outbound,\n });\n const facade = (name: string) =>\n new Proxy(\n {},\n {\n get(_t, prop) {\n if (prop === \"then\" || typeof prop === \"symbol\") return undefined;\n // Called as a plain-object entrypoint (entrypointStub): `ctx` comes last, after `env`\n // (the isolate has its own); its props cross with the call.\n if (prop === \"fetch\")\n return (req: Request, _env: unknown, ctx?: { props?: unknown }) =>\n fetch({ name, props: ctx?.props }, req);\n return (...args: unknown[]) => {\n const ctx = args.pop() as { props?: unknown };\n args.pop();\n return call({ type: \"call\", name, method: prop, args, props: ctx.props });\n };\n },\n },\n );\n const module: Record<string | symbol, unknown> = Object.fromEntries(\n Object.keys(exports).map((k) => [k, facade(k)]),\n );\n // Durable Object classes of the loaded code run in the isolate too (facets).\n module[ISOLATED] = (className: string): IsolatedClass => ({\n fetch: (cellKey, req) => fetch({ className, cellKey }, req),\n call: (cellKey, method, args) =>\n call({ type: \"object\", op: \"call\", className, cellKey, method, args }),\n restart: (cellKey) => call({ type: \"object\", op: \"restart\", className, cellKey }),\n delete: (cellKey) => call({ type: \"object\", op: \"delete\", className, cellKey }),\n attach: (cellKey, epoch, idName) =>\n call({ type: \"object\", op: \"attach\", className, cellKey, epoch, idName }),\n });\n return module as Record<string, unknown>;\n };\n return Object.assign(load, {\n close(): Promise<void> {\n const ending = [...isolates];\n isolates.clear();\n // Each isolate closes its sockets (1001), within a second; then it ends.\n return Promise.all(\n ending.map(async (isolate) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n isolate.goAway?.().catch(() => {}),\n new Promise((r) => {\n timer = setTimeout(r, 1_000);\n }),\n ]);\n clearTimeout(timer);\n isolate.terminate();\n }),\n ).then(() => {});\n },\n });\n}\n\n/** A Worker Loader's module loader whose isolates end with `close()`. */\nexport type IsolatedModuleLoader = ModuleLoader & { close(): Promise<void> };\n"]}
|
package/dist/isolate/isolate.js
CHANGED
|
@@ -6,6 +6,7 @@ import { ctxExports, entrypointStub } from "../core/handles.js";
|
|
|
6
6
|
import { runInDurableObject } from "../core/namespace.js";
|
|
7
7
|
import { createCellRuntime } from "../core/runtime.js";
|
|
8
8
|
import { realmWorkerScope } from "../project/worker-scope.js";
|
|
9
|
+
import { CfWebSocket } from "../ws/server.js";
|
|
9
10
|
import { isolatePort } from "./protocol.js";
|
|
10
11
|
export function serveIsolate(port, importCode, opts = {}) {
|
|
11
12
|
let module = {};
|
|
@@ -146,6 +147,13 @@ export function serveIsolate(port, importCode, opts = {}) {
|
|
|
146
147
|
}
|
|
147
148
|
if (m.type === "image")
|
|
148
149
|
return await images.get(m.token)?.();
|
|
150
|
+
// The host is stopping this isolate: the sockets accepted here hear 1001 first.
|
|
151
|
+
// A socket whose early frames went through a relay port hears it one hop later: the
|
|
152
|
+
// answer waits a macrotask, so the host does not terminate the isolate before that.
|
|
153
|
+
if (m.type === "goAway") {
|
|
154
|
+
CfWebSocket.goAway("the loaded Worker stopped");
|
|
155
|
+
return await new Promise((r) => setTimeout(r, 0));
|
|
156
|
+
}
|
|
149
157
|
if (m.type === "object") {
|
|
150
158
|
const { rt, stub, key } = await facet(m.className, m.cellKey);
|
|
151
159
|
if (m.op === "attach") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"isolate.js","sourceRoot":"","sources":["../../src/isolate/isolate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The isolate side: loads the Worker code in THIS realm and serves fetch/RPC for the host.\n * `importCode` is realm specific (data: URL of a bundle on Node, a site URL in browsers).\n */\nimport { ctxExports, entrypointStub } from \"../core/handles.js\";\nimport type { DurableObjectId } from \"../core/id.js\";\nimport { runInDurableObject } from \"../core/namespace.js\";\nimport { type CellRuntime, createCellRuntime, type WorkerModule } from \"../core/runtime.js\";\nimport type { Lease, LeaseHandle, OpenCellSql } from \"../ports.js\";\nimport { realmWorkerScope } from \"../project/worker-scope.js\";\nimport { type FetchTarget, isolatePort, type PortLike } from \"./protocol.js\";\n\nexport function serveIsolate(\n port: PortLike,\n importCode: (\n load: any,\n logs: (level: string, args: unknown[]) => void,\n ) => Promise<Record<string, unknown>>,\n opts: {\n patchConsole?: boolean;\n /**\n * Storage for Durable Object classes run INSIDE this isolate (facets of loaded code):\n * each facet is a cell of an isolate-local runtime, with its own SQLite.\n */\n openSql?: (load: any) => OpenCellSql | Promise<OpenCellSql>;\n } = {},\n) {\n let module: Record<string, unknown> = {};\n const runtimes = new Map<string, CellRuntime>();\n // Facet logging through the host: the isolate-local cell key → the facet's key in the\n // root's storage (celld: <root>/facets/<sha>) and the root's epoch.\n const facetKeys = new Map<string, { key: string; epoch?: number }>();\n const images = new Map<number, () => unknown>();\n let nextImage = 0;\n const hostLogged = (internal: string) => facetKeys.get(internal);\n const durability = {\n capturePages: true,\n async committed(cellKey: string, info: any) {\n const f = hostLogged(cellKey);\n if (!f) return;\n const token = ++nextImage;\n if (typeof info.image === \"function\") images.set(token, info.image);\n try {\n await call({\n type: \"durable\",\n op: \"committed\",\n cellKey: f.key,\n info: {\n seq: info.seq,\n pages: info.pages,\n epoch: f.epoch,\n image: typeof info.image === \"function\" ? token : info.image,\n },\n });\n } finally {\n images.delete(token);\n }\n },\n };\n const backup = {\n async load(cellKey: string) {\n const f = hostLogged(cellKey);\n return f\n ? ((await call({ type: \"durable\", op: \"load\", cellKey: f.key })) as Uint8Array | undefined)\n : undefined;\n },\n async save() {},\n };\n // The host holds the root's lease and attaches each facet with its epoch: handing that epoch\n // out as the facet's own lease makes its ObjectHost rebuild the local database from the\n // bucket when it opens, as a root does under a lease (a pool kept from an earlier ownership\n // is older than what other nodes wrote since). Without an epoch, the local file stays primary.\n const ownership: Lease = {\n async tryAcquire(cellKey) {\n const lost = new AbortController().signal; // fencing stays with the root's lease, in the host\n return {\n name: cellKey,\n epoch: hostLogged(cellKey)?.epoch,\n lost,\n release: async () => {},\n } as LeaseHandle;\n },\n };\n let loadMsg: any;\n let sql: Promise<OpenCellSql> | undefined;\n const objectRuntime = async (className: string) => {\n let rt = runtimes.get(className);\n if (!rt) {\n if (!opts.openSql)\n throw new Error(\"this isolate cannot host Durable Object classes (no storage)\");\n sql ??= Promise.resolve(opts.openSql(loadMsg));\n const openSql = await sql;\n if (runtimes.has(className)) return runtimes.get(className)!;\n if (typeof module[className] !== \"function\")\n throw new Error(`Loaded worker does not export class \"${className}\"`);\n rt = createCellRuntime({\n worker: { ...(module as WorkerModule), default: {} },\n durableObjects: { [className]: { className } },\n openSql,\n vars: env, // the loaded Worker's env: plain values and host stubs\n storage: { durability, backup, ownership },\n facets: true, // rows move to this run's scope on open, as celld does for facets\n ctxIdOf: (key) => {\n const name = idNames.get(key);\n return name === undefined\n ? undefined\n : (rt!.env[className] as { idFromName(n: string): DurableObjectId }).idFromName(name);\n },\n });\n runtimes.set(className, rt);\n }\n return rt;\n };\n let env: Record<string, unknown> = {};\n let exports: Record<string, unknown> = {};\n // The loaded Worker is this realm's own: its `cloudflare:workers` reads `env` and `exports` here.\n const scope = realmWorkerScope();\n // The name of the `id` a facet was given by `ctx.facets.get`, by its isolate-local cell key:\n // the facet's `ctx.id` (partyserver's `this.name`). Two facets may be given the same id; each\n // keeps its own object and storage, addressed by the parent's facet cell key.\n const idNames = new Map<string, string>();\n // A facet of loaded code: the object lives here, named by the parent's facet cell key.\n const facet = async (className: string, cellKey: string) => {\n const rt = await objectRuntime(className);\n const ns = rt.env[className] as {\n getByName(n: string): any;\n idFromName(n: string): { toString(): string };\n };\n return { rt, stub: ns.getByName(cellKey), key: `${className}/${ns.idFromName(cellKey)}` };\n };\n const exported = (name: string, props?: unknown) => {\n const target = module[name];\n if (target === undefined) throw new Error(`Loaded worker does not export \"${name}\"`);\n return entrypointStub(target, env, props, exports);\n };\n const fetchTarget = async (t: FetchTarget, req: Request): Promise<Response> =>\n \"className\" in t\n ? (await facet(t.className, t.cellKey)).stub.fetch(req)\n : exported(t.name, t.props).fetch(req);\n const handle = async (m: any) => {\n if (m.type === \"load\") {\n loadMsg = m;\n env = m.env; // host bindings arrive as stubs: every call goes back to the host\n // Logs go to the host (Tail Workers, and the host prints them): through the global\n // console, or — where the realm has other code that logs — a console scoped to the\n // loaded modules (importCode receives the sink).\n const sink = (level: string, args: unknown[]) =>\n call({ type: \"log\", level, args: safe(args) }).catch(() => {});\n if (opts.patchConsole !== false)\n for (const level of [\"log\", \"info\", \"warn\", \"error\", \"debug\"] as const)\n console[level] = (...args: unknown[]) => sink(level, args);\n if (m.outbound === \"fetcher\")\n globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) =>\n outbound(new Request(input, init))) as typeof fetch;\n if (m.outbound === \"blocked\")\n globalThis.fetch = (async () => {\n throw new Error(\n \"This worker is not permitted to access the internet via global functions like fetch(). It must use capabilities (such as bindings in 'env') to talk to the outside world.\",\n );\n }) as typeof fetch;\n scope.bind({ env, exports }); // read while the module is evaluated\n module = await importCode(m, sink);\n exports = ctxExports(module, env);\n scope.bind({ env, exports });\n return Object.fromEntries(Object.entries(module).map(([k, v]) => [k, typeof v]));\n }\n if (m.type === \"image\") return await images.get(m.token)?.();\n if (m.type === \"object\") {\n const { rt, stub, key } = await facet(m.className, m.cellKey);\n if (m.op === \"attach\") {\n facetKeys.set(key, { key: m.cellKey, epoch: m.epoch });\n if (typeof m.idName === \"string\") idNames.set(key, m.idName);\n return;\n }\n if (m.op === \"call\") return stub[m.method](...m.args);\n if (m.op === \"restart\") return rt.restartObject(key);\n if (m.op === \"delete\") {\n await runInDurableObject(\n stub,\n (_o: unknown, ctx: { storage: { deleteAll(): Promise<void> } }) =>\n ctx.storage.deleteAll(),\n );\n return rt.restartObject(key);\n }\n throw new Error(`unknown object op ${m.op}`);\n }\n if (m.type === \"call\") return exported(m.name, m.props)[m.method](...m.args);\n throw new Error(`unknown isolate message ${m.type}`);\n };\n const { call, outbound } = isolatePort(port, handle, fetchTarget);\n}\n\nconst safe = (args: unknown[]) =>\n args.map((a) => {\n try {\n return structuredClone(a);\n } catch {\n return String(a);\n }\n });\n"]}
|
|
1
|
+
{"version":3,"file":"isolate.js","sourceRoot":"","sources":["../../src/isolate/isolate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The isolate side: loads the Worker code in THIS realm and serves fetch/RPC for the host.\n * `importCode` is realm specific (data: URL of a bundle on Node, a site URL in browsers).\n */\nimport { ctxExports, entrypointStub } from \"../core/handles.js\";\nimport type { DurableObjectId } from \"../core/id.js\";\nimport { runInDurableObject } from \"../core/namespace.js\";\nimport { type CellRuntime, createCellRuntime, type WorkerModule } from \"../core/runtime.js\";\nimport type { Lease, LeaseHandle, OpenCellSql } from \"../ports.js\";\nimport { realmWorkerScope } from \"../project/worker-scope.js\";\nimport { CfWebSocket } from \"../ws/server.js\";\nimport { type FetchTarget, isolatePort, type PortLike } from \"./protocol.js\";\n\nexport function serveIsolate(\n port: PortLike,\n importCode: (\n load: any,\n logs: (level: string, args: unknown[]) => void,\n ) => Promise<Record<string, unknown>>,\n opts: {\n patchConsole?: boolean;\n /**\n * Storage for Durable Object classes run INSIDE this isolate (facets of loaded code):\n * each facet is a cell of an isolate-local runtime, with its own SQLite.\n */\n openSql?: (load: any) => OpenCellSql | Promise<OpenCellSql>;\n } = {},\n) {\n let module: Record<string, unknown> = {};\n const runtimes = new Map<string, CellRuntime>();\n // Facet logging through the host: the isolate-local cell key → the facet's key in the\n // root's storage (celld: <root>/facets/<sha>) and the root's epoch.\n const facetKeys = new Map<string, { key: string; epoch?: number }>();\n const images = new Map<number, () => unknown>();\n let nextImage = 0;\n const hostLogged = (internal: string) => facetKeys.get(internal);\n const durability = {\n capturePages: true,\n async committed(cellKey: string, info: any) {\n const f = hostLogged(cellKey);\n if (!f) return;\n const token = ++nextImage;\n if (typeof info.image === \"function\") images.set(token, info.image);\n try {\n await call({\n type: \"durable\",\n op: \"committed\",\n cellKey: f.key,\n info: {\n seq: info.seq,\n pages: info.pages,\n epoch: f.epoch,\n image: typeof info.image === \"function\" ? token : info.image,\n },\n });\n } finally {\n images.delete(token);\n }\n },\n };\n const backup = {\n async load(cellKey: string) {\n const f = hostLogged(cellKey);\n return f\n ? ((await call({ type: \"durable\", op: \"load\", cellKey: f.key })) as Uint8Array | undefined)\n : undefined;\n },\n async save() {},\n };\n // The host holds the root's lease and attaches each facet with its epoch: handing that epoch\n // out as the facet's own lease makes its ObjectHost rebuild the local database from the\n // bucket when it opens, as a root does under a lease (a pool kept from an earlier ownership\n // is older than what other nodes wrote since). Without an epoch, the local file stays primary.\n const ownership: Lease = {\n async tryAcquire(cellKey) {\n const lost = new AbortController().signal; // fencing stays with the root's lease, in the host\n return {\n name: cellKey,\n epoch: hostLogged(cellKey)?.epoch,\n lost,\n release: async () => {},\n } as LeaseHandle;\n },\n };\n let loadMsg: any;\n let sql: Promise<OpenCellSql> | undefined;\n const objectRuntime = async (className: string) => {\n let rt = runtimes.get(className);\n if (!rt) {\n if (!opts.openSql)\n throw new Error(\"this isolate cannot host Durable Object classes (no storage)\");\n sql ??= Promise.resolve(opts.openSql(loadMsg));\n const openSql = await sql;\n if (runtimes.has(className)) return runtimes.get(className)!;\n if (typeof module[className] !== \"function\")\n throw new Error(`Loaded worker does not export class \"${className}\"`);\n rt = createCellRuntime({\n worker: { ...(module as WorkerModule), default: {} },\n durableObjects: { [className]: { className } },\n openSql,\n vars: env, // the loaded Worker's env: plain values and host stubs\n storage: { durability, backup, ownership },\n facets: true, // rows move to this run's scope on open, as celld does for facets\n ctxIdOf: (key) => {\n const name = idNames.get(key);\n return name === undefined\n ? undefined\n : (rt!.env[className] as { idFromName(n: string): DurableObjectId }).idFromName(name);\n },\n });\n runtimes.set(className, rt);\n }\n return rt;\n };\n let env: Record<string, unknown> = {};\n let exports: Record<string, unknown> = {};\n // The loaded Worker is this realm's own: its `cloudflare:workers` reads `env` and `exports` here.\n const scope = realmWorkerScope();\n // The name of the `id` a facet was given by `ctx.facets.get`, by its isolate-local cell key:\n // the facet's `ctx.id` (partyserver's `this.name`). Two facets may be given the same id; each\n // keeps its own object and storage, addressed by the parent's facet cell key.\n const idNames = new Map<string, string>();\n // A facet of loaded code: the object lives here, named by the parent's facet cell key.\n const facet = async (className: string, cellKey: string) => {\n const rt = await objectRuntime(className);\n const ns = rt.env[className] as {\n getByName(n: string): any;\n idFromName(n: string): { toString(): string };\n };\n return { rt, stub: ns.getByName(cellKey), key: `${className}/${ns.idFromName(cellKey)}` };\n };\n const exported = (name: string, props?: unknown) => {\n const target = module[name];\n if (target === undefined) throw new Error(`Loaded worker does not export \"${name}\"`);\n return entrypointStub(target, env, props, exports);\n };\n const fetchTarget = async (t: FetchTarget, req: Request): Promise<Response> =>\n \"className\" in t\n ? (await facet(t.className, t.cellKey)).stub.fetch(req)\n : exported(t.name, t.props).fetch(req);\n const handle = async (m: any) => {\n if (m.type === \"load\") {\n loadMsg = m;\n env = m.env; // host bindings arrive as stubs: every call goes back to the host\n // Logs go to the host (Tail Workers, and the host prints them): through the global\n // console, or — where the realm has other code that logs — a console scoped to the\n // loaded modules (importCode receives the sink).\n const sink = (level: string, args: unknown[]) =>\n call({ type: \"log\", level, args: safe(args) }).catch(() => {});\n if (opts.patchConsole !== false)\n for (const level of [\"log\", \"info\", \"warn\", \"error\", \"debug\"] as const)\n console[level] = (...args: unknown[]) => sink(level, args);\n if (m.outbound === \"fetcher\")\n globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) =>\n outbound(new Request(input, init))) as typeof fetch;\n if (m.outbound === \"blocked\")\n globalThis.fetch = (async () => {\n throw new Error(\n \"This worker is not permitted to access the internet via global functions like fetch(). It must use capabilities (such as bindings in 'env') to talk to the outside world.\",\n );\n }) as typeof fetch;\n scope.bind({ env, exports }); // read while the module is evaluated\n module = await importCode(m, sink);\n exports = ctxExports(module, env);\n scope.bind({ env, exports });\n return Object.fromEntries(Object.entries(module).map(([k, v]) => [k, typeof v]));\n }\n if (m.type === \"image\") return await images.get(m.token)?.();\n // The host is stopping this isolate: the sockets accepted here hear 1001 first.\n // A socket whose early frames went through a relay port hears it one hop later: the\n // answer waits a macrotask, so the host does not terminate the isolate before that.\n if (m.type === \"goAway\") {\n CfWebSocket.goAway(\"the loaded Worker stopped\");\n return await new Promise((r) => setTimeout(r, 0));\n }\n if (m.type === \"object\") {\n const { rt, stub, key } = await facet(m.className, m.cellKey);\n if (m.op === \"attach\") {\n facetKeys.set(key, { key: m.cellKey, epoch: m.epoch });\n if (typeof m.idName === \"string\") idNames.set(key, m.idName);\n return;\n }\n if (m.op === \"call\") return stub[m.method](...m.args);\n if (m.op === \"restart\") return rt.restartObject(key);\n if (m.op === \"delete\") {\n await runInDurableObject(\n stub,\n (_o: unknown, ctx: { storage: { deleteAll(): Promise<void> } }) =>\n ctx.storage.deleteAll(),\n );\n return rt.restartObject(key);\n }\n throw new Error(`unknown object op ${m.op}`);\n }\n if (m.type === \"call\") return exported(m.name, m.props)[m.method](...m.args);\n throw new Error(`unknown isolate message ${m.type}`);\n };\n const { call, outbound } = isolatePort(port, handle, fetchTarget);\n}\n\nconst safe = (args: unknown[]) =>\n args.map((a) => {\n try {\n return structuredClone(a);\n } catch {\n return String(a);\n }\n });\n"]}
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
* isolate → host for the loaded code's global `fetch()` when a `globalOutbound` Fetcher routes it.
|
|
23
23
|
* A WebSocket upgrade (host → isolate) has a port of its own instead, since HTTP/1.1 drops the
|
|
24
24
|
* `Upgrade` header: its meta carries the request's method and headers; the isolate answers with
|
|
25
|
-
* the response's head
|
|
25
|
+
* the response's head and closes the port. A 101's head carries the socket's own MessagePort,
|
|
26
|
+
* transferred: the socket's frames then go straight between the client and the isolate, outside
|
|
27
|
+
* the multiplexer, so open sockets hold none of its ports (it allows `maxPorts` at once).
|
|
26
28
|
*/
|
|
27
29
|
export interface PortLike {
|
|
28
30
|
postMessage(msg: unknown, transfer?: Transferable[]): void;
|
|
@@ -56,7 +58,6 @@ type Handle = (msg: any) => Promise<unknown>;
|
|
|
56
58
|
export declare function hostPort(port: PortLike, handle: Handle, outbound?: (req: Request) => Promise<Response>): Promise<{
|
|
57
59
|
call: Call;
|
|
58
60
|
fetch: Fetch;
|
|
59
|
-
closeSockets(): void;
|
|
60
61
|
}>;
|
|
61
62
|
/**
|
|
62
63
|
* The isolate's end: accepts the ports the host opens. Everything the isolate calls the host
|
package/dist/isolate/protocol.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { fetchOverDuplex, serveFetchOverDuplex } from "@statewalker/webrun-http-streams";
|
|
2
|
-
import { callPort, duplexOverPort, listenPort, multiplexPort, NO_TIMEOUT, serveDuplexOverPort, structuredCodec, } from "@statewalker/webrun-rpc";
|
|
2
|
+
import { callPort, duplexOverPort, getPortCloseSignal, listenPort, multiplexPort, NO_TIMEOUT, serveDuplexOverPort, structuredCodec, } from "@statewalker/webrun-rpc";
|
|
3
3
|
import { cross, pipeTo, rpcPromise, STUB } from "../core/handles.js";
|
|
4
4
|
import { withUrl } from "../core/request-body.js";
|
|
5
|
-
import { portEnd, relayFrames } from "../ws/frames.js";
|
|
6
5
|
import { CellsResponse, CfWebSocket, takeClientPort } from "../ws/server.js";
|
|
7
6
|
import { carryWebSocketHeaders } from "../ws/upgrade.js";
|
|
8
7
|
/**
|
|
@@ -15,6 +14,7 @@ export async function hostPort(port, handle, outbound) {
|
|
|
15
14
|
const mux = multiplexPort(messageTarget(port), {
|
|
16
15
|
codec: structuredCodec,
|
|
17
16
|
side: "initiator",
|
|
17
|
+
maxPorts: maxPorts(),
|
|
18
18
|
onPort(p, meta) {
|
|
19
19
|
const m = meta;
|
|
20
20
|
if (m?.kind !== "outbound" || !outbound)
|
|
@@ -24,17 +24,10 @@ export async function hostPort(port, handle, outbound) {
|
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
26
|
const call = callsOver(await mux.openPort({ kind: "calls" }), handle);
|
|
27
|
-
// The sockets open into the isolate: told it stopped (1001) when it does.
|
|
28
|
-
const sockets = new Set();
|
|
29
27
|
const fetch = (target, req) => isUpgrade(req)
|
|
30
|
-
? upgradeOver(mux, { kind: "upgrade", target }, req
|
|
28
|
+
? upgradeOver(mux, { kind: "upgrade", target }, req)
|
|
31
29
|
: fetchOver(mux, { kind: "fetch", target }, req);
|
|
32
|
-
|
|
33
|
-
for (const end of sockets)
|
|
34
|
-
end(1001, "the loaded Worker stopped");
|
|
35
|
-
sockets.clear();
|
|
36
|
-
};
|
|
37
|
-
return { call, fetch, closeSockets };
|
|
30
|
+
return { call, fetch };
|
|
38
31
|
}
|
|
39
32
|
/**
|
|
40
33
|
* The isolate's end: accepts the ports the host opens. Everything the isolate calls the host
|
|
@@ -46,6 +39,7 @@ export function isolatePort(port, handle, fetch) {
|
|
|
46
39
|
const mux = multiplexPort(messageTarget(port), {
|
|
47
40
|
codec: structuredCodec,
|
|
48
41
|
side: "responder",
|
|
42
|
+
maxPorts: maxPorts(),
|
|
49
43
|
onPort(p, meta) {
|
|
50
44
|
const m = meta;
|
|
51
45
|
if (m?.kind === "fetch") {
|
|
@@ -78,24 +72,19 @@ function serveFetch(p, url, fetch) {
|
|
|
78
72
|
serveDuplexOverPort(p, serveFetchOverDuplex((req) => withUrl(req, url).then(fetch).catch(throwOwn)));
|
|
79
73
|
}
|
|
80
74
|
const isUpgrade = (req) => req.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
},
|
|
88
|
-
close: () => void p.close?.(),
|
|
89
|
-
});
|
|
75
|
+
/**
|
|
76
|
+
* Ports of an isolate's multiplexer open at once: the calls port, and one per fetch or upgrade
|
|
77
|
+
* in flight (each closes when its exchange ends). `globalThis.__cellbMaxPorts` overrides it, as
|
|
78
|
+
* for src/transport/port.ts.
|
|
79
|
+
*/
|
|
80
|
+
const maxPorts = () => globalThis.__cellbMaxPorts ?? 4096;
|
|
90
81
|
/** Statuses whose response has no body (`new Response(body, …)` refuses one). */
|
|
91
82
|
const nullBody = (status) => [101, 204, 205, 304].includes(status);
|
|
92
83
|
/**
|
|
93
|
-
* A WebSocket upgrade over a port of its own
|
|
94
|
-
* `webSocket` is a client end
|
|
95
|
-
* the socket's closing handshake, or `sockets`' close (the isolate stops). Any other answer
|
|
96
|
-
* closes the port.
|
|
84
|
+
* A WebSocket upgrade over a port of its own, closed once the isolate's answer is in. A 101
|
|
85
|
+
* becomes a Workers response whose `webSocket` is a client end over the socket's own port.
|
|
97
86
|
*/
|
|
98
|
-
async function upgradeOver(mux, meta, req
|
|
87
|
+
async function upgradeOver(mux, meta, req) {
|
|
99
88
|
const p = await mux.openPort({
|
|
100
89
|
...meta,
|
|
101
90
|
url: req.url,
|
|
@@ -103,31 +92,35 @@ async function upgradeOver(mux, meta, req, sockets) {
|
|
|
103
92
|
headers: [...req.headers],
|
|
104
93
|
});
|
|
105
94
|
return new Promise((resolve, reject) => {
|
|
95
|
+
// A port closed without an answer (the isolate refused it, or could not post one)
|
|
96
|
+
// fails the upgrade rather than leaving it pending.
|
|
97
|
+
const closed = getPortCloseSignal(p);
|
|
98
|
+
const onClosed = () => {
|
|
99
|
+
p.removeEventListener("message", onHead);
|
|
100
|
+
reject(closed?.reason ?? new Error("cellb: the isolate closed the upgrade without an answer"));
|
|
101
|
+
};
|
|
102
|
+
closed?.addEventListener("abort", onClosed, { once: true });
|
|
106
103
|
const onHead = (e) => {
|
|
107
104
|
p.removeEventListener("message", onHead);
|
|
105
|
+
closed?.removeEventListener("abort", onClosed);
|
|
106
|
+
void p.close?.();
|
|
108
107
|
try {
|
|
109
108
|
const head = e.data;
|
|
110
109
|
if ("error" in head)
|
|
111
110
|
throw head.error;
|
|
112
|
-
if (head.status
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
status: head.status,
|
|
116
|
-
statusText: head.statusText,
|
|
111
|
+
if (head.status === 101 && head.socket)
|
|
112
|
+
return resolve(new CellsResponse(null, {
|
|
113
|
+
status: 101,
|
|
117
114
|
headers: head.headers,
|
|
115
|
+
webSocket: new CfWebSocket(head.socket),
|
|
118
116
|
}));
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
sockets.add(end);
|
|
123
|
-
resolve(new CellsResponse(null, {
|
|
124
|
-
status: 101,
|
|
117
|
+
resolve(new Response(nullBody(head.status) ? null : head.body, {
|
|
118
|
+
status: head.status,
|
|
119
|
+
statusText: head.statusText,
|
|
125
120
|
headers: head.headers,
|
|
126
|
-
webSocket: new CfWebSocket(channel.port1),
|
|
127
121
|
}));
|
|
128
122
|
}
|
|
129
123
|
catch (err) {
|
|
130
|
-
void p.close?.();
|
|
131
124
|
reject(err);
|
|
132
125
|
}
|
|
133
126
|
};
|
|
@@ -135,14 +128,18 @@ async function upgradeOver(mux, meta, req, sockets) {
|
|
|
135
128
|
void p.start?.();
|
|
136
129
|
});
|
|
137
130
|
}
|
|
138
|
-
/** Serves an upgrade on port `p`:
|
|
131
|
+
/** Serves an upgrade on port `p`: a 101 hands over its client end's port; then `p` closes. */
|
|
139
132
|
function serveUpgrade(p, meta, fetch) {
|
|
140
133
|
// A browser drops `Upgrade` and `Sec-WebSocket-*` from `new Request()`: carried over here.
|
|
141
134
|
const headers = new Headers(meta.headers);
|
|
142
135
|
const req = carryWebSocketHeaders(headers, new Request(meta.url, { method: meta.method, headers }));
|
|
143
|
-
const answer = (head) => {
|
|
136
|
+
const answer = (head, transfer = []) => {
|
|
144
137
|
try {
|
|
145
|
-
|
|
138
|
+
// The multiplexer drops a message it cannot copy, so check before posting; a 101
|
|
139
|
+
// head is not checked, since its socket port is transferred rather than copied.
|
|
140
|
+
if (transfer.length === 0)
|
|
141
|
+
cloneable(head);
|
|
142
|
+
p.postMessage(head, transfer);
|
|
146
143
|
}
|
|
147
144
|
catch {
|
|
148
145
|
// An error that cannot be copied (a function in its `cause`, say): its name and message.
|
|
@@ -159,17 +156,16 @@ function serveUpgrade(p, meta, fetch) {
|
|
|
159
156
|
const res = await fetch(req);
|
|
160
157
|
const ws = res.webSocket;
|
|
161
158
|
if (res.status === 101 && ws) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
answer({ status: 101, headers: [...res.headers], body: null });
|
|
165
|
-
return;
|
|
159
|
+
const socket = takeClientPort(ws);
|
|
160
|
+
answer({ status: 101, headers: [...res.headers], body: null, socket }, [socket]);
|
|
166
161
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
162
|
+
else
|
|
163
|
+
answer({
|
|
164
|
+
status: res.status,
|
|
165
|
+
statusText: res.statusText,
|
|
166
|
+
headers: [...res.headers],
|
|
167
|
+
body: nullBody(res.status) ? null : await res.arrayBuffer(),
|
|
168
|
+
});
|
|
173
169
|
}
|
|
174
170
|
catch (error) {
|
|
175
171
|
answer({ error });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/isolate/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { fetchOverDuplex, serveFetchOverDuplex } from \"@statewalker/webrun-http-streams\";\nimport {\n callPort,\n duplexOverPort,\n listenPort,\n type MessageTarget,\n multiplexPort,\n NO_TIMEOUT,\n type PortMux,\n serveDuplexOverPort,\n structuredCodec,\n} from \"@statewalker/webrun-rpc\";\nimport { cross, pipeTo, rpcPromise, STUB } from \"../core/handles.js\";\nimport { withUrl } from \"../core/request-body.js\";\nimport { type Frame, type FrameEnd, portEnd, relayFrames } from \"../ws/frames.js\";\nimport { CellsResponse, CfWebSocket, takeClientPort } from \"../ws/server.js\";\nimport { carryWebSocketHeaders } from \"../ws/upgrade.js\";\n\n/**\n * Isolation for loaded Workers (celld runs each in its own V8 isolate): the loaded code runs\n * in a separate realm — a worker thread on Node, a Dedicated Worker in browsers — and talks\n * to the host over one MessagePort, multiplexed with `@statewalker/webrun-rpc`. The host keeps\n * everything else: bindings in the loaded Worker's env are reached through stubs, and its logs\n * come back for Tail Workers. Errors cross with their own properties (`name`, `code`, …).\n * Functions, `RpcTarget`s and stubs anywhere in a call or result cross by reference (as\n * workerd passes them): the far side gets a stub whose calls come back.\n *\n * Calls (one port, both directions; each names an `answer`, which the serving side keeps until\n * the caller has the result, so calls on it can be sent before then — promise pipelining):\n * host → isolate load {source|url, env, outbound} → loaded {exports}\n * call {name, method, args, props} → result\n * isolate → host log {level, args}\n * either way ref {id|on, path, args} (a call on a stub of the other side's value, or\n * on answer `on`: `path` is the property path\n * called, [] the value itself)\n * release {id|answer} (that stub was disposed or collected; the caller\n * has that answer)\n * Fetches: one port per request (meta: its target and URL), HTTP/1.1 with streamed bodies,\n * as cells-http over a port (src/transport/port.ts): host → isolate to an export or facet,\n * isolate → host for the loaded code's global `fetch()` when a `globalOutbound` Fetcher routes it.\n * A WebSocket upgrade (host → isolate) has a port of its own instead, since HTTP/1.1 drops the\n * `Upgrade` header: its meta carries the request's method and headers; the isolate answers with\n * the response's head (and body, unless it is a 101), then both sides relay the socket's frames.\n */\nexport interface PortLike {\n postMessage(msg: unknown, transfer?: Transferable[]): void;\n addEventListener?(type: \"message\", fn: (e: { data: unknown }) => void): void;\n removeEventListener?(type: \"message\", fn: (e: { data: unknown }) => void): void;\n on?(type: \"message\", fn: (data: unknown) => void): void;\n off?(type: \"message\", fn: (data: unknown) => void): void;\n start?(): void;\n}\n\n/** Where a fetch goes in the isolate: an export of the loaded Worker, or one of its facets. */\nexport type FetchTarget =\n | { name: string; props?: unknown }\n | { className: string; cellKey: string };\ntype Fetch = (target: FetchTarget, req: Request) => Promise<Response>;\n\nexport type Call = <T = unknown>(msg: object) => Promise<T>;\ntype Handle = (msg: any) => Promise<unknown>;\n\n/**\n * The host's end of an isolate's port: one webrun-rpc multiplexer over it, a calls port on\n * which each side calls the other (`callPort`/`listenPort`, no timeout: a call lasts as long\n * as the work it starts), and a port per fetch. `outbound` serves the loaded code's global\n * `fetch()` (its `globalOutbound` Fetcher).\n */\nexport async function hostPort(\n port: PortLike,\n handle: Handle,\n outbound?: (req: Request) => Promise<Response>,\n): Promise<{ call: Call; fetch: Fetch; closeSockets(): void }> {\n const mux = multiplexPort(messageTarget(port), {\n codec: structuredCodec,\n side: \"initiator\",\n onPort(p, meta) {\n const m = meta as { kind?: string; url: string } | undefined;\n if (m?.kind !== \"outbound\" || !outbound) return false;\n serveFetch(p, m.url, outbound);\n return true;\n },\n });\n const call = callsOver(await mux.openPort({ kind: \"calls\" }), handle);\n // The sockets open into the isolate: told it stopped (1001) when it does.\n const sockets = new Set<(code: number, reason: string) => void>();\n const fetch: Fetch = (target, req) =>\n isUpgrade(req)\n ? upgradeOver(mux, { kind: \"upgrade\", target }, req, sockets)\n : fetchOver(mux, { kind: \"fetch\", target }, req);\n const closeSockets = () => {\n for (const end of sockets) end(1001, \"the loaded Worker stopped\");\n sockets.clear();\n };\n return { call, fetch, closeSockets };\n}\n\n/**\n * The isolate's end: accepts the ports the host opens. Everything the isolate calls the host\n * for happens while serving a host call, so the calls port is open by then. `outbound` sends\n * a fetch to the host's `globalOutbound`.\n */\nexport function isolatePort(\n port: PortLike,\n handle: Handle,\n fetch: Fetch,\n): { call: Call; outbound: (req: Request) => Promise<Response> } {\n let calls: Call | undefined;\n const mux = multiplexPort(messageTarget(port), {\n codec: structuredCodec,\n side: \"responder\",\n onPort(p, meta) {\n const m = meta as { kind?: string; target: FetchTarget; url: string } | undefined;\n if (m?.kind === \"fetch\") {\n serveFetch(p, m.url, (r) => fetch(m.target, r));\n return true;\n }\n if (m?.kind === \"upgrade\") {\n serveUpgrade(p, m as unknown as UpgradeMeta, (r) => fetch(m.target, r));\n return true;\n }\n if (m?.kind !== \"calls\" || calls) return false;\n calls = callsOver(p, handle);\n return true;\n },\n });\n return {\n call: (msg) => calls!(msg),\n outbound: (req) => fetchOver(mux, { kind: \"outbound\" }, req),\n };\n}\n\n/** A fetch over a new port of `mux`; the URL rides in the meta (the HTTP/1.1 wire drops its scheme). */\nfunction fetchOver(mux: PortMux, meta: object, req: Request): Promise<Response> {\n return fetchOverDuplex(\n (input) =>\n (async function* () {\n yield* duplexOverPort(await mux.openPort({ ...meta, url: req.url }))(input);\n })(),\n req,\n );\n}\n\n/** Serves the fetch on port `p` (its URL from the meta) with `fetch`. */\nfunction serveFetch(p: MessageTarget, url: string, fetch: (req: Request) => Promise<Response>) {\n serveDuplexOverPort(\n p,\n serveFetchOverDuplex((req) => withUrl(req, url).then(fetch).catch(throwOwn)),\n );\n}\n\nconst isUpgrade = (req: Request) => req.headers.get(\"upgrade\")?.toLowerCase() === \"websocket\";\n\ninterface UpgradeMeta {\n url: string;\n method: string;\n headers: [string, string][];\n}\n/** The isolate's answer to an upgrade: the head of its response, or the error it threw. */\ntype UpgradeHead =\n | {\n status: number;\n statusText?: string;\n headers: [string, string][];\n body: ArrayBuffer | null;\n }\n | { error: unknown };\n\n/** A port of the multiplexer as one end of a frame relay. */\nconst muxEnd = (p: MessageTarget): FrameEnd => ({\n post: (f) => p.postMessage(f),\n listen: (fn) => {\n p.addEventListener(\"message\", (e) => fn(e.data as Frame));\n void p.start?.();\n },\n close: () => void p.close?.(),\n});\n\n/** Statuses whose response has no body (`new Response(body, …)` refuses one). */\nconst nullBody = (status: number) => [101, 204, 205, 304].includes(status);\n\n/**\n * A WebSocket upgrade over a port of its own. A 101 answer becomes a Workers response whose\n * `webSocket` is a client end here; its frames and the isolate's are relayed over the port until\n * the socket's closing handshake, or `sockets`' close (the isolate stops). Any other answer\n * closes the port.\n */\nasync function upgradeOver(\n mux: PortMux,\n meta: object,\n req: Request,\n sockets: Set<(code: number, reason: string) => void>,\n): Promise<Response> {\n const p = await mux.openPort({\n ...meta,\n url: req.url,\n method: req.method,\n headers: [...req.headers],\n } satisfies UpgradeMeta);\n return new Promise((resolve, reject) => {\n const onHead = (e: MessageEvent) => {\n p.removeEventListener(\"message\", onHead);\n try {\n const head = e.data as UpgradeHead;\n if (\"error\" in head) throw head.error;\n if (head.status !== 101) {\n void p.close?.();\n return resolve(\n new Response(nullBody(head.status) ? null : head.body, {\n status: head.status,\n statusText: head.statusText,\n headers: head.headers,\n }),\n );\n }\n const channel = new MessageChannel();\n const end = relayFrames(portEnd(channel.port2), muxEnd(p), () => sockets.delete(end));\n sockets.add(end);\n resolve(\n new CellsResponse(null, {\n status: 101,\n headers: head.headers,\n webSocket: new CfWebSocket(channel.port1),\n }),\n );\n } catch (err) {\n void p.close?.();\n reject(err);\n }\n };\n p.addEventListener(\"message\", onHead);\n void p.start?.();\n });\n}\n\n/** Serves an upgrade on port `p`: the loaded code's 101 hands its client end's frames to the port. */\nfunction serveUpgrade(\n p: MessageTarget,\n meta: UpgradeMeta,\n fetch: (req: Request) => Promise<Response>,\n) {\n // A browser drops `Upgrade` and `Sec-WebSocket-*` from `new Request()`: carried over here.\n const headers = new Headers(meta.headers);\n const req = carryWebSocketHeaders(\n headers,\n new Request(meta.url, { method: meta.method, headers }),\n );\n const answer = (head: UpgradeHead) => {\n try {\n p.postMessage(head);\n } catch {\n // An error that cannot be copied (a function in its `cause`, say): its name and message.\n const e = (head as { error?: { name?: unknown; message?: unknown } }).error;\n p.postMessage({\n error: Object.assign(new Error(String(e?.message ?? e)), {\n name: String(e?.name ?? \"Error\"),\n }),\n } satisfies UpgradeHead);\n }\n };\n void (async () => {\n try {\n const res = await fetch(req);\n const ws = (res as { webSocket?: CfWebSocket | null }).webSocket;\n if (res.status === 101 && ws) {\n // Listening before the head goes out: the host relays frames as soon as it has it.\n relayFrames(portEnd(takeClientPort(ws)), muxEnd(p));\n answer({ status: 101, headers: [...res.headers], body: null });\n return;\n }\n answer({\n status: res.status,\n statusText: res.statusText,\n headers: [...res.headers],\n body: nullBody(res.status) ? null : await res.arrayBuffer(),\n });\n } catch (error) {\n answer({ error });\n }\n void p.close?.();\n })();\n}\n\n/** Marks a value passed by reference in a message: `{ [REF]: id }`. */\nconst REF = \"\\u0000cellb.ref\";\n\n/**\n * The calls of one port, with values passed by reference: each side keeps the values it\n * passed (by id, and how many ids hold each); the other side calls them through stubs. A\n * stub's `Symbol.dispose` (or its garbage collection) releases its id; the last release of a\n * value runs the value's own `Symbol.dispose`, as workerd does for an `RpcTarget`. A call\n * returns an RpcPromise: calls on it go to the other side at once, naming its answer.\n */\nfunction callsOver(port: MessageTarget, handle: Handle): Call {\n const values = new Map<number, any>();\n const answers = new Map<number, unknown>();\n let lastAnswer = 0;\n const holders = new Map<unknown, number>();\n let lastId = 0;\n const encode = (v: unknown) =>\n cloneable(\n cross(\n v,\n (x) => {\n values.set(++lastId, x);\n holders.set(x, (holders.get(x) ?? 0) + 1);\n return { [REF]: lastId };\n },\n (x) => x, // the port copies the rest\n ),\n );\n const release = (id: number) => {\n const v = values.get(id);\n if (!values.delete(id)) return;\n const n = (holders.get(v) ?? 1) - 1;\n if (n) holders.set(v, n);\n else {\n holders.delete(v);\n v[Symbol.dispose]?.();\n }\n };\n const send = (msg: object) => callPort(port, msg, { timeout: NO_TIMEOUT }).catch(() => {});\n const collected = new FinalizationRegistry<number>((id) => send({ type: \"release\", id }));\n const stubOf = (id: number) => {\n const invoke = (path: string[], args: unknown[]) => call({ type: \"ref\", id, path, args });\n const stub: any = new Proxy(() => {}, {\n apply: (_t, _this, args) => invoke([], args),\n get(_t, prop) {\n if (prop === STUB) return true;\n if (prop === Symbol.dispose)\n return () => {\n collected.unregister(stub);\n send({ type: \"release\", id });\n };\n if (prop === \"then\" || typeof prop === \"symbol\") return undefined;\n return (...args: unknown[]) => invoke([prop], args);\n },\n });\n collected.register(stub, id, stub);\n return stub;\n };\n // Messages arrive as fresh copies: stubs replace the markers in place.\n const decode = (v: any): any => {\n if (Array.isArray(v)) for (let i = 0; i < v.length; i++) v[i] = decode(v[i]);\n else if (v && Object.getPrototypeOf(v) === Object.prototype) {\n if (typeof v[REF] === \"number\") return stubOf(v[REF]);\n for (const k of Object.keys(v)) v[k] = decode(v[k]);\n }\n return v;\n };\n const run = (m: any): unknown => {\n if (m.type !== \"ref\") return handle(m);\n if (m.on !== undefined) return pipeTo(answers.get(m.on), m.path, m.args);\n const v = values.get(m.id);\n if (v === undefined) throw new Error(\"A stub was used after it was disposed.\");\n return pipeTo(v, m.path, m.args);\n };\n // The answer is kept as the message arrives: a pipelined call on it may arrive before it\n // settles.\n const serve = async (m: any) => {\n if (m.type === \"release\") {\n if (m.answer === undefined) release(m.id);\n else answers.delete(m.answer);\n return;\n }\n let r: unknown;\n try {\n r = run(m);\n } catch (e) {\n r = Promise.reject(e);\n }\n answers.set(m.answer, r);\n return r;\n };\n listenPort(port, (msg) => serve(decode(msg)).then(encode).catch(throwOwn));\n const call: Call = (msg) => {\n const answer = ++lastAnswer;\n const result = (async () =>\n decode(await callPort(port, encode({ ...msg, answer }), { timeout: NO_TIMEOUT })))();\n // Calls on the answer sent before now are ahead of this release on the port.\n const done = () => send({ type: \"release\", answer });\n result.then(done, done);\n return rpcPromise(result, (path, args) => call({ type: \"ref\", on: answer, path, args }));\n };\n return call;\n}\n\n/** Errors cross with their own properties (so `code` does); `name` is inherited, so copy it. */\nfunction throwOwn(e: unknown): never {\n if (e && typeof e === \"object\")\n Object.defineProperty(e, \"name\", { value: (e as Error).name, enumerable: true });\n throw e;\n}\n\n/**\n * Throws `DataCloneError` for a value the port cannot carry: the multiplexer drops a message\n * it fails to post, and the call would wait forever.\n */\nfunction cloneable<T>(value: T): T {\n structuredClone(value);\n return value;\n}\n\n/** A worker_threads Worker is an EventEmitter; MessagePorts and Dedicated Workers are targets. */\nfunction messageTarget(port: PortLike): MessageTarget {\n if (port.addEventListener) return port as MessageTarget;\n const on = port.on!.bind(port);\n const listeners = new Map<unknown, (data: unknown) => void>();\n return {\n postMessage: (msg, transfer) => port.postMessage(msg, transfer),\n addEventListener(_type, fn) {\n const listener = (data: unknown) => void fn({ data } as MessageEvent);\n listeners.set(fn, listener);\n on(\"message\", listener);\n },\n removeEventListener(_type, fn) {\n port.off?.(\"message\", listeners.get(fn)!);\n listeners.delete(fn);\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/isolate/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACzF,OAAO,EACL,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,UAAU,EAEV,aAAa,EACb,UAAU,EAEV,mBAAmB,EACnB,eAAe,GAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAgDzD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAc,EACd,MAAc,EACd,QAA8C;IAE9C,MAAM,GAAG,GAAG,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE;QAC7C,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,QAAQ,EAAE;QACpB,MAAM,CAAC,CAAC,EAAE,IAAI;YACZ,MAAM,CAAC,GAAG,IAAkD,CAAC;YAC7D,IAAI,CAAC,EAAE,IAAI,KAAK,UAAU,IAAI,CAAC,QAAQ;gBAAE,OAAO,KAAK,CAAC;YACtD,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IACtE,MAAM,KAAK,GAAU,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CACnC,SAAS,CAAC,GAAG,CAAC;QACZ,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC;QACpD,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,IAAc,EACd,MAAc,EACd,KAAY;IAEZ,IAAI,KAAuB,CAAC;IAC5B,MAAM,GAAG,GAAG,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE;QAC7C,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,QAAQ,EAAE;QACpB,MAAM,CAAC,CAAC,EAAE,IAAI;YACZ,MAAM,CAAC,GAAG,IAAuE,CAAC;YAClF,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;gBACxB,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;gBAChD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC1B,YAAY,CAAC,CAAC,EAAE,CAA2B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;gBACxE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC;YAC/C,KAAK,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC,CAAC;IACH,OAAO;QACL,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAM,CAAC,GAAG,CAAC;QAC1B,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,CAAC;KAC7D,CAAC;AACJ,CAAC;AAED,wGAAwG;AACxG,SAAS,SAAS,CAAC,GAAY,EAAE,IAAY,EAAE,GAAY;IACzD,OAAO,eAAe,CACpB,CAAC,KAAK,EAAE,EAAE,CACR,CAAC,KAAK,SAAS,CAAC;QACd,KAAK,CAAC,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9E,CAAC,CAAC,EAAE,EACN,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,SAAS,UAAU,CAAC,CAAgB,EAAE,GAAW,EAAE,KAA0C;IAC3F,mBAAmB,CACjB,CAAC,EACD,oBAAoB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAC7E,CAAC;AACJ,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,GAAY,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,KAAK,WAAW,CAAC;AAO9F;;;;GAIG;AACH,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAE,UAA2C,CAAC,eAAe,IAAI,IAAI,CAAC;AAc5F,iFAAiF;AACjF,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAE3E;;;GAGG;AACH,KAAK,UAAU,WAAW,CAAC,GAAY,EAAE,IAAY,EAAE,GAAY;IACjE,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC;QAC3B,GAAG,IAAI;QACP,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;KACJ,CAAC,CAAC;IACzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,kFAAkF;QAClF,oDAAoD;QACpD,MAAM,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,CAAC,CAAC,mBAAmB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACzC,MAAM,CACJ,MAAM,EAAE,MAAM,IAAI,IAAI,KAAK,CAAC,yDAAyD,CAAC,CACvF,CAAC;QACJ,CAAC,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,CAAC,CAAe,EAAE,EAAE;YACjC,CAAC,CAAC,mBAAmB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACzC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC/C,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,CAAC,CAAC,IAAmB,CAAC;gBACnC,IAAI,OAAO,IAAI,IAAI;oBAAE,MAAM,IAAI,CAAC,KAAK,CAAC;gBACtC,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM;oBACpC,OAAO,OAAO,CACZ,IAAI,aAAa,CAAC,IAAI,EAAE;wBACtB,MAAM,EAAE,GAAG;wBACX,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,SAAS,EAAE,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;qBACxC,CAAC,CACH,CAAC;gBACJ,OAAO,CACL,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE;oBACrD,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;iBACtB,CAAC,CACH,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC;QACF,CAAC,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8FAA8F;AAC9F,SAAS,YAAY,CACnB,CAAgB,EAChB,IAAiB,EACjB,KAA0C;IAE1C,2FAA2F;IAC3F,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,qBAAqB,CAC/B,OAAO,EACP,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CACxD,CAAC;IACF,MAAM,MAAM,GAAG,CAAC,IAAiB,EAAE,QAAQ,GAAmB,EAAE,EAAE,EAAE;QAClE,IAAI,CAAC;YACH,iFAAiF;YACjF,gFAAgF;YAChF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS,CAAC,IAAI,CAAC,CAAC;YAC3C,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,yFAAyF;YACzF,MAAM,CAAC,GAAI,IAA0D,CAAC,KAAK,CAAC;YAC5E,CAAC,CAAC,WAAW,CAAC;gBACZ,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE;oBACvD,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,IAAI,OAAO,CAAC;iBACjC,CAAC;aACmB,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC;IACF,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,EAAE,GAAI,GAA0C,CAAC,SAAS,CAAC;YACjE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,EAAE,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;gBAClC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;YACnF,CAAC;;gBACC,MAAM,CAAC;oBACL,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,UAAU,EAAE,GAAG,CAAC,UAAU;oBAC1B,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;oBACzB,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE;iBAC5D,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACpB,CAAC;QACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;IACnB,CAAC,CAAC,EAAE,CAAC;AACP,CAAC;AAED,uEAAuE;AACvE,MAAM,GAAG,GAAG,iBAAiB,CAAC;AAE9B;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,IAAmB,EAAE,MAAc;IACpD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAe,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC3C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC3C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,MAAM,GAAG,CAAC,CAAU,EAAE,EAAE,CAC5B,SAAS,CACP,KAAK,CACH,CAAC,EACD,CAAC,CAAC,EAAE,EAAE;QACJ,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1C,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,CAAC,EACD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CACT,CACF,CAAC;IACJ,MAAM,OAAO,GAAG,CAAC,EAAU,EAAE,EAAE;QAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAAE,OAAO;QAC/B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;aACpB,CAAC;YACJ,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC3F,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAS,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1F,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE;QAC5B,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,IAAe,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,MAAM,IAAI,GAAQ,IAAI,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,EAAE;YACpC,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC;YAC5C,GAAG,CAAC,EAAE,EAAE,IAAI;gBACV,IAAI,IAAI,KAAK,IAAI;oBAAE,OAAO,IAAI,CAAC;gBAC/B,IAAI,IAAI,KAAK,MAAM,CAAC,OAAO;oBACzB,OAAO,GAAG,EAAE;wBACV,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;wBAC3B,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;oBAChC,CAAC,CAAC;gBACJ,IAAI,IAAI,KAAK,MAAM,IAAI,OAAO,IAAI,KAAK,QAAQ;oBAAE,OAAO,SAAS,CAAC;gBAClE,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;YACtD,CAAC;SACF,CAAC,CAAC;QACH,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,uEAAuE;IACvE,MAAM,MAAM,GAAG,CAAC,CAAM,EAAO,EAAE;QAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;gBAAE,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aACxE,IAAI,CAAC,IAAI,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;YAC5D,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACtD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;IACF,MAAM,GAAG,GAAG,CAAC,CAAM,EAAW,EAAE;QAC9B,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACzE,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3B,IAAI,CAAC,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC/E,OAAO,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC;IACF,yFAAyF;IACzF,WAAW;IACX,MAAM,KAAK,GAAG,KAAK,EAAE,CAAM,EAAE,EAAE;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;;gBACrC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,IAAI,CAAU,CAAC;QACf,IAAI,CAAC;YACH,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACb,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACzB,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;IACF,UAAU,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3E,MAAM,IAAI,GAAS,CAAC,GAAG,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,EAAE,UAAU,CAAC;QAC5B,MAAM,MAAM,GAAG,CAAC,KAAK,IAAI,EAAE,CACzB,MAAM,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,6EAA6E;QAC7E,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxB,OAAO,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3F,CAAC,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gGAAgG;AAChG,SAAS,QAAQ,CAAC,CAAU;IAC1B,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAC5B,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAG,CAAW,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IACnF,MAAM,CAAC,CAAC;AACV,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAI,KAAQ;IAC5B,eAAe,CAAC,KAAK,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kGAAkG;AAClG,SAAS,aAAa,CAAC,IAAc;IACnC,IAAI,IAAI,CAAC,gBAAgB;QAAE,OAAO,IAAqB,CAAC;IACxD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoC,CAAC;IAC9D,OAAO;QACL,WAAW,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC;QAC/D,gBAAgB,CAAC,KAAK,EAAE,EAAE;YACxB,MAAM,QAAQ,GAAG,CAAC,IAAa,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAkB,CAAC,CAAC;YACtE,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC5B,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC1B,CAAC;QACD,mBAAmB,CAAC,KAAK,EAAE,EAAE;YAC3B,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC,CAAC;YAC1C,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { fetchOverDuplex, serveFetchOverDuplex } from \"@statewalker/webrun-http-streams\";\nimport {\n callPort,\n duplexOverPort,\n getPortCloseSignal,\n listenPort,\n type MessageTarget,\n multiplexPort,\n NO_TIMEOUT,\n type PortMux,\n serveDuplexOverPort,\n structuredCodec,\n} from \"@statewalker/webrun-rpc\";\nimport { cross, pipeTo, rpcPromise, STUB } from \"../core/handles.js\";\nimport { withUrl } from \"../core/request-body.js\";\nimport { CellsResponse, CfWebSocket, takeClientPort } from \"../ws/server.js\";\nimport { carryWebSocketHeaders } from \"../ws/upgrade.js\";\n\n/**\n * Isolation for loaded Workers (celld runs each in its own V8 isolate): the loaded code runs\n * in a separate realm — a worker thread on Node, a Dedicated Worker in browsers — and talks\n * to the host over one MessagePort, multiplexed with `@statewalker/webrun-rpc`. The host keeps\n * everything else: bindings in the loaded Worker's env are reached through stubs, and its logs\n * come back for Tail Workers. Errors cross with their own properties (`name`, `code`, …).\n * Functions, `RpcTarget`s and stubs anywhere in a call or result cross by reference (as\n * workerd passes them): the far side gets a stub whose calls come back.\n *\n * Calls (one port, both directions; each names an `answer`, which the serving side keeps until\n * the caller has the result, so calls on it can be sent before then — promise pipelining):\n * host → isolate load {source|url, env, outbound} → loaded {exports}\n * call {name, method, args, props} → result\n * isolate → host log {level, args}\n * either way ref {id|on, path, args} (a call on a stub of the other side's value, or\n * on answer `on`: `path` is the property path\n * called, [] the value itself)\n * release {id|answer} (that stub was disposed or collected; the caller\n * has that answer)\n * Fetches: one port per request (meta: its target and URL), HTTP/1.1 with streamed bodies,\n * as cells-http over a port (src/transport/port.ts): host → isolate to an export or facet,\n * isolate → host for the loaded code's global `fetch()` when a `globalOutbound` Fetcher routes it.\n * A WebSocket upgrade (host → isolate) has a port of its own instead, since HTTP/1.1 drops the\n * `Upgrade` header: its meta carries the request's method and headers; the isolate answers with\n * the response's head and closes the port. A 101's head carries the socket's own MessagePort,\n * transferred: the socket's frames then go straight between the client and the isolate, outside\n * the multiplexer, so open sockets hold none of its ports (it allows `maxPorts` at once).\n */\nexport interface PortLike {\n postMessage(msg: unknown, transfer?: Transferable[]): void;\n addEventListener?(type: \"message\", fn: (e: { data: unknown }) => void): void;\n removeEventListener?(type: \"message\", fn: (e: { data: unknown }) => void): void;\n on?(type: \"message\", fn: (data: unknown) => void): void;\n off?(type: \"message\", fn: (data: unknown) => void): void;\n start?(): void;\n}\n\n/** Where a fetch goes in the isolate: an export of the loaded Worker, or one of its facets. */\nexport type FetchTarget =\n | { name: string; props?: unknown }\n | { className: string; cellKey: string };\ntype Fetch = (target: FetchTarget, req: Request) => Promise<Response>;\n\nexport type Call = <T = unknown>(msg: object) => Promise<T>;\ntype Handle = (msg: any) => Promise<unknown>;\n\n/**\n * The host's end of an isolate's port: one webrun-rpc multiplexer over it, a calls port on\n * which each side calls the other (`callPort`/`listenPort`, no timeout: a call lasts as long\n * as the work it starts), and a port per fetch. `outbound` serves the loaded code's global\n * `fetch()` (its `globalOutbound` Fetcher).\n */\nexport async function hostPort(\n port: PortLike,\n handle: Handle,\n outbound?: (req: Request) => Promise<Response>,\n): Promise<{ call: Call; fetch: Fetch }> {\n const mux = multiplexPort(messageTarget(port), {\n codec: structuredCodec,\n side: \"initiator\",\n maxPorts: maxPorts(),\n onPort(p, meta) {\n const m = meta as { kind?: string; url: string } | undefined;\n if (m?.kind !== \"outbound\" || !outbound) return false;\n serveFetch(p, m.url, outbound);\n return true;\n },\n });\n const call = callsOver(await mux.openPort({ kind: \"calls\" }), handle);\n const fetch: Fetch = (target, req) =>\n isUpgrade(req)\n ? upgradeOver(mux, { kind: \"upgrade\", target }, req)\n : fetchOver(mux, { kind: \"fetch\", target }, req);\n return { call, fetch };\n}\n\n/**\n * The isolate's end: accepts the ports the host opens. Everything the isolate calls the host\n * for happens while serving a host call, so the calls port is open by then. `outbound` sends\n * a fetch to the host's `globalOutbound`.\n */\nexport function isolatePort(\n port: PortLike,\n handle: Handle,\n fetch: Fetch,\n): { call: Call; outbound: (req: Request) => Promise<Response> } {\n let calls: Call | undefined;\n const mux = multiplexPort(messageTarget(port), {\n codec: structuredCodec,\n side: \"responder\",\n maxPorts: maxPorts(),\n onPort(p, meta) {\n const m = meta as { kind?: string; target: FetchTarget; url: string } | undefined;\n if (m?.kind === \"fetch\") {\n serveFetch(p, m.url, (r) => fetch(m.target, r));\n return true;\n }\n if (m?.kind === \"upgrade\") {\n serveUpgrade(p, m as unknown as UpgradeMeta, (r) => fetch(m.target, r));\n return true;\n }\n if (m?.kind !== \"calls\" || calls) return false;\n calls = callsOver(p, handle);\n return true;\n },\n });\n return {\n call: (msg) => calls!(msg),\n outbound: (req) => fetchOver(mux, { kind: \"outbound\" }, req),\n };\n}\n\n/** A fetch over a new port of `mux`; the URL rides in the meta (the HTTP/1.1 wire drops its scheme). */\nfunction fetchOver(mux: PortMux, meta: object, req: Request): Promise<Response> {\n return fetchOverDuplex(\n (input) =>\n (async function* () {\n yield* duplexOverPort(await mux.openPort({ ...meta, url: req.url }))(input);\n })(),\n req,\n );\n}\n\n/** Serves the fetch on port `p` (its URL from the meta) with `fetch`. */\nfunction serveFetch(p: MessageTarget, url: string, fetch: (req: Request) => Promise<Response>) {\n serveDuplexOverPort(\n p,\n serveFetchOverDuplex((req) => withUrl(req, url).then(fetch).catch(throwOwn)),\n );\n}\n\nconst isUpgrade = (req: Request) => req.headers.get(\"upgrade\")?.toLowerCase() === \"websocket\";\n\ninterface UpgradeMeta {\n url: string;\n method: string;\n headers: [string, string][];\n}\n/**\n * Ports of an isolate's multiplexer open at once: the calls port, and one per fetch or upgrade\n * in flight (each closes when its exchange ends). `globalThis.__cellbMaxPorts` overrides it, as\n * for src/transport/port.ts.\n */\nconst maxPorts = () => (globalThis as { __cellbMaxPorts?: number }).__cellbMaxPorts ?? 4096;\n\n/** The isolate's answer to an upgrade: the head of its response, or the error it threw. */\ntype UpgradeHead =\n | {\n status: number;\n statusText?: string;\n headers: [string, string][];\n body: ArrayBuffer | null;\n /** A 101's socket: the port of its client end, transferred. */\n socket?: MessagePort;\n }\n | { error: unknown };\n\n/** Statuses whose response has no body (`new Response(body, …)` refuses one). */\nconst nullBody = (status: number) => [101, 204, 205, 304].includes(status);\n\n/**\n * A WebSocket upgrade over a port of its own, closed once the isolate's answer is in. A 101\n * becomes a Workers response whose `webSocket` is a client end over the socket's own port.\n */\nasync function upgradeOver(mux: PortMux, meta: object, req: Request): Promise<Response> {\n const p = await mux.openPort({\n ...meta,\n url: req.url,\n method: req.method,\n headers: [...req.headers],\n } satisfies UpgradeMeta);\n return new Promise((resolve, reject) => {\n // A port closed without an answer (the isolate refused it, or could not post one)\n // fails the upgrade rather than leaving it pending.\n const closed = getPortCloseSignal(p);\n const onClosed = () => {\n p.removeEventListener(\"message\", onHead);\n reject(\n closed?.reason ?? new Error(\"cellb: the isolate closed the upgrade without an answer\"),\n );\n };\n closed?.addEventListener(\"abort\", onClosed, { once: true });\n const onHead = (e: MessageEvent) => {\n p.removeEventListener(\"message\", onHead);\n closed?.removeEventListener(\"abort\", onClosed);\n void p.close?.();\n try {\n const head = e.data as UpgradeHead;\n if (\"error\" in head) throw head.error;\n if (head.status === 101 && head.socket)\n return resolve(\n new CellsResponse(null, {\n status: 101,\n headers: head.headers,\n webSocket: new CfWebSocket(head.socket),\n }),\n );\n resolve(\n new Response(nullBody(head.status) ? null : head.body, {\n status: head.status,\n statusText: head.statusText,\n headers: head.headers,\n }),\n );\n } catch (err) {\n reject(err);\n }\n };\n p.addEventListener(\"message\", onHead);\n void p.start?.();\n });\n}\n\n/** Serves an upgrade on port `p`: a 101 hands over its client end's port; then `p` closes. */\nfunction serveUpgrade(\n p: MessageTarget,\n meta: UpgradeMeta,\n fetch: (req: Request) => Promise<Response>,\n) {\n // A browser drops `Upgrade` and `Sec-WebSocket-*` from `new Request()`: carried over here.\n const headers = new Headers(meta.headers);\n const req = carryWebSocketHeaders(\n headers,\n new Request(meta.url, { method: meta.method, headers }),\n );\n const answer = (head: UpgradeHead, transfer: Transferable[] = []) => {\n try {\n // The multiplexer drops a message it cannot copy, so check before posting; a 101\n // head is not checked, since its socket port is transferred rather than copied.\n if (transfer.length === 0) cloneable(head);\n p.postMessage(head, transfer);\n } catch {\n // An error that cannot be copied (a function in its `cause`, say): its name and message.\n const e = (head as { error?: { name?: unknown; message?: unknown } }).error;\n p.postMessage({\n error: Object.assign(new Error(String(e?.message ?? e)), {\n name: String(e?.name ?? \"Error\"),\n }),\n } satisfies UpgradeHead);\n }\n };\n void (async () => {\n try {\n const res = await fetch(req);\n const ws = (res as { webSocket?: CfWebSocket | null }).webSocket;\n if (res.status === 101 && ws) {\n const socket = takeClientPort(ws);\n answer({ status: 101, headers: [...res.headers], body: null, socket }, [socket]);\n } else\n answer({\n status: res.status,\n statusText: res.statusText,\n headers: [...res.headers],\n body: nullBody(res.status) ? null : await res.arrayBuffer(),\n });\n } catch (error) {\n answer({ error });\n }\n void p.close?.();\n })();\n}\n\n/** Marks a value passed by reference in a message: `{ [REF]: id }`. */\nconst REF = \"\\u0000cellb.ref\";\n\n/**\n * The calls of one port, with values passed by reference: each side keeps the values it\n * passed (by id, and how many ids hold each); the other side calls them through stubs. A\n * stub's `Symbol.dispose` (or its garbage collection) releases its id; the last release of a\n * value runs the value's own `Symbol.dispose`, as workerd does for an `RpcTarget`. A call\n * returns an RpcPromise: calls on it go to the other side at once, naming its answer.\n */\nfunction callsOver(port: MessageTarget, handle: Handle): Call {\n const values = new Map<number, any>();\n const answers = new Map<number, unknown>();\n let lastAnswer = 0;\n const holders = new Map<unknown, number>();\n let lastId = 0;\n const encode = (v: unknown) =>\n cloneable(\n cross(\n v,\n (x) => {\n values.set(++lastId, x);\n holders.set(x, (holders.get(x) ?? 0) + 1);\n return { [REF]: lastId };\n },\n (x) => x, // the port copies the rest\n ),\n );\n const release = (id: number) => {\n const v = values.get(id);\n if (!values.delete(id)) return;\n const n = (holders.get(v) ?? 1) - 1;\n if (n) holders.set(v, n);\n else {\n holders.delete(v);\n v[Symbol.dispose]?.();\n }\n };\n const send = (msg: object) => callPort(port, msg, { timeout: NO_TIMEOUT }).catch(() => {});\n const collected = new FinalizationRegistry<number>((id) => send({ type: \"release\", id }));\n const stubOf = (id: number) => {\n const invoke = (path: string[], args: unknown[]) => call({ type: \"ref\", id, path, args });\n const stub: any = new Proxy(() => {}, {\n apply: (_t, _this, args) => invoke([], args),\n get(_t, prop) {\n if (prop === STUB) return true;\n if (prop === Symbol.dispose)\n return () => {\n collected.unregister(stub);\n send({ type: \"release\", id });\n };\n if (prop === \"then\" || typeof prop === \"symbol\") return undefined;\n return (...args: unknown[]) => invoke([prop], args);\n },\n });\n collected.register(stub, id, stub);\n return stub;\n };\n // Messages arrive as fresh copies: stubs replace the markers in place.\n const decode = (v: any): any => {\n if (Array.isArray(v)) for (let i = 0; i < v.length; i++) v[i] = decode(v[i]);\n else if (v && Object.getPrototypeOf(v) === Object.prototype) {\n if (typeof v[REF] === \"number\") return stubOf(v[REF]);\n for (const k of Object.keys(v)) v[k] = decode(v[k]);\n }\n return v;\n };\n const run = (m: any): unknown => {\n if (m.type !== \"ref\") return handle(m);\n if (m.on !== undefined) return pipeTo(answers.get(m.on), m.path, m.args);\n const v = values.get(m.id);\n if (v === undefined) throw new Error(\"A stub was used after it was disposed.\");\n return pipeTo(v, m.path, m.args);\n };\n // The answer is kept as the message arrives: a pipelined call on it may arrive before it\n // settles.\n const serve = async (m: any) => {\n if (m.type === \"release\") {\n if (m.answer === undefined) release(m.id);\n else answers.delete(m.answer);\n return;\n }\n let r: unknown;\n try {\n r = run(m);\n } catch (e) {\n r = Promise.reject(e);\n }\n answers.set(m.answer, r);\n return r;\n };\n listenPort(port, (msg) => serve(decode(msg)).then(encode).catch(throwOwn));\n const call: Call = (msg) => {\n const answer = ++lastAnswer;\n const result = (async () =>\n decode(await callPort(port, encode({ ...msg, answer }), { timeout: NO_TIMEOUT })))();\n // Calls on the answer sent before now are ahead of this release on the port.\n const done = () => send({ type: \"release\", answer });\n result.then(done, done);\n return rpcPromise(result, (path, args) => call({ type: \"ref\", on: answer, path, args }));\n };\n return call;\n}\n\n/** Errors cross with their own properties (so `code` does); `name` is inherited, so copy it. */\nfunction throwOwn(e: unknown): never {\n if (e && typeof e === \"object\")\n Object.defineProperty(e, \"name\", { value: (e as Error).name, enumerable: true });\n throw e;\n}\n\n/**\n * Throws `DataCloneError` for a value the port cannot carry: the multiplexer drops a message\n * it fails to post, and the call would wait forever.\n */\nfunction cloneable<T>(value: T): T {\n structuredClone(value);\n return value;\n}\n\n/** A worker_threads Worker is an EventEmitter; MessagePorts and Dedicated Workers are targets. */\nfunction messageTarget(port: PortLike): MessageTarget {\n if (port.addEventListener) return port as MessageTarget;\n const on = port.on!.bind(port);\n const listeners = new Map<unknown, (data: unknown) => void>();\n return {\n postMessage: (msg, transfer) => port.postMessage(msg, transfer),\n addEventListener(_type, fn) {\n const listener = (data: unknown) => void fn({ data } as MessageEvent);\n listeners.set(fn, listener);\n on(\"message\", listener);\n },\n removeEventListener(_type, fn) {\n port.off?.(\"message\", listeners.get(fn)!);\n listeners.delete(fn);\n },\n };\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ObjectStore } from "../bucket/object-store.js";
|
|
2
2
|
import type { CellRuntime } from "../core/runtime.js";
|
|
3
|
+
import { type CfWebSocket } from "../ws/server.js";
|
|
3
4
|
/** The fleet's peer secret (celld creates it in the bucket). */
|
|
4
5
|
export declare function loadPeerKey(store: ObjectStore): Promise<Uint8Array>;
|
|
5
6
|
/** Headers that sign one peer request from `source` to `target`. */
|
|
@@ -23,6 +24,22 @@ export interface CelldPeerServer {
|
|
|
23
24
|
addr: string;
|
|
24
25
|
close(): Promise<void>;
|
|
25
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* The subprotocols a `Sec-WebSocket-Protocol` header offers, as a client may ask for them: the
|
|
29
|
+
* valid tokens, each once (case-insensitively), in the offered order. A header a client sent
|
|
30
|
+
* through celld is not trusted to be well formed, and the client class refuses a malformed list.
|
|
31
|
+
*/
|
|
32
|
+
export declare function offeredProtocols(header: string | undefined): string[];
|
|
33
|
+
/**
|
|
34
|
+
* The cell's side of a tunneled WebSocket: a client socket on the port of the 101's
|
|
35
|
+
* WebSocketPair end. It asks for the subprotocols the far client offered (`offered`, the
|
|
36
|
+
* `Sec-WebSocket-Protocol` header): a client refuses a subprotocol it did not ask for, and the
|
|
37
|
+
* cell picks one of those. Should the socket not be made, the port is closed, and the cell's
|
|
38
|
+
* end with it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function cellSocket(res: Response & {
|
|
41
|
+
webSocket: CfWebSocket;
|
|
42
|
+
}, offered: string | undefined): WebSocket;
|
|
26
43
|
/**
|
|
27
44
|
* Serves celld's peer tunnel for `runtime` on `host:port` (0: any free port). `node` is this
|
|
28
45
|
* node's name in the fleet (the same as celldBucket's `node`).
|