wasm-idle 0.4.0 → 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.
@@ -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
+ }
@@ -1,7 +1,9 @@
1
1
  import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
2
+ import type { PlaygroundRuntimeAssets } from './assets';
2
3
  import type { Sandbox } from './sandbox';
3
4
  import type { Writable } from 'svelte/store';
4
5
  declare class Clang implements Sandbox {
6
+ language: 'C' | 'CPP';
5
7
  ts: number;
6
8
  output: any;
7
9
  ondebug?: (event: DebugSessionEvent) => void;
@@ -17,7 +19,10 @@ declare class Clang implements Sandbox {
17
19
  waitingForInput: boolean;
18
20
  pendingEof: boolean;
19
21
  exit: boolean;
20
- load(path: string, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
22
+ constructor(language: 'C' | 'CPP');
23
+ load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions, _progress?: {
24
+ set?: (value: number) => void;
25
+ } | import('svelte/store').Writable<number>): Promise<void>;
21
26
  write(input: string): void;
22
27
  eof(): void;
23
28
  private flushPendingInput;
@@ -1,5 +1,7 @@
1
+ import { resolveSandboxExecutionArgs } from './options';
1
2
  import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
2
3
  class Clang {
4
+ language;
3
5
  ts = Date.now();
4
6
  output = null;
5
7
  ondebug;
@@ -15,12 +17,16 @@ class Clang {
15
17
  waitingForInput = false;
16
18
  pendingEof = false;
17
19
  exit = true;
18
- load(path, code = '', log = true, args = [], _options = {}) {
20
+ constructor(language) {
21
+ this.language = language;
22
+ }
23
+ load(runtimeAssets = '', code = '', log = true, args = [], _options = {}, _progress) {
19
24
  return new Promise(async (resolve) => {
20
25
  this.log = log;
21
26
  this.pendingInput = [];
22
27
  this.waitingForInput = false;
23
28
  this.pendingEof = false;
29
+ const path = typeof runtimeAssets === 'string' ? runtimeAssets : runtimeAssets?.rootUrl || '';
24
30
  if (!this.worker) {
25
31
  this.worker = new (await import('./worker/clang?worker')).default();
26
32
  this.worker.onmessage = () => resolve();
@@ -59,6 +65,7 @@ class Clang {
59
65
  return new Promise(async (resolve, reject) => {
60
66
  if (!this.worker)
61
67
  return reject('Worker not loaded');
68
+ const { compileArgs, programArgs } = resolveSandboxExecutionArgs(this.language, args, options);
62
69
  const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
63
70
  const handler = (event) => {
64
71
  if (!this.worker)
@@ -106,7 +113,11 @@ class Clang {
106
113
  interrupt: this.interruptBuffer,
107
114
  context: {},
108
115
  log,
109
- args,
116
+ language: this.language,
117
+ compileArgs,
118
+ programArgs,
119
+ cppVersion: options.cppVersion,
120
+ cVersion: options.cVersion,
110
121
  debug: !!options.debug,
111
122
  breakpoints: [...(options.breakpoints || [])],
112
123
  pauseOnEntry: !!options.pauseOnEntry
@@ -14,8 +14,10 @@ export default async function load(language) {
14
14
  sandbox = new Python();
15
15
  break;
16
16
  case 'C':
17
+ sandbox = new Clang('C');
18
+ break;
17
19
  case 'CPP':
18
- sandbox = new Clang();
20
+ sandbox = new Clang('CPP');
19
21
  break;
20
22
  case 'JAVA':
21
23
  sandbox = new Java();
@@ -1,4 +1,6 @@
1
- import type { CompilerDiagnostic, SandboxExecutionOptions } from './options';
1
+ import { type CompilerDiagnostic, type SandboxExecutionOptions } from './options';
2
+ import { WorkerAssetBridge } from './assetBridge';
3
+ import { type PlaygroundRuntimeAssets } from './assets';
2
4
  import type { Sandbox } from './sandbox';
3
5
  declare class Java implements Sandbox {
4
6
  output: any;
@@ -14,7 +16,10 @@ declare class Java implements Sandbox {
14
16
  oncompilerdiagnostic?: (diagnostic: CompilerDiagnostic) => void;
15
17
  waitingForInput: boolean;
16
18
  pendingEof: boolean;
17
- load(path: string, _code?: string, _log?: boolean, _args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
19
+ assetBridge: WorkerAssetBridge | null;
20
+ load(runtimeAssets?: string | PlaygroundRuntimeAssets, _code?: string, _log?: boolean, _args?: string[], _options?: SandboxExecutionOptions, progress?: {
21
+ set?: (value: number) => void;
22
+ } | import('svelte/store').Writable<number>): Promise<void>;
18
23
  write(input: string): void;
19
24
  eof(): void;
20
25
  private flushPendingInput;
@@ -1,5 +1,7 @@
1
+ import { resolveSandboxExecutionArgs } from './options';
2
+ import { WorkerAssetBridge } from './assetBridge';
3
+ import { resolveRuntimeAssetConfig } from './assets';
1
4
  import { flushBufferedEof, flushQueuedStdin, resetBufferedStdin } from './stdinBuffer';
2
- import { resolveTeaVmBaseUrl } from './teavmConfig';
3
5
  class Java {
4
6
  output = null;
5
7
  worker = null;
@@ -14,23 +16,43 @@ class Java {
14
16
  oncompilerdiagnostic;
15
17
  waitingForInput = false;
16
18
  pendingEof = false;
17
- load(path, _code = '', _log = true, _args = [], _options = {}) {
19
+ assetBridge = null;
20
+ load(runtimeAssets = '', _code = '', _log = true, _args = [], _options = {}, progress) {
18
21
  return new Promise(async (resolve, reject) => {
19
22
  this.pendingInput = [];
20
23
  this.waitingForInput = false;
21
24
  this.pendingEof = false;
22
- this.baseUrl = resolveTeaVmBaseUrl(path, typeof window !== 'undefined' ? window.location.href : '');
25
+ const assetConfig = resolveRuntimeAssetConfig('java', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
26
+ this.baseUrl = assetConfig.baseUrl;
27
+ const needsWorkerReset = !this.worker || !this.assetBridge || !this.assetBridge.matches(assetConfig);
28
+ if (needsWorkerReset && this.worker) {
29
+ this.worker.terminate();
30
+ delete this.worker;
31
+ this.assetBridge = null;
32
+ }
23
33
  if (!this.worker) {
24
34
  this.worker = new (await import('./worker/java?worker')).default();
35
+ this.assetBridge = new WorkerAssetBridge(this.worker, 'java', assetConfig, progress);
25
36
  this.worker.onmessage = (event) => {
37
+ if (this.assetBridge?.handleMessage(event))
38
+ return;
26
39
  if (event.data?.load)
27
40
  resolve();
28
41
  if (event.data?.error)
29
42
  reject(event.data.error);
30
43
  };
31
- this.worker.postMessage({ load: true, baseUrl: this.baseUrl });
44
+ this.worker.postMessage({
45
+ load: true,
46
+ assets: {
47
+ baseUrl: assetConfig.baseUrl,
48
+ useAssetBridge: assetConfig.useAssetBridge
49
+ }
50
+ });
32
51
  }
33
52
  else {
53
+ if (!this.assetBridge)
54
+ return reject('Worker asset bridge unavailable');
55
+ this.assetBridge.rebind(this.worker, assetConfig, progress);
34
56
  resolve();
35
57
  }
36
58
  });
@@ -62,9 +84,12 @@ class Java {
62
84
  return new Promise((resolve, reject) => {
63
85
  if (!this.worker)
64
86
  return reject('Worker not loaded');
87
+ const { programArgs } = resolveSandboxExecutionArgs('JAVA', args, options);
65
88
  const _uid = ++this.uid;
66
89
  this.activeReject = reject;
67
90
  const handler = (event) => {
91
+ if (this.assetBridge?.handleMessage(event))
92
+ return;
68
93
  if (!this.worker)
69
94
  return reject('Worker not loaded');
70
95
  if (_uid !== this.uid)
@@ -101,7 +126,7 @@ class Java {
101
126
  code,
102
127
  prepare,
103
128
  buffer: this.buffer,
104
- args,
129
+ args: programArgs,
105
130
  stdin: options.stdin || '',
106
131
  baseUrl: this.baseUrl
107
132
  });
@@ -118,6 +143,7 @@ class Java {
118
143
  this.uid += 1;
119
144
  this.worker?.terminate?.();
120
145
  delete this.worker;
146
+ this.assetBridge = null;
121
147
  this.exit = true;
122
148
  }
123
149
  async clear() {
@@ -1,11 +1,238 @@
1
1
  const JAVA_STDIN_HELPER_CLASS = 'WasmIdleStdin';
2
+ const JAVA_SCANNER_COMPAT_SENTINEL = '// wasm-idle Scanner compatibility shim';
3
+ const JAVA_SCANNER_COMPAT_SHIM = `${JAVA_SCANNER_COMPAT_SENTINEL}
4
+ final class Scanner implements AutoCloseable {
5
+ private final java.io.InputStream input;
6
+ private int bufferedChar = Integer.MIN_VALUE;
7
+ private String bufferedToken = null;
8
+ private boolean closed = false;
9
+
10
+ Scanner(java.io.InputStream input) {
11
+ this.input = input;
12
+ }
13
+
14
+ Scanner(String source) {
15
+ this(
16
+ new java.io.ByteArrayInputStream(
17
+ source != null ? source.getBytes() : new byte[0]
18
+ )
19
+ );
20
+ }
21
+
22
+ private void ensureOpen() {
23
+ if (closed) {
24
+ throw new IllegalStateException("Scanner closed");
25
+ }
26
+ }
27
+
28
+ private int readByteInternal() {
29
+ try {
30
+ return input.read();
31
+ } catch (java.io.IOException error) {
32
+ throw new RuntimeException(error);
33
+ }
34
+ }
35
+
36
+ private int readByte() {
37
+ ensureOpen();
38
+ if (bufferedChar != Integer.MIN_VALUE) {
39
+ int value = bufferedChar;
40
+ bufferedChar = Integer.MIN_VALUE;
41
+ return value;
42
+ }
43
+ return readByteInternal();
44
+ }
45
+
46
+ private int peekByte() {
47
+ ensureOpen();
48
+ if (bufferedChar == Integer.MIN_VALUE) {
49
+ bufferedChar = readByteInternal();
50
+ }
51
+ return bufferedChar;
52
+ }
53
+
54
+ private boolean isWhitespace(int value) {
55
+ return value == ' ' || value == '\\n' || value == '\\r' || value == '\\t' || value == '\\f';
56
+ }
57
+
58
+ private boolean skipWhitespace() {
59
+ int value = peekByte();
60
+ while (value != -1 && isWhitespace(value)) {
61
+ readByte();
62
+ value = peekByte();
63
+ }
64
+ return value != -1;
65
+ }
66
+
67
+ private String readTokenValue() {
68
+ java.lang.StringBuilder token = new java.lang.StringBuilder();
69
+ int value = peekByte();
70
+ while (value != -1 && !isWhitespace(value)) {
71
+ token.append((char) readByte());
72
+ value = peekByte();
73
+ }
74
+ return token.toString();
75
+ }
76
+
77
+ public boolean hasNext() {
78
+ ensureOpen();
79
+ if (bufferedToken != null) {
80
+ return true;
81
+ }
82
+ if (!skipWhitespace()) {
83
+ return false;
84
+ }
85
+ bufferedToken = readTokenValue();
86
+ return true;
87
+ }
88
+
89
+ public String next() {
90
+ ensureOpen();
91
+ if (bufferedToken != null) {
92
+ String token = bufferedToken;
93
+ bufferedToken = null;
94
+ return token;
95
+ }
96
+ if (!skipWhitespace()) {
97
+ throw new RuntimeException("No more tokens");
98
+ }
99
+ return readTokenValue();
100
+ }
101
+
102
+ public String nextLine() {
103
+ ensureOpen();
104
+ java.lang.StringBuilder line = new java.lang.StringBuilder();
105
+ if (bufferedToken != null) {
106
+ line.append(bufferedToken);
107
+ bufferedToken = null;
108
+ }
109
+ int value = peekByte();
110
+ if (line.length() == 0 && value == -1) {
111
+ return "";
112
+ }
113
+ if (line.length() == 0 && value == '\\r') {
114
+ readByte();
115
+ if (peekByte() == '\\n') {
116
+ readByte();
117
+ }
118
+ return "";
119
+ }
120
+ if (line.length() == 0 && value == '\\n') {
121
+ readByte();
122
+ return "";
123
+ }
124
+ while (true) {
125
+ value = readByte();
126
+ if (value == -1 || value == '\\n' || value == '\\r') {
127
+ break;
128
+ }
129
+ line.append((char) value);
130
+ }
131
+ if (value == '\\r' && peekByte() == '\\n') {
132
+ readByte();
133
+ }
134
+ return line.toString();
135
+ }
136
+
137
+ public int nextInt() {
138
+ return Integer.parseInt(next());
139
+ }
140
+
141
+ public long nextLong() {
142
+ return Long.parseLong(next());
143
+ }
144
+
145
+ public float nextFloat() {
146
+ return Float.parseFloat(next());
147
+ }
148
+
149
+ public double nextDouble() {
150
+ return Double.parseDouble(next());
151
+ }
152
+
153
+ public boolean hasNextInt() {
154
+ if (!hasNext()) {
155
+ return false;
156
+ }
157
+ try {
158
+ Integer.parseInt(bufferedToken);
159
+ return true;
160
+ } catch (RuntimeException error) {
161
+ return false;
162
+ }
163
+ }
164
+
165
+ public boolean hasNextLong() {
166
+ if (!hasNext()) {
167
+ return false;
168
+ }
169
+ try {
170
+ Long.parseLong(bufferedToken);
171
+ return true;
172
+ } catch (RuntimeException error) {
173
+ return false;
174
+ }
175
+ }
176
+
177
+ public boolean hasNextFloat() {
178
+ if (!hasNext()) {
179
+ return false;
180
+ }
181
+ try {
182
+ Float.parseFloat(bufferedToken);
183
+ return true;
184
+ } catch (RuntimeException error) {
185
+ return false;
186
+ }
187
+ }
188
+
189
+ public boolean hasNextDouble() {
190
+ if (!hasNext()) {
191
+ return false;
192
+ }
193
+ try {
194
+ Double.parseDouble(bufferedToken);
195
+ return true;
196
+ } catch (RuntimeException error) {
197
+ return false;
198
+ }
199
+ }
200
+
201
+ public void close() {
202
+ if (closed) {
203
+ return;
204
+ }
205
+ closed = true;
206
+ try {
207
+ input.close();
208
+ } catch (java.io.IOException error) {
209
+ throw new RuntimeException(error);
210
+ }
211
+ }
212
+ }`;
2
213
  export const prepareJavaStdinInjection = (code, stdin) => {
3
214
  const usesStdin = code.includes('System.in');
215
+ const usesScanner = /\bScanner\b/.test(code) &&
216
+ !code.includes(JAVA_SCANNER_COMPAT_SENTINEL) &&
217
+ !/\b(?:class|interface|enum|record)\s+Scanner\b/.test(code) &&
218
+ !/^[ \t]*import[ \t]+(?!java\.util\.Scanner\b)(?!static\b)[\w.]+\.Scanner[ \t]*;[ \t]*$/m.test(code);
4
219
  if (!usesStdin) {
220
+ if (!usesScanner) {
221
+ return {
222
+ usesStdin: false,
223
+ stdinCacheKey: '',
224
+ transformedCode: code,
225
+ helperSourcePath: null,
226
+ helperSource: null
227
+ };
228
+ }
229
+ const transformedCode = code
230
+ .replace(/^[ \t]*import[ \t]+java\.util\.Scanner[ \t]*;[ \t]*$/gm, (match) => match.replace(/[^\r\n]/g, ' '))
231
+ .replaceAll(/\bjava\.util\.Scanner\b/g, 'Scanner');
5
232
  return {
6
233
  usesStdin: false,
7
234
  stdinCacheKey: '',
8
- transformedCode: code,
235
+ transformedCode: `${transformedCode.trimEnd()}\n\n${JAVA_SCANNER_COMPAT_SHIM}\n`,
9
236
  helperSourcePath: null,
10
237
  helperSource: null
11
238
  };
@@ -15,7 +242,13 @@ export const prepareJavaStdinInjection = (code, stdin) => {
15
242
  const helperSourcePath = packageName
16
243
  ? `${packageName.replaceAll('.', '/')}/${JAVA_STDIN_HELPER_CLASS}.java`
17
244
  : `${JAVA_STDIN_HELPER_CLASS}.java`;
18
- const transformedCode = code.replaceAll('System.in', `${JAVA_STDIN_HELPER_CLASS}.open()`);
245
+ let transformedCode = code.replaceAll('System.in', `${JAVA_STDIN_HELPER_CLASS}.open()`);
246
+ if (usesScanner) {
247
+ transformedCode = transformedCode
248
+ .replace(/^[ \t]*import[ \t]+java\.util\.Scanner[ \t]*;[ \t]*$/gm, (match) => match.replace(/[^\r\n]/g, ' '))
249
+ .replaceAll(/\bjava\.util\.Scanner\b/g, 'Scanner');
250
+ transformedCode = `${transformedCode.trimEnd()}\n\n${JAVA_SCANNER_COMPAT_SHIM}\n`;
251
+ }
19
252
  const encodedBytes = [...new TextEncoder().encode(stdin)].join(', ');
20
253
  return {
21
254
  usesStdin: true,
@@ -46,4 +46,13 @@ export interface SandboxExecutionOptions {
46
46
  breakpoints?: number[];
47
47
  pauseOnEntry?: boolean;
48
48
  stdin?: string;
49
+ compileArgs?: string[];
50
+ programArgs?: string[];
51
+ cppVersion?: string;
52
+ cVersion?: string;
49
53
  }
54
+ export interface ResolvedSandboxExecutionArgs {
55
+ compileArgs: string[];
56
+ programArgs: string[];
57
+ }
58
+ export declare function resolveSandboxExecutionArgs(language: string, args?: string[], options?: SandboxExecutionOptions): ResolvedSandboxExecutionArgs;
@@ -1 +1,15 @@
1
- export {};
1
+ function cloneArgs(value) {
2
+ return Array.isArray(value) ? [...value] : [];
3
+ }
4
+ export function resolveSandboxExecutionArgs(language, args = [], options = {}) {
5
+ if (language === 'C' || language === 'CPP') {
6
+ return {
7
+ compileArgs: cloneArgs(options.compileArgs ?? args),
8
+ programArgs: cloneArgs(options.programArgs)
9
+ };
10
+ }
11
+ return {
12
+ compileArgs: cloneArgs(options.compileArgs),
13
+ programArgs: cloneArgs(options.programArgs ?? args)
14
+ };
15
+ }
@@ -1,4 +1,6 @@
1
1
  import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
2
+ import { WorkerAssetBridge } from './assetBridge';
3
+ import { type PlaygroundRuntimeAssets } from './assets';
2
4
  import type { Sandbox } from './sandbox';
3
5
  declare class Python implements Sandbox {
4
6
  ts: number;
@@ -22,7 +24,10 @@ declare class Python implements Sandbox {
22
24
  waitingForInput: boolean;
23
25
  pendingEof: boolean;
24
26
  exit: boolean;
25
- load(path: string, code?: string, log?: boolean, _args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
27
+ assetBridge: WorkerAssetBridge | null;
28
+ load(runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, _args?: string[], _options?: SandboxExecutionOptions, progress?: {
29
+ set?: (value: number) => void;
30
+ } | import('svelte/store').Writable<number>): Promise<void>;
26
31
  write(input: string): void;
27
32
  eof(): void;
28
33
  private flushPendingInput;
@@ -1,3 +1,5 @@
1
+ import { WorkerAssetBridge } from './assetBridge';
2
+ import { resolveRuntimeAssetConfig } from './assets';
1
3
  import { bufferedSequence, flushBufferedEof, flushQueuedStdin, resetBufferedStdin, waitForBufferedSequenceChange } from './stdinBuffer';
2
4
  class Python {
3
5
  ts = Date.now();
@@ -17,17 +19,44 @@ class Python {
17
19
  waitingForInput = false;
18
20
  pendingEof = false;
19
21
  exit = true;
20
- load(path, code = '', log = true, _args = [], _options = {}) {
21
- return new Promise(async (resolve) => {
22
+ assetBridge = null;
23
+ load(runtimeAssets = '', code = '', log = true, _args = [], _options = {}, progress) {
24
+ return new Promise(async (resolve, reject) => {
22
25
  this.pendingInput = [];
23
26
  this.waitingForInput = false;
24
27
  this.pendingEof = false;
28
+ const assetConfig = resolveRuntimeAssetConfig('python', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
29
+ const needsWorkerReset = !this.worker || !this.assetBridge || !this.assetBridge.matches(assetConfig);
30
+ if (needsWorkerReset && this.worker) {
31
+ this.worker.terminate();
32
+ delete this.worker;
33
+ this.assetBridge = null;
34
+ }
25
35
  if (!this.worker) {
26
36
  this.worker = new (await import('./worker/python?worker')).default();
27
- this.worker.onmessage = () => resolve();
28
- this.worker.postMessage({ load: true, path, log, code });
37
+ this.assetBridge = new WorkerAssetBridge(this.worker, 'python', assetConfig, progress);
38
+ this.worker.onmessage = (event) => {
39
+ if (this.assetBridge?.handleMessage(event))
40
+ return;
41
+ if (event.data?.load)
42
+ resolve();
43
+ if (event.data?.error)
44
+ reject(event.data.error);
45
+ };
46
+ this.worker.postMessage({
47
+ load: true,
48
+ log,
49
+ code,
50
+ assets: {
51
+ baseUrl: assetConfig.baseUrl,
52
+ useAssetBridge: assetConfig.useAssetBridge
53
+ }
54
+ });
29
55
  }
30
56
  else {
57
+ if (!this.assetBridge)
58
+ return reject('Worker asset bridge unavailable');
59
+ this.assetBridge.rebind(this.worker, assetConfig, progress);
31
60
  this.worker.postMessage({ log });
32
61
  resolve();
33
62
  }
@@ -62,6 +91,8 @@ class Python {
62
91
  return reject('Worker not loaded');
63
92
  const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
64
93
  const handler = (event) => {
94
+ if (this.assetBridge?.handleMessage(event))
95
+ return;
65
96
  if (!this.worker)
66
97
  return reject('Worker not loaded');
67
98
  if (_uid !== this.uid)
@@ -131,7 +162,8 @@ class Python {
131
162
  Atomics.store(control, 1, 5);
132
163
  Atomics.add(control, 0, 1);
133
164
  Atomics.notify(control, 0);
134
- return (await waitForBufferedSequenceChange(this.watchResultBuffer, previousSequence, 5000)) ?? '?';
165
+ return ((await waitForBufferedSequenceChange(this.watchResultBuffer, previousSequence, 5000)) ??
166
+ '?');
135
167
  }
136
168
  kill() {
137
169
  this.terminate();
@@ -159,6 +191,7 @@ class Python {
159
191
  if (!this.exit) {
160
192
  this.worker?.terminate?.();
161
193
  delete this.worker;
194
+ this.assetBridge = null;
162
195
  this.exit = true;
163
196
  }
164
197
  }