mixpanel-browser 2.74.0 → 2.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.github/workflows/integration-tests.yml +2 -2
  3. package/.github/workflows/unit-tests.yml +3 -3
  4. package/CHANGELOG.md +15 -0
  5. package/README.md +2 -2
  6. package/build.sh +10 -8
  7. package/dist/async-modules/mixpanel-recorder-bIS4LMGd.js +23595 -0
  8. package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js +2 -0
  9. package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js.map +1 -0
  10. package/dist/async-modules/mixpanel-targeting-BcAPS-Mz.js +2520 -0
  11. package/dist/async-modules/mixpanel-targeting-VOeN7RWY.min.js +2 -0
  12. package/dist/async-modules/mixpanel-targeting-VOeN7RWY.min.js.map +1 -0
  13. package/dist/mixpanel-core.cjs.d.ts +68 -0
  14. package/dist/mixpanel-core.cjs.js +802 -337
  15. package/dist/mixpanel-recorder.js +828 -40
  16. package/dist/mixpanel-recorder.min.js +1 -1
  17. package/dist/mixpanel-recorder.min.js.map +1 -1
  18. package/dist/mixpanel-targeting.js +2520 -0
  19. package/dist/mixpanel-targeting.min.js +2 -0
  20. package/dist/mixpanel-targeting.min.js.map +1 -0
  21. package/dist/mixpanel-with-async-modules.cjs.d.ts +590 -0
  22. package/dist/mixpanel-with-async-modules.cjs.js +9867 -0
  23. package/dist/mixpanel-with-async-recorder.cjs.d.ts +68 -0
  24. package/dist/mixpanel-with-async-recorder.cjs.js +802 -337
  25. package/dist/mixpanel-with-recorder.d.ts +68 -0
  26. package/dist/mixpanel-with-recorder.js +1591 -343
  27. package/dist/mixpanel-with-recorder.min.d.ts +68 -0
  28. package/dist/mixpanel-with-recorder.min.js +1 -1
  29. package/dist/mixpanel.amd.d.ts +68 -0
  30. package/dist/mixpanel.amd.js +2124 -345
  31. package/dist/mixpanel.cjs.d.ts +68 -0
  32. package/dist/mixpanel.cjs.js +2124 -345
  33. package/dist/mixpanel.globals.js +802 -337
  34. package/dist/mixpanel.min.js +185 -175
  35. package/dist/mixpanel.module.d.ts +68 -0
  36. package/dist/mixpanel.module.js +2124 -345
  37. package/dist/mixpanel.umd.d.ts +68 -0
  38. package/dist/mixpanel.umd.js +2124 -345
  39. package/dist/rrweb-bundled.js +119 -5
  40. package/dist/rrweb-compiled.js +116 -5
  41. package/logo.svg +5 -0
  42. package/package.json +5 -3
  43. package/rollup.config.mjs +189 -40
  44. package/src/autocapture/index.js +10 -27
  45. package/src/config.js +9 -3
  46. package/src/flags/index.js +269 -9
  47. package/src/index.d.ts +68 -0
  48. package/src/loaders/loader-module.js +1 -0
  49. package/src/mixpanel-core.js +83 -109
  50. package/src/recorder/index.js +2 -1
  51. package/src/recorder/recorder.js +5 -1
  52. package/src/recorder/rrweb-network-plugin.js +649 -0
  53. package/src/recorder/session-recording.js +31 -11
  54. package/src/recorder-manager.js +216 -0
  55. package/src/request-batcher.js +1 -1
  56. package/src/targeting/event-matcher.js +42 -0
  57. package/src/targeting/index.js +11 -0
  58. package/src/targeting/loader.js +36 -0
  59. package/src/utils.js +14 -9
  60. package/testServer.js +55 -0
  61. /package/src/loaders/{loader-module-with-async-recorder.js → loader-module-with-async-modules.js} +0 -0
@@ -26,6 +26,11 @@
26
26
  win = window;
27
27
  }
28
28
 
29
+ var Config = {
30
+ LIB_VERSION: '2.76.0'
31
+ };
32
+ var RECORDER_GLOBAL_NAME = '__mp_recorder';
33
+
29
34
  function _array_like_to_array(arr, len) {
30
35
  if (len == null || len > arr.length) len = arr.length;
31
36
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
@@ -640,14 +645,16 @@
640
645
  return this.nodeMetaMap.get(n2) || null;
641
646
  };
642
647
  // removes the node from idNodeMap
643
- // doesn't remove the node from nodeMetaMap
644
- _proto.removeNodeFromMap = function removeNodeFromMap(n2) {
648
+ // if permanent is true, also removes from nodeMetaMap
649
+ _proto.removeNodeFromMap = function removeNodeFromMap(n2, permanent) {
645
650
  var _this = this;
651
+ if (permanent === void 0) permanent = false;
646
652
  var id = this.getId(n2);
647
653
  this.idNodeMap.delete(id);
654
+ if (permanent) this.nodeMetaMap.delete(n2);
648
655
  if (n2.childNodes) {
649
656
  n2.childNodes.forEach(function(childNode) {
650
- return _this.removeNodeFromMap(childNode);
657
+ return _this.removeNodeFromMap(childNode, permanent);
651
658
  });
652
659
  }
653
660
  };
@@ -10389,6 +10396,15 @@
10389
10396
  _proto.generateId = function generateId() {
10390
10397
  return this.id++;
10391
10398
  };
10399
+ _proto.remove = function remove(stylesheet) {
10400
+ var id = this.styleIDMap.get(stylesheet);
10401
+ if (id !== void 0) {
10402
+ this.styleIDMap.delete(stylesheet);
10403
+ this.idStyleMap.delete(id);
10404
+ return true;
10405
+ }
10406
+ return false;
10407
+ };
10392
10408
  return StyleSheetMirror;
10393
10409
  }();
10394
10410
  function getShadowHost(n2) {
@@ -10711,7 +10727,15 @@
10711
10727
  }
10712
10728
  };
10713
10729
  while(_this.mapRemoves.length){
10714
- _this.mirror.removeNodeFromMap(_this.mapRemoves.shift());
10730
+ var removedNode = _this.mapRemoves.shift();
10731
+ if (removedNode.nodeName === "IFRAME") {
10732
+ try {
10733
+ _this.iframeManager.removeIframe(removedNode);
10734
+ } catch (e2) {}
10735
+ } else {
10736
+ _this.stylesheetManager.cleanupStylesheetsForRemovedNode(removedNode);
10737
+ }
10738
+ _this.mirror.removeNodeFromMap(removedNode);
10715
10739
  }
