mixpanel-browser 2.76.0 → 2.77.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 (49) hide show
  1. package/.github/dependabot.yml +8 -0
  2. package/.github/workflows/integration-tests.yml +2 -2
  3. package/.github/workflows/unit-tests.yml +2 -2
  4. package/CHANGELOG.md +4 -0
  5. package/dist/async-modules/{mixpanel-recorder-bIS4LMGd.js → mixpanel-recorder-DLKbUIEE.js} +84 -10
  6. package/dist/async-modules/mixpanel-recorder-wIWnMDLA.min.js +2 -0
  7. package/dist/async-modules/mixpanel-recorder-wIWnMDLA.min.js.map +1 -0
  8. package/dist/async-modules/{mixpanel-targeting-VOeN7RWY.min.js → mixpanel-targeting-CTcftSJC.min.js} +2 -2
  9. package/dist/async-modules/{mixpanel-targeting-VOeN7RWY.min.js.map → mixpanel-targeting-CTcftSJC.min.js.map} +1 -1
  10. package/dist/async-modules/{mixpanel-targeting-BcAPS-Mz.js → mixpanel-targeting-CmVvUyFM.js} +1 -1
  11. package/dist/mixpanel-core.cjs.d.ts +2 -1
  12. package/dist/mixpanel-core.cjs.js +183 -52
  13. package/dist/mixpanel-recorder.js +84 -10
  14. package/dist/mixpanel-recorder.min.js +1 -1
  15. package/dist/mixpanel-recorder.min.js.map +1 -1
  16. package/dist/mixpanel-targeting.js +1 -1
  17. package/dist/mixpanel-targeting.min.js +1 -1
  18. package/dist/mixpanel-targeting.min.js.map +1 -1
  19. package/dist/mixpanel-with-async-modules.cjs.d.ts +2 -1
  20. package/dist/mixpanel-with-async-modules.cjs.js +185 -54
  21. package/dist/mixpanel-with-async-recorder.cjs.d.ts +2 -1
  22. package/dist/mixpanel-with-async-recorder.cjs.js +185 -54
  23. package/dist/mixpanel-with-recorder.d.ts +2 -1
  24. package/dist/mixpanel-with-recorder.js +272 -90
  25. package/dist/mixpanel-with-recorder.min.d.ts +2 -1
  26. package/dist/mixpanel-with-recorder.min.js +1 -1
  27. package/dist/mixpanel.amd.d.ts +2 -1
  28. package/dist/mixpanel.amd.js +272 -90
  29. package/dist/mixpanel.cjs.d.ts +2 -1
  30. package/dist/mixpanel.cjs.js +272 -90
  31. package/dist/mixpanel.globals.js +185 -54
  32. package/dist/mixpanel.min.js +190 -186
  33. package/dist/mixpanel.module.d.ts +2 -1
  34. package/dist/mixpanel.module.js +272 -90
  35. package/dist/mixpanel.umd.d.ts +2 -1
  36. package/dist/mixpanel.umd.js +272 -90
  37. package/dist/rrweb-bundled.js +61 -9
  38. package/dist/rrweb-compiled.js +56 -9
  39. package/package.json +6 -5
  40. package/src/config.js +1 -1
  41. package/src/index.d.ts +2 -1
  42. package/src/mixpanel-core.js +1 -1
  43. package/src/recorder/session-recording.js +5 -1
  44. package/src/recorder/utils.js +27 -1
  45. package/src/recorder-manager.js +110 -2
  46. package/testServer.js +14 -1
  47. package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js +0 -2
  48. package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js.map +0 -1
  49. /package/src/loaders/{loader-module-with-async-recorder.d.ts → loader-module-with-async-modules.d.ts} +0 -0
@@ -3,7 +3,7 @@
3
3
 
4
4
  var Config = {
5
5
  DEBUG: false,
6
- LIB_VERSION: '2.76.0'
6
+ LIB_VERSION: '2.77.0'
7
7
  };
8
8
 
9
9
  // Window global names for async modules
@@ -11,8 +11,8 @@
11
11
  var RECORDER_GLOBAL_NAME = '__mp_recorder';
