@statewalker/cellb 0.3.0 → 0.4.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/dist/bucket/celld-cells.js +25 -3
- package/dist/bucket/celld-cells.js.map +1 -1
- package/dist/project/browser-shards.d.ts +8 -9
- package/dist/project/browser-shards.js +88 -52
- package/dist/project/browser-shards.js.map +1 -1
- package/dist/project/browser-tabs.d.ts +14 -4
- package/dist/project/browser-tabs.js +193 -208
- package/dist/project/browser-tabs.js.map +1 -1
- package/dist/transport/cells-http.d.ts +6 -0
- package/dist/transport/cells-http.js +22 -1
- package/dist/transport/cells-http.js.map +1 -1
- package/dist/transport/cells-link.d.ts +2 -0
- package/dist/transport/cells-link.js +5 -0
- package/dist/transport/cells-link.js.map +1 -1
- package/dist/transport/port.js +4 -0
- package/dist/transport/port.js.map +1 -1
- package/dist/ws/server.d.ts +17 -19
- package/dist/ws/server.js +53 -130
- package/dist/ws/server.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-tabs.js","sourceRoot":"","sources":["../../src/project/browser-tabs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One project shared by every tab of an origin, its shards balanced over the tabs.\n *\n * ServiceWorker: installProjectRouter(self, { basePath: \"/app/\" })\n * each page: joinProjectShards({ name, shards, spawn, config, code, serviceWorker })\n * shard worker: serveProjectShard({ sqlite3 })\n *\n * Requests to `<basePath><name>/…` reach the project's Worker through the ServiceWorker, whichever\n * tab sent them (with their URL unchanged, unless `stripBase`); a Durable Object call runs in the\n * shard that owns the object, whichever tab holds that shard.\n *\n * Each tab holds a presence lock; every tab computes the same owner for each shard from the tabs\n * present (`shardOwner`: an even split, oldest tabs first), takes the shards it should own (one\n * Web Lock each) and gives up the others. A shard's OPFS pool belongs to the origin, so its cells\n * move with it from tab to tab. A shard given up is drained first (its requests in flight\n * finish, new ones wait for the next owner); a tab that closes runs no code, so the requests in\n * flight in its shards fail (ShardOwnerLostError) and the other tabs take the shards over.\n */\nimport { CellRouter } from \"../router/cell-router.js\";\nimport { linkOverPort, serveHandlerOverPort } from \"../transport/port.js\";\nimport { SW_SOCKET } from \"../ws/sw-connector.js\";\nimport { applyWorkerCookies, withPageCookies } from \"./browser-cookies.js\";\nimport type { BrowserBucket, BrowserProjectOptions } from \"./browser-host.js\";\nimport {\n SHARD_KEEPALIVE,\n SHARD_WAKE,\n TAB_INIT,\n TAB_LINK,\n TAB_WATCH,\n type TabShardInit,\n} from \"./browser-shards.js\";\n\nconst ATTACH = \"cellb:attach\";\nconst NEED_LINKS = \"cellb:need-links\";\n/** A page opens a socket (`serviceWorkerConnector`). */\nconst SOCKET = SW_SOCKET;\nconst DRAIN = \"cellb:drain\";\n/** A shard's call waits for the ServiceWorker: this message starts it, and it asks for links. */\nconst WAKE = \"cellb:wake\";\n/** Calls between shards are in flight: the message itself resets the ServiceWorker's idle timer. */\nconst KEEPALIVE = \"cellb:keepalive\";\n\n/**\n * The tab that should run `shard`: the tabs sorted by id (ids start with the time the tab joined),\n * shard i to tab i mod n — every tab runs ⌊shards/n⌋ or ⌈shards/n⌉ shards. A tab joining or\n * leaving moves the shards whose owner index changes; the oldest tabs keep theirs first.\n */\nexport function shardOwner(shard: number, tabs: string[]): string | undefined {\n if (!tabs.length) return undefined;\n const sorted = [...tabs].sort();\n return sorted[shard % sorted.length];\n}\n\nexport interface ProjectTabsOptions {\n /** The project's name in this origin: its URL path, locks and OPFS pools derive from it. */\n name: string;\n shards: number;\n /** Starts the worker of one shard; its script calls `serveProjectShard`. */\n spawn: (shard: number) => Worker;\n config: BrowserProjectOptions[\"config\"];\n code: BrowserProjectOptions[\"code\"];\n assetFiles?: BrowserProjectOptions[\"assetFiles\"];\n /** The OpenAI-compatible endpoint behind the project's `ai` binding. */\n ai?: BrowserProjectOptions[\"ai\"];\n /** Each shard is its own fleet node, `<node>-s<shard>`. */\n bucket?: BrowserBucket;\n /** The active ServiceWorker that runs `installProjectRouter`. */\n serviceWorker: () => Promise<ServiceWorker>;\n /** Called whenever the shards this tab runs change. */\n onChange?: (owned: number[]) => void;\n /** How often the tab checks who is present (tabs that crash release their locks silently). */\n checkMs?: number;\n /**\n * A shard handed to another tab first lets its requests in flight finish, for up to this long\n * (default 5 s); new ones wait for the next owner.\n */\n drainMs?: number;\n}\n\nexport interface ProjectTabs {\n /** The shards this tab runs now. */\n owned(): number[];\n /** Ids of the tabs present (this one included). */\n tabs(): Promise<string[]>;\n /** Gives up every shard (closing its runtime first) and leaves. */\n close(): Promise<void>;\n}\n\ninterface Held {\n /** Aborts the wait for the lock, or ends the hold once running. */\n stop: AbortController;\n worker?: Worker;\n running?: Promise<void>;\n}\n\n/** The page side: this tab takes part in running the project's shards. */\nexport async function joinProjectShards(opts: ProjectTabsOptions): Promise<ProjectTabs> {\n const prefix = `cellb:${opts.name}`;\n const tabId = `${Date.now().toString(36).padStart(9, \"0\")}-${crypto.randomUUID()}`;\n const presenceLock = `${prefix}:tab:${tabId}`;\n let leave!: () => void;\n await new Promise<void>((held) => {\n void navigator.locks.request(presenceLock, () => {\n held();\n return new Promise<void>((r) => (leave = r));\n });\n });\n const channel = new BroadcastChannel(`${prefix}:tabs`);\n const held = new Map<number, Held>();\n const running = new Set<number>();\n let closed = false;\n\n const tabs = async (): Promise<string[]> => {\n const { held: locks = [] } = await navigator.locks.query();\n const ids = locks\n .map((l) => l.name ?? \"\")\n .filter((n) => n.startsWith(`${prefix}:tab:`))\n .map((n) => n.slice(`${prefix}:tab:`.length));\n return ids.includes(tabId) ? ids : [...ids, tabId];\n };\n\n const ask = (w: Worker, data: unknown, ports: MessagePort[] = []) =>\n new Promise<{\n ok: boolean;\n error?: string;\n ownerLock?: string;\n unsupported?: string[];\n }>((resolve) => {\n const { port1, port2 } = new MessageChannel();\n port1.onmessage = (e) => {\n port1.close();\n resolve(e.data);\n };\n w.postMessage(data, [port2, ...ports]);\n });\n\n /** Links a shard's worker to the router (again, after a ServiceWorker restart). */\n const attach = async (shard: number, w: Worker, ownerLock: string) => {\n const down = new MessageChannel(); // router → shard\n const up = new MessageChannel(); // shard → router\n const ctl = new MessageChannel(); // WebSocket port hand-over\n const linked = await ask(w, { type: TAB_LINK }, [down.port2, up.port2, ctl.port2]);\n if (!linked.ok) throw new Error(linked.error);\n const sw = await opts.serviceWorker();\n const routerLock = await new Promise<string>((resolve, reject) => {\n const { port1, port2 } = new MessageChannel();\n port1.onmessage = (e) => (\n port1.close(), e.data?.ok ? resolve(e.data.routerLock) : reject(new Error(e.data?.error))\n );\n sw.postMessage(\n {\n type: ATTACH,\n name: opts.name,\n shards: opts.shards,\n scriptName: opts.config.name ?? \"worker\",\n shard,\n ownerLock,\n down: down.port1,\n up: up.port1,\n ctl: ctl.port1,\n },\n [port2, down.port1, up.port1, ctl.port1],\n );\n });\n // The shard drops these links once the ServiceWorker is gone, and wakes it when it needs it.\n const watching = await ask(w, { type: TAB_WATCH, routerLock });\n if (!watching.ok) throw new Error(watching.error);\n };\n\n /** Starts the ServiceWorker again (WAKE: it asks the tabs for their shards' links), or keeps it up. */\n const tell = async (type: typeof WAKE | typeof KEEPALIVE) => {\n const sw = await opts.serviceWorker();\n sw.postMessage({ type, name: opts.name });\n };\n const owners = new Map<number, string>(); // shard → its worker's owner lock\n\n /** Asks the router to stop sending the shard new requests and let the current ones finish. */\n const drain = async (shard: number, ownerLock: string) => {\n const sw = await opts.serviceWorker();\n const ms = opts.drainMs ?? 5_000;\n await new Promise<void>((resolve) => {\n const { port1, port2 } = new MessageChannel();\n const t = setTimeout(resolve, ms + 1_000); // a ServiceWorker that does not answer\n port1.onmessage = () => (clearTimeout(t), port1.close(), resolve());\n sw.postMessage({ type: DRAIN, name: opts.name, shard, ownerLock, ms }, [port2]);\n });\n };\n\n const changed = () => opts.onChange?.([...running].sort((a, b) => a - b));\n\n /** Waits for the shard's lock, then runs its worker until `stop` is aborted. */\n const take = (shard: number) => {\n const h: Held = { stop: new AbortController() };\n held.set(shard, h);\n h.running = navigator.locks\n .request(`${prefix}:shard:${shard}`, { signal: h.stop.signal }, async () => {\n const w = opts.spawn(shard);\n h.worker = w;\n w.addEventListener(\"message\", (e) => {\n if (e.data?.type === SHARD_WAKE) void tell(WAKE).catch(() => {});\n if (e.data?.type === SHARD_KEEPALIVE) void tell(KEEPALIVE).catch(() => {});\n });\n try {\n const started = await ask(w, {\n type: TAB_INIT,\n name: opts.name,\n shard,\n shards: opts.shards,\n config: opts.config,\n code: opts.code,\n assetFiles: opts.assetFiles,\n ai: opts.ai,\n bucket: opts.bucket && { ...opts.bucket, node: `${opts.bucket.node}-s${shard}` },\n pool: `${opts.name}-shard-${shard}`,\n filesDir: `${opts.name}-files-${shard}`,\n } satisfies TabShardInit);\n if (!started.ok || !started.ownerLock) throw new Error(started.error);\n // What the configuration asks for and this runtime does not provide (a cron trigger\n // without a scheduled() handler, say): said once, not silently ignored.\n if (started.unsupported?.length)\n console.warn(\n `cellb: project ${opts.name} shard ${shard}: unsupported: ${started.unsupported.join(\"; \")}`,\n );\n owners.set(shard, started.ownerLock);\n if (!h.stop.signal.aborted) {\n await attach(shard, w, started.ownerLock);\n running.add(shard);\n changed();\n await new Promise<void>((r) =>\n h.stop.signal.aborted ? r() : h.stop.signal.addEventListener(\"abort\", () => r()),\n );\n }\n } finally {\n // A planned hand-over drains the shard's requests in flight first; then the runtime\n // closes (and the pool with it) before the lock goes to the next tab.\n const lock = owners.get(shard);\n if (lock && running.has(shard)) await drain(shard, lock).catch(() => {});\n running.delete(shard);\n owners.delete(shard);\n await ask(w, { type: \"cellb:shard-close\" }).catch(() => {});\n w.terminate();\n changed();\n }\n })\n .catch((err) => {\n // Aborted while waiting is expected; a shard that failed to start is reported.\n if (!h.stop.signal.aborted)\n console.error(\n `cellb: ${opts.name} shard ${shard} failed: ${(err as Error)?.message ?? err}`,\n );\n })\n .finally(() => {\n if (held.get(shard) === h) held.delete(shard);\n });\n };\n\n let balancing: Promise<void> | undefined;\n let again = false;\n /** Takes the shards this tab should own and gives up the others. */\n const balance = async () => {\n if (balancing) {\n again = true;\n return balancing;\n }\n balancing = (async () => {\n do {\n again = false;\n if (closed) return;\n const present = await tabs();\n for (let shard = 0; shard < opts.shards; shard++) {\n const mine = shardOwner(shard, present) === tabId;\n const h = held.get(shard);\n if (mine && !h) take(shard);\n else if (!mine && h) h.stop.abort();\n }\n } while (again);\n })().finally(() => (balancing = undefined));\n return balancing;\n };\n\n channel.onmessage = () => void balance();\n const timer = setInterval(() => void balance(), opts.checkMs ?? 1000);\n const onSw = (e: MessageEvent) => {\n if (e.data?.type !== NEED_LINKS || (e.data.name && e.data.name !== opts.name)) return;\n for (const shard of running) {\n const w = held.get(shard)?.worker;\n const lock = owners.get(shard);\n if (w && lock) void attach(shard, w, lock).catch(() => {});\n }\n };\n navigator.serviceWorker.addEventListener(\"message\", onSw);\n channel.postMessage({ joined: tabId });\n await balance();\n\n return {\n owned: () => [...running].sort((a, b) => a - b),\n tabs,\n async close() {\n closed = true;\n clearInterval(timer);\n navigator.serviceWorker.removeEventListener(\"message\", onSw);\n const stopping = [...held.values()];\n for (const h of stopping) h.stop.abort();\n await Promise.all(stopping.map((h) => h.running));\n leave();\n channel.postMessage({ left: tabId });\n channel.close();\n },\n };\n}\n\n/**\n * A ServiceWorker's global scope (its `self`), as `installProjectRouter` takes it: typed without\n * TypeScript's WebWorker lib, so that code importing cellb compiles with the DOM lib alone.\n */\nexport interface ServiceWorkerScope {\n readonly location: { readonly origin: string };\n readonly clients: unknown;\n addEventListener(type: string, listener: (event: never) => unknown): void;\n}\n\n/** The ServiceWorker side: one CellRouter per project, fed by the tabs' shard workers. */\nexport function installProjectRouter(\n scope: ServiceWorkerScope,\n opts: {\n /** Requests to `<basePath><name>/…` go to project `name`. */\n basePath: string;\n /**\n * The Worker sees the request's URL as the page sent it (default), as a Worker on a\n * Cloudflare route does: mount its routes under `<basePath><name>/`. With `stripBase`, it\n * sees `/<rest>` instead — URLs it builds from the request then lack the prefix.\n */\n stripBase?: boolean;\n attachTimeoutMs?: number;\n /**\n * A project served at the origin's root, for apps that build absolute paths (EmDash's\n * `/_emdash/…`, say) and so cannot live under `<basePath><n>/`. Every same-origin request\n * outside `basePath` and outside the `passthrough` path prefixes (the host page's own files)\n * goes to project `name` with its URL unchanged, navigations included.\n *\n * With `hostPage`, a navigation that finds no tab running the project's shards (after\n * `hostWaitMs`, default 2 s, for one to link) is redirected there, with the URL it asked\n * for as `?next=`: the page that starts the project, which a person reopening the site\n * would otherwise never reach, as every path of the origin belongs to the project.\n */\n root?: { name: string; passthrough?: string[]; hostPage?: string; hostWaitMs?: number };\n },\n): { routers: Map<string, CellRouter> } {\n const sw = scope as unknown as ServiceWorkerGlobalScope;\n const base = opts.basePath.endsWith(\"/\") ? opts.basePath : `${opts.basePath}/`;\n // Held while this ServiceWorker lives: the browser releases it when it stops the worker, which\n // tells the shards that their links to it are dead (MessagePorts report nothing).\n const routerLock = `cellb:router:${crypto.randomUUID()}`;\n const lockHeld = new Promise<void>((held) => {\n void navigator.locks.request(routerLock, () => {\n held();\n return new Promise<void>(() => {});\n });\n });\n const routers = new Map<string, CellRouter>();\n const created = new Map<string, () => void>();\n const ready = new Map<string, Promise<void>>();\n\n let asking: Promise<void> | undefined;\n const askForLinks = (name?: string) => {\n asking ??= sw.clients\n .matchAll({ type: \"window\", includeUncontrolled: true })\n .then((cs) => {\n for (const c of cs) c.postMessage({ type: NEED_LINKS, name });\n })\n .finally(() => setTimeout(() => (asking = undefined), 500));\n return asking;\n };\n\n const routerFor = (name: string, shards: number, scriptName: string) => {\n let router = routers.get(name);\n if (!router) {\n router = new CellRouter({\n shards,\n bindings: {}, // shards name the owner of every internal request (x-cellb-shard)\n scriptName,\n attachTimeoutMs: opts.attachTimeoutMs ?? 15_000,\n onMissingShard: () => void askForLinks(name),\n // The owner worker's own lock: released when it closes, its tab closes or it dies.\n watchOwner: (_shard, ownerLock) =>\n navigator.locks.request(ownerLock as string, async () => {}),\n isAlive: async (_shard, ownerLock) =>\n !!(await navigator.locks.query()).held?.some((l) => l.name === ownerLock),\n livenessIntervalMs: 5_000,\n });\n routers.set(name, router);\n created.get(name)?.();\n }\n return router;\n };\n\n /** A project's router; after a ServiceWorker restart, once a tab has attached a shard again. */\n const routerNamed = (name: string): Promise<CellRouter> => {\n const r = routers.get(name);\n if (r) return Promise.resolve(r);\n let wait = ready.get(name);\n if (!wait) {\n wait = new Promise<void>((resolve, reject) => {\n const t = setTimeout(\n () => reject(new Error(`project ${name} has no shards in any tab`)),\n opts.attachTimeoutMs ?? 15_000,\n );\n created.set(name, () => (clearTimeout(t), resolve()));\n }).finally(() => (ready.delete(name), created.delete(name)));\n ready.set(name, wait);\n void askForLinks(name);\n }\n return wait.then(() => routers.get(name)!);\n };\n\n sw.addEventListener(\"message\", (e: ExtendableMessageEvent) => {\n const m = e.data;\n const reply = e.ports[0];\n if (m?.type === ATTACH)\n e.waitUntil(\n (async () => {\n try {\n const router = routerFor(m.name, m.shards, m.scriptName);\n await serveHandlerOverPort(m.up, router.internal);\n const link = await linkOverPort(m.down);\n router.attach(m.shard, link.send, m.ownerLock, m.ctl);\n await lockHeld;\n reply?.postMessage({ ok: true, routerLock });\n } catch (err) {\n reply?.postMessage({ ok: false, error: String((err as Error)?.message ?? err) });\n }\n })(),\n );\n else if (m?.type === DRAIN)\n e.waitUntil(\n (async () => {\n await routers.get(m.name)?.drain(m.shard, m.ownerLock, m.ms);\n reply?.postMessage({ ok: true });\n })(),\n );\n else if (m?.type === WAKE)\n // Started by this message: stay up until a tab has linked the project's shards again.\n e.waitUntil(routerNamed(m.name).catch(() => {}));\n else if (m?.type === SOCKET)\n e.waitUntil(\n (async () => {\n try {\n const { name, path } = split(new URL(m.url));\n const url = new URL(m.url);\n if (opts.stripBase && !atRoot(url)) url.pathname = path;\n const { port, protocol } = await (await routerNamed(name)).connectWebSocket(\n url.toString(),\n m.protocols ?? [],\n );\n reply.postMessage({ ok: true, port, protocol }, [port]);\n } catch (err) {\n reply.postMessage({ ok: false, error: String((err as Error)?.message ?? err) });\n }\n })(),\n );\n });\n\n /** Whether `url` goes to the root project (`opts.root`). */\n const atRoot = (url: URL) =>\n !!opts.root &&\n url.origin === sw.location.origin &&\n !url.pathname.startsWith(base) &&\n !(opts.root.passthrough ?? []).some((p) => url.pathname.startsWith(p));\n\n /** `<base><name>/<rest>` → the project's name and the Worker's path `/<rest>`. */\n const split = (url: URL) => {\n if (opts.root && atRoot(url)) return { name: opts.root.name, path: url.pathname };\n if (!url.pathname.startsWith(base)) throw new Error(`not a project URL: ${url}`);\n const rest = url.pathname.slice(base.length);\n const slash = rest.indexOf(\"/\");\n const name = decodeURIComponent(slash < 0 ? rest : rest.slice(0, slash));\n return { name, path: slash < 0 ? \"/\" : rest.slice(slash) };\n };\n\n sw.addEventListener(\"fetch\", (e: FetchEvent) => {\n const url = new URL(e.request.url);\n const root = atRoot(url);\n if (!root) {\n if (url.origin !== sw.location.origin || !url.pathname.startsWith(base)) return;\n if (e.request.mode === \"navigate\") return;\n }\n const { name, path } = split(url);\n if (!name) return;\n const req = e.request;\n const target = opts.stripBase && !root ? new URL(`${path}${url.search}`, url.origin) : url;\n const hostPage = root && req.mode === \"navigate\" ? opts.root?.hostPage : undefined;\n if (hostPage)\n e.respondWith(\n runningShards(name, opts.root?.hostWaitMs ?? 2_000).then((running) =>\n running\n ? serve(name, req, target)\n : Response.redirect(\n `${new URL(hostPage, url.origin).href}?next=${encodeURIComponent(`${url.pathname}${url.search}`)}`,\n 302,\n ),\n ),\n );\n else e.respondWith(serve(name, req, target));\n });\n\n /** Whether a tab runs project `name`'s shards, waiting up to `ms` for one to link. */\n const runningShards = async (name: string, ms: number): Promise<boolean> => {\n const live = () => (routers.get(name)?.attachedShards.length ?? 0) > 0;\n if (live()) return true;\n void askForLinks(name);\n for (const until = Date.now() + ms; Date.now() < until; ) {\n await new Promise((r) => setTimeout(r, 100));\n if (live()) return true;\n }\n return false;\n };\n\n /** A request to project `name`, with the page's cookies in and the Worker's out. */\n const serve = (name: string, req: Request, target: URL): Promise<Response> =>\n routerNamed(name).then(\n async (router) => {\n try {\n return await applyWorkerCookies(\n sw,\n await router.ingress(\n new Request(target, {\n method: req.method,\n headers: await withPageCookies(sw, req),\n body: req.body,\n signal: req.signal,\n duplex: \"half\",\n } as RequestInit),\n ),\n );\n } catch (err) {\n // A response the page can read, instead of a network error with no cause.\n console.error(`cellb: ${req.method} ${target.pathname} failed:`, err);\n return new Response(`cellb: ${(err as Error)?.stack ?? err}`, { status: 502 });\n }\n },\n (err) => new Response(String((err as Error)?.message ?? err), { status: 503 }),\n );\n\n return { routers };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"browser-tabs.js","sourceRoot":"","sources":["../../src/project/browser-tabs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One project shared by every tab of an origin, its shards balanced over the tabs.\n *\n * ServiceWorker: installProjectRouter(self, { basePath: \"/app/\" })\n * each page: joinProjectShards({ name, shards, spawn, config, code, serviceWorker })\n * shard worker: serveProjectShard({ sqlite3 })\n *\n * Requests to `<basePath><name>/…` reach the project's Worker through the ServiceWorker, whichever\n * tab sent them (with their URL unchanged, unless `stripBase`); a Durable Object call runs in the\n * shard that owns the object, whichever tab holds that shard.\n *\n * Each tab holds a presence lock; every tab computes the same owner for each shard from the tabs\n * present (`shardOwner`: an even split, oldest tabs first), takes the shards it should own (one\n * Web Lock each) and gives up the others. A shard's OPFS pool belongs to the origin, so its cells\n * move with it from tab to tab. A shard given up is drained first (its requests in flight\n * finish, new ones wait for the next owner); a tab that closes runs no code, so the requests in\n * flight in its shards fail (ShardOwnerLostError) and the other tabs take the shards over.\n */\nimport {\n type DispatchFailure,\n type HandlerInfo,\n LinkLostError,\n SwHttpAdapter,\n SwHttpDispatcher,\n type WorkerLink,\n} from \"@statewalker/webrun-http-browser/sw\";\nimport { ShardOwnerLostError } from \"../router/cell-router.js\";\nimport { errorResponse } from \"../transport/cells-http.js\";\nimport { H } from \"../transport/cells-link.js\";\nimport { SW_SOCKET } from \"../ws/sw-connector.js\";\nimport { applyWorkerCookies, withPageCookies } from \"./browser-cookies.js\";\nimport type { BrowserBucket, BrowserProjectOptions } from \"./browser-host.js\";\nimport { INTERNAL_ORIGIN, TAB_INIT, type TabShardInit } from \"./browser-shards.js\";\n\n/** The ServiceWorker asks a project's tabs to attach their shards again (a host page's wait). */\nconst NEED_LINKS = \"cellb:need-links\";\n/** A page opens a socket (`serviceWorkerConnector`). */\nconst SOCKET = SW_SOCKET;\n\n/**\n * The tab that should run `shard`: the tabs sorted by id (ids start with the time the tab joined),\n * shard i to tab i mod n — every tab runs ⌊shards/n⌋ or ⌈shards/n⌉ shards. A tab joining or\n * leaving moves the shards whose owner index changes; the oldest tabs keep theirs first.\n */\nexport function shardOwner(shard: number, tabs: string[]): string | undefined {\n if (!tabs.length) return undefined;\n const sorted = [...tabs].sort();\n return sorted[shard % sorted.length];\n}\n\nexport interface ProjectTabsOptions {\n /** The project's name in this origin: its URL path, locks and OPFS pools derive from it. */\n name: string;\n shards: number;\n /** Starts the worker of one shard; its script calls `serveProjectShard`. */\n spawn: (shard: number) => Worker;\n config: BrowserProjectOptions[\"config\"];\n code: BrowserProjectOptions[\"code\"];\n assetFiles?: BrowserProjectOptions[\"assetFiles\"];\n /** The OpenAI-compatible endpoint behind the project's `ai` binding. */\n ai?: BrowserProjectOptions[\"ai\"];\n /** Each shard is its own fleet node, `<node>-s<shard>`. */\n bucket?: BrowserBucket;\n /**\n * The active ServiceWorker that runs `installProjectRouter`. This page must be inside its scope\n * and controlled by it (webrun-http-browser's adapter asks the worker to claim the page if it\n * is not, and rejects with a `ServiceWorkerControlError` after 30 s if that fails: after a hard\n * reload, say).\n */\n serviceWorker: () => Promise<ServiceWorker>;\n /** Called whenever the shards this tab runs change. */\n onChange?: (owned: number[]) => void;\n /** How often the tab checks who is present (tabs that crash release their locks silently). */\n checkMs?: number;\n /**\n * A shard handed to another tab first lets its requests in flight finish, for up to this long\n * (default 5 s); new ones wait for the next owner.\n */\n drainMs?: number;\n}\n\nexport interface ProjectTabs {\n /** The shards this tab runs now. */\n owned(): number[];\n /** Ids of the tabs present (this one included). */\n tabs(): Promise<string[]>;\n /** Gives up every shard (closing its runtime first) and leaves. */\n close(): Promise<void>;\n}\n\ninterface Held {\n /** Aborts the wait for the lock, or ends the hold once running. */\n stop: AbortController;\n worker?: Worker;\n running?: Promise<void>;\n}\n\n/** A shard's handler id under the project's key: the router's `route` picks shards by it. */\nconst shardId = (shard: number) => `shard:${shard}`;\n\n/** The page side: this tab takes part in running the project's shards. */\nexport async function joinProjectShards(opts: ProjectTabsOptions): Promise<ProjectTabs> {\n const prefix = `cellb:${opts.name}`;\n const tabId = `${Date.now().toString(36).padStart(9, \"0\")}-${crypto.randomUUID()}`;\n const presenceLock = `${prefix}:tab:${tabId}`;\n let leave!: () => void;\n await new Promise<void>((held) => {\n void navigator.locks.request(presenceLock, () => {\n held();\n return new Promise<void>((r) => (leave = r));\n });\n });\n const channel = new BroadcastChannel(`${prefix}:tabs`);\n const held = new Map<number, Held>();\n const running = new Set<number>();\n let closed = false;\n\n const tabs = async (): Promise<string[]> => {\n const { held: locks = [] } = await navigator.locks.query();\n const ids = locks\n .map((l) => l.name ?? \"\")\n .filter((n) => n.startsWith(`${prefix}:tab:`))\n .map((n) => n.slice(`${prefix}:tab:`.length));\n return ids.includes(tabId) ? ids : [...ids, tabId];\n };\n\n const ask = (w: Worker, data: unknown, ports: MessagePort[] = []) =>\n new Promise<{\n ok: boolean;\n error?: string;\n unsupported?: string[];\n }>((resolve) => {\n const { port1, port2 } = new MessageChannel();\n port1.onmessage = (e) => {\n port1.close();\n resolve(e.data);\n };\n w.postMessage(data, [port2, ...ports]);\n });\n\n // The tab's side of the ServiceWorker's router (webrun-http-browser): it attaches this tab's\n // shard workers under the project's key, and attaches them again whenever the ServiceWorker\n // asks (after a restart). The page itself attaches no handler the router would pick.\n const worker = await opts.serviceWorker();\n // The given worker's registration, not `navigator.serviceWorker.ready` (that is this page's\n // scope's registration, and never settles for a page outside every scope).\n const registration = (await navigator.serviceWorker.getRegistrations()).find((r) =>\n [r.active, r.waiting, r.installing].includes(worker),\n );\n if (!registration)\n throw new Error(`cellb: no registration holds the ServiceWorker ${worker.scriptURL}`);\n const adapter = new SwHttpAdapter({\n key: opts.name,\n registration,\n id: `tab:${tabId}`,\n });\n await adapter.start();\n const links = new Map<number, WorkerLink>();\n\n const changed = () => opts.onChange?.([...running].sort((a, b) => a - b));\n\n /** Waits for the shard's lock, then runs its worker until `stop` is aborted. */\n const take = (shard: number) => {\n const h: Held = { stop: new AbortController() };\n held.set(shard, h);\n h.running = navigator.locks\n .request(`${prefix}:shard:${shard}`, { signal: h.stop.signal }, async () => {\n const w = opts.spawn(shard);\n h.worker = w;\n try {\n const started = await ask(w, {\n type: TAB_INIT,\n name: opts.name,\n shard,\n shards: opts.shards,\n config: opts.config,\n code: opts.code,\n assetFiles: opts.assetFiles,\n ai: opts.ai,\n bucket: opts.bucket && { ...opts.bucket, node: `${opts.bucket.node}-s${shard}` },\n pool: `${opts.name}-shard-${shard}`,\n filesDir: `${opts.name}-files-${shard}`,\n } satisfies TabShardInit);\n if (!started.ok) throw new Error(started.error);\n // What the configuration asks for and this runtime does not provide (a cron trigger\n // without a scheduled() handler, say): said once, not silently ignored.\n if (started.unsupported?.length)\n console.warn(\n `cellb: project ${opts.name} shard ${shard}: unsupported: ${started.unsupported.join(\"; \")}`,\n );\n if (!h.stop.signal.aborted) {\n links.set(\n shard,\n await adapter.attachWorker(w, {\n key: opts.name,\n id: shardId(shard),\n meta: { shard },\n }),\n );\n running.add(shard);\n changed();\n await new Promise<void>((r) =>\n h.stop.signal.aborted ? r() : h.stop.signal.addEventListener(\"abort\", () => r()),\n );\n }\n } finally {\n // A planned hand-over drains the shard's requests in flight first; then the runtime\n // closes (and the pool with it) before the lock goes to the next tab.\n const link = links.get(shard);\n links.delete(shard);\n if (link && running.has(shard))\n await link.drain({ ms: opts.drainMs ?? 5_000 }).catch(() => {});\n running.delete(shard);\n await ask(w, { type: \"cellb:shard-close\" }).catch(() => {});\n w.terminate();\n changed();\n }\n })\n .catch((err) => {\n // Aborted while waiting is expected; a shard that failed to start is reported.\n if (!h.stop.signal.aborted)\n console.error(\n `cellb: ${opts.name} shard ${shard} failed: ${(err as Error)?.message ?? err}`,\n );\n })\n .finally(() => {\n if (held.get(shard) === h) held.delete(shard);\n });\n };\n\n let balancing: Promise<void> | undefined;\n let again = false;\n /** Takes the shards this tab should own and gives up the others. */\n const balance = async () => {\n if (balancing) {\n again = true;\n return balancing;\n }\n balancing = (async () => {\n do {\n again = false;\n if (closed) return;\n const present = await tabs();\n // close() may have run while `tabs()` was answering: take nothing after it.\n if (closed) return;\n for (let shard = 0; shard < opts.shards; shard++) {\n const mine = shardOwner(shard, present) === tabId;\n const h = held.get(shard);\n if (mine && !h) take(shard);\n else if (!mine && h) h.stop.abort();\n }\n } while (again);\n })().finally(() => (balancing = undefined));\n return balancing;\n };\n\n channel.onmessage = () => void balance();\n const timer = setInterval(() => void balance(), opts.checkMs ?? 1000);\n const onSw = (e: MessageEvent) => {\n if (e.data?.type !== NEED_LINKS || (e.data.name && e.data.name !== opts.name)) return;\n void adapter.relink().catch(() => {});\n };\n navigator.serviceWorker.addEventListener(\"message\", onSw);\n channel.postMessage({ joined: tabId });\n await balance();\n\n return {\n owned: () => [...running].sort((a, b) => a - b),\n tabs,\n async close() {\n closed = true;\n clearInterval(timer);\n navigator.serviceWorker.removeEventListener(\"message\", onSw);\n const stopping = [...held.values()];\n for (const h of stopping) h.stop.abort();\n await Promise.all(stopping.map((h) => h.running));\n await adapter.stop().catch(() => {});\n leave();\n channel.postMessage({ left: tabId });\n channel.close();\n },\n };\n}\n\n/**\n * A ServiceWorker's global scope (its `self`), as `installProjectRouter` takes it: typed without\n * TypeScript's WebWorker lib, so that code importing cellb compiles with the DOM lib alone.\n */\nexport interface ServiceWorkerScope {\n readonly location: { readonly origin: string };\n readonly clients: unknown;\n addEventListener(type: string, listener: (event: never) => unknown): void;\n}\n\n/**\n * The ServiceWorker side: webrun-http-browser's dispatcher, with the project as its key and the\n * tabs' shard workers as its handlers. What cellb adds: its urls (`<basePath><name>/`, a root\n * project, `http://cellb.internal/<name>/` for calls between shards), which shard a call goes to,\n * the page's cookies, no control headers from pages, and the host page.\n */\nexport function installProjectRouter(\n scope: ServiceWorkerScope,\n opts: {\n /** Requests to `<basePath><name>/…` go to project `name`. */\n basePath: string;\n /**\n * The Worker sees the request's URL as the page sent it (default), as a Worker on a\n * Cloudflare route does: mount its routes under `<basePath><name>/`. With `stripBase`, it\n * sees `/<rest>` instead — URLs it builds from the request then lack the prefix.\n */\n stripBase?: boolean;\n attachTimeoutMs?: number;\n /**\n * A project served at the origin's root, for apps that build absolute paths (EmDash's\n * `/_emdash/…`, say) and so cannot live under `<basePath><n>/`. Every same-origin request\n * outside `basePath` and outside the `passthrough` path prefixes (the host page's own files)\n * goes to project `name` with its URL unchanged, navigations included.\n *\n * With `hostPage`, a navigation that finds no tab running the project's shards (after\n * `hostWaitMs`, default 2 s, for one to link) is redirected there, with the URL it asked\n * for as `?next=`: the page that starts the project, which a person reopening the site\n * would otherwise never reach, as every path of the origin belongs to the project.\n */\n root?: { name: string; passthrough?: string[]; hostPage?: string; hostWaitMs?: number };\n },\n): { dispatcher: SwHttpDispatcher } {\n const sw = scope as unknown as ServiceWorkerGlobalScope;\n const base = opts.basePath.endsWith(\"/\") ? opts.basePath : `${opts.basePath}/`;\n const internal = new URL(INTERNAL_ORIGIN);\n\n /** Whether `url` goes to the root project (`opts.root`). */\n const atRoot = (url: URL) =>\n !!opts.root &&\n url.origin === sw.location.origin &&\n !url.pathname.startsWith(base) &&\n !(opts.root.passthrough ?? []).some((p) => url.pathname.startsWith(p));\n\n /**\n * `<base><name>/<rest>` → the project's name, the Worker's path `/<rest>`, and `<base><name>` as\n * the URL spells it (`prefix`: what `stripBase` cuts, so a name the URL leaves unescaped, `a@b`,\n * or escapes where it need not, `%61pp`, is cut as written).\n */\n const split = (url: URL): { name: string; path: string; prefix: string } | undefined => {\n if (opts.root && atRoot(url)) return { name: opts.root.name, path: url.pathname, prefix: \"\" };\n if (url.origin !== sw.location.origin || !url.pathname.startsWith(base)) return undefined;\n const rest = url.pathname.slice(base.length);\n const slash = rest.indexOf(\"/\");\n const raw = slash < 0 ? rest : rest.slice(0, slash);\n const name = decodeURIComponent(raw);\n return name\n ? { name, path: slash < 0 ? \"/\" : rest.slice(slash), prefix: `${base}${raw}` }\n : undefined;\n };\n\n const isShard = (h: HandlerInfo) => typeof (h.meta as { shard?: unknown })?.shard === \"number\";\n let rr = 0;\n const dispatcher = new SwHttpDispatcher({\n self: sw,\n handleFetch: false,\n attachTimeoutMs: opts.attachTimeoutMs ?? 15_000,\n key: (url) => {\n // Calls between shards: http://cellb.internal/<name>/.\n if (url.host === internal.host) {\n const name = url.pathname.split(\"/\")[1];\n return name ? decodeURIComponent(name) : undefined;\n }\n return split(url)?.name;\n },\n // A call between shards names its shard (it waits for that one); the rest go to the shard\n // with the fewest requests in flight, round robin among equals. The tabs' pages are handlers\n // of the key too (their adapters'), with nothing to serve: never picked.\n route: (request, _key, handlers) => {\n const shards = handlers.filter(isShard);\n const named = request.headers.get(H.shard);\n if (named !== null)\n return shards.find((h) => (h.meta as { shard: number }).shard === Number(named));\n if (!shards.length) return undefined;\n const start = rr++ % shards.length;\n let best = shards[start];\n for (let i = 1; i < shards.length; i++) {\n const h = shards[(start + i) % shards.length];\n if (h.inflight < best.inflight) best = h;\n }\n return best;\n },\n // A call between shards fails as an error its caller rethrows (postVia); a page's request\n // gets a response it can read.\n failure: (request, failure) => failed(request, failure),\n // Pages may not address objects or shards directly: no control headers on their sockets.\n socketHeaders: (headers, from) =>\n from === \"page\"\n ? headers?.filter(([name]) => !name.toLowerCase().startsWith(\"x-cellb\"))\n : headers,\n });\n dispatcher.start();\n\n const failed = (request: Request, failure: DispatchFailure): Response => {\n const named = request.headers.get(H.shard);\n const what = named !== null ? `cell shard ${named}` : \"the project\";\n const error =\n failure.kind === \"lost\" && failure.error instanceof LinkLostError && named !== null\n ? new ShardOwnerLostError(Number(named))\n : failure.kind === \"timeout\"\n ? new Error(`${what} was not attached in time`)\n : failure.kind === \"gone\"\n ? new Error(`${what} has no tab running it`)\n : (failure.error as Error);\n if (request.headers.get(H.kind) !== null) return errorResponse(error, 503);\n return new Response(`cellb: ${(error as Error)?.message ?? error}`, { status: 503 });\n };\n\n const askForLinks = async (name: string) => {\n const clients = await sw.clients.matchAll({ type: \"window\", includeUncontrolled: true });\n for (const c of clients) c.postMessage({ type: NEED_LINKS, name });\n };\n\n /** Whether a tab runs project `name`'s shards, waiting up to `ms` for one to link. */\n const runningShards = async (name: string, ms: number): Promise<boolean> => {\n await dispatcher.ready;\n const live = () => dispatcher.table.live(name).some(isShard);\n if (live()) return true;\n void askForLinks(name);\n for (const until = Date.now() + ms; Date.now() < until; ) {\n await new Promise((r) => setTimeout(r, 100));\n if (live()) return true;\n }\n return false;\n };\n\n /** Headers for a request the router sends on: no control headers from the page, its base. */\n const routed = (req: Request, prefix: string, root: boolean): Headers => {\n // Headers only: a Request built from `req` would take its body.\n const headers = new Headers(req.headers);\n for (const h of Object.values(H)) if (h !== H.cookie) headers.delete(h);\n if (opts.stripBase && !root) headers.set(H.base, prefix);\n return headers;\n };\n\n /** A request to project `name`, with the page's cookies in and the Worker's out. */\n const serve = async (\n { name, prefix }: { name: string; prefix: string },\n req: Request,\n root: boolean,\n ): Promise<Response> => {\n try {\n // A project no tab has ever served is not the router's: the page's own request goes to the\n // network, as if there were no router (not the copy below, with the page's cookies in a\n // header and a streamed body).\n await dispatcher.ready;\n if (!dispatcher.serves(name)) return await fetch(req);\n const headers = await withPageCookies(\n sw,\n new Request(req.url, { headers: routed(req, prefix, root) }),\n );\n const res = await dispatcher.dispatch(\n new Request(req.url, {\n method: req.method,\n headers,\n body: req.body,\n signal: req.signal,\n duplex: \"half\",\n } as RequestInit),\n );\n return await applyWorkerCookies(sw, res);\n } catch (err) {\n // A response the page can read, instead of a network error with no cause.\n console.error(`cellb: ${req.method} ${new URL(req.url).pathname} failed:`, err);\n return new Response(`cellb: ${(err as Error)?.stack ?? err}`, { status: 502 });\n }\n };\n\n sw.addEventListener(\"fetch\", (e: FetchEvent) => {\n const req = e.request;\n const url = new URL(req.url);\n const root = atRoot(url);\n if (!root && req.mode === \"navigate\") return; // the pages under basePath are the host's\n const project = split(url);\n if (!project) return;\n const hostPage = root && req.mode === \"navigate\" ? opts.root?.hostPage : undefined;\n if (hostPage)\n e.respondWith(\n runningShards(project.name, opts.root?.hostWaitMs ?? 2_000).then((running) =>\n running\n ? serve(project, req, root)\n : Response.redirect(\n `${new URL(hostPage, url.origin).href}?next=${encodeURIComponent(`${url.pathname}${url.search}`)}`,\n 302,\n ),\n ),\n );\n else e.respondWith(serve(project, req, root));\n });\n\n // A page's socket (`serviceWorkerConnector`): routed as a fetch would be, without the page's\n // headers.\n sw.addEventListener(\"message\", (e: ExtendableMessageEvent) => {\n const m = e.data;\n if (m?.type !== SOCKET) return;\n const reply = e.ports[0];\n e.waitUntil(\n (async () => {\n try {\n const url = new URL(m.url);\n const httpUrl = new URL(url);\n if (httpUrl.protocol === \"ws:\") httpUrl.protocol = \"http:\";\n else if (httpUrl.protocol === \"wss:\") httpUrl.protocol = \"https:\";\n const project = split(httpUrl);\n if (!project) throw new Error(`not a project URL: ${m.url}`);\n const headers: [string, string][] = [];\n if (opts.stripBase && !atRoot(httpUrl)) headers.push([H.base, project.prefix]);\n const { port, protocol } = await dispatcher.connectWebSocket(\n `${httpUrl}`,\n m.protocols ?? [],\n headers,\n );\n reply.postMessage({ ok: true, port, protocol }, [port]);\n } catch (err) {\n reply.postMessage({ ok: false, error: String((err as Error)?.message ?? err) });\n }\n })(),\n );\n });\n\n return { dispatcher };\n}\n"]}
|
|
@@ -10,6 +10,12 @@ export interface CellsHttpOptions {
|
|
|
10
10
|
* upgrades cannot cross this link.
|
|
11
11
|
*/
|
|
12
12
|
onUpgrade?: (id: string, port: MessagePort, protocol?: string) => void;
|
|
13
|
+
/**
|
|
14
|
+
* A stub.fetch answered 101 with a WebSocket, and the transport carries ports itself (a
|
|
15
|
+
* ServiceWorker's sockets): the 101, its `webSocket` included, is returned as it is, for the
|
|
16
|
+
* transport to hand the client end over.
|
|
17
|
+
*/
|
|
18
|
+
passUpgrades?: boolean;
|
|
13
19
|
}
|
|
14
20
|
/** Server side: exposes a runtime as one HTTP handler. */
|
|
15
21
|
export declare function errorResponse(e: unknown, status?: number): Response;
|
|
@@ -26,7 +26,7 @@ export function errorResponse(e, status = 500) {
|
|
|
26
26
|
export function cellsHttpHandler(runtime, opts = {}) {
|
|
27
27
|
const codec = opts.codec ?? v8Codec; // the runtime's default: V8 serialization
|
|
28
28
|
return async (incoming) => {
|
|
29
|
-
const req = await restoreScheme(incoming);
|
|
29
|
+
const req = await restoreScheme(await restoreRoute(incoming));
|
|
30
30
|
const kind = req.headers.get(H.kind);
|
|
31
31
|
if (!kind) {
|
|
32
32
|
const cookie = req.headers.get(H.cookie);
|
|
@@ -59,6 +59,8 @@ export function cellsHttpHandler(runtime, opts = {}) {
|
|
|
59
59
|
const res = (await stub.fetch(inner));
|
|
60
60
|
if (res.status !== 101 || !res.webSocket)
|
|
61
61
|
return res;
|
|
62
|
+
if (opts.passUpgrades && !wsId)
|
|
63
|
+
return res;
|
|
62
64
|
if (!wsId || !opts.onUpgrade)
|
|
63
65
|
throw new Error("WebSocket upgrade cannot cross this link (no control channel)");
|
|
64
66
|
const protocol = res.headers.get("Sec-WebSocket-Protocol") ?? undefined;
|
|
@@ -106,6 +108,25 @@ export async function remoteRuntime(send, opts = {}) {
|
|
|
106
108
|
env[binding] = new DurableObjectNamespace(keys?.[binding] ?? className, (id) => () => httpEndpoint(post, codec, binding, className, id));
|
|
107
109
|
return { env, fetch: (req) => send(withScheme(req)) };
|
|
108
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The url the request was meant for, where a router changed it to route it: a call between
|
|
113
|
+
* shards carries its own (`H.url`), and with stripBase the router's prefix (`H.base`) comes off.
|
|
114
|
+
*/
|
|
115
|
+
async function restoreRoute(req) {
|
|
116
|
+
const original = req.headers.get(H.url);
|
|
117
|
+
if (original !== null)
|
|
118
|
+
return keepUpgrade(req, await withUrl(req, new URL(original)));
|
|
119
|
+
const base = req.headers.get(H.base);
|
|
120
|
+
if (base === null)
|
|
121
|
+
return req;
|
|
122
|
+
const url = new URL(req.url);
|
|
123
|
+
if (!url.pathname.startsWith(base))
|
|
124
|
+
return req;
|
|
125
|
+
url.pathname = url.pathname.slice(base.length) || "/";
|
|
126
|
+
if (!url.pathname.startsWith("/"))
|
|
127
|
+
url.pathname = `/${url.pathname}`;
|
|
128
|
+
return keepUpgrade(req, await withUrl(req, url));
|
|
129
|
+
}
|
|
109
130
|
async function restoreScheme(req) {
|
|
110
131
|
const proto = req.headers.get(H.proto);
|
|
111
132
|
if (!proto)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cells-http.js","sourceRoot":"","sources":["../../src/transport/cells-http.ts"],"names":[],"mappings":"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","sourcesContent":["import { serializeError } from \"@statewalker/webrun-streams\";\nimport { toAnotherNode } from \"../core/handles.js\";\nimport { DurableObjectNamespace, type DurableObjectStub } from \"../core/namespace.js\";\nimport { withUrl } from \"../core/request-body.js\";\nimport type { CellRuntime } from \"../core/runtime.js\";\nimport type { ValueCodec } from \"../ports.js\";\nimport { v8Codec } from \"../v8/codec.js\";\nimport { type CfWebSocket, takeClientPort } from \"../ws/server.js\";\nimport { withHeaders, withUpgrade } from \"../ws/upgrade.js\";\nimport {\n control,\n H,\n httpEndpoint,\n keepUpgrade,\n postVia,\n type Send,\n withScheme,\n} from \"./cells-link.js\";\n\n/**\n * Everything a remote caller can do with a cell host is expressed as HTTP:\n * worker ingress, `stub.fetch()`, JS-RPC calls and the bindings descriptor.\n * The resulting `(Request) ⇒ Response` handler can then ride ANY webrun-wire\n * transport: a MessagePort (webrun-rpc), a WebSocket (webrun-streams-ws),\n * WebRTC / libp2p peers (httpeers) — via webrun-http-streams' Duplex seam.\n *\n * Control headers mark internal requests. TRUST MODEL: this handler is for an\n * internal link (page ⇄ worker, owner ⇄ forwarder). Public ingress must call\n * `runtime.fetch` directly, never this handler, or clients could address\n * objects without going through the Worker's own routing.\n */\nconst CT_VALUES = \"application/x-cellb-values\";\n\nexport interface CellsHttpOptions {\n codec?: ValueCodec;\n /**\n * A stub.fetch answered 101 with a WebSocket: hand the client end's port to the\n * caller out of band (the HTTP link cannot carry ports). Without it, WebSocket\n * upgrades cannot cross this link.\n */\n onUpgrade?: (id: string, port: MessagePort, protocol?: string) => void;\n}\n\n/** Server side: exposes a runtime as one HTTP handler. */\nexport function errorResponse(e: unknown, status = 500): Response {\n return Response.json(serializeError(e), { status, headers: { [H.kind]: \"error\" } });\n}\n\nexport function cellsHttpHandler(\n runtime: CellRuntime,\n opts: CellsHttpOptions = {},\n): (req: Request) => Promise<Response> {\n const codec = opts.codec ?? v8Codec; // the runtime's default: V8 serialization\n return async (incoming) => {\n const req = await restoreScheme(incoming);\n const kind = req.headers.get(H.kind);\n if (!kind) {\n const cookie = req.headers.get(H.cookie);\n if (cookie === null) return runtime.fetch(stripControl(req));\n // From a ServiceWorker: the cookies in, the Worker's Set-Cookie out, under H.cookie and\n // H.setCookie (a browser drops both headers from every Request and Response it builds).\n const inner = cookie ? withHeaders(stripControl(req), { cookie }) : stripControl(req);\n return tunnelSetCookies(await runtime.fetch(inner));\n }\n try {\n if (kind === \"describe\")\n return Response.json({\n bindings: runtime.durableObjectBindings,\n // The far side derives the same ids only with the same namespace keys.\n keys: Object.fromEntries(\n Object.keys(runtime.durableObjectBindings).map((b) => [\n b,\n (runtime.env[b] as { namespaceKey?: string }).namespaceKey,\n ]),\n ),\n });\n const stub = stubOf(\n runtime,\n req.headers.get(H.binding) ?? \"\",\n req.headers.get(H.target) ?? \"\",\n );\n if (kind === \"fetch\") {\n const wsId = req.headers.get(H.ws);\n let inner = stripControl(req);\n const upgrade = req.headers.get(H.upgrade);\n if (upgrade) {\n const offered = req.headers.get(H.wsProtocol);\n inner = withUpgrade(\n inner,\n upgrade,\n offered ? { \"Sec-WebSocket-Protocol\": offered } : undefined,\n );\n }\n const res = (await stub.fetch(inner)) as Response & { webSocket?: CfWebSocket | null };\n if (res.status !== 101 || !res.webSocket) return res;\n if (!wsId || !opts.onUpgrade)\n throw new Error(\"WebSocket upgrade cannot cross this link (no control channel)\");\n const protocol = res.headers.get(\"Sec-WebSocket-Protocol\") ?? undefined;\n opts.onUpgrade(wsId, takeClientPort(res.webSocket), protocol);\n return new Response(null, {\n status: 200,\n headers: {\n [H.wsAccepted]: wsId,\n ...(protocol ? { \"Sec-WebSocket-Protocol\": protocol } : {}),\n },\n });\n }\n if (kind === \"rpc\") {\n const args = codec.decode(new Uint8Array(await req.arrayBuffer())) as unknown[];\n const result = await stub[req.headers.get(H.method) ?? \"\"](...args);\n return new Response(codec.encode(toAnotherNode(result)) as BodyInit, {\n headers: { \"content-type\": CT_VALUES },\n });\n }\n throw new Error(`Unknown cellb request kind \"${kind}\"`);\n } catch (e) {\n return errorResponse(e);\n }\n };\n}\n\nfunction stubOf(runtime: CellRuntime, binding: string, target: string): DurableObjectStub {\n const ns = runtime.env[binding];\n if (!(ns instanceof DurableObjectNamespace))\n throw new Error(`Unknown Durable Object binding \"${binding}\"`);\n if (target.startsWith(\"name:\")) return ns.getByName(target.slice(5));\n if (target.startsWith(\"id:\")) return ns.get(ns.idFromString(target.slice(3)));\n throw new Error(`Invalid Durable Object target \"${target}\"`);\n}\n\nexport interface RemoteRuntime {\n /** Ingress into the remote Worker's default fetch handler. */\n fetch(req: Request): Promise<Response>;\n /** Namespaces whose stubs send HTTP over the link (idFromName computed locally). */\n readonly env: Record<string, DurableObjectNamespace>;\n}\n\n/** Client side over any `send` (fetchOverDuplex over a port / WebSocket / peer, or the handler itself). */\nexport async function remoteRuntime(\n send: Send,\n opts: CellsHttpOptions = {},\n): Promise<RemoteRuntime> {\n const codec = opts.codec ?? v8Codec; // the runtime's default: V8 serialization\n const post = postVia(send);\n const described = await post(control(\"describe\", {}));\n const { bindings, keys } = (await described.json()) as {\n bindings: Record<string, string>;\n keys?: Record<string, string>;\n };\n const env: Record<string, DurableObjectNamespace> = {};\n for (const [binding, className] of Object.entries(bindings))\n env[binding] = new DurableObjectNamespace(\n keys?.[binding] ?? className,\n (id) => () => httpEndpoint(post, codec, binding, className, id),\n );\n return { env, fetch: (req) => send(withScheme(req)) };\n}\n\nasync function restoreScheme(req: Request): Promise<Request> {\n const proto = req.headers.get(H.proto);\n if (!proto) return req;\n const url = new URL(req.url);\n if (url.protocol === `${proto}:`) return req;\n url.protocol = `${proto}:`;\n return keepUpgrade(req, await withUrl(req, url));\n}\n\n/** The Set-Cookie values of a response as one H.setCookie header (status 101 left as it is). */\nfunction tunnelSetCookies(res: Response): Response {\n const cookies = res.status === 101 ? [] : (res.headers.getSetCookie?.() ?? []);\n if (!cookies.length) return res;\n const headers = new Headers();\n for (const [k, v] of res.headers) if (k !== \"set-cookie\") headers.append(k, v);\n headers.set(H.setCookie, cookies.map(encodeURIComponent).join(\",\"));\n return new Response(res.body, { status: res.status, statusText: res.statusText, headers });\n}\n\nexport function stripControl(req: Request, keep: string[] = []): Request {\n const headers = new Headers(req.headers);\n let changed = false;\n for (const h of Object.values(H))\n if (headers.has(h) && !keep.includes(h)) headers.delete(h), (changed = true);\n if (!changed) return req;\n return keepUpgrade(req, new Request(req, { headers })); // keeps the body in Firefox too (addHeaders)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cells-http.js","sourceRoot":"","sources":["../../src/transport/cells-http.ts"],"names":[],"mappings":"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","sourcesContent":["import { serializeError } from \"@statewalker/webrun-streams\";\nimport { toAnotherNode } from \"../core/handles.js\";\nimport { DurableObjectNamespace, type DurableObjectStub } from \"../core/namespace.js\";\nimport { withUrl } from \"../core/request-body.js\";\nimport type { CellRuntime } from \"../core/runtime.js\";\nimport type { ValueCodec } from \"../ports.js\";\nimport { v8Codec } from \"../v8/codec.js\";\nimport { type CfWebSocket, takeClientPort } from \"../ws/server.js\";\nimport { withHeaders, withUpgrade } from \"../ws/upgrade.js\";\nimport {\n control,\n H,\n httpEndpoint,\n keepUpgrade,\n postVia,\n type Send,\n withScheme,\n} from \"./cells-link.js\";\n\n/**\n * Everything a remote caller can do with a cell host is expressed as HTTP:\n * worker ingress, `stub.fetch()`, JS-RPC calls and the bindings descriptor.\n * The resulting `(Request) ⇒ Response` handler can then ride ANY webrun-wire\n * transport: a MessagePort (webrun-rpc), a WebSocket (webrun-streams-ws),\n * WebRTC / libp2p peers (httpeers) — via webrun-http-streams' Duplex seam.\n *\n * Control headers mark internal requests. TRUST MODEL: this handler is for an\n * internal link (page ⇄ worker, owner ⇄ forwarder). Public ingress must call\n * `runtime.fetch` directly, never this handler, or clients could address\n * objects without going through the Worker's own routing.\n */\nconst CT_VALUES = \"application/x-cellb-values\";\n\nexport interface CellsHttpOptions {\n codec?: ValueCodec;\n /**\n * A stub.fetch answered 101 with a WebSocket: hand the client end's port to the\n * caller out of band (the HTTP link cannot carry ports). Without it, WebSocket\n * upgrades cannot cross this link.\n */\n onUpgrade?: (id: string, port: MessagePort, protocol?: string) => void;\n /**\n * A stub.fetch answered 101 with a WebSocket, and the transport carries ports itself (a\n * ServiceWorker's sockets): the 101, its `webSocket` included, is returned as it is, for the\n * transport to hand the client end over.\n */\n passUpgrades?: boolean;\n}\n\n/** Server side: exposes a runtime as one HTTP handler. */\nexport function errorResponse(e: unknown, status = 500): Response {\n return Response.json(serializeError(e), { status, headers: { [H.kind]: \"error\" } });\n}\n\nexport function cellsHttpHandler(\n runtime: CellRuntime,\n opts: CellsHttpOptions = {},\n): (req: Request) => Promise<Response> {\n const codec = opts.codec ?? v8Codec; // the runtime's default: V8 serialization\n return async (incoming) => {\n const req = await restoreScheme(await restoreRoute(incoming));\n const kind = req.headers.get(H.kind);\n if (!kind) {\n const cookie = req.headers.get(H.cookie);\n if (cookie === null) return runtime.fetch(stripControl(req));\n // From a ServiceWorker: the cookies in, the Worker's Set-Cookie out, under H.cookie and\n // H.setCookie (a browser drops both headers from every Request and Response it builds).\n const inner = cookie ? withHeaders(stripControl(req), { cookie }) : stripControl(req);\n return tunnelSetCookies(await runtime.fetch(inner));\n }\n try {\n if (kind === \"describe\")\n return Response.json({\n bindings: runtime.durableObjectBindings,\n // The far side derives the same ids only with the same namespace keys.\n keys: Object.fromEntries(\n Object.keys(runtime.durableObjectBindings).map((b) => [\n b,\n (runtime.env[b] as { namespaceKey?: string }).namespaceKey,\n ]),\n ),\n });\n const stub = stubOf(\n runtime,\n req.headers.get(H.binding) ?? \"\",\n req.headers.get(H.target) ?? \"\",\n );\n if (kind === \"fetch\") {\n const wsId = req.headers.get(H.ws);\n let inner = stripControl(req);\n const upgrade = req.headers.get(H.upgrade);\n if (upgrade) {\n const offered = req.headers.get(H.wsProtocol);\n inner = withUpgrade(\n inner,\n upgrade,\n offered ? { \"Sec-WebSocket-Protocol\": offered } : undefined,\n );\n }\n const res = (await stub.fetch(inner)) as Response & { webSocket?: CfWebSocket | null };\n if (res.status !== 101 || !res.webSocket) return res;\n if (opts.passUpgrades && !wsId) return res;\n if (!wsId || !opts.onUpgrade)\n throw new Error(\"WebSocket upgrade cannot cross this link (no control channel)\");\n const protocol = res.headers.get(\"Sec-WebSocket-Protocol\") ?? undefined;\n opts.onUpgrade(wsId, takeClientPort(res.webSocket), protocol);\n return new Response(null, {\n status: 200,\n headers: {\n [H.wsAccepted]: wsId,\n ...(protocol ? { \"Sec-WebSocket-Protocol\": protocol } : {}),\n },\n });\n }\n if (kind === \"rpc\") {\n const args = codec.decode(new Uint8Array(await req.arrayBuffer())) as unknown[];\n const result = await stub[req.headers.get(H.method) ?? \"\"](...args);\n return new Response(codec.encode(toAnotherNode(result)) as BodyInit, {\n headers: { \"content-type\": CT_VALUES },\n });\n }\n throw new Error(`Unknown cellb request kind \"${kind}\"`);\n } catch (e) {\n return errorResponse(e);\n }\n };\n}\n\nfunction stubOf(runtime: CellRuntime, binding: string, target: string): DurableObjectStub {\n const ns = runtime.env[binding];\n if (!(ns instanceof DurableObjectNamespace))\n throw new Error(`Unknown Durable Object binding \"${binding}\"`);\n if (target.startsWith(\"name:\")) return ns.getByName(target.slice(5));\n if (target.startsWith(\"id:\")) return ns.get(ns.idFromString(target.slice(3)));\n throw new Error(`Invalid Durable Object target \"${target}\"`);\n}\n\nexport interface RemoteRuntime {\n /** Ingress into the remote Worker's default fetch handler. */\n fetch(req: Request): Promise<Response>;\n /** Namespaces whose stubs send HTTP over the link (idFromName computed locally). */\n readonly env: Record<string, DurableObjectNamespace>;\n}\n\n/** Client side over any `send` (fetchOverDuplex over a port / WebSocket / peer, or the handler itself). */\nexport async function remoteRuntime(\n send: Send,\n opts: CellsHttpOptions = {},\n): Promise<RemoteRuntime> {\n const codec = opts.codec ?? v8Codec; // the runtime's default: V8 serialization\n const post = postVia(send);\n const described = await post(control(\"describe\", {}));\n const { bindings, keys } = (await described.json()) as {\n bindings: Record<string, string>;\n keys?: Record<string, string>;\n };\n const env: Record<string, DurableObjectNamespace> = {};\n for (const [binding, className] of Object.entries(bindings))\n env[binding] = new DurableObjectNamespace(\n keys?.[binding] ?? className,\n (id) => () => httpEndpoint(post, codec, binding, className, id),\n );\n return { env, fetch: (req) => send(withScheme(req)) };\n}\n\n/**\n * The url the request was meant for, where a router changed it to route it: a call between\n * shards carries its own (`H.url`), and with stripBase the router's prefix (`H.base`) comes off.\n */\nasync function restoreRoute(req: Request): Promise<Request> {\n const original = req.headers.get(H.url);\n if (original !== null) return keepUpgrade(req, await withUrl(req, new URL(original)));\n const base = req.headers.get(H.base);\n if (base === null) return req;\n const url = new URL(req.url);\n if (!url.pathname.startsWith(base)) return req;\n url.pathname = url.pathname.slice(base.length) || \"/\";\n if (!url.pathname.startsWith(\"/\")) url.pathname = `/${url.pathname}`;\n return keepUpgrade(req, await withUrl(req, url));\n}\n\nasync function restoreScheme(req: Request): Promise<Request> {\n const proto = req.headers.get(H.proto);\n if (!proto) return req;\n const url = new URL(req.url);\n if (url.protocol === `${proto}:`) return req;\n url.protocol = `${proto}:`;\n return keepUpgrade(req, await withUrl(req, url));\n}\n\n/** The Set-Cookie values of a response as one H.setCookie header (status 101 left as it is). */\nfunction tunnelSetCookies(res: Response): Response {\n const cookies = res.status === 101 ? [] : (res.headers.getSetCookie?.() ?? []);\n if (!cookies.length) return res;\n const headers = new Headers();\n for (const [k, v] of res.headers) if (k !== \"set-cookie\") headers.append(k, v);\n headers.set(H.setCookie, cookies.map(encodeURIComponent).join(\",\"));\n return new Response(res.body, { status: res.status, statusText: res.statusText, headers });\n}\n\nexport function stripControl(req: Request, keep: string[] = []): Request {\n const headers = new Headers(req.headers);\n let changed = false;\n for (const h of Object.values(H))\n if (headers.has(h) && !keep.includes(h)) headers.delete(h), (changed = true);\n if (!changed) return req;\n return keepUpgrade(req, new Request(req, { headers })); // keeps the body in Firefox too (addHeaders)\n}\n"]}
|
|
@@ -13,6 +13,8 @@ export declare const H: {
|
|
|
13
13
|
readonly upgrade: "x-cellb-upgrade";
|
|
14
14
|
readonly wsProtocol: "x-cellb-ws-protocol";
|
|
15
15
|
readonly shard: "x-cellb-shard";
|
|
16
|
+
readonly url: "x-cellb-url";
|
|
17
|
+
readonly base: "x-cellb-base";
|
|
16
18
|
readonly cookie: "x-cellb-cookie";
|
|
17
19
|
readonly setCookie: "x-cellb-set-cookie";
|
|
18
20
|
};
|
|
@@ -13,6 +13,11 @@ export const H = {
|
|
|
13
13
|
upgrade: "x-cellb-upgrade", // `Upgrade` is hop-by-hop: dropped by the HTTP/1.1 wire, carried here
|
|
14
14
|
wsProtocol: "x-cellb-ws-protocol", // `Sec-WebSocket-Protocol` is forbidden in browser Requests
|
|
15
15
|
shard: "x-cellb-shard", // the shard that owns the target, as the sending shard computed it
|
|
16
|
+
// A call between shards through a ServiceWorker goes to `http://cellb.internal/<project>/`
|
|
17
|
+
// (the router's key for it): the url the caller asked for travels here.
|
|
18
|
+
url: "x-cellb-url",
|
|
19
|
+
// stripBase: the prefix the router keeps in the url (its key) for the Worker to cut off.
|
|
20
|
+
base: "x-cellb-base",
|
|
16
21
|
// Ingress from a browser (a ServiceWorker): `Cookie` is forbidden in browser Requests and
|
|
17
22
|
// `Set-Cookie` in browser Responses, so the page's cookies travel here, and the Worker's
|
|
18
23
|
// Set-Cookie values come back URI-encoded and comma-separated (browser-cookies.ts).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cells-link.js","sourceRoot":"","sources":["../../src/transport/cells-link.ts"],"names":[],"mappings":"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","sourcesContent":["import { deserializeError } from \"@statewalker/webrun-streams\";\nimport { toAnotherNode } from \"../core/handles.js\";\nimport type { DurableObjectId } from \"../core/id.js\";\nimport type { ObjectEndpoint } from \"../core/namespace.js\";\nimport type { ValueCodec } from \"../ports.js\";\nimport { carryWebSocketHeaders } from \"../ws/upgrade.js\";\n\n/** Control headers that mark internal requests on a cells-http link (see cells-http.ts). */\nexport const H = {\n kind: \"x-cellb\", // \"describe\" | \"fetch\" | \"rpc\" | \"error\" (response)\n binding: \"x-cellb-binding\",\n target: \"x-cellb-target\", // \"name:<n>\" | \"id:<hex>\"\n method: \"x-cellb-method\",\n proto: \"x-cellb-proto\", // original scheme (the HTTP/1.1 wire drops it)\n ws: \"x-cellb-ws\", // request: id under which the caller awaits a WebSocket port\n wsAccepted: \"x-cellb-ws-accepted\", // response: the port was handed over (control channel)\n upgrade: \"x-cellb-upgrade\", // `Upgrade` is hop-by-hop: dropped by the HTTP/1.1 wire, carried here\n wsProtocol: \"x-cellb-ws-protocol\", // `Sec-WebSocket-Protocol` is forbidden in browser Requests\n shard: \"x-cellb-shard\", // the shard that owns the target, as the sending shard computed it\n // Ingress from a browser (a ServiceWorker): `Cookie` is forbidden in browser Requests and\n // `Set-Cookie` in browser Responses, so the page's cookies travel here, and the Worker's\n // Set-Cookie values come back URI-encoded and comma-separated (browser-cookies.ts).\n cookie: \"x-cellb-cookie\",\n setCookie: \"x-cellb-set-cookie\",\n} as const;\n\nexport type Send = (req: Request) => Promise<Response>;\n\n/** A `send` that carries the scheme and turns `x-cellb: error` responses back into exceptions. */\nexport function postVia(send: Send): Send {\n return async (req) => {\n const res = await send(withScheme(req));\n if (res.headers.get(H.kind) === \"error\") throw deserializeError(await res.json());\n return res;\n };\n}\n\n/** Stub endpoint whose object lives behind `post` (another worker, tab or peer). */\nexport function httpEndpoint(\n post: Send,\n codec: ValueCodec,\n binding: string,\n className: string,\n id: DurableObjectId,\n): ObjectEndpoint {\n // By name when the id came from one (only idFromName() sets `name`): keeps ctx.id.name\n // on the far side, which derives the same id with the same namespace key.\n const target = id.name !== undefined ? `name:${id.name}` : `id:${id}`;\n const base = { [H.binding]: binding, [H.target]: target };\n return {\n // Not the caller's abort signal: an aborted caller does not cancel a local object's\n // fetch() either (the object answers, nobody reads), so neither does a remote one.\n fetch: (req) => post(addHeaders(req, { [H.kind]: \"fetch\", ...base }, { detach: true })),\n async call(method, args) {\n const res = await post(\n control(\"rpc\", { ...base, [H.method]: method }, codec.encode(toAnotherNode(args))),\n );\n return codec.decode(new Uint8Array(await res.arrayBuffer()));\n },\n };\n}\n\nexport function control(kind: string, headers: Record<string, string>, body?: Uint8Array): Request {\n return new Request(\"http://cellb.internal/\", {\n method: body ? \"POST\" : \"GET\",\n headers: { [H.kind]: kind, ...headers },\n body: body as BodyInit | undefined,\n });\n}\n\n/**\n * Re-creates a request with extra headers, streaming its body through. The request itself is the\n * source, not `body: req.body`: Firefox has no `Request.body`, so that would drop the body there.\n */\nfunction addHeaders(\n req: Request,\n extra: Record<string, string>,\n opts: { detach?: boolean } = {},\n): Request {\n const headers = new Headers(req.headers);\n for (const [k, v] of Object.entries(extra)) headers.set(k, v);\n return keepUpgrade(req, new Request(req, opts.detach ? { headers, signal: null } : { headers }));\n}\n\n/** Browsers drop the forbidden `Upgrade` header from every rebuilt Request: carry it over. */\nexport function keepUpgrade(from: Request, to: Request): Request {\n return carryWebSocketHeaders(from.headers, to);\n}\n\nexport function withScheme(req: Request): Request {\n return addHeaders(req, { [H.proto]: new URL(req.url).protocol.replace(\":\", \"\") });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cells-link.js","sourceRoot":"","sources":["../../src/transport/cells-link.ts"],"names":[],"mappings":"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","sourcesContent":["import { deserializeError } from \"@statewalker/webrun-streams\";\nimport { toAnotherNode } from \"../core/handles.js\";\nimport type { DurableObjectId } from \"../core/id.js\";\nimport type { ObjectEndpoint } from \"../core/namespace.js\";\nimport type { ValueCodec } from \"../ports.js\";\nimport { carryWebSocketHeaders } from \"../ws/upgrade.js\";\n\n/** Control headers that mark internal requests on a cells-http link (see cells-http.ts). */\nexport const H = {\n kind: \"x-cellb\", // \"describe\" | \"fetch\" | \"rpc\" | \"error\" (response)\n binding: \"x-cellb-binding\",\n target: \"x-cellb-target\", // \"name:<n>\" | \"id:<hex>\"\n method: \"x-cellb-method\",\n proto: \"x-cellb-proto\", // original scheme (the HTTP/1.1 wire drops it)\n ws: \"x-cellb-ws\", // request: id under which the caller awaits a WebSocket port\n wsAccepted: \"x-cellb-ws-accepted\", // response: the port was handed over (control channel)\n upgrade: \"x-cellb-upgrade\", // `Upgrade` is hop-by-hop: dropped by the HTTP/1.1 wire, carried here\n wsProtocol: \"x-cellb-ws-protocol\", // `Sec-WebSocket-Protocol` is forbidden in browser Requests\n shard: \"x-cellb-shard\", // the shard that owns the target, as the sending shard computed it\n // A call between shards through a ServiceWorker goes to `http://cellb.internal/<project>/`\n // (the router's key for it): the url the caller asked for travels here.\n url: \"x-cellb-url\",\n // stripBase: the prefix the router keeps in the url (its key) for the Worker to cut off.\n base: \"x-cellb-base\",\n // Ingress from a browser (a ServiceWorker): `Cookie` is forbidden in browser Requests and\n // `Set-Cookie` in browser Responses, so the page's cookies travel here, and the Worker's\n // Set-Cookie values come back URI-encoded and comma-separated (browser-cookies.ts).\n cookie: \"x-cellb-cookie\",\n setCookie: \"x-cellb-set-cookie\",\n} as const;\n\nexport type Send = (req: Request) => Promise<Response>;\n\n/** A `send` that carries the scheme and turns `x-cellb: error` responses back into exceptions. */\nexport function postVia(send: Send): Send {\n return async (req) => {\n const res = await send(withScheme(req));\n if (res.headers.get(H.kind) === \"error\") throw deserializeError(await res.json());\n return res;\n };\n}\n\n/** Stub endpoint whose object lives behind `post` (another worker, tab or peer). */\nexport function httpEndpoint(\n post: Send,\n codec: ValueCodec,\n binding: string,\n className: string,\n id: DurableObjectId,\n): ObjectEndpoint {\n // By name when the id came from one (only idFromName() sets `name`): keeps ctx.id.name\n // on the far side, which derives the same id with the same namespace key.\n const target = id.name !== undefined ? `name:${id.name}` : `id:${id}`;\n const base = { [H.binding]: binding, [H.target]: target };\n return {\n // Not the caller's abort signal: an aborted caller does not cancel a local object's\n // fetch() either (the object answers, nobody reads), so neither does a remote one.\n fetch: (req) => post(addHeaders(req, { [H.kind]: \"fetch\", ...base }, { detach: true })),\n async call(method, args) {\n const res = await post(\n control(\"rpc\", { ...base, [H.method]: method }, codec.encode(toAnotherNode(args))),\n );\n return codec.decode(new Uint8Array(await res.arrayBuffer()));\n },\n };\n}\n\nexport function control(kind: string, headers: Record<string, string>, body?: Uint8Array): Request {\n return new Request(\"http://cellb.internal/\", {\n method: body ? \"POST\" : \"GET\",\n headers: { [H.kind]: kind, ...headers },\n body: body as BodyInit | undefined,\n });\n}\n\n/**\n * Re-creates a request with extra headers, streaming its body through. The request itself is the\n * source, not `body: req.body`: Firefox has no `Request.body`, so that would drop the body there.\n */\nfunction addHeaders(\n req: Request,\n extra: Record<string, string>,\n opts: { detach?: boolean } = {},\n): Request {\n const headers = new Headers(req.headers);\n for (const [k, v] of Object.entries(extra)) headers.set(k, v);\n return keepUpgrade(req, new Request(req, opts.detach ? { headers, signal: null } : { headers }));\n}\n\n/** Browsers drop the forbidden `Upgrade` header from every rebuilt Request: carry it over. */\nexport function keepUpgrade(from: Request, to: Request): Request {\n return carryWebSocketHeaders(from.headers, to);\n}\n\nexport function withScheme(req: Request): Request {\n return addHeaders(req, { [H.proto]: new URL(req.url).protocol.replace(\":\", \"\") });\n}\n"]}
|
package/dist/transport/port.js
CHANGED
|
@@ -6,6 +6,10 @@ import { withScheme } from "./cells-link.js";
|
|
|
6
6
|
* (p31-port-leak.test.ts). 4096 per link, cellb's ceiling before the links were the package's (its
|
|
7
7
|
* default is 1024): a streaming response holds its port for its whole life.
|
|
8
8
|
* `globalThis.__cellbMaxPorts` overrides it: tests that check nothing leaks set it low.
|
|
9
|
+
*
|
|
10
|
+
* Since webrun-http-browser 0.12 a body's chunks are pipelined under the link's flow-control
|
|
11
|
+
* window (256 KiB by default) instead of going one confirmed chunk at a time: a reader that stops
|
|
12
|
+
* holds about one window of its body, so the bound is about `maxPorts` windows per link.
|
|
9
13
|
*/
|
|
10
14
|
const maxPorts = () => globalThis.__cellbMaxPorts ?? 4096;
|
|
11
15
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/transport/port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAGrF,OAAO,EAEL,gBAAgB,EAEhB,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAa,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAExD
|
|
1
|
+
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/transport/port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAGrF,OAAO,EAEL,gBAAgB,EAEhB,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAa,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;;;;;GASG;AACH,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAE,UAA2C,CAAC,eAAe,IAAI,IAAI,CAAC;AAQ5F;;;;GAIG;AACH,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,IAA8B,CAAC;AAElE;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAc,EACd,OAAoB,EACpB,IAAuB;IAEvB,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;AAChG,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAc,EACd,IAAuB;IAEvB,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IAClE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,oBAAoB,CAAC,IAAc,EAAE,OAAa;IAChE,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAc;IAEd,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC3E,OAAO;QACL,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACzC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;KAC1B,CAAC;AACJ,CAAC","sourcesContent":["import { connectHttpLink, serveHttpLink } from \"@statewalker/webrun-http-browser/sw\";\nimport type { overPipe } from \"@statewalker/webrun-rpc\";\nimport type { CellRuntime } from \"../core/runtime.js\";\nimport {\n type CellsHttpOptions,\n cellsHttpHandler,\n type RemoteRuntime,\n remoteRuntime,\n} from \"./cells-http.js\";\nimport { type Send, withScheme } from \"./cells-link.js\";\n\n/**\n * Multiplexed ports per link: one per exchange in flight, given back when the exchange ends\n * (p31-port-leak.test.ts). 4096 per link, cellb's ceiling before the links were the package's (its\n * default is 1024): a streaming response holds its port for its whole life.\n * `globalThis.__cellbMaxPorts` overrides it: tests that check nothing leaks set it low.\n *\n * Since webrun-http-browser 0.12 a body's chunks are pipelined under the link's flow-control\n * window (256 KiB by default) instead of going one confirmed chunk at a time: a reader that stops\n * holds about one window of its body, so the bound is about `maxPorts` windows per link.\n */\nconst maxPorts = () => (globalThis as { __cellbMaxPorts?: number }).__cellbMaxPorts ?? 4096;\n\n/**\n * A DEDICATED message pipe (MessagePort, or a worker_threads port). Don't pass a\n * Worker object that also carries other messages — transfer a MessageChannel port.\n */\nexport type CellPort = Parameters<typeof overPipe>[0];\n\n/**\n * The links are webrun-http-browser's (`connectHttpLink` / `serveHttpLink`): HTTP/1.1 with\n * streaming bodies over webrun-rpc calls, one multiplexed port per exchange. They take a\n * MessagePort; a worker_threads port has the same `postMessage` / `addEventListener` surface.\n */\nconst asPort = (port: CellPort) => port as unknown as MessagePort;\n\n/**\n * Cell-host side: serves the runtime's HTTP surface over a port. Resolves to an async\n * disposer.\n */\nexport function serveRuntime(\n port: CellPort,\n runtime: CellRuntime,\n opts?: CellsHttpOptions,\n): Promise<() => Promise<void>> {\n return serveHttpLink(asPort(port), cellsHttpHandler(runtime, opts), { maxPorts: maxPorts() });\n}\n\n/** Front side (page, SW bridge, another tab): a RemoteRuntime over a port. */\nexport async function connectRuntime(\n port: CellPort,\n opts?: CellsHttpOptions,\n): Promise<RemoteRuntime & { close(): Promise<void> }> {\n const link = await connectHttpLink(asPort(port), { maxPorts: maxPorts() });\n const remote = await remoteRuntime((req) => link.send(req), opts);\n return Object.assign(remote, { close: () => link.close() });\n}\n\n/** Serves any HTTP handler over a port (responder side of a link). */\nexport function serveHandlerOverPort(port: CellPort, handler: Send): Promise<() => Promise<void>> {\n return serveHttpLink(asPort(port), handler, { maxPorts: maxPorts() });\n}\n\n/** A `send` over a port (initiator side of a link); the scheme rides in a header. */\nexport async function linkOverPort(\n port: CellPort,\n): Promise<{ send: Send; close(): Promise<void> }> {\n const link = await connectHttpLink(asPort(port), { maxPorts: maxPorts() });\n return {\n send: (req) => link.send(withScheme(req)),\n close: () => link.close(),\n };\n}\n"]}
|
package/dist/ws/server.d.ts
CHANGED
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
declare const kPort: unique symbol;
|
|
3
|
-
declare const kTakeQueued: unique symbol;
|
|
1
|
+
import { ServerWebSocket } from "@statewalker/webrun-http-browser/sw";
|
|
4
2
|
/**
|
|
5
|
-
* Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair)
|
|
6
|
-
*
|
|
3
|
+
* Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair), on
|
|
4
|
+
* webrun-http-browser's `ServerWebSocket` and its frames, with workerd's semantics:
|
|
5
|
+
*
|
|
6
|
+
* - events only after `accept()` (frames that arrive earlier wait), and either end may `send()`
|
|
7
|
+
* before it (celld does; the frames wait in the port);
|
|
8
|
+
* - `close()` with no code sends 1005 and waits for the echo however long; the codes Worker code
|
|
9
|
+
* may send are 1000 and 3000–4999; messages in flight when `close()` was called still arrive;
|
|
10
|
+
* - the heartbeat watch starts with the client's first ping and keeps that period: a pair's ends
|
|
11
|
+
* used in one realm never ping, and a tab that closes turns hidden (announcing a longer period)
|
|
12
|
+
* on its way out;
|
|
13
|
+
* - attachments, `peer`, `hibernatable`, and `goAway` for the ends accepted in this realm.
|
|
14
|
+
*
|
|
7
15
|
* Hibernation (`ctx.acceptWebSocket`) is a later prototype.
|
|
8
16
|
*/
|
|
9
|
-
export declare class CfWebSocket extends
|
|
17
|
+
export declare class CfWebSocket extends ServerWebSocket {
|
|
10
18
|
#private;
|
|
11
19
|
static readonly READY_STATE_CONNECTING = 0;
|
|
12
20
|
static readonly READY_STATE_OPEN = 1;
|
|
13
21
|
static readonly READY_STATE_CLOSING = 2;
|
|
14
22
|
static readonly READY_STATE_CLOSED = 3;
|
|
15
|
-
readyState: number;
|
|
16
|
-
url: string | null;
|
|
17
|
-
protocol: string | null;
|
|
18
23
|
extensions: string | null;
|
|
19
|
-
/** How binary messages arrive; other values are ignored, as in browsers. */
|
|
20
|
-
get binaryType(): "blob" | "arraybuffer";
|
|
21
|
-
set binaryType(v: "blob" | "arraybuffer");
|
|
22
|
-
[kPort]: MessagePort | undefined;
|
|
23
|
-
/** The frames that arrived before accept(), removed (takeClientPort hands them over). */
|
|
24
|
-
[kTakeQueued](): Frame[];
|
|
25
24
|
constructor(port: MessagePort);
|
|
26
25
|
/** The other end of the WebSocketPair (the runtime checks how the server end was accepted). */
|
|
27
26
|
peer?: CfWebSocket;
|
|
28
27
|
/** Accepted through ctx.acceptWebSocket() (hibernatable) rather than accept(). */
|
|
29
28
|
hibernatable: boolean;
|
|
30
|
-
get accepted(): boolean;
|
|
31
29
|
accept(): void;
|
|
30
|
+
send(data: string | ArrayBuffer | ArrayBufferView): void;
|
|
31
|
+
protected checkClose(code: number | undefined, reason: string | undefined): void;
|
|
32
|
+
protected _onClosed(): void;
|
|
32
33
|
serializeAttachment(v: unknown): void;
|
|
33
34
|
deserializeAttachment(): unknown;
|
|
34
35
|
/**
|
|
@@ -37,8 +38,6 @@ export declare class CfWebSocket extends EventTarget {
|
|
|
37
38
|
* instead of noticing through the heartbeat; Worker code cannot send 1001 itself (`close`).
|
|
38
39
|
*/
|
|
39
40
|
static goAway(reason?: string): void;
|
|
40
|
-
send(data: string | ArrayBuffer | ArrayBufferView): void;
|
|
41
|
-
close(code?: number, reason?: string): void;
|
|
42
41
|
}
|
|
43
42
|
/**
|
|
44
43
|
* The `WebSocket` global of Worker code. On workerd one class covers both ends of a
|
|
@@ -100,4 +99,3 @@ export declare class WebSocketRequestResponsePair {
|
|
|
100
99
|
readonly response: string;
|
|
101
100
|
constructor(request: string, response: string);
|
|
102
101
|
}
|
|
103
|
-
export {};
|