@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.
@@ -880,32 +880,14 @@ function transformAttribute(doc, tagName, name, value) {
880
880
  }
881
881
  return value;
882
882
  }
883
- function isIgnoreAttribute(tagName, name, _value) {
883
+ function ignoreAttribute(tagName, name, _value) {
884
884
  return (tagName === "video" || tagName === "audio") && name === "autoplay";
885
885
  }
886
- function cleanAttributes(doc, element, ignoreAttribute) {
887
- const tagName = getValidTagName$1(element);
888
- const attributes = {};
889
- const len = element.attributes.length;
890
- for (let i2 = 0; i2 < len; i2++) {
891
- const attr = element.attributes[i2];
892
- const name = attr.name;
893
- const value = attr.value;
894
- const shouldIgnoreByName = typeof ignoreAttribute === "string" ? name === ignoreAttribute : ignoreAttribute.test(name);
895
- if (!shouldIgnoreByName && !isIgnoreAttribute(tagName, name)) {
896
- attributes[name] = transformAttribute(
897
- doc,
898
- tagName,
899
- toLowerCase(name),
900
- value
901
- );
902
- }
903
- }
904
- return attributes;
886
+ function isIncludeAttribute(name, include) {
887
+ return typeof include === "string" ? name.includes(include) : include.test(name);
905
888
  }
906
- function shouldIgnoreAttribute(ignore, name) {
907
- if (!ignore) return false;
908
- return typeof ignore === "string" ? name === ignore : ignore.test(name);
889
+ function isExcludeAttribute(name, exclude) {
890
+ return typeof exclude === "string" ? name.includes(exclude) : exclude.test(name);
909
891
  }
910
892
  function _isBlockedElement(element, blockClass, blockSelector) {
911
893
  try {
@@ -1036,7 +1018,8 @@ function serializeNode(n2, options) {
1036
1018
  mirror: mirror2,
1037
1019
  blockClass,
1038
1020
  blockSelector,
1039
- ignoreAttribute,
1021
+ excludeAttribute,
1022
+ includeAttribute,
1040
1023
  needsMask,
1041
1024
  inlineStylesheet,
1042
1025
  maskInputOptions = {},
@@ -1082,7 +1065,8 @@ function serializeNode(n2, options) {
1082
1065
  doc,
1083
1066
  blockClass,
1084
1067
  blockSelector,
1085
- ignoreAttribute,
1068
+ excludeAttribute,
1069
+ includeAttribute,
1086
1070
  inlineStylesheet,
1087
1071
  maskInputOptions,
1088
1072
  maskInputFn,
@@ -1160,7 +1144,8 @@ function serializeElementNode(n2, options) {
1160
1144
  doc,
1161
1145
  blockClass,
1162
1146
  blockSelector,
1163
- ignoreAttribute,
1147
+ excludeAttribute,
1148
+ includeAttribute,
1164
1149
  inlineStylesheet,
1165
1150
  maskInputOptions = {},
1166
1151
  maskInputFn,
@@ -1174,7 +1159,22 @@ function serializeElementNode(n2, options) {
1174
1159
  } = options;
1175
1160
  const needBlock = _isBlockedElement(n2, blockClass, blockSelector);
1176
1161
  const tagName = getValidTagName$1(n2);
1177
- let attributes = cleanAttributes(doc, n2, ignoreAttribute);
1162
+ let attributes = {};
1163
+ const len = n2.attributes.length;
1164
+ for (let i2 = 0; i2 < len; i2++) {
1165
+ const attr = n2.attributes[i2];
1166
+ if (isExcludeAttribute(attr.name, excludeAttribute) && !isIncludeAttribute(attr.name, includeAttribute)) {
1167
+ continue;
1168
+ }
1169
+ if (!ignoreAttribute(tagName, attr.name, attr.value)) {
1170
+ attributes[attr.name] = transformAttribute(
1171
+ doc,
1172
+ tagName,
1173
+ toLowerCase(attr.name),
1174
+ attr.value
1175
+ );
1176
+ }
1177
+ }
1178
1178
  if (tagName === "link" && inlineStylesheet) {
1179
1179
  const stylesheet = Array.from(doc.styleSheets).find((s2) => {
1180
1180
  return s2.href === n2.href;
@@ -1388,7 +1388,8 @@ function serializeNodeWithId(n2, options) {
1388
1388
  blockSelector,
1389
1389
  maskTextClass,
1390
1390
  maskTextSelector,
1391
- ignoreAttribute,
1391
+ excludeAttribute,
1392
+ includeAttribute,
1392
1393
  skipChild = false,
1393
1394
  inlineStylesheet = true,
1394
1395
  maskInputOptions = {},
@@ -1423,7 +1424,8 @@ function serializeNodeWithId(n2, options) {
1423
1424
  mirror: mirror2,
1424
1425
  blockClass,
1425
1426
  blockSelector,
1426
- ignoreAttribute,
1427
+ excludeAttribute,
1428
+ includeAttribute,
1427
1429
  needsMask,
1428
1430
  inlineStylesheet,
1429
1431
  maskInputOptions,
@@ -1476,7 +1478,8 @@ function serializeNodeWithId(n2, options) {
1476
1478
  needsMask,
1477
1479
  maskTextClass,
1478
1480
  maskTextSelector,
1479
- ignoreAttribute,
1481
+ excludeAttribute,
1482
+ includeAttribute,
1480
1483
  skipChild,
1481
1484
  inlineStylesheet,
1482
1485
  maskInputOptions,
@@ -1536,7 +1539,8 @@ function serializeNodeWithId(n2, options) {
1536
1539
  needsMask,
1537
1540
  maskTextClass,
1538
1541
  maskTextSelector,
1539
- ignoreAttribute,
1542
+ excludeAttribute,
1543
+ includeAttribute,
1540
1544
  skipChild: false,
1541
1545
  inlineStylesheet,
1542
1546
  maskInputOptions,
@@ -1578,7 +1582,8 @@ function serializeNodeWithId(n2, options) {
1578
1582
  needsMask,
1579
1583
  maskTextClass,
1580
1584
  maskTextSelector,
1581
- ignoreAttribute,
1585
+ excludeAttribute,
1586
+ includeAttribute,
1582
1587
  skipChild: false,
1583
1588
  inlineStylesheet,
1584
1589
  maskInputOptions,
@@ -1616,7 +1621,8 @@ function snapshot(n2, options) {
1616
1621
  blockSelector = null,
1617
1622
  maskTextClass = "rr-mask",
1618
1623
  maskTextSelector = null,
1619
- ignoreAttribute = "rr-ignore",
1624
+ excludeAttribute = /^$a/,
1625
+ includeAttribute = /.+/i,
1620
1626
  inlineStylesheet = true,
1621
1627
  inlineImages = false,
1622
1628
  recordCanvas = false,
@@ -1676,7 +1682,8 @@ function snapshot(n2, options) {
1676
1682
  blockSelector,
1677
1683
  maskTextClass,
1678
1684
  maskTextSelector,
1679
- ignoreAttribute,
1685
+ excludeAttribute,
1686
+ includeAttribute,
1680
1687
  skipChild: false,
1681
1688
  inlineStylesheet,
1682
1689
  maskInputOptions,
@@ -9184,7 +9191,7 @@ function hookSetter(target, key, d, isRevoked, win = window) {
9184
9191
  return () => hookSetter(target, key, original || {}, true);
9185
9192
  }
9186
9193
  function nowTimestamp() {
9187
- return performance.timeOrigin + performance.now();
9194
+ return Math.round(performance.timeOrigin + performance.now());
9188
9195
  }
9189
9196
  function getWindowScroll(win = window) {
9190
9197
  var _a2, _b, _c, _d;
@@ -9374,7 +9381,6 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
9374
9381
  IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
9375
9382
  IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
9376
9383
  IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
9377
- IncrementalSource2[IncrementalSource2["VisibilityChange"] = 17] = "VisibilityChange";
9378
9384
  return IncrementalSource2;
9379
9385
  })(IncrementalSource || {});
9380
9386
  var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
@@ -9520,7 +9526,8 @@ class MutationBuffer {
9520
9526
  __publicField(this, "blockSelector");
9521
9527
  __publicField(this, "maskTextClass");
9522
9528
  __publicField(this, "maskTextSelector");
9523
- __publicField(this, "ignoreAttribute");
9529
+ __publicField(this, "excludeAttribute");
9530
+ __publicField(this, "includeAttribute");
9524
9531
  __publicField(this, "inlineStylesheet");
9525
9532
  __publicField(this, "maskInputOptions");
9526
9533
  __publicField(this, "maskTextFn");
@@ -9584,7 +9591,8 @@ class MutationBuffer {
9584
9591
  blockSelector: this.blockSelector,
9585
9592
  maskTextClass: this.maskTextClass,
9586
9593
  maskTextSelector: this.maskTextSelector,
9587
- ignoreAttribute: this.ignoreAttribute || "",
9594
+ excludeAttribute: this.excludeAttribute,
9595
+ includeAttribute: this.includeAttribute,
9588
9596
  skipChild: true,
9589
9597
  newlyAddedElement: true,
9590
9598
  inlineStylesheet: this.inlineStylesheet,
@@ -9704,21 +9712,6 @@ class MutationBuffer {
9704
9712
  };
9705
9713
  }).filter((text) => !addedIds.has(text.id)).filter((text) => this.mirror.has(text.id)),
9706
9714
  attributes: this.attributes.map((attribute) => {
9707
- const element = attribute.node;
9708
- const filtered = {};
9709
- for (const [name, value] of Object.entries(attribute.attributes)) {
9710
- const isIgnored2 = shouldIgnoreAttribute(this.ignoreAttribute, name);
9711
- const existedBefore = element.hasAttribute(name);
9712
- const keep = value !== null && !isIgnored2 || value === null && (!isIgnored2 || attribute.attributes[name] !== null || existedBefore);
9713
- if (keep) {
9714
- filtered[name] = value;
9715
- }
9716
- }
9717
- return {
9718
- ...attribute,
9719
- attributes: filtered
9720
- };
9721
- }).filter((attribute) => Object.keys(attribute.attributes).length > 0).map((attribute) => {
9722
9715
  const { attributes } = attribute;
9723
9716
  if (typeof attributes.style === "string") {
9724
9717
  const diffAsStr = JSON.stringify(attribute.styleDiff);
@@ -9800,8 +9793,13 @@ class MutationBuffer {
9800
9793
  case "attributes": {
9801
9794
  const target = m.target;
9802
9795
  let attributeName = m.attributeName;
9803
- let value = target.getAttribute(attributeName);
9804
- if (value === null && m.oldValue === null) {
9796
+ let value = m.target.getAttribute(attributeName);
9797
+ const propValue = target[attributeName];
9798
+ const isPhantomAttributeMutation = value === null && !target.hasAttribute(attributeName) && m.oldValue !== null && (propValue === "" || propValue === null || typeof propValue === "undefined");
9799
+ if (isPhantomAttributeMutation && !isIncludeAttribute(attributeName, this.includeAttribute)) {
9800
+ return;
9801
+ }
9802
+ if (isExcludeAttribute(attributeName, this.excludeAttribute)) {
9805
9803
  return;
9806
9804
  }
9807
9805
  if (attributeName === "value") {
@@ -9839,14 +9837,21 @@ class MutationBuffer {
9839
9837
  if (attributeName === "type" && target.tagName === "INPUT" && (m.oldValue || "").toLowerCase() === "password") {
9840
9838
  target.setAttribute("data-rr-is-password", "true");
9841
9839
  }
9842
- if (!isIgnoreAttribute(target.tagName, attributeName) && // eslint-disable-next-line @typescript-eslint/no-unsafe-call
9843
- !shouldIgnoreAttribute(this.ignoreAttribute, attributeName)) {
9840
+ if (!ignoreAttribute(target.tagName, attributeName)) {
9844
9841
  item.attributes[attributeName] = transformAttribute(
9845
9842
  this.doc,
9846
9843
  toLowerCase(target.tagName),
9847
9844
  toLowerCase(attributeName),
9848
9845
  value
9849
9846
  );
9847
+ if (value === item.attributes[attributeName] && !isIncludeAttribute(attributeName, this.includeAttribute)) {
9848
+ delete item.attributes[attributeName];
9849
+ if (Object.keys(item.attributes).length === 0) {
9850
+ this.attributes = this.attributes.filter((a2) => a2 !== item);
9851
+ this.attributeMap.delete(m.target);
9852
+ }
9853
+ return;
9854
+ }
9850
9855
  if (attributeName === "style") {
9851
9856
  if (!this.unattachedDoc) {
9852
9857
  try {
@@ -9959,7 +9964,8 @@ class MutationBuffer {
9959
9964
  "blockSelector",
9960
9965
  "maskTextClass",
9961
9966
  "maskTextSelector",
9962
- "ignoreAttribute",
9967
+ "excludeAttribute",
9968
+ "includeAttribute",
9963
9969
  "inlineStylesheet",
9964
9970
  "maskInputOptions",
9965
9971
  "maskTextFn",
@@ -10098,60 +10104,6 @@ function initMutationObserver(options, rootEl) {
10098
10104
  });
10099
10105
  return observer;
10100
10106
  }
10101
- function initVisibilityObserver({
10102
- visibilityChangeCb,
10103
- doc,
10104
- mirror: mirror2,
10105
- sampling
10106
- }) {
10107
- if (!visibilityChangeCb) {
10108
- return () => {
10109
- };
10110
- }
10111
- const observedElements = /* @__PURE__ */ new WeakMap();
10112
- const debounceThreshold = typeof sampling.visibility === "number" ? sampling.visibility : 50;
10113
- const throttledCb = throttle(
10114
- callbackWrapper((entry) => {
10115
- const target = entry.target;
10116
- const id = mirror2.getId(target);
10117
- const isVisible = entry.isIntersecting || entry.intersectionRatio > 0;
10118
- if (id !== -1) {
10119
- visibilityChangeCb({
10120
- id,
10121
- isVisible,
10122
- visibilityRatio: entry.intersectionRatio
10123
- });
10124
- }
10125
- }),
10126
- debounceThreshold,
10127
- { leading: sampling.visibility !== false, trailing: true }
10128
- );
10129
- const observer = new IntersectionObserver((entries) => {
10130
- entries.forEach((entry) => {
10131
- const target = entry.target;
10132
- if (observedElements.has(target)) {
10133
- throttledCb(entry);
10134
- } else {
10135
- observedElements.set(target, true);
10136
- }
10137
- });
10138
- }, { root: null, threshold: [0.1, 0.9] });
10139
- doc.querySelectorAll("*").forEach((el) => observer.observe(el));
10140
- const mutationObserver = new MutationObserver((mutations) => {
10141
- mutations.forEach((mutation) => {
10142
- mutation.addedNodes.forEach((node2) => {
10143
- if (node2 instanceof Element) {
10144
- observer.observe(node2);
10145
- }
10146
- });
10147
- });
10148
- });
10149
- mutationObserver.observe(doc, { childList: true, subtree: true });
10150
- return () => {
10151
- observer.disconnect();
10152
- mutationObserver.disconnect();
10153
- };
10154
- }
10155
10107
  function initMoveObserver({
10156
10108
  mousemoveCb,
10157
10109
  sampling,
@@ -10431,6 +10383,20 @@ function initInputObserver({
10431
10383
  }
10432
10384
  function cbWithDedup(target, v2) {
10433
10385
  const lastInputValue = lastInputValueMap.get(target);
10386
+ const el = target;
10387
+ const hasPlaceholder = el.hasAttribute("placeholder");
10388
+ const isEmpty = el.value === "";
10389
+ const isDefaultEmpty = typeof el.defaultValue === "string" ? el.defaultValue === "" : true;
10390
+ const isNonUser = !v2.userTriggered;
10391
+ const isRepeatEmpty = !lastInputValue || lastInputValue.text === "";
10392
+ const isLikelyPhantom = hasPlaceholder && isEmpty && isDefaultEmpty && isRepeatEmpty && isNonUser && !v2.isChecked && el.type !== "hidden" && INPUT_TAGS.includes(el.tagName);
10393
+ const isRenderDrivenTextInput = el.tagName === "INPUT" && el.type === "text" && !v2.userTriggered && v2.text === el.defaultValue && !lastInputValue && el.hasAttribute("placeholder");
10394
+ const isValueFromDefault = !v2.userTriggered && el.value === el.defaultValue && !lastInputValue && el.hasAttribute("placeholder") && !v2.isChecked && el.type !== "hidden" && INPUT_TAGS.includes(el.tagName);
10395
+ const isPhantomCheckbox = el.type === "checkbox" && !v2.userTriggered && !v2.isChecked && !lastInputValue;
10396
+ const isPhantomRadio = el.type === "radio" && !v2.userTriggered && !v2.isChecked && !lastInputValue;
10397
+ if (isLikelyPhantom || isRenderDrivenTextInput || isValueFromDefault || isPhantomCheckbox || isPhantomRadio) {
10398
+ return;
10399
+ }
10434
10400
  if (!lastInputValue || lastInputValue.text !== v2.text || lastInputValue.isChecked !== v2.isChecked) {
10435
10401
  lastInputValueMap.set(target, v2);
10436
10402
  const id = mirror2.getId(target);
@@ -10968,7 +10934,6 @@ function initCustomElementObserver({
10968
10934
  function mergeHooks(o2, hooks) {
10969
10935
  const {
10970
10936
  mutationCb,
10971
- visibilityChangeCb,
10972
10937
  mousemoveCb,
10973
10938
  mouseInteractionCb,
10974
10939
  scrollCb,
@@ -10988,12 +10953,6 @@ function mergeHooks(o2, hooks) {
10988
10953
  }
10989
10954
  mutationCb(...p);
10990
10955
  };
10991
- o2.visibilityChangeCb = (...p) => {
10992
- if (hooks.visibilityChange) {
10993
- hooks.visibilityChange(...p);
10994
- }
10995
- visibilityChangeCb(...p);
10996
- };
10997
10956
  o2.mousemoveCb = (...p) => {
10998
10957
  if (hooks.mousemove) {
10999
10958
  hooks.mousemove(...p);
@@ -11086,7 +11045,6 @@ function initObservers(o2, hooks = {}) {
11086
11045
  });
11087
11046
  const inputHandler = initInputObserver(o2);
11088
11047
  const mediaInteractionHandler = initMediaInteractionObserver(o2);
11089
- const visibleHandler = initVisibilityObserver(o2);
11090
11048
  let styleSheetObserver = () => {
11091
11049
  };
11092
11050
  let adoptedStyleSheetObserver = () => {
@@ -11116,7 +11074,6 @@ function initObservers(o2, hooks = {}) {
11116
11074
  return callbackWrapper(() => {
11117
11075
  mutationBuffers.forEach((b) => b.reset());
11118
11076
  mutationObserver == null ? void 0 : mutationObserver.disconnect();
11119
- visibleHandler();
11120
11077
  mousemoveHandler();
11121
11078
  mouseInteractionHandler();
11122
11079
  scrollHandler();
@@ -12167,6 +12124,7 @@ let wrappedEmit;
12167
12124
  let takeFullSnapshot$1;
12168
12125
  let canvasManager;
12169
12126
  let recording = false;
12127
+ const preRecordingCustomEvents = [];
12170
12128
  try {
12171
12129
  if (Array.from([1], (x2) => x2 * 2)[0] !== 2) {
12172
12130
  const cleanFrame = document.createElement("iframe");
@@ -12183,12 +12141,12 @@ function record(options = {}) {
12183
12141
  emit,
12184
12142
  checkoutEveryNms,
12185
12143
  checkoutEveryNth,
12186
- checkoutEveryEvc,
12187
12144
  blockClass = "rr-block",
12188
12145
  blockSelector = null,
12189
12146
  ignoreClass = "rr-ignore",
12190
12147
  ignoreSelector = null,
12191
- ignoreAttribute = "rr-ignore",
12148
+ excludeAttribute: _excludeAttribute,
12149
+ includeAttribute: _includeAttribute,
12192
12150
  maskTextClass = "rr-mask",
12193
12151
  maskTextSelector = null,
12194
12152
  inlineStylesheet = true,
@@ -12206,6 +12164,7 @@ function record(options = {}) {
12206
12164
  recordCanvas = false,
12207
12165
  recordCrossOriginIframes = false,
12208
12166
  recordAfter = options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load",
12167
+ flushCustomQueue = options.flushCustomQueue !== void 0 ? options.flushCustomQueue : "after",
12209
12168
  userTriggeredOnInput = false,
12210
12169
  collectFonts = false,
12211
12170
  inlineImages = false,
@@ -12237,6 +12196,8 @@ function record(options = {}) {
12237
12196
  sampling.mousemove = mousemoveWait;
12238
12197
  }
12239
12198
  mirror.reset();
12199
+ const excludeAttribute = _excludeAttribute === void 0 ? /^$a/ : _excludeAttribute;
12200
+ const includeAttribute = _includeAttribute === void 0 ? /.+/i : _includeAttribute;
12240
12201
  const maskInputOptions = maskAllInputs === true ? {
12241
12202
  color: true,
12242
12203
  date: true,
@@ -12313,8 +12274,7 @@ function record(options = {}) {
12313
12274
  incrementalSnapshotCount++;
12314
12275
  const exceedCount = checkoutEveryNth && incrementalSnapshotCount >= checkoutEveryNth;
12315
12276
  const exceedTime = checkoutEveryNms && e2.timestamp - lastFullSnapshotEvent.timestamp > checkoutEveryNms;
12316
- const isVisibilityChanged = checkoutEveryEvc && e2.type === EventType.IncrementalSnapshot && e2.data.source === IncrementalSource.VisibilityChange;
12317
- if (exceedCount || exceedTime || isVisibilityChanged) {
12277
+ if (exceedCount || exceedTime) {
12318
12278
  takeFullSnapshot$1(true);
12319
12279
  }
12320
12280
  }
@@ -12387,7 +12347,8 @@ function record(options = {}) {
12387
12347
  blockSelector,
12388
12348
  maskTextClass,
12389
12349
  maskTextSelector,
12390
- ignoreAttribute,
12350
+ excludeAttribute,
12351
+ includeAttribute,
12391
12352
  inlineStylesheet,
12392
12353
  maskInputOptions,
12393
12354
  dataURLOptions,
@@ -12429,7 +12390,8 @@ function record(options = {}) {
12429
12390
  blockSelector,
12430
12391
  maskTextClass,
12431
12392
  maskTextSelector,
12432
- ignoreAttribute,
12393
+ excludeAttribute,
12394
+ includeAttribute,
12433
12395
  inlineStylesheet,
12434
12396
  maskAllInputs: maskInputOptions,
12435
12397
  maskTextFn,
@@ -12485,15 +12447,6 @@ function record(options = {}) {
12485
12447
  return callbackWrapper(initObservers)(
12486
12448
  {
12487
12449
  mutationCb: wrappedMutationEmit,
12488
- visibilityChangeCb: (v2) => {
12489
- wrappedEmit({
12490
- type: EventType.IncrementalSnapshot,
12491
- data: {
12492
- source: IncrementalSource.VisibilityChange,
12493
- ...v2
12494
- }
12495
- });
12496
- },
12497
12450
  mousemoveCb: (positions, source) => wrappedEmit({
12498
12451
  type: EventType.IncrementalSnapshot,
12499
12452
  data: {
@@ -12575,7 +12528,8 @@ function record(options = {}) {
12575
12528
  ignoreSelector,
12576
12529
  maskTextClass,
12577
12530
  maskTextSelector,
12578
- ignoreAttribute,
12531
+ excludeAttribute,
12532
+ includeAttribute,
12579
12533
  maskInputOptions,
12580
12534
  inlineStylesheet,
12581
12535
  sampling,
@@ -12621,9 +12575,15 @@ function record(options = {}) {
12621
12575
  }
12622
12576
  });
12623
12577
  const init = () => {
12578
+ if (flushCustomQueue === "before") {
12579
+ flushPreRecordingEvents();
12580
+ }
12624
12581
  takeFullSnapshot$1();
12625
12582
  handlers.push(observe(document));
12626
12583
  recording = true;
12584
+ if (flushCustomQueue === "after") {
12585
+ flushPreRecordingEvents();
12586
+ }
12627
12587
  };
12628
12588
  if (document.readyState === "interactive" || document.readyState === "complete") {
12629
12589
  init();
@@ -12652,6 +12612,7 @@ function record(options = {}) {
12652
12612
  );
12653
12613
  }
12654
12614
  return () => {
12615
+ flushPreRecordingEvents();
12655
12616
  handlers.forEach((h) => h());
12656
12617
  processedNodeManager.destroy();
12657
12618
  recording = false;
@@ -12661,17 +12622,26 @@ function record(options = {}) {
12661
12622
  console.warn(error);
12662
12623
  }
12663
12624
  }
12664
- record.addCustomEvent = (tag, payload) => {
12665
- if (!recording) {
12666
- throw new Error("please add custom event after start recording");
12625
+ function flushPreRecordingEvents() {
12626
+ for (const e2 of preRecordingCustomEvents) {
12627
+ wrappedEmit(e2);
12667
12628
  }
12668
- wrappedEmit({
12629
+ preRecordingCustomEvents.length = 0;
12630
+ }
12631
+ record.addCustomEvent = (tag, payload) => {
12632
+ const customEvent = {
12669
12633
  type: EventType.Custom,
12670
12634
  data: {
12671
12635
  tag,
12672
12636
  payload
12673
12637
  }
12674
- });
12638
+ };
12639
+ if (!recording) {
12640
+ console.warn(`[rrweb] CustomEvent buffered before recording start: ${tag}`);
12641
+ preRecordingCustomEvents.push(customEvent);
12642
+ return;
12643
+ }
12644
+ wrappedEmit(customEvent);
12675
12645
  };
12676
12646
  record.freezePage = () => {
12677
12647
  mutationBuffers.forEach((buf) => buf.freeze());