mixpanel-browser 2.68.0 → 2.69.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.
@@ -460,6 +460,30 @@ function querySelectorAll$1(n2, selectors) {
460
460
  function mutationObserverCtor$1() {
461
461
  return getUntaintedPrototype$1("MutationObserver").constructor;
462
462
  }
463
+ function patch$1(source, name, replacement) {
464
+ try {
465
+ if (!(name in source)) {
466
+ return function() {};
467
+ }
468
+ var original = source[name];
469
+ var wrapped = replacement(original);
470
+ if (typeof wrapped === "function") {
471
+ wrapped.prototype = wrapped.prototype || {};
472
+ Object.defineProperties(wrapped, {
473
+ __rrweb_original__: {
474
+ enumerable: false,
475
+ value: original
476
+ }
477
+ });
478
+ }
479
+ source[name] = wrapped;
480
+ return function() {
481
+ source[name] = original;
482
+ };
483
+ } catch (e) {
484
+ return function() {};
485
+ }
486
+ }
463
487
  var index$1 = {
464
488
  childNodes: childNodes$1,
465
489
  parentNode: parentNode$1,
@@ -472,7 +496,8 @@ var index$1 = {
472
496
  shadowRoot: shadowRoot$1,
473
497
  querySelector: querySelector$1,
474
498
  querySelectorAll: querySelectorAll$1,
475
- mutationObserver: mutationObserverCtor$1
499
+ mutationObserver: mutationObserverCtor$1,
500
+ patch: patch$1
476
501
  };
477
502
  function isElement(n2) {
478
503
  return n2.nodeType === n2.ELEMENT_NODE;
@@ -731,26 +756,82 @@ function absolutifyURLs(cssText, href) {
731
756
  return "url(" + maybeQuote + stack.join("/") + maybeQuote + ")";
732
757
  });
733
758
  }
734
- function normalizeCssString(cssText) {
735
- return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "");
759
+ function normalizeCssString(cssText, _testNoPxNorm) {
760
+ if (_testNoPxNorm === void 0) _testNoPxNorm = false;
761
+ if (_testNoPxNorm) {
762
+ return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "");
763
+ } else {
764
+ return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "").replace(/0px/g, "0");
765
+ }
736
766
  }