12
12
 
13
13
  // Constants that are injected at build-time for the names of async modules.
14
- var RECORDER_FILENAME = 'mixpanel-recorder-bIS4LMGd.js';
15
- var TARGETING_FILENAME = 'mixpanel-targeting-BcAPS-Mz.js';
14
+ var RECORDER_FILENAME = 'mixpanel-recorder-DLKbUIEE.js';
15
+ var TARGETING_FILENAME = 'mixpanel-targeting-CmVvUyFM.js';
16
16
 
17
17
  // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
18
18
  var win;
@@ -2268,7 +2268,7 @@
2268
2268
 
2269
2269
  var MAX_DEPTH = 5;
2270
2270
 
2271
- var logger$4 = console_with_prefix('autocapture');
2271
+ var logger$5 = console_with_prefix('autocapture');
2272
2272
 
2273
2273
 
2274
2274
  function getClasses(el) {
@@ -2532,7 +2532,7 @@
2532
2532
  return false;
2533
2533
  }
2534
2534
  } catch (err) {
2535
- logger$4.critical('Error while checking element in allowElementCallback', err);
2535
+ logger$5.critical('Error while checking element in allowElementCallback', err);
2536
2536
  return false;
2537
2537
  }
2538
2538
  }
@@ -2549,7 +2549,7 @@
2549
2549
  return true;
2550
2550
  }
2551
2551
  } catch (err) {
2552
- logger$4.critical('Error while checking selector: ' + sel, err);
2552
+ logger$5.critical('Error while checking selector: ' + sel, err);
2553
2553
  }
2554
2554
  }
2555
2555
  return false;
@@ -2564,7 +2564,7 @@
2564
2564
  return true;
2565
2565
  }
2566
2566
  } catch (err) {
2567
- logger$4.critical('Error while checking element in blockElementCallback', err);
2567
+ logger$5.critical('Error while checking element in blockElementCallback', err);
2568
2568
  return true;
2569
2569
  }
2570
2570
  }
@@ -2578,7 +2578,7 @@
2578
2578
  return true;
2579
2579
  }
2580
2580
  } catch (err) {
2581
- logger$4.critical('Error while checking selector: ' + sel, err);
2581
+ logger$5.critical('Error while checking selector: ' + sel, err);
2582
2582
  }
2583
2583
  }
2584
2584
  }
@@ -3042,7 +3042,7 @@
3042
3042
  observer.observe(shadowRoot, this.observerConfig);
3043
3043
  this.shadowObservers.push(observer);
3044
3044
  } catch (e) {
3045
- logger$4.critical('Error while observing shadow root', e);
3045
+ logger$5.critical('Error while observing shadow root', e);
3046
3046
  }
3047
3047
  };
3048
3048
 
@@ -3053,7 +3053,7 @@
3053
3053
  }
3054
3054
 
3055
3055
  if (!weakSetSupported()) {
3056
- logger$4.critical('Shadow DOM observation unavailable: WeakSet not supported');
3056
+ logger$5.critical('Shadow DOM observation unavailable: WeakSet not supported');
3057
3057
  return;
3058
3058
  }
3059
3059
 
@@ -3069,7 +3069,7 @@
3069
3069
  try {
3070
3070
  this.shadowObservers[i].disconnect();
3071
3071
  } catch (e) {
3072
- logger$4.critical('Error while disconnecting shadow DOM observer', e);
3072
+ logger$5.critical('Error while disconnecting shadow DOM observer', e);
3073
3073
  }
3074
3074
  }
3075
3075
  this.shadowObservers = [];
@@ -3257,7 +3257,7 @@
3257
3257
 
3258
3258
  this.mutationObserver.observe(document.body || document.documentElement, MUTATION_OBSERVER_CONFIG);
3259
3259
  } catch (e) {
3260
- logger$4.critical('Error while setting up mutation observer', e);
3260
+ logger$5.critical('Error while setting up mutation observer', e);
3261
3261
  }
3262
3262
  }
3263
3263
 
@@ -3272,7 +3272,7 @@
3272
3272
  );
3273
3273
  this.shadowDOMObserver.start();
