mixpanel-browser 2.76.0 → 2.78.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 (52) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.github/dependabot.yml +8 -0
  3. package/.github/workflows/integration-tests.yml +2 -2
  4. package/.github/workflows/unit-tests.yml +2 -2
  5. package/CHANGELOG.md +8 -0
  6. package/dist/async-modules/mixpanel-recorder-BjSlYaNJ.min.js +2 -0
  7. package/dist/async-modules/mixpanel-recorder-BjSlYaNJ.min.js.map +1 -0
  8. package/dist/async-modules/{mixpanel-recorder-bIS4LMGd.js → mixpanel-recorder-zMBXIyeG.js} +84 -10
  9. package/dist/async-modules/{mixpanel-targeting-VOeN7RWY.min.js → mixpanel-targeting-BSHal4N9.min.js} +2 -2
  10. package/dist/async-modules/{mixpanel-targeting-VOeN7RWY.min.js.map → mixpanel-targeting-BSHal4N9.min.js.map} +1 -1
  11. package/dist/async-modules/{mixpanel-targeting-BcAPS-Mz.js → mixpanel-targeting-UHf4eBfC.js} +1 -1
  12. package/dist/mixpanel-core.cjs.d.ts +3 -1
  13. package/dist/mixpanel-core.cjs.js +292 -130
  14. package/dist/mixpanel-recorder.js +84 -10
  15. package/dist/mixpanel-recorder.min.js +1 -1
  16. package/dist/mixpanel-recorder.min.js.map +1 -1
  17. package/dist/mixpanel-targeting.js +1 -1
  18. package/dist/mixpanel-targeting.min.js +1 -1
  19. package/dist/mixpanel-targeting.min.js.map +1 -1
  20. package/dist/mixpanel-with-async-modules.cjs.d.ts +3 -1
  21. package/dist/mixpanel-with-async-modules.cjs.js +294 -132
  22. package/dist/mixpanel-with-async-recorder.cjs.d.ts +3 -1
  23. package/dist/mixpanel-with-async-recorder.cjs.js +294 -132
  24. package/dist/mixpanel-with-recorder.d.ts +3 -1
  25. package/dist/mixpanel-with-recorder.js +381 -168
  26. package/dist/mixpanel-with-recorder.min.d.ts +3 -1
  27. package/dist/mixpanel-with-recorder.min.js +1 -1
  28. package/dist/mixpanel.amd.d.ts +3 -1
  29. package/dist/mixpanel.amd.js +381 -168
  30. package/dist/mixpanel.cjs.d.ts +3 -1
  31. package/dist/mixpanel.cjs.js +381 -168
  32. package/dist/mixpanel.globals.js +294 -132
  33. package/dist/mixpanel.min.js +191 -186
  34. package/dist/mixpanel.module.d.ts +3 -1
  35. package/dist/mixpanel.module.js +381 -168
  36. package/dist/mixpanel.umd.d.ts +3 -1
  37. package/dist/mixpanel.umd.js +381 -168
  38. package/dist/rrweb-bundled.js +61 -9
  39. package/dist/rrweb-compiled.js +56 -9
  40. package/package.json +6 -5
  41. package/src/config.js +1 -1
  42. package/src/flags/CLAUDE.md +24 -0
  43. package/src/flags/index.js +109 -80
  44. package/src/index.d.ts +3 -1
  45. package/src/mixpanel-core.js +4 -2
  46. package/src/recorder/session-recording.js +5 -1
  47. package/src/recorder/utils.js +27 -1
  48. package/src/recorder-manager.js +110 -2
  49. package/testServer.js +16 -1
  50. package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js +0 -2
  51. package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js.map +0 -1
  52. /package/src/loaders/{loader-module-with-async-recorder.d.ts → loader-module-with-async-modules.d.ts} +0 -0
@@ -28,7 +28,7 @@
28
28
 
29
29
  var Config = {
30
30
  DEBUG: false,
31
- LIB_VERSION: '2.76.0'
31
+ LIB_VERSION: '2.78.0'
32
32
  };
33
33
 
34
34
  // Window global names for async modules
@@ -10736,13 +10736,7 @@
10736
10736
  };
10737
10737
  while(_this.mapRemoves.length){
10738
10738
  var removedNode = _this.mapRemoves.shift();
10739
- if (removedNode.nodeName === "IFRAME") {
10740
- try {
10741
- _this.iframeManager.removeIframe(removedNode);
10742
- } catch (e2) {}
10743
- } else {
10744
- _this.stylesheetManager.cleanupStylesheetsForRemovedNode(removedNode);
10745
- }
10739
+ _this.cleanupRemovedNode(removedNode);
10746
10740
  _this.mirror.removeNodeFromMap(removedNode);
10747
10741
  }
10748
10742
  for(var _iterator = _create_for_of_iterator_helper_loose(_this.movedSet), _step; !(_step = _iterator()).done;){
@@ -11062,6 +11056,20 @@
11062
11056
  }
11063
11057
  }
11064
11058
  });
11059
+ __publicField$1(this, "cleanupRemovedNode", function(node2) {
11060
+ if (node2.nodeName === "IFRAME") {
11061
+ try {
11062
+ _this.iframeManager.removeIframe(node2);
11063
+ } catch (e2) {}
11064
+ } else {
11065
+ try {
11066
+ _this.stylesheetManager.cleanupStylesheetsForRemovedNode(node2);
11067
+ } catch (e2) {}
11068
+ }
11069
+ node2.childNodes.forEach(function(child) {
11070
+ _this.cleanupRemovedNode(child);
11071
+ });
11072
+ });
11065
11073
  }
11066
11074
  var _proto = MutationBuffer.prototype;
11067
11075
  _proto.init = function init(options) {
@@ -13289,6 +13297,31 @@
13289
13297
  _proto.destroy = function destroy() {};
13290
13298
  return ProcessedNodeManager;
13291
13299
  }();
13300
+ function toOrigin(url) {
13301
+ try {
13302
+ var origin = new URL(url).origin;
13303
+ return origin !== "null" ? origin : null;
13304
+ } catch (e) {
13305
+ return null;
13306
+ }
13307
+ }
13308
+ function buildAllowedOriginSet(origins) {
13309
+ if (!Array.isArray(origins) || origins.length === 0) {
13310
+ throw new Error("[rrweb] allowedIframeOrigins must be a non-empty array of origin strings.");
13311
+ }
13312
+ var set = /* @__PURE__ */ new Set();
13313
+ for(var i2 = 0; i2 < origins.length; i2++){
13314
+ var entry = origins[i2];
13315
+ if (typeof entry !== "string") {
13316
+ throw new Error("[rrweb] allowedIframeOrigins[" + i2 + "] must be a string, got " + (typeof entry === "undefined" ? "undefined" : _type_of(entry)) + ".");
13317
+ }
13318
+ var origin = toOrigin(entry);
13319
+ if (origin) {
13320
+ set.add(origin);
13321
+ }
13322
+ }
13323
+ return Object.freeze(set);
13324
+ }
13292
13325
  var wrappedEmit;
13293
13326
  var takeFullSnapshot$1;
13294
13327
  var canvasManager;
@@ -13310,10 +13343,17 @@
13310
13343
  var mirror = createMirror$2();
