wasm-idle 1.0.5 → 1.0.7

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.
Files changed (112) hide show
  1. package/README.md +52 -5
  2. package/dist/index.d.ts +4 -1
  3. package/dist/index.js +2 -0
  4. package/dist/playground/assetBridge.d.ts +11 -2
  5. package/dist/playground/assetBridge.js +190 -46
  6. package/dist/playground/assets.d.ts +9 -1
  7. package/dist/playground/assets.js +29 -5
  8. package/dist/playground/awk.js +1 -0
  9. package/dist/playground/bash.d.ts +1 -0
  10. package/dist/playground/bash.js +7 -1
  11. package/dist/playground/bqn.js +1 -0
  12. package/dist/playground/bqnPreflight.d.ts +2 -1
  13. package/dist/playground/bqnPreflight.js +1 -0
  14. package/dist/playground/c3.js +1 -0
  15. package/dist/playground/cachedClang.d.ts +10 -0
  16. package/dist/playground/cachedClang.js +330 -0
  17. package/dist/playground/clang.d.ts +8 -0
  18. package/dist/playground/clang.js +47 -8
  19. package/dist/playground/clangWorkerProtocol.d.ts +57 -0
  20. package/dist/playground/clangWorkerProtocol.js +11 -0
  21. package/dist/playground/clojurescript.js +1 -0
  22. package/dist/playground/clojurescriptPreflight.d.ts +2 -1
  23. package/dist/playground/clojurescriptPreflight.js +1 -0
  24. package/dist/playground/compiledArtifactCache.d.ts +21 -0
  25. package/dist/playground/compiledArtifactCache.js +67 -0
  26. package/dist/playground/elixir.js +4 -1
  27. package/dist/playground/forth.js +1 -0
  28. package/dist/playground/forthPreflight.d.ts +2 -1
  29. package/dist/playground/forthPreflight.js +1 -0
  30. package/dist/playground/fortran.js +7 -1
  31. package/dist/playground/haskell.js +4 -1
  32. package/dist/playground/haskellAssets.d.ts +2 -1
  33. package/dist/playground/haskellAssets.js +8 -8
  34. package/dist/playground/index.d.ts +1 -1
  35. package/dist/playground/index.js +2 -2
  36. package/dist/playground/j.js +1 -0
  37. package/dist/playground/jPreflight.d.ts +2 -1
  38. package/dist/playground/jPreflight.js +1 -0
  39. package/dist/playground/janet.js +1 -0
  40. package/dist/playground/java.js +20 -8
  41. package/dist/playground/julia.js +1 -0
  42. package/dist/playground/lfortranAssets.d.ts +2 -1
  43. package/dist/playground/lfortranAssets.js +1 -0
  44. package/dist/playground/lisp.js +4 -1
  45. package/dist/playground/lispAssets.d.ts +2 -1
  46. package/dist/playground/lispAssets.js +10 -0
  47. package/dist/playground/lldbManifest.d.ts +2 -2
  48. package/dist/playground/lldbManifest.js +17 -3
  49. package/dist/playground/lldbSession.d.ts +1 -0
  50. package/dist/playground/lldbSession.js +2 -1
  51. package/dist/playground/nim.js +1 -0
  52. package/dist/playground/ocaml.d.ts +1 -0
  53. package/dist/playground/ocaml.js +12 -1
  54. package/dist/playground/options.d.ts +2 -0
  55. package/dist/playground/pascal.js +1 -0
  56. package/dist/playground/perl.js +1 -0
  57. package/dist/playground/prolog.js +1 -0
  58. package/dist/playground/python.d.ts +7 -0
  59. package/dist/playground/python.js +41 -8
  60. package/dist/playground/runtimeAssetCache.d.ts +26 -0
  61. package/dist/playground/runtimeAssetCache.js +84 -0
  62. package/dist/playground/runtimeSession.d.ts +21 -0
  63. package/dist/playground/runtimeSession.js +119 -0
  64. package/dist/playground/rust.d.ts +1 -0
  65. package/dist/playground/rust.js +11 -1
  66. package/dist/playground/rustExecutableGraph.d.ts +1 -0
  67. package/dist/playground/rustExecutableGraph.js +1 -0
  68. package/dist/playground/staticRuntimePreflight.d.ts +2 -1
  69. package/dist/playground/staticRuntimePreflight.js +4 -2
  70. package/dist/playground/staticRuntimePreflightExecute.js +1 -0
  71. package/dist/playground/staticRuntimePreflightProtocol.d.ts +2 -1
  72. package/dist/playground/staticWorkerRuntime.d.ts +3 -1
  73. package/dist/playground/staticWorkerRuntime.js +44 -3
  74. package/dist/playground/tcl.js +1 -0
  75. package/dist/playground/tinygo.d.ts +2 -0
  76. package/dist/playground/tinygo.js +57 -3
  77. package/dist/playground/tinygoExecutableGraph.d.ts +4 -1
  78. package/dist/playground/tinygoExecutableGraph.js +59 -7
  79. package/dist/playground/typescript.js +4 -1
  80. package/dist/playground/wasmGoVersion.d.ts +1 -1
  81. package/dist/playground/wasmGoVersion.js +1 -1
  82. package/dist/playground/wasmOcamlVersion.d.ts +2 -2
  83. package/dist/playground/wasmOcamlVersion.js +1 -1
  84. package/dist/playground/wasmRustVersion.d.ts +90 -24
  85. package/dist/playground/wasmRustVersion.js +98 -23
  86. package/dist/playground/wasmTinyGoVersion.d.ts +40 -29
  87. package/dist/playground/wasmTinyGoVersion.js +43 -31
  88. package/dist/playground/worker/assets.d.ts +3 -0
  89. package/dist/playground/worker/assets.js +41 -0
  90. package/dist/playground/worker/cachedClangCompile.d.ts +1 -0
  91. package/dist/playground/worker/cachedClangCompile.js +85 -0
  92. package/dist/playground/worker/cachedClangExecute.d.ts +1 -0
  93. package/dist/playground/worker/cachedClangExecute.js +67 -0
  94. package/dist/playground/worker/clang.js +2 -8
  95. package/dist/playground/worker/clangStreaming.d.ts +1 -1
  96. package/dist/playground/worker/clangStreaming.js +20 -2
  97. package/dist/playground/worker/elixir.js +10 -8
  98. package/dist/playground/worker/fortran.js +4 -7
  99. package/dist/playground/worker/haskell.js +2 -0
  100. package/dist/playground/worker/lisp.js +2 -0
  101. package/dist/playground/worker/ocaml.js +13 -1
  102. package/dist/playground/worker/python.js +3 -2
  103. package/dist/playground/worker/runtimeAssetFetch.d.ts +11 -1
  104. package/dist/playground/worker/runtimeAssetFetch.js +64 -1
  105. package/dist/playground/worker/runtimeModule.d.ts +8 -0
  106. package/dist/playground/worker/runtimeModule.js +49 -0
  107. package/dist/playground/worker/rust.js +9 -2
  108. package/dist/playground/worker/staticRuntimePreflight.js +2 -0
  109. package/dist/playground/worker/typescript.js +4 -1
  110. package/dist/playground/worker/zig.js +7 -4
  111. package/dist/playground/zig.js +4 -1
  112. package/package.json +10 -7
