@nimbus-sh/worker 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cirrus-npm-cjs.generated.d.ts +1 -1
- package/dist/cirrus-npm-cjs.generated.js +1 -1
- package/dist/facets/cirrus-real.d.ts.map +1 -1
- package/dist/facets/cirrus-real.js +6 -5
- package/dist/facets/compose.d.ts +148 -0
- package/dist/facets/compose.d.ts.map +1 -0
- package/dist/facets/compose.js +146 -0
- package/dist/facets/durable-images.d.ts +9 -5
- package/dist/facets/durable-images.d.ts.map +1 -1
- package/dist/facets/durable-images.js +17 -7
- package/dist/facets/esbuild-bundle-pool.d.ts +49 -0
- package/dist/facets/esbuild-bundle-pool.d.ts.map +1 -0
- package/dist/facets/esbuild-bundle-pool.js +122 -0
- package/dist/facets/exec-telemetry.d.ts +8 -0
- package/dist/facets/exec-telemetry.d.ts.map +1 -1
- package/dist/facets/manager.d.ts +240 -109
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +677 -342
- package/dist/facets/process.d.ts +20 -16
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +59 -28
- package/dist/facets/real-vite-fs-shim.js +2 -2
- package/dist/facets/real-vite-hmr.d.ts +3 -1
- package/dist/facets/real-vite-hmr.d.ts.map +1 -1
- package/dist/facets/real-vite-hmr.js +18 -8
- package/dist/facets/vite-dev-server.d.ts +25 -47
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +185 -219
- package/dist/facets/wasm-image-digest.d.ts +25 -0
- package/dist/facets/wasm-image-digest.d.ts.map +1 -0
- package/dist/facets/wasm-image-digest.js +25 -0
- package/dist/facets/wasm-swap-registry.d.ts +51 -92
- package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
- package/dist/facets/wasm-swap-registry.js +68 -227
- package/dist/facets/ws-terminal.d.ts +9 -31
- package/dist/facets/ws-terminal.d.ts.map +1 -1
- package/dist/facets/ws-terminal.js +58 -51
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +6 -5
- package/dist/hosted/commands.d.ts +12 -0
- package/dist/hosted/commands.d.ts.map +1 -0
- package/dist/hosted/commands.js +1658 -0
- package/dist/hosted/runtime.d.ts +114 -0
- package/dist/hosted/runtime.d.ts.map +1 -0
- package/dist/hosted/runtime.js +288 -0
- package/dist/hosted/services.d.ts +40 -0
- package/dist/hosted/services.d.ts.map +1 -0
- package/dist/hosted/services.js +512 -0
- package/dist/loaders/child-process/spawn-pool.d.ts +3 -2
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-pool.js +3 -2
- package/dist/loaders/generated-workers.d.ts +2 -2
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +3 -3
- package/dist/loaders/npm-resolve-preamble.d.ts +3 -6
- package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
- package/dist/loaders/npm-resolve-preamble.js +34 -108
- package/dist/node-shims-artifact.generated.js +3 -3
- package/dist/npm/install-batch-facet.d.ts +3 -2
- package/dist/npm/install-batch-facet.d.ts.map +1 -1
- package/dist/npm/install-batch-facet.js +45 -15
- package/dist/npm/installer.d.ts +22 -17
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +209 -193
- package/dist/npm/pre-bundle-facet.d.ts +1 -1
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +1 -1
- package/dist/npm/resolve-facet.d.ts +6 -0
- package/dist/npm/resolve-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.d.ts +21 -20
- package/dist/npm/resolve-one-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.js +85 -84
- package/dist/npm/semver.d.ts +59 -0
- package/dist/npm/semver.d.ts.map +1 -0
- package/dist/npm/semver.js +226 -0
- package/dist/router/remote-api.js +31 -1
- package/dist/runtime/bash-repl.d.ts +1 -1
- package/dist/runtime/bash-repl.d.ts.map +1 -1
- package/dist/runtime/bash-repl.js +38 -4
- package/dist/runtime/bun-runner.js +1 -1
- package/dist/runtime/html-entrypoint.d.ts +7 -0
- package/dist/runtime/html-entrypoint.d.ts.map +1 -1
- package/dist/runtime/html-entrypoint.js +18 -2
- package/dist/runtime/node-runner.d.ts +4 -1
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-runner.js +9 -2
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +305 -29
- package/dist/runtime/opencode-facet-runner.js +16 -16
- package/dist/runtime/package-manager.d.ts +35 -122
- package/dist/runtime/package-manager.d.ts.map +1 -1
- package/dist/runtime/package-manager.js +61 -596
- package/dist/runtime/process-logs-api.d.ts +1 -1
- package/dist/runtime/process-logs-api.d.ts.map +1 -1
- package/dist/runtime/process-logs-api.js +1 -1
- package/dist/runtime/python-repl.d.ts +2 -1
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +82 -59
- package/dist/runtime/repl-session.d.ts +9 -47
- package/dist/runtime/repl-session.d.ts.map +1 -1
- package/dist/runtime/repl-session.js +105 -105
- package/dist/runtime/runtime-catalog.d.ts +13 -0
- package/dist/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/runtime/runtime-catalog.js +88 -0
- package/dist/session/agent.d.ts +1 -21
- package/dist/session/agent.d.ts.map +1 -1
- package/dist/session/ai.d.ts +5 -1
- package/dist/session/ai.d.ts.map +1 -1
- package/dist/session/ai.js +7 -0
- package/dist/session/helpers.d.ts +38 -0
- package/dist/session/helpers.d.ts.map +1 -1
- package/dist/session/helpers.js +76 -0
- package/dist/session/hibernation.d.ts +2 -1
- package/dist/session/hibernation.d.ts.map +1 -1
- package/dist/session/hibernation.js +7 -4
- package/dist/session/init-phases.d.ts +1 -1
- package/dist/session/init-phases.d.ts.map +1 -1
- package/dist/session/init-phases.js +2 -12
- package/dist/session/init.d.ts +15 -36
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +50 -2327
- package/dist/session/loopback.d.ts +2 -29
- package/dist/session/loopback.d.ts.map +1 -1
- package/dist/session/loopback.js +3 -27
- package/dist/session/nimbus-session.d.ts +77 -34
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +126 -514
- package/dist/session/npm-install-port.d.ts +18 -0
- package/dist/session/npm-install-port.d.ts.map +1 -0
- package/dist/session/npm-install-port.js +37 -0
- package/dist/session/port-capability.d.ts +44 -0
- package/dist/session/port-capability.d.ts.map +1 -1
- package/dist/session/port-capability.js +110 -0
- package/dist/session/programmatic.d.ts +36 -43
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +109 -115
- package/dist/session/routes.d.ts +30 -17
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +42 -131
- package/dist/session/rpc.d.ts +15 -15
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +98 -49
- package/dist/session/serving-port.d.ts +27 -0
- package/dist/session/serving-port.d.ts.map +1 -0
- package/dist/session/serving-port.js +27 -0
- package/dist/session/start-real-vite.d.ts +12 -0
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +4 -4
- package/dist/session/supervisor-op.d.ts +4 -4
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +24 -10
- package/dist/session/supervisor-rpc.d.ts +7 -7
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +4 -0
- package/dist/session/vite-command.d.ts +18 -0
- package/dist/session/vite-command.d.ts.map +1 -0
- package/dist/session/vite-command.js +609 -0
- package/dist/session/ws.d.ts +61 -0
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +122 -12
- package/dist/shell/npm-bin-entrypoints.d.ts +0 -35
- package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
- package/dist/shell/npm-bin-entrypoints.js +12 -85
- package/dist/workspace-host.d.ts +6 -0
- package/dist/workspace-host.d.ts.map +1 -0
- package/dist/workspace-host.js +5 -0
- package/dist/workspace.d.ts +2 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +1 -0
- package/package.json +34 -9
- package/public/_assets/runtime/{node-shims-9e1380d9ee430fcc.js → node-shims-8f8c64edd2a1f0f5.js} +305 -29
- package/public/index.html +1 -1
- package/scripts/bundle-facet-workers.mjs +1 -1
- package/scripts/bundle-npm-cjs.mjs +2 -2
- package/dist/facets/on-demand-bundle-gate.d.ts +0 -65
- package/dist/facets/on-demand-bundle-gate.d.ts.map +0 -1
- package/dist/facets/on-demand-bundle-gate.js +0 -125
- package/dist/npm/install-args.d.ts +0 -21
- package/dist/npm/install-args.d.ts.map +0 -1
- package/dist/npm/install-args.js +0 -70
- package/dist/npm/npm-log.d.ts +0 -31
- package/dist/npm/npm-log.d.ts.map +0 -1
- package/dist/npm/npm-log.js +0 -51
package/README.md
CHANGED
|
@@ -216,8 +216,8 @@ config })`. The route requires a valid Nimbus JWT and `sandbox:use` scope.
|
|
|
216
216
|
For an app server, start the process and expose its virtual port:
|
|
217
217
|
|
|
218
218
|
```ts
|
|
219
|
-
await box.files.write('/home/user/app/server.js', serverSource);
|
|
220
|
-
const proc = await box.startProcess('node --watch /home/user/app/server.js');
|
|
219
|
+
await box.files.write('/home/user/example-app/server.js', serverSource);
|
|
220
|
+
const proc = await box.startProcess('node --watch /home/user/example-app/server.js');
|
|
221
221
|
const port = await box.ports.expose(3000);
|
|
222
222
|
// proc.pid is live immediately: box.processes.logs(proc.pid) streams output,
|
|
223
223
|
// box.processes.kill(proc.pid) stops it
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* (react, react-dom, scheduler). Ships as a static Worker asset alongside
|
|
7
7
|
* cirrus-plugin-react.
|
|
8
8
|
* Consumed by src/cirrus-real.ts — injected as LOADER modules, and
|
|
9
|
-
* the Vite resolver's output (e.g. /home/user/app/node_modules/react/index.js)
|
|
9
|
+
* the Vite resolver's output (e.g. /home/user/example-app/node_modules/react/index.js)
|
|
10
10
|
* is rewritten to point at one of these bundles at request time.
|
|
11
11
|
*/
|
|
12
12
|
import { type AssetsFetcher } from '@nimbus-sh/core/runtime/assets-loader.js';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* (react, react-dom, scheduler). Ships as a static Worker asset alongside
|
|
7
7
|
* cirrus-plugin-react.
|
|
8
8
|
* Consumed by src/cirrus-real.ts — injected as LOADER modules, and
|
|
9
|
-
* the Vite resolver's output (e.g. /home/user/app/node_modules/react/index.js)
|
|
9
|
+
* the Vite resolver's output (e.g. /home/user/example-app/node_modules/react/index.js)
|
|
10
10
|
* is rewritten to point at one of these bundles at request time.
|
|
11
11
|
*/
|
|
12
12
|
import { loadAssetText } from '@nimbus-sh/core/runtime/assets-loader.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cirrus-real.d.ts","sourceRoot":"","sources":["../../src/facets/cirrus-real.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAyBH,OAAO,EACL,SAAS,EAIV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AAmBtF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD,UAAU,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,mBAAmB,CAAC,GAAG,SAAS,CAAC;CACpF,GAAG,OAAO,CAiBV;AA0cD,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,mBAAmB,CAAyB;IAEpD,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,UAAU,CAAa;IAC/B;6BACyB;IACzB,OAAO,CAAC,UAAU,CAAuB;IACzC;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK,CAAgD;IAC7D;;;;4CAIwC;IACxC,OAAO,CAAC,gBAAgB,CAAa;IACrC;2BACuB;IACvB,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,cAAc,CAON;IAEhB,kEAAkE;IAC3D,GAAG,EAAE,SAAS,CAAmB;IAExC,6CAA6C;IAC7C,OAAO,CAAC,SAAS,CAA6B;gBAElC,IAAI,EAAE;QAChB,GAAG,EAAE,GAAG,CAAC;QACT,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,SAAS,CAAC;QACf,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;QACnC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC9C;IAWD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBnC;IAED;;;iEAG6D;IAC7D,OAAO,CAAC,YAAY,CAAc;IAE5B,KAAK,CAAC,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"cirrus-real.d.ts","sourceRoot":"","sources":["../../src/facets/cirrus-real.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAyBH,OAAO,EACL,SAAS,EAIV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AAmBtF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD,UAAU,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,mBAAmB,CAAC,GAAG,SAAS,CAAC;CACpF,GAAG,OAAO,CAiBV;AA0cD,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,mBAAmB,CAAyB;IAEpD,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,UAAU,CAAa;IAC/B;6BACyB;IACzB,OAAO,CAAC,UAAU,CAAuB;IACzC;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK,CAAgD;IAC7D;;;;4CAIwC;IACxC,OAAO,CAAC,gBAAgB,CAAa;IACrC;2BACuB;IACvB,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,cAAc,CAON;IAEhB,kEAAkE;IAC3D,GAAG,EAAE,SAAS,CAAmB;IAExC,6CAA6C;IAC7C,OAAO,CAAC,SAAS,CAA6B;gBAElC,IAAI,EAAE;QAChB,GAAG,EAAE,GAAG,CAAC;QACT,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,SAAS,CAAC;QACf,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;QACnC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC9C;IAWD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBnC;IAED;;;iEAG6D;IAC7D,OAAO,CAAC,YAAY,CAAc;IAE5B,KAAK,CAAC,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoQhE,IAAI,CAAC,GAAG,CAAC,EAAE,kBAAkB,GAAG,IAAI;IAqBpC;;;;OAIG;IACH,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM;IAItC,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIjC,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAItD;;;;OAIG;IACG,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IA8C1E;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAsBzD"}
|
|
@@ -741,11 +741,12 @@ export class CirrusReal {
|
|
|
741
741
|
// [D'.1] Spawn cirrus-real as a DO Facet, not a Worker.
|
|
742
742
|
//
|
|
743
743
|
// Step A: LOADER.get(stableId, configCb) → DynamicWorker stub. The
|
|
744
|
-
// stable ID per-(supervisor-DO + cirrus-real-version) makes the
|
|
744
|
+
// stable ID per-(supervisor-DO + cirrus-real-version + pid) makes the
|
|
745
745
|
// loader cache the bundled vite + plugin code across hibernation.
|
|
746
|
-
//
|
|
747
|
-
//
|
|
748
|
-
//
|
|
746
|
+
// The pid is in the key because env.SUPERVISOR bakes it — a wake
|
|
747
|
+
// strides pids, and a warm worker credentialed to the dead pid
|
|
748
|
+
// would fail every supervisor RPC with "process pid … does not
|
|
749
|
+
// exist". Two restarts on the same pid hit the warm cache.
|
|
749
750
|
// Step B: worker.getDurableObjectClass('CirrusRealVite') → the
|
|
750
751
|
// class constructor from the dynamic worker's exports. Same
|
|
751
752
|
// pattern facet-manager.ts uses for NodeProcess.
|
|
@@ -756,7 +757,7 @@ export class CirrusReal {
|
|
|
756
757
|
// DO so eviction is coherent. The same name returns the same
|
|
757
758
|
// facet on subsequent calls — that's what makes the cookie
|
|
758
759
|
// persist across supervisor reconnects.
|
|
759
|
-
const stableLoaderId = `${ctx.id.toString()}:cirrus-real-vite:${REAL_VITE_VERSION}`;
|
|
760
|
+
const stableLoaderId = `${ctx.id.toString()}:cirrus-real-vite:${REAL_VITE_VERSION}:${pid}`;
|
|
760
761
|
const facetName = 'cirrus-real-vite';
|
|
761
762
|
try {
|
|
762
763
|
const worker = this.env.LOADER.get(stableLoaderId, async () => ({
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* facets/compose.ts — the one composition of a FacetManager.
|
|
3
|
+
*
|
|
4
|
+
* A FacetManager is the session's spawn path for every resident process —
|
|
5
|
+
* `spawnWorker`, `spawnNode`, the launch journal, `_redrive`, cold-start
|
|
6
|
+
* recovery and the paced launch pump — and until this module it was
|
|
7
|
+
* constructed in exactly one place, inside NimbusSession, with hooks that
|
|
8
|
+
* closed over the session. An embedder that composes NimbusWorkspace as a
|
|
9
|
+
* library over its own Durable Object has no session and must not boot one
|
|
10
|
+
* over its filesystem (a second process table hands out pids whose write
|
|
11
|
+
* authority the filesystem already revoked), so everything behind the
|
|
12
|
+
* manager was closed to it.
|
|
13
|
+
*
|
|
14
|
+
* `composeFacetManager` is that construction, taking exactly what an
|
|
15
|
+
* embedder can supply: the Durable Object's ctx and env, its process
|
|
16
|
+
* supervisor, port registry and SQLite filesystem, an optional shared
|
|
17
|
+
* esbuild service, and the hooks a host has to answer. NimbusSession's own
|
|
18
|
+
* `ensureFacetManager` calls this same factory — there is one path — and
|
|
19
|
+
* keeps only what is session-specific (terminal writes, scrollback notices,
|
|
20
|
+
* the alarm that grants launch turns) inside its hook implementations.
|
|
21
|
+
*
|
|
22
|
+
* What the factory owns and the host does not:
|
|
23
|
+
*
|
|
24
|
+
* - `transformLargeEsm`: the isolated esbuild transform for module text
|
|
25
|
+
* past the in-isolate size bound. It needs `env.LOADER`, `env.ASSETS`
|
|
26
|
+
* and `ctx.facets` and nothing of any host, so it is the factory's
|
|
27
|
+
* default rather than every host's copy.
|
|
28
|
+
* - `resolveWorkerLaunchFallback`: the durable image-store resolver a
|
|
29
|
+
* self-owned worker launch (the session's python/ruby residents, or an
|
|
30
|
+
* embedder spawn that let the manager persist its image) re-drives
|
|
31
|
+
* from. It needs only the filesystem. An embedder that keeps its own
|
|
32
|
+
* content store answers `resolveWorkerLaunch` and this is never reached
|
|
33
|
+
* for its recipes (see `WorkerRecipe.resident`).
|
|
34
|
+
*/
|
|
35
|
+
import type { SqliteVFS } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
|
|
36
|
+
import type { PortRegistry } from '@nimbus-sh/core/runtime/port-registry.js';
|
|
37
|
+
import type { SessionProcessSupervisor } from '@nimbus-sh/core/runtime/session-process-supervisor.js';
|
|
38
|
+
import type { EsbuildService } from '@nimbus-sh/core/runtime/esbuild-service.js';
|
|
39
|
+
import { FacetManager, type FacetManagerHooks } from './manager.js';
|
|
40
|
+
import { type PortVisibility } from '../session/port-capability.js';
|
|
41
|
+
export type { FacetManagerHooks, LongRunningWorkerSpawnOptions, ResidentSpawnOptions, ResolvedWorkerLaunch, ResidentAppSummary, ResidentIdentity, ResidentRestartPolicy, SpawnedWorker, WorkerFacet, WorkerRecipe, } from './manager.js';
|
|
42
|
+
export { FacetManager, DEFAULT_WORKER_MAIN_MODULE } from './manager.js';
|
|
43
|
+
/**
|
|
44
|
+
* The hooks a host answers. Names and types are the FacetManager's own
|
|
45
|
+
* (`FacetManagerHooks`); the three a host cannot do without are required
|
|
46
|
+
* here because a manager composed without them loses launches silently —
|
|
47
|
+
* a launch turn nobody grants never resumes, an exit nobody hears leaves a
|
|
48
|
+
* process table lying, a notice nobody shows is a reset the user never
|
|
49
|
+
* learns about.
|
|
50
|
+
*/
|
|
51
|
+
export interface FacetManagerHostHooks {
|
|
52
|
+
onExternalExit: NonNullable<FacetManagerHooks['onExternalExit']>;
|
|
53
|
+
notify: NonNullable<FacetManagerHooks['notify']>;
|
|
54
|
+
requestLaunchTurn: NonNullable<FacetManagerHooks['requestLaunchTurn']>;
|
|
55
|
+
onSpawn?: FacetManagerHooks['onSpawn'];
|
|
56
|
+
resolveWorkerLaunch?: FacetManagerHooks['resolveWorkerLaunch'];
|
|
57
|
+
}
|
|
58
|
+
/** Everything a FacetManager is composed over. */
|
|
59
|
+
export interface FacetManagerDeps {
|
|
60
|
+
ctx: DurableObjectState;
|
|
61
|
+
/** The Worker's bindings: `LOADER` is required, `ASSETS` serves the esbuild wasm. */
|
|
62
|
+
env: unknown;
|
|
63
|
+
processes: SessionProcessSupervisor;
|
|
64
|
+
portRegistry: PortRegistry;
|
|
65
|
+
vfs: SqliteVFS;
|
|
66
|
+
/** A host's already-warm esbuild, shared so the wasm is initialized once. */
|
|
67
|
+
esbuild?: EsbuildService;
|
|
68
|
+
hooks: FacetManagerHostHooks;
|
|
69
|
+
}
|
|
70
|
+
/** What `composeFacetManager` hands back. */
|
|
71
|
+
export interface ComposedFacetManager {
|
|
72
|
+
manager: FacetManager;
|
|
73
|
+
/**
|
|
74
|
+
* The paced launch pump — the manager's own `pumpResidentLaunches`, not a
|
|
75
|
+
* second one. The host drives it from the turn `hooks.requestLaunchTurn`
|
|
76
|
+
* arranged (the session: its `'resident-launch'` alarm); the first pump on
|
|
77
|
+
* a fresh incarnation also drains cold-start recovery of the launch
|
|
78
|
+
* journal (fabric's `FencedWork.recoverInterrupted`, registered on the
|
|
79
|
+
* manager's `onColdStart`), so it is the cold-start recovery entry too:
|
|
80
|
+
* the session calls it once from initSession for exactly that reason.
|
|
81
|
+
*/
|
|
82
|
+
pumpLaunches: () => Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* The durable-application verbs a host answers for its embedder — the
|
|
85
|
+
* session answers the same four through its `_rpc*` surface. Each is a
|
|
86
|
+
* short composition of the exported port-capability primitives
|
|
87
|
+
* (`@nimbus-sh/worker/port-capability`) and the manager's own methods, so
|
|
88
|
+
* an embedder that wants a different composition has every piece; what is
|
|
89
|
+
* NOT here is what only the session has (dev-server restore, HMR sockets,
|
|
90
|
+
* the self-refreshing "starting" page).
|
|
91
|
+
*/
|
|
92
|
+
apps: {
|
|
93
|
+
/**
|
|
94
|
+
* Reserve (or re-answer) the port `owner` holds, minting the capability
|
|
95
|
+
* its URL is built on — minted here, stored on the reservation, so a URL
|
|
96
|
+
* handed out before the application has ever booted is the one its
|
|
97
|
+
* eventual binding re-adopts, and the one a reset re-adopts again.
|
|
98
|
+
*/
|
|
99
|
+
ensureDurableApp(input: {
|
|
100
|
+
owner: string;
|
|
101
|
+
preferredPort?: number;
|
|
102
|
+
visibility?: PortVisibility;
|
|
103
|
+
name?: string;
|
|
104
|
+
}): Promise<{
|
|
105
|
+
port: number;
|
|
106
|
+
capability: string | null;
|
|
107
|
+
visibility: PortVisibility;
|
|
108
|
+
}>;
|
|
109
|
+
/**
|
|
110
|
+
* End a durable application's contract: every launch the owner claims is
|
|
111
|
+
* killed, its journal rows purged, its port released and its durable slot
|
|
112
|
+
* freed. `port` is the address that was held; `removed` is false only
|
|
113
|
+
* when no durable application held that owner at all.
|
|
114
|
+
*/
|
|
115
|
+
removeDurableApp(owner: string): Promise<{
|
|
116
|
+
owner: string;
|
|
117
|
+
removed: boolean;
|
|
118
|
+
port: number | null;
|
|
119
|
+
}>;
|
|
120
|
+
/**
|
|
121
|
+
* Every registered port with its live capability, persisted at the
|
|
122
|
+
* moment the embedder is told it — a capability nobody has been handed
|
|
123
|
+
* does not need to survive anything.
|
|
124
|
+
*/
|
|
125
|
+
listPorts(): Promise<Array<{
|
|
126
|
+
port: number;
|
|
127
|
+
pid: number;
|
|
128
|
+
registeredAt: number;
|
|
129
|
+
capability: string;
|
|
130
|
+
}>>;
|
|
131
|
+
/**
|
|
132
|
+
* Route a request carrying a port capability to the process on `port`:
|
|
133
|
+
* a durable application a reset left dead is re-driven first
|
|
134
|
+
* (`manager.ensureDurableAppOnPort`), the persisted capability is
|
|
135
|
+
* re-adopted, and a wrong capability is a 404 — never a 403, which would
|
|
136
|
+
* confirm the port is listening. 503 with `Retry-After` while a re-drive
|
|
137
|
+
* is mid-launch; 502 when nothing serves the port. WebSocket upgrades
|
|
138
|
+
* keep fetch semantics through the registered facet.
|
|
139
|
+
*/
|
|
140
|
+
routeCapabilityPort(port: number, capability: string, request: Request, innerPath: string): Promise<Response>;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Compose a FacetManager over a host's own ctx, env, process supervisor, port
|
|
145
|
+
* registry and filesystem. See the module comment for what is defaulted.
|
|
146
|
+
*/
|
|
147
|
+
export declare function composeFacetManager(deps: FacetManagerDeps): ComposedFacetManager;
|
|
148
|
+
//# sourceMappingURL=compose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/facets/compose.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* facets/compose.ts — the one composition of a FacetManager.
|
|
3
|
+
*
|
|
4
|
+
* A FacetManager is the session's spawn path for every resident process —
|
|
5
|
+
* `spawnWorker`, `spawnNode`, the launch journal, `_redrive`, cold-start
|
|
6
|
+
* recovery and the paced launch pump — and until this module it was
|
|
7
|
+
* constructed in exactly one place, inside NimbusSession, with hooks that
|
|
8
|
+
* closed over the session. An embedder that composes NimbusWorkspace as a
|
|
9
|
+
* library over its own Durable Object has no session and must not boot one
|
|
10
|
+
* over its filesystem (a second process table hands out pids whose write
|
|
11
|
+
* authority the filesystem already revoked), so everything behind the
|
|
12
|
+
* manager was closed to it.
|
|
13
|
+
*
|
|
14
|
+
* `composeFacetManager` is that construction, taking exactly what an
|
|
15
|
+
* embedder can supply: the Durable Object's ctx and env, its process
|
|
16
|
+
* supervisor, port registry and SQLite filesystem, an optional shared
|
|
17
|
+
* esbuild service, and the hooks a host has to answer. NimbusSession's own
|
|
18
|
+
* `ensureFacetManager` calls this same factory — there is one path — and
|
|
19
|
+
* keeps only what is session-specific (terminal writes, scrollback notices,
|
|
20
|
+
* the alarm that grants launch turns) inside its hook implementations.
|
|
21
|
+
*
|
|
22
|
+
* What the factory owns and the host does not:
|
|
23
|
+
*
|
|
24
|
+
* - `transformLargeEsm`: the isolated esbuild transform for module text
|
|
25
|
+
* past the in-isolate size bound. It needs `env.LOADER`, `env.ASSETS`
|
|
26
|
+
* and `ctx.facets` and nothing of any host, so it is the factory's
|
|
27
|
+
* default rather than every host's copy.
|
|
28
|
+
* - `resolveWorkerLaunchFallback`: the durable image-store resolver a
|
|
29
|
+
* self-owned worker launch (the session's python/ruby residents, or an
|
|
30
|
+
* embedder spawn that let the manager persist its image) re-drives
|
|
31
|
+
* from. It needs only the filesystem. An embedder that keeps its own
|
|
32
|
+
* content store answers `resolveWorkerLaunch` and this is never reached
|
|
33
|
+
* for its recipes (see `WorkerRecipe.resident`).
|
|
34
|
+
*/
|
|
35
|
+
import { createPortCapability } from '@nimbus-sh/core/runtime/port-registry.js';
|
|
36
|
+
import { FacetManager } from './manager.js';
|
|
37
|
+
import { processHostFor } from '../loaders/process-host.js';
|
|
38
|
+
import { resolveDurableWorkerImage } from './durable-images.js';
|
|
39
|
+
import { persistPortCapability, readPortReservation, readPortReservationByOwner, reservePort, routeCapabilityPort, } from '../session/port-capability.js';
|
|
40
|
+
import { bindPublicPortCapability } from '../router/public-directory.js';
|
|
41
|
+
import { ESBUILD_TRANSFORM_WORKER_ID, esbuildTransformWorkerCode, } from './esbuild-transform.js';
|
|
42
|
+
import { fetchEsbuildWasmBytes } from '../runtime/esbuild-wasm-bytes.js';
|
|
43
|
+
export { FacetManager, DEFAULT_WORKER_MAIN_MODULE } from './manager.js';
|
|
44
|
+
/**
|
|
45
|
+
* Compose a FacetManager over a host's own ctx, env, process supervisor, port
|
|
46
|
+
* registry and filesystem. See the module comment for what is defaulted.
|
|
47
|
+
*/
|
|
48
|
+
export function composeFacetManager(deps) {
|
|
49
|
+
const { ctx, env, vfs } = deps;
|
|
50
|
+
const hooks = {
|
|
51
|
+
onExternalExit: deps.hooks.onExternalExit,
|
|
52
|
+
notify: deps.hooks.notify,
|
|
53
|
+
requestLaunchTurn: deps.hooks.requestLaunchTurn,
|
|
54
|
+
...(deps.hooks.onSpawn !== undefined ? { onSpawn: deps.hooks.onSpawn } : {}),
|
|
55
|
+
...(deps.hooks.resolveWorkerLaunch !== undefined
|
|
56
|
+
? { resolveWorkerLaunch: deps.hooks.resolveWorkerLaunch }
|
|
57
|
+
: {}),
|
|
58
|
+
transformLargeEsm: isolatedEsmTransform(ctx, env),
|
|
59
|
+
resolveWorkerLaunchFallback: (recipe) => resolveDurableWorkerImage(vfs, recipe),
|
|
60
|
+
};
|
|
61
|
+
const manager = new FacetManager(ctx, env, deps.processes, deps.portRegistry, processHostFor, hooks);
|
|
62
|
+
manager.setVfs(vfs);
|
|
63
|
+
if (deps.esbuild)
|
|
64
|
+
manager.setEsbuildService(deps.esbuild);
|
|
65
|
+
const { portRegistry } = deps;
|
|
66
|
+
const capabilityHost = { ctx, portRegistry };
|
|
67
|
+
return {
|
|
68
|
+
manager,
|
|
69
|
+
pumpLaunches: () => manager.pumpResidentLaunches(),
|
|
70
|
+
apps: {
|
|
71
|
+
async ensureDurableApp(input) {
|
|
72
|
+
if (typeof input.owner !== 'string' || input.owner.length === 0) {
|
|
73
|
+
throw new Error('ensureDurableApp: owner must be a non-empty string');
|
|
74
|
+
}
|
|
75
|
+
const occupied = new Set(portRegistry.getAll().map((entry) => entry.port));
|
|
76
|
+
const port = await reservePort(ctx, {
|
|
77
|
+
owner: input.owner,
|
|
78
|
+
preferredPort: input.preferredPort,
|
|
79
|
+
occupiedPorts: occupied,
|
|
80
|
+
capability: createPortCapability(),
|
|
81
|
+
...(input.visibility !== undefined ? { visibility: input.visibility } : {}),
|
|
82
|
+
...(input.name !== undefined ? { name: input.name } : {}),
|
|
83
|
+
});
|
|
84
|
+
const record = await readPortReservation(ctx, port);
|
|
85
|
+
if (record?.visibility === 'public' && record.capability !== null) {
|
|
86
|
+
// A no-op for a host whose Durable Object is not a session (the
|
|
87
|
+
// public directory keys on the session name); loud when it is a
|
|
88
|
+
// session without the binding.
|
|
89
|
+
await bindPublicPortCapability({ env, ctx }, record.capability, port, input.name);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
port,
|
|
93
|
+
capability: record?.capability ?? null,
|
|
94
|
+
visibility: record?.visibility ?? 'scoped',
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
async removeDurableApp(owner) {
|
|
98
|
+
if (typeof owner !== 'string' || owner.length === 0) {
|
|
99
|
+
throw new Error('removeDurableApp: owner must be a non-empty string');
|
|
100
|
+
}
|
|
101
|
+
const held = await readPortReservationByOwner(ctx, owner);
|
|
102
|
+
const removed = await manager.removeDurableApp(owner);
|
|
103
|
+
return { owner, removed, port: held?.port ?? null };
|
|
104
|
+
},
|
|
105
|
+
async listPorts() {
|
|
106
|
+
const entries = portRegistry.getAll();
|
|
107
|
+
await Promise.all(entries.map((entry) => persistPortCapability(capabilityHost, entry.port, entry.capability)));
|
|
108
|
+
return entries.map((entry) => ({
|
|
109
|
+
port: Number(entry.port),
|
|
110
|
+
pid: Number(entry.pid),
|
|
111
|
+
registeredAt: Number(entry.registeredAt),
|
|
112
|
+
capability: String(entry.capability),
|
|
113
|
+
}));
|
|
114
|
+
},
|
|
115
|
+
routeCapabilityPort(port, capability, request, innerPath) {
|
|
116
|
+
// The session's routeToSessionPort is the one implementation —
|
|
117
|
+
// the capability gate AND the public-bearer visibility gate live
|
|
118
|
+
// there; a copy here dropped the latter. Session-only concerns
|
|
119
|
+
// (dev-server restore, the vite-shim branch) no-op on this host.
|
|
120
|
+
return routeCapabilityPort({ ctx, portRegistry, ensureDurableAppOnPort: (p) => manager.ensureDurableAppOnPort(p) }, port, capability, request, innerPath);
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The isolated esbuild transform: module text past the in-isolate bound is
|
|
127
|
+
* transformed in a loader-backed facet that owns the esbuild wasm heap, so the
|
|
128
|
+
* host isolate never pays for it. Session-independent by construction —
|
|
129
|
+
* `env.LOADER`, `env.ASSETS` and `ctx.facets` are all it reads.
|
|
130
|
+
*/
|
|
131
|
+
function isolatedEsmTransform(ctx, env) {
|
|
132
|
+
return async (code, options) => {
|
|
133
|
+
const loader = Reflect.get(Object(env), 'LOADER');
|
|
134
|
+
if (!loader || typeof loader.get !== 'function') {
|
|
135
|
+
throw new Error('Nimbus: env.LOADER unavailable for isolated esbuild transform');
|
|
136
|
+
}
|
|
137
|
+
const assets = Reflect.get(Object(env), 'ASSETS');
|
|
138
|
+
if (!assets || typeof assets.fetch !== 'function') {
|
|
139
|
+
throw new Error('Nimbus: env.ASSETS unavailable for isolated esbuild transform');
|
|
140
|
+
}
|
|
141
|
+
const worker = await loader.get(ESBUILD_TRANSFORM_WORKER_ID, async () => esbuildTransformWorkerCode(await fetchEsbuildWasmBytes({ ASSETS: assets })));
|
|
142
|
+
const transformClass = worker.getDurableObjectClass('EsbuildTransformFacet');
|
|
143
|
+
const facet = ctx.facets.get(`esbuild-transform-${ESBUILD_TRANSFORM_WORKER_ID}`, async () => ({ class: transformClass }));
|
|
144
|
+
return facet.transform(code, options);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* durable spawn exists to survive. Durable images are kept for the
|
|
13
13
|
* application's life and released only by explicit removal.
|
|
14
14
|
*
|
|
15
|
-
* Two blobs per launch: `runner` is the
|
|
16
|
-
* is a JSON payload of `{ modules, env, vfsWasmModules }
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* them
|
|
15
|
+
* Two blobs per launch: `runner` is the main module's source text,
|
|
16
|
+
* `application` is a JSON payload of `{ modules, env, vfsWasmModules }` plus,
|
|
17
|
+
* when the launch set them, `vfsTextModules`, `mainModule` and `startArgs`.
|
|
18
|
+
* The digests in the journal's `recipe.image` are sha256 hashes of those two
|
|
19
|
+
* payloads, and a self-owned spawn mints them at spawn time; an
|
|
20
|
+
* embedder-owned spawn is given them by its own bookkeeping instead.
|
|
20
21
|
*/
|
|
21
22
|
import type { SqliteVFS } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
|
|
22
23
|
import type { ResidentCodeSpec } from '@nimbus-sh/fabric/process-fabric.js';
|
|
@@ -43,6 +44,9 @@ export declare function persistDurableWorkerImage(vfs: SqliteVFS, workerCode: st
|
|
|
43
44
|
}>;
|
|
44
45
|
env?: ResidentCodeSpec['env'];
|
|
45
46
|
vfsWasmModules?: Record<string, string>;
|
|
47
|
+
vfsTextModules?: Record<string, string>;
|
|
48
|
+
/** Recorded only when the launch named one; absent means `worker.js`. */
|
|
49
|
+
mainModule?: string;
|
|
46
50
|
startArgs?: unknown;
|
|
47
51
|
}): Promise<{
|
|
48
52
|
runner: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durable-images.d.ts","sourceRoot":"","sources":["../../src/facets/durable-images.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"durable-images.d.ts","sourceRoot":"","sources":["../../src/facets/durable-images.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEvE,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AAIlD,8EAA8E;AAC9E,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,EACxD,QAAQ,EAAE,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1D,MAAM,CAaR;AAOD;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IACP,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;IACxD,GAAG,CAAC,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GACA,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAkBlD;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAiCtC"}
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* durable spawn exists to survive. Durable images are kept for the
|
|
13
13
|
* application's life and released only by explicit removal.
|
|
14
14
|
*
|
|
15
|
-
* Two blobs per launch: `runner` is the
|
|
16
|
-
* is a JSON payload of `{ modules, env, vfsWasmModules }
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* them
|
|
15
|
+
* Two blobs per launch: `runner` is the main module's source text,
|
|
16
|
+
* `application` is a JSON payload of `{ modules, env, vfsWasmModules }` plus,
|
|
17
|
+
* when the launch set them, `vfsTextModules`, `mainModule` and `startArgs`.
|
|
18
|
+
* The digests in the journal's `recipe.image` are sha256 hashes of those two
|
|
19
|
+
* payloads, and a self-owned spawn mints them at spawn time; an
|
|
20
|
+
* embedder-owned spawn is given them by its own bookkeeping instead.
|
|
20
21
|
*/
|
|
21
22
|
import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
22
23
|
/** The directory every durable application's image blobs live under. */
|
|
@@ -54,10 +55,14 @@ export async function persistDurableWorkerImage(vfs, workerCode, payload) {
|
|
|
54
55
|
kernel.mkdir(DURABLE_IMAGE_DIR, { recursive: true });
|
|
55
56
|
const runner = await sha256Hex(workerCode);
|
|
56
57
|
kernel.writeFile(imagePath(runner), workerCode);
|
|
58
|
+
// Optional members are omitted rather than written null so an unchanged
|
|
59
|
+
// launch keeps the digest it had before the member existed.
|
|
57
60
|
const applicationPayload = JSON.stringify({
|
|
58
61
|
modules: payload.modules,
|
|
59
62
|
env: payload.env ?? null,
|
|
60
63
|
vfsWasmModules: payload.vfsWasmModules ?? null,
|
|
64
|
+
...(payload.vfsTextModules !== undefined ? { vfsTextModules: payload.vfsTextModules } : {}),
|
|
65
|
+
...(payload.mainModule !== undefined ? { mainModule: payload.mainModule } : {}),
|
|
61
66
|
...(payload.startArgs !== undefined ? { startArgs: payload.startArgs } : {}),
|
|
62
67
|
});
|
|
63
68
|
const application = await sha256Hex(applicationPayload);
|
|
@@ -83,12 +88,17 @@ export async function resolveDurableWorkerImage(vfs, recipe) {
|
|
|
83
88
|
return null;
|
|
84
89
|
}
|
|
85
90
|
const runner = new TextDecoder().decode(runnerBytes);
|
|
86
|
-
const { modules = {}, env = null, vfsWasmModules = undefined, startArgs } = JSON.parse(new TextDecoder().decode(applicationBytes));
|
|
91
|
+
const { modules = {}, env = null, vfsWasmModules = undefined, vfsTextModules, mainModule, startArgs, } = JSON.parse(new TextDecoder().decode(applicationBytes));
|
|
92
|
+
// The recipe's own record of the main module wins when the blob predates
|
|
93
|
+
// the member; both say the same thing for any launch written since.
|
|
94
|
+
const main = mainModule ?? recipe.mainModule ?? 'worker.js';
|
|
87
95
|
return {
|
|
88
96
|
env: env ?? null,
|
|
89
97
|
globalOutbound: undefined,
|
|
90
|
-
modules: {
|
|
98
|
+
modules: { ...modules, [main]: runner },
|
|
99
|
+
mainModule: main,
|
|
91
100
|
...(startArgs !== undefined ? { startArgs } : {}),
|
|
92
101
|
...(vfsWasmModules !== null ? { vfsWasmModules } : {}),
|
|
102
|
+
...(vfsTextModules !== undefined ? { vfsTextModules } : {}),
|
|
93
103
|
};
|
|
94
104
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EsbuildBundlePool — the session's single esbuild facet pool.
|
|
3
|
+
*
|
|
4
|
+
* Install-time pre-bundling and on-demand /@modules/ bundling dispatch the
|
|
5
|
+
* same worker (PRE_BUNDLE_PREAMBLE + esbuild.wasm, one slot). When each
|
|
6
|
+
* pipeline built its own IsolatePool the supervisor retained two copies of
|
|
7
|
+
* the ~12 MiB wasm bytes and warmed two loader slots per session, and the
|
|
8
|
+
* two pools never queued behind each other. One pool, owned by the session,
|
|
9
|
+
* holds one wasm copy — leased once from the shared supervisor allocation
|
|
10
|
+
* budget, the way the pre-bundler already accounted for its own — for as
|
|
11
|
+
* long as the installer and dev server live, and is disposed with them.
|
|
12
|
+
*
|
|
13
|
+
* Construction is lazy and everything heavy is imported on first use so
|
|
14
|
+
* the fabric/preamble/wasm subgraph stays out of the cold script-eval
|
|
15
|
+
* graph of sessions that never bundle.
|
|
16
|
+
*/
|
|
17
|
+
import type { IsolatePool } from '@nimbus-sh/fabric/isolate-pool.js';
|
|
18
|
+
/** The pool surface both bundling pipelines dispatch through. */
|
|
19
|
+
export type BundlePool = Pick<IsolatePool, 'submit'>;
|
|
20
|
+
/** What a bundling pipeline receives from its host. */
|
|
21
|
+
export interface BundlePoolProvider {
|
|
22
|
+
/** The session's pool, constructed on first call. */
|
|
23
|
+
acquire(): Promise<BundlePool>;
|
|
24
|
+
}
|
|
25
|
+
export declare class EsbuildBundlePool implements BundlePoolProvider {
|
|
26
|
+
private readonly env;
|
|
27
|
+
private readonly ctx;
|
|
28
|
+
private pool;
|
|
29
|
+
private pending;
|
|
30
|
+
/** Releases the wasm bytes' supervisor credit; held until dispose(). */
|
|
31
|
+
private releaseWasmCredit;
|
|
32
|
+
/** Bumped by dispose() so a construction it interrupted tears itself down. */
|
|
33
|
+
private generation;
|
|
34
|
+
constructor(env: unknown, ctx: DurableObjectState);
|
|
35
|
+
/**
|
|
36
|
+
* Callers MUST acquire the pool before taking any slice lease: the
|
|
37
|
+
* first construction reserves the full supervisor budget while the wasm
|
|
38
|
+
* bytes are fetched, so a caller already holding slice credit would
|
|
39
|
+
* wait on itself.
|
|
40
|
+
*/
|
|
41
|
+
acquire(): Promise<IsolatePool>;
|
|
42
|
+
private construct;
|
|
43
|
+
/**
|
|
44
|
+
* Tear the pool down with its host. A later acquire() constructs a fresh
|
|
45
|
+
* pool, so a session that installs again after a teardown still bundles.
|
|
46
|
+
*/
|
|
47
|
+
dispose(): void;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=esbuild-bundle-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esbuild-bundle-pool.d.ts","sourceRoot":"","sources":["../../src/facets/esbuild-bundle-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AASrE,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAErD,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC;AAQD,qBAAa,iBAAkB,YAAW,kBAAkB;IASxD,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IATtB,OAAO,CAAC,IAAI,CAA4B;IACxC,OAAO,CAAC,OAAO,CAAqC;IACpD,wEAAwE;IACxE,OAAO,CAAC,iBAAiB,CAA6B;IACtD,8EAA8E;IAC9E,OAAO,CAAC,UAAU,CAAK;gBAGJ,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,kBAAkB;IAG1C;;;;;OAKG;IACH,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC;YAqBjB,SAAS;IAmDvB;;;OAGG;IACH,OAAO,IAAI,IAAI;CAYhB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { acquireResidentSupervisorAllocation } from '@nimbus-sh/platform/heavy-alloc-coord.js';
|
|
2
|
+
import { PRE_BUNDLE_CONCURRENCY, PRE_BUNDLE_SLICE_CAP_BYTES, SUPERVISOR_IN_FLIGHT_ALLOCATION_BUDGET_BYTES, } from '@nimbus-sh/platform/limits.js';
|
|
3
|
+
function hasAssetsFetcher(env) {
|
|
4
|
+
if (typeof env !== 'object' || env === null)
|
|
5
|
+
return false;
|
|
6
|
+
const assets = Reflect.get(env, 'ASSETS');
|
|
7
|
+
return typeof assets === 'object' && assets !== null && typeof Reflect.get(assets, 'fetch') === 'function';
|
|
8
|
+
}
|
|
9
|
+
export class EsbuildBundlePool {
|
|
10
|
+
env;
|
|
11
|
+
ctx;
|
|
12
|
+
pool = null;
|
|
13
|
+
pending = null;
|
|
14
|
+
/** Releases the wasm bytes' supervisor credit; held until dispose(). */
|
|
15
|
+
releaseWasmCredit = null;
|
|
16
|
+
/** Bumped by dispose() so a construction it interrupted tears itself down. */
|
|
17
|
+
generation = 0;
|
|
18
|
+
constructor(env, ctx) {
|
|
19
|
+
this.env = env;
|
|
20
|
+
this.ctx = ctx;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Callers MUST acquire the pool before taking any slice lease: the
|
|
24
|
+
* first construction reserves the full supervisor budget while the wasm
|
|
25
|
+
* bytes are fetched, so a caller already holding slice credit would
|
|
26
|
+
* wait on itself.
|
|
27
|
+
*/
|
|
28
|
+
acquire() {
|
|
29
|
+
if (this.pool)
|
|
30
|
+
return Promise.resolve(this.pool);
|
|
31
|
+
if (this.pending)
|
|
32
|
+
return this.pending;
|
|
33
|
+
const generation = this.generation;
|
|
34
|
+
const pending = this.construct().then(({ pool, releaseWasmCredit }) => {
|
|
35
|
+
if (this.generation !== generation) {
|
|
36
|
+
// dispose() ran while the wasm bytes were in flight.
|
|
37
|
+
try {
|
|
38
|
+
pool.dispose();
|
|
39
|
+
}
|
|
40
|
+
catch { /* best-effort */ }
|
|
41
|
+
releaseWasmCredit();
|
|
42
|
+
throw new Error('EsbuildBundlePool: disposed during construction');
|
|
43
|
+
}
|
|
44
|
+
this.pool = pool;
|
|
45
|
+
this.releaseWasmCredit = releaseWasmCredit;
|
|
46
|
+
return pool;
|
|
47
|
+
}).finally(() => {
|
|
48
|
+
if (this.pending === pending)
|
|
49
|
+
this.pending = null;
|
|
50
|
+
});
|
|
51
|
+
this.pending = pending;
|
|
52
|
+
return pending;
|
|
53
|
+
}
|
|
54
|
+
async construct() {
|
|
55
|
+
if (!hasAssetsFetcher(this.env)) {
|
|
56
|
+
throw new Error('EsbuildBundlePool: env.ASSETS binding missing — the esbuild wasm asset cannot be fetched');
|
|
57
|
+
}
|
|
58
|
+
const env = this.env;
|
|
59
|
+
const [{ IsolatePool }, { PRE_BUNDLE_PREAMBLE }, { fetchEsbuildWasmBytes }] = await Promise.all([
|
|
60
|
+
import('@nimbus-sh/fabric/isolate-pool.js'),
|
|
61
|
+
import('../loaders/pre-bundle-preamble.js'),
|
|
62
|
+
import('../runtime/esbuild-wasm-bytes.js'),
|
|
63
|
+
]);
|
|
64
|
+
// Resident, and sized to what the pool will KEEP — not the whole budget.
|
|
65
|
+
//
|
|
66
|
+
// The full-budget claim this used to make is grantable exactly once: the
|
|
67
|
+
// lease it shrinks to becomes a permanent floor, so a second pool
|
|
68
|
+
// construction asked for 40 MiB against 28.1 MiB that could ever be free
|
|
69
|
+
// and parked in the FIFO with no error and no CPU. Measured on a
|
|
70
|
+
// deployed worker: capacity 41,943,040, resident 11,907,565, queued 1 for
|
|
71
|
+
// 222 s with the isolate healthy the whole time — and because the queue
|
|
72
|
+
// refuses everyone behind a waiter, the whole session stopped making
|
|
73
|
+
// progress. The bound below is the same one the payload is checked
|
|
74
|
+
// against, so the slice cap stays free for the work the pool exists for.
|
|
75
|
+
const maxRetainedWasmBytes = SUPERVISOR_IN_FLIGHT_ALLOCATION_BUDGET_BYTES - PRE_BUNDLE_SLICE_CAP_BYTES;
|
|
76
|
+
const setupAllocation = await acquireResidentSupervisorAllocation(maxRetainedWasmBytes);
|
|
77
|
+
let retained = false;
|
|
78
|
+
try {
|
|
79
|
+
const wasmBytes = await fetchEsbuildWasmBytes(env);
|
|
80
|
+
if (wasmBytes.byteLength > maxRetainedWasmBytes) {
|
|
81
|
+
throw new RangeError(`esbuild wasm payload ${wasmBytes.byteLength} exceeds the ${maxRetainedWasmBytes}-byte retained budget`);
|
|
82
|
+
}
|
|
83
|
+
// IsolatePool keeps the constructor-time module bytes until dispose(),
|
|
84
|
+
// so retain their exact credit rather than treating construction as a
|
|
85
|
+
// handoff that immediately frees the ArrayBuffer.
|
|
86
|
+
setupAllocation.shrinkTo(wasmBytes.byteLength);
|
|
87
|
+
const pool = new IsolatePool(env, this.ctx, {
|
|
88
|
+
concurrency: PRE_BUNDLE_CONCURRENCY,
|
|
89
|
+
timeoutMs: 60_000,
|
|
90
|
+
retries: 0,
|
|
91
|
+
tag: 'esbuild-bundle',
|
|
92
|
+
preamble: PRE_BUNDLE_PREAMBLE,
|
|
93
|
+
wasmModules: { 'esbuild.wasm': wasmBytes },
|
|
94
|
+
});
|
|
95
|
+
retained = true;
|
|
96
|
+
return { pool, releaseWasmCredit: setupAllocation.release };
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
if (!retained)
|
|
100
|
+
setupAllocation.release();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Tear the pool down with its host. A later acquire() constructs a fresh
|
|
105
|
+
* pool, so a session that installs again after a teardown still bundles.
|
|
106
|
+
*/
|
|
107
|
+
dispose() {
|
|
108
|
+
this.generation++;
|
|
109
|
+
const pool = this.pool;
|
|
110
|
+
this.pool = null;
|
|
111
|
+
this.pending = null;
|
|
112
|
+
if (pool) {
|
|
113
|
+
try {
|
|
114
|
+
pool.dispose();
|
|
115
|
+
}
|
|
116
|
+
catch { /* best-effort */ }
|
|
117
|
+
}
|
|
118
|
+
const release = this.releaseWasmCredit;
|
|
119
|
+
this.releaseWasmCredit = null;
|
|
120
|
+
release?.();
|
|
121
|
+
}
|
|
122
|
+
}
|