13311
13344
  function record(options) {
13312
13345
  if (options === void 0) options = {};
13313
- var emit = options.emit, checkoutEveryNms = options.checkoutEveryNms, checkoutEveryNth = options.checkoutEveryNth, _options_blockClass = options.blockClass, blockClass = _options_blockClass === void 0 ? "rr-block" : _options_blockClass, _options_blockSelector = options.blockSelector, blockSelector = _options_blockSelector === void 0 ? null : _options_blockSelector, _options_ignoreClass = options.ignoreClass, ignoreClass = _options_ignoreClass === void 0 ? "rr-ignore" : _options_ignoreClass, _options_ignoreSelector = options.ignoreSelector, ignoreSelector = _options_ignoreSelector === void 0 ? null : _options_ignoreSelector, _options_maskTextClass = options.maskTextClass, maskTextClass = _options_maskTextClass === void 0 ? "rr-mask" : _options_maskTextClass, _options_maskTextSelector = options.maskTextSelector, maskTextSelector = _options_maskTextSelector === void 0 ? null : _options_maskTextSelector, _options_inlineStylesheet = options.inlineStylesheet, inlineStylesheet = _options_inlineStylesheet === void 0 ? true : _options_inlineStylesheet, maskAllInputs = options.maskAllInputs, _maskInputOptions = options.maskInputOptions, _slimDOMOptions = options.slimDOMOptions, maskInputFn = options.maskInputFn, maskTextFn = options.maskTextFn, hooks = options.hooks, packFn = options.packFn, _options_sampling = options.sampling, sampling = _options_sampling === void 0 ? {} : _options_sampling, _options_dataURLOptions = options.dataURLOptions, dataURLOptions = _options_dataURLOptions === void 0 ? {} : _options_dataURLOptions, mousemoveWait = options.mousemoveWait, _options_recordDOM = options.recordDOM, recordDOM = _options_recordDOM === void 0 ? true : _options_recordDOM, _options_recordCanvas = options.recordCanvas, recordCanvas = _options_recordCanvas === void 0 ? false : _options_recordCanvas, _options_recordCrossOriginIframes = options.recordCrossOriginIframes, recordCrossOriginIframes = _options_recordCrossOriginIframes === void 0 ? false : _options_recordCrossOriginIframes, _options_recordAfter = options.recordAfter, recordAfter = _options_recordAfter === void 0 ? options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load" : _options_recordAfter, _options_userTriggeredOnInput = options.userTriggeredOnInput, userTriggeredOnInput = _options_userTriggeredOnInput === void 0 ? false : _options_userTriggeredOnInput, _options_collectFonts = options.collectFonts, collectFonts = _options_collectFonts === void 0 ? false : _options_collectFonts, _options_inlineImages = options.inlineImages, inlineImages = _options_inlineImages === void 0 ? false : _options_inlineImages, plugins = options.plugins, _options_keepIframeSrcFn = options.keepIframeSrcFn, keepIframeSrcFn = _options_keepIframeSrcFn === void 0 ? function() {
13346
+ var emit = options.emit, checkoutEveryNms = options.checkoutEveryNms, checkoutEveryNth = options.checkoutEveryNth, _options_blockClass = options.blockClass, blockClass = _options_blockClass === void 0 ? "rr-block" : _options_blockClass, _options_blockSelector = options.blockSelector, blockSelector = _options_blockSelector === void 0 ? null : _options_blockSelector, _options_ignoreClass = options.ignoreClass, ignoreClass = _options_ignoreClass === void 0 ? "rr-ignore" : _options_ignoreClass, _options_ignoreSelector = options.ignoreSelector, ignoreSelector = _options_ignoreSelector === void 0 ? null : _options_ignoreSelector, _options_maskTextClass = options.maskTextClass, maskTextClass = _options_maskTextClass === void 0 ? "rr-mask" : _options_maskTextClass, _options_maskTextSelector = options.maskTextSelector, maskTextSelector = _options_maskTextSelector === void 0 ? null : _options_maskTextSelector, _options_inlineStylesheet = options.inlineStylesheet, inlineStylesheet = _options_inlineStylesheet === void 0 ? true : _options_inlineStylesheet, maskAllInputs = options.maskAllInputs, _maskInputOptions = options.maskInputOptions, _slimDOMOptions = options.slimDOMOptions, maskInputFn = options.maskInputFn, maskTextFn = options.maskTextFn, hooks = options.hooks, packFn = options.packFn, _options_sampling = options.sampling, sampling = _options_sampling === void 0 ? {} : _options_sampling, _options_dataURLOptions = options.dataURLOptions, dataURLOptions = _options_dataURLOptions === void 0 ? {} : _options_dataURLOptions, mousemoveWait = options.mousemoveWait, _options_recordDOM = options.recordDOM, recordDOM = _options_recordDOM === void 0 ? true : _options_recordDOM, _options_recordCanvas = options.recordCanvas, recordCanvas = _options_recordCanvas === void 0 ? false : _options_recordCanvas, _options_recordCrossOriginIframes = options.recordCrossOriginIframes, recordCrossOriginIframes = _options_recordCrossOriginIframes === void 0 ? false : _options_recordCrossOriginIframes, allowedIframeOrigins = options.allowedIframeOrigins, _options_recordAfter = options.recordAfter, recordAfter = _options_recordAfter === void 0 ? options.recordAfter === "DOMContentLoaded" ? options.recordAfter : "load" : _options_recordAfter, _options_userTriggeredOnInput = options.userTriggeredOnInput, userTriggeredOnInput = _options_userTriggeredOnInput === void 0 ? false : _options_userTriggeredOnInput, _options_collectFonts = options.collectFonts, collectFonts = _options_collectFonts === void 0 ? false : _options_collectFonts, _options_inlineImages = options.inlineImages, inlineImages = _options_inlineImages === void 0 ? false : _options_inlineImages, plugins = options.plugins, _options_keepIframeSrcFn = options.keepIframeSrcFn, keepIframeSrcFn = _options_keepIframeSrcFn === void 0 ? function() {
13314
13347
  return false;
13315
13348
  } : _options_keepIframeSrcFn, _options_ignoreCSSAttributes = options.ignoreCSSAttributes, ignoreCSSAttributes = _options_ignoreCSSAttributes === void 0 ? /* @__PURE__ */ new Set([]) : _options_ignoreCSSAttributes, errorHandler2 = options.errorHandler;
13316
13349
  registerErrorHandler(errorHandler2);
13350
+ var validatedOrigins;
13351
+ if (recordCrossOriginIframes && allowedIframeOrigins && allowedIframeOrigins.length > 0) {
13352
+ validatedOrigins = buildAllowedOriginSet(allowedIframeOrigins);
13353
+ if (validatedOrigins.size === 0) {
13354
+ validatedOrigins = void 0;
13355
+ }
13356
+ }
13317
13357
  var inEmittingFrame = recordCrossOriginIframes ? window.parent === window : true;
13318
13358
  var passEmitsToParent = false;
13319
13359
  if (!inEmittingFrame) {
@@ -13405,7 +13445,14 @@
13405
13445
  origin: window.location.origin,
13406
13446
  isCheckout: isCheckout
13407
13447
  };
13408
- window.parent.postMessage(message, "*");
13448
+ if (validatedOrigins) {
13449
+ for(var _iterator = _create_for_of_iterator_helper_loose(validatedOrigins), _step; !(_step = _iterator()).done;){
13450
+ var targetOrigin = _step.value;
13451
+ window.parent.postMessage(message, targetOrigin);
13452
+ }
13453
+ } else {
13454
+ window.parent.postMessage(message, "*");
13455
+ }
13409
13456
  }
13410
13457
  if (e2.type === EventType.FullSnapshot) {
13411
13458
  lastFullSnapshotEvent = e2;
@@ -21189,7 +21236,7 @@
21189
21236
  };
21190
21237
  }
21191
21238
 
21192
- var logger$7 = console_with_prefix('lock');
21239
+ var logger$8 = console_with_prefix('lock');
21193
21240
 
21194
21241
  /**
21195
21242
  * SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
@@ -21241,7 +21288,7 @@
21241
21288
 
21242
21289
  var delay = function(cb) {
21243
21290
  if (new Date().getTime() - startTime > timeoutMS) {
21244
- logger$7.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
21291
+ logger$8.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
21245
21292
  storage.removeItem(keyZ);
21246
21293
  storage.removeItem(keyY);
21247
21294
  loop();
@@ -21388,7 +21435,7 @@
21388
21435
  }, this));
21389
21436
  };
21390
21437
 
21391
- var logger$6 = console_with_prefix('batch');
21438
+ var logger$7 = console_with_prefix('batch');
21392
21439
 
21393
21440
  /**
21394
21441
  * RequestQueue: queue for batching API requests with localStorage backup for retries.
@@ -21417,7 +21464,7 @@
21417
21464
  timeoutMS: options.sharedLockTimeoutMS,
21418
21465
  });
21419
21466
  }
21420
- this.reportError = options.errorReporter || _.bind(logger$6.error, logger$6);
21467
+ this.reportError = options.errorReporter || _.bind(logger$7.error, logger$7);
21421
21468
 
21422
21469
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
21423
21470
 
@@ -21750,7 +21797,7 @@
21750
21797
  // maximum interval between request retries after exponential backoff
21751
21798
  var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
21752
21799
 
21753
- var logger$5 = console_with_prefix('batch');
21800
+ var logger$6 = console_with_prefix('batch');
21754
21801
 
21755
21802
  /**
21756
21803
  * RequestBatcher: manages the queueing, flushing, retry etc of requests of one
@@ -21878,7 +21925,7 @@
21878
21925
  */
