@ricsam/isolate-daemon 0.1.13 → 0.1.15

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,31 @@ __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");
39
+ var LINKER_CONFLICT_ERROR = "Module is currently being linked by another linker";
40
+ function getErrorText(error) {
41
+ if (error instanceof Error) {
42
+ const cause = error.cause;
43
+ const causeText = cause instanceof Error ? `${cause.name}: ${cause.message}
44
+ ${cause.stack ?? ""}` : cause != null ? String(cause) : "";
45
+ return [error.name, error.message, error.stack, causeText].filter((part) => part != null && part !== "").join(`
46
+ `);
47
+ }
48
+ if (typeof error === "string") {
49
+ return error;
50
+ }
51
+ try {
52
+ return JSON.stringify(error);
53
+ } catch {
54
+ return String(error ?? "");
55
+ }
56
+ }
57
+ function isLinkerConflictError(error) {
58
+ const text = getErrorText(error).toLowerCase();
59
+ return text.includes(LINKER_CONFLICT_ERROR.toLowerCase());
60
+ }
57
61
  function handleConnection(socket, state) {
58
62
  const connection = {
59
63
  socket,
@@ -86,22 +90,17 @@ function handleConnection(socket, state) {
86
90
  const instance = state.isolates.get(isolateId);
87
91
  if (instance) {
88
92
  if (instance.namespaceId != null && !instance.isDisposed) {
89
- softDeleteRuntime(instance, state);
93
+ if (instance.isPoisoned) {
94
+ hardDeleteRuntime(instance, state).catch(() => {});
95
+ } else {
96
+ softDeleteRuntime(instance, state);
97
+ }
90
98
  } else if (!instance.isDisposed) {
91
- try {
92
- if (instance.playwrightHandle) {
93
- instance.playwrightHandle.dispose();
94
- }
95
- instance.runtime.dispose();
96
- } catch {}
97
- state.isolates.delete(isolateId);
99
+ hardDeleteRuntime(instance, state).catch(() => {});
98
100
  }
99
101
  }
100
102
  }
101
103
  for (const [, pending] of connection.pendingCallbacks) {
102
- if (pending.timeoutId) {
103
- clearTimeout(pending.timeoutId);
104
- }
105
104
  pending.reject(new Error("Connection closed"));
106
105
  }
107
106
  connection.pendingCallbacks.clear();
@@ -199,12 +198,6 @@ async function handleMessage(message, connection, state) {
199
198
  case import_isolate_protocol.MessageType.GET_TEST_COUNT:
200
199
  await handleGetTestCount(message, connection, state);
201
200
  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
201
  case import_isolate_protocol.MessageType.GET_COLLECTED_DATA:
209
202
  await handleGetCollectedData(message, connection, state);
210
203
  break;
@@ -235,34 +228,69 @@ async function handleMessage(message, connection, state) {
235
228
  case import_isolate_protocol.MessageType.CALLBACK_STREAM_END:
236
229
  handleCallbackStreamEnd(message, connection);
237
230
  break;
231
+ case import_isolate_protocol.MessageType.CLIENT_EVENT:
232
+ handleClientEvent(message, connection, state);
233
+ break;
238
234
  default:
239
235
  sendError(connection.socket, message.requestId ?? 0, import_isolate_protocol.ErrorCode.UNKNOWN_MESSAGE_TYPE, `Unknown message type: ${message.type}`);
240
236
  }
241
237
  }
238
+ async function hardDeleteRuntime(instance, state) {
239
+ try {
240
+ await instance.runtime.dispose();
241
+ } finally {
242
+ state.isolates.delete(instance.isolateId);
243
+ if (instance.namespaceId != null) {
244
+ const indexed = state.namespacedRuntimes.get(instance.namespaceId);
245
+ if (indexed?.isolateId === instance.isolateId) {
246
+ state.namespacedRuntimes.delete(instance.namespaceId);
247
+ }
248
+ }
249
+ instance.isDisposed = true;
250
+ instance.disposedAt = undefined;
251
+ instance.ownerConnection = null;
252
+ if (instance.callbackContext) {
253
+ instance.callbackContext.connection = null;
254
+ }
255
+ }
256
+ }
242
257
  function softDeleteRuntime(instance, state) {
243
258
  instance.isDisposed = true;
244
259
  instance.disposedAt = Date.now();
245
260
  instance.ownerConnection = null;
261
+ if (instance.callbackContext) {
262
+ instance.callbackContext.connection = null;
263
+ }
246
264
  instance.callbacks.clear();
247
265
  instance.runtime.timers.clearAll();
248
266
  instance.runtime.console.reset();
249
- instance.pendingCallbacks.length = 0;
267
+ instance.runtime.pendingCallbacks.length = 0;
250
268
  instance.returnedCallbacks?.clear();
251
269
  instance.returnedPromises?.clear();
252
270
  instance.returnedIterators?.clear();
271
+ instance.runtime.clearModuleCache();
253
272
  }
254
273
  function reuseNamespacedRuntime(instance, connection, message, state) {
255
274
  instance.ownerConnection = connection.socket;
256
275
  instance.isDisposed = false;
276
+ instance.isPoisoned = false;
257
277
  instance.disposedAt = undefined;
258
278
  instance.lastActivity = Date.now();
259
279
  connection.isolates.add(instance.isolateId);
260
280
  const callbacks = message.options.callbacks;
281
+ const testEnvOptions = message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment : undefined;
261
282
  if (instance.callbackContext) {
262
283
  instance.callbackContext.connection = connection;
263
284
  instance.callbackContext.consoleOnEntry = callbacks?.console?.onEntry?.callbackId;
264
285
  instance.callbackContext.fetch = callbacks?.fetch?.callbackId;
265
286
  instance.callbackContext.moduleLoader = callbacks?.moduleLoader?.callbackId;
287
+ instance.callbackContext.testEnvironmentOnEvent = testEnvOptions?.callbacks?.onEvent?.callbackId;
288
+ instance.callbackContext.playwright = {
289
+ handlerCallbackId: callbacks?.playwright?.handlerCallbackId,
290
+ onBrowserConsoleLogCallbackId: callbacks?.playwright?.onBrowserConsoleLogCallbackId,
291
+ onNetworkRequestCallbackId: callbacks?.playwright?.onNetworkRequestCallbackId,
292
+ onNetworkResponseCallbackId: callbacks?.playwright?.onNetworkResponseCallbackId
293
+ };
266
294
  instance.callbackContext.fs = {
267
295
  readFile: callbacks?.fs?.readFile?.callbackId,
268
296
  writeFile: callbacks?.fs?.writeFile?.callbackId,
@@ -299,7 +327,6 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
299
327
  }
300
328
  }
301
329
  if (callbacks?.moduleLoader) {
302
- instance.moduleLoaderCallbackId = callbacks.moduleLoader.callbackId;
303
330
  instance.callbacks.set(callbacks.moduleLoader.callbackId, callbacks.moduleLoader);
304
331
  }
305
332
  if (callbacks?.custom) {
@@ -309,23 +336,46 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
309
336
  }
310
337
  }
311
338
  }
339
+ if (testEnvOptions?.callbacks?.onEvent) {
340
+ instance.callbacks.set(testEnvOptions.callbacks.onEvent.callbackId, {
341
+ ...testEnvOptions.callbacks.onEvent,
342
+ name: "testEnvironment.onEvent"
343
+ });
344
+ }
345
+ if (callbacks?.playwright) {
346
+ instance.callbacks.set(callbacks.playwright.handlerCallbackId, {
347
+ callbackId: callbacks.playwright.handlerCallbackId,
348
+ name: "playwright.handler",
349
+ type: "async"
350
+ });
351
+ if (callbacks.playwright.onBrowserConsoleLogCallbackId !== undefined) {
352
+ instance.callbacks.set(callbacks.playwright.onBrowserConsoleLogCallbackId, {
353
+ callbackId: callbacks.playwright.onBrowserConsoleLogCallbackId,
354
+ name: "playwright.onBrowserConsoleLog",
355
+ type: "sync"
356
+ });
357
+ }
358
+ if (callbacks.playwright.onNetworkRequestCallbackId !== undefined) {
359
+ instance.callbacks.set(callbacks.playwright.onNetworkRequestCallbackId, {
360
+ callbackId: callbacks.playwright.onNetworkRequestCallbackId,
361
+ name: "playwright.onNetworkRequest",
362
+ type: "sync"
363
+ });
364
+ }
365
+ if (callbacks.playwright.onNetworkResponseCallbackId !== undefined) {
366
+ instance.callbacks.set(callbacks.playwright.onNetworkResponseCallbackId, {
367
+ callbackId: callbacks.playwright.onNetworkResponseCallbackId,
368
+ name: "playwright.onNetworkResponse",
369
+ type: "sync"
370
+ });
371
+ }
372
+ }
312
373
  instance.returnedCallbacks = new Map;
313
374
  instance.returnedPromises = new Map;
314
375
  instance.returnedIterators = new Map;
315
376
  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
377
  }
