@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.
@@ -1,20 +1,7 @@
1
- var __create = Object.create;
2
- var __getProtoOf = Object.getPrototypeOf;
3
1
  var __defProp = Object.defineProperty;
4
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
5
  var __moduleCache = /* @__PURE__ */ new WeakMap;
19
6
  var __toCommonJS = (from) => {
20
7
  var entry = __moduleCache.get(from), desc;
@@ -46,14 +33,9 @@ __export(exports_connection, {
46
33
  });
47
34
  module.exports = __toCommonJS(exports_connection);
48
35
  var import_node_crypto = require("node:crypto");
49
- var import_node_path = __toESM(require("node:path"));
50
- var import_isolated_vm = __toESM(require("isolated-vm"));
51
36
  var import_isolate_protocol = require("@ricsam/isolate-protocol");
52
37
  var import_callback_fs_handler = require("./callback-fs-handler.cjs");
53
- var import_isolate_test_environment = require("@ricsam/isolate-test-environment");
54
- var import_isolate_playwright = require("@ricsam/isolate-playwright");
55
38
  var import_isolate_runtime = require("@ricsam/isolate-runtime");
56
- var import_isolate_transform = require("@ricsam/isolate-transform");
57
39
  function handleConnection(socket, state) {
58
40
  const connection = {
59
41
  socket,
@@ -88,12 +70,7 @@ function handleConnection(socket, state) {
88
70
  if (instance.namespaceId != null && !instance.isDisposed) {
89
71
  softDeleteRuntime(instance, state);
90
72
  } else if (!instance.isDisposed) {
91
- try {
92
- if (instance.playwrightHandle) {
93
- instance.playwrightHandle.dispose();
94
- }
95
- instance.runtime.dispose();
96
- } catch {}
73
+ instance.runtime.dispose().catch(() => {});
97
74
  state.isolates.delete(isolateId);
98
75
  }
99
76
  }
@@ -199,12 +176,6 @@ async function handleMessage(message, connection, state) {
199
176
  case import_isolate_protocol.MessageType.GET_TEST_COUNT:
200
177
  await handleGetTestCount(message, connection, state);
201
178
  break;
202
- case import_isolate_protocol.MessageType.RUN_PLAYWRIGHT_TESTS:
203
- await handleRunPlaywrightTests(message, connection, state);
204
- break;
205
- case import_isolate_protocol.MessageType.RESET_PLAYWRIGHT_TESTS:
206
- await handleResetPlaywrightTests(message, connection, state);
207
- break;
208
179
  case import_isolate_protocol.MessageType.GET_COLLECTED_DATA:
209
180
  await handleGetCollectedData(message, connection, state);
210
181
  break;
@@ -235,6 +206,18 @@ async function handleMessage(message, connection, state) {
235
206
  case import_isolate_protocol.MessageType.CALLBACK_STREAM_END:
236
207
  handleCallbackStreamEnd(message, connection);
237
208
  break;
209
+ case import_isolate_protocol.MessageType.CLIENT_WS_OPENED:
210
+ handleClientWsOpened(message, connection, state);
211
+ break;
212
+ case import_isolate_protocol.MessageType.CLIENT_WS_MESSAGE:
213
+ handleClientWsMessage(message, connection, state);
214
+ break;
215
+ case import_isolate_protocol.MessageType.CLIENT_WS_CLOSED:
216
+ handleClientWsClosed(message, connection, state);
217
+ break;
218
+ case import_isolate_protocol.MessageType.CLIENT_WS_ERROR:
219
+ handleClientWsError(message, connection, state);
220
+ break;
238
221
  default:
239
222
  sendError(connection.socket, message.requestId ?? 0, import_isolate_protocol.ErrorCode.UNKNOWN_MESSAGE_TYPE, `Unknown message type: ${message.type}`);
240
223
  }
@@ -243,13 +226,17 @@ function softDeleteRuntime(instance, state) {
243
226
  instance.isDisposed = true;
244
227
  instance.disposedAt = Date.now();
245
228
  instance.ownerConnection = null;
229
+ if (instance.callbackContext) {
230
+ instance.callbackContext.connection = null;
231
+ }
246
232
  instance.callbacks.clear();
247
233
  instance.runtime.timers.clearAll();
248
234
  instance.runtime.console.reset();
249
- instance.pendingCallbacks.length = 0;
235
+ instance.runtime.pendingCallbacks.length = 0;
250
236
  instance.returnedCallbacks?.clear();
251
237
  instance.returnedPromises?.clear();
252
238
  instance.returnedIterators?.clear();
239
+ instance.runtime.clearModuleCache();
253
240
  }
254
241
  function reuseNamespacedRuntime(instance, connection, message, state) {
255
242
  instance.ownerConnection = connection.socket;
@@ -258,11 +245,19 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
258
245
  instance.lastActivity = Date.now();
259
246
  connection.isolates.add(instance.isolateId);
260
247
  const callbacks = message.options.callbacks;
248
+ const testEnvOptions = message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment : undefined;
261
249
  if (instance.callbackContext) {
262
250
  instance.callbackContext.connection = connection;
263
251
  instance.callbackContext.consoleOnEntry = callbacks?.console?.onEntry?.callbackId;
264
252
  instance.callbackContext.fetch = callbacks?.fetch?.callbackId;
265
253
  instance.callbackContext.moduleLoader = callbacks?.moduleLoader?.callbackId;
254
+ instance.callbackContext.testEnvironmentOnEvent = testEnvOptions?.callbacks?.onEvent?.callbackId;
255
+ instance.callbackContext.playwright = {
256
+ handlerCallbackId: callbacks?.playwright?.handlerCallbackId,
257
+ onBrowserConsoleLogCallbackId: callbacks?.playwright?.onBrowserConsoleLogCallbackId,
258
+ onNetworkRequestCallbackId: callbacks?.playwright?.onNetworkRequestCallbackId,
259
+ onNetworkResponseCallbackId: callbacks?.playwright?.onNetworkResponseCallbackId
260
+ };
266
261
  instance.callbackContext.fs = {
267
262
  readFile: callbacks?.fs?.readFile?.callbackId,
268
263
  writeFile: callbacks?.fs?.writeFile?.callbackId,
@@ -299,7 +294,6 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
299
294
  }
300
295
  }
301
296
  if (callbacks?.moduleLoader) {
302
- instance.moduleLoaderCallbackId = callbacks.moduleLoader.callbackId;
303
297
  instance.callbacks.set(callbacks.moduleLoader.callbackId, callbacks.moduleLoader);
304
298
  }
305
299
  if (callbacks?.custom) {
@@ -309,23 +303,46 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
309
303
  }
310
304
  }
311
305
  }
306
+ if (testEnvOptions?.callbacks?.onEvent) {
307
+ instance.callbacks.set(testEnvOptions.callbacks.onEvent.callbackId, {
308
+ ...testEnvOptions.callbacks.onEvent,
309
+ name: "testEnvironment.onEvent"
310
+ });
311
+ }
312
+ if (callbacks?.playwright) {
313
+ instance.callbacks.set(callbacks.playwright.handlerCallbackId, {
314
+ callbackId: callbacks.playwright.handlerCallbackId,
315
+ name: "playwright.handler",
316
+ type: "async"
317
+ });
318
+ if (callbacks.playwright.onBrowserConsoleLogCallbackId !== undefined) {
319
+ instance.callbacks.set(callbacks.playwright.onBrowserConsoleLogCallbackId, {
320
+ callbackId: callbacks.playwright.onBrowserConsoleLogCallbackId,
321
+ name: "playwright.onBrowserConsoleLog",
322
+ type: "sync"
323
+ });
324
+ }
325
+ if (callbacks.playwright.onNetworkRequestCallbackId !== undefined) {
326
+ instance.callbacks.set(callbacks.playwright.onNetworkRequestCallbackId, {
327
+ callbackId: callbacks.playwright.onNetworkRequestCallbackId,
328
+ name: "playwright.onNetworkRequest",
329
+ type: "sync"
330
+ });
331
+ }
332
+ if (callbacks.playwright.onNetworkResponseCallbackId !== undefined) {
333
+ instance.callbacks.set(callbacks.playwright.onNetworkResponseCallbackId, {
334
+ callbackId: callbacks.playwright.onNetworkResponseCallbackId,
335
+ name: "playwright.onNetworkResponse",
336
+ type: "sync"
337
+ });
338
+ }
339
+ }
312
340
  instance.returnedCallbacks = new Map;
313
341
  instance.returnedPromises = new Map;
314
342
  instance.returnedIterators = new Map;
315
343
  instance.nextLocalCallbackId = 1e6;
316
- if (callbacks?.custom) {
317
- const newCallbackIdMap = {};
318
- for (const [name, reg] of Object.entries(callbacks.custom)) {
319
- if (reg) {
320
- newCallbackIdMap[name] = reg.callbackId;
321
- }
322
- }
323
- try {
324
- instance.runtime.context.global.setSync("__customFnCallbackIds", new import_isolated_vm.default.ExternalCopy(newCallbackIdMap).copyInto());
325
- } catch {}
326
- }
327
344
  }
328
- function evictOldestDisposedRuntime(state) {
345
+ async function evictOldestDisposedRuntime(state) {
329
346
  let oldest = null;
330
347
  let oldestTime = Infinity;
331
348
  for (const [, instance] of state.isolates) {
@@ -338,10 +355,7 @@ function evictOldestDisposedRuntime(state) {
338
355
  }
339
356
  if (oldest) {
340
357
  try {
341
- if (oldest.playwrightHandle) {
342
- oldest.playwrightHandle.dispose();
343
- }
344
- oldest.runtime.dispose();
358
+ await oldest.runtime.dispose();
345
359
  } catch {}
346
360
  state.isolates.delete(oldest.isolateId);
347
361
  if (oldest.namespaceId != null) {
@@ -376,7 +390,7 @@ async function handleCreateRuntime(message, connection, state) {
376
390
  }
377
391
  }
378
392
  if (state.isolates.size >= state.options.maxIsolates) {
379
- if (!evictOldestDisposedRuntime(state)) {
393
+ if (!await evictOldestDisposedRuntime(state)) {
380
394
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_MEMORY_LIMIT, `Maximum isolates (${state.options.maxIsolates}) reached`);
381
395
  return;
382
396
  }
@@ -388,12 +402,18 @@ async function handleCreateRuntime(message, connection, state) {
388
402
  const fsCallbacks = message.options.callbacks?.fs;
389
403
  const moduleLoaderCallback = message.options.callbacks?.moduleLoader;
390
404
  const customCallbacks = message.options.callbacks?.custom;
391
- const pendingCallbacks = [];
392
405
  const callbackContext = {
393
406
  connection,
394
407
  consoleOnEntry: consoleCallbacks?.onEntry?.callbackId,
395
408
  fetch: fetchCallback?.callbackId,
396
409
  moduleLoader: moduleLoaderCallback?.callbackId,
410
+ testEnvironmentOnEvent: message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment.callbacks?.onEvent?.callbackId : undefined,
411
+ playwright: {
412
+ handlerCallbackId: message.options.callbacks?.playwright?.handlerCallbackId,
413
+ onBrowserConsoleLogCallbackId: message.options.callbacks?.playwright?.onBrowserConsoleLogCallbackId,
414
+ onNetworkRequestCallbackId: message.options.callbacks?.playwright?.onNetworkRequestCallbackId,
415
+ onNetworkResponseCallbackId: message.options.callbacks?.playwright?.onNetworkResponseCallbackId
416
+ },
397
417
  fs: {
398
418
  readFile: fsCallbacks?.readFile?.callbackId,
399
419
  writeFile: fsCallbacks?.writeFile?.callbackId,
@@ -405,7 +425,248 @@ async function handleCreateRuntime(message, connection, state) {
405
425
  },
406
426
  custom: new Map(customCallbacks ? Object.entries(customCallbacks).map(([name, reg]) => [name, reg.callbackId]) : [])
407
427
  };
408
- const runtime = await import_isolate_runtime.createInternalRuntime({
428
+ const instance = {
429
+ isolateId,
430
+ runtime: null,
431
+ ownerConnection: connection.socket,
432
+ callbacks: new Map,
433
+ createdAt: Date.now(),
434
+ lastActivity: Date.now(),
435
+ returnedCallbacks: new Map,
436
+ returnedPromises: new Map,
437
+ returnedIterators: new Map,
438
+ nextLocalCallbackId: 1e6,
439
+ namespaceId,
440
+ isDisposed: false,
441
+ callbackContext
442
+ };
443
+ let bridgedCustomFunctions;
444
+ let customFnMarshalOptions;
445
+ if (customCallbacks) {
446
+ const createMarshalContext = () => ({
447
+ registerCallback: (fn) => {
448
+ const callbackId = instance.nextLocalCallbackId++;
449
+ instance.returnedCallbacks.set(callbackId, fn);
450
+ return callbackId;
451
+ },
452
+ registerPromise: (promise) => {
453
+ const promiseId = instance.nextLocalCallbackId++;
454
+ instance.returnedPromises.set(promiseId, promise);
455
+ return promiseId;
456
+ },
457
+ registerIterator: (iterator) => {
458
+ const iteratorId = instance.nextLocalCallbackId++;
459
+ instance.returnedIterators.set(iteratorId, iterator);
460
+ return iteratorId;
461
+ }
462
+ });
463
+ const isPromiseRef = (value) => typeof value === "object" && value !== null && value.__type === "PromiseRef";
464
+ const isAsyncIteratorRef = (value) => typeof value === "object" && value !== null && value.__type === "AsyncIteratorRef";
465
+ const addCallbackIdsToRefs = (value) => {
466
+ if (value === null || typeof value !== "object")
467
+ return value;
468
+ if (isPromiseRef(value)) {
469
+ if ("__resolveCallbackId" in value)
470
+ return value;
471
+ const resolveCallbackId = instance.nextLocalCallbackId++;
472
+ instance.returnedCallbacks.set(resolveCallbackId, async (promiseId) => {
473
+ const promise = instance.returnedPromises.get(promiseId);
474
+ if (!promise)
475
+ throw new Error(`Promise ${promiseId} not found`);
476
+ const result2 = await promise;
477
+ instance.returnedPromises.delete(promiseId);
478
+ const ctx = createMarshalContext();
479
+ const marshalled = await import_isolate_protocol.marshalValue(result2, ctx);
480
+ return addCallbackIdsToRefs(marshalled);
481
+ });
482
+ return { ...value, __resolveCallbackId: resolveCallbackId };
483
+ }
484
+ if (isAsyncIteratorRef(value)) {
485
+ if ("__nextCallbackId" in value)
486
+ return value;
487
+ const nextCallbackId = instance.nextLocalCallbackId++;
488
+ instance.returnedCallbacks.set(nextCallbackId, async (iteratorId) => {
489
+ const iterator = instance.returnedIterators.get(iteratorId);
490
+ if (!iterator)
491
+ throw new Error(`Iterator ${iteratorId} not found`);
492
+ const result2 = await iterator.next();
493
+ if (result2.done)
494
+ instance.returnedIterators.delete(iteratorId);
495
+ const ctx = createMarshalContext();
496
+ const marshalledValue = await import_isolate_protocol.marshalValue(result2.value, ctx);
497
+ return { done: result2.done, value: addCallbackIdsToRefs(marshalledValue) };
498
+ });
499
+ const returnCallbackId = instance.nextLocalCallbackId++;
500
+ instance.returnedCallbacks.set(returnCallbackId, async (iteratorId, returnValue) => {
501
+ const iterator = instance.returnedIterators.get(iteratorId);
502
+ instance.returnedIterators.delete(iteratorId);
503
+ if (!iterator || !iterator.return)
504
+ return { done: true, value: undefined };
505
+ const result2 = await iterator.return(returnValue);
506
+ const ctx = createMarshalContext();
507
+ const marshalledValue = await import_isolate_protocol.marshalValue(result2.value, ctx);
508
+ return { done: true, value: addCallbackIdsToRefs(marshalledValue) };
509
+ });
510
+ return { ...value, __nextCallbackId: nextCallbackId, __returnCallbackId: returnCallbackId };
511
+ }
512
+ if (Array.isArray(value))
513
+ return value.map((item) => addCallbackIdsToRefs(item));
514
+ const result = {};
515
+ for (const key of Object.keys(value)) {
516
+ result[key] = addCallbackIdsToRefs(value[key]);
517
+ }
518
+ return result;
519
+ };
520
+ const LOCAL_CALLBACK_THRESHOLD = 1e6;
521
+ const invokeCallback = async (callbackId, args) => {
522
+ if (callbackId >= LOCAL_CALLBACK_THRESHOLD) {
523
+ const callback = instance.returnedCallbacks.get(callbackId);
524
+ if (!callback) {
525
+ throw new Error(`Local callback ${callbackId} not found`);
526
+ }
527
+ return await callback(...args);
528
+ } else {
529
+ const conn = callbackContext.connection;
530
+ if (!conn) {
531
+ throw new Error(`No connection available for callback ${callbackId}`);
532
+ }
533
+ return invokeClientCallback(conn, callbackId, args);
534
+ }
535
+ };
536
+ customFnMarshalOptions = { createMarshalContext, addCallbackIdsToRefs, invokeCallback };
537
+ bridgedCustomFunctions = {};
538
+ for (const [name, registration] of Object.entries(customCallbacks)) {
539
+ if (name.includes(":"))
540
+ continue;
541
+ const callbackContext_ = callbackContext;
542
+ if (registration.type === "asyncIterator") {
543
+ bridgedCustomFunctions[name] = {
544
+ type: "asyncIterator",
545
+ fn: (...args) => {
546
+ const startCallbackId = callbackContext_.custom.get(`${name}:start`);
547
+ const nextCallbackId = callbackContext_.custom.get(`${name}:next`);
548
+ const returnCallbackId = callbackContext_.custom.get(`${name}:return`);
549
+ async function* bridgedIterator() {
550
+ const conn = callbackContext_.connection;
551
+ if (!conn || startCallbackId === undefined) {
552
+ throw new Error(`AsyncIterator callback '${name}' not available`);
553
+ }
554
+ const startResult = await invokeClientCallback(conn, startCallbackId, args);
555
+ const iteratorId = startResult.iteratorId;
556
+ try {
557
+ while (true) {
558
+ const nextConn = callbackContext_.connection;
559
+ if (!nextConn || nextCallbackId === undefined) {
560
+ throw new Error(`AsyncIterator callback '${name}' not available`);
561
+ }
562
+ const nextResult = await invokeClientCallback(nextConn, nextCallbackId, [iteratorId]);
563
+ if (nextResult.done)
564
+ return nextResult.value;
565
+ yield nextResult.value;
566
+ }
567
+ } finally {
568
+ const retConn = callbackContext_.connection;
569
+ if (retConn && returnCallbackId !== undefined) {
570
+ await invokeClientCallback(retConn, returnCallbackId, [iteratorId]).catch(() => {});
571
+ }
572
+ }
573
+ }
574
+ return bridgedIterator();
575
+ }
576
+ };
577
+ } else {
578
+ bridgedCustomFunctions[name] = {
579
+ type: registration.type,
580
+ fn: async (...args) => {
581
+ const conn = callbackContext_.connection;
582
+ const cbId = callbackContext_.custom.get(name);
583
+ if (!conn || cbId === undefined) {
584
+ throw new Error(`Custom function callback '${name}' not available`);
585
+ }
586
+ return invokeClientCallback(conn, cbId, args);
587
+ }
588
+ };
589
+ }
590
+ }
591
+ }
592
+ let moduleLoader;
593
+ if (moduleLoaderCallback) {
594
+ moduleLoader = async (specifier, importer) => {
595
+ const conn = callbackContext.connection;
596
+ const cbId = callbackContext.moduleLoader;
597
+ if (!conn || cbId === undefined) {
598
+ throw new Error("Module loader callback not available");
599
+ }
600
+ return invokeClientCallback(conn, cbId, [specifier, importer]);
601
+ };
602
+ }
603
+ let testEnvironment;
604
+ if (message.options.testEnvironment) {
605
+ const testEnvOption = message.options.testEnvironment;
606
+ const testEnvOptions = typeof testEnvOption === "object" ? testEnvOption : undefined;
607
+ testEnvironment = {
608
+ onEvent: testEnvOptions?.callbacks?.onEvent ? (event) => {
609
+ const conn = callbackContext.connection;
610
+ const callbackId = callbackContext.testEnvironmentOnEvent;
611
+ if (!conn || callbackId === undefined) {
612
+ return;
613
+ }
614
+ const promise = invokeClientCallback(conn, callbackId, [JSON.stringify(event)]).catch(() => {});
615
+ instance.runtime?.pendingCallbacks?.push(promise);
616
+ } : undefined,
617
+ testTimeout: testEnvOptions?.testTimeout
618
+ };
619
+ if (testEnvOptions?.callbacks?.onEvent) {
620
+ instance.callbacks.set(testEnvOptions.callbacks.onEvent.callbackId, {
621
+ ...testEnvOptions.callbacks.onEvent,
622
+ name: "testEnvironment.onEvent"
623
+ });
624
+ }
625
+ }
626
+ let playwrightOptions;
627
+ const playwrightCallbacks = message.options.callbacks?.playwright;
628
+ if (playwrightCallbacks) {
629
+ playwrightOptions = {
630
+ handler: async (op) => {
631
+ const conn = callbackContext.connection;
632
+ const callbackId = callbackContext.playwright.handlerCallbackId;
633
+ if (!conn || callbackId === undefined) {
634
+ return {
635
+ ok: false,
636
+ error: {
637
+ name: "Error",
638
+ message: "Playwright handler callback not available"
639
+ }
640
+ };
641
+ }
642
+ try {
643
+ const resultJson = await invokeClientCallback(conn, callbackId, [JSON.stringify(op)], 60000);
644
+ return JSON.parse(resultJson);
645
+ } catch (err) {
646
+ const error = err;
647
+ return { ok: false, error: { name: error.name, message: error.message } };
648
+ }
649
+ },
650
+ console: playwrightCallbacks.console,
651
+ onEvent: (event) => {
652
+ const conn = callbackContext.connection;
653
+ if (!conn) {
654
+ return;
655
+ }
656
+ if (event.type === "browserConsoleLog" && callbackContext.playwright.onBrowserConsoleLogCallbackId !== undefined) {
657
+ const promise = invokeClientCallback(conn, callbackContext.playwright.onBrowserConsoleLogCallbackId, [{ level: event.level, stdout: event.stdout, timestamp: event.timestamp }]).catch(() => {});
658
+ instance.runtime?.pendingCallbacks?.push(promise);
659
+ } else if (event.type === "networkRequest" && callbackContext.playwright.onNetworkRequestCallbackId !== undefined) {
660
+ const promise = invokeClientCallback(conn, callbackContext.playwright.onNetworkRequestCallbackId, [event]).catch(() => {});
661
+ instance.runtime?.pendingCallbacks?.push(promise);
662
+ } else if (event.type === "networkResponse" && callbackContext.playwright.onNetworkResponseCallbackId !== undefined) {
663
+ const promise = invokeClientCallback(conn, callbackContext.playwright.onNetworkResponseCallbackId, [event]).catch(() => {});
664
+ instance.runtime?.pendingCallbacks?.push(promise);
665
+ }
666
+ }
667
+ };
668
+ }
669
+ const runtime = await import_isolate_runtime.createRuntime({
409
670
  memoryLimitMB: message.options.memoryLimitMB ?? state.options.defaultMemoryLimitMB,
410
671
  cwd: message.options.cwd,
411
672
  console: {
@@ -415,28 +676,31 @@ async function handleCreateRuntime(message, connection, state) {
415
676
  if (!conn || callbackId === undefined)
416
677
  return;
417
678
  const promise = invokeClientCallback(conn, callbackId, [entry]).catch(() => {});
418
- pendingCallbacks.push(promise);
679
+ runtime.pendingCallbacks.push(promise);
419
680
  }
420
681
  },
421
- fetch: {
422
- onFetch: async (request) => {
423
- const conn = callbackContext.connection;
424
- const callbackId = callbackContext.fetch;
425
- if (!conn || callbackId === undefined) {
426
- throw new Error("Fetch callback not available");
427
- }
428
- const serialized = await serializeRequest(request);
429
- const result = await invokeClientCallback(conn, callbackId, [serialized], 60000);
430
- if (result && typeof result === "object" && result.__streamingResponse) {
431
- const response = result.response;
432
- response.__isCallbackStream = true;
433
- return response;
434
- }
435
- return deserializeResponse(result);
682
+ fetch: async (url, init) => {
683
+ const conn = callbackContext.connection;
684
+ const callbackId = callbackContext.fetch;
685
+ if (!conn || callbackId === undefined) {
686
+ throw new Error("Fetch callback not available");
687
+ }
688
+ const serialized = {
689
+ url,
690
+ method: init.method,
691
+ headers: init.headers,
692
+ body: init.rawBody
693
+ };
694
+ const result = await invokeClientCallback(conn, callbackId, [serialized], 60000);
695
+ if (result && typeof result === "object" && result.__streamingResponse) {
696
+ const response = result.response;
697
+ response.__isCallbackStream = true;
698
+ return response;
436
699
  }
700
+ return deserializeResponse(result);
437
701
  },
438
702
  fs: {
439
- getDirectory: async (path2) => {
703
+ getDirectory: async (dirPath) => {
440
704
  const conn = callbackContext.connection;
441
705
  if (!conn) {
442
706
  throw new Error("FS callbacks not available");
@@ -445,35 +709,17 @@ async function handleCreateRuntime(message, connection, state) {
445
709
  connection: conn,
446
710
  callbackContext,
447
711
  invokeClientCallback,
448
- basePath: path2
712
+ basePath: dirPath
449
713
  });
450
714
  }
451
- }
715
+ },
716
+ moduleLoader,
717
+ customFunctions: bridgedCustomFunctions,
718
+ customFunctionsMarshalOptions: customFnMarshalOptions,
719
+ testEnvironment,
720
+ playwright: playwrightOptions
452
721
  });
453
- const instance = {
454
- isolateId,
455
- runtime,
456
- ownerConnection: connection.socket,
457
- callbacks: new Map,
458
- createdAt: Date.now(),
459
- lastActivity: Date.now(),
460
- pendingCallbacks,
461
- returnedCallbacks: new Map,
462
- returnedPromises: new Map,
463
- returnedIterators: new Map,
464
- nextLocalCallbackId: 1e6,
465
- namespaceId,
466
- isDisposed: false,
467
- callbackContext
468
- };
469
- if (moduleLoaderCallback) {
470
- instance.moduleLoaderCallbackId = moduleLoaderCallback.callbackId;
471
- instance.moduleCache = new Map;
472
- instance.moduleToFilename = new Map;
473
- }
474
- if (customCallbacks) {
475
- await setupCustomFunctions(runtime.context, customCallbacks, connection, instance);
476
- }
722
+ instance.runtime = runtime;
477
723
  if (consoleCallbacks?.onEntry) {
478
724
  instance.callbacks.set(consoleCallbacks.onEntry.callbackId, {
479
725
  ...consoleCallbacks.onEntry,
@@ -500,52 +746,33 @@ async function handleCreateRuntime(message, connection, state) {
500
746
  }
501
747
  }
502
748
  }
503
- if (message.options.testEnvironment) {
504
- const testEnvOption = message.options.testEnvironment;
505
- const testEnvOptions = typeof testEnvOption === "object" ? testEnvOption : undefined;
506
- const onEventCallback = testEnvOptions?.callbacks?.onEvent;
507
- await import_isolate_test_environment.setupTestEnvironment(runtime.context, {
508
- onEvent: onEventCallback ? (event) => {
509
- const promise = invokeClientCallback(connection, onEventCallback.callbackId, [JSON.stringify(event)]).catch(() => {});
510
- pendingCallbacks.push(promise);
511
- } : undefined,
512
- testTimeout: testEnvOptions?.testTimeout
749
+ if (playwrightCallbacks) {
750
+ instance.callbacks.set(playwrightCallbacks.handlerCallbackId, {
751
+ callbackId: playwrightCallbacks.handlerCallbackId,
752
+ name: "playwright.handler",
753
+ type: "async"
513
754
  });
514
- instance.testEnvironmentEnabled = true;
515
- if (onEventCallback) {
516
- instance.callbacks.set(onEventCallback.callbackId, {
517
- ...onEventCallback,
518
- name: "testEnvironment.onEvent"
755
+ if (playwrightCallbacks.onBrowserConsoleLogCallbackId !== undefined) {
756
+ instance.callbacks.set(playwrightCallbacks.onBrowserConsoleLogCallbackId, {
757
+ callbackId: playwrightCallbacks.onBrowserConsoleLogCallbackId,
758
+ name: "playwright.onBrowserConsoleLog",
759
+ type: "sync"
760
+ });
761
+ }
762
+ if (playwrightCallbacks.onNetworkRequestCallbackId !== undefined) {
763
+ instance.callbacks.set(playwrightCallbacks.onNetworkRequestCallbackId, {
764
+ callbackId: playwrightCallbacks.onNetworkRequestCallbackId,
765
+ name: "playwright.onNetworkRequest",
766
+ type: "sync"
767
+ });
768
+ }
769
+ if (playwrightCallbacks.onNetworkResponseCallbackId !== undefined) {
770
+ instance.callbacks.set(playwrightCallbacks.onNetworkResponseCallbackId, {
771
+ callbackId: playwrightCallbacks.onNetworkResponseCallbackId,
772
+ name: "playwright.onNetworkResponse",
773
+ type: "sync"
519
774
  });
520
775
  }
521
- }
522
- const playwrightCallbacks = message.options.callbacks?.playwright;
523
- if (playwrightCallbacks) {
524
- const handler = async (op) => {
525
- try {
526
- const resultJson = await invokeClientCallback(connection, playwrightCallbacks.handlerCallbackId, [JSON.stringify(op)]);
527
- return JSON.parse(resultJson);
528
- } catch (err) {
529
- const error = err;
530
- return { ok: false, error: { name: error.name, message: error.message } };
531
- }
532
- };
533
- instance.playwrightHandle = await import_isolate_playwright.setupPlaywright(runtime.context, {
534
- handler,
535
- console: playwrightCallbacks.console,
536
- onEvent: (event) => {
537
- if (event.type === "browserConsoleLog" && playwrightCallbacks.onBrowserConsoleLogCallbackId) {
538
- const promise = invokeClientCallback(connection, playwrightCallbacks.onBrowserConsoleLogCallbackId, [{ level: event.level, stdout: event.stdout, timestamp: event.timestamp }]).catch(() => {});
539
- pendingCallbacks.push(promise);
540
- } else if (event.type === "networkRequest" && playwrightCallbacks.onNetworkRequestCallbackId) {
541
- const promise = invokeClientCallback(connection, playwrightCallbacks.onNetworkRequestCallbackId, [event]).catch(() => {});
542
- pendingCallbacks.push(promise);
543
- } else if (event.type === "networkResponse" && playwrightCallbacks.onNetworkResponseCallbackId) {
544
- const promise = invokeClientCallback(connection, playwrightCallbacks.onNetworkResponseCallbackId, [event]).catch(() => {});
545
- pendingCallbacks.push(promise);
546
- }
547
- }
548
- });
549
776
  }
550
777
  state.isolates.set(isolateId, instance);
551
778
  connection.isolates.add(isolateId);
@@ -553,7 +780,11 @@ async function handleCreateRuntime(message, connection, state) {
553
780
  if (namespaceId != null) {
554
781
  state.namespacedRuntimes.set(namespaceId, instance);
555
782
  }
556
- instance.runtime.fetch.onWebSocketCommand((cmd) => {
783
+ runtime.fetch.onWebSocketCommand((cmd) => {
784
+ const targetConnection = callbackContext.connection;
785
+ if (!targetConnection) {
786
+ return;
787
+ }
557
788
  let data;
558
789
  if (cmd.data instanceof ArrayBuffer) {
559
790
  data = new Uint8Array(cmd.data);
@@ -571,7 +802,49 @@ async function handleCreateRuntime(message, connection, state) {
571
802
  reason: cmd.reason
572
803
  }
573
804
  };
574
- sendMessage(connection.socket, wsCommandMsg);
805
+ sendMessage(targetConnection.socket, wsCommandMsg);
806
+ });
807
+ runtime.fetch.onClientWebSocketCommand((cmd) => {
808
+ const targetConnection = callbackContext.connection;
809
+ if (!targetConnection) {
810
+ return;
811
+ }
812
+ let data;
813
+ if (cmd.data instanceof ArrayBuffer) {
814
+ data = new Uint8Array(cmd.data);
815
+ } else {
816
+ data = cmd.data;
817
+ }
818
+ if (cmd.type === "connect") {
819
+ const msg = {
820
+ type: import_isolate_protocol.MessageType.CLIENT_WS_CONNECT,
821
+ requestId: 0,
822
+ isolateId,
823
+ socketId: cmd.socketId,
824
+ url: cmd.url,
825
+ protocols: cmd.protocols
826
+ };
827
+ sendMessage(targetConnection.socket, msg);
828
+ } else if (cmd.type === "send") {
829
+ const msg = {
830
+ type: import_isolate_protocol.MessageType.CLIENT_WS_SEND,
831
+ requestId: 0,
832
+ isolateId,
833
+ socketId: cmd.socketId,
834
+ data
835
+ };
836
+ sendMessage(targetConnection.socket, msg);
837
+ } else if (cmd.type === "close") {
838
+ const msg = {
839
+ type: import_isolate_protocol.MessageType.CLIENT_WS_CLOSE,
840
+ requestId: 0,
841
+ isolateId,
842
+ socketId: cmd.socketId,
843
+ code: cmd.code,
844
+ reason: cmd.reason
845
+ };
846
+ sendMessage(targetConnection.socket, msg);
847
+ }
575
848
  });
576
849
  sendOk(connection.socket, message.requestId, { isolateId, reused: false });
577
850
  } catch (err) {
@@ -594,10 +867,7 @@ async function handleDisposeRuntime(message, connection, state) {
594
867
  if (instance.namespaceId != null) {
595
868
  softDeleteRuntime(instance, state);
596
869
  } else {
597
- if (instance.playwrightHandle) {
598
- instance.playwrightHandle.dispose();
599
- }
600
- instance.runtime.dispose();
870
+ await instance.runtime.dispose();
601
871
  state.isolates.delete(message.isolateId);
602
872
  }
603
873
  sendOk(connection.socket, message.requestId);
@@ -614,45 +884,13 @@ async function handleEval(message, connection, state) {
614
884
  }
615
885
  instance.lastActivity = Date.now();
616
886
  try {
617
- const filename = import_isolate_protocol.normalizeEntryFilename(message.filename);
618
- const transformed = await import_isolate_transform.transformEntryCode(message.code, filename);
619
- if (transformed.sourceMap) {
620
- if (!instance.sourceMaps) {
621
- instance.sourceMaps = new Map;
622
- }
623
- instance.sourceMaps.set(filename, transformed.sourceMap);
624
- }
625
- const mod = await instance.runtime.isolate.compileModule(transformed.code, {
626
- filename
887
+ await instance.runtime.eval(message.code, {
888
+ filename: message.filename,
889
+ maxExecutionMs: message.maxExecutionMs
627
890
  });
628
- if (instance.moduleLoaderCallbackId) {
629
- instance.moduleToFilename?.set(mod, filename);
630
- const resolver = createModuleResolver(instance, connection);
631
- await mod.instantiate(instance.runtime.context, resolver);
632
- } else {
633
- await mod.instantiate(instance.runtime.context, (specifier) => {
634
- throw new Error(`No module loader registered. Cannot import: ${specifier}`);
635
- });
636
- }
637
- await mod.evaluate();
638
- const ns = mod.namespace;
639
- const runRef = await ns.get("default", { reference: true });
640
- try {
641
- await runRef.apply(undefined, [], {
642
- result: { promise: true },
643
- ...message.maxExecutionMs ? { timeout: message.maxExecutionMs } : {}
644
- });
645
- } finally {
646
- runRef.release();
647
- }
648
- await Promise.all(instance.pendingCallbacks);
649
- instance.pendingCallbacks.length = 0;
650
891
  sendOk(connection.socket, message.requestId, { value: undefined });
651
892
  } catch (err) {
652
893
  const error = err;
653
- if (error.stack && instance.sourceMaps?.size) {
654
- error.stack = import_isolate_transform.mapErrorStack(error.stack, instance.sourceMaps);
655
- }
656
894
  const isTimeoutError = error.message?.includes("Script execution timed out");
657
895
  sendError(connection.socket, message.requestId, isTimeoutError ? import_isolate_protocol.ErrorCode.ISOLATE_TIMEOUT : import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
658
896
  }
@@ -762,6 +1000,35 @@ async function handleWsClose(message, connection, state) {
762
1000
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
763
1001
  }
764
1002
  }
1003
+ function handleClientWsOpened(message, connection, state) {
1004
+ const instance = state.isolates.get(message.isolateId);
1005
+ if (!instance)
1006
+ return;
1007
+ instance.lastActivity = Date.now();
1008
+ instance.runtime.fetch.dispatchClientWebSocketOpen(message.socketId, message.protocol, message.extensions);
1009
+ }
1010
+ function handleClientWsMessage(message, connection, state) {
1011
+ const instance = state.isolates.get(message.isolateId);
1012
+ if (!instance)
1013
+ return;
1014
+ instance.lastActivity = Date.now();
1015
+ const data = message.data instanceof Uint8Array ? message.data.buffer.slice(message.data.byteOffset, message.data.byteOffset + message.data.byteLength) : message.data;
1016
+ instance.runtime.fetch.dispatchClientWebSocketMessage(message.socketId, data);
1017
+ }
1018
+ function handleClientWsClosed(message, connection, state) {
1019
+ const instance = state.isolates.get(message.isolateId);
1020
+ if (!instance)
1021
+ return;
1022
+ instance.lastActivity = Date.now();
1023
+ instance.runtime.fetch.dispatchClientWebSocketClose(message.socketId, message.code, message.reason, message.wasClean);
1024
+ }
1025
+ function handleClientWsError(message, connection, state) {
1026
+ const instance = state.isolates.get(message.isolateId);
1027
+ if (!instance)
1028
+ return;
1029
+ instance.lastActivity = Date.now();
1030
+ instance.runtime.fetch.dispatchClientWebSocketError(message.socketId);
1031
+ }
765
1032
  async function handleFetchGetUpgradeRequest(message, connection, state) {
766
1033
  const instance = state.isolates.get(message.isolateId);
767
1034
  if (!instance) {
@@ -940,523 +1207,6 @@ async function invokeClientCallback(connection, callbackId, args, timeout = 1e4)
940
1207
  sendMessage(connection.socket, invoke);
941
1208
  });
942
1209
  }
943
- var ISOLATE_MARSHAL_CODE = `
944
- (function() {
945
- // Marshal a value (JavaScript → Ref)
946
- function marshalForHost(value, depth = 0) {
947
- if (depth > 100) throw new Error('Maximum marshalling depth exceeded');
948
-
949
- if (value === null) return null;
950
- if (value === undefined) return { __type: 'UndefinedRef' };
951
-
952
- const type = typeof value;
953
- if (type === 'string' || type === 'number' || type === 'boolean') return value;
954
- if (type === 'bigint') return { __type: 'BigIntRef', value: value.toString() };
955
- if (type === 'function') throw new Error('Cannot marshal functions from isolate');
956
- if (type === 'symbol') throw new Error('Cannot marshal Symbol values');
957
-
958
- if (type === 'object') {
959
- if (value instanceof Date) {
960
- return { __type: 'DateRef', timestamp: value.getTime() };
961
- }
962
- if (value instanceof RegExp) {
963
- return { __type: 'RegExpRef', source: value.source, flags: value.flags };
964
- }
965
- if (value instanceof URL) {
966
- return { __type: 'URLRef', href: value.href };
967
- }
968
- if (typeof Headers !== 'undefined' && value instanceof Headers) {
969
- const pairs = [];
970
- value.forEach((v, k) => pairs.push([k, v]));
971
- return { __type: 'HeadersRef', pairs };
972
- }
973
- if (value instanceof Uint8Array) {
974
- return { __type: 'Uint8ArrayRef', data: Array.from(value) };
975
- }
976
- if (value instanceof ArrayBuffer) {
977
- return { __type: 'Uint8ArrayRef', data: Array.from(new Uint8Array(value)) };
978
- }
979
- if (typeof Request !== 'undefined' && value instanceof Request) {
980
- throw new Error('Cannot marshal Request from isolate. Use fetch callback instead.');
981
- }
982
- if (typeof Response !== 'undefined' && value instanceof Response) {
983
- throw new Error('Cannot marshal Response from isolate. Return plain objects instead.');
984
- }
985
- if (typeof File !== 'undefined' && value instanceof File) {
986
- throw new Error('Cannot marshal File from isolate.');
987
- }
988
- if (typeof Blob !== 'undefined' && value instanceof Blob) {
989
- throw new Error('Cannot marshal Blob from isolate.');
990
- }
991
- if (typeof FormData !== 'undefined' && value instanceof FormData) {
992
- throw new Error('Cannot marshal FormData from isolate.');
993
- }
994
- if (Array.isArray(value)) {
995
- return value.map(v => marshalForHost(v, depth + 1));
996
- }
997
- // Plain object
998
- const result = {};
999
- for (const key of Object.keys(value)) {
1000
- result[key] = marshalForHost(value[key], depth + 1);
1001
- }
1002
- return result;
1003
- }
1004
- return value;
1005
- }
1006
-
1007
- // Unmarshal a value (Ref → JavaScript)
1008
- function unmarshalFromHost(value, depth = 0) {
1009
- if (depth > 100) throw new Error('Maximum unmarshalling depth exceeded');
1010
-
1011
- if (value === null) return null;
1012
- if (typeof value !== 'object') return value;
1013
-
1014
- if (value.__type) {
1015
- switch (value.__type) {
1016
- case 'UndefinedRef': return undefined;
1017
- case 'DateRef': return new Date(value.timestamp);
1018
- case 'RegExpRef': return new RegExp(value.source, value.flags);
1019
- case 'BigIntRef': return BigInt(value.value);
1020
- case 'URLRef': return new URL(value.href);
1021
- case 'HeadersRef': return new Headers(value.pairs);
1022
- case 'Uint8ArrayRef': return new Uint8Array(value.data);
1023
- case 'RequestRef': {
1024
- const init = {
1025
- method: value.method,
1026
- headers: value.headers,
1027
- body: value.body ? new Uint8Array(value.body) : null,
1028
- };
1029
- if (value.mode) init.mode = value.mode;
1030
- if (value.credentials) init.credentials = value.credentials;
1031
- if (value.cache) init.cache = value.cache;
1032
- if (value.redirect) init.redirect = value.redirect;
1033
- if (value.referrer) init.referrer = value.referrer;
1034
- if (value.referrerPolicy) init.referrerPolicy = value.referrerPolicy;
1035
- if (value.integrity) init.integrity = value.integrity;
1036
- return new Request(value.url, init);
1037
- }
1038
- case 'ResponseRef': {
1039
- return new Response(value.body ? new Uint8Array(value.body) : null, {
1040
- status: value.status,
1041
- statusText: value.statusText,
1042
- headers: value.headers,
1043
- });
1044
- }
1045
- case 'FileRef': {
1046
- if (!value.name) {
1047
- return new Blob([new Uint8Array(value.data)], { type: value.type });
1048
- }
1049
- return new File([new Uint8Array(value.data)], value.name, {
1050
- type: value.type,
1051
- lastModified: value.lastModified,
1052
- });
1053
- }
1054
- case 'FormDataRef': {
1055
- const fd = new FormData();
1056
- for (const [key, entry] of value.entries) {
1057
- if (typeof entry === 'string') {
1058
- fd.append(key, entry);
1059
- } else {
1060
- const file = unmarshalFromHost(entry, depth + 1);
1061
- fd.append(key, file);
1062
- }
1063
- }
1064
- return fd;
1065
- }
1066
- case 'CallbackRef': {
1067
- // Create a proxy function that invokes the callback
1068
- const callbackId = value.callbackId;
1069
- return function(...args) {
1070
- const argsJson = JSON.stringify(marshalForHost(args));
1071
- const resultJson = __customFn_invoke.applySyncPromise(undefined, [callbackId, argsJson]);
1072
- const result = JSON.parse(resultJson);
1073
- if (result.ok) {
1074
- return unmarshalFromHost(result.value);
1075
- } else {
1076
- const error = new Error(result.error.message);
1077
- error.name = result.error.name;
1078
- throw error;
1079
- }
1080
- };
1081
- }
1082
- case 'PromiseRef': {
1083
- // Create a proxy Promise that resolves via callback
1084
- const promiseId = value.promiseId;
1085
- return new Promise((resolve, reject) => {
1086
- try {
1087
- const argsJson = JSON.stringify([promiseId]);
1088
- const resultJson = __customFn_invoke.applySyncPromise(undefined, [value.__resolveCallbackId, argsJson]);
1089
- const result = JSON.parse(resultJson);
1090
- if (result.ok) {
1091
- resolve(unmarshalFromHost(result.value));
1092
- } else {
1093
- reject(new Error(result.error.message));
1094
- }
1095
- } catch (e) {
1096
- reject(e);
1097
- }
1098
- });
1099
- }
1100
- case 'AsyncIteratorRef': {
1101
- const iteratorId = value.iteratorId;
1102
- const nextCallbackId = value.__nextCallbackId;
1103
- const returnCallbackId = value.__returnCallbackId;
1104
- return {
1105
- [Symbol.asyncIterator]() { return this; },
1106
- async next() {
1107
- const argsJson = JSON.stringify([iteratorId]);
1108
- const resultJson = __customFn_invoke.applySyncPromise(undefined, [nextCallbackId, argsJson]);
1109
- const result = JSON.parse(resultJson);
1110
- if (!result.ok) {
1111
- const error = new Error(result.error.message);
1112
- error.name = result.error.name;
1113
- throw error;
1114
- }
1115
- return {
1116
- done: result.value.done,
1117
- value: unmarshalFromHost(result.value.value)
1118
- };
1119
- },
1120
- async return(v) {
1121
- const argsJson = JSON.stringify([iteratorId, marshalForHost(v)]);
1122
- const resultJson = __customFn_invoke.applySyncPromise(undefined, [returnCallbackId, argsJson]);
1123
- const result = JSON.parse(resultJson);
1124
- return { done: true, value: result.ok ? unmarshalFromHost(result.value) : undefined };
1125
- }
1126
- };
1127
- }
1128
- default:
1129
- // Unknown ref type, return as-is
1130
- break;
1131
- }
1132
- }
1133
-
1134
- if (Array.isArray(value)) {
1135
- return value.map(v => unmarshalFromHost(v, depth + 1));
1136
- }
1137
-
1138
- // Plain object - recursively unmarshal
1139
- const result = {};
1140
- for (const key of Object.keys(value)) {
1141
- result[key] = unmarshalFromHost(value[key], depth + 1);
1142
- }
1143
- return result;
1144
- }
1145
-
1146
- globalThis.__marshalForHost = marshalForHost;
1147
- globalThis.__unmarshalFromHost = unmarshalFromHost;
1148
- })();
1149
- `;
1150
- var LOCAL_CALLBACK_THRESHOLD = 1e6;
1151
- function isPromiseRef(value) {
1152
- return typeof value === "object" && value !== null && value.__type === "PromiseRef";
1153
- }
1154
- function isAsyncIteratorRef(value) {
1155
- return typeof value === "object" && value !== null && value.__type === "AsyncIteratorRef";
1156
- }
1157
- function isLocalCallbackId(callbackId) {
1158
- return callbackId >= LOCAL_CALLBACK_THRESHOLD;
1159
- }
1160
- async function setupCustomFunctions(context, customCallbacks, connection, instance) {
1161
- const global = context.global;
1162
- function createMarshalContext() {
1163
- return {
1164
- registerCallback: (fn) => {
1165
- const callbackId = instance.nextLocalCallbackId++;
1166
- instance.returnedCallbacks.set(callbackId, fn);
1167
- return callbackId;
1168
- },
1169
- registerPromise: (promise) => {
1170
- const promiseId = instance.nextLocalCallbackId++;
1171
- instance.returnedPromises.set(promiseId, promise);
1172
- return promiseId;
1173
- },
1174
- registerIterator: (iterator) => {
1175
- const iteratorId = instance.nextLocalCallbackId++;
1176
- instance.returnedIterators.set(iteratorId, iterator);
1177
- return iteratorId;
1178
- }
1179
- };
1180
- }
1181
- function addCallbackIdsToRefs(value) {
1182
- if (value === null || typeof value !== "object") {
1183
- return value;
1184
- }
1185
- if (isPromiseRef(value)) {
1186
- if ("__resolveCallbackId" in value) {
1187
- return value;
1188
- }
1189
- const resolveCallbackId = instance.nextLocalCallbackId++;
1190
- instance.returnedCallbacks.set(resolveCallbackId, async (promiseId) => {
1191
- const promise = instance.returnedPromises.get(promiseId);
1192
- if (!promise) {
1193
- throw new Error(`Promise ${promiseId} not found`);
1194
- }
1195
- const result2 = await promise;
1196
- instance.returnedPromises.delete(promiseId);
1197
- const ctx = createMarshalContext();
1198
- const marshalled = await import_isolate_protocol.marshalValue(result2, ctx);
1199
- return addCallbackIdsToRefs(marshalled);
1200
- });
1201
- return {
1202
- ...value,
1203
- __resolveCallbackId: resolveCallbackId
1204
- };
1205
- }
1206
- if (isAsyncIteratorRef(value)) {
1207
- if ("__nextCallbackId" in value) {
1208
- return value;
1209
- }
1210
- const nextCallbackId = instance.nextLocalCallbackId++;
1211
- instance.returnedCallbacks.set(nextCallbackId, async (iteratorId) => {
1212
- const iterator = instance.returnedIterators.get(iteratorId);
1213
- if (!iterator) {
1214
- throw new Error(`Iterator ${iteratorId} not found`);
1215
- }
1216
- const result2 = await iterator.next();
1217
- if (result2.done) {
1218
- instance.returnedIterators.delete(iteratorId);
1219
- }
1220
- const ctx = createMarshalContext();
1221
- const marshalledValue = await import_isolate_protocol.marshalValue(result2.value, ctx);
1222
- return {
1223
- done: result2.done,
1224
- value: addCallbackIdsToRefs(marshalledValue)
1225
- };
1226
- });
1227
- const returnCallbackId = instance.nextLocalCallbackId++;
1228
- instance.returnedCallbacks.set(returnCallbackId, async (iteratorId, returnValue) => {
1229
- const iterator = instance.returnedIterators.get(iteratorId);
1230
- instance.returnedIterators.delete(iteratorId);
1231
- if (!iterator || !iterator.return) {
1232
- return { done: true, value: undefined };
1233
- }
1234
- const result2 = await iterator.return(returnValue);
1235
- const ctx = createMarshalContext();
1236
- const marshalledValue = await import_isolate_protocol.marshalValue(result2.value, ctx);
1237
- return {
1238
- done: true,
1239
- value: addCallbackIdsToRefs(marshalledValue)
1240
- };
1241
- });
1242
- return {
1243
- ...value,
1244
- __nextCallbackId: nextCallbackId,
1245
- __returnCallbackId: returnCallbackId
1246
- };
1247
- }
1248
- if (Array.isArray(value)) {
1249
- return value.map((item) => addCallbackIdsToRefs(item));
1250
- }
1251
- const result = {};
1252
- for (const key of Object.keys(value)) {
1253
- result[key] = addCallbackIdsToRefs(value[key]);
1254
- }
1255
- return result;
1256
- }
1257
- const invokeCallbackRef = new import_isolated_vm.default.Reference(async (callbackId, argsJson) => {
1258
- const marshalledArgs = JSON.parse(argsJson);
1259
- const args = import_isolate_protocol.unmarshalValue(marshalledArgs);
1260
- try {
1261
- let result;
1262
- if (isLocalCallbackId(callbackId)) {
1263
- const callback = instance.returnedCallbacks.get(callbackId);
1264
- if (!callback) {
1265
- throw new Error(`Local callback ${callbackId} not found`);
1266
- }
1267
- result = await callback(...args);
1268
- } else {
1269
- const conn = instance.callbackContext?.connection || connection;
1270
- result = await invokeClientCallback(conn, callbackId, args);
1271
- }
1272
- const ctx = createMarshalContext();
1273
- const marshalledResult = await import_isolate_protocol.marshalValue({ ok: true, value: result }, ctx);
1274
- const processedResult = addCallbackIdsToRefs(marshalledResult);
1275
- return JSON.stringify(processedResult);
1276
- } catch (error) {
1277
- const err = error;
1278
- return JSON.stringify({
1279
- ok: false,
1280
- error: { message: err.message, name: err.name }
1281
- });
1282
- }
1283
- });
1284
- global.setSync("__customFn_invoke", invokeCallbackRef);
1285
- context.evalSync(ISOLATE_MARSHAL_CODE);
1286
- const callbackIdMap = {};
1287
- for (const [name, registration] of Object.entries(customCallbacks)) {
1288
- callbackIdMap[name] = registration.callbackId;
1289
- }
1290
- global.setSync("__customFnCallbackIds", new import_isolated_vm.default.ExternalCopy(callbackIdMap).copyInto());
1291
- for (const [name, registration] of Object.entries(customCallbacks)) {
1292
- if (name.includes(":")) {
1293
- continue;
1294
- }
1295
- if (registration.type === "sync") {
1296
- context.evalSync(`
1297
- globalThis.${name} = function(...args) {
1298
- const callbackId = globalThis.__customFnCallbackIds["${name}"];
1299
- const argsJson = JSON.stringify(__marshalForHost(args));
1300
- const resultJson = __customFn_invoke.applySyncPromise(
1301
- undefined,
1302
- [callbackId, argsJson]
1303
- );
1304
- const result = JSON.parse(resultJson);
1305
- if (result.ok) {
1306
- return __unmarshalFromHost(result.value);
1307
- } else {
1308
- const error = new Error(result.error.message);
1309
- error.name = result.error.name;
1310
- throw error;
1311
- }
1312
- };
1313
- `);
1314
- } else if (registration.type === "asyncIterator") {
1315
- const startReg = customCallbacks[`${name}:start`];
1316
- const nextReg = customCallbacks[`${name}:next`];
1317
- const returnReg = customCallbacks[`${name}:return`];
1318
- const throwReg = customCallbacks[`${name}:throw`];
1319
- if (!startReg || !nextReg || !returnReg || !throwReg) {
1320
- throw new Error(`Missing companion callbacks for asyncIterator function "${name}"`);
1321
- }
1322
- context.evalSync(`
1323
- globalThis.${name} = function(...args) {
1324
- // Start the iterator and get the iteratorId
1325
- const startCallbackId = globalThis.__customFnCallbackIds["${name}:start"];
1326
- const argsJson = JSON.stringify(__marshalForHost(args));
1327
- const startResultJson = __customFn_invoke.applySyncPromise(
1328
- undefined,
1329
- [startCallbackId, argsJson]
1330
- );
1331
- const startResult = JSON.parse(startResultJson);
1332
- if (!startResult.ok) {
1333
- const error = new Error(startResult.error.message);
1334
- error.name = startResult.error.name;
1335
- throw error;
1336
- }
1337
- const iteratorId = __unmarshalFromHost(startResult.value).iteratorId;
1338
-
1339
- return {
1340
- [Symbol.asyncIterator]() { return this; },
1341
- async next() {
1342
- const nextCallbackId = globalThis.__customFnCallbackIds["${name}:next"];
1343
- const argsJson = JSON.stringify(__marshalForHost([iteratorId]));
1344
- const resultJson = __customFn_invoke.applySyncPromise(
1345
- undefined,
1346
- [nextCallbackId, argsJson]
1347
- );
1348
- const result = JSON.parse(resultJson);
1349
- if (!result.ok) {
1350
- const error = new Error(result.error.message);
1351
- error.name = result.error.name;
1352
- throw error;
1353
- }
1354
- const val = __unmarshalFromHost(result.value);
1355
- return { done: val.done, value: val.value };
1356
- },
1357
- async return(v) {
1358
- const returnCallbackId = globalThis.__customFnCallbackIds["${name}:return"];
1359
- const argsJson = JSON.stringify(__marshalForHost([iteratorId, v]));
1360
- const resultJson = __customFn_invoke.applySyncPromise(
1361
- undefined,
1362
- [returnCallbackId, argsJson]
1363
- );
1364
- const result = JSON.parse(resultJson);
1365
- return { done: true, value: result.ok ? __unmarshalFromHost(result.value) : undefined };
1366
- },
1367
- async throw(e) {
1368
- const throwCallbackId = globalThis.__customFnCallbackIds["${name}:throw"];
1369
- const argsJson = JSON.stringify(__marshalForHost([iteratorId, { message: e?.message, name: e?.name }]));
1370
- const resultJson = __customFn_invoke.applySyncPromise(
1371
- undefined,
1372
- [throwCallbackId, argsJson]
1373
- );
1374
- const result = JSON.parse(resultJson);
1375
- if (!result.ok) {
1376
- const error = new Error(result.error.message);
1377
- error.name = result.error.name;
1378
- throw error;
1379
- }
1380
- const val = __unmarshalFromHost(result.value);
1381
- return { done: val.done, value: val.value };
1382
- }
1383
- };
1384
- };
1385
- `);
1386
- } else if (registration.type === "async") {
1387
- context.evalSync(`
1388
- globalThis.${name} = async function(...args) {
1389
- const callbackId = globalThis.__customFnCallbackIds["${name}"];
1390
- const argsJson = JSON.stringify(__marshalForHost(args));
1391
- const resultJson = __customFn_invoke.applySyncPromise(
1392
- undefined,
1393
- [callbackId, argsJson]
1394
- );
1395
- const result = JSON.parse(resultJson);
1396
- if (result.ok) {
1397
- return __unmarshalFromHost(result.value);
1398
- } else {
1399
- const error = new Error(result.error.message);
1400
- error.name = result.error.name;
1401
- throw error;
1402
- }
1403
- };
1404
- `);
1405
- }
1406
- }
1407
- }
1408
- function createModuleResolver(instance, connection) {
1409
- return async (specifier, referrer) => {
1410
- const cached = instance.moduleCache?.get(specifier);
1411
- if (cached)
1412
- return cached;
1413
- if (!instance.moduleLoaderCallbackId) {
1414
- throw new Error(`Module not found: ${specifier}`);
1415
- }
1416
- const importerPath = instance.moduleToFilename?.get(referrer) ?? "<unknown>";
1417
- const importerResolveDir = import_node_path.default.posix.dirname(importerPath);
1418
- const result = await invokeClientCallback(connection, instance.moduleLoaderCallbackId, [specifier, { path: importerPath, resolveDir: importerResolveDir }]);
1419
- const { code, resolveDir } = result;
1420
- const hash = import_isolate_transform.contentHash(code);
1421
- const cacheKey = `${specifier}:${hash}`;
1422
- const hashCached = instance.moduleCache?.get(cacheKey);
1423
- if (hashCached)
1424
- return hashCached;
1425
- const transformed = await import_isolate_transform.transformModuleCode(code, specifier);
1426
- if (transformed.sourceMap) {
1427
- if (!instance.sourceMaps) {
1428
- instance.sourceMaps = new Map;
1429
- }
1430
- instance.sourceMaps.set(specifier, transformed.sourceMap);
1431
- }
1432
- const mod = await instance.runtime.isolate.compileModule(transformed.code, {
1433
- filename: specifier
1434
- });
1435
- const resolvedPath = import_node_path.default.posix.join(resolveDir, import_node_path.default.posix.basename(specifier));
1436
- instance.moduleToFilename?.set(mod, resolvedPath);
1437
- const resolver = createModuleResolver(instance, connection);
1438
- await mod.instantiate(instance.runtime.context, resolver);
1439
- instance.moduleCache?.set(specifier, mod);
1440
- instance.moduleCache?.set(cacheKey, mod);
1441
- return mod;
1442
- };
1443
- }
1444
- async function serializeRequest(request) {
1445
- const headers = [];
1446
- request.headers.forEach((value, key) => {
1447
- headers.push([key, value]);
1448
- });
1449
- let body = null;
1450
- if (request.body) {
1451
- body = new Uint8Array(await request.arrayBuffer());
1452
- }
1453
- return {
1454
- method: request.method,
1455
- url: request.url,
1456
- headers,
1457
- body
1458
- };
1459
- }
1460
1210
  function deserializeResponse(data) {
1461
1211
  return new Response(data.body, {
1462
1212
  status: data.status,
@@ -1723,28 +1473,11 @@ async function handleRunTests(message, connection, state) {
1723
1473
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1724
1474
  return;
1725
1475
  }
1726
- if (!instance.testEnvironmentEnabled) {
1727
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
1728
- return;
1729
- }
1730
1476
  instance.lastActivity = Date.now();
1731
1477
  try {
1732
1478
  const timeout = message.timeout ?? 30000;
1733
- let timeoutId;
1734
- const timeoutPromise = new Promise((_, reject) => {
1735
- timeoutId = setTimeout(() => reject(new Error("Test timeout")), timeout);
1736
- });
1737
- try {
1738
- const results = await Promise.race([
1739
- import_isolate_test_environment.runTests(instance.runtime.context),
1740
- timeoutPromise
1741
- ]);
1742
- sendOk(connection.socket, message.requestId, results);
1743
- } finally {
1744
- if (timeoutId) {
1745
- clearTimeout(timeoutId);
1746
- }
1747
- }
1479
+ const results = await instance.runtime.testEnvironment.runTests(timeout);
1480
+ sendOk(connection.socket, message.requestId, results);
1748
1481
  } catch (err) {
1749
1482
  const error = err;
1750
1483
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
@@ -1756,13 +1489,9 @@ async function handleResetTestEnv(message, connection, state) {
1756
1489
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1757
1490
  return;
1758
1491
  }
1759
- if (!instance.testEnvironmentEnabled) {
1760
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
1761
- return;
1762
- }
1763
1492
  instance.lastActivity = Date.now();
1764
1493
  try {
1765
- await instance.runtime.context.eval("__resetTestEnvironment()", { promise: true });
1494
+ instance.runtime.testEnvironment.reset();
1766
1495
  sendOk(connection.socket, message.requestId);
1767
1496
  } catch (err) {
1768
1497
  const error = err;
@@ -1775,13 +1504,9 @@ async function handleHasTests(message, connection, state) {
1775
1504
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1776
1505
  return;
1777
1506
  }
1778
- if (!instance.testEnvironmentEnabled) {
1779
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
1780
- return;
1781
- }
1782
1507
  instance.lastActivity = Date.now();
1783
1508
  try {
1784
- const result = import_isolate_test_environment.hasTests(instance.runtime.context);
1509
+ const result = instance.runtime.testEnvironment.hasTests();
1785
1510
  sendOk(connection.socket, message.requestId, result);
1786
1511
  } catch (err) {
1787
1512
  const error = err;
@@ -1794,42 +1519,24 @@ async function handleGetTestCount(message, connection, state) {
1794
1519
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1795
1520
  return;
1796
1521
  }
1797
- if (!instance.testEnvironmentEnabled) {
1798
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
1799
- return;
1800
- }
1801
1522
  instance.lastActivity = Date.now();
1802
1523
  try {
1803
- const result = import_isolate_test_environment.getTestCount(instance.runtime.context);
1524
+ const result = instance.runtime.testEnvironment.getTestCount();
1804
1525
  sendOk(connection.socket, message.requestId, result);
1805
1526
  } catch (err) {
1806
1527
  const error = err;
1807
1528
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
1808
1529
  }
1809
1530
  }
1810
- async function handleRunPlaywrightTests(message, connection, _state) {
1811
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "playwright.runTests() has been removed. Use testEnvironment.runTests() instead.");
1812
- }
1813
- async function handleResetPlaywrightTests(message, connection, _state) {
1814
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "playwright.reset() has been removed. Use testEnvironment.reset() instead.");
1815
- }
1816
1531
  async function handleGetCollectedData(message, connection, state) {
1817
1532
  const instance = state.isolates.get(message.isolateId);
1818
1533
  if (!instance) {
1819
1534
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1820
1535
  return;
1821
1536
  }
1822
- if (!instance.playwrightHandle) {
1823
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Playwright not configured. Provide playwright.page in createRuntime options.");
1824
- return;
1825
- }
1826
1537
  instance.lastActivity = Date.now();
1827
1538
  try {
1828
- const data = {
1829
- browserConsoleLogs: instance.playwrightHandle.getBrowserConsoleLogs(),
1830
- networkRequests: instance.playwrightHandle.getNetworkRequests(),
1831
- networkResponses: instance.playwrightHandle.getNetworkResponses()
1832
- };
1539
+ const data = instance.runtime.playwright.getCollectedData();
1833
1540
  sendOk(connection.socket, message.requestId, data);
1834
1541
  } catch (err) {
1835
1542
  const error = err;
@@ -1842,13 +1549,9 @@ async function handleClearCollectedData(message, connection, state) {
1842
1549
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1843
1550
  return;
1844
1551
  }
1845
- if (!instance.playwrightHandle) {
1846
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Playwright not configured. Provide playwright.page in createRuntime options.");
1847
- return;
1848
- }
1849
1552
  instance.lastActivity = Date.now();
1850
1553
  try {
1851
- instance.playwrightHandle.clearCollected();
1554
+ instance.runtime.playwright.clearCollectedData();
1852
1555
  sendOk(connection.socket, message.requestId);
1853
1556
  } catch (err) {
1854
1557
  const error = err;
@@ -1856,4 +1559,4 @@ async function handleClearCollectedData(message, connection, state) {
1856
1559
  }
1857
1560
  }
1858
1561
 
1859
- //# debugId=32017BAA615D918C64756E2164756E21
1562
+ //# debugId=53A5FD566727ACE764756E2164756E21