@appsurify-testmap/rrweb-snapshot 2.1.0-alpha.7 → 2.1.1-alpha.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.
@@ -210,6 +210,42 @@ function patch(source, name, replacement) {
210
210
  };
211
211
  }
212
212
  }
213
+ function describeNode(el) {
214
+ const tag = el.tagName.toLowerCase();
215
+ const id = el.id ? `#${el.id}` : "";
216
+ const classes = el.classList.length ? "." + Array.from(el.classList).join(".") : "";
217
+ return `${tag}${id}${classes}`;
218
+ }
219
+ function getElementVisibility(el) {
220
+ var _a2;
221
+ var _a, _b;
222
+ const win = (_a2 = (_a = el.ownerDocument) == null ? void 0 : _a.defaultView) != null ? _a2 : window;
223
+ const rect = el.getBoundingClientRect();
224
+ const viewportWidth = win.innerWidth || win.document.documentElement.clientWidth || 0;
225
+ const viewportHeight = win.innerHeight || win.document.documentElement.clientHeight || 0;
226
+ const isRectVisible = rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
227
+ const style = (_b = win.getComputedStyle) == null ? void 0 : _b.call(win, el);
228
+ const isStyleVisible = !!style && style.display !== "none" && style.visibility !== "hidden" && (parseFloat(style.opacity) || 0) > 0;
229
+ const isVisible = isStyleVisible && isRectVisible;
230
+ let ratio = 0;
231
+ if (isVisible) {
232
+ const xOverlap = Math.max(
233
+ 0,
234
+ Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0)
235
+ );
236
+ const yOverlap = Math.max(
237
+ 0,
238
+ Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0)
239
+ );
240
+ const intersectionArea = xOverlap * yOverlap;
241
+ const elementArea = rect.width * rect.height;
242
+ ratio = elementArea > 0 ? intersectionArea / elementArea : 0;
243
+ }
244
+ return {
245
+ isVisible,
246
+ ratio
247
+ };
248
+ }
213
249
  const index = {
214
250
  childNodes,
215
251
  parentNode,
@@ -223,7 +259,9 @@ const index = {
223
259
  querySelector,
224
260
  querySelectorAll,
225
261
  mutationObserver: mutationObserverCtor,
226
- patch
262
+ patch,
263
+ describeNode,
264
+ getElementVisibility
227
265
  };
228
266
  function isElement(n) {
229
267
  return n.nodeType === n.ELEMENT_NODE;
@@ -610,71 +648,95 @@ function splitCssText(cssText, style, _testNoPxNorm = false) {
610
648
  function markCssSplits(cssText, style) {
611
649
  return splitCssText(cssText, style).join("/* rr_split */");
612
650
  }
613
- function getXPath(node2) {
614
- if (node2.nodeType === Node.DOCUMENT_NODE) {
615
- return "/";
651
+ function isSelectorUnique(selector, target) {
652
+ try {
653
+ const matches = document.querySelectorAll(selector);
654
+ return matches.length === 1 && matches[0] === target;
655
+ } catch (e) {
656
+ return false;
616
657
  }
617
- if (node2.nodeType === Node.DOCUMENT_TYPE_NODE) {
618
- return "/html/doctype";
658
+ }
659
+ function buildSelector(node2) {
660
+ if (!(node2 instanceof Element)) return null;
661
+ if (node2.id) {
662
+ return `#${CSS.escape(node2.id)}`;
619
663
  }
620
- if (node2.nodeType === Node.ELEMENT_NODE) {
621
- const element = node2;
622
- if (element.id) {
623
- return `//*[@id="${element.id}"]`;
624
- }
625
- if (element.tagName && element.tagName.toLowerCase() === "html") {
626
- return "/html";
627
- }
628
- if (element === document.head) {
629
- return "/html/head";
630
- }
631
- if (element === document.body) {
632
- return "/html/body";
633
- }
634
- const parentNode2 = element.parentNode;
635
- if (!parentNode2) {
636
- return "";
637
- }
638
- const siblings = Array.from(parentNode2.children).filter(
639
- (sibling) => sibling.tagName === element.tagName
640
- );
641
- const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
642
- return `${getXPath(parentNode2)}/${element.tagName.toLowerCase()}${index2}`;
664
+ const parts = [];
665
+ const tag = node2.tagName.toLowerCase();
666
+ if (node2.classList.length) {
667
+ parts.push(...Array.from(node2.classList).map((cls) => `.${CSS.escape(cls)}`));
643
668
  }
644
- if (node2.nodeType === Node.TEXT_NODE) {
645
- const parent = node2.parentNode;
646
- if (!parent) {
647
- return "";
669
+ Array.from(node2.attributes).forEach((attr) => {
670
+ if (attr.name.startsWith("data-")) {
671
+ parts.push(`[${attr.name}="${CSS.escape(attr.value)}"]`);
648
672
  }
649
- const textSiblings = Array.from(parent.childNodes).filter(
650
- (sibling) => sibling.nodeType === Node.TEXT_NODE
651
- );
652
- const index2 = textSiblings.length > 1 ? `[${textSiblings.indexOf(node2) + 1}]` : "";
653
- return `${getXPath(parent)}/text()${index2}`;
654
- }
655
- if (node2.nodeType === Node.CDATA_SECTION_NODE) {
656
- const parent = node2.parentNode;
657
- if (!parent) {
658
- return "";
673
+ });
674
+ const shortSelector = `${tag}${parts.join("")}`;
675
+ if (isSelectorUnique(shortSelector, node2)) {
676
+ return shortSelector;
677
+ }
678
+ const pathParts = [];
679
+ let current = node2;
680
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
681
+ const parent = current.parentElement;
682
+ const tagName = current.tagName.toLowerCase();
683
+ let nth = "";
684
+ if (parent) {
685
+ const siblings = Array.from(parent.children).filter(
686
+ (el) => el.tagName.toLowerCase() === tagName
687
+ );
688
+ if (siblings.length > 1) {
689
+ nth = `:nth-of-type(${siblings.indexOf(current) + 1})`;
690
+ }
659
691
  }
660
- const cdataSiblings = Array.from(parent.childNodes).filter(
661
- (sibling) => sibling.nodeType === Node.CDATA_SECTION_NODE
662
- );
663
- const index2 = cdataSiblings.length > 1 ? `[${cdataSiblings.indexOf(node2) + 1}]` : "";
664
- return `${getXPath(parent)}/text()${index2}`;
692
+ pathParts.unshift(`${tagName}${nth}`);
693
+ current = parent;
665
694
  }
666
- if (node2.nodeType === Node.COMMENT_NODE) {
667
- const parent = node2.parentNode;
668
- if (!parent) {
669
- return "";
695
+ return pathParts.join(" > ") || null;
696
+ }
697
+ function buildXPath(node2) {
698
+ switch (node2.nodeType) {
699
+ case Node.DOCUMENT_NODE:
700
+ return "/";
701
+ case Node.DOCUMENT_TYPE_NODE:
702
+ return "/html/doctype";
703
+ case Node.ELEMENT_NODE: {
704
+ const element = node2;
705
+ if (element.id) {
706
+ return `//*[@id="${CSS.escape(element.id)}"]`;
707
+ }
708
+ if (element.tagName.toLowerCase() === "html") return "/html";
709
+ if (element === document.head) return "/html/head";
710
+ if (element === document.body) return "/html/body";
711
+ const parent = element.parentNode;
712
+ if (!parent) return "";
713
+ const tag = element.tagName.toLowerCase();
714
+ const siblings = Array.from(parent.children).filter(
715
+ (el) => el.tagName.toLowerCase() === tag
716
+ );
717
+ const index2 = siblings.length > 1 ? `[${siblings.indexOf(element) + 1}]` : "";
718
+ return `${buildXPath(parent)}/${tag}${index2}`;
719
+ }
720
+ case Node.TEXT_NODE:
721
+ case Node.CDATA_SECTION_NODE:
722
+ case Node.COMMENT_NODE: {
723
+ const parent = node2.parentNode;
724
+ if (!parent) return "";
725
+ const typeMap = {
726
+ [Node.TEXT_NODE]: "text()",
727
+ [Node.CDATA_SECTION_NODE]: "text()",
728
+ // CDATA ≡ text() в XPath
729
+ [Node.COMMENT_NODE]: "comment()"
730
+ };
731
+ const sameTypeSiblings = Array.from(parent.childNodes).filter(
732
+ (sibling) => sibling.nodeType === node2.nodeType
733
+ );
734
+ const index2 = sameTypeSiblings.length > 1 ? `[${sameTypeSiblings.indexOf(node2)}]` : "";
735
+ return `${buildXPath(parent)}/${typeMap[node2.nodeType]}${index2}`;
670
736
  }
671
- const commentSiblings = Array.from(parent.childNodes).filter(
672
- (sibling) => sibling.nodeType === Node.COMMENT_NODE
673
- );
674
- const index2 = commentSiblings.length > 1 ? `[${commentSiblings.indexOf(node2) + 1}]` : "";
675
- return `${getXPath(parent)}/comment()${index2}`;
737
+ default:
738
+ return "";
676
739
  }
677
- return "";
678
740
  }
679
741
  function isTextVisible(n) {
680
742
  var _a;
@@ -690,22 +752,9 @@ function isTextVisible(n) {
690
752
  const textContent2 = (_a = n.textContent) == null ? void 0 : _a.trim();
691
753
  return textContent2 !== "";
692
754
  }
693
- function isElementVisible(n) {
694
- var _a2;
695
- var _a;
696
- const win = (_a2 = (_a = n.ownerDocument) == null ? void 0 : _a.defaultView) != null ? _a2 : null;
697
- const style = win ? win.getComputedStyle(n) : null;
698
- const isStyleVisible = style != null && style.display !== "none" && style.visibility !== "hidden" && parseFloat(style.opacity) !== 0;
699
- const rect = n.getBoundingClientRect();
700
- const result2 = isStyleVisible && isRectVisible(rect);
701
- return result2;
702
- }
703
- function isRectVisible(rect, win = window) {
704
- var _a2, _b2, _c2, _d2;
705
- var _a, _b, _c, _d;
706
- const height = (_b2 = (_a2 = win == null ? void 0 : win.innerHeight) != null ? _a2 : (_b = (_a = win == null ? void 0 : win.document) == null ? void 0 : _a.documentElement) == null ? void 0 : _b.clientHeight) != null ? _b2 : 0;
707
- const width = (_d2 = (_c2 = win == null ? void 0 : win.innerWidth) != null ? _c2 : (_d = (_c = win == null ? void 0 : win.document) == null ? void 0 : _c.documentElement) == null ? void 0 : _d.clientWidth) != null ? _d2 : 0;
708
- return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0 && rect.bottom <= height && rect.right <= width;
755
+ function isElementVisible(el) {
756
+ const visibility = index.getElementVisibility(el);
757
+ return visibility.isVisible;
709
758
  }
710
759
  const interactiveEvents = [
711
760
  "change",
@@ -938,9 +987,6 @@ function transformAttribute(doc, tagName, name, value) {
938
987
  function ignoreAttribute(tagName, name, _value) {
939
988
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
940
989
  }
941
- function isIncludeAttribute(name, include) {
942
- return typeof include === "string" ? name.includes(include) : include.test(name);
943
- }
944
990
  function isExcludeAttribute(name, exclude) {
945
991
  return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
946
992
  }
@@ -1074,7 +1120,6 @@ function serializeNode(n, options) {
1074
1120
  blockClass,
1075
1121
  blockSelector,
1076
1122
  excludeAttribute,
1077
- includeAttribute,
1078
1123
  needsMask,
1079
1124
  inlineStylesheet,
1080
1125
  maskInputOptions = {},
@@ -1088,22 +1133,19 @@ function serializeNode(n, options) {
1088
1133
  cssCaptured = false
1089
1134
  } = options;
1090
1135
  const rootId = getRootId(doc, mirror);
1091
- const xPath = getXPath(n);
1092
1136
  switch (n.nodeType) {
1093
1137
  case n.DOCUMENT_NODE:
1094
1138
  if (n.compatMode !== "CSS1Compat") {
1095
1139
  return {
1096
1140
  type: NodeType.Document,
1097
1141
  childNodes: [],
1098
- xPath,
1099
1142
  compatMode: n.compatMode
1100
1143
  // probably "BackCompat"
1101
1144
  };
1102
1145
  } else {
1103
1146
  return {
1104
1147
  type: NodeType.Document,
1105
- childNodes: [],
1106
- xPath
1148
+ childNodes: []
1107
1149
  };
1108
1150
  }
1109
1151
  case n.DOCUMENT_TYPE_NODE:
@@ -1112,8 +1154,7 @@ function serializeNode(n, options) {
1112
1154
  name: n.name,
1113
1155
  publicId: n.publicId,
1114
1156
  systemId: n.systemId,
1115
- rootId,
1116
- xPath
1157
+ rootId
1117
1158
  };
1118
1159
  case n.ELEMENT_NODE:
1119
1160
  return serializeElementNode(n, {
@@ -1121,7 +1162,6 @@ function serializeNode(n, options) {
1121
1162
  blockClass,
1122
1163
  blockSelector,
1123
1164
  excludeAttribute,
1124
- includeAttribute,
1125
1165
  inlineStylesheet,
1126
1166
  maskInputOptions,
1127
1167
  maskInputFn,
@@ -1130,8 +1170,7 @@ function serializeNode(n, options) {
1130
1170
  recordCanvas,
1131
1171
  keepIframeSrcFn,
1132
1172
  newlyAddedElement,
1133
- rootId,
1134
- xPath
1173
+ rootId
1135
1174
  });
1136
1175
  case n.TEXT_NODE:
1137
1176
  return serializeTextNode(n, {
@@ -1139,22 +1178,19 @@ function serializeNode(n, options) {
1139
1178
  needsMask,
1140
1179
  maskTextFn,
1141
1180
  rootId,
1142
- cssCaptured,
1143
- xPath
1181
+ cssCaptured
1144
1182
  });
1145
1183
  case n.CDATA_SECTION_NODE:
1146
1184
  return {
1147
1185
  type: NodeType.CDATA,
1148
1186
  textContent: "",
1149
- rootId,
1150
- xPath
1187
+ rootId
1151
1188
  };
1152
1189
  case n.COMMENT_NODE:
1153
1190
  return {
1154
1191
  type: NodeType.Comment,
1155
1192
  textContent: index.textContent(n) || "",
1156
- rootId,
1157
- xPath
1193
+ rootId
1158
1194
  };
1159
1195
  default:
1160
1196
  return false;
@@ -1166,7 +1202,7 @@ function getRootId(doc, mirror) {
1166
1202
  return docId === 1 ? void 0 : docId;
1167
1203
  }
1168
1204
  function serializeTextNode(n, options) {
1169
- const { needsMask, maskTextFn, rootId, cssCaptured, xPath } = options;
1205
+ const { needsMask, maskTextFn, rootId, cssCaptured } = options;
1170
1206
  const parent = index.parentNode(n);
1171
1207
  const parentTagName = parent && parent.tagName;
1172
1208
  let textContent2 = "";
@@ -1183,15 +1219,10 @@ function serializeTextNode(n, options) {
1183
1219
  if (!isStyle && !isScript && textContent2 && needsMask) {
1184
1220
  textContent2 = maskTextFn ? maskTextFn(textContent2, index.parentElement(n)) : textContent2.replace(/[\S]/g, "*");
1185
1221
  }
1186
- const isVisible = isTextVisible(n);
1187
- const isInteractive = isElementInteractive(n);
1188
1222
  return {
1189
1223
  type: NodeType.Text,
1190
1224
  textContent: textContent2 || "",
1191
- rootId,
1192
- isVisible,
1193
- isInteractive,
1194
- xPath
1225
+ rootId
1195
1226
  };
1196
1227
  }
1197
1228
  function serializeElementNode(n, options) {
@@ -1200,7 +1231,6 @@ function serializeElementNode(n, options) {
1200
1231
  blockClass,
1201
1232
  blockSelector,
1202
1233
  excludeAttribute,
1203
- includeAttribute,
1204
1234
  inlineStylesheet,
1205
1235
  maskInputOptions = {},
1206
1236
  maskInputFn,
@@ -1209,8 +1239,7 @@ function serializeElementNode(n, options) {
1209
1239
  recordCanvas,
1210
1240
  keepIframeSrcFn,
1211
1241
  newlyAddedElement = false,
1212
- rootId,
1213
- xPath
1242
+ rootId
1214
1243
  } = options;
1215
1244
  const needBlock = _isBlockedElement(n, blockClass, blockSelector);
1216
1245
  const tagName = getValidTagName(n);
@@ -1218,7 +1247,7 @@ function serializeElementNode(n, options) {
1218
1247
  const len = n.attributes.length;
1219
1248
  for (let i = 0; i < len; i++) {
1220
1249
  const attr = n.attributes[i];
1221
- if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1250
+ if (isExcludeAttribute(attr.name, excludeAttribute)) {
1222
1251
  continue;
1223
1252
  }
1224
1253
  if (!ignoreAttribute(tagName, attr.name, attr.value)) {
@@ -1380,8 +1409,6 @@ function serializeElementNode(n, options) {
1380
1409
  if (customElements.get(tagName)) isCustomElement = true;
1381
1410
  } catch (e) {
1382
1411
  }
1383
- const isVisible = isElementVisible(n);
1384
- const isInteractive = isElementInteractive(n);
1385
1412
  return {
1386
1413
  type: NodeType.Element,
1387
1414
  tagName,
@@ -1390,10 +1417,7 @@ function serializeElementNode(n, options) {
1390
1417
  isSVG: isSVGElement(n) || void 0,
1391
1418
  needBlock,
1392
1419
  rootId,
1393
- isCustom: isCustomElement,
1394
- isVisible,
1395
- isInteractive,
1396
- xPath
1420
+ isCustom: isCustomElement
1397
1421
  };
1398
1422
  }
1399
1423
  function lowerIfExists(maybeAttr) {
@@ -1444,7 +1468,6 @@ function serializeNodeWithId(n, options) {
1444
1468
  maskTextClass,
1445
1469
  maskTextSelector,
1446
1470
  excludeAttribute,
1447
- includeAttribute,
1448
1471
  skipChild = false,
1449
1472
  inlineStylesheet = true,
1450
1473
  maskInputOptions = {},
@@ -1480,7 +1503,6 @@ function serializeNodeWithId(n, options) {
1480
1503
  blockClass,
1481
1504
  blockSelector,
1482
1505
  excludeAttribute,
1483
- includeAttribute,
1484
1506
  needsMask,
1485
1507
  inlineStylesheet,
1486
1508
  maskInputOptions,
@@ -1506,6 +1528,22 @@ function serializeNodeWithId(n, options) {
1506
1528
  id = genId();
1507
1529
  }
1508
1530
  const serializedNode = Object.assign(_serializedNode, { id });
1531
+ if (isElement(n) || n.nodeType === Node.TEXT_NODE) {
1532
+ serializedNode.xpath = buildXPath(n);
1533
+ if (isElement(n)) {
1534
+ const selector = buildSelector(n);
1535
+ if (selector) {
1536
+ serializedNode.selector = selector;
1537
+ }
1538
+ }
1539
+ if (n.nodeType === Node.TEXT_NODE) {
1540
+ serializedNode.isVisible = isTextVisible(n);
1541
+ }
1542
+ if (n.nodeType === Node.ELEMENT_NODE) {
1543
+ serializedNode.isVisible = isElementVisible(n);
1544
+ serializedNode.isInteractive = isElementInteractive(n);
1545
+ }
1546
+ }
1509
1547
  mirror.add(n, serializedNode);
1510
1548
  if (id === IGNORED_NODE) {
1511
1549
  return null;
@@ -1534,7 +1572,6 @@ function serializeNodeWithId(n, options) {
1534
1572
  maskTextClass,
1535
1573
  maskTextSelector,
1536
1574
  excludeAttribute,
1537
- includeAttribute,
1538
1575
  skipChild,
1539
1576
  inlineStylesheet,
1540
1577
  maskInputOptions,
@@ -1595,7 +1632,6 @@ function serializeNodeWithId(n, options) {
1595
1632
  maskTextClass,
1596
1633
  maskTextSelector,
1597
1634
  excludeAttribute,
1598
- includeAttribute,
1599
1635
  skipChild: false,
1600
1636
  inlineStylesheet,
1601
1637
  maskInputOptions,
@@ -1638,7 +1674,6 @@ function serializeNodeWithId(n, options) {
1638
1674
  maskTextClass,
1639
1675
  maskTextSelector,
1640
1676
  excludeAttribute,
1641
- includeAttribute,
1642
1677
  skipChild: false,
1643
1678
  inlineStylesheet,
1644
1679
  maskInputOptions,
@@ -1676,8 +1711,7 @@ function snapshot(n, options) {
1676
1711
  blockSelector = null,
1677
1712
  maskTextClass = "rr-mask",
1678
1713
  maskTextSelector = null,
1679
- excludeAttribute = /^$a/,
1680
- includeAttribute = /.+/i,
1714
+ excludeAttribute = /.^/,
1681
1715
  inlineStylesheet = true,
1682
1716
  inlineImages = false,
1683
1717
  recordCanvas = false,
@@ -1738,7 +1772,6 @@ function snapshot(n, options) {
1738
1772
  maskTextClass,
1739
1773
  maskTextSelector,
1740
1774
  excludeAttribute,
1741
- includeAttribute,
1742
1775
  skipChild: false,
1743
1776
  inlineStylesheet,
1744
1777
  maskInputOptions,
@@ -5779,6 +5812,8 @@ exports.Mirror = Mirror;
5779
5812
  exports.absolutifyURLs = absolutifyURLs;
5780
5813
  exports.adaptCssForReplay = adaptCssForReplay;
5781
5814
  exports.buildNodeWithSN = buildNodeWithSN;
5815
+ exports.buildSelector = buildSelector;
5816
+ exports.buildXPath = buildXPath;
5782
5817
  exports.classMatchesRegex = classMatchesRegex;
5783
5818
  exports.cleanupSnapshot = cleanupSnapshot;
5784
5819
  exports.createCache = createCache;
@@ -5788,7 +5823,6 @@ exports.extractFileExtension = extractFileExtension;
5788
5823
  exports.fixSafariColons = fixSafariColons;
5789
5824
  exports.genId = genId;
5790
5825
  exports.getInputType = getInputType;
5791
- exports.getXPath = getXPath;
5792
5826
  exports.ignoreAttribute = ignoreAttribute;
5793
5827
  exports.interactiveEvents = interactiveEvents;
5794
5828
  exports.interactiveTags = interactiveTags;
@@ -5799,7 +5833,6 @@ exports.isElement = isElement;
5799
5833
  exports.isElementInteractive = isElementInteractive;
5800
5834
  exports.isElementVisible = isElementVisible;
5801
5835
  exports.isExcludeAttribute = isExcludeAttribute;
5802
- exports.isIncludeAttribute = isIncludeAttribute;
5803
5836
  exports.isNativeShadowDom = isNativeShadowDom;
5804
5837
  exports.isNodeMetaEqual = isNodeMetaEqual;
5805
5838
  exports.isShadowRoot = isShadowRoot;