wasm-idle 1.0.7 → 1.0.9
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 +48 -11
- package/dist/playground/applicationAssets.js +18 -0
- package/dist/playground/assetBridge.js +7 -0
- package/dist/playground/assets.d.ts +70 -0
- package/dist/playground/assets.js +120 -0
- package/dist/playground/cachedClang.js +43 -5
- package/dist/playground/clang.d.ts +5 -0
- package/dist/playground/clang.js +96 -0
- package/dist/playground/clangAssetIntegrity.d.ts +6 -0
- package/dist/playground/clangAssetIntegrity.js +3 -2
- package/dist/playground/clangWorkerProtocol.d.ts +12 -1
- package/dist/playground/fennel.d.ts +9 -0
- package/dist/playground/fennel.js +30 -0
- package/dist/playground/fennelBootstrap.d.ts +31 -0
- package/dist/playground/fennelBootstrap.js +118 -0
- package/dist/playground/grain.d.ts +9 -0
- package/dist/playground/grain.js +127 -0
- package/dist/playground/hy.d.ts +21 -0
- package/dist/playground/hy.js +24 -0
- package/dist/playground/index.d.ts +1 -1
- package/dist/playground/index.js +49 -0
- package/dist/playground/lua.d.ts +19 -0
- package/dist/playground/lua.js +64 -39
- package/dist/playground/objectivec.d.ts +3 -1
- package/dist/playground/objectivec.js +6 -2
- package/dist/playground/options.js +1 -1
- package/dist/playground/postgresql.d.ts +36 -0
- package/dist/playground/postgresql.js +543 -0
- package/dist/playground/python.d.ts +5 -0
- package/dist/playground/python.js +13 -0
- package/dist/playground/rescript.d.ts +5 -0
- package/dist/playground/rescript.js +58 -0
- package/dist/playground/rescriptPreflight.d.ts +26 -0
- package/dist/playground/rescriptPreflight.js +381 -0
- package/dist/playground/staticRuntimeModuleVersion.d.ts +1 -1
- package/dist/playground/staticRuntimeModuleVersion.js +1 -1
- package/dist/playground/staticRuntimePreflightExecute.d.ts +2 -1
- package/dist/playground/staticRuntimePreflightExecute.js +9 -0
- package/dist/playground/staticRuntimePreflightProtocol.d.ts +1 -1
- package/dist/playground/v.d.ts +46 -0
- package/dist/playground/v.js +703 -0
- package/dist/playground/wasmFennelVersion.d.ts +23 -0
- package/dist/playground/wasmFennelVersion.js +16 -0
- package/dist/playground/wasmGrainVersion.d.ts +24 -0
- package/dist/playground/wasmGrainVersion.js +25 -0
- package/dist/playground/wasmHyVersion.d.ts +7 -0
- package/dist/playground/wasmHyVersion.js +15 -0
- package/dist/playground/wasmReScriptVersion.d.ts +20 -0
- package/dist/playground/wasmReScriptVersion.js +20 -0
- package/dist/playground/worker/cachedClangCompile.js +37 -4
- package/dist/playground/worker/clang.js +35 -7
- package/dist/playground/worker/lua.js +48 -11
- package/dist/playground/worker/postgresql.d.ts +1 -0
- package/dist/playground/worker/postgresql.js +205 -0
- package/dist/playground/worker/python.js +106 -9
- package/dist/playground/worker/staticRuntimePreflight.js +2 -1
- package/dist/playground/worker/v.d.ts +1 -0
- package/dist/playground/worker/v.js +106 -0
- package/package.json +18 -7
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
import { resolvePostgresqlRuntimeModuleUrl } from './assets';
|
|
2
|
+
import { WorkerSession } from './workerSession';
|
|
3
|
+
import { reportWorkerProgress } from './workerProgress';
|
|
4
|
+
import { BusyError, CancelledError, DEFAULT_WORKSPACE_LIMITS, DiagnosticLimitError, OutputLimitError, RuntimeConfigurationError, TimeoutError, resolveExecutionLimits, validateExecutionWorkspace } from '@wasm-idle/core';
|
|
5
|
+
const OUTPUT_ENCODER = new TextEncoder();
|
|
6
|
+
const abortReason = (signal, phase) => signal.reason !== undefined
|
|
7
|
+
? signal.reason
|
|
8
|
+
: new DOMException(phase === 'startup'
|
|
9
|
+
? 'PostgreSQL runtime startup aborted'
|
|
10
|
+
: 'PostgreSQL execution aborted', 'AbortError');
|
|
11
|
+
class Postgresql {
|
|
12
|
+
output = null;
|
|
13
|
+
worker = null;
|
|
14
|
+
begin = 0;
|
|
15
|
+
elapse = 0;
|
|
16
|
+
uid = 0;
|
|
17
|
+
exit = true;
|
|
18
|
+
moduleUrl = '';
|
|
19
|
+
oncompilerdiagnostic;
|
|
20
|
+
activeOperation = null;
|
|
21
|
+
disposed = false;
|
|
22
|
+
disposePromise = null;
|
|
23
|
+
disposeCancellation = new CancelledError('PostgreSQL sandbox disposed', {
|
|
24
|
+
phase: 'dispose',
|
|
25
|
+
runtimeId: 'POSTGRESQL',
|
|
26
|
+
recoverable: false
|
|
27
|
+
});
|
|
28
|
+
workerSession = new WorkerSession({
|
|
29
|
+
label: 'PostgreSQL',
|
|
30
|
+
onDispose: (worker) => {
|
|
31
|
+
if (this.worker !== worker)
|
|
32
|
+
return;
|
|
33
|
+
delete this.worker;
|
|
34
|
+
this.exit = true;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
beginOperation(phase) {
|
|
38
|
+
if (this.disposed) {
|
|
39
|
+
throw new RuntimeConfigurationError('PostgreSQL sandbox is disposed', {
|
|
40
|
+
phase: 'dispose',
|
|
41
|
+
runtimeId: 'POSTGRESQL'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (this.activeOperation) {
|
|
45
|
+
throw new BusyError('PostgreSQL runtime already has an active operation', {
|
|
46
|
+
runtimeId: 'POSTGRESQL',
|
|
47
|
+
phase: this.activeOperation.phase
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const operation = {
|
|
51
|
+
token: Symbol(phase),
|
|
52
|
+
phase,
|
|
53
|
+
cancelled: false,
|
|
54
|
+
cleanedUp: false,
|
|
55
|
+
cleanups: []
|
|
56
|
+
};
|
|
57
|
+
this.activeOperation = operation;
|
|
58
|
+
return operation;
|
|
59
|
+
}
|
|
60
|
+
isOperationActive(operation) {
|
|
61
|
+
return this.activeOperation?.token === operation.token && !operation.cancelled;
|
|
62
|
+
}
|
|
63
|
+
releaseOperation(operation) {
|
|
64
|
+
if (this.activeOperation?.token !== operation.token)
|
|
65
|
+
return false;
|
|
66
|
+
this.activeOperation = null;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
cleanupOperation(operation) {
|
|
70
|
+
if (operation.cleanedUp)
|
|
71
|
+
return;
|
|
72
|
+
operation.cleanedUp = true;
|
|
73
|
+
const cleanups = operation.cleanups.splice(0);
|
|
74
|
+
for (const cleanup of cleanups) {
|
|
75
|
+
try {
|
|
76
|
+
cleanup();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Caller-owned lifecycle cleanup must not replace the operation result.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
releaseBeforeSession(operation, reason) {
|
|
84
|
+
const outcome = operation.cancelled ? operation.cancellationReason : reason;
|
|
85
|
+
this.releaseOperation(operation);
|
|
86
|
+
this.cleanupOperation(operation);
|
|
87
|
+
return outcome;
|
|
88
|
+
}
|
|
89
|
+
bindPreSessionAbort(operation, signal) {
|
|
90
|
+
if (!signal || !this.isOperationActive(operation))
|
|
91
|
+
return () => undefined;
|
|
92
|
+
let registered = false;
|
|
93
|
+
let unbound = false;
|
|
94
|
+
const unbind = () => {
|
|
95
|
+
if (unbound)
|
|
96
|
+
return;
|
|
97
|
+
unbound = true;
|
|
98
|
+
if (registered)
|
|
99
|
+
signal.removeEventListener('abort', onAbort);
|
|
100
|
+
};
|
|
101
|
+
const onAbort = () => {
|
|
102
|
+
if (!this.isOperationActive(operation))
|
|
103
|
+
return;
|
|
104
|
+
let reason;
|
|
105
|
+
try {
|
|
106
|
+
reason = abortReason(signal, operation.phase);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
reason = error;
|
|
110
|
+
}
|
|
111
|
+
operation.cancelled = true;
|
|
112
|
+
operation.cancellationReason = reason;
|
|
113
|
+
this.releaseOperation(operation);
|
|
114
|
+
this.cleanupOperation(operation);
|
|
115
|
+
};
|
|
116
|
+
operation.cleanups.push(unbind);
|
|
117
|
+
try {
|
|
118
|
+
registered = true;
|
|
119
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
120
|
+
if (signal.aborted)
|
|
121
|
+
onAbort();
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (this.isOperationActive(operation)) {
|
|
125
|
+
operation.cancelled = true;
|
|
126
|
+
operation.cancellationReason = error;
|
|
127
|
+
this.releaseOperation(operation);
|
|
128
|
+
this.cleanupOperation(operation);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return unbind;
|
|
132
|
+
}
|
|
133
|
+
bindAbortSignal(operation, signal) {
|
|
134
|
+
if (!signal || !this.isOperationActive(operation))
|
|
135
|
+
return;
|
|
136
|
+
let registered = false;
|
|
137
|
+
const onAbort = () => {
|
|
138
|
+
if (!this.isOperationActive(operation))
|
|
139
|
+
return;
|
|
140
|
+
let reason;
|
|
141
|
+
try {
|
|
142
|
+
reason = abortReason(signal, operation.phase);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
reason = error;
|
|
146
|
+
}
|
|
147
|
+
this.cancelOperation(operation, reason);
|
|
148
|
+
};
|
|
149
|
+
operation.cleanups.push(() => {
|
|
150
|
+
if (registered)
|
|
151
|
+
signal.removeEventListener('abort', onAbort);
|
|
152
|
+
});
|
|
153
|
+
try {
|
|
154
|
+
registered = true;
|
|
155
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
156
|
+
if (signal.aborted)
|
|
157
|
+
onAbort();
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
this.cancelOperation(operation, error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
bindOperationTimeout(operation, timeoutMs) {
|
|
164
|
+
if (!this.isOperationActive(operation))
|
|
165
|
+
return;
|
|
166
|
+
let timeout;
|
|
167
|
+
operation.cleanups.push(() => {
|
|
168
|
+
if (timeout !== undefined)
|
|
169
|
+
clearTimeout(timeout);
|
|
170
|
+
});
|
|
171
|
+
try {
|
|
172
|
+
timeout = setTimeout(() => {
|
|
173
|
+
if (!this.isOperationActive(operation))
|
|
174
|
+
return;
|
|
175
|
+
const label = operation.phase === 'startup' ? 'startup' : 'execution';
|
|
176
|
+
this.cancelOperation(operation, new TimeoutError(`PostgreSQL ${label} timed out after ${timeoutMs} ms`, {
|
|
177
|
+
phase: operation.phase,
|
|
178
|
+
runtimeId: 'POSTGRESQL',
|
|
179
|
+
timeoutMs
|
|
180
|
+
}));
|
|
181
|
+
}, timeoutMs);
|
|
182
|
+
if (operation.cleanedUp)
|
|
183
|
+
clearTimeout(timeout);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
this.cancelOperation(operation, error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
cancelOperation(operation, reason) {
|
|
190
|
+
if (!this.isOperationActive(operation))
|
|
191
|
+
return;
|
|
192
|
+
operation.cancelled = true;
|
|
193
|
+
operation.cancellationReason = reason;
|
|
194
|
+
this.activeOperation = null;
|
|
195
|
+
this.uid += 1;
|
|
196
|
+
this.exit = true;
|
|
197
|
+
this.workerSession.terminate(reason);
|
|
198
|
+
this.cleanupOperation(operation);
|
|
199
|
+
}
|
|
200
|
+
load(runtimeAssets = '', _code = '', _log = true, _args = [], options = {}, progress) {
|
|
201
|
+
let activeOperation;
|
|
202
|
+
try {
|
|
203
|
+
activeOperation = this.beginOperation('startup');
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
return Promise.reject(error);
|
|
207
|
+
}
|
|
208
|
+
let limits;
|
|
209
|
+
let signal;
|
|
210
|
+
let unbindPreSessionAbort = () => undefined;
|
|
211
|
+
try {
|
|
212
|
+
signal = options.signal;
|
|
213
|
+
unbindPreSessionAbort = this.bindPreSessionAbort(activeOperation, signal);
|
|
214
|
+
if (!this.isOperationActive(activeOperation)) {
|
|
215
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, 'PostgreSQL runtime startup cancelled'));
|
|
216
|
+
}
|
|
217
|
+
limits = resolveExecutionLimits(options.limits);
|
|
218
|
+
if (!this.isOperationActive(activeOperation) || signal?.aborted) {
|
|
219
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, 'PostgreSQL runtime startup cancelled'));
|
|
220
|
+
}
|
|
221
|
+
unbindPreSessionAbort();
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, error));
|
|
225
|
+
}
|
|
226
|
+
if (!this.isOperationActive(activeOperation)) {
|
|
227
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, 'PostgreSQL runtime startup cancelled'));
|
|
228
|
+
}
|
|
229
|
+
const loading = this.workerSession.load(async (resolve, reject) => {
|
|
230
|
+
const resolveOperation = () => {
|
|
231
|
+
if (!this.releaseOperation(activeOperation))
|
|
232
|
+
return;
|
|
233
|
+
resolve();
|
|
234
|
+
this.cleanupOperation(activeOperation);
|
|
235
|
+
};
|
|
236
|
+
const rejectOperation = (reason) => {
|
|
237
|
+
if (!this.releaseOperation(activeOperation))
|
|
238
|
+
return;
|
|
239
|
+
reject(reason);
|
|
240
|
+
this.cleanupOperation(activeOperation);
|
|
241
|
+
};
|
|
242
|
+
try {
|
|
243
|
+
if (!this.isOperationActive(activeOperation))
|
|
244
|
+
return;
|
|
245
|
+
const currentUrl = typeof window !== 'undefined' ? window.location.href : '';
|
|
246
|
+
const nextModuleUrl = resolvePostgresqlRuntimeModuleUrl(runtimeAssets, currentUrl);
|
|
247
|
+
if (!this.isOperationActive(activeOperation))
|
|
248
|
+
return;
|
|
249
|
+
if (this.worker && this.moduleUrl !== nextModuleUrl)
|
|
250
|
+
this.workerSession.reset();
|
|
251
|
+
this.moduleUrl = nextModuleUrl;
|
|
252
|
+
if (!this.isOperationActive(activeOperation))
|
|
253
|
+
return;
|
|
254
|
+
if (!this.worker) {
|
|
255
|
+
const WorkerConstructor = (await import('./worker/postgresql?worker')).default;
|
|
256
|
+
if (!this.isOperationActive(activeOperation))
|
|
257
|
+
return;
|
|
258
|
+
const worker = new WorkerConstructor();
|
|
259
|
+
if (!this.isOperationActive(activeOperation)) {
|
|
260
|
+
try {
|
|
261
|
+
worker.terminate();
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// The unattached worker is already detached.
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
this.worker = worker;
|
|
269
|
+
this.workerSession.attach(worker);
|
|
270
|
+
let handler;
|
|
271
|
+
handler = (event) => {
|
|
272
|
+
if (!this.isOperationActive(activeOperation) ||
|
|
273
|
+
this.worker !== worker ||
|
|
274
|
+
worker.onmessage !== handler) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
if (event.data?.load) {
|
|
279
|
+
progress?.set?.(1);
|
|
280
|
+
if (!this.isOperationActive(activeOperation) ||
|
|
281
|
+
this.worker !== worker ||
|
|
282
|
+
worker.onmessage !== handler) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
resolveOperation();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (event.data?.error !== undefined) {
|
|
289
|
+
rejectOperation(event.data.error);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
rejectOperation(error);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
worker.onmessage = handler;
|
|
297
|
+
worker.postMessage({
|
|
298
|
+
load: true,
|
|
299
|
+
moduleUrl: this.moduleUrl,
|
|
300
|
+
log: _log
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
const worker = this.worker;
|
|
305
|
+
progress?.set?.(1);
|
|
306
|
+
if (!this.isOperationActive(activeOperation) || this.worker !== worker)
|
|
307
|
+
return;
|
|
308
|
+
resolveOperation();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
rejectOperation(error);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
const timeoutMs = Math.min(2_147_483_647, limits.assetTimeoutMs + limits.startupTimeoutMs);
|
|
316
|
+
this.bindOperationTimeout(activeOperation, timeoutMs);
|
|
317
|
+
this.bindAbortSignal(activeOperation, signal);
|
|
318
|
+
return loading.finally(() => {
|
|
319
|
+
this.releaseOperation(activeOperation);
|
|
320
|
+
this.cleanupOperation(activeOperation);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
write() { }
|
|
324
|
+
eof() { }
|
|
325
|
+
run(code, prepare, _log = true, _prog, _args = [], options = {}) {
|
|
326
|
+
let activeOperation;
|
|
327
|
+
try {
|
|
328
|
+
activeOperation = this.beginOperation('execute');
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
return Promise.reject(error);
|
|
332
|
+
}
|
|
333
|
+
let limits;
|
|
334
|
+
let signal;
|
|
335
|
+
let unbindPreSessionAbort = () => undefined;
|
|
336
|
+
let workspace;
|
|
337
|
+
let stdin;
|
|
338
|
+
try {
|
|
339
|
+
signal = options.signal;
|
|
340
|
+
unbindPreSessionAbort = this.bindPreSessionAbort(activeOperation, signal);
|
|
341
|
+
if (!this.isOperationActive(activeOperation)) {
|
|
342
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, 'PostgreSQL execution cancelled'));
|
|
343
|
+
}
|
|
344
|
+
limits = resolveExecutionLimits(options.limits);
|
|
345
|
+
workspace = validateExecutionWorkspace(code, options.workspaceFiles ?? [], options.activePath ?? 'main.sql', {
|
|
346
|
+
...options.workspaceLimits,
|
|
347
|
+
maxFileBytes: Math.min(options.workspaceLimits?.maxFileBytes ??
|
|
348
|
+
DEFAULT_WORKSPACE_LIMITS.maxFileBytes, limits.maxWorkspaceBytes),
|
|
349
|
+
maxTotalBytes: Math.min(options.workspaceLimits?.maxTotalBytes ??
|
|
350
|
+
DEFAULT_WORKSPACE_LIMITS.maxTotalBytes, limits.maxWorkspaceBytes)
|
|
351
|
+
});
|
|
352
|
+
stdin = options.stdin;
|
|
353
|
+
if (!this.isOperationActive(activeOperation) || signal?.aborted) {
|
|
354
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, 'PostgreSQL execution cancelled'));
|
|
355
|
+
}
|
|
356
|
+
unbindPreSessionAbort();
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, error));
|
|
360
|
+
}
|
|
361
|
+
if (!this.isOperationActive(activeOperation)) {
|
|
362
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, 'PostgreSQL execution cancelled'));
|
|
363
|
+
}
|
|
364
|
+
const worker = this.worker;
|
|
365
|
+
if (!worker) {
|
|
366
|
+
return Promise.reject(this.releaseBeforeSession(activeOperation, 'Worker not loaded'));
|
|
367
|
+
}
|
|
368
|
+
this.exit = false;
|
|
369
|
+
const running = new Promise((resolve, reject) => {
|
|
370
|
+
const runUid = ++this.uid;
|
|
371
|
+
let diagnosticCount = 0;
|
|
372
|
+
let outputBytes = 0;
|
|
373
|
+
const workerOperation = this.workerSession.beginRun(worker, reject);
|
|
374
|
+
const timeoutMs = Math.min(2_147_483_647, limits.compileTimeoutMs + limits.runTimeoutMs);
|
|
375
|
+
this.bindOperationTimeout(activeOperation, timeoutMs);
|
|
376
|
+
this.bindAbortSignal(activeOperation, signal);
|
|
377
|
+
if (!this.isOperationActive(activeOperation))
|
|
378
|
+
return;
|
|
379
|
+
let handler;
|
|
380
|
+
const ownsRun = () => this.isOperationActive(activeOperation) &&
|
|
381
|
+
this.worker === worker &&
|
|
382
|
+
worker.onmessage === handler &&
|
|
383
|
+
runUid === this.uid;
|
|
384
|
+
const claimRun = () => {
|
|
385
|
+
if (!ownsRun())
|
|
386
|
+
return false;
|
|
387
|
+
this.elapse = Date.now() - this.begin;
|
|
388
|
+
this.exit = true;
|
|
389
|
+
this.workerSession.complete(workerOperation);
|
|
390
|
+
this.releaseOperation(activeOperation);
|
|
391
|
+
this.cleanupOperation(activeOperation);
|
|
392
|
+
try {
|
|
393
|
+
if (worker.onmessage === handler)
|
|
394
|
+
worker.onmessage = null;
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
// Handler cleanup must not replace the execution result.
|
|
398
|
+
}
|
|
399
|
+
return true;
|
|
400
|
+
};
|
|
401
|
+
const failRun = (reason, disposeWorker = false) => {
|
|
402
|
+
if (disposeWorker) {
|
|
403
|
+
if (!ownsRun())
|
|
404
|
+
return;
|
|
405
|
+
this.cancelOperation(activeOperation, reason);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (!claimRun())
|
|
409
|
+
return;
|
|
410
|
+
reject(reason);
|
|
411
|
+
};
|
|
412
|
+
handler = (event) => {
|
|
413
|
+
if (this.worker !== worker ||
|
|
414
|
+
worker.onmessage !== handler ||
|
|
415
|
+
!this.isOperationActive(activeOperation)) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
const { output, results, error, diagnostic, progress } = event.data;
|
|
420
|
+
reportWorkerProgress(_prog, progress);
|
|
421
|
+
if (!ownsRun())
|
|
422
|
+
return;
|
|
423
|
+
if (typeof output === 'string' && output.length > 0) {
|
|
424
|
+
const actual = outputBytes + OUTPUT_ENCODER.encode(output).byteLength;
|
|
425
|
+
if (actual > limits.maxOutputBytes) {
|
|
426
|
+
failRun(new OutputLimitError(`PostgreSQL output exceeded ${limits.maxOutputBytes} bytes`, {
|
|
427
|
+
actual,
|
|
428
|
+
limit: limits.maxOutputBytes,
|
|
429
|
+
phase: 'execute',
|
|
430
|
+
runtimeId: 'POSTGRESQL'
|
|
431
|
+
}), true);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
outputBytes = actual;
|
|
435
|
+
this.output?.(output);
|
|
436
|
+
if (!ownsRun())
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (diagnostic !== undefined) {
|
|
440
|
+
const actual = diagnosticCount + 1;
|
|
441
|
+
if (actual > limits.maxDiagnostics) {
|
|
442
|
+
failRun(new DiagnosticLimitError(`PostgreSQL diagnostics exceeded ${limits.maxDiagnostics} messages`, {
|
|
443
|
+
actual,
|
|
444
|
+
limit: limits.maxDiagnostics,
|
|
445
|
+
phase: 'execute',
|
|
446
|
+
runtimeId: 'POSTGRESQL'
|
|
447
|
+
}), true);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
diagnosticCount = actual;
|
|
451
|
+
this.oncompilerdiagnostic?.(diagnostic);
|
|
452
|
+
if (!ownsRun())
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (results !== undefined) {
|
|
456
|
+
if (!claimRun())
|
|
457
|
+
return;
|
|
458
|
+
resolve(results);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (error !== undefined)
|
|
462
|
+
failRun(error);
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
failRun(error, true);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
worker.onmessage = handler;
|
|
469
|
+
this.begin = Date.now();
|
|
470
|
+
try {
|
|
471
|
+
worker.postMessage({
|
|
472
|
+
code,
|
|
473
|
+
prepare,
|
|
474
|
+
activePath: workspace.activePath ?? 'main.sql',
|
|
475
|
+
workspaceFiles: workspace.workspaceFiles,
|
|
476
|
+
stdin,
|
|
477
|
+
log: _log
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
481
|
+
failRun(error);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
return running.finally(() => {
|
|
485
|
+
if (this.releaseOperation(activeOperation))
|
|
486
|
+
this.exit = true;
|
|
487
|
+
this.cleanupOperation(activeOperation);
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
kill() {
|
|
491
|
+
this.terminate();
|
|
492
|
+
}
|
|
493
|
+
terminate(reason = 'Process terminated') {
|
|
494
|
+
if (this.disposed)
|
|
495
|
+
return;
|
|
496
|
+
const activeOperation = this.activeOperation;
|
|
497
|
+
if (activeOperation) {
|
|
498
|
+
this.cancelOperation(activeOperation, reason);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
this.uid += 1;
|
|
502
|
+
this.workerSession.terminate(reason);
|
|
503
|
+
this.exit = true;
|
|
504
|
+
}
|
|
505
|
+
async clear() {
|
|
506
|
+
if (this.disposed)
|
|
507
|
+
return;
|
|
508
|
+
if (this.activeOperation) {
|
|
509
|
+
this.terminate();
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const worker = this.worker;
|
|
513
|
+
if (worker) {
|
|
514
|
+
try {
|
|
515
|
+
worker.onmessage = null;
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
// Idle handler cleanup is best effort.
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
dispose() {
|
|
523
|
+
if (this.disposePromise)
|
|
524
|
+
return this.disposePromise;
|
|
525
|
+
this.disposed = true;
|
|
526
|
+
this.disposePromise = Promise.resolve();
|
|
527
|
+
const activeOperation = this.activeOperation;
|
|
528
|
+
delete this.worker;
|
|
529
|
+
this.moduleUrl = '';
|
|
530
|
+
this.output = null;
|
|
531
|
+
this.oncompilerdiagnostic = undefined;
|
|
532
|
+
if (activeOperation) {
|
|
533
|
+
this.cancelOperation(activeOperation, this.disposeCancellation);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
this.uid += 1;
|
|
537
|
+
this.workerSession.terminate(this.disposeCancellation);
|
|
538
|
+
this.exit = true;
|
|
539
|
+
}
|
|
540
|
+
return this.disposePromise;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
export default Postgresql;
|
|
@@ -31,6 +31,11 @@ declare class Python implements Sandbox {
|
|
|
31
31
|
private ownsRuntimeAssetCache;
|
|
32
32
|
private disposed;
|
|
33
33
|
private disposal?;
|
|
34
|
+
private workerExtensionKey?;
|
|
35
|
+
/** Language executed by the shared Pyodide worker; subclasses run other Python-hosted languages. */
|
|
36
|
+
protected readonly workerLanguage: 'python' | 'hy';
|
|
37
|
+
/** Extra worker load configuration for Python-hosted languages, keyed for worker reuse. */
|
|
38
|
+
protected workerLoadExtension(_runtimeAssets: string | PlaygroundRuntimeAssets, _currentUrl: string): Record<string, unknown> | undefined;
|
|
34
39
|
private readonly workerSession;
|
|
35
40
|
private beginOperation;
|
|
36
41
|
private isOperationActive;
|
|
@@ -34,6 +34,13 @@ class Python {
|
|
|
34
34
|
ownsRuntimeAssetCache = true;
|
|
35
35
|
disposed = false;
|
|
36
36
|
disposal;
|
|
37
|
+
workerExtensionKey;
|
|
38
|
+
/** Language executed by the shared Pyodide worker; subclasses run other Python-hosted languages. */
|
|
39
|
+
workerLanguage = 'python';
|
|
40
|
+
/** Extra worker load configuration for Python-hosted languages, keyed for worker reuse. */
|
|
41
|
+
workerLoadExtension(_runtimeAssets, _currentUrl) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
37
44
|
workerSession = new WorkerSession({
|
|
38
45
|
label: 'Python',
|
|
39
46
|
onDispose: (worker) => {
|
|
@@ -206,10 +213,13 @@ class Python {
|
|
|
206
213
|
this.waitingForInput = false;
|
|
207
214
|
this.pendingEof = false;
|
|
208
215
|
const assetConfig = resolveRuntimeAssetConfig('python', runtimeAssets, typeof window !== 'undefined' ? window.location.href : '', options.persistentCache);
|
|
216
|
+
const extension = this.workerLoadExtension(runtimeAssets, typeof window !== 'undefined' ? window.location.href : '');
|
|
217
|
+
const extensionKey = JSON.stringify(extension ?? null);
|
|
209
218
|
if (!this.isOperationActive(operation))
|
|
210
219
|
return;
|
|
211
220
|
const needsWorkerReset = !this.worker ||
|
|
212
221
|
!this.assetBridge ||
|
|
222
|
+
this.workerExtensionKey !== extensionKey ||
|
|
213
223
|
!this.assetBridge.matches(assetConfig, limits.maxAssetBytes);
|
|
214
224
|
if (needsWorkerReset && this.worker)
|
|
215
225
|
this.workerSession.reset();
|
|
@@ -226,6 +236,7 @@ class Python {
|
|
|
226
236
|
return;
|
|
227
237
|
}
|
|
228
238
|
this.worker = worker;
|
|
239
|
+
this.workerExtensionKey = extensionKey;
|
|
229
240
|
this.workerSession.attach(worker);
|
|
230
241
|
const assetBridge = new WorkerAssetBridge(worker, 'python', assetConfig, progress, limits.maxAssetBytes, false, this.runtimeAssetCache);
|
|
231
242
|
if (!this.isOperationActive(operation) || this.worker !== worker) {
|
|
@@ -263,6 +274,7 @@ class Python {
|
|
|
263
274
|
load: true,
|
|
264
275
|
log,
|
|
265
276
|
code,
|
|
277
|
+
...(extension ? { extension } : {}),
|
|
266
278
|
assets: {
|
|
267
279
|
baseUrl: assetConfig.baseUrl,
|
|
268
280
|
maxAssetBytes: limits.maxAssetBytes,
|
|
@@ -464,6 +476,7 @@ class Python {
|
|
|
464
476
|
worker.postMessage({
|
|
465
477
|
code,
|
|
466
478
|
prepare,
|
|
479
|
+
language: this.workerLanguage,
|
|
467
480
|
buffer: this.buffer,
|
|
468
481
|
debugBuffer: this.debugBuffer,
|
|
469
482
|
watchBuffer: this.watchBuffer,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { resolveReScriptRuntimeAssetConfig } from './assets';
|
|
2
|
+
import { preflightStaticRuntimeAssetsInWorker } from './staticRuntimePreflight';
|
|
3
|
+
import { StaticWorkerRuntimeSandbox } from './staticWorkerRuntime';
|
|
4
|
+
import { RuntimeConfigurationError } from '@wasm-idle/core';
|
|
5
|
+
class ReScript extends StaticWorkerRuntimeSandbox {
|
|
6
|
+
constructor() {
|
|
7
|
+
super({
|
|
8
|
+
languageId: 'RESCRIPT',
|
|
9
|
+
displayName: 'ReScript',
|
|
10
|
+
defaultActivePath: 'Main.res',
|
|
11
|
+
stdin: {
|
|
12
|
+
mode: 'streaming',
|
|
13
|
+
sourceHintPattern: /\b(?:readLineSync|readFileSync)\b|\/dev\/stdin\b/
|
|
14
|
+
},
|
|
15
|
+
inlineVerifiedWorker: true,
|
|
16
|
+
runtimePreflightDelivery: 'transfer-owned',
|
|
17
|
+
resolveRuntimeAssets(runtimeAssets, currentUrl) {
|
|
18
|
+
const resolved = resolveReScriptRuntimeAssetConfig(runtimeAssets, currentUrl);
|
|
19
|
+
const profile = resolved.preflightProfile;
|
|
20
|
+
if (!/^[a-f0-9]{64}$/u.test(resolved.manifestFingerprint || '') ||
|
|
21
|
+
profile.manifestFingerprint !== resolved.manifestFingerprint ||
|
|
22
|
+
!resolved.workerReceipt) {
|
|
23
|
+
throw new RuntimeConfigurationError('ReScript runtime requires a manifest fingerprint and worker receipt.', { runtimeId: 'RESCRIPT' });
|
|
24
|
+
}
|
|
25
|
+
return resolved;
|
|
26
|
+
},
|
|
27
|
+
async preflightRuntimeAssets(urls, context) {
|
|
28
|
+
const profile = urls.preflightProfile;
|
|
29
|
+
const loadedByAsset = new Map();
|
|
30
|
+
const totalDownloadBytes = (profile.manifestReceipt?.bytes ?? 0) + (profile.compilerReceipt?.bytes ?? 0);
|
|
31
|
+
const payload = await preflightStaticRuntimeAssetsInWorker({
|
|
32
|
+
runtimeId: 'RESCRIPT',
|
|
33
|
+
displayName: 'ReScript',
|
|
34
|
+
baseUrl: urls.baseUrl,
|
|
35
|
+
manifestUrl: urls.manifestUrl || '',
|
|
36
|
+
profile,
|
|
37
|
+
limits: context.limits,
|
|
38
|
+
persistentCache: context.persistentCache,
|
|
39
|
+
signal: context.signal,
|
|
40
|
+
reportProgress(progress) {
|
|
41
|
+
if (progress.kind === 'asset') {
|
|
42
|
+
loadedByAsset.set(progress.progress.assetKey, progress.progress.loadedBytes);
|
|
43
|
+
const loadedBytes = [...loadedByAsset.values()].reduce((total, loaded) => total + loaded, 0);
|
|
44
|
+
const fraction = totalDownloadBytes > 0 ? loadedBytes / totalDownloadBytes : 0;
|
|
45
|
+
context.reportProgress(0.04 + Math.min(1, fraction) * 0.11, `Preflighting ReScript asset ${progress.progress.assetKey}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const { loadedBytes, totalBytes } = progress;
|
|
49
|
+
const fraction = totalBytes > 0 ? loadedBytes / totalBytes : 0;
|
|
50
|
+
context.reportProgress(0.15 + Math.min(1, fraction) * 0.02, 'Decompressing verified ReScript compiler');
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return context.createOwnedDelivery(payload);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export default ReScript;
|