@ricsam/isolate 0.1.10 → 0.1.11

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.
Files changed (128) hide show
  1. package/README.md +172 -18
  2. package/dist/cjs/bridge/diagnostics.cjs +37 -2
  3. package/dist/cjs/bridge/diagnostics.cjs.map +3 -3
  4. package/dist/cjs/bridge/runtime-bindings.cjs +203 -51
  5. package/dist/cjs/bridge/runtime-bindings.cjs.map +3 -3
  6. package/dist/cjs/bridge/sandbox-isolate.cjs +365 -0
  7. package/dist/cjs/bridge/sandbox-isolate.cjs.map +10 -0
  8. package/dist/cjs/host/create-isolate-host.cjs +51 -25
  9. package/dist/cjs/host/create-isolate-host.cjs.map +3 -3
  10. package/dist/cjs/host/nested-host-controller.cjs +311 -0
  11. package/dist/cjs/host/nested-host-controller.cjs.map +10 -0
  12. package/dist/cjs/index.cjs.map +1 -1
  13. package/dist/cjs/internal/browser-source.cjs +91 -0
  14. package/dist/cjs/internal/browser-source.cjs.map +10 -0
  15. package/dist/cjs/internal/client/connection.cjs +152 -172
  16. package/dist/cjs/internal/client/connection.cjs.map +3 -3
  17. package/dist/cjs/internal/daemon/callback-fs-handler.cjs +3 -3
  18. package/dist/cjs/internal/daemon/callback-fs-handler.cjs.map +3 -3
  19. package/dist/cjs/internal/daemon/connection.cjs +129 -4
  20. package/dist/cjs/internal/daemon/connection.cjs.map +3 -3
  21. package/dist/cjs/internal/playwright/client.cjs +4 -2
  22. package/dist/cjs/internal/playwright/client.cjs.map +3 -3
  23. package/dist/cjs/internal/playwright/handler.cjs +298 -25
  24. package/dist/cjs/internal/playwright/handler.cjs.map +3 -3
  25. package/dist/cjs/internal/playwright/index.cjs +54 -8
  26. package/dist/cjs/internal/playwright/index.cjs.map +3 -3
  27. package/dist/cjs/internal/playwright/types.cjs +3 -1
  28. package/dist/cjs/internal/playwright/types.cjs.map +3 -3
  29. package/dist/cjs/internal/protocol/codec.cjs +16 -5
  30. package/dist/cjs/internal/protocol/codec.cjs.map +3 -3
  31. package/dist/cjs/internal/protocol/marshalValue.cjs +37 -6
  32. package/dist/cjs/internal/protocol/marshalValue.cjs.map +3 -3
  33. package/dist/cjs/internal/protocol/types.cjs.map +2 -2
  34. package/dist/cjs/internal/runtime/index.cjs +377 -22
  35. package/dist/cjs/internal/runtime/index.cjs.map +3 -3
  36. package/dist/cjs/internal/typecheck/index.cjs +2 -1
  37. package/dist/cjs/internal/typecheck/index.cjs.map +3 -3
  38. package/dist/cjs/internal/typecheck/isolate-types.cjs +186 -13
  39. package/dist/cjs/internal/typecheck/isolate-types.cjs.map +3 -3
  40. package/dist/cjs/internal/typecheck/typecheck.cjs +2 -3
  41. package/dist/cjs/internal/typecheck/typecheck.cjs.map +3 -3
  42. package/dist/cjs/package.json +1 -1
  43. package/dist/cjs/runtime/script-runtime.cjs +14 -12
  44. package/dist/cjs/runtime/script-runtime.cjs.map +3 -3
  45. package/dist/cjs/runtime/test-runtime.cjs +113 -0
  46. package/dist/cjs/runtime/test-runtime.cjs.map +10 -0
  47. package/dist/cjs/server/app-server.cjs +16 -9
  48. package/dist/cjs/server/app-server.cjs.map +3 -3
  49. package/dist/cjs/typecheck/index.cjs +2 -1
  50. package/dist/cjs/typecheck/index.cjs.map +3 -3
  51. package/dist/mjs/bridge/diagnostics.mjs +37 -2
  52. package/dist/mjs/bridge/diagnostics.mjs.map +3 -3
  53. package/dist/mjs/bridge/runtime-bindings.mjs +206 -51
  54. package/dist/mjs/bridge/runtime-bindings.mjs.map +3 -3
  55. package/dist/mjs/bridge/sandbox-isolate.mjs +325 -0
  56. package/dist/mjs/bridge/sandbox-isolate.mjs.map +10 -0
  57. package/dist/mjs/host/create-isolate-host.mjs +53 -25
  58. package/dist/mjs/host/create-isolate-host.mjs.map +3 -3
  59. package/dist/mjs/host/nested-host-controller.mjs +275 -0
  60. package/dist/mjs/host/nested-host-controller.mjs.map +10 -0
  61. package/dist/mjs/index.mjs.map +1 -1
  62. package/dist/mjs/internal/browser-source.mjs +51 -0
  63. package/dist/mjs/internal/browser-source.mjs.map +10 -0
  64. package/dist/mjs/internal/client/connection.mjs +154 -173
  65. package/dist/mjs/internal/client/connection.mjs.map +3 -3
  66. package/dist/mjs/internal/daemon/callback-fs-handler.mjs +3 -3
  67. package/dist/mjs/internal/daemon/callback-fs-handler.mjs.map +3 -3
  68. package/dist/mjs/internal/daemon/connection.mjs +129 -4
  69. package/dist/mjs/internal/daemon/connection.mjs.map +3 -3
  70. package/dist/mjs/internal/playwright/client.mjs +7 -3
  71. package/dist/mjs/internal/playwright/client.mjs.map +3 -3
  72. package/dist/mjs/internal/playwright/handler.mjs +300 -26
  73. package/dist/mjs/internal/playwright/handler.mjs.map +3 -3
  74. package/dist/mjs/internal/playwright/index.mjs +59 -9
  75. package/dist/mjs/internal/playwright/index.mjs.map +3 -3
  76. package/dist/mjs/internal/playwright/types.mjs +3 -1
  77. package/dist/mjs/internal/playwright/types.mjs.map +3 -3
  78. package/dist/mjs/internal/protocol/codec.mjs +16 -5
  79. package/dist/mjs/internal/protocol/codec.mjs.map +3 -3
  80. package/dist/mjs/internal/protocol/marshalValue.mjs +38 -6
  81. package/dist/mjs/internal/protocol/marshalValue.mjs.map +3 -3
  82. package/dist/mjs/internal/protocol/types.mjs.map +2 -2
  83. package/dist/mjs/internal/runtime/index.mjs +377 -22
  84. package/dist/mjs/internal/runtime/index.mjs.map +3 -3
  85. package/dist/mjs/internal/typecheck/index.mjs +3 -1
  86. package/dist/mjs/internal/typecheck/index.mjs.map +3 -3
  87. package/dist/mjs/internal/typecheck/isolate-types.mjs +186 -13
  88. package/dist/mjs/internal/typecheck/isolate-types.mjs.map +3 -3
  89. package/dist/mjs/internal/typecheck/typecheck.mjs +2 -3
  90. package/dist/mjs/internal/typecheck/typecheck.mjs.map +3 -3
  91. package/dist/mjs/package.json +1 -1
  92. package/dist/mjs/runtime/script-runtime.mjs +16 -12
  93. package/dist/mjs/runtime/script-runtime.mjs.map +3 -3
  94. package/dist/mjs/runtime/test-runtime.mjs +78 -0
  95. package/dist/mjs/runtime/test-runtime.mjs.map +10 -0
  96. package/dist/mjs/server/app-server.mjs +23 -11
  97. package/dist/mjs/server/app-server.mjs.map +3 -3
  98. package/dist/mjs/typecheck/index.mjs +2 -1
  99. package/dist/mjs/typecheck/index.mjs.map +3 -3
  100. package/dist/types/bridge/diagnostics.d.ts +6 -1
  101. package/dist/types/bridge/runtime-bindings.d.ts +5 -1
  102. package/dist/types/bridge/sandbox-isolate.d.ts +15 -0
  103. package/dist/types/host/nested-host-controller.d.ts +11 -0
  104. package/dist/types/index.d.ts +1 -1
  105. package/dist/types/internal/browser-source.d.ts +11 -0
  106. package/dist/types/internal/client/types.d.ts +5 -0
  107. package/dist/types/internal/daemon/types.d.ts +0 -2
  108. package/dist/types/internal/playwright/client.d.ts +2 -2
  109. package/dist/types/internal/playwright/handler.d.ts +27 -4
  110. package/dist/types/internal/playwright/index.d.ts +2 -2
  111. package/dist/types/internal/playwright/types.d.ts +33 -1
  112. package/dist/types/internal/protocol/codec.d.ts +12 -2
  113. package/dist/types/internal/protocol/marshalValue.d.ts +3 -2
  114. package/dist/types/internal/protocol/types.d.ts +25 -1
  115. package/dist/types/internal/runtime/index.d.ts +5 -0
  116. package/dist/types/internal/typecheck/index.d.ts +1 -1
  117. package/dist/types/internal/typecheck/isolate-types.d.ts +6 -4
  118. package/dist/types/internal/typecheck/typecheck.d.ts +1 -1
  119. package/dist/types/runtime/script-runtime.d.ts +2 -1
  120. package/dist/types/runtime/test-runtime.d.ts +4 -0
  121. package/dist/types/server/app-server.d.ts +2 -1
  122. package/dist/types/types.d.ts +33 -33
  123. package/package.json +3 -3
  124. package/dist/cjs/browser/browser-runtime.cjs +0 -157
  125. package/dist/cjs/browser/browser-runtime.cjs.map +0 -10
  126. package/dist/mjs/browser/browser-runtime.mjs +0 -93
  127. package/dist/mjs/browser/browser-runtime.mjs.map +0 -10
  128. package/dist/types/browser/browser-runtime.d.ts +0 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ricsam/isolate
