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
@@ -25,7 +25,7 @@ if (typeof(window) === 'undefined') {
25
25
 
26
26
  var Config = {
27
27
  DEBUG: false,
28
- LIB_VERSION: '2.76.0'
28
+ LIB_VERSION: '2.78.0'
29
29
  };
30
30
 
31
31
  // Window global names for async modules
@@ -10733,13 +10733,7 @@ var MutationBuffer = /*#__PURE__*/ function() {
10733
10733
  };
10734
10734
  while(_this.mapRemoves.length){
10735
10735
  var removedNode = _this.mapRemoves.shift();
10736
- if (removedNode.nodeName === "IFRAME") {
10737
- try {
10738
- _this.iframeManager.removeIframe(removedNode);
10739
- } catch (e2) {}
10740
- } else {
10741
- _this.stylesheetManager.cleanupStylesheetsForRemovedNode(removedNode);
10742
- }
10736
+ _this.cleanupRemovedNode(removedNode);
10743
10737
  _this.mirror.removeNodeFromMap(removedNode);
10744
10738
  }
10745
10739
  for(var _iterator = _create_for_of_iterator_helper_loose(_this.movedSet), _step; !(_step = _iterator()).done;){
@@ -11059,6 +11053,20 @@ var MutationBuffer = /*#__PURE__*/ function() {
11059
11053
  }
11060
11054
  }
11061
11055
  });
11056
+ __publicField$1(this, "cleanupRemovedNode", function(node2) {
11057
+ if (node2.nodeName === "IFRAME") {
11058
+ try {
11059
+ _this.iframeManager.removeIframe(node2);
11060
+ } catch (e2) {}
11061
+ } else {
11062
+ try {
11063
+ _this.stylesheetManager.cleanupStylesheetsForRemovedNode(node2);
11064
+ } catch (e2) {}
11065
+ }
11066
+ node2.childNodes.forEach(function(child) {
11067
+ _this.cleanupRemovedNode(child);
11068
+ });
11069
+ });
11062
11070
  }
11063
11071
  var _proto = MutationBuffer.prototype;
