@wasm-idle/terminal 1.0.0-next.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/dist/Terminal.svelte +380 -61
- package/dist/index.d.ts +2 -2
- package/dist/types.d.ts +11 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -22,3 +22,32 @@ pnpm add wasm-idle @wasm-idle/terminal svelte
|
|
|
22
22
|
|
|
23
23
|
Compiler and language-runtime payloads are not included in this package. Configure their external
|
|
24
24
|
URLs through the injected playground binding.
|
|
25
|
+
|
|
26
|
+
## Stop lifecycle
|
|
27
|
+
|
|
28
|
+
`TerminalControl.stop()` does not resolve until the active sandbox's `kill()` or `terminate()`
|
|
29
|
+
operation resolves. Sandbox implementations may return either `void` or `Promise<void>` from those
|
|
30
|
+
methods; an asynchronous implementation must keep its promise pending until runtime resources are
|
|
31
|
+
released.
|
|
32
|
+
|
|
33
|
+
The C/C++ and Rust LLDB sandboxes use this contract to wait for both LLDB and WAMR worker teardown.
|
|
34
|
+
Callers can therefore await `stop()` before launching another debug session without overlapping the
|
|
35
|
+
previous session's workers or receiving its late events.
|
|
36
|
+
|
|
37
|
+
## Interactive runs
|
|
38
|
+
|
|
39
|
+
Set `interactive: true` only for a run that remains under explicit user control and has a working
|
|
40
|
+
stop/dispose path. The shared execution boundary then omits the wall-clock deadline only for the
|
|
41
|
+
final `run(..., prepare = false)` call. Runtime loading and `prepare()` keep their configured
|
|
42
|
+
deadlines, and AbortSignal cancellation, output/diagnostic limits, exclusive sandbox ownership, and
|
|
43
|
+
teardown remain enforced. wasm-idle uses this contract for Debug runs, which may stay paused at a
|
|
44
|
+
breakpoint indefinitely; ordinary Run requests remain time-bounded.
|
|
45
|
+
|
|
46
|
+
## Input generations
|
|
47
|
+
|
|
48
|
+
Input submitted while `prepare()` is still running remains queued for the matching `run()` when the
|
|
49
|
+
same language and runtime assets reuse that sandbox. `stop()`, `clear()`, `destroy()`, runtime
|
|
50
|
+
restart, component teardown, and a language or asset change discard queued input and EOF state
|
|
51
|
+
before retiring the sandbox. A later debug session therefore cannot consume stdin entered for an
|
|
52
|
+
older generation. Clipboard reads are asynchronous, so pasted text is accepted only if the input
|
|
53
|
+
generation that requested it is still current when the browser returns the clipboard contents.
|
package/dist/Terminal.svelte
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import {
|
|
3
|
+
RuntimeProgressController,
|
|
4
|
+
TimeoutError,
|
|
3
5
|
createRuntimeAssetsKey,
|
|
4
|
-
phaseProgress,
|
|
5
|
-
progressBandsForLanguage,
|
|
6
6
|
type DebugCommand,
|
|
7
|
-
type
|
|
7
|
+
type DebugDataBreakpoint,
|
|
8
|
+
type DebugDataBreakpointInfoArguments,
|
|
9
|
+
type DebugSessionEvent,
|
|
10
|
+
type ProgressLike
|
|
8
11
|
} from '@wasm-idle/core';
|
|
9
12
|
import { onMount } from 'svelte';
|
|
13
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
10
14
|
import '@xterm/xterm/css/xterm.css';
|
|
11
15
|
import type { Terminal as TerminalType } from '@xterm/xterm';
|
|
12
16
|
import registerAllPlugins from './plugin/index.js';
|
|
@@ -55,7 +59,9 @@
|
|
|
55
59
|
input = '',
|
|
56
60
|
inputCursor = 0,
|
|
57
61
|
pendingSandboxInput: string[] = [],
|
|
62
|
+
pendingDebugBreakpoints = new SvelteMap<string, number[]>(),
|
|
58
63
|
pendingSandboxEof = false,
|
|
64
|
+
sandboxInputGeneration = 0,
|
|
59
65
|
sandbox: BoundSandbox,
|
|
60
66
|
sandboxAcceptingInput = false,
|
|
61
67
|
first = true,
|
|
@@ -63,15 +69,91 @@
|
|
|
63
69
|
plugin = $state(),
|
|
64
70
|
ll: string | null = null,
|
|
65
71
|
loadedRuntimeAssetsKey: string | undefined = undefined,
|
|
66
|
-
stopRequested = false
|
|
72
|
+
stopRequested = false,
|
|
73
|
+
progressLifecycleCounter = 0,
|
|
74
|
+
activeExecutionProgress: ProgressLike | undefined,
|
|
75
|
+
activeExecutionLanguage: string | undefined,
|
|
76
|
+
preparedExecution:
|
|
77
|
+
| {
|
|
78
|
+
key: string;
|
|
79
|
+
sandbox: BoundSandbox;
|
|
80
|
+
}
|
|
81
|
+
| undefined;
|
|
82
|
+
let preparedExecutionGeneration = 0;
|
|
83
|
+
let lastExecutionError: unknown;
|
|
84
|
+
const progressController = new RuntimeProgressController();
|
|
85
|
+
const terminalOutputReadinessLanguages = new Set([
|
|
86
|
+
'C',
|
|
87
|
+
'CPP',
|
|
88
|
+
'OBJC',
|
|
89
|
+
'RUST',
|
|
90
|
+
'GO',
|
|
91
|
+
'D',
|
|
92
|
+
'CSHARP',
|
|
93
|
+
'FSHARP',
|
|
94
|
+
'VBNET',
|
|
95
|
+
'FORTRAN',
|
|
96
|
+
'COBOL',
|
|
97
|
+
'OCAML',
|
|
98
|
+
'HASKELL'
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
function clearPreparedExecution() {
|
|
102
|
+
preparedExecution = undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function invalidatePreparedExecution() {
|
|
106
|
+
preparedExecutionGeneration += 1;
|
|
107
|
+
clearPreparedExecution();
|
|
108
|
+
return preparedExecutionGeneration;
|
|
109
|
+
}
|
|
67
110
|
|
|
68
|
-
function
|
|
111
|
+
function executionPreparationKey(
|
|
112
|
+
language: string,
|
|
113
|
+
code: string,
|
|
114
|
+
log: boolean,
|
|
115
|
+
args: string[],
|
|
116
|
+
options: TerminalExecutionOptions
|
|
117
|
+
) {
|
|
118
|
+
try {
|
|
119
|
+
const keyOptions = { ...options };
|
|
120
|
+
delete keyOptions.signal;
|
|
121
|
+
return JSON.stringify([language, code, log, args, keyOptions]);
|
|
122
|
+
} catch {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function activityOnlyProgress(progress: ProgressLike | undefined) {
|
|
128
|
+
if (!progress) return undefined;
|
|
129
|
+
return {
|
|
130
|
+
set: progress.set,
|
|
131
|
+
report: (event: Parameters<NonNullable<ProgressLike['report']>>[0]) => {
|
|
132
|
+
if (event.kind === 'activity') progress.report?.(event);
|
|
133
|
+
}
|
|
134
|
+
} satisfies ProgressLike;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function writeTerminalOutput(text: string, executionOutput = false) {
|
|
69
138
|
if (!text) return;
|
|
139
|
+
if (
|
|
140
|
+
executionOutput &&
|
|
141
|
+
activeExecutionLanguage &&
|
|
142
|
+
terminalOutputReadinessLanguages.has(activeExecutionLanguage)
|
|
143
|
+
) {
|
|
144
|
+
activeExecutionProgress?.report?.({
|
|
145
|
+
kind: 'ready',
|
|
146
|
+
state: 'running',
|
|
147
|
+
reason: 'stdout',
|
|
148
|
+
label: 'Program output received'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
70
151
|
debugOutput += text;
|
|
71
152
|
term?.write(text);
|
|
72
153
|
}
|
|
73
154
|
|
|
74
155
|
function wait() {
|
|
156
|
+
if (term) return Promise.resolve();
|
|
75
157
|
return new Promise<void>((r) => {
|
|
76
158
|
const i = setInterval(() => {
|
|
77
159
|
if (term) {
|
|
@@ -91,7 +173,12 @@
|
|
|
91
173
|
let _tc = ++tc;
|
|
92
174
|
await wait();
|
|
93
175
|
sandboxAcceptingInput = false;
|
|
94
|
-
if (
|
|
176
|
+
if (requiresSandboxReset) clearPreparedExecution();
|
|
177
|
+
if (sandbox && requiresSandboxReset) {
|
|
178
|
+
discardPendingSandboxInput();
|
|
179
|
+
await sandbox.clear();
|
|
180
|
+
discardPendingSandboxInput();
|
|
181
|
+
}
|
|
95
182
|
input = '';
|
|
96
183
|
inputCursor = 0;
|
|
97
184
|
finish = false;
|
|
@@ -102,10 +189,21 @@
|
|
|
102
189
|
loadedRuntimeAssetsKey = currentRuntimeAssetsKey;
|
|
103
190
|
}
|
|
104
191
|
sandbox.image = onimage;
|
|
105
|
-
sandbox.ondebug =
|
|
192
|
+
sandbox.ondebug = (event) => {
|
|
193
|
+
if (event.type === 'pause') {
|
|
194
|
+
activeExecutionProgress?.report?.({
|
|
195
|
+
kind: 'ready',
|
|
196
|
+
state: 'paused',
|
|
197
|
+
reason: 'debug-paused',
|
|
198
|
+
label: 'Debugger paused'
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
ondebug?.(event);
|
|
202
|
+
};
|
|
106
203
|
sandbox.oncompilerdiagnostic = oncompilediagnostic;
|
|
107
204
|
sandbox.output = (output: string) =>
|
|
108
|
-
_tc === tc && writeTerminalOutput(output.replaceAll('\n', '\r\n'));
|
|
205
|
+
_tc === tc && writeTerminalOutput(output.replaceAll('\n', '\r\n'), true);
|
|
206
|
+
return requiresSandboxReset;
|
|
109
207
|
}
|
|
110
208
|
|
|
111
209
|
function flushPendingSandboxInput() {
|
|
@@ -121,12 +219,33 @@
|
|
|
121
219
|
}
|
|
122
220
|
}
|
|
123
221
|
|
|
222
|
+
function discardPendingSandboxInput() {
|
|
223
|
+
sandboxInputGeneration += 1;
|
|
224
|
+
pendingSandboxInput = [];
|
|
225
|
+
pendingSandboxEof = false;
|
|
226
|
+
}
|
|
227
|
+
|
|
124
228
|
function submitSandboxEof() {
|
|
125
229
|
if (sandbox && sandboxAcceptingInput) sandbox.eof?.();
|
|
126
230
|
else pendingSandboxEof = true;
|
|
127
231
|
}
|
|
128
232
|
|
|
129
|
-
function
|
|
233
|
+
function finishSandboxRun() {
|
|
234
|
+
sandboxAcceptingInput = false;
|
|
235
|
+
stopRequested = false;
|
|
236
|
+
onfinish?.();
|
|
237
|
+
finish = true;
|
|
238
|
+
if (term) term.options.cursorBlink = false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function isTimeoutError(error: unknown) {
|
|
242
|
+
return (
|
|
243
|
+
error instanceof TimeoutError ||
|
|
244
|
+
(error instanceof Error && error.name === 'TimeoutError')
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function runSandbox<T>(pr: Promise<T>, reportFinish = true, finalize = true) {
|
|
130
249
|
return pr
|
|
131
250
|
.then((x) => {
|
|
132
251
|
if (reportFinish) {
|
|
@@ -137,19 +256,37 @@
|
|
|
137
256
|
return x;
|
|
138
257
|
})
|
|
139
258
|
.catch((msg) => {
|
|
259
|
+
lastExecutionError = msg;
|
|
140
260
|
if (stopRequested) return false;
|
|
261
|
+
if (isTimeoutError(msg)) {
|
|
262
|
+
writeTerminalOutput(`\r\n\x1B[1;3;31m${msg}\u001B[?25l`);
|
|
263
|
+
throw msg;
|
|
264
|
+
}
|
|
141
265
|
writeTerminalOutput(`\r\n\x1B[1;3;31m${msg}\u001B[?25l`);
|
|
142
266
|
return false;
|
|
143
267
|
})
|
|
144
268
|
.finally(() => {
|
|
145
|
-
|
|
146
|
-
stopRequested = false;
|
|
147
|
-
onfinish?.();
|
|
148
|
-
finish = true;
|
|
149
|
-
if (term) term.options.cursorBlink = false;
|
|
269
|
+
if (finalize) finishSandboxRun();
|
|
150
270
|
});
|
|
151
271
|
}
|
|
152
272
|
|
|
273
|
+
async function withProgressLifecycle<T>(
|
|
274
|
+
language: string,
|
|
275
|
+
progress: ProgressLike | undefined,
|
|
276
|
+
operation: (progress: ProgressLike | undefined) => Promise<T>
|
|
277
|
+
) {
|
|
278
|
+
const lifecycle = progressController.begin(
|
|
279
|
+
`${language}:${++progressLifecycleCounter}`,
|
|
280
|
+
progress,
|
|
281
|
+
`Loading ${language} runtime`
|
|
282
|
+
);
|
|
283
|
+
try {
|
|
284
|
+
return await operation(lifecycle.progress);
|
|
285
|
+
} finally {
|
|
286
|
+
lifecycle.end();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
153
290
|
async function initTerm(blink = true) {
|
|
154
291
|
await wait();
|
|
155
292
|
if (!term) return;
|
|
@@ -242,8 +379,12 @@
|
|
|
242
379
|
}
|
|
243
380
|
|
|
244
381
|
const terminalControl: TerminalControl = {
|
|
382
|
+
getExecutionError: () => lastExecutionError,
|
|
245
383
|
async clear() {
|
|
246
384
|
await wait();
|
|
385
|
+
lastExecutionError = undefined;
|
|
386
|
+
invalidatePreparedExecution();
|
|
387
|
+
discardPendingSandboxInput();
|
|
247
388
|
term?.reset();
|
|
248
389
|
term?.write(`\u001B[?25l\x1b[0m\x1b[?25h`);
|
|
249
390
|
if (term) term.options.cursorBlink = false;
|
|
@@ -251,90 +392,259 @@
|
|
|
251
392
|
input = '';
|
|
252
393
|
inputCursor = 0;
|
|
253
394
|
sandboxAcceptingInput = false;
|
|
254
|
-
pendingSandboxEof = false;
|
|
255
395
|
first = true;
|
|
256
396
|
await new Promise((r) => setTimeout(r, 100));
|
|
397
|
+
discardPendingSandboxInput();
|
|
257
398
|
},
|
|
258
399
|
async prepare(
|
|
259
400
|
language: string,
|
|
260
401
|
code: string,
|
|
261
402
|
log = true,
|
|
262
|
-
prog?:
|
|
403
|
+
prog?: ProgressLike,
|
|
263
404
|
args: string[] = [],
|
|
264
405
|
options: TerminalExecutionOptions = {}
|
|
265
406
|
) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
407
|
+
return await withProgressLifecycle(language, prog, async (runProgress) => {
|
|
408
|
+
const preparationGeneration = invalidatePreparedExecution();
|
|
409
|
+
const prepareProgress = activityOnlyProgress(runProgress);
|
|
410
|
+
await Promise.all([
|
|
411
|
+
initSandbox(language).then(() =>
|
|
412
|
+
sandbox.load(code, log, args, options, prepareProgress)
|
|
413
|
+
),
|
|
414
|
+
initTerm(false)
|
|
415
|
+
]);
|
|
416
|
+
const preparationSandbox = sandbox;
|
|
417
|
+
runProgress?.report?.({
|
|
418
|
+
kind: 'activity',
|
|
419
|
+
phase: 'starting',
|
|
420
|
+
label: `Preparing ${language} program`
|
|
421
|
+
});
|
|
422
|
+
const prepared = !!(await runSandbox(
|
|
423
|
+
preparationSandbox.run(code, true, log, prepareProgress, args, options),
|
|
424
|
+
false
|
|
425
|
+
));
|
|
426
|
+
const preparationKey = executionPreparationKey(language, code, log, args, options);
|
|
427
|
+
if (
|
|
428
|
+
prepared &&
|
|
429
|
+
preparationKey &&
|
|
430
|
+
preparationGeneration === preparedExecutionGeneration &&
|
|
431
|
+
preparationSandbox === sandbox
|
|
432
|
+
) {
|
|
433
|
+
preparedExecution = { key: preparationKey, sandbox: preparationSandbox };
|
|
434
|
+
}
|
|
435
|
+
return prepared;
|
|
436
|
+
});
|
|
293
437
|
},
|
|
294
438
|
async run(
|
|
295
439
|
language: string,
|
|
296
440
|
code: string,
|
|
297
441
|
log = true,
|
|
298
|
-
prog?:
|
|
442
|
+
prog?: ProgressLike,
|
|
299
443
|
args: string[] = [],
|
|
300
444
|
options: TerminalExecutionOptions = {}
|
|
301
445
|
) {
|
|
302
|
-
await
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
446
|
+
return await withProgressLifecycle(language, prog, async (runProgress) => {
|
|
447
|
+
let executionRunWillFinalize = false;
|
|
448
|
+
const preparedCandidate = preparedExecution;
|
|
449
|
+
const executionGeneration = invalidatePreparedExecution();
|
|
450
|
+
pendingDebugBreakpoints.clear();
|
|
451
|
+
for (const { sourcePath, lines } of options.sourceBreakpoints || []) {
|
|
452
|
+
pendingDebugBreakpoints.set(sourcePath, [...lines]);
|
|
453
|
+
}
|
|
454
|
+
const [sandboxWasReset] = await Promise.all([
|
|
455
|
+
initSandbox(language).then(async (wasReset) => {
|
|
456
|
+
await sandbox.load(
|
|
457
|
+
code,
|
|
458
|
+
log,
|
|
459
|
+
args,
|
|
460
|
+
options,
|
|
461
|
+
activityOnlyProgress(runProgress)
|
|
462
|
+
);
|
|
463
|
+
return wasReset;
|
|
464
|
+
}),
|
|
465
|
+
initTerm()
|
|
466
|
+
]);
|
|
467
|
+
const executionOptions = {
|
|
468
|
+
...options,
|
|
469
|
+
sourceBreakpoints: Array.from(
|
|
470
|
+
pendingDebugBreakpoints,
|
|
471
|
+
([sourcePath, lines]) => ({
|
|
472
|
+
sourcePath,
|
|
473
|
+
lines: [...lines]
|
|
474
|
+
})
|
|
475
|
+
)
|
|
476
|
+
};
|
|
477
|
+
const preparationKey = executionPreparationKey(language, code, log, args, options);
|
|
478
|
+
const mayUsePreparedOutput =
|
|
479
|
+
executionGeneration === preparedExecutionGeneration &&
|
|
480
|
+
!sandboxWasReset &&
|
|
481
|
+
!!preparationKey &&
|
|
482
|
+
preparedCandidate?.sandbox === sandbox &&
|
|
483
|
+
preparedCandidate.key === preparationKey;
|
|
484
|
+
try {
|
|
485
|
+
if (terminalOutputReadinessLanguages.has(language) && !mayUsePreparedOutput) {
|
|
486
|
+
const prepareProgress = activityOnlyProgress(runProgress);
|
|
487
|
+
runProgress?.report?.({
|
|
488
|
+
kind: 'activity',
|
|
489
|
+
phase: 'starting',
|
|
490
|
+
label: `Preparing ${language} program`
|
|
491
|
+
});
|
|
492
|
+
const prepared = !!(await runSandbox(
|
|
493
|
+
sandbox.run(code, true, log, prepareProgress, args, options),
|
|
494
|
+
false,
|
|
495
|
+
false
|
|
496
|
+
));
|
|
497
|
+
if (!prepared) {
|
|
498
|
+
runProgress?.report?.({
|
|
499
|
+
kind: 'settled',
|
|
500
|
+
outcome: options.signal?.aborted ? 'cancelled' : 'failed',
|
|
501
|
+
label: `${language} run ended`
|
|
502
|
+
});
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
activeExecutionProgress = runProgress;
|
|
508
|
+
activeExecutionLanguage = language;
|
|
509
|
+
sandboxAcceptingInput = true;
|
|
510
|
+
flushPendingSandboxInput();
|
|
511
|
+
executionRunWillFinalize = true;
|
|
512
|
+
const result = await runSandbox(
|
|
513
|
+
sandbox.run(code, false, log, runProgress, args, executionOptions)
|
|
514
|
+
);
|
|
515
|
+
if (result === false) {
|
|
516
|
+
runProgress?.report?.({
|
|
517
|
+
kind: 'settled',
|
|
518
|
+
outcome: options.signal?.aborted ? 'cancelled' : 'failed',
|
|
519
|
+
label: `${language} run ended`
|
|
520
|
+
});
|
|
521
|
+
} else {
|
|
522
|
+
runProgress?.report?.({
|
|
523
|
+
kind: 'ready',
|
|
524
|
+
state: 'running',
|
|
525
|
+
reason: 'result',
|
|
526
|
+
label: `${language} run completed`
|
|
527
|
+
});
|
|
528
|
+
runProgress?.report?.({
|
|
529
|
+
kind: 'settled',
|
|
530
|
+
outcome: 'completed',
|
|
531
|
+
label: `${language} run completed`
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
return result;
|
|
535
|
+
} catch (error) {
|
|
536
|
+
if (isTimeoutError(error)) {
|
|
537
|
+
runProgress?.report?.({
|
|
538
|
+
kind: 'settled',
|
|
539
|
+
outcome: 'timed-out',
|
|
540
|
+
label: `${language} run timed out`
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
throw error;
|
|
544
|
+
} finally {
|
|
545
|
+
if (!executionRunWillFinalize) finishSandboxRun();
|
|
546
|
+
if (activeExecutionProgress === runProgress) {
|
|
547
|
+
activeExecutionProgress = undefined;
|
|
548
|
+
activeExecutionLanguage = undefined;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
});
|
|
309
552
|
},
|
|
310
553
|
async destroy() {
|
|
311
554
|
await wait();
|
|
555
|
+
progressController.invalidate();
|
|
556
|
+
invalidatePreparedExecution();
|
|
312
557
|
sandboxAcceptingInput = false;
|
|
313
|
-
|
|
558
|
+
discardPendingSandboxInput();
|
|
314
559
|
term?.dispose();
|
|
315
560
|
if (sandbox) await sandbox.clear();
|
|
561
|
+
discardPendingSandboxInput();
|
|
562
|
+
},
|
|
563
|
+
async restartRuntime() {
|
|
564
|
+
await wait();
|
|
565
|
+
if (!sandbox) return;
|
|
566
|
+
progressController.invalidate();
|
|
567
|
+
invalidatePreparedExecution();
|
|
568
|
+
sandboxAcceptingInput = false;
|
|
569
|
+
discardPendingSandboxInput();
|
|
570
|
+
input = '';
|
|
571
|
+
inputCursor = 0;
|
|
572
|
+
finish = true;
|
|
573
|
+
stopRequested = false;
|
|
574
|
+
tc += 1;
|
|
575
|
+
if (sandbox.restart) await sandbox.restart();
|
|
576
|
+
else await sandbox.clear();
|
|
577
|
+
discardPendingSandboxInput();
|
|
316
578
|
},
|
|
317
579
|
async stop() {
|
|
318
580
|
await wait();
|
|
581
|
+
progressController.invalidate();
|
|
582
|
+
invalidatePreparedExecution();
|
|
319
583
|
stopRequested = true;
|
|
320
584
|
finish = true;
|
|
321
585
|
sandboxAcceptingInput = false;
|
|
322
|
-
|
|
323
|
-
if (sandbox?.kill) sandbox.kill();
|
|
324
|
-
else sandbox?.terminate?.();
|
|
586
|
+
discardPendingSandboxInput();
|
|
587
|
+
if (sandbox?.kill) await sandbox.kill();
|
|
588
|
+
else await sandbox?.terminate?.();
|
|
589
|
+
discardPendingSandboxInput();
|
|
325
590
|
},
|
|
326
591
|
async debugCommand(command: DebugCommand) {
|
|
327
592
|
await wait();
|
|
328
|
-
sandbox.debugCommand?.(command);
|
|
593
|
+
await sandbox.debugCommand?.(command);
|
|
594
|
+
},
|
|
595
|
+
async debugPause() {
|
|
596
|
+
await wait();
|
|
597
|
+
await sandbox.debugPause?.();
|
|
329
598
|
},
|
|
330
|
-
async setBreakpoints(lines: number[]) {
|
|
599
|
+
async setBreakpoints(lines: number[], sourcePath?: string) {
|
|
600
|
+
pendingDebugBreakpoints.set(sourcePath || '', [...lines]);
|
|
331
601
|
await wait();
|
|
332
|
-
sandbox?.setBreakpoints?.(lines);
|
|
602
|
+
await sandbox?.setBreakpoints?.(lines, sourcePath);
|
|
333
603
|
},
|
|
334
604
|
async debugEvaluate(expression: string) {
|
|
335
605
|
await wait();
|
|
336
606
|
return (await sandbox.debugEvaluate?.(expression)) || '?';
|
|
337
607
|
},
|
|
608
|
+
async debugVariables(variablesReference: number, start?: number, count?: number) {
|
|
609
|
+
await wait();
|
|
610
|
+
return (await sandbox.debugVariables?.(variablesReference, start, count)) || [];
|
|
611
|
+
},
|
|
612
|
+
async debugScopes(frameId: number) {
|
|
613
|
+
await wait();
|
|
614
|
+
return (await sandbox.debugScopes?.(frameId)) ?? [];
|
|
615
|
+
},
|
|
616
|
+
async debugFrameScopes(frameId: number) {
|
|
617
|
+
await wait();
|
|
618
|
+
return (await sandbox.debugFrameScopes?.(frameId)) ?? [];
|
|
619
|
+
},
|
|
620
|
+
async debugFrameName(frameId: number) {
|
|
621
|
+
await wait();
|
|
622
|
+
return (await sandbox.debugFrameName?.(frameId)) ?? null;
|
|
623
|
+
},
|
|
624
|
+
async debugReadMemory(memoryReference: string, offset: number, count: number) {
|
|
625
|
+
await wait();
|
|
626
|
+
return (await sandbox.debugReadMemory?.(memoryReference, offset, count)) ?? null;
|
|
627
|
+
},
|
|
628
|
+
async debugWriteMemory(
|
|
629
|
+
memoryReference: string,
|
|
630
|
+
offset: number,
|
|
631
|
+
data: Uint8Array,
|
|
632
|
+
allowPartial?: boolean
|
|
633
|
+
) {
|
|
634
|
+
await wait();
|
|
635
|
+
return (
|
|
636
|
+
(await sandbox.debugWriteMemory?.(memoryReference, offset, data, allowPartial)) ??
|
|
637
|
+
null
|
|
638
|
+
);
|
|
639
|
+
},
|
|
640
|
+
async debugDataBreakpointInfo(arguments_: DebugDataBreakpointInfoArguments) {
|
|
641
|
+
await wait();
|
|
642
|
+
return (await sandbox.debugDataBreakpointInfo?.(arguments_)) ?? null;
|
|
643
|
+
},
|
|
644
|
+
async debugSetDataBreakpoints(breakpoints: DebugDataBreakpoint[]) {
|
|
645
|
+
await wait();
|
|
646
|
+
return (await sandbox.debugSetDataBreakpoints?.(breakpoints)) ?? [];
|
|
647
|
+
},
|
|
338
648
|
async waitForInput() {
|
|
339
649
|
await waitForInput();
|
|
340
650
|
},
|
|
@@ -477,9 +787,14 @@
|
|
|
477
787
|
submitSandboxEof();
|
|
478
788
|
} else if ((ev.ctrlKey || ev.metaKey) && ev.key.toLowerCase() === 'v') {
|
|
479
789
|
ev.preventDefault();
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
790
|
+
const generation = sandboxInputGeneration;
|
|
791
|
+
navigator.clipboard
|
|
792
|
+
.readText()
|
|
793
|
+
.then((text) => {
|
|
794
|
+
if (generation !== sandboxInputGeneration) return;
|
|
795
|
+
applyPastedText(text);
|
|
796
|
+
})
|
|
797
|
+
.catch(() => {});
|
|
483
798
|
}
|
|
484
799
|
});
|
|
485
800
|
plugin = await registerAllPlugins(term);
|
|
@@ -488,8 +803,12 @@
|
|
|
488
803
|
});
|
|
489
804
|
|
|
490
805
|
return async () => {
|
|
806
|
+
progressController.invalidate();
|
|
807
|
+
invalidatePreparedExecution();
|
|
808
|
+
discardPendingSandboxInput();
|
|
491
809
|
term?.dispose();
|
|
492
810
|
if (sandbox) await sandbox.clear();
|
|
811
|
+
discardPendingSandboxInput();
|
|
493
812
|
};
|
|
494
813
|
});
|
|
495
814
|
</script>
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Terminal from './Terminal.svelte';
|
|
2
2
|
import registerAllPlugins from './plugin/index.js';
|
|
3
3
|
import Theme from './theme.js';
|
|
4
|
-
import type { BoundSandbox, CompilerDiagnostic, CompilerDiagnosticSeverity, DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, PlaygroundBinding, SandboxExecutionOptions, TerminalControl, TerminalExecutionOptions } from './types.js';
|
|
4
|
+
import type { BoundSandbox, CompilerDiagnostic, CompilerDiagnosticSeverity, DebugCommand, DebugFrame, DebugMemory, DebugSessionEvent, DebugVariable, DebugWriteMemoryResult, PlaygroundBinding, SandboxExecutionOptions, TerminalControl, TerminalExecutionOptions } from './types.js';
|
|
5
5
|
export { Terminal, Theme, registerAllPlugins };
|
|
6
|
-
export type { BoundSandbox, CompilerDiagnostic, CompilerDiagnosticSeverity, DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, PlaygroundBinding, SandboxExecutionOptions, TerminalControl, TerminalExecutionOptions };
|
|
6
|
+
export type { BoundSandbox, CompilerDiagnostic, CompilerDiagnosticSeverity, DebugCommand, DebugFrame, DebugMemory, DebugSessionEvent, DebugVariable, DebugWriteMemoryResult, PlaygroundBinding, SandboxExecutionOptions, TerminalControl, TerminalExecutionOptions };
|
|
7
7
|
export default Terminal;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BoundSandbox as CoreBoundSandbox, DebugCommand, PlaygroundBinding as CorePlaygroundBinding, ProgressLike, SandboxExecutionOptions as CoreSandboxExecutionOptions, TerminalControl as CoreTerminalControl } from '@wasm-idle/core';
|
|
1
|
+
import type { BoundSandbox as CoreBoundSandbox, DebugCommand, DebugSourceBreakpoints, PlaygroundBinding as CorePlaygroundBinding, ProgressLike, SandboxExecutionOptions as CoreSandboxExecutionOptions, TerminalControl as CoreTerminalControl } from '@wasm-idle/core';
|
|
2
2
|
export type CompilerDiagnosticSeverity = 'error' | 'warning' | 'other';
|
|
3
3
|
export interface CompilerDiagnostic {
|
|
4
4
|
fileName?: string | null;
|
|
@@ -9,6 +9,7 @@ export interface CompilerDiagnostic {
|
|
|
9
9
|
message: string;
|
|
10
10
|
}
|
|
11
11
|
export type RustTargetTriple = 'wasm32-wasip1' | 'wasm32-wasip2' | 'wasm32-wasip3';
|
|
12
|
+
export type BrowserDebugMode = 'none' | 'trace' | 'lldb';
|
|
12
13
|
export type GoTarget = 'wasip1/wasm' | 'wasip2/wasm' | 'wasip3/wasm' | 'js/wasm';
|
|
13
14
|
export type TinyGoTarget = 'wasm' | 'wasip1' | 'wasip2' | 'wasip3';
|
|
14
15
|
export type OcamlBackend = 'js' | 'wasm';
|
|
@@ -19,10 +20,16 @@ export interface SandboxWorkspaceFile {
|
|
|
19
20
|
content: string;
|
|
20
21
|
}
|
|
21
22
|
export interface SandboxExecutionOptions {
|
|
23
|
+
debugMode?: BrowserDebugMode;
|
|
24
|
+
/** @deprecated Use debugMode. */
|
|
22
25
|
debug?: boolean;
|
|
26
|
+
/** Keep the final run alive until the host explicitly stops or disposes it. */
|
|
27
|
+
interactive?: boolean;
|
|
23
28
|
breakpoints?: number[];
|
|
29
|
+
sourceBreakpoints?: DebugSourceBreakpoints[];
|
|
24
30
|
pauseOnEntry?: boolean;
|
|
25
31
|
stdin?: string;
|
|
32
|
+
signal?: AbortSignal;
|
|
26
33
|
activePath?: string;
|
|
27
34
|
debugPath?: string;
|
|
28
35
|
workspaceFiles?: SandboxWorkspaceFile[];
|
|
@@ -47,8 +54,10 @@ export interface PlaygroundBinding extends Pick<CorePlaygroundBinding, 'runtimeA
|
|
|
47
54
|
load: (language: string) => Promise<BoundSandbox>;
|
|
48
55
|
}
|
|
49
56
|
export interface TerminalControl extends Omit<CoreTerminalControl, 'prepare' | 'run' | 'debugCommand'> {
|
|
57
|
+
/** The original failure when the compatibility runner resolves false. */
|
|
58
|
+
getExecutionError?: () => unknown;
|
|
50
59
|
prepare: (language: string, code: string, log?: boolean, progress?: ProgressLike, args?: string[], options?: TerminalExecutionOptions) => Promise<boolean>;
|
|
51
60
|
run: (language: string, code: string, log?: boolean, progress?: ProgressLike, args?: string[], options?: TerminalExecutionOptions) => Promise<boolean | string>;
|
|
52
61
|
debugCommand: (command: DebugCommand) => Promise<void>;
|
|
53
62
|
}
|
|
54
|
-
export type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, ProgressLike } from '@wasm-idle/core';
|
|
63
|
+
export type { DebugCommand, DebugFrame, DebugMemory, DebugResolvedBreakpoint, DebugSessionEvent, DebugScope, DebugVariable, DebugWriteMemoryResult, ProgressLike } from '@wasm-idle/core';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wasm-idle/terminal",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Svelte terminal component for wasm-idle playground bindings",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
],
|
|
20
20
|
"publishConfig": {
|
|
21
21
|
"access": "public",
|
|
22
|
-
"tag": "
|
|
22
|
+
"tag": "latest"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@wasm-idle/core": "1.0.0
|
|
25
|
+
"@wasm-idle/core": "1.0.0",
|
|
26
26
|
"svelte": "^5.0.0"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@xterm/addon-web-links": "^0.12.0",
|
|
34
34
|
"@xterm/addon-webgl": "^0.19.0",
|
|
35
35
|
"@xterm/xterm": "^6.0.0",
|
|
36
|
-
"@wasm-idle/core": "1.0.0
|
|
36
|
+
"@wasm-idle/core": "1.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@sveltejs/package": "^2.5.7",
|