wasm-idle 0.7.2 → 1.0.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 +760 -81
- package/dist/index.d.ts +4 -9
- package/dist/index.js +2 -4
- package/dist/playground/applicationAssets.d.ts +4 -0
- package/dist/playground/applicationAssets.js +277 -0
- package/dist/playground/assemblyscript.d.ts +41 -0
- package/dist/playground/assemblyscript.js +630 -0
- package/dist/playground/assetBridge.d.ts +18 -7
- package/dist/playground/assetBridge.js +824 -100
- package/dist/playground/assets.d.ts +813 -19
- package/dist/playground/assets.js +2096 -156
- package/dist/playground/awk.d.ts +5 -0
- package/dist/playground/awk.js +60 -0
- package/dist/playground/bash.d.ts +53 -0
- package/dist/playground/bash.js +1096 -0
- package/dist/playground/bashWorkerProtocol.d.ts +88 -0
- package/dist/playground/bashWorkerProtocol.js +135 -0
- package/dist/playground/bqn.d.ts +5 -0
- package/dist/playground/bqn.js +57 -0
- package/dist/playground/bqnPreflight.d.ts +26 -0
- package/dist/playground/bqnPreflight.js +399 -0
- package/dist/playground/c3.d.ts +15 -0
- package/dist/playground/c3.js +154 -0
- package/dist/playground/clang.d.ts +35 -19
- package/dist/playground/clang.js +241 -52
- package/dist/playground/clangAssetIntegrity.d.ts +32 -0
- package/dist/playground/clangAssetIntegrity.js +8 -0
- package/dist/playground/clojurescript.d.ts +5 -0
- package/dist/playground/clojurescript.js +59 -0
- package/dist/playground/clojurescriptPreflight.d.ts +26 -0
- package/dist/playground/clojurescriptPreflight.js +383 -0
- package/dist/playground/cobol.d.ts +46 -0
- package/dist/playground/cobol.js +703 -0
- package/dist/playground/d.d.ts +47 -0
- package/dist/playground/d.js +644 -0
- package/dist/playground/dOuterAssets.d.ts +16 -0
- package/dist/playground/dOuterAssets.js +163 -0
- package/dist/playground/dotnet.d.ts +99 -0
- package/dist/playground/dotnet.js +1131 -0
- package/dist/playground/duckdb.d.ts +36 -0
- package/dist/playground/duckdb.js +542 -0
- package/dist/playground/elixir.d.ts +18 -10
- package/dist/playground/elixir.js +630 -96
- package/dist/playground/elixirAssets.d.ts +6 -0
- package/dist/playground/elixirAssets.js +33 -0
- package/dist/playground/executionObservation.d.ts +22 -0
- package/dist/playground/executionObservation.js +65 -0
- package/dist/playground/forth.d.ts +5 -0
- package/dist/playground/forth.js +53 -0
- package/dist/playground/forthPreflight.d.ts +24 -0
- package/dist/playground/forthPreflight.js +198 -0
- package/dist/playground/fortran.d.ts +47 -0
- package/dist/playground/fortran.js +651 -0
- package/dist/playground/fortranAssets.d.ts +6 -0
- package/dist/playground/fortranAssets.js +27 -0
- package/dist/playground/gleam.d.ts +6 -0
- package/dist/playground/gleam.js +33 -0
- package/dist/playground/go.d.ts +17 -11
- package/dist/playground/go.js +376 -72
- package/dist/playground/goDebugInstrumentation.d.ts +1 -0
- package/dist/playground/goDebugInstrumentation.js +229 -0
- package/dist/playground/haskell.d.ts +44 -0
- package/dist/playground/haskell.js +678 -0
- package/dist/playground/haskellAssets.d.ts +24 -0
- package/dist/playground/haskellAssets.js +93 -0
- package/dist/playground/index.d.ts +1 -1
- package/dist/playground/index.js +360 -111
- package/dist/playground/j.d.ts +5 -0
- package/dist/playground/j.js +60 -0
- package/dist/playground/jPreflight.d.ts +26 -0
- package/dist/playground/jPreflight.js +372 -0
- package/dist/playground/janet.d.ts +5 -0
- package/dist/playground/janet.js +64 -0
- package/dist/playground/java.d.ts +24 -10
- package/dist/playground/java.js +706 -84
- package/dist/playground/javaSource.d.ts +5 -0
- package/dist/playground/javaSource.js +16 -0
- package/dist/playground/javaStdin.d.ts +1 -1
- package/dist/playground/javaStdin.js +13 -4
- package/dist/playground/julia.d.ts +5 -0
- package/dist/playground/julia.js +64 -0
- package/dist/playground/lfortran.d.ts +8 -0
- package/dist/playground/lfortran.js +30 -0
- package/dist/playground/lfortranAssets.d.ts +21 -0
- package/dist/playground/lfortranAssets.js +118 -0
- package/dist/playground/lisp.d.ts +45 -0
- package/dist/playground/lisp.js +640 -0
- package/dist/playground/lispAssets.d.ts +9 -0
- package/dist/playground/lispAssets.js +74 -0
- package/dist/playground/lldbManifest.d.ts +3 -0
- package/dist/playground/lldbManifest.js +155 -0
- package/dist/playground/lldbSession.d.ts +115 -0
- package/dist/playground/lldbSession.js +1347 -0
- package/dist/playground/loadingProgress.d.ts +16 -0
- package/dist/playground/loadingProgress.js +166 -0
- package/dist/playground/lua.d.ts +44 -0
- package/dist/playground/lua.js +630 -0
- package/dist/playground/nim.d.ts +5 -0
- package/dist/playground/nim.js +76 -0
- package/dist/playground/objectivec.d.ts +39 -0
- package/dist/playground/objectivec.js +288 -0
- package/dist/playground/ocaml.d.ts +28 -10
- package/dist/playground/ocaml.js +815 -253
- package/dist/playground/octave.d.ts +43 -0
- package/dist/playground/octave.js +783 -0
- package/dist/playground/options.d.ts +19 -40
- package/dist/playground/options.js +2 -1
- package/dist/playground/pascal.d.ts +5 -0
- package/dist/playground/pascal.js +60 -0
- package/dist/playground/perl.d.ts +5 -0
- package/dist/playground/perl.js +61 -0
- package/dist/playground/php.d.ts +44 -0
- package/dist/playground/php.js +665 -0
- package/dist/playground/prolog.d.ts +6 -0
- package/dist/playground/prolog.js +75 -0
- package/dist/playground/python.d.ts +20 -14
- package/dist/playground/python.js +445 -101
- package/dist/playground/pythonPackageLock.d.ts +6 -0
- package/dist/playground/pythonPackageLock.js +56 -0
- package/dist/playground/r.d.ts +44 -0
- package/dist/playground/r.js +637 -0
- package/dist/playground/ruby.d.ts +46 -0
- package/dist/playground/ruby.js +732 -0
- package/dist/playground/rubyAssets.d.ts +23 -0
- package/dist/playground/rubyAssets.js +68 -0
- package/dist/playground/rubyTestPreflight.d.ts +2 -0
- package/dist/playground/rubyTestPreflight.js +18 -0
- package/dist/playground/runtimeModule.d.ts +1 -0
- package/dist/playground/runtimeModule.js +5 -0
- package/dist/playground/rust.d.ts +44 -13
- package/dist/playground/rust.js +714 -76
- package/dist/playground/rustExecutableGraph.d.ts +85 -0
- package/dist/playground/rustExecutableGraph.js +946 -0
- package/dist/playground/rustWorkerLimits.d.ts +17 -0
- package/dist/playground/rustWorkerLimits.js +34 -0
- package/dist/playground/sandbox.d.ts +16 -10
- package/dist/playground/sharedBuffer.d.ts +6 -0
- package/dist/playground/sharedBuffer.js +12 -0
- package/dist/playground/sqlite.d.ts +38 -0
- package/dist/playground/sqlite.js +550 -0
- package/dist/playground/staticRuntimeModuleVersion.d.ts +1 -0
- package/dist/playground/staticRuntimeModuleVersion.js +2 -0
- package/dist/playground/staticRuntimePreflight.d.ts +13 -0
- package/dist/playground/staticRuntimePreflight.js +231 -0
- package/dist/playground/staticRuntimePreflightExecute.d.ts +8 -0
- package/dist/playground/staticRuntimePreflightExecute.js +66 -0
- package/dist/playground/staticRuntimePreflightProtocol.d.ts +68 -0
- package/dist/playground/staticRuntimePreflightProtocol.js +339 -0
- package/dist/playground/staticStdinRing.d.ts +45 -0
- package/dist/playground/staticStdinRing.js +158 -0
- package/dist/playground/staticWorkerRuntime.d.ts +154 -0
- package/dist/playground/staticWorkerRuntime.js +1917 -0
- package/dist/playground/stdinBuffer.d.ts +9 -7
- package/dist/playground/stdinBuffer.js +9 -18
- package/dist/playground/swift.d.ts +5 -0
- package/dist/playground/swift.js +17 -0
- package/dist/playground/tcl.d.ts +5 -0
- package/dist/playground/tcl.js +62 -0
- package/dist/playground/teavmConfig.d.ts +1 -1
- package/dist/playground/teavmConfig.js +2 -2
- package/dist/playground/tinygo.d.ts +48 -18
- package/dist/playground/tinygo.js +1202 -329
- package/dist/playground/tinygoExecutableGraph.d.ts +44 -0
- package/dist/playground/tinygoExecutableGraph.js +449 -0
- package/dist/playground/typescript.d.ts +55 -0
- package/dist/playground/typescript.js +924 -0
- package/dist/playground/wasm.d.ts +43 -0
- package/dist/playground/wasm.js +608 -0
- package/dist/playground/wasmAwkVersion.d.ts +59 -0
- package/dist/playground/wasmAwkVersion.js +31 -0
- package/dist/playground/wasmBashVersion.d.ts +63 -0
- package/dist/playground/wasmBashVersion.js +36 -0
- package/dist/playground/wasmBqnVersion.d.ts +24 -0
- package/dist/playground/wasmBqnVersion.js +24 -0
- package/dist/playground/wasmC3Version.d.ts +20 -0
- package/dist/playground/wasmC3Version.js +21 -0
- package/dist/playground/wasmClojureScriptVersion.d.ts +21 -0
- package/dist/playground/wasmClojureScriptVersion.js +21 -0
- package/dist/playground/wasmDIntegrity.d.ts +15 -0
- package/dist/playground/wasmDIntegrity.js +15 -0
- package/dist/playground/wasmDVersion.d.ts +1 -0
- package/dist/playground/wasmDVersion.js +1 -0
- package/dist/playground/wasmDebugVersion.d.ts +6 -0
- package/dist/playground/wasmDebugVersion.js +6 -0
- package/dist/playground/wasmDotnetVersion.d.ts +1 -0
- package/dist/playground/wasmDotnetVersion.js +1 -0
- package/dist/playground/wasmElixirVersion.d.ts +21 -1
- package/dist/playground/wasmElixirVersion.js +21 -1
- package/dist/playground/wasmForthVersion.d.ts +18 -0
- package/dist/playground/wasmForthVersion.js +18 -0
- package/dist/playground/wasmFortranExecutionAssets.d.ts +15 -0
- package/dist/playground/wasmFortranExecutionAssets.js +15 -0
- package/dist/playground/wasmFortranVersion.d.ts +1 -0
- package/dist/playground/wasmFortranVersion.js +1 -0
- package/dist/playground/wasmGleamVersion.d.ts +5 -0
- package/dist/playground/wasmGleamVersion.js +5 -0
- package/dist/playground/wasmGoVersion.d.ts +1 -1
- package/dist/playground/wasmGoVersion.js +1 -1
- package/dist/playground/wasmHaskellVersion.d.ts +1 -0
- package/dist/playground/wasmHaskellVersion.js +1 -0
- package/dist/playground/wasmJVersion.d.ts +24 -0
- package/dist/playground/wasmJVersion.js +24 -0
- package/dist/playground/wasmJanetVersion.d.ts +53 -0
- package/dist/playground/wasmJanetVersion.js +30 -0
- package/dist/playground/wasmJuliaVersion.d.ts +71 -0
- package/dist/playground/wasmJuliaVersion.js +39 -0
- package/dist/playground/wasmLfortranVersion.d.ts +28 -0
- package/dist/playground/wasmLfortranVersion.js +29 -0
- package/dist/playground/wasmLispVersion.d.ts +1 -0
- package/dist/playground/wasmLispVersion.js +1 -0
- package/dist/playground/wasmLuaVersion.d.ts +1 -0
- package/dist/playground/wasmLuaVersion.js +1 -0
- package/dist/playground/wasmNimVersion.d.ts +125 -0
- package/dist/playground/wasmNimVersion.js +66 -0
- package/dist/playground/wasmObjectiveCVersion.d.ts +27 -0
- package/dist/playground/wasmObjectiveCVersion.js +27 -0
- package/dist/playground/wasmOcamlVersion.d.ts +12 -1
- package/dist/playground/wasmOcamlVersion.js +12 -1
- package/dist/playground/wasmOctaveVersion.d.ts +1 -0
- package/dist/playground/wasmOctaveVersion.js +1 -0
- package/dist/playground/wasmPascalVersion.d.ts +61 -0
- package/dist/playground/wasmPascalVersion.js +34 -0
- package/dist/playground/wasmPerlVersion.d.ts +71 -0
- package/dist/playground/wasmPerlVersion.js +39 -0
- package/dist/playground/wasmPrologVersion.d.ts +31 -0
- package/dist/playground/wasmPrologVersion.js +31 -0
- package/dist/playground/wasmRVersion.d.ts +1 -0
- package/dist/playground/wasmRVersion.js +1 -0
- package/dist/playground/wasmRustVersion.d.ts +1306 -1
- package/dist/playground/wasmRustVersion.js +1458 -1
- package/dist/playground/wasmSwiftVersion.d.ts +1 -0
- package/dist/playground/wasmSwiftVersion.js +1 -0
- package/dist/playground/wasmTclVersion.d.ts +85 -0
- package/dist/playground/wasmTclVersion.js +46 -0
- package/dist/playground/wasmTinyGoVersion.d.ts +98 -1
- package/dist/playground/wasmTinyGoVersion.js +107 -1
- package/dist/playground/wasmTypeScriptVersion.d.ts +5 -0
- package/dist/playground/wasmTypeScriptVersion.js +5 -0
- package/dist/playground/wasmWatVersion.d.ts +1 -0
- package/dist/playground/wasmWatVersion.js +1 -0
- package/dist/playground/wasmZigVersion.d.ts +13 -0
- package/dist/playground/wasmZigVersion.js +13 -0
- package/dist/playground/wat.d.ts +44 -0
- package/dist/playground/wat.js +622 -0
- package/dist/playground/worker/assemblyscript.js +339 -0
- package/dist/playground/worker/assets.d.ts +7 -0
- package/dist/playground/worker/assets.js +288 -39
- package/dist/playground/worker/bash.js +338 -0
- package/dist/playground/worker/bashNestedBootstrap.d.ts +6 -0
- package/dist/playground/worker/bashNestedBootstrap.js +59 -0
- package/dist/playground/worker/bashRuntime.d.ts +89 -0
- package/dist/playground/worker/bashRuntime.js +982 -0
- package/dist/playground/worker/clang.js +92 -13
- package/dist/playground/worker/cobol.js +107 -0
- package/dist/playground/worker/d.js +199 -0
- package/dist/playground/worker/dotnet.js +141 -0
- package/dist/playground/worker/duckdb.d.ts +1 -0
- package/dist/playground/worker/duckdb.js +136 -0
- package/dist/playground/worker/elixir.js +307 -41
- package/dist/playground/worker/fortran.d.ts +1 -0
- package/dist/playground/worker/fortran.js +295 -0
- package/dist/playground/worker/go.js +246 -40
- package/dist/playground/worker/haskell.d.ts +7 -0
- package/dist/playground/worker/haskell.js +465 -0
- package/dist/playground/worker/java.js +59 -20
- package/dist/playground/worker/lisp.d.ts +1 -0
- package/dist/playground/worker/lisp.js +195 -0
- package/dist/playground/worker/lua.d.ts +1 -0
- package/dist/playground/worker/lua.js +163 -0
- package/dist/playground/worker/objectivec.d.ts +1 -0
- package/dist/playground/worker/objectivec.js +10 -0
- package/dist/playground/worker/ocaml.js +206 -58
- package/dist/playground/worker/php.d.ts +1 -0
- package/dist/playground/worker/php.js +261 -0
- package/dist/playground/worker/python.js +201 -44
- package/dist/playground/worker/r.d.ts +1 -0
- package/dist/playground/worker/r.js +192 -0
- package/dist/playground/worker/ruby.d.ts +1 -0
- package/dist/playground/worker/ruby.js +248 -0
- package/dist/playground/worker/runtimeAssetFetch.d.ts +15 -0
- package/dist/playground/worker/runtimeAssetFetch.js +375 -0
- package/dist/playground/worker/rust.js +546 -20
- package/dist/playground/worker/sqlite.d.ts +1 -0
- package/dist/playground/worker/sqlite.js +112 -0
- package/dist/playground/worker/staticRuntimePreflight.d.ts +1 -0
- package/dist/playground/worker/staticRuntimePreflight.js +56 -0
- package/dist/playground/worker/tinygo.js +34 -9
- package/dist/playground/worker/typescript.d.ts +1 -0
- package/dist/playground/worker/typescript.js +251 -0
- package/dist/playground/worker/wasm.d.ts +1 -0
- package/dist/playground/worker/wasm.js +273 -0
- package/dist/playground/worker/wat.d.ts +1 -0
- package/dist/playground/worker/wat.js +188 -0
- package/dist/playground/worker/zig.d.ts +8 -0
- package/dist/playground/worker/zig.js +578 -0
- package/dist/playground/workerProgress.d.ts +15 -0
- package/dist/playground/workerProgress.js +72 -0
- package/dist/playground/workerSession.d.ts +28 -0
- package/dist/playground/workerSession.js +164 -0
- package/dist/playground/zig.d.ts +46 -0
- package/dist/playground/zig.js +690 -0
- package/dist/playground/zigAssets.d.ts +6 -0
- package/dist/playground/zigAssets.js +49 -0
- package/package.json +234 -105
- package/dist/clang/app.d.ts +0 -75
- package/dist/clang/app.js +0 -744
- package/dist/clang/apply.d.ts +0 -2
- package/dist/clang/apply.js +0 -11
- package/dist/clang/color.d.ts +0 -3
- package/dist/clang/color.js +0 -3
- package/dist/clang/encode.d.ts +0 -4
- package/dist/clang/encode.js +0 -20
- package/dist/clang/error.d.ts +0 -14
- package/dist/clang/error.js +0 -27
- package/dist/clang/gccCompat.d.ts +0 -15
- package/dist/clang/gccCompat.js +0 -767
- package/dist/clang/index.d.ts +0 -85
- package/dist/clang/index.js +0 -1077
- package/dist/clang/memory/index.d.ts +0 -4
- package/dist/clang/memory/index.js +0 -4
- package/dist/clang/memory/memFs.d.ts +0 -35
- package/dist/clang/memory/memFs.js +0 -132
- package/dist/clang/memory/memory.d.ts +0 -22
- package/dist/clang/memory/memory.js +0 -73
- package/dist/clang/memory/tar.d.ts +0 -2
- package/dist/clang/memory/tar.js +0 -59
- package/dist/clang/url.d.ts +0 -4
- package/dist/clang/url.js +0 -5
- package/dist/clang/wasm.d.ts +0 -4
- package/dist/clang/wasm.js +0 -50
- package/dist/clangd/config.d.ts +0 -18
- package/dist/clangd/config.js +0 -14
- package/dist/clangd/jsonStream.d.ts +0 -9
- package/dist/clangd/jsonStream.js +0 -48
- package/dist/clangd/session.d.ts +0 -18
- package/dist/clangd/session.js +0 -112
- package/dist/clangd/worker.js +0 -131
- package/dist/debug/controller.svelte.d.ts +0 -43
- package/dist/debug/controller.svelte.js +0 -268
- package/dist/debug/editor/index.d.ts +0 -2
- package/dist/debug/editor/index.js +0 -1
- package/dist/debug/editor/monaco.d.ts +0 -26
- package/dist/debug/editor/monaco.js +0 -128
- package/dist/debug/expression.d.ts +0 -26
- package/dist/debug/expression.js +0 -547
- package/dist/debug/index.d.ts +0 -8
- package/dist/debug/index.js +0 -5
- package/dist/debug/language/cpp.d.ts +0 -2
- package/dist/debug/language/cpp.js +0 -7
- package/dist/debug/language/index.d.ts +0 -3
- package/dist/debug/language/index.js +0 -2
- package/dist/debug/language/python.d.ts +0 -2
- package/dist/debug/language/python.js +0 -14
- package/dist/debug/language/types.d.ts +0 -6
- package/dist/debug/locals.d.ts +0 -2
- package/dist/debug/locals.js +0 -8
- package/dist/lsp/cpp/server.d.ts +0 -2
- package/dist/lsp/cpp/server.js +0 -68
- package/dist/lsp/index.d.ts +0 -5
- package/dist/lsp/index.js +0 -4
- package/dist/lsp/python/main.worker.js +0 -92
- package/dist/lsp/python/package/arcturus_python_lsp/__init__.py +0 -3
- package/dist/lsp/python/package/arcturus_python_lsp/server.py +0 -462
- package/dist/lsp/python/server.d.ts +0 -2
- package/dist/lsp/python/server.js +0 -54
- package/dist/lsp/registry.d.ts +0 -2
- package/dist/lsp/registry.js +0 -12
- package/dist/lsp/runtime.d.ts +0 -5
- package/dist/lsp/runtime.js +0 -31
- package/dist/lsp/types.d.ts +0 -17
- package/dist/terminal/Terminal.svelte +0 -422
- package/dist/terminal/Terminal.svelte.d.ts +0 -26
- package/dist/terminal/index.d.ts +0 -7
- package/dist/terminal/index.js +0 -5
- package/dist/terminal/plugin/index.d.ts +0 -9
- package/dist/terminal/plugin/index.js +0 -19
- package/dist/terminal/theme.d.ts +0 -49
- package/dist/terminal/theme.js +0 -49
- package/dist/terminal/types.d.ts +0 -18
- package/dist/utils/vscodeJsonrpcBrowser.d.ts +0 -5
- package/dist/utils/vscodeJsonrpcBrowser.js +0 -4
- /package/dist/{clangd/worker.d.ts → playground/worker/assemblyscript.d.ts} +0 -0
- /package/dist/{debug/language/types.js → playground/worker/bash.d.ts} +0 -0
- /package/dist/{lsp/python/main.worker.d.ts → playground/worker/cobol.d.ts} +0 -0
- /package/dist/{lsp/types.js → playground/worker/d.d.ts} +0 -0
- /package/dist/{terminal/types.js → playground/worker/dotnet.d.ts} +0 -0
|
@@ -0,0 +1,1917 @@
|
|
|
1
|
+
import { AssetNotFoundError, AssetTooLargeError, BusyError, CancelledError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, ProtocolError, RuntimeConfigurationError, RuntimeExecutionError, RuntimeProgressController, RuntimeWorkerLifetimeController, TimeoutError, WorkerStartupError, isWasmIdleError, resolveExecutionLimits, validateExecutionWorkspace, verifyRuntimeAssetIntegrity } from '@wasm-idle/core';
|
|
2
|
+
import { resolveSandboxExecutionArgs } from './options';
|
|
3
|
+
import { StaticStdinRingHost } from './staticStdinRing';
|
|
4
|
+
import { deserializeStaticRuntimePreflightError, isStaticRuntimePreflightSerializedError, inspectStaticRuntimePreflightBytes } from './staticRuntimePreflightProtocol';
|
|
5
|
+
import { reportWorkerProgress } from './workerProgress';
|
|
6
|
+
const ownedDeliveryStateByTicket = new WeakMap();
|
|
7
|
+
const WORKER_READY_MESSAGE = '__wasmIdleStaticWorkerReady';
|
|
8
|
+
const outputEncoder = new TextEncoder();
|
|
9
|
+
function cancelWorkerScriptResponse(response, reason) {
|
|
10
|
+
try {
|
|
11
|
+
void Promise.resolve(response.body?.cancel(reason)).catch(() => undefined);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Preserve the boundary failure that caused cancellation.
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class StaticWorkerRuntimeSandbox {
|
|
18
|
+
config;
|
|
19
|
+
output = null;
|
|
20
|
+
worker = null;
|
|
21
|
+
pendingInput = [];
|
|
22
|
+
begin = 0;
|
|
23
|
+
elapse = 0;
|
|
24
|
+
uid = 0;
|
|
25
|
+
exit = true;
|
|
26
|
+
baseUrl = '';
|
|
27
|
+
workerUrl = '';
|
|
28
|
+
manifestUrl = '';
|
|
29
|
+
manifestFingerprint = '';
|
|
30
|
+
preflightKey = '';
|
|
31
|
+
workerReceipt = null;
|
|
32
|
+
activeReject = null;
|
|
33
|
+
oncompilerdiagnostic;
|
|
34
|
+
pendingEof = false;
|
|
35
|
+
stdinWaiters = [];
|
|
36
|
+
activeRun = null;
|
|
37
|
+
bootstrapUrl = '';
|
|
38
|
+
disposed = false;
|
|
39
|
+
disposePromise = null;
|
|
40
|
+
disposeReason = null;
|
|
41
|
+
lifecycleProgress;
|
|
42
|
+
progressController = new RuntimeProgressController();
|
|
43
|
+
progressUid = 0;
|
|
44
|
+
startingRunId = null;
|
|
45
|
+
startupReject = null;
|
|
46
|
+
workerLease = null;
|
|
47
|
+
workerLifetimeController;
|
|
48
|
+
workerStartAbortController = null;
|
|
49
|
+
workerGeneration = 0;
|
|
50
|
+
workerStartPromise = null;
|
|
51
|
+
resolvedRuntimeUrls = null;
|
|
52
|
+
workerRuntimePreflight = new WeakMap();
|
|
53
|
+
workerRuntimePreflightMaxAssetBytes = new WeakMap();
|
|
54
|
+
workersWithCachedRuntimePreflight = new WeakSet();
|
|
55
|
+
workerStartMaxAssetBytes = null;
|
|
56
|
+
constructor(config) {
|
|
57
|
+
this.config = config;
|
|
58
|
+
const workerLifetime = config.workerLifetime ?? { mode: 'per-run' };
|
|
59
|
+
if (this.usesOwnedRuntimePreflight() && !config.preflightRuntimeAssets) {
|
|
60
|
+
throw new RuntimeConfigurationError(`${config.displayName} transfer-owned runtime preflight requires a runtime preflight callback.`, { phase: 'startup', runtimeId: config.languageId });
|
|
61
|
+
}
|
|
62
|
+
if (config.runtimePreflightDelivery === 'transfer-owned' &&
|
|
63
|
+
workerLifetime.mode !== 'per-run') {
|
|
64
|
+
throw new RuntimeConfigurationError(`${config.displayName} transfer-owned runtime preflight requires per-run workers.`, { phase: 'startup', runtimeId: config.languageId });
|
|
65
|
+
}
|
|
66
|
+
if (config.runtimePreflightDelivery === 'transfer-owned-worker-cache' &&
|
|
67
|
+
workerLifetime.mode !== 'persistent') {
|
|
68
|
+
throw new RuntimeConfigurationError(`${config.displayName} transfer-owned worker-cache preflight requires persistent workers.`, { phase: 'startup', runtimeId: config.languageId });
|
|
69
|
+
}
|
|
70
|
+
this.workerLifetimeController = new RuntimeWorkerLifetimeController({
|
|
71
|
+
policy: workerLifetime,
|
|
72
|
+
runtimeId: config.languageId,
|
|
73
|
+
createWorker: (context) => {
|
|
74
|
+
context.generation = ++this.workerGeneration;
|
|
75
|
+
return this.startWorker(context.generation, context.progress, context.controls);
|
|
76
|
+
},
|
|
77
|
+
disposeWorker: (worker) => this.retireManagedWorker(worker)
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
usesOwnedRuntimePreflight() {
|
|
81
|
+
return (this.config.runtimePreflightDelivery === 'transfer-owned' ||
|
|
82
|
+
this.config.runtimePreflightDelivery === 'transfer-owned-worker-cache');
|
|
83
|
+
}
|
|
84
|
+
usesWorkerCachedRuntimePreflight() {
|
|
85
|
+
return this.config.runtimePreflightDelivery === 'transfer-owned-worker-cache';
|
|
86
|
+
}
|
|
87
|
+
resetWorkerForLowerAssetLimit(maxAssetBytes) {
|
|
88
|
+
if (!this.usesOwnedRuntimePreflight())
|
|
89
|
+
return;
|
|
90
|
+
const currentLimit = this.worker
|
|
91
|
+
? this.workerRuntimePreflightMaxAssetBytes.get(this.worker)
|
|
92
|
+
: this.workerStartPromise
|
|
93
|
+
? (this.workerStartMaxAssetBytes ?? undefined)
|
|
94
|
+
: undefined;
|
|
95
|
+
if (currentLimit !== undefined && maxAssetBytes < currentLimit) {
|
|
96
|
+
this.disposeWorker(new CancelledError(`${this.config.displayName} worker reset for a lower runtime asset limit`, { phase: 'asset', runtimeId: this.config.languageId }));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
getDisposeReason() {
|
|
100
|
+
if (!this.disposeReason) {
|
|
101
|
+
this.disposeReason = new CancelledError(`${this.config.displayName} runtime was disposed`, {
|
|
102
|
+
phase: 'dispose',
|
|
103
|
+
runtimeId: this.config.languageId
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return this.disposeReason;
|
|
107
|
+
}
|
|
108
|
+
assertNotDisposed() {
|
|
109
|
+
if (!this.disposed)
|
|
110
|
+
return;
|
|
111
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} runtime is disposed`, {
|
|
112
|
+
phase: 'dispose',
|
|
113
|
+
runtimeId: this.config.languageId
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
assertOperationNotDisposed() {
|
|
117
|
+
if (this.disposed)
|
|
118
|
+
throw this.getDisposeReason();
|
|
119
|
+
}
|
|
120
|
+
preserveDisposeReason(error) {
|
|
121
|
+
return this.disposed ? this.getDisposeReason() : error;
|
|
122
|
+
}
|
|
123
|
+
get stdinMode() {
|
|
124
|
+
if (this.config.stdin.mode !== 'streaming')
|
|
125
|
+
return this.config.stdin.mode;
|
|
126
|
+
const isolated = globalThis.crossOriginIsolated;
|
|
127
|
+
return typeof SharedArrayBuffer === 'function' &&
|
|
128
|
+
typeof Atomics === 'object' &&
|
|
129
|
+
typeof Atomics.notify === 'function' &&
|
|
130
|
+
(typeof isolated !== 'boolean' || isolated)
|
|
131
|
+
? 'streaming'
|
|
132
|
+
: 'prebuffered';
|
|
133
|
+
}
|
|
134
|
+
async load(runtimeAssets = '', code = '', _log = true, _args = [], options = {}, progress) {
|
|
135
|
+
this.assertNotDisposed();
|
|
136
|
+
try {
|
|
137
|
+
const controls = this.resolveExecutionControls(options);
|
|
138
|
+
this.assertOperationNotDisposed();
|
|
139
|
+
if (controls.signal?.aborted) {
|
|
140
|
+
throw new CancelledError(`${this.config.displayName} startup cancelled`, {
|
|
141
|
+
cause: controls.signal.reason,
|
|
142
|
+
phase: 'startup',
|
|
143
|
+
runtimeId: this.config.languageId
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
this.validateWorkspace(code, options, controls.limits);
|
|
147
|
+
this.assertOperationNotDisposed();
|
|
148
|
+
const progressSink = this.selectProgress(progress);
|
|
149
|
+
const currentUrl = typeof window !== 'undefined' ? window.location.href : '';
|
|
150
|
+
const urls = this.config.resolveRuntimeAssets(runtimeAssets, currentUrl);
|
|
151
|
+
this.assertOperationNotDisposed();
|
|
152
|
+
const nextManifestUrl = urls.manifestUrl || '';
|
|
153
|
+
const nextManifestFingerprint = urls.manifestFingerprint || '';
|
|
154
|
+
const nextPreflightKey = urls.preflightKey || '';
|
|
155
|
+
const nextWorkerReceipt = urls.workerReceipt
|
|
156
|
+
? Object.freeze({
|
|
157
|
+
bytes: urls.workerReceipt.bytes,
|
|
158
|
+
sha256: urls.workerReceipt.sha256
|
|
159
|
+
})
|
|
160
|
+
: null;
|
|
161
|
+
if (this.config.inlineVerifiedWorker &&
|
|
162
|
+
(!nextWorkerReceipt ||
|
|
163
|
+
!Number.isSafeInteger(nextWorkerReceipt.bytes) ||
|
|
164
|
+
nextWorkerReceipt.bytes <= 0 ||
|
|
165
|
+
!/^[a-f0-9]{64}$/u.test(nextWorkerReceipt.sha256))) {
|
|
166
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} requires a pinned worker receipt.`, { runtimeId: this.config.languageId });
|
|
167
|
+
}
|
|
168
|
+
const runtimeChanged = this.baseUrl !== urls.baseUrl ||
|
|
169
|
+
this.workerUrl !== urls.workerUrl ||
|
|
170
|
+
this.manifestUrl !== nextManifestUrl ||
|
|
171
|
+
this.manifestFingerprint !== nextManifestFingerprint ||
|
|
172
|
+
this.preflightKey !== nextPreflightKey ||
|
|
173
|
+
this.workerReceipt?.bytes !== nextWorkerReceipt?.bytes ||
|
|
174
|
+
this.workerReceipt?.sha256 !== nextWorkerReceipt?.sha256;
|
|
175
|
+
if (runtimeChanged &&
|
|
176
|
+
(this.worker ||
|
|
177
|
+
this.workerStartPromise ||
|
|
178
|
+
this.activeRun ||
|
|
179
|
+
this.startingRunId ||
|
|
180
|
+
this.workerLifetimeController.totalWorkers > 0)) {
|
|
181
|
+
this.terminate();
|
|
182
|
+
}
|
|
183
|
+
this.baseUrl = urls.baseUrl;
|
|
184
|
+
this.workerUrl = urls.workerUrl;
|
|
185
|
+
this.manifestUrl = nextManifestUrl;
|
|
186
|
+
this.manifestFingerprint = nextManifestFingerprint;
|
|
187
|
+
this.preflightKey = nextPreflightKey;
|
|
188
|
+
this.resolvedRuntimeUrls = urls;
|
|
189
|
+
this.workerReceipt = nextWorkerReceipt;
|
|
190
|
+
this.resetWorkerForLowerAssetLimit(controls.limits.maxAssetBytes);
|
|
191
|
+
if (!this.baseUrl || !this.workerUrl) {
|
|
192
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} runtime is not configured.`, { runtimeId: this.config.languageId });
|
|
193
|
+
}
|
|
194
|
+
if (!runtimeChanged &&
|
|
195
|
+
(this.workerStartPromise || this.workerLifetimeController.idleWorkers > 0)) {
|
|
196
|
+
await this.workerStartPromise;
|
|
197
|
+
this.assertOperationNotDisposed();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const lifecycle = this.beginProgressLifecycle(progressSink, `Resolving ${this.config.displayName} runtime`);
|
|
201
|
+
try {
|
|
202
|
+
this.assertOperationNotDisposed();
|
|
203
|
+
this.reportProgress(lifecycle.progress, 0.02, `Resolving ${this.config.displayName} runtime`);
|
|
204
|
+
this.assertOperationNotDisposed();
|
|
205
|
+
await this.ensureWorkerStarted(lifecycle.progress, controls);
|
|
206
|
+
this.assertOperationNotDisposed();
|
|
207
|
+
this.releasePreparedWorkerIfIdle();
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
lifecycle.end();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
throw this.preserveDisposeReason(error);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
write(input) {
|
|
218
|
+
if (this.disposed)
|
|
219
|
+
return;
|
|
220
|
+
const activeRun = this.activeRun;
|
|
221
|
+
if (activeRun?.stdinRing) {
|
|
222
|
+
try {
|
|
223
|
+
activeRun.stdinRing.enqueue(input);
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
this.rejectRun(activeRun.id, error);
|
|
227
|
+
}
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this.pendingInput.push(input);
|
|
231
|
+
}
|
|
232
|
+
eof() {
|
|
233
|
+
if (this.disposed)
|
|
234
|
+
return;
|
|
235
|
+
const activeRun = this.activeRun;
|
|
236
|
+
if (activeRun?.stdinRing) {
|
|
237
|
+
try {
|
|
238
|
+
activeRun.stdinRing.close();
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
this.rejectRun(activeRun.id, error);
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this.pendingEof = true;
|
|
246
|
+
this.resolveStdinWaiters();
|
|
247
|
+
}
|
|
248
|
+
resolveStdinWaiters() {
|
|
249
|
+
const waiters = this.stdinWaiters.splice(0);
|
|
250
|
+
for (const waiter of waiters)
|
|
251
|
+
waiter.resolve();
|
|
252
|
+
}
|
|
253
|
+
rejectStdinWaiters(reason) {
|
|
254
|
+
const waiters = this.stdinWaiters.splice(0);
|
|
255
|
+
for (const waiter of waiters)
|
|
256
|
+
waiter.reject(reason);
|
|
257
|
+
}
|
|
258
|
+
clearPendingStdin() {
|
|
259
|
+
this.pendingInput = [];
|
|
260
|
+
this.pendingEof = false;
|
|
261
|
+
}
|
|
262
|
+
sourceMayReadStdin(code) {
|
|
263
|
+
if (this.config.stdin.mode === 'none')
|
|
264
|
+
return false;
|
|
265
|
+
const pattern = this.config.stdin.sourceHintPattern;
|
|
266
|
+
pattern.lastIndex = 0;
|
|
267
|
+
return pattern.test(code);
|
|
268
|
+
}
|
|
269
|
+
async collectStdinForRun(code, options, activeRun) {
|
|
270
|
+
if (this.stdinMode === 'none') {
|
|
271
|
+
this.clearPendingStdin();
|
|
272
|
+
return { stdin: undefined, stdinEof: false };
|
|
273
|
+
}
|
|
274
|
+
if (typeof options.stdin === 'string') {
|
|
275
|
+
this.clearPendingStdin();
|
|
276
|
+
return { stdin: options.stdin, stdinEof: true };
|
|
277
|
+
}
|
|
278
|
+
if (!this.sourceMayReadStdin(code) && this.pendingInput.length === 0 && !this.pendingEof) {
|
|
279
|
+
return { stdin: undefined, stdinEof: false };
|
|
280
|
+
}
|
|
281
|
+
if (!this.pendingEof) {
|
|
282
|
+
this.reportRunReady(activeRun, 'waiting-input', 'stdin-request', `${this.config.displayName} runtime ready for input`);
|
|
283
|
+
if (this.activeRun !== activeRun) {
|
|
284
|
+
throw new CancelledError(`${this.config.displayName} run terminated`, {
|
|
285
|
+
phase: 'execute',
|
|
286
|
+
runtimeId: this.config.languageId
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
while (!this.pendingEof) {
|
|
291
|
+
await new Promise((resolve, reject) => {
|
|
292
|
+
this.stdinWaiters.push({ resolve, reject });
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const stdin = this.pendingInput.join('');
|
|
296
|
+
this.clearPendingStdin();
|
|
297
|
+
return { stdin, stdinEof: true };
|
|
298
|
+
}
|
|
299
|
+
selectProgress(progress) {
|
|
300
|
+
if (progress)
|
|
301
|
+
this.lifecycleProgress = progress;
|
|
302
|
+
return progress || this.lifecycleProgress;
|
|
303
|
+
}
|
|
304
|
+
beginProgressLifecycle(progress, stage) {
|
|
305
|
+
return this.progressController.begin(`${this.config.languageId.toLowerCase()}-${++this.progressUid}`, progress, stage);
|
|
306
|
+
}
|
|
307
|
+
resolveExecutionControls(options) {
|
|
308
|
+
return {
|
|
309
|
+
limits: resolveExecutionLimits(options.limits),
|
|
310
|
+
...(options.signal ? { signal: options.signal } : {})
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
validateWorkspace(code, options, limits) {
|
|
314
|
+
const workspaceLimits = {
|
|
315
|
+
...options.workspaceLimits,
|
|
316
|
+
maxFileBytes: Math.min(options.workspaceLimits?.maxFileBytes ?? DEFAULT_WORKSPACE_LIMITS.maxFileBytes, limits.maxWorkspaceBytes),
|
|
317
|
+
maxTotalBytes: Math.min(options.workspaceLimits?.maxTotalBytes ?? DEFAULT_WORKSPACE_LIMITS.maxTotalBytes, limits.maxWorkspaceBytes)
|
|
318
|
+
};
|
|
319
|
+
return validateExecutionWorkspace(code, options.workspaceFiles ?? [], options.activePath ?? this.config.defaultActivePath, workspaceLimits);
|
|
320
|
+
}
|
|
321
|
+
reportProgress(progress, value, stage) {
|
|
322
|
+
if (!progress)
|
|
323
|
+
return;
|
|
324
|
+
const clamped = Number.isFinite(value) ? Math.max(0, Math.min(value, 1)) : 0;
|
|
325
|
+
progress.set?.(clamped, stage);
|
|
326
|
+
}
|
|
327
|
+
reportRunReady(activeRun, state, reason, label) {
|
|
328
|
+
if (activeRun.readyReported || activeRun.settledReported)
|
|
329
|
+
return;
|
|
330
|
+
reportWorkerProgress(activeRun.progress, { kind: 'ready', state, reason, label });
|
|
331
|
+
activeRun.readyReported = true;
|
|
332
|
+
}
|
|
333
|
+
reportRunSettled(activeRun, outcome, label) {
|
|
334
|
+
if (activeRun.settledReported)
|
|
335
|
+
return;
|
|
336
|
+
activeRun.settledReported = true;
|
|
337
|
+
reportWorkerProgress(activeRun.progress, { kind: 'settled', outcome, label });
|
|
338
|
+
}
|
|
339
|
+
async verifyWorkerReceipt(bytes, receipt, signal) {
|
|
340
|
+
const abortReason = () => signal.reason ?? new DOMException('Worker script verification aborted', 'AbortError');
|
|
341
|
+
let onAbort;
|
|
342
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
343
|
+
onAbort = () => reject(abortReason());
|
|
344
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
345
|
+
});
|
|
346
|
+
try {
|
|
347
|
+
if (signal.aborted)
|
|
348
|
+
throw abortReason();
|
|
349
|
+
await Promise.race([
|
|
350
|
+
verifyRuntimeAssetIntegrity({
|
|
351
|
+
asset: this.workerUrl,
|
|
352
|
+
bytes,
|
|
353
|
+
expected: receipt,
|
|
354
|
+
runtimeId: this.config.languageId
|
|
355
|
+
}),
|
|
356
|
+
aborted
|
|
357
|
+
]);
|
|
358
|
+
if (signal.aborted)
|
|
359
|
+
throw abortReason();
|
|
360
|
+
}
|
|
361
|
+
finally {
|
|
362
|
+
if (onAbort)
|
|
363
|
+
signal.removeEventListener('abort', onAbort);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async preloadWorkerScript(progress, controls) {
|
|
367
|
+
const { limits, signal } = controls;
|
|
368
|
+
const workerReceipt = this.config.inlineVerifiedWorker ? this.workerReceipt : null;
|
|
369
|
+
const workerByteLimit = workerReceipt?.bytes ?? limits.maxAssetBytes;
|
|
370
|
+
if (workerReceipt && workerReceipt.bytes > limits.maxAssetBytes) {
|
|
371
|
+
throw new AssetTooLargeError(`${this.config.displayName} worker script exceeds ${limits.maxAssetBytes} bytes`, {
|
|
372
|
+
actual: workerReceipt.bytes,
|
|
373
|
+
limit: limits.maxAssetBytes,
|
|
374
|
+
runtimeId: this.config.languageId
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
if (signal?.aborted) {
|
|
378
|
+
throw new CancelledError(`${this.config.displayName} worker download cancelled`, {
|
|
379
|
+
cause: signal.reason,
|
|
380
|
+
phase: 'asset',
|
|
381
|
+
runtimeId: this.config.languageId
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
let workerRequestUrl;
|
|
385
|
+
try {
|
|
386
|
+
workerRequestUrl =
|
|
387
|
+
typeof window === 'undefined'
|
|
388
|
+
? new URL(this.workerUrl)
|
|
389
|
+
: new URL(this.workerUrl, window.location.href);
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} worker script URL is invalid`, { runtimeId: this.config.languageId });
|
|
393
|
+
}
|
|
394
|
+
if (workerRequestUrl.protocol !== 'https:' && workerRequestUrl.protocol !== 'http:') {
|
|
395
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} worker script URL must use HTTP(S)`, { runtimeId: this.config.languageId });
|
|
396
|
+
}
|
|
397
|
+
if (workerRequestUrl.username || workerRequestUrl.password) {
|
|
398
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} worker script URL must not include credentials`, { runtimeId: this.config.languageId });
|
|
399
|
+
}
|
|
400
|
+
if (workerRequestUrl.hash) {
|
|
401
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} worker script URL must not include a fragment`, { runtimeId: this.config.languageId });
|
|
402
|
+
}
|
|
403
|
+
const phaseController = new AbortController();
|
|
404
|
+
let timedOut = false;
|
|
405
|
+
const onAbort = () => phaseController.abort(signal?.reason);
|
|
406
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
407
|
+
const timeout = setTimeout(() => {
|
|
408
|
+
timedOut = true;
|
|
409
|
+
phaseController.abort();
|
|
410
|
+
}, limits.assetTimeoutMs);
|
|
411
|
+
try {
|
|
412
|
+
this.reportProgress(progress, 0.05, `Loading ${this.config.displayName} worker script`);
|
|
413
|
+
if (phaseController.signal.aborted) {
|
|
414
|
+
throw (phaseController.signal.reason ??
|
|
415
|
+
new DOMException('Worker script fetch aborted', 'AbortError'));
|
|
416
|
+
}
|
|
417
|
+
const pendingResponse = Promise.resolve(fetch(workerRequestUrl.href, {
|
|
418
|
+
cache: this.config.inlineVerifiedWorker ? 'no-store' : 'force-cache',
|
|
419
|
+
credentials: 'omit',
|
|
420
|
+
redirect: 'error',
|
|
421
|
+
referrerPolicy: 'no-referrer',
|
|
422
|
+
signal: phaseController.signal
|
|
423
|
+
}));
|
|
424
|
+
const response = await new Promise((resolve, reject) => {
|
|
425
|
+
let settled = false;
|
|
426
|
+
const onPhaseAbort = () => {
|
|
427
|
+
if (settled)
|
|
428
|
+
return;
|
|
429
|
+
settled = true;
|
|
430
|
+
phaseController.signal.removeEventListener('abort', onPhaseAbort);
|
|
431
|
+
reject(phaseController.signal.reason ??
|
|
432
|
+
new DOMException('Worker script fetch aborted', 'AbortError'));
|
|
433
|
+
};
|
|
434
|
+
phaseController.signal.addEventListener('abort', onPhaseAbort, { once: true });
|
|
435
|
+
void pendingResponse.then((candidate) => {
|
|
436
|
+
if (settled) {
|
|
437
|
+
const reason = phaseController.signal.reason ??
|
|
438
|
+
new DOMException('Worker script fetch aborted', 'AbortError');
|
|
439
|
+
cancelWorkerScriptResponse(candidate, reason);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
settled = true;
|
|
443
|
+
phaseController.signal.removeEventListener('abort', onPhaseAbort);
|
|
444
|
+
resolve(candidate);
|
|
445
|
+
}, (error) => {
|
|
446
|
+
if (settled)
|
|
447
|
+
return;
|
|
448
|
+
settled = true;
|
|
449
|
+
phaseController.signal.removeEventListener('abort', onPhaseAbort);
|
|
450
|
+
reject(error);
|
|
451
|
+
});
|
|
452
|
+
if (phaseController.signal.aborted)
|
|
453
|
+
onPhaseAbort();
|
|
454
|
+
});
|
|
455
|
+
if (!response.url && this.config.requireExactWorkerResponseUrl) {
|
|
456
|
+
const error = new ProtocolError(`${this.config.displayName} worker script response did not expose an exact final URL`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
457
|
+
cancelWorkerScriptResponse(response, error);
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
if (response.url) {
|
|
461
|
+
let finalResponseUrl;
|
|
462
|
+
try {
|
|
463
|
+
finalResponseUrl = new URL(response.url).href;
|
|
464
|
+
}
|
|
465
|
+
catch {
|
|
466
|
+
const error = new ProtocolError(`${this.config.displayName} worker script returned an invalid final URL: ${response.url}`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
467
|
+
cancelWorkerScriptResponse(response, error);
|
|
468
|
+
throw error;
|
|
469
|
+
}
|
|
470
|
+
if (finalResponseUrl !== workerRequestUrl.href) {
|
|
471
|
+
const error = new ProtocolError(`${this.config.displayName} worker script response URL mismatch: expected ${workerRequestUrl.href}, received ${finalResponseUrl}`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
472
|
+
cancelWorkerScriptResponse(response, error);
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (!response.ok) {
|
|
477
|
+
const error = new AssetNotFoundError(`${this.config.displayName} worker script failed to load: HTTP ${response.status}`, { runtimeId: this.config.languageId });
|
|
478
|
+
cancelWorkerScriptResponse(response, error);
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
481
|
+
const rawContentLength = response.headers.get('content-length');
|
|
482
|
+
let total = 0;
|
|
483
|
+
if (rawContentLength !== null) {
|
|
484
|
+
const normalizedContentLength = rawContentLength.trim();
|
|
485
|
+
const declaredLength = Number(normalizedContentLength);
|
|
486
|
+
if (!/^\d+$/u.test(normalizedContentLength) ||
|
|
487
|
+
!Number.isSafeInteger(declaredLength)) {
|
|
488
|
+
const error = new ProtocolError(`${this.config.displayName} worker script has an invalid Content-Length: ${rawContentLength}`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
489
|
+
cancelWorkerScriptResponse(response, error);
|
|
490
|
+
throw error;
|
|
491
|
+
}
|
|
492
|
+
total = declaredLength;
|
|
493
|
+
}
|
|
494
|
+
if (total > workerByteLimit) {
|
|
495
|
+
const error = new AssetTooLargeError(`${this.config.displayName} worker script exceeds ${workerByteLimit} bytes`, {
|
|
496
|
+
actual: total,
|
|
497
|
+
limit: workerByteLimit,
|
|
498
|
+
runtimeId: this.config.languageId
|
|
499
|
+
});
|
|
500
|
+
cancelWorkerScriptResponse(response, error);
|
|
501
|
+
throw error;
|
|
502
|
+
}
|
|
503
|
+
if (!response.body) {
|
|
504
|
+
let cancelOnAbort;
|
|
505
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
506
|
+
cancelOnAbort = () => reject(phaseController.signal.reason ??
|
|
507
|
+
new DOMException('Worker script read aborted', 'AbortError'));
|
|
508
|
+
phaseController.signal.addEventListener('abort', cancelOnAbort, {
|
|
509
|
+
once: true
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
try {
|
|
513
|
+
if (phaseController.signal.aborted) {
|
|
514
|
+
throw (phaseController.signal.reason ??
|
|
515
|
+
new DOMException('Worker script read aborted', 'AbortError'));
|
|
516
|
+
}
|
|
517
|
+
const materialized = response.arrayBuffer();
|
|
518
|
+
const buffer = await Promise.race([materialized, aborted]);
|
|
519
|
+
if (phaseController.signal.aborted) {
|
|
520
|
+
throw (phaseController.signal.reason ??
|
|
521
|
+
new DOMException('Worker script read aborted', 'AbortError'));
|
|
522
|
+
}
|
|
523
|
+
if (buffer.byteLength > workerByteLimit) {
|
|
524
|
+
throw new AssetTooLargeError(`${this.config.displayName} worker script exceeds ${workerByteLimit} bytes`, {
|
|
525
|
+
actual: buffer.byteLength,
|
|
526
|
+
limit: workerByteLimit,
|
|
527
|
+
runtimeId: this.config.languageId
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
this.reportProgress(progress, 0.2, `${this.config.displayName} worker downloaded`);
|
|
531
|
+
const bytes = new Uint8Array(buffer);
|
|
532
|
+
if (workerReceipt) {
|
|
533
|
+
await this.verifyWorkerReceipt(bytes, workerReceipt, phaseController.signal);
|
|
534
|
+
return bytes;
|
|
535
|
+
}
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
finally {
|
|
539
|
+
if (cancelOnAbort) {
|
|
540
|
+
phaseController.signal.removeEventListener('abort', cancelOnAbort);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
const reader = response.body.getReader();
|
|
545
|
+
const chunks = [];
|
|
546
|
+
let readerCancelled = false;
|
|
547
|
+
let cancelOnAbort;
|
|
548
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
549
|
+
cancelOnAbort = () => {
|
|
550
|
+
const reason = phaseController.signal.reason ??
|
|
551
|
+
new DOMException('Worker script read aborted', 'AbortError');
|
|
552
|
+
if (!readerCancelled) {
|
|
553
|
+
readerCancelled = true;
|
|
554
|
+
try {
|
|
555
|
+
void reader.cancel(reason).catch(() => undefined);
|
|
556
|
+
}
|
|
557
|
+
catch { }
|
|
558
|
+
}
|
|
559
|
+
reject(reason);
|
|
560
|
+
};
|
|
561
|
+
phaseController.signal.addEventListener('abort', cancelOnAbort, { once: true });
|
|
562
|
+
});
|
|
563
|
+
let loaded = 0;
|
|
564
|
+
let releaseError;
|
|
565
|
+
try {
|
|
566
|
+
while (true) {
|
|
567
|
+
if (phaseController.signal.aborted) {
|
|
568
|
+
throw (phaseController.signal.reason ??
|
|
569
|
+
new DOMException('Worker script read aborted', 'AbortError'));
|
|
570
|
+
}
|
|
571
|
+
const pendingRead = reader.read();
|
|
572
|
+
const { done, value } = await Promise.race([pendingRead, aborted]);
|
|
573
|
+
if (phaseController.signal.aborted) {
|
|
574
|
+
throw (phaseController.signal.reason ??
|
|
575
|
+
new DOMException('Worker script read aborted', 'AbortError'));
|
|
576
|
+
}
|
|
577
|
+
if (done)
|
|
578
|
+
break;
|
|
579
|
+
loaded += value.byteLength;
|
|
580
|
+
if (loaded > workerByteLimit) {
|
|
581
|
+
const error = new AssetTooLargeError(`${this.config.displayName} worker script exceeds ${workerByteLimit} bytes`, {
|
|
582
|
+
actual: loaded,
|
|
583
|
+
limit: workerByteLimit,
|
|
584
|
+
runtimeId: this.config.languageId
|
|
585
|
+
});
|
|
586
|
+
readerCancelled = true;
|
|
587
|
+
try {
|
|
588
|
+
void reader.cancel(error).catch(() => undefined);
|
|
589
|
+
}
|
|
590
|
+
catch { }
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
593
|
+
if (workerReceipt)
|
|
594
|
+
chunks.push(value.slice());
|
|
595
|
+
const ratio = total > 0 ? Math.min(loaded / total, 1) : 0.5;
|
|
596
|
+
this.reportProgress(progress, 0.05 + ratio * 0.15, `Loading ${this.config.displayName} worker script`);
|
|
597
|
+
}
|
|
598
|
+
this.reportProgress(progress, 0.2, `${this.config.displayName} worker downloaded`);
|
|
599
|
+
}
|
|
600
|
+
catch (error) {
|
|
601
|
+
if (phaseController.signal.aborted) {
|
|
602
|
+
const reason = phaseController.signal.reason ??
|
|
603
|
+
new DOMException('Worker script read aborted', 'AbortError');
|
|
604
|
+
if (!readerCancelled) {
|
|
605
|
+
readerCancelled = true;
|
|
606
|
+
try {
|
|
607
|
+
void reader.cancel(reason).catch(() => undefined);
|
|
608
|
+
}
|
|
609
|
+
catch { }
|
|
610
|
+
}
|
|
611
|
+
throw reason;
|
|
612
|
+
}
|
|
613
|
+
if (!readerCancelled) {
|
|
614
|
+
readerCancelled = true;
|
|
615
|
+
try {
|
|
616
|
+
void reader.cancel(error).catch(() => undefined);
|
|
617
|
+
}
|
|
618
|
+
catch { }
|
|
619
|
+
}
|
|
620
|
+
throw error;
|
|
621
|
+
}
|
|
622
|
+
finally {
|
|
623
|
+
if (cancelOnAbort) {
|
|
624
|
+
phaseController.signal.removeEventListener('abort', cancelOnAbort);
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
reader.releaseLock();
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
if (!phaseController.signal.aborted)
|
|
631
|
+
releaseError = error;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (releaseError)
|
|
635
|
+
throw releaseError;
|
|
636
|
+
if (workerReceipt) {
|
|
637
|
+
const bytes = new Uint8Array(loaded);
|
|
638
|
+
let offset = 0;
|
|
639
|
+
for (const chunk of chunks) {
|
|
640
|
+
bytes.set(chunk, offset);
|
|
641
|
+
offset += chunk.byteLength;
|
|
642
|
+
}
|
|
643
|
+
await this.verifyWorkerReceipt(bytes, workerReceipt, phaseController.signal);
|
|
644
|
+
return bytes;
|
|
645
|
+
}
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
catch (error) {
|
|
649
|
+
if (this.disposed)
|
|
650
|
+
throw this.getDisposeReason();
|
|
651
|
+
if (isWasmIdleError(error))
|
|
652
|
+
throw error;
|
|
653
|
+
if (timedOut) {
|
|
654
|
+
throw new TimeoutError(`${this.config.displayName} worker download timed out after ${limits.assetTimeoutMs} ms`, {
|
|
655
|
+
phase: 'asset',
|
|
656
|
+
runtimeId: this.config.languageId,
|
|
657
|
+
timeoutMs: limits.assetTimeoutMs
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
if (signal?.aborted) {
|
|
661
|
+
throw new CancelledError(`${this.config.displayName} worker download cancelled`, {
|
|
662
|
+
cause: signal.reason,
|
|
663
|
+
phase: 'asset',
|
|
664
|
+
runtimeId: this.config.languageId
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
throw new Error(`${this.config.displayName} worker script failed to load: ${this.errorMessage(error)}`);
|
|
668
|
+
}
|
|
669
|
+
finally {
|
|
670
|
+
clearTimeout(timeout);
|
|
671
|
+
try {
|
|
672
|
+
signal?.removeEventListener('abort', onAbort);
|
|
673
|
+
}
|
|
674
|
+
catch {
|
|
675
|
+
// Caller-owned signal cleanup must not replace the download result.
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
createBootstrapUrl(verifiedWorkerBytes) {
|
|
680
|
+
if (typeof Blob !== 'function' ||
|
|
681
|
+
typeof URL?.createObjectURL !== 'function' ||
|
|
682
|
+
typeof URL?.revokeObjectURL !== 'function') {
|
|
683
|
+
throw new Error(`${this.config.displayName} worker bootstrap is unavailable.`);
|
|
684
|
+
}
|
|
685
|
+
let verifiedWorkerSource;
|
|
686
|
+
if (this.config.inlineVerifiedWorker) {
|
|
687
|
+
if (!verifiedWorkerBytes) {
|
|
688
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} verified worker bytes are unavailable.`, { runtimeId: this.config.languageId });
|
|
689
|
+
}
|
|
690
|
+
try {
|
|
691
|
+
new TextDecoder('utf-8', { fatal: true }).decode(verifiedWorkerBytes);
|
|
692
|
+
}
|
|
693
|
+
catch (error) {
|
|
694
|
+
throw new ProtocolError(`${this.config.displayName} worker script is not valid UTF-8`, {
|
|
695
|
+
cause: error,
|
|
696
|
+
phase: 'asset',
|
|
697
|
+
runtimeId: this.config.languageId
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
verifiedWorkerSource = verifiedWorkerBytes;
|
|
701
|
+
}
|
|
702
|
+
const prefix = `const __wasmIdleNativePostMessage = self.postMessage.bind(self);
|
|
703
|
+
let __wasmIdleRunId = null;
|
|
704
|
+
const __wasmIdleExecutionKeys = ['output', 'results', 'error', 'diagnostic', 'progress', 'evidence'];
|
|
705
|
+
self.addEventListener('message', (event) => {
|
|
706
|
+
const message = event.data;
|
|
707
|
+
const runId = message?.runId;
|
|
708
|
+
if (message?.run !== true || typeof runId !== 'string') return;
|
|
709
|
+
if (__wasmIdleRunId !== null) {
|
|
710
|
+
event.stopImmediatePropagation();
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
__wasmIdleRunId = runId;
|
|
714
|
+
}, { capture: true });
|
|
715
|
+
self.postMessage = (message, transferOrOptions) => {
|
|
716
|
+
const executionMessage = __wasmIdleRunId !== null &&
|
|
717
|
+
message !== null && typeof message === 'object' &&
|
|
718
|
+
(__wasmIdleExecutionKeys.some((key) => Object.prototype.hasOwnProperty.call(message, key)) ||
|
|
719
|
+
message.type === 'stdin-request' || message.type === 'execution-ready');
|
|
720
|
+
const correlated = executionMessage
|
|
721
|
+
? Object.assign({}, message, { runId: __wasmIdleRunId })
|
|
722
|
+
: message;
|
|
723
|
+
const terminalRunId = executionMessage &&
|
|
724
|
+
(Object.prototype.hasOwnProperty.call(message, 'results') ||
|
|
725
|
+
Object.prototype.hasOwnProperty.call(message, 'error'))
|
|
726
|
+
? __wasmIdleRunId
|
|
727
|
+
: null;
|
|
728
|
+
try {
|
|
729
|
+
return transferOrOptions === undefined
|
|
730
|
+
? __wasmIdleNativePostMessage(correlated)
|
|
731
|
+
: __wasmIdleNativePostMessage(correlated, transferOrOptions);
|
|
732
|
+
} finally {
|
|
733
|
+
if (terminalRunId !== null && __wasmIdleRunId === terminalRunId) __wasmIdleRunId = null;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
`;
|
|
737
|
+
const suffix = `__wasmIdleNativePostMessage({ ${JSON.stringify(WORKER_READY_MESSAGE)}: true });\n`;
|
|
738
|
+
const workerSource = this.config.moduleWorker
|
|
739
|
+
? `await import(${JSON.stringify(this.workerUrl)});`
|
|
740
|
+
: `importScripts(${JSON.stringify(this.workerUrl)});`;
|
|
741
|
+
const parts = verifiedWorkerSource
|
|
742
|
+
? [
|
|
743
|
+
prefix,
|
|
744
|
+
'\n/* wasm-idle verified worker source */\n',
|
|
745
|
+
verifiedWorkerSource,
|
|
746
|
+
'\n',
|
|
747
|
+
suffix
|
|
748
|
+
]
|
|
749
|
+
: [prefix, workerSource, '\n', suffix];
|
|
750
|
+
return URL.createObjectURL(new Blob(parts, { type: 'text/javascript' }));
|
|
751
|
+
}
|
|
752
|
+
revokeBootstrapUrl() {
|
|
753
|
+
if (!this.bootstrapUrl)
|
|
754
|
+
return;
|
|
755
|
+
const bootstrapUrl = this.bootstrapUrl;
|
|
756
|
+
this.bootstrapUrl = '';
|
|
757
|
+
try {
|
|
758
|
+
URL.revokeObjectURL(bootstrapUrl);
|
|
759
|
+
}
|
|
760
|
+
catch {
|
|
761
|
+
// The bootstrap is already detached; cleanup must not replace the lifecycle result.
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
ensureWorkerStarted(progress, controls) {
|
|
765
|
+
this.assertOperationNotDisposed();
|
|
766
|
+
if (this.workerStartPromise)
|
|
767
|
+
return this.workerStartPromise;
|
|
768
|
+
const startAbortController = new AbortController();
|
|
769
|
+
let generation = this.workerGeneration;
|
|
770
|
+
this.workerStartMaxAssetBytes = this.usesOwnedRuntimePreflight()
|
|
771
|
+
? controls.limits.maxAssetBytes
|
|
772
|
+
: null;
|
|
773
|
+
this.workerStartAbortController = startAbortController;
|
|
774
|
+
const callerSignal = controls.signal;
|
|
775
|
+
let callerListenerAttempted = false;
|
|
776
|
+
const ownsStartup = () => !this.disposed &&
|
|
777
|
+
this.workerGeneration === generation &&
|
|
778
|
+
this.workerStartAbortController === startAbortController;
|
|
779
|
+
const invalidatedReason = () => {
|
|
780
|
+
if (this.disposed)
|
|
781
|
+
return this.getDisposeReason();
|
|
782
|
+
if (startAbortController.signal.aborted) {
|
|
783
|
+
let cause;
|
|
784
|
+
try {
|
|
785
|
+
cause = startAbortController.signal.reason;
|
|
786
|
+
}
|
|
787
|
+
catch (error) {
|
|
788
|
+
cause = error;
|
|
789
|
+
}
|
|
790
|
+
return new CancelledError(`${this.config.displayName} worker startup cancelled`, {
|
|
791
|
+
cause,
|
|
792
|
+
phase: 'startup',
|
|
793
|
+
runtimeId: this.config.languageId
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
return new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
797
|
+
phase: 'startup',
|
|
798
|
+
runtimeId: this.config.languageId
|
|
799
|
+
});
|
|
800
|
+
};
|
|
801
|
+
const abortStartup = (reason) => {
|
|
802
|
+
if (startAbortController.signal.aborted)
|
|
803
|
+
return;
|
|
804
|
+
try {
|
|
805
|
+
startAbortController.abort(reason);
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
// Reservation state is detached separately; abort is best effort.
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
const forwardAbort = () => {
|
|
812
|
+
if (startAbortController.signal.aborted)
|
|
813
|
+
return;
|
|
814
|
+
let reason;
|
|
815
|
+
try {
|
|
816
|
+
reason = callerSignal?.reason;
|
|
817
|
+
}
|
|
818
|
+
catch (error) {
|
|
819
|
+
reason = error;
|
|
820
|
+
}
|
|
821
|
+
if (!ownsStartup() || startAbortController.signal.aborted)
|
|
822
|
+
return;
|
|
823
|
+
abortStartup(reason);
|
|
824
|
+
};
|
|
825
|
+
const cleanupStartSignal = () => {
|
|
826
|
+
if (callerListenerAttempted) {
|
|
827
|
+
try {
|
|
828
|
+
callerSignal?.removeEventListener('abort', forwardAbort);
|
|
829
|
+
}
|
|
830
|
+
catch {
|
|
831
|
+
// Caller-owned signal cleanup must not replace startup settlement.
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
if (this.workerStartAbortController === startAbortController) {
|
|
835
|
+
this.workerStartAbortController = null;
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
let startPromise;
|
|
839
|
+
try {
|
|
840
|
+
if (callerSignal) {
|
|
841
|
+
callerListenerAttempted = true;
|
|
842
|
+
callerSignal.addEventListener('abort', forwardAbort, { once: true });
|
|
843
|
+
if (!ownsStartup())
|
|
844
|
+
throw invalidatedReason();
|
|
845
|
+
const callerAborted = callerSignal.aborted;
|
|
846
|
+
if (!ownsStartup())
|
|
847
|
+
throw invalidatedReason();
|
|
848
|
+
if (callerAborted)
|
|
849
|
+
forwardAbort();
|
|
850
|
+
if (!ownsStartup() || startAbortController.signal.aborted) {
|
|
851
|
+
throw invalidatedReason();
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
const creationContext = {
|
|
855
|
+
generation,
|
|
856
|
+
progress,
|
|
857
|
+
controls: {
|
|
858
|
+
limits: controls.limits,
|
|
859
|
+
signal: startAbortController.signal
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
const acquisition = this.workerLifetimeController.acquire(creationContext);
|
|
863
|
+
generation = creationContext.generation;
|
|
864
|
+
startPromise = acquisition.then((lease) => {
|
|
865
|
+
if (!ownsStartup() || startAbortController.signal.aborted) {
|
|
866
|
+
lease.release({ reusable: false });
|
|
867
|
+
throw invalidatedReason();
|
|
868
|
+
}
|
|
869
|
+
if (this.worker && this.worker !== lease.worker) {
|
|
870
|
+
lease.release({ reusable: false });
|
|
871
|
+
throw new CancelledError(`${this.config.displayName} worker startup was superseded`, { phase: 'startup', runtimeId: this.config.languageId });
|
|
872
|
+
}
|
|
873
|
+
this.worker = lease.worker;
|
|
874
|
+
this.workerLease = lease;
|
|
875
|
+
return lease.worker;
|
|
876
|
+
});
|
|
877
|
+
if (!ownsStartup()) {
|
|
878
|
+
void startPromise.catch(() => undefined);
|
|
879
|
+
throw invalidatedReason();
|
|
880
|
+
}
|
|
881
|
+
this.workerStartPromise = startPromise;
|
|
882
|
+
}
|
|
883
|
+
catch (error) {
|
|
884
|
+
const reason = ownsStartup() ? this.preserveDisposeReason(error) : invalidatedReason();
|
|
885
|
+
abortStartup(reason);
|
|
886
|
+
cleanupStartSignal();
|
|
887
|
+
throw reason;
|
|
888
|
+
}
|
|
889
|
+
void startPromise.then(() => cleanupStartSignal(), (error) => {
|
|
890
|
+
cleanupStartSignal();
|
|
891
|
+
if (this.workerStartPromise === startPromise)
|
|
892
|
+
this.disposeWorker(error);
|
|
893
|
+
});
|
|
894
|
+
return startPromise;
|
|
895
|
+
}
|
|
896
|
+
async startWorker(generation, progress, controls) {
|
|
897
|
+
let runtimePreflight;
|
|
898
|
+
if (this.config.preflightRuntimeAssets) {
|
|
899
|
+
const urls = this.resolvedRuntimeUrls;
|
|
900
|
+
if (!urls) {
|
|
901
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} runtime preflight configuration is unavailable.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
902
|
+
}
|
|
903
|
+
const preflightKey = urls.preflightKey || urls.manifestFingerprint || '';
|
|
904
|
+
if (this.usesOwnedRuntimePreflight() && preflightKey.length === 0) {
|
|
905
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} transfer-owned runtime preflight requires a stable preflight key.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
906
|
+
}
|
|
907
|
+
const operation = {
|
|
908
|
+
active: true,
|
|
909
|
+
claimedTicket: null,
|
|
910
|
+
generation,
|
|
911
|
+
owner: this,
|
|
912
|
+
preflightKey,
|
|
913
|
+
runtimeId: this.config.languageId
|
|
914
|
+
};
|
|
915
|
+
this.reportProgress(progress, 0.03, `Preflighting ${this.config.displayName} runtime`);
|
|
916
|
+
try {
|
|
917
|
+
runtimePreflight = await this.config.preflightRuntimeAssets(urls, {
|
|
918
|
+
limits: controls.limits,
|
|
919
|
+
signal: controls.signal,
|
|
920
|
+
reportProgress: (value, stage) => this.reportProgress(progress, value, stage),
|
|
921
|
+
createOwnedDelivery: (payload) => {
|
|
922
|
+
const currentUrls = this.resolvedRuntimeUrls;
|
|
923
|
+
const currentPreflightKey = currentUrls?.preflightKey || currentUrls?.manifestFingerprint || '';
|
|
924
|
+
if (!operation.active ||
|
|
925
|
+
this.disposed ||
|
|
926
|
+
controls.signal?.aborted ||
|
|
927
|
+
generation !== this.workerGeneration ||
|
|
928
|
+
currentPreflightKey !== operation.preflightKey) {
|
|
929
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} runtime preflight delivery operation is stale.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
930
|
+
}
|
|
931
|
+
if (operation.claimedTicket) {
|
|
932
|
+
this.retireRuntimePreflight(operation.claimedTicket);
|
|
933
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} runtime preflight may create only one owned delivery.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
934
|
+
}
|
|
935
|
+
const prototype = payload !== null && typeof payload === 'object'
|
|
936
|
+
? Object.getPrototypeOf(payload)
|
|
937
|
+
: undefined;
|
|
938
|
+
if (payload === null ||
|
|
939
|
+
typeof payload !== 'object' ||
|
|
940
|
+
Array.isArray(payload) ||
|
|
941
|
+
(prototype !== Object.prototype && prototype !== null) ||
|
|
942
|
+
!Object.isFrozen(payload)) {
|
|
943
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} owned runtime preflight payload must be a frozen plain object.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
944
|
+
}
|
|
945
|
+
const transferables = [];
|
|
946
|
+
const seenBuffers = new Set();
|
|
947
|
+
for (const key of Reflect.ownKeys(payload)) {
|
|
948
|
+
if (typeof key !== 'string') {
|
|
949
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} owned runtime preflight payload cannot contain symbol keys.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
950
|
+
}
|
|
951
|
+
const descriptor = Object.getOwnPropertyDescriptor(payload, key);
|
|
952
|
+
if (!descriptor || !descriptor.enumerable || !('value' in descriptor)) {
|
|
953
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} owned runtime preflight payload properties must be enumerable data properties.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
954
|
+
}
|
|
955
|
+
const value = descriptor.value;
|
|
956
|
+
const bytes = inspectStaticRuntimePreflightBytes(value);
|
|
957
|
+
if (bytes) {
|
|
958
|
+
if (bytes.byteLength === 0 ||
|
|
959
|
+
bytes.byteOffset !== 0 ||
|
|
960
|
+
bytes.byteLength !== bytes.bufferByteLength) {
|
|
961
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} owned runtime preflight bytes must span non-empty whole ArrayBuffers.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
962
|
+
}
|
|
963
|
+
if (seenBuffers.has(bytes.buffer)) {
|
|
964
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} owned runtime preflight ArrayBuffers must be unique.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
965
|
+
}
|
|
966
|
+
seenBuffers.add(bytes.buffer);
|
|
967
|
+
transferables.push(bytes.buffer);
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
if (value !== null &&
|
|
971
|
+
(typeof value === 'object' ||
|
|
972
|
+
typeof value === 'function' ||
|
|
973
|
+
typeof value === 'symbol')) {
|
|
974
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} owned runtime preflight payload accepts only primitive scalars and Uint8Arrays.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (transferables.length === 0) {
|
|
978
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} owned runtime preflight payload contains no transferable bytes.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
979
|
+
}
|
|
980
|
+
const ticket = Object.freeze({});
|
|
981
|
+
ownedDeliveryStateByTicket.set(ticket, {
|
|
982
|
+
operation,
|
|
983
|
+
payload: payload,
|
|
984
|
+
status: 'available',
|
|
985
|
+
transferables
|
|
986
|
+
});
|
|
987
|
+
operation.claimedTicket = ticket;
|
|
988
|
+
return ticket;
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
catch (error) {
|
|
993
|
+
operation.active = false;
|
|
994
|
+
if (operation.claimedTicket) {
|
|
995
|
+
this.retireRuntimePreflight(operation.claimedTicket);
|
|
996
|
+
}
|
|
997
|
+
throw error;
|
|
998
|
+
}
|
|
999
|
+
operation.active = false;
|
|
1000
|
+
const returnedDelivery = runtimePreflight !== null && typeof runtimePreflight === 'object'
|
|
1001
|
+
? ownedDeliveryStateByTicket.get(runtimePreflight)
|
|
1002
|
+
: undefined;
|
|
1003
|
+
if (this.usesOwnedRuntimePreflight()) {
|
|
1004
|
+
const currentUrls = this.resolvedRuntimeUrls;
|
|
1005
|
+
const currentPreflightKey = currentUrls?.preflightKey || currentUrls?.manifestFingerprint || '';
|
|
1006
|
+
if (!returnedDelivery ||
|
|
1007
|
+
returnedDelivery.operation !== operation ||
|
|
1008
|
+
returnedDelivery.status !== 'available' ||
|
|
1009
|
+
operation.claimedTicket !== runtimePreflight ||
|
|
1010
|
+
generation !== this.workerGeneration ||
|
|
1011
|
+
currentPreflightKey !== operation.preflightKey) {
|
|
1012
|
+
if (operation.claimedTicket) {
|
|
1013
|
+
this.retireRuntimePreflight(operation.claimedTicket);
|
|
1014
|
+
}
|
|
1015
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} transfer-owned runtime preflight must return this operation's owned delivery ticket.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
else if (operation.claimedTicket || returnedDelivery) {
|
|
1019
|
+
if (operation.claimedTicket) {
|
|
1020
|
+
this.retireRuntimePreflight(operation.claimedTicket);
|
|
1021
|
+
}
|
|
1022
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} structured-clone runtime preflight cannot return an owned delivery ticket.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
1023
|
+
}
|
|
1024
|
+
if (runtimePreflight === undefined || runtimePreflight === null) {
|
|
1025
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} runtime preflight returned no verified assets.`, { phase: 'asset', runtimeId: this.config.languageId });
|
|
1026
|
+
}
|
|
1027
|
+
try {
|
|
1028
|
+
this.reportProgress(progress, 0.18, `${this.config.displayName} runtime preflight complete`);
|
|
1029
|
+
}
|
|
1030
|
+
catch (error) {
|
|
1031
|
+
this.retireRuntimePreflight(runtimePreflight);
|
|
1032
|
+
throw error;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
let worker;
|
|
1036
|
+
let workerCreated = false;
|
|
1037
|
+
try {
|
|
1038
|
+
if (this.disposed)
|
|
1039
|
+
throw this.getDisposeReason();
|
|
1040
|
+
if (generation !== this.workerGeneration) {
|
|
1041
|
+
throw new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1042
|
+
phase: 'startup',
|
|
1043
|
+
runtimeId: this.config.languageId
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
const verifiedWorkerBytes = await this.preloadWorkerScript(progress, controls);
|
|
1047
|
+
if (this.disposed)
|
|
1048
|
+
throw this.getDisposeReason();
|
|
1049
|
+
if (generation !== this.workerGeneration) {
|
|
1050
|
+
throw new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1051
|
+
phase: 'startup',
|
|
1052
|
+
runtimeId: this.config.languageId
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
try {
|
|
1056
|
+
this.reportProgress(progress, 0.22, `Starting ${this.config.displayName} worker`);
|
|
1057
|
+
}
|
|
1058
|
+
catch (error) {
|
|
1059
|
+
throw this.preserveDisposeReason(error);
|
|
1060
|
+
}
|
|
1061
|
+
if (this.disposed)
|
|
1062
|
+
throw this.getDisposeReason();
|
|
1063
|
+
if (generation !== this.workerGeneration) {
|
|
1064
|
+
throw new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1065
|
+
phase: 'startup',
|
|
1066
|
+
runtimeId: this.config.languageId
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
this.bootstrapUrl = this.createBootstrapUrl(verifiedWorkerBytes);
|
|
1070
|
+
if (this.disposed || generation !== this.workerGeneration) {
|
|
1071
|
+
this.revokeBootstrapUrl();
|
|
1072
|
+
if (this.disposed)
|
|
1073
|
+
throw this.getDisposeReason();
|
|
1074
|
+
throw new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1075
|
+
phase: 'startup',
|
|
1076
|
+
runtimeId: this.config.languageId
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
try {
|
|
1080
|
+
worker = this.config.moduleWorker
|
|
1081
|
+
? new Worker(this.bootstrapUrl, { type: 'module' })
|
|
1082
|
+
: new Worker(this.bootstrapUrl);
|
|
1083
|
+
workerCreated = true;
|
|
1084
|
+
}
|
|
1085
|
+
catch (error) {
|
|
1086
|
+
this.revokeBootstrapUrl();
|
|
1087
|
+
if (this.disposed)
|
|
1088
|
+
throw this.getDisposeReason();
|
|
1089
|
+
if (generation !== this.workerGeneration) {
|
|
1090
|
+
throw new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1091
|
+
phase: 'startup',
|
|
1092
|
+
runtimeId: this.config.languageId
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
throw new WorkerStartupError(`${this.config.displayName} worker failed to start: ${this.errorMessage(error)}`, { cause: error, runtimeId: this.config.languageId });
|
|
1096
|
+
}
|
|
1097
|
+
if (runtimePreflight !== undefined) {
|
|
1098
|
+
this.workerRuntimePreflight.set(worker, runtimePreflight);
|
|
1099
|
+
}
|
|
1100
|
+
if (this.usesOwnedRuntimePreflight()) {
|
|
1101
|
+
this.workerRuntimePreflightMaxAssetBytes.set(worker, controls.limits.maxAssetBytes);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
catch (error) {
|
|
1105
|
+
if (workerCreated)
|
|
1106
|
+
this.detachAndTerminateWorker(worker);
|
|
1107
|
+
this.retireRuntimePreflight(runtimePreflight);
|
|
1108
|
+
throw error;
|
|
1109
|
+
}
|
|
1110
|
+
if (this.disposed || generation !== this.workerGeneration) {
|
|
1111
|
+
this.detachAndTerminateWorker(worker);
|
|
1112
|
+
this.revokeBootstrapUrl();
|
|
1113
|
+
if (this.disposed)
|
|
1114
|
+
throw this.getDisposeReason();
|
|
1115
|
+
throw new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1116
|
+
phase: 'startup',
|
|
1117
|
+
runtimeId: this.config.languageId
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
return await new Promise((resolve, reject) => {
|
|
1121
|
+
let settled = false;
|
|
1122
|
+
let timeout;
|
|
1123
|
+
const cleanup = () => {
|
|
1124
|
+
if (timeout !== undefined) {
|
|
1125
|
+
try {
|
|
1126
|
+
clearTimeout(timeout);
|
|
1127
|
+
}
|
|
1128
|
+
catch {
|
|
1129
|
+
// Timer cleanup is best effort once startup has settled.
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
try {
|
|
1133
|
+
controls.signal?.removeEventListener('abort', onAbort);
|
|
1134
|
+
}
|
|
1135
|
+
catch {
|
|
1136
|
+
// Caller-owned signal cleanup must not prevent startup settlement.
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
const rejectStartup = (reason) => {
|
|
1140
|
+
if (settled)
|
|
1141
|
+
return false;
|
|
1142
|
+
settled = true;
|
|
1143
|
+
cleanup();
|
|
1144
|
+
if (this.startupReject === rejectStartup)
|
|
1145
|
+
this.startupReject = null;
|
|
1146
|
+
reject(reason);
|
|
1147
|
+
return true;
|
|
1148
|
+
};
|
|
1149
|
+
const retireStartedWorker = () => {
|
|
1150
|
+
if (generation === this.workerGeneration && this.worker === worker) {
|
|
1151
|
+
this.disposeWorker();
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
this.detachAndTerminateWorker(worker);
|
|
1155
|
+
};
|
|
1156
|
+
const failStartup = (reason) => {
|
|
1157
|
+
if (!rejectStartup(this.preserveDisposeReason(reason)))
|
|
1158
|
+
return;
|
|
1159
|
+
retireStartedWorker();
|
|
1160
|
+
};
|
|
1161
|
+
const onAbort = () => {
|
|
1162
|
+
let signalReason;
|
|
1163
|
+
try {
|
|
1164
|
+
signalReason = controls.signal?.reason;
|
|
1165
|
+
}
|
|
1166
|
+
catch (error) {
|
|
1167
|
+
signalReason = error;
|
|
1168
|
+
}
|
|
1169
|
+
failStartup(signalReason === this.disposeReason
|
|
1170
|
+
? this.getDisposeReason()
|
|
1171
|
+
: new CancelledError(`${this.config.displayName} worker startup cancelled`, {
|
|
1172
|
+
cause: signalReason,
|
|
1173
|
+
phase: 'startup',
|
|
1174
|
+
runtimeId: this.config.languageId
|
|
1175
|
+
}));
|
|
1176
|
+
};
|
|
1177
|
+
this.worker = worker;
|
|
1178
|
+
this.startupReject = rejectStartup;
|
|
1179
|
+
try {
|
|
1180
|
+
timeout = setTimeout(() => {
|
|
1181
|
+
failStartup(new TimeoutError(`${this.config.displayName} worker startup timed out after ${controls.limits.startupTimeoutMs} ms`, {
|
|
1182
|
+
phase: 'startup',
|
|
1183
|
+
runtimeId: this.config.languageId,
|
|
1184
|
+
timeoutMs: controls.limits.startupTimeoutMs
|
|
1185
|
+
}));
|
|
1186
|
+
}, controls.limits.startupTimeoutMs);
|
|
1187
|
+
worker.onmessage = (event) => {
|
|
1188
|
+
if (generation !== this.workerGeneration || this.worker !== worker)
|
|
1189
|
+
return;
|
|
1190
|
+
if (event.data?.__wasmIdleStaticWorkerReady) {
|
|
1191
|
+
if (settled || this.startupReject !== rejectStartup)
|
|
1192
|
+
return;
|
|
1193
|
+
try {
|
|
1194
|
+
this.reportProgress(progress, 0.25, `${this.config.displayName} worker ready`);
|
|
1195
|
+
}
|
|
1196
|
+
catch (error) {
|
|
1197
|
+
failStartup(error);
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (settled ||
|
|
1201
|
+
generation !== this.workerGeneration ||
|
|
1202
|
+
this.worker !== worker ||
|
|
1203
|
+
this.startupReject !== rejectStartup) {
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
settled = true;
|
|
1207
|
+
cleanup();
|
|
1208
|
+
this.startupReject = null;
|
|
1209
|
+
this.revokeBootstrapUrl();
|
|
1210
|
+
resolve(worker);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
this.handleWorkerMessage(event);
|
|
1214
|
+
};
|
|
1215
|
+
worker.onerror = (event) => {
|
|
1216
|
+
if (generation !== this.workerGeneration || this.worker !== worker)
|
|
1217
|
+
return;
|
|
1218
|
+
event.preventDefault?.();
|
|
1219
|
+
const message = this.formatWorkerError(event);
|
|
1220
|
+
const options = {
|
|
1221
|
+
cause: event.error ?? event,
|
|
1222
|
+
runtimeId: this.config.languageId
|
|
1223
|
+
};
|
|
1224
|
+
this.handleWorkerFailure(this.startupReject
|
|
1225
|
+
? new WorkerStartupError(message, options)
|
|
1226
|
+
: new RuntimeExecutionError(message, options));
|
|
1227
|
+
};
|
|
1228
|
+
worker.onmessageerror = (event) => {
|
|
1229
|
+
if (generation !== this.workerGeneration || this.worker !== worker)
|
|
1230
|
+
return;
|
|
1231
|
+
this.handleWorkerFailure(new ProtocolError(`${this.config.displayName} worker message deserialization failed`, {
|
|
1232
|
+
cause: event,
|
|
1233
|
+
phase: 'protocol',
|
|
1234
|
+
runtimeId: this.config.languageId
|
|
1235
|
+
}));
|
|
1236
|
+
};
|
|
1237
|
+
controls.signal?.addEventListener('abort', onAbort, { once: true });
|
|
1238
|
+
if (controls.signal?.aborted)
|
|
1239
|
+
onAbort();
|
|
1240
|
+
}
|
|
1241
|
+
catch (error) {
|
|
1242
|
+
failStartup(error);
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
handleWorkerMessage(event) {
|
|
1247
|
+
const activeRun = this.activeRun;
|
|
1248
|
+
if (!activeRun)
|
|
1249
|
+
return;
|
|
1250
|
+
if (event.data?.runId !== activeRun.id)
|
|
1251
|
+
return;
|
|
1252
|
+
try {
|
|
1253
|
+
if (event.data?.type === 'execution-ready') {
|
|
1254
|
+
activeRun.startExecutionDeadline?.();
|
|
1255
|
+
activeRun.startExecutionDeadline = undefined;
|
|
1256
|
+
this.reportRunReady(activeRun, 'running', 'started', `${this.config.displayName} program started`);
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
if (event.data?.type === 'stdin-request') {
|
|
1260
|
+
activeRun.stdinRing?.consumerRequestedInput();
|
|
1261
|
+
if (this.activeRun === activeRun) {
|
|
1262
|
+
this.reportRunReady(activeRun, 'waiting-input', 'stdin-request', `${this.config.displayName} runtime ready for input`);
|
|
1263
|
+
}
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
const { output, stream, results, error, diagnostic, progress, failure, evidence } = event.data || {};
|
|
1267
|
+
if (evidence !== undefined)
|
|
1268
|
+
this.config.onEvidence?.(evidence);
|
|
1269
|
+
if (this.activeRun !== activeRun)
|
|
1270
|
+
return;
|
|
1271
|
+
if (progress && typeof progress === 'object' && 'kind' in progress) {
|
|
1272
|
+
if (progress.kind !== 'settled') {
|
|
1273
|
+
const lifecycleEvent = reportWorkerProgress(activeRun.progress, progress);
|
|
1274
|
+
if (lifecycleEvent?.kind === 'ready')
|
|
1275
|
+
activeRun.readyReported = true;
|
|
1276
|
+
if (this.activeRun !== activeRun)
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
else if (progress && typeof progress.percent === 'number') {
|
|
1281
|
+
const runtimeProgress = Math.max(0, Math.min(progress.percent / 100, 1));
|
|
1282
|
+
this.reportProgress(activeRun.progress, 0.3 + runtimeProgress * 0.65, progress.stage || `Running ${this.config.displayName}`);
|
|
1283
|
+
if (this.activeRun !== activeRun)
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (typeof output === 'string' && output.length > 0) {
|
|
1287
|
+
const outputBytes = activeRun.outputBytes + outputEncoder.encode(output).byteLength;
|
|
1288
|
+
if (outputBytes > activeRun.limits.maxOutputBytes) {
|
|
1289
|
+
this.rejectRun(activeRun.id, new OutputLimitError(`${this.config.displayName} output exceeded ${activeRun.limits.maxOutputBytes} bytes`, {
|
|
1290
|
+
actual: outputBytes,
|
|
1291
|
+
limit: activeRun.limits.maxOutputBytes,
|
|
1292
|
+
phase: 'execute',
|
|
1293
|
+
runtimeId: this.config.languageId
|
|
1294
|
+
}));
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
this.reportRunReady(activeRun, 'running', stream === 'stderr' ? 'stderr' : 'stdout', `${this.config.displayName} program output received`);
|
|
1298
|
+
if (this.activeRun !== activeRun)
|
|
1299
|
+
return;
|
|
1300
|
+
activeRun.outputBytes = outputBytes;
|
|
1301
|
+
this.output?.(output);
|
|
1302
|
+
if (this.activeRun !== activeRun)
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
if (diagnostic) {
|
|
1306
|
+
const diagnosticCount = activeRun.diagnosticCount + 1;
|
|
1307
|
+
if (diagnosticCount > activeRun.limits.maxDiagnostics) {
|
|
1308
|
+
this.rejectRun(activeRun.id, new DiagnosticLimitError(`${this.config.displayName} diagnostics exceeded ${activeRun.limits.maxDiagnostics} messages`, {
|
|
1309
|
+
actual: diagnosticCount,
|
|
1310
|
+
limit: activeRun.limits.maxDiagnostics,
|
|
1311
|
+
phase: 'execute',
|
|
1312
|
+
runtimeId: this.config.languageId
|
|
1313
|
+
}));
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
activeRun.diagnosticCount = diagnosticCount;
|
|
1317
|
+
this.oncompilerdiagnostic?.(diagnostic);
|
|
1318
|
+
if (this.activeRun !== activeRun)
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
if (typeof error === 'string') {
|
|
1322
|
+
this.rejectRun(activeRun.id, isStaticRuntimePreflightSerializedError(failure)
|
|
1323
|
+
? deserializeStaticRuntimePreflightError(failure, this.config.languageId)
|
|
1324
|
+
: error);
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
if (results !== undefined) {
|
|
1328
|
+
this.resolveRun(activeRun.id, typeof results === 'string' ? results : results);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
catch (error) {
|
|
1332
|
+
if (this.activeRun === activeRun)
|
|
1333
|
+
this.rejectRun(activeRun.id, error);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
formatWorkerError(event) {
|
|
1337
|
+
const location = event.filename && event.lineno
|
|
1338
|
+
? ` (${event.filename}:${event.lineno}:${event.colno})`
|
|
1339
|
+
: '';
|
|
1340
|
+
return `${this.config.displayName} worker script error: ${event.message || 'unknown error'}${location}`;
|
|
1341
|
+
}
|
|
1342
|
+
errorMessage(error) {
|
|
1343
|
+
if (error instanceof Error)
|
|
1344
|
+
return error.message;
|
|
1345
|
+
if (error !== null &&
|
|
1346
|
+
typeof error === 'object' &&
|
|
1347
|
+
'message' in error &&
|
|
1348
|
+
typeof error.message === 'string') {
|
|
1349
|
+
return error.message;
|
|
1350
|
+
}
|
|
1351
|
+
return String(error);
|
|
1352
|
+
}
|
|
1353
|
+
handleWorkerFailure(reason) {
|
|
1354
|
+
this.startupReject?.(reason);
|
|
1355
|
+
this.startupReject = null;
|
|
1356
|
+
if (this.activeRun)
|
|
1357
|
+
this.rejectRun(this.activeRun.id, reason);
|
|
1358
|
+
else
|
|
1359
|
+
this.disposeWorker();
|
|
1360
|
+
}
|
|
1361
|
+
resolveRun(id, result) {
|
|
1362
|
+
const activeRun = this.activeRun;
|
|
1363
|
+
if (!activeRun || activeRun.id !== id)
|
|
1364
|
+
return;
|
|
1365
|
+
try {
|
|
1366
|
+
if (!activeRun.readyReported && !activeRun.settledReported) {
|
|
1367
|
+
this.reportRunReady(activeRun, 'running', 'result', `${this.config.displayName} run complete`);
|
|
1368
|
+
if (this.activeRun !== activeRun)
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
this.reportRunSettled(activeRun, 'completed', `${this.config.displayName} run complete`);
|
|
1372
|
+
}
|
|
1373
|
+
catch (error) {
|
|
1374
|
+
if (this.activeRun === activeRun)
|
|
1375
|
+
this.rejectRun(id, error);
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
if (this.activeRun !== activeRun)
|
|
1379
|
+
return;
|
|
1380
|
+
const claimedRun = this.claimRun(id);
|
|
1381
|
+
if (claimedRun !== activeRun)
|
|
1382
|
+
return;
|
|
1383
|
+
this.elapse = Date.now() - this.begin;
|
|
1384
|
+
this.exit = true;
|
|
1385
|
+
this.clearPendingStdin();
|
|
1386
|
+
this.releaseWorkerLease(true);
|
|
1387
|
+
this.cleanupRun(activeRun);
|
|
1388
|
+
activeRun.resolve(result);
|
|
1389
|
+
}
|
|
1390
|
+
rejectRun(id, reason) {
|
|
1391
|
+
const activeRun = this.claimRun(id);
|
|
1392
|
+
if (!activeRun)
|
|
1393
|
+
return;
|
|
1394
|
+
try {
|
|
1395
|
+
this.reportRunSettled(activeRun, reason instanceof TimeoutError
|
|
1396
|
+
? 'timed-out'
|
|
1397
|
+
: reason instanceof CancelledError
|
|
1398
|
+
? 'cancelled'
|
|
1399
|
+
: 'failed', `${this.config.displayName} run ended`);
|
|
1400
|
+
}
|
|
1401
|
+
catch {
|
|
1402
|
+
// Progress observers must not prevent a failed run from releasing its worker.
|
|
1403
|
+
}
|
|
1404
|
+
this.elapse = Date.now() - this.begin;
|
|
1405
|
+
this.exit = true;
|
|
1406
|
+
this.clearPendingStdin();
|
|
1407
|
+
this.startupReject?.(reason);
|
|
1408
|
+
this.disposeWorker();
|
|
1409
|
+
this.cleanupRun(activeRun);
|
|
1410
|
+
activeRun.reject(reason);
|
|
1411
|
+
}
|
|
1412
|
+
claimRun(id) {
|
|
1413
|
+
const activeRun = this.activeRun;
|
|
1414
|
+
if (!activeRun || activeRun.id !== id)
|
|
1415
|
+
return null;
|
|
1416
|
+
this.activeRun = null;
|
|
1417
|
+
this.activeReject = null;
|
|
1418
|
+
return activeRun;
|
|
1419
|
+
}
|
|
1420
|
+
cleanupRun(activeRun) {
|
|
1421
|
+
try {
|
|
1422
|
+
activeRun.cleanup();
|
|
1423
|
+
}
|
|
1424
|
+
catch {
|
|
1425
|
+
// Lifecycle cleanup is best effort and must not replace the run result.
|
|
1426
|
+
}
|
|
1427
|
+
try {
|
|
1428
|
+
activeRun.stdinRing?.cancel();
|
|
1429
|
+
}
|
|
1430
|
+
catch {
|
|
1431
|
+
// The stdin channel is already detached; preserve the run result.
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
releasePreparedWorkerIfIdle() {
|
|
1435
|
+
if (this.workerLifetimeController.policy.mode === 'per-run')
|
|
1436
|
+
return;
|
|
1437
|
+
if (this.activeRun || this.startingRunId)
|
|
1438
|
+
return;
|
|
1439
|
+
this.releaseWorkerLease(true);
|
|
1440
|
+
}
|
|
1441
|
+
releaseWorkerLease(reusable) {
|
|
1442
|
+
const lease = this.workerLease;
|
|
1443
|
+
this.workerLease = null;
|
|
1444
|
+
this.workerStartPromise = null;
|
|
1445
|
+
this.workerStartMaxAssetBytes = null;
|
|
1446
|
+
if (!lease)
|
|
1447
|
+
return;
|
|
1448
|
+
try {
|
|
1449
|
+
lease.release({ reusable });
|
|
1450
|
+
}
|
|
1451
|
+
catch {
|
|
1452
|
+
// A hostile timer implementation must not replace a completed run.
|
|
1453
|
+
this.workerLifetimeController.evictIdle();
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
handleMemoryPressure() {
|
|
1457
|
+
return this.workerLifetimeController.handleMemoryPressure();
|
|
1458
|
+
}
|
|
1459
|
+
retireManagedWorker(worker) {
|
|
1460
|
+
if (this.worker === worker) {
|
|
1461
|
+
delete this.worker;
|
|
1462
|
+
this.workerStartPromise = null;
|
|
1463
|
+
this.workerStartMaxAssetBytes = null;
|
|
1464
|
+
this.workerGeneration += 1;
|
|
1465
|
+
}
|
|
1466
|
+
this.detachAndTerminateWorker(worker);
|
|
1467
|
+
}
|
|
1468
|
+
retireRuntimePreflight(runtimePreflight) {
|
|
1469
|
+
if (runtimePreflight === null || typeof runtimePreflight !== 'object')
|
|
1470
|
+
return;
|
|
1471
|
+
const state = ownedDeliveryStateByTicket.get(runtimePreflight);
|
|
1472
|
+
if (!state || state.operation.owner !== this || state.status === 'retired')
|
|
1473
|
+
return;
|
|
1474
|
+
state.status = 'retired';
|
|
1475
|
+
state.payload = null;
|
|
1476
|
+
state.transferables = null;
|
|
1477
|
+
}
|
|
1478
|
+
detachAndTerminateWorker(worker) {
|
|
1479
|
+
this.retireRuntimePreflight(this.workerRuntimePreflight.get(worker));
|
|
1480
|
+
this.workerRuntimePreflight.delete(worker);
|
|
1481
|
+
this.workerRuntimePreflightMaxAssetBytes.delete(worker);
|
|
1482
|
+
this.workersWithCachedRuntimePreflight.delete(worker);
|
|
1483
|
+
try {
|
|
1484
|
+
worker.onmessage = null;
|
|
1485
|
+
}
|
|
1486
|
+
catch {
|
|
1487
|
+
// Continue detaching and terminating even if a custom worker setter fails.
|
|
1488
|
+
}
|
|
1489
|
+
try {
|
|
1490
|
+
worker.onerror = null;
|
|
1491
|
+
}
|
|
1492
|
+
catch {
|
|
1493
|
+
// Continue detaching and terminating even if a custom worker setter fails.
|
|
1494
|
+
}
|
|
1495
|
+
try {
|
|
1496
|
+
worker.onmessageerror = null;
|
|
1497
|
+
}
|
|
1498
|
+
catch {
|
|
1499
|
+
// Continue detaching and terminating even if a custom worker setter fails.
|
|
1500
|
+
}
|
|
1501
|
+
try {
|
|
1502
|
+
worker.terminate();
|
|
1503
|
+
}
|
|
1504
|
+
catch {
|
|
1505
|
+
// The worker is detached before cleanup, so a throwing terminate remains harmless.
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
disposeWorker(reason) {
|
|
1509
|
+
const startAbortController = this.workerStartAbortController;
|
|
1510
|
+
this.workerStartAbortController = null;
|
|
1511
|
+
this.workerGeneration += 1;
|
|
1512
|
+
this.workerStartPromise = null;
|
|
1513
|
+
this.workerStartMaxAssetBytes = null;
|
|
1514
|
+
this.startupReject = null;
|
|
1515
|
+
const lease = this.workerLease;
|
|
1516
|
+
this.workerLease = null;
|
|
1517
|
+
const worker = this.worker;
|
|
1518
|
+
delete this.worker;
|
|
1519
|
+
this.revokeBootstrapUrl();
|
|
1520
|
+
if (startAbortController && !startAbortController.signal.aborted) {
|
|
1521
|
+
try {
|
|
1522
|
+
startAbortController.abort(reason);
|
|
1523
|
+
}
|
|
1524
|
+
catch {
|
|
1525
|
+
// Startup state is already detached; abort cleanup is best effort.
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
if (lease) {
|
|
1529
|
+
lease.release({ reusable: false });
|
|
1530
|
+
if (worker && worker !== lease.worker)
|
|
1531
|
+
this.detachAndTerminateWorker(worker);
|
|
1532
|
+
}
|
|
1533
|
+
else if (worker && !this.workerLifetimeController.retireWorker(worker)) {
|
|
1534
|
+
this.detachAndTerminateWorker(worker);
|
|
1535
|
+
}
|
|
1536
|
+
this.workerLifetimeController.evictIdle();
|
|
1537
|
+
}
|
|
1538
|
+
run(code, prepare, _log = true, _prog, args = [], options = {}) {
|
|
1539
|
+
try {
|
|
1540
|
+
this.assertNotDisposed();
|
|
1541
|
+
}
|
|
1542
|
+
catch (error) {
|
|
1543
|
+
return Promise.reject(error);
|
|
1544
|
+
}
|
|
1545
|
+
if (!this.baseUrl || !this.workerUrl) {
|
|
1546
|
+
return Promise.reject(new RuntimeConfigurationError(`${this.config.displayName} runtime is not configured.`, { runtimeId: this.config.languageId }));
|
|
1547
|
+
}
|
|
1548
|
+
if (this.activeRun || this.startingRunId) {
|
|
1549
|
+
return Promise.reject(new BusyError(`${this.config.displayName} runtime already has an active run`, {
|
|
1550
|
+
runtimeId: this.config.languageId
|
|
1551
|
+
}));
|
|
1552
|
+
}
|
|
1553
|
+
let controls;
|
|
1554
|
+
let workspace;
|
|
1555
|
+
try {
|
|
1556
|
+
controls = this.resolveExecutionControls(options);
|
|
1557
|
+
this.assertOperationNotDisposed();
|
|
1558
|
+
workspace = this.validateWorkspace(code, options, controls.limits);
|
|
1559
|
+
this.assertOperationNotDisposed();
|
|
1560
|
+
}
|
|
1561
|
+
catch (error) {
|
|
1562
|
+
return Promise.reject(this.preserveDisposeReason(error));
|
|
1563
|
+
}
|
|
1564
|
+
try {
|
|
1565
|
+
if (controls.signal?.aborted) {
|
|
1566
|
+
return Promise.reject(new CancelledError(`${this.config.displayName} run cancelled`, {
|
|
1567
|
+
cause: controls.signal.reason,
|
|
1568
|
+
phase: 'execute',
|
|
1569
|
+
runtimeId: this.config.languageId
|
|
1570
|
+
}));
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
catch (error) {
|
|
1574
|
+
return Promise.reject(this.preserveDisposeReason(error));
|
|
1575
|
+
}
|
|
1576
|
+
this.resetWorkerForLowerAssetLimit(controls.limits.maxAssetBytes);
|
|
1577
|
+
const id = `static-${++this.uid}`;
|
|
1578
|
+
this.startingRunId = id;
|
|
1579
|
+
const progressSink = this.selectProgress(_prog);
|
|
1580
|
+
let lifecycle;
|
|
1581
|
+
try {
|
|
1582
|
+
lifecycle = this.beginProgressLifecycle(progressSink, prepare
|
|
1583
|
+
? `Preparing ${this.config.displayName} runtime`
|
|
1584
|
+
: `Starting ${this.config.displayName} run`);
|
|
1585
|
+
}
|
|
1586
|
+
catch (error) {
|
|
1587
|
+
if (this.startingRunId === id)
|
|
1588
|
+
this.startingRunId = null;
|
|
1589
|
+
return Promise.reject(this.preserveDisposeReason(error));
|
|
1590
|
+
}
|
|
1591
|
+
if (this.startingRunId !== id) {
|
|
1592
|
+
lifecycle.end();
|
|
1593
|
+
return Promise.reject(this.disposed
|
|
1594
|
+
? this.getDisposeReason()
|
|
1595
|
+
: new CancelledError(prepare
|
|
1596
|
+
? `${this.config.displayName} worker startup terminated`
|
|
1597
|
+
: `${this.config.displayName} run terminated`, {
|
|
1598
|
+
phase: prepare ? 'startup' : 'execute',
|
|
1599
|
+
runtimeId: this.config.languageId
|
|
1600
|
+
}));
|
|
1601
|
+
}
|
|
1602
|
+
const progress = lifecycle.progress;
|
|
1603
|
+
if (prepare) {
|
|
1604
|
+
let startup;
|
|
1605
|
+
try {
|
|
1606
|
+
startup = this.ensureWorkerStarted(progress, controls);
|
|
1607
|
+
}
|
|
1608
|
+
catch (error) {
|
|
1609
|
+
startup = Promise.reject(error);
|
|
1610
|
+
}
|
|
1611
|
+
return startup
|
|
1612
|
+
.then(() => {
|
|
1613
|
+
if (this.startingRunId !== id) {
|
|
1614
|
+
throw new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1615
|
+
phase: 'startup',
|
|
1616
|
+
runtimeId: this.config.languageId
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
this.assertOperationNotDisposed();
|
|
1620
|
+
this.reportProgress(progress, 0.25, `${this.config.displayName} worker ready`);
|
|
1621
|
+
this.assertOperationNotDisposed();
|
|
1622
|
+
if (this.startingRunId !== id) {
|
|
1623
|
+
throw new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1624
|
+
phase: 'startup',
|
|
1625
|
+
runtimeId: this.config.languageId
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
return true;
|
|
1629
|
+
})
|
|
1630
|
+
.catch((error) => {
|
|
1631
|
+
throw this.preserveDisposeReason(error);
|
|
1632
|
+
})
|
|
1633
|
+
.finally(() => {
|
|
1634
|
+
if (this.startingRunId === id)
|
|
1635
|
+
this.startingRunId = null;
|
|
1636
|
+
this.releasePreparedWorkerIfIdle();
|
|
1637
|
+
lifecycle.end();
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
this.exit = false;
|
|
1641
|
+
return new Promise((resolve, reject) => {
|
|
1642
|
+
let stdinRing;
|
|
1643
|
+
if (this.stdinMode === 'streaming') {
|
|
1644
|
+
try {
|
|
1645
|
+
const maxBufferedBytes = Math.min(128 * 1024, controls.limits.maxWorkspaceBytes);
|
|
1646
|
+
stdinRing = new StaticStdinRingHost({
|
|
1647
|
+
capacity: Math.min(64 * 1024, maxBufferedBytes),
|
|
1648
|
+
maxBufferedBytes
|
|
1649
|
+
});
|
|
1650
|
+
const explicitStdin = typeof options.stdin === 'string';
|
|
1651
|
+
const initialInput = explicitStdin
|
|
1652
|
+
? (options.stdin ?? '')
|
|
1653
|
+
: this.pendingInput.join('');
|
|
1654
|
+
const initialEof = explicitStdin || this.pendingEof;
|
|
1655
|
+
this.assertOperationNotDisposed();
|
|
1656
|
+
this.clearPendingStdin();
|
|
1657
|
+
stdinRing.enqueue(initialInput);
|
|
1658
|
+
if (initialEof)
|
|
1659
|
+
stdinRing.close();
|
|
1660
|
+
}
|
|
1661
|
+
catch (error) {
|
|
1662
|
+
try {
|
|
1663
|
+
stdinRing?.cancel();
|
|
1664
|
+
}
|
|
1665
|
+
catch {
|
|
1666
|
+
// A local stdin channel must not outlive a rejected reservation.
|
|
1667
|
+
}
|
|
1668
|
+
if (this.startingRunId === id)
|
|
1669
|
+
this.startingRunId = null;
|
|
1670
|
+
this.exit = true;
|
|
1671
|
+
reject(this.preserveDisposeReason(error));
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
let deadline;
|
|
1676
|
+
const onAbort = () => {
|
|
1677
|
+
if (this.activeRun?.id !== id)
|
|
1678
|
+
return;
|
|
1679
|
+
const error = new CancelledError(`${this.config.displayName} run cancelled`, {
|
|
1680
|
+
cause: controls.signal?.reason,
|
|
1681
|
+
phase: 'execute',
|
|
1682
|
+
runtimeId: this.config.languageId
|
|
1683
|
+
});
|
|
1684
|
+
this.rejectStdinWaiters(error);
|
|
1685
|
+
this.rejectRun(id, error);
|
|
1686
|
+
};
|
|
1687
|
+
const cleanup = () => {
|
|
1688
|
+
if (deadline !== undefined)
|
|
1689
|
+
clearTimeout(deadline);
|
|
1690
|
+
controls.signal?.removeEventListener('abort', onAbort);
|
|
1691
|
+
};
|
|
1692
|
+
this.activeRun = {
|
|
1693
|
+
cleanup,
|
|
1694
|
+
diagnosticCount: 0,
|
|
1695
|
+
id,
|
|
1696
|
+
limits: controls.limits,
|
|
1697
|
+
outputBytes: 0,
|
|
1698
|
+
progress,
|
|
1699
|
+
readyReported: false,
|
|
1700
|
+
resolve,
|
|
1701
|
+
reject,
|
|
1702
|
+
settledReported: false,
|
|
1703
|
+
...(stdinRing ? { stdinRing } : {})
|
|
1704
|
+
};
|
|
1705
|
+
this.activeReject = reject;
|
|
1706
|
+
this.startingRunId = null;
|
|
1707
|
+
this.begin = Date.now();
|
|
1708
|
+
try {
|
|
1709
|
+
controls.signal?.addEventListener('abort', onAbort, { once: true });
|
|
1710
|
+
}
|
|
1711
|
+
catch (error) {
|
|
1712
|
+
this.rejectRun(id, error);
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
if (controls.signal?.aborted) {
|
|
1716
|
+
onAbort();
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
void (async () => {
|
|
1720
|
+
try {
|
|
1721
|
+
const worker = await this.ensureWorkerStarted(progress, controls);
|
|
1722
|
+
const activeRun = this.activeRun;
|
|
1723
|
+
if (!activeRun || activeRun.id !== id)
|
|
1724
|
+
return;
|
|
1725
|
+
const { stdin, stdinEof } = activeRun.stdinRing
|
|
1726
|
+
? { stdin: undefined, stdinEof: false }
|
|
1727
|
+
: await this.collectStdinForRun(code, options, activeRun);
|
|
1728
|
+
if (this.activeRun?.id !== id)
|
|
1729
|
+
return;
|
|
1730
|
+
const setDeadline = (timeoutMs, phase) => {
|
|
1731
|
+
timeoutMs = Math.min(2_147_483_647, timeoutMs);
|
|
1732
|
+
if (deadline !== undefined)
|
|
1733
|
+
clearTimeout(deadline);
|
|
1734
|
+
deadline = setTimeout(() => this.rejectRun(id, new TimeoutError(`${this.config.displayName} ${this.config.enforcePhaseTimeouts ? phase : 'execution'} timed out after ${timeoutMs} ms`, { phase, runtimeId: this.config.languageId, timeoutMs })), timeoutMs);
|
|
1735
|
+
};
|
|
1736
|
+
if (this.config.enforcePhaseTimeouts) {
|
|
1737
|
+
setDeadline(controls.limits.compileTimeoutMs, 'compile');
|
|
1738
|
+
activeRun.startExecutionDeadline = () => setDeadline(controls.limits.runTimeoutMs, 'execute');
|
|
1739
|
+
}
|
|
1740
|
+
else {
|
|
1741
|
+
setDeadline(Math.min(2_147_483_647, controls.limits.compileTimeoutMs + controls.limits.runTimeoutMs), 'execute');
|
|
1742
|
+
}
|
|
1743
|
+
const { programArgs } = resolveSandboxExecutionArgs(this.config.languageId, args, options);
|
|
1744
|
+
this.reportProgress(progress, 0.3, `Loading ${this.config.displayName} runtime`);
|
|
1745
|
+
if (this.disposed || this.activeRun !== activeRun || this.worker !== worker) {
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
try {
|
|
1749
|
+
const storedRuntimePreflight = this.workerRuntimePreflight.get(worker);
|
|
1750
|
+
let runtimePreflight = storedRuntimePreflight;
|
|
1751
|
+
let transferList;
|
|
1752
|
+
let deliveredOwnedPreflight = false;
|
|
1753
|
+
try {
|
|
1754
|
+
if (this.usesOwnedRuntimePreflight()) {
|
|
1755
|
+
const state = storedRuntimePreflight !== null &&
|
|
1756
|
+
typeof storedRuntimePreflight === 'object'
|
|
1757
|
+
? ownedDeliveryStateByTicket.get(storedRuntimePreflight)
|
|
1758
|
+
: undefined;
|
|
1759
|
+
const currentUrls = this.resolvedRuntimeUrls;
|
|
1760
|
+
const currentPreflightKey = currentUrls?.preflightKey ||
|
|
1761
|
+
currentUrls?.manifestFingerprint ||
|
|
1762
|
+
'';
|
|
1763
|
+
if (state &&
|
|
1764
|
+
state.operation.owner === this &&
|
|
1765
|
+
state.operation.runtimeId === this.config.languageId &&
|
|
1766
|
+
state.operation.generation === this.workerGeneration &&
|
|
1767
|
+
state.operation.preflightKey === currentPreflightKey &&
|
|
1768
|
+
state.status === 'available' &&
|
|
1769
|
+
state.payload &&
|
|
1770
|
+
state.transferables) {
|
|
1771
|
+
runtimePreflight = state.payload;
|
|
1772
|
+
transferList = state.transferables;
|
|
1773
|
+
state.status = 'consumed';
|
|
1774
|
+
state.payload = null;
|
|
1775
|
+
state.transferables = null;
|
|
1776
|
+
deliveredOwnedPreflight = true;
|
|
1777
|
+
}
|
|
1778
|
+
else if (this.usesWorkerCachedRuntimePreflight() &&
|
|
1779
|
+
storedRuntimePreflight === undefined &&
|
|
1780
|
+
this.workersWithCachedRuntimePreflight.has(worker)) {
|
|
1781
|
+
runtimePreflight = undefined;
|
|
1782
|
+
}
|
|
1783
|
+
else {
|
|
1784
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} runtime preflight owned delivery is unavailable or stale.`, { phase: 'protocol', runtimeId: this.config.languageId });
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
else if (storedRuntimePreflight !== null &&
|
|
1788
|
+
typeof storedRuntimePreflight === 'object' &&
|
|
1789
|
+
ownedDeliveryStateByTicket.has(storedRuntimePreflight)) {
|
|
1790
|
+
throw new RuntimeConfigurationError(`${this.config.displayName} structured-clone runtime preflight cannot dispatch an owned delivery ticket.`, { phase: 'protocol', runtimeId: this.config.languageId });
|
|
1791
|
+
}
|
|
1792
|
+
const message = {
|
|
1793
|
+
run: true,
|
|
1794
|
+
runId: id,
|
|
1795
|
+
baseUrl: this.baseUrl,
|
|
1796
|
+
manifestUrl: this.manifestUrl,
|
|
1797
|
+
manifestFingerprint: this.manifestFingerprint,
|
|
1798
|
+
...(runtimePreflight === undefined ? {} : { runtimePreflight }),
|
|
1799
|
+
maxAssetBytes: controls.limits.maxAssetBytes,
|
|
1800
|
+
...(this.config.includeExecutionLimits
|
|
1801
|
+
? { limits: controls.limits }
|
|
1802
|
+
: {}),
|
|
1803
|
+
code,
|
|
1804
|
+
args: programArgs,
|
|
1805
|
+
stdin,
|
|
1806
|
+
stdinEof,
|
|
1807
|
+
...(activeRun.stdinRing
|
|
1808
|
+
? { stdinChannel: activeRun.stdinRing.descriptor }
|
|
1809
|
+
: {}),
|
|
1810
|
+
activePath: workspace.activePath,
|
|
1811
|
+
workspaceFiles: workspace.workspaceFiles,
|
|
1812
|
+
log: _log
|
|
1813
|
+
};
|
|
1814
|
+
if (transferList)
|
|
1815
|
+
worker.postMessage(message, transferList);
|
|
1816
|
+
else
|
|
1817
|
+
worker.postMessage(message);
|
|
1818
|
+
if (deliveredOwnedPreflight &&
|
|
1819
|
+
this.usesWorkerCachedRuntimePreflight()) {
|
|
1820
|
+
this.workersWithCachedRuntimePreflight.add(worker);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
finally {
|
|
1824
|
+
if (this.usesOwnedRuntimePreflight()) {
|
|
1825
|
+
this.workerRuntimePreflight.delete(worker);
|
|
1826
|
+
this.retireRuntimePreflight(storedRuntimePreflight);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
catch (error) {
|
|
1831
|
+
throw new ProtocolError(`${this.config.displayName} worker run dispatch failed: ${this.errorMessage(error)}`, {
|
|
1832
|
+
cause: error,
|
|
1833
|
+
phase: 'protocol',
|
|
1834
|
+
runtimeId: this.config.languageId
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
catch (error) {
|
|
1839
|
+
const reason = this.preserveDisposeReason(error);
|
|
1840
|
+
this.rejectRun(id, isWasmIdleError(reason) ? reason : this.errorMessage(reason));
|
|
1841
|
+
}
|
|
1842
|
+
})();
|
|
1843
|
+
}).finally(() => lifecycle.end());
|
|
1844
|
+
}
|
|
1845
|
+
kill() {
|
|
1846
|
+
this.terminate();
|
|
1847
|
+
}
|
|
1848
|
+
terminateLifecycle(startupReason, runReason) {
|
|
1849
|
+
const activeRun = this.activeRun;
|
|
1850
|
+
if (activeRun) {
|
|
1851
|
+
try {
|
|
1852
|
+
this.reportRunSettled(activeRun, 'cancelled', `${this.config.displayName} run ended`);
|
|
1853
|
+
}
|
|
1854
|
+
catch {
|
|
1855
|
+
// Progress observers must not prevent explicit runtime termination.
|
|
1856
|
+
}
|
|
1857
|
+
if (this.activeRun !== activeRun)
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
const workerReason = this.activeRun ? runReason : startupReason;
|
|
1861
|
+
this.progressController.invalidate();
|
|
1862
|
+
this.uid += 1;
|
|
1863
|
+
this.startingRunId = null;
|
|
1864
|
+
this.startupReject?.(startupReason);
|
|
1865
|
+
this.startupReject = null;
|
|
1866
|
+
this.rejectStdinWaiters(runReason);
|
|
1867
|
+
if (this.activeRun) {
|
|
1868
|
+
const activeRun = this.claimRun(this.activeRun.id);
|
|
1869
|
+
if (activeRun) {
|
|
1870
|
+
this.cleanupRun(activeRun);
|
|
1871
|
+
activeRun.reject(runReason);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
this.activeReject = null;
|
|
1875
|
+
this.clearPendingStdin();
|
|
1876
|
+
this.exit = true;
|
|
1877
|
+
this.disposeWorker(workerReason);
|
|
1878
|
+
}
|
|
1879
|
+
terminate() {
|
|
1880
|
+
if (this.disposed)
|
|
1881
|
+
return;
|
|
1882
|
+
const startupReason = new CancelledError(`${this.config.displayName} worker startup terminated`, {
|
|
1883
|
+
phase: 'startup',
|
|
1884
|
+
runtimeId: this.config.languageId
|
|
1885
|
+
});
|
|
1886
|
+
const runReason = new CancelledError(`${this.config.displayName} run terminated`, {
|
|
1887
|
+
phase: 'execute',
|
|
1888
|
+
runtimeId: this.config.languageId
|
|
1889
|
+
});
|
|
1890
|
+
this.terminateLifecycle(startupReason, runReason);
|
|
1891
|
+
}
|
|
1892
|
+
dispose() {
|
|
1893
|
+
if (this.disposePromise)
|
|
1894
|
+
return this.disposePromise;
|
|
1895
|
+
this.disposed = true;
|
|
1896
|
+
this.disposePromise = Promise.resolve();
|
|
1897
|
+
const reason = this.getDisposeReason();
|
|
1898
|
+
this.terminateLifecycle(reason, reason);
|
|
1899
|
+
this.workerLifetimeController.dispose();
|
|
1900
|
+
this.lifecycleProgress = undefined;
|
|
1901
|
+
this.output = null;
|
|
1902
|
+
this.oncompilerdiagnostic = undefined;
|
|
1903
|
+
this.baseUrl = '';
|
|
1904
|
+
this.workerUrl = '';
|
|
1905
|
+
this.manifestUrl = '';
|
|
1906
|
+
this.manifestFingerprint = '';
|
|
1907
|
+
this.preflightKey = '';
|
|
1908
|
+
this.resolvedRuntimeUrls = null;
|
|
1909
|
+
this.workerReceipt = null;
|
|
1910
|
+
return this.disposePromise;
|
|
1911
|
+
}
|
|
1912
|
+
async clear() {
|
|
1913
|
+
if (!this.disposed)
|
|
1914
|
+
this.terminate();
|
|
1915
|
+
this.lifecycleProgress = undefined;
|
|
1916
|
+
}
|
|
1917
|
+
}
|