11064
11072
  _proto.init = function init(options) {
@@ -13286,6 +13294,31 @@ var ProcessedNodeManager = /*#__PURE__*/ function() {
13286
13294
  _proto.destroy = function destroy() {};
13287
13295
  return ProcessedNodeManager;
13288
13296
  }();
13297
+ function toOrigin(url) {
13298
+ try {
13299
+ var origin = new URL(url).origin;
13300
+ return origin !== "null" ? origin : null;
13301
+ } catch (e) {
13302
+ return null;
13303
+ }
13304
+ }
13305
+ function buildAllowedOriginSet(origins) {
13306
+ if (!Array.isArray(origins) || origins.length === 0) {
13307
+ throw new Error("[rrweb] allowedIframeOrigins must be a non-empty array of origin strings.");
13308
+ }
13309
+ var set = /* @__PURE__ */ new Set();
13310
+ for(var i2 = 0; i2 < origins.length; i2++){
13311
+ var entry = origins[i2];
13312
+ if (typeof entry !== "string") {
13313
+ throw new Error("[rrweb] allowedIframeOrigins[" + i2 + "] must be a string, got " + (typeof entry === "undefined" ? "undefined" : _type_of(entry)) + ".");
13314
+ }
13315
+ var origin = toOrigin(entry);
13316
+ if (origin) {
13317
+ set.add(origin);
13318
+ }
13319
+ }
13320
+ return Object.freeze(set);
13321
+ }
13289
13322
  var wrappedEmit;
13290
13323
  var takeFullSnapshot$1;
13291
13324
  var canvasManager;
@@ -13307,10 +13340,17 @@ try {
13307
13340
  var mirror = createMirror$2();
13308
13341
  function record(options) {
13309
13342
  if (options === void 0) options = {};
13310
- 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() {
13343
+ 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() {
13311
13344
  return false;
13312
13345
  } : _options_keepIframeSrcFn, _options_ignoreCSSAttributes = options.ignoreCSSAttributes, ignoreCSSAttributes = _options_ignoreCSSAttributes === void 0 ? /* @__PURE__ */ new Set([]) : _options_ignoreCSSAttributes, errorHandler2 = options.errorHandler;
13313
13346
  registerErrorHandler(errorHandler2);
13347
+ var validatedOrigins;
13348
+ if (recordCrossOriginIframes && allowedIframeOrigins && allowedIframeOrigins.length > 0) {
13349
+ validatedOrigins = buildAllowedOriginSet(allowedIframeOrigins);
13350
+ if (validatedOrigins.size === 0) {
13351
+ validatedOrigins = void 0;
13352
+ }
13353
+ }
13314
13354
  var inEmittingFrame = recordCrossOriginIframes ? window.parent === window : true;
13315
13355
  var passEmitsToParent = false;
13316
13356
  if (!inEmittingFrame) {
@@ -13402,7 +13442,14 @@ function record(options) {
13402
13442
  origin: window.location.origin,
13403
13443
  isCheckout: isCheckout
13404
13444
  };
13405
- window.parent.postMessage(message, "*");
13445
+ if (validatedOrigins) {
13446
+ for(var _iterator = _create_for_of_iterator_helper_loose(validatedOrigins), _step; !(_step = _iterator()).done;){
13447
+ var targetOrigin = _step.value;
13448
+ window.parent.postMessage(message, targetOrigin);
13449
+ }
13450
+ } else {
13451
+ window.parent.postMessage(message, "*");
13452
+ }
13406
13453
  }
13407
13454
  if (e2.type === EventType.FullSnapshot) {
13408
13455
  lastFullSnapshotEvent = e2;
@@ -21186,7 +21233,7 @@ function _addOptOutCheck(method, getConfigValue) {
21186
21233
  };
21187
21234
  }
21188
21235
 
21189
- var logger$7 = console_with_prefix('lock');
21236
+ var logger$8 = console_with_prefix('lock');
21190
21237
 
21191
21238
  /**
21192
21239
  * SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
@@ -21238,7 +21285,7 @@ SharedLock.prototype.withLock = function(lockedCB, pid) {
21238
21285
 
21239
21286
  var delay = function(cb) {
21240
21287
  if (new Date().getTime() - startTime > timeoutMS) {
21241
- logger$7.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
21288
+ logger$8.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
21242
21289
  storage.removeItem(keyZ);
21243
21290
  storage.removeItem(keyY);
21244
21291
  loop();
@@ -21385,7 +21432,7 @@ LocalStorageWrapper.prototype.removeItem = function (key) {
21385
21432
  }, this));
21386
21433
  };
21387
21434
 
21388
- var logger$6 = console_with_prefix('batch');
21435
+ var logger$7 = console_with_prefix('batch');
21389
21436
 
21390
21437
  /**
21391
21438
  * RequestQueue: queue for batching API requests with localStorage backup for retries.
@@ -21414,7 +21461,7 @@ var RequestQueue = function (storageKey, options) {
21414
21461
  timeoutMS: options.sharedLockTimeoutMS,
21415
21462
  });
21416
21463
  }
21417
- this.reportError = options.errorReporter || _.bind(logger$6.error, logger$6);
21464
+ this.reportError = options.errorReporter || _.bind(logger$7.error, logger$7);
21418
21465
 
21419
21466
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
21420
21467
 
@@ -21747,7 +21794,7 @@ RequestQueue.prototype.clear = function () {
21747
21794
  // maximum interval between request retries after exponential backoff
21748
21795
  var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
21749
21796
 
21750
- var logger$5 = console_with_prefix('batch');
21797
+ var logger$6 = console_with_prefix('batch');
21751
21798
 
21752
21799
  /**
21753
21800
  * RequestBatcher: manages the queueing, flushing, retry etc of requests of one
@@ -21875,7 +21922,7 @@ RequestBatcher.prototype.sendRequestPromise = function(data, options) {
21875
21922
  */
21876
21923
  RequestBatcher.prototype.flush = function(options) {
21877
21924
  if (this.requestInProgress) {
21878
- logger$5.log('Flush: Request already in progress');
21925
+ logger$6.log('Flush: Request already in progress');
21879
21926
  return PromisePolyfill.resolve();
21880
21927
  }
21881
21928
 
@@ -22052,7 +22099,7 @@ RequestBatcher.prototype.flush = function(options) {
22052
22099
  if (options.unloading) {
22053
22100
  requestOptions.transport = 'sendBeacon';
22054
22101
  }
22055
- logger$5.log('MIXPANEL REQUEST:', dataForRequest);
22102
+ logger$6.log('MIXPANEL REQUEST:', dataForRequest);
22056
22103
  return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
22057
22104
  }, this))
22058
22105
  .catch(_.bind(function(err) {
@@ -22065,7 +22112,7 @@ RequestBatcher.prototype.flush = function(options) {
22065
22112
  * Log error to global logger and optional user-defined logger.
22066
22113
  */
22067
22114
  RequestBatcher.prototype.reportError = function(msg, err) {
22068
- logger$5.error.apply(logger$5.error, arguments);
22115
+ logger$6.error.apply(logger$6.error, arguments);
22069
22116
  if (this.errorReporter) {
22070
22117
  try {
22071
22118
  if (!(err instanceof Error)) {
@@ -22073,7 +22120,7 @@ RequestBatcher.prototype.reportError = function(msg, err) {
22073
22120
  }
22074
22121
  this.errorReporter(msg, err);
22075
22122
  } catch(err) {
22076
- logger$5.error(err);
22123
+ logger$6.error(err);
22077
22124
  }
22078
22125
  }
22079
22126
  };
@@ -22090,6 +22137,29 @@ var isRecordingExpired = function(serializedRecording) {
22090
22137
 
22091
22138
  var RECORD_ENQUEUE_THROTTLE_MS = 250;
22092
22139
 
22140
+ var validateAllowedOrigins = function(origins, logger) {
22141
+ if (!_.isArray(origins)) {
22142
+ if (origins) {
22143
+ logger.critical('record_allowed_iframe_origins must be an array of origin strings, cross-origin recording will be disabled.');
22144
+ }
22145
+ return [];
22146
+ }
22147
+ var valid = [];
22148
+ for (var i = 0; i < origins.length; i++) {
22149
+ try {
22150
+ var origin = new URL(origins[i]).origin;
22151
+ if (origin === 'null') {
22152
+ logger.critical(origins[i] + ' has an opaque origin. Skipping this entry.');
22153
+ continue;
22154
+ }
22155
+ valid.push(origin);
22156
+ } catch (e) {
22157
+ logger.critical(origins[i] + ' is not a valid origin URL. Skipping this entry.');
22158
+ }
22159
+ }
22160
+ return valid;
22161
+ };
22162
+
22093
22163
  // stateless utils
22094
22164
  // mostly from https://github.com/mixpanel/mixpanel-js/blob/989ada50f518edab47b9c4fd9535f9fbd5ec5fc0/src/autotrack-utils.js
22095
22165
 
@@ -22195,7 +22265,7 @@ var EVENT_HANDLER_ATTRIBUTES = [
22195
22265
 
22196
22266
  var MAX_DEPTH = 5;
22197
22267
 
22198
- var logger$4 = console_with_prefix('autocapture');
22268
+ var logger$5 = console_with_prefix('autocapture');
22199
22269
 
22200
22270
 
22201
22271
  function getClasses(el) {
@@ -22459,7 +22529,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
22459
22529
  return false;
22460
22530
  }
22461
22531
  } catch (err) {
22462
- logger$4.critical('Error while checking element in allowElementCallback', err);
22532
+ logger$5.critical('Error while checking element in allowElementCallback', err);
22463
22533
  return false;
22464
22534
  }
22465
22535
  }
@@ -22476,7 +22546,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
22476
22546
  return true;
22477
22547
  }
22478
22548
  } catch (err) {
22479
- logger$4.critical('Error while checking selector: ' + sel, err);
22549
+ logger$5.critical('Error while checking selector: ' + sel, err);
22480
22550
  }
22481
22551
  }
22482
22552
  return false;
@@ -22491,7 +22561,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
22491
22561
  return true;
22492
22562
  }
22493
22563
  } catch (err) {
22494
- logger$4.critical('Error while checking element in blockElementCallback', err);
22564
+ logger$5.critical('Error while checking element in blockElementCallback', err);
22495
22565
  return true;
22496
22566
  }
22497
22567
  }
@@ -22505,7 +22575,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
22505
22575
  return true;
22506
22576
  }
22507
22577
  } catch (err) {
22508
- logger$4.critical('Error while checking selector: ' + sel, err);
22578
+ logger$5.critical('Error while checking selector: ' + sel, err);
22509
22579
  }
22510
22580
  }
22511
22581
  }
