@xbrowser/cli 1.2.2 → 1.3.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/{browser-CWI6BXYK.js → browser-ALV2PLW4.js} +2 -2
- package/dist/{browser-PZX7PO23.js → browser-VKZVOOVV.js} +1 -1
- package/dist/{browser-JP2LFPR2.js → browser-WMQRPYXX.js} +2 -2
- package/dist/{cdp-driver-S5STYUZZ.js → cdp-driver-72HOBP4C.js} +1 -1
- package/dist/{cdp-driver-RPUNQBGM.js → cdp-driver-JZPU5YXY.js} +49 -1
- package/dist/{chunk-PHBK3TRN.js → chunk-2QQDTXDL.js} +4 -2
- package/dist/{chunk-SLQR57XZ.js → chunk-7OGKQWXE.js} +1 -1
- package/dist/{chunk-AT4PHAJY.js → chunk-GJAV3QGG.js} +2 -2
- package/dist/{chunk-L53IDAWK.js → chunk-MJFYLKGL.js} +3 -2
- package/dist/{chunk-QFROODUU.js → chunk-N4PIGZDG.js} +49 -1
- package/dist/{chunk-MNFOCOL6.js → chunk-TLOHV3FP.js} +49 -1
- package/dist/{chunk-6V57JME6.js → chunk-UZQYMXR5.js} +1 -1
- package/dist/cli.js +178 -51
- package/dist/daemon-main.js +68 -43
- package/dist/{extract-BSYBM4MR.js → extract-O46CC533.js} +3 -2
- package/dist/{extract-2ZFW2MX7.js → extract-RM62AJXW.js} +1 -1
- package/dist/{filter-3JQWBM5F.js → filter-K6FGRJQU.js} +1 -1
- package/dist/{filter-EDTFGLS5.js → filter-TAAYMSYI.js} +2 -2
- package/dist/index.d.ts +10 -0
- package/dist/index.js +183 -56
- package/dist/{plugin-singleton-ZBVTWEYK.js → plugin-singleton-SYJF6BD6.js} +1 -1
- package/dist/{session-replayer-IXLSCF5U.js → session-replayer-JIVFXVUL.js} +1 -1
- package/package.json +1 -1
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-7OGKQWXE.js";
|
|
24
|
+
import "./chunk-N4PIGZDG.js";
|
|
25
25
|
import "./chunk-TNEN6VQ2.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-KFQGP6VL.js";
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-UZQYMXR5.js";
|
|
24
|
+
import "./chunk-N4PIGZDG.js";
|
|
25
25
|
import "./chunk-TNEN6VQ2.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-ABXMBNQ6.js";
|
|
@@ -1358,7 +1358,26 @@ Last error: ${lastError.message}` : "";
|
|
|
1358
1358
|
return this.locator(`xpath=//*[contains(text(),'${escaped}')]`);
|
|
1359
1359
|
}
|
|
1360
1360
|
getByRole(role, opts) {
|
|
1361
|
-
|
|
1361
|
+
const ROLE_TO_TAGS = {
|
|
1362
|
+
button: ["button"],
|
|
1363
|
+
link: ["a[href]"],
|
|
1364
|
+
heading: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
1365
|
+
textbox: ['input[type="text"]', "input:not([type])", "textarea"],
|
|
1366
|
+
checkbox: ['input[type="checkbox"]'],
|
|
1367
|
+
radio: ['input[type="radio"]'],
|
|
1368
|
+
searchbox: ['input[type="search"]'],
|
|
1369
|
+
combobox: ["select"],
|
|
1370
|
+
img: ["img"],
|
|
1371
|
+
navigation: ["nav"],
|
|
1372
|
+
article: ["article"],
|
|
1373
|
+
banner: ["header"],
|
|
1374
|
+
contentinfo: ["footer"],
|
|
1375
|
+
main: ["main"],
|
|
1376
|
+
complementary: ["aside"]
|
|
1377
|
+
};
|
|
1378
|
+
const tags = ROLE_TO_TAGS[role] || [];
|
|
1379
|
+
const tagSel = tags.length > 0 ? tags.join(",") + "," : "";
|
|
1380
|
+
let sel = `${tagSel}[role="${role}"]`;
|
|
1362
1381
|
if (opts?.name) {
|
|
1363
1382
|
sel += opts.exact ? `[aria-label="${opts.name}"]` : `[aria-label*="${opts.name}"]`;
|
|
1364
1383
|
}
|
|
@@ -1601,6 +1620,35 @@ Last error: ${lastError.message}` : "";
|
|
|
1601
1620
|
frames() {
|
|
1602
1621
|
return [this.mainFrame()];
|
|
1603
1622
|
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Discover all frames (main + iframes) via CDP Page.getFrameTree.
|
|
1625
|
+
* This is async because CDP doesn't maintain a frame list client-side.
|
|
1626
|
+
*/
|
|
1627
|
+
async discoverFrames() {
|
|
1628
|
+
try {
|
|
1629
|
+
const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
|
|
1630
|
+
const frames = [];
|
|
1631
|
+
const collect = (node) => {
|
|
1632
|
+
frames.push({
|
|
1633
|
+
name: () => node.frame.name || "",
|
|
1634
|
+
url: () => node.frame.url,
|
|
1635
|
+
// Helper for frame.ts to pick by index
|
|
1636
|
+
childFrames: () => (node.childFrames || []).map((c) => ({
|
|
1637
|
+
name: () => c.frame.name || "",
|
|
1638
|
+
url: () => c.frame.url,
|
|
1639
|
+
childFrames: () => []
|
|
1640
|
+
}))
|
|
1641
|
+
});
|
|
1642
|
+
for (const child of node.childFrames || []) {
|
|
1643
|
+
collect({ frame: child.frame, childFrames: [] });
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1646
|
+
collect(result.frameTree);
|
|
1647
|
+
return frames;
|
|
1648
|
+
} catch {
|
|
1649
|
+
return [this.mainFrame()];
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1604
1652
|
// ── CDP helpers exposed for locator/element ─────────────────
|
|
1605
1653
|
/** Query a single element, returns CDP nodeId or 0 if not found */
|
|
1606
1654
|
async querySelector(selector) {
|
|
@@ -399,8 +399,10 @@ var XBrowserPluginLoader = class {
|
|
|
399
399
|
const instance = await this.loadPlugin(indexPath, entry.name);
|
|
400
400
|
loaded.push(instance);
|
|
401
401
|
} catch (err) {
|
|
402
|
-
|
|
403
|
-
|
|
402
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
403
|
+
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" load failed: ${errMsg}`);
|
|
404
|
+
if (errMsg.includes("Cannot find module") && errMsg.includes("shared/")) {
|
|
405
|
+
console.warn(` \u{1F4A1} This plugin needs shared/ dependencies. Try: xbrowser plugin install shared`);
|
|
404
406
|
}
|
|
405
407
|
}
|
|
406
408
|
}
|
|
@@ -24,9 +24,9 @@ var DEFAULT_EXCLUDE_TYPES = [
|
|
|
24
24
|
function filterRecording(inputPath, outputPath, excludeTypes) {
|
|
25
25
|
const content = fs.readFileSync(inputPath, "utf-8");
|
|
26
26
|
const recording = yaml.parse(content);
|
|
27
|
-
const
|
|
28
|
-
const events = recording.events || [];
|
|
27
|
+
const events = recording.actions || recording.events || [];
|
|
29
28
|
const originalCount = events.length;
|
|
29
|
+
const exclude = excludeTypes || DEFAULT_EXCLUDE_TYPES;
|
|
30
30
|
const filteredEvents = events.filter((event) => {
|
|
31
31
|
return !exclude.includes(event.type);
|
|
32
32
|
});
|
|
@@ -4,9 +4,10 @@ import * as yaml from "yaml";
|
|
|
4
4
|
function extractRecording(filePath) {
|
|
5
5
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
6
6
|
const recording = yaml.parse(content);
|
|
7
|
+
const events = recording.actions || recording.events || [];
|
|
7
8
|
const keyEvents = [];
|
|
8
9
|
const eventTypes = {};
|
|
9
|
-
for (const event of
|
|
10
|
+
for (const event of events) {
|
|
10
11
|
const type = event.type;
|
|
11
12
|
eventTypes[type] = (eventTypes[type] || 0) + 1;
|
|
12
13
|
if (["click", "input", "type", "keydown", "keypress", "hover", "hover_enter", "hover_leave"].includes(type)) {
|
|
@@ -25,7 +26,7 @@ function extractRecording(filePath) {
|
|
|
25
26
|
}
|
|
26
27
|
return {
|
|
27
28
|
startUrl: recording.startUrl,
|
|
28
|
-
totalEvents:
|
|
29
|
+
totalEvents: events.length,
|
|
29
30
|
keyEventsCount: keyEvents.length,
|
|
30
31
|
eventTypes,
|
|
31
32
|
operations: keyEvents.map((e, i) => ({
|
|
@@ -1357,7 +1357,26 @@ Last error: ${lastError.message}` : "";
|
|
|
1357
1357
|
return this.locator(`xpath=//*[contains(text(),'${escaped}')]`);
|
|
1358
1358
|
}
|
|
1359
1359
|
getByRole(role, opts) {
|
|
1360
|
-
|
|
1360
|
+
const ROLE_TO_TAGS = {
|
|
1361
|
+
button: ["button"],
|
|
1362
|
+
link: ["a[href]"],
|
|
1363
|
+
heading: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
1364
|
+
textbox: ['input[type="text"]', "input:not([type])", "textarea"],
|
|
1365
|
+
checkbox: ['input[type="checkbox"]'],
|
|
1366
|
+
radio: ['input[type="radio"]'],
|
|
1367
|
+
searchbox: ['input[type="search"]'],
|
|
1368
|
+
combobox: ["select"],
|
|
1369
|
+
img: ["img"],
|
|
1370
|
+
navigation: ["nav"],
|
|
1371
|
+
article: ["article"],
|
|
1372
|
+
banner: ["header"],
|
|
1373
|
+
contentinfo: ["footer"],
|
|
1374
|
+
main: ["main"],
|
|
1375
|
+
complementary: ["aside"]
|
|
1376
|
+
};
|
|
1377
|
+
const tags = ROLE_TO_TAGS[role] || [];
|
|
1378
|
+
const tagSel = tags.length > 0 ? tags.join(",") + "," : "";
|
|
1379
|
+
let sel = `${tagSel}[role="${role}"]`;
|
|
1361
1380
|
if (opts?.name) {
|
|
1362
1381
|
sel += opts.exact ? `[aria-label="${opts.name}"]` : `[aria-label*="${opts.name}"]`;
|
|
1363
1382
|
}
|
|
@@ -1600,6 +1619,35 @@ Last error: ${lastError.message}` : "";
|
|
|
1600
1619
|
frames() {
|
|
1601
1620
|
return [this.mainFrame()];
|
|
1602
1621
|
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Discover all frames (main + iframes) via CDP Page.getFrameTree.
|
|
1624
|
+
* This is async because CDP doesn't maintain a frame list client-side.
|
|
1625
|
+
*/
|
|
1626
|
+
async discoverFrames() {
|
|
1627
|
+
try {
|
|
1628
|
+
const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
|
|
1629
|
+
const frames = [];
|
|
1630
|
+
const collect = (node) => {
|
|
1631
|
+
frames.push({
|
|
1632
|
+
name: () => node.frame.name || "",
|
|
1633
|
+
url: () => node.frame.url,
|
|
1634
|
+
// Helper for frame.ts to pick by index
|
|
1635
|
+
childFrames: () => (node.childFrames || []).map((c) => ({
|
|
1636
|
+
name: () => c.frame.name || "",
|
|
1637
|
+
url: () => c.frame.url,
|
|
1638
|
+
childFrames: () => []
|
|
1639
|
+
}))
|
|
1640
|
+
});
|
|
1641
|
+
for (const child of node.childFrames || []) {
|
|
1642
|
+
collect({ frame: child.frame, childFrames: [] });
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
collect(result.frameTree);
|
|
1646
|
+
return frames;
|
|
1647
|
+
} catch {
|
|
1648
|
+
return [this.mainFrame()];
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1603
1651
|
// ── CDP helpers exposed for locator/element ─────────────────
|
|
1604
1652
|
/** Query a single element, returns CDP nodeId or 0 if not found */
|
|
1605
1653
|
async querySelector(selector) {
|
|
@@ -1363,7 +1363,26 @@ Last error: ${lastError.message}` : "";
|
|
|
1363
1363
|
return this.locator(`xpath=//*[contains(text(),'${escaped}')]`);
|
|
1364
1364
|
}
|
|
1365
1365
|
getByRole(role, opts) {
|
|
1366
|
-
|
|
1366
|
+
const ROLE_TO_TAGS = {
|
|
1367
|
+
button: ["button"],
|
|
1368
|
+
link: ["a[href]"],
|
|
1369
|
+
heading: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
1370
|
+
textbox: ['input[type="text"]', "input:not([type])", "textarea"],
|
|
1371
|
+
checkbox: ['input[type="checkbox"]'],
|
|
1372
|
+
radio: ['input[type="radio"]'],
|
|
1373
|
+
searchbox: ['input[type="search"]'],
|
|
1374
|
+
combobox: ["select"],
|
|
1375
|
+
img: ["img"],
|
|
1376
|
+
navigation: ["nav"],
|
|
1377
|
+
article: ["article"],
|
|
1378
|
+
banner: ["header"],
|
|
1379
|
+
contentinfo: ["footer"],
|
|
1380
|
+
main: ["main"],
|
|
1381
|
+
complementary: ["aside"]
|
|
1382
|
+
};
|
|
1383
|
+
const tags = ROLE_TO_TAGS[role] || [];
|
|
1384
|
+
const tagSel = tags.length > 0 ? tags.join(",") + "," : "";
|
|
1385
|
+
let sel = `${tagSel}[role="${role}"]`;
|
|
1367
1386
|
if (opts?.name) {
|
|
1368
1387
|
sel += opts.exact ? `[aria-label="${opts.name}"]` : `[aria-label*="${opts.name}"]`;
|
|
1369
1388
|
}
|
|
@@ -1606,6 +1625,35 @@ Last error: ${lastError.message}` : "";
|
|
|
1606
1625
|
frames() {
|
|
1607
1626
|
return [this.mainFrame()];
|
|
1608
1627
|
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Discover all frames (main + iframes) via CDP Page.getFrameTree.
|
|
1630
|
+
* This is async because CDP doesn't maintain a frame list client-side.
|
|
1631
|
+
*/
|
|
1632
|
+
async discoverFrames() {
|
|
1633
|
+
try {
|
|
1634
|
+
const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
|
|
1635
|
+
const frames = [];
|
|
1636
|
+
const collect = (node) => {
|
|
1637
|
+
frames.push({
|
|
1638
|
+
name: () => node.frame.name || "",
|
|
1639
|
+
url: () => node.frame.url,
|
|
1640
|
+
// Helper for frame.ts to pick by index
|
|
1641
|
+
childFrames: () => (node.childFrames || []).map((c) => ({
|
|
1642
|
+
name: () => c.frame.name || "",
|
|
1643
|
+
url: () => c.frame.url,
|
|
1644
|
+
childFrames: () => []
|
|
1645
|
+
}))
|
|
1646
|
+
});
|
|
1647
|
+
for (const child of node.childFrames || []) {
|
|
1648
|
+
collect({ frame: child.frame, childFrames: [] });
|
|
1649
|
+
}
|
|
1650
|
+
};
|
|
1651
|
+
collect(result.frameTree);
|
|
1652
|
+
return frames;
|
|
1653
|
+
} catch {
|
|
1654
|
+
return [this.mainFrame()];
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1609
1657
|
// ── CDP helpers exposed for locator/element ─────────────────
|
|
1610
1658
|
/** Query a single element, returns CDP nodeId or 0 if not found */
|
|
1611
1659
|
async querySelector(selector) {
|