@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.
Files changed (27) hide show
  1. package/dist/{anti-bot-DR56Y63V.js → anti-bot-GTTYNEFB.js} +1 -1
  2. package/dist/{browser-T3V3JWVH.js → browser-Q5APBNF6.js} +1 -1
  3. package/dist/{browser-HBL72GPZ.js → browser-V3JIWSTR.js} +2 -2
  4. package/dist/{browser-WQZ3D6AE.js → browser-XNU7JQYC.js} +3 -2
  5. package/dist/{cdp-driver-J3YQ4LJV.js → cdp-driver-GD6YBBGE.js} +1 -1
  6. package/dist/cdp-driver-VEK6BNN6.js +49 -0
  7. package/dist/{cdp-driver-2T4P4ZE2.js → cdp-driver-XFTTZI5O.js} +436 -56
  8. package/dist/chunk-3FWLW7FS.js +106 -0
  9. package/dist/chunk-7OFM755Z.js +3509 -0
  10. package/dist/{chunk-5UGR6MUK.js → chunk-HBHOKZPN.js} +51 -16
  11. package/dist/{chunk-BAHSRZIX.js → chunk-HVPUVVSA.js} +13 -10
  12. package/dist/{chunk-NITFVWWS.js → chunk-JEDP4PJW.js} +448 -65
  13. package/dist/{chunk-INTQPBYF.js → chunk-JKVUFP3G.js} +6 -2
  14. package/dist/{chunk-4452SPFI.js → chunk-NODRQGOK.js} +51 -16
  15. package/dist/{chunk-YMUSHPU4.js → chunk-WHPBNUKB.js} +35 -1
  16. package/dist/{chunk-WSCP7QCJ.js → chunk-XKQVUFUS.js} +13 -10
  17. package/dist/{chunk-RRBXV7KE.js → chunk-XQ4HRPDJ.js} +384 -102
  18. package/dist/cli.js +72 -26
  19. package/dist/{daemon-client-GKEPT4NY.js → daemon-client-MMDRYCF5.js} +35 -1
  20. package/dist/{daemon-client-O6BYVRXV.js → daemon-client-Y2YSZCGK.js} +1 -1
  21. package/dist/daemon-main.js +89 -31
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.js +73 -27
  24. package/dist/{session-recorder-H3KEYU26.js → session-recorder-3BEVWHOK.js} +1 -1
  25. package/dist/{session-recorder-QRZMKFVL.js → session-recorder-SLDBENVF.js} +1 -1
  26. package/dist/{session-replayer-LJUC4TI7.js → session-replayer-WIUYVN5J.js} +90 -2
  27. 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 expires = this.dedupMap.get(dedupFromAction);
2007
- if (expires && Date.now() < expires) {
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 this.captureElementScreenshot(this.page, {
2025
- type: action.type,
2026
- element: actionToPush.element
2027
- });
2028
- await this.captureAutoSnapshot(this.page, actionToPush, "after").catch(() => {
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() + 2e3);
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 expires = this.dedupMap.get(dedupFromSignal);
2750
- if (expires && Date.now() < expires) {
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() + 2e3);
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 rpcCall("replay", { file, session, slowMo }, 12e4);
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-RRBXV7KE.js";
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
- try {
279
- await Promise.race([
280
- page.evaluate("1"),
281
- new Promise((_, reject) => setTimeout(() => reject(new Error("liveness probe timeout")), 1500))
282
- ]);
283
- return true;
284
- } catch {
285
- return false;
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-QRZMKFVL.js");
712
+ const { SessionRecorder } = await import("./session-recorder-SLDBENVF.js");
710
713
  SessionRecorder.cleanup(session.name);
711
714
  } catch {
712
715
  }