@nimbus-sh/worker 0.9.0 → 0.11.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/NOTICE.md +5 -0
- package/README.md +10 -0
- package/dist/_shared/session-router.d.ts +7 -0
- package/dist/_shared/session-router.d.ts.map +1 -1
- package/dist/_shared/session-router.js +7 -0
- package/dist/bindings/body.d.ts +6 -0
- package/dist/bindings/body.d.ts.map +1 -0
- package/dist/bindings/body.js +37 -0
- package/dist/bindings/kv.d.ts +0 -2
- package/dist/bindings/kv.d.ts.map +1 -1
- package/dist/bindings/kv.js +3 -42
- package/dist/bindings/r2.d.ts +0 -2
- package/dist/bindings/r2.d.ts.map +1 -1
- package/dist/bindings/r2.js +4 -44
- package/dist/esbuild-cli-artifact.generated.d.ts +18 -0
- package/dist/esbuild-cli-artifact.generated.d.ts.map +1 -0
- package/dist/esbuild-cli-artifact.generated.js +17 -0
- package/dist/esbuild-wasm-bundle.generated.d.ts +24 -24
- package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
- package/dist/esbuild-wasm-bundle.generated.js +24 -24
- package/dist/facets/cirrus-real.d.ts.map +1 -1
- package/dist/facets/cirrus-real.js +11 -6
- package/dist/facets/compose.d.ts +11 -7
- package/dist/facets/compose.d.ts.map +1 -1
- package/dist/facets/compose.js +8 -31
- package/dist/facets/data-plan.d.ts +79 -0
- package/dist/facets/data-plan.d.ts.map +1 -0
- package/dist/facets/data-plan.js +477 -0
- package/dist/facets/esbuild-bundle-pool.d.ts +1 -1
- package/dist/facets/esbuild-bundle-pool.js +3 -3
- package/dist/facets/esbuild-transform.d.ts +38 -8
- package/dist/facets/esbuild-transform.d.ts.map +1 -1
- package/dist/facets/esbuild-transform.js +219 -19
- package/dist/facets/exec-telemetry.d.ts +7 -3
- package/dist/facets/exec-telemetry.d.ts.map +1 -1
- package/dist/facets/manager.d.ts +198 -69
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +1276 -652
- package/dist/facets/opencode-staging.d.ts +0 -2
- package/dist/facets/opencode-staging.d.ts.map +1 -1
- package/dist/facets/opencode-staging.js +5 -11
- package/dist/facets/process.d.ts +4 -0
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +3 -0
- package/dist/facets/read-profile.d.ts +149 -0
- package/dist/facets/read-profile.d.ts.map +1 -0
- package/dist/facets/read-profile.js +483 -0
- package/dist/facets/real-vite-hmr.d.ts +2 -0
- package/dist/facets/real-vite-hmr.d.ts.map +1 -1
- package/dist/facets/real-vite-hmr.js +3 -2
- package/dist/facets/resident-identity.d.ts +0 -1
- package/dist/facets/resident-identity.d.ts.map +1 -1
- package/dist/facets/resident-identity.js +0 -3
- package/dist/facets/wasm-swap-registry.d.ts +34 -25
- package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
- package/dist/facets/wasm-swap-registry.js +62 -56
- package/dist/facets/ws-terminal.d.ts +4 -4
- package/dist/facets/ws-terminal.d.ts.map +1 -1
- package/dist/facets/ws-terminal.js +4 -5
- package/dist/git/commands.d.ts +33 -10
- package/dist/git/commands.d.ts.map +1 -1
- package/dist/git/commands.js +1891 -197
- package/dist/git/network-facet.d.ts +12 -2
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +69 -18
- package/dist/git/unified-diff.d.ts +74 -0
- package/dist/git/unified-diff.d.ts.map +1 -0
- package/dist/git/unified-diff.js +676 -0
- package/dist/git-bundle.generated.d.ts +7 -11
- package/dist/git-bundle.generated.d.ts.map +1 -1
- package/dist/git-bundle.generated.js +7 -11
- package/dist/hosted/commands.d.ts.map +1 -1
- package/dist/hosted/commands.js +64 -245
- package/dist/hosted/runtime.d.ts +22 -15
- package/dist/hosted/runtime.d.ts.map +1 -1
- package/dist/hosted/runtime.js +72 -14
- package/dist/hosted/services.d.ts +8 -1
- package/dist/hosted/services.d.ts.map +1 -1
- package/dist/hosted/services.js +12 -13
- package/dist/hosted/session.d.ts +165 -0
- package/dist/hosted/session.d.ts.map +1 -0
- package/dist/hosted/session.js +132 -0
- package/dist/loaders/generated-workers.d.ts +6 -3
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +8 -5
- package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
- package/dist/loaders/npm-resolve-preamble.js +0 -27
- package/dist/loaders/pre-bundle-preamble.d.ts +6 -3
- package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -1
- package/dist/loaders/pre-bundle-preamble.js +13 -8
- package/dist/node-shims-artifact.generated.d.ts +13 -4
- package/dist/node-shims-artifact.generated.d.ts.map +1 -1
- package/dist/node-shims-artifact.generated.js +16 -7
- package/dist/npm/bin-links.d.ts +3 -1
- package/dist/npm/bin-links.d.ts.map +1 -1
- package/dist/npm/bin-links.js +32 -8
- package/dist/npm/cache.d.ts +11 -1
- package/dist/npm/cache.d.ts.map +1 -1
- package/dist/npm/cache.js +21 -7
- package/dist/npm/install-batch-facet.d.ts +4 -2
- package/dist/npm/install-batch-facet.d.ts.map +1 -1
- package/dist/npm/install-batch-facet.js +32 -18
- package/dist/npm/installer.d.ts +23 -3
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +330 -131
- package/dist/npm/npx-install.d.ts +3 -1
- package/dist/npm/npx-install.d.ts.map +1 -1
- package/dist/npm/npx-install.js +4 -1
- package/dist/npm/package-lock.d.ts +32 -0
- package/dist/npm/package-lock.d.ts.map +1 -0
- package/dist/npm/package-lock.js +65 -0
- package/dist/npm/placement.d.ts +16 -0
- package/dist/npm/placement.d.ts.map +1 -0
- package/dist/npm/placement.js +33 -0
- package/dist/npm/pre-bundle-facet.d.ts +1 -30
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +3 -5
- package/dist/npm/r2-cache.d.ts +12 -6
- package/dist/npm/r2-cache.d.ts.map +1 -1
- package/dist/npm/r2-cache.js +34 -16
- package/dist/npm/resolve-one-facet.d.ts +3 -0
- package/dist/npm/resolve-one-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.js +15 -3
- package/dist/npm/resolver.d.ts +11 -13
- package/dist/npm/resolver.d.ts.map +1 -1
- package/dist/npm/resolver.js +12 -25
- package/dist/opencode-artifact.generated.js +2 -2
- package/dist/replica/routing.d.ts +2 -38
- package/dist/replica/routing.d.ts.map +1 -1
- package/dist/replica/routing.js +3 -55
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +15 -2
- package/dist/router/public-directory.d.ts +18 -0
- package/dist/router/public-directory.d.ts.map +1 -1
- package/dist/router/public-directory.js +1 -1
- package/dist/router/remote-api.d.ts +3 -1
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +42 -79
- package/dist/runtime/bash-repl.js +2 -2
- package/dist/runtime/bun-repl.d.ts.map +1 -1
- package/dist/runtime/bun-repl.js +2 -13
- package/dist/runtime/cpython-resident.d.ts.map +1 -1
- package/dist/runtime/cpython-resident.js +2 -10
- package/dist/runtime/esbuild-wasm-bytes.d.ts +37 -18
- package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
- package/dist/runtime/esbuild-wasm-bytes.js +66 -92
- package/dist/runtime/facet-loader-host.d.ts.map +1 -1
- package/dist/runtime/facet-loader-host.js +0 -7
- package/dist/runtime/git-bundle-artifact.d.ts +17 -0
- package/dist/runtime/git-bundle-artifact.d.ts.map +1 -0
- package/dist/runtime/git-bundle-artifact.js +25 -0
- package/dist/runtime/node-repl.d.ts.map +1 -1
- package/dist/runtime/node-repl.js +2 -13
- package/dist/runtime/node-shims-artifact.d.ts +22 -23
- package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
- package/dist/runtime/node-shims-artifact.js +48 -78
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +2358 -996
- package/dist/runtime/opencode-artifact.d.ts.map +1 -1
- package/dist/runtime/opencode-artifact.js +17 -53
- package/dist/runtime/opencode-facet-runner.d.ts +3 -6
- package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
- package/dist/runtime/opencode-facet-runner.js +45 -10
- package/dist/runtime/opentui-wasm-bytes.d.ts.map +1 -1
- package/dist/runtime/opentui-wasm-bytes.js +16 -49
- package/dist/runtime/package-manager.d.ts +0 -2
- package/dist/runtime/package-manager.d.ts.map +1 -1
- package/dist/runtime/package-manager.js +0 -3
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +4 -35
- package/dist/runtime/repl-session.d.ts +10 -0
- package/dist/runtime/repl-session.d.ts.map +1 -1
- package/dist/runtime/repl-session.js +7 -0
- package/dist/runtime/ruby-repl.d.ts.map +1 -1
- package/dist/runtime/ruby-repl.js +2 -2
- package/dist/runtime/ruby-resident.d.ts.map +1 -1
- package/dist/runtime/ruby-resident.js +41 -51
- package/dist/runtime/runtime-catalog.d.ts +10 -3
- package/dist/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/runtime/runtime-catalog.js +125 -15
- package/dist/runtime/sqlite-wasm-bytes.d.ts +4 -4
- package/dist/runtime/sqlite-wasm-bytes.d.ts.map +1 -1
- package/dist/runtime/sqlite-wasm-bytes.js +21 -57
- package/dist/runtime/staged-source.d.ts +67 -0
- package/dist/runtime/staged-source.d.ts.map +1 -0
- package/dist/runtime/staged-source.js +113 -0
- package/dist/runtime/wasi/preamble.d.ts +0 -22
- package/dist/runtime/wasi/preamble.d.ts.map +1 -1
- package/dist/runtime/wasi/preamble.js +98 -1425
- package/dist/runtime-catalog.generated.js +1 -1
- package/dist/session/agent.d.ts +14 -2
- package/dist/session/agent.d.ts.map +1 -1
- package/dist/session/agent.js +57 -19
- package/dist/session/ai.d.ts +0 -11
- package/dist/session/ai.d.ts.map +1 -1
- package/dist/session/ai.js +0 -4
- package/dist/session/fs-watch.d.ts +0 -11
- package/dist/session/fs-watch.d.ts.map +1 -1
- package/dist/session/fs-watch.js +0 -21
- package/dist/session/hibernation.d.ts +68 -2
- package/dist/session/hibernation.d.ts.map +1 -1
- package/dist/session/hibernation.js +95 -1
- package/dist/session/init-phases.d.ts +1 -2
- package/dist/session/init-phases.d.ts.map +1 -1
- package/dist/session/init-phases.js +1 -2
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +7 -20
- package/dist/session/keys.d.ts +0 -3
- package/dist/session/keys.d.ts.map +1 -1
- package/dist/session/keys.js +0 -3
- package/dist/session/legacy-reset.d.ts +11 -0
- package/dist/session/legacy-reset.d.ts.map +1 -0
- package/dist/session/legacy-reset.js +16 -0
- package/dist/session/nimbus-session.d.ts +50 -16
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +77 -71
- package/dist/session/npm-install-port.d.ts.map +1 -1
- package/dist/session/npm-install-port.js +2 -0
- package/dist/session/programmatic.d.ts +13 -18
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +53 -39
- package/dist/session/replica-routes.d.ts +2 -3
- package/dist/session/replica-routes.d.ts.map +1 -1
- package/dist/session/replica-routes.js +3 -10
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +11 -12
- package/dist/session/rpc.d.ts +108 -21
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +194 -39
- package/dist/session/start-real-vite.js +2 -2
- package/dist/session/state-store.d.ts +1 -17
- package/dist/session/state-store.d.ts.map +1 -1
- package/dist/session/state-store.js +3 -53
- package/dist/session/supervisor-op.d.ts +7 -0
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +6 -1
- package/dist/session/supervisor-rpc.d.ts +116 -19
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +194 -84
- package/dist/session/vite-command.d.ts.map +1 -1
- package/dist/session/vite-command.js +17 -14
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +20 -8
- package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
- package/dist/shell/npm-bin-entrypoints.js +30 -5
- package/dist/vfs/facet-resident-limits.d.ts +33 -0
- package/dist/vfs/facet-resident-limits.d.ts.map +1 -0
- package/dist/vfs/facet-resident-limits.js +33 -0
- package/dist/vfs/facet-resident-store.d.ts +2 -10
- package/dist/vfs/facet-resident-store.d.ts.map +1 -1
- package/dist/vfs/facet-resident-store.js +1594 -219
- package/dist/workspace-host.d.ts +3 -1
- package/dist/workspace-host.d.ts.map +1 -1
- package/dist/workspace-host.js +2 -0
- package/dist/wrangler/nimbus-wrangler.d.ts +0 -2
- package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
- package/dist/wrangler/nimbus-wrangler.js +8 -22
- package/package.json +34 -7
- package/public/_assets/esbuild-0.24.2.js +2380 -0
- package/public/_assets/opencode/1.16.2/index-attach.js +2 -2
- package/public/_assets/runtime/esbuild-cli-c4fda380d52befc1.js +6892 -0
- package/public/_assets/runtime/git-de3e60c9f3ff006a.js +103 -0
- package/public/_assets/runtime/{node-shims-acaa037e6d26ec0d.js → node-shims-06565b121555c194.js} +2879 -992
- package/public/_assets/runtime/resident-store-6ffb1939c53d982f.js +2264 -0
- package/public/_assets/runtime/vfs-write-ledger-e501e8fc793bc2c1.js +889 -0
- package/scripts/build-opencode-attach-entry.mjs +51 -1
- package/scripts/bundle-esbuild-wasm.mjs +55 -39
- package/scripts/bundle-facet-workers.mjs +159 -10
- package/scripts/bundle-git.mjs +88 -27
- package/scripts/bundle-node-shims.mjs +72 -46
- package/scripts/bundle-runtime.mjs +18 -421
- package/scripts/cf-git-patch.mjs +137 -0
- package/scripts/patch-install-deps.mjs +49 -96
- package/scripts/runtime-specs.mjs +435 -0
- package/vendor/git-http.generated.d.ts +82 -0
- package/vendor/git-upstream.generated.d.ts +4378 -0
- package/vendor/git.LICENSE +7 -0
- package/vendor/git.generated.d.mts +21 -0
- package/vendor/git.generated.mjs +103 -0
- package/dist/replica/suspension.d.ts +0 -36
- package/dist/replica/suspension.d.ts.map +0 -1
- package/dist/replica/suspension.js +0 -49
- package/dist/runtime/static-server.d.ts +0 -3
- package/dist/runtime/static-server.d.ts.map +0 -1
- package/dist/runtime/static-server.js +0 -133
- package/scripts/clean-dist.mjs +0 -7
|
@@ -13,7 +13,7 @@ import { z } from 'zod';
|
|
|
13
13
|
import { buildRubyPreamble, normalizeRubyFacetResult, } from '@nimbus-sh/core/runtime/ruby-runner.js';
|
|
14
14
|
import { VIRTUAL_SOCKET_KERNEL_SRC } from '@nimbus-sh/core/runtime/virtual-socket-kernel.generated.js';
|
|
15
15
|
const RubySocketProcessBootResponseSchema = z.object({
|
|
16
|
-
state: z.
|
|
16
|
+
state: z.enum(['listening', 'exited']),
|
|
17
17
|
port: z.number().optional(),
|
|
18
18
|
stdout: z.string().optional(),
|
|
19
19
|
stderr: z.string().optional(),
|
|
@@ -35,7 +35,11 @@ export function rubyResidentStart(facetMgr) {
|
|
|
35
35
|
// value may carry — so whichever host runs this process reads it itself.
|
|
36
36
|
vfsWasmModules: { 'ruby+stdlib.wasm': args.wasmVfsPath },
|
|
37
37
|
startArgs: args.startArgs,
|
|
38
|
+
foreground: { signal: args.signal, write: args.write },
|
|
38
39
|
}).catch(() => null);
|
|
40
|
+
// Interrupted while it waited to bind or exit: the process is gone with it.
|
|
41
|
+
if (args.signal.aborted)
|
|
42
|
+
return { exitCode: 130, stdout: '', stderr: '' };
|
|
39
43
|
if (!spawned) {
|
|
40
44
|
return { exitCode: 1, stdout: '', stderr: 'ruby process boot failed\n' };
|
|
41
45
|
}
|
|
@@ -69,34 +73,16 @@ export function rubyResidentStart(facetMgr) {
|
|
|
69
73
|
port: routeablePort,
|
|
70
74
|
};
|
|
71
75
|
}
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
spawnedPid: spawned.pid,
|
|
79
|
-
port: reservedPorts[0],
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
if (boot.state === 'exited') {
|
|
83
|
-
const result = normalizeRubyFacetResult(boot.result) || {
|
|
84
|
-
exitCode: 1,
|
|
85
|
-
stdout: '',
|
|
86
|
-
stderr: 'ruby process returned an invalid exit payload\n',
|
|
87
|
-
};
|
|
88
|
-
facetMgr.finishProcess(spawned.pid, result.exitCode, result.stderr || 'ruby process exited');
|
|
89
|
-
return {
|
|
90
|
-
exitCode: result.exitCode,
|
|
91
|
-
stdout: result.stdout,
|
|
92
|
-
stderr: result.stderr || result.error || '',
|
|
93
|
-
};
|
|
94
|
-
}
|
|
76
|
+
const result = normalizeRubyFacetResult(boot.result) || {
|
|
77
|
+
exitCode: 1,
|
|
78
|
+
stdout: '',
|
|
79
|
+
stderr: 'ruby process returned an invalid exit payload\n',
|
|
80
|
+
};
|
|
81
|
+
facetMgr.finishProcess(spawned.pid, result.exitCode, result.stderr || 'ruby process exited');
|
|
95
82
|
return {
|
|
96
|
-
exitCode:
|
|
97
|
-
stdout:
|
|
98
|
-
stderr:
|
|
99
|
-
spawnedPid: spawned.pid,
|
|
83
|
+
exitCode: result.exitCode,
|
|
84
|
+
stdout: result.stdout,
|
|
85
|
+
stderr: result.stderr || result.error || '',
|
|
100
86
|
};
|
|
101
87
|
};
|
|
102
88
|
}
|
|
@@ -205,6 +191,19 @@ export function buildRubySocketProcessWorker(preamble) {
|
|
|
205
191
|
' return true;',
|
|
206
192
|
'};',
|
|
207
193
|
'',
|
|
194
|
+
// What the process writes goes out as it is written: to the command that
|
|
195
|
+
// launched it while that command waits, to the process log after. One
|
|
196
|
+
// write at a time, in order; the runner's own markers are not output.
|
|
197
|
+
'globalThis.__nimbusRubyEmitChain = globalThis.__nimbusRubyEmitChain || Promise.resolve();',
|
|
198
|
+
'globalThis.__nimbusRubyEmit = function __nimbusRubyEmit(stream, text) {',
|
|
199
|
+
' const supervisor = globalThis.__nimbusRubySupervisor;',
|
|
200
|
+
' if (!supervisor) return;',
|
|
201
|
+
' const clean = String(text).replace(/__NIMBUS_RESUMED_(true|false)_[^\\n]*\\n?/g, "").replace(/__NIMBUS_RUBY_EXIT_-?\\d+\\n?/g, "");',
|
|
202
|
+
' if (!clean) return;',
|
|
203
|
+
' const bytes = new TextEncoder().encode(clean);',
|
|
204
|
+
' globalThis.__nimbusRubyEmitChain = globalThis.__nimbusRubyEmitChain.then(() => supervisor[stream](bytes)).catch(() => {});',
|
|
205
|
+
'};',
|
|
206
|
+
'',
|
|
208
207
|
'async function __nimbusStartRubyProcess(args) {',
|
|
209
208
|
' if (!globalThis.__nimbusRubyProcessPromise) {',
|
|
210
209
|
' const stdoutStart = (globalThis.__nimbusRubyStdout || []).length;',
|
|
@@ -216,7 +215,6 @@ export function buildRubySocketProcessWorker(preamble) {
|
|
|
216
215
|
' userEnv: args.userEnv || {},',
|
|
217
216
|
' progName: args.progName || "ruby",',
|
|
218
217
|
' cwd: args.cwd || "/home/user",',
|
|
219
|
-
' fsSnapshot: args.fsSnapshot,',
|
|
220
218
|
' };',
|
|
221
219
|
' globalThis.__nimbusRubyProcessPromise = globalThis.__rubyRun(globalThis.__nimbusRubyProcessArgs).then((result) => {',
|
|
222
220
|
' globalThis.__nimbusRubyProcessResult = result;',
|
|
@@ -224,18 +222,20 @@ export function buildRubySocketProcessWorker(preamble) {
|
|
|
224
222
|
' });',
|
|
225
223
|
' }',
|
|
226
224
|
' const started = globalThis.__nimbusRubyProcessOutputStart || { stdoutStart: 0, stderrStart: 0 };',
|
|
227
|
-
|
|
225
|
+
// No deadline: a server reports once it binds, however long its requires take.
|
|
226
|
+
' const listen = globalThis.__nimbusVirtualSockets.waitForListen().then((port) => ({ state: "listening", port }));',
|
|
228
227
|
' const exit = globalThis.__nimbusRubyProcessPromise.then((result) => ({ state: "exited", result }));',
|
|
229
228
|
' const first = await Promise.race([listen, exit]);',
|
|
230
229
|
' const registrations = globalThis.__nimbusVirtualPortRegistrationPromises || [];',
|
|
231
230
|
' if (registrations.length > 0) await Promise.allSettled(registrations.splice(0));',
|
|
232
|
-
|
|
233
|
-
'
|
|
231
|
+
// Streamed already, so the answer carries none of it.
|
|
232
|
+
' await globalThis.__nimbusRubyEmitChain;',
|
|
233
|
+
' const streamed = !!globalThis.__nimbusRubySupervisor;',
|
|
234
|
+
' const stdout = streamed ? "" : (globalThis.__nimbusRubyStdout || []).slice(started.stdoutStart).join("");',
|
|
235
|
+
' const stderr = streamed ? "" : (globalThis.__nimbusRubyStderr || []).slice(started.stderrStart).join("");',
|
|
234
236
|
' if (first.state === "listening") return { state: "listening", port: first.port, stdout, stderr };',
|
|
235
|
-
'
|
|
236
|
-
'
|
|
237
|
-
' if (currentPort) return { state: "listening", port: currentPort, stdout, stderr };',
|
|
238
|
-
' return { state: "running", stdout, stderr };',
|
|
237
|
+
' const result = streamed ? { ...first.result, stdout: "", stderr: "" } : first.result;',
|
|
238
|
+
' return { state: "exited", result, stdout, stderr };',
|
|
239
239
|
'}',
|
|
240
240
|
'',
|
|
241
241
|
// Only adopt a real binding: routed handleHttpRequest/fetch hops resolve
|
|
@@ -244,25 +244,15 @@ export function buildRubySocketProcessWorker(preamble) {
|
|
|
244
244
|
'function __nimbusAdoptRubySupervisor(env) {',
|
|
245
245
|
' const supervisor = env && env.SUPERVISOR;',
|
|
246
246
|
' if (supervisor) globalThis.__nimbusRubySupervisor = supervisor;',
|
|
247
|
-
// The WASI filesystem takes the same stub
|
|
248
|
-
//
|
|
249
|
-
//
|
|
247
|
+
// The WASI filesystem takes the same stub: every file syscall the server
|
|
248
|
+
// makes reads and writes the session VFS directly, so a server that never
|
|
249
|
+
// exits has nothing to flush between requests.
|
|
250
250
|
' __wasiAdoptSupervisor(supervisor);',
|
|
251
251
|
'}',
|
|
252
|
-
// A resident process parks between requests, and parking is the only
|
|
253
|
-
// moment "durable while running" can be made true: by the time the caller
|
|
254
|
-
// holds a response, everything the request wrote has reached the VFS.
|
|
255
|
-
'async function __nimbusParkRuby(value) {',
|
|
256
|
-
// Revalidate drains first, then spends ONE round trip on the subtree
|
|
257
|
-
// revision. An unchanged subtree keeps the whole cache; a changed one
|
|
258
|
-
// drops the clean half so the next read sees another process's writes.
|
|
259
|
-
' await __wasiRevalidateFS();',
|
|
260
|
-
' return value;',
|
|
261
|
-
'}',
|
|
262
252
|
'export class NimbusProcess extends DurableObject {',
|
|
263
253
|
' async startProcess(args) {',
|
|
264
254
|
' __nimbusAdoptRubySupervisor(this.env);',
|
|
265
|
-
' return
|
|
255
|
+
' return __nimbusStartRubyProcess(args || {});',
|
|
266
256
|
' }',
|
|
267
257
|
' async fetch(request) {',
|
|
268
258
|
' __nimbusAdoptRubySupervisor(this.env);',
|
|
@@ -273,7 +263,7 @@ export function buildRubySocketProcessWorker(preamble) {
|
|
|
273
263
|
' const hinted = Number(request.headers.get("X-Nimbus-Port") || 0);',
|
|
274
264
|
' const port = hinted || Array.from(globalThis.__nimbusVirtualSockets.listeners.keys())[0];',
|
|
275
265
|
' if (!port) return new Response("Nimbus Ruby process has no listening virtual socket", { status: 502 });',
|
|
276
|
-
' return
|
|
266
|
+
' return globalThis.__nimbusVirtualSockets.handleHttpRequest(port, request);',
|
|
277
267
|
' }',
|
|
278
268
|
'}',
|
|
279
269
|
].join('\n');
|
|
@@ -53,6 +53,7 @@ type R2BucketLike = {
|
|
|
53
53
|
get(key: string): Promise<{
|
|
54
54
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
55
55
|
text(): Promise<string>;
|
|
56
|
+
readonly body: ReadableStream<Uint8Array>;
|
|
56
57
|
} | null>;
|
|
57
58
|
} | null | undefined;
|
|
58
59
|
/** Minimal env shape this module consumes. */
|
|
@@ -95,15 +96,21 @@ export declare function fetchCatalog(env: RuntimeCatalogEnv): Promise<RuntimeCat
|
|
|
95
96
|
*/
|
|
96
97
|
export declare function fetchManifest(env: RuntimeCatalogEnv, entry: CatalogVersionEntry): Promise<RuntimeManifest>;
|
|
97
98
|
/**
|
|
98
|
-
*
|
|
99
|
-
* digest that same entry carries
|
|
99
|
+
* Stream the blob a manifest file entry points at, verified against the
|
|
100
|
+
* digest that same entry carries: from the colo cache when it has it, else
|
|
101
|
+
* from R2, read once either way. The stream errors at its end, rather than
|
|
102
|
+
* closing, with a {@link RuntimeBlobDigestMismatch} when its bytes do not
|
|
103
|
+
* hash to the digest; a colo-cache entry that fails is evicted and
|
|
104
|
+
* distrusted first, so the installer's second read of that blob comes from
|
|
105
|
+
* R2 whatever the cache holds by then. The installer commits
|
|
106
|
+
* a blob only after that clean close, so no step holds a blob whole.
|
|
100
107
|
*
|
|
101
108
|
* The digest is not optional and does not travel separately from the key:
|
|
102
109
|
* a `ManifestFile` always has both, and it is the only thing this takes.
|
|
103
110
|
* Blobs are interpreters, so an unverified read here is arbitrary code
|
|
104
111
|
* execution in whichever session installs it.
|
|
105
112
|
*/
|
|
106
|
-
export declare function fetchBlob(env: RuntimeCatalogEnv, file: ManifestFile): Promise<Uint8Array
|
|
113
|
+
export declare function fetchBlob(env: RuntimeCatalogEnv, file: ManifestFile): Promise<ReadableStream<Uint8Array>>;
|
|
107
114
|
export declare function runtimeAbiForCatalogName(name: string): RuntimePackageAbi;
|
|
108
115
|
/**
|
|
109
116
|
* The R2 catalog as a core `RuntimeSource` — the worker adapter half of the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-catalog.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAKH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,6CAA6C,CAAC;AAErD,OAAO,
|
|
1
|
+
{"version":3,"file":"runtime-catalog.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAKH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAOL,KAAK,aAAa,EACnB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,yCAAyC,CAAC;AAEjD,2CAA2C;AAC3C,KAAK,YAAY,GAAG;IAClB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QACxB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;QACxB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;KAC3C,GAAG,IAAI,CAAC,CAAC;CACX,GAAG,IAAI,GAAG,SAAS,CAAC;AAErB,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,oBAAoB,CAAC,EAAE,YAAY,CAAC;CACrC;AAID,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB;2EACuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAC/C;AAiBD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAElE;AA4DD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAuBlF;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,eAAe,CAAC,CA8B1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,iBAAiB,EACtB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CA8BrC;AA2DD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAExE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,iBAAiB,GAAG,aAAa,CAwE1E"}
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
* makes a stale pin a cache miss rather than an outage.
|
|
47
47
|
*/
|
|
48
48
|
import { z } from 'zod/v4';
|
|
49
|
-
import { sha256Hex } from '@nimbus-sh/core/_shared/crypto.js';
|
|
49
|
+
import { sha256Hex, sha256Incremental } from '@nimbus-sh/core/_shared/crypto.js';
|
|
50
50
|
import { RUNTIME_CATALOG_SHA256 } from '../runtime-catalog.generated.js';
|
|
51
51
|
import { HexSha256Schema, parseRuntimeManifest, } from '@nimbus-sh/core/runtime/runtime-manifest.js';
|
|
52
52
|
import { runtimeEntrypoints } from '@nimbus-sh/core/runtime/installed-runtimes.js';
|
|
53
|
-
import { splitRuntimeSpec, SUPERSEDED_RUNTIMES, } from '@nimbus-sh/core/runtime/runtime-package.js';
|
|
53
|
+
import { blobPieces, RuntimeBlobDigestMismatch, splitRuntimeSpec, SUPERSEDED_RUNTIMES, } from '@nimbus-sh/core/runtime/runtime-package.js';
|
|
54
54
|
import { NIMBUS_RUNTIME_ABIS, NATIVE_UNSUPPORTED_ABI, } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
55
55
|
const CatalogVersionEntrySchema = z.object({
|
|
56
56
|
manifest: z.string().min(1),
|
|
@@ -162,8 +162,14 @@ export async function fetchManifest(env, entry) {
|
|
|
162
162
|
return parseRuntimeManifest(parseJsonBytes(bytes));
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
165
|
-
*
|
|
166
|
-
* digest that same entry carries
|
|
165
|
+
* Stream the blob a manifest file entry points at, verified against the
|
|
166
|
+
* digest that same entry carries: from the colo cache when it has it, else
|
|
167
|
+
* from R2, read once either way. The stream errors at its end, rather than
|
|
168
|
+
* closing, with a {@link RuntimeBlobDigestMismatch} when its bytes do not
|
|
169
|
+
* hash to the digest; a colo-cache entry that fails is evicted and
|
|
170
|
+
* distrusted first, so the installer's second read of that blob comes from
|
|
171
|
+
* R2 whatever the cache holds by then. The installer commits
|
|
172
|
+
* a blob only after that clean close, so no step holds a blob whole.
|
|
167
173
|
*
|
|
168
174
|
* The digest is not optional and does not travel separately from the key:
|
|
169
175
|
* a `ManifestFile` always has both, and it is the only thing this takes.
|
|
@@ -175,9 +181,15 @@ export async function fetchBlob(env, file) {
|
|
|
175
181
|
if (!address) {
|
|
176
182
|
throw new Error(`manifest entry ${file.path} has no usable sha256 (${file.sha256})`);
|
|
177
183
|
}
|
|
178
|
-
const cached = await
|
|
179
|
-
if (cached)
|
|
180
|
-
return cached
|
|
184
|
+
const cached = l2Distrusted.has(address.sha256) ? null : await l2GetBody(address);
|
|
185
|
+
if (cached) {
|
|
186
|
+
return verifiedRead(address, cached, null, async (actual) => {
|
|
187
|
+
l2Distrusted.add(address.sha256);
|
|
188
|
+
await l2Evict(address);
|
|
189
|
+
return `sha256 mismatch for blob ${file.content}: manifest expects ${address.sha256}, `
|
|
190
|
+
+ `the colo cache held ${actual} and was evicted`;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
181
193
|
const r2 = env.NIMBUS_RUNTIME_CACHE;
|
|
182
194
|
if (!r2) {
|
|
183
195
|
throw new Error('NIMBUS_RUNTIME_CACHE binding missing — blob cannot be fetched');
|
|
@@ -186,14 +198,59 @@ export async function fetchBlob(env, file) {
|
|
|
186
198
|
if (!obj) {
|
|
187
199
|
throw new Error(`blob ${file.content} not in R2 — manifest references a missing blob`);
|
|
188
200
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
return verifiedRead(address, obj.body, l2FillWriter(address), (actual) => `sha256 mismatch for blob ${file.content}: manifest expects ${address.sha256}, R2 holds ${actual}`);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* `body` as a stream that errors at its end, with a
|
|
205
|
+
* {@link RuntimeBlobDigestMismatch}, rather than closing, unless it hashed to
|
|
206
|
+
* `address`. It is read once. When `fill` is given, the same read feeds L2:
|
|
207
|
+
* each piece reaches the cache before the consumer, and the cache entry is
|
|
208
|
+
* closed only after the digest matched; a mismatch, a failed read or an
|
|
209
|
+
* abandoned stream aborts it, and an aborted body stores nothing. The
|
|
210
|
+
* cache's own pace bounds the pair, so neither side buffers what the other
|
|
211
|
+
* lags by.
|
|
212
|
+
*/
|
|
213
|
+
function verifiedRead(address, body, fill, mismatch) {
|
|
214
|
+
const pieces = blobPieces(body);
|
|
215
|
+
const digest = sha256Incremental();
|
|
216
|
+
const drop = (reason) => {
|
|
217
|
+
fill?.abort(reason).catch(() => { });
|
|
218
|
+
fill = null;
|
|
219
|
+
};
|
|
220
|
+
return new ReadableStream({
|
|
221
|
+
async pull(controller) {
|
|
222
|
+
try {
|
|
223
|
+
const next = await pieces.next();
|
|
224
|
+
if (next.done) {
|
|
225
|
+
const actual = await digest.hex();
|
|
226
|
+
if (actual !== address.sha256)
|
|
227
|
+
throw new RuntimeBlobDigestMismatch(await mismatch(actual));
|
|
228
|
+
fill?.close().catch(() => { });
|
|
229
|
+
controller.close();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
await digest.update(next.value);
|
|
233
|
+
if (fill) {
|
|
234
|
+
try {
|
|
235
|
+
await fill.ready;
|
|
236
|
+
fill.write(next.value).catch(() => { });
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
drop(error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
controller.enqueue(next.value);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
drop(error);
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
async cancel(reason) {
|
|
250
|
+
drop(reason);
|
|
251
|
+
await pieces.return(undefined);
|
|
252
|
+
},
|
|
253
|
+
}, { highWaterMark: 0 });
|
|
197
254
|
}
|
|
198
255
|
// ── RuntimeSource ────────────────────────────────────────────────────
|
|
199
256
|
export function runtimeAbiForCatalogName(name) {
|
|
@@ -314,6 +371,59 @@ async function l2Put(verified, contentType) {
|
|
|
314
371
|
}
|
|
315
372
|
catch { /* best-effort */ }
|
|
316
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* The body of the blob entry at `address`, or null on a miss or a stripped
|
|
376
|
+
* Cache API. Unverified: served only through {@link verifiedRead}, whose
|
|
377
|
+
* mismatch evicts it and sends later reads of that blob to R2. Hashing an entry before serving it would read every
|
|
378
|
+
* blob from the cache twice, which was most of an install's time.
|
|
379
|
+
*/
|
|
380
|
+
async function l2GetBody(address) {
|
|
381
|
+
try {
|
|
382
|
+
const caches = globalThis.caches;
|
|
383
|
+
if (!caches?.default)
|
|
384
|
+
return null;
|
|
385
|
+
const hit = await caches.default.match(new Request(l2Url(address)));
|
|
386
|
+
return hit?.ok && hit.body ? hit.body : null;
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Blob digests whose colo-cache entry failed verification in this isolate.
|
|
394
|
+
* Their reads go to R2 from then on: a purge lands some time after it is
|
|
395
|
+
* asked for, and a shared cache can be poisoned again, so the installer's
|
|
396
|
+
* second read of a failed blob must not depend on either.
|
|
397
|
+
*/
|
|
398
|
+
const l2Distrusted = new Set();
|
|
399
|
+
/** Drop the entry at `address` from the colo cache for other isolates. */
|
|
400
|
+
async function l2Evict(address) {
|
|
401
|
+
try {
|
|
402
|
+
await globalThis.caches?.default?.delete(new Request(l2Url(address)));
|
|
403
|
+
}
|
|
404
|
+
catch { /* best-effort */ }
|
|
405
|
+
}
|
|
406
|
+
/** A writer whose bytes become the L2 entry at `address` when it closes;
|
|
407
|
+
* aborting it stores nothing. Null where there is no cache. */
|
|
408
|
+
function l2FillWriter(address) {
|
|
409
|
+
try {
|
|
410
|
+
const caches = globalThis.caches;
|
|
411
|
+
if (!caches?.default)
|
|
412
|
+
return null;
|
|
413
|
+
const { readable, writable } = new TransformStream();
|
|
414
|
+
const resp = new Response(readable, {
|
|
415
|
+
headers: {
|
|
416
|
+
'Content-Type': 'application/octet-stream',
|
|
417
|
+
'Cache-Control': 'public, max-age=31536000, immutable',
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
caches.default.put(new Request(l2Url(address)), resp).catch(() => { });
|
|
421
|
+
return writable.getWriter();
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
317
427
|
function l2Url(address) {
|
|
318
428
|
return `${L2_NS}/${address.scope}/${address.sha256}`;
|
|
319
429
|
}
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* via its `instantiateWasm` hook with that pre-compiled module —
|
|
11
11
|
* request-time WebAssembly.compile(bytes) is blocked inside facets.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* NO module-scope cache (no supervisor residency). The read itself is
|
|
14
|
+
* runtime/staged-source.ts: L2 keyed by the version-pinned asset URL, ASSETS
|
|
15
|
+
* as the source of truth, and a sha-256 check on both tiers so a stale or
|
|
16
|
+
* tampered asset never gets compiled.
|
|
17
17
|
*/
|
|
18
18
|
/**
|
|
19
19
|
* Path inside env.ASSETS where the sql.js wasm binary lives. Versioned so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-wasm-bytes.d.ts","sourceRoot":"","sources":["../../src/runtime/sqlite-wasm-bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"sqlite-wasm-bytes.d.ts","sourceRoot":"","sources":["../../src/runtime/sqlite-wasm-bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,+BAAyC,CAAC;AAE7E,kEAAkE;AAClE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC;CACpD;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,2DAAqE,CAAC;AAErG;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAElF"}
|
|
@@ -10,15 +10,14 @@
|
|
|
10
10
|
* via its `instantiateWasm` hook with that pre-compiled module —
|
|
11
11
|
* request-time WebAssembly.compile(bytes) is blocked inside facets.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* NO module-scope cache (no supervisor residency). The read itself is
|
|
14
|
+
* runtime/staged-source.ts: L2 keyed by the version-pinned asset URL, ASSETS
|
|
15
|
+
* as the source of truth, and a sha-256 check on both tiers so a stale or
|
|
16
|
+
* tampered asset never gets compiled.
|
|
17
17
|
*/
|
|
18
18
|
import { SQLJS_VERSION } from '@nimbus-sh/core/constants.js';
|
|
19
19
|
import { SQLITE_WASM_SHA256 } from '../sqlite-wasm-bundle.generated.js';
|
|
20
|
-
import {
|
|
21
|
-
import { sha256Hex } from '@nimbus-sh/core/_shared/crypto.js';
|
|
20
|
+
import { fetchStagedBytes } from './staged-source.js';
|
|
22
21
|
/**
|
|
23
22
|
* Path inside env.ASSETS where the sql.js wasm binary lives. Versioned so
|
|
24
23
|
* a future sql.js bump produces a different asset name and forces a fresh
|
|
@@ -38,55 +37,20 @@ export const SQLITE_WASM_L2_KEY = `https://nimbus-cache.invalid/_assets/sqljs-${
|
|
|
38
37
|
* failures are silent — ASSETS is always the source of truth. A non-200
|
|
39
38
|
* from ASSETS, or a digest mismatch on either tier, throws.
|
|
40
39
|
*/
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
// ── L2 fast path ────────────────────────────────────────────────
|
|
44
|
-
let ab = null;
|
|
45
|
-
try {
|
|
46
|
-
if (caches?.default) {
|
|
47
|
-
const hit = await caches.default.match(new Request(SQLITE_WASM_L2_KEY));
|
|
48
|
-
if (hit && hit.ok)
|
|
49
|
-
ab = await hit.arrayBuffer();
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
catch { /* fall through to ASSETS */ }
|
|
53
|
-
const fromCache = ab !== null;
|
|
54
|
-
if (!ab) {
|
|
55
|
-
// ── ASSETS path ───────────────────────────────────────────────
|
|
56
|
-
const url = `https://nimbus-internal.invalid${SQLITE_WASM_ASSET_PATH}`;
|
|
57
|
-
const res = await env.ASSETS.fetch(new Request(url));
|
|
58
|
-
try {
|
|
59
|
-
if (!res.ok) {
|
|
60
|
-
throw new Error(`sql.js wasm asset fetch failed: ${res.status} ${res.statusText} ` +
|
|
61
|
-
`for ${SQLITE_WASM_ASSET_PATH} — deploy is missing the wasm asset`);
|
|
62
|
-
}
|
|
63
|
-
ab = await res.arrayBuffer();
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
disposeRpcResource(res);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
const digest = await sha256Hex(ab);
|
|
70
|
-
if (digest !== SQLITE_WASM_SHA256) {
|
|
71
|
-
throw new Error(`sql.js wasm integrity check failed: expected ${SQLITE_WASM_SHA256}, got ` +
|
|
72
|
-
`${digest} (${fromCache ? 'L2 cache' : 'ASSETS'}) for ${SQLITE_WASM_ASSET_PATH} — ` +
|
|
73
|
-
'the staged asset is corrupt or out of sync; rerun ' +
|
|
74
|
-
'scripts/bundle-sqlite-wasm.mjs and redeploy');
|
|
75
|
-
}
|
|
76
|
-
// ── L2 write-back (best-effort) ─────────────────────────────────
|
|
77
|
-
if (!fromCache) {
|
|
78
|
-
try {
|
|
79
|
-
if (caches?.default) {
|
|
80
|
-
const writeBack = new Response(new Uint8Array(ab), {
|
|
81
|
-
headers: {
|
|
82
|
-
'Content-Type': 'application/wasm',
|
|
83
|
-
'Cache-Control': 'public, max-age=31536000, immutable',
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
await caches.default.put(new Request(SQLITE_WASM_L2_KEY), writeBack);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
catch { /* silent */ }
|
|
90
|
-
}
|
|
91
|
-
return ab;
|
|
40
|
+
export function fetchSqliteWasmBytes(env) {
|
|
41
|
+
return fetchStagedBytes(env, SQLITE_WASM_ASSET);
|
|
92
42
|
}
|
|
43
|
+
const SQLITE_WASM_ASSET = {
|
|
44
|
+
path: SQLITE_WASM_ASSET_PATH,
|
|
45
|
+
l2Key: SQLITE_WASM_L2_KEY,
|
|
46
|
+
sha256: SQLITE_WASM_SHA256,
|
|
47
|
+
contentType: 'application/wasm',
|
|
48
|
+
poisonedCache: 'reject',
|
|
49
|
+
missingBinding: `Nimbus: node:sqlite requires an env.ASSETS binding (serves ${SQLITE_WASM_ASSET_PATH})`,
|
|
50
|
+
fetchFailed: (res) => `sql.js wasm asset fetch failed: ${res.status} ${res.statusText} ` +
|
|
51
|
+
`for ${SQLITE_WASM_ASSET_PATH} — deploy is missing the wasm asset`,
|
|
52
|
+
integrityFailed: (digest, from) => `sql.js wasm integrity check failed: expected ${SQLITE_WASM_SHA256}, got ` +
|
|
53
|
+
`${digest} (${from}) for ${SQLITE_WASM_ASSET_PATH} — ` +
|
|
54
|
+
'the staged asset is corrupt or out of sync; rerun ' +
|
|
55
|
+
'scripts/bundle-sqlite-wasm.mjs and redeploy',
|
|
56
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* staged-source.ts — the one supervisor-side read of a build artifact a bundle
|
|
3
|
+
* script staged under public/_assets/ instead of inlining it in the Worker
|
|
4
|
+
* bundle (wasm modules, facet source texts).
|
|
5
|
+
*
|
|
6
|
+
* The staging script pins each artifact by sha-256 in a `.generated.ts`. This
|
|
7
|
+
* module reads it through L2 (caches.default) under a key that changes with
|
|
8
|
+
* the bytes, with ASSETS as the source of truth, and hands out nothing that
|
|
9
|
+
* fails the pinned digest: a stale or partial asset never reaches workerd's
|
|
10
|
+
* loader or a facet. L2 is written only with verified bytes read from ASSETS.
|
|
11
|
+
*
|
|
12
|
+
* Artifacts differ only in what they name in errors and in what a bad L2 entry
|
|
13
|
+
* means to their caller (`poisonedCache`), so each passes those in.
|
|
14
|
+
*/
|
|
15
|
+
/** Minimal env shape — any env with an ASSETS Fetcher binding. */
|
|
16
|
+
export interface StagedSourceEnv {
|
|
17
|
+
ASSETS?: {
|
|
18
|
+
fetch(req: Request): Promise<Response>;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** One staged artifact, as its `.generated.ts` pins it. */
|
|
22
|
+
export interface StagedAsset {
|
|
23
|
+
/** Asset path ASSETS serves, e.g. `/_assets/runtime/node-shims-<buildId>.js`. */
|
|
24
|
+
path: string;
|
|
25
|
+
/** L2 key; carries the build id or version, so it changes with the bytes. */
|
|
26
|
+
l2Key: string;
|
|
27
|
+
/** Full digest the bytes must match. */
|
|
28
|
+
sha256: string;
|
|
29
|
+
/** Content-Type of the L2 copy, where the artifact has one. */
|
|
30
|
+
contentType?: string;
|
|
31
|
+
/**
|
|
32
|
+
* An L2 entry that fails the digest: `reject` throws, `refetch` deletes the
|
|
33
|
+
* entry and reads ASSETS instead (whose bytes then replace it).
|
|
34
|
+
*/
|
|
35
|
+
poisonedCache: 'reject' | 'refetch';
|
|
36
|
+
/** Message for a deploy with no ASSETS binding. */
|
|
37
|
+
missingBinding: string;
|
|
38
|
+
/** Message for a non-OK ASSETS response. */
|
|
39
|
+
fetchFailed(res: Response): string;
|
|
40
|
+
/** Message for bytes that do not match `sha256`. */
|
|
41
|
+
integrityFailed(digest: string, from: 'L2 cache' | 'ASSETS'): string;
|
|
42
|
+
}
|
|
43
|
+
/** Fetch a staged artifact's bytes and verify them against its pinned digest. */
|
|
44
|
+
export declare function fetchStagedBytes(env: StagedSourceEnv, asset: StagedAsset): Promise<ArrayBuffer>;
|
|
45
|
+
/** {@link fetchStagedBytes}, decoded as UTF-8. */
|
|
46
|
+
export declare function fetchStagedText(env: StagedSourceEnv, asset: StagedAsset): Promise<string>;
|
|
47
|
+
/**
|
|
48
|
+
* One load per isolate: every caller shares the first call's promise, and a
|
|
49
|
+
* rejected one is dropped so the next call loads again instead of pinning the
|
|
50
|
+
* error.
|
|
51
|
+
*/
|
|
52
|
+
export declare function memoizeUntilRejected<A, T>(load: (arg: A) => Promise<T>): (arg: A) => Promise<T>;
|
|
53
|
+
/**
|
|
54
|
+
* The `StagedAsset` of a facet source text staged under
|
|
55
|
+
* public/_assets/runtime/ by `stagedBy` and needed by `requiredBy`: a bad L2
|
|
56
|
+
* entry is replaced from ASSETS, since every later fetch in the colo for the
|
|
57
|
+
* build would otherwise fail on it.
|
|
58
|
+
*/
|
|
59
|
+
export declare function stagedRuntimeSource(source: {
|
|
60
|
+
label: string;
|
|
61
|
+
entry: string;
|
|
62
|
+
buildId: string;
|
|
63
|
+
sha256: string;
|
|
64
|
+
stagedBy: string;
|
|
65
|
+
requiredBy: string;
|
|
66
|
+
}): StagedAsset;
|
|
67
|
+
//# sourceMappingURL=staged-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staged-source.d.ts","sourceRoot":"","sources":["../../src/runtime/staged-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,kEAAkE;AAClE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC;CACrD;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;IACpC,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,oDAAoD;IACpD,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;CACtE;AAED,iFAAiF;AACjF,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA2CrG;AAED,kDAAkD;AAClD,wBAAsB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAE/F;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAU/F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,WAAW,CAkBd"}
|