737
- function splitCssText(cssText, style) {
767
+ function splitCssText(cssText, style, _testNoPxNorm) {
768
+ if (_testNoPxNorm === void 0) _testNoPxNorm = false;
738
769
  var childNodes2 = Array.from(style.childNodes);
739
770
  var splits = [];
771
+ var iterCount = 0;
740
772
  if (childNodes2.length > 1 && cssText && typeof cssText === "string") {
741
- var cssTextNorm = normalizeCssString(cssText);
773
+ var cssTextNorm = normalizeCssString(cssText, _testNoPxNorm);
774
+ var normFactor = cssTextNorm.length / cssText.length;
742
775
  for(var i2 = 1; i2 < childNodes2.length; i2++){
743
776
  if (childNodes2[i2].textContent && typeof childNodes2[i2].textContent === "string") {
744
- var textContentNorm = normalizeCssString(childNodes2[i2].textContent);
745
- for(var j = 3; j < textContentNorm.length; j++){
746
- var bit = textContentNorm.substring(0, j);
747
- if (cssTextNorm.split(bit).length === 2) {
748
- var splitNorm = cssTextNorm.indexOf(bit);
749
- for(var k = splitNorm; k < cssText.length; k++){
750
- if (normalizeCssString(cssText.substring(0, k)).length === splitNorm) {
777
+ var textContentNorm = normalizeCssString(childNodes2[i2].textContent, _testNoPxNorm);
778
+ var jLimit = 100;
779
+ var j = 3;
780
+ for(; j < textContentNorm.length; j++){
781
+ if (// keep consuming css identifiers (to get a decent chunk more quickly)
782
+ textContentNorm[j].match(/[a-zA-Z0-9]/) || // substring needs to be unique to this section
783
+ textContentNorm.indexOf(textContentNorm.substring(0, j), 1) !== -1) {
784
+ continue;
785
+ }
786
+ break;
787
+ }
788
+ for(; j < textContentNorm.length; j++){
789
+ var startSubstring = textContentNorm.substring(0, j);
790
+ var cssNormSplits = cssTextNorm.split(startSubstring);
791
+ var splitNorm = -1;
792
+ if (cssNormSplits.length === 2) {
793
+ splitNorm = cssNormSplits[0].length;
794
+ } else if (cssNormSplits.length > 2 && cssNormSplits[0] === "" && childNodes2[i2 - 1].textContent !== "") {
795
+ splitNorm = cssTextNorm.indexOf(startSubstring, 1);
796
+ } else if (cssNormSplits.length === 1) {
797
+ startSubstring = startSubstring.substring(0, startSubstring.length - 1);
798
+ cssNormSplits = cssTextNorm.split(startSubstring);
799
+ if (cssNormSplits.length <= 1) {
800
+ splits.push(cssText);
801
+ return splits;
802
+ }
803
+ j = jLimit + 1;
804
+ } else if (j === textContentNorm.length - 1) {
805
+ splitNorm = cssTextNorm.indexOf(startSubstring);
806
+ }
807
+ if (cssNormSplits.length >= 2 && j > jLimit) {
808
+ var prevTextContent = childNodes2[i2 - 1].textContent;
809
+ if (prevTextContent && typeof prevTextContent === "string") {
810
+ var prevMinLength = normalizeCssString(prevTextContent).length;
811
+ splitNorm = cssTextNorm.indexOf(startSubstring, prevMinLength);
812
+ }
813
+ if (splitNorm === -1) {
814
+ splitNorm = cssNormSplits[0].length;
815
+ }
816
+ }
817
+ if (splitNorm !== -1) {
818
+ var k = Math.floor(splitNorm / normFactor);
819
+ for(; k > 0 && k < cssText.length;){
820
+ iterCount += 1;
821
+ if (iterCount > 50 * childNodes2.length) {
822
+ splits.push(cssText);
823
+ return splits;
824
+ }
825
+ var normPart = normalizeCssString(cssText.substring(0, k), _testNoPxNorm);
826
+ if (normPart.length === splitNorm) {
751
827
  splits.push(cssText.substring(0, k));
752
828
  cssText = cssText.substring(k);
829
+ cssTextNorm = cssTextNorm.substring(splitNorm);
753
830
  break;
831
+ } else if (normPart.length < splitNorm) {
832
+ k += Math.max(1, Math.floor((splitNorm - normPart.length) / normFactor));
833
+ } else {
834
+ k -= Math.max(1, Math.floor((normPart.length - splitNorm) * normFactor));
754
835
  }
755
836
  }
756
837
  break;
@@ -1267,7 +1348,7 @@ function slimDOMExcluded(sn, slimDOMOptions) {
1267
1348
  } else if (sn.type === NodeType$3.Element) {
1268
1349
  if (slimDOMOptions.script && // script tag
1269
1350
  (sn.tagName === "script" || // (module)preload link
1270
- sn.tagName === "link" && (sn.attributes.rel === "preload" || sn.attributes.rel === "modulepreload") && sn.attributes.as === "script" || // prefetch link
1351
+ sn.tagName === "link" && (sn.attributes.rel === "preload" && sn.attributes.as === "script" || sn.attributes.rel === "modulepreload") || // prefetch link
1271
1352
  sn.tagName === "link" && sn.attributes.rel === "prefetch" && typeof sn.attributes.href === "string" && extractFileExtension(sn.attributes.href) === "js")) {
1272
1353
  return true;
1273
1354
  } else if (slimDOMOptions.headFavicon && (sn.tagName === "link" && sn.attributes.rel === "shortcut icon" || sn.tagName === "meta" && (lowerIfExists(sn.attributes.name).match(/^msapplication-tile(image|color)$/) || lowerIfExists(sn.attributes.name) === "application-name" || lowerIfExists(sn.attributes.rel) === "icon" || lowerIfExists(sn.attributes.rel) === "apple-touch-icon" || lowerIfExists(sn.attributes.rel) === "shortcut icon"))) {
@@ -5764,11 +5845,16 @@ function getTagName(n2) {
5764
5845
  function adaptCssForReplay(cssText, cache) {
5765
5846
  var cachedStyle = cache == null ? void 0 : cache.stylesWithHoverClass.get(cssText);
5766
5847
  if (cachedStyle) return cachedStyle;
5767
- var ast = postcss$1$1([
5768
- mediaSelectorPlugin,
5769
- pseudoClassPlugin
5770
- ]).process(cssText);
5771
- var result2 = ast.css;
5848
+ var result2 = cssText;
5849
+ try {
5850
+ var ast = postcss$1$1([
5851
+ mediaSelectorPlugin,
5852
+ pseudoClassPlugin
5853
+ ]).process(cssText);
5854
+ result2 = ast.css;
5855
+ } catch (error) {
5856
+ console.warn("Failed to adapt css for replay", error);
5857
+ }
5772
5858
  cache == null ? void 0 : cache.stylesWithHoverClass.set(cssText, result2);
5773
5859
  return result2;
5774
5860
  }
@@ -5790,11 +5876,39 @@ function applyCssSplits(n2, cssText, hackCss, cache) {
5790
5876
  while(cssTextSplits.length > 1 && cssTextSplits.length > childTextNodes.length){
5791
5877
  cssTextSplits.splice(-2, 2, cssTextSplits.slice(-2).join(""));
5792
5878
  }
5879
+ var adaptedCss = "";
5880
+ if (hackCss) {
5881
+ adaptedCss = adaptCssForReplay(cssTextSplits.join(""), cache);
5882
+ }
5883
+ var startIndex = 0;
5793
5884
  for(var i2 = 0; i2 < childTextNodes.length; i2++){
5885
+ if (i2 === cssTextSplits.length) {
5886
+ break;
5887
+ }
5794
5888
  var childTextNode = childTextNodes[i2];
5795
- var cssTextSection = cssTextSplits[i2];
5796
- if (childTextNode && cssTextSection) {
5797
- childTextNode.textContent = hackCss ? adaptCssForReplay(cssTextSection, cache) : cssTextSection;
5889
+ if (!hackCss) {
5890
+ childTextNode.textContent = cssTextSplits[i2];
5891
+ } else if (i2 < cssTextSplits.length - 1) {
5892
+ var endIndex = startIndex;
5893
+ var endSearch = cssTextSplits[i2 + 1].length;
5894
+ endSearch = Math.min(endSearch, 30);
5895
+ var found = false;
5896
+ for(; endSearch > 2; endSearch--){
5897
+ var searchBit = cssTextSplits[i2 + 1].substring(0, endSearch);
5898
+ var searchIndex = adaptedCss.substring(startIndex).indexOf(searchBit);
5899
+ found = searchIndex !== -1;
5900
+ if (found) {
5901
+ endIndex += searchIndex;
5902
+ break;
5903
+ }
5904
+ }
5905
+ if (!found) {
5906
+ endIndex += cssTextSplits[i2].length;
5907
+ }
5908
+ childTextNode.textContent = adaptedCss.substring(startIndex, endIndex);
5909
+ startIndex = endIndex;
5910
+ } else {
5911
+ childTextNode.textContent = adaptedCss.substring(startIndex);
5798
5912
  }
5799
5913
  }
5800
5914
  }
@@ -5918,7 +6032,7 @@ function buildNode(n2, options) {
5918
6032
  } else if (tagName === "meta" && n2.attributes["http-equiv"] === "Content-Security-Policy" && name === "content") {
5919
6033
  node2.setAttribute("csp-content", value.toString());
5920
6034
  continue;
5921
- } else if (tagName === "link" && (n2.attributes.rel === "preload" || n2.attributes.rel === "modulepreload") && n2.attributes.as === "script") {} else if (tagName === "link" && n2.attributes.rel === "prefetch" && typeof n2.attributes.href === "string" && n2.attributes.href.endsWith(".js")) {} else if (tagName === "img" && n2.attributes.srcset && n2.attributes.rr_dataURL) {
6035
+ } else if (tagName === "link" && (n2.attributes.rel === "preload" && n2.attributes.as === "script" || n2.attributes.rel === "modulepreload")) {} else if (tagName === "link" && n2.attributes.rel === "prefetch" && typeof n2.attributes.href === "string" && extractFileExtension(n2.attributes.href) === "js") {} else if (tagName === "img" && n2.attributes.srcset && n2.attributes.rr_dataURL) {
5922
6036
  node2.setAttribute("rrweb-original-srcset", n2.attributes.srcset);
5923
6037
  } else {
5924
6038
  node2.setAttribute(name, value.toString());
@@ -11791,6 +11905,30 @@ function querySelectorAll(n2, selectors) {
11791
11905
  function mutationObserverCtor() {
11792
11906
  return getUntaintedPrototype("MutationObserver").constructor;
11793
11907
  }
11908
+ function patch(source, name, replacement) {
11909
+ try {
11910
+ if (!(name in source)) {
11911
+ return function() {};
11912
+ }
11913
+ var original = source[name];
11914
+ var wrapped = replacement(original);
11915
+ if (typeof wrapped === "function") {
11916
+ wrapped.prototype = wrapped.prototype || {};
11917
+ Object.defineProperties(wrapped, {
11918
+ __rrweb_original__: {
11919
+ enumerable: false,
11920
+ value: original
11921
+ }
11922
+ });
11923
+ }
11924
+ source[name] = wrapped;
11925
+ return function() {
11926
+ source[name] = original;
11927
+ };
11928
+ } catch (e) {
11929
+ return function() {};
11930
+ }
11931
+ }
11794
11932
  var index = {
11795
11933
  childNodes: childNodes,
11796
11934
  parentNode: parentNode,
@@ -11803,7 +11941,8 @@ var index = {
11803
11941
  shadowRoot: shadowRoot,
11804
11942
  querySelector: querySelector,
11805
11943
  querySelectorAll: querySelectorAll,
11806
- mutationObserver: mutationObserverCtor
11944
+ mutationObserver: mutationObserverCtor,
11945
+ patch: patch
11807
11946
  };
11808
11947
  function on(type, fn, target) {
11809
11948
  if (target === void 0) target = document;
@@ -11896,30 +12035,6 @@ function hookSetter(target, key, d, isRevoked, win) {
11896
12035
  return hookSetter(target, key, original || {}, true);
11897
12036
  };
11898
12037
  }
11899
- function patch(source, name, replacement) {
11900
- try {
11901
- if (!(name in source)) {
11902
- return function() {};
11903
- }
11904
- var original = source[name];
11905
- var wrapped = replacement(original);
11906
- if (typeof wrapped === "function") {
11907
- wrapped.prototype = wrapped.prototype || {};
11908
- Object.defineProperties(wrapped, {
11909
- __rrweb_original__: {
11910
- enumerable: false,
11911
- value: original
11912
- }
11913
- });
11914
- }
11915
- source[name] = wrapped;
11916
- return function() {
11917
- source[name] = original;
11918
- };
11919
- } catch (e) {
11920
- return function() {};
11921
- }
11922
- }
11923
12038
  var nowTimestamp = Date.now;
11924
12039
  if (!/* @__PURE__ */ /[1-9][0-9]{12}/.test(Date.now().toString())) {
11925
12040
  nowTimestamp = function() {
@@ -12202,7 +12317,6 @@ var utils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty(
12202
12317
  return nowTimestamp;
12203
12318
  },
12204
12319
  on: on,
12205
- patch: patch,
12206
12320
  polyfill: polyfill$1,
12207
12321
  queueToResolveTrees: queueToResolveTrees,
12208
12322
  shadowHostInDom: shadowHostInDom,
@@ -12659,9 +12773,17 @@ var MutationBuffer = /*#__PURE__*/ function() {
12659
12773
  _this.attributes.push(item);
12660
12774
  _this.attributeMap.set(textarea, item);
12661
12775
  }
12662
- item.attributes.value = Array.from(index.childNodes(textarea), function(cn) {
12776
+ var value = Array.from(index.childNodes(textarea), function(cn) {
12663
12777
  return index.textContent(cn) || "";
12664
12778
  }).join("");
12779
+ item.attributes.value = maskInputValue({
12780
+ element: textarea,
12781
+ maskInputOptions: _this.maskInputOptions,
12782
+ tagName: textarea.tagName,
12783
+ type: getInputType(textarea),
12784
+ value: value,
12785
+ maskInputFn: _this.maskInputFn
12786
+ });
12665
12787
  });
12666
12788
  __publicField(this, "processMutation", function(m) {
12667
12789
  if (isIgnored(m.target, _this.mirror, _this.slimDOMOptions)) {
@@ -15462,8 +15584,15 @@ function record(options) {
15462
15584
  }, window));
15463
15585
  }
15464
15586
  return function() {
15465
- handlers.forEach(function(h) {
15466
- return h();
15587
+ handlers.forEach(function(handler) {
15588
+ try {
15589
+ handler();
15590
+ } catch (error) {
15591
+ var msg = String(error).toLowerCase();
15592
+ if (!msg.includes("cross-origin")) {
15593
+ console.warn(error);
15594
+ }
15595
+ }
15467
15596
  });
15468
15597
  processedNodeManager.destroy();
15469
15598
  recording = false;
@@ -17093,21 +17222,21 @@ var Replayer = /*#__PURE__*/ function() {
17093
17222
  event: event
17094
17223
  }
17095
17224
  });
17096
- var last_index = _this.service.state.context.events.length - 1;
17097
- if (!_this.config.liveMode && event === _this.service.state.context.events[last_index]) {
17225
+ var lastIndex = _this.service.state.context.events.length - 1;
17226
+ if (!_this.config.liveMode && event === _this.service.state.context.events[lastIndex]) {
17098
17227
  var finish = function() {
17099
- if (last_index < _this.service.state.context.events.length - 1) {
17228
+ if (lastIndex < _this.service.state.context.events.length - 1) {
17100
17229
  return;
17101
17230
  }
17102
17231
  _this.backToNormal();
17103
17232
  _this.service.send("END");
17104
17233
  _this.emitter.emit(ReplayerEvents.Finish);
17105
17234
  };
17106
- var finish_buffer = 50;
17235
+ var finishBuffer = 50;
17107
17236
  if (event.type === EventType.IncrementalSnapshot && event.data.source === IncrementalSource.MouseMove && event.data.positions.length) {
17108
- finish_buffer += Math.max(0, -event.data.positions[0].timeOffset);
17237
+ finishBuffer += Math.max(0, -event.data.positions[0].timeOffset);
17109
17238
  }
17110
- setTimeout(finish, finish_buffer);
17239
+ setTimeout(finish, finishBuffer);
17111
17240
  }
17112
17241
  _this.emitter.emit(ReplayerEvents.EventCast, event);
17113
17242
  };
@@ -18300,8 +18429,9 @@ var Replayer = /*#__PURE__*/ function() {
18300
18429
  if (attributeName === "_cssText" && (target.nodeName === "LINK" || target.nodeName === "STYLE")) {
18301
18430
  try {
18302
18431
  var newSn = mirror2.getMeta(target);
18303
- Object.assign(newSn.attributes, mutation.attributes);
18304
- var newNode = buildNodeWithSN(newSn, {
18432
+ var newNode = buildNodeWithSN(_extends({}, newSn, {
18433
+ attributes: _extends({}, newSn.attributes, mutation.attributes)
18434
+ }), {
18305
18435
  doc: target.ownerDocument,
18306
18436
  // can be Document or RRDocument
18307
18437
  mirror: mirror2,
@@ -18309,6 +18439,7 @@ var Replayer = /*#__PURE__*/ function() {
18309
18439
  hackCss: true,
18310
18440
  cache: _this.cache
18311
18441
  });
18442
+ Object.assign(newSn.attributes, mutation.attributes);
18312
18443
  var siblingNode = target.nextSibling;
18313
18444
  var parentNode2 = target.parentNode;
18314
18445
  if (newNode && parentNode2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.68.0",
3
+ "version": "2.69.0",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "module": "dist/mixpanel.module.js",
@@ -23,6 +23,18 @@
23
23
  "validate": "npm ls"
24
24
  },
25
25
  "types": "./src/index.d.ts",
26
+ "exports": {
27
+ "./src/loaders/loader-module-core": {
28
+ "types": "./src/index.d.ts",
29
+ "import": "./src/loaders/loader-module-core.js",
30
+ "require": "./src/loaders/loader-module-core.js"
31
+ },
32
+ "./src/loaders/loader-module-with-async-recorder": {
33
+ "types": "./src/index.d.ts",
34
+ "import": "./src/loaders/loader-module-with-async-recorder.js",
35
+ "require": "./src/loaders/loader-module-with-async-recorder.js"
36
+ }
37
+ },
26
38
  "repository": {
27
39
  "type": "git",
28
40
  "url": "https://github.com/mixpanel/mixpanel-js.git"
@@ -66,6 +78,6 @@
66
78
  "webpack": "1.12.2"
67
79
  },
68
80
  "dependencies": {
69
- "rrweb": "2.0.0-alpha.18"
81
+ "@mixpanel/rrweb": "2.0.0-alpha.18.1"
70
82
  }
71
83
  }
package/rollup.config.mjs CHANGED
@@ -8,7 +8,7 @@ const COMPILED_RRWEB_PATH = 'build/rrweb-compiled.js';
8
8
 
9
9
  const aliasRrweb = () => alias({
10
10
  entries: [
11
- { find: 'rrweb', replacement: COMPILED_RRWEB_PATH },
11
+ { find: '@mixpanel/rrweb', replacement: COMPILED_RRWEB_PATH },
12
12
  ]
13
13
  });
14
14
 
@@ -24,7 +24,7 @@ const MINIFY = process.env.MINIFY || process.env.FULL;
24
24
  const MAIN_BUILDS = [
25
25
  // compile rrweb first to es5 with swc, we'll replace the import later on
26
26
  {
27
- 'input': 'rrweb',
27
+ 'input': '@mixpanel/rrweb',
28
28
  'output': [
29
29
  {
30
30
  file: COMPILED_RRWEB_PATH,
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  var Config = {
2
2
  DEBUG: false,
3
- LIB_VERSION: '2.68.0'
3
+ LIB_VERSION: '2.69.0'
4
4
  };
5
5
 
6
6
  export default Config;
package/src/index.d.ts CHANGED
@@ -198,6 +198,7 @@ export interface Config {
198
198
  record_inline_images: boolean;
199
199
  record_mask_text_class: string;
200
200
  record_mask_text_selector: string;
201
+ record_min_ms: number;
201
202
  record_max_ms: number;
202
203
  record_sessions_percent: number;
203
204
  record_canvas: boolean;
@@ -392,7 +392,9 @@ MixpanelLib.prototype._init = function(token, config, name) {
392
392
  * This is primarily used for session recording, where data must be isolated to the current tab.
393
393
  */
394
394
  MixpanelLib.prototype._init_tab_id = function() {
395
- if (_.sessionStorage.is_supported()) {
395
+ if (this.get_config('disable_persistence')) {
396
+ console.log('Tab ID initialization skipped due to disable_persistence config');
397
+ } else if (_.sessionStorage.is_supported()) {
396
398
  try {
397
399
  var key_suffix = this.get_config('name') + '_' + this.get_config('token');
398
400
  var tab_id_key = 'mp_tab_id_' + key_suffix;
@@ -426,6 +428,11 @@ MixpanelLib.prototype.get_tab_id = function () {
426
428
  };
427
429
 
428
430
  MixpanelLib.prototype._should_load_recorder = function () {
431
+ if (this.get_config('disable_persistence')) {
432
+ console.log('Load recorder check skipped due to disable_persistence config');
433
+ return Promise.resolve(false);
434
+ }
435
+
429
436
  var recording_registry_idb = new IDBStorageWrapper(RECORDING_REGISTRY_STORE_NAME);
430
437
  var tab_id = this.get_tab_id();
431
438
  return recording_registry_idb.init()
@@ -1,4 +1,4 @@
1
- import { record } from 'rrweb';
1
+ import { record } from '@mixpanel/rrweb';
2
2
  import { Promise } from '../promise-polyfill';
3
3
  import { SessionRecording } from './session-recording';
4
4
  import { RecordingRegistry } from './recording-registry';
@@ -8,12 +8,17 @@ import { isRecordingExpired } from './utils';
8
8
  * Makes sure that only one tab can be recording at a time.
9
9
  */
10
10
  var RecordingRegistry = function (options) {
11
+ /** @type {IDBStorageWrapper} */
11
12
  this.idb = new IDBStorageWrapper(RECORDING_REGISTRY_STORE_NAME);
12
13
  this.errorReporter = options.errorReporter;
13
14
  this.mixpanelInstance = options.mixpanelInstance;
14
15
  this.sharedLockStorage = options.sharedLockStorage;
15
16
  };
16
17
 
18
+ RecordingRegistry.prototype.isPersistenceEnabled = function() {
19
+ return !this.mixpanelInstance.get_config('disable_persistence');
20
+ };
21
+
17
22
  RecordingRegistry.prototype.handleError = function (err) {
18
23
  this.errorReporter('IndexedDB error: ', err);
19
24
  };
@@ -22,6 +27,10 @@ RecordingRegistry.prototype.handleError = function (err) {
22
27
  * @param {import('./session-recording').SerializedRecording} serializedRecording
23
28
  */
24
29
  RecordingRegistry.prototype.setActiveRecording = function (serializedRecording) {
30
+ if (!this.isPersistenceEnabled()) {
31
+ return Promise.resolve();
32
+ }
33
+
25
34
  var tabId = serializedRecording['tabId'];
26
35
  if (!tabId) {
27
36
  console.warn('No tab ID is set, cannot persist recording metadata.');
@@ -39,6 +48,10 @@ RecordingRegistry.prototype.setActiveRecording = function (serializedRecording)
39
48
  * @returns {Promise<import('./session-recording').SerializedRecording>}
40
49
  */
41
50
  RecordingRegistry.prototype.getActiveRecording = function () {
51
+ if (!this.isPersistenceEnabled()) {
52
+ return Promise.resolve(null);
53
+ }
54
+
42
55
  return this.idb.init()
43
56
  .then(function () {
44
57
  return this.idb.getItem(this.mixpanelInstance.get_tab_id());
@@ -50,8 +63,16 @@ RecordingRegistry.prototype.getActiveRecording = function () {
50
63
  };
51
64
 
52
65
  RecordingRegistry.prototype.clearActiveRecording = function () {
53
- // mark recording as expired instead of deleting it in case the page unloads mid-flush and doesn't make it to ingestion.
54
- // this will ensure the next pageload will flush the remaining events, but not try to continue the recording.
66
+ if (this.isPersistenceEnabled()) {
67
+ // mark recording as expired instead of deleting it in case the page unloads mid-flush and doesn't make it to ingestion.
68
+ // this will ensure the next pageload will flush the remaining events, but not try to continue the recording.
69
+ return this.markActiveRecordingExpired();
70
+ } else {
71
+ return this.deleteActiveRecording();
72
+ }
73
+ };
74
+
75
+ RecordingRegistry.prototype.markActiveRecordingExpired = function () {
55
76
  return this.getActiveRecording()
56
77
  .then(function (serializedRecording) {
57
78
  if (serializedRecording) {
@@ -62,11 +83,25 @@ RecordingRegistry.prototype.clearActiveRecording = function () {
62
83
  .catch(this.handleError.bind(this));
63
84
  };
64
85
 
86
+ RecordingRegistry.prototype.deleteActiveRecording = function () {
87
+ // avoid initializing IDB if this registry instance hasn't already written a recording
88
+ if (this.idb.isInitialized()) {
89
+ return this.idb.removeItem(this.mixpanelInstance.get_tab_id())
90
+ .catch(this.handleError.bind(this));
91
+ } else {
92
+ return Promise.resolve();
93
+ }
94
+ };
95
+
65
96
  /**
66
97
  * Flush any inactive recordings from the registry to minimize data loss.
67
98
  * The main idea here is that we can flush remaining rrweb events on the next page load if a tab is closed mid-batch.
68
99
  */
69
100
  RecordingRegistry.prototype.flushInactiveRecordings = function () {
101
+ if (!this.isPersistenceEnabled()) {
102
+ return Promise.resolve([]);
103
+ }
104
+
70
105
  return this.idb.init()
71
106
  .then(function() {
72
107
  return this.idb.getAll();
@@ -1,5 +1,5 @@
1
1
  import { window } from '../window';
2
- import { IncrementalSource, EventType } from 'rrweb';
2
+ import { IncrementalSource, EventType } from '@mixpanel/rrweb';
3
3
  import { MAX_RECORDING_MS, MAX_VALUE_FOR_MIN_RECORDING_MS, console_with_prefix, NOOP_FUNC, _, localStorageSupported} from '../utils'; // eslint-disable-line camelcase
4
4
  import { IDBStorageWrapper, RECORDING_EVENTS_STORE_NAME } from '../storage/indexed-db';
5
5
  import { addOptOutCheckMixpanelLib } from '../gdpr-utils';
@@ -101,10 +101,9 @@ var SessionRecording = function(options) {
101
101
 
102
102
  // disable persistence if localStorage is not supported
103
103
  // request-queue will automatically disable persistence if indexedDB fails to initialize
104
- var usePersistence = localStorageSupported(options.sharedLockStorage, true);
104
+ var usePersistence = localStorageSupported(options.sharedLockStorage, true) && !this.getConfig('disable_persistence');
105
105
 
106
106
  // each replay has its own batcher key to avoid conflicts between rrweb events of different recordings
107
- // this will be important when persistence is introduced
108
107
  this.batcherKey = '__mprec_' + this.getConfig('name') + '_' + this.getConfig('token') + '_' + this.replayId;
109
108
  this.queueStorage = new IDBStorageWrapper(RECORDING_EVENTS_STORE_NAME);
110
109
  this.batcher = new RequestBatcher(this.batcherKey, {
@@ -50,7 +50,7 @@ var RequestQueue = function (storageKey, options) {
50
50
  };
51
51
 
52
52
  RequestQueue.prototype.ensureInit = function () {
53
- if (this.initialized) {
53
+ if (this.initialized || !this.usePersistence) {
54
54
  return Promise.resolve();
55
55
  }
56
56
 
@@ -61,6 +61,10 @@ IDBStorageWrapper.prototype.init = function () {
61
61
  });
62
62
  };
63
63
 
64
+ IDBStorageWrapper.prototype.isInitialized = function () {
65
+ return !!this.dbPromise;
66
+ };
67
+
64
68
  /**
65
69
  * @param {IDBTransactionMode} mode
66
70
  * @param {function(IDBObjectStore): void} storeCb
@@ -13,6 +13,10 @@ LocalStorageWrapper.prototype.init = function () {
13
13
  return Promise.resolve();
14
14
  };
15
15
 
16
+ LocalStorageWrapper.prototype.isInitialized = function () {
17
+ return true;
18
+ };
19
+
16
20
  LocalStorageWrapper.prototype.setItem = function (key, value) {
17
21
  return new Promise(_.bind(function (resolve, reject) {
18
22
  try {
@@ -6,6 +6,7 @@
6
6
  /**
7
7
  * @typedef {Object} StorageWrapper
8
8
  * @property {function():Promise<void>} init - Initializes the wrapper, async storage like IDB needs to create a table and upgrade if needed.
9
+ * @property {function():boolean} isInitialized - Checks if the storage is initialized.
9
10
  * @property {function(string, any):Promise<void>} setItem - Sets an item in storage.
10
11
  * @property {function(string):Promise<any>} getItem - Retrieves an item from storage.
11
12
  * @property {function(string):Promise<void>} removeItem - Removes an item from storage.