@nimbus-sh/worker 0.1.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 +219 -0
- package/dist/_shared/bytes.d.ts +18 -0
- package/dist/_shared/bytes.d.ts.map +1 -0
- package/dist/_shared/bytes.js +17 -0
- package/dist/_shared/cache-stats.d.ts +146 -0
- package/dist/_shared/cache-stats.d.ts.map +1 -0
- package/dist/_shared/cache-stats.js +162 -0
- package/dist/_shared/exports-resolver.d.ts +76 -0
- package/dist/_shared/exports-resolver.d.ts.map +1 -0
- package/dist/_shared/exports-resolver.js +284 -0
- package/dist/_shared/install-phase.d.ts +47 -0
- package/dist/_shared/install-phase.d.ts.map +1 -0
- package/dist/_shared/install-phase.js +1 -0
- package/dist/_shared/real-node-imports.d.ts +38 -0
- package/dist/_shared/real-node-imports.d.ts.map +1 -0
- package/dist/_shared/real-node-imports.js +46 -0
- package/dist/_shared/retry.d.ts +93 -0
- package/dist/_shared/retry.d.ts.map +1 -0
- package/dist/_shared/retry.js +161 -0
- package/dist/_shared/session-id.d.ts +39 -0
- package/dist/_shared/session-id.d.ts.map +1 -0
- package/dist/_shared/session-id.js +127 -0
- package/dist/_shared/session-router.d.ts +104 -0
- package/dist/_shared/session-router.d.ts.map +1 -0
- package/dist/_shared/session-router.js +165 -0
- package/dist/_shared/w7-frame.d.ts +93 -0
- package/dist/_shared/w7-frame.d.ts.map +1 -0
- package/dist/_shared/w7-frame.js +402 -0
- package/dist/auth/index.d.ts +16 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +15 -0
- package/dist/auth/middleware.d.ts +85 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +150 -0
- package/dist/auth/token.d.ts +96 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +226 -0
- package/dist/auth/types.d.ts +141 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +114 -0
- package/dist/bindings/d1.d.ts +102 -0
- package/dist/bindings/d1.d.ts.map +1 -0
- package/dist/bindings/d1.js +549 -0
- package/dist/bindings/kv.d.ts +82 -0
- package/dist/bindings/kv.d.ts.map +1 -0
- package/dist/bindings/kv.js +290 -0
- package/dist/bindings/r2.d.ts +137 -0
- package/dist/bindings/r2.d.ts.map +1 -0
- package/dist/bindings/r2.js +379 -0
- package/dist/cirrus-npm-cjs.generated.d.ts +17 -0
- package/dist/cirrus-npm-cjs.generated.d.ts.map +1 -0
- package/dist/cirrus-npm-cjs.generated.js +41 -0
- package/dist/cirrus-plugin-react.generated.d.ts +34 -0
- package/dist/cirrus-plugin-react.generated.d.ts.map +1 -0
- package/dist/cirrus-plugin-react.generated.js +33 -0
- package/dist/constants.d.ts +34 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +116 -0
- package/dist/esbuild-wasm-bundle.generated.d.ts +41 -0
- package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -0
- package/dist/esbuild-wasm-bundle.generated.js +40 -0
- package/dist/facets/cirrus-real.d.ts +147 -0
- package/dist/facets/cirrus-real.d.ts.map +1 -0
- package/dist/facets/cirrus-real.js +981 -0
- package/dist/facets/inner-do-registry.d.ts +41 -0
- package/dist/facets/inner-do-registry.d.ts.map +1 -0
- package/dist/facets/inner-do-registry.js +51 -0
- package/dist/facets/manager.d.ts +314 -0
- package/dist/facets/manager.d.ts.map +1 -0
- package/dist/facets/manager.js +2681 -0
- package/dist/facets/process.d.ts +318 -0
- package/dist/facets/process.d.ts.map +1 -0
- package/dist/facets/process.js +567 -0
- package/dist/facets/real-vite-fs-shim.d.ts +151 -0
- package/dist/facets/real-vite-fs-shim.d.ts.map +1 -0
- package/dist/facets/real-vite-fs-shim.js +1279 -0
- package/dist/facets/real-vite-hmr.d.ts +142 -0
- package/dist/facets/real-vite-hmr.d.ts.map +1 -0
- package/dist/facets/real-vite-hmr.js +476 -0
- package/dist/facets/vite-dev-server.d.ts +293 -0
- package/dist/facets/vite-dev-server.d.ts.map +1 -0
- package/dist/facets/vite-dev-server.js +2580 -0
- package/dist/facets/wasm-swap-registry.d.ts +261 -0
- package/dist/facets/wasm-swap-registry.d.ts.map +1 -0
- package/dist/facets/wasm-swap-registry.js +551 -0
- package/dist/facets/ws-terminal.d.ts +107 -0
- package/dist/facets/ws-terminal.d.ts.map +1 -0
- package/dist/facets/ws-terminal.js +207 -0
- package/dist/git/commands.d.ts +13 -0
- package/dist/git/commands.d.ts.map +1 -0
- package/dist/git/commands.js +633 -0
- package/dist/git/network-facet.d.ts +61 -0
- package/dist/git/network-facet.d.ts.map +1 -0
- package/dist/git/network-facet.js +700 -0
- package/dist/git-bundle.generated.d.ts +16 -0
- package/dist/git-bundle.generated.d.ts.map +1 -0
- package/dist/git-bundle.generated.js +15 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/loaders/child-process/spawn-facet.d.ts +90 -0
- package/dist/loaders/child-process/spawn-facet.d.ts.map +1 -0
- package/dist/loaders/child-process/spawn-facet.js +103 -0
- package/dist/loaders/child-process/spawn-pool.d.ts +110 -0
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -0
- package/dist/loaders/child-process/spawn-pool.js +161 -0
- package/dist/loaders/fanout-pool.d.ts +195 -0
- package/dist/loaders/fanout-pool.d.ts.map +1 -0
- package/dist/loaders/fanout-pool.js +326 -0
- package/dist/loaders/generated-workers.d.ts +26 -0
- package/dist/loaders/generated-workers.d.ts.map +1 -0
- package/dist/loaders/generated-workers.js +25 -0
- package/dist/loaders/index.d.ts +15 -0
- package/dist/loaders/index.d.ts.map +1 -0
- package/dist/loaders/index.js +13 -0
- package/dist/loaders/loader-pool.d.ts +273 -0
- package/dist/loaders/loader-pool.d.ts.map +1 -0
- package/dist/loaders/loader-pool.js +624 -0
- package/dist/loaders/npm-resolve-preamble.d.ts +34 -0
- package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -0
- package/dist/loaders/npm-resolve-preamble.js +237 -0
- package/dist/loaders/pre-bundle-preamble.d.ts +41 -0
- package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -0
- package/dist/loaders/pre-bundle-preamble.js +114 -0
- package/dist/loaders/vendor/codegen.d.ts +24 -0
- package/dist/loaders/vendor/codegen.d.ts.map +1 -0
- package/dist/loaders/vendor/codegen.js +44 -0
- package/dist/loaders/vendor/errors.d.ts +24 -0
- package/dist/loaders/vendor/errors.d.ts.map +1 -0
- package/dist/loaders/vendor/errors.js +46 -0
- package/dist/loaders/vendor/index.d.ts +16 -0
- package/dist/loaders/vendor/index.d.ts.map +1 -0
- package/dist/loaders/vendor/index.js +11 -0
- package/dist/loaders/vendor/pool.d.ts +88 -0
- package/dist/loaders/vendor/pool.d.ts.map +1 -0
- package/dist/loaders/vendor/pool.js +353 -0
- package/dist/loaders/vendor/primitives.d.ts +11 -0
- package/dist/loaders/vendor/primitives.d.ts.map +1 -0
- package/dist/loaders/vendor/primitives.js +33 -0
- package/dist/loaders/vendor/serialize.d.ts +3 -0
- package/dist/loaders/vendor/serialize.d.ts.map +1 -0
- package/dist/loaders/vendor/serialize.js +25 -0
- package/dist/loaders/vendor/types.d.ts +60 -0
- package/dist/loaders/vendor/types.d.ts.map +1 -0
- package/dist/loaders/vendor/types.js +4 -0
- package/dist/npm/cache.d.ts +136 -0
- package/dist/npm/cache.d.ts.map +1 -0
- package/dist/npm/cache.js +429 -0
- package/dist/npm/install-batch-facet.d.ts +129 -0
- package/dist/npm/install-batch-facet.d.ts.map +1 -0
- package/dist/npm/install-batch-facet.js +598 -0
- package/dist/npm/install-facet.d.ts +29 -0
- package/dist/npm/install-facet.d.ts.map +1 -0
- package/dist/npm/install-facet.js +9 -0
- package/dist/npm/installer.d.ts +239 -0
- package/dist/npm/installer.d.ts.map +1 -0
- package/dist/npm/installer.js +2022 -0
- package/dist/npm/npx-install.d.ts +74 -0
- package/dist/npm/npx-install.d.ts.map +1 -0
- package/dist/npm/npx-install.js +302 -0
- package/dist/npm/pre-bundle-facet.d.ts +168 -0
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -0
- package/dist/npm/pre-bundle-facet.js +653 -0
- package/dist/npm/r2-cache.d.ts +286 -0
- package/dist/npm/r2-cache.d.ts.map +1 -0
- package/dist/npm/r2-cache.js +506 -0
- package/dist/npm/resolve-facet.d.ts +202 -0
- package/dist/npm/resolve-facet.d.ts.map +1 -0
- package/dist/npm/resolve-facet.js +833 -0
- package/dist/npm/resolve-one-facet.d.ts +204 -0
- package/dist/npm/resolve-one-facet.d.ts.map +1 -0
- package/dist/npm/resolve-one-facet.js +437 -0
- package/dist/npm/resolver.d.ts +109 -0
- package/dist/npm/resolver.d.ts.map +1 -0
- package/dist/npm/resolver.js +899 -0
- package/dist/npm/tarball-stream.d.ts +85 -0
- package/dist/npm/tarball-stream.d.ts.map +1 -0
- package/dist/npm/tarball-stream.js +205 -0
- package/dist/npm/tarball.d.ts +51 -0
- package/dist/npm/tarball.d.ts.map +1 -0
- package/dist/npm/tarball.js +185 -0
- package/dist/observability/diag-counters.d.ts +292 -0
- package/dist/observability/diag-counters.d.ts.map +1 -0
- package/dist/observability/diag-counters.js +302 -0
- package/dist/observability/heap-estimate.d.ts +166 -0
- package/dist/observability/heap-estimate.d.ts.map +1 -0
- package/dist/observability/heap-estimate.js +192 -0
- package/dist/observability/heavy-alloc-coord.d.ts +95 -0
- package/dist/observability/heavy-alloc-coord.d.ts.map +1 -0
- package/dist/observability/heavy-alloc-coord.js +149 -0
- package/dist/observability/oom-classify.d.ts +56 -0
- package/dist/observability/oom-classify.d.ts.map +1 -0
- package/dist/observability/oom-classify.js +118 -0
- package/dist/observability/oom-discriminator.d.ts +192 -0
- package/dist/observability/oom-discriminator.d.ts.map +1 -0
- package/dist/observability/oom-discriminator.js +233 -0
- package/dist/real-vite-bundle.generated.d.ts +46 -0
- package/dist/real-vite-bundle.generated.d.ts.map +1 -0
- package/dist/real-vite-bundle.generated.js +53 -0
- package/dist/replica/routing.d.ts +171 -0
- package/dist/replica/routing.d.ts.map +1 -0
- package/dist/replica/routing.js +288 -0
- package/dist/replica/suspension.d.ts +36 -0
- package/dist/replica/suspension.d.ts.map +1 -0
- package/dist/replica/suspension.js +49 -0
- package/dist/router/index.d.ts +156 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +220 -0
- package/dist/runtime/assets-loader.d.ts +71 -0
- package/dist/runtime/assets-loader.d.ts.map +1 -0
- package/dist/runtime/assets-loader.js +128 -0
- package/dist/runtime/barrel-detect.d.ts +46 -0
- package/dist/runtime/barrel-detect.d.ts.map +1 -0
- package/dist/runtime/barrel-detect.js +73 -0
- package/dist/runtime/barrel-synthesizer.d.ts +181 -0
- package/dist/runtime/barrel-synthesizer.d.ts.map +1 -0
- package/dist/runtime/barrel-synthesizer.js +541 -0
- package/dist/runtime/bun-repl.d.ts +46 -0
- package/dist/runtime/bun-repl.d.ts.map +1 -0
- package/dist/runtime/bun-repl.js +326 -0
- package/dist/runtime/bun-runner.d.ts +66 -0
- package/dist/runtime/bun-runner.d.ts.map +1 -0
- package/dist/runtime/bun-runner.js +219 -0
- package/dist/runtime/clang-runner.d.ts +38 -0
- package/dist/runtime/clang-runner.d.ts.map +1 -0
- package/dist/runtime/clang-runner.js +1033 -0
- package/dist/runtime/comment-strip.d.ts +37 -0
- package/dist/runtime/comment-strip.d.ts.map +1 -0
- package/dist/runtime/comment-strip.js +74 -0
- package/dist/runtime/esbuild-service.d.ts +201 -0
- package/dist/runtime/esbuild-service.d.ts.map +1 -0
- package/dist/runtime/esbuild-service.js +1444 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts +87 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -0
- package/dist/runtime/esbuild-wasm-bytes.js +131 -0
- package/dist/runtime/framework-detect.d.ts +71 -0
- package/dist/runtime/framework-detect.d.ts.map +1 -0
- package/dist/runtime/framework-detect.js +178 -0
- package/dist/runtime/long-running-handle.d.ts +133 -0
- package/dist/runtime/long-running-handle.d.ts.map +1 -0
- package/dist/runtime/long-running-handle.js +192 -0
- package/dist/runtime/node-repl.d.ts +36 -0
- package/dist/runtime/node-repl.d.ts.map +1 -0
- package/dist/runtime/node-repl.js +293 -0
- package/dist/runtime/node-runner.d.ts +111 -0
- package/dist/runtime/node-runner.d.ts.map +1 -0
- package/dist/runtime/node-runner.js +194 -0
- package/dist/runtime/node-shims.d.ts +24 -0
- package/dist/runtime/node-shims.d.ts.map +1 -0
- package/dist/runtime/node-shims.js +3019 -0
- package/dist/runtime/package-manager.d.ts +118 -0
- package/dist/runtime/package-manager.d.ts.map +1 -0
- package/dist/runtime/package-manager.js +420 -0
- package/dist/runtime/port-registry.d.ts +80 -0
- package/dist/runtime/port-registry.d.ts.map +1 -0
- package/dist/runtime/port-registry.js +166 -0
- package/dist/runtime/process-logs-api.d.ts +102 -0
- package/dist/runtime/process-logs-api.d.ts.map +1 -0
- package/dist/runtime/process-logs-api.js +253 -0
- package/dist/runtime/process-logs.d.ts +311 -0
- package/dist/runtime/process-logs.d.ts.map +1 -0
- package/dist/runtime/process-logs.js +656 -0
- package/dist/runtime/process-table.d.ts +65 -0
- package/dist/runtime/process-table.d.ts.map +1 -0
- package/dist/runtime/process-table.js +107 -0
- package/dist/runtime/project-detect.d.ts +21 -0
- package/dist/runtime/project-detect.d.ts.map +1 -0
- package/dist/runtime/project-detect.js +46 -0
- package/dist/runtime/python-repl.d.ts +66 -0
- package/dist/runtime/python-repl.d.ts.map +1 -0
- package/dist/runtime/python-repl.js +563 -0
- package/dist/runtime/python-runner.d.ts +50 -0
- package/dist/runtime/python-runner.d.ts.map +1 -0
- package/dist/runtime/python-runner.js +870 -0
- package/dist/runtime/repl-session.d.ts +160 -0
- package/dist/runtime/repl-session.d.ts.map +1 -0
- package/dist/runtime/repl-session.js +393 -0
- package/dist/runtime/require-resolver.d.ts +52 -0
- package/dist/runtime/require-resolver.d.ts.map +1 -0
- package/dist/runtime/require-resolver.js +690 -0
- package/dist/runtime/router-basename.d.ts +36 -0
- package/dist/runtime/router-basename.d.ts.map +1 -0
- package/dist/runtime/router-basename.js +310 -0
- package/dist/runtime/ruby-repl.d.ts +43 -0
- package/dist/runtime/ruby-repl.d.ts.map +1 -0
- package/dist/runtime/ruby-repl.js +229 -0
- package/dist/runtime/ruby-runner.d.ts +62 -0
- package/dist/runtime/ruby-runner.d.ts.map +1 -0
- package/dist/runtime/ruby-runner.js +737 -0
- package/dist/runtime/runtime-catalog.d.ts +99 -0
- package/dist/runtime/runtime-catalog.d.ts.map +1 -0
- package/dist/runtime/runtime-catalog.js +183 -0
- package/dist/runtime/runtime-registry.d.ts +136 -0
- package/dist/runtime/runtime-registry.d.ts.map +1 -0
- package/dist/runtime/runtime-registry.js +318 -0
- package/dist/runtime/streams.d.ts +13 -0
- package/dist/runtime/streams.d.ts.map +1 -0
- package/dist/runtime/streams.js +402 -0
- package/dist/runtime/wasi-instance.d.ts +190 -0
- package/dist/runtime/wasi-instance.d.ts.map +1 -0
- package/dist/runtime/wasi-instance.js +2044 -0
- package/dist/runtime/wasm-runner.d.ts +117 -0
- package/dist/runtime/wasm-runner.d.ts.map +1 -0
- package/dist/runtime/wasm-runner.js +705 -0
- package/dist/session/bindings.d.ts +168 -0
- package/dist/session/bindings.d.ts.map +1 -0
- package/dist/session/bindings.js +524 -0
- package/dist/session/ctx-exports.d.ts +16 -0
- package/dist/session/ctx-exports.d.ts.map +1 -0
- package/dist/session/ctx-exports.js +22 -0
- package/dist/session/diag.d.ts +62 -0
- package/dist/session/diag.d.ts.map +1 -0
- package/dist/session/diag.js +118 -0
- package/dist/session/fs-watch.d.ts +132 -0
- package/dist/session/fs-watch.d.ts.map +1 -0
- package/dist/session/fs-watch.js +255 -0
- package/dist/session/helpers.d.ts +150 -0
- package/dist/session/helpers.d.ts.map +1 -0
- package/dist/session/helpers.js +472 -0
- package/dist/session/hibernation.d.ts +145 -0
- package/dist/session/hibernation.d.ts.map +1 -0
- package/dist/session/hibernation.js +385 -0
- package/dist/session/init-phases.d.ts +87 -0
- package/dist/session/init-phases.d.ts.map +1 -0
- package/dist/session/init-phases.js +137 -0
- package/dist/session/init.d.ts +46 -0
- package/dist/session/init.d.ts.map +1 -0
- package/dist/session/init.js +3579 -0
- package/dist/session/keys.d.ts +56 -0
- package/dist/session/keys.d.ts.map +1 -0
- package/dist/session/keys.js +55 -0
- package/dist/session/nimbus-session.d.ts +434 -0
- package/dist/session/nimbus-session.d.ts.map +1 -0
- package/dist/session/nimbus-session.js +1084 -0
- package/dist/session/programmatic.d.ts +105 -0
- package/dist/session/programmatic.d.ts.map +1 -0
- package/dist/session/programmatic.js +326 -0
- package/dist/session/replica-routes.d.ts +68 -0
- package/dist/session/replica-routes.d.ts.map +1 -0
- package/dist/session/replica-routes.js +77 -0
- package/dist/session/routes.d.ts +29 -0
- package/dist/session/routes.d.ts.map +1 -0
- package/dist/session/routes.js +1331 -0
- package/dist/session/rpc.d.ts +323 -0
- package/dist/session/rpc.d.ts.map +1 -0
- package/dist/session/rpc.js +808 -0
- package/dist/session/state-store.d.ts +210 -0
- package/dist/session/state-store.d.ts.map +1 -0
- package/dist/session/state-store.js +459 -0
- package/dist/session/supervisor-rpc.d.ts +269 -0
- package/dist/session/supervisor-rpc.d.ts.map +1 -0
- package/dist/session/supervisor-rpc.js +437 -0
- package/dist/session/ws-hibernation-config.d.ts +64 -0
- package/dist/session/ws-hibernation-config.d.ts.map +1 -0
- package/dist/session/ws-hibernation-config.js +89 -0
- package/dist/session/ws.d.ts +95 -0
- package/dist/session/ws.d.ts.map +1 -0
- package/dist/session/ws.js +395 -0
- package/dist/shell/features.d.ts +410 -0
- package/dist/shell/features.d.ts.map +1 -0
- package/dist/shell/features.js +1885 -0
- package/dist/shell/unix-commands.d.ts +15 -0
- package/dist/shell/unix-commands.d.ts.map +1 -0
- package/dist/shell/unix-commands.js +3703 -0
- package/dist/stubs/lifo-ui.d.ts +10 -0
- package/dist/stubs/lifo-ui.d.ts.map +1 -0
- package/dist/stubs/lifo-ui.js +1 -0
- package/dist/tailwind-play.generated.d.ts +19 -0
- package/dist/tailwind-play.generated.d.ts.map +1 -0
- package/dist/tailwind-play.generated.js +18 -0
- package/dist/vfs/dev-provider.d.ts +64 -0
- package/dist/vfs/dev-provider.d.ts.map +1 -0
- package/dist/vfs/dev-provider.js +191 -0
- package/dist/vfs/events.d.ts +51 -0
- package/dist/vfs/events.d.ts.map +1 -0
- package/dist/vfs/events.js +127 -0
- package/dist/vfs/path.d.ts +30 -0
- package/dist/vfs/path.d.ts.map +1 -0
- package/dist/vfs/path.js +41 -0
- package/dist/vfs/seed-project.d.ts +62 -0
- package/dist/vfs/seed-project.d.ts.map +1 -0
- package/dist/vfs/seed-project.js +1045 -0
- package/dist/vfs/sqlite-vfs.d.ts +435 -0
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -0
- package/dist/vfs/sqlite-vfs.js +1670 -0
- package/dist/vfs/symlink-registry.d.ts +64 -0
- package/dist/vfs/symlink-registry.d.ts.map +1 -0
- package/dist/vfs/symlink-registry.js +137 -0
- package/dist/wrangler/nimbus-wrangler.d.ts +157 -0
- package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -0
- package/dist/wrangler/nimbus-wrangler.js +931 -0
- package/package.json +77 -0
- package/public/_assets/cirrus-plugin-react.bundle.js +70740 -0
- package/public/_assets/esbuild-0.24.2.wasm +0 -0
- package/public/_assets/real-vite-bundle.js +101478 -0
- package/public/_assets/rollup.wasm +0 -0
- package/public/favicon.svg +8 -0
- package/public/index.html +565 -0
- package/public/s/index.html +2379 -0
- package/scripts/bundle-esbuild-wasm.mjs +244 -0
- package/scripts/bundle-facet-workers.mjs +137 -0
- package/scripts/bundle-git.mjs +118 -0
- package/scripts/bundle-npm-cjs.mjs +363 -0
- package/scripts/bundle-plugin-react.mjs +480 -0
- package/scripts/bundle-real-vite.mjs +959 -0
- package/scripts/bundle-runtime.mjs +600 -0
- package/scripts/bundle-tailwind-play.mjs +130 -0
- package/scripts/patch-lifo-ui.mjs +131 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm-install-batch-facet.ts — single-facet batch installer.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists
|
|
5
|
+
* ───────────────
|
|
6
|
+
* The previous architecture (src/npm-install-facet.ts + pool.map) spawned
|
|
7
|
+
* ONE dynamic worker per pool slot. With concurrency=4, that's 4 permanent
|
|
8
|
+
* loader entries in workerd's loader cache (each `loader.get(id, …)` call
|
|
9
|
+
* is cached by id and the cache is never released — confirmed in
|
|
10
|
+
* src/parallel/facet-pool.ts:328-348). Combine with:
|
|
11
|
+
* - resolver-facet pool: 1 loader entry
|
|
12
|
+
* - fetch-proxy: 1 loader entry
|
|
13
|
+
* - pre-bundle pool: 1 effective entry
|
|
14
|
+
* - install pool.map: 4 entries
|
|
15
|
+
* = 7 concurrent dynamic workers, tripping workerd's per-DO cap with
|
|
16
|
+
* "Too many concurrent dynamic workers" the moment install-pool tries
|
|
17
|
+
* to spawn its 4th slot.
|
|
18
|
+
*
|
|
19
|
+
* The fix: ONE facet for the whole install batch. The facet receives
|
|
20
|
+
* the full FacetPackageSpec[] and loops internally with pLimit(3),
|
|
21
|
+
* producing 1 loader entry instead of 4. Same architectural shape as
|
|
22
|
+
* src/npm-resolve-facet.ts — proven to work in production (commit 9194998).
|
|
23
|
+
*
|
|
24
|
+
* Memory plan inside the facet (pLimit=3, 16 MiB flush threshold):
|
|
25
|
+
* - 3 concurrent tarball pipelines: each holds at most 16 MiB of
|
|
26
|
+
* pending-flush bytes + 1× tarball-decompress state (~5-10 MiB) +
|
|
27
|
+
* integrity-hash buffer (compressed tarball size, ~1-3 MiB).
|
|
28
|
+
* - Peak ≈ 3 × (16 + 10 + 3) = ~87 MiB inside the facet's 128 MiB cap.
|
|
29
|
+
* - ~40 MiB headroom for V8 + tar-parser closure state.
|
|
30
|
+
*
|
|
31
|
+
* The per-package logic (fetch + integrity-verify + gunzip + tar-parse +
|
|
32
|
+
* writeBatch flush) is identical to src/npm-install-facet.ts — kept
|
|
33
|
+
* inlined here as a closure rather than imported because cloudflare-parallel
|
|
34
|
+
* serializes via fn.toString() and we cannot import from sibling modules
|
|
35
|
+
* across the isolate boundary. If the per-package logic in the legacy
|
|
36
|
+
* facet changes, mirror the change here.
|
|
37
|
+
*
|
|
38
|
+
* Stability invariants (cloudflare-parallel):
|
|
39
|
+
* - No `this` references.
|
|
40
|
+
* - No closure capture other than args + preamble names.
|
|
41
|
+
* - Preamble symbols (streamTarEntries, readableStreamToAsyncIterable,
|
|
42
|
+
* MAX_FILE_BYTES) referenced via @ts-ignore.
|
|
43
|
+
*/
|
|
44
|
+
// ── Facet function ──────────────────────────────────────────────────────
|
|
45
|
+
//
|
|
46
|
+
// Runs inside a NimbusLoaderPool isolate. Serialised via fn.toString();
|
|
47
|
+
// the helpers it references at top-level scope (streamTarEntries,
|
|
48
|
+
// readableStreamToAsyncIterable, MAX_FILE_BYTES) are NOT in the facet's
|
|
49
|
+
// lexical scope — the pool injects them via the preamble. No static
|
|
50
|
+
// imports of those names; references are bare identifiers.
|
|
51
|
+
export const installPackagesInFacet = async function installPackagesInFacet(batch, env) {
|
|
52
|
+
const tBatchStart = Date.now();
|
|
53
|
+
if (!batch || typeof batch !== 'object' || !Array.isArray(batch.packages)) {
|
|
54
|
+
throw new Error('installPackagesInFacet: missing batch.packages');
|
|
55
|
+
}
|
|
56
|
+
if (!env || !env.SUPERVISOR || typeof env.SUPERVISOR.writeBatch !== 'function') {
|
|
57
|
+
throw new Error('installPackagesInFacet: env.SUPERVISOR.writeBatch missing');
|
|
58
|
+
}
|
|
59
|
+
// [W7] Detect streaming RPC support ONCE per batch — the typeof check
|
|
60
|
+
// is cheap but we don't want to repeat it inside every flush hot path.
|
|
61
|
+
const supportsStreaming = typeof env.SUPERVISOR.writeBatchStream === 'function';
|
|
62
|
+
// [W4] Cap on how long we wait for the R2 cache before committing to
|
|
63
|
+
// the network response. 300 ms is generous enough for a regional R2
|
|
64
|
+
// GET (typically 30-100 ms) but bounds worst-case loss on a miss.
|
|
65
|
+
// Tunable; if cache hit-rate plateau is high in prod, raising this
|
|
66
|
+
// slightly may capture more wins on slow colos.
|
|
67
|
+
const R2_RACE_TIMEOUT_MS = 300;
|
|
68
|
+
const concurrency = Math.max(1, Math.min(batch.concurrency ?? 3, 8));
|
|
69
|
+
// ── pLimit (inlined; preamble doesn't carry a limiter helper) ────────
|
|
70
|
+
// Identical semantics to src/npm-resolver.ts:31-50 / src/npm-resolve-facet.ts.
|
|
71
|
+
let active = 0;
|
|
72
|
+
const queue = [];
|
|
73
|
+
const limit = (fn) => {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const run = async () => {
|
|
76
|
+
active++;
|
|
77
|
+
try {
|
|
78
|
+
resolve(await fn());
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
reject(e);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
active--;
|
|
85
|
+
if (queue.length > 0)
|
|
86
|
+
queue.shift()();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
if (active < concurrency)
|
|
90
|
+
run();
|
|
91
|
+
else
|
|
92
|
+
queue.push(run);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
// ── Counters (facet-local; folded into result.facetCounters at end) ──
|
|
96
|
+
let inFlight = 0;
|
|
97
|
+
let inFlightPeak = 0;
|
|
98
|
+
let cumulativeBytesDecoded = 0; // bytes of tarball body successfully read
|
|
99
|
+
let tarballsCompleted = 0;
|
|
100
|
+
// [W4] Pipelined-RPC race outcomes, folded back into supervisor diag.
|
|
101
|
+
let pipelinedTarballRaceWins = 0;
|
|
102
|
+
let pipelinedTarballRaceLosses = 0;
|
|
103
|
+
// cache-obs-2: per-tier event accumulator. Filled in the L2/L3
|
|
104
|
+
// (supervisor RPC return.events) and L4 (post-network-fetch)
|
|
105
|
+
// branches. Returned in result.cacheStatEvents at the end of the
|
|
106
|
+
// batch. installer.ts folds these into the DO-side cache-stats
|
|
107
|
+
// singleton via recordCacheStatEvents — same pattern as
|
|
108
|
+
// recordR2RaceCounters at installer.ts:1168.
|
|
109
|
+
const cacheStatEvents = [];
|
|
110
|
+
// Lower threshold than per-package: shared-buffer flushes happen
|
|
111
|
+
// across packages, so smaller chunks keep individual transactions
|
|
112
|
+
// short. Wave-2 shipped 16 MiB and produced batch-fanout abort
|
|
113
|
+
// (whole-batch RPC age-out). Tightened to 4 MiB.
|
|
114
|
+
const SHARED_RPC_FLUSH_THRESHOLD = 4 * 1024 * 1024;
|
|
115
|
+
const INODE_OVERHEAD = 160;
|
|
116
|
+
const CHUNK_OVERHEAD = 96;
|
|
117
|
+
let sharedInodes = [];
|
|
118
|
+
let sharedChunks = [];
|
|
119
|
+
let sharedBufferedBytes = 0;
|
|
120
|
+
// Mutex: only one flush runs at a time. Concurrent installs awaiting
|
|
121
|
+
// flush() will line up behind this promise and resolve in arrival
|
|
122
|
+
// order — the W7 frame is opaque to ordering so this is safe.
|
|
123
|
+
let sharedFlushInFlight = null;
|
|
124
|
+
const doSharedFlush = async () => {
|
|
125
|
+
if (sharedInodes.length === 0 && sharedChunks.length === 0)
|
|
126
|
+
return;
|
|
127
|
+
// Snapshot current contents and reset the buffer BEFORE awaiting
|
|
128
|
+
// the RPC so a concurrent install can start filling the next batch.
|
|
129
|
+
const inodesNow = sharedInodes;
|
|
130
|
+
const chunksNow = sharedChunks;
|
|
131
|
+
sharedInodes = [];
|
|
132
|
+
sharedChunks = [];
|
|
133
|
+
sharedBufferedBytes = 0;
|
|
134
|
+
if (supportsStreaming) {
|
|
135
|
+
// @ts-ignore — preamble symbol.
|
|
136
|
+
const stream = encodeWriteBatchStream({ inodes: inodesNow, chunks: chunksNow });
|
|
137
|
+
await env.SUPERVISOR.writeBatchStream(stream);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
await env.SUPERVISOR.writeBatch({ inodes: inodesNow, chunks: chunksNow });
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const sharedFlush = async () => {
|
|
144
|
+
// Serialize: wait for any in-flight flush to complete first; then
|
|
145
|
+
// start ours. Subsequent waiters chain after this one. Promise
|
|
146
|
+
// chain is unbounded but each link is awaited once — no leaks.
|
|
147
|
+
const prior = sharedFlushInFlight;
|
|
148
|
+
const myFlush = (async () => {
|
|
149
|
+
if (prior) {
|
|
150
|
+
try {
|
|
151
|
+
await prior;
|
|
152
|
+
}
|
|
153
|
+
catch { /* surface our own error, not the prior's */ }
|
|
154
|
+
}
|
|
155
|
+
await doSharedFlush();
|
|
156
|
+
})();
|
|
157
|
+
sharedFlushInFlight = myFlush;
|
|
158
|
+
try {
|
|
159
|
+
await myFlush;
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
// If we're still the head of the chain, clear the slot so memory
|
|
163
|
+
// doesn't grow unbounded over a long install.
|
|
164
|
+
if (sharedFlushInFlight === myFlush)
|
|
165
|
+
sharedFlushInFlight = null;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
// ── Per-package install (inlined fetchAndStagePackage logic) ─────────
|
|
169
|
+
//
|
|
170
|
+
// Mirrors src/npm-install-facet.ts:fetchAndStagePackage. Kept inline
|
|
171
|
+
// because cloudflare-parallel serializes this whole function via
|
|
172
|
+
// fn.toString() — we cannot import from a sibling module across the
|
|
173
|
+
// isolate boundary. Keep this logic in sync with npm-install-facet.ts.
|
|
174
|
+
const installOne = async (spec) => {
|
|
175
|
+
const t0 = Date.now();
|
|
176
|
+
const warnings = [];
|
|
177
|
+
inFlight++;
|
|
178
|
+
if (inFlight > inFlightPeak)
|
|
179
|
+
inFlightPeak = inFlight;
|
|
180
|
+
try {
|
|
181
|
+
// [W4] 1a. Race R2 cache lookup against network fetch.
|
|
182
|
+
//
|
|
183
|
+
// Strategy: kick BOTH off concurrently. Wait at most R2_RACE_TIMEOUT_MS
|
|
184
|
+
// for the R2 GET; if R2 returns first AND the bytes pass integrity,
|
|
185
|
+
// we use them and the network leg gets cancelled. Otherwise the
|
|
186
|
+
// network response (which has been making progress in the
|
|
187
|
+
// background) takes over.
|
|
188
|
+
//
|
|
189
|
+
// Soft-fail: if env.SUPERVISOR.getCachedTarball isn't defined
|
|
190
|
+
// (older supervisor deployment), the R2 leg becomes a noop and
|
|
191
|
+
// we go straight to the network path with no overhead.
|
|
192
|
+
const r2Available = typeof env.SUPERVISOR.getCachedTarball === 'function';
|
|
193
|
+
// cache-obs-2: supervisor return shape evolved from `Uint8Array
|
|
194
|
+
// | null` to `{ bytes, events }`. The race wrapper handles both:
|
|
195
|
+
// a v1 supervisor returns bare bytes (or null); a v2 supervisor
|
|
196
|
+
// returns the envelope. The downstream code only needs `bytes`
|
|
197
|
+
// — the envelope's events are spliced into cacheStatEvents.
|
|
198
|
+
const r2P = r2Available
|
|
199
|
+
? Promise.race([
|
|
200
|
+
env.SUPERVISOR.getCachedTarball(spec.name, spec.version),
|
|
201
|
+
new Promise((rs) => setTimeout(() => rs(null), R2_RACE_TIMEOUT_MS)),
|
|
202
|
+
]).catch(() => null)
|
|
203
|
+
: Promise.resolve(null);
|
|
204
|
+
// [W4] Captured compressed bytes for write-back to R2 on miss.
|
|
205
|
+
// Populated by the integrity-tee path below; remains null when
|
|
206
|
+
// integrity isn't present (rare; we only writeback when we can
|
|
207
|
+
// verify on next read). Hoisted to installOne scope per W4-plan
|
|
208
|
+
// §11 finding #4 lifecycle correctness.
|
|
209
|
+
let capturedTgzBytes = null;
|
|
210
|
+
let r2HitBytes = null;
|
|
211
|
+
// 1b. Try R2 first (bounded wait).
|
|
212
|
+
if (r2Available) {
|
|
213
|
+
try {
|
|
214
|
+
const r2Result = await r2P;
|
|
215
|
+
// Adapt to BOTH supervisor return shapes:
|
|
216
|
+
// v1: Uint8Array | null
|
|
217
|
+
// v2: { bytes: Uint8Array | null, events: [...] }
|
|
218
|
+
if (r2Result && typeof r2Result === 'object' && !(r2Result instanceof Uint8Array)) {
|
|
219
|
+
const env2 = r2Result;
|
|
220
|
+
r2HitBytes = env2.bytes;
|
|
221
|
+
// cache-obs-2: splice supervisor's per-tier events into
|
|
222
|
+
// the facet's accumulator. Filter to known tiers/kinds
|
|
223
|
+
// so a future supervisor schema change doesn't poison
|
|
224
|
+
// the result.
|
|
225
|
+
if (Array.isArray(env2.events)) {
|
|
226
|
+
for (const e of env2.events) {
|
|
227
|
+
if (!e || (e.kind !== 'hit' && e.kind !== 'miss'))
|
|
228
|
+
continue;
|
|
229
|
+
if (e.tier !== 'L2' && e.tier !== 'L3')
|
|
230
|
+
continue;
|
|
231
|
+
if (e.cacheKind !== 'tarball')
|
|
232
|
+
continue;
|
|
233
|
+
if (e.kind === 'hit') {
|
|
234
|
+
cacheStatEvents.push({
|
|
235
|
+
kind: 'hit',
|
|
236
|
+
tier: e.tier,
|
|
237
|
+
cacheKind: 'tarball',
|
|
238
|
+
bytes: typeof e.bytes === 'number' ? e.bytes : 0,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
cacheStatEvents.push({ kind: 'miss', tier: e.tier, cacheKind: 'tarball' });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
r2HitBytes = r2Result;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
r2HitBytes = null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// ── R2 HIT path ──────────────────────────────────────────────
|
|
256
|
+
// We have bytes from R2. Verify integrity if supplied; on
|
|
257
|
+
// mismatch fall through to network. On success, synthesize a
|
|
258
|
+
// body stream and skip network entirely.
|
|
259
|
+
let resp;
|
|
260
|
+
// Definitely-assigned by either the R2-hit branch OR the network
|
|
261
|
+
// branch below; explicit `!` keeps TS happy without runtime cost.
|
|
262
|
+
let bytesStream;
|
|
263
|
+
let integrityPromise = Promise.resolve();
|
|
264
|
+
if (r2HitBytes && r2HitBytes.length > 0) {
|
|
265
|
+
// Integrity-verify the R2 bytes ONCE before we use them. If
|
|
266
|
+
// mismatch, treat as a cache miss + best-effort delete.
|
|
267
|
+
let integrityOk = true;
|
|
268
|
+
if (spec.integrity && spec.integrity.indexOf('-') !== -1) {
|
|
269
|
+
const dash = spec.integrity.indexOf('-');
|
|
270
|
+
const algo = spec.integrity.slice(0, dash).toLowerCase();
|
|
271
|
+
const expectedB64 = spec.integrity.slice(dash + 1);
|
|
272
|
+
const subtleAlgo = algo === 'sha512' ? 'SHA-512'
|
|
273
|
+
: algo === 'sha384' ? 'SHA-384'
|
|
274
|
+
: algo === 'sha256' ? 'SHA-256'
|
|
275
|
+
: algo === 'sha1' ? 'SHA-1'
|
|
276
|
+
: '';
|
|
277
|
+
if (subtleAlgo) {
|
|
278
|
+
const digest = await crypto.subtle.digest(subtleAlgo, r2HitBytes);
|
|
279
|
+
const dBytes = new Uint8Array(digest);
|
|
280
|
+
let bin = '';
|
|
281
|
+
for (let i = 0; i < dBytes.length; i++)
|
|
282
|
+
bin += String.fromCharCode(dBytes[i]);
|
|
283
|
+
const gotB64 = btoa(bin);
|
|
284
|
+
if (gotB64 !== expectedB64) {
|
|
285
|
+
integrityOk = false;
|
|
286
|
+
warnings.push(`R2 cache integrity mismatch for ${spec.name}@${spec.version}; falling through to network`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (integrityOk) {
|
|
291
|
+
pipelinedTarballRaceWins++;
|
|
292
|
+
tarballsCompleted++;
|
|
293
|
+
cumulativeBytesDecoded += r2HitBytes.length;
|
|
294
|
+
// Synthesize a Response body from the R2 bytes so the
|
|
295
|
+
// existing decompress+tar pipeline below works unchanged.
|
|
296
|
+
// No tee needed: integrity already verified.
|
|
297
|
+
bytesStream = new Response(r2HitBytes).body;
|
|
298
|
+
resp = new Response(r2HitBytes, { status: 200 });
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
// Integrity-mismatch: drop R2 hit; fall through to network
|
|
302
|
+
// after best-effort cache delete.
|
|
303
|
+
r2HitBytes = null;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (!r2HitBytes) {
|
|
307
|
+
pipelinedTarballRaceLosses++;
|
|
308
|
+
// 1c. Fetch with retry on 5xx + network errors.
|
|
309
|
+
// Budget: 3 retries, jittered backoff 500/1500/4500 ms ±25%.
|
|
310
|
+
const FACET_BACKOFF_MS = [500, 1500, 4500];
|
|
311
|
+
const FACET_RETRIES = 3;
|
|
312
|
+
let lastErr;
|
|
313
|
+
for (let attempt = 0; attempt <= FACET_RETRIES; attempt++) {
|
|
314
|
+
try {
|
|
315
|
+
const r = await fetch(spec.tarballUrl);
|
|
316
|
+
if (r.ok || r.status < 500 || r.status > 599) {
|
|
317
|
+
resp = r;
|
|
318
|
+
lastErr = undefined;
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
await r.body?.cancel();
|
|
323
|
+
}
|
|
324
|
+
catch { /* best-effort */ }
|
|
325
|
+
lastErr = new Error(`HTTP ${r.status}`);
|
|
326
|
+
if (attempt === FACET_RETRIES) {
|
|
327
|
+
resp = r;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
const base = FACET_BACKOFF_MS[Math.min(attempt, FACET_BACKOFF_MS.length - 1)];
|
|
331
|
+
const jitter = Math.round(base + (Math.random() * 2 - 1) * base * 0.25);
|
|
332
|
+
const delayMs = Math.max(0, jitter);
|
|
333
|
+
warnings.push(`retry ${attempt + 1}/${FACET_RETRIES} after ${delayMs}ms (HTTP ${r.status})`);
|
|
334
|
+
await new Promise((rs) => setTimeout(rs, delayMs));
|
|
335
|
+
}
|
|
336
|
+
catch (e) {
|
|
337
|
+
lastErr = e;
|
|
338
|
+
if (attempt === FACET_RETRIES)
|
|
339
|
+
break;
|
|
340
|
+
const base = FACET_BACKOFF_MS[Math.min(attempt, FACET_BACKOFF_MS.length - 1)];
|
|
341
|
+
const jitter = Math.round(base + (Math.random() * 2 - 1) * base * 0.25);
|
|
342
|
+
const delayMs = Math.max(0, jitter);
|
|
343
|
+
const reason = e?.name === 'AbortError' ? 'timeout' : (e?.message || String(e));
|
|
344
|
+
warnings.push(`retry ${attempt + 1}/${FACET_RETRIES} after ${delayMs}ms (${reason})`);
|
|
345
|
+
await new Promise((rs) => setTimeout(rs, delayMs));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (!resp) {
|
|
349
|
+
return {
|
|
350
|
+
name: spec.name, version: spec.version,
|
|
351
|
+
fileCount: 0, bytesWritten: 0, elapsed: Date.now() - t0, warnings,
|
|
352
|
+
errorText: `fetch failed: ${lastErr?.message || String(lastErr)}`,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (!resp.ok) {
|
|
356
|
+
return {
|
|
357
|
+
name: spec.name, version: spec.version,
|
|
358
|
+
fileCount: 0, bytesWritten: 0, elapsed: Date.now() - t0, warnings,
|
|
359
|
+
errorText: `HTTP ${resp.status}`,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const body = resp.body;
|
|
363
|
+
if (!body) {
|
|
364
|
+
return {
|
|
365
|
+
name: spec.name, version: spec.version,
|
|
366
|
+
fileCount: 0, bytesWritten: 0, elapsed: Date.now() - t0, warnings,
|
|
367
|
+
errorText: 'no response body',
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
// cache-obs-2: record the L4 (registry.npmjs.org) hit. We're
|
|
371
|
+
// about to stream the body — the byte count is known either
|
|
372
|
+
// via the response's Content-Length header OR we can sum it
|
|
373
|
+
// as we read. Prefer the header for accuracy (it's the
|
|
374
|
+
// authoritative size); fall back to 0 when missing (some
|
|
375
|
+
// registry mirrors omit it for chunked responses).
|
|
376
|
+
const l4ContentLength = (() => {
|
|
377
|
+
const cl = resp.headers.get('content-length');
|
|
378
|
+
if (!cl)
|
|
379
|
+
return 0;
|
|
380
|
+
const n = parseInt(cl, 10);
|
|
381
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
382
|
+
})();
|
|
383
|
+
cacheStatEvents.push({
|
|
384
|
+
kind: 'hit',
|
|
385
|
+
tier: 'L4',
|
|
386
|
+
cacheKind: 'tarball',
|
|
387
|
+
bytes: l4ContentLength,
|
|
388
|
+
});
|
|
389
|
+
// 2. Integrity verify (if supplied) AND capture bytes for R2 write-back.
|
|
390
|
+
if (spec.integrity && spec.integrity.indexOf('-') !== -1) {
|
|
391
|
+
const dash = spec.integrity.indexOf('-');
|
|
392
|
+
const algo = spec.integrity.slice(0, dash).toLowerCase();
|
|
393
|
+
const expectedB64 = spec.integrity.slice(dash + 1);
|
|
394
|
+
const subtleAlgo = algo === 'sha512' ? 'SHA-512'
|
|
395
|
+
: algo === 'sha384' ? 'SHA-384'
|
|
396
|
+
: algo === 'sha256' ? 'SHA-256'
|
|
397
|
+
: algo === 'sha1' ? 'SHA-1'
|
|
398
|
+
: '';
|
|
399
|
+
if (!subtleAlgo) {
|
|
400
|
+
warnings.push(`unknown integrity algo "${algo}"; skipped verification`);
|
|
401
|
+
bytesStream = body;
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
const [s1, s2] = body.tee();
|
|
405
|
+
bytesStream = s1;
|
|
406
|
+
integrityPromise = (async () => {
|
|
407
|
+
const chunks = [];
|
|
408
|
+
const reader = s2.getReader();
|
|
409
|
+
let total = 0;
|
|
410
|
+
while (true) {
|
|
411
|
+
const { value, done } = await reader.read();
|
|
412
|
+
if (done)
|
|
413
|
+
break;
|
|
414
|
+
if (value) {
|
|
415
|
+
chunks.push(value);
|
|
416
|
+
total += value.length;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
cumulativeBytesDecoded += total;
|
|
420
|
+
const flat = new Uint8Array(total);
|
|
421
|
+
let o = 0;
|
|
422
|
+
for (const c of chunks) {
|
|
423
|
+
flat.set(c, o);
|
|
424
|
+
o += c.length;
|
|
425
|
+
}
|
|
426
|
+
const digest = await crypto.subtle.digest(subtleAlgo, flat);
|
|
427
|
+
const bytes = new Uint8Array(digest);
|
|
428
|
+
let bin = '';
|
|
429
|
+
for (let i = 0; i < bytes.length; i++)
|
|
430
|
+
bin += String.fromCharCode(bytes[i]);
|
|
431
|
+
const gotB64 = btoa(bin);
|
|
432
|
+
if (gotB64 !== expectedB64) {
|
|
433
|
+
throw new Error(`integrity mismatch for ${spec.name}@${spec.version}: expected ${algo}-${expectedB64}, got ${algo}-${gotB64}`);
|
|
434
|
+
}
|
|
435
|
+
// [W4] Capture for R2 write-back. Lifecycle: this assignment
|
|
436
|
+
// happens before integrityPromise resolves, which is awaited
|
|
437
|
+
// before flush() finishes. installOne then awaits the put
|
|
438
|
+
// before returning, so capturedTgzBytes is always populated
|
|
439
|
+
// by the time we reach the write-back code below.
|
|
440
|
+
capturedTgzBytes = flat;
|
|
441
|
+
})();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
bytesStream = body;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
// Already have bytesStream from R2 hit; just suppress
|
|
450
|
+
// unused-variable warning on resp.
|
|
451
|
+
void resp;
|
|
452
|
+
}
|
|
453
|
+
// 3. Decompress + tar parse (streaming).
|
|
454
|
+
const decompressed = bytesStream.pipeThrough(new DecompressionStream('gzip'));
|
|
455
|
+
// @ts-ignore — preamble symbol.
|
|
456
|
+
const asyncIter = readableStreamToAsyncIterable(decompressed);
|
|
457
|
+
// 4. Build + flush BatchWritePayload(s) at the threshold.
|
|
458
|
+
// Pre-W7 threshold: 16 MiB to keep the structured-clone payload
|
|
459
|
+
// well under workerd's 32 MiB cap with 6% serialization
|
|
460
|
+
// overhead. With pLimit=3, total in-flight pending flush bytes
|
|
461
|
+
// peaked at 3 × 16 = 48 MiB inside the 128 MiB cap.
|
|
462
|
+
// W7 (streaming path): the RPC has no 32 MiB cap because the
|
|
463
|
+
// bytes traverse the boundary as a flow-controlled byte stream.
|
|
464
|
+
// We could in principle drop this threshold entirely (one
|
|
465
|
+
// flush per package), but a memory-pressure boundary is still
|
|
466
|
+
// useful — a single 100 MiB tarball would otherwise hold 100
|
|
467
|
+
// MiB resident in the chunks array before flushing. Keep the
|
|
468
|
+
// threshold; raise it if/when measured peak heap suggests the
|
|
469
|
+
// legacy 16 MiB is now the bottleneck on the streaming path.
|
|
470
|
+
const pkgDir = spec.pkgDir;
|
|
471
|
+
// [P0a wave-2] Use SHARED inode/chunk buffer (declared at outer
|
|
472
|
+
// scope) so flushes are per-peer-shard, not per-package. Tracked
|
|
473
|
+
// per-package totals stay local for the result object.
|
|
474
|
+
let totalFileInodes = 0;
|
|
475
|
+
let totalBytesWritten = 0;
|
|
476
|
+
const dirSet = new Set();
|
|
477
|
+
const parentOf = (p) => (p.includes('/') ? p.substring(0, p.lastIndexOf('/')) : '');
|
|
478
|
+
dirSet.add(pkgDir);
|
|
479
|
+
const onSkip = (name, size, reason) => {
|
|
480
|
+
if (reason === 'too-large') {
|
|
481
|
+
warnings.push(`skipped "${name}" (${size} bytes) — exceeds per-file cap; file not installed`);
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
// @ts-ignore — preamble symbol.
|
|
485
|
+
for await (const entry of streamTarEntries(asyncIter, onSkip)) {
|
|
486
|
+
const filePath = pkgDir + '/' + entry.name;
|
|
487
|
+
const parts = filePath.split('/');
|
|
488
|
+
for (let i = 1; i < parts.length; i++) {
|
|
489
|
+
dirSet.add(parts.slice(0, i).join('/'));
|
|
490
|
+
}
|
|
491
|
+
const data = entry.data;
|
|
492
|
+
const size = data.length;
|
|
493
|
+
const chunkCount = size === 0 ? 0 : Math.ceil(size / spec.chunkSize);
|
|
494
|
+
sharedInodes.push({
|
|
495
|
+
path: filePath, parentPath: parentOf(filePath), isDir: false,
|
|
496
|
+
size, mtime: spec.mtime, mode: 0o644, chunkCount,
|
|
497
|
+
});
|
|
498
|
+
sharedBufferedBytes += INODE_OVERHEAD + filePath.length * 2;
|
|
499
|
+
totalFileInodes += 1;
|
|
500
|
+
totalBytesWritten += size;
|
|
501
|
+
if (size > 0) {
|
|
502
|
+
if (size <= spec.chunkSize) {
|
|
503
|
+
sharedChunks.push({ path: filePath, chunkId: 0, data });
|
|
504
|
+
sharedBufferedBytes += CHUNK_OVERHEAD + filePath.length + data.length;
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
for (let c = 0; c < chunkCount; c++) {
|
|
508
|
+
const slice = data.slice(c * spec.chunkSize, (c + 1) * spec.chunkSize);
|
|
509
|
+
sharedChunks.push({ path: filePath, chunkId: c, data: slice });
|
|
510
|
+
sharedBufferedBytes += CHUNK_OVERHEAD + filePath.length + slice.length;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (sharedBufferedBytes >= SHARED_RPC_FLUSH_THRESHOLD) {
|
|
515
|
+
await sharedFlush();
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// Wait for integrity verification before final flush.
|
|
519
|
+
await integrityPromise;
|
|
520
|
+
// Append directory inodes to the SHARED buffer. Don't flush per-
|
|
521
|
+
// package end — let the threshold-based flush coalesce across
|
|
522
|
+
// packages. The end-of-batch flush below catches anything left.
|
|
523
|
+
for (const d of dirSet) {
|
|
524
|
+
sharedInodes.push({
|
|
525
|
+
path: d, parentPath: parentOf(d), isDir: true,
|
|
526
|
+
size: 0, mtime: spec.mtime, mode: 0o755, chunkCount: 0,
|
|
527
|
+
});
|
|
528
|
+
sharedBufferedBytes += INODE_OVERHEAD + d.length * 2;
|
|
529
|
+
}
|
|
530
|
+
// Threshold-based flush (NOT per-package) — only fires if the
|
|
531
|
+
// shared buffer has crossed SHARED_RPC_FLUSH_THRESHOLD as a
|
|
532
|
+
// result of this package's contributions. Otherwise the buffer
|
|
533
|
+
// continues accumulating across the next package(s) in pLimit.
|
|
534
|
+
if (sharedBufferedBytes >= SHARED_RPC_FLUSH_THRESHOLD) {
|
|
535
|
+
await sharedFlush();
|
|
536
|
+
}
|
|
537
|
+
// [W4] Write tarball to R2 cache after a successful network install
|
|
538
|
+
// so the next tenant on the platform skips the round-trip to npm.
|
|
539
|
+
// Awaited (not `void`) per W4-plan §11 finding #2: the facet's
|
|
540
|
+
// lifecycle ends when this fn returns; an unawaited put may be
|
|
541
|
+
// torn down before the R2 write completes.
|
|
542
|
+
//
|
|
543
|
+
// Counter only increments tarballsCompleted on the network-fetch
|
|
544
|
+
// path (R2-hit path bumps it earlier). Avoids double counting.
|
|
545
|
+
if (!r2HitBytes) {
|
|
546
|
+
tarballsCompleted++;
|
|
547
|
+
if (capturedTgzBytes && typeof env.SUPERVISOR.putCachedTarball === 'function') {
|
|
548
|
+
try {
|
|
549
|
+
await env.SUPERVISOR.putCachedTarball(spec.name, spec.version, capturedTgzBytes);
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
// Best-effort cache write — never fail the install on R2 errors.
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
name: spec.name, version: spec.version,
|
|
558
|
+
fileCount: totalFileInodes, bytesWritten: totalBytesWritten,
|
|
559
|
+
elapsed: Date.now() - t0, warnings,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
catch (e) {
|
|
563
|
+
return {
|
|
564
|
+
name: spec.name, version: spec.version,
|
|
565
|
+
fileCount: 0, bytesWritten: 0, elapsed: Date.now() - t0, warnings,
|
|
566
|
+
errorText: e?.message || String(e),
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
finally {
|
|
570
|
+
inFlight = Math.max(0, inFlight - 1);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
// ── Dispatch all packages with internal pLimit ───────────────────────
|
|
574
|
+
const perPackage = await Promise.all(batch.packages.map((spec) => limit(() => installOne(spec))));
|
|
575
|
+
// [P0a wave-2] End-of-batch shared flush. Drains the last buffered
|
|
576
|
+
// contributions (the per-package flush is threshold-based — anything
|
|
577
|
+
// below the threshold sits here until end-of-batch).
|
|
578
|
+
await sharedFlush();
|
|
579
|
+
// Wait for any chained flush still in-flight from the threshold path.
|
|
580
|
+
if (sharedFlushInFlight) {
|
|
581
|
+
try {
|
|
582
|
+
await sharedFlushInFlight;
|
|
583
|
+
}
|
|
584
|
+
catch { /* errored flushes already surfaced */ }
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
perPackage,
|
|
588
|
+
elapsed: Date.now() - tBatchStart,
|
|
589
|
+
facetCounters: {
|
|
590
|
+
tarballsCompleted,
|
|
591
|
+
cumulativeBytesDecoded,
|
|
592
|
+
peakInFlight: inFlightPeak,
|
|
593
|
+
pipelinedTarballRaceWins,
|
|
594
|
+
pipelinedTarballRaceLosses,
|
|
595
|
+
},
|
|
596
|
+
cacheStatEvents,
|
|
597
|
+
};
|
|
598
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm-install-facet.ts — per-package install spec shape.
|
|
3
|
+
*
|
|
4
|
+
* The supervisor↔facet wire shape for one npm package install. Sent
|
|
5
|
+
* as a `FacetPackageSpec[]` over a single RPC call to the install
|
|
6
|
+
* batch facet (src/npm/install-batch-facet.ts), which loops with
|
|
7
|
+
* pLimit(3) and writes each package into the VFS in parallel.
|
|
8
|
+
*/
|
|
9
|
+
/** Per-package install spec the supervisor sends to the install
|
|
10
|
+
* batch facet. One per package; the spec list rides as a single
|
|
11
|
+
* argument across one RPC call. */
|
|
12
|
+
export interface FacetPackageSpec {
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
tarballUrl: string;
|
|
16
|
+
/**
|
|
17
|
+
* npm subresource-integrity string, e.g. "sha512-...base64...".
|
|
18
|
+
* When present, the facet verifies it before extraction.
|
|
19
|
+
* Empty/missing = skip verification (older packages on npm don't publish it).
|
|
20
|
+
*/
|
|
21
|
+
integrity: string;
|
|
22
|
+
/** Absolute path inside the VFS where this package is installed. */
|
|
23
|
+
pkgDir: string;
|
|
24
|
+
/** mtime for every inode written by this package (ms since epoch). */
|
|
25
|
+
mtime: number;
|
|
26
|
+
/** Chunk size used by the VFS (must match sqlite-vfs.ts CHUNK_SIZE). */
|
|
27
|
+
chunkSize: number;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=install-facet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-facet.d.ts","sourceRoot":"","sources":["../../src/npm/install-facet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;oCAEoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm-install-facet.ts — per-package install spec shape.
|
|
3
|
+
*
|
|
4
|
+
* The supervisor↔facet wire shape for one npm package install. Sent
|
|
5
|
+
* as a `FacetPackageSpec[]` over a single RPC call to the install
|
|
6
|
+
* batch facet (src/npm/install-batch-facet.ts), which loops with
|
|
7
|
+
* pLimit(3) and writes each package into the VFS in parallel.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|