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.
- package/README.md +52 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -0
- package/dist/playground/assetBridge.d.ts +11 -2
- package/dist/playground/assetBridge.js +190 -46
- package/dist/playground/assets.d.ts +9 -1
- package/dist/playground/assets.js +29 -5
- package/dist/playground/awk.js +1 -0
- package/dist/playground/bash.d.ts +1 -0
- package/dist/playground/bash.js +7 -1
- package/dist/playground/bqn.js +1 -0
- package/dist/playground/bqnPreflight.d.ts +2 -1
- package/dist/playground/bqnPreflight.js +1 -0
- package/dist/playground/c3.js +1 -0
- package/dist/playground/cachedClang.d.ts +10 -0
- package/dist/playground/cachedClang.js +330 -0
- package/dist/playground/clang.d.ts +8 -0
- package/dist/playground/clang.js +47 -8
- package/dist/playground/clangWorkerProtocol.d.ts +57 -0
- package/dist/playground/clangWorkerProtocol.js +11 -0
- package/dist/playground/clojurescript.js +1 -0
- package/dist/playground/clojurescriptPreflight.d.ts +2 -1
- package/dist/playground/clojurescriptPreflight.js +1 -0
- package/dist/playground/compiledArtifactCache.d.ts +21 -0
- package/dist/playground/compiledArtifactCache.js +67 -0
- package/dist/playground/elixir.js +4 -1
- package/dist/playground/forth.js +1 -0
- package/dist/playground/forthPreflight.d.ts +2 -1
- package/dist/playground/forthPreflight.js +1 -0
- package/dist/playground/fortran.js +7 -1
- package/dist/playground/haskell.js +4 -1
- package/dist/playground/haskellAssets.d.ts +2 -1
- package/dist/playground/haskellAssets.js +8 -8
- package/dist/playground/index.d.ts +1 -1
- package/dist/playground/index.js +2 -2
- package/dist/playground/j.js +1 -0
- package/dist/playground/jPreflight.d.ts +2 -1
- package/dist/playground/jPreflight.js +1 -0
- package/dist/playground/janet.js +1 -0
- package/dist/playground/java.js +20 -8
- package/dist/playground/julia.js +1 -0
- package/dist/playground/lfortranAssets.d.ts +2 -1
- package/dist/playground/lfortranAssets.js +1 -0
- package/dist/playground/lisp.js +4 -1
- package/dist/playground/lispAssets.d.ts +2 -1
- package/dist/playground/lispAssets.js +10 -0
- package/dist/playground/lldbManifest.d.ts +2 -2
- package/dist/playground/lldbManifest.js +17 -3
- package/dist/playground/lldbSession.d.ts +1 -0
- package/dist/playground/lldbSession.js +2 -1
- package/dist/playground/nim.js +1 -0
- package/dist/playground/ocaml.d.ts +1 -0
- package/dist/playground/ocaml.js +12 -1
- package/dist/playground/options.d.ts +2 -0
- package/dist/playground/pascal.js +1 -0
- package/dist/playground/perl.js +1 -0
- package/dist/playground/prolog.js +1 -0
- package/dist/playground/python.d.ts +7 -0
- package/dist/playground/python.js +41 -8
- package/dist/playground/runtimeAssetCache.d.ts +26 -0
- package/dist/playground/runtimeAssetCache.js +84 -0
- package/dist/playground/runtimeSession.d.ts +21 -0
- package/dist/playground/runtimeSession.js +119 -0
- package/dist/playground/rust.d.ts +1 -0
- package/dist/playground/rust.js +11 -1
- package/dist/playground/rustExecutableGraph.d.ts +1 -0
- package/dist/playground/rustExecutableGraph.js +1 -0
- package/dist/playground/staticRuntimePreflight.d.ts +2 -1
- package/dist/playground/staticRuntimePreflight.js +4 -2
- package/dist/playground/staticRuntimePreflightExecute.js +1 -0
- package/dist/playground/staticRuntimePreflightProtocol.d.ts +2 -1
- package/dist/playground/staticWorkerRuntime.d.ts +3 -1
- package/dist/playground/staticWorkerRuntime.js +44 -3
- package/dist/playground/tcl.js +1 -0
- package/dist/playground/tinygo.d.ts +2 -0
- package/dist/playground/tinygo.js +57 -3
- package/dist/playground/tinygoExecutableGraph.d.ts +4 -1
- package/dist/playground/tinygoExecutableGraph.js +59 -7
- package/dist/playground/typescript.js +4 -1
- package/dist/playground/wasmGoVersion.d.ts +1 -1
- package/dist/playground/wasmGoVersion.js +1 -1
- package/dist/playground/wasmOcamlVersion.d.ts +2 -2
- package/dist/playground/wasmOcamlVersion.js +1 -1
- package/dist/playground/wasmRustVersion.d.ts +90 -24
- package/dist/playground/wasmRustVersion.js +98 -23
- package/dist/playground/wasmTinyGoVersion.d.ts +40 -29
- package/dist/playground/wasmTinyGoVersion.js +43 -31
- package/dist/playground/worker/assets.d.ts +3 -0
- package/dist/playground/worker/assets.js +41 -0
- package/dist/playground/worker/cachedClangCompile.d.ts +1 -0
- package/dist/playground/worker/cachedClangCompile.js +85 -0
- package/dist/playground/worker/cachedClangExecute.d.ts +1 -0
- package/dist/playground/worker/cachedClangExecute.js +67 -0
- package/dist/playground/worker/clang.js +2 -8
- package/dist/playground/worker/clangStreaming.d.ts +1 -1
- package/dist/playground/worker/clangStreaming.js +20 -2
- package/dist/playground/worker/elixir.js +10 -8
- package/dist/playground/worker/fortran.js +4 -7
- package/dist/playground/worker/haskell.js +2 -0
- package/dist/playground/worker/lisp.js +2 -0
- package/dist/playground/worker/ocaml.js +13 -1
- package/dist/playground/worker/python.js +3 -2
- package/dist/playground/worker/runtimeAssetFetch.d.ts +11 -1
- package/dist/playground/worker/runtimeAssetFetch.js +64 -1
- package/dist/playground/worker/runtimeModule.d.ts +8 -0
- package/dist/playground/worker/runtimeModule.js +49 -0
- package/dist/playground/worker/rust.js +9 -2
- package/dist/playground/worker/staticRuntimePreflight.js +2 -0
- package/dist/playground/worker/typescript.js +4 -1
- package/dist/playground/worker/zig.js +7 -4
- package/dist/playground/zig.js +4 -1
- package/package.json +10 -7
package/dist/playground/ocaml.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createWasmIdleSharedBuffer } from './sharedBuffer';
|
|
|
5
5
|
import { WorkerSession } from './workerSession';
|
|
6
6
|
import { reportWorkerInputReady, reportWorkerProgress } from './workerProgress';
|
|
7
7
|
import { WASM_OCAML_RUNTIME_PROFILE } from './wasmOcamlVersion';
|
|
8
|
-
import { BusyError, CancelledError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, ProtocolError, RuntimeConfigurationError, TimeoutError, WorkspaceValidationError, resolveExecutionLimits, validateExecutionWorkspace } from '@wasm-idle/core';
|
|
8
|
+
import { BusyError, CancelledError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, ProtocolError, RuntimeConfigurationError, TimeoutError, WorkspaceValidationError, resolveExecutionLimits, resolveRuntimeAssetCacheOptions, validateExecutionWorkspace } from '@wasm-idle/core';
|
|
9
9
|
const OCAML_EXECUTION_LIMIT_KEYS = [
|
|
10
10
|
'assetTimeoutMs',
|
|
11
11
|
'startupTimeoutMs',
|
|
@@ -55,6 +55,7 @@ class Ocaml {
|
|
|
55
55
|
moduleReceiptIdentity = '';
|
|
56
56
|
manifestReceiptIdentity = '';
|
|
57
57
|
maxAssetBytes = 0;
|
|
58
|
+
persistentCache = resolveRuntimeAssetCacheOptions();
|
|
58
59
|
oncompilerdiagnostic;
|
|
59
60
|
waitingForInput = false;
|
|
60
61
|
pendingEof = false;
|
|
@@ -308,6 +309,7 @@ class Ocaml {
|
|
|
308
309
|
let nextModuleReceipt;
|
|
309
310
|
let nextManifestReceipt;
|
|
310
311
|
let buffer;
|
|
312
|
+
let persistentCache;
|
|
311
313
|
try {
|
|
312
314
|
signal = options.signal;
|
|
313
315
|
if (!this.isOperationActive(operation)) {
|
|
@@ -322,6 +324,7 @@ class Ocaml {
|
|
|
322
324
|
return Promise.reject(this.releaseBeforeSession(operation, 'OCaml runtime startup cancelled'));
|
|
323
325
|
}
|
|
324
326
|
limits = this.resolveOperationLimits(operation, configuredLimits);
|
|
327
|
+
persistentCache = resolveRuntimeAssetCacheOptions(typeof runtimeAssets === 'object' ? runtimeAssets?.persistentCache : undefined, options.persistentCache);
|
|
325
328
|
if (!this.isOperationActive(operation)) {
|
|
326
329
|
return Promise.reject(this.releaseBeforeSession(operation, 'OCaml runtime startup cancelled'));
|
|
327
330
|
}
|
|
@@ -459,6 +462,7 @@ class Ocaml {
|
|
|
459
462
|
this.moduleReceiptIdentity = receiptIdentity(nextModuleReceipt);
|
|
460
463
|
this.manifestReceiptIdentity = receiptIdentity(nextManifestReceipt);
|
|
461
464
|
this.maxAssetBytes = limits.maxAssetBytes;
|
|
465
|
+
this.persistentCache = persistentCache;
|
|
462
466
|
resolve();
|
|
463
467
|
this.completeOperation(operation);
|
|
464
468
|
return;
|
|
@@ -473,6 +477,7 @@ class Ocaml {
|
|
|
473
477
|
worker.onmessage = handler;
|
|
474
478
|
worker.postMessage({
|
|
475
479
|
load: true,
|
|
480
|
+
persistentCache,
|
|
476
481
|
moduleUrl: nextModuleUrl,
|
|
477
482
|
manifestUrl: nextManifestUrl,
|
|
478
483
|
moduleReceipt: nextModuleReceipt,
|
|
@@ -481,6 +486,7 @@ class Ocaml {
|
|
|
481
486
|
});
|
|
482
487
|
}
|
|
483
488
|
else {
|
|
489
|
+
this.persistentCache = persistentCache;
|
|
484
490
|
progress?.set?.(1);
|
|
485
491
|
if (!this.isOperationActive(operation))
|
|
486
492
|
return;
|
|
@@ -529,6 +535,7 @@ class Ocaml {
|
|
|
529
535
|
let buffer;
|
|
530
536
|
let outputCallback;
|
|
531
537
|
let onDiagnostic;
|
|
538
|
+
let persistentCache;
|
|
532
539
|
try {
|
|
533
540
|
signal = options.signal;
|
|
534
541
|
if (!this.isOperationActive(operation)) {
|
|
@@ -545,6 +552,9 @@ class Ocaml {
|
|
|
545
552
|
if (!configuredWorker)
|
|
546
553
|
throw 'Worker not loaded';
|
|
547
554
|
worker = configuredWorker;
|
|
555
|
+
persistentCache = resolveRuntimeAssetCacheOptions(this.persistentCache, options.persistentCache);
|
|
556
|
+
if (!this.isOperationActive(operation))
|
|
557
|
+
throw this.releaseBeforeSession(operation, 'OCaml execution cancelled');
|
|
548
558
|
const configuredTarget = options.ocamlBackend;
|
|
549
559
|
if (!this.isOperationActive(operation)) {
|
|
550
560
|
throw this.releaseBeforeSession(operation, 'OCaml execution cancelled');
|
|
@@ -814,6 +824,7 @@ class Ocaml {
|
|
|
814
824
|
try {
|
|
815
825
|
worker.postMessage({
|
|
816
826
|
code,
|
|
827
|
+
persistentCache,
|
|
817
828
|
prepare,
|
|
818
829
|
target,
|
|
819
830
|
wasmBinaryenMode,
|
|
@@ -21,6 +21,8 @@ export interface SandboxWorkspaceFile {
|
|
|
21
21
|
content: string;
|
|
22
22
|
}
|
|
23
23
|
export interface SandboxExecutionOptions {
|
|
24
|
+
/** Overrides instance/global persistent asset caching for this operation. */
|
|
25
|
+
persistentCache?: import('@wasm-idle/core').RuntimeAssetCacheOptions;
|
|
24
26
|
debugMode?: BrowserDebugMode;
|
|
25
27
|
/** @deprecated Use debugMode. true maps to trace unless the caller explicitly selects lldb. */
|
|
26
28
|
debug?: boolean;
|
|
@@ -40,6 +40,7 @@ class Pascal extends StaticWorkerRuntimeSandbox {
|
|
|
40
40
|
systemPascalUrl: resolved.systemPascalUrl,
|
|
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);
|
package/dist/playground/perl.js
CHANGED
|
@@ -38,6 +38,7 @@ class Perl extends StaticWorkerRuntimeSandbox {
|
|
|
38
38
|
manifestUrl: urls.manifestUrl || '',
|
|
39
39
|
profile,
|
|
40
40
|
limits: context.limits,
|
|
41
|
+
persistentCache: context.persistentCache,
|
|
41
42
|
signal: context.signal,
|
|
42
43
|
reportProgress(progress) {
|
|
43
44
|
loadedByAsset.set(progress.assetKey, progress.loadedBytes);
|
|
@@ -47,6 +47,7 @@ class Prolog extends StaticWorkerRuntimeSandbox {
|
|
|
47
47
|
manifestUrl: urls.manifestUrl || '',
|
|
48
48
|
profile,
|
|
49
49
|
limits: context.limits,
|
|
50
|
+
persistentCache: context.persistentCache,
|
|
50
51
|
signal: context.signal,
|
|
51
52
|
reportProgress(progress) {
|
|
52
53
|
if (progress.kind === 'asset') {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
2
2
|
import { WorkerAssetBridge } from './assetBridge';
|
|
3
|
+
import { RuntimeAssetCache } from './runtimeAssetCache';
|
|
3
4
|
import { type PlaygroundRuntimeAssets } from './assets';
|
|
4
5
|
import type { Sandbox, SandboxProgress } from './sandbox';
|
|
5
6
|
declare class Python implements Sandbox {
|
|
@@ -26,6 +27,10 @@ declare class Python implements Sandbox {
|
|
|
26
27
|
exit: boolean;
|
|
27
28
|
assetBridge: WorkerAssetBridge | null;
|
|
28
29
|
private activeOperation;
|
|
30
|
+
private runtimeAssetCache;
|
|
31
|
+
private ownsRuntimeAssetCache;
|
|
32
|
+
private disposed;
|
|
33
|
+
private disposal?;
|
|
29
34
|
private readonly workerSession;
|
|
30
35
|
private beginOperation;
|
|
31
36
|
private isOperationActive;
|
|
@@ -46,5 +51,7 @@ declare class Python implements Sandbox {
|
|
|
46
51
|
kill(): void;
|
|
47
52
|
terminate(reason?: unknown): void;
|
|
48
53
|
clear(): Promise<void>;
|
|
54
|
+
setRuntimeAssetCache(cache: RuntimeAssetCache): void;
|
|
55
|
+
dispose(): Promise<void>;
|
|
49
56
|
}
|
|
50
57
|
export default Python;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WorkerAssetBridge } from './assetBridge';
|
|
2
|
+
import { RuntimeAssetCache } from './runtimeAssetCache';
|
|
2
3
|
import { resolveRuntimeAssetConfig } from './assets';
|
|
3
4
|
import { bufferedSequence, flushBufferedEof, flushQueuedStdin, resetBufferedStdin, waitForBufferedSequenceChange } from './stdinBuffer';
|
|
4
5
|
import { createWasmIdleSharedBuffer, requireSharedArrayBuffer } from './sharedBuffer';
|
|
@@ -29,6 +30,10 @@ class Python {
|
|
|
29
30
|
exit = true;
|
|
30
31
|
assetBridge = null;
|
|
31
32
|
activeOperation = null;
|
|
33
|
+
runtimeAssetCache = new RuntimeAssetCache();
|
|
34
|
+
ownsRuntimeAssetCache = true;
|
|
35
|
+
disposed = false;
|
|
36
|
+
disposal;
|
|
32
37
|
workerSession = new WorkerSession({
|
|
33
38
|
label: 'Python',
|
|
34
39
|
onDispose: (worker) => {
|
|
@@ -44,6 +49,8 @@ class Python {
|
|
|
44
49
|
}
|
|
45
50
|
});
|
|
46
51
|
beginOperation(phase) {
|
|
52
|
+
if (this.disposed)
|
|
53
|
+
throw new Error('Python runtime has been disposed');
|
|
47
54
|
if (this.activeOperation) {
|
|
48
55
|
throw new BusyError('Python runtime already has an active operation', {
|
|
49
56
|
runtimeId: 'PYTHON3',
|
|
@@ -198,7 +205,7 @@ class Python {
|
|
|
198
205
|
this.pendingInput = [];
|
|
199
206
|
this.waitingForInput = false;
|
|
200
207
|
this.pendingEof = false;
|
|
201
|
-
const assetConfig = resolveRuntimeAssetConfig('python', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
|
|
208
|
+
const assetConfig = resolveRuntimeAssetConfig('python', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '', options.persistentCache);
|
|
202
209
|
if (!this.isOperationActive(operation))
|
|
203
210
|
return;
|
|
204
211
|
const needsWorkerReset = !this.worker ||
|
|
@@ -220,7 +227,7 @@ class Python {
|
|
|
220
227
|
}
|
|
221
228
|
this.worker = worker;
|
|
222
229
|
this.workerSession.attach(worker);
|
|
223
|
-
const assetBridge = new WorkerAssetBridge(worker, 'python', assetConfig, progress, limits.maxAssetBytes);
|
|
230
|
+
const assetBridge = new WorkerAssetBridge(worker, 'python', assetConfig, progress, limits.maxAssetBytes, false, this.runtimeAssetCache);
|
|
224
231
|
if (!this.isOperationActive(operation) || this.worker !== worker) {
|
|
225
232
|
assetBridge.dispose();
|
|
226
233
|
return;
|
|
@@ -259,7 +266,8 @@ class Python {
|
|
|
259
266
|
assets: {
|
|
260
267
|
baseUrl: assetConfig.baseUrl,
|
|
261
268
|
maxAssetBytes: limits.maxAssetBytes,
|
|
262
|
-
useAssetBridge: assetConfig.useAssetBridge
|
|
269
|
+
useAssetBridge: assetConfig.useAssetBridge,
|
|
270
|
+
useModuleBridge: true
|
|
263
271
|
}
|
|
264
272
|
});
|
|
265
273
|
return;
|
|
@@ -335,11 +343,13 @@ class Python {
|
|
|
335
343
|
activePath = options.activePath;
|
|
336
344
|
debugPath = options.debugPath;
|
|
337
345
|
workspaceFiles = options.workspaceFiles;
|
|
346
|
+
const persistentCache = options.persistentCache;
|
|
338
347
|
if (!this.isOperationActive(activeOperation)) {
|
|
339
348
|
return Promise.reject(this.releaseBeforeSession(activeOperation, 'Python execution cancelled'));
|
|
340
349
|
}
|
|
341
350
|
if (debug)
|
|
342
351
|
requireSharedArrayBuffer('Python debugging');
|
|
352
|
+
this.assetBridge?.setExecutionPersistentCache(persistentCache);
|
|
343
353
|
this.setBreakpoints(debug ? breakpoints : []);
|
|
344
354
|
}
|
|
345
355
|
catch (error) {
|
|
@@ -545,11 +555,34 @@ class Python {
|
|
|
545
555
|
resetBufferedStdin(this.watchResultBuffer);
|
|
546
556
|
const debugBuffer = new Int32Array(this.debugBuffer);
|
|
547
557
|
debugBuffer.fill(0);
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
558
|
+
new Uint8Array(this.interruptBuffer).fill(0);
|
|
559
|
+
}
|
|
560
|
+
setRuntimeAssetCache(cache) {
|
|
561
|
+
if (this.disposed)
|
|
562
|
+
throw new Error('Python runtime has been disposed');
|
|
563
|
+
if (this.worker || this.activeOperation)
|
|
564
|
+
throw new Error('Cannot replace an active runtime cache');
|
|
565
|
+
if (cache === this.runtimeAssetCache)
|
|
566
|
+
return;
|
|
567
|
+
if (this.ownsRuntimeAssetCache)
|
|
568
|
+
this.runtimeAssetCache.dispose();
|
|
569
|
+
this.runtimeAssetCache = cache;
|
|
570
|
+
this.ownsRuntimeAssetCache = false;
|
|
571
|
+
}
|
|
572
|
+
dispose() {
|
|
573
|
+
if (this.disposal)
|
|
574
|
+
return this.disposal;
|
|
575
|
+
this.disposed = true;
|
|
576
|
+
this.disposal = Promise.resolve()
|
|
577
|
+
.then(() => this.clear())
|
|
578
|
+
.finally(() => {
|
|
579
|
+
if (this.ownsRuntimeAssetCache)
|
|
580
|
+
this.runtimeAssetCache.dispose();
|
|
581
|
+
this.output = null;
|
|
582
|
+
this.ondebug = undefined;
|
|
583
|
+
this.image = undefined;
|
|
584
|
+
});
|
|
585
|
+
return this.disposal;
|
|
553
586
|
}
|
|
554
587
|
}
|
|
555
588
|
export default Python;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Immutable bootstrap assets only: never store a worker, Wasm instance, or user filesystem. */
|
|
2
|
+
export declare class RuntimeAssetCache {
|
|
3
|
+
private readonly maxBytes;
|
|
4
|
+
private readonly maxEntries;
|
|
5
|
+
private entries;
|
|
6
|
+
private identities;
|
|
7
|
+
private nextIdentity;
|
|
8
|
+
private bytes;
|
|
9
|
+
private hits;
|
|
10
|
+
private misses;
|
|
11
|
+
private disposed;
|
|
12
|
+
constructor(maxBytes?: number, maxEntries?: number);
|
|
13
|
+
/** Identity is owner-local; a custom loader must never alias another loader. */
|
|
14
|
+
identity(value: object | undefined): number;
|
|
15
|
+
get<T>(key: string): T | undefined;
|
|
16
|
+
/** Only the trusted host publishes fully verified results; failures are never retained. */
|
|
17
|
+
set<T>(key: string, value: T, bytes: number): void;
|
|
18
|
+
stats(): {
|
|
19
|
+
hits: number;
|
|
20
|
+
misses: number;
|
|
21
|
+
entries: number;
|
|
22
|
+
bytes: number;
|
|
23
|
+
disposed: boolean;
|
|
24
|
+
};
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** Immutable bootstrap assets only: never store a worker, Wasm instance, or user filesystem. */
|
|
2
|
+
export class RuntimeAssetCache {
|
|
3
|
+
maxBytes;
|
|
4
|
+
maxEntries;
|
|
5
|
+
entries = new Map();
|
|
6
|
+
identities = new WeakMap();
|
|
7
|
+
nextIdentity = 0;
|
|
8
|
+
bytes = 0;
|
|
9
|
+
hits = 0;
|
|
10
|
+
misses = 0;
|
|
11
|
+
disposed = false;
|
|
12
|
+
constructor(maxBytes = 256 * 1024 * 1024, maxEntries = 64) {
|
|
13
|
+
this.maxBytes = maxBytes;
|
|
14
|
+
this.maxEntries = maxEntries;
|
|
15
|
+
if (!Number.isSafeInteger(maxBytes) ||
|
|
16
|
+
maxBytes < 0 ||
|
|
17
|
+
!Number.isSafeInteger(maxEntries) ||
|
|
18
|
+
maxEntries < 0) {
|
|
19
|
+
throw new TypeError('Runtime cache limits must be non-negative safe integers');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Identity is owner-local; a custom loader must never alias another loader. */
|
|
23
|
+
identity(value) {
|
|
24
|
+
if (!value)
|
|
25
|
+
return 0;
|
|
26
|
+
let identity = this.identities.get(value);
|
|
27
|
+
if (identity === undefined) {
|
|
28
|
+
identity = ++this.nextIdentity;
|
|
29
|
+
this.identities.set(value, identity);
|
|
30
|
+
}
|
|
31
|
+
return identity;
|
|
32
|
+
}
|
|
33
|
+
get(key) {
|
|
34
|
+
if (this.disposed)
|
|
35
|
+
return undefined;
|
|
36
|
+
const entry = this.entries.get(key);
|
|
37
|
+
if (!entry) {
|
|
38
|
+
this.misses++;
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
this.hits++;
|
|
42
|
+
this.entries.delete(key);
|
|
43
|
+
this.entries.set(key, entry);
|
|
44
|
+
return entry.value;
|
|
45
|
+
}
|
|
46
|
+
/** Only the trusted host publishes fully verified results; failures are never retained. */
|
|
47
|
+
set(key, value, bytes) {
|
|
48
|
+
if (this.disposed)
|
|
49
|
+
return;
|
|
50
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0)
|
|
51
|
+
throw new TypeError('Invalid cache byte count');
|
|
52
|
+
const charge = bytes + key.length * 2;
|
|
53
|
+
if (this.maxEntries === 0 || charge > this.maxBytes)
|
|
54
|
+
return;
|
|
55
|
+
const previous = this.entries.get(key);
|
|
56
|
+
if (previous)
|
|
57
|
+
this.bytes -= previous.bytes;
|
|
58
|
+
this.entries.delete(key);
|
|
59
|
+
this.entries.set(key, { value, bytes: charge });
|
|
60
|
+
this.bytes += charge;
|
|
61
|
+
while (this.entries.size > this.maxEntries || this.bytes > this.maxBytes) {
|
|
62
|
+
const oldest = this.entries.keys().next().value;
|
|
63
|
+
this.bytes -= this.entries.get(oldest).bytes;
|
|
64
|
+
this.entries.delete(oldest);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
stats() {
|
|
68
|
+
return {
|
|
69
|
+
hits: this.hits,
|
|
70
|
+
misses: this.misses,
|
|
71
|
+
entries: this.entries.size,
|
|
72
|
+
bytes: this.bytes,
|
|
73
|
+
disposed: this.disposed
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
dispose() {
|
|
77
|
+
if (this.disposed)
|
|
78
|
+
return;
|
|
79
|
+
this.disposed = true;
|
|
80
|
+
this.entries.clear();
|
|
81
|
+
this.identities = new WeakMap();
|
|
82
|
+
this.bytes = 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type PlaygroundBindingOptions } from '@wasm-idle/core';
|
|
2
|
+
import type { PlaygroundBinding, SandboxRuntimeAssets } from './sandbox';
|
|
3
|
+
import { RuntimeAssetCache } from './runtimeAssetCache';
|
|
4
|
+
import { CompiledArtifactCache } from './compiledArtifactCache';
|
|
5
|
+
export interface PlaygroundRuntimeSession {
|
|
6
|
+
createBinding(): PlaygroundBinding & {
|
|
7
|
+
dispose(): Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
selectLanguage(language: string): Promise<void>;
|
|
10
|
+
dispose(): Promise<void>;
|
|
11
|
+
getCacheStats(): {
|
|
12
|
+
language: string | undefined;
|
|
13
|
+
assets: ReturnType<RuntimeAssetCache['stats']>;
|
|
14
|
+
artifacts: ReturnType<CompiledArtifactCache['stats']>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A browser-owned language session. Panels own disposable execution leases, not the
|
|
19
|
+
* compiler cache. A problem/editor change may release all leases without cooling it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createRuntimeSession(runtimeAssets: SandboxRuntimeAssets, options?: PlaygroundBindingOptions): PlaygroundRuntimeSession;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createPlaygroundBinding, normalizeLanguageId, RuntimeConfigurationError } from '@wasm-idle/core';
|
|
2
|
+
import playground from './index';
|
|
3
|
+
import { RuntimeAssetCache } from './runtimeAssetCache';
|
|
4
|
+
import { CompiledArtifactCache } from './compiledArtifactCache';
|
|
5
|
+
import { createCachedClangSandbox } from './cachedClang';
|
|
6
|
+
/**
|
|
7
|
+
* A browser-owned language session. Panels own disposable execution leases, not the
|
|
8
|
+
* compiler cache. A problem/editor change may release all leases without cooling it.
|
|
9
|
+
*/
|
|
10
|
+
export function createRuntimeSession(runtimeAssets, options = {}) {
|
|
11
|
+
let language;
|
|
12
|
+
let epoch = 0;
|
|
13
|
+
let disposed = false;
|
|
14
|
+
let disposal;
|
|
15
|
+
let assetCache = new RuntimeAssetCache();
|
|
16
|
+
let artifactCache = new CompiledArtifactCache();
|
|
17
|
+
const leases = new Set();
|
|
18
|
+
let retiring = Promise.resolve();
|
|
19
|
+
const stale = () => new RuntimeConfigurationError('Runtime session was disposed or its language changed', {
|
|
20
|
+
phase: 'dispose'
|
|
21
|
+
});
|
|
22
|
+
const release = (owned) => {
|
|
23
|
+
const previous = retiring;
|
|
24
|
+
retiring = Promise.allSettled([previous, ...owned.map((lease) => lease.dispose())]).then((results) => {
|
|
25
|
+
const failure = results.find((result) => result.status === 'rejected');
|
|
26
|
+
if (failure?.status === 'rejected')
|
|
27
|
+
throw failure.reason;
|
|
28
|
+
});
|
|
29
|
+
// Errors remain visible to the caller without unhandled background rejections.
|
|
30
|
+
void retiring.catch(() => undefined);
|
|
31
|
+
return retiring;
|
|
32
|
+
};
|
|
33
|
+
const session = {
|
|
34
|
+
async selectLanguage(value) {
|
|
35
|
+
if (disposed)
|
|
36
|
+
throw stale();
|
|
37
|
+
const next = normalizeLanguageId(value) || value.trim().toUpperCase();
|
|
38
|
+
if (!next)
|
|
39
|
+
return;
|
|
40
|
+
if (next === language)
|
|
41
|
+
return await retiring;
|
|
42
|
+
language = next;
|
|
43
|
+
epoch++;
|
|
44
|
+
assetCache.dispose();
|
|
45
|
+
artifactCache.clear();
|
|
46
|
+
assetCache = new RuntimeAssetCache();
|
|
47
|
+
artifactCache = new CompiledArtifactCache();
|
|
48
|
+
await release([...leases].filter((lease) => lease.epoch !== undefined));
|
|
49
|
+
},
|
|
50
|
+
createBinding() {
|
|
51
|
+
if (disposed)
|
|
52
|
+
throw stale();
|
|
53
|
+
let closed = false;
|
|
54
|
+
let closing;
|
|
55
|
+
const lease = {
|
|
56
|
+
epoch: undefined,
|
|
57
|
+
dispose() {
|
|
58
|
+
if (closing)
|
|
59
|
+
return closing;
|
|
60
|
+
closed = true;
|
|
61
|
+
leases.delete(lease);
|
|
62
|
+
closing = Promise.resolve().then(() => core.dispose());
|
|
63
|
+
return closing;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const core = createPlaygroundBinding(runtimeAssets, async (target) => {
|
|
67
|
+
if (closed || disposed)
|
|
68
|
+
throw stale();
|
|
69
|
+
const selected = session.selectLanguage(target);
|
|
70
|
+
const selectedEpoch = epoch;
|
|
71
|
+
await selected;
|
|
72
|
+
if (closed || disposed || epoch !== selectedEpoch)
|
|
73
|
+
throw stale();
|
|
74
|
+
lease.epoch = selectedEpoch;
|
|
75
|
+
const cache = assetCache;
|
|
76
|
+
const artifacts = artifactCache;
|
|
77
|
+
const sandbox = (await playground(target));
|
|
78
|
+
if (closed || disposed || epoch !== selectedEpoch) {
|
|
79
|
+
if (sandbox.dispose)
|
|
80
|
+
await sandbox.dispose();
|
|
81
|
+
else
|
|
82
|
+
await sandbox.terminate();
|
|
83
|
+
throw stale();
|
|
84
|
+
}
|
|
85
|
+
sandbox.setRuntimeAssetCache?.(cache);
|
|
86
|
+
return target === 'C' || target === 'CPP'
|
|
87
|
+
? createCachedClangSandbox(sandbox, target, cache, artifacts)
|
|
88
|
+
: sandbox;
|
|
89
|
+
}, options);
|
|
90
|
+
leases.add(lease);
|
|
91
|
+
const binding = core;
|
|
92
|
+
// Do not override core.dispose in place: the lease calls that exact owner method.
|
|
93
|
+
return new Proxy(binding, {
|
|
94
|
+
get(target, property) {
|
|
95
|
+
if (property === 'dispose')
|
|
96
|
+
return lease.dispose;
|
|
97
|
+
const value = Reflect.get(target, property);
|
|
98
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
dispose() {
|
|
103
|
+
if (disposal)
|
|
104
|
+
return disposal;
|
|
105
|
+
disposed = true;
|
|
106
|
+
epoch++;
|
|
107
|
+
assetCache.dispose();
|
|
108
|
+
artifactCache.clear();
|
|
109
|
+
disposal = release([...leases]);
|
|
110
|
+
return disposal;
|
|
111
|
+
},
|
|
112
|
+
getCacheStats: () => ({
|
|
113
|
+
language,
|
|
114
|
+
assets: assetCache.stats(),
|
|
115
|
+
artifacts: artifactCache.stats()
|
|
116
|
+
})
|
|
117
|
+
};
|
|
118
|
+
return session;
|
|
119
|
+
}
|
|
@@ -19,6 +19,7 @@ declare class Rust implements Sandbox {
|
|
|
19
19
|
debugManifestUrl: string;
|
|
20
20
|
debugManifestReceipt?: Readonly<RuntimeAssetIntegrityEntry>;
|
|
21
21
|
executableGraphFingerprint: string;
|
|
22
|
+
private persistentCacheBaseline?;
|
|
22
23
|
oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
23
24
|
waitingForInput: boolean;
|
|
24
25
|
pendingEof: boolean;
|
package/dist/playground/rust.js
CHANGED
|
@@ -8,7 +8,7 @@ import { resolveRustNonDebugResourceLimits } from './rustWorkerLimits';
|
|
|
8
8
|
import { WASM_RUST_EXECUTABLE_GRAPH_PROFILE, WASM_RUST_RUNTIME_PROFILE } from './wasmRustVersion';
|
|
9
9
|
import { WorkerSession } from './workerSession';
|
|
10
10
|
import { reportWorkerInputReady, reportWorkerProgress } from './workerProgress';
|
|
11
|
-
import { resolveExecutionLimits, TimeoutError } from '@wasm-idle/core';
|
|
11
|
+
import { resolveExecutionLimits, resolveRuntimeAssetCacheOptions, TimeoutError } from '@wasm-idle/core';
|
|
12
12
|
const debugBreakpointBufferInts = 1028;
|
|
13
13
|
const rustLldbSourcePath = '/workspace/main.rs';
|
|
14
14
|
const outputEncoder = new TextEncoder();
|
|
@@ -54,6 +54,7 @@ class Rust {
|
|
|
54
54
|
debugManifestUrl = '';
|
|
55
55
|
debugManifestReceipt;
|
|
56
56
|
executableGraphFingerprint = '';
|
|
57
|
+
persistentCacheBaseline;
|
|
57
58
|
oncompilerdiagnostic;
|
|
58
59
|
waitingForInput = false;
|
|
59
60
|
pendingEof = false;
|
|
@@ -122,6 +123,7 @@ class Rust {
|
|
|
122
123
|
let clearCandidateWait = () => { };
|
|
123
124
|
try {
|
|
124
125
|
const currentUrl = typeof window !== 'undefined' ? window.location.href : '';
|
|
126
|
+
const persistentCache = resolveRuntimeAssetCacheOptions(typeof runtimeAssets === 'object' ? runtimeAssets.persistentCache : undefined, _options.persistentCache);
|
|
125
127
|
const nextCompilerUrl = resolveRustCompilerUrl(runtimeAssets, currentUrl);
|
|
126
128
|
const nextDebugModuleUrl = resolveRustDebugModuleUrl(runtimeAssets, currentUrl);
|
|
127
129
|
const debugRuntime = resolveDebugRuntimeUrls(runtimeAssets, currentUrl);
|
|
@@ -175,6 +177,7 @@ class Rust {
|
|
|
175
177
|
this.executableGraphFingerprint !==
|
|
176
178
|
WASM_RUST_EXECUTABLE_GRAPH_PROFILE.fingerprint;
|
|
177
179
|
if (!needsWorkerReset) {
|
|
180
|
+
this.persistentCacheBaseline = persistentCache;
|
|
178
181
|
this.debugRuntimeBaseUrl = nextDebugRuntimeBaseUrl;
|
|
179
182
|
this.debugManifestUrl = nextDebugManifestUrl;
|
|
180
183
|
this.debugManifestReceipt = nextDebugManifestReceipt;
|
|
@@ -182,6 +185,7 @@ class Rust {
|
|
|
182
185
|
return;
|
|
183
186
|
}
|
|
184
187
|
nextExecutableGraph = await loadVerifiedRustExecutableGraph({
|
|
188
|
+
persistentCache,
|
|
185
189
|
moduleUrl: nextCompilerUrl,
|
|
186
190
|
currentUrl,
|
|
187
191
|
profile: WASM_RUST_EXECUTABLE_GRAPH_PROFILE,
|
|
@@ -255,6 +259,7 @@ class Rust {
|
|
|
255
259
|
};
|
|
256
260
|
candidateWorker.postMessage({
|
|
257
261
|
load: true,
|
|
262
|
+
persistentCache,
|
|
258
263
|
compilerUrl: nextExecutableGraph.entryUrl,
|
|
259
264
|
debugModuleUrl: nextDebugModuleUrl,
|
|
260
265
|
path: nextAssetPath,
|
|
@@ -274,6 +279,7 @@ class Rust {
|
|
|
274
279
|
this.waitingForInput = false;
|
|
275
280
|
this.pendingEof = false;
|
|
276
281
|
this.compilerUrl = nextCompilerUrl;
|
|
282
|
+
this.persistentCacheBaseline = persistentCache;
|
|
277
283
|
this.debugModuleUrl = nextDebugModuleUrl;
|
|
278
284
|
this.assetPath = nextAssetPath;
|
|
279
285
|
this.debugRuntimeBaseUrl = nextDebugRuntimeBaseUrl;
|
|
@@ -360,7 +366,9 @@ class Rust {
|
|
|
360
366
|
}
|
|
361
367
|
let limits;
|
|
362
368
|
let nonDebugResourceLimits;
|
|
369
|
+
let persistentCache;
|
|
363
370
|
try {
|
|
371
|
+
persistentCache = resolveRuntimeAssetCacheOptions(this.persistentCacheBaseline, options.persistentCache);
|
|
364
372
|
limits = resolveExecutionLimits(options.limits);
|
|
365
373
|
nonDebugResourceLimits =
|
|
366
374
|
debugMode === 'none' ? resolveRustNonDebugResourceLimits(limits) : undefined;
|
|
@@ -567,6 +575,7 @@ class Rust {
|
|
|
567
575
|
startPhaseTimeout('run', limits.runTimeoutMs);
|
|
568
576
|
const compilerWorker = this.worker;
|
|
569
577
|
const lldbSession = new LldbSandboxSession({
|
|
578
|
+
persistentCache,
|
|
570
579
|
manifestUrl: this.debugManifestUrl,
|
|
571
580
|
manifestReceipt: this.debugManifestReceipt,
|
|
572
581
|
runtimeBaseUrl: this.debugRuntimeBaseUrl,
|
|
@@ -671,6 +680,7 @@ class Rust {
|
|
|
671
680
|
this.begin = Date.now();
|
|
672
681
|
try {
|
|
673
682
|
this.worker.postMessage({
|
|
683
|
+
persistentCache,
|
|
674
684
|
code,
|
|
675
685
|
prepare,
|
|
676
686
|
buffer: this.buffer,
|
|
@@ -70,6 +70,7 @@ export interface RustExecutableGraphProgress {
|
|
|
70
70
|
readonly totalBytes: number;
|
|
71
71
|
}
|
|
72
72
|
export interface LoadRustExecutableGraphOptions {
|
|
73
|
+
readonly persistentCache?: import('@wasm-idle/core').RuntimeAssetCacheOptions;
|
|
73
74
|
readonly moduleUrl: string;
|
|
74
75
|
readonly currentUrl?: string;
|
|
75
76
|
readonly profile: unknown;
|
|
@@ -822,6 +822,7 @@ export async function loadVerifiedRustExecutableGraph(options) {
|
|
|
822
822
|
if (options.assetTimeoutMs !== undefined)
|
|
823
823
|
limits.assetTimeoutMs = options.assetTimeoutMs;
|
|
824
824
|
const preflight = await preflightRuntimeAssets({
|
|
825
|
+
persistentCache: options.persistentCache,
|
|
825
826
|
manifest: createPreflightManifest(profile, sourceContext.keysByPath),
|
|
826
827
|
runtimeId: 'rust/executable-graph',
|
|
827
828
|
rootUrl: sourceContext.storageRootUrl,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ExecutionLimits } from '@wasm-idle/core';
|
|
1
|
+
import { type ExecutionLimits, type RuntimeAssetCacheOptions } from '@wasm-idle/core';
|
|
2
2
|
import { type StaticRuntimePreflightProgress, type StaticRuntimePreflightRuntimeId } from './staticRuntimePreflightProtocol';
|
|
3
3
|
export interface StaticRuntimeWorkerPreflightRequest {
|
|
4
4
|
readonly runtimeId: StaticRuntimePreflightRuntimeId;
|
|
@@ -7,6 +7,7 @@ export interface StaticRuntimeWorkerPreflightRequest {
|
|
|
7
7
|
readonly manifestUrl: string;
|
|
8
8
|
readonly profile: unknown;
|
|
9
9
|
readonly limits?: Partial<ExecutionLimits>;
|
|
10
|
+
readonly persistentCache?: RuntimeAssetCacheOptions;
|
|
10
11
|
readonly signal?: AbortSignal;
|
|
11
12
|
readonly reportProgress?: (progress: StaticRuntimePreflightProgress) => void;
|
|
12
13
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { CancelledError, ProtocolError, TimeoutError, WorkerStartupError, resolveExecutionLimits } from '@wasm-idle/core';
|
|
1
|
+
import { CancelledError, ProtocolError, TimeoutError, WorkerStartupError, resolveExecutionLimits, resolveRuntimeAssetCacheOptions } from '@wasm-idle/core';
|
|
2
2
|
import { STATIC_RUNTIME_PREFLIGHT_PROTOCOL_VERSION, collectStaticRuntimePreflightTransferables, deserializeStaticRuntimePreflightError, isStaticRuntimePreflightProgress, isStaticRuntimePreflightSerializedError } from './staticRuntimePreflightProtocol';
|
|
3
3
|
const REQUEST_ID = 1;
|
|
4
4
|
export async function preflightStaticRuntimeAssetsInWorker(request) {
|
|
5
5
|
const limits = resolveExecutionLimits(request.limits);
|
|
6
|
+
const persistentCache = resolveRuntimeAssetCacheOptions(request.persistentCache);
|
|
6
7
|
if (request.signal?.aborted) {
|
|
7
8
|
throw new CancelledError(`${request.displayName} runtime preflight cancelled`, {
|
|
8
9
|
cause: request.signal.reason,
|
|
@@ -210,7 +211,8 @@ export async function preflightStaticRuntimeAssetsInWorker(request) {
|
|
|
210
211
|
baseUrl: request.baseUrl,
|
|
211
212
|
manifestUrl: request.manifestUrl,
|
|
212
213
|
profile: request.profile,
|
|
213
|
-
limits
|
|
214
|
+
limits,
|
|
215
|
+
persistentCache
|
|
214
216
|
});
|
|
215
217
|
}
|
|
216
218
|
catch (error) {
|
|
@@ -8,6 +8,7 @@ export async function executeStaticRuntimePreflight(request, reportProgress, sig
|
|
|
8
8
|
baseUrl: request.baseUrl,
|
|
9
9
|
manifestUrl: request.manifestUrl,
|
|
10
10
|
limits: request.limits,
|
|
11
|
+
persistentCache: request.persistentCache,
|
|
11
12
|
signal,
|
|
12
13
|
reportProgress: (progress) => reportProgress({ kind: 'asset', progress })
|
|
13
14
|
};
|