328
- function evictOldestDisposedRuntime(state) {
378
+ async function evictOldestDisposedRuntime(state) {
329
379
  let oldest = null;
330
380
  let oldestTime = Infinity;
331
381
  for (const [, instance] of state.isolates) {
@@ -338,21 +388,15 @@ function evictOldestDisposedRuntime(state) {
338
388
  }
339
389
  if (oldest) {
340
390
  try {
341
- if (oldest.playwrightHandle) {
342
- oldest.playwrightHandle.dispose();
343
- }
344
- oldest.runtime.dispose();
391
+ await hardDeleteRuntime(oldest, state);
345
392
  } catch {}
346
- state.isolates.delete(oldest.isolateId);
347
- if (oldest.namespaceId != null) {
348
- state.namespacedRuntimes.delete(oldest.namespaceId);
349
- }
350
393
  return true;
351
394
  }
352
395
  return false;
353
396
  }
354
397
  async function handleCreateRuntime(message, connection, state) {
355
398
  const namespaceId = message.options.namespaceId;
399
+ let namespaceCreationLocked = false;
356
400
  if (namespaceId != null) {
357
401
  const existing = state.namespacedRuntimes.get(namespaceId);
358
402
  if (existing) {
@@ -374,26 +418,38 @@ async function handleCreateRuntime(message, connection, state) {
374
418
  });
375
419
  return;
376
420
  }
377
- }
378
- if (state.isolates.size >= state.options.maxIsolates) {
379
- if (!evictOldestDisposedRuntime(state)) {
380
- sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_MEMORY_LIMIT, `Maximum isolates (${state.options.maxIsolates}) reached`);
421
+ if (state.namespacedCreatesInFlight.has(namespaceId)) {
422
+ sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, `Namespace "${namespaceId}" creation already in progress`);
381
423
  return;
382
424
  }
425
+ state.namespacedCreatesInFlight.add(namespaceId);
426
+ namespaceCreationLocked = true;
383
427
  }
384
428
  try {
429
+ if (state.isolates.size >= state.options.maxIsolates) {
430
+ if (!await evictOldestDisposedRuntime(state)) {
431
+ sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_MEMORY_LIMIT, `Maximum isolates (${state.options.maxIsolates}) reached`);
432
+ return;
433
+ }
434
+ }
385
435
  const isolateId = import_node_crypto.randomUUID();
386
436
  const consoleCallbacks = message.options.callbacks?.console;
387
437
  const fetchCallback = message.options.callbacks?.fetch;
388
438
  const fsCallbacks = message.options.callbacks?.fs;
389
439
  const moduleLoaderCallback = message.options.callbacks?.moduleLoader;
390
440
  const customCallbacks = message.options.callbacks?.custom;
391
- const pendingCallbacks = [];
392
441
  const callbackContext = {
393
442
  connection,
394
443
  consoleOnEntry: consoleCallbacks?.onEntry?.callbackId,
395
444
  fetch: fetchCallback?.callbackId,
396
445
  moduleLoader: moduleLoaderCallback?.callbackId,
446
+ testEnvironmentOnEvent: message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment.callbacks?.onEvent?.callbackId : undefined,
447
+ playwright: {
448
+ handlerCallbackId: message.options.callbacks?.playwright?.handlerCallbackId,
449
+ onBrowserConsoleLogCallbackId: message.options.callbacks?.playwright?.onBrowserConsoleLogCallbackId,
450
+ onNetworkRequestCallbackId: message.options.callbacks?.playwright?.onNetworkRequestCallbackId,
451
+ onNetworkResponseCallbackId: message.options.callbacks?.playwright?.onNetworkResponseCallbackId
452
+ },
397
453
  fs: {
398
454
  readFile: fsCallbacks?.readFile?.callbackId,
399
455
  writeFile: fsCallbacks?.writeFile?.callbackId,
@@ -405,7 +461,247 @@ async function handleCreateRuntime(message, connection, state) {
405
461
  },
406
462
  custom: new Map(customCallbacks ? Object.entries(customCallbacks).map(([name, reg]) => [name, reg.callbackId]) : [])
407
463
  };
408
- const runtime = await import_isolate_runtime.createInternalRuntime({
464
+ const instance = {
465
+ isolateId,
466
+ runtime: null,
467
+ ownerConnection: connection.socket,
468
+ callbacks: new Map,
469
+ createdAt: Date.now(),
470
+ lastActivity: Date.now(),
471
+ returnedCallbacks: new Map,
472
+ returnedPromises: new Map,
473
+ returnedIterators: new Map,
474
+ nextLocalCallbackId: 1e6,
475
+ namespaceId,
476
+ isDisposed: false,
477
+ isPoisoned: false,
478
+ callbackContext
479
+ };
480
+ let bridgedCustomFunctions;
481
+ let customFnMarshalOptions;
482
+ if (customCallbacks) {
483
+ const createMarshalContext = () => ({
484
+ registerCallback: (fn) => {
485
+ const callbackId = instance.nextLocalCallbackId++;
486
+ instance.returnedCallbacks.set(callbackId, fn);
487
+ return callbackId;
488
+ },
489
+ registerPromise: (promise) => {
490
+ const promiseId = instance.nextLocalCallbackId++;
491
+ instance.returnedPromises.set(promiseId, promise);
492
+ return promiseId;
493
+ },
494
+ registerIterator: (iterator) => {
495
+ const iteratorId = instance.nextLocalCallbackId++;
496
+ instance.returnedIterators.set(iteratorId, iterator);
497
+ return iteratorId;
498
+ }
499
+ });
500
+ const addCallbackIdsToRefs = (value) => {
501
+ if (value === null || typeof value !== "object")
502
+ return value;
503
+ if (import_isolate_protocol.isPromiseRef(value)) {
504
+ if ("__resolveCallbackId" in value)
505
+ return value;
506
+ const resolveCallbackId = instance.nextLocalCallbackId++;
507
+ instance.returnedCallbacks.set(resolveCallbackId, async (promiseId) => {
508
+ const promise = instance.returnedPromises.get(promiseId);
509
+ if (!promise)
510
+ throw new Error(`Promise ${promiseId} not found`);
511
+ const result2 = await promise;
512
+ instance.returnedPromises.delete(promiseId);
513
+ const ctx = createMarshalContext();
514
+ const marshalled = await import_isolate_protocol.marshalValue(result2, ctx);
515
+ return addCallbackIdsToRefs(marshalled);
516
+ });
517
+ return { ...value, __resolveCallbackId: resolveCallbackId };
518
+ }
519
+ if (import_isolate_protocol.isAsyncIteratorRef(value)) {
520
+ if ("__nextCallbackId" in value)
521
+ return value;
522
+ const nextCallbackId = instance.nextLocalCallbackId++;
523
+ instance.returnedCallbacks.set(nextCallbackId, async (iteratorId) => {
524
+ const iterator = instance.returnedIterators.get(iteratorId);
525
+ if (!iterator)
526
+ throw new Error(`Iterator ${iteratorId} not found`);
527
+ const result2 = await iterator.next();
528
+ if (result2.done)
529
+ instance.returnedIterators.delete(iteratorId);
530
+ const ctx = createMarshalContext();
531
+ const marshalledValue = await import_isolate_protocol.marshalValue(result2.value, ctx);
532
+ return { done: result2.done, value: addCallbackIdsToRefs(marshalledValue) };
533
+ });
534
+ const returnCallbackId = instance.nextLocalCallbackId++;
535
+ instance.returnedCallbacks.set(returnCallbackId, async (iteratorId, returnValue) => {
536
+ const iterator = instance.returnedIterators.get(iteratorId);
537
+ instance.returnedIterators.delete(iteratorId);
538
+ if (!iterator || !iterator.return)
539
+ return { done: true, value: undefined };
540
+ const result2 = await iterator.return(returnValue);
541
+ const ctx = createMarshalContext();
542
+ const marshalledValue = await import_isolate_protocol.marshalValue(result2.value, ctx);
543
+ return { done: true, value: addCallbackIdsToRefs(marshalledValue) };
544
+ });
545
+ return { ...value, __nextCallbackId: nextCallbackId, __returnCallbackId: returnCallbackId };
546
+ }
547
+ if (Array.isArray(value))
548
+ return value.map((item) => addCallbackIdsToRefs(item));
549
+ const result = {};
550
+ for (const key of Object.keys(value)) {
551
+ result[key] = addCallbackIdsToRefs(value[key]);
552
+ }
553
+ return result;
554
+ };
555
+ const LOCAL_CALLBACK_THRESHOLD = 1e6;
556
+ const invokeCallback = async (callbackId, args) => {
557
+ if (callbackId >= LOCAL_CALLBACK_THRESHOLD) {
558
+ const callback = instance.returnedCallbacks.get(callbackId);
559
+ if (!callback) {
560
+ throw new Error(`Local callback ${callbackId} not found`);
561
+ }
562
+ return await callback(...args);
563
+ } else {
564
+ const conn = callbackContext.connection;
565
+ if (!conn) {
566
+ throw new Error(`No connection available for callback ${callbackId}`);
567
+ }
568
+ return invokeClientCallback(conn, callbackId, args);
569
+ }
570
+ };
571
+ customFnMarshalOptions = { createMarshalContext, addCallbackIdsToRefs, invokeCallback };
572
+ bridgedCustomFunctions = {};
573
+ for (const [name, registration] of Object.entries(customCallbacks)) {
574
+ if (name.includes(":"))
575
+ continue;
576
+ const callbackContext_ = callbackContext;
577
+ if (registration.type === "asyncIterator") {
578
+ bridgedCustomFunctions[name] = {
579
+ type: "asyncIterator",
580
+ fn: (...args) => {
581
+ const startCallbackId = callbackContext_.custom.get(`${name}:start`);
582
+ const nextCallbackId = callbackContext_.custom.get(`${name}:next`);
583
+ const returnCallbackId = callbackContext_.custom.get(`${name}:return`);
584
+ async function* bridgedIterator() {
585
+ const conn = callbackContext_.connection;
586
+ if (!conn || startCallbackId === undefined) {
587
+ throw new Error(`AsyncIterator callback '${name}' not available`);
588
+ }
589
+ const startResult = await invokeClientCallback(conn, startCallbackId, args);
590
+ const iteratorId = startResult.iteratorId;
591
+ try {
592
+ while (true) {
593
+ const nextConn = callbackContext_.connection;
594
+ if (!nextConn || nextCallbackId === undefined) {
595
+ throw new Error(`AsyncIterator callback '${name}' not available`);
596
+ }
597
+ const nextResult = await invokeClientCallback(nextConn, nextCallbackId, [iteratorId]);
598
+ if (nextResult.done)
599
+ return nextResult.value;
600
+ yield nextResult.value;
601
+ }
602
+ } finally {
603
+ const retConn = callbackContext_.connection;
604
+ if (retConn && returnCallbackId !== undefined) {
605
+ await invokeClientCallback(retConn, returnCallbackId, [iteratorId]).catch(() => {});
606
+ }
607
+ }
608
+ }
609
+ return bridgedIterator();
610
+ }
611
+ };
612
+ } else {
613
+ bridgedCustomFunctions[name] = {
614
+ type: registration.type,
615
+ fn: async (...args) => {
616
+ const conn = callbackContext_.connection;
617
+ const cbId = callbackContext_.custom.get(name);
618
+ if (!conn || cbId === undefined) {
619
+ throw new Error(`Custom function callback '${name}' not available`);
620
+ }
621
+ return invokeClientCallback(conn, cbId, args);
622
+ }
623
+ };
624
+ }
625
+ }
626
+ }
627
+ let moduleLoader;
628
+ if (moduleLoaderCallback) {
629
+ moduleLoader = async (specifier, importer) => {
630
+ const conn = callbackContext.connection;
631
+ const cbId = callbackContext.moduleLoader;
632
+ if (!conn || cbId === undefined) {
633
+ throw new Error("Module loader callback not available");
634
+ }
635
+ return invokeClientCallback(conn, cbId, [specifier, importer]);
636
+ };
637
+ }
638
+ let testEnvironment;
639
+ if (message.options.testEnvironment) {
640
+ const testEnvOption = message.options.testEnvironment;
641
+ const testEnvOptions = typeof testEnvOption === "object" ? testEnvOption : undefined;
642
+ testEnvironment = {
643
+ onEvent: testEnvOptions?.callbacks?.onEvent ? (event) => {
644
+ const conn = callbackContext.connection;
645
+ const callbackId = callbackContext.testEnvironmentOnEvent;
646
+ if (!conn || callbackId === undefined) {
647
+ return;
648
+ }
649
+ const promise = invokeClientCallback(conn, callbackId, [JSON.stringify(event)]).catch(() => {});
650
+ instance.runtime?.pendingCallbacks?.push(promise);
651
+ } : undefined,
652
+ testTimeout: testEnvOptions?.testTimeout
653
+ };
654
+ if (testEnvOptions?.callbacks?.onEvent) {
655
+ instance.callbacks.set(testEnvOptions.callbacks.onEvent.callbackId, {
656
+ ...testEnvOptions.callbacks.onEvent,
657
+ name: "testEnvironment.onEvent"
658
+ });
659
+ }
660
+ }
661
+ let playwrightOptions;
662
+ const playwrightCallbacks = message.options.callbacks?.playwright;
663
+ if (playwrightCallbacks) {
664
+ playwrightOptions = {
665
+ handler: async (op) => {
666
+ const conn = callbackContext.connection;
667
+ const callbackId = callbackContext.playwright.handlerCallbackId;
668
+ if (!conn || callbackId === undefined) {
669
+ return {
670
+ ok: false,
671
+ error: {
672
+ name: "Error",
673
+ message: "Playwright handler callback not available"
674
+ }
675
+ };
676
+ }
677
+ try {
678
+ const resultJson = await invokeClientCallback(conn, callbackId, [JSON.stringify(op)]);
679
+ return JSON.parse(resultJson);
680
+ } catch (err) {
681
+ const error = err;
682
+ return { ok: false, error: { name: error.name, message: error.message } };
683
+ }
684
+ },
685
+ console: playwrightCallbacks.console,
686
+ onEvent: (event) => {
687
+ const conn = callbackContext.connection;
688
+ if (!conn) {
689
+ return;
690
+ }
691
+ if (event.type === "browserConsoleLog" && callbackContext.playwright.onBrowserConsoleLogCallbackId !== undefined) {
692
+ const promise = invokeClientCallback(conn, callbackContext.playwright.onBrowserConsoleLogCallbackId, [{ level: event.level, stdout: event.stdout, timestamp: event.timestamp }]).catch(() => {});
693
+ instance.runtime?.pendingCallbacks?.push(promise);
694
+ } else if (event.type === "networkRequest" && callbackContext.playwright.onNetworkRequestCallbackId !== undefined) {
695
+ const promise = invokeClientCallback(conn, callbackContext.playwright.onNetworkRequestCallbackId, [event]).catch(() => {});
696
+ instance.runtime?.pendingCallbacks?.push(promise);
697
+ } else if (event.type === "networkResponse" && callbackContext.playwright.onNetworkResponseCallbackId !== undefined) {
698
+ const promise = invokeClientCallback(conn, callbackContext.playwright.onNetworkResponseCallbackId, [event]).catch(() => {});
699
+ instance.runtime?.pendingCallbacks?.push(promise);
700
+ }
701
+ }
702
+ };
703
+ }
704
+ const runtime = await import_isolate_runtime.createRuntime({
409
705
  memoryLimitMB: message.options.memoryLimitMB ?? state.options.defaultMemoryLimitMB,
410
706
  cwd: message.options.cwd,
411
707
  console: {
@@ -415,28 +711,31 @@ async function handleCreateRuntime(message, connection, state) {
415
711
  if (!conn || callbackId === undefined)
416
712
  return;
417
713
  const promise = invokeClientCallback(conn, callbackId, [entry]).catch(() => {});
418
- pendingCallbacks.push(promise);
714
+ runtime.pendingCallbacks.push(promise);
419
715
  }
420
716
  },
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);
717
+ fetch: async (url, init) => {
718
+ const conn = callbackContext.connection;
719
+ const callbackId = callbackContext.fetch;
720
+ if (!conn || callbackId === undefined) {
721
+ throw new Error("Fetch callback not available");
436
722
  }
723
+ const serialized = {
724
+ url,
725
+ method: init.method,
726
+ headers: init.headers,
727
+ body: init.rawBody
728
+ };
729
+ const result = await invokeClientCallback(conn, callbackId, [serialized]);
730
+ if (result && typeof result === "object" && result.__streamingResponse) {
731
+ const response = result.response;
732
+ response.__isCallbackStream = true;
733
+ return response;
734
+ }
735
+ return import_isolate_protocol.deserializeResponse(result);
437
736
  },
438
737
  fs: {
439
- getDirectory: async (path2) => {
738
+ getDirectory: async (dirPath) => {
440
739
  const conn = callbackContext.connection;
441
740
  if (!conn) {
442
741
  throw new Error("FS callbacks not available");
@@ -445,35 +744,17 @@ async function handleCreateRuntime(message, connection, state) {
445
744
  connection: conn,
446
745
  callbackContext,
447
746
  invokeClientCallback,
448
- basePath: path2
747
+ basePath: dirPath
449
748
  });
450
749
  }
451
- }
750
+ },
751
+ moduleLoader,
752
+ customFunctions: bridgedCustomFunctions,
753
+ customFunctionsMarshalOptions: customFnMarshalOptions,
754
+ testEnvironment,
755
+ playwright: playwrightOptions
452
756
  });
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
- }
757
+ instance.runtime = runtime;
477
758
  if (consoleCallbacks?.onEntry) {
478
759
  instance.callbacks.set(consoleCallbacks.onEntry.callbackId, {
479
760
  ...consoleCallbacks.onEntry,
@@ -500,52 +781,33 @@ async function handleCreateRuntime(message, connection, state) {
500
781
  }
501
782
  }
502
783
  }
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
784
+ if (playwrightCallbacks) {
785
+ instance.callbacks.set(playwrightCallbacks.handlerCallbackId, {
786
+ callbackId: playwrightCallbacks.handlerCallbackId,
787
+ name: "playwright.handler",
788
+ type: "async"
513
789
  });
514
- instance.testEnvironmentEnabled = true;
515
- if (onEventCallback) {
516
- instance.callbacks.set(onEventCallback.callbackId, {
517
- ...onEventCallback,
518
- name: "testEnvironment.onEvent"
790
+ if (playwrightCallbacks.onBrowserConsoleLogCallbackId !== undefined) {
791
+ instance.callbacks.set(playwrightCallbacks.onBrowserConsoleLogCallbackId, {
792
+ callbackId: playwrightCallbacks.onBrowserConsoleLogCallbackId,
793
+ name: "playwright.onBrowserConsoleLog",
794
+ type: "sync"
795
+ });
796
+ }
797
+ if (playwrightCallbacks.onNetworkRequestCallbackId !== undefined) {
798
+ instance.callbacks.set(playwrightCallbacks.onNetworkRequestCallbackId, {
799
+ callbackId: playwrightCallbacks.onNetworkRequestCallbackId,
800
+ name: "playwright.onNetworkRequest",
801
+ type: "sync"
802
+ });
803
+ }
804
+ if (playwrightCallbacks.onNetworkResponseCallbackId !== undefined) {
805
+ instance.callbacks.set(playwrightCallbacks.onNetworkResponseCallbackId, {
806
+ callbackId: playwrightCallbacks.onNetworkResponseCallbackId,
807
+ name: "playwright.onNetworkResponse",
808
+ type: "sync"
519
809
  });
520
810
  }
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
811
  }
550
812
  state.isolates.set(isolateId, instance);
551
813
  connection.isolates.add(isolateId);
@@ -553,30 +815,99 @@ async function handleCreateRuntime(message, connection, state) {
553
815
  if (namespaceId != null) {
554
816
  state.namespacedRuntimes.set(namespaceId, instance);
555
817
  }
556
- instance.runtime.fetch.onWebSocketCommand((cmd) => {
818
+ runtime.fetch.onWebSocketCommand((cmd) => {
819
+ const targetConnection = callbackContext.connection;
820
+ if (!targetConnection) {
821
+ return;
822
+ }
557
823
  let data;
558
824
  if (cmd.data instanceof ArrayBuffer) {
559
825
  data = new Uint8Array(cmd.data);
560
826
  } else {
561
827
  data = cmd.data;
562
828
  }
563
- const wsCommandMsg = {
564
- type: import_isolate_protocol.MessageType.WS_COMMAND,
829
+ const payload = {
830
+ type: cmd.type,
831
+ connectionId: cmd.connectionId,
832
+ data,
833
+ code: cmd.code,
834
+ reason: cmd.reason
835
+ };
836
+ sendMessage(targetConnection.socket, {
837
+ type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
565
838
  isolateId,
566
- command: {
567
- type: cmd.type,
568
- connectionId: cmd.connectionId,
569
- data,
839
+ event: import_isolate_protocol.IsolateEvents.WS_COMMAND,
840
+ payload
841
+ });
842
+ });
843
+ runtime.fetch.onClientWebSocketCommand((cmd) => {
844
+ const targetConnection = callbackContext.connection;
845
+ if (!targetConnection) {
846
+ return;
847
+ }
848
+ let data;
849
+ if (cmd.data instanceof ArrayBuffer) {
850
+ data = new Uint8Array(cmd.data);
851
+ } else {
852
+ data = cmd.data;
853
+ }
854
+ if (cmd.type === "connect") {
855
+ const payload = {
856
+ socketId: cmd.socketId,
857
+ url: cmd.url,
858
+ protocols: cmd.protocols
859
+ };
860
+ sendMessage(targetConnection.socket, {
861
+ type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
862
+ isolateId,
863
+ event: import_isolate_protocol.IsolateEvents.WS_CLIENT_CONNECT,
864
+ payload
865
+ });
866
+ } else if (cmd.type === "send") {
867
+ const payload = {
868
+ socketId: cmd.socketId,
869
+ data
870
+ };
871
+ sendMessage(targetConnection.socket, {
872
+ type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
873
+ isolateId,
874
+ event: import_isolate_protocol.IsolateEvents.WS_CLIENT_SEND,
875
+ payload
876
+ });
877
+ } else if (cmd.type === "close") {
878
+ const payload = {
879
+ socketId: cmd.socketId,
570
880
  code: cmd.code,
571
881
  reason: cmd.reason
572
- }
573
- };
574
- sendMessage(connection.socket, wsCommandMsg);
882
+ };
883
+ sendMessage(targetConnection.socket, {
884
+ type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
885
+ isolateId,
886
+ event: import_isolate_protocol.IsolateEvents.WS_CLIENT_CLOSE,
887
+ payload
888
+ });
889
+ }
890
+ });
891
+ runtime.fetch.onEvent((eventName, payload) => {
892
+ const targetConnection = callbackContext.connection;
893
+ if (!targetConnection) {
894
+ return;
895
+ }
896
+ sendMessage(targetConnection.socket, {
897
+ type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
898
+ isolateId,
899
+ event: eventName,
900
+ payload
901
+ });
575
902
  });
576
903
  sendOk(connection.socket, message.requestId, { isolateId, reused: false });
577
904
  } catch (err) {
578
905
  const error = err;
579
906
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
907
+ } finally {
908
+ if (namespaceCreationLocked && namespaceId != null) {
909
+ state.namespacedCreatesInFlight.delete(namespaceId);
910
+ }
580
911
  }
581
912
  }
582
913
  async function handleDisposeRuntime(message, connection, state) {
@@ -592,17 +923,20 @@ async function handleDisposeRuntime(message, connection, state) {
592
923
  try {
593
924
  connection.isolates.delete(message.isolateId);
594
925
  if (instance.namespaceId != null) {
595
- softDeleteRuntime(instance, state);
596
- } else {
597
- if (instance.playwrightHandle) {
598
- instance.playwrightHandle.dispose();
926
+ if (instance.isPoisoned) {
927
+ await hardDeleteRuntime(instance, state);
928
+ } else {
929
+ softDeleteRuntime(instance, state);
599
930
  }
600
- instance.runtime.dispose();
601
- state.isolates.delete(message.isolateId);
931
+ } else {
932
+ await hardDeleteRuntime(instance, state);
602
933
  }
603
934
  sendOk(connection.socket, message.requestId);
604
935
  } catch (err) {
605
936
  const error = err;
937
+ if (instance.namespaceId != null && isLinkerConflictError(error)) {
938
+ instance.isPoisoned = true;
939
+ }
606
940
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
607
941
  }
608
942
  }
@@ -614,47 +948,16 @@ async function handleEval(message, connection, state) {
614
948
  }
615
949
  instance.lastActivity = Date.now();
616
950
  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
951
+ await instance.runtime.eval(message.code, {
952
+ filename: message.filename
627
953
  });
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
954
  sendOk(connection.socket, message.requestId, { value: undefined });
651
955
  } catch (err) {
652
956
  const error = err;
653
- if (error.stack && instance.sourceMaps?.size) {
654
- error.stack = import_isolate_transform.mapErrorStack(error.stack, instance.sourceMaps);
957
+ if (instance.namespaceId != null && isLinkerConflictError(error)) {
958
+ instance.isPoisoned = true;
655
959
  }
656
- const isTimeoutError = error.message?.includes("Script execution timed out");
657
- 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 });
960
+ sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
658
961
  }
659
962
  }
660
963
  async function handleDispatchRequest(message, connection, state) {
@@ -762,6 +1065,39 @@ async function handleWsClose(message, connection, state) {
762
1065
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
763
1066
  }
764
1067
  }
1068
+ function handleClientEvent(message, connection, state) {
1069
+ const instance = state.isolates.get(message.isolateId);
1070
+ if (!instance)
1071
+ return;
1072
+ instance.lastActivity = Date.now();
1073
+ switch (message.event) {
1074
+ case import_isolate_protocol.ClientEvents.WS_CLIENT_OPENED: {
1075
+ const payload = message.payload;
1076
+ instance.runtime.fetch.dispatchClientWebSocketOpen(payload.socketId, payload.protocol, payload.extensions);
1077
+ break;
1078
+ }
1079
+ case import_isolate_protocol.ClientEvents.WS_CLIENT_MESSAGE: {
1080
+ const payload = message.payload;
1081
+ const data = payload.data instanceof Uint8Array ? payload.data.buffer.slice(payload.data.byteOffset, payload.data.byteOffset + payload.data.byteLength) : payload.data;
1082
+ instance.runtime.fetch.dispatchClientWebSocketMessage(payload.socketId, data);
1083
+ break;
1084
+ }
1085
+ case import_isolate_protocol.ClientEvents.WS_CLIENT_CLOSED: {
1086
+ const payload = message.payload;
1087
+ instance.runtime.fetch.dispatchClientWebSocketClose(payload.socketId, payload.code, payload.reason, payload.wasClean);
1088
+ break;
1089
+ }
1090
+ case import_isolate_protocol.ClientEvents.WS_CLIENT_ERROR: {
1091
+ const payload = message.payload;
1092
+ instance.runtime.fetch.dispatchClientWebSocketError(payload.socketId);
1093
+ break;
1094
+ }
1095
+ default: {
1096
+ instance.runtime.fetch.dispatchEvent(message.event, message.payload);
1097
+ break;
1098
+ }
1099
+ }
1100
+ }
765
1101
  async function handleFetchGetUpgradeRequest(message, connection, state) {
766
1102
  const instance = state.isolates.get(message.isolateId);
767
1103
  if (!instance) {
@@ -904,9 +1240,6 @@ function handleCallbackResponse(message, connection) {
904
1240
  return;
905
1241
  }
906
1242
  connection.pendingCallbacks.delete(message.requestId);
907
- if (pending.timeoutId) {
908
- clearTimeout(pending.timeoutId);
909
- }
910
1243
  if (message.error) {
911
1244
  const error = new Error(message.error.message);
912
1245
  error.name = message.error.name;
@@ -918,17 +1251,12 @@ function handleCallbackResponse(message, connection) {
918
1251
  pending.resolve(message.result);
919
1252
  }
920
1253
  }
921
- async function invokeClientCallback(connection, callbackId, args, timeout = 1e4) {
1254
+ async function invokeClientCallback(connection, callbackId, args) {
922
1255
  const requestId = connection.nextCallbackId++;
923
1256
  return new Promise((resolve, reject) => {
924
- const timeoutId = setTimeout(() => {
925
- connection.pendingCallbacks.delete(requestId);
926
- reject(new Error("Callback timeout"));
927
- }, timeout);
928
1257
  const pending = {
929
1258
  resolve,
930
- reject,
931
- timeoutId
1259
+ reject
932
1260
  };
933
1261
  connection.pendingCallbacks.set(requestId, pending);
934
1262
  const invoke = {
@@ -940,530 +1268,6 @@ async function invokeClientCallback(connection, callbackId, args, timeout = 1e4)
940
1268
  sendMessage(connection.socket, invoke);
941
1269
  });
942
1270
  }
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
- function deserializeResponse(data) {
1461
- return new Response(data.body, {
1462
- status: data.status,
1463
- statusText: data.statusText,
1464
- headers: data.headers
1465
- });
1466
- }
1467
1271
  function handleStreamPush(message, connection) {
1468
1272
  const receiver = connection.streamReceivers.get(message.streamId);
1469
1273
  if (!receiver) {
@@ -1591,9 +1395,6 @@ function handleCallbackStreamStart(message, connection) {
1591
1395
  const pending = connection.pendingCallbacks.get(message.requestId);
1592
1396
  if (pending) {
1593
1397
  connection.pendingCallbacks.delete(message.requestId);
1594
- if (pending.timeoutId) {
1595
- clearTimeout(pending.timeoutId);
1596
- }
1597
1398
  const response = new Response(readableStream, {
1598
1399
  status: message.metadata.status,
1599
1400
  statusText: message.metadata.statusText,
@@ -1723,28 +1524,11 @@ async function handleRunTests(message, connection, state) {
1723
1524
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1724
1525
  return;
1725
1526
  }
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
1527
  instance.lastActivity = Date.now();
1731
1528
  try {
1732
1529
  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
- }
1530
+ const results = await instance.runtime.testEnvironment.runTests(timeout);
1531
+ sendOk(connection.socket, message.requestId, results);
1748
1532
  } catch (err) {
1749
1533
  const error = err;
1750
1534
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
@@ -1756,13 +1540,9 @@ async function handleResetTestEnv(message, connection, state) {
1756
1540
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1757
1541
  return;
1758
1542
  }
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
1543
  instance.lastActivity = Date.now();
1764
1544
  try {
1765
- await instance.runtime.context.eval("__resetTestEnvironment()", { promise: true });
1545
+ instance.runtime.testEnvironment.reset();
1766
1546
  sendOk(connection.socket, message.requestId);
1767
1547
  } catch (err) {
1768
1548
  const error = err;
@@ -1775,13 +1555,9 @@ async function handleHasTests(message, connection, state) {
1775
1555
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1776
1556
  return;
1777
1557
  }
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
1558
  instance.lastActivity = Date.now();
1783
1559
  try {
1784
- const result = import_isolate_test_environment.hasTests(instance.runtime.context);
1560
+ const result = instance.runtime.testEnvironment.hasTests();
1785
1561
  sendOk(connection.socket, message.requestId, result);
1786
1562
  } catch (err) {
1787
1563
  const error = err;
@@ -1794,42 +1570,24 @@ async function handleGetTestCount(message, connection, state) {
1794
1570
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1795
1571
  return;
1796
1572
  }
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
1573
  instance.lastActivity = Date.now();
1802
1574
  try {
1803
- const result = import_isolate_test_environment.getTestCount(instance.runtime.context);
1575
+ const result = instance.runtime.testEnvironment.getTestCount();
1804
1576
  sendOk(connection.socket, message.requestId, result);
1805
1577
  } catch (err) {
1806
1578
  const error = err;
1807
1579
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
1808
1580
  }
1809
1581
  }
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
1582
  async function handleGetCollectedData(message, connection, state) {
1817
1583
  const instance = state.isolates.get(message.isolateId);
1818
1584
  if (!instance) {
1819
1585
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1820
1586
  return;
1821
1587
  }
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
1588
  instance.lastActivity = Date.now();
1827
1589
  try {
1828
- const data = {
1829
- browserConsoleLogs: instance.playwrightHandle.getBrowserConsoleLogs(),
1830
- networkRequests: instance.playwrightHandle.getNetworkRequests(),
1831
- networkResponses: instance.playwrightHandle.getNetworkResponses()
1832
- };
1590
+ const data = instance.runtime.playwright.getCollectedData();
1833
1591
  sendOk(connection.socket, message.requestId, data);
1834
1592
  } catch (err) {
1835
1593
  const error = err;
@@ -1842,13 +1600,9 @@ async function handleClearCollectedData(message, connection, state) {
1842
1600
  sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
1843
1601
  return;
1844
1602
  }
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
1603
  instance.lastActivity = Date.now();
1850
1604
  try {
1851
- instance.playwrightHandle.clearCollected();
1605
+ instance.runtime.playwright.clearCollectedData();
1852
1606
  sendOk(connection.socket, message.requestId);
1853
1607
  } catch (err) {
1854
1608
  const error = err;
@@ -1856,4 +1610,4 @@ async function handleClearCollectedData(message, connection, state) {
1856
1610
  }
1857
1611
  }
1858
1612
 
1859
- //# debugId=32017BAA615D918C64756E2164756E21
1613
+ //# debugId=20B12DB74E4F312D64756E2164756E21