@xbrowser/cli 1.0.0 → 1.0.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 +17 -26
- package/dist/{browser-GURRY444.js → browser-GITRHHFO.js} +4 -3
- package/dist/{browser-DSVV4GHS.js → browser-R56O3CW6.js} +3 -3
- package/dist/{browser-53KUFEEM.js → browser-ZJOZB5CR.js} +4 -4
- package/dist/{cdp-driver-MNPR3HZH.js → cdp-driver-BE3FOMRN.js} +324 -58
- package/dist/{cdp-driver-SSXUGXP6.js → cdp-driver-TOPYJIFL.js} +3 -3
- package/dist/chunk-2SVQTI2O.js +2794 -0
- package/dist/{chunk-2MFXKN32.js → chunk-ACFE6PKF.js} +1013 -119
- package/dist/chunk-BBMRDUYQ.js +260 -0
- package/dist/{chunk-E4O5ZU3H.js → chunk-CAFNSGYM.js} +393 -95
- package/dist/{chunk-DTJRVA76.js → chunk-ETCO4SNK.js} +2 -2
- package/dist/{chunk-YKOHDEFV.js → chunk-JPA2ZT2R.js} +69 -36
- package/dist/{chunk-T4J4C2NZ.js → chunk-JPHCY4TC.js} +12 -2
- package/dist/chunk-KFQGP6VL.js +33 -0
- package/dist/{chunk-ITKPSIP7.js → chunk-MDAPTB7C.js} +6 -25
- package/dist/chunk-OZKD3W4X.js +417 -0
- package/dist/{chunk-42RPMJ76.js → chunk-PPG4D2EW.js} +325 -59
- package/dist/{chunk-IDVD44ED.js → chunk-Q4IGYTKR.js} +19 -7
- package/dist/{chunk-2BQZIT3S.js → chunk-QIK2I3VQ.js} +86 -2501
- package/dist/chunk-WJRE55TN.js +83 -0
- package/dist/cli.js +1435 -1077
- package/dist/{convert-EGFYNICZ.js → convert-LB3GJTLR.js} +3 -3
- package/dist/{convert-EKQVHKB4.js → convert-R3XXYKC6.js} +2 -2
- package/dist/{daemon-client-3VM7VU7O.js → daemon-client-DRCUMNHK.js} +25 -74
- package/dist/{daemon-client-YAVQ343A.js → daemon-client-UZZEHHIV.js} +2 -2
- package/dist/daemon-main.js +2200 -1691
- package/dist/{extract-JUOQQX4V.js → extract-2ZFW2MX7.js} +1 -1
- package/dist/{extract-L2IW3IUB.js → extract-BSYBM4MR.js} +1 -1
- package/dist/{filter-HC4RA7JY.js → filter-KCFO4RSV.js} +1 -1
- package/dist/{filter-VID2GGZ7.js → filter-T7DSZ2X7.js} +1 -1
- package/dist/{human-interaction-W753RVJB.js → human-interaction-UKAS5ZXV.js} +2 -2
- package/dist/index.d.ts +165 -108
- package/dist/index.js +2531 -1680
- package/dist/launcher-QUJ4M2VS.js +19 -0
- package/dist/{launcher-KA7J32K5.js → launcher-YARP45UY.js} +1 -1
- package/dist/{network-store-66A2RATI.js → network-store-XGZ25FFC.js} +1 -1
- package/dist/{network-store-BN6QEZ7R.js → network-store-YVDNUREI.js} +1 -1
- package/dist/{parse-action-dsl-T3DYC33D.js → parse-action-dsl-UM333TL2.js} +1 -1
- package/dist/{proxy-WKGUCH2C.js → proxy-LV4BJ5RC.js} +1 -1
- package/dist/session-recorder-RTDGURIJ.js +8 -0
- package/dist/session-recorder-YI7YYM36.js +7 -0
- package/dist/session-replayer-GLTUICSD.js +276 -0
- package/dist/site-knowledge-SYC6VCDB.js +23 -0
- package/package.json +5 -4
- package/dist/screenshot-CWAWMXVA.js +0 -28
- package/dist/session-recorder-MA75PKTQ.js +0 -7
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getCDPTargets,
|
|
5
5
|
killChrome,
|
|
6
6
|
launchChrome
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JPHCY4TC.js";
|
|
8
8
|
import {
|
|
9
9
|
__require
|
|
10
10
|
} from "./chunk-3RG5ZIWI.js";
|
|
@@ -247,36 +247,36 @@ function resolveKeyMapping(key) {
|
|
|
247
247
|
return { key, code: key };
|
|
248
248
|
}
|
|
249
249
|
var KEY_MAP = {
|
|
250
|
-
Enter: { key: "Enter", code: "Enter", text: "\r" },
|
|
251
|
-
Tab: { key: "Tab", code: "Tab", text: " " },
|
|
252
|
-
Escape: { key: "Escape", code: "Escape" },
|
|
253
|
-
Backspace: { key: "Backspace", code: "Backspace" },
|
|
254
|
-
Delete: { key: "Delete", code: "Delete" },
|
|
255
|
-
Space: { key: " ", code: "Space", text: " " },
|
|
256
|
-
ArrowUp: { key: "ArrowUp", code: "ArrowUp" },
|
|
257
|
-
ArrowDown: { key: "ArrowDown", code: "ArrowDown" },
|
|
258
|
-
ArrowLeft: { key: "ArrowLeft", code: "ArrowLeft" },
|
|
259
|
-
ArrowRight: { key: "ArrowRight", code: "ArrowRight" },
|
|
260
|
-
Home: { key: "Home", code: "Home" },
|
|
261
|
-
End: { key: "End", code: "End" },
|
|
262
|
-
PageUp: { key: "PageUp", code: "PageUp" },
|
|
263
|
-
PageDown: { key: "PageDown", code: "PageDown" },
|
|
264
|
-
Control: { key: "Control", code: "ControlLeft" },
|
|
265
|
-
Shift: { key: "Shift", code: "ShiftLeft" },
|
|
266
|
-
Alt: { key: "Alt", code: "AltLeft" },
|
|
267
|
-
Meta: { key: "Meta", code: "MetaLeft" },
|
|
268
|
-
F1: { key: "F1", code: "F1" },
|
|
269
|
-
F2: { key: "F2", code: "F2" },
|
|
270
|
-
F3: { key: "F3", code: "F3" },
|
|
271
|
-
F4: { key: "F4", code: "F4" },
|
|
272
|
-
F5: { key: "F5", code: "F5" },
|
|
273
|
-
F6: { key: "F6", code: "F6" },
|
|
274
|
-
F7: { key: "F7", code: "F7" },
|
|
275
|
-
F8: { key: "F8", code: "F8" },
|
|
276
|
-
F9: { key: "F9", code: "F9" },
|
|
277
|
-
F10: { key: "F10", code: "F10" },
|
|
278
|
-
F11: { key: "F11", code: "F11" },
|
|
279
|
-
F12: { key: "F12", code: "F12" }
|
|
250
|
+
Enter: { key: "Enter", code: "Enter", text: "\r", keyCode: 13 },
|
|
251
|
+
Tab: { key: "Tab", code: "Tab", text: " ", keyCode: 9 },
|
|
252
|
+
Escape: { key: "Escape", code: "Escape", keyCode: 27 },
|
|
253
|
+
Backspace: { key: "Backspace", code: "Backspace", keyCode: 8 },
|
|
254
|
+
Delete: { key: "Delete", code: "Delete", keyCode: 46 },
|
|
255
|
+
Space: { key: " ", code: "Space", text: " ", keyCode: 32 },
|
|
256
|
+
ArrowUp: { key: "ArrowUp", code: "ArrowUp", keyCode: 38 },
|
|
257
|
+
ArrowDown: { key: "ArrowDown", code: "ArrowDown", keyCode: 40 },
|
|
258
|
+
ArrowLeft: { key: "ArrowLeft", code: "ArrowLeft", keyCode: 37 },
|
|
259
|
+
ArrowRight: { key: "ArrowRight", code: "ArrowRight", keyCode: 39 },
|
|
260
|
+
Home: { key: "Home", code: "Home", keyCode: 36 },
|
|
261
|
+
End: { key: "End", code: "End", keyCode: 35 },
|
|
262
|
+
PageUp: { key: "PageUp", code: "PageUp", keyCode: 33 },
|
|
263
|
+
PageDown: { key: "PageDown", code: "PageDown", keyCode: 34 },
|
|
264
|
+
Control: { key: "Control", code: "ControlLeft", keyCode: 17 },
|
|
265
|
+
Shift: { key: "Shift", code: "ShiftLeft", keyCode: 16 },
|
|
266
|
+
Alt: { key: "Alt", code: "AltLeft", keyCode: 18 },
|
|
267
|
+
Meta: { key: "Meta", code: "MetaLeft", keyCode: 91 },
|
|
268
|
+
F1: { key: "F1", code: "F1", keyCode: 112 },
|
|
269
|
+
F2: { key: "F2", code: "F2", keyCode: 113 },
|
|
270
|
+
F3: { key: "F3", code: "F3", keyCode: 114 },
|
|
271
|
+
F4: { key: "F4", code: "F4", keyCode: 115 },
|
|
272
|
+
F5: { key: "F5", code: "F5", keyCode: 116 },
|
|
273
|
+
F6: { key: "F6", code: "F6", keyCode: 117 },
|
|
274
|
+
F7: { key: "F7", code: "F7", keyCode: 118 },
|
|
275
|
+
F8: { key: "F8", code: "F8", keyCode: 119 },
|
|
276
|
+
F9: { key: "F9", code: "F9", keyCode: 120 },
|
|
277
|
+
F10: { key: "F10", code: "F10", keyCode: 121 },
|
|
278
|
+
F11: { key: "F11", code: "F11", keyCode: 122 },
|
|
279
|
+
F12: { key: "F12", code: "F12", keyCode: 123 }
|
|
280
280
|
};
|
|
281
281
|
function sleep2(ms) {
|
|
282
282
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -971,7 +971,7 @@ function createXBRequest(page, data) {
|
|
|
971
971
|
}
|
|
972
972
|
};
|
|
973
973
|
}
|
|
974
|
-
function createXBRouteFetch(conn, sessionId, params) {
|
|
974
|
+
function createXBRouteFetch(conn, sessionId, params, emitter) {
|
|
975
975
|
const request = createXBRequest(null, {
|
|
976
976
|
requestId: params.requestId,
|
|
977
977
|
url: params.request.url,
|
|
@@ -1009,6 +1009,16 @@ function createXBRouteFetch(conn, sessionId, params) {
|
|
|
1009
1009
|
responseHeaders: Object.entries(headers).map(([k, v]) => ({ name: k, value: v })),
|
|
1010
1010
|
body: bodyBytes.toString("base64")
|
|
1011
1011
|
}, sessionId);
|
|
1012
|
+
if (emitter) {
|
|
1013
|
+
const responseData = {
|
|
1014
|
+
requestId: params.requestId,
|
|
1015
|
+
status: opts.status ?? 200,
|
|
1016
|
+
url: params.request.url,
|
|
1017
|
+
headers
|
|
1018
|
+
};
|
|
1019
|
+
const response = createXBResponse(responseData, conn, sessionId);
|
|
1020
|
+
emitter.emit("response", response);
|
|
1021
|
+
}
|
|
1012
1022
|
}
|
|
1013
1023
|
};
|
|
1014
1024
|
}
|
|
@@ -1230,6 +1240,25 @@ Last error: ${lastError.message}` : "";
|
|
|
1230
1240
|
}
|
|
1231
1241
|
return result.result?.value;
|
|
1232
1242
|
}
|
|
1243
|
+
/** evaluateHandle — evaluates fn and returns a handle for element bounding box */
|
|
1244
|
+
async evaluateHandle(fn, ...args) {
|
|
1245
|
+
let expression;
|
|
1246
|
+
if (typeof fn === "string") {
|
|
1247
|
+
expression = fn;
|
|
1248
|
+
} else {
|
|
1249
|
+
const argStr = args.length > 0 ? `...${JSON.stringify(args)}` : "";
|
|
1250
|
+
expression = `(()=>{const __fn=(${fn.toString()});const __el=__fn(${argStr});if(__el&&typeof __el.getBoundingClientRect==='function'){const r=__el.getBoundingClientRect();return JSON.parse(JSON.stringify({x:r.x,y:r.y,w:r.width,h:r.height}));}return null;})()`;
|
|
1251
|
+
}
|
|
1252
|
+
const result = await this.conn.send("Runtime.evaluate", { expression, returnByValue: true }).catch(() => ({ result: { value: null } }));
|
|
1253
|
+
let box = null;
|
|
1254
|
+
try {
|
|
1255
|
+
box = JSON.parse(result.result?.value);
|
|
1256
|
+
} catch {
|
|
1257
|
+
}
|
|
1258
|
+
return {
|
|
1259
|
+
asElement: () => box ? { boundingBox: async () => box } : null
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1233
1262
|
async $eval(selector, fn, ...args) {
|
|
1234
1263
|
const fnBody = typeof fn === "function" ? fn.toString() : fn;
|
|
1235
1264
|
return this.evaluate(
|
|
@@ -1445,6 +1474,26 @@ Last error: ${lastError.message}` : "";
|
|
|
1445
1474
|
off(event, handler) {
|
|
1446
1475
|
this._emitter.off(event, handler);
|
|
1447
1476
|
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Wait for a one-shot event (Playwright-compatible subset).
|
|
1479
|
+
* Used to listen for 'filechooser', 'dialog', 'popup', 'framenavigated', etc.
|
|
1480
|
+
*/
|
|
1481
|
+
async waitForEvent(event, opts = {}) {
|
|
1482
|
+
const timeout = opts.timeout ?? 3e4;
|
|
1483
|
+
return new Promise((resolve, reject) => {
|
|
1484
|
+
const timer = setTimeout(() => {
|
|
1485
|
+
this._emitter.off(event, handler);
|
|
1486
|
+
reject(new Error(`waitForEvent('${event}') timeout after ${timeout}ms`));
|
|
1487
|
+
}, timeout);
|
|
1488
|
+
const handler = (...args) => {
|
|
1489
|
+
if (opts.predicate && !opts.predicate(...args)) return;
|
|
1490
|
+
clearTimeout(timer);
|
|
1491
|
+
this._emitter.off(event, handler);
|
|
1492
|
+
resolve(args.length === 1 ? args[0] : args);
|
|
1493
|
+
};
|
|
1494
|
+
this._emitter.on(event, handler);
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1448
1497
|
// ── Lifecycle ───────────────────────────────────────────────
|
|
1449
1498
|
async close() {
|
|
1450
1499
|
if (this._closed) return;
|
|
@@ -1608,6 +1657,40 @@ Last error: ${lastError.message}` : "";
|
|
|
1608
1657
|
this._emit("dialog", dialog);
|
|
1609
1658
|
})
|
|
1610
1659
|
);
|
|
1660
|
+
this._subscriptions.push(
|
|
1661
|
+
this.conn.subscribe("Page.fileChooserOpened", this.sessionId, async (params) => {
|
|
1662
|
+
const p = params;
|
|
1663
|
+
let selector = "";
|
|
1664
|
+
try {
|
|
1665
|
+
const result = await this.conn.send("DOM.describeNode", { backendNodeId: p.backendNodeId }, this.sessionId);
|
|
1666
|
+
const attrs = result.node?.attributes || [];
|
|
1667
|
+
const idIdx = attrs.indexOf("id");
|
|
1668
|
+
if (idIdx >= 0) selector = "#" + attrs[idIdx + 1];
|
|
1669
|
+
} catch {
|
|
1670
|
+
}
|
|
1671
|
+
if (!selector) {
|
|
1672
|
+
try {
|
|
1673
|
+
const result = await this.conn.send("DOM.resolveNode", { backendNodeId: p.backendNodeId }, this.sessionId);
|
|
1674
|
+
const evalResult = await this.conn.send("Runtime.callFunctionOn", {
|
|
1675
|
+
objectId: result.objectId,
|
|
1676
|
+
functionDeclaration: 'function() { return this.id || this.name || "" }',
|
|
1677
|
+
returnByValue: true
|
|
1678
|
+
}, this.sessionId);
|
|
1679
|
+
if (evalResult.result?.value) selector = "#" + evalResult.result.value;
|
|
1680
|
+
} catch {
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
const fileChooser = {
|
|
1684
|
+
selector,
|
|
1685
|
+
isMultiple: p.mode === "selectMultiple",
|
|
1686
|
+
setFiles: async (files) => {
|
|
1687
|
+
const fileArray = Array.isArray(files) ? files : [files];
|
|
1688
|
+
await this.setInputFiles(selector || 'input[type="file"]', fileArray);
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
this._emit("filechooser", fileChooser);
|
|
1692
|
+
})
|
|
1693
|
+
);
|
|
1611
1694
|
}
|
|
1612
1695
|
setupNetworkEvents() {
|
|
1613
1696
|
this._subscriptions.push(
|
|
@@ -1621,7 +1704,17 @@ Last error: ${lastError.message}` : "";
|
|
|
1621
1704
|
postData: p.request.postData ?? null,
|
|
1622
1705
|
resourceType: p.type
|
|
1623
1706
|
});
|
|
1624
|
-
this._emit("request",
|
|
1707
|
+
this._emit("request", createXBRequest(
|
|
1708
|
+
null,
|
|
1709
|
+
{
|
|
1710
|
+
requestId: p.requestId,
|
|
1711
|
+
url: p.request.url,
|
|
1712
|
+
method: p.request.method,
|
|
1713
|
+
headers: p.request.headers,
|
|
1714
|
+
postData: p.request.postData ?? null,
|
|
1715
|
+
resourceType: p.type
|
|
1716
|
+
}
|
|
1717
|
+
));
|
|
1625
1718
|
this.checkNetworkIdle();
|
|
1626
1719
|
})
|
|
1627
1720
|
);
|
|
@@ -1633,7 +1726,11 @@ Last error: ${lastError.message}` : "";
|
|
|
1633
1726
|
url: p.response.url,
|
|
1634
1727
|
headers: p.response.headers
|
|
1635
1728
|
});
|
|
1636
|
-
this._emit("response",
|
|
1729
|
+
this._emit("response", createXBResponse(
|
|
1730
|
+
{ requestId: p.requestId, status: p.response.status, url: p.response.url, headers: p.response.headers },
|
|
1731
|
+
this.conn,
|
|
1732
|
+
this.sessionId
|
|
1733
|
+
));
|
|
1637
1734
|
})
|
|
1638
1735
|
);
|
|
1639
1736
|
this._subscriptions.push(
|
|
@@ -1719,14 +1816,21 @@ Last error: ${lastError.message}` : "";
|
|
|
1719
1816
|
reject(new Error(`waitForResponse timed out after ${timeout}ms`));
|
|
1720
1817
|
}, timeout);
|
|
1721
1818
|
const handler = (params) => {
|
|
1722
|
-
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1819
|
+
let response;
|
|
1820
|
+
const respObj = params;
|
|
1821
|
+
if (respObj.response) {
|
|
1822
|
+
const data = {
|
|
1823
|
+
requestId: respObj.requestId || "",
|
|
1824
|
+
status: respObj.response.status || 0,
|
|
1825
|
+
url: respObj.response.url || "",
|
|
1826
|
+
headers: respObj.response.headers || {}
|
|
1827
|
+
};
|
|
1828
|
+
response = createXBResponse(data, this.conn, this.sessionId);
|
|
1829
|
+
} else if (typeof params.status === "function") {
|
|
1830
|
+
response = params;
|
|
1831
|
+
} else {
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1730
1834
|
if (predicate(response)) {
|
|
1731
1835
|
clearTimeout(timer);
|
|
1732
1836
|
this._emitter.removeListener("response", handler);
|
|
@@ -1750,16 +1854,22 @@ Last error: ${lastError.message}` : "";
|
|
|
1750
1854
|
reject(new Error(`waitForRequest timed out after ${timeout}ms`));
|
|
1751
1855
|
}, timeout);
|
|
1752
1856
|
const handler = (params) => {
|
|
1753
|
-
|
|
1754
|
-
const
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1857
|
+
let request;
|
|
1858
|
+
const reqObj = params;
|
|
1859
|
+
if (reqObj.request) {
|
|
1860
|
+
request = createXBRequest(this, {
|
|
1861
|
+
requestId: reqObj.requestId || "",
|
|
1862
|
+
url: reqObj.request.url || "",
|
|
1863
|
+
method: reqObj.request.method || "",
|
|
1864
|
+
headers: reqObj.request.headers || {},
|
|
1865
|
+
postData: reqObj.request.postData ?? null,
|
|
1866
|
+
resourceType: reqObj.type || ""
|
|
1867
|
+
});
|
|
1868
|
+
} else if (typeof params.url === "function") {
|
|
1869
|
+
request = params;
|
|
1870
|
+
} else {
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1763
1873
|
if (predicate(request)) {
|
|
1764
1874
|
clearTimeout(timer);
|
|
1765
1875
|
this._emitter.removeListener("request", handler);
|
|
@@ -1820,7 +1930,18 @@ Last error: ${lastError.message}` : "";
|
|
|
1820
1930
|
const requestUrl = params.request.url;
|
|
1821
1931
|
for (const { regex, handler } of this._routeHandlers) {
|
|
1822
1932
|
if (regex.test(requestUrl)) {
|
|
1823
|
-
|
|
1933
|
+
this._emit("request", createXBRequest(
|
|
1934
|
+
null,
|
|
1935
|
+
{
|
|
1936
|
+
requestId: params.requestId,
|
|
1937
|
+
url: params.request.url,
|
|
1938
|
+
method: params.request.method,
|
|
1939
|
+
headers: params.request.headers,
|
|
1940
|
+
postData: params.request.postData ?? null,
|
|
1941
|
+
resourceType: params.resourceType
|
|
1942
|
+
}
|
|
1943
|
+
));
|
|
1944
|
+
const route = createXBRouteFetch(this.conn, this.sessionId, params, this._emitter);
|
|
1824
1945
|
try {
|
|
1825
1946
|
await handler(route);
|
|
1826
1947
|
} catch {
|
|
@@ -1987,6 +2108,7 @@ var XBContextImpl = class {
|
|
|
1987
2108
|
const page = new XBPageImpl(this.conn, sessionId, targetId, this, this._browser);
|
|
1988
2109
|
await page._init();
|
|
1989
2110
|
this._pages.push(page);
|
|
2111
|
+
this.forwardPageEvents(page);
|
|
1990
2112
|
if (this.options.viewport) {
|
|
1991
2113
|
await page.setViewportSize(this.options.viewport).catch(() => {
|
|
1992
2114
|
});
|
|
@@ -2028,7 +2150,10 @@ var XBContextImpl = class {
|
|
|
2028
2150
|
}
|
|
2029
2151
|
this._browser._removeContext(this.contextId);
|
|
2030
2152
|
}
|
|
2031
|
-
async newCDPSession(
|
|
2153
|
+
async newCDPSession(page) {
|
|
2154
|
+
if (page instanceof XBPageImpl) {
|
|
2155
|
+
return new XBCDPSessionImpl(this.conn, page.sessionId);
|
|
2156
|
+
}
|
|
2032
2157
|
return new XBCDPSessionImpl(this.conn);
|
|
2033
2158
|
}
|
|
2034
2159
|
async addInitScript(script) {
|
|
@@ -2065,7 +2190,31 @@ var XBContextImpl = class {
|
|
|
2065
2190
|
off(event, handler) {
|
|
2066
2191
|
this._emitter.off(event, handler);
|
|
2067
2192
|
}
|
|
2193
|
+
/**
|
|
2194
|
+
* Register a page that was attached to an existing target (discovered via
|
|
2195
|
+
* Target.getTargets). Used by XBBrowserImpl.discoverContexts() to wire up
|
|
2196
|
+
* pages from the user's existing browser session into the context wrapper
|
|
2197
|
+
* so they appear in `context.pages()` and can be reused by plugins.
|
|
2198
|
+
*/
|
|
2199
|
+
_addDiscoveredPage(page) {
|
|
2200
|
+
const exists = this._pages.some((p) => p._targetId === page._targetId);
|
|
2201
|
+
if (exists) return;
|
|
2202
|
+
this._pages.push(page);
|
|
2203
|
+
this.forwardPageEvents(page);
|
|
2204
|
+
}
|
|
2068
2205
|
// ── Private ─────────────────────────────────────────────────
|
|
2206
|
+
/** Forward page-level events (request, response, etc.) to context listeners */
|
|
2207
|
+
forwardPageEvents(page) {
|
|
2208
|
+
const forward = (event) => {
|
|
2209
|
+
page.on(event, (...args) => {
|
|
2210
|
+
this._emitter.emit(event, ...args);
|
|
2211
|
+
});
|
|
2212
|
+
};
|
|
2213
|
+
forward("request");
|
|
2214
|
+
forward("response");
|
|
2215
|
+
forward("requestfailed");
|
|
2216
|
+
forward("requestfinished");
|
|
2217
|
+
}
|
|
2069
2218
|
setupAutoAttach() {
|
|
2070
2219
|
this.targetAttachedHandler = (paramsRaw) => {
|
|
2071
2220
|
const params = paramsRaw;
|
|
@@ -2090,6 +2239,7 @@ var XBContextImpl = class {
|
|
|
2090
2239
|
});
|
|
2091
2240
|
}
|
|
2092
2241
|
this._pages.push(page);
|
|
2242
|
+
this.forwardPageEvents(page);
|
|
2093
2243
|
this._emitter.emit("page", page);
|
|
2094
2244
|
});
|
|
2095
2245
|
};
|
|
@@ -2106,10 +2256,17 @@ var XBBrowserImpl = class {
|
|
|
2106
2256
|
childProcess = null;
|
|
2107
2257
|
tmpDir;
|
|
2108
2258
|
_exitHandler = null;
|
|
2109
|
-
|
|
2259
|
+
/**
|
|
2260
|
+
* Original CDP endpoint (HTTP or ws URL) used to construct this browser.
|
|
2261
|
+
* Used by discoverContexts() as a fallback to HTTP /json/list when
|
|
2262
|
+
* Target.getTargets doesn't return page-type targets (e.g. cdp-tunnel proxy).
|
|
2263
|
+
*/
|
|
2264
|
+
cdpEndpoint;
|
|
2265
|
+
constructor(conn, childProcess, tmpDir, cdpEndpoint) {
|
|
2110
2266
|
this.conn = conn;
|
|
2111
2267
|
this.childProcess = childProcess ?? null;
|
|
2112
2268
|
this.tmpDir = tmpDir;
|
|
2269
|
+
this.cdpEndpoint = cdpEndpoint;
|
|
2113
2270
|
conn.on("disconnect", () => {
|
|
2114
2271
|
this._disconnected = true;
|
|
2115
2272
|
this._emitter.emit("disconnected");
|
|
@@ -2153,7 +2310,7 @@ var XBBrowserImpl = class {
|
|
|
2153
2310
|
this._exitHandler = null;
|
|
2154
2311
|
}
|
|
2155
2312
|
if (this.childProcess) {
|
|
2156
|
-
const { killChrome: killChrome2 } = await import("./launcher-
|
|
2313
|
+
const { killChrome: killChrome2 } = await import("./launcher-YARP45UY.js");
|
|
2157
2314
|
await killChrome2(this.childProcess, this.tmpDir);
|
|
2158
2315
|
}
|
|
2159
2316
|
await this.conn.close();
|
|
@@ -2183,6 +2340,10 @@ var XBBrowserImpl = class {
|
|
|
2183
2340
|
contextId,
|
|
2184
2341
|
context
|
|
2185
2342
|
});
|
|
2343
|
+
if (this.childProcess) {
|
|
2344
|
+
this._enableAutoAttach().catch(() => {
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2186
2347
|
return context;
|
|
2187
2348
|
}
|
|
2188
2349
|
contexts() {
|
|
@@ -2211,6 +2372,23 @@ var XBBrowserImpl = class {
|
|
|
2211
2372
|
async _detachFromTarget(sessionId) {
|
|
2212
2373
|
await this.conn.send("Target.detachFromTarget", { sessionId });
|
|
2213
2374
|
}
|
|
2375
|
+
/**
|
|
2376
|
+
* Derive the HTTP /json base URL from the original cdpEndpoint for use
|
|
2377
|
+
* as a fallback when Target.getTargets doesn't return page targets.
|
|
2378
|
+
* Supports both http:// and ws:// input formats.
|
|
2379
|
+
*/
|
|
2380
|
+
_httpFallbackURL() {
|
|
2381
|
+
if (!this.cdpEndpoint) return void 0;
|
|
2382
|
+
if (this.cdpEndpoint.startsWith("http://") || this.cdpEndpoint.startsWith("https://")) {
|
|
2383
|
+
return this.cdpEndpoint;
|
|
2384
|
+
}
|
|
2385
|
+
if (this.cdpEndpoint.startsWith("ws://") || this.cdpEndpoint.startsWith("wss://")) {
|
|
2386
|
+
const url = this.cdpEndpoint.replace(/^ws/, "http");
|
|
2387
|
+
const slashIdx = url.indexOf("/", url.indexOf("//") + 2);
|
|
2388
|
+
return slashIdx >= 0 ? url.substring(0, slashIdx) : url;
|
|
2389
|
+
}
|
|
2390
|
+
return void 0;
|
|
2391
|
+
}
|
|
2214
2392
|
/** Create a new page target within a browser context */
|
|
2215
2393
|
async _createTarget(contextId, url = "about:blank") {
|
|
2216
2394
|
const params = { url };
|
|
@@ -2227,10 +2405,97 @@ var XBBrowserImpl = class {
|
|
|
2227
2405
|
async _enableAutoAttach() {
|
|
2228
2406
|
await this.conn.send("Target.setAutoAttach", {
|
|
2229
2407
|
autoAttach: true,
|
|
2230
|
-
waitForDebuggerOnStart:
|
|
2408
|
+
waitForDebuggerOnStart: false,
|
|
2231
2409
|
flatten: true
|
|
2232
2410
|
});
|
|
2233
2411
|
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Discover existing browser contexts and pages via Target.getTargets.
|
|
2414
|
+
*
|
|
2415
|
+
* For CDP tunnel connections (cdp-tunnel, attach scenarios), the
|
|
2416
|
+
* Target.attachedToTarget auto-attach flow is unreliable. Without this
|
|
2417
|
+
* call, `b.contexts()` would return [] and callers would fall back to
|
|
2418
|
+
* `b.newContext()` — which creates an isolated context with NO cookies
|
|
2419
|
+
* shared with the user's existing browser session (causing login failures).
|
|
2420
|
+
*
|
|
2421
|
+
* This method:
|
|
2422
|
+
* 1. Queries Target.getTargets to enumerate all page targets
|
|
2423
|
+
* 2. Groups them by browserContextId
|
|
2424
|
+
* 3. Attaches to each existing page via Target.attachToTarget
|
|
2425
|
+
* 4. Wraps the discovered pages in a XBContextImpl and registers it in
|
|
2426
|
+
* this._contexts so `contexts()` returns the user's actual contexts
|
|
2427
|
+
* 5. Enables Target.setAutoAttach for future pages
|
|
2428
|
+
*
|
|
2429
|
+
* No-op for self-launched browsers (they already populated contexts via
|
|
2430
|
+
* newContext() + childProcess-gated auto-attach).
|
|
2431
|
+
*/
|
|
2432
|
+
async discoverContexts() {
|
|
2433
|
+
if (this._disconnected) return;
|
|
2434
|
+
let targetInfos = [];
|
|
2435
|
+
try {
|
|
2436
|
+
const result = await this.conn.send(
|
|
2437
|
+
"Target.getTargets"
|
|
2438
|
+
);
|
|
2439
|
+
targetInfos = result.targetInfos ?? [];
|
|
2440
|
+
} catch {
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
const pageTargets = targetInfos.filter((t) => t.type === "page");
|
|
2444
|
+
const httpFallbackUrl = this._httpFallbackURL();
|
|
2445
|
+
if (pageTargets.length === 0 && httpFallbackUrl) {
|
|
2446
|
+
console.log(`[discoverContexts] Target.getTargets returned ${targetInfos.length} targets (0 page type). Falling back to HTTP /json/list at ${httpFallbackUrl}`);
|
|
2447
|
+
try {
|
|
2448
|
+
const { getCDPTargets: getCDPTargets2 } = await import("./launcher-YARP45UY.js");
|
|
2449
|
+
const httpPages = await getCDPTargets2(httpFallbackUrl);
|
|
2450
|
+
console.log(`[discoverContexts] HTTP /json/list returned ${httpPages.length} pages`);
|
|
2451
|
+
for (const p of httpPages) {
|
|
2452
|
+
if (p.type !== "page") continue;
|
|
2453
|
+
if (!p.url || p.url.startsWith("chrome://") || p.url.startsWith("devtools://")) continue;
|
|
2454
|
+
targetInfos.push({
|
|
2455
|
+
targetId: p.id,
|
|
2456
|
+
type: "page",
|
|
2457
|
+
url: p.url,
|
|
2458
|
+
title: p.title
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
console.log(`[discoverContexts] After HTTP fallback: ${targetInfos.length} total targets, ${targetInfos.filter((t) => t.type === "page").length} pages`);
|
|
2462
|
+
} catch (err) {
|
|
2463
|
+
console.log(`[discoverContexts] HTTP fallback failed: ${err.message}`);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
const pagesByContext = /* @__PURE__ */ new Map();
|
|
2467
|
+
for (const t of targetInfos) {
|
|
2468
|
+
if (t.type !== "page") continue;
|
|
2469
|
+
if (!t.url || t.url.startsWith("chrome://") || t.url.startsWith("devtools://")) {
|
|
2470
|
+
continue;
|
|
2471
|
+
}
|
|
2472
|
+
const ctxId = t.browserContextId || "default";
|
|
2473
|
+
if (!pagesByContext.has(ctxId)) pagesByContext.set(ctxId, []);
|
|
2474
|
+
pagesByContext.get(ctxId).push(t);
|
|
2475
|
+
}
|
|
2476
|
+
for (const [ctxId, pages] of pagesByContext) {
|
|
2477
|
+
if (this._contexts.has(ctxId)) continue;
|
|
2478
|
+
const context = new XBContextImpl(this.conn, ctxId, this, {});
|
|
2479
|
+
for (const p of pages) {
|
|
2480
|
+
try {
|
|
2481
|
+
const sessionId = await this._attachToTarget(p.targetId);
|
|
2482
|
+
const page = new XBPageImpl(this.conn, sessionId, p.targetId, context, this);
|
|
2483
|
+
await page._init();
|
|
2484
|
+
context._addDiscoveredPage(page);
|
|
2485
|
+
} catch {
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
this._contexts.set(ctxId, { contextId: ctxId, context });
|
|
2489
|
+
}
|
|
2490
|
+
try {
|
|
2491
|
+
await this.conn.send("Target.setAutoAttach", {
|
|
2492
|
+
autoAttach: true,
|
|
2493
|
+
waitForDebuggerOnStart: false,
|
|
2494
|
+
flatten: true
|
|
2495
|
+
});
|
|
2496
|
+
} catch {
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2234
2499
|
};
|
|
2235
2500
|
|
|
2236
2501
|
// src/cdp-driver/connection.ts
|
|
@@ -2510,7 +2775,8 @@ async function launch(options = {}) {
|
|
|
2510
2775
|
}
|
|
2511
2776
|
const conn = new CDPConnection(wsEndpoint);
|
|
2512
2777
|
await conn.ready();
|
|
2513
|
-
const
|
|
2778
|
+
const httpEndpoint = options.cdpEndpoint && !options.cdpEndpoint.startsWith("ws") ? options.cdpEndpoint : void 0;
|
|
2779
|
+
const browser = new XBBrowserImpl(conn, childProcess, tmpDir, httpEndpoint);
|
|
2514
2780
|
return { browser, wsEndpoint };
|
|
2515
2781
|
}
|
|
2516
2782
|
export {
|
|
@@ -14,15 +14,15 @@ import {
|
|
|
14
14
|
scrollIntoView,
|
|
15
15
|
waitForActionable,
|
|
16
16
|
waitForNetworkIdle
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-PPG4D2EW.js";
|
|
18
18
|
import {
|
|
19
19
|
connectToCDP,
|
|
20
20
|
findChrome,
|
|
21
21
|
getCDPTargets,
|
|
22
22
|
killChrome,
|
|
23
23
|
launchChrome
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-BBMRDUYQ.js";
|
|
25
|
+
import "./chunk-KFQGP6VL.js";
|
|
26
26
|
export {
|
|
27
27
|
CDPConnection,
|
|
28
28
|
CDPProtocolError,
|