@@ -23061,7 +23131,7 @@ function shouldMaskText(element, privacyConfig) {
23061
23131
  *
23062
23132
  */
23063
23133
 
23064
- var logger$3 = console_with_prefix('network-plugin');
23134
+ var logger$4 = console_with_prefix('network-plugin');
23065
23135
 
23066
23136
  /**
23067
23137
  * Get the time origin for converting performance timestamps to absolute timestamps.
@@ -23213,7 +23283,7 @@ function truncateBody(str) {
23213
23283
  return str;
23214
23284
  }
23215
23285
  if (str.length > MAX_BODY_SIZE) {
23216
- logger$3.error('Body truncated from ' + str.length + ' to ' + MAX_BODY_SIZE + ' characters');
23286
+ logger$4.error('Body truncated from ' + str.length + ' to ' + MAX_BODY_SIZE + ' characters');
23217
23287
  return str.substring(0, MAX_BODY_SIZE) + '... [truncated]';
23218
23288
  }
23219
23289
  return str;
@@ -23227,7 +23297,7 @@ function truncateBody(str) {
23227
23297
  */
23228
23298
  function initPerformanceObserver(cb, win, options) {
23229
23299
  if (!win.PerformanceObserver) {
23230
- logger$3.error('PerformanceObserver not supported');
23300
+ logger$4.error('PerformanceObserver not supported');
23231
23301
  return function() {
23232
23302
  //
23233
23303
  };
@@ -23380,7 +23450,7 @@ function getRequestPerformanceEntry(win, initiatorType, url, after, before, atte
23380
23450
  attempt = 0;
23381
23451
  }
23382
23452
  if (attempt > 10) {
23383
- logger$3.error('Cannot find performance entry');
23453
+ logger$4.error('Cannot find performance entry');
23384
23454
  return Promise.resolve(null);
23385
23455
  }
23386
23456
  var urlPerformanceEntries = /** @type {PerformanceResourceTiming[]} */ (
@@ -23501,7 +23571,7 @@ function initXhrObserver(cb, win, options) {
23501
23571
  )
23502
23572
  .then(function(entry) {
23503
23573
  if (!entry) {
23504
- logger$3.error('Failed to get performance entry for XHR request to ' + req.url);
23574
+ logger$4.error('Failed to get performance entry for XHR request to ' + req.url);
23505
23575
  return;
23506
23576
  }
23507
23577
  /** @type {NetworkRequest} */
@@ -23521,7 +23591,7 @@ function initXhrObserver(cb, win, options) {
23521
23591
  cb({ requests: [request] });
23522
23592
  })
23523
23593
  .catch(function(e) {
23524
- logger$3.error('Error recording XHR request to ' + req.url + ': ' + String(e));
23594
+ logger$4.error('Error recording XHR request to ' + req.url + ': ' + String(e));
23525
23595
  });
23526
23596
  });
23527
23597
 
@@ -23613,7 +23683,7 @@ function initFetchObserver(cb, win, options) {
23613
23683
  })
23614
23684
  .then(function(entry) {
23615
23685
  if (!entry) {
23616
- logger$3.error('Failed to get performance entry for fetch request to ' + req.url);
23686
+ logger$4.error('Failed to get performance entry for fetch request to ' + req.url);
23617
23687
  return;
23618
23688
  }
23619
23689
  /** @type {NetworkRequest} */
@@ -23633,7 +23703,7 @@ function initFetchObserver(cb, win, options) {
23633
23703
  cb({ requests: [request] });
23634
23704
  })
23635
23705
  .catch(function (e) {
23636
- logger$3.error('Error recording fetch request to ' + req.url + ': ' + String(e));
23706
+ logger$4.error('Error recording fetch request to ' + req.url + ': ' + String(e));
23637
23707
  });
23638
23708
 
23639
23709
  return originalFetchPromise;
@@ -23706,7 +23776,7 @@ var getRecordNetworkPlugin = function(options) {
23706
23776
  */
23707
23777
 
23708
23778
 
23709
- var logger$2 = console_with_prefix('recorder');
23779
+ var logger$3 = console_with_prefix('recorder');
23710
23780
  var CompressionStream = win['CompressionStream'];
23711
23781
 
23712
23782
  var RECORDER_BATCHER_LIB_CONFIG = {
@@ -23886,14 +23956,14 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
23886
23956
  }
23887
23957
 
23888
23958
  if (this._stopRecording !== null) {
23889
- logger$2.log('Recording already in progress, skipping startRecording.');
23959
+ logger$3.log('Recording already in progress, skipping startRecording.');
23890
23960
  return;
23891
23961
  }
23892
23962
 
23893
23963
  this.recordMaxMs = this.getConfig('record_max_ms');
23894
23964
  if (this.recordMaxMs > MAX_RECORDING_MS) {
23895
23965
  this.recordMaxMs = MAX_RECORDING_MS;
23896
- logger$2.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
23966
+ logger$3.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
23897
23967
  }
23898
23968
 
23899
23969
  if (!this.maxExpires) {
@@ -23957,6 +24027,8 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
23957
24027
  );
23958
24028
  }
23959
24029
 
24030
+ var validatedOrigins = validateAllowedOrigins(this.getConfig('record_allowed_iframe_origins'), logger$3);
24031
+
23960
24032
  try {
23961
24033
  this._stopRecording = this._rrwebRecord({
23962
24034
  'emit': function (ev) {
@@ -23991,6 +24063,8 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
23991
24063
  'maskTextSelector': '*',
23992
24064
  'maskInputFn': this._getMaskFn(shouldMaskInput, privacyConfig),
23993
24065
  'maskTextFn': this._getMaskFn(shouldMaskText, privacyConfig),
24066
+ 'recordCrossOriginIframes': validatedOrigins.length > 0,
24067
+ 'allowedIframeOrigins': validatedOrigins,
23994
24068
  'recordCanvas': this.getConfig('record_canvas'),
23995
24069
  'sampling': {
23996
24070
  'canvas': 15
@@ -24215,14 +24289,14 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
24215
24289
 
24216
24290
 
24217
24291
  SessionRecording.prototype.reportError = function(msg, err) {
24218
- logger$2.error.apply(logger$2.error, arguments);
24292
+ logger$3.error.apply(logger$3.error, arguments);
24219
24293
  try {
24220
24294
  if (!err && !(msg instanceof Error)) {
24221
24295
  msg = new Error(msg);
24222
24296
  }
24223
24297
  this.getConfig('error_reporter')(msg, err);
24224
24298
  } catch(err) {
24225
- logger$2.error(err);
24299
+ logger$3.error(err);
24226
24300
  }
24227
24301
  };
24228
24302
 
@@ -24251,7 +24325,7 @@ SessionRecording.prototype._getRecordMinMs = function() {
24251
24325
  var configValue = this.getConfig('record_min_ms');
24252
24326
 
24253
24327
  if (configValue > MAX_VALUE_FOR_MIN_RECORDING_MS) {
24254
- logger$2.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
24328
+ logger$3.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
24255
24329
  return MAX_VALUE_FOR_MIN_RECORDING_MS;
24256
24330
  }
24257
24331
 
@@ -24414,7 +24488,7 @@ RecordingRegistry.prototype.flushInactiveRecordings = function () {
24414
24488
  .catch(this.handleError.bind(this));
24415
24489
  };
24416
24490
 
24417
- var logger$1 = console_with_prefix('recorder');
24491
+ var logger$2 = console_with_prefix('recorder');
24418
24492
 
24419
24493
  /**
24420
24494
  * Recorder API: bundles rrweb and and exposes methods to start and stop recordings.
@@ -24430,7 +24504,7 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
24430
24504
  */
24431
24505
  this.recordingRegistry = new RecordingRegistry({
24432
24506
  mixpanelInstance: this.mixpanelInstance,
24433
- errorReporter: logger$1.error,
24507
+ errorReporter: logger$2.error,
24434
24508
  sharedLockStorage: sharedLockStorage
24435
24509
  });
24436
24510
  this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
@@ -24442,17 +24516,17 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
24442
24516
  MixpanelRecorder.prototype.startRecording = function(options) {
24443
24517
  options = options || {};
24444
24518
  if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
24445
- logger$1.log('Recording already in progress, skipping startRecording.');
24519
+ logger$2.log('Recording already in progress, skipping startRecording.');
24446
24520
  return;
24447
24521
  }
24448
24522
 
24449
24523
  var onIdleTimeout = function () {
24450
- logger$1.log('Idle timeout reached, restarting recording.');
24524
+ logger$2.log('Idle timeout reached, restarting recording.');
24451
24525
  this.resetRecording();
24452
24526
  }.bind(this);
24453
24527
 
24454
24528
  var onMaxLengthReached = function () {
24455
- logger$1.log('Max recording length reached, stopping recording.');
24529
+ logger$2.log('Max recording length reached, stopping recording.');
24456
24530
  this.resetRecording();
24457
24531
  }.bind(this);
24458
24532
 
@@ -24522,7 +24596,7 @@ MixpanelRecorder.prototype.resumeRecording = function (startNewIfInactive) {
24522
24596
  } else if (startNewIfInactive) {
24523
24597
  return this.startRecording({shouldStopBatcher: false});
24524
24598
  } else {
24525
- logger$1.log('No resumable recording found.');
24599
+ logger$2.log('No resumable recording found.');
24526
24600
  return null;
24527
24601
  }
24528
24602
  }.bind(this));
@@ -25187,7 +25261,7 @@ ShadowDOMObserver.prototype.observeShadowRoot = function(shadowRoot) {
25187
25261
  observer.observe(shadowRoot, this.observerConfig);
25188
25262
  this.shadowObservers.push(observer);
25189
25263
  } catch (e) {
25190
- logger$4.critical('Error while observing shadow root', e);
25264
+ logger$5.critical('Error while observing shadow root', e);
25191
25265
  }
25192
25266
  };
25193
25267
 
@@ -25198,7 +25272,7 @@ ShadowDOMObserver.prototype.start = function() {
25198
25272
  }
25199
25273
 
25200
25274
  if (!weakSetSupported()) {
25201
- logger$4.critical('Shadow DOM observation unavailable: WeakSet not supported');
25275
+ logger$5.critical('Shadow DOM observation unavailable: WeakSet not supported');
25202
25276
  return;
25203
25277
  }
25204
25278
 
@@ -25214,7 +25288,7 @@ ShadowDOMObserver.prototype.stop = function() {
25214
25288
  try {
25215
25289
  this.shadowObservers[i].disconnect();
25216
25290
  } catch (e) {
25217
- logger$4.critical('Error while disconnecting shadow DOM observer', e);
25291
+ logger$5.critical('Error while disconnecting shadow DOM observer', e);
25218
25292
  }
25219
25293
  }
25220
25294
  this.shadowObservers = [];
@@ -25402,7 +25476,7 @@ DeadClickTracker.prototype.startTracking = function() {
25402
25476
 
25403
25477
  this.mutationObserver.observe(document.body || document.documentElement, MUTATION_OBSERVER_CONFIG);
25404
25478
  } catch (e) {
25405
- logger$4.critical('Error while setting up mutation observer', e);
25479
+ logger$5.critical('Error while setting up mutation observer', e);
25406
25480
  }
25407
25481
  }
25408
25482
 
@@ -25417,7 +25491,7 @@ DeadClickTracker.prototype.startTracking = function() {
25417
25491
  );
25418
25492
  this.shadowDOMObserver.start();
25419
25493
  } catch (e) {
25420
- logger$4.critical('Error while setting up shadow DOM observer', e);
25494
+ logger$5.critical('Error while setting up shadow DOM observer', e);
25421
25495
  this.shadowDOMObserver = null;
25422
25496
  }
25423
25497
  }
@@ -25444,7 +25518,7 @@ DeadClickTracker.prototype.stopTracking = function() {
25444
25518
  try {
25445
25519
  listener.target.removeEventListener(listener.event, listener.handler, listener.options);
25446
25520
  } catch (e) {
25447
- logger$4.critical('Error while removing event listener', e);
25521
+ logger$5.critical('Error while removing event listener', e);
25448
25522
  }
25449
25523
  }
25450
25524
  this.eventListeners = [];
@@ -25453,7 +25527,7 @@ DeadClickTracker.prototype.stopTracking = function() {
25453
25527
  try {
25454
25528
  this.mutationObserver.disconnect();
25455
25529
  } catch (e) {
25456
- logger$4.critical('Error while disconnecting mutation observer', e);
25530
+ logger$5.critical('Error while disconnecting mutation observer', e);
25457
25531
  }
25458
25532
  this.mutationObserver = null;
25459
25533
  }
@@ -25462,7 +25536,7 @@ DeadClickTracker.prototype.stopTracking = function() {
25462
25536
  try {
25463
25537
  this.shadowDOMObserver.stop();
25464
25538
  } catch (e) {
25465
- logger$4.critical('Error while stopping shadow DOM observer', e);
25539
+ logger$5.critical('Error while stopping shadow DOM observer', e);
25466
25540
  }
25467
25541
  this.shadowDOMObserver = null;
25468
25542
  }
@@ -25540,7 +25614,7 @@ var Autocapture = function(mp) {
25540
25614
 
25541
25615
  Autocapture.prototype.init = function() {
25542
25616
  if (!minDOMApisSupported()) {
25543
- logger$4.critical('Autocapture unavailable: missing required DOM APIs');
25617
+ logger$5.critical('Autocapture unavailable: missing required DOM APIs');
25544
25618
  return;
25545
25619
  }
25546
25620
  this.initPageListeners();
@@ -25580,7 +25654,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
25580
25654
  try {
25581
25655
  return !urlMatchesRegexList(currentUrl, allowUrlRegexes);
25582
25656
  } catch (err) {
25583
- logger$4.critical('Error while checking block URL regexes: ', err);
25657
+ logger$5.critical('Error while checking block URL regexes: ', err);
25584
25658
  return true;
25585
25659
  }
25586
25660
  }
@@ -25593,7 +25667,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
25593
25667
  try {
25594
25668
  return urlMatchesRegexList(currentUrl, blockUrlRegexes);
25595
25669
  } catch (err) {
25596
- logger$4.critical('Error while checking block URL regexes: ', err);
25670
+ logger$5.critical('Error while checking block URL regexes: ', err);
25597
25671
  return true;
25598
25672
  }
25599
25673
  };
@@ -25731,7 +25805,7 @@ Autocapture.prototype._initScrollDepthTracking = function() {
25731
25805
  return;
25732
25806
  }
25733
25807
 
25734
- logger$4.log('Initializing scroll depth tracking');
25808
+ logger$5.log('Initializing scroll depth tracking');
25735
25809
 
25736
25810
  this.maxScrollViewDepth = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
25737
25811
 
@@ -25757,7 +25831,7 @@ Autocapture.prototype.initClickTracking = function() {
25757
25831
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
25758
25832
  return;
25759
25833
  }
25760
- logger$4.log('Initializing click tracking');
25834
+ logger$5.log('Initializing click tracking');
25761
25835
 
25762
25836
  this.listenerClick = function(ev) {
25763
25837
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
@@ -25776,7 +25850,7 @@ Autocapture.prototype.initDeadClickTracking = function() {
25776
25850
  return;
25777
25851
  }
25778
25852
 
25779
- logger$4.log('Initializing dead click tracking');
25853
+ logger$5.log('Initializing dead click tracking');
25780
25854
  if (!this._deadClickTracker) {
25781
25855
  this._deadClickTracker = new DeadClickTracker(function(deadClickEvent) {
25782
25856
  this.trackDomEvent(deadClickEvent, MP_EV_DEAD_CLICK);
@@ -25810,7 +25884,7 @@ Autocapture.prototype.initInputTracking = function() {
25810
25884
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
25811
25885
  return;
25812
25886
  }
25813
- logger$4.log('Initializing input tracking');
25887
+ logger$5.log('Initializing input tracking');
25814
25888
 
25815
25889
  this.listenerChange = function(ev) {
25816
25890
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -25827,7 +25901,7 @@ Autocapture.prototype.initPageviewTracking = function() {
25827
25901
  if (!this.pageviewTrackingConfig()) {
25828
25902
  return;
25829
25903
  }
25830
- logger$4.log('Initializing pageview tracking');
25904
+ logger$5.log('Initializing pageview tracking');
25831
25905
 
25832
25906
  var previousTrackedUrl = '';
25833
25907
  var tracked = false;
@@ -25862,7 +25936,7 @@ Autocapture.prototype.initPageviewTracking = function() {
25862
25936
  }
25863
25937
  if (didPathChange) {
25864
25938
  this.lastScrollCheckpoint = 0;
25865
- logger$4.log('Path change: re-initializing scroll depth checkpoints');
25939
+ logger$5.log('Path change: re-initializing scroll depth checkpoints');
25866
25940
  }
25867
25941
  }
25868
25942
  }.bind(this));
@@ -25877,7 +25951,7 @@ Autocapture.prototype.initRageClickTracking = function() {
25877
25951
  return;
25878
25952
  }
25879
25953
 
25880
- logger$4.log('Initializing rage click tracking');
25954
+ logger$5.log('Initializing rage click tracking');
25881
25955
  if (!this._rageClickTracker) {
25882
25956
  this._rageClickTracker = new RageClickTracker();
25883
25957
  }
@@ -25907,7 +25981,7 @@ Autocapture.prototype.initScrollTracking = function() {
25907
25981
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
25908
25982
  return;
25909
25983
  }
25910
- logger$4.log('Initializing scroll tracking');
25984
+ logger$5.log('Initializing scroll tracking');
25911
25985
  this.lastScrollCheckpoint = 0;
25912
25986
 
25913
25987
  var scrollTrackFunction = function() {
@@ -25944,7 +26018,7 @@ Autocapture.prototype.initScrollTracking = function() {
25944
26018
  }
25945
26019
  }
25946
26020
  } catch (err) {
25947
- logger$4.critical('Error while calculating scroll percentage', err);
26021
+ logger$5.critical('Error while calculating scroll percentage', err);
25948
26022
  }
25949
26023
  if (shouldTrack) {
25950
26024
  this.mp.track(MP_EV_SCROLL, props);
@@ -25962,7 +26036,7 @@ Autocapture.prototype.initSubmitTracking = function() {
25962
26036
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
25963
26037
  return;
25964
26038
  }
25965
- logger$4.log('Initializing submit tracking');
26039
+ logger$5.log('Initializing submit tracking');
25966
26040
 
25967
26041
  this.listenerSubmit = function(ev) {
25968
26042
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -25984,7 +26058,7 @@ Autocapture.prototype.initPageLeaveTracking = function() {
25984
26058
  return;
25985
26059
  }
25986
26060
 
25987
- logger$4.log('Initializing page visibility tracking.');
26061
+ logger$5.log('Initializing page visibility tracking.');
25988
26062
  this._initScrollDepthTracking();
25989
26063
  var previousTrackedUrl = _.info.currentUrl();
25990
26064
 
@@ -26069,7 +26143,7 @@ var getTargetingPromise = function(loadExtraBundle, targetingSrc) {
26069
26143
  return win[TARGETING_GLOBAL_NAME];
26070
26144
  };
26071
26145
 
26072
- var logger = console_with_prefix('flags');
26146
+ var logger$1 = console_with_prefix('flags');
26073
26147
  var FLAGS_CONFIG_KEY = 'flags';
26074
26148
 
26075
26149
  var CONFIG_CONTEXT = 'context';
@@ -26112,12 +26186,14 @@ var FeatureFlagManager = function(initOptions) {
26112
26186
 
26113
26187
  FeatureFlagManager.prototype.init = function() {
26114
26188
  if (!this.minApisSupported()) {
26115
- logger.critical('Feature Flags unavailable: missing minimum required APIs');
26189
+ logger$1.critical('Feature Flags unavailable: missing minimum required APIs');
26116
26190
  return;
26117
26191
  }
26118
26192
 
26119
26193
  this.flags = null;
26120
- this.fetchFlags();
26194
+ this.fetchFlags().catch(function() {
26195
+ logger$1.error('Error fetching flags during init');
26196
+ });
26121
26197
 
26122
26198
  this.trackedFeatures = new Set();
26123
26199
  this.pendingFirstTimeEvents = {};
@@ -26147,7 +26223,7 @@ FeatureFlagManager.prototype.isSystemEnabled = function() {
26147
26223
 
26148
26224
  FeatureFlagManager.prototype.updateContext = function(newContext, options) {
26149
26225
  if (!this.isSystemEnabled()) {
26150
- logger.critical('Feature Flags not enabled, cannot update context');
26226
+ logger$1.critical('Feature Flags not enabled, cannot update context');
26151
26227
  return Promise.resolve();
26152
26228
  }
26153
26229
 
@@ -26158,13 +26234,17 @@ FeatureFlagManager.prototype.updateContext = function(newContext, options) {
26158
26234
  var oldContext = (options && options['replace']) ? {} : this.getConfig(CONFIG_CONTEXT);
26159
26235
  ffConfig[CONFIG_CONTEXT] = _.extend({}, oldContext, newContext);
26160
26236
 
26161
- this.setMpConfig(FLAGS_CONFIG_KEY, ffConfig);
26162
- return this.fetchFlags();
26237
+ var configUpdate = {};
26238
+ configUpdate[FLAGS_CONFIG_KEY] = ffConfig;
26239
+ this.setMpConfig(configUpdate);
26240
+ return this.fetchFlags().catch(function() {
26241
+ logger$1.error('Error fetching flags during updateContext');
26242
+ });
26163
26243
  };
26164
26244
 
26165
26245
  FeatureFlagManager.prototype.areFlagsReady = function() {
26166
26246
  if (!this.isSystemEnabled()) {
26167
- logger.error('Feature Flags not enabled');
26247
+ logger$1.error('Feature Flags not enabled');
26168
26248
  }
26169
26249
  return !!this.flags;
26170
26250
  };
@@ -26177,7 +26257,7 @@ FeatureFlagManager.prototype.fetchFlags = function() {
26177
26257
  var distinctId = this.getMpProperty('distinct_id');
26178
26258
  var deviceId = this.getMpProperty('$device_id');
26179
26259
  var traceparent = generateTraceparent();
26180
- logger.log('Fetching flags for distinct ID: ' + distinctId);
26260
+ logger$1.log('Fetching flags for distinct ID: ' + distinctId);
26181
26261
 
26182
26262
  var context = _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT));
26183
26263
  var searchParams = new URLSearchParams();
@@ -26196,99 +26276,113 @@ FeatureFlagManager.prototype.fetchFlags = function() {
26196
26276
  }
26197
26277
  }).then(function(response) {
26198
26278
  this.markFetchComplete();
26199
- return response.json().then(function(responseBody) {
26200
- var responseFlags = responseBody['flags'];
26201
- if (!responseFlags) {
26202
- throw new Error('No flags in API response');
26203
- }
26204
- var flags = new Map();
26205
- var pendingFirstTimeEvents = {};
26206
-
26207
- // Process flags from response
26208
- _.each(responseFlags, function(data, key) {
26209
- // Check if this flag has any activated first-time events this session
26210
- var hasActivatedEvent = false;
26211
- var prefix = key + ':';
26212
- _.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
26213
- if (eventKey.startsWith(prefix)) {
26214
- hasActivatedEvent = true;
26215
- }
26216
- });
26279
+ return response.json();
26280
+ }.bind(this)).then(function(responseBody) {
26281
+ var responseFlags = responseBody['flags'];
26282
+ if (!responseFlags) {
26283
+ throw new Error('No flags in API response');
26284
+ }
26285
+ var flags = new Map();
26286
+ var pendingFirstTimeEvents = {};
26287
+
26288
+ // Process flags from response
26289
+ _.each(responseFlags, function(data, key) {
26290
+ // Check if this flag has any activated first-time events this session
26291
+ var hasActivatedEvent = false;
26292
+ var prefix = key + ':';
26293
+ _.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
26294
+ if (eventKey.startsWith(prefix)) {
26295
+ hasActivatedEvent = true;
26296
+ }
26297
+ });
26217
26298
 
26218
- if (hasActivatedEvent) {
26219
- // Preserve the activated variant, don't overwrite with server's current variant
26220
- var currentFlag = this.flags && this.flags.get(key);
26221
- if (currentFlag) {
26222
- flags.set(key, currentFlag);
26223
- }
26224
- } else {
26225
- // Use server's current variant
26226
- flags.set(key, {
26227
- 'key': data['variant_key'],
26228
- 'value': data['variant_value'],
26229
- 'experiment_id': data['experiment_id'],
26230
- 'is_experiment_active': data['is_experiment_active'],
26231
- 'is_qa_tester': data['is_qa_tester']
26232
- });
26299
+ if (hasActivatedEvent) {
26300
+ // Preserve the activated variant, don't overwrite with server's current variant
26301
+ var currentFlag = this.flags && this.flags.get(key);
26302
+ if (currentFlag) {
26303
+ flags.set(key, currentFlag);
26233
26304
  }
26234
- }, this);
26305
+ } else {
26306
+ // Use server's current variant
26307
+ flags.set(key, {
26308
+ 'key': data['variant_key'],
26309
+ 'value': data['variant_value'],
26310
+ 'experiment_id': data['experiment_id'],
26311
+ 'is_experiment_active': data['is_experiment_active'],
26312
+ 'is_qa_tester': data['is_qa_tester']
26313
+ });
26314
+ }
26315
+ }, this);
26235
26316
 
26236
- // Process top-level pending_first_time_events array
26237
- var topLevelDefinitions = responseBody['pending_first_time_events'];
26238
- if (topLevelDefinitions && topLevelDefinitions.length > 0) {
26239
- _.each(topLevelDefinitions, function(def) {
26240
- var flagKey = def['flag_key'];
26241
- var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
26317
+ // Process top-level pending_first_time_events array
26318
+ var topLevelDefinitions = responseBody['pending_first_time_events'];
26319
+ if (topLevelDefinitions && topLevelDefinitions.length > 0) {
26320
+ _.each(topLevelDefinitions, function(def) {
26321
+ var flagKey = def['flag_key'];
26322
+ var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
26242
26323
 
26243
- // Skip if this specific event has already been activated this session
26244
- if (this.activatedFirstTimeEvents[eventKey]) {
26245
- return;
26246
- }
26324
+ // Skip if this specific event has already been activated this session
26325
+ if (this.activatedFirstTimeEvents[eventKey]) {
26326
+ return;
26327
+ }
26247
26328
 
26248
- // Store pending event definition using composite key
26249
- pendingFirstTimeEvents[eventKey] = {
26250
- 'flag_key': flagKey,
26251
- 'flag_id': def['flag_id'],
26252
- 'project_id': def['project_id'],
26253
- 'first_time_event_hash': def['first_time_event_hash'],
26254
- 'event_name': def['event_name'],
26255
- 'property_filters': def['property_filters'],
26256
- 'pending_variant': def['pending_variant']
26257
- };
26258
- }, this);
26259
- }
26329
+ // Store pending event definition using composite key
26330
+ pendingFirstTimeEvents[eventKey] = {
26331
+ 'flag_key': flagKey,
26332
+ 'flag_id': def['flag_id'],
26333
+ 'project_id': def['project_id'],
26334
+ 'first_time_event_hash': def['first_time_event_hash'],
26335
+ 'event_name': def['event_name'],
26336
+ 'property_filters': def['property_filters'],
26337
+ 'pending_variant': def['pending_variant']
26338
+ };
26339
+ }, this);
26340
+ }
26260
26341
 
26261
- // Preserve any activated orphaned flags (flags that were activated but are no longer in response)
26262
- if (this.activatedFirstTimeEvents) {
26263
- _.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
26264
- var flagKey = getFlagKeyFromPendingEventKey(eventKey);
26265
- if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
26266
- // Keep the activated flag even though it's not in the new response
26267
- flags.set(flagKey, this.flags.get(flagKey));
26268
- }
26269
- }, this);
26270
- }
26342
+ // Preserve any activated orphaned flags (flags that were activated but are no longer in response)
26343
+ if (this.activatedFirstTimeEvents) {
26344
+ _.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
26345
+ var flagKey = getFlagKeyFromPendingEventKey(eventKey);
26346
+ if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
26347
+ // Keep the activated flag even though it's not in the new response
26348
+ flags.set(flagKey, this.flags.get(flagKey));
26349
+ }
26350
+ }, this);
26351
+ }
26271
26352
 
26272
- this.flags = flags;
26273
- this.pendingFirstTimeEvents = pendingFirstTimeEvents;
26274
- this._traceparent = traceparent;
26353
+ this.flags = flags;
26354
+ this.pendingFirstTimeEvents = pendingFirstTimeEvents;
26355
+ this._traceparent = traceparent;
26275
26356
 
26276
- this._loadTargetingIfNeeded();
26277
- }.bind(this)).catch(function(error) {
26278
- this.markFetchComplete();
26279
- logger.error(error);
26280
- }.bind(this));
26357
+ this._loadTargetingIfNeeded();
26281
26358
  }.bind(this)).catch(function(error) {
26282
- this.markFetchComplete();
26283
- logger.error(error);
26359
+ if (this._fetchInProgressStartTime) {
26360
+ this.markFetchComplete();
26361
+ }
26362
+ logger$1.error(error);
26363
+ throw error;
26284
26364
  }.bind(this));
