@xbrowser/cli 1.9.9 → 1.11.0
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/{anti-bot-DR56Y63V.js → anti-bot-GTTYNEFB.js} +1 -1
- package/dist/{browser-FFYPFTBV.js → browser-Q5APBNF6.js} +1 -1
- package/dist/{browser-YCO4GJMZ.js → browser-V3JIWSTR.js} +2 -2
- package/dist/{browser-RHWUAMTB.js → browser-XNU7JQYC.js} +3 -2
- package/dist/{cdp-driver-J3YQ4LJV.js → cdp-driver-GD6YBBGE.js} +1 -1
- package/dist/cdp-driver-VEK6BNN6.js +49 -0
- package/dist/{cdp-driver-2T4P4ZE2.js → cdp-driver-XFTTZI5O.js} +436 -56
- package/dist/chunk-3FWLW7FS.js +106 -0
- package/dist/chunk-7OFM755Z.js +3509 -0
- package/dist/{chunk-5UGR6MUK.js → chunk-HBHOKZPN.js} +51 -16
- package/dist/{chunk-6OZWKXSW.js → chunk-HVPUVVSA.js} +72 -15
- package/dist/{chunk-GUTBIYFW.js → chunk-JEDP4PJW.js} +507 -70
- package/dist/{chunk-INTQPBYF.js → chunk-JKVUFP3G.js} +6 -2
- package/dist/{chunk-4452SPFI.js → chunk-NODRQGOK.js} +51 -16
- package/dist/{chunk-YMUSHPU4.js → chunk-WHPBNUKB.js} +35 -1
- package/dist/{chunk-RZM5CNIY.js → chunk-XKQVUFUS.js} +72 -15
- package/dist/{chunk-RRBXV7KE.js → chunk-XQ4HRPDJ.js} +384 -102
- package/dist/cli.js +79 -29
- package/dist/{daemon-client-GKEPT4NY.js → daemon-client-MMDRYCF5.js} +35 -1
- package/dist/{daemon-client-O6BYVRXV.js → daemon-client-Y2YSZCGK.js} +1 -1
- package/dist/daemon-main.js +103 -32
- package/dist/index.d.ts +8 -0
- package/dist/index.js +94 -31
- package/dist/{session-recorder-H3KEYU26.js → session-recorder-3BEVWHOK.js} +1 -1
- package/dist/{session-recorder-QRZMKFVL.js → session-recorder-SLDBENVF.js} +1 -1
- package/dist/{session-replayer-LJUC4TI7.js → session-replayer-WIUYVN5J.js} +90 -2
- package/package.json +1 -1
|
@@ -1490,6 +1490,33 @@ var ACTION_SIGNAL_SCRIPT = `
|
|
|
1490
1490
|
}
|
|
1491
1491
|
}, true);
|
|
1492
1492
|
|
|
1493
|
+
// \u2500\u2500 Pointer-based drag (modern sliders, sortable lists) \u2500\u2500
|
|
1494
|
+
// HTML5 dragstart/drop never fires for pointer-driven drags \u2014 capture via
|
|
1495
|
+
// pointerdown/pointerup with a movement threshold instead (rec-duel d08).
|
|
1496
|
+
var __xb_ptr_down = null;
|
|
1497
|
+
document.addEventListener('pointerdown', function(e) {
|
|
1498
|
+
var t = actualTarget(e);
|
|
1499
|
+
__xb_ptr_down = { x: e.clientX, y: e.clientY, element: describe(t), ts: Date.now() };
|
|
1500
|
+
}, true);
|
|
1501
|
+
document.addEventListener('pointerup', function(e) {
|
|
1502
|
+
if (!__xb_ptr_down) return;
|
|
1503
|
+
var start = __xb_ptr_down;
|
|
1504
|
+
__xb_ptr_down = null;
|
|
1505
|
+
var dx = e.clientX - start.x, dy = e.clientY - start.y;
|
|
1506
|
+
if (Math.abs(dx) < 15 && Math.abs(dy) < 15) return; // tap/click, not a drag
|
|
1507
|
+
pushAction('drag', {
|
|
1508
|
+
x: e.clientX,
|
|
1509
|
+
y: e.clientY,
|
|
1510
|
+
element: start.element,
|
|
1511
|
+
drag: {
|
|
1512
|
+
fromX: start.x,
|
|
1513
|
+
fromY: start.y,
|
|
1514
|
+
toX: e.clientX,
|
|
1515
|
+
toY: e.clientY,
|
|
1516
|
+
},
|
|
1517
|
+
});
|
|
1518
|
+
}, true);
|
|
1519
|
+
|
|
1493
1520
|
// \u2500\u2500 Drag & drop \u2500\u2500
|
|
1494
1521
|
var __xb_drag_source = null;
|
|
1495
1522
|
var __xb_drag_start_pos = null;
|
|
@@ -2382,8 +2409,7 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
2382
2409
|
"resize",
|
|
2383
2410
|
"clipboard",
|
|
2384
2411
|
"touch",
|
|
2385
|
-
"contextmenu"
|
|
2386
|
-
"drag"
|
|
2412
|
+
"contextmenu"
|
|
2387
2413
|
]);
|
|
2388
2414
|
constructor(context, page, sessionName) {
|
|
2389
2415
|
this.context = context;
|
|
@@ -2400,8 +2426,8 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
2400
2426
|
async recordCommandAction(action) {
|
|
2401
2427
|
const dedupFromAction = this.dedupKey(action.type, action.selector, action.element?.tag, action.value);
|
|
2402
2428
|
if (dedupFromAction) {
|
|
2403
|
-
const
|
|
2404
|
-
if (
|
|
2429
|
+
const entry = this.dedupMap.get(dedupFromAction);
|
|
2430
|
+
if (entry && entry.origin === "signal" && Date.now() < entry.expires) {
|
|
2405
2431
|
return;
|
|
2406
2432
|
}
|
|
2407
2433
|
}
|
|
@@ -2418,19 +2444,25 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
2418
2444
|
value: action.value
|
|
2419
2445
|
};
|
|
2420
2446
|
this.actions.push(actionToPush);
|
|
2421
|
-
actionToPush.elementScreenshot = await
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2447
|
+
actionToPush.elementScreenshot = await Promise.race([
|
|
2448
|
+
this.captureElementScreenshot(this.page, {
|
|
2449
|
+
type: action.type,
|
|
2450
|
+
element: actionToPush.element
|
|
2451
|
+
}).catch(() => void 0),
|
|
2452
|
+
new Promise((r) => setTimeout(r, 3e3))
|
|
2453
|
+
]);
|
|
2454
|
+
await Promise.race([
|
|
2455
|
+
this.captureAutoSnapshot(this.page, actionToPush, "after").catch(() => {
|
|
2456
|
+
}),
|
|
2457
|
+
new Promise((r) => setTimeout(r, 6e3))
|
|
2458
|
+
]);
|
|
2427
2459
|
this.lastActionTs = ts;
|
|
2428
|
-
this.dedupMap.set(dedupFromAction, Date.now() +
|
|
2460
|
+
this.dedupMap.set(dedupFromAction, { expires: Date.now() + 4e3, origin: "command" });
|
|
2429
2461
|
this.dedupActionCount++;
|
|
2430
2462
|
if (this.dedupActionCount % 200 === 0) {
|
|
2431
2463
|
const now = Date.now();
|
|
2432
2464
|
for (const [k, v] of this.dedupMap) {
|
|
2433
|
-
if (now >= v) this.dedupMap.delete(k);
|
|
2465
|
+
if (now >= v.expires) this.dedupMap.delete(k);
|
|
2434
2466
|
}
|
|
2435
2467
|
}
|
|
2436
2468
|
if (action.url && action.url !== "about:blank") {
|
|
@@ -3143,8 +3175,8 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
3143
3175
|
}
|
|
3144
3176
|
const dedupFromSignal = this.dedupKey(raw.type, raw.element?.selector, raw.element?.tag, raw.value);
|
|
3145
3177
|
if (dedupFromSignal) {
|
|
3146
|
-
const
|
|
3147
|
-
if (
|
|
3178
|
+
const entry = this.dedupMap.get(dedupFromSignal);
|
|
3179
|
+
if (entry && entry.origin === "command" && Date.now() < entry.expires) {
|
|
3148
3180
|
continue;
|
|
3149
3181
|
}
|
|
3150
3182
|
}
|
|
@@ -3187,7 +3219,10 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
3187
3219
|
// run before the last sample lands — passthrough keeps whatever has
|
|
3188
3220
|
// been collected so far.
|
|
3189
3221
|
hoverContext: raw.hoverContext,
|
|
3190
|
-
files: raw.files
|
|
3222
|
+
files: raw.files,
|
|
3223
|
+
// Pointer-drag payload (fromX/fromY/toX/toY) — replayer replays the
|
|
3224
|
+
// drag from these coordinates; dropping it made drags no-ops (d08)
|
|
3225
|
+
drag: raw.drag
|
|
3191
3226
|
});
|
|
3192
3227
|
const pushed = this.actions[this.actions.length - 1];
|
|
3193
3228
|
pushed.elementScreenshot = await this.captureElementScreenshot(page, raw);
|
|
@@ -3196,7 +3231,7 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
3196
3231
|
this.lastActionTs = raw.ts;
|
|
3197
3232
|
const dedupKey = this.dedupKey(raw.type, raw.element?.selector, raw.element?.tag, raw.value);
|
|
3198
3233
|
if (dedupKey) {
|
|
3199
|
-
this.dedupMap.set(dedupKey, Date.now() +
|
|
3234
|
+
this.dedupMap.set(dedupKey, { expires: Date.now() + 4e3, origin: "signal" });
|
|
3200
3235
|
this.dedupActionCount++;
|
|
3201
3236
|
}
|
|
3202
3237
|
const verifyTypes = /* @__PURE__ */ new Set(["click", "submit", "input", "change", "cdp-click", "cdp-fill"]);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
launch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XQ4HRPDJ.js";
|
|
4
4
|
import {
|
|
5
5
|
errMsg
|
|
6
6
|
} from "./chunk-GDKLH7ZY.js";
|
|
@@ -1637,9 +1637,32 @@ function deleteSessionDiskMeta(name) {
|
|
|
1637
1637
|
} catch {
|
|
1638
1638
|
}
|
|
1639
1639
|
}
|
|
1640
|
+
async function isSessionPageAlive(session) {
|
|
1641
|
+
const page = session.page;
|
|
1642
|
+
if (!page || typeof page.evaluate !== "function") return false;
|
|
1643
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1644
|
+
try {
|
|
1645
|
+
await Promise.race([
|
|
1646
|
+
page.evaluate("1"),
|
|
1647
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("liveness probe timeout")), 1500))
|
|
1648
|
+
]);
|
|
1649
|
+
return true;
|
|
1650
|
+
} catch {
|
|
1651
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return false;
|
|
1655
|
+
}
|
|
1640
1656
|
async function findOrRestoreSession(name, cdpEndpoint) {
|
|
1641
1657
|
const inMem = findSession(name);
|
|
1642
|
-
if (inMem)
|
|
1658
|
+
if (inMem) {
|
|
1659
|
+
if (await isSessionPageAlive(inMem)) return inMem;
|
|
1660
|
+
logSessionEvent("stale_session", `name="${name}" \u2014 page \u63A2\u9488\u5931\u8D25\uFF08\u6D4F\u89C8\u5668\u53EF\u80FD\u5DF2\u91CD\u542F\uFF09\uFF0C\u5C31\u5730\u91CD\u5EFA`);
|
|
1661
|
+
try {
|
|
1662
|
+
await closeSessionByName(name);
|
|
1663
|
+
} catch {
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1643
1666
|
const meta = readSessionDiskMeta(name);
|
|
1644
1667
|
if (!meta) return void 0;
|
|
1645
1668
|
const ep = cdpEndpoint || meta.cdpEndpoint;
|
|
@@ -1902,14 +1925,6 @@ async function createSession(name, url, options) {
|
|
|
1902
1925
|
let context;
|
|
1903
1926
|
let page;
|
|
1904
1927
|
if (isCDP) {
|
|
1905
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
1906
|
-
let contexts = b.contexts();
|
|
1907
|
-
if (contexts.length === 0) {
|
|
1908
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
1909
|
-
contexts = b.contexts();
|
|
1910
|
-
}
|
|
1911
|
-
context = contexts[0] || await b.newContext();
|
|
1912
|
-
let targetPage = null;
|
|
1913
1928
|
const targetHostname = url ? (() => {
|
|
1914
1929
|
try {
|
|
1915
1930
|
return new URL(url).hostname;
|
|
@@ -1917,12 +1932,54 @@ async function createSession(name, url, options) {
|
|
|
1917
1932
|
return "";
|
|
1918
1933
|
}
|
|
1919
1934
|
})() : "";
|
|
1935
|
+
const isRealPageUrl = (u) => !!u && u !== "about:blank" && !u.startsWith("chrome://") && !u.startsWith("chrome-untrusted://") && !u.startsWith("chrome-error://");
|
|
1936
|
+
const resolvePageUrl = async (p) => {
|
|
1937
|
+
const u = p.url();
|
|
1938
|
+
if (u && u !== "about:blank") return u;
|
|
1939
|
+
try {
|
|
1940
|
+
return await p.evaluate("location.href");
|
|
1941
|
+
} catch {
|
|
1942
|
+
return u ?? "";
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
const pollStart = Date.now();
|
|
1946
|
+
for (; ; ) {
|
|
1947
|
+
const ctxs = b.contexts();
|
|
1948
|
+
let hostHit = false;
|
|
1949
|
+
let anyHit = false;
|
|
1950
|
+
for (const ctx of ctxs) {
|
|
1951
|
+
for (const p of ctx.pages()) {
|
|
1952
|
+
const u = await resolvePageUrl(p);
|
|
1953
|
+
if (!isRealPageUrl(u)) continue;
|
|
1954
|
+
anyHit = true;
|
|
1955
|
+
if (targetHostname && u.includes(targetHostname)) {
|
|
1956
|
+
hostHit = true;
|
|
1957
|
+
break;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
if (hostHit) break;
|
|
1961
|
+
}
|
|
1962
|
+
if (process.env.XB_DEBUG_SESSION_POLL) {
|
|
1963
|
+
console.error(`[poll] t=${Date.now() - pollStart}ms ctxs=${ctxs.length} hostHit=${hostHit} anyHit=${anyHit}`);
|
|
1964
|
+
}
|
|
1965
|
+
if (hostHit) break;
|
|
1966
|
+
if (anyHit && Date.now() - pollStart >= 2e3) break;
|
|
1967
|
+
if (Date.now() - pollStart >= 5e3) break;
|
|
1968
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1969
|
+
}
|
|
1970
|
+
let contexts = b.contexts();
|
|
1971
|
+
if (contexts.length === 0) {
|
|
1972
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
1973
|
+
contexts = b.contexts();
|
|
1974
|
+
}
|
|
1975
|
+
context = contexts[0] || await b.newContext();
|
|
1976
|
+
let targetPage = null;
|
|
1920
1977
|
if (targetHostname) {
|
|
1921
1978
|
for (const ctx of contexts) {
|
|
1922
1979
|
const pages = ctx.pages();
|
|
1923
1980
|
for (const p of pages) {
|
|
1924
|
-
const pUrl = p
|
|
1925
|
-
if (
|
|
1981
|
+
const pUrl = await resolvePageUrl(p);
|
|
1982
|
+
if (isRealPageUrl(pUrl) && pUrl.includes(targetHostname)) {
|
|
1926
1983
|
targetPage = p;
|
|
1927
1984
|
break;
|
|
1928
1985
|
}
|
|
@@ -1934,8 +1991,8 @@ async function createSession(name, url, options) {
|
|
|
1934
1991
|
for (const ctx of contexts) {
|
|
1935
1992
|
const pages = ctx.pages();
|
|
1936
1993
|
for (const p of pages) {
|
|
1937
|
-
const pUrl = p
|
|
1938
|
-
if (
|
|
1994
|
+
const pUrl = await resolvePageUrl(p);
|
|
1995
|
+
if (isRealPageUrl(pUrl)) {
|
|
1939
1996
|
targetPage = p;
|
|
1940
1997
|
break;
|
|
1941
1998
|
}
|
|
@@ -2017,7 +2074,7 @@ async function closeSessionByName(name) {
|
|
|
2017
2074
|
} catch {
|
|
2018
2075
|
}
|
|
2019
2076
|
try {
|
|
2020
|
-
const { SessionRecorder } = await import("./session-recorder-
|
|
2077
|
+
const { SessionRecorder } = await import("./session-recorder-3BEVWHOK.js");
|
|
2021
2078
|
SessionRecorder.cleanup(session.name);
|
|
2022
2079
|
} catch {
|
|
2023
2080
|
}
|