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
@@ -1,4 +1,4 @@
1
- import { type ExecutionLimits, type RuntimeAssetPreflightProgress, type RuntimeErrorCode, type RuntimePhase, type RuntimeResourceKind } from '@wasm-idle/core';
1
+ import { type ExecutionLimits, type RuntimeAssetCacheOptions, type RuntimeAssetPreflightProgress, type RuntimeErrorCode, type RuntimePhase, type RuntimeResourceKind } from '@wasm-idle/core';
2
2
  export declare const STATIC_RUNTIME_PREFLIGHT_PROTOCOL_VERSION: 1;
3
3
  export type StaticRuntimePreflightRuntimeId = 'BQN' | 'CLOJURESCRIPT' | 'FORTH' | 'J' | 'JANET' | 'PROLOG';
4
4
  export type StaticRuntimePreflightProgress = {
@@ -19,6 +19,7 @@ export interface StaticRuntimePreflightRequestMessage {
19
19
  readonly manifestUrl: string;
20
20
  readonly profile: unknown;
21
21
  readonly limits: ExecutionLimits;
22
+ readonly persistentCache?: RuntimeAssetCacheOptions;
22
23
  }
23
24
  export interface StaticRuntimePreflightProgressMessage {
24
25
  readonly protocolVersion: typeof STATIC_RUNTIME_PREFLIGHT_PROTOCOL_VERSION;
@@ -1,5 +1,5 @@
1
1
  import type { PlaygroundRuntimeAssets } from './assets';
2
- import { type ExecutionLimits, type RuntimeAssetIntegrityEntry, type RuntimeStdinMode, type RuntimeWorkerLifetimePolicy } from '@wasm-idle/core';
2
+ import { type ExecutionLimits, type RuntimeAssetIntegrityEntry, type RuntimeAssetCacheOptions, type RuntimeStdinMode, type RuntimeWorkerLifetimePolicy } from '@wasm-idle/core';
3
3
  import { type CompilerDiagnostic, type SandboxExecutionOptions } from './options';
4
4
  import type { Sandbox, SandboxProgress } from './sandbox';
5
5
  type StaticWorkerReceipt = Readonly<Required<Pick<RuntimeAssetIntegrityEntry, 'bytes' | 'sha256'>>>;
@@ -14,6 +14,7 @@ export interface StaticWorkerRuntimeUrls {
14
14
  }
15
15
  export interface StaticWorkerRuntimePreflightContext {
16
16
  readonly limits: ExecutionLimits;
17
+ readonly persistentCache?: RuntimeAssetCacheOptions;
17
18
  readonly signal?: AbortSignal;
18
19
  readonly reportProgress: (value: number, stage?: string) => void;
19
20
  /**
@@ -85,6 +86,7 @@ export declare class StaticWorkerRuntimeSandbox implements Sandbox {
85
86
  private disposePromise;
86
87
  private disposeReason;
87
88
  private lifecycleProgress?;
89
+ private persistentCache?;
88
90
  private readonly progressController;
89
91
  private progressUid;
90
92
  private startingRunId;
@@ -1,4 +1,4 @@
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';
1
+ import { AssetNotFoundError, AssetTooLargeError, BusyError, CancelledError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, ProtocolError, RuntimeConfigurationError, RuntimeExecutionError, RuntimeProgressController, RuntimeWorkerLifetimeController, TimeoutError, WorkerStartupError, isWasmIdleError, resolveExecutionLimits, resolveRuntimeAssetCacheOptions, readPersistentRuntimeAsset, writePersistentRuntimeAsset, validateExecutionWorkspace, verifyRuntimeAssetIntegrity } from '@wasm-idle/core';
2
2
  import { resolveSandboxExecutionArgs } from './options';
3
3
  import { StaticStdinRingHost } from './staticStdinRing';
4
4
  import { deserializeStaticRuntimePreflightError, isStaticRuntimePreflightSerializedError, inspectStaticRuntimePreflightBytes } from './staticRuntimePreflightProtocol';
@@ -39,6 +39,7 @@ export class StaticWorkerRuntimeSandbox {
39
39
  disposePromise = null;
40
40
  disposeReason = null;
41
41
  lifecycleProgress;
42
+ persistentCache;
42
43
  progressController = new RuntimeProgressController();
43
44
  progressUid = 0;
44
45
  startingRunId = null;
@@ -134,7 +135,8 @@ export class StaticWorkerRuntimeSandbox {
134
135
  async load(runtimeAssets = '', code = '', _log = true, _args = [], options = {}, progress) {
135
136
  this.assertNotDisposed();
136
137
  try {
137
- const controls = this.resolveExecutionControls(options);
138
+ const persistentCache = typeof runtimeAssets === 'string' ? undefined : runtimeAssets.persistentCache;
139
+ const controls = this.resolveExecutionControls(options, persistentCache ?? {});
138
140
  this.assertOperationNotDisposed();
139
141
  if (controls.signal?.aborted) {
140
142
  throw new CancelledError(`${this.config.displayName} startup cancelled`, {
@@ -149,6 +151,7 @@ export class StaticWorkerRuntimeSandbox {
149
151
  const currentUrl = typeof window !== 'undefined' ? window.location.href : '';
150
152
  const urls = this.config.resolveRuntimeAssets(runtimeAssets, currentUrl);
151
153
  this.assertOperationNotDisposed();
154
+ this.persistentCache = persistentCache;
152
155
  const nextManifestUrl = urls.manifestUrl || '';
153
156
  const nextManifestFingerprint = urls.manifestFingerprint || '';
154
157
  const nextPreflightKey = urls.preflightKey || '';
@@ -304,9 +307,10 @@ export class StaticWorkerRuntimeSandbox {
304
307
  beginProgressLifecycle(progress, stage) {
305
308
  return this.progressController.begin(`${this.config.languageId.toLowerCase()}-${++this.progressUid}`, progress, stage);
306
309
  }
307
- resolveExecutionControls(options) {
310
+ resolveExecutionControls(options, persistentCache = this.persistentCache) {
308
311
  return {
309
312
  limits: resolveExecutionLimits(options.limits),
313
+ persistentCache: resolveRuntimeAssetCacheOptions(persistentCache, options.persistentCache),
310
314
  ...(options.signal ? { signal: options.signal } : {})
311
315
  };
312
316
  }
@@ -400,6 +404,17 @@ export class StaticWorkerRuntimeSandbox {
400
404
  if (workerRequestUrl.hash) {
401
405
  throw new RuntimeConfigurationError(`${this.config.displayName} worker script URL must not include a fragment`, { runtimeId: this.config.languageId });
402
406
  }
407
+ const persistentIdentity = workerReceipt
408
+ ? {
409
+ url: workerRequestUrl.href,
410
+ sha256: workerReceipt.sha256,
411
+ bytes: workerReceipt.bytes,
412
+ validationKey: JSON.stringify([
413
+ 'static-worker-v1',
414
+ this.config.requireExactWorkerResponseUrl === true
415
+ ])
416
+ }
417
+ : undefined;
403
418
  const phaseController = new AbortController();
404
419
  let timedOut = false;
405
420
  const onAbort = () => phaseController.abort(signal?.reason);
@@ -414,6 +429,18 @@ export class StaticWorkerRuntimeSandbox {
414
429
  throw (phaseController.signal.reason ??
415
430
  new DOMException('Worker script fetch aborted', 'AbortError'));
416
431
  }
432
+ if (persistentIdentity && workerReceipt) {
433
+ const cached = await readPersistentRuntimeAsset({
434
+ identity: persistentIdentity,
435
+ cache: controls.persistentCache,
436
+ signal: phaseController.signal
437
+ });
438
+ if (cached) {
439
+ await this.verifyWorkerReceipt(cached, workerReceipt, phaseController.signal);
440
+ this.reportProgress(progress, 0.2, `${this.config.displayName} worker cached`);
441
+ return Uint8Array.from(cached);
442
+ }
443
+ }
417
444
  const pendingResponse = Promise.resolve(fetch(workerRequestUrl.href, {
418
445
  cache: this.config.inlineVerifiedWorker ? 'no-store' : 'force-cache',
419
446
  credentials: 'omit',
@@ -531,6 +558,12 @@ export class StaticWorkerRuntimeSandbox {
531
558
  const bytes = new Uint8Array(buffer);
532
559
  if (workerReceipt) {
533
560
  await this.verifyWorkerReceipt(bytes, workerReceipt, phaseController.signal);
561
+ await writePersistentRuntimeAsset({
562
+ identity: persistentIdentity,
563
+ cache: controls.persistentCache,
564
+ signal: phaseController.signal,
565
+ bytes
566
+ });
534
567
  return bytes;
535
568
  }
536
569
  return undefined;
@@ -641,6 +674,12 @@ export class StaticWorkerRuntimeSandbox {
641
674
  offset += chunk.byteLength;
642
675
  }
643
676
  await this.verifyWorkerReceipt(bytes, workerReceipt, phaseController.signal);
677
+ await writePersistentRuntimeAsset({
678
+ identity: persistentIdentity,
679
+ cache: controls.persistentCache,
680
+ signal: phaseController.signal,
681
+ bytes
682
+ });
644
683
  return bytes;
645
684
  }
646
685
  return undefined;
@@ -856,6 +895,7 @@ self.postMessage = (message, transferOrOptions) => {
856
895
  progress,
857
896
  controls: {
858
897
  limits: controls.limits,
898
+ persistentCache: controls.persistentCache,
859
899
  signal: startAbortController.signal
860
900
  }
861
901
  };
@@ -916,6 +956,7 @@ self.postMessage = (message, transferOrOptions) => {
916
956
  try {
917
957
  runtimePreflight = await this.config.preflightRuntimeAssets(urls, {
918
958
  limits: controls.limits,
959
+ persistentCache: controls.persistentCache,
919
960
  signal: controls.signal,
920
961
  reportProgress: (value, stage) => this.reportProgress(progress, value, stage),
921
962
  createOwnedDelivery: (payload) => {
@@ -39,6 +39,7 @@ class Tcl extends StaticWorkerRuntimeSandbox {
39
39
  manifestUrl: urls.manifestUrl || '',
40
40
  profile,
41
41
  limits: context.limits,
42
+ persistentCache: context.persistentCache,
42
43
  signal: context.signal,
43
44
  reportProgress(progress) {
44
45
  loadedByAsset.set(progress.assetKey, progress.loadedBytes);
@@ -46,6 +46,8 @@ declare class TinyGo implements Sandbox {
46
46
  rustRuntimeBaseUrl: string;
47
47
  oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
48
48
  assetLoader: TinyGoRuntimeAssetLoader | undefined;
49
+ private persistentCache?;
50
+ private persistentCacheBaseline?;
49
51
  runtimeProfile: TinyGoUpstreamRuntimeProfile | undefined;
50
52
  executableGraph: LoadedTinyGoExecutableGraph | null;
51
53
  runtime: TinyGoRuntimeHooks | null;
@@ -5,7 +5,7 @@ import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinB
5
5
  import { createWasmIdleSharedBuffer } from './sharedBuffer';
6
6
  import { WorkerSession } from './workerSession';
7
7
  import { reportWorkerProgress } from './workerProgress';
8
- import { BusyError, CancelledError, DEFAULT_EXECUTION_LIMITS, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, RuntimeConfigurationError, TimeoutError, WorkspaceValidationError, resolveExecutionLimits, validateExecutionWorkspace } from '@wasm-idle/core';
8
+ import { BusyError, CancelledError, DEFAULT_EXECUTION_LIMITS, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, RuntimeConfigurationError, TimeoutError, WorkspaceValidationError, resolveExecutionLimits, resolveRuntimeAssetCacheOptions, fetchPinnedRuntimeAsset, validateExecutionWorkspace } from '@wasm-idle/core';
9
9
  const ACTIVITY_PREFIX_PATTERN = /^\[\d{2}:\d{2}:\d{2}\]\s?/gm;
10
10
  const MAX_TIMER_DELAY_MS = 2_147_483_647;
11
11
  const TINYGO_BROWSER_COMPILER_DEFAULT_MAX_WASM_MEMORY_BYTES = 2 * 1024 * 1024 * 1024;
@@ -73,6 +73,8 @@ class TinyGo {
73
73
  rustRuntimeBaseUrl = '';
74
74
  oncompilerdiagnostic;
75
75
  assetLoader = undefined;
76
+ persistentCache;
77
+ persistentCacheBaseline;
76
78
  runtimeProfile = undefined;
77
79
  executableGraph = null;
78
80
  runtime = null;
@@ -117,8 +119,11 @@ class TinyGo {
117
119
  }
118
120
  load(runtimeAssets = '', _code = '', _log = true, _args = [], options = {}, progress) {
119
121
  return this.executeOperation('startup', options, async (operation) => {
122
+ const persistentCache = resolveRuntimeAssetCacheOptions(typeof runtimeAssets === 'object' ? runtimeAssets.persistentCache : undefined, options.persistentCache);
120
123
  try {
121
124
  this.assertOperation(operation);
125
+ this.persistentCacheBaseline = persistentCache;
126
+ this.persistentCache = persistentCache;
122
127
  this.clearPendingStdin();
123
128
  const currentUrl = typeof window !== 'undefined' ? window.location.href : '';
124
129
  this.assertOperation(operation);
@@ -692,6 +697,7 @@ class TinyGo {
692
697
  });
693
698
  }
694
699
  nextExecutableGraph = await loadVerifiedTinyGoExecutableGraph({
700
+ persistentCache: this.persistentCache,
695
701
  moduleUrl,
696
702
  profile: WASM_TINYGO_EXECUTABLE_GRAPH_PROFILE,
697
703
  signal: operation.controller.signal,
@@ -700,6 +706,42 @@ class TinyGo {
700
706
  });
701
707
  this.assertOperation(operation);
702
708
  const executableGraph = nextExecutableGraph;
709
+ // The upstream loader owns gzip fallback and logical-byte verification.
710
+ // Preserve raw .wasm probes; cache only exact profile-pinned transport URLs.
711
+ const transportReceipts = new Map();
712
+ for (const [path, receipt] of [
713
+ [runtimeProfile.manifestPath, runtimeProfile.manifestReceipt],
714
+ ...Object.entries(runtimeProfile.assetReceipts)
715
+ ]) {
716
+ const transportPath = receipt.uncompressedSha256 ? `${path}.gz` : path;
717
+ transportReceipts.set(new URL(transportPath, executableGraph.assetBaseUrl).href, receipt);
718
+ }
719
+ const pinnedFetch = async (input, init) => {
720
+ const url = typeof input === 'string'
721
+ ? input
722
+ : input instanceof URL
723
+ ? input.href
724
+ : input.url;
725
+ const receipt = transportReceipts.get(url);
726
+ if (!receipt)
727
+ return globalThis.fetch(input, init);
728
+ const owner = this.activeOperation;
729
+ if (!owner)
730
+ throw new Error('TinyGo asset load has no active operation');
731
+ this.assertOperation(owner);
732
+ const bytes = await fetchPinnedRuntimeAsset({
733
+ url,
734
+ receipt,
735
+ persistentCache: this.persistentCache,
736
+ maxAssetBytes: owner.limits?.maxAssetBytes ?? maxAssetBytes,
737
+ signal: init?.signal ?? owner.controller.signal
738
+ });
739
+ this.assertOperation(owner);
740
+ // This is a verified byte adapter, not a network response with a fabricated URL.
741
+ return new Response(Uint8Array.from(bytes).buffer, {
742
+ headers: { 'Content-Length': String(bytes.byteLength) }
743
+ });
744
+ };
703
745
  const runtimeModule = (await import(
704
746
  /* @vite-ignore */ executableGraph.entryUrl));
705
747
  this.assertOperation(operation);
@@ -762,7 +804,9 @@ class TinyGo {
762
804
  assetsPromise = loadUpstreamAssets({
763
805
  assetBaseUrl: executableGraph.assetBaseUrl,
764
806
  profile: runtimeProfile,
765
- ...(assetLoader ? { loader: assetLoader } : {}),
807
+ ...(assetLoader
808
+ ? { loader: assetLoader }
809
+ : { fetchImpl: pinnedFetch }),
766
810
  onProgress: commonOptions.onProgress,
767
811
  signal: controller.signal,
768
812
  maxAssetBytes: assetLimit
@@ -1051,6 +1095,7 @@ class TinyGo {
1051
1095
  }
1052
1096
  run(code, prepare, _log = true, _prog, args = [], options = {}) {
1053
1097
  return this.executeOperation('execute', options, async (operation, request) => {
1098
+ this.persistentCache = request.persistentCache;
1054
1099
  this.exit = false;
1055
1100
  try {
1056
1101
  this.begin = Date.now();
@@ -1155,6 +1200,8 @@ class TinyGo {
1155
1200
  throw error;
1156
1201
  }
1157
1202
  }, (operation) => {
1203
+ this.assertOperation(operation);
1204
+ const persistentCache = resolveRuntimeAssetCacheOptions(this.persistentCacheBaseline, options.persistentCache);
1158
1205
  this.assertOperation(operation);
1159
1206
  if (typeof code !== 'string') {
1160
1207
  throw new TypeError('TinyGo source code must be a string');
@@ -1281,7 +1328,14 @@ class TinyGo {
1281
1328
  const buffer = this.buffer;
1282
1329
  this.assertOperation(operation);
1283
1330
  operation.buffer = buffer;
1284
- return { buffer, programArgs, stdin, target, workspaceFiles: runtimeWorkspace };
1331
+ return {
1332
+ buffer,
1333
+ programArgs,
1334
+ stdin,
1335
+ target,
1336
+ workspaceFiles: runtimeWorkspace,
1337
+ persistentCache
1338
+ };
1285
1339
  });
1286
1340
  }
1287
1341
  resetOwnedBuffers(operationBuffer) {
@@ -1,6 +1,6 @@
1
1
  export declare const TINYGO_EXECUTABLE_GRAPH_FORMAT: "wasm-idle-tinygo-executable-graph-v1";
2
2
  export declare const TINYGO_EXECUTABLE_GRAPH_FINGERPRINT_DOMAIN: "wasm-idle:tinygo-executable-graph:v1\n";
3
- export type TinyGoExecutableImportKind = 'static' | 'dynamic' | 'worker';
3
+ export type TinyGoExecutableImportKind = 'static' | 'dynamic' | 'worker' | 'asset';
4
4
  export interface TinyGoExecutableGraphImport {
5
5
  readonly specifier: string;
6
6
  readonly target: string;
@@ -9,6 +9,8 @@ export interface TinyGoExecutableGraphImport {
9
9
  export interface TinyGoExecutableGraphModule {
10
10
  readonly bytes: number;
11
11
  readonly sha256: string;
12
+ readonly uncompressedBytes?: number;
13
+ readonly uncompressedSha256?: string;
12
14
  readonly imports: readonly TinyGoExecutableGraphImport[];
13
15
  }
14
16
  export interface TinyGoExecutableGraphProfile {
@@ -30,6 +32,7 @@ export interface TinyGoExecutableGraphProgress {
30
32
  readonly totalBytes: number;
31
33
  }
32
34
  export interface LoadTinyGoExecutableGraphOptions {
35
+ readonly persistentCache?: import('@wasm-idle/core').RuntimeAssetCacheOptions;
33
36
  readonly moduleUrl: string;
34
37
  readonly currentUrl?: string;
35
38
  readonly profile: unknown;
@@ -1,4 +1,5 @@
1
1
  import { RUNTIME_REGISTRY_MANIFEST_SCHEMA_VERSION, RuntimeConfigurationError, preflightRuntimeAssets } from '@wasm-idle/core';
2
+ import { decompressGzip } from '@wasm-idle/llvm-core';
2
3
  export const TINYGO_EXECUTABLE_GRAPH_FORMAT = 'wasm-idle-tinygo-executable-graph-v1';
3
4
  export const TINYGO_EXECUTABLE_GRAPH_FINGERPRINT_DOMAIN = 'wasm-idle:tinygo-executable-graph:v1\n';
4
5
  const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
@@ -92,8 +93,16 @@ export function snapshotTinyGoExecutableGraphProfile(value) {
92
93
  const modules = {};
93
94
  for (const rawPath of modulePaths) {
94
95
  const modulePath = requireSafePath(rawPath, 'TinyGo executable module path');
96
+ const binary = /^assets\/upstream-binaryen-[a-f0-9]{16}\.wasm\.gz\.bin$/.test(modulePath);
97
+ if (!modulePath.endsWith('.js') && !binary)
98
+ throw configurationError('Unsupported TinyGo graph asset path');
95
99
  const sourceModule = requireRecord(sourceModules[rawPath], `TinyGo executable module ${modulePath}`);
96
- requireExactKeys(sourceModule, ['bytes', 'sha256', 'imports'], `TinyGo executable module ${modulePath}`);
100
+ requireExactKeys(sourceModule, [
101
+ 'bytes',
102
+ 'sha256',
103
+ 'imports',
104
+ ...(binary ? ['uncompressedBytes', 'uncompressedSha256'] : [])
105
+ ], `TinyGo executable module ${modulePath}`);
97
106
  if (!Number.isSafeInteger(sourceModule.bytes) || Number(sourceModule.bytes) <= 0) {
98
107
  throw configurationError(`TinyGo executable module ${modulePath} has an invalid size`);
99
108
  }
@@ -104,6 +113,15 @@ export function snapshotTinyGoExecutableGraphProfile(value) {
104
113
  throw configurationError(`TinyGo executable module ${modulePath} imports must be an array`);
105
114
  }
106
115
  const specifiers = new Set();
116
+ if (binary &&
117
+ (sourceModule.imports.length ||
118
+ !Number.isSafeInteger(sourceModule.uncompressedBytes) ||
119
+ Number(sourceModule.uncompressedBytes) < 8 ||
120
+ Number(sourceModule.uncompressedBytes) > 64 * 1024 * 1024 ||
121
+ typeof sourceModule.uncompressedSha256 !== 'string' ||
122
+ !SHA256_PATTERN.test(sourceModule.uncompressedSha256))) {
123
+ throw configurationError('Binaryen Wasm has invalid logical receipts or imports');
124
+ }
107
125
  const imports = sourceModule.imports.map((rawImport, index) => {
108
126
  const graphImport = requireRecord(rawImport, `TinyGo executable module ${modulePath} import ${index}`);
109
127
  requireExactKeys(graphImport, ['specifier', 'target', 'kind'], `TinyGo executable module ${modulePath} import ${index}`);
@@ -113,12 +131,14 @@ export function snapshotTinyGoExecutableGraphProfile(value) {
113
131
  throw configurationError(`TinyGo executable module ${modulePath} has an invalid or duplicate import specifier`);
114
132
  }
115
133
  specifiers.add(specifier);
116
- if (!['static', 'dynamic', 'worker'].includes(String(graphImport.kind))) {
134
+ if (!['static', 'dynamic', 'worker', 'asset'].includes(String(graphImport.kind))) {
117
135
  throw configurationError(`TinyGo executable module ${modulePath} import ${specifier} has an invalid kind`);
118
136
  }
119
137
  if (resolveImportTarget(modulePath, specifier) !== target) {
120
138
  throw configurationError(`TinyGo executable module ${modulePath} import ${specifier} does not resolve to ${target}`);
121
139
  }
140
+ if ((graphImport.kind === 'asset') !== target.endsWith('.wasm.gz.bin'))
141
+ throw configurationError('TinyGo graph import kind does not match its target');
122
142
  importCount += 1;
123
143
  if (importCount > MAX_IMPORTS) {
124
144
  throw configurationError(`TinyGo executable graph exceeds ${MAX_IMPORTS} imports`);
@@ -132,10 +152,16 @@ export function snapshotTinyGoExecutableGraphProfile(value) {
132
152
  modules[modulePath] = Object.freeze({
133
153
  bytes: Number(sourceModule.bytes),
134
154
  sha256: sourceModule.sha256,
155
+ ...(binary
156
+ ? {
157
+ uncompressedBytes: Number(sourceModule.uncompressedBytes),
158
+ uncompressedSha256: sourceModule.uncompressedSha256
159
+ }
160
+ : {}),
135
161
  imports: Object.freeze(imports)
136
162
  });
137
163
  }
138
- if (!Object.prototype.hasOwnProperty.call(modules, entryPath)) {
164
+ if (!Object.prototype.hasOwnProperty.call(modules, entryPath) || !entryPath.endsWith('.js')) {
139
165
  throw configurationError('TinyGo executable graph entry module is missing');
140
166
  }
141
167
  for (const [modulePath, module] of Object.entries(modules)) {
@@ -179,6 +205,8 @@ export function canonicalTinyGoExecutableGraphProfile(profile) {
179
205
  for (const modulePath of paths) {
180
206
  const module = profile.modules[modulePath];
181
207
  canonical += `module\0${modulePath}\0${module.bytes}\0${module.sha256}\n`;
208
+ if (module.uncompressedBytes !== undefined)
209
+ canonical += `wasm\0${modulePath}\0${module.uncompressedBytes}\0${module.uncompressedSha256}\n`;
182
210
  }
183
211
  const edges = paths.flatMap((modulePath) => profile.modules[modulePath].imports.map((graphImport) => ({
184
212
  importer: modulePath,
@@ -312,11 +340,15 @@ function createPreflightManifest(profile, keysByPath) {
312
340
  key: keysByPath[modulePath],
313
341
  path: modulePath,
314
342
  compressedSha256: receipt.sha256,
315
- uncompressedSha256: receipt.sha256,
343
+ uncompressedSha256: receipt.uncompressedSha256 ?? receipt.sha256,
316
344
  compressedBytes: receipt.bytes,
317
- uncompressedBytes: receipt.bytes,
318
- mediaType: 'text/javascript',
319
- encoding: 'identity'
345
+ uncompressedBytes: receipt.uncompressedBytes ?? receipt.bytes,
346
+ mediaType: receipt.uncompressedBytes !== undefined
347
+ ? 'application/wasm'
348
+ : 'text/javascript',
349
+ encoding: receipt.uncompressedBytes !== undefined
350
+ ? 'gzip'
351
+ : 'identity'
320
352
  };
321
353
  }),
322
354
  contracts: {
@@ -357,6 +389,7 @@ export async function loadVerifiedTinyGoExecutableGraph(options) {
357
389
  if (options.assetTimeoutMs !== undefined)
358
390
  limits.assetTimeoutMs = options.assetTimeoutMs;
359
391
  const preflight = await preflightRuntimeAssets({
392
+ persistentCache: options.persistentCache,
360
393
  manifest: createPreflightManifest(profile, urls.keysByPath),
361
394
  runtimeId: 'tinygo/executable-graph',
362
395
  rootUrl: urls.baseUrl,
@@ -378,7 +411,17 @@ export async function loadVerifiedTinyGoExecutableGraph(options) {
378
411
  : undefined
379
412
  });
380
413
  const sources = {};
414
+ const binaries = {};
381
415
  for (const modulePath of Object.keys(profile.modules)) {
416
+ const receipt = profile.modules[modulePath];
417
+ if (receipt.uncompressedBytes !== undefined) {
418
+ const bytes = await decompressGzip(preflight.assets[urls.keysByPath[modulePath]].bytes, modulePath, receipt.uncompressedBytes, options.signal);
419
+ if (bytes.byteLength !== receipt.uncompressedBytes ||
420
+ (await sha256Hex(bytes, options.signal)) !== receipt.uncompressedSha256)
421
+ throw configurationError('Binaryen Wasm logical receipt mismatch');
422
+ binaries[modulePath] = bytes;
423
+ continue;
424
+ }
382
425
  try {
383
426
  sources[modulePath] = fatalDecoder.decode(preflight.assets[urls.keysByPath[modulePath]].bytes);
384
427
  }
@@ -420,6 +463,15 @@ export async function loadVerifiedTinyGoExecutableGraph(options) {
420
463
  };
421
464
  try {
422
465
  for (const modulePath of order) {
466
+ if (profile.modules[modulePath].uncompressedBytes !== undefined) {
467
+ const bytes = binaries[modulePath];
468
+ const blobUrl = URL.createObjectURL(new Blob([Uint8Array.from(bytes)], { type: 'application/wasm' }));
469
+ ownedUrls.push(blobUrl);
470
+ if (!SAFE_BLOB_URL_PATTERN.test(blobUrl))
471
+ throw configurationError('TinyGo graph received an unsafe Wasm Blob URL');
472
+ moduleUrls[modulePath] = blobUrl;
473
+ continue;
474
+ }
423
475
  let source = sources[modulePath];
424
476
  for (const graphImport of profile.modules[modulePath].imports) {
425
477
  source = replaceUniqueSpecifier(source, graphImport.specifier, moduleUrls[graphImport.target], modulePath);
@@ -1,5 +1,5 @@
1
1
  import { resolveTypeScriptModuleUrl } from './assets';
2
- import { AssetTooLargeError, BusyError, CancelledError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, RuntimeConfigurationError, TimeoutError, resolveExecutionLimits, validateExecutionWorkspace } from '@wasm-idle/core';
2
+ import { AssetTooLargeError, BusyError, CancelledError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, RuntimeConfigurationError, TimeoutError, resolveExecutionLimits, resolveRuntimeAssetCacheOptions, validateExecutionWorkspace } from '@wasm-idle/core';
3
3
  import {} from './options';
4
4
  import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
5
5
  import { createWasmIdleSharedBuffer } from './sharedBuffer';
@@ -477,6 +477,9 @@ class TypeScriptSandbox {
477
477
  }
478
478
  worker.postMessage({
479
479
  load: true,
480
+ persistentCache: resolveRuntimeAssetCacheOptions(typeof runtimeAssets === 'object'
481
+ ? runtimeAssets?.persistentCache
482
+ : undefined, options.persistentCache),
480
483
  moduleUrl: nextModuleUrl,
481
484
  moduleReceipt: { ...moduleReceipt },
482
485
  maxAssetBytes: limits.maxAssetBytes
@@ -1 +1 @@
1
- export declare const WASM_GO_ASSET_VERSION = "ec36de55be7334e4";
1
+ export declare const WASM_GO_ASSET_VERSION = "c92a5b9dc0c99663";
@@ -1 +1 @@
1
- export const WASM_GO_ASSET_VERSION = 'ec36de55be7334e4';
1
+ export const WASM_GO_ASSET_VERSION = 'c92a5b9dc0c99663';
@@ -1,6 +1,6 @@
1
- export declare const WASM_OCAML_ASSET_VERSION = "68238d5506114430a0e57ee849a4946d11ff4517b1b4e6c56607b7c60a8cfabb";
1
+ export declare const WASM_OCAML_ASSET_VERSION = "0c26bdcda532857e22b68ae61490c18006d3d58e5a06351e48c6c0dfd439c147";
2
2
  export declare const WASM_OCAML_RUNTIME_PROFILE: Readonly<{
3
- fingerprint: "68238d5506114430a0e57ee849a4946d11ff4517b1b4e6c56607b7c60a8cfabb";
3
+ fingerprint: "0c26bdcda532857e22b68ae61490c18006d3d58e5a06351e48c6c0dfd439c147";
4
4
  moduleReceipt: Readonly<{
5
5
  bytes: 1892;
6
6
  sha256: "17d80beb633799e9021dc1b851bc76283494d981ec20fc315462d740284fa13a";
@@ -1,4 +1,4 @@
1
- export const WASM_OCAML_ASSET_VERSION = '68238d5506114430a0e57ee849a4946d11ff4517b1b4e6c56607b7c60a8cfabb';
1
+ export const WASM_OCAML_ASSET_VERSION = '0c26bdcda532857e22b68ae61490c18006d3d58e5a06351e48c6c0dfd439c147';
2
2
  export const WASM_OCAML_RUNTIME_PROFILE = Object.freeze({
3
3
  fingerprint: WASM_OCAML_ASSET_VERSION,
4
4
  moduleReceipt: Object.freeze({