@ricsam/isolate-runtime 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.
- package/README.md +88 -27
- package/dist/cjs/index.cjs +308 -39
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/index.mjs +314 -25
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/index.d.ts +41 -5
- package/package.json +1 -1
- package/dist/cjs/internal.cjs +0 -95
- package/dist/cjs/internal.cjs.map +0 -10
- package/dist/mjs/internal.mjs +0 -52
- package/dist/mjs/internal.mjs.map +0 -10
- package/dist/types/internal.d.ts +0 -39
package/README.md
CHANGED
|
@@ -115,20 +115,16 @@ interface RuntimeOptions {
|
|
|
115
115
|
cwd?: string;
|
|
116
116
|
/** Enable test environment (describe, it, expect) */
|
|
117
117
|
testEnvironment?: boolean | TestEnvironmentOptions;
|
|
118
|
-
/** Playwright options -
|
|
118
|
+
/** Playwright options (handler-first public API) */
|
|
119
119
|
playwright?: PlaywrightOptions;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
interface PlaywrightOptions {
|
|
123
|
-
|
|
123
|
+
handler: (op: PlaywrightOperation) => Promise<PlaywrightResult>;
|
|
124
124
|
timeout?: number;
|
|
125
|
-
baseUrl?: string;
|
|
126
125
|
/** Print browser console logs to stdout */
|
|
127
126
|
console?: boolean;
|
|
128
|
-
|
|
129
|
-
onBrowserConsoleLog?: (entry: { level: string; stdout: string; timestamp: number }) => void;
|
|
130
|
-
onNetworkRequest?: (info: { url: string; method: string; headers: Record<string, string>; timestamp: number }) => void;
|
|
131
|
-
onNetworkResponse?: (info: { url: string; status: number; headers: Record<string, string>; timestamp: number }) => void;
|
|
127
|
+
onEvent?: (event: PlaywrightEvent) => void;
|
|
132
128
|
}
|
|
133
129
|
```
|
|
134
130
|
|
|
@@ -310,21 +306,27 @@ type TestEvent =
|
|
|
310
306
|
|
|
311
307
|
## Playwright Integration
|
|
312
308
|
|
|
313
|
-
Run browser automation with untrusted code.
|
|
309
|
+
Run browser automation with untrusted code. Public API is handler-first:
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import { defaultPlaywrightHandler } from "@ricsam/isolate-playwright/client";
|
|
313
|
+
|
|
314
|
+
playwright: { handler: defaultPlaywrightHandler(page) }
|
|
315
|
+
```
|
|
314
316
|
|
|
315
317
|
### Script Mode (No Tests)
|
|
316
318
|
|
|
317
319
|
```typescript
|
|
318
320
|
import { createRuntime } from "@ricsam/isolate-runtime";
|
|
319
321
|
import { chromium } from "playwright";
|
|
322
|
+
import { defaultPlaywrightHandler } from "@ricsam/isolate-playwright/client";
|
|
320
323
|
|
|
321
324
|
const browser = await chromium.launch({ headless: true });
|
|
322
325
|
const page = await browser.newPage();
|
|
323
326
|
|
|
324
327
|
const runtime = await createRuntime({
|
|
325
328
|
playwright: {
|
|
326
|
-
page,
|
|
327
|
-
baseUrl: "https://example.com",
|
|
329
|
+
handler: defaultPlaywrightHandler(page),
|
|
328
330
|
console: true, // Print browser console to stdout
|
|
329
331
|
},
|
|
330
332
|
});
|
|
@@ -351,6 +353,7 @@ Combine `testEnvironment` and `playwright` for browser testing. Playwright exten
|
|
|
351
353
|
```typescript
|
|
352
354
|
import { createRuntime } from "@ricsam/isolate-runtime";
|
|
353
355
|
import { chromium } from "playwright";
|
|
356
|
+
import { defaultPlaywrightHandler } from "@ricsam/isolate-playwright/client";
|
|
354
357
|
|
|
355
358
|
const browser = await chromium.launch({ headless: true });
|
|
356
359
|
const page = await browser.newPage();
|
|
@@ -358,9 +361,12 @@ const page = await browser.newPage();
|
|
|
358
361
|
const runtime = await createRuntime({
|
|
359
362
|
testEnvironment: true, // Provides describe, it, expect
|
|
360
363
|
playwright: {
|
|
361
|
-
page,
|
|
362
|
-
|
|
363
|
-
|
|
364
|
+
handler: defaultPlaywrightHandler(page),
|
|
365
|
+
onEvent: (event) => {
|
|
366
|
+
if (event.type === "browserConsoleLog") {
|
|
367
|
+
console.log("[browser]", event.stdout);
|
|
368
|
+
}
|
|
369
|
+
},
|
|
364
370
|
},
|
|
365
371
|
});
|
|
366
372
|
|
|
@@ -386,6 +392,74 @@ await runtime.dispose();
|
|
|
386
392
|
await browser.close();
|
|
387
393
|
```
|
|
388
394
|
|
|
395
|
+
### Multi-Page Testing
|
|
396
|
+
|
|
397
|
+
For tests that need multiple pages or browser contexts, provide `createPage` and/or `createContext` callbacks:
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
import { createRuntime } from "@ricsam/isolate-runtime";
|
|
401
|
+
import { chromium } from "playwright";
|
|
402
|
+
import { defaultPlaywrightHandler } from "@ricsam/isolate-playwright/client";
|
|
403
|
+
|
|
404
|
+
const browser = await chromium.launch({ headless: true });
|
|
405
|
+
const browserContext = await browser.newContext();
|
|
406
|
+
const page = await browserContext.newPage();
|
|
407
|
+
|
|
408
|
+
const runtime = await createRuntime({
|
|
409
|
+
testEnvironment: true,
|
|
410
|
+
playwright: {
|
|
411
|
+
handler: defaultPlaywrightHandler(page, {
|
|
412
|
+
// Called when isolate code calls context.newPage()
|
|
413
|
+
createPage: async (context) => context.newPage(),
|
|
414
|
+
// Called when isolate code calls browser.newContext()
|
|
415
|
+
createContext: async (options) => browser.newContext(options),
|
|
416
|
+
}),
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
await runtime.eval(`
|
|
421
|
+
test('multi-page test', async () => {
|
|
422
|
+
// Create additional pages
|
|
423
|
+
const page2 = await context.newPage();
|
|
424
|
+
|
|
425
|
+
// Navigate independently
|
|
426
|
+
await page.goto('https://example.com/page1');
|
|
427
|
+
await page2.goto('https://example.com/page2');
|
|
428
|
+
|
|
429
|
+
// Work with multiple pages
|
|
430
|
+
await page.locator('#button').click();
|
|
431
|
+
await page2.locator('#input').fill('text');
|
|
432
|
+
|
|
433
|
+
await page2.close();
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test('multi-context test', async () => {
|
|
437
|
+
// Create isolated context (separate cookies, storage)
|
|
438
|
+
const ctx2 = await browser.newContext();
|
|
439
|
+
const page2 = await ctx2.newPage();
|
|
440
|
+
|
|
441
|
+
// Cookies are isolated between contexts
|
|
442
|
+
await context.addCookies([{ name: 'test', value: '1', domain: 'example.com', path: '/' }]);
|
|
443
|
+
const ctx1Cookies = await context.cookies();
|
|
444
|
+
const ctx2Cookies = await ctx2.cookies();
|
|
445
|
+
|
|
446
|
+
expect(ctx1Cookies.some(c => c.name === 'test')).toBe(true);
|
|
447
|
+
expect(ctx2Cookies.some(c => c.name === 'test')).toBe(false);
|
|
448
|
+
|
|
449
|
+
await ctx2.close();
|
|
450
|
+
});
|
|
451
|
+
`);
|
|
452
|
+
|
|
453
|
+
const results = await runtime.testEnvironment.runTests();
|
|
454
|
+
await runtime.dispose();
|
|
455
|
+
await browser.close();
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**Behavior without lifecycle callbacks:**
|
|
459
|
+
- `context.newPage()` without `createPage`: Throws error
|
|
460
|
+
- `browser.newContext()` without `createContext`: Throws error
|
|
461
|
+
- `context.cookies()`, `context.addCookies()`, `context.clearCookies()`: Work without callbacks
|
|
462
|
+
|
|
389
463
|
## Included APIs
|
|
390
464
|
|
|
391
465
|
- Core (Blob, File, streams, URL, TextEncoder/Decoder)
|
|
@@ -397,20 +471,7 @@ await browser.close();
|
|
|
397
471
|
- Fetch API
|
|
398
472
|
- File System (if handler provided)
|
|
399
473
|
- Test Environment (if enabled)
|
|
400
|
-
- Playwright (if
|
|
401
|
-
|
|
402
|
-
## Legacy API
|
|
403
|
-
|
|
404
|
-
For backwards compatibility with code that needs direct isolate/context access:
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
import { createLegacyRuntime } from "@ricsam/isolate-runtime";
|
|
408
|
-
|
|
409
|
-
const runtime = await createLegacyRuntime();
|
|
410
|
-
// runtime.isolate and runtime.context are available
|
|
411
|
-
await runtime.context.eval(`console.log("Hello")`);
|
|
412
|
-
runtime.dispose(); // sync
|
|
413
|
-
```
|
|
474
|
+
- Playwright (if handler provided)
|
|
414
475
|
|
|
415
476
|
## License
|
|
416
477
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -4,23 +4,6 @@ var __defProp = Object.defineProperty;
|
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __reExport = (target, mod, secondTarget) => {
|
|
8
|
-
for (let key of __getOwnPropNames(mod))
|
|
9
|
-
if (!__hasOwnProp.call(target, key) && key !== "default")
|
|
10
|
-
__defProp(target, key, {
|
|
11
|
-
get: () => mod[key],
|
|
12
|
-
enumerable: true
|
|
13
|
-
});
|
|
14
|
-
if (secondTarget) {
|
|
15
|
-
for (let key of __getOwnPropNames(mod))
|
|
16
|
-
if (!__hasOwnProp.call(secondTarget, key) && key !== "default")
|
|
17
|
-
__defProp(secondTarget, key, {
|
|
18
|
-
get: () => mod[key],
|
|
19
|
-
enumerable: true
|
|
20
|
-
});
|
|
21
|
-
return secondTarget;
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
7
|
var __toESM = (mod, isNodeMode, target) => {
|
|
25
8
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
26
9
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
@@ -74,6 +57,8 @@ __export(exports_src, {
|
|
|
74
57
|
normalizeEntryFilename: () => import_isolate_protocol2.normalizeEntryFilename,
|
|
75
58
|
hasTests: () => import_isolate_test_environment2.hasTests,
|
|
76
59
|
getTestCount: () => import_isolate_test_environment2.getTestCount,
|
|
60
|
+
getDefaultPlaywrightHandlerMetadata: () => import_isolate_playwright2.getDefaultPlaywrightHandlerMetadata,
|
|
61
|
+
defaultPlaywrightHandler: () => import_isolate_playwright2.defaultPlaywrightHandler,
|
|
77
62
|
createRuntime: () => createRuntime,
|
|
78
63
|
createPlaywrightHandler: () => import_isolate_playwright2.createPlaywrightHandler,
|
|
79
64
|
createNodeFileSystemHandler: () => import_isolate_fs2.createNodeFileSystemHandler
|
|
@@ -106,7 +91,6 @@ var import_isolate_path2 = require("@ricsam/isolate-path");
|
|
|
106
91
|
var import_isolate_timers2 = require("@ricsam/isolate-timers");
|
|
107
92
|
var import_isolate_test_environment2 = require("@ricsam/isolate-test-environment");
|
|
108
93
|
var import_isolate_playwright2 = require("@ricsam/isolate-playwright");
|
|
109
|
-
__reExport(exports_src, require("./internal.cjs"), module.exports);
|
|
110
94
|
var iteratorSessions = new Map;
|
|
111
95
|
var nextIteratorId = 1;
|
|
112
96
|
var ISOLATE_MARSHAL_CODE = `
|
|
@@ -232,6 +216,68 @@ var ISOLATE_MARSHAL_CODE = `
|
|
|
232
216
|
}
|
|
233
217
|
return fd;
|
|
234
218
|
}
|
|
219
|
+
case 'CallbackRef': {
|
|
220
|
+
// Create a proxy function that invokes the callback
|
|
221
|
+
const callbackId = value.callbackId;
|
|
222
|
+
return function(...args) {
|
|
223
|
+
const argsJson = JSON.stringify(marshalForHost(args));
|
|
224
|
+
const resultJson = __customFn_invoke.applySyncPromise(undefined, [callbackId, argsJson]);
|
|
225
|
+
const result = JSON.parse(resultJson);
|
|
226
|
+
if (result.ok) {
|
|
227
|
+
return unmarshalFromHost(result.value);
|
|
228
|
+
} else {
|
|
229
|
+
const error = new Error(result.error.message);
|
|
230
|
+
error.name = result.error.name;
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
case 'PromiseRef': {
|
|
236
|
+
// Create a proxy Promise that resolves via callback
|
|
237
|
+
const promiseId = value.promiseId;
|
|
238
|
+
return new Promise((resolve, reject) => {
|
|
239
|
+
try {
|
|
240
|
+
const argsJson = JSON.stringify([promiseId]);
|
|
241
|
+
const resultJson = __customFn_invoke.applySyncPromise(undefined, [value.__resolveCallbackId, argsJson]);
|
|
242
|
+
const result = JSON.parse(resultJson);
|
|
243
|
+
if (result.ok) {
|
|
244
|
+
resolve(unmarshalFromHost(result.value));
|
|
245
|
+
} else {
|
|
246
|
+
reject(new Error(result.error.message));
|
|
247
|
+
}
|
|
248
|
+
} catch (e) {
|
|
249
|
+
reject(e);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
case 'AsyncIteratorRef': {
|
|
254
|
+
const iteratorId = value.iteratorId;
|
|
255
|
+
const nextCallbackId = value.__nextCallbackId;
|
|
256
|
+
const returnCallbackId = value.__returnCallbackId;
|
|
257
|
+
return {
|
|
258
|
+
[Symbol.asyncIterator]() { return this; },
|
|
259
|
+
async next() {
|
|
260
|
+
const argsJson = JSON.stringify([iteratorId]);
|
|
261
|
+
const resultJson = __customFn_invoke.applySyncPromise(undefined, [nextCallbackId, argsJson]);
|
|
262
|
+
const result = JSON.parse(resultJson);
|
|
263
|
+
if (!result.ok) {
|
|
264
|
+
const error = new Error(result.error.message);
|
|
265
|
+
error.name = result.error.name;
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
done: result.value.done,
|
|
270
|
+
value: unmarshalFromHost(result.value.value)
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
async return(v) {
|
|
274
|
+
const argsJson = JSON.stringify([iteratorId, marshalForHost(v)]);
|
|
275
|
+
const resultJson = __customFn_invoke.applySyncPromise(undefined, [returnCallbackId, argsJson]);
|
|
276
|
+
const result = JSON.parse(resultJson);
|
|
277
|
+
return { done: true, value: result.ok ? unmarshalFromHost(result.value) : undefined };
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
235
281
|
default:
|
|
236
282
|
// Unknown ref type, return as-is
|
|
237
283
|
break;
|
|
@@ -255,9 +301,27 @@ var ISOLATE_MARSHAL_CODE = `
|
|
|
255
301
|
globalThis.__unmarshalFromHost = unmarshalFromHost;
|
|
256
302
|
})();
|
|
257
303
|
`;
|
|
258
|
-
async function setupCustomFunctions(context, customFunctions) {
|
|
304
|
+
async function setupCustomFunctions(context, customFunctions, marshalOptions) {
|
|
259
305
|
const global = context.global;
|
|
260
|
-
const invokeCallbackRef = new import_isolated_vm.default.Reference(async (
|
|
306
|
+
const invokeCallbackRef = new import_isolated_vm.default.Reference(async (nameOrId, argsJson) => {
|
|
307
|
+
if (typeof nameOrId === "number" && marshalOptions) {
|
|
308
|
+
const rawArgs2 = JSON.parse(argsJson);
|
|
309
|
+
const args2 = import_isolate_protocol3.unmarshalValue(rawArgs2);
|
|
310
|
+
try {
|
|
311
|
+
const result = await marshalOptions.invokeCallback(nameOrId, args2);
|
|
312
|
+
const ctx = marshalOptions.createMarshalContext();
|
|
313
|
+
const marshalledResult = await import_isolate_protocol3.marshalValue(result, ctx);
|
|
314
|
+
const processedResult = marshalOptions.addCallbackIdsToRefs(marshalledResult);
|
|
315
|
+
return JSON.stringify({ ok: true, value: processedResult });
|
|
316
|
+
} catch (error) {
|
|
317
|
+
const err = error;
|
|
318
|
+
return JSON.stringify({
|
|
319
|
+
ok: false,
|
|
320
|
+
error: { message: err.message, name: err.name }
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const name = String(nameOrId);
|
|
261
325
|
const def = customFunctions[name];
|
|
262
326
|
if (!def) {
|
|
263
327
|
return JSON.stringify({
|
|
@@ -271,7 +335,13 @@ async function setupCustomFunctions(context, customFunctions) {
|
|
|
271
335
|
const rawArgs = JSON.parse(argsJson);
|
|
272
336
|
const args = import_isolate_protocol3.unmarshalValue(rawArgs);
|
|
273
337
|
try {
|
|
274
|
-
const result =
|
|
338
|
+
const result = await def.fn(...args);
|
|
339
|
+
if (marshalOptions) {
|
|
340
|
+
const ctx = marshalOptions.createMarshalContext();
|
|
341
|
+
const marshalledResult2 = await import_isolate_protocol3.marshalValue(result, ctx);
|
|
342
|
+
const processedResult = marshalOptions.addCallbackIdsToRefs(marshalledResult2);
|
|
343
|
+
return JSON.stringify({ ok: true, value: processedResult });
|
|
344
|
+
}
|
|
275
345
|
const marshalledResult = await import_isolate_protocol3.marshalValue(result);
|
|
276
346
|
return JSON.stringify({ ok: true, value: marshalledResult });
|
|
277
347
|
} catch (error) {
|
|
@@ -327,7 +397,14 @@ async function setupCustomFunctions(context, customFunctions) {
|
|
|
327
397
|
if (result.done) {
|
|
328
398
|
iteratorSessions.delete(iteratorId);
|
|
329
399
|
}
|
|
330
|
-
|
|
400
|
+
let marshalledValue;
|
|
401
|
+
if (marshalOptions) {
|
|
402
|
+
const ctx = marshalOptions.createMarshalContext();
|
|
403
|
+
marshalledValue = await import_isolate_protocol3.marshalValue(result.value, ctx);
|
|
404
|
+
marshalledValue = marshalOptions.addCallbackIdsToRefs(marshalledValue);
|
|
405
|
+
} else {
|
|
406
|
+
marshalledValue = await import_isolate_protocol3.marshalValue(result.value);
|
|
407
|
+
}
|
|
331
408
|
return JSON.stringify({
|
|
332
409
|
ok: true,
|
|
333
410
|
done: result.done,
|
|
@@ -353,7 +430,14 @@ async function setupCustomFunctions(context, customFunctions) {
|
|
|
353
430
|
const value = import_isolate_protocol3.unmarshalValue(rawValue);
|
|
354
431
|
const result = await session.iterator.return?.(value);
|
|
355
432
|
iteratorSessions.delete(iteratorId);
|
|
356
|
-
|
|
433
|
+
let marshalledValue;
|
|
434
|
+
if (marshalOptions) {
|
|
435
|
+
const ctx = marshalOptions.createMarshalContext();
|
|
436
|
+
marshalledValue = await import_isolate_protocol3.marshalValue(result?.value, ctx);
|
|
437
|
+
marshalledValue = marshalOptions.addCallbackIdsToRefs(marshalledValue);
|
|
438
|
+
} else {
|
|
439
|
+
marshalledValue = await import_isolate_protocol3.marshalValue(result?.value);
|
|
440
|
+
}
|
|
357
441
|
return JSON.stringify({ ok: true, done: true, value: marshalledValue });
|
|
358
442
|
} catch (error) {
|
|
359
443
|
const err = error;
|
|
@@ -383,7 +467,14 @@ async function setupCustomFunctions(context, customFunctions) {
|
|
|
383
467
|
});
|
|
384
468
|
const result = await session.iterator.throw?.(error);
|
|
385
469
|
iteratorSessions.delete(iteratorId);
|
|
386
|
-
|
|
470
|
+
let marshalledValue;
|
|
471
|
+
if (marshalOptions) {
|
|
472
|
+
const ctx = marshalOptions.createMarshalContext();
|
|
473
|
+
marshalledValue = await import_isolate_protocol3.marshalValue(result?.value, ctx);
|
|
474
|
+
marshalledValue = marshalOptions.addCallbackIdsToRefs(marshalledValue);
|
|
475
|
+
} else {
|
|
476
|
+
marshalledValue = await import_isolate_protocol3.marshalValue(result?.value);
|
|
477
|
+
}
|
|
387
478
|
return JSON.stringify({
|
|
388
479
|
ok: true,
|
|
389
480
|
done: result?.done ?? true,
|
|
@@ -474,8 +565,114 @@ async function setupCustomFunctions(context, customFunctions) {
|
|
|
474
565
|
}
|
|
475
566
|
return invokeCallbackRef;
|
|
476
567
|
}
|
|
568
|
+
function createLocalCustomFunctionsMarshalOptions() {
|
|
569
|
+
const returnedCallbacks = new Map;
|
|
570
|
+
const returnedPromises = new Map;
|
|
571
|
+
const returnedIterators = new Map;
|
|
572
|
+
let nextLocalCallbackId = 1e6;
|
|
573
|
+
const createMarshalContext = () => ({
|
|
574
|
+
registerCallback: (fn) => {
|
|
575
|
+
const callbackId = nextLocalCallbackId++;
|
|
576
|
+
returnedCallbacks.set(callbackId, fn);
|
|
577
|
+
return callbackId;
|
|
578
|
+
},
|
|
579
|
+
registerPromise: (promise) => {
|
|
580
|
+
const promiseId = nextLocalCallbackId++;
|
|
581
|
+
returnedPromises.set(promiseId, promise);
|
|
582
|
+
return promiseId;
|
|
583
|
+
},
|
|
584
|
+
registerIterator: (iterator) => {
|
|
585
|
+
const iteratorId = nextLocalCallbackId++;
|
|
586
|
+
returnedIterators.set(iteratorId, iterator);
|
|
587
|
+
return iteratorId;
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
const isPromiseRef = (value) => typeof value === "object" && value !== null && value.__type === "PromiseRef";
|
|
591
|
+
const isAsyncIteratorRef = (value) => typeof value === "object" && value !== null && value.__type === "AsyncIteratorRef";
|
|
592
|
+
const addCallbackIdsToRefs = (value) => {
|
|
593
|
+
if (value === null || typeof value !== "object")
|
|
594
|
+
return value;
|
|
595
|
+
if (isPromiseRef(value)) {
|
|
596
|
+
if ("__resolveCallbackId" in value)
|
|
597
|
+
return value;
|
|
598
|
+
const resolveCallbackId = nextLocalCallbackId++;
|
|
599
|
+
returnedCallbacks.set(resolveCallbackId, async (promiseId) => {
|
|
600
|
+
const promise = returnedPromises.get(promiseId);
|
|
601
|
+
if (!promise) {
|
|
602
|
+
throw new Error(`Promise ${promiseId} not found`);
|
|
603
|
+
}
|
|
604
|
+
const result2 = await promise;
|
|
605
|
+
returnedPromises.delete(promiseId);
|
|
606
|
+
const ctx = createMarshalContext();
|
|
607
|
+
const marshalled = await import_isolate_protocol3.marshalValue(result2, ctx);
|
|
608
|
+
return addCallbackIdsToRefs(marshalled);
|
|
609
|
+
});
|
|
610
|
+
return { ...value, __resolveCallbackId: resolveCallbackId };
|
|
611
|
+
}
|
|
612
|
+
if (isAsyncIteratorRef(value)) {
|
|
613
|
+
if ("__nextCallbackId" in value)
|
|
614
|
+
return value;
|
|
615
|
+
const nextCallbackId = nextLocalCallbackId++;
|
|
616
|
+
returnedCallbacks.set(nextCallbackId, async (iteratorId) => {
|
|
617
|
+
const iterator = returnedIterators.get(iteratorId);
|
|
618
|
+
if (!iterator) {
|
|
619
|
+
throw new Error(`Iterator ${iteratorId} not found`);
|
|
620
|
+
}
|
|
621
|
+
const result2 = await iterator.next();
|
|
622
|
+
if (result2.done) {
|
|
623
|
+
returnedIterators.delete(iteratorId);
|
|
624
|
+
}
|
|
625
|
+
const ctx = createMarshalContext();
|
|
626
|
+
const marshalledValue = await import_isolate_protocol3.marshalValue(result2.value, ctx);
|
|
627
|
+
return {
|
|
628
|
+
done: result2.done,
|
|
629
|
+
value: addCallbackIdsToRefs(marshalledValue)
|
|
630
|
+
};
|
|
631
|
+
});
|
|
632
|
+
const returnCallbackId = nextLocalCallbackId++;
|
|
633
|
+
returnedCallbacks.set(returnCallbackId, async (iteratorId, returnValue) => {
|
|
634
|
+
const iterator = returnedIterators.get(iteratorId);
|
|
635
|
+
returnedIterators.delete(iteratorId);
|
|
636
|
+
if (!iterator || !iterator.return) {
|
|
637
|
+
return { done: true, value: undefined };
|
|
638
|
+
}
|
|
639
|
+
const result2 = await iterator.return(returnValue);
|
|
640
|
+
const ctx = createMarshalContext();
|
|
641
|
+
const marshalledValue = await import_isolate_protocol3.marshalValue(result2.value, ctx);
|
|
642
|
+
return {
|
|
643
|
+
done: true,
|
|
644
|
+
value: addCallbackIdsToRefs(marshalledValue)
|
|
645
|
+
};
|
|
646
|
+
});
|
|
647
|
+
return {
|
|
648
|
+
...value,
|
|
649
|
+
__nextCallbackId: nextCallbackId,
|
|
650
|
+
__returnCallbackId: returnCallbackId
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
if (Array.isArray(value)) {
|
|
654
|
+
return value.map((item) => addCallbackIdsToRefs(item));
|
|
655
|
+
}
|
|
656
|
+
const result = {};
|
|
657
|
+
for (const key of Object.keys(value)) {
|
|
658
|
+
result[key] = addCallbackIdsToRefs(value[key]);
|
|
659
|
+
}
|
|
660
|
+
return result;
|
|
661
|
+
};
|
|
662
|
+
const invokeCallback = async (callbackId, args) => {
|
|
663
|
+
const callback = returnedCallbacks.get(callbackId);
|
|
664
|
+
if (!callback) {
|
|
665
|
+
throw new Error(`Local callback ${callbackId} not found`);
|
|
666
|
+
}
|
|
667
|
+
return await callback(...args);
|
|
668
|
+
};
|
|
669
|
+
return { createMarshalContext, addCallbackIdsToRefs, invokeCallback };
|
|
670
|
+
}
|
|
477
671
|
function createModuleResolver(state) {
|
|
478
672
|
return async (specifier, referrer) => {
|
|
673
|
+
const staticCached = state.staticModuleCache.get(specifier);
|
|
674
|
+
if (staticCached)
|
|
675
|
+
return staticCached;
|
|
479
676
|
const cached = state.moduleCache.get(specifier);
|
|
480
677
|
if (cached)
|
|
481
678
|
return cached;
|
|
@@ -484,16 +681,21 @@ function createModuleResolver(state) {
|
|
|
484
681
|
}
|
|
485
682
|
const importerPath = state.moduleToFilename.get(referrer) ?? "<unknown>";
|
|
486
683
|
const importerResolveDir = import_node_path.default.posix.dirname(importerPath);
|
|
487
|
-
const
|
|
684
|
+
const result = await state.moduleLoader(specifier, {
|
|
488
685
|
path: importerPath,
|
|
489
686
|
resolveDir: importerResolveDir
|
|
490
687
|
});
|
|
688
|
+
const { code, resolveDir } = result;
|
|
491
689
|
const hash = import_isolate_transform.contentHash(code);
|
|
492
690
|
const cacheKey = `${specifier}:${hash}`;
|
|
493
691
|
const hashCached = state.moduleCache.get(cacheKey);
|
|
494
692
|
if (hashCached)
|
|
495
693
|
return hashCached;
|
|
496
|
-
|
|
694
|
+
let transformed = state.transformCache.get(hash);
|
|
695
|
+
if (!transformed) {
|
|
696
|
+
transformed = await import_isolate_transform.transformModuleCode(code, specifier);
|
|
697
|
+
state.transformCache.set(hash, transformed);
|
|
698
|
+
}
|
|
497
699
|
if (transformed.sourceMap) {
|
|
498
700
|
state.sourceMaps.set(specifier, transformed.sourceMap);
|
|
499
701
|
}
|
|
@@ -502,8 +704,12 @@ function createModuleResolver(state) {
|
|
|
502
704
|
});
|
|
503
705
|
const resolvedPath = import_node_path.default.posix.join(resolveDir, import_node_path.default.posix.basename(specifier));
|
|
504
706
|
state.moduleToFilename.set(mod, resolvedPath);
|
|
505
|
-
|
|
506
|
-
|
|
707
|
+
if (result.static) {
|
|
708
|
+
state.staticModuleCache.set(specifier, mod);
|
|
709
|
+
} else {
|
|
710
|
+
state.moduleCache.set(specifier, mod);
|
|
711
|
+
state.moduleCache.set(cacheKey, mod);
|
|
712
|
+
}
|
|
507
713
|
const resolver = createModuleResolver(state);
|
|
508
714
|
await mod.instantiate(state.context, resolver);
|
|
509
715
|
return mod;
|
|
@@ -514,8 +720,8 @@ function convertFetchCallback(callback) {
|
|
|
514
720
|
return {};
|
|
515
721
|
}
|
|
516
722
|
return {
|
|
517
|
-
onFetch: async (
|
|
518
|
-
return Promise.resolve(callback(
|
|
723
|
+
onFetch: async (url, init) => {
|
|
724
|
+
return Promise.resolve(callback(url, init));
|
|
519
725
|
}
|
|
520
726
|
};
|
|
521
727
|
}
|
|
@@ -531,8 +737,11 @@ async function createRuntime(options) {
|
|
|
531
737
|
context,
|
|
532
738
|
handles: {},
|
|
533
739
|
moduleCache: new Map,
|
|
740
|
+
staticModuleCache: new Map,
|
|
741
|
+
transformCache: new Map,
|
|
534
742
|
moduleToFilename: new Map,
|
|
535
743
|
sourceMaps: new Map,
|
|
744
|
+
pendingCallbacks: [],
|
|
536
745
|
moduleLoader: opts.moduleLoader,
|
|
537
746
|
customFunctions: opts.customFunctions
|
|
538
747
|
};
|
|
@@ -547,13 +756,17 @@ async function createRuntime(options) {
|
|
|
547
756
|
state.handles.fs = await import_isolate_fs.setupFs(context, opts.fs);
|
|
548
757
|
}
|
|
549
758
|
if (opts.customFunctions) {
|
|
550
|
-
|
|
759
|
+
const customMarshalOptions = opts.customFunctionsMarshalOptions ?? createLocalCustomFunctionsMarshalOptions();
|
|
760
|
+
state.customFnInvokeRef = await setupCustomFunctions(context, opts.customFunctions, customMarshalOptions);
|
|
551
761
|
}
|
|
552
762
|
if (opts.testEnvironment) {
|
|
553
763
|
const testEnvOptions = typeof opts.testEnvironment === "object" ? opts.testEnvironment : undefined;
|
|
554
764
|
state.handles.testEnvironment = await import_isolate_test_environment.setupTestEnvironment(context, testEnvOptions);
|
|
555
765
|
}
|
|
556
766
|
if (opts.playwright) {
|
|
767
|
+
if (!opts.playwright.handler) {
|
|
768
|
+
throw new Error("Playwright configured without handler. Provide playwright.handler in createRuntime options.");
|
|
769
|
+
}
|
|
557
770
|
let eventCallback = opts.playwright.onEvent;
|
|
558
771
|
if (opts.playwright.console && opts.console?.onEntry) {
|
|
559
772
|
const originalCallback = eventCallback;
|
|
@@ -572,13 +785,13 @@ async function createRuntime(options) {
|
|
|
572
785
|
}
|
|
573
786
|
};
|
|
574
787
|
}
|
|
575
|
-
|
|
576
|
-
|
|
788
|
+
const playwrightSetupOptions = {
|
|
789
|
+
handler: opts.playwright.handler,
|
|
577
790
|
timeout: opts.playwright.timeout,
|
|
578
|
-
baseUrl: opts.playwright.baseUrl,
|
|
579
791
|
console: opts.playwright.console && !opts.console?.onEntry,
|
|
580
792
|
onEvent: eventCallback
|
|
581
|
-
}
|
|
793
|
+
};
|
|
794
|
+
state.handles.playwright = await import_isolate_playwright.setupPlaywright(context, playwrightSetupOptions);
|
|
582
795
|
}
|
|
583
796
|
const fetchHandle = {
|
|
584
797
|
async dispatchRequest(request, options2) {
|
|
@@ -634,6 +847,36 @@ async function createRuntime(options) {
|
|
|
634
847
|
throw new Error("Fetch handle not available");
|
|
635
848
|
}
|
|
636
849
|
return state.handles.fetch.hasActiveConnections();
|
|
850
|
+
},
|
|
851
|
+
dispatchClientWebSocketOpen(socketId, protocol, extensions) {
|
|
852
|
+
if (!state.handles.fetch) {
|
|
853
|
+
throw new Error("Fetch handle not available");
|
|
854
|
+
}
|
|
855
|
+
state.handles.fetch.dispatchClientWebSocketOpen(socketId, protocol, extensions);
|
|
856
|
+
},
|
|
857
|
+
dispatchClientWebSocketMessage(socketId, data) {
|
|
858
|
+
if (!state.handles.fetch) {
|
|
859
|
+
throw new Error("Fetch handle not available");
|
|
860
|
+
}
|
|
861
|
+
state.handles.fetch.dispatchClientWebSocketMessage(socketId, data);
|
|
862
|
+
},
|
|
863
|
+
dispatchClientWebSocketClose(socketId, code, reason, wasClean) {
|
|
864
|
+
if (!state.handles.fetch) {
|
|
865
|
+
throw new Error("Fetch handle not available");
|
|
866
|
+
}
|
|
867
|
+
state.handles.fetch.dispatchClientWebSocketClose(socketId, code, reason, wasClean);
|
|
868
|
+
},
|
|
869
|
+
dispatchClientWebSocketError(socketId) {
|
|
870
|
+
if (!state.handles.fetch) {
|
|
871
|
+
throw new Error("Fetch handle not available");
|
|
872
|
+
}
|
|
873
|
+
state.handles.fetch.dispatchClientWebSocketError(socketId);
|
|
874
|
+
},
|
|
875
|
+
onClientWebSocketCommand(callback) {
|
|
876
|
+
if (!state.handles.fetch) {
|
|
877
|
+
throw new Error("Fetch handle not available");
|
|
878
|
+
}
|
|
879
|
+
return state.handles.fetch.onClientWebSocketCommand(callback);
|
|
637
880
|
}
|
|
638
881
|
};
|
|
639
882
|
const timersHandle = {
|
|
@@ -656,11 +899,27 @@ async function createRuntime(options) {
|
|
|
656
899
|
}
|
|
657
900
|
};
|
|
658
901
|
const testEnvironmentHandle = {
|
|
659
|
-
async runTests(
|
|
902
|
+
async runTests(timeout) {
|
|
660
903
|
if (!state.handles.testEnvironment) {
|
|
661
904
|
throw new Error("Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
662
905
|
}
|
|
663
|
-
|
|
906
|
+
if (timeout === undefined) {
|
|
907
|
+
return import_isolate_test_environment.runTests(state.context);
|
|
908
|
+
}
|
|
909
|
+
let timeoutId;
|
|
910
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
911
|
+
timeoutId = setTimeout(() => reject(new Error("Test timeout")), timeout);
|
|
912
|
+
});
|
|
913
|
+
try {
|
|
914
|
+
return await Promise.race([
|
|
915
|
+
import_isolate_test_environment.runTests(state.context),
|
|
916
|
+
timeoutPromise
|
|
917
|
+
]);
|
|
918
|
+
} finally {
|
|
919
|
+
if (timeoutId) {
|
|
920
|
+
clearTimeout(timeoutId);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
664
923
|
},
|
|
665
924
|
hasTests() {
|
|
666
925
|
if (!state.handles.testEnvironment) {
|
|
@@ -681,7 +940,7 @@ async function createRuntime(options) {
|
|
|
681
940
|
const playwrightHandle = {
|
|
682
941
|
getCollectedData() {
|
|
683
942
|
if (!state.handles.playwright) {
|
|
684
|
-
throw new Error("Playwright not configured. Provide playwright.
|
|
943
|
+
throw new Error("Playwright not configured. Provide playwright.handler in createRuntime options.");
|
|
685
944
|
}
|
|
686
945
|
return {
|
|
687
946
|
browserConsoleLogs: state.handles.playwright.getBrowserConsoleLogs(),
|
|
@@ -695,6 +954,7 @@ async function createRuntime(options) {
|
|
|
695
954
|
};
|
|
696
955
|
return {
|
|
697
956
|
id,
|
|
957
|
+
pendingCallbacks: state.pendingCallbacks,
|
|
698
958
|
fetch: fetchHandle,
|
|
699
959
|
timers: timersHandle,
|
|
700
960
|
console: consoleHandle,
|
|
@@ -725,6 +985,10 @@ async function createRuntime(options) {
|
|
|
725
985
|
} finally {
|
|
726
986
|
runRef.release();
|
|
727
987
|
}
|
|
988
|
+
if (state.pendingCallbacks.length > 0) {
|
|
989
|
+
await Promise.all(state.pendingCallbacks);
|
|
990
|
+
state.pendingCallbacks.length = 0;
|
|
991
|
+
}
|
|
728
992
|
} catch (err) {
|
|
729
993
|
const error = err;
|
|
730
994
|
if (error.stack && state.sourceMaps.size > 0) {
|
|
@@ -733,6 +997,11 @@ async function createRuntime(options) {
|
|
|
733
997
|
throw error;
|
|
734
998
|
}
|
|
735
999
|
},
|
|
1000
|
+
clearModuleCache() {
|
|
1001
|
+
state.moduleCache.clear();
|
|
1002
|
+
state.moduleToFilename.clear();
|
|
1003
|
+
state.sourceMaps.clear();
|
|
1004
|
+
},
|
|
736
1005
|
async dispose() {
|
|
737
1006
|
if (state.customFnInvokeRef) {
|
|
738
1007
|
state.customFnInvokeRef.release();
|
|
@@ -754,4 +1023,4 @@ async function createRuntime(options) {
|
|
|
754
1023
|
};
|
|
755
1024
|
}
|
|
756
1025
|
|
|
757
|
-
//# debugId=
|
|
1026
|
+
//# debugId=6EFB8447C7EE485F64756E2164756E21
|