@statewalker/cellb 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +323 -2
- package/dist/adapters/files-backup.d.ts +16 -0
- package/dist/adapters/files-backup.js +32 -0
- package/dist/adapters/files-backup.js.map +1 -0
- package/dist/adapters/files-changelog.d.ts +33 -0
- package/dist/adapters/files-changelog.js +96 -0
- package/dist/adapters/files-changelog.js.map +1 -0
- package/dist/adapters/files-ltx.d.ts +35 -0
- package/dist/adapters/files-ltx.js +9 -0
- package/dist/adapters/files-ltx.js.map +1 -0
- package/dist/adapters/ltx-vfs.d.ts +21 -0
- package/dist/adapters/ltx-vfs.js +286 -0
- package/dist/adapters/ltx-vfs.js.map +1 -0
- package/dist/adapters/node-sqlite.d.ts +45 -0
- package/dist/adapters/node-sqlite.js +254 -0
- package/dist/adapters/node-sqlite.js.map +1 -0
- package/dist/adapters/wasm-page-hook.d.ts +26 -0
- package/dist/adapters/wasm-page-hook.js +55 -0
- package/dist/adapters/wasm-page-hook.js.map +1 -0
- package/dist/adapters/wasm-session.d.ts +11 -0
- package/dist/adapters/wasm-session.js +76 -0
- package/dist/adapters/wasm-session.js.map +1 -0
- package/dist/adapters/wasm-sqlite.d.ts +89 -0
- package/dist/adapters/wasm-sqlite.js +258 -0
- package/dist/adapters/wasm-sqlite.js.map +1 -0
- package/dist/bindings/assets.d.ts +26 -0
- package/dist/bindings/assets.js +260 -0
- package/dist/bindings/assets.js.map +1 -0
- package/dist/bindings/cache.d.ts +16 -0
- package/dist/bindings/cache.js +34 -0
- package/dist/bindings/cache.js.map +1 -0
- package/dist/bindings/celld-kv.d.ts +26 -0
- package/dist/bindings/celld-kv.js +81 -0
- package/dist/bindings/celld-kv.js.map +1 -0
- package/dist/bindings/celld-queues.d.ts +56 -0
- package/dist/bindings/celld-queues.js +223 -0
- package/dist/bindings/celld-queues.js.map +1 -0
- package/dist/bindings/celld-workflows.d.ts +22 -0
- package/dist/bindings/celld-workflows.js +54 -0
- package/dist/bindings/celld-workflows.js.map +1 -0
- package/dist/bindings/cron.d.ts +27 -0
- package/dist/bindings/cron.js +232 -0
- package/dist/bindings/cron.js.map +1 -0
- package/dist/bindings/d1-migrations.d.ts +22 -0
- package/dist/bindings/d1-migrations.js +32 -0
- package/dist/bindings/d1-migrations.js.map +1 -0
- package/dist/bindings/html-rewriter.d.ts +8 -0
- package/dist/bindings/html-rewriter.js +75 -0
- package/dist/bindings/html-rewriter.js.map +1 -0
- package/dist/bindings/r2-celld.d.ts +3 -0
- package/dist/bindings/r2-celld.js +113 -0
- package/dist/bindings/r2-celld.js.map +1 -0
- package/dist/bindings/r2.d.ts +236 -0
- package/dist/bindings/r2.js +419 -0
- package/dist/bindings/r2.js.map +1 -0
- package/dist/bindings/worker-loader.d.ts +83 -0
- package/dist/bindings/worker-loader.js +183 -0
- package/dist/bindings/worker-loader.js.map +1 -0
- package/dist/bindings/workers-ai.d.ts +39 -0
- package/dist/bindings/workers-ai.js +175 -0
- package/dist/bindings/workers-ai.js.map +1 -0
- package/dist/browser.d.ts +7 -0
- package/dist/browser.js +8 -0
- package/dist/browser.js.map +1 -0
- package/dist/bucket/celld-cells.d.ts +86 -0
- package/dist/bucket/celld-cells.js +273 -0
- package/dist/bucket/celld-cells.js.map +1 -0
- package/dist/bucket/celld-format.d.ts +10 -0
- package/dist/bucket/celld-format.js +27 -0
- package/dist/bucket/celld-format.js.map +1 -0
- package/dist/bucket/idb-object-store.d.ts +9 -0
- package/dist/bucket/idb-object-store.js +73 -0
- package/dist/bucket/idb-object-store.js.map +1 -0
- package/dist/bucket/ltx-log.d.ts +34 -0
- package/dist/bucket/ltx-log.js +289 -0
- package/dist/bucket/ltx-log.js.map +1 -0
- package/dist/bucket/object-store.d.ts +51 -0
- package/dist/bucket/object-store.js +114 -0
- package/dist/bucket/object-store.js.map +1 -0
- package/dist/bucket/s3-object-store.d.ts +25 -0
- package/dist/bucket/s3-object-store.js +151 -0
- package/dist/bucket/s3-object-store.js.map +1 -0
- package/dist/bucket/sqlite-object-store.d.ts +10 -0
- package/dist/bucket/sqlite-object-store.js +107 -0
- package/dist/bucket/sqlite-object-store.js.map +1 -0
- package/dist/core/alarms.d.ts +27 -0
- package/dist/core/alarms.js +88 -0
- package/dist/core/alarms.js.map +1 -0
- package/dist/core/async-context.d.ts +34 -0
- package/dist/core/async-context.js +50 -0
- package/dist/core/async-context.js.map +1 -0
- package/dist/core/celld-schema.d.ts +14 -0
- package/dist/core/celld-schema.js +34 -0
- package/dist/core/celld-schema.js.map +1 -0
- package/dist/core/durable-object.d.ts +28 -0
- package/dist/core/durable-object.js +31 -0
- package/dist/core/durable-object.js.map +1 -0
- package/dist/core/gates.d.ts +33 -0
- package/dist/core/gates.js +89 -0
- package/dist/core/gates.js.map +1 -0
- package/dist/core/handles.d.ts +90 -0
- package/dist/core/handles.js +224 -0
- package/dist/core/handles.js.map +1 -0
- package/dist/core/id.d.ts +19 -0
- package/dist/core/id.js +64 -0
- package/dist/core/id.js.map +1 -0
- package/dist/core/namespace.d.ts +38 -0
- package/dist/core/namespace.js +141 -0
- package/dist/core/namespace.js.map +1 -0
- package/dist/core/object-host.d.ts +91 -0
- package/dist/core/object-host.js +616 -0
- package/dist/core/object-host.js.map +1 -0
- package/dist/core/request-body.d.ts +9 -0
- package/dist/core/request-body.js +55 -0
- package/dist/core/request-body.js.map +1 -0
- package/dist/core/runtime.d.ts +121 -0
- package/dist/core/runtime.js +366 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/shard.d.ts +7 -0
- package/dist/core/shard.js +10 -0
- package/dist/core/shard.js.map +1 -0
- package/dist/core/sql-split.d.ts +15 -0
- package/dist/core/sql-split.js +101 -0
- package/dist/core/sql-split.js.map +1 -0
- package/dist/core/sql-storage.d.ts +53 -0
- package/dist/core/sql-storage.js +407 -0
- package/dist/core/sql-storage.js.map +1 -0
- package/dist/core/state.d.ts +44 -0
- package/dist/core/state.js +93 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/storage.d.ts +103 -0
- package/dist/core/storage.js +351 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/worker-rejections.d.ts +21 -0
- package/dist/core/worker-rejections.js +55 -0
- package/dist/core/worker-rejections.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/isolate/browser.d.ts +16 -0
- package/dist/isolate/browser.js +37 -0
- package/dist/isolate/browser.js.map +1 -0
- package/dist/isolate/host.d.ts +24 -0
- package/dist/isolate/host.js +74 -0
- package/dist/isolate/host.js.map +1 -0
- package/dist/isolate/isolate-worker.d.ts +1 -0
- package/dist/isolate/isolate-worker.js +53 -0
- package/dist/isolate/isolate-worker.js.map +1 -0
- package/dist/isolate/isolate.d.ts +10 -0
- package/dist/isolate/isolate.js +169 -0
- package/dist/isolate/isolate.js.map +1 -0
- package/dist/isolate/node-entry.d.ts +1 -0
- package/dist/isolate/node-entry.js +12 -0
- package/dist/isolate/node-entry.js.map +1 -0
- package/dist/isolate/protocol.d.ts +66 -0
- package/dist/isolate/protocol.js +205 -0
- package/dist/isolate/protocol.js.map +1 -0
- package/dist/ltx/crc64.d.ts +6 -0
- package/dist/ltx/crc64.js +46 -0
- package/dist/ltx/crc64.js.map +1 -0
- package/dist/ltx/ltx-page-server.d.ts +65 -0
- package/dist/ltx/ltx-page-server.js +309 -0
- package/dist/ltx/ltx-page-server.js.map +1 -0
- package/dist/ltx/ltx-pages.d.ts +81 -0
- package/dist/ltx/ltx-pages.js +315 -0
- package/dist/ltx/ltx-pages.js.map +1 -0
- package/dist/ltx/ltx-store-worker.d.ts +1 -0
- package/dist/ltx/ltx-store-worker.js +38 -0
- package/dist/ltx/ltx-store-worker.js.map +1 -0
- package/dist/ltx/ltx.d.ts +45 -0
- package/dist/ltx/ltx.js +203 -0
- package/dist/ltx/ltx.js.map +1 -0
- package/dist/ltx/lz4.d.ts +9 -0
- package/dist/ltx/lz4.js +156 -0
- package/dist/ltx/lz4.js.map +1 -0
- package/dist/ltx/opfs-sync-files.d.ts +23 -0
- package/dist/ltx/opfs-sync-files.js +191 -0
- package/dist/ltx/opfs-sync-files.js.map +1 -0
- package/dist/ltx/xxh32.d.ts +1 -0
- package/dist/ltx/xxh32.js +43 -0
- package/dist/ltx/xxh32.js.map +1 -0
- package/dist/miniflare-compat/blob-service.d.ts +13 -0
- package/dist/miniflare-compat/blob-service.js +66 -0
- package/dist/miniflare-compat/blob-service.js.map +1 -0
- package/dist/miniflare-compat/kv-binding.d.ts +44 -0
- package/dist/miniflare-compat/kv-binding.js +99 -0
- package/dist/miniflare-compat/kv-binding.js.map +1 -0
- package/dist/miniflare-compat/shims/assert.d.ts +15 -0
- package/dist/miniflare-compat/shims/assert.js +24 -0
- package/dist/miniflare-compat/shims/assert.js.map +1 -0
- package/dist/miniflare-compat/shims/workerd-cloudflare.d.ts +7 -0
- package/dist/miniflare-compat/shims/workerd-cloudflare.js +6 -0
- package/dist/miniflare-compat/shims/workerd-cloudflare.js.map +1 -0
- package/dist/miniflare-compat/shims/workerd-globals.d.ts +1 -0
- package/dist/miniflare-compat/shims/workerd-globals.js +30 -0
- package/dist/miniflare-compat/shims/workerd-globals.js.map +1 -0
- package/dist/miniflare-compat/shims/workerd-internal.d.ts +20 -0
- package/dist/miniflare-compat/shims/workerd-internal.js +23 -0
- package/dist/miniflare-compat/shims/workerd-internal.js.map +1 -0
- package/dist/miniflare-compat/shims/workerd-performance.d.ts +4 -0
- package/dist/miniflare-compat/shims/workerd-performance.js +17 -0
- package/dist/miniflare-compat/shims/workerd-performance.js.map +1 -0
- package/dist/miniflare-compat/simulator-fetcher.d.ts +9 -0
- package/dist/miniflare-compat/simulator-fetcher.js +14 -0
- package/dist/miniflare-compat/simulator-fetcher.js.map +1 -0
- package/dist/miniflare-compat/vendored.d.ts +15 -0
- package/dist/miniflare-compat/vendored.js +27 -0
- package/dist/miniflare-compat/vendored.js.map +1 -0
- package/dist/node-compat/async_hooks.d.ts +22 -0
- package/dist/node-compat/async_hooks.js +176 -0
- package/dist/node-compat/async_hooks.js.map +1 -0
- package/dist/node-compat/browser-registry.d.ts +3 -0
- package/dist/node-compat/browser-registry.js +39 -0
- package/dist/node-compat/browser-registry.js.map +1 -0
- package/dist/node-compat/crypto.d.ts +60 -0
- package/dist/node-compat/crypto.js +143 -0
- package/dist/node-compat/crypto.js.map +1 -0
- package/dist/node-compat/diagnostics_channel.d.ts +27 -0
- package/dist/node-compat/diagnostics_channel.js +46 -0
- package/dist/node-compat/diagnostics_channel.js.map +1 -0
- package/dist/node-compat/fs.d.ts +35 -0
- package/dist/node-compat/fs.js +40 -0
- package/dist/node-compat/fs.js.map +1 -0
- package/dist/node-compat/globals.d.ts +3 -0
- package/dist/node-compat/globals.js +4 -0
- package/dist/node-compat/globals.js.map +1 -0
- package/dist/node-compat/install.d.ts +5 -0
- package/dist/node-compat/install.js +12 -0
- package/dist/node-compat/install.js.map +1 -0
- package/dist/node-compat/module.d.ts +12 -0
- package/dist/node-compat/module.js +36 -0
- package/dist/node-compat/module.js.map +1 -0
- package/dist/node-compat/modules.d.ts +8 -0
- package/dist/node-compat/modules.js +23 -0
- package/dist/node-compat/modules.js.map +1 -0
- package/dist/node-compat/os.d.ts +36 -0
- package/dist/node-compat/os.js +36 -0
- package/dist/node-compat/os.js.map +1 -0
- package/dist/node-compat/process.d.ts +14 -0
- package/dist/node-compat/process.js +16 -0
- package/dist/node-compat/process.js.map +1 -0
- package/dist/node-compat/shims.d.ts +4 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.js +6 -0
- package/dist/node.js.map +1 -0
- package/dist/peer/celld-peer.d.ts +53 -0
- package/dist/peer/celld-peer.js +555 -0
- package/dist/peer/celld-peer.js.map +1 -0
- package/dist/ports.d.ts +183 -0
- package/dist/ports.js +10 -0
- package/dist/ports.js.map +1 -0
- package/dist/project/browser-bundle.d.ts +17 -0
- package/dist/project/browser-bundle.js +125 -0
- package/dist/project/browser-bundle.js.map +1 -0
- package/dist/project/browser-cookies.d.ts +12 -0
- package/dist/project/browser-cookies.js +90 -0
- package/dist/project/browser-cookies.js.map +1 -0
- package/dist/project/browser-host.d.ts +173 -0
- package/dist/project/browser-host.js +200 -0
- package/dist/project/browser-host.js.map +1 -0
- package/dist/project/browser-modules.d.ts +17 -0
- package/dist/project/browser-modules.js +89 -0
- package/dist/project/browser-modules.js.map +1 -0
- package/dist/project/browser-shards.d.ts +62 -0
- package/dist/project/browser-shards.js +210 -0
- package/dist/project/browser-shards.js.map +1 -0
- package/dist/project/browser-tabs.d.ts +103 -0
- package/dist/project/browser-tabs.js +418 -0
- package/dist/project/browser-tabs.js.map +1 -0
- package/dist/project/cloudflare-email.d.ts +8 -0
- package/dist/project/cloudflare-email.js +11 -0
- package/dist/project/cloudflare-email.js.map +1 -0
- package/dist/project/cloudflare-workers.d.ts +12 -0
- package/dist/project/cloudflare-workers.js +16 -0
- package/dist/project/cloudflare-workers.js.map +1 -0
- package/dist/project/host-modules.d.ts +4 -0
- package/dist/project/host-modules.js +40 -0
- package/dist/project/host-modules.js.map +1 -0
- package/dist/project/jsonc.d.ts +2 -0
- package/dist/project/jsonc.js +27 -0
- package/dist/project/jsonc.js.map +1 -0
- package/dist/project/lower-await.d.ts +2 -0
- package/dist/project/lower-await.js +151 -0
- package/dist/project/lower-await.js.map +1 -0
- package/dist/project/node-project.d.ts +38 -0
- package/dist/project/node-project.js +373 -0
- package/dist/project/node-project.js.map +1 -0
- package/dist/project/own-module.d.ts +6 -0
- package/dist/project/own-module.js +13 -0
- package/dist/project/own-module.js.map +1 -0
- package/dist/project/scheduler.d.ts +2 -0
- package/dist/project/scheduler.js +3 -0
- package/dist/project/scheduler.js.map +1 -0
- package/dist/project/site.d.ts +74 -0
- package/dist/project/site.js +206 -0
- package/dist/project/site.js.map +1 -0
- package/dist/project/virtual-modules.d.ts +3 -0
- package/dist/project/worker-scope.d.ts +27 -0
- package/dist/project/worker-scope.js +46 -0
- package/dist/project/worker-scope.js.map +1 -0
- package/dist/project/workerd-message-channel.d.ts +13 -0
- package/dist/project/workerd-message-channel.js +137 -0
- package/dist/project/workerd-message-channel.js.map +1 -0
- package/dist/project/workers-global-names.d.ts +6 -0
- package/dist/project/workers-global-names.js +15 -0
- package/dist/project/workers-global-names.js.map +1 -0
- package/dist/project/workers-globals-module.d.ts +8 -0
- package/dist/project/workers-globals-module.js +9 -0
- package/dist/project/workers-globals-module.js.map +1 -0
- package/dist/project/workers-globals.d.ts +9 -0
- package/dist/project/workers-globals.js +19 -0
- package/dist/project/workers-globals.js.map +1 -0
- package/dist/project/wrangler.d.ts +116 -0
- package/dist/project/wrangler.js +342 -0
- package/dist/project/wrangler.js.map +1 -0
- package/dist/router/cell-router.d.ts +72 -0
- package/dist/router/cell-router.js +247 -0
- package/dist/router/cell-router.js.map +1 -0
- package/dist/router/shard-worker.d.ts +44 -0
- package/dist/router/shard-worker.js +163 -0
- package/dist/router/shard-worker.js.map +1 -0
- package/dist/transport/cells-http.d.ts +25 -0
- package/dist/transport/cells-http.js +141 -0
- package/dist/transport/cells-http.js.map +1 -0
- package/dist/transport/cells-link.d.ts +27 -0
- package/dist/transport/cells-link.js +71 -0
- package/dist/transport/cells-link.js.map +1 -0
- package/dist/transport/port.d.ts +26 -0
- package/dist/transport/port.js +100 -0
- package/dist/transport/port.js.map +1 -0
- package/dist/v8/codec.d.ts +6 -0
- package/dist/v8/codec.js +9 -0
- package/dist/v8/codec.js.map +1 -0
- package/dist/v8/serialize.d.ts +10 -0
- package/dist/v8/serialize.js +601 -0
- package/dist/v8/serialize.js.map +1 -0
- package/dist/ws/client.d.ts +25 -0
- package/dist/ws/client.js +203 -0
- package/dist/ws/client.js.map +1 -0
- package/dist/ws/control.d.ts +48 -0
- package/dist/ws/control.js +44 -0
- package/dist/ws/control.js.map +1 -0
- package/dist/ws/frames.d.ts +38 -0
- package/dist/ws/frames.js +39 -0
- package/dist/ws/frames.js.map +1 -0
- package/dist/ws/server.d.ts +99 -0
- package/dist/ws/server.js +295 -0
- package/dist/ws/server.js.map +1 -0
- package/dist/ws/sw-connector.d.ts +10 -0
- package/dist/ws/sw-connector.js +25 -0
- package/dist/ws/sw-connector.js.map +1 -0
- package/dist/ws/upgrade.d.ts +28 -0
- package/dist/ws/upgrade.js +75 -0
- package/dist/ws/upgrade.js.map +1 -0
- package/package.json +111 -3
- package/vendor/celld/LICENSE +202 -0
- package/vendor/celld/kv.js +1131 -0
- package/vendor/celld/queues.js +1656 -0
- package/vendor/celld/workflows.js +1610 -0
- package/vendor/html-rewriter-wasm/html_rewriter.js +925 -0
- package/vendor/html-rewriter-wasm/html_rewriter_bg.wasm +0 -0
- package/vendor/miniflare/d1-api.js +738 -0
- package/vendor/miniflare/d1-database.js +6978 -0
- package/vendor/miniflare/kv-namespace.js +3061 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { serializeError } from "@statewalker/webrun-streams";
|
|
2
|
+
import { toAnotherNode } from "../core/handles.js";
|
|
3
|
+
import { DurableObjectNamespace } from "../core/namespace.js";
|
|
4
|
+
import { withUrl } from "../core/request-body.js";
|
|
5
|
+
import { v8Codec } from "../v8/codec.js";
|
|
6
|
+
import { takeClientPort } from "../ws/server.js";
|
|
7
|
+
import { withHeaders, withUpgrade } from "../ws/upgrade.js";
|
|
8
|
+
import { control, H, httpEndpoint, keepUpgrade, postVia, withScheme, } from "./cells-link.js";
|
|
9
|
+
/**
|
|
10
|
+
* Everything a remote caller can do with a cell host is expressed as HTTP:
|
|
11
|
+
* worker ingress, `stub.fetch()`, JS-RPC calls and the bindings descriptor.
|
|
12
|
+
* The resulting `(Request) ⇒ Response` handler can then ride ANY webrun-wire
|
|
13
|
+
* transport: a MessagePort (webrun-rpc), a WebSocket (webrun-streams-ws),
|
|
14
|
+
* WebRTC / libp2p peers (httpeers) — via webrun-http-streams' Duplex seam.
|
|
15
|
+
*
|
|
16
|
+
* Control headers mark internal requests. TRUST MODEL: this handler is for an
|
|
17
|
+
* internal link (page ⇄ worker, owner ⇄ forwarder). Public ingress must call
|
|
18
|
+
* `runtime.fetch` directly, never this handler, or clients could address
|
|
19
|
+
* objects without going through the Worker's own routing.
|
|
20
|
+
*/
|
|
21
|
+
const CT_VALUES = "application/x-cellb-values";
|
|
22
|
+
/** Server side: exposes a runtime as one HTTP handler. */
|
|
23
|
+
export function errorResponse(e, status = 500) {
|
|
24
|
+
return Response.json(serializeError(e), { status, headers: { [H.kind]: "error" } });
|
|
25
|
+
}
|
|
26
|
+
export function cellsHttpHandler(runtime, opts = {}) {
|
|
27
|
+
const codec = opts.codec ?? v8Codec; // the runtime's default: V8 serialization
|
|
28
|
+
return async (incoming) => {
|
|
29
|
+
const req = await restoreScheme(incoming);
|
|
30
|
+
const kind = req.headers.get(H.kind);
|
|
31
|
+
if (!kind) {
|
|
32
|
+
const cookie = req.headers.get(H.cookie);
|
|
33
|
+
if (cookie === null)
|
|
34
|
+
return runtime.fetch(stripControl(req));
|
|
35
|
+
// From a ServiceWorker: the cookies in, the Worker's Set-Cookie out, under H.cookie and
|
|
36
|
+
// H.setCookie (a browser drops both headers from every Request and Response it builds).
|
|
37
|
+
const inner = cookie ? withHeaders(stripControl(req), { cookie }) : stripControl(req);
|
|
38
|
+
return tunnelSetCookies(await runtime.fetch(inner));
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
if (kind === "describe")
|
|
42
|
+
return Response.json({
|
|
43
|
+
bindings: runtime.durableObjectBindings,
|
|
44
|
+
// The far side derives the same ids only with the same namespace keys.
|
|
45
|
+
keys: Object.fromEntries(Object.keys(runtime.durableObjectBindings).map((b) => [
|
|
46
|
+
b,
|
|
47
|
+
runtime.env[b].namespaceKey,
|
|
48
|
+
])),
|
|
49
|
+
});
|
|
50
|
+
const stub = stubOf(runtime, req.headers.get(H.binding) ?? "", req.headers.get(H.target) ?? "");
|
|
51
|
+
if (kind === "fetch") {
|
|
52
|
+
const wsId = req.headers.get(H.ws);
|
|
53
|
+
let inner = stripControl(req);
|
|
54
|
+
const upgrade = req.headers.get(H.upgrade);
|
|
55
|
+
if (upgrade) {
|
|
56
|
+
const offered = req.headers.get(H.wsProtocol);
|
|
57
|
+
inner = withUpgrade(inner, upgrade, offered ? { "Sec-WebSocket-Protocol": offered } : undefined);
|
|
58
|
+
}
|
|
59
|
+
const res = (await stub.fetch(inner));
|
|
60
|
+
if (res.status !== 101 || !res.webSocket)
|
|
61
|
+
return res;
|
|
62
|
+
if (!wsId || !opts.onUpgrade)
|
|
63
|
+
throw new Error("WebSocket upgrade cannot cross this link (no control channel)");
|
|
64
|
+
const protocol = res.headers.get("Sec-WebSocket-Protocol") ?? undefined;
|
|
65
|
+
opts.onUpgrade(wsId, takeClientPort(res.webSocket), protocol);
|
|
66
|
+
return new Response(null, {
|
|
67
|
+
status: 200,
|
|
68
|
+
headers: {
|
|
69
|
+
[H.wsAccepted]: wsId,
|
|
70
|
+
...(protocol ? { "Sec-WebSocket-Protocol": protocol } : {}),
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (kind === "rpc") {
|
|
75
|
+
const args = codec.decode(new Uint8Array(await req.arrayBuffer()));
|
|
76
|
+
const result = await stub[req.headers.get(H.method) ?? ""](...args);
|
|
77
|
+
return new Response(codec.encode(toAnotherNode(result)), {
|
|
78
|
+
headers: { "content-type": CT_VALUES },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`Unknown cellb request kind "${kind}"`);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
return errorResponse(e);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function stubOf(runtime, binding, target) {
|
|
89
|
+
const ns = runtime.env[binding];
|
|
90
|
+
if (!(ns instanceof DurableObjectNamespace))
|
|
91
|
+
throw new Error(`Unknown Durable Object binding "${binding}"`);
|
|
92
|
+
if (target.startsWith("name:"))
|
|
93
|
+
return ns.getByName(target.slice(5));
|
|
94
|
+
if (target.startsWith("id:"))
|
|
95
|
+
return ns.get(ns.idFromString(target.slice(3)));
|
|
96
|
+
throw new Error(`Invalid Durable Object target "${target}"`);
|
|
97
|
+
}
|
|
98
|
+
/** Client side over any `send` (fetchOverDuplex over a port / WebSocket / peer, or the handler itself). */
|
|
99
|
+
export async function remoteRuntime(send, opts = {}) {
|
|
100
|
+
const codec = opts.codec ?? v8Codec; // the runtime's default: V8 serialization
|
|
101
|
+
const post = postVia(send);
|
|
102
|
+
const described = await post(control("describe", {}));
|
|
103
|
+
const { bindings, keys } = (await described.json());
|
|
104
|
+
const env = {};
|
|
105
|
+
for (const [binding, className] of Object.entries(bindings))
|
|
106
|
+
env[binding] = new DurableObjectNamespace(keys?.[binding] ?? className, (id) => () => httpEndpoint(post, codec, binding, className, id));
|
|
107
|
+
return { env, fetch: (req) => send(withScheme(req)) };
|
|
108
|
+
}
|
|
109
|
+
async function restoreScheme(req) {
|
|
110
|
+
const proto = req.headers.get(H.proto);
|
|
111
|
+
if (!proto)
|
|
112
|
+
return req;
|
|
113
|
+
const url = new URL(req.url);
|
|
114
|
+
if (url.protocol === `${proto}:`)
|
|
115
|
+
return req;
|
|
116
|
+
url.protocol = `${proto}:`;
|
|
117
|
+
return keepUpgrade(req, await withUrl(req, url));
|
|
118
|
+
}
|
|
119
|
+
/** The Set-Cookie values of a response as one H.setCookie header (status 101 left as it is). */
|
|
120
|
+
function tunnelSetCookies(res) {
|
|
121
|
+
const cookies = res.status === 101 ? [] : (res.headers.getSetCookie?.() ?? []);
|
|
122
|
+
if (!cookies.length)
|
|
123
|
+
return res;
|
|
124
|
+
const headers = new Headers();
|
|
125
|
+
for (const [k, v] of res.headers)
|
|
126
|
+
if (k !== "set-cookie")
|
|
127
|
+
headers.append(k, v);
|
|
128
|
+
headers.set(H.setCookie, cookies.map(encodeURIComponent).join(","));
|
|
129
|
+
return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
|
|
130
|
+
}
|
|
131
|
+
export function stripControl(req, keep = []) {
|
|
132
|
+
const headers = new Headers(req.headers);
|
|
133
|
+
let changed = false;
|
|
134
|
+
for (const h of Object.values(H))
|
|
135
|
+
if (headers.has(h) && !keep.includes(h))
|
|
136
|
+
headers.delete(h), (changed = true);
|
|
137
|
+
if (!changed)
|
|
138
|
+
return req;
|
|
139
|
+
return keepUpgrade(req, new Request(req, { headers })); // keeps the body in Firefox too (addHeaders)
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=cells-http.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DurableObjectId } from "../core/id.js";
|
|
2
|
+
import type { ObjectEndpoint } from "../core/namespace.js";
|
|
3
|
+
import type { ValueCodec } from "../ports.js";
|
|
4
|
+
/** Control headers that mark internal requests on a cells-http link (see cells-http.ts). */
|
|
5
|
+
export declare const H: {
|
|
6
|
+
readonly kind: "x-cellb";
|
|
7
|
+
readonly binding: "x-cellb-binding";
|
|
8
|
+
readonly target: "x-cellb-target";
|
|
9
|
+
readonly method: "x-cellb-method";
|
|
10
|
+
readonly proto: "x-cellb-proto";
|
|
11
|
+
readonly ws: "x-cellb-ws";
|
|
12
|
+
readonly wsAccepted: "x-cellb-ws-accepted";
|
|
13
|
+
readonly upgrade: "x-cellb-upgrade";
|
|
14
|
+
readonly wsProtocol: "x-cellb-ws-protocol";
|
|
15
|
+
readonly shard: "x-cellb-shard";
|
|
16
|
+
readonly cookie: "x-cellb-cookie";
|
|
17
|
+
readonly setCookie: "x-cellb-set-cookie";
|
|
18
|
+
};
|
|
19
|
+
export type Send = (req: Request) => Promise<Response>;
|
|
20
|
+
/** A `send` that carries the scheme and turns `x-cellb: error` responses back into exceptions. */
|
|
21
|
+
export declare function postVia(send: Send): Send;
|
|
22
|
+
/** Stub endpoint whose object lives behind `post` (another worker, tab or peer). */
|
|
23
|
+
export declare function httpEndpoint(post: Send, codec: ValueCodec, binding: string, className: string, id: DurableObjectId): ObjectEndpoint;
|
|
24
|
+
export declare function control(kind: string, headers: Record<string, string>, body?: Uint8Array): Request;
|
|
25
|
+
/** Browsers drop the forbidden `Upgrade` header from every rebuilt Request: carry it over. */
|
|
26
|
+
export declare function keepUpgrade(from: Request, to: Request): Request;
|
|
27
|
+
export declare function withScheme(req: Request): Request;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { deserializeError } from "@statewalker/webrun-streams";
|
|
2
|
+
import { toAnotherNode } from "../core/handles.js";
|
|
3
|
+
import { carryWebSocketHeaders } from "../ws/upgrade.js";
|
|
4
|
+
/** Control headers that mark internal requests on a cells-http link (see cells-http.ts). */
|
|
5
|
+
export const H = {
|
|
6
|
+
kind: "x-cellb", // "describe" | "fetch" | "rpc" | "error" (response)
|
|
7
|
+
binding: "x-cellb-binding",
|
|
8
|
+
target: "x-cellb-target", // "name:<n>" | "id:<hex>"
|
|
9
|
+
method: "x-cellb-method",
|
|
10
|
+
proto: "x-cellb-proto", // original scheme (the HTTP/1.1 wire drops it)
|
|
11
|
+
ws: "x-cellb-ws", // request: id under which the caller awaits a WebSocket port
|
|
12
|
+
wsAccepted: "x-cellb-ws-accepted", // response: the port was handed over (control channel)
|
|
13
|
+
upgrade: "x-cellb-upgrade", // `Upgrade` is hop-by-hop: dropped by the HTTP/1.1 wire, carried here
|
|
14
|
+
wsProtocol: "x-cellb-ws-protocol", // `Sec-WebSocket-Protocol` is forbidden in browser Requests
|
|
15
|
+
shard: "x-cellb-shard", // the shard that owns the target, as the sending shard computed it
|
|
16
|
+
// Ingress from a browser (a ServiceWorker): `Cookie` is forbidden in browser Requests and
|
|
17
|
+
// `Set-Cookie` in browser Responses, so the page's cookies travel here, and the Worker's
|
|
18
|
+
// Set-Cookie values come back URI-encoded and comma-separated (browser-cookies.ts).
|
|
19
|
+
cookie: "x-cellb-cookie",
|
|
20
|
+
setCookie: "x-cellb-set-cookie",
|
|
21
|
+
};
|
|
22
|
+
/** A `send` that carries the scheme and turns `x-cellb: error` responses back into exceptions. */
|
|
23
|
+
export function postVia(send) {
|
|
24
|
+
return async (req) => {
|
|
25
|
+
const res = await send(withScheme(req));
|
|
26
|
+
if (res.headers.get(H.kind) === "error")
|
|
27
|
+
throw deserializeError(await res.json());
|
|
28
|
+
return res;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Stub endpoint whose object lives behind `post` (another worker, tab or peer). */
|
|
32
|
+
export function httpEndpoint(post, codec, binding, className, id) {
|
|
33
|
+
// By name when the id came from one (only idFromName() sets `name`): keeps ctx.id.name
|
|
34
|
+
// on the far side, which derives the same id with the same namespace key.
|
|
35
|
+
const target = id.name !== undefined ? `name:${id.name}` : `id:${id}`;
|
|
36
|
+
const base = { [H.binding]: binding, [H.target]: target };
|
|
37
|
+
return {
|
|
38
|
+
// Not the caller's abort signal: an aborted caller does not cancel a local object's
|
|
39
|
+
// fetch() either (the object answers, nobody reads), so neither does a remote one.
|
|
40
|
+
fetch: (req) => post(addHeaders(req, { [H.kind]: "fetch", ...base }, { detach: true })),
|
|
41
|
+
async call(method, args) {
|
|
42
|
+
const res = await post(control("rpc", { ...base, [H.method]: method }, codec.encode(toAnotherNode(args))));
|
|
43
|
+
return codec.decode(new Uint8Array(await res.arrayBuffer()));
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function control(kind, headers, body) {
|
|
48
|
+
return new Request("http://cellb.internal/", {
|
|
49
|
+
method: body ? "POST" : "GET",
|
|
50
|
+
headers: { [H.kind]: kind, ...headers },
|
|
51
|
+
body: body,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Re-creates a request with extra headers, streaming its body through. The request itself is the
|
|
56
|
+
* source, not `body: req.body`: Firefox has no `Request.body`, so that would drop the body there.
|
|
57
|
+
*/
|
|
58
|
+
function addHeaders(req, extra, opts = {}) {
|
|
59
|
+
const headers = new Headers(req.headers);
|
|
60
|
+
for (const [k, v] of Object.entries(extra))
|
|
61
|
+
headers.set(k, v);
|
|
62
|
+
return keepUpgrade(req, new Request(req, opts.detach ? { headers, signal: null } : { headers }));
|
|
63
|
+
}
|
|
64
|
+
/** Browsers drop the forbidden `Upgrade` header from every rebuilt Request: carry it over. */
|
|
65
|
+
export function keepUpgrade(from, to) {
|
|
66
|
+
return carryWebSocketHeaders(from.headers, to);
|
|
67
|
+
}
|
|
68
|
+
export function withScheme(req) {
|
|
69
|
+
return addHeaders(req, { [H.proto]: new URL(req.url).protocol.replace(":", "") });
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=cells-link.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { overPipe } from "@statewalker/webrun-rpc";
|
|
2
|
+
import type { CellRuntime } from "../core/runtime.js";
|
|
3
|
+
import { type CellsHttpOptions, type RemoteRuntime } from "./cells-http.js";
|
|
4
|
+
import { type Send } from "./cells-link.js";
|
|
5
|
+
/**
|
|
6
|
+
* A DEDICATED message pipe (MessagePort, or a worker_threads port). Don't pass a
|
|
7
|
+
* Worker object that also carries other messages — transfer a MessageChannel port.
|
|
8
|
+
*/
|
|
9
|
+
export type CellPort = Parameters<typeof overPipe>[0];
|
|
10
|
+
/**
|
|
11
|
+
* Cell-host side: serves the runtime's HTTP surface over a port with
|
|
12
|
+
* webrun-rpc (mux + backpressure) and webrun-http-streams (HTTP/1.1 on the
|
|
13
|
+
* Duplex, streaming bodies). Resolves to an async disposer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function serveRuntime(port: CellPort, runtime: CellRuntime, opts?: CellsHttpOptions): Promise<() => Promise<void>>;
|
|
16
|
+
/** Front side (page, SW bridge, another tab): a RemoteRuntime over a port. */
|
|
17
|
+
export declare function connectRuntime(port: CellPort, opts?: CellsHttpOptions): Promise<RemoteRuntime & {
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}>;
|
|
20
|
+
/** Serves any HTTP handler over a port (responder side of a link). */
|
|
21
|
+
export declare function serveHandlerOverPort(port: CellPort, handler: Send): Promise<() => Promise<void>>;
|
|
22
|
+
/** A `send` over a port (initiator side of a link); the scheme rides in a header. */
|
|
23
|
+
export declare function linkOverPort(port: CellPort): Promise<{
|
|
24
|
+
send: Send;
|
|
25
|
+
close(): Promise<void>;
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { fetchOverDuplex, serveFetchOverDuplex } from "@statewalker/webrun-http-streams";
|
|
2
|
+
import { connect, overPipe, serve, structuredCodec } from "@statewalker/webrun-rpc";
|
|
3
|
+
import { cellsHttpHandler, remoteRuntime, } from "./cells-http.js";
|
|
4
|
+
import { withScheme } from "./cells-link.js";
|
|
5
|
+
/**
|
|
6
|
+
* Multiplexed ports per link: one per exchange in flight (they are released when an exchange
|
|
7
|
+
* ends, is cancelled or has no body: fetchClosing, servingClosing; p31-port-leak.test.ts).
|
|
8
|
+
* `globalThis.__cellbMaxPorts` overrides it (tests that check nothing leaks set it low).
|
|
9
|
+
*/
|
|
10
|
+
const maxPorts = () => globalThis.__cellbMaxPorts ?? 4096;
|
|
11
|
+
/**
|
|
12
|
+
* Cell-host side: serves the runtime's HTTP surface over a port with
|
|
13
|
+
* webrun-rpc (mux + backpressure) and webrun-http-streams (HTTP/1.1 on the
|
|
14
|
+
* Duplex, streaming bodies). Resolves to an async disposer.
|
|
15
|
+
*/
|
|
16
|
+
export function serveRuntime(port, runtime, opts) {
|
|
17
|
+
return serve({ mux: overPipe(port, { codec: structuredCodec, side: "responder", maxPorts: maxPorts() }) }, servingClosing(serveFetchOverDuplex(cellsHttpHandler(runtime, opts))));
|
|
18
|
+
}
|
|
19
|
+
async function fetchClosing(call, req) {
|
|
20
|
+
let output;
|
|
21
|
+
const res = await fetchOverDuplex(((input) => {
|
|
22
|
+
output = call(input);
|
|
23
|
+
return output;
|
|
24
|
+
}), req);
|
|
25
|
+
const drain = () => void (async () => {
|
|
26
|
+
try {
|
|
27
|
+
for await (const _ of output)
|
|
28
|
+
;
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
31
|
+
})();
|
|
32
|
+
if (!output)
|
|
33
|
+
return res;
|
|
34
|
+
if (res.body === null) {
|
|
35
|
+
drain();
|
|
36
|
+
return res;
|
|
37
|
+
}
|
|
38
|
+
// The same once the body is read to its end, or cancelled by its reader (then the exchange
|
|
39
|
+
// is abandoned: the call's iterator is returned, which closes its port).
|
|
40
|
+
const reader = res.body.getReader();
|
|
41
|
+
const body = new ReadableStream({
|
|
42
|
+
async pull(ctl) {
|
|
43
|
+
const r = await reader.read();
|
|
44
|
+
if (r.done) {
|
|
45
|
+
ctl.close();
|
|
46
|
+
drain();
|
|
47
|
+
}
|
|
48
|
+
else
|
|
49
|
+
ctl.enqueue(r.value);
|
|
50
|
+
},
|
|
51
|
+
async cancel(reason) {
|
|
52
|
+
await reader.cancel(reason).catch(() => { });
|
|
53
|
+
await output.return?.(undefined).catch?.(() => { });
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
return Object.assign(new Response(body, res), {
|
|
57
|
+
webSocket: res.webSocket ?? null,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function servingClosing(duplex) {
|
|
61
|
+
return async function* (input) {
|
|
62
|
+
const it = input[Symbol.asyncIterator]();
|
|
63
|
+
const rest = { [Symbol.asyncIterator]: () => it };
|
|
64
|
+
try {
|
|
65
|
+
yield* duplex(rest);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
void (async () => {
|
|
69
|
+
try {
|
|
70
|
+
for (let r = await it.next(); !r.done; r = await it.next())
|
|
71
|
+
;
|
|
72
|
+
}
|
|
73
|
+
catch { }
|
|
74
|
+
})();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Front side (page, SW bridge, another tab): a RemoteRuntime over a port. */
|
|
79
|
+
export async function connectRuntime(port, opts) {
|
|
80
|
+
const { call, close } = await connect({
|
|
81
|
+
mux: overPipe(port, { codec: structuredCodec, side: "initiator", maxPorts: maxPorts() }),
|
|
82
|
+
});
|
|
83
|
+
const remote = await remoteRuntime((req) => fetchClosing(call, req), opts);
|
|
84
|
+
return Object.assign(remote, { close: async () => void (await close()) });
|
|
85
|
+
}
|
|
86
|
+
/** Serves any HTTP handler over a port (responder side of a link). */
|
|
87
|
+
export function serveHandlerOverPort(port, handler) {
|
|
88
|
+
return serve({ mux: overPipe(port, { codec: structuredCodec, side: "responder", maxPorts: maxPorts() }) }, servingClosing(serveFetchOverDuplex(handler)));
|
|
89
|
+
}
|
|
90
|
+
/** A `send` over a port (initiator side of a link); the scheme rides in a header. */
|
|
91
|
+
export async function linkOverPort(port) {
|
|
92
|
+
const { call, close } = await connect({
|
|
93
|
+
mux: overPipe(port, { codec: structuredCodec, side: "initiator", maxPorts: maxPorts() }),
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
send: (req) => fetchClosing(call, withScheme(req)),
|
|
97
|
+
close: async () => void (await close()),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/transport/port.ts"],"names":[],"mappings":"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","sourcesContent":["import { fetchOverDuplex, serveFetchOverDuplex } from \"@statewalker/webrun-http-streams\";\nimport { connect, overPipe, serve, structuredCodec } 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 (they are released when an exchange\n * ends, is cancelled or has no body: fetchClosing, servingClosing; p31-port-leak.test.ts).\n * `globalThis.__cellbMaxPorts` overrides it (tests that check nothing leaks set it low).\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 * Cell-host side: serves the runtime's HTTP surface over a port with\n * webrun-rpc (mux + backpressure) and webrun-http-streams (HTTP/1.1 on the\n * Duplex, streaming bodies). Resolves to an async disposer.\n */\nexport function serveRuntime(\n port: CellPort,\n runtime: CellRuntime,\n opts?: CellsHttpOptions,\n): Promise<() => Promise<void>> {\n return serve(\n { mux: overPipe(port, { codec: structuredCodec, side: \"responder\", maxPorts: maxPorts() }) },\n servingClosing(serveFetchOverDuplex(cellsHttpHandler(runtime, opts))),\n ) as Promise<() => Promise<void>>;\n}\n\n/**\n * fetchOverDuplex, draining the call's output when the response has no body (HEAD, 204, 304…):\n * webrun-http-streams then returns without reading the exchange to its end, and the call's\n * multiplexed port stayed open, one per such request, until `maxPorts` (1024) refused calls.\n */\ntype Call = Parameters<typeof fetchOverDuplex>[0];\nasync function fetchClosing(call: Call, req: Request): Promise<Response> {\n let output: AsyncIterable<unknown> | undefined;\n const res = await fetchOverDuplex(\n ((input: unknown) => {\n output = (call as (i: unknown) => AsyncIterable<unknown>)(input);\n return output;\n }) as Call,\n req,\n );\n const drain = () =>\n void (async () => {\n try {\n for await (const _ of output as AsyncIterable<unknown>);\n } catch {}\n })();\n if (!output) return res;\n if (res.body === null) {\n drain();\n return res;\n }\n // The same once the body is read to its end, or cancelled by its reader (then the exchange\n // is abandoned: the call's iterator is returned, which closes its port).\n const reader = (res.body as ReadableStream<Uint8Array>).getReader();\n const body = new ReadableStream<Uint8Array>({\n async pull(ctl) {\n const r = await reader.read();\n if (r.done) {\n ctl.close();\n drain();\n } else ctl.enqueue(r.value);\n },\n async cancel(reason) {\n await reader.cancel(reason).catch(() => {});\n await (output as AsyncGenerator<unknown>).return?.(undefined).catch?.(() => {});\n },\n });\n return Object.assign(new Response(body, res), {\n webSocket: (res as { webSocket?: unknown }).webSocket ?? null,\n });\n}\n\n/**\n * serveFetchOverDuplex, draining what is left of the request side once the response is out:\n * a handler that does not read the request body (a GET) left the exchange half open, and its\n * multiplexed port open, one per request.\n */\ntype Duplex = ReturnType<typeof serveFetchOverDuplex>;\nfunction servingClosing(duplex: Duplex): Duplex {\n return async function* (input: AsyncIterable<unknown>) {\n const it = input[Symbol.asyncIterator]();\n const rest: AsyncIterable<unknown> = { [Symbol.asyncIterator]: () => it };\n try {\n yield* (duplex as (i: AsyncIterable<unknown>) => AsyncIterable<unknown>)(rest);\n } finally {\n void (async () => {\n try {\n for (let r = await it.next(); !r.done; r = await it.next());\n } catch {}\n })();\n }\n } as unknown as Duplex;\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 { call, close } = await connect({\n mux: overPipe(port, { codec: structuredCodec, side: \"initiator\", maxPorts: maxPorts() }),\n });\n const remote = await remoteRuntime((req) => fetchClosing(call, req), opts);\n return Object.assign(remote, { close: async () => void (await 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 serve(\n { mux: overPipe(port, { codec: structuredCodec, side: \"responder\", maxPorts: maxPorts() }) },\n servingClosing(serveFetchOverDuplex(handler)),\n ) as Promise<() => Promise<void>>;\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 { call, close } = await connect({\n mux: overPipe(port, { codec: structuredCodec, side: \"initiator\", maxPorts: maxPorts() }),\n });\n return {\n send: (req) => fetchClosing(call, withScheme(req)),\n close: async () => void (await close()),\n };\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ValueCodec } from "../ports.js";
|
|
2
|
+
import { type HostObjects } from "./serialize.js";
|
|
3
|
+
/** Storage values in V8's serialization format — what celld (and workerd) store. */
|
|
4
|
+
export declare const v8Codec: ValueCodec;
|
|
5
|
+
/** V8 serialization with an embedder's own objects (e.g. stored stubs). */
|
|
6
|
+
export declare const v8CodecWith: (host: HostObjects) => ValueCodec;
|
package/dist/v8/codec.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { deserialize, serialize } from "./serialize.js";
|
|
2
|
+
/** Storage values in V8's serialization format — what celld (and workerd) store. */
|
|
3
|
+
export const v8Codec = { encode: serialize, decode: deserialize };
|
|
4
|
+
/** V8 serialization with an embedder's own objects (e.g. stored stubs). */
|
|
5
|
+
export const v8CodecWith = (host) => ({
|
|
6
|
+
encode: (v) => serialize(v, host),
|
|
7
|
+
decode: (b) => deserialize(b, host),
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/v8/codec.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAoB,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE1E,oFAAoF;AACpF,MAAM,CAAC,MAAM,OAAO,GAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAE9E,2EAA2E;AAC3E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAiB,EAAc,EAAE,CAAC,CAAC;IAC7D,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;IACjC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC;CACpC,CAAC,CAAC","sourcesContent":["import type { ValueCodec } from \"../ports.js\";\nimport { deserialize, type HostObjects, serialize } from \"./serialize.js\";\n\n/** Storage values in V8's serialization format — what celld (and workerd) store. */\nexport const v8Codec: ValueCodec = { encode: serialize, decode: deserialize };\n\n/** V8 serialization with an embedder's own objects (e.g. stored stubs). */\nexport const v8CodecWith = (host: HostObjects): ValueCodec => ({\n encode: (v) => serialize(v, host),\n decode: (b) => deserialize(b, host),\n});\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An embedder's own objects (V8's host objects, tag `\`), written as a length and bytes:
|
|
3
|
+
* `encode` claims a value (bytes) or leaves it (undefined); `decode` restores it.
|
|
4
|
+
*/
|
|
5
|
+
export interface HostObjects {
|
|
6
|
+
encode(v: object): Uint8Array | undefined;
|
|
7
|
+
decode(bytes: Uint8Array): unknown;
|
|
8
|
+
}
|
|
9
|
+
export declare function serialize(value: unknown, host?: HostObjects): Uint8Array;
|
|
10
|
+
export declare function deserialize(bytes: Uint8Array, host?: HostObjects): unknown;
|