3274
3274
  } catch (e) {
3275
- logger$4.critical('Error while setting up shadow DOM observer', e);
3275
+ logger$5.critical('Error while setting up shadow DOM observer', e);
3276
3276
  this.shadowDOMObserver = null;
3277
3277
  }
3278
3278
  }
@@ -3299,7 +3299,7 @@
3299
3299
  try {
3300
3300
  listener.target.removeEventListener(listener.event, listener.handler, listener.options);
3301
3301
  } catch (e) {
3302
- logger$4.critical('Error while removing event listener', e);
3302
+ logger$5.critical('Error while removing event listener', e);
3303
3303
  }
3304
3304
  }
3305
3305
  this.eventListeners = [];
@@ -3308,7 +3308,7 @@
3308
3308
  try {
3309
3309
  this.mutationObserver.disconnect();
3310
3310
  } catch (e) {
3311
- logger$4.critical('Error while disconnecting mutation observer', e);
3311
+ logger$5.critical('Error while disconnecting mutation observer', e);
3312
3312
  }
3313
3313
  this.mutationObserver = null;
3314
3314
  }
@@ -3317,7 +3317,7 @@
3317
3317
  try {
3318
3318
  this.shadowDOMObserver.stop();
3319
3319
  } catch (e) {
3320
- logger$4.critical('Error while stopping shadow DOM observer', e);
3320
+ logger$5.critical('Error while stopping shadow DOM observer', e);
3321
3321
  }
3322
3322
  this.shadowDOMObserver = null;
3323
3323
  }
@@ -3395,7 +3395,7 @@
3395
3395
 
3396
3396
  Autocapture.prototype.init = function() {
3397
3397
  if (!minDOMApisSupported()) {
3398
- logger$4.critical('Autocapture unavailable: missing required DOM APIs');
3398
+ logger$5.critical('Autocapture unavailable: missing required DOM APIs');
3399
3399
  return;
3400
3400
  }
3401
3401
  this.initPageListeners();
@@ -3435,7 +3435,7 @@
3435
3435
  try {
3436
3436
  return !urlMatchesRegexList(currentUrl, allowUrlRegexes);
3437
3437
  } catch (err) {
3438
- logger$4.critical('Error while checking block URL regexes: ', err);
3438
+ logger$5.critical('Error while checking block URL regexes: ', err);
3439
3439
  return true;
3440
3440
  }
3441
3441
  }
@@ -3448,7 +3448,7 @@
3448
3448
  try {
3449
3449
  return urlMatchesRegexList(currentUrl, blockUrlRegexes);
3450
3450
  } catch (err) {
3451
- logger$4.critical('Error while checking block URL regexes: ', err);
3451
+ logger$5.critical('Error while checking block URL regexes: ', err);
3452
3452
  return true;
3453
3453
  }
3454
3454
  };
@@ -3586,7 +3586,7 @@
3586
3586
  return;
3587
3587
  }
3588
3588
 
3589
- logger$4.log('Initializing scroll depth tracking');
3589
+ logger$5.log('Initializing scroll depth tracking');
3590
3590
 
3591
3591
  this.maxScrollViewDepth = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
3592
3592
 
@@ -3612,7 +3612,7 @@
3612
3612
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
3613
3613
  return;
3614
3614
  }
3615
- logger$4.log('Initializing click tracking');
3615
+ logger$5.log('Initializing click tracking');
3616
3616
 
3617
3617
  this.listenerClick = function(ev) {
3618
3618
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
@@ -3631,7 +3631,7 @@
3631
3631
  return;
3632
3632
  }
3633
3633
 
