@ricsam/isolate-daemon 0.1.13 → 0.1.14
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/dist/cjs/connection.cjs +453 -750
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/connection.mjs +455 -753
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/types.d.ts +11 -19
- package/package.json +1 -1
package/dist/mjs/connection.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// packages/isolate-daemon/src/connection.ts
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import ivm from "isolated-vm";
|
|
5
3
|
import {
|
|
6
4
|
createFrameParser,
|
|
7
5
|
buildFrame,
|
|
@@ -9,29 +7,12 @@ import {
|
|
|
9
7
|
ErrorCode,
|
|
10
8
|
STREAM_CHUNK_SIZE,
|
|
11
9
|
STREAM_DEFAULT_CREDIT,
|
|
12
|
-
|
|
13
|
-
marshalValue,
|
|
14
|
-
unmarshalValue
|
|
10
|
+
marshalValue
|
|
15
11
|
} from "@ricsam/isolate-protocol";
|
|
16
12
|
import { createCallbackFileSystemHandler } from "./callback-fs-handler.mjs";
|
|
17
13
|
import {
|
|
18
|
-
|
|
19
|
-
runTests as runTestsInContext,
|
|
20
|
-
hasTests as hasTestsInContext,
|
|
21
|
-
getTestCount as getTestCountInContext
|
|
22
|
-
} from "@ricsam/isolate-test-environment";
|
|
23
|
-
import {
|
|
24
|
-
setupPlaywright
|
|
25
|
-
} from "@ricsam/isolate-playwright";
|
|
26
|
-
import {
|
|
27
|
-
createInternalRuntime
|
|
14
|
+
createRuntime
|
|
28
15
|
} from "@ricsam/isolate-runtime";
|
|
29
|
-
import {
|
|
30
|
-
transformEntryCode,
|
|
31
|
-
transformModuleCode,
|
|
32
|
-
contentHash,
|
|
33
|
-
mapErrorStack
|
|
34
|
-
} from "@ricsam/isolate-transform";
|
|
35
16
|
function handleConnection(socket, state) {
|
|
36
17
|
const connection = {
|
|
37
18
|
socket,
|
|
@@ -66,12 +47,7 @@ function handleConnection(socket, state) {
|
|
|
66
47
|
if (instance.namespaceId != null && !instance.isDisposed) {
|
|
67
48
|
softDeleteRuntime(instance, state);
|
|
68
49
|
} else if (!instance.isDisposed) {
|
|
69
|
-
|
|
70
|
-
if (instance.playwrightHandle) {
|
|
71
|
-
instance.playwrightHandle.dispose();
|
|
72
|
-
}
|
|
73
|
-
instance.runtime.dispose();
|
|
74
|
-
} catch {}
|
|
50
|
+
instance.runtime.dispose().catch(() => {});
|
|
75
51
|
state.isolates.delete(isolateId);
|
|
76
52
|
}
|
|
77
53
|
}
|
|
@@ -177,12 +153,6 @@ async function handleMessage(message, connection, state) {
|
|
|
177
153
|
case MessageType.GET_TEST_COUNT:
|
|
178
154
|
await handleGetTestCount(message, connection, state);
|
|
179
155
|
break;
|
|
180
|
-
case MessageType.RUN_PLAYWRIGHT_TESTS:
|
|
181
|
-
await handleRunPlaywrightTests(message, connection, state);
|
|
182
|
-
break;
|
|
183
|
-
case MessageType.RESET_PLAYWRIGHT_TESTS:
|
|
184
|
-
await handleResetPlaywrightTests(message, connection, state);
|
|
185
|
-
break;
|
|
186
156
|
case MessageType.GET_COLLECTED_DATA:
|
|
187
157
|
await handleGetCollectedData(message, connection, state);
|
|
188
158
|
break;
|
|
@@ -213,6 +183,18 @@ async function handleMessage(message, connection, state) {
|
|
|
213
183
|
case MessageType.CALLBACK_STREAM_END:
|
|
214
184
|
handleCallbackStreamEnd(message, connection);
|
|
215
185
|
break;
|
|
186
|
+
case MessageType.CLIENT_WS_OPENED:
|
|
187
|
+
handleClientWsOpened(message, connection, state);
|
|
188
|
+
break;
|
|
189
|
+
case MessageType.CLIENT_WS_MESSAGE:
|
|
190
|
+
handleClientWsMessage(message, connection, state);
|
|
191
|
+
break;
|
|
192
|
+
case MessageType.CLIENT_WS_CLOSED:
|
|
193
|
+
handleClientWsClosed(message, connection, state);
|
|
194
|
+
break;
|
|
195
|
+
case MessageType.CLIENT_WS_ERROR:
|
|
196
|
+
handleClientWsError(message, connection, state);
|
|
197
|
+
break;
|
|
216
198
|
default:
|
|
217
199
|
sendError(connection.socket, message.requestId ?? 0, ErrorCode.UNKNOWN_MESSAGE_TYPE, `Unknown message type: ${message.type}`);
|
|
218
200
|
}
|
|
@@ -221,13 +203,17 @@ function softDeleteRuntime(instance, state) {
|
|
|
221
203
|
instance.isDisposed = true;
|
|
222
204
|
instance.disposedAt = Date.now();
|
|
223
205
|
instance.ownerConnection = null;
|
|
206
|
+
if (instance.callbackContext) {
|
|
207
|
+
instance.callbackContext.connection = null;
|
|
208
|
+
}
|
|
224
209
|
instance.callbacks.clear();
|
|
225
210
|
instance.runtime.timers.clearAll();
|
|
226
211
|
instance.runtime.console.reset();
|
|
227
|
-
instance.pendingCallbacks.length = 0;
|
|
212
|
+
instance.runtime.pendingCallbacks.length = 0;
|
|
228
213
|
instance.returnedCallbacks?.clear();
|
|
229
214
|
instance.returnedPromises?.clear();
|
|
230
215
|
instance.returnedIterators?.clear();
|
|
216
|
+
instance.runtime.clearModuleCache();
|
|
231
217
|
}
|
|
232
218
|
function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
233
219
|
instance.ownerConnection = connection.socket;
|
|
@@ -236,11 +222,19 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
236
222
|
instance.lastActivity = Date.now();
|
|
237
223
|
connection.isolates.add(instance.isolateId);
|
|
238
224
|
const callbacks = message.options.callbacks;
|
|
225
|
+
const testEnvOptions = message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment : undefined;
|
|
239
226
|
if (instance.callbackContext) {
|
|
240
227
|
instance.callbackContext.connection = connection;
|
|
241
228
|
instance.callbackContext.consoleOnEntry = callbacks?.console?.onEntry?.callbackId;
|
|
242
229
|
instance.callbackContext.fetch = callbacks?.fetch?.callbackId;
|
|
243
230
|
instance.callbackContext.moduleLoader = callbacks?.moduleLoader?.callbackId;
|
|
231
|
+
instance.callbackContext.testEnvironmentOnEvent = testEnvOptions?.callbacks?.onEvent?.callbackId;
|
|
232
|
+
instance.callbackContext.playwright = {
|
|
233
|
+
handlerCallbackId: callbacks?.playwright?.handlerCallbackId,
|
|
234
|
+
onBrowserConsoleLogCallbackId: callbacks?.playwright?.onBrowserConsoleLogCallbackId,
|
|
235
|
+
onNetworkRequestCallbackId: callbacks?.playwright?.onNetworkRequestCallbackId,
|
|
236
|
+
onNetworkResponseCallbackId: callbacks?.playwright?.onNetworkResponseCallbackId
|
|
237
|
+
};
|
|
244
238
|
instance.callbackContext.fs = {
|
|
245
239
|
readFile: callbacks?.fs?.readFile?.callbackId,
|
|
246
240
|
writeFile: callbacks?.fs?.writeFile?.callbackId,
|
|
@@ -277,7 +271,6 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
277
271
|
}
|
|
278
272
|
}
|
|
279
273
|
if (callbacks?.moduleLoader) {
|
|
280
|
-
instance.moduleLoaderCallbackId = callbacks.moduleLoader.callbackId;
|
|
281
274
|
instance.callbacks.set(callbacks.moduleLoader.callbackId, callbacks.moduleLoader);
|
|
282
275
|
}
|
|
283
276
|
if (callbacks?.custom) {
|
|
@@ -287,23 +280,46 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
287
280
|
}
|
|
288
281
|
}
|
|
289
282
|
}
|
|
283
|
+
if (testEnvOptions?.callbacks?.onEvent) {
|
|
284
|
+
instance.callbacks.set(testEnvOptions.callbacks.onEvent.callbackId, {
|
|
285
|
+
...testEnvOptions.callbacks.onEvent,
|
|
286
|
+
name: "testEnvironment.onEvent"
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (callbacks?.playwright) {
|
|
290
|
+
instance.callbacks.set(callbacks.playwright.handlerCallbackId, {
|
|
291
|
+
callbackId: callbacks.playwright.handlerCallbackId,
|
|
292
|
+
name: "playwright.handler",
|
|
293
|
+
type: "async"
|
|
294
|
+
});
|
|
295
|
+
if (callbacks.playwright.onBrowserConsoleLogCallbackId !== undefined) {
|
|
296
|
+
instance.callbacks.set(callbacks.playwright.onBrowserConsoleLogCallbackId, {
|
|
297
|
+
callbackId: callbacks.playwright.onBrowserConsoleLogCallbackId,
|
|
298
|
+
name: "playwright.onBrowserConsoleLog",
|
|
299
|
+
type: "sync"
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (callbacks.playwright.onNetworkRequestCallbackId !== undefined) {
|
|
303
|
+
instance.callbacks.set(callbacks.playwright.onNetworkRequestCallbackId, {
|
|
304
|
+
callbackId: callbacks.playwright.onNetworkRequestCallbackId,
|
|
305
|
+
name: "playwright.onNetworkRequest",
|
|
306
|
+
type: "sync"
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
if (callbacks.playwright.onNetworkResponseCallbackId !== undefined) {
|
|
310
|
+
instance.callbacks.set(callbacks.playwright.onNetworkResponseCallbackId, {
|
|
311
|
+
callbackId: callbacks.playwright.onNetworkResponseCallbackId,
|
|
312
|
+
name: "playwright.onNetworkResponse",
|
|
313
|
+
type: "sync"
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
290
317
|
instance.returnedCallbacks = new Map;
|
|
291
318
|
instance.returnedPromises = new Map;
|
|
292
319
|
instance.returnedIterators = new Map;
|
|
293
320
|
instance.nextLocalCallbackId = 1e6;
|
|
294
|
-
if (callbacks?.custom) {
|
|
295
|
-
const newCallbackIdMap = {};
|
|
296
|
-
for (const [name, reg] of Object.entries(callbacks.custom)) {
|
|
297
|
-
if (reg) {
|
|
298
|
-
newCallbackIdMap[name] = reg.callbackId;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
try {
|
|
302
|
-
instance.runtime.context.global.setSync("__customFnCallbackIds", new ivm.ExternalCopy(newCallbackIdMap).copyInto());
|
|
303
|
-
} catch {}
|
|
304
|
-
}
|
|
305
321
|
}
|
|
306
|
-
function evictOldestDisposedRuntime(state) {
|
|
322
|
+
async function evictOldestDisposedRuntime(state) {
|
|
307
323
|
let oldest = null;
|
|
308
324
|
let oldestTime = Infinity;
|
|
309
325
|
for (const [, instance] of state.isolates) {
|
|
@@ -316,10 +332,7 @@ function evictOldestDisposedRuntime(state) {
|
|
|
316
332
|
}
|
|
317
333
|
if (oldest) {
|
|
318
334
|
try {
|
|
319
|
-
|
|
320
|
-
oldest.playwrightHandle.dispose();
|
|
321
|
-
}
|
|
322
|
-
oldest.runtime.dispose();
|
|
335
|
+
await oldest.runtime.dispose();
|
|
323
336
|
} catch {}
|
|
324
337
|
state.isolates.delete(oldest.isolateId);
|
|
325
338
|
if (oldest.namespaceId != null) {
|
|
@@ -354,7 +367,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
354
367
|
}
|
|
355
368
|
}
|
|
356
369
|
if (state.isolates.size >= state.options.maxIsolates) {
|
|
357
|
-
if (!evictOldestDisposedRuntime(state)) {
|
|
370
|
+
if (!await evictOldestDisposedRuntime(state)) {
|
|
358
371
|
sendError(connection.socket, message.requestId, ErrorCode.ISOLATE_MEMORY_LIMIT, `Maximum isolates (${state.options.maxIsolates}) reached`);
|
|
359
372
|
return;
|
|
360
373
|
}
|
|
@@ -366,12 +379,18 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
366
379
|
const fsCallbacks = message.options.callbacks?.fs;
|
|
367
380
|
const moduleLoaderCallback = message.options.callbacks?.moduleLoader;
|
|
368
381
|
const customCallbacks = message.options.callbacks?.custom;
|
|
369
|
-
const pendingCallbacks = [];
|
|
370
382
|
const callbackContext = {
|
|
371
383
|
connection,
|
|
372
384
|
consoleOnEntry: consoleCallbacks?.onEntry?.callbackId,
|
|
373
385
|
fetch: fetchCallback?.callbackId,
|
|
374
386
|
moduleLoader: moduleLoaderCallback?.callbackId,
|
|
387
|
+
testEnvironmentOnEvent: message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment.callbacks?.onEvent?.callbackId : undefined,
|
|
388
|
+
playwright: {
|
|
389
|
+
handlerCallbackId: message.options.callbacks?.playwright?.handlerCallbackId,
|
|
390
|
+
onBrowserConsoleLogCallbackId: message.options.callbacks?.playwright?.onBrowserConsoleLogCallbackId,
|
|
391
|
+
onNetworkRequestCallbackId: message.options.callbacks?.playwright?.onNetworkRequestCallbackId,
|
|
392
|
+
onNetworkResponseCallbackId: message.options.callbacks?.playwright?.onNetworkResponseCallbackId
|
|
393
|
+
},
|
|
375
394
|
fs: {
|
|
376
395
|
readFile: fsCallbacks?.readFile?.callbackId,
|
|
377
396
|
writeFile: fsCallbacks?.writeFile?.callbackId,
|
|
@@ -383,7 +402,248 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
383
402
|
},
|
|
384
403
|
custom: new Map(customCallbacks ? Object.entries(customCallbacks).map(([name, reg]) => [name, reg.callbackId]) : [])
|
|
385
404
|
};
|
|
386
|
-
const
|
|
405
|
+
const instance = {
|
|
406
|
+
isolateId,
|
|
407
|
+
runtime: null,
|
|
408
|
+
ownerConnection: connection.socket,
|
|
409
|
+
callbacks: new Map,
|
|
410
|
+
createdAt: Date.now(),
|
|
411
|
+
lastActivity: Date.now(),
|
|
412
|
+
returnedCallbacks: new Map,
|
|
413
|
+
returnedPromises: new Map,
|
|
414
|
+
returnedIterators: new Map,
|
|
415
|
+
nextLocalCallbackId: 1e6,
|
|
416
|
+
namespaceId,
|
|
417
|
+
isDisposed: false,
|
|
418
|
+
callbackContext
|
|
419
|
+
};
|
|
420
|
+
let bridgedCustomFunctions;
|
|
421
|
+
let customFnMarshalOptions;
|
|
422
|
+
if (customCallbacks) {
|
|
423
|
+
const createMarshalContext = () => ({
|
|
424
|
+
registerCallback: (fn) => {
|
|
425
|
+
const callbackId = instance.nextLocalCallbackId++;
|
|
426
|
+
instance.returnedCallbacks.set(callbackId, fn);
|
|
427
|
+
return callbackId;
|
|
428
|
+
},
|
|
429
|
+
registerPromise: (promise) => {
|
|
430
|
+
const promiseId = instance.nextLocalCallbackId++;
|
|
431
|
+
instance.returnedPromises.set(promiseId, promise);
|
|
432
|
+
return promiseId;
|
|
433
|
+
},
|
|
434
|
+
registerIterator: (iterator) => {
|
|
435
|
+
const iteratorId = instance.nextLocalCallbackId++;
|
|
436
|
+
instance.returnedIterators.set(iteratorId, iterator);
|
|
437
|
+
return iteratorId;
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
const isPromiseRef = (value) => typeof value === "object" && value !== null && value.__type === "PromiseRef";
|
|
441
|
+
const isAsyncIteratorRef = (value) => typeof value === "object" && value !== null && value.__type === "AsyncIteratorRef";
|
|
442
|
+
const addCallbackIdsToRefs = (value) => {
|
|
443
|
+
if (value === null || typeof value !== "object")
|
|
444
|
+
return value;
|
|
445
|
+
if (isPromiseRef(value)) {
|
|
446
|
+
if ("__resolveCallbackId" in value)
|
|
447
|
+
return value;
|
|
448
|
+
const resolveCallbackId = instance.nextLocalCallbackId++;
|
|
449
|
+
instance.returnedCallbacks.set(resolveCallbackId, async (promiseId) => {
|
|
450
|
+
const promise = instance.returnedPromises.get(promiseId);
|
|
451
|
+
if (!promise)
|
|
452
|
+
throw new Error(`Promise ${promiseId} not found`);
|
|
453
|
+
const result2 = await promise;
|
|
454
|
+
instance.returnedPromises.delete(promiseId);
|
|
455
|
+
const ctx = createMarshalContext();
|
|
456
|
+
const marshalled = await marshalValue(result2, ctx);
|
|
457
|
+
return addCallbackIdsToRefs(marshalled);
|
|
458
|
+
});
|
|
459
|
+
return { ...value, __resolveCallbackId: resolveCallbackId };
|
|
460
|
+
}
|
|
461
|
+
if (isAsyncIteratorRef(value)) {
|
|
462
|
+
if ("__nextCallbackId" in value)
|
|
463
|
+
return value;
|
|
464
|
+
const nextCallbackId = instance.nextLocalCallbackId++;
|
|
465
|
+
instance.returnedCallbacks.set(nextCallbackId, async (iteratorId) => {
|
|
466
|
+
const iterator = instance.returnedIterators.get(iteratorId);
|
|
467
|
+
if (!iterator)
|
|
468
|
+
throw new Error(`Iterator ${iteratorId} not found`);
|
|
469
|
+
const result2 = await iterator.next();
|
|
470
|
+
if (result2.done)
|
|
471
|
+
instance.returnedIterators.delete(iteratorId);
|
|
472
|
+
const ctx = createMarshalContext();
|
|
473
|
+
const marshalledValue = await marshalValue(result2.value, ctx);
|
|
474
|
+
return { done: result2.done, value: addCallbackIdsToRefs(marshalledValue) };
|
|
475
|
+
});
|
|
476
|
+
const returnCallbackId = instance.nextLocalCallbackId++;
|
|
477
|
+
instance.returnedCallbacks.set(returnCallbackId, async (iteratorId, returnValue) => {
|
|
478
|
+
const iterator = instance.returnedIterators.get(iteratorId);
|
|
479
|
+
instance.returnedIterators.delete(iteratorId);
|
|
480
|
+
if (!iterator || !iterator.return)
|
|
481
|
+
return { done: true, value: undefined };
|
|
482
|
+
const result2 = await iterator.return(returnValue);
|
|
483
|
+
const ctx = createMarshalContext();
|
|
484
|
+
const marshalledValue = await marshalValue(result2.value, ctx);
|
|
485
|
+
return { done: true, value: addCallbackIdsToRefs(marshalledValue) };
|
|
486
|
+
});
|
|
487
|
+
return { ...value, __nextCallbackId: nextCallbackId, __returnCallbackId: returnCallbackId };
|
|
488
|
+
}
|
|
489
|
+
if (Array.isArray(value))
|
|
490
|
+
return value.map((item) => addCallbackIdsToRefs(item));
|
|
491
|
+
const result = {};
|
|
492
|
+
for (const key of Object.keys(value)) {
|
|
493
|
+
result[key] = addCallbackIdsToRefs(value[key]);
|
|
494
|
+
}
|
|
495
|
+
return result;
|
|
496
|
+
};
|
|
497
|
+
const LOCAL_CALLBACK_THRESHOLD = 1e6;
|
|
498
|
+
const invokeCallback = async (callbackId, args) => {
|
|
499
|
+
if (callbackId >= LOCAL_CALLBACK_THRESHOLD) {
|
|
500
|
+
const callback = instance.returnedCallbacks.get(callbackId);
|
|
501
|
+
if (!callback) {
|
|
502
|
+
throw new Error(`Local callback ${callbackId} not found`);
|
|
503
|
+
}
|
|
504
|
+
return await callback(...args);
|
|
505
|
+
} else {
|
|
506
|
+
const conn = callbackContext.connection;
|
|
507
|
+
if (!conn) {
|
|
508
|
+
throw new Error(`No connection available for callback ${callbackId}`);
|
|
509
|
+
}
|
|
510
|
+
return invokeClientCallback(conn, callbackId, args);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
customFnMarshalOptions = { createMarshalContext, addCallbackIdsToRefs, invokeCallback };
|
|
514
|
+
bridgedCustomFunctions = {};
|
|
515
|
+
for (const [name, registration] of Object.entries(customCallbacks)) {
|
|
516
|
+
if (name.includes(":"))
|
|
517
|
+
continue;
|
|
518
|
+
const callbackContext_ = callbackContext;
|
|
519
|
+
if (registration.type === "asyncIterator") {
|
|
520
|
+
bridgedCustomFunctions[name] = {
|
|
521
|
+
type: "asyncIterator",
|
|
522
|
+
fn: (...args) => {
|
|
523
|
+
const startCallbackId = callbackContext_.custom.get(`${name}:start`);
|
|
524
|
+
const nextCallbackId = callbackContext_.custom.get(`${name}:next`);
|
|
525
|
+
const returnCallbackId = callbackContext_.custom.get(`${name}:return`);
|
|
526
|
+
async function* bridgedIterator() {
|
|
527
|
+
const conn = callbackContext_.connection;
|
|
528
|
+
if (!conn || startCallbackId === undefined) {
|
|
529
|
+
throw new Error(`AsyncIterator callback '${name}' not available`);
|
|
530
|
+
}
|
|
531
|
+
const startResult = await invokeClientCallback(conn, startCallbackId, args);
|
|
532
|
+
const iteratorId = startResult.iteratorId;
|
|
533
|
+
try {
|
|
534
|
+
while (true) {
|
|
535
|
+
const nextConn = callbackContext_.connection;
|
|
536
|
+
if (!nextConn || nextCallbackId === undefined) {
|
|
537
|
+
throw new Error(`AsyncIterator callback '${name}' not available`);
|
|
538
|
+
}
|
|
539
|
+
const nextResult = await invokeClientCallback(nextConn, nextCallbackId, [iteratorId]);
|
|
540
|
+
if (nextResult.done)
|
|
541
|
+
return nextResult.value;
|
|
542
|
+
yield nextResult.value;
|
|
543
|
+
}
|
|
544
|
+
} finally {
|
|
545
|
+
const retConn = callbackContext_.connection;
|
|
546
|
+
if (retConn && returnCallbackId !== undefined) {
|
|
547
|
+
await invokeClientCallback(retConn, returnCallbackId, [iteratorId]).catch(() => {});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return bridgedIterator();
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
} else {
|
|
555
|
+
bridgedCustomFunctions[name] = {
|
|
556
|
+
type: registration.type,
|
|
557
|
+
fn: async (...args) => {
|
|
558
|
+
const conn = callbackContext_.connection;
|
|
559
|
+
const cbId = callbackContext_.custom.get(name);
|
|
560
|
+
if (!conn || cbId === undefined) {
|
|
561
|
+
throw new Error(`Custom function callback '${name}' not available`);
|
|
562
|
+
}
|
|
563
|
+
return invokeClientCallback(conn, cbId, args);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
let moduleLoader;
|
|
570
|
+
if (moduleLoaderCallback) {
|
|
571
|
+
moduleLoader = async (specifier, importer) => {
|
|
572
|
+
const conn = callbackContext.connection;
|
|
573
|
+
const cbId = callbackContext.moduleLoader;
|
|
574
|
+
if (!conn || cbId === undefined) {
|
|
575
|
+
throw new Error("Module loader callback not available");
|
|
576
|
+
}
|
|
577
|
+
return invokeClientCallback(conn, cbId, [specifier, importer]);
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
let testEnvironment;
|
|
581
|
+
if (message.options.testEnvironment) {
|
|
582
|
+
const testEnvOption = message.options.testEnvironment;
|
|
583
|
+
const testEnvOptions = typeof testEnvOption === "object" ? testEnvOption : undefined;
|
|
584
|
+
testEnvironment = {
|
|
585
|
+
onEvent: testEnvOptions?.callbacks?.onEvent ? (event) => {
|
|
586
|
+
const conn = callbackContext.connection;
|
|
587
|
+
const callbackId = callbackContext.testEnvironmentOnEvent;
|
|
588
|
+
if (!conn || callbackId === undefined) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const promise = invokeClientCallback(conn, callbackId, [JSON.stringify(event)]).catch(() => {});
|
|
592
|
+
instance.runtime?.pendingCallbacks?.push(promise);
|
|
593
|
+
} : undefined,
|
|
594
|
+
testTimeout: testEnvOptions?.testTimeout
|
|
595
|
+
};
|
|
596
|
+
if (testEnvOptions?.callbacks?.onEvent) {
|
|
597
|
+
instance.callbacks.set(testEnvOptions.callbacks.onEvent.callbackId, {
|
|
598
|
+
...testEnvOptions.callbacks.onEvent,
|
|
599
|
+
name: "testEnvironment.onEvent"
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
let playwrightOptions;
|
|
604
|
+
const playwrightCallbacks = message.options.callbacks?.playwright;
|
|
605
|
+
if (playwrightCallbacks) {
|
|
606
|
+
playwrightOptions = {
|
|
607
|
+
handler: async (op) => {
|
|
608
|
+
const conn = callbackContext.connection;
|
|
609
|
+
const callbackId = callbackContext.playwright.handlerCallbackId;
|
|
610
|
+
if (!conn || callbackId === undefined) {
|
|
611
|
+
return {
|
|
612
|
+
ok: false,
|
|
613
|
+
error: {
|
|
614
|
+
name: "Error",
|
|
615
|
+
message: "Playwright handler callback not available"
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
const resultJson = await invokeClientCallback(conn, callbackId, [JSON.stringify(op)], 60000);
|
|
621
|
+
return JSON.parse(resultJson);
|
|
622
|
+
} catch (err) {
|
|
623
|
+
const error = err;
|
|
624
|
+
return { ok: false, error: { name: error.name, message: error.message } };
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
console: playwrightCallbacks.console,
|
|
628
|
+
onEvent: (event) => {
|
|
629
|
+
const conn = callbackContext.connection;
|
|
630
|
+
if (!conn) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (event.type === "browserConsoleLog" && callbackContext.playwright.onBrowserConsoleLogCallbackId !== undefined) {
|
|
634
|
+
const promise = invokeClientCallback(conn, callbackContext.playwright.onBrowserConsoleLogCallbackId, [{ level: event.level, stdout: event.stdout, timestamp: event.timestamp }]).catch(() => {});
|
|
635
|
+
instance.runtime?.pendingCallbacks?.push(promise);
|
|
636
|
+
} else if (event.type === "networkRequest" && callbackContext.playwright.onNetworkRequestCallbackId !== undefined) {
|
|
637
|
+
const promise = invokeClientCallback(conn, callbackContext.playwright.onNetworkRequestCallbackId, [event]).catch(() => {});
|
|
638
|
+
instance.runtime?.pendingCallbacks?.push(promise);
|
|
639
|
+
} else if (event.type === "networkResponse" && callbackContext.playwright.onNetworkResponseCallbackId !== undefined) {
|
|
640
|
+
const promise = invokeClientCallback(conn, callbackContext.playwright.onNetworkResponseCallbackId, [event]).catch(() => {});
|
|
641
|
+
instance.runtime?.pendingCallbacks?.push(promise);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
const runtime = await createRuntime({
|
|
387
647
|
memoryLimitMB: message.options.memoryLimitMB ?? state.options.defaultMemoryLimitMB,
|
|
388
648
|
cwd: message.options.cwd,
|
|
389
649
|
console: {
|
|
@@ -393,28 +653,31 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
393
653
|
if (!conn || callbackId === undefined)
|
|
394
654
|
return;
|
|
395
655
|
const promise = invokeClientCallback(conn, callbackId, [entry]).catch(() => {});
|
|
396
|
-
pendingCallbacks.push(promise);
|
|
656
|
+
runtime.pendingCallbacks.push(promise);
|
|
397
657
|
}
|
|
398
658
|
},
|
|
399
|
-
fetch: {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
659
|
+
fetch: async (url, init) => {
|
|
660
|
+
const conn = callbackContext.connection;
|
|
661
|
+
const callbackId = callbackContext.fetch;
|
|
662
|
+
if (!conn || callbackId === undefined) {
|
|
663
|
+
throw new Error("Fetch callback not available");
|
|
664
|
+
}
|
|
665
|
+
const serialized = {
|
|
666
|
+
url,
|
|
667
|
+
method: init.method,
|
|
668
|
+
headers: init.headers,
|
|
669
|
+
body: init.rawBody
|
|
670
|
+
};
|
|
671
|
+
const result = await invokeClientCallback(conn, callbackId, [serialized], 60000);
|
|
672
|
+
if (result && typeof result === "object" && result.__streamingResponse) {
|
|
673
|
+
const response = result.response;
|
|
674
|
+
response.__isCallbackStream = true;
|
|
675
|
+
return response;
|
|
414
676
|
}
|
|
677
|
+
return deserializeResponse(result);
|
|
415
678
|
},
|
|
416
679
|
fs: {
|
|
417
|
-
getDirectory: async (
|
|
680
|
+
getDirectory: async (dirPath) => {
|
|
418
681
|
const conn = callbackContext.connection;
|
|
419
682
|
if (!conn) {
|
|
420
683
|
throw new Error("FS callbacks not available");
|
|
@@ -423,35 +686,17 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
423
686
|
connection: conn,
|
|
424
687
|
callbackContext,
|
|
425
688
|
invokeClientCallback,
|
|
426
|
-
basePath:
|
|
689
|
+
basePath: dirPath
|
|
427
690
|
});
|
|
428
691
|
}
|
|
429
|
-
}
|
|
692
|
+
},
|
|
693
|
+
moduleLoader,
|
|
694
|
+
customFunctions: bridgedCustomFunctions,
|
|
695
|
+
customFunctionsMarshalOptions: customFnMarshalOptions,
|
|
696
|
+
testEnvironment,
|
|
697
|
+
playwright: playwrightOptions
|
|
430
698
|
});
|
|
431
|
-
|
|
432
|
-
isolateId,
|
|
433
|
-
runtime,
|
|
434
|
-
ownerConnection: connection.socket,
|
|
435
|
-
callbacks: new Map,
|
|
436
|
-
createdAt: Date.now(),
|
|
437
|
-
lastActivity: Date.now(),
|
|
438
|
-
pendingCallbacks,
|
|
439
|
-
returnedCallbacks: new Map,
|
|
440
|
-
returnedPromises: new Map,
|
|
441
|
-
returnedIterators: new Map,
|
|
442
|
-
nextLocalCallbackId: 1e6,
|
|
443
|
-
namespaceId,
|
|
444
|
-
isDisposed: false,
|
|
445
|
-
callbackContext
|
|
446
|
-
};
|
|
447
|
-
if (moduleLoaderCallback) {
|
|
448
|
-
instance.moduleLoaderCallbackId = moduleLoaderCallback.callbackId;
|
|
449
|
-
instance.moduleCache = new Map;
|
|
450
|
-
instance.moduleToFilename = new Map;
|
|
451
|
-
}
|
|
452
|
-
if (customCallbacks) {
|
|
453
|
-
await setupCustomFunctions(runtime.context, customCallbacks, connection, instance);
|
|
454
|
-
}
|
|
699
|
+
instance.runtime = runtime;
|
|
455
700
|
if (consoleCallbacks?.onEntry) {
|
|
456
701
|
instance.callbacks.set(consoleCallbacks.onEntry.callbackId, {
|
|
457
702
|
...consoleCallbacks.onEntry,
|
|
@@ -478,52 +723,33 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
478
723
|
}
|
|
479
724
|
}
|
|
480
725
|
}
|
|
481
|
-
if (
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
onEvent: onEventCallback ? (event) => {
|
|
487
|
-
const promise = invokeClientCallback(connection, onEventCallback.callbackId, [JSON.stringify(event)]).catch(() => {});
|
|
488
|
-
pendingCallbacks.push(promise);
|
|
489
|
-
} : undefined,
|
|
490
|
-
testTimeout: testEnvOptions?.testTimeout
|
|
726
|
+
if (playwrightCallbacks) {
|
|
727
|
+
instance.callbacks.set(playwrightCallbacks.handlerCallbackId, {
|
|
728
|
+
callbackId: playwrightCallbacks.handlerCallbackId,
|
|
729
|
+
name: "playwright.handler",
|
|
730
|
+
type: "async"
|
|
491
731
|
});
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
732
|
+
if (playwrightCallbacks.onBrowserConsoleLogCallbackId !== undefined) {
|
|
733
|
+
instance.callbacks.set(playwrightCallbacks.onBrowserConsoleLogCallbackId, {
|
|
734
|
+
callbackId: playwrightCallbacks.onBrowserConsoleLogCallbackId,
|
|
735
|
+
name: "playwright.onBrowserConsoleLog",
|
|
736
|
+
type: "sync"
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
if (playwrightCallbacks.onNetworkRequestCallbackId !== undefined) {
|
|
740
|
+
instance.callbacks.set(playwrightCallbacks.onNetworkRequestCallbackId, {
|
|
741
|
+
callbackId: playwrightCallbacks.onNetworkRequestCallbackId,
|
|
742
|
+
name: "playwright.onNetworkRequest",
|
|
743
|
+
type: "sync"
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
if (playwrightCallbacks.onNetworkResponseCallbackId !== undefined) {
|
|
747
|
+
instance.callbacks.set(playwrightCallbacks.onNetworkResponseCallbackId, {
|
|
748
|
+
callbackId: playwrightCallbacks.onNetworkResponseCallbackId,
|
|
749
|
+
name: "playwright.onNetworkResponse",
|
|
750
|
+
type: "sync"
|
|
497
751
|
});
|
|
498
752
|
}
|
|
499
|
-
}
|
|
500
|
-
const playwrightCallbacks = message.options.callbacks?.playwright;
|
|
501
|
-
if (playwrightCallbacks) {
|
|
502
|
-
const handler = async (op) => {
|
|
503
|
-
try {
|
|
504
|
-
const resultJson = await invokeClientCallback(connection, playwrightCallbacks.handlerCallbackId, [JSON.stringify(op)]);
|
|
505
|
-
return JSON.parse(resultJson);
|
|
506
|
-
} catch (err) {
|
|
507
|
-
const error = err;
|
|
508
|
-
return { ok: false, error: { name: error.name, message: error.message } };
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
|
-
instance.playwrightHandle = await setupPlaywright(runtime.context, {
|
|
512
|
-
handler,
|
|
513
|
-
console: playwrightCallbacks.console,
|
|
514
|
-
onEvent: (event) => {
|
|
515
|
-
if (event.type === "browserConsoleLog" && playwrightCallbacks.onBrowserConsoleLogCallbackId) {
|
|
516
|
-
const promise = invokeClientCallback(connection, playwrightCallbacks.onBrowserConsoleLogCallbackId, [{ level: event.level, stdout: event.stdout, timestamp: event.timestamp }]).catch(() => {});
|
|
517
|
-
pendingCallbacks.push(promise);
|
|
518
|
-
} else if (event.type === "networkRequest" && playwrightCallbacks.onNetworkRequestCallbackId) {
|
|
519
|
-
const promise = invokeClientCallback(connection, playwrightCallbacks.onNetworkRequestCallbackId, [event]).catch(() => {});
|
|
520
|
-
pendingCallbacks.push(promise);
|
|
521
|
-
} else if (event.type === "networkResponse" && playwrightCallbacks.onNetworkResponseCallbackId) {
|
|
522
|
-
const promise = invokeClientCallback(connection, playwrightCallbacks.onNetworkResponseCallbackId, [event]).catch(() => {});
|
|
523
|
-
pendingCallbacks.push(promise);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
});
|
|
527
753
|
}
|
|
528
754
|
state.isolates.set(isolateId, instance);
|
|
529
755
|
connection.isolates.add(isolateId);
|
|
@@ -531,7 +757,11 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
531
757
|
if (namespaceId != null) {
|
|
532
758
|
state.namespacedRuntimes.set(namespaceId, instance);
|
|
533
759
|
}
|
|
534
|
-
|
|
760
|
+
runtime.fetch.onWebSocketCommand((cmd) => {
|
|
761
|
+
const targetConnection = callbackContext.connection;
|
|
762
|
+
if (!targetConnection) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
535
765
|
let data;
|
|
536
766
|
if (cmd.data instanceof ArrayBuffer) {
|
|
537
767
|
data = new Uint8Array(cmd.data);
|
|
@@ -549,7 +779,49 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
549
779
|
reason: cmd.reason
|
|
550
780
|
}
|
|
551
781
|
};
|
|
552
|
-
sendMessage(
|
|
782
|
+
sendMessage(targetConnection.socket, wsCommandMsg);
|
|
783
|
+
});
|
|
784
|
+
runtime.fetch.onClientWebSocketCommand((cmd) => {
|
|
785
|
+
const targetConnection = callbackContext.connection;
|
|
786
|
+
if (!targetConnection) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
let data;
|
|
790
|
+
if (cmd.data instanceof ArrayBuffer) {
|
|
791
|
+
data = new Uint8Array(cmd.data);
|
|
792
|
+
} else {
|
|
793
|
+
data = cmd.data;
|
|
794
|
+
}
|
|
795
|
+
if (cmd.type === "connect") {
|
|
796
|
+
const msg = {
|
|
797
|
+
type: MessageType.CLIENT_WS_CONNECT,
|
|
798
|
+
requestId: 0,
|
|
799
|
+
isolateId,
|
|
800
|
+
socketId: cmd.socketId,
|
|
801
|
+
url: cmd.url,
|
|
802
|
+
protocols: cmd.protocols
|
|
803
|
+
};
|
|
804
|
+
sendMessage(targetConnection.socket, msg);
|
|
805
|
+
} else if (cmd.type === "send") {
|
|
806
|
+
const msg = {
|
|
807
|
+
type: MessageType.CLIENT_WS_SEND,
|
|
808
|
+
requestId: 0,
|
|
809
|
+
isolateId,
|
|
810
|
+
socketId: cmd.socketId,
|
|
811
|
+
data
|
|
812
|
+
};
|
|
813
|
+
sendMessage(targetConnection.socket, msg);
|
|
814
|
+
} else if (cmd.type === "close") {
|
|
815
|
+
const msg = {
|
|
816
|
+
type: MessageType.CLIENT_WS_CLOSE,
|
|
817
|
+
requestId: 0,
|
|
818
|
+
isolateId,
|
|
819
|
+
socketId: cmd.socketId,
|
|
820
|
+
code: cmd.code,
|
|
821
|
+
reason: cmd.reason
|
|
822
|
+
};
|
|
823
|
+
sendMessage(targetConnection.socket, msg);
|
|
824
|
+
}
|
|
553
825
|
});
|
|
554
826
|
sendOk(connection.socket, message.requestId, { isolateId, reused: false });
|
|
555
827
|
} catch (err) {
|
|
@@ -572,10 +844,7 @@ async function handleDisposeRuntime(message, connection, state) {
|
|
|
572
844
|
if (instance.namespaceId != null) {
|
|
573
845
|
softDeleteRuntime(instance, state);
|
|
574
846
|
} else {
|
|
575
|
-
|
|
576
|
-
instance.playwrightHandle.dispose();
|
|
577
|
-
}
|
|
578
|
-
instance.runtime.dispose();
|
|
847
|
+
await instance.runtime.dispose();
|
|
579
848
|
state.isolates.delete(message.isolateId);
|
|
580
849
|
}
|
|
581
850
|
sendOk(connection.socket, message.requestId);
|
|
@@ -592,45 +861,13 @@ async function handleEval(message, connection, state) {
|
|
|
592
861
|
}
|
|
593
862
|
instance.lastActivity = Date.now();
|
|
594
863
|
try {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
if (!instance.sourceMaps) {
|
|
599
|
-
instance.sourceMaps = new Map;
|
|
600
|
-
}
|
|
601
|
-
instance.sourceMaps.set(filename, transformed.sourceMap);
|
|
602
|
-
}
|
|
603
|
-
const mod = await instance.runtime.isolate.compileModule(transformed.code, {
|
|
604
|
-
filename
|
|
864
|
+
await instance.runtime.eval(message.code, {
|
|
865
|
+
filename: message.filename,
|
|
866
|
+
maxExecutionMs: message.maxExecutionMs
|
|
605
867
|
});
|
|
606
|
-
if (instance.moduleLoaderCallbackId) {
|
|
607
|
-
instance.moduleToFilename?.set(mod, filename);
|
|
608
|
-
const resolver = createModuleResolver(instance, connection);
|
|
609
|
-
await mod.instantiate(instance.runtime.context, resolver);
|
|
610
|
-
} else {
|
|
611
|
-
await mod.instantiate(instance.runtime.context, (specifier) => {
|
|
612
|
-
throw new Error(`No module loader registered. Cannot import: ${specifier}`);
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
await mod.evaluate();
|
|
616
|
-
const ns = mod.namespace;
|
|
617
|
-
const runRef = await ns.get("default", { reference: true });
|
|
618
|
-
try {
|
|
619
|
-
await runRef.apply(undefined, [], {
|
|
620
|
-
result: { promise: true },
|
|
621
|
-
...message.maxExecutionMs ? { timeout: message.maxExecutionMs } : {}
|
|
622
|
-
});
|
|
623
|
-
} finally {
|
|
624
|
-
runRef.release();
|
|
625
|
-
}
|
|
626
|
-
await Promise.all(instance.pendingCallbacks);
|
|
627
|
-
instance.pendingCallbacks.length = 0;
|
|
628
868
|
sendOk(connection.socket, message.requestId, { value: undefined });
|
|
629
869
|
} catch (err) {
|
|
630
870
|
const error = err;
|
|
631
|
-
if (error.stack && instance.sourceMaps?.size) {
|
|
632
|
-
error.stack = mapErrorStack(error.stack, instance.sourceMaps);
|
|
633
|
-
}
|
|
634
871
|
const isTimeoutError = error.message?.includes("Script execution timed out");
|
|
635
872
|
sendError(connection.socket, message.requestId, isTimeoutError ? ErrorCode.ISOLATE_TIMEOUT : ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
636
873
|
}
|
|
@@ -740,6 +977,35 @@ async function handleWsClose(message, connection, state) {
|
|
|
740
977
|
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
741
978
|
}
|
|
742
979
|
}
|
|
980
|
+
function handleClientWsOpened(message, connection, state) {
|
|
981
|
+
const instance = state.isolates.get(message.isolateId);
|
|
982
|
+
if (!instance)
|
|
983
|
+
return;
|
|
984
|
+
instance.lastActivity = Date.now();
|
|
985
|
+
instance.runtime.fetch.dispatchClientWebSocketOpen(message.socketId, message.protocol, message.extensions);
|
|
986
|
+
}
|
|
987
|
+
function handleClientWsMessage(message, connection, state) {
|
|
988
|
+
const instance = state.isolates.get(message.isolateId);
|
|
989
|
+
if (!instance)
|
|
990
|
+
return;
|
|
991
|
+
instance.lastActivity = Date.now();
|
|
992
|
+
const data = message.data instanceof Uint8Array ? message.data.buffer.slice(message.data.byteOffset, message.data.byteOffset + message.data.byteLength) : message.data;
|
|
993
|
+
instance.runtime.fetch.dispatchClientWebSocketMessage(message.socketId, data);
|
|
994
|
+
}
|
|
995
|
+
function handleClientWsClosed(message, connection, state) {
|
|
996
|
+
const instance = state.isolates.get(message.isolateId);
|
|
997
|
+
if (!instance)
|
|
998
|
+
return;
|
|
999
|
+
instance.lastActivity = Date.now();
|
|
1000
|
+
instance.runtime.fetch.dispatchClientWebSocketClose(message.socketId, message.code, message.reason, message.wasClean);
|
|
1001
|
+
}
|
|
1002
|
+
function handleClientWsError(message, connection, state) {
|
|
1003
|
+
const instance = state.isolates.get(message.isolateId);
|
|
1004
|
+
if (!instance)
|
|
1005
|
+
return;
|
|
1006
|
+
instance.lastActivity = Date.now();
|
|
1007
|
+
instance.runtime.fetch.dispatchClientWebSocketError(message.socketId);
|
|
1008
|
+
}
|
|
743
1009
|
async function handleFetchGetUpgradeRequest(message, connection, state) {
|
|
744
1010
|
const instance = state.isolates.get(message.isolateId);
|
|
745
1011
|
if (!instance) {
|
|
@@ -918,523 +1184,6 @@ async function invokeClientCallback(connection, callbackId, args, timeout = 1e4)
|
|
|
918
1184
|
sendMessage(connection.socket, invoke);
|
|
919
1185
|
});
|
|
920
1186
|
}
|
|
921
|
-
var ISOLATE_MARSHAL_CODE = `
|
|
922
|
-
(function() {
|
|
923
|
-
// Marshal a value (JavaScript → Ref)
|
|
924
|
-
function marshalForHost(value, depth = 0) {
|
|
925
|
-
if (depth > 100) throw new Error('Maximum marshalling depth exceeded');
|
|
926
|
-
|
|
927
|
-
if (value === null) return null;
|
|
928
|
-
if (value === undefined) return { __type: 'UndefinedRef' };
|
|
929
|
-
|
|
930
|
-
const type = typeof value;
|
|
931
|
-
if (type === 'string' || type === 'number' || type === 'boolean') return value;
|
|
932
|
-
if (type === 'bigint') return { __type: 'BigIntRef', value: value.toString() };
|
|
933
|
-
if (type === 'function') throw new Error('Cannot marshal functions from isolate');
|
|
934
|
-
if (type === 'symbol') throw new Error('Cannot marshal Symbol values');
|
|
935
|
-
|
|
936
|
-
if (type === 'object') {
|
|
937
|
-
if (value instanceof Date) {
|
|
938
|
-
return { __type: 'DateRef', timestamp: value.getTime() };
|
|
939
|
-
}
|
|
940
|
-
if (value instanceof RegExp) {
|
|
941
|
-
return { __type: 'RegExpRef', source: value.source, flags: value.flags };
|
|
942
|
-
}
|
|
943
|
-
if (value instanceof URL) {
|
|
944
|
-
return { __type: 'URLRef', href: value.href };
|
|
945
|
-
}
|
|
946
|
-
if (typeof Headers !== 'undefined' && value instanceof Headers) {
|
|
947
|
-
const pairs = [];
|
|
948
|
-
value.forEach((v, k) => pairs.push([k, v]));
|
|
949
|
-
return { __type: 'HeadersRef', pairs };
|
|
950
|
-
}
|
|
951
|
-
if (value instanceof Uint8Array) {
|
|
952
|
-
return { __type: 'Uint8ArrayRef', data: Array.from(value) };
|
|
953
|
-
}
|
|
954
|
-
if (value instanceof ArrayBuffer) {
|
|
955
|
-
return { __type: 'Uint8ArrayRef', data: Array.from(new Uint8Array(value)) };
|
|
956
|
-
}
|
|
957
|
-
if (typeof Request !== 'undefined' && value instanceof Request) {
|
|
958
|
-
throw new Error('Cannot marshal Request from isolate. Use fetch callback instead.');
|
|
959
|
-
}
|
|
960
|
-
if (typeof Response !== 'undefined' && value instanceof Response) {
|
|
961
|
-
throw new Error('Cannot marshal Response from isolate. Return plain objects instead.');
|
|
962
|
-
}
|
|
963
|
-
if (typeof File !== 'undefined' && value instanceof File) {
|
|
964
|
-
throw new Error('Cannot marshal File from isolate.');
|
|
965
|
-
}
|
|
966
|
-
if (typeof Blob !== 'undefined' && value instanceof Blob) {
|
|
967
|
-
throw new Error('Cannot marshal Blob from isolate.');
|
|
968
|
-
}
|
|
969
|
-
if (typeof FormData !== 'undefined' && value instanceof FormData) {
|
|
970
|
-
throw new Error('Cannot marshal FormData from isolate.');
|
|
971
|
-
}
|
|
972
|
-
if (Array.isArray(value)) {
|
|
973
|
-
return value.map(v => marshalForHost(v, depth + 1));
|
|
974
|
-
}
|
|
975
|
-
// Plain object
|
|
976
|
-
const result = {};
|
|
977
|
-
for (const key of Object.keys(value)) {
|
|
978
|
-
result[key] = marshalForHost(value[key], depth + 1);
|
|
979
|
-
}
|
|
980
|
-
return result;
|
|
981
|
-
}
|
|
982
|
-
return value;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// Unmarshal a value (Ref → JavaScript)
|
|
986
|
-
function unmarshalFromHost(value, depth = 0) {
|
|
987
|
-
if (depth > 100) throw new Error('Maximum unmarshalling depth exceeded');
|
|
988
|
-
|
|
989
|
-
if (value === null) return null;
|
|
990
|
-
if (typeof value !== 'object') return value;
|
|
991
|
-
|
|
992
|
-
if (value.__type) {
|
|
993
|
-
switch (value.__type) {
|
|
994
|
-
case 'UndefinedRef': return undefined;
|
|
995
|
-
case 'DateRef': return new Date(value.timestamp);
|
|
996
|
-
case 'RegExpRef': return new RegExp(value.source, value.flags);
|
|
997
|
-
case 'BigIntRef': return BigInt(value.value);
|
|
998
|
-
case 'URLRef': return new URL(value.href);
|
|
999
|
-
case 'HeadersRef': return new Headers(value.pairs);
|
|
1000
|
-
case 'Uint8ArrayRef': return new Uint8Array(value.data);
|
|
1001
|
-
case 'RequestRef': {
|
|
1002
|
-
const init = {
|
|
1003
|
-
method: value.method,
|
|
1004
|
-
headers: value.headers,
|
|
1005
|
-
body: value.body ? new Uint8Array(value.body) : null,
|
|
1006
|
-
};
|
|
1007
|
-
if (value.mode) init.mode = value.mode;
|
|
1008
|
-
if (value.credentials) init.credentials = value.credentials;
|
|
1009
|
-
if (value.cache) init.cache = value.cache;
|
|
1010
|
-
if (value.redirect) init.redirect = value.redirect;
|
|
1011
|
-
if (value.referrer) init.referrer = value.referrer;
|
|
1012
|
-
if (value.referrerPolicy) init.referrerPolicy = value.referrerPolicy;
|
|
1013
|
-
if (value.integrity) init.integrity = value.integrity;
|
|
1014
|
-
return new Request(value.url, init);
|
|
1015
|
-
}
|
|
1016
|
-
case 'ResponseRef': {
|
|
1017
|
-
return new Response(value.body ? new Uint8Array(value.body) : null, {
|
|
1018
|
-
status: value.status,
|
|
1019
|
-
statusText: value.statusText,
|
|
1020
|
-
headers: value.headers,
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
case 'FileRef': {
|
|
1024
|
-
if (!value.name) {
|
|
1025
|
-
return new Blob([new Uint8Array(value.data)], { type: value.type });
|
|
1026
|
-
}
|
|
1027
|
-
return new File([new Uint8Array(value.data)], value.name, {
|
|
1028
|
-
type: value.type,
|
|
1029
|
-
lastModified: value.lastModified,
|
|
1030
|
-
});
|
|
1031
|
-
}
|
|
1032
|
-
case 'FormDataRef': {
|
|
1033
|
-
const fd = new FormData();
|
|
1034
|
-
for (const [key, entry] of value.entries) {
|
|
1035
|
-
if (typeof entry === 'string') {
|
|
1036
|
-
fd.append(key, entry);
|
|
1037
|
-
} else {
|
|
1038
|
-
const file = unmarshalFromHost(entry, depth + 1);
|
|
1039
|
-
fd.append(key, file);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
return fd;
|
|
1043
|
-
}
|
|
1044
|
-
case 'CallbackRef': {
|
|
1045
|
-
// Create a proxy function that invokes the callback
|
|
1046
|
-
const callbackId = value.callbackId;
|
|
1047
|
-
return function(...args) {
|
|
1048
|
-
const argsJson = JSON.stringify(marshalForHost(args));
|
|
1049
|
-
const resultJson = __customFn_invoke.applySyncPromise(undefined, [callbackId, argsJson]);
|
|
1050
|
-
const result = JSON.parse(resultJson);
|
|
1051
|
-
if (result.ok) {
|
|
1052
|
-
return unmarshalFromHost(result.value);
|
|
1053
|
-
} else {
|
|
1054
|
-
const error = new Error(result.error.message);
|
|
1055
|
-
error.name = result.error.name;
|
|
1056
|
-
throw error;
|
|
1057
|
-
}
|
|
1058
|
-
};
|
|
1059
|
-
}
|
|
1060
|
-
case 'PromiseRef': {
|
|
1061
|
-
// Create a proxy Promise that resolves via callback
|
|
1062
|
-
const promiseId = value.promiseId;
|
|
1063
|
-
return new Promise((resolve, reject) => {
|
|
1064
|
-
try {
|
|
1065
|
-
const argsJson = JSON.stringify([promiseId]);
|
|
1066
|
-
const resultJson = __customFn_invoke.applySyncPromise(undefined, [value.__resolveCallbackId, argsJson]);
|
|
1067
|
-
const result = JSON.parse(resultJson);
|
|
1068
|
-
if (result.ok) {
|
|
1069
|
-
resolve(unmarshalFromHost(result.value));
|
|
1070
|
-
} else {
|
|
1071
|
-
reject(new Error(result.error.message));
|
|
1072
|
-
}
|
|
1073
|
-
} catch (e) {
|
|
1074
|
-
reject(e);
|
|
1075
|
-
}
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1078
|
-
case 'AsyncIteratorRef': {
|
|
1079
|
-
const iteratorId = value.iteratorId;
|
|
1080
|
-
const nextCallbackId = value.__nextCallbackId;
|
|
1081
|
-
const returnCallbackId = value.__returnCallbackId;
|
|
1082
|
-
return {
|
|
1083
|
-
[Symbol.asyncIterator]() { return this; },
|
|
1084
|
-
async next() {
|
|
1085
|
-
const argsJson = JSON.stringify([iteratorId]);
|
|
1086
|
-
const resultJson = __customFn_invoke.applySyncPromise(undefined, [nextCallbackId, argsJson]);
|
|
1087
|
-
const result = JSON.parse(resultJson);
|
|
1088
|
-
if (!result.ok) {
|
|
1089
|
-
const error = new Error(result.error.message);
|
|
1090
|
-
error.name = result.error.name;
|
|
1091
|
-
throw error;
|
|
1092
|
-
}
|
|
1093
|
-
return {
|
|
1094
|
-
done: result.value.done,
|
|
1095
|
-
value: unmarshalFromHost(result.value.value)
|
|
1096
|
-
};
|
|
1097
|
-
},
|
|
1098
|
-
async return(v) {
|
|
1099
|
-
const argsJson = JSON.stringify([iteratorId, marshalForHost(v)]);
|
|
1100
|
-
const resultJson = __customFn_invoke.applySyncPromise(undefined, [returnCallbackId, argsJson]);
|
|
1101
|
-
const result = JSON.parse(resultJson);
|
|
1102
|
-
return { done: true, value: result.ok ? unmarshalFromHost(result.value) : undefined };
|
|
1103
|
-
}
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
default:
|
|
1107
|
-
// Unknown ref type, return as-is
|
|
1108
|
-
break;
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
if (Array.isArray(value)) {
|
|
1113
|
-
return value.map(v => unmarshalFromHost(v, depth + 1));
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
// Plain object - recursively unmarshal
|
|
1117
|
-
const result = {};
|
|
1118
|
-
for (const key of Object.keys(value)) {
|
|
1119
|
-
result[key] = unmarshalFromHost(value[key], depth + 1);
|
|
1120
|
-
}
|
|
1121
|
-
return result;
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
globalThis.__marshalForHost = marshalForHost;
|
|
1125
|
-
globalThis.__unmarshalFromHost = unmarshalFromHost;
|
|
1126
|
-
})();
|
|
1127
|
-
`;
|
|
1128
|
-
var LOCAL_CALLBACK_THRESHOLD = 1e6;
|
|
1129
|
-
function isPromiseRef(value) {
|
|
1130
|
-
return typeof value === "object" && value !== null && value.__type === "PromiseRef";
|
|
1131
|
-
}
|
|
1132
|
-
function isAsyncIteratorRef(value) {
|
|
1133
|
-
return typeof value === "object" && value !== null && value.__type === "AsyncIteratorRef";
|
|
1134
|
-
}
|
|
1135
|
-
function isLocalCallbackId(callbackId) {
|
|
1136
|
-
return callbackId >= LOCAL_CALLBACK_THRESHOLD;
|
|
1137
|
-
}
|
|
1138
|
-
async function setupCustomFunctions(context, customCallbacks, connection, instance) {
|
|
1139
|
-
const global = context.global;
|
|
1140
|
-
function createMarshalContext() {
|
|
1141
|
-
return {
|
|
1142
|
-
registerCallback: (fn) => {
|
|
1143
|
-
const callbackId = instance.nextLocalCallbackId++;
|
|
1144
|
-
instance.returnedCallbacks.set(callbackId, fn);
|
|
1145
|
-
return callbackId;
|
|
1146
|
-
},
|
|
1147
|
-
registerPromise: (promise) => {
|
|
1148
|
-
const promiseId = instance.nextLocalCallbackId++;
|
|
1149
|
-
instance.returnedPromises.set(promiseId, promise);
|
|
1150
|
-
return promiseId;
|
|
1151
|
-
},
|
|
1152
|
-
registerIterator: (iterator) => {
|
|
1153
|
-
const iteratorId = instance.nextLocalCallbackId++;
|
|
1154
|
-
instance.returnedIterators.set(iteratorId, iterator);
|
|
1155
|
-
return iteratorId;
|
|
1156
|
-
}
|
|
1157
|
-
};
|
|
1158
|
-
}
|
|
1159
|
-
function addCallbackIdsToRefs(value) {
|
|
1160
|
-
if (value === null || typeof value !== "object") {
|
|
1161
|
-
return value;
|
|
1162
|
-
}
|
|
1163
|
-
if (isPromiseRef(value)) {
|
|
1164
|
-
if ("__resolveCallbackId" in value) {
|
|
1165
|
-
return value;
|
|
1166
|
-
}
|
|
1167
|
-
const resolveCallbackId = instance.nextLocalCallbackId++;
|
|
1168
|
-
instance.returnedCallbacks.set(resolveCallbackId, async (promiseId) => {
|
|
1169
|
-
const promise = instance.returnedPromises.get(promiseId);
|
|
1170
|
-
if (!promise) {
|
|
1171
|
-
throw new Error(`Promise ${promiseId} not found`);
|
|
1172
|
-
}
|
|
1173
|
-
const result2 = await promise;
|
|
1174
|
-
instance.returnedPromises.delete(promiseId);
|
|
1175
|
-
const ctx = createMarshalContext();
|
|
1176
|
-
const marshalled = await marshalValue(result2, ctx);
|
|
1177
|
-
return addCallbackIdsToRefs(marshalled);
|
|
1178
|
-
});
|
|
1179
|
-
return {
|
|
1180
|
-
...value,
|
|
1181
|
-
__resolveCallbackId: resolveCallbackId
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
if (isAsyncIteratorRef(value)) {
|
|
1185
|
-
if ("__nextCallbackId" in value) {
|
|
1186
|
-
return value;
|
|
1187
|
-
}
|
|
1188
|
-
const nextCallbackId = instance.nextLocalCallbackId++;
|
|
1189
|
-
instance.returnedCallbacks.set(nextCallbackId, async (iteratorId) => {
|
|
1190
|
-
const iterator = instance.returnedIterators.get(iteratorId);
|
|
1191
|
-
if (!iterator) {
|
|
1192
|
-
throw new Error(`Iterator ${iteratorId} not found`);
|
|
1193
|
-
}
|
|
1194
|
-
const result2 = await iterator.next();
|
|
1195
|
-
if (result2.done) {
|
|
1196
|
-
instance.returnedIterators.delete(iteratorId);
|
|
1197
|
-
}
|
|
1198
|
-
const ctx = createMarshalContext();
|
|
1199
|
-
const marshalledValue = await marshalValue(result2.value, ctx);
|
|
1200
|
-
return {
|
|
1201
|
-
done: result2.done,
|
|
1202
|
-
value: addCallbackIdsToRefs(marshalledValue)
|
|
1203
|
-
};
|
|
1204
|
-
});
|
|
1205
|
-
const returnCallbackId = instance.nextLocalCallbackId++;
|
|
1206
|
-
instance.returnedCallbacks.set(returnCallbackId, async (iteratorId, returnValue) => {
|
|
1207
|
-
const iterator = instance.returnedIterators.get(iteratorId);
|
|
1208
|
-
instance.returnedIterators.delete(iteratorId);
|
|
1209
|
-
if (!iterator || !iterator.return) {
|
|
1210
|
-
return { done: true, value: undefined };
|
|
1211
|
-
}
|
|
1212
|
-
const result2 = await iterator.return(returnValue);
|
|
1213
|
-
const ctx = createMarshalContext();
|
|
1214
|
-
const marshalledValue = await marshalValue(result2.value, ctx);
|
|
1215
|
-
return {
|
|
1216
|
-
done: true,
|
|
1217
|
-
value: addCallbackIdsToRefs(marshalledValue)
|
|
1218
|
-
};
|
|
1219
|
-
});
|
|
1220
|
-
return {
|
|
1221
|
-
...value,
|
|
1222
|
-
__nextCallbackId: nextCallbackId,
|
|
1223
|
-
__returnCallbackId: returnCallbackId
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
if (Array.isArray(value)) {
|
|
1227
|
-
return value.map((item) => addCallbackIdsToRefs(item));
|
|
1228
|
-
}
|
|
1229
|
-
const result = {};
|
|
1230
|
-
for (const key of Object.keys(value)) {
|
|
1231
|
-
result[key] = addCallbackIdsToRefs(value[key]);
|
|
1232
|
-
}
|
|
1233
|
-
return result;
|
|
1234
|
-
}
|
|
1235
|
-
const invokeCallbackRef = new ivm.Reference(async (callbackId, argsJson) => {
|
|
1236
|
-
const marshalledArgs = JSON.parse(argsJson);
|
|
1237
|
-
const args = unmarshalValue(marshalledArgs);
|
|
1238
|
-
try {
|
|
1239
|
-
let result;
|
|
1240
|
-
if (isLocalCallbackId(callbackId)) {
|
|
1241
|
-
const callback = instance.returnedCallbacks.get(callbackId);
|
|
1242
|
-
if (!callback) {
|
|
1243
|
-
throw new Error(`Local callback ${callbackId} not found`);
|
|
1244
|
-
}
|
|
1245
|
-
result = await callback(...args);
|
|
1246
|
-
} else {
|
|
1247
|
-
const conn = instance.callbackContext?.connection || connection;
|
|
1248
|
-
result = await invokeClientCallback(conn, callbackId, args);
|
|
1249
|
-
}
|
|
1250
|
-
const ctx = createMarshalContext();
|
|
1251
|
-
const marshalledResult = await marshalValue({ ok: true, value: result }, ctx);
|
|
1252
|
-
const processedResult = addCallbackIdsToRefs(marshalledResult);
|
|
1253
|
-
return JSON.stringify(processedResult);
|
|
1254
|
-
} catch (error) {
|
|
1255
|
-
const err = error;
|
|
1256
|
-
return JSON.stringify({
|
|
1257
|
-
ok: false,
|
|
1258
|
-
error: { message: err.message, name: err.name }
|
|
1259
|
-
});
|
|
1260
|
-
}
|
|
1261
|
-
});
|
|
1262
|
-
global.setSync("__customFn_invoke", invokeCallbackRef);
|
|
1263
|
-
context.evalSync(ISOLATE_MARSHAL_CODE);
|
|
1264
|
-
const callbackIdMap = {};
|
|
1265
|
-
for (const [name, registration] of Object.entries(customCallbacks)) {
|
|
1266
|
-
callbackIdMap[name] = registration.callbackId;
|
|
1267
|
-
}
|
|
1268
|
-
global.setSync("__customFnCallbackIds", new ivm.ExternalCopy(callbackIdMap).copyInto());
|
|
1269
|
-
for (const [name, registration] of Object.entries(customCallbacks)) {
|
|
1270
|
-
if (name.includes(":")) {
|
|
1271
|
-
continue;
|
|
1272
|
-
}
|
|
1273
|
-
if (registration.type === "sync") {
|
|
1274
|
-
context.evalSync(`
|
|
1275
|
-
globalThis.${name} = function(...args) {
|
|
1276
|
-
const callbackId = globalThis.__customFnCallbackIds["${name}"];
|
|
1277
|
-
const argsJson = JSON.stringify(__marshalForHost(args));
|
|
1278
|
-
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1279
|
-
undefined,
|
|
1280
|
-
[callbackId, argsJson]
|
|
1281
|
-
);
|
|
1282
|
-
const result = JSON.parse(resultJson);
|
|
1283
|
-
if (result.ok) {
|
|
1284
|
-
return __unmarshalFromHost(result.value);
|
|
1285
|
-
} else {
|
|
1286
|
-
const error = new Error(result.error.message);
|
|
1287
|
-
error.name = result.error.name;
|
|
1288
|
-
throw error;
|
|
1289
|
-
}
|
|
1290
|
-
};
|
|
1291
|
-
`);
|
|
1292
|
-
} else if (registration.type === "asyncIterator") {
|
|
1293
|
-
const startReg = customCallbacks[`${name}:start`];
|
|
1294
|
-
const nextReg = customCallbacks[`${name}:next`];
|
|
1295
|
-
const returnReg = customCallbacks[`${name}:return`];
|
|
1296
|
-
const throwReg = customCallbacks[`${name}:throw`];
|
|
1297
|
-
if (!startReg || !nextReg || !returnReg || !throwReg) {
|
|
1298
|
-
throw new Error(`Missing companion callbacks for asyncIterator function "${name}"`);
|
|
1299
|
-
}
|
|
1300
|
-
context.evalSync(`
|
|
1301
|
-
globalThis.${name} = function(...args) {
|
|
1302
|
-
// Start the iterator and get the iteratorId
|
|
1303
|
-
const startCallbackId = globalThis.__customFnCallbackIds["${name}:start"];
|
|
1304
|
-
const argsJson = JSON.stringify(__marshalForHost(args));
|
|
1305
|
-
const startResultJson = __customFn_invoke.applySyncPromise(
|
|
1306
|
-
undefined,
|
|
1307
|
-
[startCallbackId, argsJson]
|
|
1308
|
-
);
|
|
1309
|
-
const startResult = JSON.parse(startResultJson);
|
|
1310
|
-
if (!startResult.ok) {
|
|
1311
|
-
const error = new Error(startResult.error.message);
|
|
1312
|
-
error.name = startResult.error.name;
|
|
1313
|
-
throw error;
|
|
1314
|
-
}
|
|
1315
|
-
const iteratorId = __unmarshalFromHost(startResult.value).iteratorId;
|
|
1316
|
-
|
|
1317
|
-
return {
|
|
1318
|
-
[Symbol.asyncIterator]() { return this; },
|
|
1319
|
-
async next() {
|
|
1320
|
-
const nextCallbackId = globalThis.__customFnCallbackIds["${name}:next"];
|
|
1321
|
-
const argsJson = JSON.stringify(__marshalForHost([iteratorId]));
|
|
1322
|
-
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1323
|
-
undefined,
|
|
1324
|
-
[nextCallbackId, argsJson]
|
|
1325
|
-
);
|
|
1326
|
-
const result = JSON.parse(resultJson);
|
|
1327
|
-
if (!result.ok) {
|
|
1328
|
-
const error = new Error(result.error.message);
|
|
1329
|
-
error.name = result.error.name;
|
|
1330
|
-
throw error;
|
|
1331
|
-
}
|
|
1332
|
-
const val = __unmarshalFromHost(result.value);
|
|
1333
|
-
return { done: val.done, value: val.value };
|
|
1334
|
-
},
|
|
1335
|
-
async return(v) {
|
|
1336
|
-
const returnCallbackId = globalThis.__customFnCallbackIds["${name}:return"];
|
|
1337
|
-
const argsJson = JSON.stringify(__marshalForHost([iteratorId, v]));
|
|
1338
|
-
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1339
|
-
undefined,
|
|
1340
|
-
[returnCallbackId, argsJson]
|
|
1341
|
-
);
|
|
1342
|
-
const result = JSON.parse(resultJson);
|
|
1343
|
-
return { done: true, value: result.ok ? __unmarshalFromHost(result.value) : undefined };
|
|
1344
|
-
},
|
|
1345
|
-
async throw(e) {
|
|
1346
|
-
const throwCallbackId = globalThis.__customFnCallbackIds["${name}:throw"];
|
|
1347
|
-
const argsJson = JSON.stringify(__marshalForHost([iteratorId, { message: e?.message, name: e?.name }]));
|
|
1348
|
-
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1349
|
-
undefined,
|
|
1350
|
-
[throwCallbackId, argsJson]
|
|
1351
|
-
);
|
|
1352
|
-
const result = JSON.parse(resultJson);
|
|
1353
|
-
if (!result.ok) {
|
|
1354
|
-
const error = new Error(result.error.message);
|
|
1355
|
-
error.name = result.error.name;
|
|
1356
|
-
throw error;
|
|
1357
|
-
}
|
|
1358
|
-
const val = __unmarshalFromHost(result.value);
|
|
1359
|
-
return { done: val.done, value: val.value };
|
|
1360
|
-
}
|
|
1361
|
-
};
|
|
1362
|
-
};
|
|
1363
|
-
`);
|
|
1364
|
-
} else if (registration.type === "async") {
|
|
1365
|
-
context.evalSync(`
|
|
1366
|
-
globalThis.${name} = async function(...args) {
|
|
1367
|
-
const callbackId = globalThis.__customFnCallbackIds["${name}"];
|
|
1368
|
-
const argsJson = JSON.stringify(__marshalForHost(args));
|
|
1369
|
-
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1370
|
-
undefined,
|
|
1371
|
-
[callbackId, argsJson]
|
|
1372
|
-
);
|
|
1373
|
-
const result = JSON.parse(resultJson);
|
|
1374
|
-
if (result.ok) {
|
|
1375
|
-
return __unmarshalFromHost(result.value);
|
|
1376
|
-
} else {
|
|
1377
|
-
const error = new Error(result.error.message);
|
|
1378
|
-
error.name = result.error.name;
|
|
1379
|
-
throw error;
|
|
1380
|
-
}
|
|
1381
|
-
};
|
|
1382
|
-
`);
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
function createModuleResolver(instance, connection) {
|
|
1387
|
-
return async (specifier, referrer) => {
|
|
1388
|
-
const cached = instance.moduleCache?.get(specifier);
|
|
1389
|
-
if (cached)
|
|
1390
|
-
return cached;
|
|
1391
|
-
if (!instance.moduleLoaderCallbackId) {
|
|
1392
|
-
throw new Error(`Module not found: ${specifier}`);
|
|
1393
|
-
}
|
|
1394
|
-
const importerPath = instance.moduleToFilename?.get(referrer) ?? "<unknown>";
|
|
1395
|
-
const importerResolveDir = path.posix.dirname(importerPath);
|
|
1396
|
-
const result = await invokeClientCallback(connection, instance.moduleLoaderCallbackId, [specifier, { path: importerPath, resolveDir: importerResolveDir }]);
|
|
1397
|
-
const { code, resolveDir } = result;
|
|
1398
|
-
const hash = contentHash(code);
|
|
1399
|
-
const cacheKey = `${specifier}:${hash}`;
|
|
1400
|
-
const hashCached = instance.moduleCache?.get(cacheKey);
|
|
1401
|
-
if (hashCached)
|
|
1402
|
-
return hashCached;
|
|
1403
|
-
const transformed = await transformModuleCode(code, specifier);
|
|
1404
|
-
if (transformed.sourceMap) {
|
|
1405
|
-
if (!instance.sourceMaps) {
|
|
1406
|
-
instance.sourceMaps = new Map;
|
|
1407
|
-
}
|
|
1408
|
-
instance.sourceMaps.set(specifier, transformed.sourceMap);
|
|
1409
|
-
}
|
|
1410
|
-
const mod = await instance.runtime.isolate.compileModule(transformed.code, {
|
|
1411
|
-
filename: specifier
|
|
1412
|
-
});
|
|
1413
|
-
const resolvedPath = path.posix.join(resolveDir, path.posix.basename(specifier));
|
|
1414
|
-
instance.moduleToFilename?.set(mod, resolvedPath);
|
|
1415
|
-
const resolver = createModuleResolver(instance, connection);
|
|
1416
|
-
await mod.instantiate(instance.runtime.context, resolver);
|
|
1417
|
-
instance.moduleCache?.set(specifier, mod);
|
|
1418
|
-
instance.moduleCache?.set(cacheKey, mod);
|
|
1419
|
-
return mod;
|
|
1420
|
-
};
|
|
1421
|
-
}
|
|
1422
|
-
async function serializeRequest(request) {
|
|
1423
|
-
const headers = [];
|
|
1424
|
-
request.headers.forEach((value, key) => {
|
|
1425
|
-
headers.push([key, value]);
|
|
1426
|
-
});
|
|
1427
|
-
let body = null;
|
|
1428
|
-
if (request.body) {
|
|
1429
|
-
body = new Uint8Array(await request.arrayBuffer());
|
|
1430
|
-
}
|
|
1431
|
-
return {
|
|
1432
|
-
method: request.method,
|
|
1433
|
-
url: request.url,
|
|
1434
|
-
headers,
|
|
1435
|
-
body
|
|
1436
|
-
};
|
|
1437
|
-
}
|
|
1438
1187
|
function deserializeResponse(data) {
|
|
1439
1188
|
return new Response(data.body, {
|
|
1440
1189
|
status: data.status,
|
|
@@ -1701,28 +1450,11 @@ async function handleRunTests(message, connection, state) {
|
|
|
1701
1450
|
sendError(connection.socket, message.requestId, ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
1702
1451
|
return;
|
|
1703
1452
|
}
|
|
1704
|
-
if (!instance.testEnvironmentEnabled) {
|
|
1705
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
1706
|
-
return;
|
|
1707
|
-
}
|
|
1708
1453
|
instance.lastActivity = Date.now();
|
|
1709
1454
|
try {
|
|
1710
1455
|
const timeout = message.timeout ?? 30000;
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
timeoutId = setTimeout(() => reject(new Error("Test timeout")), timeout);
|
|
1714
|
-
});
|
|
1715
|
-
try {
|
|
1716
|
-
const results = await Promise.race([
|
|
1717
|
-
runTestsInContext(instance.runtime.context),
|
|
1718
|
-
timeoutPromise
|
|
1719
|
-
]);
|
|
1720
|
-
sendOk(connection.socket, message.requestId, results);
|
|
1721
|
-
} finally {
|
|
1722
|
-
if (timeoutId) {
|
|
1723
|
-
clearTimeout(timeoutId);
|
|
1724
|
-
}
|
|
1725
|
-
}
|
|
1456
|
+
const results = await instance.runtime.testEnvironment.runTests(timeout);
|
|
1457
|
+
sendOk(connection.socket, message.requestId, results);
|
|
1726
1458
|
} catch (err) {
|
|
1727
1459
|
const error = err;
|
|
1728
1460
|
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
@@ -1734,13 +1466,9 @@ async function handleResetTestEnv(message, connection, state) {
|
|
|
1734
1466
|
sendError(connection.socket, message.requestId, ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
1735
1467
|
return;
|
|
1736
1468
|
}
|
|
1737
|
-
if (!instance.testEnvironmentEnabled) {
|
|
1738
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
1739
|
-
return;
|
|
1740
|
-
}
|
|
1741
1469
|
instance.lastActivity = Date.now();
|
|
1742
1470
|
try {
|
|
1743
|
-
|
|
1471
|
+
instance.runtime.testEnvironment.reset();
|
|
1744
1472
|
sendOk(connection.socket, message.requestId);
|
|
1745
1473
|
} catch (err) {
|
|
1746
1474
|
const error = err;
|
|
@@ -1753,13 +1481,9 @@ async function handleHasTests(message, connection, state) {
|
|
|
1753
1481
|
sendError(connection.socket, message.requestId, ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
1754
1482
|
return;
|
|
1755
1483
|
}
|
|
1756
|
-
if (!instance.testEnvironmentEnabled) {
|
|
1757
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
1758
|
-
return;
|
|
1759
|
-
}
|
|
1760
1484
|
instance.lastActivity = Date.now();
|
|
1761
1485
|
try {
|
|
1762
|
-
const result =
|
|
1486
|
+
const result = instance.runtime.testEnvironment.hasTests();
|
|
1763
1487
|
sendOk(connection.socket, message.requestId, result);
|
|
1764
1488
|
} catch (err) {
|
|
1765
1489
|
const error = err;
|
|
@@ -1772,42 +1496,24 @@ async function handleGetTestCount(message, connection, state) {
|
|
|
1772
1496
|
sendError(connection.socket, message.requestId, ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
1773
1497
|
return;
|
|
1774
1498
|
}
|
|
1775
|
-
if (!instance.testEnvironmentEnabled) {
|
|
1776
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
1777
|
-
return;
|
|
1778
|
-
}
|
|
1779
1499
|
instance.lastActivity = Date.now();
|
|
1780
1500
|
try {
|
|
1781
|
-
const result =
|
|
1501
|
+
const result = instance.runtime.testEnvironment.getTestCount();
|
|
1782
1502
|
sendOk(connection.socket, message.requestId, result);
|
|
1783
1503
|
} catch (err) {
|
|
1784
1504
|
const error = err;
|
|
1785
1505
|
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1786
1506
|
}
|
|
1787
1507
|
}
|
|
1788
|
-
async function handleRunPlaywrightTests(message, connection, _state) {
|
|
1789
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, "playwright.runTests() has been removed. Use testEnvironment.runTests() instead.");
|
|
1790
|
-
}
|
|
1791
|
-
async function handleResetPlaywrightTests(message, connection, _state) {
|
|
1792
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, "playwright.reset() has been removed. Use testEnvironment.reset() instead.");
|
|
1793
|
-
}
|
|
1794
1508
|
async function handleGetCollectedData(message, connection, state) {
|
|
1795
1509
|
const instance = state.isolates.get(message.isolateId);
|
|
1796
1510
|
if (!instance) {
|
|
1797
1511
|
sendError(connection.socket, message.requestId, ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
1798
1512
|
return;
|
|
1799
1513
|
}
|
|
1800
|
-
if (!instance.playwrightHandle) {
|
|
1801
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, "Playwright not configured. Provide playwright.page in createRuntime options.");
|
|
1802
|
-
return;
|
|
1803
|
-
}
|
|
1804
1514
|
instance.lastActivity = Date.now();
|
|
1805
1515
|
try {
|
|
1806
|
-
const data =
|
|
1807
|
-
browserConsoleLogs: instance.playwrightHandle.getBrowserConsoleLogs(),
|
|
1808
|
-
networkRequests: instance.playwrightHandle.getNetworkRequests(),
|
|
1809
|
-
networkResponses: instance.playwrightHandle.getNetworkResponses()
|
|
1810
|
-
};
|
|
1516
|
+
const data = instance.runtime.playwright.getCollectedData();
|
|
1811
1517
|
sendOk(connection.socket, message.requestId, data);
|
|
1812
1518
|
} catch (err) {
|
|
1813
1519
|
const error = err;
|
|
@@ -1820,13 +1526,9 @@ async function handleClearCollectedData(message, connection, state) {
|
|
|
1820
1526
|
sendError(connection.socket, message.requestId, ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
1821
1527
|
return;
|
|
1822
1528
|
}
|
|
1823
|
-
if (!instance.playwrightHandle) {
|
|
1824
|
-
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, "Playwright not configured. Provide playwright.page in createRuntime options.");
|
|
1825
|
-
return;
|
|
1826
|
-
}
|
|
1827
1529
|
instance.lastActivity = Date.now();
|
|
1828
1530
|
try {
|
|
1829
|
-
instance.
|
|
1531
|
+
instance.runtime.playwright.clearCollectedData();
|
|
1830
1532
|
sendOk(connection.socket, message.requestId);
|
|
1831
1533
|
} catch (err) {
|
|
1832
1534
|
const error = err;
|
|
@@ -1837,4 +1539,4 @@ export {
|
|
|
1837
1539
|
handleConnection
|
|
1838
1540
|
};
|
|
1839
1541
|
|
|
1840
|
-
//# debugId=
|
|
1542
|
+
//# debugId=C809F161C354C84D64756E2164756E21
|