browserclaw 0.11.4 → 0.11.6

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 CHANGED
@@ -15,16 +15,16 @@ The AI-native browser automation library — born from [OpenClaw](https://github
15
15
  ```typescript
16
16
  import { BrowserClaw } from 'browserclaw';
17
17
 
18
- const browser = await BrowserClaw.launch({ url: 'https://example.com' });
18
+ const browser = await BrowserClaw.launch({ url: 'https://demo.playwright.dev/todomvc' });
19
19
  const page = await browser.currentPage();
20
20
 
21
21
  // Snapshot — the core feature
22
22
  const { snapshot, refs } = await page.snapshot();
23
23
  // snapshot: AI-readable text tree
24
- // refs: { "e1": { role: "link", name: "More info" }, "e2": { role: "button", name: "Submit" } }
24
+ // refs: { "e1": { role: "textbox", name: "What needs to be done?" }, "e2": { role: "link", name: "Playwright" } }
25
25
 
26
- await page.click('e1'); // Click by ref
27
- await page.type('e3', 'hello'); // Type by ref
26
+ await page.type('e1', 'Buy groceries', { submit: true }); // Type by ref
27
+ await page.click('e2'); // Click by ref
28
28
  await browser.stop();
29
29
  ```
30
30
 
@@ -102,20 +102,20 @@ Requires a Chromium-based browser installed on the system (Chrome, Brave, Edge,
102
102
  ## How It Works
103
103
 
104
104
  ```
105
- ┌─────────────┐ snapshot() ┌─────────────────────────────────┐
106
- │ Web Page │ ──────────────► │ AI-readable text tree
107
- │ │ │
108
- │ [buttons] │ │ - heading "Example Domain"
109
- │ [links] │ │ - paragraph "This domain..."
110
- │ [inputs] │ │ - link "More information" [e1]
111
- └─────────────┘ └──────────────┬──────────────────┘
105
+ ┌─────────────┐ snapshot() ┌──────────────────────────────────────────┐
106
+ │ Web Page │ ──────────────► │ AI-readable text tree
107
+ │ │ │
108
+ │ [buttons] │ │ - heading "todos"
109
+ │ [links] │ │ - textbox "What needs to be done?" [e1]
110
+ │ [inputs] │ │ - link "Playwright" [e2]
111
+ └─────────────┘ └──────────────┬───────────────────────────┘
112
112
 
113
113
  AI reads snapshot,
114
- decides: click e1
114
+ decides: type in e1
115
115
 
116
- ┌─────────────┐ click('e1') ┌──────────────▼──────────────────┐
116
+ ┌─────────────┐ type('e1',...) ┌──────────────▼──────────────────┐
117
117
  │ Web Page │ ◄────────────── │ Ref "e1" resolves to a │
118
- │ (navigated)│ │ Playwright locator — one ref, │
118
+ │ (updated) │ │ Playwright locator — one ref, │
119
119
  │ │ │ one exact element │
120
120
  └─────────────┘ └─────────────────────────────────┘
121
121
  ```
@@ -133,7 +133,7 @@ Requires a Chromium-based browser installed on the system (Chrome, Brave, Edge,
133
133
  ```typescript
134
134
  // Launch a new Chrome instance (auto-detects Chrome/Brave/Edge/Chromium)
135
135
  const browser = await BrowserClaw.launch({
136
- url: 'https://example.com', // navigate initial tab (no extra tabs)
136
+ url: 'https://demo.playwright.dev/todomvc', // navigate initial tab (no extra tabs)
137
137
  headless: false, // default: false (visible window)
138
138
  executablePath: '...', // optional: specific browser path
139
139
  cdpPort: 9222, // default: 9222
@@ -159,7 +159,7 @@ const browser = await BrowserClaw.connect();
159
159
  ### Pages & Tabs
160
160
 
161
161
  ```typescript
162
- const page = await browser.open('https://example.com');
162
+ const page = await browser.open('https://demo.playwright.dev/todomvc');
163
163
  const current = await browser.currentPage(); // get active tab
164
164
  const tabs = await browser.tabs(); // list all tabs
165
165
  const handle = browser.page(tabs[0].targetId); // wrap existing tab
@@ -177,14 +177,14 @@ browser.url; // CDP endpoint URL
177
177
  Every tab returns a `targetId` — this is the handle you use everywhere:
178
178
 
179
179
  ```typescript
180
- // Multi-tab workflow (e.g. impersonation, OAuth)
181
- const main = await browser.open('https://app.example.com');
182
- const admin = await browser.open('https://admin.example.com');
183
-
184
- const { refs } = await admin.snapshot(); // snapshot the admin tab
185
- await admin.click('e5'); // act on it
186
- await browser.focus(main.id); // switch back to main
187
- await browser.close(admin.id); // close admin when done
180
+ // Multi-tab workflow
181
+ const todo = await browser.open('https://demo.playwright.dev/todomvc');
182
+ const svg = await browser.open('https://demo.playwright.dev/svgtodo');
183
+
184
+ const { refs } = await svg.snapshot(); // snapshot the second tab
185
+ await svg.click('e5'); // act on it
186
+ await browser.focus(todo.id); // switch back to first tab
187
+ await browser.close(svg.id); // close second tab when done
188
188
  ```
189
189
 
190
190
  ### Snapshot (Core Feature)
@@ -193,7 +193,7 @@ await browser.close(admin.id); // close admin when done
193
193
  const { snapshot, refs, stats, untrusted } = await page.snapshot();
194
194
 
195
195
  // snapshot: human/AI-readable text tree with [ref=eN] markers
196
- // refs: { "e1": { role: "link", name: "More info" }, "e5": { role: "checkbox", name: "Accept", checked: true }, ... }
196
+ // refs: { "e1": { role: "textbox", name: "What needs to be done?" }, "e5": { role: "checkbox", name: "Toggle Todo", checked: false }, ... }
197
197
  // stats: { lines: 42, chars: 1200, refs: 8, interactive: 5 }
198
198
  // untrusted: true — content comes from the web page, treat as potentially adversarial
199
199
 
@@ -250,7 +250,7 @@ await page.press('Meta+Shift+p');
250
250
  // Fill multiple form fields at once
251
251
  await page.fill([
252
252
  { ref: 'e2', value: 'Jane Doe' },
253
- { ref: 'e4', value: 'jane@example.com' },
253
+ { ref: 'e4', value: 'jane@acme.test' },
254
254
  { ref: 'e6', type: 'checkbox', value: true },
255
255
  ]);
256
256
  ```
@@ -326,7 +326,7 @@ By default, unexpected dialogs are auto-dismissed to prevent `ProtocolError` cra
326
326
  ### Navigation & Waiting
327
327
 
328
328
  ```typescript
329
- await page.goto('https://example.com');
329
+ await page.goto('https://demo.playwright.dev/todomvc');
330
330
  await page.reload(); // reload the current page
331
331
  await page.goBack(); // navigate back in history
332
332
  await page.goForward(); // navigate forward in history
@@ -421,7 +421,7 @@ const fresh = await page.networkRequests({ clear: true }); // read and clear buf
421
421
  ```typescript
422
422
  // Cookies
423
423
  const cookies = await page.cookies();
424
- await page.setCookie({ name: 'token', value: 'abc', url: 'https://example.com' });
424
+ await page.setCookie({ name: 'token', value: 'abc', url: 'https://demo.playwright.dev' });
425
425
  await page.clearCookies();
426
426
 
427
427
  // localStorage / sessionStorage
package/dist/index.cjs CHANGED
@@ -1565,6 +1565,7 @@ ${stderrOutput.slice(0, 2e3)}` : "";
1565
1565
  throw new Error(`Failed to start Chrome CDP on port ${String(cdpPort)}.${sandboxHint}${stderrHint}`);
1566
1566
  }
1567
1567
  proc.stderr.off("data", onStderr);
1568
+ proc.stderr.resume();
1568
1569
  stderrChunks.length = 0;
1569
1570
  return {
1570
1571
  pid: proc.pid ?? -1,
@@ -1894,6 +1895,7 @@ function ensurePageState(page) {
1894
1895
  page.on("dialog", (dialog) => {
1895
1896
  if (state.armIdDialog > 0) return;
1896
1897
  if (state.dialogHandler) {
1898
+ const handler = state.dialogHandler;
1897
1899
  let handled = false;
1898
1900
  const event = {
1899
1901
  type: dialog.type(),
@@ -1908,7 +1910,7 @@ function ensurePageState(page) {
1908
1910
  return dialog.dismiss();
1909
1911
  }
1910
1912
  };
1911
- Promise.resolve(state.dialogHandler(event)).then(() => {
1913
+ Promise.resolve().then(() => handler(event)).then(() => {
1912
1914
  if (!handled) {
1913
1915
  dialog.dismiss().catch((err) => {
1914
1916
  console.warn(
@@ -2116,7 +2118,9 @@ async function fetchJsonForCdp(url, timeoutMs) {
2116
2118
  const res = await fetch(url, { signal: ctrl.signal });
2117
2119
  if (!res.ok) return null;
2118
2120
  return await res.json();
2119
- } catch {
2121
+ } catch (err) {
2122
+ if (process.env.DEBUG !== void 0 && process.env.DEBUG !== "")
2123
+ console.warn(`[browserclaw] fetchJsonForCdp ${url} failed: ${err instanceof Error ? err.message : String(err)}`);
2120
2124
  return null;
2121
2125
  } finally {
2122
2126
  clearTimeout(t);
@@ -2324,11 +2328,15 @@ async function disconnectBrowser() {
2324
2328
  for (const p of connectingByCdpUrl.values()) {
2325
2329
  try {
2326
2330
  await p;
2327
- } catch {
2331
+ } catch (err) {
2332
+ console.warn(
2333
+ `[browserclaw] disconnectBrowser: pending connect failed: ${err instanceof Error ? err.message : String(err)}`
2334
+ );
2328
2335
  }
2329
2336
  }
2330
2337
  }
2331
2338
  for (const cur of cachedByCdpUrl.values()) {
2339
+ clearRoleRefsForCdpUrl(cur.cdpUrl);
2332
2340
  if (cur.onDisconnected && typeof cur.browser.off === "function")
2333
2341
  cur.browser.off("disconnected", cur.onDisconnected);
2334
2342
  await cur.browser.close().catch(() => {
@@ -2450,12 +2458,17 @@ var forceDisconnectPlaywrightForTarget = forceDisconnectPlaywrightConnection;
2450
2458
  function getAllPages(browser) {
2451
2459
  return browser.contexts().flatMap((c) => c.pages());
2452
2460
  }
2461
+ var pageTargetIdCache = /* @__PURE__ */ new WeakMap();
2453
2462
  async function pageTargetId(page) {
2463
+ const cached = pageTargetIdCache.get(page);
2464
+ if (cached !== void 0) return cached;
2454
2465
  const session = await page.context().newCDPSession(page);
2455
2466
  try {
2456
2467
  const info = await session.send("Target.getTargetInfo");
2457
2468
  const targetInfo = info.targetInfo;
2458
- return (targetInfo?.targetId ?? "").trim() || null;
2469
+ const id = (targetInfo?.targetId ?? "").trim() || null;
2470
+ if (id !== null) pageTargetIdCache.set(page, id);
2471
+ return id;
2459
2472
  } finally {
2460
2473
  await session.detach().catch(() => {
2461
2474
  });
@@ -2485,17 +2498,19 @@ async function findPageByTargetIdViaTargetList(pages, targetId, cdpUrl) {
2485
2498
  }
2486
2499
  async function findPageByTargetId(browser, targetId, cdpUrl) {
2487
2500
  const pages = getAllPages(browser);
2488
- let resolvedViaCdp = false;
2489
- for (const page of pages) {
2490
- let tid = null;
2491
- try {
2492
- tid = await pageTargetId(page);
2493
- resolvedViaCdp = true;
2494
- } catch {
2495
- tid = null;
2496
- }
2497
- if (tid !== null && tid !== "" && tid === targetId) return page;
2498
- }
2501
+ const results = await Promise.all(
2502
+ pages.map(async (page) => {
2503
+ try {
2504
+ const tid = await pageTargetId(page);
2505
+ return { page, tid };
2506
+ } catch {
2507
+ return { page, tid: null };
2508
+ }
2509
+ })
2510
+ );
2511
+ const resolvedViaCdp = results.some(({ tid }) => tid !== null);
2512
+ const matched = results.find(({ tid }) => tid !== null && tid !== "" && tid === targetId);
2513
+ if (matched) return matched.page;
2499
2514
  if (cdpUrl !== void 0 && cdpUrl !== "") {
2500
2515
  try {
2501
2516
  return await findPageByTargetIdViaTargetList(pages, targetId, cdpUrl);
@@ -2551,9 +2566,6 @@ async function getPageForTargetId(opts) {
2551
2566
  );
2552
2567
  }
2553
2568
  if (isBlockedPageRef(opts.cdpUrl, found)) throw new BlockedBrowserTargetError();
2554
- const foundTargetId = await pageTargetId(found).catch(() => null);
2555
- if (foundTargetId !== null && foundTargetId !== "" && isBlockedTarget(opts.cdpUrl, foundTargetId))
2556
- throw new BlockedBrowserTargetError();
2557
2569
  return found;
2558
2570
  }
2559
2571
  async function resolvePageByTargetIdOrThrow(opts) {
@@ -3206,7 +3218,7 @@ async function writeViaSiblingTempPath(params) {
3206
3218
  const requestedTargetPath = path.resolve(params.targetPath);
3207
3219
  const targetPath = await promises$1.realpath(path.dirname(requestedTargetPath)).then((realDir) => path.join(realDir, path.basename(requestedTargetPath))).catch(() => requestedTargetPath);
3208
3220
  const relativeTargetPath = path.relative(rootDir, targetPath);
3209
- if (!relativeTargetPath || relativeTargetPath === ".." || relativeTargetPath.startsWith(`..${path.sep}`) || isAbsolute(relativeTargetPath)) {
3221
+ if (!relativeTargetPath || relativeTargetPath === ".." || relativeTargetPath.startsWith(`..${path.sep}`) || path.isAbsolute(relativeTargetPath)) {
3210
3222
  throw new Error("Target path is outside the allowed root");
3211
3223
  }
3212
3224
  const tempPath = buildSiblingTempPath(targetPath);
@@ -3221,9 +3233,6 @@ async function writeViaSiblingTempPath(params) {
3221
3233
  });
3222
3234
  }
3223
3235
  }
3224
- function isAbsolute(p) {
3225
- return p.startsWith("/") || /^[a-zA-Z]:/.test(p);
3226
- }
3227
3236
  async function assertBrowserNavigationResultAllowed(opts) {
3228
3237
  const rawUrl = opts.url.trim();
3229
3238
  if (rawUrl === "") return;
@@ -3264,7 +3273,6 @@ async function setCheckedViaEvaluate(locator, checked) {
3264
3273
  else input.checked = desired;
3265
3274
  input.dispatchEvent(new Event("input", { bubbles: true }));
3266
3275
  input.dispatchEvent(new Event("change", { bubbles: true }));
3267
- input.click();
3268
3276
  }, checked);
3269
3277
  }
3270
3278
  function resolveLocator(page, resolved) {
@@ -3441,7 +3449,10 @@ async function fillFormViaPlaywright(opts) {
3441
3449
  const checked = rawValue === true || rawValue === 1 || rawValue === "1" || rawValue === "true";
3442
3450
  try {
3443
3451
  await locator.setChecked(checked, { timeout, force: true });
3444
- } catch {
3452
+ } catch (setCheckedErr) {
3453
+ console.warn(
3454
+ `[browserclaw] setChecked fallback for ref "${ref}": ${setCheckedErr instanceof Error ? setCheckedErr.message : String(setCheckedErr)}`
3455
+ );
3445
3456
  try {
3446
3457
  await setCheckedViaEvaluate(locator, checked);
3447
3458
  } catch (err) {
@@ -3553,6 +3564,7 @@ async function armFileUploadViaPlaywright(opts) {
3553
3564
  scopeLabel: `uploads directory (${DEFAULT_UPLOAD_DIR})`
3554
3565
  });
3555
3566
  if (!uploadPathsResult.ok) {
3567
+ console.warn(`[browserclaw] armFileUpload: path validation failed: ${uploadPathsResult.error}`);
3556
3568
  try {
3557
3569
  await page.keyboard.press("Escape");
3558
3570
  } catch {
@@ -4602,14 +4614,17 @@ async function traceStopViaPlaywright(opts) {
4602
4614
  if (!ctxState.traceActive) {
4603
4615
  throw new Error("No active trace. Start a trace before stopping it.");
4604
4616
  }
4605
- await writeViaSiblingTempPath({
4606
- rootDir: path.dirname(opts.path),
4607
- targetPath: opts.path,
4608
- writeTemp: async (tempPath) => {
4609
- await context.tracing.stop({ path: tempPath });
4610
- }
4611
- });
4612
- ctxState.traceActive = false;
4617
+ try {
4618
+ await writeViaSiblingTempPath({
4619
+ rootDir: path.dirname(opts.path),
4620
+ targetPath: opts.path,
4621
+ writeTemp: async (tempPath) => {
4622
+ await context.tracing.stop({ path: tempPath });
4623
+ }
4624
+ });
4625
+ } finally {
4626
+ ctxState.traceActive = false;
4627
+ }
4613
4628
  }
4614
4629
 
4615
4630
  // src/snapshot/ref-map.ts
@@ -4808,7 +4823,7 @@ function buildRoleSnapshotFromAriaSnapshot(ariaSnapshot, options = {}) {
4808
4823
  const isInteractive = INTERACTIVE_ROLES.has(role);
4809
4824
  const isContent = CONTENT_ROLES.has(role);
4810
4825
  const isStructural = STRUCTURAL_ROLES.has(role);
4811
- if (options.compact === true && isStructural && name === "") continue;
4826
+ if (options.compact === true && isStructural && !name) continue;
4812
4827
  if (!(isInteractive || isContent && name !== "")) {
4813
4828
  result.push(line);
4814
4829
  continue;
@@ -4819,7 +4834,7 @@ function buildRoleSnapshotFromAriaSnapshot(ariaSnapshot, options = {}) {
4819
4834
  const state = parseStateFromSuffix(suffix);
4820
4835
  refs[ref] = { role, name, nth, ...state };
4821
4836
  let enhanced = `${prefix}${roleRaw}`;
4822
- if (name !== "") enhanced += ` "${name}"`;
4837
+ if (name) enhanced += ` "${name}"`;
4823
4838
  enhanced += ` [ref=${ref}]`;
4824
4839
  if (nth > 0) enhanced += ` [nth=${String(nth)}]`;
4825
4840
  if (suffix !== "") enhanced += suffix;
@@ -4897,7 +4912,7 @@ function buildRoleSnapshotFromAiSnapshot(aiSnapshot, options = {}) {
4897
4912
  }
4898
4913
  const role = roleRaw.toLowerCase();
4899
4914
  const isStructural = STRUCTURAL_ROLES.has(role);
4900
- if (options.compact === true && isStructural && name === "") continue;
4915
+ if (options.compact === true && isStructural && !name) continue;
4901
4916
  const ref = parseAiSnapshotRef(suffix);
4902
4917
  const state = parseStateFromSuffix(suffix);
4903
4918
  if (ref !== null) {
@@ -4905,9 +4920,9 @@ function buildRoleSnapshotFromAiSnapshot(aiSnapshot, options = {}) {
4905
4920
  out.push(line);
4906
4921
  } else if (INTERACTIVE_ROLES.has(role)) {
4907
4922
  const generatedRef = nextGeneratedRef();
4908
- refs[generatedRef] = { role, ...name !== "" ? { name } : {}, ...state };
4923
+ refs[generatedRef] = { role, ...name ? { name } : {}, ...state };
4909
4924
  let enhanced = `${prefix}${roleRaw}`;
4910
- if (name !== "") enhanced += ` "${name}"`;
4925
+ if (name) enhanced += ` "${name}"`;
4911
4926
  enhanced += ` [ref=${generatedRef}]`;
4912
4927
  if (suffix.trim() !== "") enhanced += suffix;
4913
4928
  out.push(enhanced);
@@ -4988,7 +5003,7 @@ async function snapshotRole(opts) {
4988
5003
  if (!maybe._snapshotForAI) {
4989
5004
  throw new Error("refs=aria requires Playwright _snapshotForAI support.");
4990
5005
  }
4991
- const result = await maybe._snapshotForAI({ timeout: 5e3, track: "response" });
5006
+ const result = await maybe._snapshotForAI({ timeout: normalizeTimeoutMs(opts.timeoutMs, 5e3), track: "response" });
4992
5007
  const built2 = buildRoleSnapshotFromAiSnapshot(String(result.full), opts.options);
4993
5008
  storeRoleRefsForTarget({
4994
5009
  page,
@@ -5498,7 +5513,7 @@ var CrawlPage = class {
5498
5513
  * ```ts
5499
5514
  * await page.fill([
5500
5515
  * { ref: 'e2', type: 'text', value: 'Jane Doe' },
5501
- * { ref: 'e4', type: 'text', value: 'jane@example.com' },
5516
+ * { ref: 'e4', type: 'text', value: 'jane@acme.test' },
5502
5517
  * { ref: 'e6', type: 'checkbox', value: true },
5503
5518
  * ]);
5504
5519
  * ```
@@ -5551,17 +5566,18 @@ var CrawlPage = class {
5551
5566
  });
5552
5567
  }
5553
5568
  /**
5554
- * Arm a one-shot dialog handler (alert, confirm, prompt).
5569
+ * Arm a one-shot dialog handler (alert, confirm, prompt). Fire-and-forget:
5570
+ * returns immediately once the arm is registered. The dialog is handled in
5571
+ * the background when it fires.
5555
5572
  *
5556
- * Returns a promise store it (don't await), trigger the dialog, then await it.
5573
+ * Call this BEFORE triggering the action that opens the dialog.
5557
5574
  *
5558
5575
  * @param opts - Dialog options (accept/dismiss, prompt text, timeout)
5559
5576
  *
5560
5577
  * @example
5561
5578
  * ```ts
5562
- * const dialogDone = page.armDialog({ accept: true }); // don't await here
5563
- * await page.click('e5'); // triggers confirm()
5564
- * await dialogDone; // wait for dialog to be handled
5579
+ * await page.armDialog({ accept: true }); // registers the handler, returns immediately
5580
+ * await page.click('e5'); // triggers confirm() — handled in background
5565
5581
  * ```
5566
5582
  */
5567
5583
  async armDialog(opts) {
@@ -6043,7 +6059,7 @@ var CrawlPage = class {
6043
6059
  * await page.setCookie({
6044
6060
  * name: 'token',
6045
6061
  * value: 'abc123',
6046
- * url: 'https://example.com',
6062
+ * url: 'https://demo.playwright.dev/todomvc',
6047
6063
  * });
6048
6064
  * ```
6049
6065
  */
@@ -6295,7 +6311,7 @@ var CrawlPage = class {
6295
6311
  *
6296
6312
  * @example
6297
6313
  * ```ts
6298
- * await page.goto('https://example.com');
6314
+ * await page.goto('https://demo.playwright.dev/todomvc');
6299
6315
  * const challenge = await page.detectChallenge();
6300
6316
  * if (challenge?.kind === 'cloudflare-js') {
6301
6317
  * const { resolved } = await page.waitForChallenge({ timeoutMs: 20000 });
@@ -6356,7 +6372,7 @@ var CrawlPage = class {
6356
6372
  *
6357
6373
  * // Use Playwright selectors
6358
6374
  * const input = await page.locator('input[name="email"]');
6359
- * await input.fill('test@example.com');
6375
+ * await input.fill('test@acme.test');
6360
6376
  * ```
6361
6377
  */
6362
6378
  async locator(selector) {
@@ -6387,14 +6403,14 @@ var BrowserClaw = class _BrowserClaw {
6387
6403
  * @example
6388
6404
  * ```ts
6389
6405
  * // Launch and navigate to a URL
6390
- * const browser = await BrowserClaw.launch({ url: 'https://example.com' });
6406
+ * const browser = await BrowserClaw.launch({ url: 'https://demo.playwright.dev/todomvc' });
6391
6407
  *
6392
6408
  * // Headless mode
6393
- * const browser = await BrowserClaw.launch({ url: 'https://example.com', headless: true });
6409
+ * const browser = await BrowserClaw.launch({ url: 'https://demo.playwright.dev/todomvc', headless: true });
6394
6410
  *
6395
6411
  * // Specific browser
6396
6412
  * const browser = await BrowserClaw.launch({
6397
- * url: 'https://example.com',
6413
+ * url: 'https://demo.playwright.dev/todomvc',
6398
6414
  * executablePath: '/usr/bin/google-chrome',
6399
6415
  * });
6400
6416
  * ```
@@ -6450,7 +6466,7 @@ var BrowserClaw = class _BrowserClaw {
6450
6466
  *
6451
6467
  * @example
6452
6468
  * ```ts
6453
- * const page = await browser.open('https://example.com');
6469
+ * const page = await browser.open('https://demo.playwright.dev/todomvc');
6454
6470
  * const { snapshot, refs } = await page.snapshot();
6455
6471
  * ```
6456
6472
  */
@@ -6557,6 +6573,7 @@ var BrowserClaw = class _BrowserClaw {
6557
6573
  }
6558
6574
  };
6559
6575
 
6576
+ exports.BlockedBrowserTargetError = BlockedBrowserTargetError;
6560
6577
  exports.BrowserClaw = BrowserClaw;
6561
6578
  exports.BrowserTabNotFoundError = BrowserTabNotFoundError;
6562
6579
  exports.CrawlPage = CrawlPage;
@@ -6587,6 +6604,7 @@ exports.resolveBoundedDelayMs = resolveBoundedDelayMs;
6587
6604
  exports.resolveInteractionTimeoutMs = resolveInteractionTimeoutMs;
6588
6605
  exports.resolvePageByTargetIdOrThrow = resolvePageByTargetIdOrThrow;
6589
6606
  exports.resolvePinnedHostnameWithPolicy = resolvePinnedHostnameWithPolicy;
6607
+ exports.resolveStrictExistingPathsWithinRoot = resolveStrictExistingPathsWithinRoot;
6590
6608
  exports.resolveStrictExistingUploadPaths = resolveStrictExistingUploadPaths;
6591
6609
  exports.sanitizeUntrustedFileName = sanitizeUntrustedFileName;
6592
6610
  exports.setDialogHandler = setDialogHandler;