3634
- logger$4.log('Initializing dead click tracking');
3634
+ logger$5.log('Initializing dead click tracking');
3635
3635
  if (!this._deadClickTracker) {
3636
3636
  this._deadClickTracker = new DeadClickTracker(function(deadClickEvent) {
3637
3637
  this.trackDomEvent(deadClickEvent, MP_EV_DEAD_CLICK);
@@ -3665,7 +3665,7 @@
3665
3665
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
3666
3666
  return;
3667
3667
  }
3668
- logger$4.log('Initializing input tracking');
3668
+ logger$5.log('Initializing input tracking');
3669
3669
 
3670
3670
  this.listenerChange = function(ev) {
3671
3671
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -3682,7 +3682,7 @@
3682
3682
  if (!this.pageviewTrackingConfig()) {
3683
3683
  return;
3684
3684
  }
3685
- logger$4.log('Initializing pageview tracking');
3685
+ logger$5.log('Initializing pageview tracking');
3686
3686
 
3687
3687
  var previousTrackedUrl = '';
3688
3688
  var tracked = false;
@@ -3717,7 +3717,7 @@
3717
3717
  }
3718
3718
  if (didPathChange) {
3719
3719
  this.lastScrollCheckpoint = 0;
3720
- logger$4.log('Path change: re-initializing scroll depth checkpoints');
3720
+ logger$5.log('Path change: re-initializing scroll depth checkpoints');
3721
3721
  }
3722
3722
  }
3723
3723
  }.bind(this));
@@ -3732,7 +3732,7 @@
3732
3732
  return;
3733
3733
  }
3734
3734
 
3735
- logger$4.log('Initializing rage click tracking');
3735
+ logger$5.log('Initializing rage click tracking');
3736
3736
  if (!this._rageClickTracker) {
3737
3737
  this._rageClickTracker = new RageClickTracker();
3738
3738
  }
@@ -3762,7 +3762,7 @@
3762
3762
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
3763
3763
  return;
3764
3764
  }
3765
- logger$4.log('Initializing scroll tracking');
3765
+ logger$5.log('Initializing scroll tracking');
3766
3766
  this.lastScrollCheckpoint = 0;
3767
3767
 
3768
3768
  var scrollTrackFunction = function() {
@@ -3799,7 +3799,7 @@
3799
3799
  }
3800
3800
  }
3801
3801
  } catch (err) {
3802
- logger$4.critical('Error while calculating scroll percentage', err);
3802
+ logger$5.critical('Error while calculating scroll percentage', err);
3803
3803
  }
3804
3804
  if (shouldTrack) {
3805
3805
  this.mp.track(MP_EV_SCROLL, props);
@@ -3817,7 +3817,7 @@
3817
3817
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
3818
3818
  return;
3819
3819
  }
3820
- logger$4.log('Initializing submit tracking');
3820
+ logger$5.log('Initializing submit tracking');
3821
3821
 
3822
3822
  this.listenerSubmit = function(ev) {
3823
3823
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -3839,7 +3839,7 @@
3839
3839
  return;
3840
3840
  }
3841
3841
 
3842
- logger$4.log('Initializing page visibility tracking.');
3842
+ logger$5.log('Initializing page visibility tracking.');
3843
3843
  this._initScrollDepthTracking();
3844
3844
  var previousTrackedUrl = _.info.currentUrl();
3845
3845
 
@@ -3924,7 +3924,7 @@
3924
3924
  return win[TARGETING_GLOBAL_NAME];
3925
3925
  };
3926
3926
 
3927
- var logger$3 = console_with_prefix('flags');
3927
+ var logger$4 = console_with_prefix('flags');
3928
3928
  var FLAGS_CONFIG_KEY = 'flags';
3929
3929
 
3930
3930
  var CONFIG_CONTEXT = 'context';
@@ -3967,7 +3967,7 @@
3967
3967
 
