browserclaw 0.8.0 → 0.8.1
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/dist/index.cjs +157 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -2
- package/dist/index.d.ts +124 -2
- package/dist/index.js +157 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1929,6 +1929,37 @@ function ensurePageState(page) {
|
|
|
1929
1929
|
});
|
|
1930
1930
|
page.on("dialog", (dialog) => {
|
|
1931
1931
|
if (state.armIdDialog > 0) return;
|
|
1932
|
+
if (state.dialogHandler) {
|
|
1933
|
+
let handled = false;
|
|
1934
|
+
const event = {
|
|
1935
|
+
type: dialog.type(),
|
|
1936
|
+
message: dialog.message(),
|
|
1937
|
+
defaultValue: dialog.defaultValue(),
|
|
1938
|
+
accept: (promptText) => {
|
|
1939
|
+
handled = true;
|
|
1940
|
+
return dialog.accept(promptText);
|
|
1941
|
+
},
|
|
1942
|
+
dismiss: () => {
|
|
1943
|
+
handled = true;
|
|
1944
|
+
return dialog.dismiss();
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1947
|
+
Promise.resolve(state.dialogHandler(event)).then(() => {
|
|
1948
|
+
if (!handled) {
|
|
1949
|
+
dialog.dismiss().catch((err) => {
|
|
1950
|
+
console.warn(`[browserclaw] Failed to auto-dismiss dialog: ${err instanceof Error ? err.message : String(err)}`);
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
}).catch((err) => {
|
|
1954
|
+
console.warn(`[browserclaw] onDialog handler error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1955
|
+
if (!handled) {
|
|
1956
|
+
dialog.dismiss().catch((dismissErr) => {
|
|
1957
|
+
console.warn(`[browserclaw] Failed to dismiss dialog after handler error: ${dismissErr instanceof Error ? dismissErr.message : String(dismissErr)}`);
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1932
1963
|
dialog.dismiss().catch((err) => {
|
|
1933
1964
|
console.warn(`[browserclaw] Failed to dismiss dialog: ${err instanceof Error ? err.message : String(err)}`);
|
|
1934
1965
|
});
|
|
@@ -1940,6 +1971,11 @@ function ensurePageState(page) {
|
|
|
1940
1971
|
}
|
|
1941
1972
|
return state;
|
|
1942
1973
|
}
|
|
1974
|
+
async function setDialogHandler(opts) {
|
|
1975
|
+
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
1976
|
+
const state = ensurePageState(page);
|
|
1977
|
+
state.dialogHandler = opts.handler;
|
|
1978
|
+
}
|
|
1943
1979
|
function applyStealthToPage(page) {
|
|
1944
1980
|
page.evaluate(STEALTH_SCRIPT).catch((e) => {
|
|
1945
1981
|
if (process.env.DEBUG !== void 0 && process.env.DEBUG !== "")
|
|
@@ -4822,6 +4858,32 @@ var CrawlPage = class {
|
|
|
4822
4858
|
timeoutMs: opts?.timeoutMs
|
|
4823
4859
|
});
|
|
4824
4860
|
}
|
|
4861
|
+
/**
|
|
4862
|
+
* Click an element by CSS selector (no snapshot/ref needed).
|
|
4863
|
+
*
|
|
4864
|
+
* Finds and clicks atomically — no stale ref problem.
|
|
4865
|
+
*
|
|
4866
|
+
* @param selector - CSS selector (e.g. `'#submit-btn'`, `'.modal button'`)
|
|
4867
|
+
* @param opts - Click options (double-click, button, modifiers)
|
|
4868
|
+
*
|
|
4869
|
+
* @example
|
|
4870
|
+
* ```ts
|
|
4871
|
+
* await page.clickBySelector('#submit-btn');
|
|
4872
|
+
* await page.clickBySelector('.modal .close', { button: 'right' });
|
|
4873
|
+
* ```
|
|
4874
|
+
*/
|
|
4875
|
+
async clickBySelector(selector, opts) {
|
|
4876
|
+
return clickViaPlaywright({
|
|
4877
|
+
cdpUrl: this.cdpUrl,
|
|
4878
|
+
targetId: this.targetId,
|
|
4879
|
+
selector,
|
|
4880
|
+
doubleClick: opts?.doubleClick,
|
|
4881
|
+
button: opts?.button,
|
|
4882
|
+
modifiers: opts?.modifiers,
|
|
4883
|
+
delayMs: opts?.delayMs,
|
|
4884
|
+
timeoutMs: opts?.timeoutMs
|
|
4885
|
+
});
|
|
4886
|
+
}
|
|
4825
4887
|
/**
|
|
4826
4888
|
* Click at specific page coordinates.
|
|
4827
4889
|
*
|
|
@@ -5065,6 +5127,48 @@ var CrawlPage = class {
|
|
|
5065
5127
|
timeoutMs: opts.timeoutMs
|
|
5066
5128
|
});
|
|
5067
5129
|
}
|
|
5130
|
+
/**
|
|
5131
|
+
* Register a persistent dialog handler for all dialogs (alert, confirm, prompt, beforeunload).
|
|
5132
|
+
*
|
|
5133
|
+
* Unlike `armDialog()` which handles a single expected dialog, `onDialog()` handles
|
|
5134
|
+
* every dialog that appears until cleared. This prevents unexpected dialogs from
|
|
5135
|
+
* blocking the page.
|
|
5136
|
+
*
|
|
5137
|
+
* The handler receives a `DialogEvent` with `accept()` and `dismiss()` methods.
|
|
5138
|
+
* If the handler throws or doesn't call either, the dialog is auto-dismissed.
|
|
5139
|
+
*
|
|
5140
|
+
* Pass `undefined` or `null` to clear the handler and restore default auto-dismiss.
|
|
5141
|
+
*
|
|
5142
|
+
* Note: `armDialog()` takes priority — if a one-shot handler is armed, it handles
|
|
5143
|
+
* the next dialog instead of the persistent handler.
|
|
5144
|
+
*
|
|
5145
|
+
* @param handler - Callback for dialog events, or `undefined`/`null` to clear
|
|
5146
|
+
*
|
|
5147
|
+
* @example
|
|
5148
|
+
* ```ts
|
|
5149
|
+
* // Accept all confirm dialogs, dismiss everything else
|
|
5150
|
+
* page.onDialog((event) => {
|
|
5151
|
+
* if (event.type === 'confirm') event.accept();
|
|
5152
|
+
* else event.dismiss();
|
|
5153
|
+
* });
|
|
5154
|
+
*
|
|
5155
|
+
* // Log and auto-accept all dialogs
|
|
5156
|
+
* page.onDialog(async (event) => {
|
|
5157
|
+
* console.log(`Dialog: ${event.type} — ${event.message}`);
|
|
5158
|
+
* await event.accept();
|
|
5159
|
+
* });
|
|
5160
|
+
*
|
|
5161
|
+
* // Clear the handler (restore default auto-dismiss)
|
|
5162
|
+
* page.onDialog(undefined);
|
|
5163
|
+
* ```
|
|
5164
|
+
*/
|
|
5165
|
+
async onDialog(handler) {
|
|
5166
|
+
return setDialogHandler({
|
|
5167
|
+
cdpUrl: this.cdpUrl,
|
|
5168
|
+
targetId: this.targetId,
|
|
5169
|
+
handler: handler ?? void 0
|
|
5170
|
+
});
|
|
5171
|
+
}
|
|
5068
5172
|
/**
|
|
5069
5173
|
* Arm a one-shot file chooser handler.
|
|
5070
5174
|
*
|
|
@@ -5732,6 +5836,58 @@ var CrawlPage = class {
|
|
|
5732
5836
|
pollMs: opts?.pollMs
|
|
5733
5837
|
});
|
|
5734
5838
|
}
|
|
5839
|
+
// ── Playwright Escape Hatches ─────────────────────────────────
|
|
5840
|
+
/**
|
|
5841
|
+
* Get the underlying Playwright `Page` object for this tab.
|
|
5842
|
+
*
|
|
5843
|
+
* Use this when browserclaw's API doesn't cover your use case and you need
|
|
5844
|
+
* direct access to Playwright's full API (custom locator strategies,
|
|
5845
|
+
* frame manipulation, request interception, etc.).
|
|
5846
|
+
*
|
|
5847
|
+
* **Warning:** Modifications made via the raw Playwright page may conflict
|
|
5848
|
+
* with browserclaw's internal state (e.g. ref tracking). Use with care.
|
|
5849
|
+
*
|
|
5850
|
+
* @returns The Playwright `Page` instance
|
|
5851
|
+
*
|
|
5852
|
+
* @example
|
|
5853
|
+
* ```ts
|
|
5854
|
+
* const pwPage = await page.playwrightPage();
|
|
5855
|
+
*
|
|
5856
|
+
* // Use Playwright's full API directly
|
|
5857
|
+
* await pwPage.locator('.my-component').waitFor({ state: 'visible' });
|
|
5858
|
+
* await pwPage.route('**\/api/**', route => route.fulfill({ body: '{}' }));
|
|
5859
|
+
*
|
|
5860
|
+
* // Access frames
|
|
5861
|
+
* const frame = pwPage.frameLocator('#my-iframe');
|
|
5862
|
+
* ```
|
|
5863
|
+
*/
|
|
5864
|
+
async playwrightPage() {
|
|
5865
|
+
return getRestoredPageForTarget({ cdpUrl: this.cdpUrl, targetId: this.targetId });
|
|
5866
|
+
}
|
|
5867
|
+
/**
|
|
5868
|
+
* Create a Playwright `Locator` for a CSS selector on this page.
|
|
5869
|
+
*
|
|
5870
|
+
* Convenience method that returns a Playwright locator without needing
|
|
5871
|
+
* to first obtain the Page object. Useful for one-off Playwright operations.
|
|
5872
|
+
*
|
|
5873
|
+
* @param selector - CSS selector or Playwright selector string
|
|
5874
|
+
* @returns A Playwright `Locator` instance
|
|
5875
|
+
*
|
|
5876
|
+
* @example
|
|
5877
|
+
* ```ts
|
|
5878
|
+
* const loc = await page.locator('.modal-dialog button.confirm');
|
|
5879
|
+
* await loc.waitFor({ state: 'visible' });
|
|
5880
|
+
* await loc.click();
|
|
5881
|
+
*
|
|
5882
|
+
* // Use Playwright selectors
|
|
5883
|
+
* const input = await page.locator('input[name="email"]');
|
|
5884
|
+
* await input.fill('test@example.com');
|
|
5885
|
+
* ```
|
|
5886
|
+
*/
|
|
5887
|
+
async locator(selector) {
|
|
5888
|
+
const pwPage = await getRestoredPageForTarget({ cdpUrl: this.cdpUrl, targetId: this.targetId });
|
|
5889
|
+
return pwPage.locator(selector);
|
|
5890
|
+
}
|
|
5735
5891
|
};
|
|
5736
5892
|
var BrowserClaw = class _BrowserClaw {
|
|
5737
5893
|
cdpUrl;
|
|
@@ -5925,6 +6081,7 @@ exports.resolvePageByTargetIdOrThrow = resolvePageByTargetIdOrThrow;
|
|
|
5925
6081
|
exports.resolvePinnedHostnameWithPolicy = resolvePinnedHostnameWithPolicy;
|
|
5926
6082
|
exports.resolveStrictExistingUploadPaths = resolveStrictExistingUploadPaths;
|
|
5927
6083
|
exports.sanitizeUntrustedFileName = sanitizeUntrustedFileName;
|
|
6084
|
+
exports.setDialogHandler = setDialogHandler;
|
|
5928
6085
|
exports.waitForChallengeViaPlaywright = waitForChallengeViaPlaywright;
|
|
5929
6086
|
exports.withBrowserNavigationPolicy = withBrowserNavigationPolicy;
|
|
5930
6087
|
exports.withPageScopedCdpClient = withPageScopedCdpClient;
|