browserclaw 0.10.0 → 0.10.2
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 +126 -55
- package/dist/index.cjs +113 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -2
- package/dist/index.d.ts +66 -2
- package/dist/index.js +113 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -77,6 +77,7 @@ type BatchAction = {
|
|
|
77
77
|
url?: string;
|
|
78
78
|
loadState?: 'load' | 'domcontentloaded' | 'networkidle';
|
|
79
79
|
fn?: string;
|
|
80
|
+
arg?: unknown;
|
|
80
81
|
targetId?: string;
|
|
81
82
|
timeoutMs?: number;
|
|
82
83
|
} | {
|
|
@@ -411,7 +412,9 @@ interface WaitOptions {
|
|
|
411
412
|
/** Wait for a specific page load state */
|
|
412
413
|
loadState?: 'load' | 'domcontentloaded' | 'networkidle';
|
|
413
414
|
/** Wait until a JavaScript function returns truthy (evaluated in browser context). Accepts a string or a serializable function. */
|
|
414
|
-
fn?: string | (() => unknown);
|
|
415
|
+
fn?: string | ((arg?: unknown) => unknown);
|
|
416
|
+
/** Serializable argument passed to `fn` in the browser context. Use an array or object to pass multiple values. */
|
|
417
|
+
arg?: unknown;
|
|
415
418
|
/** Timeout for each condition in milliseconds. Default: `20000` */
|
|
416
419
|
timeoutMs?: number;
|
|
417
420
|
}
|
|
@@ -540,6 +543,23 @@ interface DialogEvent {
|
|
|
540
543
|
}
|
|
541
544
|
/** Callback for persistent dialog handling. */
|
|
542
545
|
type DialogHandler = (event: DialogEvent) => void | Promise<void>;
|
|
546
|
+
/** Result of waiting for a network request to complete. */
|
|
547
|
+
interface RequestResult {
|
|
548
|
+
/** The final request URL */
|
|
549
|
+
url: string;
|
|
550
|
+
/** HTTP method (e.g. `'GET'`, `'POST'`) */
|
|
551
|
+
method: string;
|
|
552
|
+
/** Request body (for POST/PUT/PATCH requests) */
|
|
553
|
+
postData?: string;
|
|
554
|
+
/** HTTP response status code */
|
|
555
|
+
status: number;
|
|
556
|
+
/** Whether the response status was 2xx */
|
|
557
|
+
ok: boolean;
|
|
558
|
+
/** Response body text */
|
|
559
|
+
responseBody?: string;
|
|
560
|
+
/** Whether the response body was truncated due to maxChars */
|
|
561
|
+
truncated?: boolean;
|
|
562
|
+
}
|
|
543
563
|
/** Result of intercepting a response body. */
|
|
544
564
|
interface ResponseBodyResult {
|
|
545
565
|
/** The response URL */
|
|
@@ -1165,6 +1185,30 @@ declare class CrawlPage {
|
|
|
1165
1185
|
timeoutMs?: number;
|
|
1166
1186
|
maxChars?: number;
|
|
1167
1187
|
}): Promise<ResponseBodyResult>;
|
|
1188
|
+
/**
|
|
1189
|
+
* Wait for a network request matching a URL pattern and return request + response details.
|
|
1190
|
+
*
|
|
1191
|
+
* Unlike `networkRequests()` which only captures metadata, this method captures
|
|
1192
|
+
* the full request body (POST data) and response body.
|
|
1193
|
+
*
|
|
1194
|
+
* @param url - URL string or pattern to match (supports `*` wildcards and substring matching)
|
|
1195
|
+
* @param opts - Options (method filter, timeoutMs, maxChars for response body)
|
|
1196
|
+
* @returns Request method, postData, response status, and response body
|
|
1197
|
+
*
|
|
1198
|
+
* @example
|
|
1199
|
+
* ```ts
|
|
1200
|
+
* const reqPromise = page.waitForRequest('/api/submit', { method: 'POST' });
|
|
1201
|
+
* await page.click('e5'); // submit a form
|
|
1202
|
+
* const req = await reqPromise;
|
|
1203
|
+
* console.log(req.postData); // form body
|
|
1204
|
+
* console.log(req.status, req.responseBody); // response
|
|
1205
|
+
* ```
|
|
1206
|
+
*/
|
|
1207
|
+
waitForRequest(url: string, opts?: {
|
|
1208
|
+
method?: string;
|
|
1209
|
+
timeoutMs?: number;
|
|
1210
|
+
maxChars?: number;
|
|
1211
|
+
}): Promise<RequestResult>;
|
|
1168
1212
|
/**
|
|
1169
1213
|
* Get console messages captured from the page.
|
|
1170
1214
|
*
|
|
@@ -1535,6 +1579,26 @@ declare class BrowserClaw {
|
|
|
1535
1579
|
* @returns Array of tab information objects
|
|
1536
1580
|
*/
|
|
1537
1581
|
tabs(): Promise<BrowserTab[]>;
|
|
1582
|
+
/**
|
|
1583
|
+
* Wait for a tab matching the given criteria and return a page handle.
|
|
1584
|
+
*
|
|
1585
|
+
* Polls open tabs until one matches, then focuses it and returns a CrawlPage.
|
|
1586
|
+
*
|
|
1587
|
+
* @param opts - Match criteria (urlContains, titleContains) and timeout
|
|
1588
|
+
* @returns A CrawlPage for the matched tab
|
|
1589
|
+
*
|
|
1590
|
+
* @example
|
|
1591
|
+
* ```ts
|
|
1592
|
+
* await page.click('e5'); // opens a new tab
|
|
1593
|
+
* const appPage = await browser.waitForTab({ urlContains: 'app-web' });
|
|
1594
|
+
* const { snapshot } = await appPage.snapshot();
|
|
1595
|
+
* ```
|
|
1596
|
+
*/
|
|
1597
|
+
waitForTab(opts: {
|
|
1598
|
+
urlContains?: string;
|
|
1599
|
+
titleContains?: string;
|
|
1600
|
+
timeoutMs?: number;
|
|
1601
|
+
}): Promise<CrawlPage>;
|
|
1538
1602
|
/**
|
|
1539
1603
|
* Bring a tab to the foreground.
|
|
1540
1604
|
*
|
|
@@ -1793,4 +1857,4 @@ declare function waitForChallengeViaPlaywright(opts: {
|
|
|
1793
1857
|
pollMs?: number;
|
|
1794
1858
|
}): Promise<ChallengeWaitResult>;
|
|
1795
1859
|
|
|
1796
|
-
export { type AriaNode, type AriaSnapshotResult, type BatchAction, type BatchActionResult, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserNavigationRequestLike, type BrowserTab, BrowserTabNotFoundError, type ChallengeInfo, type ChallengeKind, type ChallengeWaitResult, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type ContextState, type CookieData, CrawlPage, type DialogEvent, type DialogHandler, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type PinnedHostname, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, STEALTH_SCRIPT, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|
|
1860
|
+
export { type AriaNode, type AriaSnapshotResult, type BatchAction, type BatchActionResult, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserNavigationRequestLike, type BrowserTab, BrowserTabNotFoundError, type ChallengeInfo, type ChallengeKind, type ChallengeWaitResult, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type ContextState, type CookieData, CrawlPage, type DialogEvent, type DialogHandler, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type PinnedHostname, type RequestResult, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, STEALTH_SCRIPT, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|
package/dist/index.d.ts
CHANGED
|
@@ -77,6 +77,7 @@ type BatchAction = {
|
|
|
77
77
|
url?: string;
|
|
78
78
|
loadState?: 'load' | 'domcontentloaded' | 'networkidle';
|
|
79
79
|
fn?: string;
|
|
80
|
+
arg?: unknown;
|
|
80
81
|
targetId?: string;
|
|
81
82
|
timeoutMs?: number;
|
|
82
83
|
} | {
|
|
@@ -411,7 +412,9 @@ interface WaitOptions {
|
|
|
411
412
|
/** Wait for a specific page load state */
|
|
412
413
|
loadState?: 'load' | 'domcontentloaded' | 'networkidle';
|
|
413
414
|
/** Wait until a JavaScript function returns truthy (evaluated in browser context). Accepts a string or a serializable function. */
|
|
414
|
-
fn?: string | (() => unknown);
|
|
415
|
+
fn?: string | ((arg?: unknown) => unknown);
|
|
416
|
+
/** Serializable argument passed to `fn` in the browser context. Use an array or object to pass multiple values. */
|
|
417
|
+
arg?: unknown;
|
|
415
418
|
/** Timeout for each condition in milliseconds. Default: `20000` */
|
|
416
419
|
timeoutMs?: number;
|
|
417
420
|
}
|
|
@@ -540,6 +543,23 @@ interface DialogEvent {
|
|
|
540
543
|
}
|
|
541
544
|
/** Callback for persistent dialog handling. */
|
|
542
545
|
type DialogHandler = (event: DialogEvent) => void | Promise<void>;
|
|
546
|
+
/** Result of waiting for a network request to complete. */
|
|
547
|
+
interface RequestResult {
|
|
548
|
+
/** The final request URL */
|
|
549
|
+
url: string;
|
|
550
|
+
/** HTTP method (e.g. `'GET'`, `'POST'`) */
|
|
551
|
+
method: string;
|
|
552
|
+
/** Request body (for POST/PUT/PATCH requests) */
|
|
553
|
+
postData?: string;
|
|
554
|
+
/** HTTP response status code */
|
|
555
|
+
status: number;
|
|
556
|
+
/** Whether the response status was 2xx */
|
|
557
|
+
ok: boolean;
|
|
558
|
+
/** Response body text */
|
|
559
|
+
responseBody?: string;
|
|
560
|
+
/** Whether the response body was truncated due to maxChars */
|
|
561
|
+
truncated?: boolean;
|
|
562
|
+
}
|
|
543
563
|
/** Result of intercepting a response body. */
|
|
544
564
|
interface ResponseBodyResult {
|
|
545
565
|
/** The response URL */
|
|
@@ -1165,6 +1185,30 @@ declare class CrawlPage {
|
|
|
1165
1185
|
timeoutMs?: number;
|
|
1166
1186
|
maxChars?: number;
|
|
1167
1187
|
}): Promise<ResponseBodyResult>;
|
|
1188
|
+
/**
|
|
1189
|
+
* Wait for a network request matching a URL pattern and return request + response details.
|
|
1190
|
+
*
|
|
1191
|
+
* Unlike `networkRequests()` which only captures metadata, this method captures
|
|
1192
|
+
* the full request body (POST data) and response body.
|
|
1193
|
+
*
|
|
1194
|
+
* @param url - URL string or pattern to match (supports `*` wildcards and substring matching)
|
|
1195
|
+
* @param opts - Options (method filter, timeoutMs, maxChars for response body)
|
|
1196
|
+
* @returns Request method, postData, response status, and response body
|
|
1197
|
+
*
|
|
1198
|
+
* @example
|
|
1199
|
+
* ```ts
|
|
1200
|
+
* const reqPromise = page.waitForRequest('/api/submit', { method: 'POST' });
|
|
1201
|
+
* await page.click('e5'); // submit a form
|
|
1202
|
+
* const req = await reqPromise;
|
|
1203
|
+
* console.log(req.postData); // form body
|
|
1204
|
+
* console.log(req.status, req.responseBody); // response
|
|
1205
|
+
* ```
|
|
1206
|
+
*/
|
|
1207
|
+
waitForRequest(url: string, opts?: {
|
|
1208
|
+
method?: string;
|
|
1209
|
+
timeoutMs?: number;
|
|
1210
|
+
maxChars?: number;
|
|
1211
|
+
}): Promise<RequestResult>;
|
|
1168
1212
|
/**
|
|
1169
1213
|
* Get console messages captured from the page.
|
|
1170
1214
|
*
|
|
@@ -1535,6 +1579,26 @@ declare class BrowserClaw {
|
|
|
1535
1579
|
* @returns Array of tab information objects
|
|
1536
1580
|
*/
|
|
1537
1581
|
tabs(): Promise<BrowserTab[]>;
|
|
1582
|
+
/**
|
|
1583
|
+
* Wait for a tab matching the given criteria and return a page handle.
|
|
1584
|
+
*
|
|
1585
|
+
* Polls open tabs until one matches, then focuses it and returns a CrawlPage.
|
|
1586
|
+
*
|
|
1587
|
+
* @param opts - Match criteria (urlContains, titleContains) and timeout
|
|
1588
|
+
* @returns A CrawlPage for the matched tab
|
|
1589
|
+
*
|
|
1590
|
+
* @example
|
|
1591
|
+
* ```ts
|
|
1592
|
+
* await page.click('e5'); // opens a new tab
|
|
1593
|
+
* const appPage = await browser.waitForTab({ urlContains: 'app-web' });
|
|
1594
|
+
* const { snapshot } = await appPage.snapshot();
|
|
1595
|
+
* ```
|
|
1596
|
+
*/
|
|
1597
|
+
waitForTab(opts: {
|
|
1598
|
+
urlContains?: string;
|
|
1599
|
+
titleContains?: string;
|
|
1600
|
+
timeoutMs?: number;
|
|
1601
|
+
}): Promise<CrawlPage>;
|
|
1538
1602
|
/**
|
|
1539
1603
|
* Bring a tab to the foreground.
|
|
1540
1604
|
*
|
|
@@ -1793,4 +1857,4 @@ declare function waitForChallengeViaPlaywright(opts: {
|
|
|
1793
1857
|
pollMs?: number;
|
|
1794
1858
|
}): Promise<ChallengeWaitResult>;
|
|
1795
1859
|
|
|
1796
|
-
export { type AriaNode, type AriaSnapshotResult, type BatchAction, type BatchActionResult, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserNavigationRequestLike, type BrowserTab, BrowserTabNotFoundError, type ChallengeInfo, type ChallengeKind, type ChallengeWaitResult, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type ContextState, type CookieData, CrawlPage, type DialogEvent, type DialogHandler, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type PinnedHostname, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, STEALTH_SCRIPT, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|
|
1860
|
+
export { type AriaNode, type AriaSnapshotResult, type BatchAction, type BatchActionResult, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserNavigationRequestLike, type BrowserTab, BrowserTabNotFoundError, type ChallengeInfo, type ChallengeKind, type ChallengeWaitResult, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type ContextState, type CookieData, CrawlPage, type DialogEvent, type DialogHandler, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type PinnedHostname, type RequestResult, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, STEALTH_SCRIPT, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|
package/dist/index.js
CHANGED
|
@@ -3107,7 +3107,7 @@ async function pressAndHoldViaCdp(opts) {
|
|
|
3107
3107
|
async function clickByTextViaPlaywright(opts) {
|
|
3108
3108
|
const page = await getRestoredPageForTarget(opts);
|
|
3109
3109
|
const timeout = resolveInteractionTimeoutMs(opts.timeoutMs);
|
|
3110
|
-
const locator = page.getByText(opts.text, { exact: opts.exact }).or(page.getByTitle(opts.text, { exact: opts.exact })).first();
|
|
3110
|
+
const locator = page.getByText(opts.text, { exact: opts.exact }).and(page.locator(":visible")).or(page.getByTitle(opts.text, { exact: opts.exact })).first();
|
|
3111
3111
|
try {
|
|
3112
3112
|
await locator.click({ timeout, button: opts.button, modifiers: opts.modifiers });
|
|
3113
3113
|
} catch (err) {
|
|
@@ -3507,6 +3507,27 @@ async function focusPageByTargetIdViaPlaywright(opts) {
|
|
|
3507
3507
|
}
|
|
3508
3508
|
}
|
|
3509
3509
|
}
|
|
3510
|
+
async function waitForTabViaPlaywright(opts) {
|
|
3511
|
+
if (opts.urlContains === void 0 && opts.titleContains === void 0)
|
|
3512
|
+
throw new Error("urlContains or titleContains is required");
|
|
3513
|
+
const timeout = Math.max(1e3, Math.min(12e4, opts.timeoutMs ?? 3e4));
|
|
3514
|
+
const start = Date.now();
|
|
3515
|
+
const POLL_INTERVAL_MS = 250;
|
|
3516
|
+
while (Date.now() - start < timeout) {
|
|
3517
|
+
const tabs = await listPagesViaPlaywright({ cdpUrl: opts.cdpUrl });
|
|
3518
|
+
const match = tabs.find((t) => {
|
|
3519
|
+
if (opts.urlContains !== void 0 && !t.url.includes(opts.urlContains)) return false;
|
|
3520
|
+
if (opts.titleContains !== void 0 && !t.title.includes(opts.titleContains)) return false;
|
|
3521
|
+
return true;
|
|
3522
|
+
});
|
|
3523
|
+
if (match) return match;
|
|
3524
|
+
await new Promise((resolve2) => setTimeout(resolve2, POLL_INTERVAL_MS));
|
|
3525
|
+
}
|
|
3526
|
+
const criteria = [];
|
|
3527
|
+
if (opts.urlContains !== void 0) criteria.push(`url contains "${opts.urlContains}"`);
|
|
3528
|
+
if (opts.titleContains !== void 0) criteria.push(`title contains "${opts.titleContains}"`);
|
|
3529
|
+
throw new Error(`Timed out waiting for tab: ${criteria.join(", ")}`);
|
|
3530
|
+
}
|
|
3510
3531
|
async function resizeViewportViaPlaywright(opts) {
|
|
3511
3532
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
3512
3533
|
ensurePageState(page);
|
|
@@ -3550,10 +3571,10 @@ async function waitForViaPlaywright(opts) {
|
|
|
3550
3571
|
}
|
|
3551
3572
|
if (opts.fn !== void 0) {
|
|
3552
3573
|
if (typeof opts.fn === "function") {
|
|
3553
|
-
await page.waitForFunction(opts.fn,
|
|
3574
|
+
await page.waitForFunction(opts.fn, opts.arg, { timeout });
|
|
3554
3575
|
} else {
|
|
3555
3576
|
const fn = opts.fn.trim();
|
|
3556
|
-
if (fn !== "") await page.waitForFunction(fn,
|
|
3577
|
+
if (fn !== "") await page.waitForFunction(fn, opts.arg, { timeout });
|
|
3557
3578
|
}
|
|
3558
3579
|
}
|
|
3559
3580
|
}
|
|
@@ -3666,6 +3687,7 @@ async function executeSingleAction(action, cdpUrl, targetId, evaluateEnabled, de
|
|
|
3666
3687
|
url: action.url,
|
|
3667
3688
|
loadState: action.loadState,
|
|
3668
3689
|
fn: action.fn,
|
|
3690
|
+
arg: action.arg,
|
|
3669
3691
|
timeoutMs: action.timeoutMs
|
|
3670
3692
|
});
|
|
3671
3693
|
break;
|
|
@@ -4141,6 +4163,40 @@ async function responseBodyViaPlaywright(opts) {
|
|
|
4141
4163
|
truncated
|
|
4142
4164
|
};
|
|
4143
4165
|
}
|
|
4166
|
+
async function waitForRequestViaPlaywright(opts) {
|
|
4167
|
+
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
4168
|
+
ensurePageState(page);
|
|
4169
|
+
const timeout = normalizeTimeoutMs(opts.timeoutMs, 3e4, 12e4);
|
|
4170
|
+
const pattern = opts.url.trim();
|
|
4171
|
+
if (!pattern) throw new Error("url is required");
|
|
4172
|
+
const upperMethod = opts.method !== void 0 ? opts.method.toUpperCase() : void 0;
|
|
4173
|
+
const response = await page.waitForResponse(
|
|
4174
|
+
(resp) => matchUrlPattern(pattern, resp.url()) && (upperMethod === void 0 || resp.request().method() === upperMethod),
|
|
4175
|
+
{ timeout }
|
|
4176
|
+
);
|
|
4177
|
+
const request = response.request();
|
|
4178
|
+
let responseBody;
|
|
4179
|
+
let truncated = false;
|
|
4180
|
+
try {
|
|
4181
|
+
responseBody = await response.text();
|
|
4182
|
+
const maxChars = typeof opts.maxChars === "number" && Number.isFinite(opts.maxChars) ? Math.max(1, Math.min(5e6, Math.floor(opts.maxChars))) : 2e5;
|
|
4183
|
+
if (responseBody.length > maxChars) {
|
|
4184
|
+
responseBody = responseBody.slice(0, maxChars);
|
|
4185
|
+
truncated = true;
|
|
4186
|
+
}
|
|
4187
|
+
} catch (err) {
|
|
4188
|
+
console.warn("[browserclaw] response body unavailable:", err instanceof Error ? err.message : String(err));
|
|
4189
|
+
}
|
|
4190
|
+
return {
|
|
4191
|
+
url: response.url(),
|
|
4192
|
+
method: request.method(),
|
|
4193
|
+
postData: request.postData() ?? void 0,
|
|
4194
|
+
status: response.status(),
|
|
4195
|
+
ok: response.ok(),
|
|
4196
|
+
responseBody,
|
|
4197
|
+
truncated
|
|
4198
|
+
};
|
|
4199
|
+
}
|
|
4144
4200
|
|
|
4145
4201
|
// src/capture/screenshot.ts
|
|
4146
4202
|
async function takeScreenshotViaPlaywright(opts) {
|
|
@@ -5577,6 +5633,35 @@ var CrawlPage = class {
|
|
|
5577
5633
|
maxChars: opts?.maxChars
|
|
5578
5634
|
});
|
|
5579
5635
|
}
|
|
5636
|
+
/**
|
|
5637
|
+
* Wait for a network request matching a URL pattern and return request + response details.
|
|
5638
|
+
*
|
|
5639
|
+
* Unlike `networkRequests()` which only captures metadata, this method captures
|
|
5640
|
+
* the full request body (POST data) and response body.
|
|
5641
|
+
*
|
|
5642
|
+
* @param url - URL string or pattern to match (supports `*` wildcards and substring matching)
|
|
5643
|
+
* @param opts - Options (method filter, timeoutMs, maxChars for response body)
|
|
5644
|
+
* @returns Request method, postData, response status, and response body
|
|
5645
|
+
*
|
|
5646
|
+
* @example
|
|
5647
|
+
* ```ts
|
|
5648
|
+
* const reqPromise = page.waitForRequest('/api/submit', { method: 'POST' });
|
|
5649
|
+
* await page.click('e5'); // submit a form
|
|
5650
|
+
* const req = await reqPromise;
|
|
5651
|
+
* console.log(req.postData); // form body
|
|
5652
|
+
* console.log(req.status, req.responseBody); // response
|
|
5653
|
+
* ```
|
|
5654
|
+
*/
|
|
5655
|
+
async waitForRequest(url, opts) {
|
|
5656
|
+
return waitForRequestViaPlaywright({
|
|
5657
|
+
cdpUrl: this.cdpUrl,
|
|
5658
|
+
targetId: this.targetId,
|
|
5659
|
+
url,
|
|
5660
|
+
method: opts?.method,
|
|
5661
|
+
timeoutMs: opts?.timeoutMs,
|
|
5662
|
+
maxChars: opts?.maxChars
|
|
5663
|
+
});
|
|
5664
|
+
}
|
|
5580
5665
|
/**
|
|
5581
5666
|
* Get console messages captured from the page.
|
|
5582
5667
|
*
|
|
@@ -6099,6 +6184,31 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
6099
6184
|
async tabs() {
|
|
6100
6185
|
return listPagesViaPlaywright({ cdpUrl: this.cdpUrl });
|
|
6101
6186
|
}
|
|
6187
|
+
/**
|
|
6188
|
+
* Wait for a tab matching the given criteria and return a page handle.
|
|
6189
|
+
*
|
|
6190
|
+
* Polls open tabs until one matches, then focuses it and returns a CrawlPage.
|
|
6191
|
+
*
|
|
6192
|
+
* @param opts - Match criteria (urlContains, titleContains) and timeout
|
|
6193
|
+
* @returns A CrawlPage for the matched tab
|
|
6194
|
+
*
|
|
6195
|
+
* @example
|
|
6196
|
+
* ```ts
|
|
6197
|
+
* await page.click('e5'); // opens a new tab
|
|
6198
|
+
* const appPage = await browser.waitForTab({ urlContains: 'app-web' });
|
|
6199
|
+
* const { snapshot } = await appPage.snapshot();
|
|
6200
|
+
* ```
|
|
6201
|
+
*/
|
|
6202
|
+
async waitForTab(opts) {
|
|
6203
|
+
const tab = await waitForTabViaPlaywright({
|
|
6204
|
+
cdpUrl: this.cdpUrl,
|
|
6205
|
+
urlContains: opts.urlContains,
|
|
6206
|
+
titleContains: opts.titleContains,
|
|
6207
|
+
timeoutMs: opts.timeoutMs
|
|
6208
|
+
});
|
|
6209
|
+
await focusPageByTargetIdViaPlaywright({ cdpUrl: this.cdpUrl, targetId: tab.targetId });
|
|
6210
|
+
return new CrawlPage(this.cdpUrl, tab.targetId, this.ssrfPolicy);
|
|
6211
|
+
}
|
|
6102
6212
|
/**
|
|
6103
6213
|
* Bring a tab to the foreground.
|
|
6104
6214
|
*
|