3968
3968
  FeatureFlagManager.prototype.init = function() {
3969
3969
  if (!this.minApisSupported()) {
3970
- logger$3.critical('Feature Flags unavailable: missing minimum required APIs');
3970
+ logger$4.critical('Feature Flags unavailable: missing minimum required APIs');
3971
3971
  return;
3972
3972
  }
3973
3973
 
@@ -4002,7 +4002,7 @@
4002
4002
 
4003
4003
  FeatureFlagManager.prototype.updateContext = function(newContext, options) {
4004
4004
  if (!this.isSystemEnabled()) {
4005
- logger$3.critical('Feature Flags not enabled, cannot update context');
4005
+ logger$4.critical('Feature Flags not enabled, cannot update context');
4006
4006
  return Promise.resolve();
4007
4007
  }
4008
4008
 
@@ -4019,7 +4019,7 @@
4019
4019
 
4020
4020
  FeatureFlagManager.prototype.areFlagsReady = function() {
4021
4021
  if (!this.isSystemEnabled()) {
4022
- logger$3.error('Feature Flags not enabled');
4022
+ logger$4.error('Feature Flags not enabled');
4023
4023
  }
4024
4024
  return !!this.flags;
4025
4025
  };
@@ -4032,7 +4032,7 @@
4032
4032
  var distinctId = this.getMpProperty('distinct_id');
4033
4033
  var deviceId = this.getMpProperty('$device_id');
4034
4034
  var traceparent = generateTraceparent();
4035
- logger$3.log('Fetching flags for distinct ID: ' + distinctId);
4035
+ logger$4.log('Fetching flags for distinct ID: ' + distinctId);
4036
4036
 
4037
4037
  var context = _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT));
4038
4038
  var searchParams = new URLSearchParams();
@@ -4131,11 +4131,11 @@
4131
4131
  this._loadTargetingIfNeeded();
4132
4132
  }.bind(this)).catch(function(error) {
4133
4133
  this.markFetchComplete();
4134
- logger$3.error(error);
4134
+ logger$4.error(error);
4135
4135
  }.bind(this));
4136
4136
  }.bind(this)).catch(function(error) {
4137
4137
  this.markFetchComplete();
4138
- logger$3.error(error);
4138
+ logger$4.error(error);
4139
4139
  }.bind(this));
4140
4140
 
4141
4141
  return this.fetchPromise;
@@ -4143,7 +4143,7 @@
4143
4143
 
4144
4144
  FeatureFlagManager.prototype.markFetchComplete = function() {
4145
4145
  if (!this._fetchInProgressStartTime) {
4146
- logger$3.error('Fetch in progress started time not set, cannot mark fetch complete');
4146
+ logger$4.error('Fetch in progress started time not set, cannot mark fetch complete');
4147
4147
  return;
4148
4148
  }
4149
4149
  this._fetchStartTime = this._fetchInProgressStartTime;
@@ -4165,7 +4165,7 @@
4165
4165
 
4166
4166
  if (hasPropertyFilters) {
4167
4167
  this.getTargeting().then(function() {
4168
- logger$3.log('targeting loaded for property filter evaluation');
4168
+ logger$4.log('targeting loaded for property filter evaluation');
4169
4169
  });
4170
4170
  }
4171
4171
  };
@@ -4180,7 +4180,7 @@
4180
4180
  this.loadExtraBundle.bind(this),
4181
4181
  this.targetingSrc
4182
4182
  ).catch(function(error) {
4183
- logger$3.error('Failed to load targeting: ' + error);
4183
+ logger$4.error('Failed to load targeting: ' + error);
4184
4184
  }.bind(this));
4185
4185
  };
4186
4186
 
@@ -4234,7 +4234,7 @@
4234
4234
 
4235
4235
  // If no targeting library and event has property filters, skip it
4236
4236
  if (!targeting && pendingEvent['property_filters'] && !_.isEmptyObject(pendingEvent['property_filters'])) {
4237
- logger$3.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
4237
+ logger$4.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
4238
4238
  return;
4239
4239
  }
4240
4240
 
@@ -4257,7 +4257,7 @@
4257
4257
  }
4258
4258
 
4259
4259
  if (matchResult.error) {
4260
- logger$3.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
4260
+ logger$4.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
4261
4261
  return;
4262
4262
  }
4263
4263
 
@@ -4265,7 +4265,7 @@
4265
4265
  return;
4266
4266
  }
4267
4267
 
4268
- logger$3.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
4268
+ logger$4.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
4269
4269
 
4270
4270
  var newVariant = {
4271
4271
  'key': pendingEvent['pending_variant']['variant_key'],
@@ -4306,7 +4306,7 @@
4306
4306
  'first_time_event_hash': firstTimeEventHash
4307
4307
  };
4308
4308
 