10716
10740
  for(var _iterator = _create_for_of_iterator_helper_loose(_this.movedSet), _step; !(_step = _iterator()).done;){
10717
10741
  var n2 = _step.value;
@@ -11085,6 +11109,9 @@
11085
11109
  this.shadowDomManager.reset();
11086
11110
  this.canvasManager.reset();
11087
11111
  };
11112
+ _proto.getDoc = function getDoc() {
11113
+ return this.doc;
11114
+ };
11088
11115
  return MutationBuffer;
11089
11116
  }();
11090
11117
  function deepDelete(addsSet, n2) {
@@ -11185,6 +11212,14 @@
11185
11212
  });
11186
11213
  return observer;
11187
11214
  }
11215
+ function removeMutationBufferForDoc(doc) {
11216
+ for(var i2 = mutationBuffers.length - 1; i2 >= 0; i2--){
11217
+ var buffer = mutationBuffers[i2];
11218
+ if (buffer.getDoc() === doc) {
11219
+ mutationBuffers.splice(i2, 1);
11220
+ }
11221
+ }
11222
+ }
11188
11223
  function initMoveObserver(param) {
11189
11224
  var mousemoveCb = param.mousemoveCb, sampling = param.sampling, doc = param.doc, mirror2 = param.mirror;
11190
11225
  if (sampling.mousemove === false) {
@@ -12200,6 +12235,8 @@
12200
12235
  __publicField$1(this, "crossOriginIframeMirror", new CrossOriginIframeMirror(genId));
12201
12236
  __publicField$1(this, "crossOriginIframeStyleMirror");
12202
12237
  __publicField$1(this, "crossOriginIframeRootIdMap", /* @__PURE__ */ new WeakMap());
12238
+ __publicField$1(this, "iframeContentDocumentMap", /* @__PURE__ */ new WeakMap());
12239
+ __publicField$1(this, "iframeObserverCleanupMap", /* @__PURE__ */ new WeakMap());
12203
12240
  __publicField$1(this, "mirror");
12204
12241
  __publicField$1(this, "mutationCb");
12205
12242
  __publicField$1(this, "wrappedEmit");
@@ -12221,6 +12258,31 @@
12221
12258
  this.iframes.set(iframeEl, true);
12222
12259
  if (iframeEl.contentWindow) this.crossOriginIframeMap.set(iframeEl.contentWindow, iframeEl);
12223
12260
  };
12261
+ _proto.getIframeContentDocument = function getIframeContentDocument(iframeEl) {
12262
+ return this.iframeContentDocumentMap.get(iframeEl);
12263
+ };
12264
+ _proto.setObserverCleanup = function setObserverCleanup(iframeEl, cleanup) {
12265
+ this.iframeObserverCleanupMap.set(iframeEl, cleanup);
12266
+ };
12267
+ _proto.getObserverCleanup = function getObserverCleanup(iframeEl) {
12268
+ return this.iframeObserverCleanupMap.get(iframeEl);
12269
+ };
12270
+ _proto.removeIframe = function removeIframe(iframeEl) {
12271
+ var storedDoc = this.iframeContentDocumentMap.get(iframeEl);
12272
+ if (storedDoc) {
12273
+ this.stylesheetManager.cleanupStylesheetsForRemovedNode(storedDoc);
12274
+ this.mirror.removeNodeFromMap(storedDoc, true);
12275
+ }
12276
+ this.iframes.delete(iframeEl);
12277
+ this.iframeContentDocumentMap.delete(iframeEl);
12278
+ var observerCleanup = this.iframeObserverCleanupMap.get(iframeEl);
12279
+ if (observerCleanup) {
12280
+ try {
12281
+ observerCleanup();
12282
+ } catch (e2) {}
12283
+ this.iframeObserverCleanupMap.delete(iframeEl);
12284
+ }
12285
+ };
12224
12286
  _proto.addLoadListener = function addLoadListener(cb) {
12225
12287
  this.loadListener = cb;
12226
12288
  };
@@ -12239,6 +12301,9 @@
12239
12301
  attributes: [],
12240
12302
  isAttachIframe: true
12241
12303
  });
12304
+ if (iframeEl.contentDocument) {
12305
+ this.iframeContentDocumentMap.set(iframeEl, iframeEl.contentDocument);
12306
+ }
12242
12307
  if (this.recordCrossOriginIframes) (_a2 = iframeEl.contentWindow) == null ? void 0 : _a2.addEventListener("message", this.handleMessage.bind(this));
12243
12308
  (_b = this.loadListener) == null ? void 0 : _b.call(this, iframeEl);
12244
12309
  if (iframeEl.contentDocument && iframeEl.contentDocument.adoptedStyleSheets && iframeEl.contentDocument.adoptedStyleSheets.length > 0) this.stylesheetManager.adoptStyleSheets(iframeEl.contentDocument.adoptedStyleSheets, this.mirror.getId(iframeEl.contentDocument));
@@ -13151,6 +13216,41 @@
13151
13216
  this.styleMirror.reset();
13152
13217
  this.trackedLinkElements = /* @__PURE__ */ new WeakSet();
13153
13218
  };
13219
+ /**
13220
+ * Cleans up stylesheets associated with a removed node.
13221
+ *
13222
+ * @param removedNode - The node that was removed from the DOM.
13223
+ */ _proto.cleanupStylesheetsForRemovedNode = function cleanupStylesheetsForRemovedNode(removedNode) {
13224
+ var _this = this;
13225
+ try {
13226
+ if (removedNode.nodeType === Node.DOCUMENT_NODE) {
13227
+ var doc = removedNode;
13228
+ if (doc.adoptedStyleSheets) {
13229
+ for(var _iterator = _create_for_of_iterator_helper_loose(doc.adoptedStyleSheets), _step; !(_step = _iterator()).done;){
13230
+ var sheet = _step.value;
13231
+ this.styleMirror.remove(sheet);
13232
+ }
13233
+ }
13234
+ }
13235
+ if (removedNode.nodeName === "STYLE") {
13236
+ var styleEl = removedNode;
13237
+ if (styleEl.sheet) {
13238
+ this.styleMirror.remove(styleEl.sheet);
13239
+ }
13240
+ }
13241
+ if (removedNode.nodeName === "LINK" && removedNode.rel === "stylesheet") {
13242
+ var linkEl = removedNode;
13243
+ if (linkEl.sheet) {
13244
+ this.styleMirror.remove(linkEl.sheet);
13245
+ }
13246
+ }
13247
+ if (removedNode.childNodes) {
13248
+ removedNode.childNodes.forEach(function(child) {
13249
+ _this.cleanupStylesheetsForRemovedNode(child);
13250
+ });
13251
+ }
13252
+ } catch (e2) {}
13253
+ };
13154
13254
  // TODO: take snapshot on stylesheet reload by applying event listener
