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
@@ -2,7 +2,7 @@
2
2
 
3
3
  var Config = {
4
4
  DEBUG: false,
5
- LIB_VERSION: '2.76.0'
5
+ LIB_VERSION: '2.77.0'
6
6
  };
7
7
 
8
8
  // Window global names for async modules
@@ -10,8 +10,8 @@ var TARGETING_GLOBAL_NAME = '__mp_targeting';
10
10
  var RECORDER_GLOBAL_NAME = '__mp_recorder';
11
11
 
12
12
  // Constants that are injected at build-time for the names of async modules.
13
- var RECORDER_FILENAME = 'mixpanel-recorder-bIS4LMGd.js';
14
- var TARGETING_FILENAME = 'mixpanel-targeting-BcAPS-Mz.js';
13
+ var RECORDER_FILENAME = 'mixpanel-recorder-DLKbUIEE.js';
14
+ var TARGETING_FILENAME = 'mixpanel-targeting-CmVvUyFM.js';
15
15
 
16
16
  // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
17
17
  var win;
@@ -2267,7 +2267,7 @@ var EVENT_HANDLER_ATTRIBUTES = [
2267
2267
 
2268
2268
  var MAX_DEPTH = 5;
2269
2269
 
2270
- var logger$4 = console_with_prefix('autocapture');
2270
+ var logger$5 = console_with_prefix('autocapture');
2271
2271
 
2272
2272
 
2273
2273
  function getClasses(el) {
@@ -2531,7 +2531,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
2531
2531
  return false;
2532
2532
  }
2533
2533
  } catch (err) {
2534
- logger$4.critical('Error while checking element in allowElementCallback', err);
2534
+ logger$5.critical('Error while checking element in allowElementCallback', err);
2535
2535
  return false;
2536
2536
  }
2537
2537
  }
@@ -2548,7 +2548,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
2548
2548
  return true;
2549
2549
  }
2550
2550
  } catch (err) {
2551
- logger$4.critical('Error while checking selector: ' + sel, err);
2551
+ logger$5.critical('Error while checking selector: ' + sel, err);
2552
2552
  }
2553
2553
  }
2554
2554
  return false;
@@ -2563,7 +2563,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
2563
2563
  return true;
2564
2564
  }
2565
2565
  } catch (err) {
2566
- logger$4.critical('Error while checking element in blockElementCallback', err);
2566
+ logger$5.critical('Error while checking element in blockElementCallback', err);
2567
2567
  return true;
2568
2568
  }
2569
2569
  }
@@ -2577,7 +2577,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
2577
2577
  return true;
2578
2578
  }
2579
2579
  } catch (err) {
2580
- logger$4.critical('Error while checking selector: ' + sel, err);
2580
+ logger$5.critical('Error while checking selector: ' + sel, err);
2581
2581
  }
2582
2582
  }
2583
2583
  }
@@ -3041,7 +3041,7 @@ ShadowDOMObserver.prototype.observeShadowRoot = function(shadowRoot) {
3041
3041
  observer.observe(shadowRoot, this.observerConfig);
3042
3042
  this.shadowObservers.push(observer);
3043
3043
  } catch (e) {
3044
- logger$4.critical('Error while observing shadow root', e);
3044
+ logger$5.critical('Error while observing shadow root', e);
3045
3045
  }
3046
3046
  };
3047
3047
 
@@ -3052,7 +3052,7 @@ ShadowDOMObserver.prototype.start = function() {
3052
3052
  }
3053
3053
 
3054
3054
  if (!weakSetSupported()) {
3055
- logger$4.critical('Shadow DOM observation unavailable: WeakSet not supported');
3055
+ logger$5.critical('Shadow DOM observation unavailable: WeakSet not supported');
3056
3056
  return;
3057
3057
  }
3058
3058
 
@@ -3068,7 +3068,7 @@ ShadowDOMObserver.prototype.stop = function() {
3068
3068
  try {
3069
3069
  this.shadowObservers[i].disconnect();
3070
3070
  } catch (e) {
3071
- logger$4.critical('Error while disconnecting shadow DOM observer', e);
3071
+ logger$5.critical('Error while disconnecting shadow DOM observer', e);
3072
3072
  }
3073
3073
  }