26285
26365
 
26286
26366
  return this.fetchPromise;
26287
26367
  };
26288
26368
 
26369
+ FeatureFlagManager.prototype.loadFlags = function() {
26370
+ if (!this.isSystemEnabled()) {
26371
+ return Promise.resolve();
26372
+ }
26373
+ if (!this.trackedFeatures) {
26374
+ logger$1.error('loadFlags called before init');
26375
+ return Promise.resolve();
26376
+ }
26377
+ if (this._fetchInProgressStartTime) {
26378
+ return this.fetchPromise;
26379
+ }
26380
+ return this.fetchFlags();
26381
+ };
26382
+
26289
26383
  FeatureFlagManager.prototype.markFetchComplete = function() {
26290
26384
  if (!this._fetchInProgressStartTime) {
26291
- logger.error('Fetch in progress started time not set, cannot mark fetch complete');
26385
+ logger$1.error('Fetch in progress started time not set, cannot mark fetch complete');
26292
26386
  return;
26293
26387
  }
26294
26388
  this._fetchStartTime = this._fetchInProgressStartTime;
@@ -26310,7 +26404,7 @@ FeatureFlagManager.prototype._loadTargetingIfNeeded = function() {
26310
26404
 
26311
26405
  if (hasPropertyFilters) {
26312
26406
  this.getTargeting().then(function() {
26313
- logger.log('targeting loaded for property filter evaluation');
26407
+ logger$1.log('targeting loaded for property filter evaluation');
26314
26408
  });
26315
26409
  }
26316
26410
  };
@@ -26325,7 +26419,7 @@ FeatureFlagManager.prototype.getTargeting = function() {
26325
26419
  this.loadExtraBundle.bind(this),
26326
26420
  this.targetingSrc
26327
26421
  ).catch(function(error) {
26328
- logger.error('Failed to load targeting: ' + error);
26422
+ logger$1.error('Failed to load targeting: ' + error);
26329
26423
  }.bind(this));
26330
26424
  };