package/README.md CHANGED
@@ -15,6 +15,10 @@ Compiler source pins, patches, and reproducible asset builds live in
15
15
  [`seo-rii/wasm-llvm`](https://github.com/seo-rii/wasm-llvm); wasm-idle loads the generated
16
16
  artifacts from external URLs instead of embedding them in npm packages.
17
17
 
18
+ Verified downloads can be retained across reloads using a version-pinned, capacity-bounded
19
+ CacheStorage/IndexedDB cache. See [persistent asset cache configuration and coverage](docs/runtime-asset-cache.md)
20
+ for global/per-call opt-out, cleanup and prefetch APIs.
21
+
18
22
  ## Language support policy
19
23
 
20
24
  wasm-idle language support must run user code through the real language implementation in the
@@ -221,9 +225,9 @@ when they exist.
221
225
  | Language / IDs | Package/version base | Execution defaults / flags | Customization |
222
226
  | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
223
227
  | C3<br>`C3` | wasm-llvm/c3-browser: C3 0.8.3 / LLVM 22.1.8 | Verified c3c and builtin LLD compile real C3 in a fresh browser Worker; UTF-8 env.readByte/env.writeByte ABI and exported main; std::io and WASI unavailable | `runtimeAssets.c3.baseUrl`, `stdin`, `activePath`, `workspaceFiles`; fixed compiler flags and bounded Wasm memories |
224
- | C<br>`C` | @wasm-idle/llvm-core@1.0.3 / Clang 22.1.8 WASI sysroot from the `wasm-llvm` producer | `clang` for `wasm32-wasi`; default `-std=gnu11`; LLDB mode compiles untouched source with embedded DWARF at `-g -O0` and runs it in WAMR; normal runs still use the browser WASI host; supports `stdin` and `programArgs` | `runtimeAssets.clang.baseUrl`/`loader` or `rootUrl`; `compileArgs`, `programArgs`, `cVersion`, `activePath`, `workspaceFiles`, `debugMode`, `breakpoints`, `pauseOnEntry` |
225
- | C++<br>`CPP` | @wasm-idle/llvm-core@1.0.3 / Clang 22.1.8 WASI sysroot from the `wasm-llvm` producer | `clang++` for `wasm32-wasi`; default `-std=gnu++20`; LLDB mode compiles untouched source with embedded DWARF at `-g -O0` and runs it in WAMR; normal runs still use the browser WASI host; supports `stdin` and `programArgs` | `runtimeAssets.clang.baseUrl`/`loader` or `rootUrl`; `compileArgs`, `programArgs`, `cppVersion`, `activePath`, `workspaceFiles`, `debugMode`, `breakpoints`, `pauseOnEntry` |
226
- | Objective-C<br>`OBJC` | GNUstep libobjc2 v2.3 assets from the `wasm-llvm` producer + @wasm-idle/llvm-core@1.0.3 | `clang -x objective-c -fobjc-runtime=gnustep-2.0 -fblocks` for `wasm32-wasi`; links `libobjc.a`, `libgnustep-base.a`, and `libffi.a` when Foundation is imported; Foundation headers are inlined from `foundation-headers.json`; includes a constructor wrapper for Objective-C class registration; auto-compiles `.m`/`.mm`/`.c` workspace files; trace debug instruments active source and uses wasm-idle controls; supports `stdin` and `programArgs`; large Objective-C assets may be served as gzip-only `.gz` files through the service worker or worker fallback | `runtimeAssets.objectivec.baseUrl`/`libobjcUrl`/`headersUrl`/`libgnustepBaseUrl`/`libgnustepBaseObjectUrl`/`foundationHeadersUrl`/`libffiUrl` or `PUBLIC_WASM_OBJECTIVEC_*`; `runtimeAssets.clang.baseUrl`/`loader` for the clang toolchain; `activePath`, `workspaceFiles`, `compileArgs`, `debug`, `breakpoints`, `pauseOnEntry` |
228
+ | C<br>`C` | @wasm-idle/llvm-core@1.0.7 / Clang 22.1.8 WASI sysroot from the `wasm-llvm` producer | `clang` for `wasm32-wasi`; default `-std=gnu11`; LLDB mode compiles untouched source with embedded DWARF at `-g -O0` and runs it in WAMR; normal runs still use the browser WASI host; supports `stdin` and `programArgs` | `runtimeAssets.clang.baseUrl`/`loader` or `rootUrl`; `compileArgs`, `programArgs`, `cVersion`, `activePath`, `workspaceFiles`, `debugMode`, `breakpoints`, `pauseOnEntry` |
229
+ | C++<br>`CPP` | @wasm-idle/llvm-core@1.0.7 / Clang 22.1.8 WASI sysroot from the `wasm-llvm` producer | `clang++` for `wasm32-wasi`; default `-std=gnu++20`; LLDB mode compiles untouched source with embedded DWARF at `-g -O0` and runs it in WAMR; normal runs still use the browser WASI host; supports `stdin` and `programArgs` | `runtimeAssets.clang.baseUrl`/`loader` or `rootUrl`; `compileArgs`, `programArgs`, `cppVersion`, `activePath`, `workspaceFiles`, `debugMode`, `breakpoints`, `pauseOnEntry` |
230
+ | Objective-C<br>`OBJC` | GNUstep libobjc2 v2.3 assets from the `wasm-llvm` producer + @wasm-idle/llvm-core@1.0.7 | `clang -x objective-c -fobjc-runtime=gnustep-2.0 -fblocks` for `wasm32-wasi`; links `libobjc.a`, `libgnustep-base.a`, and `libffi.a` when Foundation is imported; Foundation headers are inlined from `foundation-headers.json`; includes a constructor wrapper for Objective-C class registration; auto-compiles `.m`/`.mm`/`.c` workspace files; trace debug instruments active source and uses wasm-idle controls; supports `stdin` and `programArgs`; large Objective-C assets may be served as gzip-only `.gz` files through the service worker or worker fallback | `runtimeAssets.objectivec.baseUrl`/`libobjcUrl`/`headersUrl`/`libgnustepBaseUrl`/`libgnustepBaseObjectUrl`/`foundationHeadersUrl`/`libffiUrl` or `PUBLIC_WASM_OBJECTIVEC_*`; `runtimeAssets.clang.baseUrl`/`loader` for the clang toolchain; `activePath`, `workspaceFiles`, `compileArgs`, `debug`, `breakpoints`, `pauseOnEntry` |
227
231
  | Python<br>`PYTHON3` | static ESM `static/pyodide/pyodide.mjs` / pyodide@0.29.3 | loads `pyodide.mjs`, `pyodide.asm.js`, `pyodide.asm.wasm`, and `python_stdlib.zip` from the configured static asset tree on demand; supports `stdin`, workspace files, and trace debugging | `runtimeAssets.python.baseUrl`/`loader` or `rootUrl`; `stdin`, `activePath`, `workspaceFiles`, `debug`, `breakpoints`, `pauseOnEntry`, `debugPath` |
228
232
  | Java<br>`JAVA` | @wasm-idle/runtime-teavm@0.0.0 / TeaVM compiler assets | `compiler.wasm` compiles Java to browser WASM/JS; supports `stdin` and `programArgs` | `runtimeAssets.java.baseUrl`/`loader` or `rootUrl` |
229
233
  | Rust<br>`RUST` | wasm-rust@0.1.0 / rust-1.99.0-browser-integrated-v1 + integrated LLVM/LLD 22.1.8 from the `wasm-llvm` producer; exact rustc/LLVM provenance is recorded for LLDB compatibility | browser host `wasm32-wasip1-threads`; default target `wasm32-wasip1`, selectable `wasm32-wasip1`, `wasm32-wasip2`, `wasm32-wasip3`; LLDB mode is currently limited to `wasm32-wasip1` and emits embedded DWARF with `-C debuginfo=2 -C opt-level=0`; normal Preview 1/2/3 execution remains unchanged; supports `stdin` and `programArgs` | `runtimeAssets.rust.compilerUrl`, `runtimeAssets.rust.debugModuleUrl`, or `PUBLIC_WASM_RUST_COMPILER_URL`; `rootUrl`, `rustTargetTriple`, `programArgs`, `debugMode`; compiler requests accept `edition`, `crateType`, `extendedTimeout`, `log`, and `onProgress`; runtime manifest controls compiler memory, timeout, and shared workspace size |
@@ -261,8 +265,8 @@ when they exist.
261
265
  | Ruby<br>`RUBY` | receipt-pinned `static/wasm-ruby` profile produced from @ruby/3.4-wasm-wasi@2.9.3-2.9.4 + @ruby/wasm-wasi@2.9.3-2.9.4 | host-verifies the manifest, canonical stored module and gzip bytes, and logical WASM before transferring three owned buffers to a no-fetch CRuby 3.4 worker; supports `stdin`, `programArgs`, and workspace files | `rootUrl` mirrors reuse the bundled profile; explicit `runtimeAssets.ruby` overrides require one complete profile and same-origin canonical paths; URL-only `PUBLIC_WASM_RUBY_*` overrides fail closed; `stdin`, `programArgs`, `workspaceFiles` |
262
266
  | Haskell<br>`HASKELL` | ghc-in-browser / GHC 9.14.0.20251031 WASI rootfs | loads `dyld.mjs`, `rootfs.tar.zst`, `bsdtar.wasm`; `compileArgs` become GHC args, otherwise legacy `args` become GHC args | `runtimeAssets.haskell.moduleUrl`/`rootfsUrl`/`bsdtarUrl`; `mainSoPath`, `searchDirs`, `activePath`, `workspaceFiles` |
263
267
  | LFortran<br>`LFORTRAN` | receipt-pinned `wasm-llvm/producer/lfortran-browser` artifacts in `static/wasm-lfortran` | real LFortran 0.65.0-97-gab867a23 LLVM evaluator compiles and executes Emscripten side modules in a fresh Worker; shared-ring stdin supports delayed READ and EOF; experimental Fortran feature coverage | `runtimeAssets.lfortran.baseUrl` relocates the reviewed bundle; `stdin`, `activePath`, workspace data/include files, cancellation and execution limits; no program arguments or multi-file module build orchestration |
264
- | Fortran<br>`FORTRAN` | Netlib f2c 2022-09-09 + `@cowasm/f2c 1.0.0` libf2c + @wasm-idle/llvm-core@1.0.3 | runs `f2c.wasm` in WASI, compiles generated C with the llvm-core Clang host, links `libf2c.a`, then executes the resulting WASI module with `stdin` and `programArgs` | `runtimeAssets.fortran.baseUrl`/`f2cWasmUrl`/`libf2cUrl`/`f2cHeaderUrl`/`analyzerUrl` or `PUBLIC_WASM_FORTRAN_*`; `runtimeAssets.clang.baseUrl`/`loader` for the C backend; `activePath`, `workspaceFiles`, `compileArgs` |
265
- | COBOL<br>`COBOL` | GnuCOBOL 3.2 + GMP 6.3.0 assets from the `wasm-llvm` producer + @wasm-idle/llvm-core@1.0.3 | native gzip delivery for the frontend Wasm and filesystem tar assets; translates free-format COBOL with the real GnuCOBOL `cobc` frontend, compiles the generated C with the llvm-core Clang host, links libcob/GMP, and executes the resulting WASI module with `stdin` and `programArgs` | `runtimeAssets.cobol.baseUrl` or `PUBLIC_WASM_COBOL_BASE_URL`; `runtimeAssets.clang.baseUrl`/`loader` for the C backend; `activePath`, `workspaceFiles`, `compileArgs` |
268
+ | Fortran<br>`FORTRAN` | Netlib f2c 2022-09-09 + `@cowasm/f2c 1.0.0` libf2c + @wasm-idle/llvm-core@1.0.7 | runs `f2c.wasm` in WASI, compiles generated C with the llvm-core Clang host, links `libf2c.a`, then executes the resulting WASI module with `stdin` and `programArgs` | `runtimeAssets.fortran.baseUrl`/`f2cWasmUrl`/`libf2cUrl`/`f2cHeaderUrl`/`analyzerUrl` or `PUBLIC_WASM_FORTRAN_*`; `runtimeAssets.clang.baseUrl`/`loader` for the C backend; `activePath`, `workspaceFiles`, `compileArgs` |
269
+ | COBOL<br>`COBOL` | GnuCOBOL 3.2 + GMP 6.3.0 assets from the `wasm-llvm` producer + @wasm-idle/llvm-core@1.0.7 | native gzip delivery for the frontend Wasm and filesystem tar assets; translates free-format COBOL with the real GnuCOBOL `cobc` frontend, compiles the generated C with the llvm-core Clang host, links libcob/GMP, and executes the resulting WASI module with `stdin` and `programArgs` | `runtimeAssets.cobol.baseUrl` or `PUBLIC_WASM_COBOL_BASE_URL`; `runtimeAssets.clang.baseUrl`/`loader` for the C backend; `activePath`, `workspaceFiles`, `compileArgs` |
266
270
  | R<br>`R` | versioned static `static/webr/<hash>/webr.js` / webr@0.6.0 | loads the browser ESM entry `webr.js` and its WebR runtime files from the configured static asset tree on demand; supports `stdin`, `programArgs`, and workspace files | `runtimeAssets.r.baseUrl` or `PUBLIC_WASM_R_BASE_URL`; `stdin`, `programArgs`, `activePath`, `workspaceFiles` |
267
271
  | Octave<br>`OCTAVE` | Octave 10.3.0 (octave-10.3.0-pl5321h996e327_3.tar.bz2) | Octave CLI Emscripten worker; supports `stdin` and `programArgs` | `runtimeAssets.octave.baseUrl`/`workerUrl`/`manifestUrl` or `PUBLIC_WASM_OCTAVE_*` |
268
272
  | DuckDB<br>`DUCKDB` | static ESM `static/wasm-duckdb/runtime.mjs` produced from @duckdb/duckdb-wasm@1.33.1-dev45.0 | selects the best DuckDB-Wasm MVP/EH bundle on demand and creates a fresh in-memory database per run; `stdin` is registered as `stdin.txt` and `/dev/stdin` rather than terminal stdin | `runtimeAssets.duckdb.moduleUrl` or `PUBLIC_WASM_DUCKDB_MODULE_URL` or `rootUrl`; `stdin`, `activePath`, `workspaceFiles` |
@@ -323,6 +327,15 @@ toolchains can be consumed from pinned external runtime repositories:
323
327
  normal runtime workspace packages. `tools/dool` contains the Docker judge backend for Elixir and
324
328
  the other server-side language runners.
325
329
 
330
+ Deployment builds retain both legacy `.gz` and canonical `.gz.bin` URLs by default.
331
+ After building and compressing the page, `pnpm run compact:build-runtimes` reports
332
+ byte-identical duplicates without changing files. Add `--drop-legacy-aliases` to
333
+ remove those duplicate files and add compatibility routing to the generated service
334
+ worker. This only preserves legacy URLs in service-worker-controlled requests:
335
+ external SDKs and uncontrolled first requests to the removed `.gz` URLs need to
336
+ migrate to `.gz.bin` or use server aliases. Opt in only after that migration. Source
337
+ assets and producer receipts stay intact; rerun compaction after each page build.
338
+
326
339
  Useful workspace commands:
327
340
 
328
341
  ```bash
@@ -900,6 +913,40 @@ The pack contains the same locked upstream files, not an import-based subset. Ea
900
913
 
901
914
  `node scripts/sync-runtime.mjs wasm-ruby` regenerates both profiles from the pinned producer inputs. `node scripts/sync-wasm-ruby-split.mjs --verify` additionally checks split-profile freshness without replacing published files.
902
915
 
916
+ ### Retaining compiler caches across editor navigation
917
+
918
+ Create one `createRuntimeSession(assets)` owner per browser application and use
919
+ `session.createBinding()` for each editor or execution panel. C/C++ and Python
920
+ keep immutable compiler/runtime modules outside their disposable execution Workers:
921
+
922
+ ```ts
923
+ import { createRuntimeSession } from 'wasm-idle';
924
+
925
+ const session = createRuntimeSession(assets); // browser scope, not an SSR singleton
926
+ const panel = session.createBinding();
927
+ const sandbox = await panel.load('PYTHON');
928
+ await sandbox.load('print("hi")', false);
929
+ await sandbox.clear(); // reset execution state; retain compiler/runtime cache
930
+ await panel.dispose(); // close this panel; retain the session's cache
931
+
932
+ const nextPanel = session.createBinding(); // another problem, same language
933
+ await nextPanel.load('PYTHON');
934
+ await session.selectLanguage('CPP'); // release old-language panels and cache
935
+ await session.dispose(); // full, terminal release of this owner
936
+ ```
937
+
938
+ Selecting a language does not download assets. The owner retains only one language;
939
+ selecting an unsupported/server-only language also releases the previous cache.
940
+ Executions never share mutable Wasm instances, user files or stdin. C/C++ additionally
941
+ reuse compiled programs when source, workspace and compiler options match, while
942
+ custom loaders and debugging retain the original execution path. Python still creates
943
+ a fresh interpreter after `clear()`; module reuse does not eliminate interpreter startup.
944
+
945
+ The asset cache is LRU-bounded to 64 entries and 256 MiB of logical asset bytes
946
+ (not a measurement of the browser engine's compiled-code memory). C/C++ program
947
+ artifacts are independently bounded to 8 entries and 32 MiB. `getCacheStats()` exposes
948
+ cache counters for diagnostics. Other languages currently keep their existing lifecycle.
949
+
903
950
  ### Optional idle runtime prewarming
904
951
 
905
952
  `createPlaygroundBinding(assets, { prewarm: true })` enables explicit
package/dist/index.d.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import playground, { createPlaygroundBinding } from './playground';
2
2
  export default playground;
3
3
  export { createPlaygroundBinding, playground };
4
+ export { createRuntimeSession } from './playground/runtimeSession';
5
+ export { configureRuntimeAssetCache, createRuntimeAssetCacheBackend, fetchPinnedRuntimeAsset, prefetchRuntimeAssets, resolveRuntimeAssetLockEntry, type PrefetchRuntimeAssetsOptions, type PrefetchRuntimeAssetsResult, type RuntimeAssetCacheStats, type RuntimeAssetCacheBackend, type RuntimeAssetCacheBackendIdentity, type PinnedRuntimeAssetRequest, getRuntimeAssetCacheOptions, getRuntimeAssetCacheStats, clearRuntimeAssetCache, pruneRuntimeAssetCache, requestRuntimeAssetCachePersistence, type RuntimeAssetCacheOptions } from '@wasm-idle/core';
6
+ export type { PlaygroundRuntimeSession } from './playground/runtimeSession';
4
7
  export { isSharedArrayBufferAvailable, requireSharedArrayBuffer } from './playground/sharedBuffer';
5
8
  export type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, TerminalControl } from '@wasm-idle/core';
6
9
  export type { CompilerDiagnostic, SandboxExecutionOptions } from './playground/options';
7
10
  export type { PlaygroundRuntimeAssets, RuntimeAssetConfig, RuntimeAssetLoadRequest, RuntimeAssetLoader, RuntimeAssetLoaderResult } from './playground/assets';
8
11
  export type { BoundSandbox, PlaygroundBinding, PlaygroundTerminalProps, SandboxProgress, SandboxRuntimeAssets } from './playground/sandbox';
9
- export type { PlaygroundBindingOptions } from "@wasm-idle/core";
12
+ export type { PlaygroundBindingOptions } from '@wasm-idle/core';
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import playground, { createPlaygroundBinding } from './playground';
2
2
  export default playground;
3
3
  export { createPlaygroundBinding, playground };
4
+ export { createRuntimeSession } from './playground/runtimeSession';
5
+ export { configureRuntimeAssetCache, createRuntimeAssetCacheBackend, fetchPinnedRuntimeAsset, prefetchRuntimeAssets, resolveRuntimeAssetLockEntry, getRuntimeAssetCacheOptions, getRuntimeAssetCacheStats, clearRuntimeAssetCache, pruneRuntimeAssetCache, requestRuntimeAssetCachePersistence } from '@wasm-idle/core';
4
6
  export { isSharedArrayBufferAvailable, requireSharedArrayBuffer } from './playground/sharedBuffer';
@@ -1,10 +1,14 @@
1
1
  import { type ResolvedRuntimeAssetConfig, type RuntimeAssetRuntime } from './assets';
2
- import { type ProgressLike } from '@wasm-idle/core';
2
+ import { type RuntimeAssetCacheOptions, type ResolvedRuntimeAssetCacheOptions, type ProgressLike } from '@wasm-idle/core';
3
+ import { RuntimeAssetCache } from './runtimeAssetCache';
3
4
  export declare const boundedUtf8ByteLength: (value: string, maxBytes?: number) => number;
4
5
  export declare class WorkerAssetBridge {
6
+ private readonly cache?;
5
7
  private worker;
6
8
  private readonly runtime;
7
9
  private config;
10
+ private persistentCacheBaseline;
11
+ private readonly persistentCacheBySignal;
8
12
  private progress;
9
13
  private expectedAssets;
10
14
  private languageSysroots;
@@ -13,8 +17,10 @@ export declare class WorkerAssetBridge {
13
17
  private state;
14
18
  private readonly activeLoads;
15
19
  private maxAssetBytes;
16
- constructor(worker: Worker, runtime: RuntimeAssetRuntime, config: ResolvedRuntimeAssetConfig, progress?: ProgressLike, maxAssetBytes?: number, languageSysroots?: boolean);
20
+ constructor(worker: Worker, runtime: RuntimeAssetRuntime, config: ResolvedRuntimeAssetConfig, progress?: ProgressLike, maxAssetBytes?: number, languageSysroots?: boolean, cache?: RuntimeAssetCache | undefined);
17
21
  matches(config: ResolvedRuntimeAssetConfig, maxAssetBytes?: number, languageSysroots?: boolean): boolean;
22
+ /** Apply one execution's policy without changing the configuration established by load(). */
23
+ setExecutionPersistentCache(override?: RuntimeAssetCacheOptions): ResolvedRuntimeAssetCacheOptions;
18
24
  rebind(worker: Worker, config: ResolvedRuntimeAssetConfig, progress?: ProgressLike, maxAssetBytes?: number, languageSysroots?: boolean): void;
19
25
  dispose(): void;
20
26
  resetProgress(progress?: ProgressLike): void;
@@ -23,10 +29,13 @@ export declare class WorkerAssetBridge {
23
29
  private sourceAssetByteLimit;
24
30
  handleMessage(event: MessageEvent<any>): boolean;
25
31
  private respond;
32
+ private loadVerifiedAsset;
33
+ private validateAssetRequest;
26
34
  private loadAsset;
27
35
  private verifyIntegrity;
28
36
  private normalizeLoaderResult;
29
37
  private fetchAsset;
38
+ private fetchAssetFromNetwork;
30
39
  private requireAllowedAssetUrl;
31
40
  private abortActiveLoads;
32
41
  }
@@ -1,9 +1,12 @@
1
1
  import { RUNTIME_LOAD_ASSETS } from './assets';
2
- import { ProtocolError, verifyRuntimeAssetIntegrity, verifyRuntimeAssetPair } from '@wasm-idle/core';
2
+ import { ProtocolError, verifyRuntimeAssetIntegrity, verifyRuntimeAssetPair, readPersistentRuntimeAsset, writePersistentRuntimeAsset, resolveRuntimeAssetLockEntry, resolveRuntimeAssetCacheOptions } from '@wasm-idle/core';
3
3
  import { decompressGzip } from '@wasm-idle/llvm-core';
4
4
  import { readPythonPackageAssets } from './pythonPackageLock';
5
5
  import { BUNDLED_CLANG_LANGUAGE_SYSROOT_PROFILES } from './clangAssetIntegrity';
6
6
  import { shouldStreamBundledClang } from './clangStreamingPolicy';
7
+ import { compileVerifiedWasmAsset } from '@wasm-idle/llvm-core/core/verified-wasm';
8
+ import { BUNDLED_CLANG_ASSET_INTEGRITY } from './clangAssetIntegrity';
9
+ import { RuntimeAssetCache } from './runtimeAssetCache';
7
10
  const encoder = new TextEncoder();
8
11
  const DEFAULT_STREAM_BUFFER_BYTES = 64 * 1024;
9
12
  const MAX_RUNTIME_ASSET_BYTES = 128 * 1024 * 1024;
@@ -372,9 +375,12 @@ class RuntimeLoadProgress {
372
375
  }
373
376
  }
374
377
  export class WorkerAssetBridge {
378
+ cache;
375
379
  worker;
376
380
  runtime;
377
381
  config;
382
+ persistentCacheBaseline;
383
+ persistentCacheBySignal = new WeakMap();
378
384
  progress;
379
385
  expectedAssets;
380
386
  languageSysroots;
@@ -383,10 +389,12 @@ export class WorkerAssetBridge {
383
389
  state = 'active';
384
390
  activeLoads = new Set();
385
391
  maxAssetBytes;
386
- constructor(worker, runtime, config, progress, maxAssetBytes = MAX_RUNTIME_ASSET_BYTES, languageSysroots = false) {
392
+ constructor(worker, runtime, config, progress, maxAssetBytes = MAX_RUNTIME_ASSET_BYTES, languageSysroots = false, cache) {
393
+ this.cache = cache;
387
394
  this.worker = worker;
388
395
  this.runtime = runtime;
389
- this.config = config;
396
+ this.persistentCacheBaseline = resolveRuntimeAssetCacheOptions(config.persistentCache);
397
+ this.config = { ...config, persistentCache: this.persistentCacheBaseline };
390
398
  this.maxAssetBytes = requireBridgeMaxAssetBytes(maxAssetBytes);
391
399
  this.languageSysroots = canUseClangLanguageSysroots(runtime, config, languageSysroots);
392
400
  this.progress = new RuntimeLoadProgress(runtime, this.languageSysroots);
@@ -402,7 +410,17 @@ export class WorkerAssetBridge {
402
410
  this.config.loader === config.loader &&
403
411
  integrityKey(this.config) === integrityKey(config) &&
404
412
  allowedBaseUrlsKey(this.config) === allowedBaseUrlsKey(config) &&
405
- this.config.useAssetBridge === config.useAssetBridge);
413
+ this.config.useAssetBridge === config.useAssetBridge &&
414
+ JSON.stringify(this.persistentCacheBaseline) ===
415
+ JSON.stringify(resolveRuntimeAssetCacheOptions(config.persistentCache)));
416
+ }
417
+ /** Apply one execution's policy without changing the configuration established by load(). */
418
+ setExecutionPersistentCache(override) {
419
+ if (this.state !== 'active')
420
+ throw new Error('Cannot configure an inactive worker asset bridge');
421
+ const cache = resolveRuntimeAssetCacheOptions(this.persistentCacheBaseline, override);
422
+ this.config = { ...this.config, persistentCache: cache };
423
+ return cache;
406
424
  }
407
425
  rebind(worker, config, progress, maxAssetBytes = this.maxAssetBytes, languageSysroots = this.languageSysroots) {
408
426
  if (this.state === 'disposed') {
@@ -425,7 +443,8 @@ export class WorkerAssetBridge {
425
443
  throw new Error('Cannot rebind a disposed worker asset bridge');
426
444
  }
427
445
  this.worker = worker;
428
- this.config = config;
446
+ this.persistentCacheBaseline = resolveRuntimeAssetCacheOptions(config.persistentCache);
447
+ this.config = { ...config, persistentCache: this.persistentCacheBaseline };
429
448
  this.maxAssetBytes = nextMaxAssetBytes;
430
449
  this.languageSysroots = nextLanguageSysroots;
431
450
  this.expectedAssets = expectedAssetsForRuntime(this.runtime, nextLanguageSysroots);
@@ -520,53 +539,75 @@ export class WorkerAssetBridge {
520
539
  const worker = this.worker;
521
540
  const generation = this.generation;
522
541
  const controller = new AbortController();
542
+ this.persistentCacheBySignal.set(controller.signal, resolveRuntimeAssetCacheOptions(this.config.persistentCache));
523
543
  this.activeLoads.add(controller);
524
544
  try {
525
- const loaded = await this.loadAsset(request.asset, controller.signal);
526
- const deliveryBytes = canonicalUint8Array(loaded.bytes);
527
- const sourceAssetByteLimit = this.sourceAssetByteLimit(request.asset);
528
- const runtimeAssetByteLimit = this.runtimeAssetByteLimit(request.asset);
529
- requireRuntimeAssetSize(request.asset, deliveryBytes.byteLength, sourceAssetByteLimit);
530
- let normalizedRuntimeBytes;
531
- if (request.asset.endsWith('.gz')) {
532
- this.progress.activity('decompressing', request.asset);
533
- normalizedRuntimeBytes = await decompressGzip(deliveryBytes, request.asset, runtimeAssetByteLimit, controller.signal);
534
- }
535
- else {
536
- normalizedRuntimeBytes = deliveryBytes;
537
- }
538
- const runtimeBytes = canonicalUint8Array(normalizedRuntimeBytes);
539
- requireRuntimeAssetSize(request.asset, runtimeBytes.byteLength, runtimeAssetByteLimit);
540
- const httpDecodedGzip = (loaded.contentEncoding || '')
541
- .toLowerCase()
542
- .split(',')
543
- .map((encoding) => encoding.trim())
544
- .includes('gzip');
545
- if (controller.signal.aborted || generation !== this.generation)
546
- return;
547
- let cancelOnAbort;
548
- const aborted = new Promise((_resolve, reject) => {
549
- cancelOnAbort = () => reject(runtimeAssetAbortReason(controller.signal));
550
- controller.signal.addEventListener('abort', cancelOnAbort, { once: true });
551
- });
552
- try {
553
- if (this.config.integrity?.[request.asset]) {
554
- this.progress.activity('verifying', request.asset);
545
+ this.validateAssetRequest(request.asset);
546
+ const key = JSON.stringify([
547
+ this.runtime,
548
+ this.config.baseUrl,
549
+ this.cache?.identity(this.config.loader),
550
+ integrityKey(this.config),
551
+ allowedBaseUrlsKey(this.config),
552
+ this.config.useAssetBridge,
553
+ this.maxAssetBytes,
554
+ this.languageSysroots,
555
+ request.asset,
556
+ request.module === true ? 'module' : 'bytes'
557
+ ]);
558
+ if (request.module === true) {
559
+ if (!/\.wasm(?:\.gz)?$/u.test(request.asset)) {
560
+ throw new Error('Only runtime Wasm assets can be compiled');
555
561
  }
556
- const verification = this.verifyIntegrity(request.asset, deliveryBytes, runtimeBytes, loaded.mimeType, !httpDecodedGzip);
557
- await Promise.race([verification, aborted]);
562
+ let module = this.cache?.get(key);
563
+ if (!module) {
564
+ const streaming = this.runtime === 'clang' &&
565
+ shouldStreamBundledClang(this.config) &&
566
+ (request.asset === 'bin/clang.wasm.gz' ||
567
+ request.asset === 'bin/lld.wasm.gz');
568
+ let bytes;
569
+ if (streaming) {
570
+ const receipt = BUNDLED_CLANG_ASSET_INTEGRITY[request.asset];
571
+ module = await compileVerifiedWasmAsset(new URL(request.asset, this.config.baseUrl).href, receipt, {
572
+ fetch: globalThis.fetch.bind(globalThis),
573
+ persistentCache: this.persistentCacheBySignal.get(controller.signal),
574
+ maxAssetBytes: this.maxAssetBytes,
575
+ signal: controller.signal,
576
+ onProgress: (loaded, total) => this.progress.update(request.asset, loaded, total)
577
+ });
578
+ bytes = receipt.uncompressedBytes;
579
+ }
580
+ else {
581
+ const loaded = await this.loadVerifiedAsset(request.asset, controller.signal);
582
+ bytes = loaded.bytes.byteLength;
583
+ module = await WebAssembly.compile(Uint8Array.from(loaded.bytes));
584
+ }
585
+ if (controller.signal.aborted || generation !== this.generation)
586
+ return;
587
+ this.cache?.set(key, module, bytes);
588
+ }
589
+ if (controller.signal.aborted || generation !== this.generation)
590
+ return;
591
+ worker.postMessage({ assetResponse: { id: request.id, ok: true, module } });
592
+ return;
558
593
  }
559
- finally {
560
- if (cancelOnAbort) {
561
- controller.signal.removeEventListener('abort', cancelOnAbort);
594
+ let loaded = this.cache?.get(key);
595
+ if (!loaded) {
596
+ loaded = await this.loadVerifiedAsset(request.asset, controller.signal);
597
+ if (controller.signal.aborted || generation !== this.generation)
598
+ return;
599
+ // Keep private copies: never transfer or lend the owner cache's backing buffer.
600
+ if (this.cache) {
601
+ loaded = { bytes: Uint8Array.from(loaded.bytes), mimeType: loaded.mimeType };
602
+ this.cache.set(key, loaded, loaded.bytes.byteLength);
562
603
  }
563
604
  }
564
- if (controller.signal.aborted || generation !== this.generation)
565
- return;
566
605
  if (this.runtime === 'python' && request.asset === 'pyodide-lock.json') {
567
- this.pythonPackageAssets = readPythonPackageAssets(runtimeBytes);
606
+ this.pythonPackageAssets = readPythonPackageAssets(loaded.bytes);
568
607
  }
569
- const buffer = transferBuffer(runtimeBytes, normalizedRuntimeBytes === deliveryBytes ? loaded.transferOwnership : true);
608
+ if (controller.signal.aborted || generation !== this.generation)
609
+ return;
610
+ const buffer = transferBuffer(loaded.bytes, this.cache ? false : loaded.transferOwnership);
570
611
  worker.postMessage({
571
612
  assetResponse: {
572
613
  id: request.id,
@@ -589,20 +630,73 @@ export class WorkerAssetBridge {
589
630
  });
590
631
  }
591
632
  catch {
592
- // The worker may already be terminated. There is no remaining response channel.
633
+ // The worker may already be terminated.
593
634
  }
594
635
  }
595
636
  finally {
596
637
  this.activeLoads.delete(controller);
597
638
  }
598
639
  }
599
- async loadAsset(asset, signal) {
640
+ async loadVerifiedAsset(asset, signal) {
641
+ const loaded = await this.loadAsset(asset, signal);
642
+ const deliveryBytes = canonicalUint8Array(loaded.bytes);
643
+ const sourceAssetByteLimit = this.sourceAssetByteLimit(asset);
644
+ const runtimeAssetByteLimit = this.runtimeAssetByteLimit(asset);
645
+ requireRuntimeAssetSize(asset, deliveryBytes.byteLength, sourceAssetByteLimit);
646
+ let normalizedRuntimeBytes;
647
+ if (asset.endsWith('.gz')) {
648
+ this.progress.activity('decompressing', asset);
649
+ normalizedRuntimeBytes = await decompressGzip(deliveryBytes, asset, runtimeAssetByteLimit, signal);
650
+ }
651
+ else {
652
+ normalizedRuntimeBytes = deliveryBytes;
653
+ }
654
+ const runtimeBytes = canonicalUint8Array(normalizedRuntimeBytes);
655
+ requireRuntimeAssetSize(asset, runtimeBytes.byteLength, runtimeAssetByteLimit);
656
+ const httpDecodedGzip = (loaded.contentEncoding || '')
657
+ .toLowerCase()
658
+ .split(',')
659
+ .map((encoding) => encoding.trim())
660
+ .includes('gzip');
661
+ if (signal.aborted)
662
+ throw runtimeAssetAbortReason(signal);
663
+ let cancelOnAbort;
664
+ const aborted = new Promise((_resolve, reject) => {
665
+ cancelOnAbort = () => reject(runtimeAssetAbortReason(signal));
666
+ signal.addEventListener('abort', cancelOnAbort, { once: true });
667
+ });
668
+ try {
669
+ if (this.config.integrity?.[asset]) {
670
+ this.progress.activity('verifying', asset);
671
+ }
672
+ const verification = this.verifyIntegrity(asset, deliveryBytes, runtimeBytes, loaded.mimeType, !httpDecodedGzip);
673
+ await Promise.race([verification, aborted]);
674
+ }
675
+ finally {
676
+ if (cancelOnAbort) {
677
+ signal.removeEventListener('abort', cancelOnAbort);
678
+ }
679
+ }
680
+ if (signal.aborted)
681
+ throw runtimeAssetAbortReason(signal);
682
+ if (this.runtime === 'python' && asset === 'pyodide-lock.json')
683
+ readPythonPackageAssets(runtimeBytes);
684
+ return {
685
+ bytes: runtimeBytes,
686
+ mimeType: loaded.mimeType,
687
+ transferOwnership: normalizedRuntimeBytes === deliveryBytes ? loaded.transferOwnership : true
688
+ };
689
+ }
690
+ validateAssetRequest(asset) {
600
691
  if (!this.expectedAssets.has(asset) && !this.pythonPackageAssets.has(asset)) {
601
692
  throw new Error(`Unexpected ${this.runtime} runtime asset: ${asset}`);
602
693
  }
603
694
  if (this.config.integrity && !Object.hasOwn(this.config.integrity, asset)) {
604
695
  throw new Error(`Runtime asset ${asset} is missing integrity metadata`);
605
696
  }
697
+ }
698
+ async loadAsset(asset, signal) {
699
+ this.validateAssetRequest(asset);
606
700
  if (signal.aborted) {
607
701
  throw runtimeAssetAbortReason(signal);
608
702
  }
@@ -753,6 +847,56 @@ export class WorkerAssetBridge {
753
847
  return null;
754
848
  }
755
849
  async fetchAsset(url, asset, signal, maxAssetBytes = this.sourceAssetByteLimit(asset)) {
850
+ const requestUrl = this.requireAllowedAssetUrl(asset, url);
851
+ const persistentCache = this.persistentCacheBySignal.get(signal) ?? this.config.persistentCache;
852
+ const configured = this.config.integrity?.[asset];
853
+ const expected = typeof configured === 'string' ? { sha256: configured } : configured;
854
+ const lock = this.config.assetPrefix
855
+ ? resolveRuntimeAssetLockEntry(requestUrl, {
856
+ assetRoot: this.config.baseUrl,
857
+ assetPrefix: this.config.assetPrefix
858
+ })
859
+ : undefined;
860
+ // Existing .gz receipts without a pair describe logical bytes, not stored gzip bytes.
861
+ const receipt = expected && (!asset.endsWith('.gz') || expected.uncompressedSha256) ? expected : lock;
862
+ const identity = receipt
863
+ ? {
864
+ url: requestUrl.href,
865
+ sha256: receipt.sha256,
866
+ bytes: receipt.bytes,
867
+ validationKey: JSON.stringify([
868
+ 'bridge-v1',
869
+ this.config.baseUrl,
870
+ allowedBaseUrlsKey(this.config),
871
+ maxAssetBytes
872
+ ])
873
+ }
874
+ : undefined;
875
+ if (identity && (identity.bytes === undefined || identity.bytes <= maxAssetBytes)) {
876
+ const bytes = await readPersistentRuntimeAsset({
877
+ identity,
878
+ cache: persistentCache,
879
+ signal
880
+ });
881
+ if (bytes) {
882
+ requireRuntimeAssetSize(asset, bytes.byteLength, maxAssetBytes);
883
+ this.progress.update(asset, bytes.byteLength, bytes.byteLength);
884
+ return { bytes, mimeType: receipt?.mediaType, transferOwnership: true };
885
+ }
886
+ }
887
+ const loaded = await this.fetchAssetFromNetwork(url, asset, signal, maxAssetBytes);
888
+ if (identity && !loaded.contentEncoding) {
889
+ // Only checksum-matching bytes are published. Existing runtime validation still runs.
890
+ await writePersistentRuntimeAsset({
891
+ identity,
892
+ cache: persistentCache,
893
+ signal,
894
+ bytes: loaded.bytes
895
+ });
896
+ }
897
+ return loaded;
898
+ }
899
+ async fetchAssetFromNetwork(url, asset, signal, maxAssetBytes = this.sourceAssetByteLimit(asset)) {
756
900
  const requestUrl = this.requireAllowedAssetUrl(asset, url);
757
901
  if (signal.aborted)
758
902
  throw runtimeAssetAbortReason(signal);
@@ -1,3 +1,4 @@
1
+ import { type RuntimeAssetCacheOptions } from '@wasm-idle/core';
1
2
  import { type DOuterAssetReceipts } from './dOuterAssets';
2
3
  import type { ElixirRuntimeAssetReceipts } from './elixirAssets';
3
4
  import { type FortranExecutionAssetReceipts } from './fortranAssets';
@@ -25,6 +26,7 @@ export type RuntimeAssetLoader = (request: RuntimeAssetLoadRequest) => RuntimeAs
25
26
  export type RuntimeAssetIntegrityEntry = CoreRuntimeAssetIntegrityEntry;
26
27
  export type RuntimeAssetIntegrityMap = Record<string, string | RuntimeAssetIntegrityEntry>;
27
28
  export interface RuntimeAssetConfig {
29
+ persistentCache?: import('@wasm-idle/core').RuntimeAssetCacheOptions;
28
30
  baseUrl?: string;
29
31
  loader?: RuntimeAssetLoader;
30
32
  loaderKey?: string;
@@ -139,11 +141,14 @@ export interface LispRuntimeAssetConfig {
139
141
  moduleUrl?: string;
140
142
  manifestUrl?: string;
141
143
  manifestFingerprint?: string;
144
+ /** Optional raw JSON byte receipt; distinct from the canonical manifest fingerprint. */
145
+ manifestReceipt?: RuntimeAssetIntegrityEntry;
142
146
  }
143
147
  export interface ResolvedLispRuntimeAssetConfig {
144
148
  moduleUrl: string;
145
149
  manifestUrl: string;
146
150
  manifestFingerprint: string;
151
+ manifestReceipt?: RuntimeAssetIntegrityEntry;
147
152
  }
148
153
  export interface RubyRuntimeAssetConfig {
149
154
  /** Keep full stdlib compatibility, but load its data separately from the core Wasm. */
@@ -563,6 +568,9 @@ export interface TinyGoRuntimeAssetLoaderRequest {
563
568
  export type TinyGoRuntimeAssetLoaderResult = RuntimeAssetDataResult | RuntimeAssetUrlResult | string | URL | ArrayBuffer | Uint8Array | Blob | null | undefined;
564
569
  export type TinyGoRuntimeAssetLoader = (request: TinyGoRuntimeAssetLoaderRequest) => TinyGoRuntimeAssetLoaderResult | Promise<TinyGoRuntimeAssetLoaderResult>;
565
570
  export interface ResolvedRuntimeAssetConfig {
571
+ persistentCache?: import('@wasm-idle/core').RuntimeAssetCacheOptions;
572
+ /** Built-in lock prefix; custom loaders use only explicitly supplied receipts. */
573
+ assetPrefix?: string;
566
574
  baseUrl: string;
567
575
  loader?: RuntimeAssetLoader;
568
576
  integrity?: RuntimeAssetIntegrityMap;
@@ -607,7 +615,7 @@ export declare const RUNTIME_LOAD_ASSETS: {
607
615
  clang: readonly ["runtime-manifest.v1.json", "bin/memfs.wasm.gz", "bin/clang.wasm.gz", "bin/lld.wasm.gz", "bin/sysroot.tar.gz"];
608
616
  clangd: readonly ["clangd.js", "clangd.wasm.gz"];
609
617
  };
610
- export declare function resolveRuntimeAssetConfig(runtime: RuntimeAssetRuntime, options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): ResolvedRuntimeAssetConfig;
618
+ export declare function resolveRuntimeAssetConfig(runtime: RuntimeAssetRuntime, options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string, callCache?: RuntimeAssetCacheOptions): ResolvedRuntimeAssetConfig;
611
619
  export declare function resolveRustCompilerUrl(options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): string;
612
620
  export declare function resolveDebugRuntimeUrls(options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): {
613
621
  baseUrl: string;
@@ -1,4 +1,5 @@
1
1
  import { env as dynamicPublicEnv } from '$env/dynamic/public';
2
+ import { resolveRuntimeAssetCacheOptions, resolveRuntimeAssetLockEntry } from '@wasm-idle/core';
2
3
  import { BUNDLED_CLANG_ASSET_INTEGRITY } from './clangAssetIntegrity';
3
4
  import { snapshotDOuterAssetConfig } from './dOuterAssets';
4
5
  import { snapshotFortranExecutionAssetReceipts } from './fortranAssets';
@@ -100,7 +101,21 @@ const RUNTIME_ASSET_FOLDERS = {
100
101
  const resolveRuntimeRootBaseUrl = (config, rootUrl = '', currentUrl = '') => config.resolveRootBaseUrl?.(rootUrl, currentUrl) ||
101
102
  resolveFolderRuntimeBaseUrl(config.folder, rootUrl, currentUrl);
102
103
  const resolveRuntimeConfiguredBaseUrl = (config, baseUrl, currentUrl = '') => config.resolveConfiguredBaseUrl?.(baseUrl, currentUrl) || normalizeBaseUrl(baseUrl, currentUrl);
103
- export function resolveRuntimeAssetConfig(runtime, options, currentUrl = '') {
104
+ export function resolveRuntimeAssetConfig(runtime, options, currentUrl = '', callCache) {
105
+ const config = resolveRuntimeAssetConfigWithoutCache(runtime, options, currentUrl);
106
+ const runtimeConfig = typeof options === 'object' ? options?.[runtime] : undefined;
107
+ const persistentCache = resolveRuntimeAssetCacheOptions(typeof options === 'object' ? options?.persistentCache : undefined, runtimeConfig?.persistentCache, callCache);
108
+ const assetPrefix = !config.loader && !runtimeConfig?.baseUrl
109
+ ? RUNTIME_ASSET_FOLDERS[runtime].folder
110
+ : undefined;
111
+ return {
112
+ ...config,
113
+ persistentCache,
114
+ assetPrefix,
115
+ useAssetBridge: config.useAssetBridge || (persistentCache.enabled && !!assetPrefix)
116
+ };
117
+ }
118
+ function resolveRuntimeAssetConfigWithoutCache(runtime, options, currentUrl = '') {
104
119
  const runtimeFolder = RUNTIME_ASSET_FOLDERS[runtime];
105
120
  if (typeof options === 'string') {
106
121
  const integrity = resolveRuntimeAssetIntegrity(runtime, undefined);
@@ -733,12 +748,21 @@ export function resolveLispRuntimeAssetConfig(options, currentUrl = '') {
733
748
  const configuredFingerprint = configured?.manifestFingerprint?.trim() ||
734
749
  (publicEnv.PUBLIC_WASM_LISP_MANIFEST_FINGERPRINT || '').trim();
735
750
  const usesBundledRoot = !configuredModuleUrl && !publicModuleUrl && rootUrl !== undefined;
751
+ const manifestUrl = configuredManifestUrl
752
+ ? resolveConfiguredUrl(configuredManifestUrl, currentUrl)
753
+ : deriveLispManifestUrl(moduleUrl, currentUrl);
754
+ const manifestReceipt = configured?.manifestReceipt ??
755
+ (usesBundledRoot && !configuredManifestUrl && !configuredFingerprint
756
+ ? resolveRuntimeAssetLockEntry(manifestUrl, {
757
+ assetRoot: new URL('./', new URL(moduleUrl, currentUrl || 'http://localhost/')),
758
+ assetPrefix: 'wasm-lisp'
759
+ })
760
+ : undefined);
736
761
  return {
737
762
  moduleUrl,
738
- manifestUrl: configuredManifestUrl
739
- ? resolveConfiguredUrl(configuredManifestUrl, currentUrl)
740
- : deriveLispManifestUrl(moduleUrl, currentUrl),
741
- manifestFingerprint: configuredFingerprint || (usesBundledRoot ? WASM_LISP_ASSET_VERSION : '')
763
+ manifestUrl,
764
+ manifestFingerprint: configuredFingerprint || (usesBundledRoot ? WASM_LISP_ASSET_VERSION : ''),
765
+ ...(manifestReceipt ? { manifestReceipt: Object.freeze({ ...manifestReceipt }) } : {})
742
766
  };
743
767
  }
744
768
  const RUBY_RUNTIME_CONFIG_KEYS = [
@@ -40,6 +40,7 @@ class Awk extends StaticWorkerRuntimeSandbox {
40
40
  manifestUrl: resolved.manifestUrl,
41
41
  profile,
42
42
  limits: context.limits,
43
+ persistentCache: context.persistentCache,
43
44
  signal: context.signal,
44
45
  reportProgress(progress) {
45
46
  loadedByAsset.set(progress.assetKey, progress.loadedBytes);
@@ -14,6 +14,7 @@ declare class Bash implements Sandbox {
14
14
  private activeOperation;
15
15
  private readyWorker;
16
16
  private loadedConfig;
17
+ private persistentCache?;
17
18
  private requestUid;
18
19
  private sessionUid;
19
20
  private disposed;