13155
13255
  _proto.trackStylesheetInLinkElement = function trackStylesheetInLinkElement(_linkEl) {};
13156
13256
  return StylesheetManager;
@@ -13605,7 +13705,23 @@
13605
13705
  };
13606
13706
  iframeManager.addLoadListener(function(iframeEl) {
13607
13707
  try {
13608
- handlers.push(observe(iframeEl.contentDocument));
13708
+ var iframeDoc = iframeEl.contentDocument;
13709
+ var iframeHandler = observe(iframeDoc);
13710
+ handlers.push(iframeHandler);
13711
+ var existingCleanup = iframeManager.getObserverCleanup(iframeEl);
13712
+ iframeManager.setObserverCleanup(iframeEl, function() {
13713
+ if (existingCleanup) {
13714
+ try {
13715
+ existingCleanup();
13716
+ } catch (e2) {}
13717
+ }
13718
+ try {
13719
+ iframeHandler();
13720
+ var idx = handlers.indexOf(iframeHandler);
13721
+ if (idx !== -1) handlers.splice(idx, 1);
13722
+ removeMutationBufferForDoc(iframeDoc);
13723
+ } catch (e2) {}
13724
+ });
13609
13725
  } catch (error) {
13610
13726
  console.warn(error);
13611
13727
  }
@@ -18014,7 +18130,7 @@
18014
18130
  var __publicField = function(obj, key, value) {
18015
18131
  return __defNormalProp(obj, (typeof key === "undefined" ? "undefined" : _type_of(key)) !== "symbol" ? key + "" : key, value);
18016
18132
  };
18017
- function patch(source, name, replacement) {
18133
+ function patch$3(source, name, replacement) {
18018
18134
  try {
18019
18135
  if (!(name in source)) {
18020
18136
  return function() {};
@@ -18431,7 +18547,7 @@
18431
18547
  if (!_logger[level]) {
18432
18548
  return function() {};
18433
18549
  }
18434
- return patch(_logger, level, function(original) {
18550
+ return patch$3(_logger, level, function(original) {
18435
18551
  var _this1 = _this;
18436
18552
  return function() {
18437
18553
  for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
@@ -18852,10 +18968,6 @@
18852
18968
  PromisePolyfill = NpoPromise;
18853
18969
  }
18854
18970
 
18855
- var Config = {
18856
- LIB_VERSION: '2.74.0'
18857
- };
18858
-
18859
18971
  /* eslint camelcase: "off", eqeqeq: "off" */
18860
18972
 
18861
18973
  // Maximum allowed session recording length
@@ -19007,15 +19119,8 @@
19007
19119
  return toString.call(obj) === '[object Array]';
19008
19120
  };
19009
19121
 
19010
- // from a comment on http://dbj.org/dbj/?p=286
19011
- // fails on only one very rare and deliberate custom object:
19012
- // var bomb = { toString : undefined, valueOf: function(o) { return "function BOMBA!"; }};
19013
19122
  _.isFunction = function(f) {
19014
- try {
19015
- return /^\s*\bfunction\b/.test(f);
19016
- } catch (x) {
19017
- return false;
19018
- }
19123
+ return typeof f === 'function';
19019
19124
  };
19020
19125
 
19021
19126
  _.isArguments = function(obj) {
@@ -20528,6 +20633,17 @@
20528
20633
 
20529
20634
  var NOOP_FUNC = function () {};
20530
20635
 
20636
+ var urlMatchesRegexList = function (url, regexList) {
20637
+ var matches = false;
20638
+ for (var i = 0; i < regexList.length; i++) {
20639
+ if (url.match(regexList[i])) {
20640
+ matches = true;
20641
+ break;
20642
+ }
20643
+ }
20644
+ return matches;
20645
+ };
20646
+
20531
20647
  var JSONStringify = null, JSONParse = null;
20532
20648
  if (typeof JSON !== 'undefined') {
20533
20649
  JSONStringify = JSON.stringify;
@@ -20867,7 +20983,7 @@
20867
20983
  };
20868
20984
  }
20869
20985
 
20870
- var logger$4 = console_with_prefix('lock');
20986
+ var logger$5 = console_with_prefix('lock');
20871
20987
 
20872
20988
  /**
20873
20989
  * SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
@@ -20919,7 +21035,7 @@
20919
21035
 
20920
21036
  var delay = function(cb) {
20921
21037
  if (new Date().getTime() - startTime > timeoutMS) {
20922
- logger$4.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
21038
+ logger$5.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
20923
21039
  storage.removeItem(keyZ);
20924
21040
  storage.removeItem(keyY);
20925
21041
  loop();
@@ -21066,7 +21182,7 @@
21066
21182
  }, this));
21067
21183
  };
21068
21184
 
21069
- var logger$3 = console_with_prefix('batch');
21185
+ var logger$4 = console_with_prefix('batch');
21070
21186
 
21071
21187
  /**
21072
21188
  * RequestQueue: queue for batching API requests with localStorage backup for retries.
@@ -21095,7 +21211,7 @@
21095
21211
  timeoutMS: options.sharedLockTimeoutMS,
21096
21212
  });
21097
21213
  }
21098
- this.reportError = options.errorReporter || _.bind(logger$3.error, logger$3);
21214
+ this.reportError = options.errorReporter || _.bind(logger$4.error, logger$4);
21099
21215
 
21100
21216
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
21101
21217
 
@@ -21428,7 +21544,7 @@
21428
21544
  // maximum interval between request retries after exponential backoff
21429
21545
  var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
21430
21546
 
21431
- var logger$2 = console_with_prefix('batch');
21547
+ var logger$3 = console_with_prefix('batch');
21432
21548
 
21433
21549
  /**
21434
21550
  * RequestBatcher: manages the queueing, flushing, retry etc of requests of one
@@ -21556,7 +21672,7 @@
21556
21672
  */
21557
21673
  RequestBatcher.prototype.flush = function(options) {
21558
21674
  if (this.requestInProgress) {
21559
- logger$2.log('Flush: Request already in progress');
21675
+ logger$3.log('Flush: Request already in progress');
21560
21676
  return PromisePolyfill.resolve();
21561
21677
  }
21562
21678
 
@@ -21733,7 +21849,7 @@
21733
21849
  if (options.unloading) {
21734
21850
  requestOptions.transport = 'sendBeacon';
21735
21851
  }
21736
- logger$2.log('MIXPANEL REQUEST:', dataForRequest);
21852
+ logger$3.log('MIXPANEL REQUEST:', dataForRequest);
21737
21853
  return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
21738
21854
  }, this))
21739
21855
  .catch(_.bind(function(err) {
@@ -21746,7 +21862,7 @@
21746
21862
  * Log error to global logger and optional user-defined logger.
21747
21863
  */
21748
21864
  RequestBatcher.prototype.reportError = function(msg, err) {
21749
- logger$2.error.apply(logger$2.error, arguments);
21865
+ logger$3.error.apply(logger$3.error, arguments);
21750
21866
  if (this.errorReporter) {
21751
21867
  try {
21752
21868
  if (!(err instanceof Error)) {
@@ -21754,7 +21870,7 @@
21754
21870
  }
21755
21871
  this.errorReporter(msg, err);
21756
21872
  } catch(err) {
21757
- logger$2.error(err);
21873
+ logger$3.error(err);
21758
21874
  }
21759
21875
  }
21760
21876
  };
@@ -21972,6 +22088,655 @@
21972
22088
  }
21973
22089
  }
21974
22090
 
22091
+ /**
22092
+ * This is a port of the open rrweb network plugin in this PR https://github.com/rrweb-io/rrweb/pull/1105
22093
+ * the hope is that eventually this can be replaced with the official plugin once it's published (and we sync the mixpanel rrweb fork)
22094
+ *
22095
+ * This plugin incorporates some important fixes for fetch/XHR body recording that are not yet in the main rrweb repo, as well as makes
22096
+ * header and body recording more restrictive by requiring an allowlist instead of content type / blocklist.
22097
+ *
22098
+ */
22099
+
22100
+ var logger$2 = console_with_prefix('network-plugin');
22101
+
22102
+ /**
22103
+ * Get the time origin for converting performance timestamps to absolute timestamps.
22104
+ * Uses Date.now() - performance.now() instead of performance.timeOrigin because
22105
+ * browsers can report timeOrigin values that are skewed from actual time, and some
22106
+ * browsers (notably older Safari versions) don't implement timeOrigin at all.
22107
+ * See: https://github.com/getsentry/sentry-javascript/blob/e856e40b6e71a73252e788cd42b5260f81c9c88e/packages/utils/src/time.ts#L49-L70
22108
+ * @param {Window} win
22109
+ * @returns {number}
22110
+ */
22111
+ function getTimeOrigin(win) {
22112
+ return Math.round(Date.now() - win.performance.now());
22113
+ }
22114
+
22115
+ /**
22116
+ * @typedef {import('../index.d.ts').InitiatorType} InitiatorType
22117
+ * @typedef {import('../index.d.ts').NetworkRequest} NetworkRequest
22118
+ * @typedef {import('../index.d.ts').NetworkRecordOptions} NetworkRecordOptions
22119
+ * @typedef {import('../index.d.ts').NetworkData} NetworkData
22120
+ */
22121
+
22122
+ /**
22123
+ * @typedef {Record<string, string>} Headers
22124
+ */
22125
+
22126
+ /**
22127
+ * @typedef {string | Document | Blob | ArrayBufferView | ArrayBuffer | FormData | URLSearchParams | ReadableStream<Uint8Array> | null} Body
22128
+ */
22129
+
22130
+ /**
22131
+ * @callback networkCallback
22132
+ * @param {NetworkData} data
22133
+ * @returns {void}
22134
+ */
22135
+
22136
+ /**
22137
+ * @callback listenerHandler
22138
+ * @returns {void}
22139
+ */
22140
+
22141
+ /**
22142
+ * @typedef {(PerformanceNavigationTiming | PerformanceResourceTiming) & { responseStatus?: number }} ObservedPerformanceEntry
22143
+ */
22144
+
22145
+ /**
22146
+ * @typedef {Object} RecordPlugin
22147
+ * @property {string} name
22148
+ * @property {(callback: networkCallback, win: Window, options: NetworkRecordOptions) => listenerHandler} observer
22149
+ * @property {NetworkRecordOptions} [options]
22150
+ */
22151
+
22152
+ /** @type {Required<NetworkRecordOptions>} */
22153
+ var defaultNetworkOptions = {
22154
+ initiatorTypes: [
22155
+ 'audio',
22156
+ 'beacon',
22157
+ 'body',
22158
+ 'css',
22159
+ 'early-hint',
22160
+ 'embed',
22161
+ 'fetch',
22162
+ 'frame',
22163
+ 'iframe',
22164
+ 'icon',
22165
+ 'image',
22166
+ 'img',
22167
+ 'input',
22168
+ 'link',
22169
+ 'navigation',
22170
+ 'object',
22171
+ 'ping',
22172
+ 'script',
22173
+ 'track',
22174
+ 'video',
22175
+ 'xmlhttprequest',
22176
+ ],
22177
+ ignoreRequestFn: function() { return false; },
22178
+ recordHeaders: {
22179
+ request: [],
22180
+ response: [],
22181
+ },
22182
+ recordBodyUrls: {
22183
+ request: [],
22184
+ response: [],
22185
+ },
22186
+ recordInitialRequests: false,
22187
+ };
22188
+
22189
+ /**
22190
+ * @param {PerformanceEntry} entry
22191
+ * @returns {entry is PerformanceNavigationTiming}
22192
+ */
22193
+ function isNavigationTiming(entry) {
22194
+ return entry.entryType === 'navigation';
22195
+ }
22196
+
22197
+ /**
22198
+ * @param {PerformanceEntry} entry
22199
+ * @returns {entry is PerformanceResourceTiming}
22200
+ */
22201
+ function isResourceTiming (entry) {
22202
+ return entry.entryType === 'resource';
22203
+ }
22204
+
22205
+ function findLast(array, predicate) {
22206
+ var length = array.length;
22207
+ for (var i = length - 1; i >= 0; i -= 1) {
22208
+ if (predicate(array[i])) {
22209
+ return array[i];
22210
+ }
22211
+ }
22212
+ }
22213
+
22214
+ /**
22215
+ * Monkey-patches a method on an object with a wrapped version, returning a function that restores the original.
22216
+ * Adapted from Sentry's `fill` utility:
22217
+ * https://github.com/getsentry/sentry-javascript/blob/de5c5cbe177b4334386e747857225eec36a91ea1/packages/core/src/utils/object.ts#L67-L95
22218
+ *
22219
+ * @param {object} source - The object containing the method to patch
22220
+ * @param {string} name - The method name to patch
22221
+ * @param {function} replacementFactory - A function that receives the original method and returns the replacement
22222
+ * @returns {function} A function that restores the original method
22223
+ */
22224
+ function patch(source, name, replacementFactory) {
22225
+ if (!(name in source) || typeof source[name] !== 'function') {
22226
+ return function() {};
22227
+ }
22228
+ var original = source[name];
22229
+ var wrapped = replacementFactory(original);
22230
+ source[name] = wrapped;
22231
+ return function() {
22232
+ source[name] = original;
22233
+ };
22234
+ }
22235
+
22236
+
22237
+ /**
22238
+ * Maximum body size to record (1MB)
22239
+ */
22240
+ var MAX_BODY_SIZE = 1024 * 1024;
22241
+
22242
+ /**
22243
+ * Truncate string if it exceeds max size
22244
+ * @param {string} str
22245
+ * @returns {string}
22246
+ */
22247
+ function truncateBody(str) {
22248
+ if (!str || typeof str !== 'string') {
22249
+ return str;
22250
+ }
22251
+ if (str.length > MAX_BODY_SIZE) {
22252
+ logger$2.error('Body truncated from ' + str.length + ' to ' + MAX_BODY_SIZE + ' characters');
22253
+ return str.substring(0, MAX_BODY_SIZE) + '... [truncated]';
22254
+ }
22255
+ return str;
22256
+ }
22257
+
22258
+ /**
22259
+ * @param {networkCallback} cb
22260
+ * @param {Window} win
22261
+ * @param {Required<NetworkRecordOptions>} options
22262
+ * @returns {listenerHandler}
22263
+ */
22264
+ function initPerformanceObserver(cb, win, options) {
22265
+ if (!win.PerformanceObserver) {
22266
+ logger$2.error('PerformanceObserver not supported');
22267
+ return function() {
22268
+ //
22269
+ };
22270
+ }
22271
+ if (options.recordInitialRequests) {
22272
+ var initialPerformanceEntries = win.performance
22273
+ .getEntries()
22274
+ .filter(function(entry) {
22275
+ return isNavigationTiming(entry) ||
22276
+ (isResourceTiming(entry) &&
22277
+ options.initiatorTypes.includes(entry.initiatorType));
22278
+ });
22279
+ cb({
22280
+ requests: initialPerformanceEntries.map(function(entry) {
22281
+ return {
22282
+ url: entry.name,
22283
+ initiatorType: /** @type {InitiatorType} */ (entry.initiatorType),
22284
+ status: 'responseStatus' in entry ? entry.responseStatus : undefined,
22285
+ startTime: Math.round(entry.startTime),
22286
+ endTime: Math.round(entry.responseEnd),
22287
+ timeOrigin: getTimeOrigin(win),
22288
+ };
22289
+ }),
22290
+ isInitial: true,
22291
+ });
22292
+ }
22293
+ var observer = new win.PerformanceObserver(function(entries) {
22294
+ var performanceEntries = entries
22295
+ .getEntries()
22296
+ .filter(function(entry) {
22297
+ return isResourceTiming(entry) &&
22298
+ options.initiatorTypes.includes(entry.initiatorType) &&
22299
+ entry.initiatorType !== 'xmlhttprequest' &&
22300
+ entry.initiatorType !== 'fetch';
22301
+ });
22302
+ cb({
22303
+ requests: performanceEntries.map(function(entry) {
22304
+ return {
22305
+ url: entry.name,
22306
+ initiatorType: /** @type {InitiatorType} */ (entry.initiatorType),
22307
+ status: 'responseStatus' in entry ? entry.responseStatus : undefined,
22308
+ startTime: Math.round(entry.startTime),
22309
+ endTime: Math.round(entry.responseEnd),
22310
+ timeOrigin: getTimeOrigin(win),
22311
+ };
22312
+ }),
22313
+ });
22314
+ });
22315
+ observer.observe({ entryTypes: ['navigation', 'resource'] });
22316
+ return function() {
22317
+ observer.disconnect();
22318
+ };
22319
+ }
22320
+
22321
+ /**
22322
+ * Variation of the original rrweb function that requires an allowlist for headers instead of supporting boolean options
22323
+ * @param {'request' | 'response'} type
22324
+ * @param {NetworkRecordOptions['recordHeaders']} recordHeaders
22325
+ * @param {string} headerName
22326
+ * @returns {boolean}
22327
+ */
22328
+ function shouldRecordHeader(type, recordHeaders, headerName) {
22329
+ if (!recordHeaders[type] || recordHeaders[type].length === 0) {
22330
+ return false;
22331
+ }
22332
+
22333
+ return recordHeaders[type].includes(headerName.toLowerCase());
22334
+ }
22335
+
22336
+ /**
22337
+ * Variation of the original rrweb function that requires an allowlist for URLs instead of supporting boolean options or by content type
22338
+ * @param {'request' | 'response'} type
22339
+ * @param {NetworkRecordOptions['recordBodyUrls']} recordBodyUrls
22340
+ * @param {string} url
22341
+ * @returns {boolean}
22342
+ */
22343
+ function shouldRecordBody(type, recordBodyUrls, url) {
22344
+ if (!recordBodyUrls[type] || recordBodyUrls[type].length === 0) {
22345
+ return false;
22346
+ }
22347
+
22348
+ return urlMatchesRegexList(url, recordBodyUrls[type]);
22349
+ }
22350
+
22351
+ function tryReadXHRBody(body) {
22352
+ if (body === null || body === undefined) {
22353
+ return null;
22354
+ }
22355
+
22356
+ var result;
22357
+ if (typeof body === 'string') {
22358
+ result = body;
22359
+ } else if (body instanceof Document) {
22360
+ result = body.textContent;
22361
+ } else if (body instanceof FormData) {
22362
+ result = _.HTTPBuildQuery(body);
22363
+ } else if (_.isObject(body)) {
22364
+ try {
22365
+ result = JSON.stringify(body);
22366
+ } catch (e) {
22367
+ return 'Failed to stringify response object';
22368
+ }
22369
+ } else {
22370
+ return 'Cannot read body of type ' + typeof body;
22371
+ }
22372
+
22373
+ return truncateBody(result);
22374
+ }
22375
+
22376
+ /**
22377
+ * @param {Request | Response} r
22378
+ * @returns {Promise<string>}
22379
+ */
22380
+ function tryReadFetchBody(r) {
22381
+ return new Promise(function(resolve) {
22382
+ var timeout = setTimeout(function() {
22383
+ resolve('Timeout while trying to read body');
22384
+ }, 500);
22385
+ try {
22386
+ r.clone()
22387
+ .text()
22388
+ .then(
22389
+ function(txt) {
22390
+ clearTimeout(timeout);
22391
+ resolve(truncateBody(txt));
22392
+ },
22393
+ function(reason) {
22394
+ clearTimeout(timeout);
22395
+ resolve('Failed to read body: ' + String(reason));
22396
+ }
22397
+ );
22398
+ } catch (e) {
22399
+ clearTimeout(timeout);
22400
+ resolve('Failed to read body: ' + String(e));
22401
+ }
22402
+ });
22403
+ }
22404
+
22405
+ /**
22406
+ * @param {Window} win
22407
+ * @param {string} initiatorType
22408
+ * @param {string} url
22409
+ * @param {number} [after]
22410
+ * @param {number} [before]
22411
+ * @param {number} [attempt]
22412
+ * @returns {Promise<PerformanceResourceTiming>}
22413
+ */
22414
+ function getRequestPerformanceEntry(win, initiatorType, url, after, before, attempt) {
22415
+ if (attempt === undefined) {
22416
+ attempt = 0;
22417
+ }
22418
+ if (attempt > 10) {
22419
+ logger$2.error('Cannot find performance entry');
22420
+ return Promise.resolve(null);
22421
+ }
22422
+ var urlPerformanceEntries = /** @type {PerformanceResourceTiming[]} */ (
22423
+ win.performance.getEntriesByName(url)
22424
+ );
22425
+ var performanceEntry = findLast(
22426
+ urlPerformanceEntries,
22427
+ function(entry) {
22428
+ return isResourceTiming(entry) &&
22429
+ entry.initiatorType === initiatorType &&
22430
+ (!after || entry.startTime >= after) &&
22431
+ (!before || entry.startTime <= before);
22432
+ }
22433
+ );
22434
+ if (!performanceEntry) {
22435
+ return new Promise(function(resolve) {
22436
+ setTimeout(resolve, 50 * attempt);
22437
+ }).then(function() {
22438
+ return getRequestPerformanceEntry(
22439
+ win,
22440
+ initiatorType,
22441
+ url,
22442
+ after,
22443
+ before,
22444
+ attempt + 1
22445
+ );
22446
+ });
22447
+ }
22448
+ return Promise.resolve(performanceEntry);
22449
+ }
22450
+
22451
+ /**
22452
+ * @param {networkCallback} cb
22453
+ * @param {Window} win
22454
+ * @param {Required<NetworkRecordOptions>} options
22455
+ * @returns {listenerHandler}
22456
+ */
22457
+ function initXhrObserver(cb, win, options) {
22458
+ if (!options.initiatorTypes.includes('xmlhttprequest')) {
22459
+ return function() {
22460
+ //
22461
+ };
22462
+ }
22463
+ var restorePatch = patch(
22464
+ win.XMLHttpRequest.prototype,
22465
+ 'open',
22466
+ function(/** @type {typeof XMLHttpRequest.prototype.open} */ originalOpen) {
22467
+ return function(
22468
+ /** @type {string} */ method,
22469
+ /** @type {string | URL} */ url,
22470
+ /** @type {boolean} */ async,
22471
+ username, password
22472
+ ) {
22473
+ if (async === undefined) {
22474
+ async = true;
22475
+ }
22476
+ var xhr = /** @type {XMLHttpRequest} */ (this);
22477
+ var req = new Request(url, { method: method });
22478
+ /** @type {Partial<NetworkRequest>} */
22479
+ var networkRequest = {};
22480
+ /** @type {number | undefined} */
22481
+ var after;
22482
+ /** @type {number | undefined} */
22483
+ var before;
22484
+
22485
+ /** @type {Headers} */
22486
+ var requestHeaders = {};
22487
+ var originalSetRequestHeader = xhr.setRequestHeader.bind(xhr);
22488
+ xhr.setRequestHeader = function(/** @type {string} */ header, /** @type {string} */ value) {
22489
+ if (shouldRecordHeader('request', options.recordHeaders, header)) {
22490
+ requestHeaders[header] = value;
22491
+ }
22492
+ return originalSetRequestHeader(header, value);
22493
+ };
22494
+ networkRequest.requestHeaders = requestHeaders;
22495
+
22496
+ var originalSend = xhr.send.bind(xhr);
22497
+ xhr.send = function(/** @type {Body} */ body) {
22498
+ if (shouldRecordBody('request', options.recordBodyUrls, req.url)) {
22499
+ networkRequest.requestBody = tryReadXHRBody(body);
22500
+ }
22501
+ after = win.performance.now();
22502
+ return originalSend(body);
22503
+ };
22504
+ xhr.addEventListener('readystatechange', function() {
22505
+ if (xhr.readyState !== xhr.DONE) {
22506
+ return;
22507
+ }
22508
+ before = win.performance.now();
22509
+ /** @type {Headers} */
22510
+ var responseHeaders = {};
22511
+ var rawHeaders = xhr.getAllResponseHeaders();
22512
+ if (rawHeaders) {
22513
+ var headers = rawHeaders.trim().split(/[\r\n]+/);
22514
+ headers.forEach(function(line) {
22515
+ if (!line) return;
22516
+ var colonIndex = line.indexOf(': ');
22517
+ if (colonIndex === -1) return;
22518
+ var header = line.substring(0, colonIndex);
22519
+ var value = line.substring(colonIndex + 2);
22520
+ if (header && shouldRecordHeader('response', options.recordHeaders, header)) {
22521
+ responseHeaders[header] = value;
22522
+ }
22523
+ });
22524
+ }
22525
+ networkRequest.responseHeaders = responseHeaders;
22526
+ if (
22527
+ shouldRecordBody('response', options.recordBodyUrls, req.url)
22528
+ ) {
22529
+ networkRequest.responseBody = tryReadXHRBody(xhr.response);
22530
+ }
22531
+ getRequestPerformanceEntry(
22532
+ win,
22533
+ 'xmlhttprequest',
22534
+ req.url,
22535
+ after,
22536
+ before
22537
+ )
22538
+ .then(function(entry) {
22539
+ if (!entry) {
22540
+ logger$2.error('Failed to get performance entry for XHR request to ' + req.url);
22541
+ return;
22542
+ }
22543
+ /** @type {NetworkRequest} */
22544
+ var request = {
22545
+ url: entry.name,
22546
+ method: req.method,
22547
+ initiatorType: /** @type {InitiatorType} */ (entry.initiatorType),
22548
+ status: xhr.status,
22549
+ startTime: Math.round(entry.startTime),
22550
+ endTime: Math.round(entry.responseEnd),
22551
+ timeOrigin: getTimeOrigin(win),
22552
+ requestHeaders: networkRequest.requestHeaders,
22553
+ requestBody: networkRequest.requestBody,
22554
+ responseHeaders: networkRequest.responseHeaders,
22555
+ responseBody: networkRequest.responseBody,
22556
+ };
22557
+ cb({ requests: [request] });
22558
+ })
22559
+ .catch(function(e) {
22560
+ logger$2.error('Error recording XHR request to ' + req.url + ': ' + String(e));
22561
+ });
22562
+ });
22563
+
22564
+ originalOpen.call(xhr, method, url, async, username, password);
22565
+ };
22566
+ }
22567
+ );
22568
+ return function() {
22569
+ restorePatch();
22570
+ };
22571
+ }
22572
+
22573
+ /**
22574
+ * @param {networkCallback} cb
22575
+ * @param {Window} win
22576
+ * @param {Required<NetworkRecordOptions>} options
22577
+ * @returns {listenerHandler}
22578
+ */
22579
+ function initFetchObserver(cb, win, options) {
22580
+ if (!options.initiatorTypes.includes('fetch')) {
22581
+ return function() {
22582
+ //
22583
+ };
22584
+ }
22585
+
22586
+ var restorePatch = patch(win, 'fetch', function(/** @type {typeof fetch} */ originalFetch) {
22587
+ return function() {
22588
+ var req = new Request(arguments[0], arguments[1]);
22589
+ /** @type {Response | undefined} */
22590
+ var res;
22591
+ /** @type {Partial<NetworkRequest>} */
22592
+ var networkRequest = {};
22593
+ /** @type {number | undefined} */
22594
+ var after;
22595
+ /** @type {number | undefined} */
22596
+ var before;
22597
+
22598
+ var originalFetchPromise;
22599
+ var requestBodyPromise = Promise.resolve(undefined);
22600
+ var responseBodyPromise = Promise.resolve(undefined);
22601
+ try {
22602
+ /** @type {Headers} */
22603
+ var requestHeaders = {};
22604
+ req.headers.forEach(function(value, header) {
22605
+ if (shouldRecordHeader('request', options.recordHeaders, header)) {
22606
+ requestHeaders[header] = value;
22607
+ }
22608
+ });
22609
+ networkRequest.requestHeaders = requestHeaders;
22610
+
22611
+ if (shouldRecordBody('request', options.recordBodyUrls, req.url)) {
22612
+ requestBodyPromise = tryReadFetchBody(req)
22613
+ .then(function(body) {
22614
+ networkRequest.requestBody = body;
22615
+ });
22616
+ }
22617
+
22618
+ after = win.performance.now();
22619
+ originalFetchPromise = originalFetch.apply(win, arguments).then(function(response) {
22620
+ res = response;
22621
+ before = win.performance.now();
22622
+
22623
+ /** @type {Headers} */
22624
+ var responseHeaders = {};
22625
+ res.headers.forEach(function(value, header) {
22626
+ if (shouldRecordHeader('response', options.recordHeaders, header)) {
22627
+ responseHeaders[header] = value;
22628
+ }
22629
+ });
22630
+ networkRequest.responseHeaders = responseHeaders;
22631
+
22632
+ if (shouldRecordBody('response', options.recordBodyUrls, req.url)) {
22633
+ responseBodyPromise = tryReadFetchBody(res)
22634
+ .then(function(body) {
22635
+ networkRequest.responseBody = body;
22636
+ });
22637
+ }
22638
+
22639
+ return res;
22640
+ });
22641
+ } catch (e) {
22642
+ originalFetchPromise = Promise.reject(e);
22643
+ }
22644
+
22645
+ // await concurrently so we don't delay the fetch response
22646
+ Promise.all([requestBodyPromise, responseBodyPromise, originalFetchPromise])
22647
+ .then(function () {
22648
+ return getRequestPerformanceEntry(win, 'fetch', req.url, after, before);
22649
+ })
22650
+ .then(function(entry) {
22651
+ if (!entry) {
22652
+ logger$2.error('Failed to get performance entry for fetch request to ' + req.url);
22653
+ return;
22654
+ }
22655
+ /** @type {NetworkRequest} */
22656
+ var request = {
22657
+ url: entry.name,
22658
+ method: req.method,
22659
+ initiatorType: /** @type {InitiatorType} */ (entry.initiatorType),
22660
+ status: res ? res.status : undefined,
22661
+ startTime: Math.round(entry.startTime),
22662
+ endTime: Math.round(entry.responseEnd),
22663
+ timeOrigin: getTimeOrigin(win),
22664
+ requestHeaders: networkRequest.requestHeaders,
22665
+ requestBody: networkRequest.requestBody,
22666
+ responseHeaders: networkRequest.responseHeaders,
22667
+ responseBody: networkRequest.responseBody,
22668
+ };
22669
+ cb({ requests: [request] });
22670
+ })
22671
+ .catch(function (e) {
22672
+ logger$2.error('Error recording fetch request to ' + req.url + ': ' + String(e));
22673
+ });
22674
+
22675
+ return originalFetchPromise;
22676
+ };
22677
+ });
22678
+ return function() {
22679
+ restorePatch();
22680
+ };
22681
+ }
22682
+
22683
+ /**
22684
+ * @param {networkCallback} callback
22685
+ * @param {Window} win
22686
+ * @param {NetworkRecordOptions} options
22687
+ * @returns {listenerHandler}
22688
+ */
22689
+ function initNetworkObserver(callback, win, options) {
22690
+ if (!('performance' in win)) {
22691
+ return function() {
22692
+ //
22693
+ };
22694
+ }
22695
+
22696
+ var recordHeaders = Object.assign({}, defaultNetworkOptions.recordHeaders, options.recordHeaders || {});
22697
+ var recordBodyUrls = Object.assign({}, defaultNetworkOptions.recordBodyUrls, options.recordBodyUrls || {});
22698
+ options = Object.assign({}, options, {
22699
+ recordHeaders: recordHeaders,
22700
+ recordBodyUrls: recordBodyUrls,
22701
+ });
22702
+ var networkOptions = /** @type {Required<NetworkRecordOptions>} */ Object.assign({}, defaultNetworkOptions, options);
22703
+
22704
+ /** @type {networkCallback} */
22705
+ var cb = function(data) {
22706
+ var requests = data.requests.filter(function(request) {
22707
+ var shouldIgnoreUrl = urlMatchesRegexList(request.url, networkOptions.ignoreRequestUrls || []);
22708
+ return !shouldIgnoreUrl && !networkOptions.ignoreRequestFn(request);
22709
+ });
22710
+ if (requests.length > 0 || data.isInitial) {
22711
+ callback(Object.assign({}, data, { requests: requests }));
22712
+ }
22713
+ };
22714
+ var performanceObserver = initPerformanceObserver(cb, win, networkOptions);
22715
+ var xhrObserver = initXhrObserver(cb, win, networkOptions);
22716
+ var fetchObserver = initFetchObserver(cb, win, networkOptions);
22717
+ return function() {
22718
+ performanceObserver();
22719
+ xhrObserver();
22720
+ fetchObserver();
22721
+ };
22722
+ }
22723
+
22724
+ // arbitrary .mp suffix in case rrweb does publish this plugin later and we use it but need to handle
22725
+ // a changed format in the mixpanel product.
22726
+ var NETWORK_PLUGIN_NAME = 'rrweb/network@1.mp';
22727
+
22728
+ /**
22729
+ * @param {NetworkRecordOptions} [options]
22730
+ * @returns {RecordPlugin}
22731
+ */
22732
+ var getRecordNetworkPlugin = function(options) {
22733
+ return {
22734
+ name: NETWORK_PLUGIN_NAME,
22735
+ observer: initNetworkObserver,
22736
+ options: options,
22737
+ };
22738
+ };
22739
+
21975
22740
  /**
21976
22741
  * @typedef {import('../index').RecordPrivacyConfig} RecordPrivacyConfig
21977
22742
  */
@@ -22205,6 +22970,29 @@
22205
22970
 
22206
22971
  var privacyConfig = getPrivacyConfig(this._mixpanel);
22207
22972
 
22973
+ var plugins = [];
22974
+ if (this.getConfig('record_network')) {
22975
+ var options = this.getConfig('record_network_options') || {};
22976
+ // don't track requests to Mixpanel /record API
22977
+ var ignoreRequestUrls = (options.ignoreRequestUrls || []).slice();
22978
+ ignoreRequestUrls.push(this._getApiRoute());
22979
+ options.ignoreRequestUrls = ignoreRequestUrls;
22980
+
22981
+ plugins.push(getRecordNetworkPlugin(options));
22982
+ }
22983
+
22984
+ if (this.getConfig('record_console')) {
22985
+ plugins.push(
22986
+ getRecordConsolePlugin({
22987
+ stringifyOptions: {
22988
+ stringLengthLimit: 1000,
22989
+ numOfKeysLimit: 50,
22990
+ depthOfLimit: 2
22991
+ }
22992
+ })
22993
+ );
22994
+ }
22995
+
22208
22996
  try {
22209
22997
  this._stopRecording = this._rrwebRecord({
22210
22998
  'emit': function (ev) {
@@ -22243,15 +23031,7 @@
22243
23031
  'sampling': {
22244
23032
  'canvas': 15
22245
23033
  },
22246
- 'plugins': this.getConfig('record_console') ? [
22247
- getRecordConsolePlugin({
22248
- stringifyOptions: {
22249
- stringLengthLimit: 1000,
22250
- numOfKeysLimit: 50,
22251
- depthOfLimit: 2
22252
- }
22253
- })
22254
- ] : []
23034
+ 'plugins': plugins,
22255
23035
  });
22256
23036
  } catch (err) {
22257
23037
  this.reportError('Unexpected error when starting rrweb recording.', err);
@@ -22366,6 +23146,10 @@
22366
23146
  return recording;
22367
23147
  };
22368
23148
 
23149
+ SessionRecording.prototype._getApiRoute = function () {
23150
+ return this.getConfig('api_routes')['record'];
23151
+ };
23152
+
22369
23153
  SessionRecording.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
22370
23154
  var onSuccess = function (response, responseBody) {
22371
23155
  // Update batch specific props only if the request was successful to guarantee ordering.
@@ -22385,7 +23169,7 @@
22385
23169
  });
22386
23170
  }.bind(this);
22387
23171
  var apiHost = (this._mixpanel.get_api_host && this._mixpanel.get_api_host('record')) || this.getConfig('api_host');
22388
- win['fetch'](apiHost + '/' + this.getConfig('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
23172
+ win['fetch'](apiHost + '/' + this._getApiRoute() + '?' + new URLSearchParams(reqParams), {
22389
23173
  'method': 'POST',
22390
23174
  'headers': {
22391
23175
  'Authorization': 'Basic ' + btoa(this.getConfig('token') + ':'),
@@ -22786,8 +23570,12 @@
22786
23570
  this.startRecording({shouldStopBatcher: true});
22787
23571
  };
22788
23572
 
23573
+ MixpanelRecorder.prototype.isRecording = function () {
23574
+ return this.activeRecording && !this.activeRecording.isRrwebStopped();
23575
+ };
23576
+
22789
23577
  MixpanelRecorder.prototype.getActiveReplayId = function () {
22790
- if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
23578
+ if (this.isRecording()) {
22791
23579
  return this.activeRecording.replayId;
22792
23580
  } else {
22793
23581
  return null;
@@ -22802,6 +23590,6 @@
22802
23590
  }
22803
23591
  });
22804
23592
 
22805
- win['__mp_recorder'] = MixpanelRecorder;
23593
+ win[RECORDER_GLOBAL_NAME] = MixpanelRecorder;
22806
23594
 
22807
23595
  })();