26331
26425
 
@@ -26379,7 +26473,7 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
26379
26473
 
26380
26474
  // If no targeting library and event has property filters, skip it
26381
26475
  if (!targeting && pendingEvent['property_filters'] && !_.isEmptyObject(pendingEvent['property_filters'])) {
26382
- logger.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
26476
+ logger$1.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
26383
26477
  return;
26384
26478
  }
26385
26479
 
@@ -26402,7 +26496,7 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
26402
26496
  }
26403
26497
 
26404
26498
  if (matchResult.error) {
26405
- logger.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
26499
+ logger$1.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
26406
26500
  return;
26407
26501
  }
26408
26502
 
@@ -26410,7 +26504,7 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
26410
26504
  return;
26411
26505
  }
26412
26506
 
26413
- logger.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
26507
+ logger$1.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
26414
26508
 
26415
26509
  var newVariant = {
26416
26510
  'key': pendingEvent['pending_variant']['variant_key'],
@@ -26451,7 +26545,7 @@ FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId,
26451
26545
  'first_time_event_hash': firstTimeEventHash
26452
26546
  };
26453
26547
 
26454
- logger.log('Recording first-time event for flag: ' + flagId);
26548
+ logger$1.log('Recording first-time event for flag: ' + flagId);
26455
26549
 
