@appsurify-testmap/rrweb-record 2.1.0-alpha.3 → 2.1.0-alpha.6

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.
@@ -927,32 +927,14 @@ function transformAttribute(doc, tagName, name, value) {
927
927
  }
928
928
  return value;
929
929
  }
930
- function isIgnoreAttribute(tagName, name, _value) {
930
+ function ignoreAttribute(tagName, name, _value) {
931
931
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
932
932
  }
933
- function cleanAttributes(doc, element, ignoreAttribute) {
934
- const tagName = getValidTagName$1(element);
935
- const attributes = {};
936
- const len = element.attributes.length;
937
- for (let i2 = 0; i2 < len; i2++) {
938
- const attr = element.attributes[i2];
939
- const name = attr.name;
940
- const value = attr.value;
941
- const shouldIgnoreByName = typeof ignoreAttribute === "string" ? name === ignoreAttribute : ignoreAttribute.test(name);
942
- if (!shouldIgnoreByName && !isIgnoreAttribute(tagName, name)) {
943
- attributes[name] = transformAttribute(
944
- doc,
945
- tagName,
946
- toLowerCase(name),
947
- value
948
- );
949
- }
950
- }
951
- return attributes;
933
+ function isIncludeAttribute(name, include) {
934
+ return typeof include === "string" ? name.includes(include) : include.test(name);
952
935
  }
953
- function shouldIgnoreAttribute(ignore, name) {
954
- if (!ignore) return false;
955
- return typeof ignore === "string" ? name === ignore : ignore.test(name);
936
+ function isExcludeAttribute(name, exclude) {
937
+ return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
956
938
  }
957
939
  function _isBlockedElement(element, blockClass, blockSelector) {
958
940
  try {
@@ -1083,7 +1065,8 @@ function serializeNode(n2, options) {
1083
1065
  mirror: mirror2,
1084
1066
  blockClass,
1085
1067
  blockSelector,
1086
- ignoreAttribute,
1068
+ excludeAttribute,
1069
+ includeAttribute,
1087
1070
  needsMask,
1088
1071
  inlineStylesheet,
1089
1072
  maskInputOptions = {},
@@ -1129,7 +1112,8 @@ function serializeNode(n2, options) {
1129
1112
  doc,
1130
1113
  blockClass,
1131
1114
  blockSelector,
1132
- ignoreAttribute,
1115
+ excludeAttribute,
1116
+ includeAttribute,
1133
1117
  inlineStylesheet,
1134
1118
  maskInputOptions,
1135
1119
  maskInputFn,
@@ -1207,7 +1191,8 @@ function serializeElementNode(n2, options) {
1207
1191
  doc,
1208
1192
  blockClass,
1209
1193
  blockSelector,
1210
- ignoreAttribute,
1194
+ excludeAttribute,
1195
+ includeAttribute,
1211
1196
  inlineStylesheet,
1212
1197
  maskInputOptions = {},
1213
1198
  maskInputFn,
@@ -1221,7 +1206,22 @@ function serializeElementNode(n2, options) {
1221
1206
  } = options;
1222
1207
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1223
1208
  const tagName = getValidTagName$1(n2);
1224
- let attributes = cleanAttributes(doc, n2, ignoreAttribute);
1209
+ let attributes = {};
1210
+ const len = n2.attributes.length;
1211
+ for (let i2 = 0; i2 < len; i2++) {
1212
+ const attr = n2.attributes[i2];
1213
+ if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1214
+ continue;
1215
+ }
1216
+ if (!ignoreAttribute(tagName, attr.name, attr.value)) {
1217
+ attributes[attr.name] = transformAttribute(
1218
+ doc,
1219
+ tagName,
1220
+ toLowerCase(attr.name),
1221
+ attr.value
1222
+ );
1223
+ }
1224
+ }
1225
1225
  if (tagName === "link" && inlineStylesheet) {
1226
1226
  const stylesheet = Array.from(doc.styleSheets).find((s2) => {
1227
1227
  return s2.href === n2.href;
@@ -1435,7 +1435,8 @@ function serializeNodeWithId(n2, options) {
1435
1435
  blockSelector,
1436
1436
  maskTextClass,
1437
1437
  maskTextSelector,
1438
- ignoreAttribute,
1438
+ excludeAttribute,
1439
+ includeAttribute,
1439
1440
  skipChild = false,
1440
1441
  inlineStylesheet = true,
1441
1442
  maskInputOptions = {},
@@ -1470,7 +1471,8 @@ function serializeNodeWithId(n2, options) {
1470
1471
  mirror: mirror2,
1471
1472
  blockClass,
1472
1473
  blockSelector,
1473
- ignoreAttribute,
1474
+ excludeAttribute,
1475
+ includeAttribute,
1474
1476
  needsMask,
1475
1477
  inlineStylesheet,
1476
1478
  maskInputOptions,
@@ -1523,7 +1525,8 @@ function serializeNodeWithId(n2, options) {
1523
1525
  needsMask,
1524
1526
  maskTextClass,
1525
1527
  maskTextSelector,
1526
- ignoreAttribute,
1528
+ excludeAttribute,
1529
+ includeAttribute,
1527
1530
  skipChild,
1528
1531
  inlineStylesheet,
1529
1532
  maskInputOptions,
@@ -1583,7 +1586,8 @@ function serializeNodeWithId(n2, options) {
1583
1586
  needsMask,
1584
1587
  maskTextClass,
1585
1588
  maskTextSelector,
1586
- ignoreAttribute,
1589
+ excludeAttribute,
1590
+ includeAttribute,
1587
1591
  skipChild: false,
1588
1592
  inlineStylesheet,
1589
1593
  maskInputOptions,
@@ -1625,7 +1629,8 @@ function serializeNodeWithId(n2, options) {
1625
1629
  needsMask,
1626
1630
  maskTextClass,
1627
1631
  maskTextSelector,
1628
- ignoreAttribute,
1632
+ excludeAttribute,
1633
+ includeAttribute,
1629
1634
  skipChild: false,
1630
1635
  inlineStylesheet,
1631
1636
  maskInputOptions,
@@ -1663,7 +1668,8 @@ function snapshot(n2, options) {
1663
1668
  blockSelector = null,
1664
1669
  maskTextClass = "rr-mask",
1665
1670
  maskTextSelector = null,
1666
- ignoreAttribute = "rr-ignore",
1671
+ excludeAttribute = /^$a/,
1672
+ includeAttribute = /.+/i,
1667
1673
  inlineStylesheet = true,
1668
1674
  inlineImages = false,
1669
1675
  recordCanvas = false,
@@ -1723,7 +1729,8 @@ function snapshot(n2, options) {
1723
1729
  blockSelector,
1724
1730
  maskTextClass,
1725
1731
  maskTextSelector,
1726
- ignoreAttribute,
1732
+ excludeAttribute,
1733
+ includeAttribute,
1727
1734
  skipChild: false,
1728
1735
  inlineStylesheet,
1729
1736
  maskInputOptions,
@@ -9239,7 +9246,7 @@ function hookSetter(target, key, d, isRevoked, win = window) {
9239
9246
  return () => hookSetter(target, key, original || {}, true);
9240
9247
  }
9241
9248
  function nowTimestamp() {
9242
- return performance.timeOrigin + performance.now();
9249
+ return Math.round(performance.timeOrigin + performance.now());
9243
9250
  }
9244
9251
  function getWindowScroll(win = window) {
9245
9252
  var _a2, _b, _c, _d;
@@ -9430,7 +9437,6 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
9430
9437
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
9431
9438
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
9432
9439
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
9433
- IncrementalSource2[IncrementalSource2["VisibilityChange"] = 17] = "VisibilityChange";
9434
9440
  return IncrementalSource2;
9435
9441
  })(IncrementalSource || {});
9436
9442
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -9576,7 +9582,8 @@ class MutationBuffer {
9576
9582
  __publicField(this, "blockSelector");
9577
9583
  __publicField(this, "maskTextClass");
9578
9584
  __publicField(this, "maskTextSelector");
9579
- __publicField(this, "ignoreAttribute");
9585
+ __publicField(this, "excludeAttribute");
9586
+ __publicField(this, "includeAttribute");
9580
9587
  __publicField(this, "inlineStylesheet");
9581
9588
  __publicField(this, "maskInputOptions");
9582
9589
  __publicField(this, "maskTextFn");
@@ -9640,7 +9647,8 @@ class MutationBuffer {
9640
9647
  blockSelector: this.blockSelector,
9641
9648
  maskTextClass: this.maskTextClass,
9642
9649
  maskTextSelector: this.maskTextSelector,
9643
- ignoreAttribute: this.ignoreAttribute || "",
9650
+ excludeAttribute: this.excludeAttribute,
9651
+ includeAttribute: this.includeAttribute,
9644
9652
  skipChild: true,
9645
9653
  newlyAddedElement: true,
9646
9654
  inlineStylesheet: this.inlineStylesheet,
@@ -9760,20 +9768,6 @@ class MutationBuffer {
9760
9768
  };
9761
9769
  }).filter((text) => !addedIds.has(text.id)).filter((text) => this.mirror.has(text.id)),
9762
9770
  attributes: this.attributes.map((attribute) => {
9763
- const element = attribute.node;
9764
- const filtered = {};
9765
- for (const [name, value] of Object.entries(attribute.attributes)) {
9766
- const isIgnored2 = shouldIgnoreAttribute(this.ignoreAttribute, name);
9767
- const existedBefore = element.hasAttribute(name);
9768
- const keep = value !== null && !isIgnored2 || value === null && (!isIgnored2 || attribute.attributes[name] !== null || existedBefore);
9769
- if (keep) {
9770
- filtered[name] = value;
9771
- }
9772
- }
9773
- return __spreadProps(__spreadValues({}, attribute), {
9774
- attributes: filtered
9775
- });
9776
- }).filter((attribute) => Object.keys(attribute.attributes).length > 0).map((attribute) => {
9777
9771
  const { attributes } = attribute;
9778
9772
  if (typeof attributes.style === "string") {
9779
9773
  const diffAsStr = JSON.stringify(attribute.styleDiff);
@@ -9855,8 +9849,13 @@ class MutationBuffer {
9855
9849
  case "attributes": {
9856
9850
  const target = m.target;
9857
9851
  let attributeName = m.attributeName;
9858
- let value = target.getAttribute(attributeName);
9859
- if (value === null && m.oldValue === null) {
9852
+ let value = m.target.getAttribute(attributeName);
9853
+ const propValue = target[attributeName];
9854
+ const isPhantomAttributeMutation = value === null && !target.hasAttribute(attributeName) && m.oldValue !== null && (propValue === "" || propValue === null || typeof propValue === "undefined");
9855
+ if (isPhantomAttributeMutation && !isIncludeAttribute(attributeName, this.includeAttribute)) {
9856
+ return;
9857
+ }
9858
+ if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
9860
9859
  return;
9861
9860
  }
9862
9861
  if (attributeName === "value") {
@@ -9894,14 +9893,21 @@ class MutationBuffer {
9894
9893
  if (attributeName === "type" && target.tagName === "INPUT" && (m.oldValue || "").toLowerCase() === "password") {
9895
9894
  target.setAttribute("data-rr-is-password", "true");
9896
9895
  }
9897
- if (!isIgnoreAttribute(target.tagName, attributeName) && // eslint-disable-next-line @typescript-eslint/no-unsafe-call
9898
- !shouldIgnoreAttribute(this.ignoreAttribute, attributeName)) {
9896
+ if (!ignoreAttribute(target.tagName, attributeName)) {
9899
9897
  item.attributes[attributeName] = transformAttribute(
9900
9898
  this.doc,
9901
9899
  toLowerCase(target.tagName),
9902
9900
  toLowerCase(attributeName),
9903
9901
  value
9904
9902
  );
9903
+ if (value === item.attributes[attributeName] && !isIncludeAttribute(attributeName, this.includeAttribute)) {
9904
+ delete item.attributes[attributeName];
9905
+ if (Object.keys(item.attributes).length === 0) {
9906
+ this.attributes = this.attributes.filter((a2) => a2 !== item);
9907
+ this.attributeMap.delete(m.target);
9908
+ }
9909
+ return;
9910
+ }
9905
9911
  if (attributeName === "style") {
9906
9912
  if (!this.unattachedDoc) {
9907
9913
  try {
@@ -10014,7 +10020,8 @@ class MutationBuffer {
10014
10020
  "blockSelector",
10015
10021
  "maskTextClass",
10016
10022
  "maskTextSelector",
10017
- "ignoreAttribute",
10023
+ "excludeAttribute",
10024
+ "includeAttribute",
10018
10025
  "inlineStylesheet",
10019
10026
  "maskInputOptions",
10020
10027
  "maskTextFn",
@@ -10153,60 +10160,6 @@ function initMutationObserver(options, rootEl) {
10153
10160
  });
10154
10161
  return observer;
10155
10162
  }
10156
- function initVisibilityObserver({
10157
- visibilityChangeCb,
10158
- doc,
10159
- mirror: mirror2,
10160
- sampling
10161
- }) {
10162
- if (!visibilityChangeCb) {
10163
- return () => {
10164
- };
10165
- }
10166
- const observedElements = /* @__PURE__ */ new WeakMap();
10167
- const debounceThreshold = typeof sampling.visibility === "number" ? sampling.visibility : 50;
10168
- const throttledCb = throttle(
10169
- callbackWrapper((entry) => {
10170
- const target = entry.target;
10171
- const id = mirror2.getId(target);
10172
- const isVisible = entry.isIntersecting || entry.intersectionRatio > 0;
10173
- if (id !== -1) {
10174
- visibilityChangeCb({
10175
- id,
10176
- isVisible,
10177
- visibilityRatio: entry.intersectionRatio
10178
- });
10179
- }
10180
- }),
10181
- debounceThreshold,
10182
- { leading: sampling.visibility !== false, trailing: true }
10183
- );
10184
- const observer = new IntersectionObserver((entries) => {
10185
- entries.forEach((entry) => {
10186
- const target = entry.target;
10187
- if (observedElements.has(target)) {
10188
- throttledCb(entry);
10189
- } else {
10190
- observedElements.set(target, true);
10191
- }
10192
- });
10193
- }, { root: null, threshold: [0.1, 0.9] });
10194
- doc.querySelectorAll("*").forEach((el) => observer.observe(el));
10195
- const mutationObserver = new MutationObserver((mutations) => {
10196
- mutations.forEach((mutation) => {
10197
- mutation.addedNodes.forEach((node2) => {
10198
- if (node2 instanceof Element) {
10199
- observer.observe(node2);
10200
- }
10201
- });
10202
- });
10203
- });
10204
- mutationObserver.observe(doc, { childList: true, subtree: true });
10205
- return () => {
10206
- observer.disconnect();
10207
- mutationObserver.disconnect();
10208
- };
10209
- }
10210
10163
  function initMoveObserver({
10211
10164
  mousemoveCb,
10212
10165
  sampling,
@@ -10485,6 +10438,20 @@ function initInputObserver({
10485
10438
  }
10486
10439
  function cbWithDedup(target, v2) {
10487
10440
  const lastInputValue = lastInputValueMap.get(target);
10441
+ const el = target;
10442
+ const hasPlaceholder = el.hasAttribute("placeholder");
10443
+ const isEmpty = el.value === "";
10444
+ const isDefaultEmpty = typeof el.defaultValue === "string" ? el.defaultValue === "" : true;
10445
+ const isNonUser = !v2.userTriggered;
10446
+ const isRepeatEmpty = !lastInputValue || lastInputValue.text === "";
10447
+ const isLikelyPhantom = hasPlaceholder && isEmpty && isDefaultEmpty && isRepeatEmpty && isNonUser && !v2.isChecked && el.type !== "hidden" && INPUT_TAGS.includes(el.tagName);
10448
+ const isRenderDrivenTextInput = el.tagName === "INPUT" && el.type === "text" && !v2.userTriggered && v2.text === el.defaultValue && !lastInputValue && el.hasAttribute("placeholder");
10449
+ const isValueFromDefault = !v2.userTriggered && el.value === el.defaultValue && !lastInputValue && el.hasAttribute("placeholder") && !v2.isChecked && el.type !== "hidden" && INPUT_TAGS.includes(el.tagName);
10450
+ const isPhantomCheckbox = el.type === "checkbox" && !v2.userTriggered && !v2.isChecked && !lastInputValue;
10451
+ const isPhantomRadio = el.type === "radio" && !v2.userTriggered && !v2.isChecked && !lastInputValue;
10452
+ if (isLikelyPhantom || isRenderDrivenTextInput || isValueFromDefault || isPhantomCheckbox || isPhantomRadio) {
10453
+ return;
10454
+ }
10488
10455
  if (!lastInputValue || lastInputValue.text !== v2.text || lastInputValue.isChecked !== v2.isChecked) {
10489
10456
  lastInputValueMap.set(target, v2);
10490
10457
  const id = mirror2.getId(target);
@@ -11021,7 +10988,6 @@ function initCustomElementObserver({
11021
10988
  function mergeHooks(o2, hooks) {
11022
10989
  const {
11023
10990
  mutationCb,
11024
- visibilityChangeCb,
11025
10991
  mousemoveCb,
11026
10992
  mouseInteractionCb,
11027
10993
  scrollCb,
@@ -11041,12 +11007,6 @@ function mergeHooks(o2, hooks) {
11041
11007
  }
11042
11008
  mutationCb(...p);
11043
11009
  };
11044
- o2.visibilityChangeCb = (...p) => {
11045
- if (hooks.visibilityChange) {
11046
- hooks.visibilityChange(...p);
11047
- }
11048
- visibilityChangeCb(...p);
11049
- };
11050
11010
  o2.mousemoveCb = (...p) => {
11051
11011
  if (hooks.mousemove) {
11052
11012
  hooks.mousemove(...p);
@@ -11139,7 +11099,6 @@ function initObservers(o2, hooks = {}) {
11139
11099
  });
11140
11100
  const inputHandler = initInputObserver(o2);
11141
11101
  const mediaInteractionHandler = initMediaInteractionObserver(o2);
11142
- const visibleHandler = initVisibilityObserver(o2);
11143
11102
  let styleSheetObserver = () => {
11144
11103
  };
11145
11104
  let adoptedStyleSheetObserver = () => {
@@ -11169,7 +11128,6 @@ function initObservers(o2, hooks = {}) {
11169
11128
  return callbackWrapper(() => {
11170
11129
  mutationBuffers.forEach((b) => b.reset());
11171
11130
  mutationObserver == null ? void 0 : mutationObserver.disconnect();
11172
- visibleHandler();
11173
11131
  mousemoveHandler();
11174
11132
  mouseInteractionHandler();
11175
11133
  scrollHandler();
@@ -12218,6 +12176,7 @@ let wrappedEmit;
12218
12176
  let takeFullSnapshot$1;
12219
12177
  let canvasManager;
12220
12178
  let recording = false;
12179
+ const preRecordingCustomEvents = [];
12221
12180
  try {
12222
12181
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
12223
12182
  const cleanFrame = document.createElement("iframe");
@@ -12234,12 +12193,12 @@ function record(options = {}) {
12234
12193
  emit,
12235
12194
  checkoutEveryNms,
12236
12195
  checkoutEveryNth,
12237
- checkoutEveryEvc,
12238
12196
  blockClass = "rr-block",
12239
12197
  blockSelector = null,
12240
12198
  ignoreClass = "rr-ignore",
12241
12199
  ignoreSelector = null,
12242
- ignoreAttribute = "rr-ignore",
12200
+ excludeAttribute: _excludeAttribute,
12201
+ includeAttribute: _includeAttribute,
12243
12202
  maskTextClass = "rr-mask",
12244
12203
  maskTextSelector = null,
12245
12204
  inlineStylesheet = true,
@@ -12257,6 +12216,7 @@ function record(options = {}) {
12257
12216
  recordCanvas = false,
12258
12217
  recordCrossOriginIframes = false,
12259
12218
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
12219
+ flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
12260
12220
  userTriggeredOnInput = false,
12261
12221
  collectFonts = false,
12262
12222
  inlineImages = false,
@@ -12288,6 +12248,8 @@ function record(options = {}) {
12288
12248
  sampling.mousemove = mousemoveWait;
12289
12249
  }
12290
12250
  mirror.reset();
12251
+ const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
12252
+ const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
12291
12253
  const maskInputOptions = maskAllInputs === true ? {
12292
12254
  color: true,
12293
12255
  date: true,
@@ -12364,8 +12326,7 @@ function record(options = {}) {
12364
12326
  incrementalSnapshotCount++;
12365
12327
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
12366
12328
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
12367
- const isVisibilityChanged = checkoutEveryEvc && e2.type === EventType.IncrementalSnapshot && e2.data.source === IncrementalSource.VisibilityChange;
12368
- if (exceedCount || exceedTime || isVisibilityChanged) {
12329
+ if (exceedCount || exceedTime) {
12369
12330
  takeFullSnapshot$1(true);
12370
12331
  }
12371
12332
  }
@@ -12434,7 +12395,8 @@ function record(options = {}) {
12434
12395
  blockSelector,
12435
12396
  maskTextClass,
12436
12397
  maskTextSelector,
12437
- ignoreAttribute,
12398
+ excludeAttribute,
12399
+ includeAttribute,
12438
12400
  inlineStylesheet,
12439
12401
  maskInputOptions,
12440
12402
  dataURLOptions,
@@ -12476,7 +12438,8 @@ function record(options = {}) {
12476
12438
  blockSelector,
12477
12439
  maskTextClass,
12478
12440
  maskTextSelector,
12479
- ignoreAttribute,
12441
+ excludeAttribute,
12442
+ includeAttribute,
12480
12443
  inlineStylesheet,
12481
12444
  maskAllInputs: maskInputOptions,
12482
12445
  maskTextFn,
@@ -12532,14 +12495,6 @@ function record(options = {}) {
12532
12495
  return callbackWrapper(initObservers)(
12533
12496
  {
12534
12497
  mutationCb: wrappedMutationEmit,
12535
- visibilityChangeCb: (v2) => {
12536
- wrappedEmit({
12537
- type: EventType.IncrementalSnapshot,
12538
- data: __spreadValues({
12539
- source: IncrementalSource.VisibilityChange
12540
- }, v2)
12541
- });
12542
- },
12543
12498
  mousemoveCb: (positions, source) => wrappedEmit({
12544
12499
  type: EventType.IncrementalSnapshot,
12545
12500
  data: {
@@ -12612,7 +12567,8 @@ function record(options = {}) {
12612
12567
  ignoreSelector,
12613
12568
  maskTextClass,
12614
12569
  maskTextSelector,
12615
- ignoreAttribute,
12570
+ excludeAttribute,
12571
+ includeAttribute,
12616
12572
  maskInputOptions,
12617
12573
  inlineStylesheet,
12618
12574
  sampling,
@@ -12658,9 +12614,15 @@ function record(options = {}) {
12658
12614
  }
12659
12615
  });
12660
12616
  const init = () => {
12617
+ if (flushCustomQueue === "before") {
12618
+ flushPreRecordingEvents();
12619
+ }
12661
12620
  takeFullSnapshot$1();
12662
12621
  handlers.push(observe(document));
12663
12622
  recording = true;
12623
+ if (flushCustomQueue === "after") {
12624
+ flushPreRecordingEvents();
12625
+ }
12664
12626
  };
12665
12627
  if (document.readyState === "interactive" || document.readyState === "complete") {
12666
12628
  init();
@@ -12689,6 +12651,7 @@ function record(options = {}) {
12689
12651
  );
12690
12652
  }
12691
12653
  return () => {
12654
+ flushPreRecordingEvents();
12692
12655
  handlers.forEach((h) => h());
12693
12656
  processedNodeManager.destroy();
12694
12657
  recording = false;
@@ -12698,17 +12661,26 @@ function record(options = {}) {
12698
12661
  console.warn(error);
12699
12662
  }
12700
12663
  }
12701
- record.addCustomEvent = (tag, payload) => {
12702
- if (!recording) {
12703
- throw new Error("please add custom event after start recording");
12664
+ function flushPreRecordingEvents() {
12665
+ for (const e2 of preRecordingCustomEvents) {
12666
+ wrappedEmit(e2);
12704
12667
  }
12705
- wrappedEmit({
12668
+ preRecordingCustomEvents.length = 0;
12669
+ }
12670
+ record.addCustomEvent = (tag, payload) => {
12671
+ const customEvent = {
12706
12672
  type: EventType.Custom,
12707
12673
  data: {
12708
12674
  tag,
12709
12675
  payload
12710
12676
  }
12711
- });
12677
+ };
12678
+ if (!recording) {
12679
+ console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
12680
+ preRecordingCustomEvents.push(customEvent);
12681
+ return;
12682
+ }
12683
+ wrappedEmit(customEvent);
12712
12684
  };
12713
12685
  record.freezePage = () => {
12714
12686
  mutationBuffers.forEach((buf) => buf.freeze());