@xbrowser/cli 1.10.0 → 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-T3V3JWVH.js → browser-Q5APBNF6.js} +1 -1
- package/dist/{browser-HBL72GPZ.js → browser-V3JIWSTR.js} +2 -2
- package/dist/{browser-WQZ3D6AE.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-BAHSRZIX.js → chunk-HVPUVVSA.js} +13 -10
- package/dist/{chunk-NITFVWWS.js → chunk-JEDP4PJW.js} +448 -65
- 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-WSCP7QCJ.js → chunk-XKQVUFUS.js} +13 -10
- package/dist/{chunk-RRBXV7KE.js → chunk-XQ4HRPDJ.js} +384 -102
- package/dist/cli.js +72 -26
- 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 +89 -31
- package/dist/index.d.ts +8 -0
- package/dist/index.js +73 -27
- 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
|
@@ -1093,6 +1093,33 @@ var ACTION_SIGNAL_SCRIPT = `
|
|
|
1093
1093
|
}
|
|
1094
1094
|
}, true);
|
|
1095
1095
|
|
|
1096
|
+
// \u2500\u2500 Pointer-based drag (modern sliders, sortable lists) \u2500\u2500
|
|
1097
|
+
// HTML5 dragstart/drop never fires for pointer-driven drags \u2014 capture via
|
|
1098
|
+
// pointerdown/pointerup with a movement threshold instead (rec-duel d08).
|
|
1099
|
+
var __xb_ptr_down = null;
|
|
1100
|
+
document.addEventListener('pointerdown', function(e) {
|
|
1101
|
+
var t = actualTarget(e);
|
|
1102
|
+
__xb_ptr_down = { x: e.clientX, y: e.clientY, element: describe(t), ts: Date.now() };
|
|
1103
|
+
}, true);
|
|
1104
|
+
document.addEventListener('pointerup', function(e) {
|
|
1105
|
+
if (!__xb_ptr_down) return;
|
|
1106
|
+
var start = __xb_ptr_down;
|
|
1107
|
+
__xb_ptr_down = null;
|
|
1108
|
+
var dx = e.clientX - start.x, dy = e.clientY - start.y;
|
|
1109
|
+
if (Math.abs(dx) < 15 && Math.abs(dy) < 15) return; // tap/click, not a drag
|
|
1110
|
+
pushAction('drag', {
|
|
1111
|
+
x: e.clientX,
|
|
1112
|
+
y: e.clientY,
|
|
1113
|
+
element: start.element,
|
|
1114
|
+
drag: {
|
|
1115
|
+
fromX: start.x,
|
|
1116
|
+
fromY: start.y,
|
|
1117
|
+
toX: e.clientX,
|
|
1118
|
+
toY: e.clientY,
|
|
1119
|
+
},
|
|
1120
|
+
});
|
|
1121
|
+
}, true);
|
|
1122
|
+
|
|
1096
1123
|
// \u2500\u2500 Drag & drop \u2500\u2500
|
|
1097
1124
|
var __xb_drag_source = null;
|
|
1098
1125
|
var __xb_drag_start_pos = null;
|
|
@@ -1985,8 +2012,7 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
1985
2012
|
"resize",
|
|
1986
2013
|
"clipboard",
|
|
1987
2014
|
"touch",
|
|
1988
|
-
"contextmenu"
|
|
1989
|
-
"drag"
|
|
2015
|
+
"contextmenu"
|
|
1990
2016
|
]);
|
|
1991
2017
|
constructor(context, page, sessionName) {
|
|
1992
2018
|
this.context = context;
|
|
@@ -2003,8 +2029,8 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
2003
2029
|
async recordCommandAction(action) {
|
|
2004
2030
|
const dedupFromAction = this.dedupKey(action.type, action.selector, action.element?.tag, action.value);
|
|
2005
2031
|
if (dedupFromAction) {
|
|
2006
|
-
const
|
|
2007
|
-
if (
|
|
2032
|
+
const entry = this.dedupMap.get(dedupFromAction);
|
|
2033
|
+
if (entry && entry.origin === "signal" && Date.now() < entry.expires) {
|
|
2008
2034
|
return;
|
|
2009
2035
|
}
|
|
2010
2036
|
}
|
|
@@ -2021,19 +2047,25 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
2021
2047
|
value: action.value
|
|
2022
2048
|
};
|
|
2023
2049
|
this.actions.push(actionToPush);
|
|
2024
|
-
actionToPush.elementScreenshot = await
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2050
|
+
actionToPush.elementScreenshot = await Promise.race([
|
|
2051
|
+
this.captureElementScreenshot(this.page, {
|
|
2052
|
+
type: action.type,
|
|
2053
|
+
element: actionToPush.element
|
|
2054
|
+
}).catch(() => void 0),
|
|
2055
|
+
new Promise((r) => setTimeout(r, 3e3))
|
|
2056
|
+
]);
|
|
2057
|
+
await Promise.race([
|
|
2058
|
+
this.captureAutoSnapshot(this.page, actionToPush, "after").catch(() => {
|
|
2059
|
+
}),
|
|
2060
|
+
new Promise((r) => setTimeout(r, 6e3))
|
|
2061
|
+
]);
|
|
2030
2062
|
this.lastActionTs = ts;
|
|
2031
|
-
this.dedupMap.set(dedupFromAction, Date.now() +
|
|
2063
|
+
this.dedupMap.set(dedupFromAction, { expires: Date.now() + 4e3, origin: "command" });
|
|
2032
2064
|
this.dedupActionCount++;
|
|
2033
2065
|
if (this.dedupActionCount % 200 === 0) {
|
|
2034
2066
|
const now = Date.now();
|
|
2035
2067
|
for (const [k, v] of this.dedupMap) {
|
|
2036
|
-
if (now >= v) this.dedupMap.delete(k);
|
|
2068
|
+
if (now >= v.expires) this.dedupMap.delete(k);
|
|
2037
2069
|
}
|
|
2038
2070
|
}
|
|
2039
2071
|
if (action.url && action.url !== "about:blank") {
|
|
@@ -2746,8 +2778,8 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
2746
2778
|
}
|
|
2747
2779
|
const dedupFromSignal = this.dedupKey(raw.type, raw.element?.selector, raw.element?.tag, raw.value);
|
|
2748
2780
|
if (dedupFromSignal) {
|
|
2749
|
-
const
|
|
2750
|
-
if (
|
|
2781
|
+
const entry = this.dedupMap.get(dedupFromSignal);
|
|
2782
|
+
if (entry && entry.origin === "command" && Date.now() < entry.expires) {
|
|
2751
2783
|
continue;
|
|
2752
2784
|
}
|
|
2753
2785
|
}
|
|
@@ -2790,7 +2822,10 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
2790
2822
|
// run before the last sample lands — passthrough keeps whatever has
|
|
2791
2823
|
// been collected so far.
|
|
2792
2824
|
hoverContext: raw.hoverContext,
|
|
2793
|
-
files: raw.files
|
|
2825
|
+
files: raw.files,
|
|
2826
|
+
// Pointer-drag payload (fromX/fromY/toX/toY) — replayer replays the
|
|
2827
|
+
// drag from these coordinates; dropping it made drags no-ops (d08)
|
|
2828
|
+
drag: raw.drag
|
|
2794
2829
|
});
|
|
2795
2830
|
const pushed = this.actions[this.actions.length - 1];
|
|
2796
2831
|
pushed.elementScreenshot = await this.captureElementScreenshot(page, raw);
|
|
@@ -2799,7 +2834,7 @@ var SessionRecorder = class _SessionRecorder {
|
|
|
2799
2834
|
this.lastActionTs = raw.ts;
|
|
2800
2835
|
const dedupKey = this.dedupKey(raw.type, raw.element?.selector, raw.element?.tag, raw.value);
|
|
2801
2836
|
if (dedupKey) {
|
|
2802
|
-
this.dedupMap.set(dedupKey, Date.now() +
|
|
2837
|
+
this.dedupMap.set(dedupKey, { expires: Date.now() + 4e3, origin: "signal" });
|
|
2803
2838
|
this.dedupActionCount++;
|
|
2804
2839
|
}
|
|
2805
2840
|
const verifyTypes = /* @__PURE__ */ new Set(["click", "submit", "input", "change", "cdp-click", "cdp-fill"]);
|
|
@@ -13,6 +13,7 @@ var pkg = require2("../package.json");
|
|
|
13
13
|
var version = pkg.version;
|
|
14
14
|
|
|
15
15
|
// src/client/daemon-client.ts
|
|
16
|
+
import { request as httpRequest } from "http";
|
|
16
17
|
var DAEMON_PORT = 9224;
|
|
17
18
|
var DAEMON_BASE = `http://localhost:${DAEMON_PORT}`;
|
|
18
19
|
var _ensurePromise = null;
|
|
@@ -207,7 +208,40 @@ async function forwardRecordSummary(session) {
|
|
|
207
208
|
return rpcCall("record:summary", { session }, 5e3);
|
|
208
209
|
}
|
|
209
210
|
async function forwardReplay(file, session, slowMo) {
|
|
210
|
-
return
|
|
211
|
+
return longRpcCall("replay", { file, session, slowMo }, 20 * 6e4);
|
|
212
|
+
}
|
|
213
|
+
function longRpcCall(method, params, timeoutMs) {
|
|
214
|
+
return new Promise((resolve, reject) => {
|
|
215
|
+
const body = JSON.stringify({ method, params });
|
|
216
|
+
const req = httpRequest(
|
|
217
|
+
{
|
|
218
|
+
hostname: "127.0.0.1",
|
|
219
|
+
port: DAEMON_PORT,
|
|
220
|
+
path: "/rpc",
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) }
|
|
223
|
+
},
|
|
224
|
+
(res) => {
|
|
225
|
+
const chunks = [];
|
|
226
|
+
res.on("data", (c) => chunks.push(c));
|
|
227
|
+
res.on("end", () => {
|
|
228
|
+
try {
|
|
229
|
+
const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
230
|
+
if (parsed && typeof parsed.error === "string" && Object.keys(parsed).length === 1) {
|
|
231
|
+
reject(new Error(`Daemon error: ${parsed.error}`));
|
|
232
|
+
} else {
|
|
233
|
+
resolve(parsed);
|
|
234
|
+
}
|
|
235
|
+
} catch (e) {
|
|
236
|
+
reject(e instanceof Error ? e : new Error(String(e)));
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
req.setTimeout(timeoutMs, () => req.destroy(new Error(`replay RPC timeout after ${timeoutMs}ms`)));
|
|
242
|
+
req.on("error", reject);
|
|
243
|
+
req.end(body);
|
|
244
|
+
});
|
|
211
245
|
}
|
|
212
246
|
async function forwardRecordCheckpoint(session, type, hint, selector) {
|
|
213
247
|
return rpcCall("record:checkpoint", { session, type, hint, selector }, 1e4);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
launch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7OFM755Z.js";
|
|
4
4
|
import {
|
|
5
5
|
errMsg
|
|
6
6
|
} from "./chunk-GDKLH7ZY.js";
|
|
@@ -275,15 +275,18 @@ function deleteSessionDiskMeta(name) {
|
|
|
275
275
|
async function isSessionPageAlive(session) {
|
|
276
276
|
const page = session.page;
|
|
277
277
|
if (!page || typeof page.evaluate !== "function") return false;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
278
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
279
|
+
try {
|
|
280
|
+
await Promise.race([
|
|
281
|
+
page.evaluate("1"),
|
|
282
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("liveness probe timeout")), 1500))
|
|
283
|
+
]);
|
|
284
|
+
return true;
|
|
285
|
+
} catch {
|
|
286
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
287
|
+
}
|
|
286
288
|
}
|
|
289
|
+
return false;
|
|
287
290
|
}
|
|
288
291
|
async function findOrRestoreSession(name, cdpEndpoint) {
|
|
289
292
|
const inMem = findSession(name);
|
|
@@ -706,7 +709,7 @@ async function closeSessionByName(name) {
|
|
|
706
709
|
} catch {
|
|
707
710
|
}
|
|
708
711
|
try {
|
|
709
|
-
const { SessionRecorder } = await import("./session-recorder-
|
|
712
|
+
const { SessionRecorder } = await import("./session-recorder-SLDBENVF.js");
|
|
710
713
|
SessionRecorder.cleanup(session.name);
|
|
711
714
|
} catch {
|
|
712
715
|
}
|