21879
21926
  RequestBatcher.prototype.flush = function(options) {
21880
21927
  if (this.requestInProgress) {
21881
- logger$5.log('Flush: Request already in progress');
21928
+ logger$6.log('Flush: Request already in progress');
21882
21929
  return PromisePolyfill.resolve();
21883
21930
  }
21884
21931
 
@@ -22055,7 +22102,7 @@
22055
22102
  if (options.unloading) {
22056
22103
  requestOptions.transport = 'sendBeacon';
22057
22104
  }
22058
- logger$5.log('MIXPANEL REQUEST:', dataForRequest);
22105
+ logger$6.log('MIXPANEL REQUEST:', dataForRequest);
22059
22106
  return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
22060
22107
  }, this))
22061
22108
  .catch(_.bind(function(err) {
@@ -22068,7 +22115,7 @@
22068
22115
  * Log error to global logger and optional user-defined logger.
22069
22116
  */
22070
22117
  RequestBatcher.prototype.reportError = function(msg, err) {
22071
- logger$5.error.apply(logger$5.error, arguments);
22118
+ logger$6.error.apply(logger$6.error, arguments);
22072
22119
  if (this.errorReporter) {
22073
22120
  try {
22074
22121
  if (!(err instanceof Error)) {
@@ -22076,7 +22123,7 @@
22076
22123
  }
22077
22124
  this.errorReporter(msg, err);
22078
22125
  } catch(err) {
22079
- logger$5.error(err);
22126
+ logger$6.error(err);
22080
22127
  }
22081
22128
  }
22082
22129
  };
@@ -22093,6 +22140,29 @@
22093
22140
 
22094
22141
  var RECORD_ENQUEUE_THROTTLE_MS = 250;
22095
22142
 
22143
+ var validateAllowedOrigins = function(origins, logger) {
22144
+ if (!_.isArray(origins)) {
22145
+ if (origins) {
22146
+ logger.critical('record_allowed_iframe_origins must be an array of origin strings, cross-origin recording will be disabled.');
22147
+ }
22148
+ return [];
22149
+ }
22150
+ var valid = [];
22151
+ for (var i = 0; i < origins.length; i++) {
22152
+ try {
22153
+ var origin = new URL(origins[i]).origin;
22154
+ if (origin === 'null') {
22155
+ logger.critical(origins[i] + ' has an opaque origin. Skipping this entry.');
22156
+ continue;
22157
+ }
22158
+ valid.push(origin);
22159
+ } catch (e) {
22160
+ logger.critical(origins[i] + ' is not a valid origin URL. Skipping this entry.');
22161
+ }
22162
+ }
22163
+ return valid;
22164
+ };
22165
+
22096
22166
  // stateless utils
22097
22167
  // mostly from https://github.com/mixpanel/mixpanel-js/blob/989ada50f518edab47b9c4fd9535f9fbd5ec5fc0/src/autotrack-utils.js
22098
22168
 
@@ -22198,7 +22268,7 @@
22198
22268
 
22199
22269
  var MAX_DEPTH = 5;
22200
22270
 
22201
- var logger$4 = console_with_prefix('autocapture');
22271
+ var logger$5 = console_with_prefix('autocapture');
22202
22272
 
22203
22273
 
22204
22274
  function getClasses(el) {
@@ -22462,7 +22532,7 @@
22462
22532
  return false;
22463
22533
  }
22464
22534
  } catch (err) {
22465
- logger$4.critical('Error while checking element in allowElementCallback', err);
22535
+ logger$5.critical('Error while checking element in allowElementCallback', err);
22466
22536
  return false;
22467
22537
  }
22468
22538
  }
@@ -22479,7 +22549,7 @@
22479
22549
  return true;
22480
22550
  }
22481
22551
  } catch (err) {
22482
- logger$4.critical('Error while checking selector: ' + sel, err);
22552
+ logger$5.critical('Error while checking selector: ' + sel, err);
22483
22553
  }
22484
22554
  }
22485
22555
  return false;
@@ -22494,7 +22564,7 @@
22494
22564
  return true;
22495
22565
  }
22496
22566
  } catch (err) {
22497
- logger$4.critical('Error while checking element in blockElementCallback', err);
22567
+ logger$5.critical('Error while checking element in blockElementCallback', err);
22498
22568
  return true;
22499
22569
  }
22500
22570
  }
@@ -22508,7 +22578,7 @@
22508
22578
  return true;
22509
22579
  }
22510
22580
  } catch (err) {
22511
- logger$4.critical('Error while checking selector: ' + sel, err);
22581
+ logger$5.critical('Error while checking selector: ' + sel, err);
22512
22582
  }
22513
22583
  }
22514
22584
  }
@@ -23064,7 +23134,7 @@
23064
23134
  *
23065
23135
  */
23066
23136
 
23067
- var logger$3 = console_with_prefix('network-plugin');
23137
+ var logger$4 = console_with_prefix('network-plugin');
23068
23138
 
23069
23139
  /**
23070
23140
  * Get the time origin for converting performance timestamps to absolute timestamps.
@@ -23216,7 +23286,7 @@
23216
23286
  return str;
23217
23287
  }
23218
23288
  if (str.length > MAX_BODY_SIZE) {
23219
- logger$3.error('Body truncated from ' + str.length + ' to ' + MAX_BODY_SIZE + ' characters');
23289
+ logger$4.error('Body truncated from ' + str.length + ' to ' + MAX_BODY_SIZE + ' characters');
23220
23290
  return str.substring(0, MAX_BODY_SIZE) + '... [truncated]';
23221
23291
  }
23222
23292
  return str;
@@ -23230,7 +23300,7 @@
23230
23300
  */
23231
23301
  function initPerformanceObserver(cb, win, options) {
23232
23302
  if (!win.PerformanceObserver) {
23233
- logger$3.error('PerformanceObserver not supported');
23303
+ logger$4.error('PerformanceObserver not supported');
23234
23304
  return function() {
23235
23305
  //
23236
23306
  };
@@ -23383,7 +23453,7 @@
23383
23453
  attempt = 0;
23384
23454
  }
23385
23455
  if (attempt > 10) {
23386
- logger$3.error('Cannot find performance entry');
23456
+ logger$4.error('Cannot find performance entry');
23387
23457
  return Promise.resolve(null);
23388
23458
  }
23389
23459
  var urlPerformanceEntries = /** @type {PerformanceResourceTiming[]} */ (
@@ -23504,7 +23574,7 @@
23504
23574
  )
23505
23575
  .then(function(entry) {
23506
23576
  if (!entry) {
23507
- logger$3.error('Failed to get performance entry for XHR request to ' + req.url);
23577
+ logger$4.error('Failed to get performance entry for XHR request to ' + req.url);
23508
23578
  return;
23509
23579
  }
23510
23580
  /** @type {NetworkRequest} */
@@ -23524,7 +23594,7 @@
23524
23594
  cb({ requests: [request] });
23525
23595
  })
23526
23596
  .catch(function(e) {
23527
- logger$3.error('Error recording XHR request to ' + req.url + ': ' + String(e));
23597
+ logger$4.error('Error recording XHR request to ' + req.url + ': ' + String(e));
23528
23598
  });
23529
23599
  });
23530
23600
 
@@ -23616,7 +23686,7 @@
23616
23686
  })
