@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,870 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* python-runner.ts — Pyodide v0.29.4 runner (True-OS Wave-3 v2 / Pyodide v1).
|
|
3
|
+
*
|
|
4
|
+
* v1 scope per /workspace/.seal-internal/2026-05-10-pyodide-research/plan.md
|
|
5
|
+
* D1-D7:
|
|
6
|
+
* - `python --version` / `python -c '<code>'` / `python script.py`
|
|
7
|
+
* - stdlib subset (full python_stdlib.zip ships)
|
|
8
|
+
* - stdout/stderr → processLogs (Process tab integration)
|
|
9
|
+
* - exit code via sys.exit(N) or unhandled exception → 1
|
|
10
|
+
* - argv passed through to sys.argv
|
|
11
|
+
*
|
|
12
|
+
* Out of v1 (queued for v2/v3):
|
|
13
|
+
* - REPL mode (`python` with no args)
|
|
14
|
+
* - File I/O beyond reading the entry script
|
|
15
|
+
* - `pip install` / `loadPackage` / native-extension packages
|
|
16
|
+
* - Sync HTTP (urllib3 / requests blocked without JSPI)
|
|
17
|
+
*
|
|
18
|
+
* Architecture: SAME LOADER-modules transport as clang-runner/wasm-
|
|
19
|
+
* runner. The Pyodide wasm bytes ship via the LOADER `modules` map
|
|
20
|
+
* (CSP allows wasm code-gen at module-load time, not at request
|
|
21
|
+
* time). The Pyodide.asm.js + stdlib zip ride via the loader-pool
|
|
22
|
+
* `context` field (JSON-stringified into the inner worker.js at
|
|
23
|
+
* module-load).
|
|
24
|
+
*
|
|
25
|
+
* Per wasm-csp/findings.md §4b: Pyodide.asm.wasm (10.1 MB on disk)
|
|
26
|
+
* compiles in 314 ms via LOADER on PROD. With our v1 deployment of
|
|
27
|
+
* 0.29.4 (8.25 MB asm.wasm), this is well under the empirical
|
|
28
|
+
* ~32 MiB per-call ceiling.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Build the python-runner factory. Called once at session init; the
|
|
32
|
+
* returned factory binds the manifest + install root for each
|
|
33
|
+
* registered entrypoint (`python`, `python3`).
|
|
34
|
+
*/
|
|
35
|
+
export function makePythonRunnerFactory(deps) {
|
|
36
|
+
const { facetMgr, vfs } = deps;
|
|
37
|
+
return function pythonRunnerFactory(manifest, installRoot, binName, _binKind) {
|
|
38
|
+
const findFile = (rel) => {
|
|
39
|
+
const entry = manifest.files.find((f) => f.path === rel);
|
|
40
|
+
return entry ? `${installRoot}/${entry.path}` : null;
|
|
41
|
+
};
|
|
42
|
+
const asmWasmVfs = findFile('share/pyodide/pyodide.asm.wasm');
|
|
43
|
+
const asmJsVfs = findFile('share/pyodide/pyodide.asm.js');
|
|
44
|
+
const stdlibVfs = findFile('share/pyodide/python_stdlib.zip');
|
|
45
|
+
return async function pythonBinHandler(ctx) {
|
|
46
|
+
const argv = ctx.args || [];
|
|
47
|
+
const cwd = ctx.cwd || '/home/user';
|
|
48
|
+
// ── --version / --help fast paths (no wasm boot) ─────────────
|
|
49
|
+
if (argv.includes('--version') || argv.includes('-V')) {
|
|
50
|
+
ctx.stdout.write(`Python 3.13.2 (Pyodide 0.29.4, Nimbus runtime)\n`);
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
54
|
+
ctx.stdout.write(`usage: ${binName} [option] ... [-c cmd | -m mod | file | -] [arg] ...\n`);
|
|
55
|
+
ctx.stdout.write(`Nimbus Pyodide 0.29.4 / Python 3.13 runtime.\n`);
|
|
56
|
+
ctx.stdout.write(`Supported v1: -c <code>, <file.py>, stdin via -\n`);
|
|
57
|
+
ctx.stdout.write(`Not supported: REPL (no args), pip, native extensions\n`);
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
// ── Resolve install bytes ────────────────────────────────────
|
|
61
|
+
if (!asmWasmVfs || !vfs.exists(asmWasmVfs)) {
|
|
62
|
+
ctx.stderr.write(`${binName}: pyodide.asm.wasm missing (re-run 'nimbus install python')\n`);
|
|
63
|
+
return 127;
|
|
64
|
+
}
|
|
65
|
+
if (!asmJsVfs || !vfs.exists(asmJsVfs)) {
|
|
66
|
+
ctx.stderr.write(`${binName}: pyodide.asm.js missing\n`);
|
|
67
|
+
return 127;
|
|
68
|
+
}
|
|
69
|
+
if (!stdlibVfs || !vfs.exists(stdlibVfs)) {
|
|
70
|
+
ctx.stderr.write(`${binName}: python_stdlib.zip missing\n`);
|
|
71
|
+
return 127;
|
|
72
|
+
}
|
|
73
|
+
const asmWasmBytes = vfs.readFile(asmWasmVfs);
|
|
74
|
+
const asmJsBytes = vfs.readFile(asmJsVfs);
|
|
75
|
+
const stdlibBytes = vfs.readFile(stdlibVfs);
|
|
76
|
+
// ── Parse argv ───────────────────────────────────────────────
|
|
77
|
+
// Supported in v1:
|
|
78
|
+
// python -c '<code>' run inline code, args[i+1..] in sys.argv
|
|
79
|
+
// python <file.py> [args...] run script, args in sys.argv
|
|
80
|
+
// python (no args) → not supported; REPL is v2
|
|
81
|
+
// python - read code from stdin (advanced)
|
|
82
|
+
const parsed = parsePythonArgv(argv);
|
|
83
|
+
if (parsed.error) {
|
|
84
|
+
ctx.stderr.write(`${binName}: ${parsed.error}\n`);
|
|
85
|
+
return parsed.exitCode;
|
|
86
|
+
}
|
|
87
|
+
// For -c mode: code comes from argv. For script-file mode:
|
|
88
|
+
// read from VFS. For stdin mode: we collect stdin upfront.
|
|
89
|
+
let userCode = '';
|
|
90
|
+
let progName = binName;
|
|
91
|
+
let pyArgv = [binName];
|
|
92
|
+
if (parsed.mode === 'inline') {
|
|
93
|
+
userCode = parsed.inlineCode;
|
|
94
|
+
pyArgv = ['-c', ...parsed.scriptArgs];
|
|
95
|
+
}
|
|
96
|
+
else if (parsed.mode === 'script') {
|
|
97
|
+
// Read script from VFS, resolving relative to cwd.
|
|
98
|
+
const absPath = resolveVfsPath(parsed.scriptPath, cwd);
|
|
99
|
+
if (!vfs.exists(absPath)) {
|
|
100
|
+
ctx.stderr.write(`${binName}: ${parsed.scriptPath}: No such file or directory\n`);
|
|
101
|
+
return 2;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
userCode = new TextDecoder('utf-8').decode(vfs.readFile(absPath));
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
ctx.stderr.write(`${binName}: ${parsed.scriptPath}: ${e?.message || e}\n`);
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
progName = parsed.scriptPath;
|
|
111
|
+
pyArgv = [parsed.scriptPath, ...parsed.scriptArgs];
|
|
112
|
+
}
|
|
113
|
+
else if (parsed.mode === 'stdin') {
|
|
114
|
+
// Read all of stdin from ctx (lifo-sh wires it when the pipe
|
|
115
|
+
// is filled). Pyodide receives it as the program source.
|
|
116
|
+
const stdinReader = ctx.stdin;
|
|
117
|
+
if (stdinReader && typeof stdinReader.read === 'function') {
|
|
118
|
+
// The shell's ctx.stdin is a stream-like with .read() that
|
|
119
|
+
// returns the accumulated buffer up to EOF. Lifo-sh's pipe
|
|
120
|
+
// implementation feeds the upstream's stdout into this.
|
|
121
|
+
userCode = await stdinReader.read();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// No piped stdin → empty program.
|
|
125
|
+
userCode = '';
|
|
126
|
+
}
|
|
127
|
+
pyArgv = ['-', ...parsed.scriptArgs];
|
|
128
|
+
}
|
|
129
|
+
// env passed to Python's os.environ. We forward NIMBUS_*,
|
|
130
|
+
// PATH-ish, and a default HOME if not set.
|
|
131
|
+
const userEnv = { ...(ctx.env || {}) };
|
|
132
|
+
if (!userEnv.HOME)
|
|
133
|
+
userEnv.HOME = '/home/pyodide';
|
|
134
|
+
if (!userEnv.PYTHONUNBUFFERED)
|
|
135
|
+
userEnv.PYTHONUNBUFFERED = '1';
|
|
136
|
+
// ── Dispatch the facet ───────────────────────────────────────
|
|
137
|
+
const result = await dispatchPythonFacet(facetMgr, {
|
|
138
|
+
asmWasmBytes,
|
|
139
|
+
asmJsBytes,
|
|
140
|
+
stdlibBytes,
|
|
141
|
+
userCode,
|
|
142
|
+
pyArgv,
|
|
143
|
+
userEnv,
|
|
144
|
+
progName,
|
|
145
|
+
});
|
|
146
|
+
if (result.stdout)
|
|
147
|
+
ctx.stdout.write(result.stdout);
|
|
148
|
+
if (result.stderr)
|
|
149
|
+
ctx.stderr.write(result.stderr);
|
|
150
|
+
if (result.error) {
|
|
151
|
+
ctx.stderr.write(`${binName}: ${result.error}\n`);
|
|
152
|
+
return 1;
|
|
153
|
+
}
|
|
154
|
+
return result.exitCode;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function parsePythonArgv(argv) {
|
|
159
|
+
// Walk argv left-to-right looking for the first non-flag token OR
|
|
160
|
+
// the -c / -m mode-switches. Python's CLI is more involved (-O,
|
|
161
|
+
// -B, -E, -W, -I, etc.) but for v1 we accept and ignore unknown
|
|
162
|
+
// single-letter flags that don't take args, and error loudly on
|
|
163
|
+
// unsupported ones.
|
|
164
|
+
let i = 0;
|
|
165
|
+
while (i < argv.length) {
|
|
166
|
+
const a = argv[i];
|
|
167
|
+
if (a === '-c') {
|
|
168
|
+
const code = argv[i + 1];
|
|
169
|
+
if (code === undefined) {
|
|
170
|
+
return { mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2,
|
|
171
|
+
error: "Argument expected for the -c option" };
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
mode: 'inline',
|
|
175
|
+
inlineCode: code,
|
|
176
|
+
scriptPath: '',
|
|
177
|
+
scriptArgs: argv.slice(i + 2),
|
|
178
|
+
exitCode: 0,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (a === '-m') {
|
|
182
|
+
const mod = argv[i + 1];
|
|
183
|
+
if (mod === undefined) {
|
|
184
|
+
return { mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2,
|
|
185
|
+
error: "Argument expected for the -m option" };
|
|
186
|
+
}
|
|
187
|
+
// -m <mod> [args...] → runpy.run_module(mod)
|
|
188
|
+
const inlineCode = `import runpy, sys\nsys.argv = ${JSON.stringify([mod, ...argv.slice(i + 2)])}\nrunpy.run_module(${JSON.stringify(mod)}, run_name='__main__', alter_sys=True)\n`;
|
|
189
|
+
return {
|
|
190
|
+
mode: 'inline',
|
|
191
|
+
inlineCode,
|
|
192
|
+
scriptPath: '',
|
|
193
|
+
scriptArgs: argv.slice(i + 2),
|
|
194
|
+
exitCode: 0,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (a === '-') {
|
|
198
|
+
return { mode: 'stdin', inlineCode: '', scriptPath: '-', scriptArgs: argv.slice(i + 1), exitCode: 0 };
|
|
199
|
+
}
|
|
200
|
+
if (!a.startsWith('-')) {
|
|
201
|
+
return {
|
|
202
|
+
mode: 'script',
|
|
203
|
+
inlineCode: '',
|
|
204
|
+
scriptPath: a,
|
|
205
|
+
scriptArgs: argv.slice(i + 1),
|
|
206
|
+
exitCode: 0,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// Unknown flag — for v1, silently skip single-char flags that
|
|
210
|
+
// are commonly harmless (-O, -B, -u, -E, -I). Error on others.
|
|
211
|
+
if (/^-[OBuEItcsx]+$/.test(a)) {
|
|
212
|
+
i++;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
return { mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2,
|
|
216
|
+
error: `unknown option: ${a}` };
|
|
217
|
+
}
|
|
218
|
+
// No mode argument provided — REPL is not supported in v1.
|
|
219
|
+
return { mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2,
|
|
220
|
+
error: "REPL not supported in v1. Use 'python -c \"code\"' or 'python script.py'." };
|
|
221
|
+
}
|
|
222
|
+
function resolveVfsPath(rel, cwd) {
|
|
223
|
+
const cwdN = cwd.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
224
|
+
if (rel.startsWith('/'))
|
|
225
|
+
return rel.replace(/^\/+/, '');
|
|
226
|
+
if (rel === '.')
|
|
227
|
+
return cwdN;
|
|
228
|
+
return `${cwdN}/${rel}`;
|
|
229
|
+
}
|
|
230
|
+
function uint8ToBase64(u8) {
|
|
231
|
+
const CHUNK = 0x8000;
|
|
232
|
+
let s = '';
|
|
233
|
+
for (let i = 0; i < u8.length; i += CHUNK) {
|
|
234
|
+
s += String.fromCharCode.apply(null, Array.from(u8.subarray(i, Math.min(i + CHUNK, u8.length))));
|
|
235
|
+
}
|
|
236
|
+
return btoa(s);
|
|
237
|
+
}
|
|
238
|
+
async function dispatchPythonFacet(facetMgr, args) {
|
|
239
|
+
const toAB = (u8) => u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
|
|
240
|
+
// pyodide.asm.js is INLINED into the preamble (1 MiB text source).
|
|
241
|
+
// Workerd's CSP allows wasm code-gen AND JS top-level evaluation at
|
|
242
|
+
// module-load time, but BLOCKS `new Function(src)` at request time.
|
|
243
|
+
// The asm.js source declares `var _createPyodideModule = ...` at
|
|
244
|
+
// module-top; spliced into the preamble it becomes a module-scope
|
|
245
|
+
// binding we expose via `globalThis._createPyodideModule = ...`
|
|
246
|
+
// right after.
|
|
247
|
+
//
|
|
248
|
+
// stdlib.zip stays in the context (base64) — it's data not code,
|
|
249
|
+
// and the context blob is evaluated at module-load too.
|
|
250
|
+
let asmJsSrc = new TextDecoder('utf-8').decode(args.asmJsBytes);
|
|
251
|
+
// ── Source-patch the asm.js's "Cannot determine runtime environment" branch ──
|
|
252
|
+
//
|
|
253
|
+
// Even with our globalThis.process / WorkerGlobalScope / self / location
|
|
254
|
+
// shims in place, the asm.js's pyodide_js_init() IIFE's env-detect (via
|
|
255
|
+
// bn → vn) somehow lands d.IN_BROWSER_WEB_WORKER = false at request time
|
|
256
|
+
// on workerd. Our supervisor-side mirror of the same logic returns
|
|
257
|
+
// IN_BROWSER_WEB_WORKER = true with the same shims; the discrepancy
|
|
258
|
+
// remains unidentified after P8-P17 of diagnostic instrumentation.
|
|
259
|
+
//
|
|
260
|
+
// Pragmatic v1 fix: source-patch the if-chain to ALWAYS take the
|
|
261
|
+
// IN_BROWSER_WEB_WORKER branch (regardless of `d`). The branch's body
|
|
262
|
+
// assigns `Fe` to a globalThis.importScripts-based loadScript helper.
|
|
263
|
+
// We don't actually call Fe (Pyodide only invokes loadScript when
|
|
264
|
+
// dynamic-loading a wasm sibling; our instantiateWasm override pre-
|
|
265
|
+
// empts that). So the patch is functionally a no-op other than making
|
|
266
|
+
// the throw unreachable.
|
|
267
|
+
//
|
|
268
|
+
// The patch target string is a literal in the minified asm.js — stable
|
|
269
|
+
// across rebuilds because it's a unique identifier path.
|
|
270
|
+
const PATCH_NEEDLE = 'else throw new Error("Cannot determine runtime environment")';
|
|
271
|
+
const PATCH_REPLACE = '/* nimbus-patch: was: ' + PATCH_NEEDLE + ' */';
|
|
272
|
+
if (asmJsSrc.includes(PATCH_NEEDLE)) {
|
|
273
|
+
asmJsSrc = asmJsSrc.replace(PATCH_NEEDLE, PATCH_REPLACE);
|
|
274
|
+
}
|
|
275
|
+
// Also patch the if-chain head so we ALWAYS set Fe (force-take the
|
|
276
|
+
// first branch — IN_BROWSER_MAIN_THREAD's loadScript path uses
|
|
277
|
+
// `await import(t)` which works in workerd at module-load time).
|
|
278
|
+
// The previous patch alone is insufficient because if NO branch matches,
|
|
279
|
+
// Fe stays undefined → calling it would TypeError later.
|
|
280
|
+
const HEAD_NEEDLE = 'if(d.IN_BROWSER_MAIN_THREAD)';
|
|
281
|
+
const HEAD_REPLACE = 'if(true||d.IN_BROWSER_MAIN_THREAD)';
|
|
282
|
+
if (asmJsSrc.includes(HEAD_NEEDLE)) {
|
|
283
|
+
asmJsSrc = asmJsSrc.replace(HEAD_NEEDLE, HEAD_REPLACE);
|
|
284
|
+
}
|
|
285
|
+
const stdlibB64 = uint8ToBase64(args.stdlibBytes);
|
|
286
|
+
// Build the preamble with stdlib bytes spliced in. The preamble runs
|
|
287
|
+
// the entire Pyodide bootstrap at child-facet module-init time —
|
|
288
|
+
// where workerd's CSP permits `new WebAssembly.Module(rawBytes)` (the
|
|
289
|
+
// convertJsFunctionToWasm code path which is the v1 hang root).
|
|
290
|
+
// Per-call __pyodideRun just executes runPython on the cached
|
|
291
|
+
// Pyodide instance.
|
|
292
|
+
const preamble = buildPyodidePreamble(asmJsSrc, stdlibB64);
|
|
293
|
+
const { NimbusLoaderPool } = await import('../loaders/loader-pool.js');
|
|
294
|
+
const env = facetMgr.env;
|
|
295
|
+
const ctx = facetMgr.ctx;
|
|
296
|
+
const pool = new NimbusLoaderPool(env, ctx, {
|
|
297
|
+
tag: 'python-runner',
|
|
298
|
+
concurrency: 1,
|
|
299
|
+
omitSupervisor: true,
|
|
300
|
+
preamble,
|
|
301
|
+
});
|
|
302
|
+
// v2 simplification: stdlibB64 and asmWasmMod are now embedded in the
|
|
303
|
+
// preamble (stdlib via base64 splice; wasm via __NIMBUS_WASM table set
|
|
304
|
+
// by the loader-pool). The facet fn only needs the per-call inputs.
|
|
305
|
+
const facetFn = async function pythonFacetCall(inArgs) {
|
|
306
|
+
const fn = globalThis.__pyodideRun;
|
|
307
|
+
if (typeof fn !== 'function') {
|
|
308
|
+
return { exitCode: 127, stdout: '', stderr: '',
|
|
309
|
+
error: 'python-runner preamble missing: __pyodideRun not in scope' };
|
|
310
|
+
}
|
|
311
|
+
return await fn({
|
|
312
|
+
userCode: inArgs.userCode,
|
|
313
|
+
pyArgv: inArgs.pyArgv,
|
|
314
|
+
userEnv: inArgs.userEnv,
|
|
315
|
+
progName: inArgs.progName,
|
|
316
|
+
});
|
|
317
|
+
};
|
|
318
|
+
try {
|
|
319
|
+
const result = await pool.submit(facetFn, {
|
|
320
|
+
userCode: args.userCode,
|
|
321
|
+
pyArgv: args.pyArgv,
|
|
322
|
+
userEnv: args.userEnv,
|
|
323
|
+
progName: args.progName,
|
|
324
|
+
}, {
|
|
325
|
+
wasmModules: {
|
|
326
|
+
'pyodide.asm.wasm': toAB(args.asmWasmBytes),
|
|
327
|
+
},
|
|
328
|
+
timeoutMs: 300_000,
|
|
329
|
+
});
|
|
330
|
+
return {
|
|
331
|
+
exitCode: result.exitCode,
|
|
332
|
+
stdout: result.stdout || '',
|
|
333
|
+
stderr: result.stderr || '',
|
|
334
|
+
error: result.error,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
return {
|
|
339
|
+
exitCode: 1,
|
|
340
|
+
stdout: '',
|
|
341
|
+
stderr: '',
|
|
342
|
+
error: `python-runner dispatch failed: ${e?.message || e}`,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Compose the per-call preamble by splicing the pyodide.asm.js source
|
|
348
|
+
* verbatim ahead of the __pyodideRun helper. Workerd compiles this
|
|
349
|
+
* blob as JS at module-load time (where `var` declarations + globals
|
|
350
|
+
* assignment are allowed), then the asm.js's `var _createPyodideModule`
|
|
351
|
+
* is hoisted onto globalThis.
|
|
352
|
+
*/
|
|
353
|
+
// REPL-W1: exported so the adjacent src/runtime/python-repl.ts can
|
|
354
|
+
// reuse the canonical preamble verbatim. Additive change only —
|
|
355
|
+
// internal callers are unchanged. The `export` keyword is the sole
|
|
356
|
+
// modification to python-runner.ts in the REPL-W1 wave.
|
|
357
|
+
export function buildPyodidePreamble(asmJsSrc, stdlibB64) {
|
|
358
|
+
return [
|
|
359
|
+
'// ── Pre-asm.js environment shims ───────────────────────────────',
|
|
360
|
+
'// Pyodide.asm.js detects its environment via heuristics. In',
|
|
361
|
+
'// workerd, several detections fire wrong:',
|
|
362
|
+
'// - ENVIRONMENT_IS_NODE: workerd defines process + process.versions.node',
|
|
363
|
+
'// under nodejs_compat → Pyodide tries require("fs"), require("path"),',
|
|
364
|
+
'// require("crypto"), require("ws"), require("child_process"). We',
|
|
365
|
+
'// don\'t want any of those code paths because instantiateWasm is',
|
|
366
|
+
'// overridden anyway.',
|
|
367
|
+
'// - ENVIRONMENT_IS_WORKER (typeof WorkerGlobalScope !== "undefined"):',
|
|
368
|
+
'// true in workerd. Pyodide reads `self.location.href`. workerd has',
|
|
369
|
+
'// `self` (== globalThis) but `self.location` is undefined. Stub it.',
|
|
370
|
+
'// - document.currentScript?.src: workerd has no document; the',
|
|
371
|
+
'// optional-chain on undefined is fine, but we shim it explicitly',
|
|
372
|
+
'// to remove any drift across pyodide versions.',
|
|
373
|
+
'//',
|
|
374
|
+
'// CRITICAL: ENVIRONMENT_IS_NODE is computed inside the async-factory',
|
|
375
|
+
'// returned by the asm.js IIFE — i.e., it\'s evaluated WHEN',
|
|
376
|
+
'// _createPyodideModule(settings) is CALLED, not when the asm.js',
|
|
377
|
+
'// module-init runs. We therefore need to keep globalThis.process =',
|
|
378
|
+
'// undefined across that call, not just the asm.js inline above. The',
|
|
379
|
+
'// __pyodideRun helper below does the save+restore around the call.',
|
|
380
|
+
'// IMPORTANT: env-detection in asm.js happens at the IIFE OUTER scope',
|
|
381
|
+
'// (runs at module-load time, during the asm.js inline below), via',
|
|
382
|
+
'// var f = oe();',
|
|
383
|
+
'// `oe()` reads globalThis.process / typeof WorkerGlobalScope / typeof',
|
|
384
|
+
'// self instanceof WorkerGlobalScope and stores derived flags in `f`',
|
|
385
|
+
'// (captured into closure scope as `d`). Later inside the async factory',
|
|
386
|
+
'// (at request time), loadScript selects on `d.IN_BROWSER_WEB_WORKER`.',
|
|
387
|
+
'//',
|
|
388
|
+
'// So the shims MUST be in place BEFORE the asm.js inline runs.',
|
|
389
|
+
'// __pyodideRun\'s save+restore around _createPyodideModule covers the',
|
|
390
|
+
'// inner async factory call BUT NOT this outer IIFE evaluation. We',
|
|
391
|
+
'// therefore stub at module-load too, save the originals, and restore',
|
|
392
|
+
'// AT THE END OF THE PREAMBLE (after the asm.js inline returns).',
|
|
393
|
+
'const __nimbusOrigProcess = globalThis.process;',
|
|
394
|
+
'const __nimbusOrigWGS = globalThis.WorkerGlobalScope;',
|
|
395
|
+
'try { globalThis.process = undefined; } catch (e) { /* non-writable; fall through */ }',
|
|
396
|
+
'// Defense in depth: if globalThis.process survived the = undefined',
|
|
397
|
+
'// (workerd treats it as non-configurable in some setups), mark it',
|
|
398
|
+
'// browser-like so Pyodide\'s `!process.browser` check fails → IN_NODE = false.',
|
|
399
|
+
'try {',
|
|
400
|
+
' if (globalThis.process && typeof globalThis.process === "object") globalThis.process.browser = true;',
|
|
401
|
+
'} catch (e) { /* fail-soft */ }',
|
|
402
|
+
'globalThis.WorkerGlobalScope = Object;',
|
|
403
|
+
'if (typeof globalThis.location !== "object" || globalThis.location === null) {',
|
|
404
|
+
' globalThis.location = { href: "pyodide://nimbus/", origin: "pyodide://nimbus", toString() { return this.href; } };',
|
|
405
|
+
'}',
|
|
406
|
+
'if (typeof globalThis.document === "undefined") globalThis.document = undefined;',
|
|
407
|
+
'if (typeof globalThis.self === "undefined") globalThis.self = globalThis;',
|
|
408
|
+
'',
|
|
409
|
+
'// pyodide_js_init() inside the asm.js IIFE constructs a FinalizationRegistry',
|
|
410
|
+
'// to bridge JS-side GC to Python ref-cleanup. workerd does not expose',
|
|
411
|
+
'// FinalizationRegistry by default (it is gated behind the `enable_weak_ref`',
|
|
412
|
+
'// compat flag, on by default after 2025-05-05). For older compat dates',
|
|
413
|
+
'// the constructor is undefined and pyodide_js_init throws ReferenceError.',
|
|
414
|
+
'// Provide a no-op class shim — registered callbacks are simply never',
|
|
415
|
+
'// invoked. This is memory-leaky for long-lived workers (Python objects',
|
|
416
|
+
'// holding JS proxies will be retained beyond their natural lifetime),',
|
|
417
|
+
'// but acceptable for v1 (per-request bootstrap; workerd reaps on isolate',
|
|
418
|
+
'// reuse anyway). Future v3: switch to compat_date >= 2025-05-05 and drop.',
|
|
419
|
+
'if (typeof globalThis.FinalizationRegistry === "undefined") {',
|
|
420
|
+
' globalThis.FinalizationRegistry = class FinalizationRegistry {',
|
|
421
|
+
' constructor(_cleanup) {}',
|
|
422
|
+
' register(_target, _heldValue, _token) {}',
|
|
423
|
+
' unregister(_token) {}',
|
|
424
|
+
' };',
|
|
425
|
+
'}',
|
|
426
|
+
'',
|
|
427
|
+
'// ── BEGIN: pyodide.asm.js (inlined; ~1 MiB) ─────────────────────',
|
|
428
|
+
'// Module-load time evaluation. Declares `var _createPyodideModule`',
|
|
429
|
+
'// at module scope; the next line hoists it onto globalThis so the',
|
|
430
|
+
'// __pyodideRun helper below can reach it across slot reuses.',
|
|
431
|
+
asmJsSrc,
|
|
432
|
+
'if (typeof _createPyodideModule === \'function\') {',
|
|
433
|
+
' globalThis._createPyodideModule = _createPyodideModule;',
|
|
434
|
+
'}',
|
|
435
|
+
'// Restore originals after the asm.js IIFE outer captures env flags.',
|
|
436
|
+
'// (The bootstrap promise below re-hides them inside its own async',
|
|
437
|
+
'// scope so the env-detection inside the async factory body sees the',
|
|
438
|
+
'// stubbed values.)',
|
|
439
|
+
'try { globalThis.process = __nimbusOrigProcess; } catch (e) { /* fall through */ }',
|
|
440
|
+
'try { if (globalThis.process && globalThis.process.browser === true) delete globalThis.process.browser; } catch (e) {}',
|
|
441
|
+
'globalThis.WorkerGlobalScope = __nimbusOrigWGS;',
|
|
442
|
+
'// ── END: pyodide.asm.js inline ──────────────────────────────────',
|
|
443
|
+
'',
|
|
444
|
+
buildPreambleTail(stdlibB64),
|
|
445
|
+
].join('\n');
|
|
446
|
+
}
|
|
447
|
+
// ── Facet preamble ───────────────────────────────────────────────────
|
|
448
|
+
//
|
|
449
|
+
// The preamble runs at facet module-init (workerd compiles it once
|
|
450
|
+
// per slot). It exposes __pyodideRun(args) on globalThis which the
|
|
451
|
+
// dispatched facet fn calls.
|
|
452
|
+
//
|
|
453
|
+
// Strategy mirrors Cloudflare's `python-entrypoint-helper.ts`:
|
|
454
|
+
// 1. Decode pyodide.asm.js bytes from context → JS source string.
|
|
455
|
+
// 2. new Function(asmJsSrc + '; return _createPyodideModule')()
|
|
456
|
+
// — runs the Emscripten loader, returns the factory. workerd
|
|
457
|
+
// allows new Function at module-init time.
|
|
458
|
+
// 3. Build the Emscripten settings object directly (skip the
|
|
459
|
+
// public loadPyodide wrapper — we don't need its file-system
|
|
460
|
+
// probing, lockfile loader, indexURL juggling). Override:
|
|
461
|
+
// - instantiateWasm: feed precompiled module from __NIMBUS_WASM
|
|
462
|
+
// - preRun: write python_stdlib.zip into MEMFS at /lib/python313.zip
|
|
463
|
+
// - print / printErr: capture to stdout/stderr buffers
|
|
464
|
+
// - onExit: capture exit code
|
|
465
|
+
// - stdin: feed user input if any
|
|
466
|
+
// - arguments_: sys.argv
|
|
467
|
+
// - thisProgram: progName (becomes sys.argv[0])
|
|
468
|
+
// - ENV: user env vars
|
|
469
|
+
// 4. Call _createPyodideModule(settings) → Pyodide instance
|
|
470
|
+
// 5. Run user code via Pyodide.runPython
|
|
471
|
+
// 6. Return captured stdout/stderr/exitCode
|
|
472
|
+
// pyodide.asm.js is inlined ABOVE this preamble by
|
|
473
|
+
// buildPyodidePreamble(asmJsSrc). It declares 'var
|
|
474
|
+
// _createPyodideModule = (() => { ... })()' at module-top and we
|
|
475
|
+
// hoist it onto globalThis right after the inline. By the time
|
|
476
|
+
// __pyodideRun is invoked at request time, globalThis._createPyodideModule
|
|
477
|
+
// is already populated; we never invoke `new Function` (CSP-blocked
|
|
478
|
+
// at request time).
|
|
479
|
+
// ── Preamble tail builder ─────────────────────────────────────────────
|
|
480
|
+
//
|
|
481
|
+
// Generates the preamble tail with stdlibB64 spliced as a constant so
|
|
482
|
+
// the bootstrap can run at child-facet module-init time (not request
|
|
483
|
+
// time). Module-init time is where workerd permits
|
|
484
|
+
// `new WebAssembly.Module(rawBytes)`, which Pyodide's
|
|
485
|
+
// convertJsFunctionToWasm uses to build JS-callback-to-wasm shims.
|
|
486
|
+
// Without module-init context, that throws CompileError and the
|
|
487
|
+
// bootstrap promise never resolves → workerd cancels the request as
|
|
488
|
+
// hung. (This v2 redesign was directly motivated by the
|
|
489
|
+
// /tmp/pyodide-smoketest finding — see
|
|
490
|
+
// /workspace/.seal-internal/2026-05-11-pyodide-v2/smoketest-result.md.)
|
|
491
|
+
//
|
|
492
|
+
// Architecture:
|
|
493
|
+
//
|
|
494
|
+
// PREAMBLE (runs at child-facet module-init):
|
|
495
|
+
// ┌─────────────────────────────────────────────────────────────┐
|
|
496
|
+
// │ - env shims (FinalizationRegistry, process, WGS, location) │
|
|
497
|
+
// │ - asm.js eval (defines _createPyodideModule) │
|
|
498
|
+
// │ - stdlibB64 constant + decoded stdlibBytes │
|
|
499
|
+
// │ - sentinel wasm module compiled │
|
|
500
|
+
// │ - globalThis.__pyodideBootstrap = (async () => { ... })() │
|
|
501
|
+
// │ ├─ preRun: installStdlib, setHome, setEnv, │
|
|
502
|
+
// │ │ initializeNativeFS, gateRuntimeInit │
|
|
503
|
+
// │ ├─ _createPyodideModule(settings) called │
|
|
504
|
+
// │ ├─ Awaits at gate (until request time) │
|
|
505
|
+
// │ └─ Resolves with pyodideMod after gate release │
|
|
506
|
+
// └─────────────────────────────────────────────────────────────┘
|
|
507
|
+
//
|
|
508
|
+
// __pyodideRun(args) (runs at child-facet request handler):
|
|
509
|
+
// ┌─────────────────────────────────────────────────────────────┐
|
|
510
|
+
// │ - Release the bootstrap gate (crypto.getRandomValues OK now)│
|
|
511
|
+
// │ - Await __pyodideBootstrap → pyodideMod │
|
|
512
|
+
// │ - finalizeBootstrap → pyodide │
|
|
513
|
+
// │ - runPython(userCode) → stdout/stderr/exitCode │
|
|
514
|
+
// └─────────────────────────────────────────────────────────────┘
|
|
515
|
+
//
|
|
516
|
+
function buildPreambleTail(stdlibB64) {
|
|
517
|
+
return `
|
|
518
|
+
// ── BEGIN: python-runner preamble tail (Pyodide 0.29.4, Nimbus v2) ──
|
|
519
|
+
|
|
520
|
+
// Stdlib bytes spliced into preamble at facet-build time. ~3.2 MiB
|
|
521
|
+
// base64 text — decoded once at module-init. Same content as
|
|
522
|
+
// share/pyodide/python_stdlib.zip in the runtime cache.
|
|
523
|
+
const __NIMBUS_STDLIB_B64 = ${JSON.stringify(stdlibB64)};
|
|
524
|
+
|
|
525
|
+
// Decode base64 → Uint8Array. Run at module-init time (synchronous).
|
|
526
|
+
const __nimbusStdlibBytes = (function decode(b64) {
|
|
527
|
+
const bin = atob(b64);
|
|
528
|
+
const u8 = new Uint8Array(bin.length);
|
|
529
|
+
for (let i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i);
|
|
530
|
+
return u8;
|
|
531
|
+
})(__NIMBUS_STDLIB_B64);
|
|
532
|
+
|
|
533
|
+
// Sentinel-module setup. Pyodide's pyodide.mjs compiles a tiny standalone
|
|
534
|
+
// wasm blob that exports {create_sentinel, is_sentinel} and attaches its
|
|
535
|
+
// exports as imports.sentinel BEFORE instantiating pyodide.asm.wasm. We
|
|
536
|
+
// bypass loadPyodide → must replicate here. The base64 blob below is
|
|
537
|
+
// verbatim from pyodide.mjs (variable \`G\` in v0.29.4).
|
|
538
|
+
const __NIMBUS_SENTINEL_WASM_B64 = 'AGFzbQEAAAABDANfAGAAAW9gAW8BfwMDAgECByECD2NyZWF0ZV9zZW50aW5lbAAAC2lzX3NlbnRpbmVsAAEKEwIHAPsBAPsbCwkAIAD7GvsUAAs=';
|
|
539
|
+
let __nimbusSentinelExports;
|
|
540
|
+
try {
|
|
541
|
+
const bin = atob(__NIMBUS_SENTINEL_WASM_B64);
|
|
542
|
+
const sentinelBytes = new Uint8Array(bin.length);
|
|
543
|
+
for (let i = 0; i < bin.length; i++) sentinelBytes[i] = bin.charCodeAt(i);
|
|
544
|
+
// Synchronous wasm compile at module-init (CSP permits here).
|
|
545
|
+
const sentinelMod = new WebAssembly.Module(sentinelBytes);
|
|
546
|
+
const sentinelInst = new WebAssembly.Instance(sentinelMod);
|
|
547
|
+
__nimbusSentinelExports = sentinelInst.exports;
|
|
548
|
+
} catch (_e) {
|
|
549
|
+
// CSP fallback — pyodide.mjs's K() also has this Symbol-based path.
|
|
550
|
+
const marker = Symbol('sentinel');
|
|
551
|
+
__nimbusSentinelExports = {
|
|
552
|
+
create_sentinel: function() { return marker; },
|
|
553
|
+
is_sentinel: function(v) { return v === marker; },
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Module-init stdout/stderr capture buffers. Used by Pyodide's print/
|
|
558
|
+
// printErr (set in settings below). The per-call __pyodideRun grabs a
|
|
559
|
+
// slice from these buffers to isolate output per invocation.
|
|
560
|
+
globalThis.__nimbusPyStdout = globalThis.__nimbusPyStdout || [];
|
|
561
|
+
globalThis.__nimbusPyStderr = globalThis.__nimbusPyStderr || [];
|
|
562
|
+
|
|
563
|
+
// Bootstrap gate release fn — populated by gateRuntimeInit preRun hook
|
|
564
|
+
// during _createPyodideModule's synchronous preRun pass.
|
|
565
|
+
globalThis.__nimbusReleaseGate = null;
|
|
566
|
+
|
|
567
|
+
// ── Bootstrap promise (kicked off at child-facet module-init) ────────
|
|
568
|
+
//
|
|
569
|
+
// CRITICAL: this promise is created at module-init time so the
|
|
570
|
+
// _createPyodideModule call (and its synchronous wasm-module-compile
|
|
571
|
+
// side effects, including convertJsFunctionToWasm) happens in
|
|
572
|
+
// startup-CSP context. The promise body's awaits run as microtasks
|
|
573
|
+
// after module-init returns, but the SYNCHRONOUS portion of
|
|
574
|
+
// _createPyodideModule (wasm instantiate + preRun + reportUndefinedSymbols
|
|
575
|
+
// + convertJsFunctionToWasm) all completes before the first await.
|
|
576
|
+
//
|
|
577
|
+
// We hide process + WGS inside this async function so the env-detection
|
|
578
|
+
// inside Pyodide's async factory body sees the stubbed values, then
|
|
579
|
+
// restore them in the finally block.
|
|
580
|
+
globalThis.__pyodideBootstrap = (async function nimbusBootstrap() {
|
|
581
|
+
if (typeof globalThis._createPyodideModule !== 'function') {
|
|
582
|
+
return { ok: false, error: '_createPyodideModule not installed by inline asm.js' };
|
|
583
|
+
}
|
|
584
|
+
const __origProcess = globalThis.process;
|
|
585
|
+
const __origWGS = globalThis.WorkerGlobalScope;
|
|
586
|
+
try { globalThis.process = undefined; } catch {}
|
|
587
|
+
try {
|
|
588
|
+
if (globalThis.process && typeof globalThis.process === 'object') {
|
|
589
|
+
globalThis.process.browser = true;
|
|
590
|
+
}
|
|
591
|
+
} catch {}
|
|
592
|
+
try { globalThis.WorkerGlobalScope = Object; } catch {}
|
|
593
|
+
|
|
594
|
+
try {
|
|
595
|
+
// Get the wasm module that the loader-pool injected via __NIMBUS_WASM.
|
|
596
|
+
const wasmTable = globalThis.__NIMBUS_WASM || {};
|
|
597
|
+
const asmWasmMod = wasmTable['pyodide.asm.wasm'];
|
|
598
|
+
if (!asmWasmMod) {
|
|
599
|
+
return { ok: false, error: '__NIMBUS_WASM missing pyodide.asm.wasm' };
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Initial Pyodide config — note: args, env, progName come from the
|
|
603
|
+
// per-call args at request time. We use sensible defaults here so
|
|
604
|
+
// the bootstrap can complete; per-call __pyodideRun overrides via
|
|
605
|
+
// M.ENV / pyodide.runPython arguments.
|
|
606
|
+
const config = {
|
|
607
|
+
indexURL: '/pyodide/',
|
|
608
|
+
fullStdLib: false,
|
|
609
|
+
jsglobals: globalThis,
|
|
610
|
+
args: [],
|
|
611
|
+
env: { HOME: '/home/pyodide', PYTHONINSPECT: '1' },
|
|
612
|
+
packages: [],
|
|
613
|
+
lockFileContents: '{"packages":{}}',
|
|
614
|
+
packageCacheDir: undefined,
|
|
615
|
+
enableRunUntilComplete: true,
|
|
616
|
+
checkAPIVersion: false,
|
|
617
|
+
_sysExecutable: 'python',
|
|
618
|
+
BUILD_ID: 'nimbus-pyodide-0.29.4',
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
const settings = {
|
|
622
|
+
noInitialRun: false,
|
|
623
|
+
noExitRuntime: true,
|
|
624
|
+
noImageDecoding: true,
|
|
625
|
+
noAudioDecoding: true,
|
|
626
|
+
noWasmDecoding: false,
|
|
627
|
+
print: function(s) { globalThis.__nimbusPyStdout.push(s + '\\n'); },
|
|
628
|
+
printErr: function(s) { globalThis.__nimbusPyStderr.push(s + '\\n'); },
|
|
629
|
+
// onExit captures the SystemExit code from Pyodide via Emscripten's
|
|
630
|
+
// ExitStatus path. The per-call __pyodideRun resets __nimbusExitCode
|
|
631
|
+
// to undefined before runPython and reads it after.
|
|
632
|
+
onExit: function(code) { globalThis.__nimbusExitCode = code | 0; },
|
|
633
|
+
thisProgram: config._sysExecutable,
|
|
634
|
+
arguments: config.args,
|
|
635
|
+
API: { config: config, runtimeEnv: { IN_NODE: false, IN_BROWSER: false, IN_SHELL: false } },
|
|
636
|
+
locateFile: function(path) { return '/pyodide/' + path; },
|
|
637
|
+
instantiateWasm: function(imports, successCallback) {
|
|
638
|
+
// Attach sentinel namespace synchronously — Pyodide expects
|
|
639
|
+
// imports.sentinel = {create_sentinel, is_sentinel}.
|
|
640
|
+
imports.sentinel = __nimbusSentinelExports;
|
|
641
|
+
WebAssembly.instantiate(asmWasmMod, imports).then(function(result) {
|
|
642
|
+
const inst = (result instanceof WebAssembly.Instance ? result : result.instance);
|
|
643
|
+
try {
|
|
644
|
+
successCallback(inst, asmWasmMod);
|
|
645
|
+
} catch (cbErr) {
|
|
646
|
+
globalThis.__nimbusPyStderr.push('[python-runner] receiveInstance threw: ' + (cbErr && cbErr.message) + '\\n');
|
|
647
|
+
throw cbErr;
|
|
648
|
+
}
|
|
649
|
+
}).catch(function(e) {
|
|
650
|
+
globalThis.__nimbusPyStderr.push('[python-runner] wasm instantiate failed: ' + (e && e.message) + '\\n');
|
|
651
|
+
});
|
|
652
|
+
return {};
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// Faithful replication of pyodide.mjs's
|
|
657
|
+
// getFileSystemInitializationFuncs ordering, plus our request-gate.
|
|
658
|
+
settings.preRun = [
|
|
659
|
+
function installStdlib(M) {
|
|
660
|
+
M.addRunDependency('nimbus-install-stdlib');
|
|
661
|
+
try {
|
|
662
|
+
const verWord = M.HEAPU32[M._Py_Version >>> 2];
|
|
663
|
+
const major = (verWord >>> 24) & 0xff;
|
|
664
|
+
const minor = (verWord >>> 16) & 0xff;
|
|
665
|
+
M.API.pyVersionTuple = [major, minor, (verWord >>> 8) & 0xff];
|
|
666
|
+
M.FS.mkdirTree('/lib');
|
|
667
|
+
M.API.sitePackages = '/lib/python' + major + '.' + minor + '/site-packages';
|
|
668
|
+
M.FS.mkdirTree(M.API.sitePackages);
|
|
669
|
+
M.FS.writeFile('/lib/python' + major + minor + '.zip', __nimbusStdlibBytes);
|
|
670
|
+
} catch (e) {
|
|
671
|
+
globalThis.__nimbusPyStderr.push('[python-runner] installStdlib failed: ' + (e && e.message) + '\\n');
|
|
672
|
+
} finally {
|
|
673
|
+
M.removeRunDependency('nimbus-install-stdlib');
|
|
674
|
+
}
|
|
675
|
+
},
|
|
676
|
+
function setHome(M) {
|
|
677
|
+
let home = config.env.HOME;
|
|
678
|
+
try { M.FS.mkdirTree(home); } catch { home = '/'; }
|
|
679
|
+
try { M.FS.chdir(home); } catch {}
|
|
680
|
+
},
|
|
681
|
+
function setEnv(M) {
|
|
682
|
+
try { Object.assign(M.ENV, config.env); } catch {}
|
|
683
|
+
},
|
|
684
|
+
function initializeNativeFS(M) {
|
|
685
|
+
try {
|
|
686
|
+
M.FS.filesystems.NATIVEFS_ASYNC = {
|
|
687
|
+
mount: function() { throw new Error('NATIVEFS_ASYNC not implemented'); },
|
|
688
|
+
syncfs: function(_m, _p, cb) { cb(); },
|
|
689
|
+
};
|
|
690
|
+
} catch {}
|
|
691
|
+
},
|
|
692
|
+
function gateRuntimeInit(M) {
|
|
693
|
+
// Adds a runDependency that defers Pyodide's doRun (callMain →
|
|
694
|
+
// CPython __wasm_call_ctors → crypto.getRandomValues — blocked
|
|
695
|
+
// at module-init) until our request handler releases it.
|
|
696
|
+
M.addRunDependency('nimbus-request-gate');
|
|
697
|
+
globalThis.__nimbusReleaseGate = function() {
|
|
698
|
+
try { M.removeRunDependency('nimbus-request-gate'); } catch {}
|
|
699
|
+
};
|
|
700
|
+
},
|
|
701
|
+
];
|
|
702
|
+
|
|
703
|
+
// Stash settings on globalThis so __pyodideRun can mutate config
|
|
704
|
+
// (args/env/progName) at request time before runPython.
|
|
705
|
+
globalThis.__nimbusPyConfig = config;
|
|
706
|
+
|
|
707
|
+
// Kick off _createPyodideModule. Its synchronous portion runs all
|
|
708
|
+
// preRun hooks (registering the gate) and instantiates the wasm
|
|
709
|
+
// module (including convertJsFunctionToWasm in
|
|
710
|
+
// reportUndefinedSymbols — the v1 hang root). The returned promise
|
|
711
|
+
// resolves when the gate is released at request time.
|
|
712
|
+
const modPromise = globalThis._createPyodideModule(settings);
|
|
713
|
+
const pyodideMod = await modPromise;
|
|
714
|
+
return { ok: true, mod: pyodideMod };
|
|
715
|
+
} catch (e) {
|
|
716
|
+
return { ok: false, error: '_createPyodideModule failed: ' + (e && e.message), stack: e && e.stack };
|
|
717
|
+
} finally {
|
|
718
|
+
try { globalThis.process = __origProcess; } catch {}
|
|
719
|
+
try {
|
|
720
|
+
if (globalThis.process && globalThis.process.browser === true) {
|
|
721
|
+
delete globalThis.process.browser;
|
|
722
|
+
}
|
|
723
|
+
} catch {}
|
|
724
|
+
globalThis.WorkerGlobalScope = __origWGS;
|
|
725
|
+
}
|
|
726
|
+
})();
|
|
727
|
+
|
|
728
|
+
// ── Per-call entry point ─────────────────────────────────────────────
|
|
729
|
+
//
|
|
730
|
+
// Invoked from the LOADER child facet's execute() (which calls the
|
|
731
|
+
// serialized facetFn that does globalThis.__pyodideRun(args)).
|
|
732
|
+
//
|
|
733
|
+
// At this point the bootstrap promise is mid-flight at the gate.
|
|
734
|
+
// We release the gate, await the bootstrap completion (which now
|
|
735
|
+
// runs CPython init in request-handler CSP context where
|
|
736
|
+
// crypto.getRandomValues is permitted), then finalizeBootstrap +
|
|
737
|
+
// runPython.
|
|
738
|
+
globalThis.__pyodideRun = async function __pyodideRun(args) {
|
|
739
|
+
// Tracks where in the user's request output begins (the bootstrap
|
|
740
|
+
// may have produced some print() output too, but in practice it
|
|
741
|
+
// doesn't — CPython's Py_Initialize is silent).
|
|
742
|
+
const stdoutStart = globalThis.__nimbusPyStdout.length;
|
|
743
|
+
const stderrStart = globalThis.__nimbusPyStderr.length;
|
|
744
|
+
|
|
745
|
+
// Override config args/env/progName for THIS call. The bootstrap
|
|
746
|
+
// used defaults; the user's actual sys.argv and env are applied here.
|
|
747
|
+
// setEnv preRun ran with the old config — we re-apply onto M.ENV
|
|
748
|
+
// via runPython below.
|
|
749
|
+
if (globalThis.__nimbusPyConfig) {
|
|
750
|
+
globalThis.__nimbusPyConfig.args = args.pyArgv.slice(1);
|
|
751
|
+
globalThis.__nimbusPyConfig.env = Object.assign({}, globalThis.__nimbusPyConfig.env, args.userEnv || {});
|
|
752
|
+
globalThis.__nimbusPyConfig._sysExecutable = args.progName;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Release the bootstrap gate (registered by gateRuntimeInit preRun).
|
|
756
|
+
// Required: workerd blocks crypto.getRandomValues at module-init.
|
|
757
|
+
// Releasing at request time lets Pyodide's doRun → callMain →
|
|
758
|
+
// __wasm_call_ctors → randomFill run in request-handler context.
|
|
759
|
+
if (typeof globalThis.__nimbusReleaseGate === 'function') {
|
|
760
|
+
globalThis.__nimbusReleaseGate();
|
|
761
|
+
// Make idempotent so a re-entrant call doesn't double-release.
|
|
762
|
+
globalThis.__nimbusReleaseGate = function() {};
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Await bootstrap completion.
|
|
766
|
+
const boot = await globalThis.__pyodideBootstrap;
|
|
767
|
+
if (!boot.ok || !boot.mod) {
|
|
768
|
+
return {
|
|
769
|
+
exitCode: 1,
|
|
770
|
+
stdout: globalThis.__nimbusPyStdout.slice(stdoutStart).join(''),
|
|
771
|
+
stderr: globalThis.__nimbusPyStderr.slice(stderrStart).join(''),
|
|
772
|
+
error: 'pyodide bootstrap failed: ' + (boot.error || 'unknown'),
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
const pyodideMod = boot.mod;
|
|
776
|
+
|
|
777
|
+
// Apply user env to MEMFS now that CPython is up. (The preRun setEnv
|
|
778
|
+
// ran with bootstrap defaults; we layer the user env on top.)
|
|
779
|
+
if (args.userEnv) {
|
|
780
|
+
try { Object.assign(pyodideMod.ENV, args.userEnv); } catch {}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// finalizeBootstrap returns the public Pyodide JS API (the one with
|
|
784
|
+
// .runPython, .globals, .registerJsModule). It registers Python-side
|
|
785
|
+
// js-module hooks via register_js_finder; calling it twice on the
|
|
786
|
+
// same pyodideMod throws "JsFinder already registered". So we cache
|
|
787
|
+
// the result globally and reuse it across multiple __pyodideRun
|
|
788
|
+
// invocations (which happen when the child-facet pool reuses the
|
|
789
|
+
// same Pyodide instance for multiple python -c calls in one session).
|
|
790
|
+
let pyodide = globalThis.__nimbusPyodideInstance;
|
|
791
|
+
if (!pyodide) {
|
|
792
|
+
try {
|
|
793
|
+
pyodide = pyodideMod.API.finalizeBootstrap(undefined, undefined);
|
|
794
|
+
globalThis.__nimbusPyodideInstance = pyodide;
|
|
795
|
+
} catch (e) {
|
|
796
|
+
return {
|
|
797
|
+
exitCode: 1,
|
|
798
|
+
stdout: globalThis.__nimbusPyStdout.slice(stdoutStart).join(''),
|
|
799
|
+
stderr: globalThis.__nimbusPyStderr.slice(stderrStart).join(''),
|
|
800
|
+
error: 'finalizeBootstrap failed: ' + (e && e.message),
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Update sys.argv to reflect this call's progName + pyArgv. Pyodide's
|
|
806
|
+
// thisProgram is fixed at bootstrap time (when callMain ran); we
|
|
807
|
+
// overwrite sys.argv at request time via a tiny runPython prelude.
|
|
808
|
+
// pyArgv is the user-visible argv (script name first, then args);
|
|
809
|
+
// progName is the executable that prefixes errors.
|
|
810
|
+
try {
|
|
811
|
+
const argv = [args.progName].concat((args.pyArgv || []).slice(0));
|
|
812
|
+
// If pyArgv already includes the script as [0], use it directly.
|
|
813
|
+
// Convention from the python-runner caller: pyArgv = [progName,
|
|
814
|
+
// ...args]. We set sys.argv = pyArgv with [0] = progName.
|
|
815
|
+
const effectiveArgv = (args.pyArgv && args.pyArgv.length > 0) ? args.pyArgv : argv;
|
|
816
|
+
pyodide.runPython(
|
|
817
|
+
'import sys\\nsys.argv = ' +
|
|
818
|
+
JSON.stringify(effectiveArgv)
|
|
819
|
+
);
|
|
820
|
+
} catch { /* best-effort */ }
|
|
821
|
+
|
|
822
|
+
// Reset onExit capture for this call.
|
|
823
|
+
globalThis.__nimbusExitCode = undefined;
|
|
824
|
+
|
|
825
|
+
// Run the user code.
|
|
826
|
+
let exitCode = 0;
|
|
827
|
+
if (args.userCode) {
|
|
828
|
+
try {
|
|
829
|
+
pyodide.runPython(args.userCode);
|
|
830
|
+
// If runPython returned normally, exit code is 0 unless onExit
|
|
831
|
+
// was invoked (which can happen if user called sys.exit but
|
|
832
|
+
// Pyodide handled it via ExitStatus before throwing).
|
|
833
|
+
if (typeof globalThis.__nimbusExitCode === 'number') {
|
|
834
|
+
exitCode = globalThis.__nimbusExitCode;
|
|
835
|
+
}
|
|
836
|
+
} catch (e) {
|
|
837
|
+
// Check onExit-captured code first (set by Emscripten's ExitStatus
|
|
838
|
+
// path before the throw).
|
|
839
|
+
if (typeof globalThis.__nimbusExitCode === 'number') {
|
|
840
|
+
exitCode = globalThis.__nimbusExitCode;
|
|
841
|
+
} else if (e && typeof e.message === 'string') {
|
|
842
|
+
// Fall back to message parsing. Pyodide surfaces SystemExit as
|
|
843
|
+
// a PythonError with message containing "SystemExit: <code>".
|
|
844
|
+
const m = e.message.match(/SystemExit:\\s*(-?\\d+)/);
|
|
845
|
+
if (m) {
|
|
846
|
+
exitCode = parseInt(m[1], 10);
|
|
847
|
+
} else if (/SystemExit/.test(e.message)) {
|
|
848
|
+
// SystemExit with no numeric arg (e.g., sys.exit() or sys.exit(None))
|
|
849
|
+
exitCode = 0;
|
|
850
|
+
} else {
|
|
851
|
+
globalThis.__nimbusPyStderr.push(e.message + (e.message.endsWith('\\n') ? '' : '\\n'));
|
|
852
|
+
exitCode = 1;
|
|
853
|
+
}
|
|
854
|
+
} else {
|
|
855
|
+
globalThis.__nimbusPyStderr.push('[python-runner] unknown error: ' + e + '\\n');
|
|
856
|
+
exitCode = 1;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
return {
|
|
862
|
+
exitCode: exitCode,
|
|
863
|
+
stdout: globalThis.__nimbusPyStdout.slice(stdoutStart).join(''),
|
|
864
|
+
stderr: globalThis.__nimbusPyStderr.slice(stderrStart).join(''),
|
|
865
|
+
};
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
// ── END: python-runner preamble ───────────────────────────────────────
|
|
869
|
+
`;
|
|
870
|
+
}
|