26456
26550
  // Fire-and-forget POST request
26457
26551
  this.fetch.call(win, url, {
@@ -26464,14 +26558,14 @@ FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId,
26464
26558
  'body': JSON.stringify(payload)
26465
26559
  }).catch(function(error) {
26466
26560
  // Silent failure - cohort sync will catch up
26467
- logger.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
26561
+ logger$1.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
26468
26562
  });
26469
26563
  };
26470
26564
 
26471
26565
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
26472
26566
  if (!this.fetchPromise) {
26473
26567
  return new Promise(function(resolve) {
26474
- logger.critical('Feature Flags not initialized');
26568
+ logger$1.critical('Feature Flags not initialized');
26475
26569
  resolve(fallback);
26476
26570
  });
26477
26571
  }
@@ -26479,19 +26573,19 @@ FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
26479
26573
  return this.fetchPromise.then(function() {
26480
26574
  return this.getVariantSync(featureName, fallback);
26481
26575
  }.bind(this)).catch(function(error) {
26482
- logger.error(error);
26576
+ logger$1.error(error);
26483
26577
  return fallback;
26484
26578
  });
26485
26579
  };
26486
26580
 
26487
26581
  FeatureFlagManager.prototype.getVariantSync = function(featureName, fallback) {
26488
26582
  if (!this.areFlagsReady()) {
26489
- logger.log('Flags not loaded yet');
26583
+ logger$1.log('Flags not loaded yet');
26490
26584
  return fallback;
26491
26585
  }
26492
26586
  var feature = this.flags.get(featureName);
26493
26587
  if (!feature) {
26494
- logger.log('No flag found: "' + featureName + '"');
26588
+ logger$1.log('No flag found: "' + featureName + '"');
26495
26589
  return fallback;
26496
26590
  }
26497
26591
  this.trackFeatureCheck(featureName, feature);
@@ -26502,14 +26596,14 @@ FeatureFlagManager.prototype.getVariantValue = function(featureName, fallbackVal
26502
26596
  return this.getVariant(featureName, {'value': fallbackValue}).then(function(feature) {
26503
26597
  return feature['value'];
26504
26598
  }).catch(function(error) {
26505
- logger.error(error);
26599
+ logger$1.error(error);
26506
26600
  return fallbackValue;
26507
26601
  });
26508
26602
  };
26509
26603
 
26510
26604
  // TODO remove deprecated method
26511
26605
  FeatureFlagManager.prototype.getFeatureData = function(featureName, fallbackValue) {
26512
- logger.critical('mixpanel.flags.get_feature_data() is deprecated and will be removed in a future release. Use mixpanel.flags.get_variant_value() instead.');
26606
+ 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.');
26513
26607
  return this.getVariantValue(featureName, fallbackValue);
26514
26608
  };
26515
26609
 
@@ -26521,7 +26615,7 @@ FeatureFlagManager.prototype.isEnabled = function(featureName, fallbackValue) {
26521
26615
  return this.getVariantValue(featureName).then(function() {
26522
26616
  return this.isEnabledSync(featureName, fallbackValue);
26523
26617
  }.bind(this)).catch(function(error) {
26524
- logger.error(error);
26618
+ logger$1.error(error);
26525
26619
  return fallbackValue;
26526
26620
  });
26527
26621
  };
@@ -26530,7 +26624,7 @@ FeatureFlagManager.prototype.isEnabledSync = function(featureName, fallbackValue
26530
26624
  fallbackValue = fallbackValue || false;
26531
26625
  var val = this.getVariantValueSync(featureName, fallbackValue);
26532
26626
  if (val !== true && val !== false) {
26533
- logger.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
26627
+ logger$1.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
26534
26628
  val = fallbackValue;
26535
26629
  }
26536
26630
  return val;
@@ -26565,6 +26659,13 @@ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature)
26565
26659
  this.track('$experiment_started', trackingProperties);
26566
26660
  };