23617
23687
  .then(function(entry) {
23618
23688
  if (!entry) {
23619
- logger$3.error('Failed to get performance entry for fetch request to ' + req.url);
23689
+ logger$4.error('Failed to get performance entry for fetch request to ' + req.url);
23620
23690
  return;
23621
23691
  }
23622
23692
  /** @type {NetworkRequest} */
@@ -23636,7 +23706,7 @@
23636
23706
  cb({ requests: [request] });
23637
23707
  })
23638
23708
  .catch(function (e) {
23639
- logger$3.error('Error recording fetch request to ' + req.url + ': ' + String(e));
23709
+ logger$4.error('Error recording fetch request to ' + req.url + ': ' + String(e));
23640
23710
  });
23641
23711
 
23642
23712
  return originalFetchPromise;
@@ -23709,7 +23779,7 @@
23709
23779
  */
23710
23780
 
23711
23781
 
23712
- var logger$2 = console_with_prefix('recorder');
23782
+ var logger$3 = console_with_prefix('recorder');
23713
23783
  var CompressionStream = win['CompressionStream'];
23714
23784
 
23715
23785
  var RECORDER_BATCHER_LIB_CONFIG = {
@@ -23889,14 +23959,14 @@
23889
23959
  }
23890
23960
 
23891
23961
  if (this._stopRecording !== null) {
23892
- logger$2.log('Recording already in progress, skipping startRecording.');
23962
+ logger$3.log('Recording already in progress, skipping startRecording.');
23893
23963
  return;
23894
23964
  }
23895
23965
 
23896
23966
  this.recordMaxMs = this.getConfig('record_max_ms');
23897
23967
  if (this.recordMaxMs > MAX_RECORDING_MS) {
23898
23968
  this.recordMaxMs = MAX_RECORDING_MS;
23899
- logger$2.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
23969
+ logger$3.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
23900
23970
  }
23901
23971
 
23902
23972
  if (!this.maxExpires) {
@@ -23960,6 +24030,8 @@
23960
24030
  );
23961
24031
  }
23962
24032
 
24033
+ var validatedOrigins = validateAllowedOrigins(this.getConfig('record_allowed_iframe_origins'), logger$3);
24034
+
23963
24035
  try {
23964
24036
  this._stopRecording = this._rrwebRecord({
23965
24037
  'emit': function (ev) {
@@ -23994,6 +24066,8 @@
23994
24066
  'maskTextSelector': '*',
23995
24067
  'maskInputFn': this._getMaskFn(shouldMaskInput, privacyConfig),
23996
24068
  'maskTextFn': this._getMaskFn(shouldMaskText, privacyConfig),
24069
+ 'recordCrossOriginIframes': validatedOrigins.length > 0,
24070
+ 'allowedIframeOrigins': validatedOrigins,
23997
24071
  'recordCanvas': this.getConfig('record_canvas'),
23998
24072
  'sampling': {
23999
24073
  'canvas': 15
@@ -24218,14 +24292,14 @@
24218
24292
 
24219
24293
 
24220
24294
  SessionRecording.prototype.reportError = function(msg, err) {
24221
- logger$2.error.apply(logger$2.error, arguments);
24295
+ logger$3.error.apply(logger$3.error, arguments);
24222
24296
  try {
24223
24297
  if (!err && !(msg instanceof Error)) {
24224
24298
  msg = new Error(msg);
24225
24299
  }
24226
24300
  this.getConfig('error_reporter')(msg, err);
24227
24301
  } catch(err) {
24228
- logger$2.error(err);
24302
+ logger$3.error(err);
24229
24303
  }
24230
24304
  };
24231
24305
 
@@ -24254,7 +24328,7 @@
24254
24328
  var configValue = this.getConfig('record_min_ms');
24255
24329
 
24256
24330
  if (configValue > MAX_VALUE_FOR_MIN_RECORDING_MS) {
24257
- logger$2.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
24331
+ logger$3.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
24258
24332
  return MAX_VALUE_FOR_MIN_RECORDING_MS;
24259
24333
  }
24260
24334
 
@@ -24417,7 +24491,7 @@
24417
24491
  .catch(this.handleError.bind(this));
24418
24492
  };
24419
24493
 
24420
- var logger$1 = console_with_prefix('recorder');
24494
+ var logger$2 = console_with_prefix('recorder');
24421
24495
 
24422
24496
  /**
24423
24497
  * Recorder API: bundles rrweb and and exposes methods to start and stop recordings.
@@ -24433,7 +24507,7 @@
24433
24507
  */
24434
24508
  this.recordingRegistry = new RecordingRegistry({
24435
24509
  mixpanelInstance: this.mixpanelInstance,
24436
- errorReporter: logger$1.error,
24510
+ errorReporter: logger$2.error,
24437
24511
  sharedLockStorage: sharedLockStorage
24438
24512
  });
24439
24513
  this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
@@ -24445,17 +24519,17 @@
24445
24519
  MixpanelRecorder.prototype.startRecording = function(options) {
24446
24520
  options = options || {};
24447
24521
  if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
24448
- logger$1.log('Recording already in progress, skipping startRecording.');
24522
+ logger$2.log('Recording already in progress, skipping startRecording.');
24449
24523
  return;
24450
24524
  }
24451
24525
 
24452
24526
  var onIdleTimeout = function () {
24453
- logger$1.log('Idle timeout reached, restarting recording.');
24527
+ logger$2.log('Idle timeout reached, restarting recording.');
24454
24528
  this.resetRecording();
24455
24529
  }.bind(this);
24456
24530
 
24457
24531
  var onMaxLengthReached = function () {
24458
- logger$1.log('Max recording length reached, stopping recording.');
24532
+ logger$2.log('Max recording length reached, stopping recording.');
24459
24533
  this.resetRecording();
24460
24534
  }.bind(this);
24461
24535
 
@@ -24525,7 +24599,7 @@
24525
24599
  } else if (startNewIfInactive) {
24526
24600
  return this.startRecording({shouldStopBatcher: false});
24527
24601
  } else {
24528
- logger$1.log('No resumable recording found.');
24602
+ logger$2.log('No resumable recording found.');
24529
24603
  return null;
24530
24604
  }
24531
24605
  }.bind(this));
@@ -24659,7 +24733,7 @@
24659
24733
  observer.observe(shadowRoot, this.observerConfig);
24660
24734
  this.shadowObservers.push(observer);
24661
24735
  } catch (e) {
24662
- logger$4.critical('Error while observing shadow root', e);
24736
+ logger$5.critical('Error while observing shadow root', e);
24663
24737
  }
24664
24738
  };
24665
24739
 
@@ -24670,7 +24744,7 @@
24670
24744
  }
24671
24745
 
24672
24746
  if (!weakSetSupported()) {
24673
- logger$4.critical('Shadow DOM observation unavailable: WeakSet not supported');
24747
+ logger$5.critical('Shadow DOM observation unavailable: WeakSet not supported');
24674
24748
  return;
24675
24749
  }
24676
24750
 
@@ -24686,7 +24760,7 @@
24686
24760
  try {
24687
24761
  this.shadowObservers[i].disconnect();
24688
24762
  } catch (e) {
24689
- logger$4.critical('Error while disconnecting shadow DOM observer', e);
24763
+ logger$5.critical('Error while disconnecting shadow DOM observer', e);
24690
24764
  }
24691
24765
  }
24692
24766
  this.shadowObservers = [];
@@ -24874,7 +24948,7 @@
24874
24948
 
24875
24949
  this.mutationObserver.observe(document.body || document.documentElement, MUTATION_OBSERVER_CONFIG);
24876
24950
  } catch (e) {
24877
- logger$4.critical('Error while setting up mutation observer', e);
24951
+ logger$5.critical('Error while setting up mutation observer', e);
24878
24952
  }
24879
24953
  }
24880
24954
 
@@ -24889,7 +24963,7 @@
24889
24963
  );
24890
24964
  this.shadowDOMObserver.start();
24891
24965
  } catch (e) {
24892
- logger$4.critical('Error while setting up shadow DOM observer', e);
24966
+ logger$5.critical('Error while setting up shadow DOM observer', e);
24893
24967
  this.shadowDOMObserver = null;
24894
24968
  }
24895
24969
  }
