wasm-idle 0.3.2 → 0.4.1
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 +22 -2
- package/dist/clang/app.d.ts +2 -1
- package/dist/clang/app.js +166 -137
- package/dist/clang/index.d.ts +6 -0
- package/dist/clang/index.js +137 -21
- package/dist/debug/expression.js +135 -19
- package/dist/debug/index.d.ts +1 -1
- package/dist/debug/index.js +1 -1
- package/dist/debug/language/index.d.ts +1 -0
- package/dist/debug/language/index.js +1 -0
- package/dist/debug/language/python.d.ts +2 -0
- package/dist/debug/language/python.js +14 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/playground/assetBridge.d.ts +20 -0
- package/dist/playground/assetBridge.js +192 -0
- package/dist/playground/assets.d.ts +32 -0
- package/dist/playground/assets.js +97 -0
- package/dist/playground/clang.d.ts +11 -3
- package/dist/playground/clang.js +57 -20
- package/dist/playground/index.d.ts +2 -1
- package/dist/playground/index.js +10 -2
- package/dist/playground/java.d.ts +33 -0
- package/dist/playground/java.js +161 -0
- package/dist/playground/javaStdin.d.ts +8 -0
- package/dist/playground/javaStdin.js +323 -0
- package/dist/playground/options.d.ts +19 -0
- package/dist/playground/options.js +15 -1
- package/dist/playground/python.d.ts +21 -6
- package/dist/playground/python.js +135 -25
- package/dist/playground/sandbox.d.ts +5 -2
- package/dist/playground/stdinBuffer.d.ts +7 -0
- package/dist/playground/stdinBuffer.js +100 -0
- package/dist/playground/teavmConfig.d.ts +3 -0
- package/dist/playground/teavmConfig.js +14 -0
- package/dist/playground/worker/assets.d.ts +12 -0
- package/dist/playground/worker/assets.js +257 -0
- package/dist/playground/worker/clang.js +13 -23
- package/dist/playground/worker/java.d.ts +1 -0
- package/dist/playground/worker/java.js +228 -0
- package/dist/playground/worker/python.js +264 -58
- package/dist/terminal/Terminal.svelte +98 -18
- package/dist/terminal/Terminal.svelte.d.ts +4 -1
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +4 -2
- package/package.json +11 -11
package/dist/debug/expression.js
CHANGED
|
@@ -8,6 +8,38 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
8
8
|
values.set(variable.name, variable.value);
|
|
9
9
|
const parsedValues = new Map();
|
|
10
10
|
const tokens = [];
|
|
11
|
+
const parseQuotedValue = (text, start) => {
|
|
12
|
+
const quote = text[start];
|
|
13
|
+
if (quote !== "'" && quote !== '"')
|
|
14
|
+
throw new Error('expected quoted string');
|
|
15
|
+
let index = start + 1;
|
|
16
|
+
let value = '';
|
|
17
|
+
while (index < text.length) {
|
|
18
|
+
const character = text[index];
|
|
19
|
+
if (!character)
|
|
20
|
+
break;
|
|
21
|
+
if (character === '\\') {
|
|
22
|
+
const escaped = text[index + 1];
|
|
23
|
+
if (!escaped)
|
|
24
|
+
throw new Error('unterminated string literal');
|
|
25
|
+
if (escaped === 'n')
|
|
26
|
+
value += '\n';
|
|
27
|
+
else if (escaped === 'r')
|
|
28
|
+
value += '\r';
|
|
29
|
+
else if (escaped === 't')
|
|
30
|
+
value += '\t';
|
|
31
|
+
else
|
|
32
|
+
value += escaped;
|
|
33
|
+
index += 2;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (character === quote)
|
|
37
|
+
return { value, next: index + 1 };
|
|
38
|
+
value += character;
|
|
39
|
+
index += 1;
|
|
40
|
+
}
|
|
41
|
+
throw new Error('unterminated string literal');
|
|
42
|
+
};
|
|
11
43
|
for (let index = 0; index < source.length;) {
|
|
12
44
|
const character = source[index];
|
|
13
45
|
if (!character)
|
|
@@ -32,6 +64,12 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
32
64
|
index += operator.length;
|
|
33
65
|
continue;
|
|
34
66
|
}
|
|
67
|
+
if (character === "'" || character === '"') {
|
|
68
|
+
const parsed = parseQuotedValue(source, index);
|
|
69
|
+
tokens.push({ type: 'string', value: parsed.value });
|
|
70
|
+
index = parsed.next;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
35
73
|
const number = source.slice(index).match(/^\d+(?:\.\d+)?/);
|
|
36
74
|
if (number?.[0]) {
|
|
37
75
|
tokens.push({ type: 'number', value: number[0] });
|
|
@@ -40,8 +78,26 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
40
78
|
}
|
|
41
79
|
const identifier = source.slice(index).match(/^[A-Za-z_]\w*/);
|
|
42
80
|
if (identifier?.[0]) {
|
|
43
|
-
if (identifier[0] === 'true' ||
|
|
44
|
-
|
|
81
|
+
if (identifier[0] === 'true' ||
|
|
82
|
+
identifier[0] === 'false' ||
|
|
83
|
+
identifier[0] === 'True' ||
|
|
84
|
+
identifier[0] === 'False') {
|
|
85
|
+
tokens.push({
|
|
86
|
+
type: 'boolean',
|
|
87
|
+
value: identifier[0] === 'true' || identifier[0] === 'True'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else if (identifier[0] === 'null' || identifier[0] === 'None') {
|
|
91
|
+
tokens.push({ type: 'null' });
|
|
92
|
+
}
|
|
93
|
+
else if (identifier[0] === 'and') {
|
|
94
|
+
tokens.push({ type: 'operator', value: '&&' });
|
|
95
|
+
}
|
|
96
|
+
else if (identifier[0] === 'or') {
|
|
97
|
+
tokens.push({ type: 'operator', value: '||' });
|
|
98
|
+
}
|
|
99
|
+
else if (identifier[0] === 'not') {
|
|
100
|
+
tokens.push({ type: 'operator', value: '!' });
|
|
45
101
|
}
|
|
46
102
|
else {
|
|
47
103
|
tokens.push({ type: 'identifier', value: identifier[0] });
|
|
@@ -81,16 +137,44 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
81
137
|
throw new Error('unsupported array preview');
|
|
82
138
|
}
|
|
83
139
|
}
|
|
84
|
-
if (character ===
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
140
|
+
if (character === '(') {
|
|
141
|
+
index += 1;
|
|
142
|
+
const items = [];
|
|
143
|
+
while (true) {
|
|
144
|
+
while (/\s/.test(text[index] || ''))
|
|
145
|
+
index += 1;
|
|
146
|
+
if (text[index] === ')')
|
|
147
|
+
return { value: items, next: index + 1 };
|
|
148
|
+
if (text.startsWith('...', index))
|
|
149
|
+
throw new Error('unavailable');
|
|
150
|
+
const item = parsePreviewValue(text, index);
|
|
151
|
+
items.push(item.value);
|
|
152
|
+
index = item.next;
|
|
153
|
+
while (/\s/.test(text[index] || ''))
|
|
154
|
+
index += 1;
|
|
155
|
+
if (text[index] === ',') {
|
|
156
|
+
index += 1;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (text[index] === ')')
|
|
160
|
+
return { value: items, next: index + 1 };
|
|
161
|
+
throw new Error('unsupported tuple preview');
|
|
162
|
+
}
|
|
89
163
|
}
|
|
164
|
+
if (character === "'" || character === '"')
|
|
165
|
+
return parseQuotedValue(text, index);
|
|
90
166
|
if (text.startsWith('true', index))
|
|
91
167
|
return { value: true, next: index + 4 };
|
|
92
168
|
if (text.startsWith('false', index))
|
|
93
169
|
return { value: false, next: index + 5 };
|
|
170
|
+
if (text.startsWith('True', index))
|
|
171
|
+
return { value: true, next: index + 4 };
|
|
172
|
+
if (text.startsWith('False', index))
|
|
173
|
+
return { value: false, next: index + 5 };
|
|
174
|
+
if (text.startsWith('null', index))
|
|
175
|
+
return { value: null, next: index + 4 };
|
|
176
|
+
if (text.startsWith('None', index))
|
|
177
|
+
return { value: null, next: index + 4 };
|
|
94
178
|
const number = text.slice(index).match(/^-?\d+(?:\.\d+)?/);
|
|
95
179
|
if (number?.[0])
|
|
96
180
|
return { value: Number(number[0]), next: index + number[0].length };
|
|
@@ -102,17 +186,24 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
102
186
|
const value = values.get(name);
|
|
103
187
|
if (value == null || value === '?')
|
|
104
188
|
throw new Error('unavailable');
|
|
105
|
-
if (value === 'true' || value === 'false') {
|
|
106
|
-
const parsed = value === 'true';
|
|
189
|
+
if (value === 'true' || value === 'false' || value === 'True' || value === 'False') {
|
|
190
|
+
const parsed = value === 'true' || value === 'True';
|
|
107
191
|
parsedValues.set(name, parsed);
|
|
108
192
|
return parsed;
|
|
109
193
|
}
|
|
194
|
+
if (value === 'null' || value === 'None') {
|
|
195
|
+
parsedValues.set(name, null);
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
110
198
|
const numeric = Number(value);
|
|
111
199
|
if (!Number.isNaN(numeric)) {
|
|
112
200
|
parsedValues.set(name, numeric);
|
|
113
201
|
return numeric;
|
|
114
202
|
}
|
|
115
|
-
if (value.startsWith('[')
|
|
203
|
+
if (value.startsWith('[') ||
|
|
204
|
+
value.startsWith('(') ||
|
|
205
|
+
value.startsWith("'") ||
|
|
206
|
+
value.startsWith('"')) {
|
|
116
207
|
const parsed = parsePreviewValue(value).value;
|
|
117
208
|
parsedValues.set(name, parsed);
|
|
118
209
|
return parsed;
|
|
@@ -131,10 +222,21 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
131
222
|
cursor += 1;
|
|
132
223
|
return token.value;
|
|
133
224
|
}
|
|
225
|
+
if (token.type === 'null') {
|
|
226
|
+
cursor += 1;
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
if (token.type === 'string') {
|
|
230
|
+
cursor += 1;
|
|
231
|
+
return token.value;
|
|
232
|
+
}
|
|
134
233
|
if (token.type === 'identifier') {
|
|
135
234
|
cursor += 1;
|
|
136
235
|
let resolved = resolveIdentifierValue(token.value);
|
|
137
|
-
while (
|
|
236
|
+
while (true) {
|
|
237
|
+
const bracket = tokens[cursor];
|
|
238
|
+
if (!bracket || bracket.type !== 'bracket' || bracket.value !== '[')
|
|
239
|
+
break;
|
|
138
240
|
cursor += 1;
|
|
139
241
|
const index = Number(parseOr());
|
|
140
242
|
const closing = tokens[cursor];
|
|
@@ -201,8 +303,14 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
201
303
|
return left;
|
|
202
304
|
cursor += 1;
|
|
203
305
|
const right = parseMul();
|
|
204
|
-
if (operator.value === '+')
|
|
205
|
-
|
|
306
|
+
if (operator.value === '+') {
|
|
307
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
308
|
+
left = `${left ?? 'null'}${right ?? 'null'}`;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
left = Number(left) + Number(right);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
206
314
|
if (operator.value === '-')
|
|
207
315
|
left = Number(left) - Number(right);
|
|
208
316
|
}
|
|
@@ -215,14 +323,16 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
215
323
|
return left;
|
|
216
324
|
cursor += 1;
|
|
217
325
|
const right = parseAdd();
|
|
326
|
+
const leftValue = typeof left === 'string' && typeof right === 'string' ? left : Number(left);
|
|
327
|
+
const rightValue = typeof left === 'string' && typeof right === 'string' ? right : Number(right);
|
|
218
328
|
if (operator.value === '<')
|
|
219
|
-
left =
|
|
329
|
+
left = leftValue < rightValue;
|
|
220
330
|
if (operator.value === '<=')
|
|
221
|
-
left =
|
|
331
|
+
left = leftValue <= rightValue;
|
|
222
332
|
if (operator.value === '>')
|
|
223
|
-
left =
|
|
333
|
+
left = leftValue > rightValue;
|
|
224
334
|
if (operator.value === '>=')
|
|
225
|
-
left =
|
|
335
|
+
left = leftValue >= rightValue;
|
|
226
336
|
}
|
|
227
337
|
};
|
|
228
338
|
const parseEquality = () => {
|
|
@@ -241,7 +351,10 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
241
351
|
};
|
|
242
352
|
const parseAnd = () => {
|
|
243
353
|
let left = parseEquality();
|
|
244
|
-
while (
|
|
354
|
+
while (true) {
|
|
355
|
+
const operator = tokens[cursor];
|
|
356
|
+
if (!operator || operator.type !== 'operator' || operator.value !== '&&')
|
|
357
|
+
break;
|
|
245
358
|
cursor += 1;
|
|
246
359
|
left = Boolean(left) && Boolean(parseEquality());
|
|
247
360
|
}
|
|
@@ -249,7 +362,10 @@ export function evaluateDebugExpression(expression, variables) {
|
|
|
249
362
|
};
|
|
250
363
|
const parseOr = () => {
|
|
251
364
|
let left = parseAnd();
|
|
252
|
-
while (
|
|
365
|
+
while (true) {
|
|
366
|
+
const operator = tokens[cursor];
|
|
367
|
+
if (!operator || operator.type !== 'operator' || operator.value !== '||')
|
|
368
|
+
break;
|
|
253
369
|
cursor += 1;
|
|
254
370
|
left = Boolean(left) || Boolean(parseAnd());
|
|
255
371
|
}
|
package/dist/debug/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { evaluateDebugExpression } from './expression';
|
|
2
2
|
export { selectInlineDebugLocals } from './locals';
|
|
3
|
-
export { cppDebugLanguageAdapter } from './language';
|
|
3
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter } from './language';
|
|
4
4
|
export { MonacoDebugView } from './editor';
|
|
5
5
|
export type { DebugLanguageAdapter } from './language';
|
package/dist/debug/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { evaluateDebugExpression } from './expression';
|
|
2
2
|
export { selectInlineDebugLocals } from './locals';
|
|
3
|
-
export { cppDebugLanguageAdapter } from './language';
|
|
3
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter } from './language';
|
|
4
4
|
export { MonacoDebugView } from './editor';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { evaluateDebugExpression } from '../expression';
|
|
2
|
+
const selectInlinePythonLocals = (lineText, locals) => {
|
|
3
|
+
const sanitized = lineText
|
|
4
|
+
.replace(/#.*$/g, ' ')
|
|
5
|
+
.replace(/"(?:\\.|[^"\\])*"/g, ' ')
|
|
6
|
+
.replace(/'(?:\\.|[^'\\])*'/g, ' ');
|
|
7
|
+
const identifiers = new Set(sanitized.match(/[A-Za-z_]\w*/g) || []);
|
|
8
|
+
return locals.filter((variable) => identifiers.has(variable.name));
|
|
9
|
+
};
|
|
10
|
+
export const pythonDebugLanguageAdapter = {
|
|
11
|
+
id: 'python',
|
|
12
|
+
evaluateExpression: evaluateDebugExpression,
|
|
13
|
+
selectInlineLocals: selectInlinePythonLocals
|
|
14
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import Terminal from './terminal';
|
|
2
|
-
export { cppDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
2
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
3
3
|
import playground from './playground';
|
|
4
4
|
export default Terminal;
|
|
5
5
|
export { playground };
|
|
6
6
|
export type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, SandboxExecutionOptions, TerminalControl } from './terminal';
|
|
7
|
+
export type { CompilerDiagnostic } from './playground/options';
|
|
8
|
+
export type { PlaygroundRuntimeAssets, RuntimeAssetConfig, RuntimeAssetLoadRequest, RuntimeAssetLoader, RuntimeAssetLoaderResult } from './playground/assets';
|
|
7
9
|
export type { DebugLanguageAdapter } from './debug';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Terminal from './terminal';
|
|
2
|
-
export { cppDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
2
|
+
export { cppDebugLanguageAdapter, pythonDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
3
3
|
import playground from './playground';
|
|
4
4
|
export default Terminal;
|
|
5
5
|
export { playground };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ResolvedRuntimeAssetConfig, type RuntimeAssetRuntime } from './assets';
|
|
2
|
+
type ProgressLike = {
|
|
3
|
+
set?: (value: number) => void;
|
|
4
|
+
};
|
|
5
|
+
export declare class WorkerAssetBridge {
|
|
6
|
+
private worker;
|
|
7
|
+
private readonly runtime;
|
|
8
|
+
private config;
|
|
9
|
+
private readonly progress;
|
|
10
|
+
constructor(worker: Worker, runtime: RuntimeAssetRuntime, config: ResolvedRuntimeAssetConfig, progress?: ProgressLike);
|
|
11
|
+
matches(config: ResolvedRuntimeAssetConfig): boolean;
|
|
12
|
+
rebind(worker: Worker, config: ResolvedRuntimeAssetConfig, progress?: ProgressLike): void;
|
|
13
|
+
resetProgress(progress?: ProgressLike): void;
|
|
14
|
+
handleMessage(event: MessageEvent<any>): boolean;
|
|
15
|
+
private respond;
|
|
16
|
+
private loadAsset;
|
|
17
|
+
private normalizeLoaderResult;
|
|
18
|
+
private fetchAsset;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { JAVA_RUNTIME_LOAD_ASSETS, PYTHON_RUNTIME_LOAD_ASSETS } from './assets';
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
const transferBuffer = (bytes) => bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength
|
|
4
|
+
? bytes.buffer
|
|
5
|
+
: bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
6
|
+
const expectedAssetsForRuntime = (runtime) => new Set(runtime === 'python' ? [...PYTHON_RUNTIME_LOAD_ASSETS] : [...JAVA_RUNTIME_LOAD_ASSETS]);
|
|
7
|
+
class RuntimeLoadProgress {
|
|
8
|
+
fractions = new Map();
|
|
9
|
+
expectedAssets;
|
|
10
|
+
progress;
|
|
11
|
+
constructor(runtime) {
|
|
12
|
+
this.expectedAssets = expectedAssetsForRuntime(runtime);
|
|
13
|
+
this.reset();
|
|
14
|
+
}
|
|
15
|
+
reset(progress) {
|
|
16
|
+
this.progress = progress;
|
|
17
|
+
this.fractions.clear();
|
|
18
|
+
for (const asset of this.expectedAssets)
|
|
19
|
+
this.fractions.set(asset, 0);
|
|
20
|
+
this.emit();
|
|
21
|
+
}
|
|
22
|
+
update(asset, loaded, total) {
|
|
23
|
+
if (!this.expectedAssets.has(asset))
|
|
24
|
+
return;
|
|
25
|
+
const fraction = total && total > 0 ? Math.min(loaded / total, 1) : loaded > 0 ? 1 : 0;
|
|
26
|
+
this.fractions.set(asset, fraction);
|
|
27
|
+
this.emit();
|
|
28
|
+
}
|
|
29
|
+
emit() {
|
|
30
|
+
if (!this.progress)
|
|
31
|
+
return;
|
|
32
|
+
if (!this.fractions.size)
|
|
33
|
+
return this.progress.set?.(0);
|
|
34
|
+
let total = 0;
|
|
35
|
+
for (const fraction of this.fractions.values())
|
|
36
|
+
total += fraction;
|
|
37
|
+
this.progress.set?.(total / this.fractions.size);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class WorkerAssetBridge {
|
|
41
|
+
worker;
|
|
42
|
+
runtime;
|
|
43
|
+
config;
|
|
44
|
+
progress;
|
|
45
|
+
constructor(worker, runtime, config, progress) {
|
|
46
|
+
this.worker = worker;
|
|
47
|
+
this.runtime = runtime;
|
|
48
|
+
this.config = config;
|
|
49
|
+
this.progress = new RuntimeLoadProgress(runtime);
|
|
50
|
+
this.progress.reset(progress);
|
|
51
|
+
}
|
|
52
|
+
matches(config) {
|
|
53
|
+
return (this.config.baseUrl === config.baseUrl &&
|
|
54
|
+
this.config.loader === config.loader &&
|
|
55
|
+
this.config.useAssetBridge === config.useAssetBridge);
|
|
56
|
+
}
|
|
57
|
+
rebind(worker, config, progress) {
|
|
58
|
+
this.worker = worker;
|
|
59
|
+
this.config = config;
|
|
60
|
+
this.progress.reset(progress);
|
|
61
|
+
}
|
|
62
|
+
resetProgress(progress) {
|
|
63
|
+
this.progress.reset(progress);
|
|
64
|
+
}
|
|
65
|
+
handleMessage(event) {
|
|
66
|
+
const assetRequest = event.data?.assetRequest;
|
|
67
|
+
if (assetRequest) {
|
|
68
|
+
void this.respond(assetRequest);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
const assetProgress = event.data?.assetProgress;
|
|
72
|
+
if (assetProgress) {
|
|
73
|
+
this.progress.update(assetProgress.asset, assetProgress.loaded, assetProgress.total);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
async respond(request) {
|
|
79
|
+
try {
|
|
80
|
+
const loaded = await this.loadAsset(request.asset);
|
|
81
|
+
const buffer = transferBuffer(loaded.bytes);
|
|
82
|
+
this.worker.postMessage({
|
|
83
|
+
assetResponse: {
|
|
84
|
+
id: request.id,
|
|
85
|
+
ok: true,
|
|
86
|
+
bytes: buffer,
|
|
87
|
+
mimeType: loaded.mimeType
|
|
88
|
+
}
|
|
89
|
+
}, [buffer]);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
this.worker.postMessage({
|
|
93
|
+
assetResponse: {
|
|
94
|
+
id: request.id,
|
|
95
|
+
ok: false,
|
|
96
|
+
error: error instanceof Error ? error.message : String(error)
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async loadAsset(asset) {
|
|
102
|
+
const reportProgress = (loaded, total) => this.progress.update(asset, loaded, total);
|
|
103
|
+
if (this.config.loader) {
|
|
104
|
+
const loaded = await this.normalizeLoaderResult(await this.config.loader({
|
|
105
|
+
runtime: this.runtime,
|
|
106
|
+
asset,
|
|
107
|
+
reportProgress
|
|
108
|
+
}), asset);
|
|
109
|
+
if (loaded)
|
|
110
|
+
return loaded;
|
|
111
|
+
}
|
|
112
|
+
return await this.fetchAsset(new URL(asset, this.config.baseUrl).href, asset);
|
|
113
|
+
}
|
|
114
|
+
async normalizeLoaderResult(result, asset) {
|
|
115
|
+
if (!result)
|
|
116
|
+
return null;
|
|
117
|
+
if (typeof result === 'string' || result instanceof URL) {
|
|
118
|
+
return await this.fetchAsset(String(result), asset);
|
|
119
|
+
}
|
|
120
|
+
if (result instanceof ArrayBuffer) {
|
|
121
|
+
const bytes = new Uint8Array(result);
|
|
122
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
123
|
+
return { bytes };
|
|
124
|
+
}
|
|
125
|
+
if (result instanceof Uint8Array) {
|
|
126
|
+
this.progress.update(asset, result.byteLength, result.byteLength);
|
|
127
|
+
return { bytes: result };
|
|
128
|
+
}
|
|
129
|
+
if (result instanceof Blob) {
|
|
130
|
+
const bytes = new Uint8Array(await result.arrayBuffer());
|
|
131
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
132
|
+
return { bytes, mimeType: result.type || undefined };
|
|
133
|
+
}
|
|
134
|
+
if ('url' in result && result.url) {
|
|
135
|
+
return await this.fetchAsset(String(result.url), asset);
|
|
136
|
+
}
|
|
137
|
+
if ('data' in result) {
|
|
138
|
+
if (typeof result.data === 'string') {
|
|
139
|
+
const bytes = encoder.encode(result.data);
|
|
140
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
141
|
+
return { bytes, mimeType: result.mimeType };
|
|
142
|
+
}
|
|
143
|
+
if (result.data instanceof ArrayBuffer) {
|
|
144
|
+
const bytes = new Uint8Array(result.data);
|
|
145
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
146
|
+
return { bytes, mimeType: result.mimeType };
|
|
147
|
+
}
|
|
148
|
+
if (result.data instanceof Uint8Array) {
|
|
149
|
+
this.progress.update(asset, result.data.byteLength, result.data.byteLength);
|
|
150
|
+
return { bytes: result.data, mimeType: result.mimeType };
|
|
151
|
+
}
|
|
152
|
+
const bytes = new Uint8Array(await result.data.arrayBuffer());
|
|
153
|
+
this.progress.update(asset, bytes.byteLength, bytes.byteLength);
|
|
154
|
+
return { bytes, mimeType: result.mimeType || result.data.type || undefined };
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
async fetchAsset(url, asset) {
|
|
159
|
+
const response = await fetch(url);
|
|
160
|
+
if (!response.ok)
|
|
161
|
+
throw new Error(`Failed to load ${asset}: ${response.status}`);
|
|
162
|
+
const contentLength = Number(response.headers.get('content-length') || 0) || undefined;
|
|
163
|
+
const mimeType = response.headers.get('content-type') || undefined;
|
|
164
|
+
if (!response.body) {
|
|
165
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
166
|
+
this.progress.update(asset, bytes.byteLength, contentLength ?? bytes.byteLength);
|
|
167
|
+
return { bytes, mimeType };
|
|
168
|
+
}
|
|
169
|
+
const reader = response.body.getReader();
|
|
170
|
+
let receivedLength = 0;
|
|
171
|
+
const chunks = [];
|
|
172
|
+
while (true) {
|
|
173
|
+
const { done, value } = await reader.read();
|
|
174
|
+
if (done)
|
|
175
|
+
break;
|
|
176
|
+
if (!value)
|
|
177
|
+
continue;
|
|
178
|
+
const chunk = Uint8Array.from(value);
|
|
179
|
+
chunks.push(chunk);
|
|
180
|
+
receivedLength += chunk.byteLength;
|
|
181
|
+
this.progress.update(asset, receivedLength, contentLength);
|
|
182
|
+
}
|
|
183
|
+
const bytes = new Uint8Array(receivedLength);
|
|
184
|
+
let position = 0;
|
|
185
|
+
for (const chunk of chunks) {
|
|
186
|
+
bytes.set(chunk, position);
|
|
187
|
+
position += chunk.byteLength;
|
|
188
|
+
}
|
|
189
|
+
this.progress.update(asset, receivedLength, contentLength ?? receivedLength);
|
|
190
|
+
return { bytes, mimeType };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type RuntimeAssetRuntime = 'python' | 'java';
|
|
2
|
+
export interface RuntimeAssetLoadRequest {
|
|
3
|
+
runtime: RuntimeAssetRuntime;
|
|
4
|
+
asset: string;
|
|
5
|
+
reportProgress: (loaded: number, total?: number) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface RuntimeAssetDataResult {
|
|
8
|
+
data: string | ArrayBuffer | Uint8Array | Blob;
|
|
9
|
+
mimeType?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RuntimeAssetUrlResult {
|
|
12
|
+
url: string | URL;
|
|
13
|
+
}
|
|
14
|
+
export type RuntimeAssetLoaderResult = RuntimeAssetDataResult | RuntimeAssetUrlResult | string | URL | ArrayBuffer | Uint8Array | Blob | null | undefined;
|
|
15
|
+
export type RuntimeAssetLoader = (request: RuntimeAssetLoadRequest) => RuntimeAssetLoaderResult | Promise<RuntimeAssetLoaderResult>;
|
|
16
|
+
export interface RuntimeAssetConfig {
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
loader?: RuntimeAssetLoader;
|
|
19
|
+
}
|
|
20
|
+
export interface PlaygroundRuntimeAssets {
|
|
21
|
+
rootUrl?: string;
|
|
22
|
+
python?: RuntimeAssetConfig;
|
|
23
|
+
java?: RuntimeAssetConfig;
|
|
24
|
+
}
|
|
25
|
+
export interface ResolvedRuntimeAssetConfig {
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
loader?: RuntimeAssetLoader;
|
|
28
|
+
useAssetBridge: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare const PYTHON_RUNTIME_LOAD_ASSETS: readonly ["pyodide.asm.js", "pyodide-lock.json", "pyodide.asm.wasm", "python_stdlib.zip"];
|
|
31
|
+
export declare const JAVA_RUNTIME_LOAD_ASSETS: readonly ["compiler.wasm-runtime.js", "compiler.wasm", "compile-classlib-teavm.bin", "runtime-classlib-teavm.bin"];
|
|
32
|
+
export declare function resolveRuntimeAssetConfig(runtime: RuntimeAssetRuntime, options: string | PlaygroundRuntimeAssets | undefined, currentUrl?: string): ResolvedRuntimeAssetConfig;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { normalizeTeaVmBaseUrl, resolveTeaVmBaseUrl } from './teavmConfig';
|
|
2
|
+
export const PYTHON_RUNTIME_LOAD_ASSETS = [
|
|
3
|
+
'pyodide.asm.js',
|
|
4
|
+
'pyodide-lock.json',
|
|
5
|
+
'pyodide.asm.wasm',
|
|
6
|
+
'python_stdlib.zip'
|
|
7
|
+
];
|
|
8
|
+
export const JAVA_RUNTIME_LOAD_ASSETS = [
|
|
9
|
+
'compiler.wasm-runtime.js',
|
|
10
|
+
'compiler.wasm',
|
|
11
|
+
'compile-classlib-teavm.bin',
|
|
12
|
+
'runtime-classlib-teavm.bin'
|
|
13
|
+
];
|
|
14
|
+
const PYTHON_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/python/';
|
|
15
|
+
const JAVA_VIRTUAL_BASE_URL = 'https://wasm-idle.invalid/java/';
|
|
16
|
+
const normalizeBaseUrl = (baseUrl, currentUrl = '') => {
|
|
17
|
+
const normalized = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
|
|
18
|
+
return currentUrl ? new URL(normalized, currentUrl).href : normalized;
|
|
19
|
+
};
|
|
20
|
+
const normalizeRootUrl = (rootUrl) => rootUrl.endsWith('/') ? rootUrl.slice(0, -1) : rootUrl;
|
|
21
|
+
const resolvePythonBaseUrl = (rootUrl = '', currentUrl = '') => normalizeBaseUrl(`${normalizeRootUrl(rootUrl) || ''}/pyodide/`, currentUrl);
|
|
22
|
+
const resolvePythonRuntimeAssetConfig = (options, currentUrl = '') => {
|
|
23
|
+
if (typeof options === 'string') {
|
|
24
|
+
return {
|
|
25
|
+
baseUrl: resolvePythonBaseUrl(options, currentUrl),
|
|
26
|
+
useAssetBridge: false
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const runtimeConfig = options?.python;
|
|
30
|
+
if (runtimeConfig?.baseUrl) {
|
|
31
|
+
return {
|
|
32
|
+
baseUrl: normalizeBaseUrl(runtimeConfig.baseUrl, currentUrl),
|
|
33
|
+
loader: runtimeConfig.loader,
|
|
34
|
+
useAssetBridge: !!runtimeConfig.loader
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (options?.rootUrl) {
|
|
38
|
+
return {
|
|
39
|
+
baseUrl: resolvePythonBaseUrl(options.rootUrl, currentUrl),
|
|
40
|
+
loader: runtimeConfig?.loader,
|
|
41
|
+
useAssetBridge: !!runtimeConfig?.loader
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (runtimeConfig?.loader) {
|
|
45
|
+
return {
|
|
46
|
+
baseUrl: PYTHON_VIRTUAL_BASE_URL,
|
|
47
|
+
loader: runtimeConfig.loader,
|
|
48
|
+
useAssetBridge: true
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
baseUrl: resolvePythonBaseUrl('', currentUrl),
|
|
53
|
+
useAssetBridge: false
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const resolveJavaBaseUrl = (rootUrl = '', currentUrl = '') => resolveTeaVmBaseUrl(rootUrl, currentUrl);
|
|
57
|
+
const resolveJavaRuntimeAssetConfig = (options, currentUrl = '') => {
|
|
58
|
+
if (typeof options === 'string') {
|
|
59
|
+
return {
|
|
60
|
+
baseUrl: resolveJavaBaseUrl(options, currentUrl),
|
|
61
|
+
useAssetBridge: false
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const runtimeConfig = options?.java;
|
|
65
|
+
if (runtimeConfig?.baseUrl) {
|
|
66
|
+
return {
|
|
67
|
+
baseUrl: currentUrl
|
|
68
|
+
? new URL(normalizeTeaVmBaseUrl(runtimeConfig.baseUrl), currentUrl).href
|
|
69
|
+
: normalizeTeaVmBaseUrl(runtimeConfig.baseUrl),
|
|
70
|
+
loader: runtimeConfig.loader,
|
|
71
|
+
useAssetBridge: !!runtimeConfig.loader
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (options?.rootUrl) {
|
|
75
|
+
return {
|
|
76
|
+
baseUrl: resolveJavaBaseUrl(options.rootUrl, currentUrl),
|
|
77
|
+
loader: runtimeConfig?.loader,
|
|
78
|
+
useAssetBridge: !!runtimeConfig?.loader
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (runtimeConfig?.loader) {
|
|
82
|
+
return {
|
|
83
|
+
baseUrl: JAVA_VIRTUAL_BASE_URL,
|
|
84
|
+
loader: runtimeConfig.loader,
|
|
85
|
+
useAssetBridge: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
baseUrl: resolveJavaBaseUrl('', currentUrl),
|
|
90
|
+
useAssetBridge: false
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
export function resolveRuntimeAssetConfig(runtime, options, currentUrl = '') {
|
|
94
|
+
return runtime === 'python'
|
|
95
|
+
? resolvePythonRuntimeAssetConfig(options, currentUrl)
|
|
96
|
+
: resolveJavaRuntimeAssetConfig(options, currentUrl);
|
|
97
|
+
}
|