26567
26661
 
26662
+ FeatureFlagManager.prototype.whenReady = function() {
26663
+ if (this.fetchPromise) {
26664
+ return this.fetchPromise;
26665
+ }
26666
+ return Promise.resolve();
26667
+ };
26668
+
26568
26669
  FeatureFlagManager.prototype.minApisSupported = function() {
26569
26670
  return !!this.fetch &&
26570
26671
  typeof Promise !== 'undefined' &&
@@ -26581,7 +26682,9 @@ FeatureFlagManager.prototype['get_variant_value'] = FeatureFlagManager.prototype
26581
26682
  FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
26582
26683
  FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
26583
26684
  FeatureFlagManager.prototype['is_enabled_sync'] = FeatureFlagManager.prototype.isEnabledSync;
26685
+ FeatureFlagManager.prototype['load_flags'] = FeatureFlagManager.prototype.loadFlags;
26584
26686
  FeatureFlagManager.prototype['update_context'] = FeatureFlagManager.prototype.updateContext;
26687
+ FeatureFlagManager.prototype['when_ready'] = FeatureFlagManager.prototype.whenReady;
26585
26688
 
26586
26689
  // Deprecated method
26587
26690
  FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
@@ -26592,6 +26695,12 @@ FeatureFlagManager.prototype['getTargeting'] = FeatureFlagManager.prototype.getT
26592
26695
  /* eslint camelcase: "off" */
26593
26696
 
26594
26697
 
26698
+ var logger = console_with_prefix('recorder');
26699
+
26700
+ var IFRAME_HANDSHAKE_REQUEST = 'mp_iframe_handshake_request';
26701
+ var IFRAME_HANDSHAKE_RESPONSE = 'mp_iframe_handshake_response';
26702
+
26703
+
26595
26704
  /**
26596
26705
  * RecorderManager: manages session recording initialization, lifecycle and state
26597
26706
  * @constructor
@@ -26611,6 +26720,8 @@ var RecorderManager = function(initOptions) {
26611
26720
  this.libBasePath = initOptions.libBasePath;
26612
26721
 
26613
26722
  this._recorder = null;
26723
+ this._parentReplayId = null;
26724
+ this._parentFrameRetryInterval = null;
26614
26725
  };
26615
26726
 
26616
26727
  RecorderManager.prototype.shouldLoadRecorder = function() {
@@ -26664,6 +26775,22 @@ RecorderManager.prototype.checkAndStartSessionRecording = function(force_start,
26664
26775
  }, this));
26665
26776
  }, this);
26666
26777
 
26778
+ // Cross-origin iframe handling
26779
+ var allowedOrigins = validateAllowedOrigins(this.getMpConfig('record_allowed_iframe_origins'), logger);
26780
+ var isCrossOriginRecordingEnabled = allowedOrigins.length > 0;
26781
+
26782
+ if (isCrossOriginRecordingEnabled) {
26783
+ // listen for handshake requests from their own child iframes (including nested)
26784
+ this._setupParentFrameListener(allowedOrigins);
26785
+
26786
+ if (win.parent !== win) {
26787
+ // also wait for parent's replay ID
26788
+ this._setupChildFrameListener(allowedOrigins, loadRecorder);
26789
+ this._sendParentFrameRequestWithRetry(allowedOrigins);
26790
+ return PromisePolyfill.resolve();
26791
+ }
26792
+ }
26793
+
26667
26794
  /**
26668
26795
  * If the user is sampled or start_session_recording is called, we always load the recorder since it's guaranteed a recording should start.
26669
26796
  * 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.
@@ -26783,6 +26910,10 @@ RecorderManager.prototype.getSessionReplayUrl = function() {
26783
26910
  };
26784
26911
 
26785
26912
  RecorderManager.prototype.getSessionReplayId = function() {
26913
+ // Child iframe uses parent's replay ID
26914
+ if (this._parentReplayId) {
26915
+ return this._parentReplayId;
26916
+ }
26786
26917
  var replay_id = null;
26787
26918
  if (this._recorder) {
26788
26919
  replay_id = this._recorder['replayId'];
@@ -26795,6 +26926,86 @@ RecorderManager.prototype.getRecorder = function() {
26795
26926
  return this._recorder;
26796
26927
  };
26797
26928
 
26929
+ RecorderManager.prototype._setupChildFrameListener = function(allowedOrigins, loadRecorder) {
26930
+ if (this._childFrameMessageHandler) {
26931
+ return;
26932
+ }
26933
+ var self = this;
26934
+ this._childFrameMessageHandler = function(event) {
26935
+ if (allowedOrigins.indexOf(event.origin) === -1) return;
26936
+ var data = event.data;
26937
+ if (data && data['type'] === IFRAME_HANDSHAKE_RESPONSE && data['token'] === self.getMpConfig('token') && data['replayId']) {
26938
+ self._parentReplayId = data['replayId'];
26939
+ if (data['distinctId']) {
26940
+ self.mixpanelInstance['identify'](data['distinctId']);
26941
+ }
26942
+ self._parentFrameRetryActive = false;
26943
+ win.removeEventListener('message', self._childFrameMessageHandler);
26944
+ self._childFrameMessageHandler = null;
26945
+ loadRecorder(true);
26946
+ }
26947
+ };
26948
+ win.addEventListener('message', this._childFrameMessageHandler);
26949
+ };
26950
+
26951
+ RecorderManager.prototype._sendParentFrameRequest = function(allowedOrigins) {
26952
+ var message = {};
26953
+ message['type'] = IFRAME_HANDSHAKE_REQUEST;
26954
+ message['token'] = this.getMpConfig('token');
26955
+ for (var i = 0; i < allowedOrigins.length; i++) {
26956
+ try {
26957
+ win.parent.postMessage(message, allowedOrigins[i]);
26958
+ } catch (e) {
26959
+ // origin mismatch - ignore
26960
+ }
26961
+ }
26962
+ };
26963
+
26964
+ RecorderManager.prototype._sendParentFrameRequestWithRetry = function(allowedOrigins) {
26965
+ var self = this;
26966
+ var maxRetries = 10;
26967
+ var retryCount = 0;
26968
+ var delay = 50;
26969
+ this._parentFrameRetryActive = true;
26970
+
26971
+ this._sendParentFrameRequest(allowedOrigins);
26972
+
26973
+ function scheduleRetry() {
26974
+ setTimeout(function() {
26975
+ if (!self._parentFrameRetryActive || self._parentReplayId || ++retryCount >= maxRetries) {
26976
+ return;
26977
+ }
26978
+ self._sendParentFrameRequest(allowedOrigins);
26979
+ delay *= 2;
26980
+ scheduleRetry();
26981
+ }, delay);
26982
+ }
26983
+ scheduleRetry();
26984
+ };
26985
+
26986
+ RecorderManager.prototype._setupParentFrameListener = function(allowedOrigins) {
26987
+ if (this._parentFrameMessageHandler) {
26988
+ return;
26989
+ }
26990
+ var self = this;
26991
+ this._parentFrameMessageHandler = function(event) {
26992
+ if (allowedOrigins.indexOf(event.origin) === -1) return;
26993
+ var data = event.data;
26994
+ if (data && data['type'] === IFRAME_HANDSHAKE_REQUEST && data['token'] === self.getMpConfig('token')) {
26995
+ var replayId = self.getSessionReplayId();
26996
+ if (replayId) {
26997
+ var response = {};
26998
+ response['type'] = IFRAME_HANDSHAKE_RESPONSE;
26999
+ response['token'] = self.getMpConfig('token');
27000
+ response['replayId'] = replayId;
27001
+ response['distinctId'] = self.getDistinctId();
27002
+ event.source.postMessage(response, event.origin);
27003
+ }
27004
+ }
27005
+ };
27006
+ win.addEventListener('message', this._parentFrameMessageHandler);
27007
+ };
27008
+
26798
27009
  safewrapClass(RecorderManager);
26799
27010
 
26800
27011
  /* eslint camelcase: "off" */
@@ -28169,7 +28380,6 @@ var INIT_SNIPPET = 1;
28169
28380
  /** @const */ var SETTING_FALLBACK = 'fallback';
28170
28381
  /** @const */ var SETTING_DISABLED = 'disabled';
28171
28382
 
28172
-
28173
28383
  /*
28174
28384
  * Dynamic... constants? Is that an oxymoron?
28175
28385
  */
@@ -28254,6 +28464,7 @@ var DEFAULT_CONFIG = {
28254
28464
  'batch_request_timeout_ms': 90000,
28255
28465
  'batch_autostart': true,
28256
28466
  'hooks': {},
28467
+ 'record_allowed_iframe_origins': [],
28257
28468
  'record_block_class': new RegExp('^(mp-block|fs-exclude|amp-block|rr-block|ph-no-capture)$'),
28258
28469
  'record_block_selector': 'img, video, audio',
28259
28470
  'record_canvas': false,
@@ -29829,7 +30040,9 @@ MixpanelLib.prototype.identify = function(
29829
30040
 
29830
30041
  // check feature flags again if distinct id has changed
29831
30042
  if (new_distinct_id !== previous_distinct_id) {
29832
- this.flags.fetchFlags();
30043
+ this.flags.fetchFlags().catch(function() {
30044
+ console$1.error('[flags] Error fetching flags during identify');
30045
+ });
29833
30046
  }
29834
30047
  };
29835
30048