@@ -24916,7 +24990,7 @@
24916
24990
  try {
24917
24991
  listener.target.removeEventListener(listener.event, listener.handler, listener.options);
24918
24992
  } catch (e) {
24919
- logger$4.critical('Error while removing event listener', e);
24993
+ logger$5.critical('Error while removing event listener', e);
24920
24994
  }
24921
24995
  }
24922
24996
  this.eventListeners = [];
@@ -24925,7 +24999,7 @@
24925
24999
  try {
24926
25000
  this.mutationObserver.disconnect();
24927
25001
  } catch (e) {
24928
- logger$4.critical('Error while disconnecting mutation observer', e);
25002
+ logger$5.critical('Error while disconnecting mutation observer', e);
24929
25003
  }
24930
25004
  this.mutationObserver = null;
24931
25005
  }
@@ -24934,7 +25008,7 @@
24934
25008
  try {
24935
25009
  this.shadowDOMObserver.stop();
24936
25010
  } catch (e) {
24937
- logger$4.critical('Error while stopping shadow DOM observer', e);
25011
+ logger$5.critical('Error while stopping shadow DOM observer', e);
24938
25012
  }
24939
25013
  this.shadowDOMObserver = null;
24940
25014
  }
@@ -25012,7 +25086,7 @@
25012
25086
 
25013
25087
  Autocapture.prototype.init = function() {
25014
25088
  if (!minDOMApisSupported()) {
25015
- logger$4.critical('Autocapture unavailable: missing required DOM APIs');
25089
+ logger$5.critical('Autocapture unavailable: missing required DOM APIs');
25016
25090
  return;
25017
25091
  }
25018
25092
  this.initPageListeners();
@@ -25052,7 +25126,7 @@
25052
25126
  try {
25053
25127
  return !urlMatchesRegexList(currentUrl, allowUrlRegexes);
25054
25128
  } catch (err) {
25055
- logger$4.critical('Error while checking block URL regexes: ', err);
25129
+ logger$5.critical('Error while checking block URL regexes: ', err);
25056
25130
  return true;
25057
25131
  }
25058
25132
  }
@@ -25065,7 +25139,7 @@
25065
25139
  try {
25066
25140
  return urlMatchesRegexList(currentUrl, blockUrlRegexes);
25067
25141
  } catch (err) {
25068
- logger$4.critical('Error while checking block URL regexes: ', err);
25142
+ logger$5.critical('Error while checking block URL regexes: ', err);
25069
25143
  return true;
25070
25144
  }
25071
25145
  };
@@ -25203,7 +25277,7 @@
25203
25277
  return;
25204
25278
  }
25205
25279
 
25206
- logger$4.log('Initializing scroll depth tracking');
25280
+ logger$5.log('Initializing scroll depth tracking');
25207
25281
 
25208
25282
  this.maxScrollViewDepth = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
25209
25283
 
@@ -25229,7 +25303,7 @@
25229
25303
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
25230
25304
  return;
25231
25305
  }
25232
- logger$4.log('Initializing click tracking');
25306
+ logger$5.log('Initializing click tracking');
25233
25307
 
25234
25308
  this.listenerClick = function(ev) {
25235
25309
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
@@ -25248,7 +25322,7 @@
25248
25322
  return;
25249
25323
  }
25250
25324
 
25251
- logger$4.log('Initializing dead click tracking');
25325
+ logger$5.log('Initializing dead click tracking');
25252
25326
  if (!this._deadClickTracker) {
25253
25327
  this._deadClickTracker = new DeadClickTracker(function(deadClickEvent) {
25254
25328
  this.trackDomEvent(deadClickEvent, MP_EV_DEAD_CLICK);
@@ -25282,7 +25356,7 @@
25282
25356
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
25283
25357
  return;
25284
25358
  }
25285
- logger$4.log('Initializing input tracking');
25359
+ logger$5.log('Initializing input tracking');
25286
25360
 
25287
25361
  this.listenerChange = function(ev) {
25288
25362
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -25299,7 +25373,7 @@
25299
25373
  if (!this.pageviewTrackingConfig()) {
25300
25374
  return;
25301
25375
  }
25302
- logger$4.log('Initializing pageview tracking');
25376
+ logger$5.log('Initializing pageview tracking');
25303
25377
 
25304
25378
  var previousTrackedUrl = '';
25305
25379
  var tracked = false;
@@ -25334,7 +25408,7 @@
25334
25408
  }
25335
25409
  if (didPathChange) {
25336
25410
  this.lastScrollCheckpoint = 0;
25337
- logger$4.log('Path change: re-initializing scroll depth checkpoints');
25411
+ logger$5.log('Path change: re-initializing scroll depth checkpoints');
25338
25412
  }
25339
25413
  }
25340
25414
  }.bind(this));
@@ -25349,7 +25423,7 @@
25349
25423
  return;
25350
25424
  }
25351
25425
 
25352
- logger$4.log('Initializing rage click tracking');
25426
+ logger$5.log('Initializing rage click tracking');
25353
25427
  if (!this._rageClickTracker) {
25354
25428
  this._rageClickTracker = new RageClickTracker();
25355
25429
  }
@@ -25379,7 +25453,7 @@
25379
25453
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
25380
25454
  return;
25381
25455
  }
25382
- logger$4.log('Initializing scroll tracking');
25456
+ logger$5.log('Initializing scroll tracking');
25383
25457
  this.lastScrollCheckpoint = 0;
25384
25458
 
25385
25459
  var scrollTrackFunction = function() {
@@ -25416,7 +25490,7 @@
25416
25490
  }
25417
25491
  }
25418
25492
  } catch (err) {
25419
- logger$4.critical('Error while calculating scroll percentage', err);
25493
+ logger$5.critical('Error while calculating scroll percentage', err);
25420
25494
  }
25421
25495
  if (shouldTrack) {
25422
25496
  this.mp.track(MP_EV_SCROLL, props);
@@ -25434,7 +25508,7 @@
25434
25508
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
25435
25509
  return;
25436
25510
  }
25437
- logger$4.log('Initializing submit tracking');
25511
+ logger$5.log('Initializing submit tracking');
25438
25512
 
25439
25513
  this.listenerSubmit = function(ev) {
25440
25514
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -25456,7 +25530,7 @@
25456
25530
  return;
25457
25531
  }
25458
25532
 
25459
- logger$4.log('Initializing page visibility tracking.');
25533
+ logger$5.log('Initializing page visibility tracking.');
25460
25534
  this._initScrollDepthTracking();
25461
25535
  var previousTrackedUrl = _.info.currentUrl();
25462
25536
 
@@ -25541,7 +25615,7 @@
25541
25615
  return win[TARGETING_GLOBAL_NAME];
25542
25616
  };
25543
25617
 
25544
- var logger = console_with_prefix('flags');
25618
+ var logger$1 = console_with_prefix('flags');
25545
25619
  var FLAGS_CONFIG_KEY = 'flags';
25546
25620
 
25547
25621
  var CONFIG_CONTEXT = 'context';
@@ -25584,12 +25658,14 @@
25584
25658
 
