@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,700 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* git-network-facet.ts — Facet-based git clone/fetch/pull.
|
|
3
|
+
*
|
|
4
|
+
* Runs isomorphic-git's network operations (clone/fetch/pull) inside a
|
|
5
|
+
* dynamic worker (LOADER.load) to escape the supervisor DO's CPU budget
|
|
6
|
+
* and to avoid the known DO fetch() hang in wrangler local dev.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* - Facet holds a buffered fs adapter: writes accumulate in memory
|
|
10
|
+
* - When buffer reaches WAVE_SIZE files or WAVE_BYTES bytes, flush
|
|
11
|
+
* via ONE supervisor.writeBatch() RPC (atomic transactionSync).
|
|
12
|
+
* - At clone end, a final flush commits remaining buffered state.
|
|
13
|
+
* - Reads fall through: buffer → supervisor.readFile / supervisor.stat.
|
|
14
|
+
*
|
|
15
|
+
* Why this fixes the hang:
|
|
16
|
+
* - CPU-heavy packfile delta resolution runs in facet (own CPU budget)
|
|
17
|
+
* - No per-file RPC round-trips — ~1 writeBatch per 500 files
|
|
18
|
+
* - Packfile network fetch works (facet fetch is reliable, DO fetch hangs)
|
|
19
|
+
* - cf-git's nonBlocking=true option yields to event loop between batches
|
|
20
|
+
*
|
|
21
|
+
* See docs/analysis in git-network-facet plan — the canonical write-up lives
|
|
22
|
+
* in the PR that introduced this file.
|
|
23
|
+
*/
|
|
24
|
+
import { getCtxExports } from '../session/ctx-exports.js';
|
|
25
|
+
import { CF_COMPAT_DATE } from '../constants.js';
|
|
26
|
+
import { GIT_BUNDLE_CODE } from '../git-bundle.generated.js';
|
|
27
|
+
import { W7_FRAME_PREAMBLE } from '../loaders/generated-workers.js';
|
|
28
|
+
/**
|
|
29
|
+
* Run a git network op inside a facet. Returns when complete or timed out.
|
|
30
|
+
*/
|
|
31
|
+
export async function execGitNetwork(ctx, env, opts) {
|
|
32
|
+
const start = Date.now();
|
|
33
|
+
try {
|
|
34
|
+
if (!env?.LOADER?.load) {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
error: 'env.LOADER.load not available — cannot spawn git facet',
|
|
38
|
+
elapsed: Date.now() - start,
|
|
39
|
+
filesWritten: 0,
|
|
40
|
+
bytesWritten: 0,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const ctxExports = getCtxExports();
|
|
44
|
+
const supervisorBinding = ctxExports?.SupervisorRPC
|
|
45
|
+
? ctxExports.SupervisorRPC({ props: { doId: ctx.id.toString(), pid: 0 } })
|
|
46
|
+
: undefined;
|
|
47
|
+
if (!supervisorBinding) {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
error: 'SupervisorRPC binding not available',
|
|
51
|
+
elapsed: Date.now() - start,
|
|
52
|
+
filesWritten: 0,
|
|
53
|
+
bytesWritten: 0,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const worker = env.LOADER.load({
|
|
57
|
+
compatibilityDate: CF_COMPAT_DATE,
|
|
58
|
+
compatibilityFlags: ['nodejs_compat'],
|
|
59
|
+
mainModule: 'git-network-worker.js',
|
|
60
|
+
// Facet gets:
|
|
61
|
+
// - its own worker code (git-network-worker.js), with the
|
|
62
|
+
// W7 frame helpers (encodeWriteBatchStream + supporting
|
|
63
|
+
// state) prepended so the buffered fs adapter can call
|
|
64
|
+
// them as bare identifiers — the same shape NimbusLoaderPool's
|
|
65
|
+
// `preamble` option provides for npm install. This is the
|
|
66
|
+
// fix for git-freeze: writeBatch via structured-clone
|
|
67
|
+
// accumulates ~4 MiB residency per wave in the ctx.exports
|
|
68
|
+
// SupervisorRPC wrapper isolate, which OOMs after ~5 waves
|
|
69
|
+
// on real repos. writeBatchStream uses a 256 KiB-highwater
|
|
70
|
+
// ReadableStream, keeping wrapper-isolate residency
|
|
71
|
+
// bounded. See audit/sections/GIT-FREEZE-retro.md.
|
|
72
|
+
// - the pre-bundled isomorphic-git (git-bundle.js)
|
|
73
|
+
modules: {
|
|
74
|
+
'git-network-worker.js': W7_FRAME_PREAMBLE + '\n' + generateGitNetworkFacetCode(),
|
|
75
|
+
'git-bundle.js': GIT_BUNDLE_CODE,
|
|
76
|
+
},
|
|
77
|
+
env: { SUPERVISOR: supervisorBinding },
|
|
78
|
+
});
|
|
79
|
+
const entrypoint = worker.getEntrypoint();
|
|
80
|
+
// Every one of (worker, entrypoint, response, supervisorBinding) may be
|
|
81
|
+
// a cross-isolate RPC stub. Without explicit disposal they accumulate
|
|
82
|
+
// in the supervisor's live-reference set until the enclosing DO request
|
|
83
|
+
// returns — and during an npm install that request lives for tens of
|
|
84
|
+
// seconds, during which git clone + 200 packument resolves can each
|
|
85
|
+
// contribute stubs. When enough pile up, workerd's isolate-shutdown
|
|
86
|
+
// queue trips `queueState != ACTIVE` (see WORKERD-CRASH.md).
|
|
87
|
+
//
|
|
88
|
+
// Helper: best-effort Symbol.dispose. ES2023 added the symbol; the
|
|
89
|
+
// tsconfig target is ES2022, so we reach for it via any-cast. Non-RPC
|
|
90
|
+
// objects have no dispose handler and the call is a no-op.
|
|
91
|
+
const disposerKey = Symbol.dispose;
|
|
92
|
+
const dispose = (obj) => {
|
|
93
|
+
if (!obj || !disposerKey)
|
|
94
|
+
return;
|
|
95
|
+
const fn = obj[disposerKey];
|
|
96
|
+
if (typeof fn === 'function') {
|
|
97
|
+
try {
|
|
98
|
+
fn.call(obj);
|
|
99
|
+
}
|
|
100
|
+
catch { /* best-effort */ }
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const timeoutMs = opts.timeout ?? 300_000;
|
|
104
|
+
const timeout = new Promise((_, rej) => setTimeout(() => rej(new Error(`git ${opts.op} timed out after ${timeoutMs / 1000}s`)), timeoutMs));
|
|
105
|
+
// Parse the facet's response and dispose its RPC stub before the
|
|
106
|
+
// caller sees the value. A bare `.then(r => r.json())` drops the `r`
|
|
107
|
+
// reference but leaves the stub live until the surrounding event
|
|
108
|
+
// handler completes.
|
|
109
|
+
const call = entrypoint.fetch(new Request('http://git/op', {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
body: JSON.stringify(opts),
|
|
112
|
+
})).then(async (r) => {
|
|
113
|
+
try {
|
|
114
|
+
return await r.json();
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
dispose(r);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
let result;
|
|
121
|
+
try {
|
|
122
|
+
result = await Promise.race([call, timeout]);
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
// Tear down the facet's RPC stubs regardless of success / timeout.
|
|
126
|
+
// `entrypoint` and `worker` are both cross-isolate stubs; disposing
|
|
127
|
+
// them lets workerd reclaim the dynamic worker's memory eagerly.
|
|
128
|
+
// `supervisorBinding` is the SupervisorRPC stub we minted above —
|
|
129
|
+
// it's from ctxExports (local to the supervisor's own isolate) so
|
|
130
|
+
// in theory it doesn't leak across isolates, but disposing is cheap
|
|
131
|
+
// and symmetric with how the facet's env.SUPERVISOR is handled on
|
|
132
|
+
// the other side.
|
|
133
|
+
dispose(entrypoint);
|
|
134
|
+
dispose(worker);
|
|
135
|
+
dispose(supervisorBinding);
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
success: !!result?.success,
|
|
139
|
+
error: result?.error,
|
|
140
|
+
elapsed: Date.now() - start,
|
|
141
|
+
filesWritten: Number(result?.filesWritten ?? 0),
|
|
142
|
+
bytesWritten: Number(result?.bytesWritten ?? 0),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
error: e?.message || String(e),
|
|
149
|
+
elapsed: Date.now() - start,
|
|
150
|
+
filesWritten: 0,
|
|
151
|
+
bytesWritten: 0,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Generate the dynamic worker code for the git network facet.
|
|
157
|
+
*
|
|
158
|
+
* Exports `default { async fetch(request, workerEnv) { ... } }`.
|
|
159
|
+
* Reads op args from the POST body, runs isomorphic-git with a buffered
|
|
160
|
+
* fs adapter, flushes writes to supervisor via writeBatch RPC.
|
|
161
|
+
*/
|
|
162
|
+
function generateGitNetworkFacetCode() {
|
|
163
|
+
return `
|
|
164
|
+
const CHUNK_SIZE = 65536; // must match sqlite-vfs.ts
|
|
165
|
+
const WAVE_FILES = 500; // flush every N buffered files
|
|
166
|
+
const WAVE_BYTES = 4 * 1024 * 1024; // or every 4MB
|
|
167
|
+
|
|
168
|
+
function normalizePath(p) {
|
|
169
|
+
const parts = String(p || '').split('/');
|
|
170
|
+
const out = [];
|
|
171
|
+
for (const seg of parts) {
|
|
172
|
+
if (seg === '..' && out.length > 0) out.pop();
|
|
173
|
+
else if (seg !== '.' && seg !== '' && seg !== undefined) out.push(seg);
|
|
174
|
+
}
|
|
175
|
+
return out.join('/');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function parentOf(p) {
|
|
179
|
+
return p.includes('/') ? p.substring(0, p.lastIndexOf('/')) : '';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function enoent(filepath) {
|
|
183
|
+
const err = new Error('ENOENT: no such file or directory, ' + filepath);
|
|
184
|
+
err.code = 'ENOENT'; err.errno = -2;
|
|
185
|
+
return err;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function dirStatObj() {
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
const d = new Date(now);
|
|
191
|
+
return {
|
|
192
|
+
isFile: () => false, isDirectory: () => true, isSymbolicLink: () => false,
|
|
193
|
+
size: 0, mode: 0o755, type: 'dir',
|
|
194
|
+
mtimeMs: now, mtime: d, ctimeMs: now, ctime: d, atimeMs: now, atime: d,
|
|
195
|
+
uid: 1000, gid: 1000, dev: 0, ino: 0, nlink: 1,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function fileStatObj(size) {
|
|
200
|
+
const now = Date.now();
|
|
201
|
+
const d = new Date(now);
|
|
202
|
+
return {
|
|
203
|
+
isFile: () => true, isDirectory: () => false, isSymbolicLink: () => false,
|
|
204
|
+
size, mode: 0o644, type: 'file',
|
|
205
|
+
mtimeMs: now, mtime: d, ctimeMs: now, ctime: d, atimeMs: now, atime: d,
|
|
206
|
+
uid: 1000, gid: 1000, dev: 0, ino: 0, nlink: 1,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function convertSupervisorStat(st) {
|
|
211
|
+
if (!st) return null;
|
|
212
|
+
const mtimeMs = Number(st.mtime) || Date.now();
|
|
213
|
+
const d = new Date(mtimeMs);
|
|
214
|
+
const isDir = st.type === 'directory' || st.type === 'dir';
|
|
215
|
+
return {
|
|
216
|
+
isFile: () => !isDir,
|
|
217
|
+
isDirectory: () => isDir,
|
|
218
|
+
isSymbolicLink: () => false,
|
|
219
|
+
size: Number(st.size) || 0,
|
|
220
|
+
mode: Number(st.mode) || (isDir ? 0o755 : 0o644),
|
|
221
|
+
type: isDir ? 'dir' : 'file',
|
|
222
|
+
mtimeMs, mtime: d, ctimeMs: mtimeMs, ctime: d, atimeMs: mtimeMs, atime: d,
|
|
223
|
+
uid: 1000, gid: 1000, dev: 0, ino: 0, nlink: 1,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Build a BatchWritePayload from the current write buffer.
|
|
229
|
+
* Files + all their parent directories become inodes; file content is
|
|
230
|
+
* chunked at CHUNK_SIZE boundaries to match sqlite-vfs.
|
|
231
|
+
*/
|
|
232
|
+
function buildPayload(writeBuffer, dirBuffer, deleteSet) {
|
|
233
|
+
const inodes = [];
|
|
234
|
+
const chunks = [];
|
|
235
|
+
const dirs = new Set();
|
|
236
|
+
const mtime = Date.now();
|
|
237
|
+
|
|
238
|
+
// Collect all parent directories for files
|
|
239
|
+
for (const [path] of writeBuffer) {
|
|
240
|
+
const parts = path.split('/');
|
|
241
|
+
for (let i = 1; i < parts.length; i++) {
|
|
242
|
+
const d = parts.slice(0, i).join('/');
|
|
243
|
+
if (d) dirs.add(d);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Explicit mkdir entries
|
|
247
|
+
for (const d of dirBuffer) {
|
|
248
|
+
if (!d) continue;
|
|
249
|
+
const parts = d.split('/');
|
|
250
|
+
for (let i = 1; i <= parts.length; i++) {
|
|
251
|
+
const pd = parts.slice(0, i).join('/');
|
|
252
|
+
if (pd) dirs.add(pd);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
for (const dir of dirs) {
|
|
257
|
+
inodes.push({
|
|
258
|
+
path: dir, parentPath: parentOf(dir), isDir: true,
|
|
259
|
+
size: 0, mtime, mode: 0o755, chunkCount: 0,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
for (const [path, data] of writeBuffer) {
|
|
264
|
+
const size = data.length;
|
|
265
|
+
const chunkCount = size === 0 ? 0 : Math.ceil(size / CHUNK_SIZE);
|
|
266
|
+
inodes.push({
|
|
267
|
+
path, parentPath: parentOf(path), isDir: false,
|
|
268
|
+
size, mtime, mode: 0o644, chunkCount,
|
|
269
|
+
});
|
|
270
|
+
if (size === 0) continue;
|
|
271
|
+
if (size <= CHUNK_SIZE) {
|
|
272
|
+
chunks.push({ path, chunkId: 0, data });
|
|
273
|
+
} else {
|
|
274
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
275
|
+
chunks.push({
|
|
276
|
+
path, chunkId: i,
|
|
277
|
+
data: data.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const deletePaths = deleteSet && deleteSet.size > 0 ? [...deleteSet] : undefined;
|
|
284
|
+
return { inodes, chunks, deletePaths };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Create the buffered fs adapter isomorphic-git will use.
|
|
289
|
+
* Writes buffer in-memory; reads check buffer then fall back to supervisor.
|
|
290
|
+
*/
|
|
291
|
+
function createBufferedFs(supervisor, stats) {
|
|
292
|
+
const writeBuffer = new Map(); // path → Uint8Array (insertion ordered = FIFO)
|
|
293
|
+
const dirBuffer = new Set();
|
|
294
|
+
const deleteBuffer = new Set();
|
|
295
|
+
let bufferBytes = 0;
|
|
296
|
+
|
|
297
|
+
// W7 streaming detection. The supervisor's RPC class exposes
|
|
298
|
+
// writeBatchStream() when the deployed code carries the W7 frame
|
|
299
|
+
// protocol. Older supervisors only have writeBatch(). The facet
|
|
300
|
+
// checks once at boot; subsequent flushWave() invocations branch
|
|
301
|
+
// on supportsStreaming.
|
|
302
|
+
//
|
|
303
|
+
// encodeWriteBatchStream is a top-level function in the W7 frame
|
|
304
|
+
// preamble that's been prepended to this worker's main module
|
|
305
|
+
// source (see the modules map at the top of execGitNetwork).
|
|
306
|
+
// It's a module-local identifier — referenced as a bare name
|
|
307
|
+
// exactly like the npm install-batch-facet does at
|
|
308
|
+
// src/npm/install-batch-facet.ts:429.
|
|
309
|
+
//
|
|
310
|
+
// Why streaming matters here: writeBatch sends the full payload
|
|
311
|
+
// through a single structured-clone RPC. For a clone that
|
|
312
|
+
// produces multiple 4 MiB waves, the wrapper isolate hosting
|
|
313
|
+
// ctx.exports SupervisorRPC accumulates resident bytes per wave.
|
|
314
|
+
// After ~5 waves it OOMs at 128 MiB and downstream stat RPCs
|
|
315
|
+
// hang — exactly the git-freeze symptom. writeBatchStream uses
|
|
316
|
+
// a type:'bytes' ReadableStream with a 256 KiB highwater, so the
|
|
317
|
+
// wrapper isolate sees bounded residency regardless of wave size.
|
|
318
|
+
// Same pattern as src/npm/install-batch-facet.ts:421-440.
|
|
319
|
+
async function flushWave() {
|
|
320
|
+
if (writeBuffer.size === 0 && dirBuffer.size === 0 && deleteBuffer.size === 0) return;
|
|
321
|
+
const payload = buildPayload(writeBuffer, dirBuffer, deleteBuffer);
|
|
322
|
+
// Snapshot stats counters BEFORE clearing the buffers so the increments
|
|
323
|
+
// below see the wave's true size, not zero.
|
|
324
|
+
const wavefilesWritten = writeBuffer.size;
|
|
325
|
+
const wavebytesWritten = bufferBytes;
|
|
326
|
+
// Release facet-side buffer references BEFORE awaiting the RPC.
|
|
327
|
+
//
|
|
328
|
+
// After buildPayload, payload.chunks aliases each writeBuffer entry's
|
|
329
|
+
// Uint8Array (the small-file path; large-file path uses fresh slices).
|
|
330
|
+
// payload is the only consumer that needs those bytes for the duration
|
|
331
|
+
// of the await. Holding them in writeBuffer too just doubles facet-side
|
|
332
|
+
// residency during the await.
|
|
333
|
+
//
|
|
334
|
+
// Empirically (Q4 prod verification at probe-prod-post-fix-2026-05-09T14-54-31Z.txt)
|
|
335
|
+
// the facet OOMs around the third long-clone wave on a real repo
|
|
336
|
+
// with the buffers retained. Releasing them here means the writeBuffer
|
|
337
|
+
// Map drops to size 0, the underlying Uint8Array entries are reachable
|
|
338
|
+
// ONLY through payload.chunks, and as the W7 encoder advances past
|
|
339
|
+
// each chunk the JS engine can collect the consumed entries. Net
|
|
340
|
+
// facet-side residency during the await drops from ~2× wave bytes
|
|
341
|
+
// to ~1× wave bytes.
|
|
342
|
+
//
|
|
343
|
+
// Safety: if the await throws, the outer fetch handler at
|
|
344
|
+
// network-facet.ts:644 calls flushWave() again best-effort —
|
|
345
|
+
// writeBuffer is already empty, so that's a no-op. The error
|
|
346
|
+
// propagates as the JSON response body. NO data is lost: bytes were
|
|
347
|
+
// either committed by the supervisor before the throw, or are
|
|
348
|
+
// forever gone (we can't replay a transferred byte stream anyway).
|
|
349
|
+
writeBuffer.clear();
|
|
350
|
+
dirBuffer.clear();
|
|
351
|
+
deleteBuffer.clear();
|
|
352
|
+
bufferBytes = 0;
|
|
353
|
+
// W7 streaming path. encodeWriteBatchStream is a top-level
|
|
354
|
+
// function in the prepended W7_FRAME_PREAMBLE source. The pre-W7
|
|
355
|
+
// structured-clone fallback (writeBatch) was deleted in the
|
|
356
|
+
// legacy-cleanup wave — all live supervisors carry the streaming
|
|
357
|
+
// RPC since 2026-05-09 (commit 89a64ef9).
|
|
358
|
+
// @ts-ignore — preamble symbol injected at module-prepend time.
|
|
359
|
+
const stream = encodeWriteBatchStream(payload);
|
|
360
|
+
await supervisor.writeBatchStream(stream);
|
|
361
|
+
stats.filesWritten += wavefilesWritten;
|
|
362
|
+
stats.bytesWritten += wavebytesWritten;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async function maybeFlush() {
|
|
366
|
+
if (writeBuffer.size >= WAVE_FILES || bufferBytes >= WAVE_BYTES) {
|
|
367
|
+
await flushWave();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const fs = {
|
|
372
|
+
promises: {
|
|
373
|
+
async readFile(filepath, opts) {
|
|
374
|
+
const p = normalizePath(filepath);
|
|
375
|
+
// Check buffer first (FIFO insertion order preserves what git wrote)
|
|
376
|
+
if (writeBuffer.has(p)) {
|
|
377
|
+
const data = writeBuffer.get(p);
|
|
378
|
+
if (opts && opts.encoding === 'utf8') return new TextDecoder().decode(data);
|
|
379
|
+
return data;
|
|
380
|
+
}
|
|
381
|
+
if (deleteBuffer.has(p)) {
|
|
382
|
+
throw enoent(filepath);
|
|
383
|
+
}
|
|
384
|
+
// Fall through to supervisor — use byte-preserving RPC for binary safety
|
|
385
|
+
// (git object files, packfiles must NOT round-trip through TextDecoder)
|
|
386
|
+
const content = await supervisor.readFileBytes(p);
|
|
387
|
+
if (content === null || content === undefined) throw enoent(filepath);
|
|
388
|
+
const data = content instanceof Uint8Array ? content : new Uint8Array(content);
|
|
389
|
+
if (opts && opts.encoding === 'utf8') return new TextDecoder().decode(data);
|
|
390
|
+
return data;
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
async writeFile(filepath, data, opts) {
|
|
394
|
+
const p = normalizePath(filepath);
|
|
395
|
+
// Single-ownership at ingress (fetch-once-consume-once).
|
|
396
|
+
//
|
|
397
|
+
// The W7 streaming path (writeBatchStream) enqueues each chunk's
|
|
398
|
+
// Uint8Array into a type:'bytes' ReadableStream that traverses the
|
|
399
|
+
// RPC boundary; workerd transfers each enqueued buffer's underlying
|
|
400
|
+
// ArrayBuffer to the receiver. If ANY other live reference to that
|
|
401
|
+
// ArrayBuffer (or any view over it) exists when transfer happens,
|
|
402
|
+
// the next operation that constructs a typed-array view over the
|
|
403
|
+
// detached buffer throws
|
|
404
|
+
// "Cannot perform Construct on a detached ArrayBuffer"
|
|
405
|
+
// inside the byte-stream RPC machinery — the prod-only failure
|
|
406
|
+
// mode in audit/probes/git-freeze/detached-buffer-tail-2026-05-09T14-35-39Z.jsonl
|
|
407
|
+
// (pre-fix prod d185e0d1).
|
|
408
|
+
//
|
|
409
|
+
// Aliasing sources observed (Q4-A first-deploy verification at
|
|
410
|
+
// probe-prod-post-fix-2026-05-09T14-54-31Z.txt = OK,
|
|
411
|
+
// Q4-B refined-fix verification at
|
|
412
|
+
// probe-prod-post-fix-2026-05-09T15-04-00Z.txt = REGRESSED back
|
|
413
|
+
// to detached-AB at 2 s):
|
|
414
|
+
// - isomorphic-git's pack indexer passes subarray() views of a
|
|
415
|
+
// packfile-sized parent ArrayBuffer to writeFile. Different
|
|
416
|
+
// paths share the same parent. Caught by a subarray-only
|
|
417
|
+
// copy strategy.
|
|
418
|
+
// - SAME parent ArrayBuffer can also be passed as a WHOLE-view
|
|
419
|
+
// Uint8Array (byteOffset=0, length=buffer.byteLength) by
|
|
420
|
+
// pako's inflate output reuse pattern (pako pools its output
|
|
421
|
+
// buffers). Two consecutive writeFile calls can then share
|
|
422
|
+
// a parent without ANY of them being a "subarray" by the
|
|
423
|
+
// isolated-view test. NOT caught by subarray-only copy —
|
|
424
|
+
// the Q4-B regression confirms this empirically.
|
|
425
|
+
//
|
|
426
|
+
// Fix: UNCONDITIONAL copy on the Uint8Array path. ONE invariant
|
|
427
|
+
// at ONE ingress point: every writeBuffer entry has its own
|
|
428
|
+
// dedicated ArrayBuffer, no shared parent with anything the
|
|
429
|
+
// caller owns. Memory cost is real (one O(N) byte copy per
|
|
430
|
+
// writeFile) but correctness is mandatory.
|
|
431
|
+
//
|
|
432
|
+
// Companion fix: flushWave above releases writeBuffer references
|
|
433
|
+
// BEFORE awaiting the writeBatchStream RPC. Without that, the
|
|
434
|
+
// copies here would double facet-side residency during the await
|
|
435
|
+
// and surface a separate latent wrapper-isolate OOM during long
|
|
436
|
+
// checkouts. Together, the writeFile copy + flushWave clear-
|
|
437
|
+
// before-await keep the facet's heap bounded by ~1× wave bytes
|
|
438
|
+
// (the live payload variable), not 2×.
|
|
439
|
+
//
|
|
440
|
+
// Q4 prod e2e verification:
|
|
441
|
+
// - probe-prod-post-fix-2026-05-09T14-54-31Z.txt: copy WITHOUT
|
|
442
|
+
// clear-before-await → OOM at frame 1450/1601, 46 568 ms wall.
|
|
443
|
+
// Same 1450 stop-point as the original P3-era freeze, but
|
|
444
|
+
// a different cause (now memory, not the OOM-on-stat dead-
|
|
445
|
+
// lock the original P3 fix addressed).
|
|
446
|
+
// - probe-prod-post-fix-2026-05-09T15-09-30Z.txt: copy WITH
|
|
447
|
+
// clear-before-await → CLONE PASS in 11.1 s, 1609 files,
|
|
448
|
+
// final frame 1601/1601 (loaded === total).
|
|
449
|
+
let buf;
|
|
450
|
+
if (typeof data === 'string') {
|
|
451
|
+
buf = new TextEncoder().encode(data); // fresh ArrayBuffer
|
|
452
|
+
} else if (data instanceof Uint8Array) {
|
|
453
|
+
// new Uint8Array(N) + .set(data) allocates a fresh ArrayBuffer
|
|
454
|
+
// of length N and copies bytes from data. The result has zero
|
|
455
|
+
// aliasing relation to data.buffer.
|
|
456
|
+
buf = new Uint8Array(data.length);
|
|
457
|
+
buf.set(data);
|
|
458
|
+
} else {
|
|
459
|
+
// ArrayBuffer (or ArrayBufferView w/o Uint8Array). Construct a
|
|
460
|
+
// Uint8Array over a fresh ArrayBuffer, copy bytes in.
|
|
461
|
+
const src = new Uint8Array(data);
|
|
462
|
+
buf = new Uint8Array(src.length);
|
|
463
|
+
buf.set(src);
|
|
464
|
+
}
|
|
465
|
+
// Remove from deleteBuffer if previously deleted
|
|
466
|
+
deleteBuffer.delete(p);
|
|
467
|
+
// Replace in writeBuffer (size delta tracked)
|
|
468
|
+
if (writeBuffer.has(p)) {
|
|
469
|
+
bufferBytes -= writeBuffer.get(p).length;
|
|
470
|
+
}
|
|
471
|
+
writeBuffer.set(p, buf);
|
|
472
|
+
bufferBytes += buf.length;
|
|
473
|
+
await maybeFlush();
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
async unlink(filepath) {
|
|
477
|
+
const p = normalizePath(filepath);
|
|
478
|
+
if (writeBuffer.has(p)) {
|
|
479
|
+
bufferBytes -= writeBuffer.get(p).length;
|
|
480
|
+
writeBuffer.delete(p);
|
|
481
|
+
}
|
|
482
|
+
deleteBuffer.add(p);
|
|
483
|
+
await maybeFlush();
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
async readdir(filepath) {
|
|
487
|
+
const p = normalizePath(filepath);
|
|
488
|
+
// Start with supervisor's view
|
|
489
|
+
let names = [];
|
|
490
|
+
try {
|
|
491
|
+
const entries = await supervisor.readdir(p);
|
|
492
|
+
names = Array.isArray(entries) ? entries.map(e => e.name) : [];
|
|
493
|
+
} catch { names = []; }
|
|
494
|
+
const set = new Set(names);
|
|
495
|
+
// Add buffered children: anything whose parent == p
|
|
496
|
+
const prefix = p ? p + '/' : '';
|
|
497
|
+
for (const [bp] of writeBuffer) {
|
|
498
|
+
if (!bp.startsWith(prefix)) continue;
|
|
499
|
+
const rest = bp.slice(prefix.length);
|
|
500
|
+
if (!rest) continue;
|
|
501
|
+
const firstSeg = rest.split('/')[0];
|
|
502
|
+
if (firstSeg) set.add(firstSeg);
|
|
503
|
+
}
|
|
504
|
+
for (const bd of dirBuffer) {
|
|
505
|
+
if (!bd.startsWith(prefix)) continue;
|
|
506
|
+
const rest = bd.slice(prefix.length);
|
|
507
|
+
if (!rest) continue;
|
|
508
|
+
const firstSeg = rest.split('/')[0];
|
|
509
|
+
if (firstSeg) set.add(firstSeg);
|
|
510
|
+
}
|
|
511
|
+
// Remove deleted
|
|
512
|
+
for (const dp of deleteBuffer) {
|
|
513
|
+
if (!dp.startsWith(prefix)) continue;
|
|
514
|
+
const rest = dp.slice(prefix.length);
|
|
515
|
+
if (rest.indexOf('/') < 0) set.delete(rest);
|
|
516
|
+
}
|
|
517
|
+
return [...set];
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
async mkdir(filepath) {
|
|
521
|
+
const p = normalizePath(filepath);
|
|
522
|
+
if (!p) return;
|
|
523
|
+
dirBuffer.add(p);
|
|
524
|
+
deleteBuffer.delete(p);
|
|
525
|
+
// Also add all ancestors
|
|
526
|
+
const parts = p.split('/');
|
|
527
|
+
for (let i = 1; i < parts.length; i++) {
|
|
528
|
+
const anc = parts.slice(0, i).join('/');
|
|
529
|
+
if (anc) dirBuffer.add(anc);
|
|
530
|
+
}
|
|
531
|
+
await maybeFlush();
|
|
532
|
+
},
|
|
533
|
+
|
|
534
|
+
async rmdir(filepath) {
|
|
535
|
+
const p = normalizePath(filepath);
|
|
536
|
+
dirBuffer.delete(p);
|
|
537
|
+
deleteBuffer.add(p);
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
async stat(filepath) {
|
|
541
|
+
const p = normalizePath(filepath);
|
|
542
|
+
if (writeBuffer.has(p)) return fileStatObj(writeBuffer.get(p).length);
|
|
543
|
+
if (dirBuffer.has(p)) return dirStatObj();
|
|
544
|
+
if (deleteBuffer.has(p)) throw enoent(filepath);
|
|
545
|
+
if (!p) return dirStatObj();
|
|
546
|
+
const st = await supervisor.stat(p);
|
|
547
|
+
if (!st) throw enoent(filepath);
|
|
548
|
+
return convertSupervisorStat(st);
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
async lstat(filepath) { return this.stat(filepath); },
|
|
552
|
+
|
|
553
|
+
async chmod() { /* no-op */ },
|
|
554
|
+
async symlink() { /* no-op */ },
|
|
555
|
+
async readlink(p) { return p; },
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
return { fs, flushWave };
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export default {
|
|
563
|
+
async fetch(request, workerEnv) {
|
|
564
|
+
const supervisor = workerEnv && workerEnv.SUPERVISOR;
|
|
565
|
+
if (!supervisor) {
|
|
566
|
+
return Response.json({
|
|
567
|
+
success: false, error: 'SUPERVISOR binding missing in facet env',
|
|
568
|
+
filesWritten: 0, bytesWritten: 0,
|
|
569
|
+
}, { status: 500 });
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
let opts;
|
|
573
|
+
try {
|
|
574
|
+
opts = await request.json();
|
|
575
|
+
} catch (e) {
|
|
576
|
+
return Response.json({
|
|
577
|
+
success: false, error: 'Invalid request body: ' + (e && e.message),
|
|
578
|
+
filesWritten: 0, bytesWritten: 0,
|
|
579
|
+
}, { status: 400 });
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const log = (msg) => {
|
|
583
|
+
try { supervisor.stdout(msg).catch(() => {}); } catch {}
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const stats = { filesWritten: 0, bytesWritten: 0 };
|
|
587
|
+
const { fs, flushWave } = createBufferedFs(supervisor, stats);
|
|
588
|
+
|
|
589
|
+
// Import the pre-bundled isomorphic-git + http/web.
|
|
590
|
+
// The bundle is provided via LOADER.load()'s modules record;
|
|
591
|
+
// see scripts/bundle-git.mjs and src/git-bundle.generated.ts.
|
|
592
|
+
let git, http;
|
|
593
|
+
try {
|
|
594
|
+
const bundle = await import('./git-bundle.js');
|
|
595
|
+
git = bundle.git;
|
|
596
|
+
// http/web has both { request } named and { default: { request } };
|
|
597
|
+
// the namespace bundle.gitHttp exposes request directly, which is
|
|
598
|
+
// what isomorphic-git looks for.
|
|
599
|
+
http = bundle.gitHttp;
|
|
600
|
+
} catch (e) {
|
|
601
|
+
return Response.json({
|
|
602
|
+
success: false, error: 'Failed to load bundled isomorphic-git: ' + (e && e.message),
|
|
603
|
+
filesWritten: 0, bytesWritten: 0,
|
|
604
|
+
}, { status: 500 });
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Throttle onProgress emissions to ≥100ms apart (audit R1).
|
|
608
|
+
// isomorphic-git fires this callback per packfile object — thousands
|
|
609
|
+
// of times for a medium repo. Each call does supervisor.stdout(...),
|
|
610
|
+
// a facet→supervisor RPC that consumes input-gate time on the
|
|
611
|
+
// supervisor DO and serialises behind other in-flight async work
|
|
612
|
+
// (including shell keystrokes). Also emit unconditionally on phase
|
|
613
|
+
// completion (loaded === total) so users still see the final frame
|
|
614
|
+
// and any phase transition.
|
|
615
|
+
let lastLogAt = 0;
|
|
616
|
+
let lastLoggedPhase = '';
|
|
617
|
+
const onProgress = async (e) => {
|
|
618
|
+
if (!e || !e.phase) return;
|
|
619
|
+
const now = Date.now();
|
|
620
|
+
const phaseChanged = e.phase !== lastLoggedPhase;
|
|
621
|
+
const phaseDone = e.total && e.loaded === e.total;
|
|
622
|
+
const dueByTime = now - lastLogAt >= 100;
|
|
623
|
+
if (!phaseChanged && !phaseDone && !dueByTime) return;
|
|
624
|
+
lastLogAt = now;
|
|
625
|
+
lastLoggedPhase = e.phase;
|
|
626
|
+
log('\\r[git] ' + e.phase + ' ' + (e.loaded || 0) + '/' + (e.total || '?'));
|
|
627
|
+
};
|
|
628
|
+
const onAuth = () => opts.auth || { username: '', password: '' };
|
|
629
|
+
|
|
630
|
+
try {
|
|
631
|
+
if (opts.op === 'clone') {
|
|
632
|
+
if (!opts.url) throw new Error('clone: url required');
|
|
633
|
+
await git.clone({
|
|
634
|
+
fs, http,
|
|
635
|
+
dir: opts.dir,
|
|
636
|
+
url: opts.url,
|
|
637
|
+
singleBranch: true,
|
|
638
|
+
depth: opts.depth || 1,
|
|
639
|
+
nonBlocking: true,
|
|
640
|
+
batchSize: 50,
|
|
641
|
+
onProgress,
|
|
642
|
+
onAuth,
|
|
643
|
+
});
|
|
644
|
+
} else if (opts.op === 'fetch') {
|
|
645
|
+
await git.fetch({
|
|
646
|
+
fs, http,
|
|
647
|
+
dir: opts.dir,
|
|
648
|
+
remote: opts.remote || 'origin',
|
|
649
|
+
depth: opts.depth,
|
|
650
|
+
singleBranch: true,
|
|
651
|
+
onProgress,
|
|
652
|
+
onAuth,
|
|
653
|
+
});
|
|
654
|
+
} else if (opts.op === 'pull') {
|
|
655
|
+
await git.pull({
|
|
656
|
+
fs, http,
|
|
657
|
+
dir: opts.dir,
|
|
658
|
+
remote: opts.remote || 'origin',
|
|
659
|
+
ref: opts.ref,
|
|
660
|
+
singleBranch: true,
|
|
661
|
+
author: opts.author || { name: 'user', email: 'user@nimbus.dev' },
|
|
662
|
+
onProgress,
|
|
663
|
+
onAuth,
|
|
664
|
+
});
|
|
665
|
+
} else if (opts.op === 'push') {
|
|
666
|
+
await git.push({
|
|
667
|
+
fs, http,
|
|
668
|
+
dir: opts.dir,
|
|
669
|
+
remote: opts.remote || 'origin',
|
|
670
|
+
ref: opts.ref,
|
|
671
|
+
onProgress,
|
|
672
|
+
onAuth,
|
|
673
|
+
});
|
|
674
|
+
} else {
|
|
675
|
+
throw new Error('Unknown op: ' + opts.op);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Final flush — commit any remaining buffered writes
|
|
679
|
+
await flushWave();
|
|
680
|
+
log('\\n');
|
|
681
|
+
|
|
682
|
+
return Response.json({
|
|
683
|
+
success: true,
|
|
684
|
+
filesWritten: stats.filesWritten,
|
|
685
|
+
bytesWritten: stats.bytesWritten,
|
|
686
|
+
});
|
|
687
|
+
} catch (e) {
|
|
688
|
+
// Best-effort flush of partial state so user can inspect what landed
|
|
689
|
+
try { await flushWave(); } catch {}
|
|
690
|
+
return Response.json({
|
|
691
|
+
success: false,
|
|
692
|
+
error: (e && e.message) || String(e),
|
|
693
|
+
filesWritten: stats.filesWritten,
|
|
694
|
+
bytesWritten: stats.bytesWritten,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
};
|
|
699
|
+
`;
|
|
700
|
+
}
|