4309
- logger$3.log('Recording first-time event for flag: ' + flagId);
4309
+ logger$4.log('Recording first-time event for flag: ' + flagId);
4310
4310
 
4311
4311
  // Fire-and-forget POST request
4312
4312
  this.fetch.call(win, url, {
@@ -4319,14 +4319,14 @@
4319
4319
  'body': JSON.stringify(payload)
4320
4320
  }).catch(function(error) {
4321
4321
  // Silent failure - cohort sync will catch up
4322
- logger$3.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
4322
+ logger$4.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
4323
4323
  });
4324
4324
  };
4325
4325
 
4326
4326
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
4327
4327
  if (!this.fetchPromise) {
4328
4328
  return new Promise(function(resolve) {
4329
- logger$3.critical('Feature Flags not initialized');
4329
+ logger$4.critical('Feature Flags not initialized');
4330
4330
  resolve(fallback);
4331
4331
  });
4332
4332
  }
@@ -4334,19 +4334,19 @@
4334
4334
  return this.fetchPromise.then(function() {
4335
4335
  return this.getVariantSync(featureName, fallback);
4336
4336
  }.bind(this)).catch(function(error) {
4337
- logger$3.error(error);
4337
+ logger$4.error(error);
4338
4338
  return fallback;
4339
4339
  });
4340
4340
  };
4341
4341
 
4342
4342
  FeatureFlagManager.prototype.getVariantSync = function(featureName, fallback) {
4343
4343
  if (!this.areFlagsReady()) {
4344
- logger$3.log('Flags not loaded yet');
4344
+ logger$4.log('Flags not loaded yet');
4345
4345
  return fallback;
4346
4346
  }
4347
4347
  var feature = this.flags.get(featureName);
4348
4348
  if (!feature) {
4349
- logger$3.log('No flag found: "' + featureName + '"');
4349
+ logger$4.log('No flag found: "' + featureName + '"');
4350
4350
  return fallback;
4351
4351
  }
4352
4352
  this.trackFeatureCheck(featureName, feature);
@@ -4357,14 +4357,14 @@
4357
4357
  return this.getVariant(featureName, {'value': fallbackValue}).then(function(feature) {
4358
4358
  return feature['value'];
4359
4359
  }).catch(function(error) {
4360
- logger$3.error(error);
4360
+ logger$4.error(error);
4361
4361
  return fallbackValue;
4362
4362
  });
4363
4363
  };
4364
4364
 
4365
4365
  // TODO remove deprecated method
4366
4366
  FeatureFlagManager.prototype.getFeatureData = function(featureName, fallbackValue) {
4367
- logger$3.critical('mixpanel.flags.get_feature_data() is deprecated and will be removed in a future release. Use mixpanel.flags.get_variant_value() instead.');
4367
+ logger$4.critical('mixpanel.flags.get_feature_data() is deprecated and will be removed in a future release. Use mixpanel.flags.get_variant_value() instead.');
4368
4368
  return this.getVariantValue(featureName, fallbackValue);
4369
4369
  };
4370
4370
 
@@ -4376,7 +4376,7 @@
4376
4376
  return this.getVariantValue(featureName).then(function() {
4377
4377
  return this.isEnabledSync(featureName, fallbackValue);
4378
4378
  }.bind(this)).catch(function(error) {
4379
- logger$3.error(error);
4379
+ logger$4.error(error);
4380
4380
  return fallbackValue;
4381
4381
  });
4382
4382
  };
@@ -4385,7 +4385,7 @@
4385
4385
  fallbackValue = fallbackValue || false;
4386
4386
  var val = this.getVariantValueSync(featureName, fallbackValue);
4387
4387
  if (val !== true && val !== false) {
4388
- logger$3.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
4388
+ logger$4.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
4389
4389
  val = fallbackValue;
4390
4390
  }
4391
4391
  return val;
@@ -4580,9 +4580,38 @@
4580
4580
  return !serializedRecording || now > serializedRecording['maxExpires'] || now > serializedRecording['idleExpires'];
4581
4581
  };
4582
4582
 