25585
25659
  FeatureFlagManager.prototype.init = function() {
25586
25660
  if (!this.minApisSupported()) {
25587
- logger.critical('Feature Flags unavailable: missing minimum required APIs');
25661
+ logger$1.critical('Feature Flags unavailable: missing minimum required APIs');
25588
25662
  return;
25589
25663
  }
25590
25664
 
25591
25665
  this.flags = null;
25592
- this.fetchFlags();
25666
+ this.fetchFlags().catch(function() {
25667
+ logger$1.error('Error fetching flags during init');
25668
+ });
25593
25669
 
25594
25670
  this.trackedFeatures = new Set();
25595
25671
  this.pendingFirstTimeEvents = {};
@@ -25619,7 +25695,7 @@
25619
25695
 
25620
25696
  FeatureFlagManager.prototype.updateContext = function(newContext, options) {
25621
25697
  if (!this.isSystemEnabled()) {
25622
- logger.critical('Feature Flags not enabled, cannot update context');
25698
+ logger$1.critical('Feature Flags not enabled, cannot update context');
25623
25699
  return Promise.resolve();
25624
25700
  }
25625
25701
 
@@ -25630,13 +25706,17 @@
25630
25706
  var oldContext = (options && options['replace']) ? {} : this.getConfig(CONFIG_CONTEXT);
25631
25707
  ffConfig[CONFIG_CONTEXT] = _.extend({}, oldContext, newContext);
25632
25708
 
25633
- this.setMpConfig(FLAGS_CONFIG_KEY, ffConfig);
25634
- return this.fetchFlags();
25709
+ var configUpdate = {};
25710
+ configUpdate[FLAGS_CONFIG_KEY] = ffConfig;
25711
+ this.setMpConfig(configUpdate);
25712
+ return this.fetchFlags().catch(function() {
25713
+ logger$1.error('Error fetching flags during updateContext');
25714
+ });
25635
25715
  };
25636
25716
 
25637
25717
  FeatureFlagManager.prototype.areFlagsReady = function() {
25638
25718
  if (!this.isSystemEnabled()) {
25639
- logger.error('Feature Flags not enabled');
25719
+ logger$1.error('Feature Flags not enabled');
25640
25720
  }
25641
25721
  return !!this.flags;
25642
25722
  };
@@ -25649,7 +25729,7 @@
25649
25729
  var distinctId = this.getMpProperty('distinct_id');
25650
25730
  var deviceId = this.getMpProperty('$device_id');
25651
25731
  var traceparent = generateTraceparent();
25652
- logger.log('Fetching flags for distinct ID: ' + distinctId);
25732
+ logger$1.log('Fetching flags for distinct ID: ' + distinctId);
25653
25733
 
25654
25734
  var context = _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT));
25655
25735
  var searchParams = new URLSearchParams();
@@ -25668,99 +25748,113 @@
25668
25748
  }
25669
25749
  }).then(function(response) {
25670
25750
  this.markFetchComplete();
25671
- return response.json().then(function(responseBody) {
25672
- var responseFlags = responseBody['flags'];
25673
- if (!responseFlags) {
25674
- throw new Error('No flags in API response');
25675
- }
25676
- var flags = new Map();
25677
- var pendingFirstTimeEvents = {};
25678
-
25679
- // Process flags from response
25680
- _.each(responseFlags, function(data, key) {
25681
- // Check if this flag has any activated first-time events this session
25682
- var hasActivatedEvent = false;
25683
- var prefix = key + ':';
25684
- _.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
25685
- if (eventKey.startsWith(prefix)) {
25686
- hasActivatedEvent = true;
25687
- }
25688
- });
25751
+ return response.json();
25752
+ }.bind(this)).then(function(responseBody) {
25753
+ var responseFlags = responseBody['flags'];
25754
+ if (!responseFlags) {
25755
+ throw new Error('No flags in API response');
25756
+ }
25757
+ var flags = new Map();
25758
+ var pendingFirstTimeEvents = {};
25759
+
25760
+ // Process flags from response
25761
+ _.each(responseFlags, function(data, key) {
25762
+ // Check if this flag has any activated first-time events this session
25763
+ var hasActivatedEvent = false;
25764
+ var prefix = key + ':';
25765
+ _.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
25766
+ if (eventKey.startsWith(prefix)) {
25767
+ hasActivatedEvent = true;
25768
+ }
25769
+ });
25689
25770
 
25690
- if (hasActivatedEvent) {
25691
- // Preserve the activated variant, don't overwrite with server's current variant
25692
- var currentFlag = this.flags && this.flags.get(key);
25693
- if (currentFlag) {
25694
- flags.set(key, currentFlag);
25695
- }
25696
- } else {
25697
- // Use server's current variant
25698
- flags.set(key, {
25699
- 'key': data['variant_key'],
25700
- 'value': data['variant_value'],
25701
- 'experiment_id': data['experiment_id'],
25702
- 'is_experiment_active': data['is_experiment_active'],
25703
- 'is_qa_tester': data['is_qa_tester']
25704
- });
25771
+ if (hasActivatedEvent) {
25772
+ // Preserve the activated variant, don't overwrite with server's current variant
25773
+ var currentFlag = this.flags && this.flags.get(key);
25774
+ if (currentFlag) {
25775
+ flags.set(key, currentFlag);
25705
25776
  }
25706
- }, this);
25777
+ } else {
25778
+ // Use server's current variant
25779
+ flags.set(key, {
25780
+ 'key': data['variant_key'],
25781
+ 'value': data['variant_value'],
25782
+ 'experiment_id': data['experiment_id'],
25783
+ 'is_experiment_active': data['is_experiment_active'],
25784
+ 'is_qa_tester': data['is_qa_tester']
25785
+ });
25786
+ }
25787
+ }, this);
25707
25788
 
25708
- // Process top-level pending_first_time_events array
25709
- var topLevelDefinitions = responseBody['pending_first_time_events'];
25710
- if (topLevelDefinitions && topLevelDefinitions.length > 0) {
25711
- _.each(topLevelDefinitions, function(def) {
25712
- var flagKey = def['flag_key'];
25713
- var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
25789
+ // Process top-level pending_first_time_events array
25790
+ var topLevelDefinitions = responseBody['pending_first_time_events'];
25791
+ if (topLevelDefinitions && topLevelDefinitions.length > 0) {
25792
+ _.each(topLevelDefinitions, function(def) {
25793
+ var flagKey = def['flag_key'];
25794
+ var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
25714
25795
 
25715
- // Skip if this specific event has already been activated this session
25716
- if (this.activatedFirstTimeEvents[eventKey]) {
25717
- return;
25718
- }
25796
+ // Skip if this specific event has already been activated this session
25797
+ if (this.activatedFirstTimeEvents[eventKey]) {
25798
+ return;
25799
+ }
25719
25800
 
25720
- // Store pending event definition using composite key
25721
- pendingFirstTimeEvents[eventKey] = {
25722
- 'flag_key': flagKey,
25723
- 'flag_id': def['flag_id'],
25724
- 'project_id': def['project_id'],
25725
- 'first_time_event_hash': def['first_time_event_hash'],
25726
- 'event_name': def['event_name'],
25727
- 'property_filters': def['property_filters'],
25728
- 'pending_variant': def['pending_variant']
25729
- };
25730
- }, this);
25731
- }
25801
+ // Store pending event definition using composite key
25802
+ pendingFirstTimeEvents[eventKey] = {
25803
+ 'flag_key': flagKey,
25804
+ 'flag_id': def['flag_id'],
25805
+ 'project_id': def['project_id'],
25806
+ 'first_time_event_hash': def['first_time_event_hash'],
25807
+ 'event_name': def['event_name'],
25808
+ 'property_filters': def['property_filters'],
25809
+ 'pending_variant': def['pending_variant']
25810
+ };
25811
+ }, this);
25812
+ }
25732
25813
 
25733
- // Preserve any activated orphaned flags (flags that were activated but are no longer in response)
25734
- if (this.activatedFirstTimeEvents) {
25735
- _.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
25736
- var flagKey = getFlagKeyFromPendingEventKey(eventKey);
25737
- if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
25738
- // Keep the activated flag even though it's not in the new response
25739
- flags.set(flagKey, this.flags.get(flagKey));
25740
- }
25741
- }, this);
25742
- }
25814
+ // Preserve any activated orphaned flags (flags that were activated but are no longer in response)
25815
+ if (this.activatedFirstTimeEvents) {
25816
+ _.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
25817
+ var flagKey = getFlagKeyFromPendingEventKey(eventKey);
25818
+ if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
25819
+ // Keep the activated flag even though it's not in the new response
25820
+ flags.set(flagKey, this.flags.get(flagKey));
25821
+ }
25822
+ }, this);
25823
+ }
25743
25824
 
25744
- this.flags = flags;
25745
- this.pendingFirstTimeEvents = pendingFirstTimeEvents;
25746
- this._traceparent = traceparent;
25825
+ this.flags = flags;
25826
+ this.pendingFirstTimeEvents = pendingFirstTimeEvents;
25827
+ this._traceparent = traceparent;
25747
25828
 