2
2
 
3
- `@ricsam/isolate` is a runtime-centric JavaScript sandbox built on an async-context-enabled [`@ricsam/isolated-vm`](https://github.com/ricsam/isolated-vm) engine build. It gives you a single host API for running isolated code with web-style capabilities such as `fetch`, files, streams, server handlers, module loading, and Playwright-backed browser tests.
3
+ `@ricsam/isolate` is a runtime-centric JavaScript sandbox built on an async-context-enabled [`@ricsam/isolated-vm`](https://github.com/ricsam/isolated-vm) engine build. It gives you a single host API for running isolated code with web-style capabilities such as `fetch`, files, streams, server handlers, nested sandboxes, module loading, and Playwright-backed browser tests.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,7 +10,7 @@ npm add @ricsam/isolate @ricsam/isolated-vm
10
10
 
11
11
  The `@ricsam/isolated-vm` peer includes the `createContext({ asyncContext: true })` support required by this repo. Upstream `isolated-vm` will fail fast during runtime boot with a clear AsyncContext error.
12
12
 
13
- Install Playwright when you want browser runtimes:
13
+ Install Playwright when you want browser-enabled runtimes or test runtimes:
14
14
 
15
15
  ```bash
16
16
  npm add playwright
@@ -29,7 +29,9 @@ The host can create three runtime styles:
29
29
 
30
30
  - `host.createRuntime()` for scripts, agents, and ad hoc execution
31
31
  - `host.createAppServer()` for `serve()`-based request handlers
32
- - `host.createBrowserRuntime()` for Playwright-backed execution
32
+ - `host.createTestRuntime()` for test suites with optional Playwright-backed browser access
33
+
34
+ Inside sandbox code, `@ricsam/isolate` is also available as a synthetic module. It exports a sandbox-only `createIsolateHost()` that lets a runtime create nested runtimes, app servers, and test runtimes without exposing daemon configuration to the sandbox.
33
35
 
34
36
  ## Host Bindings
35
37
 
@@ -40,9 +42,12 @@ Each runtime is configured through `bindings`, which describe how sandboxed code
40
42
  - `files` exposes a safe, root-scoped filesystem
41
43
  - `modules` resolves virtual modules, source trees, and mounted packages
42
44
  - `tools` exposes async host functions and async iterators
45
+ - `browser` exposes a Playwright-like browser factory backed by host `createContext()` and `createPage()` callbacks
43
46
 
44
47
  Every host callback receives a `HostCallContext` with an `AbortSignal`, runtime identity, resource identity, and request metadata.
45
48
 
49
+ `bindings.browser` is intentionally smaller than a full Playwright browser. It injects a global `browser` object with `browser.newContext()` and `browser.contexts()`, and returned contexts expose `context.newPage()` and `context.pages()`. Browser-level shutdown stays on the host side, while the sandbox can still close pages and contexts that it created.
50
+
46
51
  ## Async Context
47
52
 
48
53
  Runtimes created by `@ricsam/isolate` enable the TC39 proposal-style `AsyncContext` global inside the sandbox. This is an experimental surface for now, and the proposal API is used to implement the `node:async_hooks` shim exported to sandboxed code.
@@ -145,6 +150,51 @@ try {
145
150
  }
146
151
  ```
147
152
 
153
+ ## Nested Hosts Inside The Sandbox
154
+
155
+ Sandbox code can import `@ricsam/isolate` and create child runtimes against the same top-level host connection.
156
+
157
+ ```ts
158
+ const runtime = await host.createRuntime({
159
+ bindings: {
160
+ console: {
161
+ onEntry(entry) {
162
+ if (entry.type === "output") {
163
+ console.log(entry.stdout);
164
+ }
165
+ },
166
+ },
167
+ },
168
+ });
169
+
170
+ await runtime.eval(`
171
+ import { createIsolateHost } from "@ricsam/isolate";
172
+
173
+ const nestedHost = createIsolateHost();
174
+ const child = await nestedHost.createRuntime({
175
+ bindings: {
176
+ tools: {
177
+ greet: async (name) => "hello " + name,
178
+ },
179
+ },
180
+ });
181
+
182
+ await child.eval('console.log(await greet("nested"))');
183
+ await child.dispose();
184
+ await nestedHost.close();
185
+ `);
186
+ ```
187
+
188
+ Nested hosts support:
189
+
190
+ - `createRuntime()`
191
+ - `createAppServer()`
192
+ - `createTestRuntime()`
193
+ - `diagnostics()`
194
+ - `close()`
195
+
196
+ Child runtimes can reuse the same binding shapes as top-level runtimes. That includes isolate-authored callbacks, async iterators, module resolvers, file bindings, and browser handles.
197
+
148
198
  ## App Servers
149
199
 
150
200
  `createAppServer()` is the long-lived server-oriented API. It boots a runtime around an entry module that calls `serve()` and then lets the host dispatch requests into it.
@@ -183,51 +233,151 @@ await host.close();
183
233
 
184
234
  `server.handle()` returns either a normal HTTP response or WebSocket upgrade metadata. The `server.ws` helpers let the host continue an upgraded connection by sending open, message, close, and error events back into the runtime.
185
235
 
186
- ## Browser Runtimes
236
+ ## Browser Bindings In Script And Server Runtimes
187
237
 
188
- `createBrowserRuntime()` runs sandboxed code against a Playwright page while keeping the host in control of file access, diagnostics, and browser event collection.
238
+ If you provide `bindings.browser`, script and app runtimes get a global `browser` factory even when they are not full Playwright browser runtimes.
189
239
 
190
240
  ```ts
191
241
  import { chromium } from "playwright";
192
242
  import { createIsolateHost } from "@ricsam/isolate";
193
243
 
194
244
  const browser = await chromium.launch();
195
- const context = await browser.newContext();
196
- const page = await context.newPage();
245
+ const host = await createIsolateHost();
246
+
247
+ const runtime = await host.createRuntime({
248
+ bindings: {
249
+ browser: {
250
+ createContext: async (options) =>
251
+ await browser.newContext(options ?? undefined),
252
+ createPage: async (contextInstance) =>
253
+ await contextInstance.newPage(),
254
+ },
255
+ },
256
+ });
257
+
258
+ await runtime.eval(`
259
+ const ctx = await browser.newContext({
260
+ viewport: { width: 1280, height: 720 },
261
+ });
262
+ const page = await ctx.newPage();
263
+
264
+ await page.goto("https://example.com");
265
+ console.log(await page.title());
266
+ console.log(typeof browser.close);
267
+
268
+ await page.close();
269
+ await ctx.close();
270
+ `);
197
271
 
272
+ await runtime.dispose();
273
+ await browser.close();
274
+ await host.close();
275
+ ```
276
+
277
+ In these runtimes:
278
+
279
+ - `browser.newContext()` is available
280
+ - `browser.contexts()` is available
281
+ - `context.newPage()` is available
282
+ - `context.pages()` is available
283
+ - `page.close()` and `context.close()` are available
284
+ - `browser.close()` is not exposed inside the sandbox
285
+ - `page` and `context` are never injected as implicit globals
286
+
287
+ ## Test Runtimes
288
+
289
+ `createTestRuntime()` enables `describe`, `test`/`it`, hooks, and `expect`. If you also provide `bindings.browser`, the same test runtime gets Playwright-style browser access and matcher support, but you are responsible for explicit page/context lifecycle inside the suite.
290
+
291
+ ```ts
292
+ import { chromium } from "playwright";
293
+ import { createIsolateHost } from "@ricsam/isolate";
294
+
295
+ const browser = await chromium.launch();
198
296
  const host = await createIsolateHost();
199
- const runtime = await host.createBrowserRuntime({
200
- key: "example/browser",
201
- bindings: {},
202
- features: { tests: true },
203
- browser: {
204
- page,
205
- captureConsole: true,
297
+ const runtime = await host.createTestRuntime({
298
+ key: "example/browser-test",
299
+ bindings: {
300
+ browser: {
301
+ captureConsole: true,
302
+ createContext: async (options) =>
303
+ await browser.newContext(options ?? undefined),
304
+ createPage: async (contextInstance) =>
305
+ await contextInstance.newPage(),
306
+ },
206
307
  },
207
308
  });
208
309
 
209
310
  const result = await runtime.run(
210
311
  `
312
+ let ctx;
313
+ let page;
314
+
315
+ beforeAll(async () => {
316
+ ctx = await browser.newContext();
317
+ page = await ctx.newPage();
318
+ });
319
+
320
+ afterAll(async () => {
321
+ await ctx.close();
322
+ });
323
+
211
324
  test("loads a page", async () => {
212
- await page.goto("https://example.com");
325
+ expect((await browser.contexts()).length).toBe(1);
326
+ expect((await ctx.pages()).length).toBe(1);
327
+ await page.goto("https://example.com", {
328
+ waitUntil: "domcontentloaded",
329
+ });
213
330
  await expect(page).toHaveTitle(/Example Domain/);
214
331
  });
215
332
  `,
216
333
  {
217
334
  filename: "/browser-test.ts",
218
- asTestSuite: true,
219
335
  timeoutMs: 10_000,
220
336
  },
221
337
  );
222
338
 
223
- console.log(result.tests);
339
+ console.log(result);
224
340
 
225
341
  await runtime.dispose();
226
- await context.close();
227
342
  await browser.close();
228
343
  await host.close();
229
344
  ```
230
345
 
346
+ From inside another sandbox, `nestedHost.createTestRuntime()` can reuse the sandbox `browser` handle:
347
+
348
+ ```ts
349
+ import { createIsolateHost } from "@ricsam/isolate";
350
+
351
+ const nestedHost = createIsolateHost();
352
+ const child = await nestedHost.createTestRuntime({
353
+ bindings: {
354
+ browser,
355
+ },
356
+ });
357
+
358
+ await child.run(`
359
+ let ctx;
360
+ let page;
361
+
362
+ beforeAll(async () => {
363
+ ctx = await browser.newContext();
364
+ page = await ctx.newPage();
365
+ });
366
+
367
+ afterAll(async () => {
368
+ await ctx.close();
369
+ });
370
+
371
+ test("loads a nested page", async () => {
372
+ await page.goto("https://example.com");
373
+ await expect(page).toHaveTitle(/Example Domain/);
374
+ });
375
+ `);
376
+
377
+ await child.dispose();
378
+ await nestedHost.close();
379
+ ```
380
+
231
381
  ## Module Resolution
232
382
 
233
383
  `createModuleResolver()` is a fluent builder. You can mix and match:
@@ -278,6 +428,10 @@ Built-in profiles:
278
428
 
279
429
  Capabilities can extend a profile with `fetch`, `files`, `tests`, `browser`, `tools`, `console`, `encoding`, and `timers`.
280
430
 
431
+ - Use `browser` when sandbox code should typecheck `browser.newContext()`, `browser.contexts()`, `context.newPage()`, and `context.pages()`
432
+ - The browser test profile does not assume implicit global `page` or `context`
433
+ - The synthetic sandbox import `import { createIsolateHost } from "@ricsam/isolate"` is included in all type profiles
434
+
281
435
  ## Daemon CLI
282
436
 
283
437
  The package also exposes an `isolate-daemon` binary:
@@ -39,7 +39,8 @@ var __export = (target, all) => {
39
39
  // src/bridge/diagnostics.ts
40
40
  var exports_diagnostics = {};
41
41
  __export(exports_diagnostics, {
42
- createRuntimeDiagnostics: () => createRuntimeDiagnostics
42
+ createRuntimeDiagnostics: () => createRuntimeDiagnostics,
43
+ createBrowserDiagnostics: () => createBrowserDiagnostics
43
44
  });
44
45
  module.exports = __toCommonJS(exports_diagnostics);
45
46
  function createRuntimeDiagnostics() {
@@ -54,5 +55,39 @@ function createRuntimeDiagnostics() {
54
55
  lifecycleState: "idle"
55
56
  };
56
57
  }
58
+ function createBrowserDiagnostics(collectedData, trackedResources) {
59
+ const contextIds = new Set;
60
+ const pageIds = new Set;
61
+ for (const entry of collectedData.browserConsoleLogs) {
62
+ contextIds.add(entry.contextId);
63
+ pageIds.add(entry.pageId);
64
+ }
65
+ for (const entry of collectedData.pageErrors) {
66
+ contextIds.add(entry.contextId);
67
+ pageIds.add(entry.pageId);
68
+ }
69
+ for (const entry of collectedData.networkRequests) {
70
+ contextIds.add(entry.contextId);
71
+ pageIds.add(entry.pageId);
72
+ }
73
+ for (const entry of collectedData.networkResponses) {
74
+ contextIds.add(entry.contextId);
75
+ pageIds.add(entry.pageId);
76
+ }
77
+ for (const entry of collectedData.requestFailures) {
78
+ contextIds.add(entry.contextId);
79
+ pageIds.add(entry.pageId);
80
+ }
81
+ return {
82
+ contexts: trackedResources?.contexts.length ?? contextIds.size,
83
+ pages: trackedResources?.pages.length ?? pageIds.size,
84
+ browserConsoleLogs: collectedData.browserConsoleLogs.length,
85
+ networkRequests: collectedData.networkRequests.length,
86
+ networkResponses: collectedData.networkResponses.length,
87
+ pageErrors: collectedData.pageErrors.length,
88
+ requestFailures: collectedData.requestFailures.length,
89
+ collectedData
90
+ };
91
+ }
57
92
 
58
- //# debugId=40FF8D24B6369A0D64756E2164756E21
93
+ //# debugId=8BF2916FF62F72B164756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../src/bridge/diagnostics.ts"],
4
4
  "sourcesContent": [
5
- "import type { RuntimeDiagnostics } from \"../types.cjs\";\n\nexport interface MutableRuntimeDiagnostics extends RuntimeDiagnostics {\n activeRequests: number;\n activeResources: number;\n pendingFiles: number;\n pendingFetches: number;\n pendingModules: number;\n pendingTools: number;\n streamCount: number;\n lifecycleState: \"idle\" | \"active\" | \"reloading\" | \"disposing\";\n}\n\nexport function createRuntimeDiagnostics(): MutableRuntimeDiagnostics {\n return {\n activeRequests: 0,\n activeResources: 0,\n pendingFiles: 0,\n pendingFetches: 0,\n pendingModules: 0,\n pendingTools: 0,\n streamCount: 0,\n lifecycleState: \"idle\",\n };\n}\n"
5
+ "import type { CollectedData } from \"../internal/client/index.cjs\";\nimport type { BrowserDiagnostics, RuntimeDiagnostics } from \"../types.cjs\";\n\nexport interface MutableRuntimeDiagnostics extends RuntimeDiagnostics {\n activeRequests: number;\n activeResources: number;\n pendingFiles: number;\n pendingFetches: number;\n pendingModules: number;\n pendingTools: number;\n streamCount: number;\n lifecycleState: \"idle\" | \"active\" | \"reloading\" | \"disposing\";\n}\n\nexport function createRuntimeDiagnostics(): MutableRuntimeDiagnostics {\n return {\n activeRequests: 0,\n activeResources: 0,\n pendingFiles: 0,\n pendingFetches: 0,\n pendingModules: 0,\n pendingTools: 0,\n streamCount: 0,\n lifecycleState: \"idle\",\n };\n}\n\nexport function createBrowserDiagnostics(\n collectedData: CollectedData,\n trackedResources?: { contexts: string[]; pages: string[] },\n): BrowserDiagnostics {\n const contextIds = new Set<string>();\n const pageIds = new Set<string>();\n for (const entry of collectedData.browserConsoleLogs) {\n contextIds.add(entry.contextId);\n pageIds.add(entry.pageId);\n }\n for (const entry of collectedData.pageErrors) {\n contextIds.add(entry.contextId);\n pageIds.add(entry.pageId);\n }\n for (const entry of collectedData.networkRequests) {\n contextIds.add(entry.contextId);\n pageIds.add(entry.pageId);\n }\n for (const entry of collectedData.networkResponses) {\n contextIds.add(entry.contextId);\n pageIds.add(entry.pageId);\n }\n for (const entry of collectedData.requestFailures) {\n contextIds.add(entry.contextId);\n pageIds.add(entry.pageId);\n }\n\n return {\n contexts: trackedResources?.contexts.length ?? contextIds.size,\n pages: trackedResources?.pages.length ?? pageIds.size,\n browserConsoleLogs: collectedData.browserConsoleLogs.length,\n networkRequests: collectedData.networkRequests.length,\n networkResponses: collectedData.networkResponses.length,\n pageErrors: collectedData.pageErrors.length,\n requestFailures: collectedData.requestFailures.length,\n collectedData,\n };\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaO,SAAS,wBAAwB,GAA8B;AAAA,EACpE,OAAO;AAAA,IACL,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,aAAa;AAAA,IACb,gBAAgB;AAAA,EAClB;AAAA;",
8
- "debugId": "40FF8D24B6369A0D64756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcO,SAAS,wBAAwB,GAA8B;AAAA,EACpE,OAAO;AAAA,IACL,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,aAAa;AAAA,IACb,gBAAgB;AAAA,EAClB;AAAA;AAGK,SAAS,wBAAwB,CACtC,eACA,kBACoB;AAAA,EACpB,MAAM,aAAa,IAAI;AAAA,EACvB,MAAM,UAAU,IAAI;AAAA,EACpB,WAAW,SAAS,cAAc,oBAAoB;AAAA,IACpD,WAAW,IAAI,MAAM,SAAS;AAAA,IAC9B,QAAQ,IAAI,MAAM,MAAM;AAAA,EAC1B;AAAA,EACA,WAAW,SAAS,cAAc,YAAY;AAAA,IAC5C,WAAW,IAAI,MAAM,SAAS;AAAA,IAC9B,QAAQ,IAAI,MAAM,MAAM;AAAA,EAC1B;AAAA,EACA,WAAW,SAAS,cAAc,iBAAiB;AAAA,IACjD,WAAW,IAAI,MAAM,SAAS;AAAA,IAC9B,QAAQ,IAAI,MAAM,MAAM;AAAA,EAC1B;AAAA,EACA,WAAW,SAAS,cAAc,kBAAkB;AAAA,IAClD,WAAW,IAAI,MAAM,SAAS;AAAA,IAC9B,QAAQ,IAAI,MAAM,MAAM;AAAA,EAC1B;AAAA,EACA,WAAW,SAAS,cAAc,iBAAiB;AAAA,IACjD,WAAW,IAAI,MAAM,SAAS;AAAA,IAC9B,QAAQ,IAAI,MAAM,MAAM;AAAA,EAC1B;AAAA,EAEA,OAAO;AAAA,IACL,UAAU,kBAAkB,SAAS,UAAU,WAAW;AAAA,IAC1D,OAAO,kBAAkB,MAAM,UAAU,QAAQ;AAAA,IACjD,oBAAoB,cAAc,mBAAmB;AAAA,IACrD,iBAAiB,cAAc,gBAAgB;AAAA,IAC/C,kBAAkB,cAAc,iBAAiB;AAAA,IACjD,YAAY,cAAc,WAAW;AAAA,IACrC,iBAAiB,cAAc,gBAAgB;AAAA,IAC/C;AAAA,EACF;AAAA;",
8
+ "debugId": "8BF2916FF62F72B164756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -69,7 +69,9 @@ __export(exports_runtime_bindings, {
69
69
  });
70
70
  module.exports = __toCommonJS(exports_runtime_bindings);
71
71
  var import_node_path = __toESM(require("node:path"));
72
+ var import_client = require("../internal/playwright/client.cjs");
72
73
  var import_request_context = require("./request-context.cjs");
74
+ var import_sandbox_isolate = require("./sandbox-isolate.cjs");
73
75
  function createAbortError(reason) {
74
76
  if (reason instanceof Error) {
75
77
  return reason;
@@ -171,11 +173,30 @@ async function normalizeModuleResolveResult(specifier, result, fallbackResolveDi
171
173
  };
172
174
  }
173
175
  function isAsyncGeneratorFunction(handler) {
174
- return handler.constructor.name === "AsyncGeneratorFunction";
176
+ return handler.constructor.name === "AsyncGeneratorFunction" || handler.__isolateCallbackKind === "asyncGenerator";
175
177
  }
176
- function createRuntimeBindingsAdapter(bindings, getRuntimeId, diagnostics) {
178
+ function isResponseDescriptor(value) {
179
+ return Boolean(value && typeof value === "object" && value.__type === "ResponseRef" && Array.isArray(value.headers));
180
+ }
181
+ function normalizeFetchResponse(value) {
182
+ if (value instanceof Response) {
183
+ return value;
184
+ }
185
+ if (isResponseDescriptor(value)) {
186
+ const body = value.body ? new Uint8Array(value.body) : null;
187
+ return new Response(body, {
188
+ status: value.status,
189
+ statusText: value.statusText,
190
+ headers: value.headers
191
+ });
192
+ }
193
+ throw new TypeError("Fetch bindings must return a Response.");
194
+ }
195
+ function createRuntimeBindingsAdapter(bindings, getRuntimeId, diagnostics, options) {
177
196
  const contextFactory = createHostCallContextFactory(getRuntimeId);
178
- const moduleLoader = createModuleLoader(bindings.modules, contextFactory.createHostCallContext, diagnostics);
197
+ const moduleLoader = createModuleLoader(bindings.modules, contextFactory.createHostCallContext, diagnostics, options?.nestedHost);
198
+ const customFunctions = createCustomFunctions(bindings.tools, options?.nestedHost, contextFactory.createHostCallContext, diagnostics);
199
+ const browserPlaywright = createBrowserPlaywrightOptions(bindings.browser, contextFactory.createHostCallContext);
179
200
  return {
180
201
  runtimeOptions: {
181
202
  console: bindings.console?.onEntry ? {
@@ -195,7 +216,7 @@ function createRuntimeBindingsAdapter(bindings, getRuntimeId, diagnostics) {
195
216
  body: init.rawBody ? init.rawBody.slice(0) : null,
196
217
  signal: context.signal
197
218
  });
198
- return await bindings.fetch(request, context);
219
+ return normalizeFetchResponse(await bindings.fetch(request, context));
199
220
  } finally {
200
221
  diagnostics.pendingFetches -= 1;
201
222
  diagnostics.activeResources -= 1;
@@ -246,12 +267,12 @@ function createRuntimeBindingsAdapter(bindings, getRuntimeId, diagnostics) {
246
267
  diagnostics.activeResources -= 1;
247
268
  }
248
269
  } : undefined,
249
- mkdir: bindings.files.mkdir ? async (dirPath, options) => {
270
+ mkdir: bindings.files.mkdir ? async (dirPath, options2) => {
250
271
  diagnostics.pendingFiles += 1;
251
272
  diagnostics.activeResources += 1;
252
273
  try {
253
274
  const context = contextFactory.createHostCallContext(`files:mkdir:${crypto.randomUUID()}`);
254
- return await bindings.files.mkdir(dirPath, options, context);
275
+ return await bindings.files.mkdir(dirPath, options2, context);
255
276
  } finally {
256
277
  diagnostics.pendingFiles -= 1;
257
278
  diagnostics.activeResources -= 1;
@@ -292,62 +313,38 @@ function createRuntimeBindingsAdapter(bindings, getRuntimeId, diagnostics) {
292
313
  } : undefined
293
314
  } : undefined,
294
315
  moduleLoader,
295
- customFunctions: bindings.tools ? Object.fromEntries(Object.entries(bindings.tools).map(([name, handler]) => {
296
- if (isAsyncGeneratorFunction(handler)) {
297
- return [
298
- name,
299
- {
300
- type: "asyncIterator",
301
- fn: (...args) => {
302
- diagnostics.pendingTools += 1;
303
- diagnostics.activeResources += 1;
304
- const context = contextFactory.createHostCallContext(`tool:${name}:${crypto.randomUUID()}`);
305
- const iterator = handler(...args, context);
306
- return async function* () {
307
- try {
308
- yield* iterator;
309
- } finally {
310
- diagnostics.pendingTools -= 1;
311
- diagnostics.activeResources -= 1;
312
- }
313
- }();
314
- }
315
- }
316
- ];
317
- }
318
- return [
319
- name,
320
- {
321
- type: "async",
322
- fn: async (...args) => {
323
- diagnostics.pendingTools += 1;
324
- diagnostics.activeResources += 1;
325
- try {
326
- const context = contextFactory.createHostCallContext(`tool:${name}:${crypto.randomUUID()}`);
327
- return await handler(...args, context);
328
- } finally {
329
- diagnostics.pendingTools -= 1;
330
- diagnostics.activeResources -= 1;
331
- }
332
- }
333
- }
334
- ];
335
- })) : undefined
316
+ customFunctions,
317
+ playwright: browserPlaywright
336
318
  },
337
319
  abort: contextFactory.abort,
338
320
  reset: contextFactory.reset
339
321
  };
340
322
  }
341
- function createModuleLoader(resolver, createHostCallContext, diagnostics) {
342
- if (!resolver) {
323
+ function createModuleLoader(resolver, createHostCallContext, diagnostics, nestedHost) {
324
+ if (!resolver && !nestedHost) {
343
325
  return;
344
326
  }
345
327
  return async (specifier, importer) => {
328
+ if (nestedHost && specifier === import_sandbox_isolate.SANDBOX_ISOLATE_MODULE_SPECIFIER) {
329
+ return {
330
+ code: import_sandbox_isolate.SANDBOX_ISOLATE_MODULE_SOURCE,
331
+ filename: "isolate-sandbox.js",
332
+ resolveDir: "/",
333
+ static: true
334
+ };
335
+ }
336
+ if (!resolver) {
337
+ throw new Error(`Unable to resolve module: ${specifier}`);
338
+ }
346
339
  diagnostics.pendingModules += 1;
347
340
  diagnostics.activeResources += 1;
348
341
  try {
349
342
  const context = createHostCallContext(`module:${crypto.randomUUID()}`);
350
- return await resolver.resolve(specifier, importer, context);
343
+ const resolved = await normalizeExplicitModuleResult(specifier, resolver.resolve(specifier, importer, context), importer.resolveDir);
344
+ if (!resolved) {
345
+ throw new Error(`Unable to resolve module: ${specifier}`);
346
+ }
347
+ return resolved;
351
348
  } finally {
352
349
  diagnostics.pendingModules -= 1;
353
350
  diagnostics.activeResources -= 1;
@@ -363,5 +360,160 @@ async function tryResolveModule(resolver, specifier, importer, context) {
363
360
  async function normalizeExplicitModuleResult(specifier, result, fallbackResolveDir) {
364
361
  return normalizeModuleResolveResult(specifier, result, fallbackResolveDir);
365
362
  }
363
+ function createBrowserPlaywrightOptions(browser, createHostCallContext) {
364
+ if (!browser) {
365
+ return;
366
+ }
367
+ return {
368
+ handler: import_client.createPlaywrightFactoryHandler({
369
+ createContext: browser.createContext ? async (options) => {
370
+ const context = createHostCallContext(`browser:createContext:${crypto.randomUUID()}`);
371
+ return await browser.createContext(options, context);
372
+ } : undefined,
373
+ createPage: browser.createPage ? async (contextHandle) => {
374
+ const context = createHostCallContext(`browser:createPage:${crypto.randomUUID()}`);
375
+ return await browser.createPage(contextHandle, context);
376
+ } : undefined,
377
+ readFile: browser.readFile ? async (filePath) => {
378
+ const context = createHostCallContext(`browser:readFile:${crypto.randomUUID()}`);
379
+ const buffer = await browser.readFile(filePath, context);
380
+ return {
381
+ name: import_node_path.default.basename(filePath),
382
+ mimeType: "application/octet-stream",
383
+ buffer
384
+ };
385
+ } : undefined,
386
+ writeFile: browser.writeFile ? async (filePath, data) => {
387
+ const context = createHostCallContext(`browser:writeFile:${crypto.randomUUID()}`);
388
+ await browser.writeFile(filePath, data, context);
389
+ } : undefined
390
+ }),
391
+ hasDefaultPage: false,
392
+ console: browser.captureConsole ?? false,
393
+ onEvent: browser.onEvent ? (event) => {
394
+ const context = createHostCallContext(`browser:event:${event.type}:${crypto.randomUUID()}`);
395
+ browser.onEvent?.(event, context);
396
+ } : undefined
397
+ };
398
+ }
399
+ function createCustomFunctions(tools, nestedHost, createHostCallContext, diagnostics) {
400
+ const definitions = {};
401
+ if (tools) {
402
+ for (const [name, handler] of Object.entries(tools)) {
403
+ if (isAsyncGeneratorFunction(handler)) {
404
+ definitions[name] = {
405
+ type: "asyncIterator",
406
+ fn: (...args) => {
407
+ diagnostics.pendingTools += 1;
408
+ diagnostics.activeResources += 1;
409
+ const context = createHostCallContext(`tool:${name}:${crypto.randomUUID()}`);
410
+ const iteratorResult = handler(...args, context);
411
+ return async function* () {
412
+ const iterator = await iteratorResult;
413
+ const iterable = iterator && typeof iterator[Symbol.asyncIterator] === "function" ? iterator : iterator && typeof iterator.next === "function" ? {
414
+ [Symbol.asyncIterator]() {
415
+ return iterator;
416
+ }
417
+ } : null;
418
+ try {
419
+ if (!iterable) {
420
+ throw new TypeError(`Tool ${name} did not return an async iterator.`);
421
+ }
422
+ yield* iterable;
423
+ } finally {
424
+ diagnostics.pendingTools -= 1;
425
+ diagnostics.activeResources -= 1;
426
+ }
427
+ }();
428
+ }
429
+ };
430
+ continue;
431
+ }
432
+ definitions[name] = {
433
+ type: "async",
434
+ fn: async (...args) => {
435
+ diagnostics.pendingTools += 1;
436
+ diagnostics.activeResources += 1;
437
+ try {
438
+ const context = createHostCallContext(`tool:${name}:${crypto.randomUUID()}`);
439
+ return await handler(...args, context);
440
+ } finally {
441
+ diagnostics.pendingTools -= 1;
442
+ diagnostics.activeResources -= 1;
443
+ }
444
+ }
445
+ };
446
+ }
447
+ }
448
+ if (nestedHost) {
449
+ const reservedNames = [
450
+ "__isolateHost_createHost",
451
+ "__isolateHost_closeHost",
452
+ "__isolateHost_hostDiagnostics",
453
+ "__isolateHost_createResource",
454
+ "__isolateHost_callResource",
455
+ "__isolateHost_drainCallbacks"
456
+ ];
457
+ for (const name of reservedNames) {
458
+ if (definitions[name]) {
459
+ throw new Error(`Tool name ${name} is reserved for internal sandbox host bindings.`);
460
+ }
461
+ }
462
+ definitions.__isolateHost_createHost = {
463
+ type: "async",
464
+ fn: async () => {
465
+ const context = createHostCallContext(`nestedHost:createHost:${crypto.randomUUID()}`);
466
+ return await nestedHost.createHost(context);
467
+ }
468
+ };
469
+ definitions.__isolateHost_closeHost = {
470
+ type: "async",
471
+ fn: async (...args) => {
472
+ const hostId = args[0];
473
+ const context = createHostCallContext(`nestedHost:closeHost:${crypto.randomUUID()}`);
474
+ await nestedHost.closeHost(hostId, context);
475
+ }
476
+ };
477
+ definitions.__isolateHost_hostDiagnostics = {
478
+ type: "async",
479
+ fn: async (...args) => {
480
+ const hostId = args[0];
481
+ const context = createHostCallContext(`nestedHost:diagnostics:${crypto.randomUUID()}`);
482
+ return await nestedHost.diagnostics(hostId, context);
483
+ }
484
+ };
485
+ definitions.__isolateHost_createResource = {
486
+ type: "async",
487
+ fn: async (...args) => {
488
+ const hostId = args[0];
489
+ const kind = args[1];
490
+ const resourceOptions = args[2];
491
+ const context = createHostCallContext(`nestedHost:createResource:${kind}:${crypto.randomUUID()}`);
492
+ return await nestedHost.createResource(hostId, kind, resourceOptions, context);
493
+ }
494
+ };
495
+ definitions.__isolateHost_callResource = {
496
+ type: "async",
497
+ fn: async (...args) => {
498
+ const kind = args[0];
499
+ const resourceId = args[1];
500
+ const method = args[2];
501
+ const methodArgs = args[3];
502
+ const context = createHostCallContext(`nestedHost:callResource:${kind}:${method}:${crypto.randomUUID()}`);
503
+ return await nestedHost.callResource(kind, resourceId, method, Array.isArray(methodArgs) ? methodArgs : [], context);
504
+ }
505
+ };
506
+ definitions.__isolateHost_drainCallbacks = {
507
+ type: "async",
508
+ fn: async (...args) => {
509
+ const callback = args[0];
510
+ if (typeof callback === "function") {
511
+ await callback();
512
+ }
513
+ }
514
+ };
515
+ }
516
+ return Object.keys(definitions).length > 0 ? definitions : undefined;
517
+ }
366
518
 
367
- //# debugId=D27BEABC8CB87D9964756E2164756E21
519
+ //# debugId=8146451726A4D1D664756E2164756E21