3074
3074
  this.shadowObservers = [];
@@ -3256,7 +3256,7 @@ DeadClickTracker.prototype.startTracking = function() {
3256
3256
 
3257
3257
  this.mutationObserver.observe(document.body || document.documentElement, MUTATION_OBSERVER_CONFIG);
3258
3258
  } catch (e) {
3259
- logger$4.critical('Error while setting up mutation observer', e);
3259
+ logger$5.critical('Error while setting up mutation observer', e);
3260
3260
  }
3261
3261
  }
3262
3262
 
@@ -3271,7 +3271,7 @@ DeadClickTracker.prototype.startTracking = function() {
3271
3271
  );
3272
3272
  this.shadowDOMObserver.start();
3273
3273
  } catch (e) {
3274
- logger$4.critical('Error while setting up shadow DOM observer', e);
3274
+ logger$5.critical('Error while setting up shadow DOM observer', e);
3275
3275
  this.shadowDOMObserver = null;
3276
3276
  }
3277
3277
  }
@@ -3298,7 +3298,7 @@ DeadClickTracker.prototype.stopTracking = function() {
3298
3298
  try {
3299
3299
  listener.target.removeEventListener(listener.event, listener.handler, listener.options);
3300
3300
  } catch (e) {
3301
- logger$4.critical('Error while removing event listener', e);
3301
+ logger$5.critical('Error while removing event listener', e);
3302
3302
  }
3303
3303
  }
3304
3304
  this.eventListeners = [];
@@ -3307,7 +3307,7 @@ DeadClickTracker.prototype.stopTracking = function() {
3307
3307
  try {
3308
3308
  this.mutationObserver.disconnect();
3309
3309
  } catch (e) {
3310
- logger$4.critical('Error while disconnecting mutation observer', e);
3310
+ logger$5.critical('Error while disconnecting mutation observer', e);
3311
3311
  }
3312
3312
  this.mutationObserver = null;
3313
3313
  }
@@ -3316,7 +3316,7 @@ DeadClickTracker.prototype.stopTracking = function() {
3316
3316
  try {
3317
3317
  this.shadowDOMObserver.stop();
3318
3318
  } catch (e) {
3319
- logger$4.critical('Error while stopping shadow DOM observer', e);
3319
+ logger$5.critical('Error while stopping shadow DOM observer', e);
3320
3320
  }
3321
3321
  this.shadowDOMObserver = null;
3322
3322
  }
@@ -3394,7 +3394,7 @@ var Autocapture = function(mp) {
3394
3394
 
3395
3395
  Autocapture.prototype.init = function() {
3396
3396
  if (!minDOMApisSupported()) {
3397
- logger$4.critical('Autocapture unavailable: missing required DOM APIs');
3397
+ logger$5.critical('Autocapture unavailable: missing required DOM APIs');
3398
3398
  return;
3399
3399
  }
3400
3400
  this.initPageListeners();
@@ -3434,7 +3434,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
3434
3434
  try {
3435
3435
  return !urlMatchesRegexList(currentUrl, allowUrlRegexes);
3436
3436
  } catch (err) {
3437
- logger$4.critical('Error while checking block URL regexes: ', err);
3437
+ logger$5.critical('Error while checking block URL regexes: ', err);
3438
3438
  return true;
3439
3439
  }
3440
3440
  }
@@ -3447,7 +3447,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
3447
3447
  try {
3448
3448
  return urlMatchesRegexList(currentUrl, blockUrlRegexes);
3449
3449
  } catch (err) {
3450
- logger$4.critical('Error while checking block URL regexes: ', err);
3450
+ logger$5.critical('Error while checking block URL regexes: ', err);
3451
3451
  return true;
3452
3452
  }
3453
3453
  };
@@ -3585,7 +3585,7 @@ Autocapture.prototype._initScrollDepthTracking = function() {
3585
3585
  return;
3586
3586
  }