25748
- this._loadTargetingIfNeeded();
25749
- }.bind(this)).catch(function(error) {
25750
- this.markFetchComplete();
25751
- logger.error(error);
25752
- }.bind(this));
25829
+ this._loadTargetingIfNeeded();
25753
25830
  }.bind(this)).catch(function(error) {
25754
- this.markFetchComplete();
25755
- logger.error(error);
25831
+ if (this._fetchInProgressStartTime) {
25832
+ this.markFetchComplete();
25833
+ }
25834
+ logger$1.error(error);
25835
+ throw error;
25756
25836
  }.bind(this));
25757
25837
 
25758
25838
  return this.fetchPromise;
25759
25839
  };
25760
25840
 
25841
+ FeatureFlagManager.prototype.loadFlags = function() {
25842
+ if (!this.isSystemEnabled()) {
25843
+ return Promise.resolve();
25844
+ }
25845
+ if (!this.trackedFeatures) {
25846
+ logger$1.error('loadFlags called before init');
25847
+ return Promise.resolve();
25848
+ }
25849
+ if (this._fetchInProgressStartTime) {
25850
+ return this.fetchPromise;
25851
+ }
25852
+ return this.fetchFlags();
25853
+ };
25854
+
25761
25855
  FeatureFlagManager.prototype.markFetchComplete = function() {
25762
25856
  if (!this._fetchInProgressStartTime) {
25763
- logger.error('Fetch in progress started time not set, cannot mark fetch complete');
25857
+ logger$1.error('Fetch in progress started time not set, cannot mark fetch complete');
25764
25858
  return;
25765
25859
  }
25766
25860
  this._fetchStartTime = this._fetchInProgressStartTime;
@@ -25782,7 +25876,7 @@
25782
25876
 
25783
25877
  if (hasPropertyFilters) {
25784
25878
  this.getTargeting().then(function() {
25785
- logger.log('targeting loaded for property filter evaluation');
25879
+ logger$1.log('targeting loaded for property filter evaluation');
25786
25880
  });
25787
25881
  }
25788
25882
  };
@@ -25797,7 +25891,7 @@
25797
25891
  this.loadExtraBundle.bind(this),
25798
25892
  this.targetingSrc
25799
25893
  ).catch(function(error) {
25800
- logger.error('Failed to load targeting: ' + error);
25894
+ logger$1.error('Failed to load targeting: ' + error);
25801
25895
  }.bind(this));
25802
25896
  };
25803
25897
 
@@ -25851,7 +25945,7 @@
25851
25945
 
25852
25946
  // If no targeting library and event has property filters, skip it
25853
25947
  if (!targeting && pendingEvent['property_filters'] && !_.isEmptyObject(pendingEvent['property_filters'])) {
25854
- logger.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
25948
+ logger$1.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
25855
25949
  return;
25856
25950
  }
25857
25951
 
@@ -25874,7 +25968,7 @@
25874
25968
  }
25875
25969
 
25876
25970
  if (matchResult.error) {
25877
- logger.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
25971
+ logger$1.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
25878
25972
  return;
25879
25973
  }
25880
25974
 
@@ -25882,7 +25976,7 @@
25882
25976
  return;
25883
25977
  }
25884
25978
 
25885
- logger.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
25979
+ logger$1.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
25886
25980
 
25887
25981
  var newVariant = {
25888
25982
  'key': pendingEvent['pending_variant']['variant_key'],
@@ -25923,7 +26017,7 @@
25923
26017
  'first_time_event_hash': firstTimeEventHash
25924
26018
  };
25925
26019
 
25926
- logger.log('Recording first-time event for flag: ' + flagId);
26020
+ logger$1.log('Recording first-time event for flag: ' + flagId);
25927
26021
 
25928
26022
  // Fire-and-forget POST request
25929
26023
  this.fetch.call(win, url, {
@@ -25936,14 +26030,14 @@
25936
26030
  'body': JSON.stringify(payload)
25937
26031
  }).catch(function(error) {
25938
26032
  // Silent failure - cohort sync will catch up
25939
- logger.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
26033
+ logger$1.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
25940
26034
  });
25941
26035
  };
25942
26036
 
25943
26037
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
25944
26038
  if (!this.fetchPromise) {
25945
26039
  return new Promise(function(resolve) {
25946
- logger.critical('Feature Flags not initialized');
26040
+ logger$1.critical('Feature Flags not initialized');
25947
26041
  resolve(fallback);
25948
26042
  });
25949
26043
  }
@@ -25951,19 +26045,19 @@
25951
26045
  return this.fetchPromise.then(function() {
25952
26046
  return this.getVariantSync(featureName, fallback);
25953
26047
  }.bind(this)).catch(function(error) {
25954
- logger.error(error);
26048
+ logger$1.error(error);
25955
26049
  return fallback;
25956
26050
  });
25957
26051
  };
25958
26052
 
25959
26053
  FeatureFlagManager.prototype.getVariantSync = function(featureName, fallback) {
25960
26054
  if (!this.areFlagsReady()) {
25961
- logger.log('Flags not loaded yet');
26055
+ logger$1.log('Flags not loaded yet');
25962
26056
  return fallback;
25963
26057
  }
25964
26058
  var feature = this.flags.get(featureName);
25965
26059
  if (!feature) {
25966
- logger.log('No flag found: "' + featureName + '"');
26060
+ logger$1.log('No flag found: "' + featureName + '"');
25967
26061
  return fallback;
25968
26062
  }
25969
26063
  this.trackFeatureCheck(featureName, feature);
@@ -25974,14 +26068,14 @@
25974
26068
  return this.getVariant(featureName, {'value': fallbackValue}).then(function(feature) {
25975
26069
  return feature['value'];
25976
26070
  }).catch(function(error) {
25977
- logger.error(error);
26071
+ logger$1.error(error);
25978
26072
  return fallbackValue;
25979
26073
  });
25980
26074
  };
25981
26075
 
25982
26076
  // TODO remove deprecated method
25983
26077
  FeatureFlagManager.prototype.getFeatureData = function(featureName, fallbackValue) {
25984
- logger.critical('mixpanel.flags.get_feature_data() is deprecated and will be removed in a future release. Use mixpanel.flags.get_variant_value() instead.');
26078
+ logger$1.critical('mixpanel.flags.get_feature_data() is deprecated and will be removed in a future release. Use mixpanel.flags.get_variant_value() instead.');
25985
26079
  return this.getVariantValue(featureName, fallbackValue);
25986
26080
  };
25987
26081
 
@@ -25993,7 +26087,7 @@
25993
26087
  return this.getVariantValue(featureName).then(function() {
25994
26088
  return this.isEnabledSync(featureName, fallbackValue);
25995
26089
  }.bind(this)).catch(function(error) {
25996
- logger.error(error);
26090
+ logger$1.error(error);
25997
26091
  return fallbackValue;
25998
26092
  });
25999
26093
  };
@@ -26002,7 +26096,7 @@
26002
26096
  fallbackValue = fallbackValue || false;
26003
26097
  var val = this.getVariantValueSync(featureName, fallbackValue);
26004
26098
  if (val !== true && val !== false) {
26005
- logger.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
26099
+ logger$1.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
26006
26100
  val = fallbackValue;
26007
26101
  }
26008
26102
  return val;
@@ -26037,6 +26131,13 @@
26037
26131
  this.track('$experiment_started', trackingProperties);
26038
26132
  };
26039
26133
 