4583
+ var validateAllowedOrigins = function(origins, logger) {
4584
+ if (!_.isArray(origins)) {
4585
+ if (origins) {
4586
+ logger.critical('record_allowed_iframe_origins must be an array of origin strings, cross-origin recording will be disabled.');
4587
+ }
4588
+ return [];
4589
+ }
4590
+ var valid = [];
4591
+ for (var i = 0; i < origins.length; i++) {
4592
+ try {
4593
+ var origin = new URL(origins[i]).origin;
4594
+ if (origin === 'null') {
4595
+ logger.critical(origins[i] + ' has an opaque origin. Skipping this entry.');
4596
+ continue;
4597
+ }
4598
+ valid.push(origin);
4599
+ } catch (e) {
4600
+ logger.critical(origins[i] + ' is not a valid origin URL. Skipping this entry.');
4601
+ }
4602
+ }
4603
+ return valid;
4604
+ };
4605
+
4583
4606
  /* eslint camelcase: "off" */
4584
4607
 
4585
4608
 
4609
+ var logger$3 = console_with_prefix('recorder');
4610
+
4611
+ var IFRAME_HANDSHAKE_REQUEST = 'mp_iframe_handshake_request';
4612
+ var IFRAME_HANDSHAKE_RESPONSE = 'mp_iframe_handshake_response';
4613
+
4614
+
4586
4615
  /**
4587
4616
  * RecorderManager: manages session recording initialization, lifecycle and state
4588
4617
  * @constructor
@@ -4602,6 +4631,8 @@
4602
4631
  this.libBasePath = initOptions.libBasePath;
4603
4632
 
4604
4633
  this._recorder = null;
4634
+ this._parentReplayId = null;
4635
+ this._parentFrameRetryInterval = null;
4605
4636
  };
4606
4637
 
4607
4638
  RecorderManager.prototype.shouldLoadRecorder = function() {
@@ -4655,6 +4686,22 @@
4655
4686
  }, this));
4656
4687
  }, this);
4657
4688
 
4689
+ // Cross-origin iframe handling
4690
+ var allowedOrigins = validateAllowedOrigins(this.getMpConfig('record_allowed_iframe_origins'), logger$3);
4691
+ var isCrossOriginRecordingEnabled = allowedOrigins.length > 0;
4692
+
4693
+ if (isCrossOriginRecordingEnabled) {
4694
+ // listen for handshake requests from their own child iframes (including nested)
4695
+ this._setupParentFrameListener(allowedOrigins);
4696
+
4697
+ if (win.parent !== win) {
4698
+ // also wait for parent's replay ID
4699
+ this._setupChildFrameListener(allowedOrigins, loadRecorder);
4700
+ this._sendParentFrameRequestWithRetry(allowedOrigins);
4701
+ return PromisePolyfill.resolve();
4702
+ }
4703
+ }
4704
+
4658
4705
  /**
4659
4706
  * If the user is sampled or start_session_recording is called, we always load the recorder since it's guaranteed a recording should start.
4660
4707
  * 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.
@@ -4774,6 +4821,10 @@
4774
4821
  };
4775
4822
 
4776
4823
  RecorderManager.prototype.getSessionReplayId = function() {
4824
+ // Child iframe uses parent's replay ID
4825
+ if (this._parentReplayId) {
4826
+ return this._parentReplayId;
4827
+ }
4777
4828
  var replay_id = null;
4778
4829
  if (this._recorder) {
4779
4830
  replay_id = this._recorder['replayId'];
@@ -4786,6 +4837,86 @@
4786
4837
  return this._recorder;
4787
4838
  };
4788
4839
 
4840
+ RecorderManager.prototype._setupChildFrameListener = function(allowedOrigins, loadRecorder) {
4841
+ if (this._childFrameMessageHandler) {
4842
+ return;
4843
+ }
4844
+ var self = this;
4845
+ this._childFrameMessageHandler = function(event) {
4846
+ if (allowedOrigins.indexOf(event.origin) === -1) return;
4847
+ var data = event.data;
4848
+ if (data && data['type'] === IFRAME_HANDSHAKE_RESPONSE && data['token'] === self.getMpConfig('token') && data['replayId']) {
4849
+ self._parentReplayId = data['replayId'];
4850
+ if (data['distinctId']) {
4851
+ self.mixpanelInstance['identify'](data['distinctId']);
4852
+ }
4853
+ self._parentFrameRetryActive = false;
4854
+ win.removeEventListener('message', self._childFrameMessageHandler);
4855
+ self._childFrameMessageHandler = null;
4856
+ loadRecorder(true);
4857
+ }
4858
+ };
4859
+ win.addEventListener('message', this._childFrameMessageHandler);
4860
+ };
4861
+
4862
+ RecorderManager.prototype._sendParentFrameRequest = function(allowedOrigins) {
4863
+ var message = {};
4864
+ message['type'] = IFRAME_HANDSHAKE_REQUEST;
4865
+ message['token'] = this.getMpConfig('token');
4866
+ for (var i = 0; i < allowedOrigins.length; i++) {
4867
+ try {
4868
+ win.parent.postMessage(message, allowedOrigins[i]);
4869
+ } catch (e) {
4870
+ // origin mismatch - ignore
4871
+ }
4872
+ }
4873
+ };
4874
+
4875
+ RecorderManager.prototype._sendParentFrameRequestWithRetry = function(allowedOrigins) {
4876
+ var self = this;
4877
+ var maxRetries = 10;
4878
+ var retryCount = 0;
4879
+ var delay = 50;
4880
+ this._parentFrameRetryActive = true;
4881
+
4882
+ this._sendParentFrameRequest(allowedOrigins);
4883
+
4884
+ function scheduleRetry() {
4885
+ setTimeout(function() {
4886
+ if (!self._parentFrameRetryActive || self._parentReplayId || ++retryCount >= maxRetries) {
4887
+ return;
4888
+ }
4889
+ self._sendParentFrameRequest(allowedOrigins);
4890
+ delay *= 2;
4891
+ scheduleRetry();
4892
+ }, delay);
4893
+ }
4894
+ scheduleRetry();
4895
+ };
4896
+
4897
+ RecorderManager.prototype._setupParentFrameListener = function(allowedOrigins) {
4898
+ if (this._parentFrameMessageHandler) {
4899
+ return;
4900
+ }
4901
+ var self = this;
4902
+ this._parentFrameMessageHandler = function(event) {
4903
+ if (allowedOrigins.indexOf(event.origin) === -1) return;
4904
+ var data = event.data;
4905
+ if (data && data['type'] === IFRAME_HANDSHAKE_REQUEST && data['token'] === self.getMpConfig('token')) {
4906
+ var replayId = self.getSessionReplayId();
4907
+ if (replayId) {
4908
+ var response = {};
4909
+ response['type'] = IFRAME_HANDSHAKE_RESPONSE;
4910
+ response['token'] = self.getMpConfig('token');
4911
+ response['replayId'] = replayId;
4912
+ response['distinctId'] = self.getDistinctId();
4913
+ event.source.postMessage(response, event.origin);
4914
+ }
4915
+ }
4916
+ };
4917
+ win.addEventListener('message', this._parentFrameMessageHandler);
4918
+ };
4919
+
4789
4920
  safewrapClass(RecorderManager);
4790
4921
 
4791
4922
  /* eslint camelcase: "off" */
@@ -7352,7 +7483,6 @@
7352
7483
  /** @const */ var SETTING_FALLBACK = 'fallback';
7353
7484
  /** @const */ var SETTING_DISABLED = 'disabled';
7354
7485
 
7355
-
7356
7486
  /*
7357
7487
  * Dynamic... constants? Is that an oxymoron?
7358
7488
  */
@@ -7437,6 +7567,7 @@
7437
7567
  'batch_request_timeout_ms': 90000,
7438
7568
  'batch_autostart': true,
7439
7569
  'hooks': {},
7570
+ 'record_allowed_iframe_origins': [],
7440
7571
  'record_block_class': new RegExp('^(mp-block|fs-exclude|amp-block|rr-block|ph-no-capture)$'),
7441
7572
  'record_block_selector': 'img, video, audio',
7442
7573
  'record_canvas': false,