3587
3587
 
3588
- logger$4.log('Initializing scroll depth tracking');
3588
+ logger$5.log('Initializing scroll depth tracking');
3589
3589
 
3590
3590
  this.maxScrollViewDepth = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
3591
3591
 
@@ -3611,7 +3611,7 @@ Autocapture.prototype.initClickTracking = function() {
3611
3611
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
3612
3612
  return;
3613
3613
  }
3614
- logger$4.log('Initializing click tracking');
3614
+ logger$5.log('Initializing click tracking');
3615
3615
 
3616
3616
  this.listenerClick = function(ev) {
3617
3617
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
@@ -3630,7 +3630,7 @@ Autocapture.prototype.initDeadClickTracking = function() {
3630
3630
  return;
3631
3631
  }
3632
3632
 
3633
- logger$4.log('Initializing dead click tracking');
3633
+ logger$5.log('Initializing dead click tracking');
3634
3634
  if (!this._deadClickTracker) {
3635
3635
  this._deadClickTracker = new DeadClickTracker(function(deadClickEvent) {
3636
3636
  this.trackDomEvent(deadClickEvent, MP_EV_DEAD_CLICK);
@@ -3664,7 +3664,7 @@ Autocapture.prototype.initInputTracking = function() {
3664
3664
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
3665
3665
  return;
3666
3666
  }
3667
- logger$4.log('Initializing input tracking');
3667
+ logger$5.log('Initializing input tracking');
3668
3668
 
3669
3669
  this.listenerChange = function(ev) {
3670
3670
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -3681,7 +3681,7 @@ Autocapture.prototype.initPageviewTracking = function() {
3681
3681
  if (!this.pageviewTrackingConfig()) {
3682
3682
  return;
3683
3683
  }
3684
- logger$4.log('Initializing pageview tracking');
3684
+ logger$5.log('Initializing pageview tracking');
3685
3685
 
3686
3686
  var previousTrackedUrl = '';
3687
3687
  var tracked = false;
@@ -3716,7 +3716,7 @@ Autocapture.prototype.initPageviewTracking = function() {
3716
3716
  }
3717
3717
  if (didPathChange) {
3718
3718
  this.lastScrollCheckpoint = 0;
3719
- logger$4.log('Path change: re-initializing scroll depth checkpoints');
3719
+ logger$5.log('Path change: re-initializing scroll depth checkpoints');
3720
3720
  }
3721
3721
  }
3722
3722
  }.bind(this));
@@ -3731,7 +3731,7 @@ Autocapture.prototype.initRageClickTracking = function() {
3731
3731
  return;
3732
3732
  }
3733
3733
 
3734
- logger$4.log('Initializing rage click tracking');
3734
+ logger$5.log('Initializing rage click tracking');
3735
3735
  if (!this._rageClickTracker) {
3736
3736
  this._rageClickTracker = new RageClickTracker();
3737
3737
  }
@@ -3761,7 +3761,7 @@ Autocapture.prototype.initScrollTracking = function() {
3761
3761
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
3762
3762
  return;
3763
3763
  }
3764
- logger$4.log('Initializing scroll tracking');
3764
+ logger$5.log('Initializing scroll tracking');
3765
3765
  this.lastScrollCheckpoint = 0;
3766
3766
 
3767
3767
  var scrollTrackFunction = function() {
@@ -3798,7 +3798,7 @@ Autocapture.prototype.initScrollTracking = function() {
3798
3798
  }
3799
3799
  }
3800
3800
  } catch (err) {
3801
- logger$4.critical('Error while calculating scroll percentage', err);
3801
+ logger$5.critical('Error while calculating scroll percentage', err);
3802
3802
  }
3803
3803
  if (shouldTrack) {
3804
3804
  this.mp.track(MP_EV_SCROLL, props);
@@ -3816,7 +3816,7 @@ Autocapture.prototype.initSubmitTracking = function() {
3816
3816
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
3817
3817
  return;
3818
3818
  }
3819
- logger$4.log('Initializing submit tracking');
3819
+ logger$5.log('Initializing submit tracking');
3820
3820
 
3821
3821
  this.listenerSubmit = function(ev) {
3822
3822
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -3838,7 +3838,7 @@ Autocapture.prototype.initPageLeaveTracking = function() {
3838
3838
  return;
3839
3839
  }
3840
3840
 
3841
- logger$4.log('Initializing page visibility tracking.');
3841
+ logger$5.log('Initializing page visibility tracking.');
3842
3842
  this._initScrollDepthTracking();
3843
3843
  var previousTrackedUrl = _.info.currentUrl();
3844
3844
 
@@ -3923,7 +3923,7 @@ var getTargetingPromise = function(loadExtraBundle, targetingSrc) {
3923
3923
  return win[TARGETING_GLOBAL_NAME];
3924
3924
  };
3925
3925
 
3926
- var logger$3 = console_with_prefix('flags');
3926
+ var logger$4 = console_with_prefix('flags');
3927
3927
  var FLAGS_CONFIG_KEY = 'flags';
3928
3928
 
3929
3929
  var CONFIG_CONTEXT = 'context';
@@ -3966,7 +3966,7 @@ var FeatureFlagManager = function(initOptions) {
3966
3966
 
3967
3967
  FeatureFlagManager.prototype.init = function() {
3968
3968
  if (!this.minApisSupported()) {
3969
- logger$3.critical('Feature Flags unavailable: missing minimum required APIs');
3969
+ logger$4.critical('Feature Flags unavailable: missing minimum required APIs');
3970
3970
  return;
3971
3971
  }
3972
3972
 
@@ -4001,7 +4001,7 @@ FeatureFlagManager.prototype.isSystemEnabled = function() {
4001
4001
 
4002
4002
  FeatureFlagManager.prototype.updateContext = function(newContext, options) {
4003
4003
  if (!this.isSystemEnabled()) {
4004
- logger$3.critical('Feature Flags not enabled, cannot update context');
4004
+ logger$4.critical('Feature Flags not enabled, cannot update context');
4005
4005
  return Promise.resolve();
4006
4006
  }
4007
4007
 
@@ -4018,7 +4018,7 @@ FeatureFlagManager.prototype.updateContext = function(newContext, options) {
4018
4018
 
4019
4019
  FeatureFlagManager.prototype.areFlagsReady = function() {
4020
4020
  if (!this.isSystemEnabled()) {
4021
- logger$3.error('Feature Flags not enabled');
4021
+ logger$4.error('Feature Flags not enabled');
4022
4022
  }
4023
4023
  return !!this.flags;
4024
4024
  };
@@ -4031,7 +4031,7 @@ FeatureFlagManager.prototype.fetchFlags = function() {
4031
4031
  var distinctId = this.getMpProperty('distinct_id');
4032
4032
  var deviceId = this.getMpProperty('$device_id');
4033
4033
  var traceparent = generateTraceparent();
4034
- logger$3.log('Fetching flags for distinct ID: ' + distinctId);
4034
+ logger$4.log('Fetching flags for distinct ID: ' + distinctId);
4035
4035
 
4036
4036
  var context = _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT));
4037
4037
  var searchParams = new URLSearchParams();
@@ -4130,11 +4130,11 @@ FeatureFlagManager.prototype.fetchFlags = function() {
4130
4130
  this._loadTargetingIfNeeded();
4131
4131
  }.bind(this)).catch(function(error) {
4132
4132
  this.markFetchComplete();
4133
- logger$3.error(error);
4133
+ logger$4.error(error);
4134
4134
  }.bind(this));
4135
4135
  }.bind(this)).catch(function(error) {
4136
4136
  this.markFetchComplete();
4137
- logger$3.error(error);
4137
+ logger$4.error(error);
4138
4138
  }.bind(this));
4139
4139
 
4140
4140
  return this.fetchPromise;
@@ -4142,7 +4142,7 @@ FeatureFlagManager.prototype.fetchFlags = function() {
4142
4142
 
4143
4143
  FeatureFlagManager.prototype.markFetchComplete = function() {
4144
4144
  if (!this._fetchInProgressStartTime) {
4145
- logger$3.error('Fetch in progress started time not set, cannot mark fetch complete');
4145
+ logger$4.error('Fetch in progress started time not set, cannot mark fetch complete');
4146
4146
  return;
4147
4147
  }
4148
4148
  this._fetchStartTime = this._fetchInProgressStartTime;
@@ -4164,7 +4164,7 @@ FeatureFlagManager.prototype._loadTargetingIfNeeded = function() {
4164
4164
 
4165
4165
  if (hasPropertyFilters) {
4166
4166
  this.getTargeting().then(function() {
4167
- logger$3.log('targeting loaded for property filter evaluation');
4167
+ logger$4.log('targeting loaded for property filter evaluation');
4168
4168
  });
4169
4169
  }
4170
4170
  };
@@ -4179,7 +4179,7 @@ FeatureFlagManager.prototype.getTargeting = function() {
4179
4179
  this.loadExtraBundle.bind(this),
4180
4180
  this.targetingSrc
4181
4181
  ).catch(function(error) {
4182
- logger$3.error('Failed to load targeting: ' + error);
4182
+ logger$4.error('Failed to load targeting: ' + error);
4183
4183
  }.bind(this));
4184
4184
  };
4185
4185
 
@@ -4233,7 +4233,7 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
4233
4233
 
4234
4234
  // If no targeting library and event has property filters, skip it
4235
4235
  if (!targeting && pendingEvent['property_filters'] && !_.isEmptyObject(pendingEvent['property_filters'])) {
4236
- logger$3.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
4236
+ logger$4.warn('Skipping event check for "' + flagKey + '" - property filters require targeting library');
4237
4237
  return;
4238
4238
  }
4239
4239
 
@@ -4256,7 +4256,7 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
4256
4256
  }
4257
4257
 
4258
4258
  if (matchResult.error) {
4259
- logger$3.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
4259
+ logger$4.error('Error checking first-time event for flag "' + flagKey + '": ' + matchResult.error);
4260
4260
  return;
4261
4261
  }
4262
4262
 
@@ -4264,7 +4264,7 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
4264
4264
  return;
4265
4265
  }
4266
4266
 
4267
- logger$3.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
4267
+ logger$4.log('First-time event matched for flag "' + flagKey + '": ' + eventName);
4268
4268
 
4269
4269
  var newVariant = {
4270
4270
  'key': pendingEvent['pending_variant']['variant_key'],
@@ -4305,7 +4305,7 @@ FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId,
4305
4305
  'first_time_event_hash': firstTimeEventHash
4306
4306
  };
4307
4307
 
4308
- logger$3.log('Recording first-time event for flag: ' + flagId);
4308
+ logger$4.log('Recording first-time event for flag: ' + flagId);
4309
4309
 
4310
4310
  // Fire-and-forget POST request
4311
4311
  this.fetch.call(win, url, {
@@ -4318,14 +4318,14 @@ FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId,
4318
4318
  'body': JSON.stringify(payload)
4319
4319
  }).catch(function(error) {
4320
4320
  // Silent failure - cohort sync will catch up
4321
- logger$3.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
4321
+ logger$4.error('Failed to record first-time event for flag ' + flagId + ': ' + error);
4322
4322
  });
4323
4323
  };
4324
4324
 
4325
4325
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
4326
4326
  if (!this.fetchPromise) {
4327
4327
  return new Promise(function(resolve) {
4328
- logger$3.critical('Feature Flags not initialized');
4328
+ logger$4.critical('Feature Flags not initialized');
4329
4329
  resolve(fallback);
4330
4330
  });
4331
4331
  }
@@ -4333,19 +4333,19 @@ FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
4333
4333
  return this.fetchPromise.then(function() {
4334
4334
  return this.getVariantSync(featureName, fallback);
4335
4335
  }.bind(this)).catch(function(error) {
4336
- logger$3.error(error);
4336
+ logger$4.error(error);
4337
4337
  return fallback;
4338
4338
  });
4339
4339
  };
4340
4340
 
4341
4341
  FeatureFlagManager.prototype.getVariantSync = function(featureName, fallback) {
4342
4342
  if (!this.areFlagsReady()) {
4343
- logger$3.log('Flags not loaded yet');
4343
+ logger$4.log('Flags not loaded yet');
4344
4344
  return fallback;
4345
4345
  }
4346
4346
  var feature = this.flags.get(featureName);
4347
4347
  if (!feature) {
4348
- logger$3.log('No flag found: "' + featureName + '"');
4348
+ logger$4.log('No flag found: "' + featureName + '"');
4349
4349
  return fallback;
4350
4350
  }
4351
4351
  this.trackFeatureCheck(featureName, feature);
@@ -4356,14 +4356,14 @@ FeatureFlagManager.prototype.getVariantValue = function(featureName, fallbackVal
4356
4356
  return this.getVariant(featureName, {'value': fallbackValue}).then(function(feature) {
4357
4357
  return feature['value'];
4358
4358
  }).catch(function(error) {
4359
- logger$3.error(error);
4359
+ logger$4.error(error);
4360
4360
  return fallbackValue;
4361
4361
  });
4362
4362
  };
4363
4363
 
4364
4364
  // TODO remove deprecated method
4365
4365
  FeatureFlagManager.prototype.getFeatureData = function(featureName, fallbackValue) {
4366
- 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.');
4366
+ 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.');
4367
4367
  return this.getVariantValue(featureName, fallbackValue);
4368
4368
  };
4369
4369
 
@@ -4375,7 +4375,7 @@ FeatureFlagManager.prototype.isEnabled = function(featureName, fallbackValue) {
4375
4375
  return this.getVariantValue(featureName).then(function() {
4376
4376
  return this.isEnabledSync(featureName, fallbackValue);
4377
4377
  }.bind(this)).catch(function(error) {
4378
- logger$3.error(error);
4378
+ logger$4.error(error);
4379
4379
  return fallbackValue;
4380
4380
  });
4381
4381
  };
@@ -4384,7 +4384,7 @@ FeatureFlagManager.prototype.isEnabledSync = function(featureName, fallbackValue
4384
4384
  fallbackValue = fallbackValue || false;
4385
4385
  var val = this.getVariantValueSync(featureName, fallbackValue);
4386
4386
  if (val !== true && val !== false) {
4387
- logger$3.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
4387
+ logger$4.error('Feature flag "' + featureName + '" value: ' + val + ' is not a boolean; returning fallback value: ' + fallbackValue);
4388
4388
  val = fallbackValue;
4389
4389
  }
4390
4390
  return val;
@@ -4579,9 +4579,38 @@ var isRecordingExpired = function(serializedRecording) {
4579
4579
  return !serializedRecording || now > serializedRecording['maxExpires'] || now > serializedRecording['idleExpires'];
4580
4580
  };
4581
4581
 
4582
+ var validateAllowedOrigins = function(origins, logger) {
4583
+ if (!_.isArray(origins)) {
4584
+ if (origins) {
4585
+ logger.critical('record_allowed_iframe_origins must be an array of origin strings, cross-origin recording will be disabled.');
4586
+ }
4587
+ return [];
4588
+ }
4589
+ var valid = [];
4590
+ for (var i = 0; i < origins.length; i++) {
4591
+ try {
4592
+ var origin = new URL(origins[i]).origin;
4593
+ if (origin === 'null') {
4594
+ logger.critical(origins[i] + ' has an opaque origin. Skipping this entry.');
4595
+ continue;
4596
+ }
4597
+ valid.push(origin);
4598
+ } catch (e) {
4599
+ logger.critical(origins[i] + ' is not a valid origin URL. Skipping this entry.');
4600
+ }
4601
+ }
4602
+ return valid;
4603
+ };
4604
+
4582
4605
  /* eslint camelcase: "off" */
4583
4606
 
4584
4607
 
4608
+ var logger$3 = console_with_prefix('recorder');
4609
+
4610
+ var IFRAME_HANDSHAKE_REQUEST = 'mp_iframe_handshake_request';
4611
+ var IFRAME_HANDSHAKE_RESPONSE = 'mp_iframe_handshake_response';
4612
+
4613
+
4585
4614
  /**
4586
4615
  * RecorderManager: manages session recording initialization, lifecycle and state
4587
4616
  * @constructor
@@ -4601,6 +4630,8 @@ var RecorderManager = function(initOptions) {
4601
4630
  this.libBasePath = initOptions.libBasePath;
4602
4631
 
4603
4632
  this._recorder = null;
4633
+ this._parentReplayId = null;
4634
+ this._parentFrameRetryInterval = null;
4604
4635
  };
4605
4636
 
4606
4637
  RecorderManager.prototype.shouldLoadRecorder = function() {
@@ -4654,6 +4685,22 @@ RecorderManager.prototype.checkAndStartSessionRecording = function(force_start,
4654
4685
  }, this));
4655
4686
  }, this);
4656
4687
 
4688
+ // Cross-origin iframe handling
4689
+ var allowedOrigins = validateAllowedOrigins(this.getMpConfig('record_allowed_iframe_origins'), logger$3);
4690
+ var isCrossOriginRecordingEnabled = allowedOrigins.length > 0;
4691
+
4692
+ if (isCrossOriginRecordingEnabled) {
4693
+ // listen for handshake requests from their own child iframes (including nested)
4694
+ this._setupParentFrameListener(allowedOrigins);
4695
+
4696
+ if (win.parent !== win) {
4697
+ // also wait for parent's replay ID
4698
+ this._setupChildFrameListener(allowedOrigins, loadRecorder);
4699
+ this._sendParentFrameRequestWithRetry(allowedOrigins);
4700
+ return PromisePolyfill.resolve();
4701
+ }
4702
+ }
4703
+
4657
4704
  /**
4658
4705
  * If the user is sampled or start_session_recording is called, we always load the recorder since it's guaranteed a recording should start.
4659
4706
  * 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.
@@ -4773,6 +4820,10 @@ RecorderManager.prototype.getSessionReplayUrl = function() {
4773
4820
  };
4774
4821
 
4775
4822
  RecorderManager.prototype.getSessionReplayId = function() {
4823
+ // Child iframe uses parent's replay ID
4824
+ if (this._parentReplayId) {
4825
+ return this._parentReplayId;
4826
+ }
4776
4827
  var replay_id = null;
4777
4828
  if (this._recorder) {
4778
4829
  replay_id = this._recorder['replayId'];
@@ -4785,6 +4836,86 @@ RecorderManager.prototype.getRecorder = function() {
4785
4836
  return this._recorder;
4786
4837
  };
4787
4838
 
4839
+ RecorderManager.prototype._setupChildFrameListener = function(allowedOrigins, loadRecorder) {
4840
+ if (this._childFrameMessageHandler) {
4841
+ return;
4842
+ }
4843
+ var self = this;
4844
+ this._childFrameMessageHandler = function(event) {
4845
+ if (allowedOrigins.indexOf(event.origin) === -1) return;
4846
+ var data = event.data;
4847
+ if (data && data['type'] === IFRAME_HANDSHAKE_RESPONSE && data['token'] === self.getMpConfig('token') && data['replayId']) {
4848
+ self._parentReplayId = data['replayId'];
4849
+ if (data['distinctId']) {
4850
+ self.mixpanelInstance['identify'](data['distinctId']);
4851
+ }
4852
+ self._parentFrameRetryActive = false;
4853
+ win.removeEventListener('message', self._childFrameMessageHandler);
4854
+ self._childFrameMessageHandler = null;
4855
+ loadRecorder(true);
4856
+ }
4857
+ };
4858
+ win.addEventListener('message', this._childFrameMessageHandler);
4859
+ };
4860
+
4861
+ RecorderManager.prototype._sendParentFrameRequest = function(allowedOrigins) {
4862
+ var message = {};
4863
+ message['type'] = IFRAME_HANDSHAKE_REQUEST;
4864
+ message['token'] = this.getMpConfig('token');
4865
+ for (var i = 0; i < allowedOrigins.length; i++) {
4866
+ try {
4867
+ win.parent.postMessage(message, allowedOrigins[i]);
4868
+ } catch (e) {
4869
+ // origin mismatch - ignore
4870
+ }
4871
+ }
4872
+ };
4873
+
4874
+ RecorderManager.prototype._sendParentFrameRequestWithRetry = function(allowedOrigins) {
4875
+ var self = this;
4876
+ var maxRetries = 10;
4877
+ var retryCount = 0;
4878
+ var delay = 50;
4879
+ this._parentFrameRetryActive = true;
4880
+
4881
+ this._sendParentFrameRequest(allowedOrigins);
4882
+
4883
+ function scheduleRetry() {
4884
+ setTimeout(function() {
4885
+ if (!self._parentFrameRetryActive || self._parentReplayId || ++retryCount >= maxRetries) {
4886
+ return;
4887
+ }
4888
+ self._sendParentFrameRequest(allowedOrigins);
4889
+ delay *= 2;
4890
+ scheduleRetry();
4891
+ }, delay);
4892
+ }
4893
+ scheduleRetry();
4894
+ };
4895
+
4896
+ RecorderManager.prototype._setupParentFrameListener = function(allowedOrigins) {
4897
+ if (this._parentFrameMessageHandler) {
4898
+ return;
4899
+ }
4900
+ var self = this;
4901
+ this._parentFrameMessageHandler = function(event) {
4902
+ if (allowedOrigins.indexOf(event.origin) === -1) return;
4903
+ var data = event.data;
4904
+ if (data && data['type'] === IFRAME_HANDSHAKE_REQUEST && data['token'] === self.getMpConfig('token')) {
4905
+ var replayId = self.getSessionReplayId();
4906
+ if (replayId) {
4907
+ var response = {};
4908
+ response['type'] = IFRAME_HANDSHAKE_RESPONSE;
4909
+ response['token'] = self.getMpConfig('token');
4910
+ response['replayId'] = replayId;
4911
+ response['distinctId'] = self.getDistinctId();
4912
+ event.source.postMessage(response, event.origin);
4913
+ }
4914
+ }
4915
+ };
4916
+ win.addEventListener('message', this._parentFrameMessageHandler);
4917
+ };
4918
+
4788
4919
  safewrapClass(RecorderManager);
4789
4920
 
4790
4921
  /* eslint camelcase: "off" */
@@ -7351,7 +7482,6 @@ var INIT_SNIPPET = 1;
7351
7482
  /** @const */ var SETTING_FALLBACK = 'fallback';
7352
7483
  /** @const */ var SETTING_DISABLED = 'disabled';
7353
7484
 
7354
-
7355
7485
  /*
7356
7486
  * Dynamic... constants? Is that an oxymoron?
7357
7487
  */
@@ -7436,6 +7566,7 @@ var DEFAULT_CONFIG = {
7436
7566
  'batch_request_timeout_ms': 90000,
7437
7567
  'batch_autostart': true,
7438
7568
  'hooks': {},
7569
+ 'record_allowed_iframe_origins': [],
7439
7570
  'record_block_class': new RegExp('^(mp-block|fs-exclude|amp-block|rr-block|ph-no-capture)$'),
7440
7571
  'record_block_selector': 'img, video, audio',
7441
7572
  'record_canvas': false,
@@ -252,11 +252,12 @@ export interface Config {
252
252
  record_mask_all_inputs: boolean;
253
253
  record_min_ms: number;
254
254
  record_max_ms: number;
255
- record_sessions_percent: number;
255
+ record_allowed_iframe_origins: string[];
256
256
  record_canvas: boolean;
257
257
  recording_event_triggers: RecordingEventTriggers;
258
258
  record_heatmap_data: boolean;
259
259
  remote_settings_mode: RemoteSettingType;
260
+ record_sessions_percent: number;
260
261
  hooks: {
261
262
  before_identify?: (new_distinct_id: string) => string | null;
262
263
  before_register?: (