26134
+ FeatureFlagManager.prototype.whenReady = function() {
26135
+ if (this.fetchPromise) {
26136
+ return this.fetchPromise;
26137
+ }
26138
+ return Promise.resolve();
26139
+ };
26140
+
26040
26141
  FeatureFlagManager.prototype.minApisSupported = function() {
26041
26142
  return !!this.fetch &&
26042
26143
  typeof Promise !== 'undefined' &&
@@ -26053,7 +26154,9 @@
26053
26154
  FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
26054
26155
  FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
26055
26156
  FeatureFlagManager.prototype['is_enabled_sync'] = FeatureFlagManager.prototype.isEnabledSync;
26157
+ FeatureFlagManager.prototype['load_flags'] = FeatureFlagManager.prototype.loadFlags;
26056
26158
  FeatureFlagManager.prototype['update_context'] = FeatureFlagManager.prototype.updateContext;
26159
+ FeatureFlagManager.prototype['when_ready'] = FeatureFlagManager.prototype.whenReady;
26057
26160
 
26058
26161
  // Deprecated method
26059
26162
  FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
@@ -26064,6 +26167,12 @@
26064
26167
  /* eslint camelcase: "off" */
26065
26168
 
26066
26169
 
26170
+ var logger = console_with_prefix('recorder');
26171
+
26172
+ var IFRAME_HANDSHAKE_REQUEST = 'mp_iframe_handshake_request';
26173
+ var IFRAME_HANDSHAKE_RESPONSE = 'mp_iframe_handshake_response';
26174
+
26175
+
26067
26176
  /**
26068
26177
  * RecorderManager: manages session recording initialization, lifecycle and state
26069
26178
  * @constructor
@@ -26083,6 +26192,8 @@
26083
26192
  this.libBasePath = initOptions.libBasePath;
26084
26193
 
26085
26194
  this._recorder = null;
26195
+ this._parentReplayId = null;
26196
+ this._parentFrameRetryInterval = null;
26086
26197
  };
26087
26198
 
26088
26199
  RecorderManager.prototype.shouldLoadRecorder = function() {
@@ -26136,6 +26247,22 @@
26136
26247
  }, this));
26137
26248
  }, this);
26138
26249
 
26250
+ // Cross-origin iframe handling
26251
+ var allowedOrigins = validateAllowedOrigins(this.getMpConfig('record_allowed_iframe_origins'), logger);
26252
+ var isCrossOriginRecordingEnabled = allowedOrigins.length > 0;
26253
+
26254
+ if (isCrossOriginRecordingEnabled) {
26255
+ // listen for handshake requests from their own child iframes (including nested)
26256
+ this._setupParentFrameListener(allowedOrigins);
26257
+
26258
+ if (win.parent !== win) {
26259
+ // also wait for parent's replay ID
26260
+ this._setupChildFrameListener(allowedOrigins, loadRecorder);
26261
+ this._sendParentFrameRequestWithRetry(allowedOrigins);
26262
+ return PromisePolyfill.resolve();
26263
+ }
26264
+ }
26265
+
26139
26266
  /**
26140
26267
  * If the user is sampled or start_session_recording is called, we always load the recorder since it's guaranteed a recording should start.
26141
26268
  * Otherwise, if the recording registry has any records then it's likely there's a recording in progress or orphaned data that needs to be flushed.
@@ -26255,6 +26382,10 @@
26255
26382
  };
26256
26383
 
26257
26384
  RecorderManager.prototype.getSessionReplayId = function() {
26385
+ // Child iframe uses parent's replay ID
26386
+ if (this._parentReplayId) {
26387
+ return this._parentReplayId;
26388
+ }
26258
26389
  var replay_id = null;
26259
26390
  if (this._recorder) {
26260
26391
  replay_id = this._recorder['replayId'];
@@ -26267,6 +26398,86 @@
26267
26398
  return this._recorder;
26268
26399
  };
26269
26400
 
26401
+ RecorderManager.prototype._setupChildFrameListener = function(allowedOrigins, loadRecorder) {
26402
+ if (this._childFrameMessageHandler) {
26403
+ return;
26404
+ }
26405
+ var self = this;
26406
+ this._childFrameMessageHandler = function(event) {
26407
+ if (allowedOrigins.indexOf(event.origin) === -1) return;
26408
+ var data = event.data;
26409
+ if (data && data['type'] === IFRAME_HANDSHAKE_RESPONSE && data['token'] === self.getMpConfig('token') && data['replayId']) {
26410
+ self._parentReplayId = data['replayId'];
26411
+ if (data['distinctId']) {
26412
+ self.mixpanelInstance['identify'](data['distinctId']);
26413
+ }
26414
+ self._parentFrameRetryActive = false;
26415
+ win.removeEventListener('message', self._childFrameMessageHandler);
26416
+ self._childFrameMessageHandler = null;
26417
+ loadRecorder(true);
26418
+ }
26419
+ };
26420
+ win.addEventListener('message', this._childFrameMessageHandler);
26421
+ };
26422
+
26423
+ RecorderManager.prototype._sendParentFrameRequest = function(allowedOrigins) {
26424
+ var message = {};
26425
+ message['type'] = IFRAME_HANDSHAKE_REQUEST;
26426
+ message['token'] = this.getMpConfig('token');
26427
+ for (var i = 0; i < allowedOrigins.length; i++) {
26428
+ try {
26429
+ win.parent.postMessage(message, allowedOrigins[i]);
26430
+ } catch (e) {
26431
+ // origin mismatch - ignore
26432
+ }
26433
+ }
26434
+ };
26435
+
26436
+ RecorderManager.prototype._sendParentFrameRequestWithRetry = function(allowedOrigins) {
26437
+ var self = this;
26438
+ var maxRetries = 10;
26439
+ var retryCount = 0;
26440
+ var delay = 50;
26441
+ this._parentFrameRetryActive = true;
26442
+
26443
+ this._sendParentFrameRequest(allowedOrigins);
26444
+
26445
+ function scheduleRetry() {
26446
+ setTimeout(function() {
26447
+ if (!self._parentFrameRetryActive || self._parentReplayId || ++retryCount >= maxRetries) {
26448
+ return;
26449
+ }
26450
+ self._sendParentFrameRequest(allowedOrigins);
26451
+ delay *= 2;
26452
+ scheduleRetry();
26453
+ }, delay);
26454
+ }
26455
+ scheduleRetry();
26456
+ };
26457
+
26458
+ RecorderManager.prototype._setupParentFrameListener = function(allowedOrigins) {
26459
+ if (this._parentFrameMessageHandler) {
26460
+ return;
26461
+ }
26462
+ var self = this;
26463
+ this._parentFrameMessageHandler = function(event) {
26464
+ if (allowedOrigins.indexOf(event.origin) === -1) return;
26465
+ var data = event.data;
26466
+ if (data && data['type'] === IFRAME_HANDSHAKE_REQUEST && data['token'] === self.getMpConfig('token')) {
26467
+ var replayId = self.getSessionReplayId();
26468
+ if (replayId) {
26469
+ var response = {};
26470
+ response['type'] = IFRAME_HANDSHAKE_RESPONSE;
26471
+ response['token'] = self.getMpConfig('token');
26472
+ response['replayId'] = replayId;
26473
+ response['distinctId'] = self.getDistinctId();
26474
+ event.source.postMessage(response, event.origin);
26475
+ }
26476
+ }
26477
+ };
26478
+ win.addEventListener('message', this._parentFrameMessageHandler);
26479
+ };
26480
+
26270
26481
  safewrapClass(RecorderManager);
26271
26482
 
26272
26483
  /* eslint camelcase: "off" */
@@ -27641,7 +27852,6 @@
27641
27852
  /** @const */ var SETTING_FALLBACK = 'fallback';
27642
27853
  /** @const */ var SETTING_DISABLED = 'disabled';
27643
27854
 
27644
-
27645
27855
  /*
27646
27856
  * Dynamic... constants? Is that an oxymoron?
27647
27857
  */
@@ -27726,6 +27936,7 @@
27726
27936
  'batch_request_timeout_ms': 90000,
27727
27937
  'batch_autostart': true,
27728
27938
  'hooks': {},
27939
+ 'record_allowed_iframe_origins': [],
27729
27940
  'record_block_class': new RegExp('^(mp-block|fs-exclude|amp-block|rr-block|ph-no-capture)$'),
27730
27941
  'record_block_selector': 'img, video, audio',
27731
27942
  'record_canvas': false,
@@ -29301,7 +29512,9 @@
29301
29512
 
29302
29513
  // check feature flags again if distinct id has changed
29303
29514
  if (new_distinct_id !== previous_distinct_id) {
29304
- this.flags.fetchFlags();
29515
+ this.flags.fetchFlags().catch(function() {
29516
+ console$1.error('[flags] Error fetching flags during identify');
29517
+ });
29305
29518
  }
29306
29519
  };
29307
29520