mixpanel-browser 2.78.0 → 2.80.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 (67) hide show
  1. package/.eslintrc.json +12 -0
  2. package/.github/workflows/integration-tests.yml +1 -0
  3. package/.github/workflows/openfeature-provider-tests.yml +31 -0
  4. package/CHANGELOG.md +14 -1
  5. package/build.sh +2 -2
  6. package/dist/async-modules/{mixpanel-recorder-BjSlYaNJ.min.js → mixpanel-recorder-B61POiHc.min.js} +2 -2
  7. package/dist/async-modules/mixpanel-recorder-B61POiHc.min.js.map +1 -0
  8. package/dist/async-modules/{mixpanel-recorder-zMBXIyeG.js → mixpanel-recorder-C3AW7mPl.js} +63 -35
  9. package/dist/async-modules/{mixpanel-targeting-UHf4eBfC.js → mixpanel-targeting-CBwOQJZw.js} +24 -13
  10. package/dist/async-modules/mixpanel-targeting-kdl-eE-1.min.js +2 -0
  11. package/dist/async-modules/mixpanel-targeting-kdl-eE-1.min.js.map +1 -0
  12. package/dist/mixpanel-core.cjs.d.ts +45 -1
  13. package/dist/mixpanel-core.cjs.js +577 -209
  14. package/dist/mixpanel-recorder.js +63 -35
  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 +24 -13
  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 +45 -1
  21. package/dist/mixpanel-with-async-modules.cjs.js +579 -211
  22. package/dist/mixpanel-with-async-recorder.cjs.d.ts +45 -1
  23. package/dist/mixpanel-with-async-recorder.cjs.js +579 -211
  24. package/dist/mixpanel-with-recorder.d.ts +45 -1
  25. package/dist/mixpanel-with-recorder.js +508 -136
  26. package/dist/mixpanel-with-recorder.min.d.ts +45 -1
  27. package/dist/mixpanel-with-recorder.min.js +1 -1
  28. package/dist/mixpanel.amd.d.ts +45 -1
  29. package/dist/mixpanel.amd.js +508 -136
  30. package/dist/mixpanel.cjs.d.ts +45 -1
  31. package/dist/mixpanel.cjs.js +508 -136
  32. package/dist/mixpanel.globals.js +579 -211
  33. package/dist/mixpanel.min.js +200 -190
  34. package/dist/mixpanel.module.d.ts +45 -1
  35. package/dist/mixpanel.module.js +508 -136
  36. package/dist/mixpanel.umd.d.ts +45 -1
  37. package/dist/mixpanel.umd.js +508 -136
  38. package/package.json +1 -1
  39. package/packages/openfeature-web-provider/README.md +357 -0
  40. package/packages/openfeature-web-provider/package-lock.json +1636 -0
  41. package/packages/openfeature-web-provider/package.json +51 -0
  42. package/packages/openfeature-web-provider/rollup.config.browser.mjs +26 -0
  43. package/packages/openfeature-web-provider/src/MixpanelProvider.ts +302 -0
  44. package/packages/openfeature-web-provider/src/index.ts +1 -0
  45. package/packages/openfeature-web-provider/src/types.ts +72 -0
  46. package/packages/openfeature-web-provider/test/MixpanelProvider.spec.ts +484 -0
  47. package/packages/openfeature-web-provider/tsconfig.json +15 -0
  48. package/src/autocapture/index.js +17 -12
  49. package/src/config.js +1 -1
  50. package/src/flags/flags-persistence.js +176 -0
  51. package/src/flags/index.js +176 -25
  52. package/src/index.d.ts +45 -1
  53. package/src/mixpanel-core.js +24 -7
  54. package/src/recorder/idb-config.js +16 -0
  55. package/src/recorder/recording-registry.js +7 -2
  56. package/src/recorder/session-recording.js +15 -6
  57. package/src/recorder-manager.js +7 -2
  58. package/src/request-queue.js +1 -2
  59. package/src/shared-lock.js +2 -3
  60. package/src/storage/indexed-db.js +16 -15
  61. package/src/storage/local-storage.js +5 -3
  62. package/src/utils.js +25 -12
  63. package/tsconfig.base.json +9 -0
  64. package/.claude/settings.local.json +0 -16
  65. package/dist/async-modules/mixpanel-recorder-BjSlYaNJ.min.js.map +0 -1
  66. package/dist/async-modules/mixpanel-targeting-BSHal4N9.min.js +0 -2
  67. package/dist/async-modules/mixpanel-targeting-BSHal4N9.min.js.map +0 -1
@@ -25,7 +25,7 @@ if (typeof(window) === 'undefined') {
25
25
 
26
26
  var Config = {
27
27
  DEBUG: false,
28
- LIB_VERSION: '2.78.0'
28
+ LIB_VERSION: '2.80.0'
29
29
  };
30
30
 
31
31
  // Window global names for async modules
@@ -19123,6 +19123,7 @@ var log_func_with_prefix = function(func, prefix) {
19123
19123
  var console_with_prefix = function(prefix) {
19124
19124
  return {
19125
19125
  log: log_func_with_prefix(console$1.log, prefix),
19126
+ warn: log_func_with_prefix(console$1.warn, prefix),
19126
19127
  error: log_func_with_prefix(console$1.error, prefix),
19127
19128
  critical: log_func_with_prefix(console$1.critical, prefix)
19128
19129
  };
@@ -20069,7 +20070,8 @@ var localStorageSupported = function(storage, forceCheck) {
20069
20070
  if (_localStorageSupported !== null && !forceCheck) {
20070
20071
  return _localStorageSupported;
20071
20072
  }
20072
- return _localStorageSupported = _testStorageSupported(storage || win.localStorage);
20073
+
20074
+ return _localStorageSupported = _testStorageSupported(storage);
20073
20075
  };
20074
20076
 
20075
20077
  var _sessionStorageSupported = null;
@@ -20077,7 +20079,8 @@ var sessionStorageSupported = function(storage, forceCheck) {
20077
20079
  if (_sessionStorageSupported !== null && !forceCheck) {
20078
20080
  return _sessionStorageSupported;
20079
20081
  }
20080
- return _sessionStorageSupported = _testStorageSupported(storage || win.sessionStorage);
20082
+
20083
+ return _sessionStorageSupported = _testStorageSupported(storage);
20081
20084
  };
20082
20085
 
20083
20086
  function _storageWrapper(storage, name, is_supported_fn) {
@@ -20127,17 +20130,26 @@ function _storageWrapper(storage, name, is_supported_fn) {
20127
20130
  };
20128
20131
  }
20129
20132
 
20130
- // Safari errors out accessing localStorage/sessionStorage when cookies are disabled,
20131
- // so create dummy storage wrappers that silently fail as a fallback.
20132
- var windowLocalStorage = null, windowSessionStorage = null;
20133
- try {
20134
- windowLocalStorage = win.localStorage;
20135
- windowSessionStorage = win.sessionStorage;
20136
- // eslint-disable-next-line no-empty
20137
- } catch (_err) {}
20133
+ // Safari and other browsers may error out accessing localStorage/sessionStorage
20134
+ // when cookies are disabled, so wrap access in a try-catch.
20135
+ var getLocalStorage = function() {
20136
+ try {
20137
+ return win.localStorage; // eslint-disable-line no-restricted-properties
20138
+ } catch (_err) {
20139
+ return null;
20140
+ }
20141
+ };
20142
+
20143
+ var getSessionStorage = function() {
20144
+ try {
20145
+ return win.sessionStorage; // eslint-disable-line no-restricted-properties
20146
+ } catch (_err) {
20147
+ return null;
20148
+ }
20149
+ };
20138
20150
 
20139
- _.localStorage = _storageWrapper(windowLocalStorage, 'localStorage', localStorageSupported);
20140
- _.sessionStorage = _storageWrapper(windowSessionStorage, 'sessionStorage', sessionStorageSupported);
20151
+ _.localStorage = _storageWrapper(getLocalStorage(), 'localStorage', localStorageSupported);
20152
+ _.sessionStorage = _storageWrapper(getSessionStorage(), 'sessionStorage', sessionStorageSupported);
20141
20153
 
20142
20154
  _.register_event = (function() {
20143
20155
  // written by Dean Edwards, 2005
@@ -20806,29 +20818,26 @@ _['JSONEncode'] = _.JSONEncode;
20806
20818
  _['toArray'] = _.toArray;
20807
20819
  _['NPO'] = NpoPromise;
20808
20820
 
20809
- var MIXPANEL_DB_NAME = 'mixpanelBrowserDb';
20810
-
20811
- var RECORDING_EVENTS_STORE_NAME = 'mixpanelRecordingEvents';
20812
- var RECORDING_REGISTRY_STORE_NAME = 'mixpanelRecordingRegistry';
20813
-
20814
- // note: increment the version number when adding new object stores
20815
- var DB_VERSION = 1;
20816
- var OBJECT_STORES = [RECORDING_EVENTS_STORE_NAME, RECORDING_REGISTRY_STORE_NAME];
20817
-
20818
20821
  /**
20819
20822
  * @type {import('./wrapper').StorageWrapper}
20820
20823
  */
20821
- var IDBStorageWrapper = function (storeName) {
20824
+ var IDBStorageWrapper = function (dbName, storeName, versionData) {
20825
+ this.dbName = dbName;
20826
+ this.storeName = storeName;
20827
+ this.version = versionData.version;
20828
+ this.storeNamesInDb = versionData.storeNames;
20822
20829
  /**
20823
20830
  * @type {Promise<IDBDatabase>|null}
20824
20831
  */
20825
20832
  this.dbPromise = null;
20826
- this.storeName = storeName;
20827
20833
  };
20828
20834
 
20829
20835
  IDBStorageWrapper.prototype._openDb = function () {
20836
+ var dbName = this.dbName;
20837
+ var version = this.version;
20838
+ var storeNamesInDb = this.storeNamesInDb;
20830
20839
  return new PromisePolyfill(function (resolve, reject) {
20831
- var openRequest = win.indexedDB.open(MIXPANEL_DB_NAME, DB_VERSION);
20840
+ var openRequest = win.indexedDB.open(dbName, version);
20832
20841
  openRequest['onerror'] = function () {
20833
20842
  reject(openRequest.error);
20834
20843
  };
@@ -20840,8 +20849,10 @@ IDBStorageWrapper.prototype._openDb = function () {
20840
20849
  openRequest['onupgradeneeded'] = function (ev) {
20841
20850
  var db = ev.target.result;
20842
20851
 
20843
- OBJECT_STORES.forEach(function (storeName) {
20844
- db.createObjectStore(storeName);
20852
+ storeNamesInDb.forEach(function (storeName) {
20853
+ if (!db.objectStoreNames.contains(storeName)) {
20854
+ db.createObjectStore(storeName);
20855
+ }
20845
20856
  });
20846
20857
  };
20847
20858
  });
@@ -20933,6 +20944,16 @@ IDBStorageWrapper.prototype.getAll = function () {
20933
20944
  });
20934
20945
  };
20935
20946
 
20947
+ var MIXPANEL_BROWSER_DB_NAME = 'mixpanelBrowserDb';
20948
+ var RECORDING_EVENTS_STORE_NAME = 'mixpanelRecordingEvents';
20949
+ var RECORDING_REGISTRY_STORE_NAME = 'mixpanelRecordingRegistry';
20950
+
20951
+ // Keeping these two properties closeby, as adding additional stores to a DB in IndexedDB requires a version increment
20952
+ var RECORDER_VERSION_DATA = {
20953
+ version: 1,
20954
+ storeNames: [RECORDING_EVENTS_STORE_NAME, RECORDING_REGISTRY_STORE_NAME]
20955
+ };
20956
+
20936
20957
  /**
20937
20958
  * GDPR utils
20938
20959
  *
@@ -21233,7 +21254,7 @@ function _addOptOutCheck(method, getConfigValue) {
21233
21254
  };
21234
21255
  }
21235
21256
 
21236
- var logger$8 = console_with_prefix('lock');
21257
+ var logger$9 = console_with_prefix('lock');
21237
21258
 
21238
21259
  /**
21239
21260
  * SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
@@ -21259,7 +21280,7 @@ var SharedLock = function(key, options) {
21259
21280
  options = options || {};
21260
21281
 
21261
21282
  this.storageKey = key;
21262
- this.storage = options.storage || win.localStorage;
21283
+ this.storage = options.storage || getLocalStorage();
21263
21284
  this.pollIntervalMS = options.pollIntervalMS || 100;
21264
21285
  this.timeoutMS = options.timeoutMS || 2000;
21265
21286
 
@@ -21285,7 +21306,7 @@ SharedLock.prototype.withLock = function(lockedCB, pid) {
21285
21306
 
21286
21307
  var delay = function(cb) {
21287
21308
  if (new Date().getTime() - startTime > timeoutMS) {
21288
- logger$8.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
21309
+ logger$9.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
21289
21310
  storage.removeItem(keyZ);
21290
21311
  storage.removeItem(keyY);
21291
21312
  loop();
@@ -21387,10 +21408,13 @@ SharedLock.prototype.withLock = function(lockedCB, pid) {
21387
21408
  * @type {import('./wrapper').StorageWrapper}
21388
21409
  */
21389
21410
  var LocalStorageWrapper = function (storageOverride) {
21390
- this.storage = storageOverride || win.localStorage;
21411
+ this.storage = storageOverride || getLocalStorage();
21391
21412
  };
21392
21413
 
21393
21414
  LocalStorageWrapper.prototype.init = function () {
21415
+ if (!this.storage) {
21416
+ return PromisePolyfill.reject(new Error('localStorage is not available'));
21417
+ }
21394
21418
  return PromisePolyfill.resolve();
21395
21419
  };
21396
21420
 
@@ -21432,7 +21456,7 @@ LocalStorageWrapper.prototype.removeItem = function (key) {
21432
21456
  }, this));
21433
21457
  };
21434
21458
 
21435
- var logger$7 = console_with_prefix('batch');
21459
+ var logger$8 = console_with_prefix('batch');
21436
21460
 
21437
21461
  /**
21438
21462
  * RequestQueue: queue for batching API requests with localStorage backup for retries.
@@ -21457,11 +21481,11 @@ var RequestQueue = function (storageKey, options) {
21457
21481
  if (this.usePersistence) {
21458
21482
  this.queueStorage = options.queueStorage || new LocalStorageWrapper();
21459
21483
  this.lock = new SharedLock(storageKey, {
21460
- storage: options.sharedLockStorage || win.localStorage,
21484
+ storage: options.sharedLockStorage,
21461
21485
  timeoutMS: options.sharedLockTimeoutMS,
21462
21486
  });
21463
21487
  }
21464
- this.reportError = options.errorReporter || _.bind(logger$7.error, logger$7);
21488
+ this.reportError = options.errorReporter || _.bind(logger$8.error, logger$8);
21465
21489
 
21466
21490
  this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
21467
21491
 
@@ -21794,7 +21818,7 @@ RequestQueue.prototype.clear = function () {
21794
21818
  // maximum interval between request retries after exponential backoff
21795
21819
  var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
21796
21820
 
21797
- var logger$6 = console_with_prefix('batch');
21821
+ var logger$7 = console_with_prefix('batch');
21798
21822
 
21799
21823
  /**
21800
21824
  * RequestBatcher: manages the queueing, flushing, retry etc of requests of one
@@ -21922,7 +21946,7 @@ RequestBatcher.prototype.sendRequestPromise = function(data, options) {
21922
21946
  */
21923
21947
  RequestBatcher.prototype.flush = function(options) {
21924
21948
  if (this.requestInProgress) {
21925
- logger$6.log('Flush: Request already in progress');
21949
+ logger$7.log('Flush: Request already in progress');
21926
21950
  return PromisePolyfill.resolve();
21927
21951
  }
21928
21952
 
@@ -22099,7 +22123,7 @@ RequestBatcher.prototype.flush = function(options) {
22099
22123
  if (options.unloading) {
22100
22124
  requestOptions.transport = 'sendBeacon';
22101
22125
  }
22102
- logger$6.log('MIXPANEL REQUEST:', dataForRequest);
22126
+ logger$7.log('MIXPANEL REQUEST:', dataForRequest);
22103
22127
  return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
22104
22128
  }, this))
22105
22129
  .catch(_.bind(function(err) {
@@ -22112,7 +22136,7 @@ RequestBatcher.prototype.flush = function(options) {
22112
22136
  * Log error to global logger and optional user-defined logger.
22113
22137
  */
22114
22138
  RequestBatcher.prototype.reportError = function(msg, err) {
22115
- logger$6.error.apply(logger$6.error, arguments);
22139
+ logger$7.error.apply(logger$7.error, arguments);
22116
22140
  if (this.errorReporter) {
22117
22141
  try {
22118
22142
  if (!(err instanceof Error)) {
@@ -22120,7 +22144,7 @@ RequestBatcher.prototype.reportError = function(msg, err) {
22120
22144
  }
22121
22145
  this.errorReporter(msg, err);
22122
22146
  } catch(err) {
22123
- logger$6.error(err);
22147
+ logger$7.error(err);
22124
22148
  }
22125
22149
  }
22126
22150
  };
@@ -22265,7 +22289,7 @@ var EVENT_HANDLER_ATTRIBUTES = [
22265
22289
 
22266
22290
  var MAX_DEPTH = 5;
22267
22291
 
22268
- var logger$5 = console_with_prefix('autocapture');
22292
+ var logger$6 = console_with_prefix('autocapture');
22269
22293
 
22270
22294
 
22271
22295
  function getClasses(el) {
@@ -22529,7 +22553,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
22529
22553
  return false;
22530
22554
  }
22531
22555
  } catch (err) {
22532
- logger$5.critical('Error while checking element in allowElementCallback', err);
22556
+ logger$6.critical('Error while checking element in allowElementCallback', err);
22533
22557
  return false;
22534
22558
  }
22535
22559
  }
@@ -22546,7 +22570,7 @@ function isElementAllowed(el, ev, allowElementCallback, allowSelectors) {
22546
22570
  return true;
22547
22571
  }
22548
22572
  } catch (err) {
22549
- logger$5.critical('Error while checking selector: ' + sel, err);
22573
+ logger$6.critical('Error while checking selector: ' + sel, err);
22550
22574
  }
22551
22575
  }
22552
22576
  return false;
@@ -22561,7 +22585,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
22561
22585
  return true;
22562
22586
  }
22563
22587
  } catch (err) {
22564
- logger$5.critical('Error while checking element in blockElementCallback', err);
22588
+ logger$6.critical('Error while checking element in blockElementCallback', err);
22565
22589
  return true;
22566
22590
  }
22567
22591
  }
@@ -22575,7 +22599,7 @@ function isElementBlocked(el, ev, blockElementCallback, blockSelectors) {
22575
22599
  return true;
22576
22600
  }
22577
22601
  } catch (err) {
22578
- logger$5.critical('Error while checking selector: ' + sel, err);
22602
+ logger$6.critical('Error while checking selector: ' + sel, err);
22579
22603
  }
22580
22604
  }
22581
22605
  }
@@ -23131,7 +23155,7 @@ function shouldMaskText(element, privacyConfig) {
23131
23155
  *
23132
23156
  */
23133
23157
 
23134
- var logger$4 = console_with_prefix('network-plugin');
23158
+ var logger$5 = console_with_prefix('network-plugin');
23135
23159
 
23136
23160
  /**
23137
23161
  * Get the time origin for converting performance timestamps to absolute timestamps.
@@ -23283,7 +23307,7 @@ function truncateBody(str) {
23283
23307
  return str;
23284
23308
  }
23285
23309
  if (str.length > MAX_BODY_SIZE) {
23286
- logger$4.error('Body truncated from ' + str.length + ' to ' + MAX_BODY_SIZE + ' characters');
23310
+ logger$5.error('Body truncated from ' + str.length + ' to ' + MAX_BODY_SIZE + ' characters');
23287
23311
  return str.substring(0, MAX_BODY_SIZE) + '... [truncated]';
23288
23312
  }
23289
23313
  return str;
@@ -23297,7 +23321,7 @@ function truncateBody(str) {
23297
23321
  */
23298
23322
  function initPerformanceObserver(cb, win, options) {
23299
23323
  if (!win.PerformanceObserver) {
23300
- logger$4.error('PerformanceObserver not supported');
23324
+ logger$5.error('PerformanceObserver not supported');
23301
23325
  return function() {
23302
23326
  //
23303
23327
  };
@@ -23450,7 +23474,7 @@ function getRequestPerformanceEntry(win, initiatorType, url, after, before, atte
23450
23474
  attempt = 0;
23451
23475
  }
23452
23476
  if (attempt > 10) {
23453
- logger$4.error('Cannot find performance entry');
23477
+ logger$5.error('Cannot find performance entry');
23454
23478
  return Promise.resolve(null);
23455
23479
  }
23456
23480
  var urlPerformanceEntries = /** @type {PerformanceResourceTiming[]} */ (
@@ -23571,7 +23595,7 @@ function initXhrObserver(cb, win, options) {
23571
23595
  )
23572
23596
  .then(function(entry) {
23573
23597
  if (!entry) {
23574
- logger$4.error('Failed to get performance entry for XHR request to ' + req.url);
23598
+ logger$5.error('Failed to get performance entry for XHR request to ' + req.url);
23575
23599
  return;
23576
23600
  }
23577
23601
  /** @type {NetworkRequest} */
@@ -23591,7 +23615,7 @@ function initXhrObserver(cb, win, options) {
23591
23615
  cb({ requests: [request] });
23592
23616
  })
23593
23617
  .catch(function(e) {
23594
- logger$4.error('Error recording XHR request to ' + req.url + ': ' + String(e));
23618
+ logger$5.error('Error recording XHR request to ' + req.url + ': ' + String(e));
23595
23619
  });
23596
23620
  });
23597
23621
 
@@ -23683,7 +23707,7 @@ function initFetchObserver(cb, win, options) {
23683
23707
  })
23684
23708
  .then(function(entry) {
23685
23709
  if (!entry) {
23686
- logger$4.error('Failed to get performance entry for fetch request to ' + req.url);
23710
+ logger$5.error('Failed to get performance entry for fetch request to ' + req.url);
23687
23711
  return;
23688
23712
  }
23689
23713
  /** @type {NetworkRequest} */
@@ -23703,7 +23727,7 @@ function initFetchObserver(cb, win, options) {
23703
23727
  cb({ requests: [request] });
23704
23728
  })
23705
23729
  .catch(function (e) {
23706
- logger$4.error('Error recording fetch request to ' + req.url + ': ' + String(e));
23730
+ logger$5.error('Error recording fetch request to ' + req.url + ': ' + String(e));
23707
23731
  });
23708
23732
 
23709
23733
  return originalFetchPromise;
@@ -23776,7 +23800,7 @@ var getRecordNetworkPlugin = function(options) {
23776
23800
  */
23777
23801
 
23778
23802
 
23779
- var logger$3 = console_with_prefix('recorder');
23803
+ var logger$4 = console_with_prefix('recorder');
23780
23804
  var CompressionStream = win['CompressionStream'];
23781
23805
 
23782
23806
  var RECORDER_BATCHER_LIB_CONFIG = {
@@ -23870,14 +23894,15 @@ var SessionRecording = function(options) {
23870
23894
 
23871
23895
  this.recordMaxMs = MAX_RECORDING_MS;
23872
23896
  this.recordMinMs = 0;
23897
+ this._recordMinMsCheckStart = null;
23873
23898
 
23874
23899
  // disable persistence if localStorage is not supported
23875
23900
  // request-queue will automatically disable persistence if indexedDB fails to initialize
23876
- var usePersistence = localStorageSupported(options.sharedLockStorage, true) && !this.getConfig('disable_persistence');
23901
+ var usePersistence = localStorageSupported(options.sharedLockStorage || getLocalStorage(), true) && !this.getConfig('disable_persistence');
23877
23902
 
23878
23903
  // each replay has its own batcher key to avoid conflicts between rrweb events of different recordings
23879
23904
  this.batcherKey = '__mprec_' + this.getConfig('name') + '_' + this.getConfig('token') + '_' + this.replayId;
23880
- this.queueStorage = new IDBStorageWrapper(RECORDING_EVENTS_STORE_NAME);
23905
+ this.queueStorage = new IDBStorageWrapper(MIXPANEL_BROWSER_DB_NAME, RECORDING_EVENTS_STORE_NAME, RECORDER_VERSION_DATA);
23881
23906
  this.batcher = new RequestBatcher(this.batcherKey, {
23882
23907
  errorReporter: this.reportError.bind(this),
23883
23908
  flushOnlyOnInterval: true,
@@ -23956,14 +23981,14 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
23956
23981
  }
23957
23982
 
23958
23983
  if (this._stopRecording !== null) {
23959
- logger$3.log('Recording already in progress, skipping startRecording.');
23984
+ logger$4.log('Recording already in progress, skipping startRecording.');
23960
23985
  return;
23961
23986
  }
23962
23987
 
23963
23988
  this.recordMaxMs = this.getConfig('record_max_ms');
23964
23989
  if (this.recordMaxMs > MAX_RECORDING_MS) {
23965
23990
  this.recordMaxMs = MAX_RECORDING_MS;
23966
- logger$3.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
23991
+ logger$4.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
23967
23992
  }
23968
23993
 
23969
23994
  if (!this.maxExpires) {
@@ -23985,6 +24010,7 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
23985
24010
  // this also applies if the minimum recording length has not been hit yet
23986
24011
  // so that we don't send data until we know the recording will be long enough
23987
24012
  this.batcher.stop();
24013
+ this._recordMinMsCheckStart = null;
23988
24014
  } else {
23989
24015
  this.batcher.start();
23990
24016
  }
@@ -24027,7 +24053,7 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
24027
24053
  );
24028
24054
  }
24029
24055
 
24030
- var validatedOrigins = validateAllowedOrigins(this.getConfig('record_allowed_iframe_origins'), logger$3);
24056
+ var validatedOrigins = validateAllowedOrigins(this.getConfig('record_allowed_iframe_origins'), logger$4);
24031
24057
 
24032
24058
  try {
24033
24059
  this._stopRecording = this._rrwebRecord({
@@ -24036,9 +24062,11 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
24036
24062
  this._onIdleTimeout();
24037
24063
  return;
24038
24064
  }
24065
+ if (this._recordMinMsCheckStart === null) {
24066
+ this._recordMinMsCheckStart = ev.timestamp;
24067
+ }
24039
24068
  if (isUserEvent(ev)) {
24040
- if (this.batcher.stopped && new Date().getTime() - this.replayStartTime >= this.recordMinMs) {
24041
- // start flushing again after user activity
24069
+ if (this.batcher.stopped && ev.timestamp - this._recordMinMsCheckStart >= this.recordMinMs) {
24042
24070
  this.batcher.start();
24043
24071
  }
24044
24072
  resetIdleTimeout();
@@ -24289,14 +24317,14 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
24289
24317
 
24290
24318
 
24291
24319
  SessionRecording.prototype.reportError = function(msg, err) {
24292
- logger$3.error.apply(logger$3.error, arguments);
24320
+ logger$4.error.apply(logger$4.error, arguments);
24293
24321
  try {
24294
24322
  if (!err && !(msg instanceof Error)) {
24295
24323
  msg = new Error(msg);
24296
24324
  }
24297
24325
  this.getConfig('error_reporter')(msg, err);
24298
24326
  } catch(err) {
24299
- logger$3.error(err);
24327
+ logger$4.error(err);
24300
24328
  }
24301
24329
  };
24302
24330
 
@@ -24325,7 +24353,7 @@ SessionRecording.prototype._getRecordMinMs = function() {
24325
24353
  var configValue = this.getConfig('record_min_ms');
24326
24354
 
24327
24355
  if (configValue > MAX_VALUE_FOR_MIN_RECORDING_MS) {
24328
- logger$3.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
24356
+ logger$4.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
24329
24357
  return MAX_VALUE_FOR_MIN_RECORDING_MS;
24330
24358
  }
24331
24359
 
@@ -24367,7 +24395,7 @@ SessionRecording.prototype._getMaskFn = function(shouldMaskFn, privacyConfig) {
24367
24395
  */
24368
24396
  var RecordingRegistry = function (options) {
24369
24397
  /** @type {IDBStorageWrapper} */
24370
- this.idb = new IDBStorageWrapper(RECORDING_REGISTRY_STORE_NAME);
24398
+ this.idb = new IDBStorageWrapper(MIXPANEL_BROWSER_DB_NAME, RECORDING_REGISTRY_STORE_NAME, RECORDER_VERSION_DATA);
24371
24399
  this.errorReporter = options.errorReporter;
24372
24400
  this.mixpanelInstance = options.mixpanelInstance;
24373
24401
  this.sharedLockStorage = options.sharedLockStorage;
@@ -24488,7 +24516,7 @@ RecordingRegistry.prototype.flushInactiveRecordings = function () {
24488
24516
  .catch(this.handleError.bind(this));
24489
24517
  };
24490
24518
 
24491
- var logger$2 = console_with_prefix('recorder');
24519
+ var logger$3 = console_with_prefix('recorder');
24492
24520
 
24493
24521
  /**
24494
24522
  * Recorder API: bundles rrweb and and exposes methods to start and stop recordings.
@@ -24504,7 +24532,7 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
24504
24532
  */
24505
24533
  this.recordingRegistry = new RecordingRegistry({
24506
24534
  mixpanelInstance: this.mixpanelInstance,
24507
- errorReporter: logger$2.error,
24535
+ errorReporter: logger$3.error,
24508
24536
  sharedLockStorage: sharedLockStorage
24509
24537
  });
24510
24538
  this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
@@ -24516,17 +24544,17 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
24516
24544
  MixpanelRecorder.prototype.startRecording = function(options) {
24517
24545
  options = options || {};
24518
24546
  if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
24519
- logger$2.log('Recording already in progress, skipping startRecording.');
24547
+ logger$3.log('Recording already in progress, skipping startRecording.');
24520
24548
  return;
24521
24549
  }
24522
24550
 
24523
24551
  var onIdleTimeout = function () {
24524
- logger$2.log('Idle timeout reached, restarting recording.');
24552
+ logger$3.log('Idle timeout reached, restarting recording.');
24525
24553
  this.resetRecording();
24526
24554
  }.bind(this);
24527
24555
 
24528
24556
  var onMaxLengthReached = function () {
24529
- logger$2.log('Max recording length reached, stopping recording.');
24557
+ logger$3.log('Max recording length reached, stopping recording.');
24530
24558
  this.resetRecording();
24531
24559
  }.bind(this);
24532
24560
 
@@ -24596,7 +24624,7 @@ MixpanelRecorder.prototype.resumeRecording = function (startNewIfInactive) {
24596
24624
  } else if (startNewIfInactive) {
24597
24625
  return this.startRecording({shouldStopBatcher: false});
24598
24626
  } else {
24599
- logger$2.log('No resumable recording found.');
24627
+ logger$3.log('No resumable recording found.');
24600
24628
  return null;
24601
24629
  }
24602
24630
  }.bind(this));
@@ -25261,7 +25289,7 @@ ShadowDOMObserver.prototype.observeShadowRoot = function(shadowRoot) {
25261
25289
  observer.observe(shadowRoot, this.observerConfig);
25262
25290
  this.shadowObservers.push(observer);
25263
25291
  } catch (e) {
25264
- logger$5.critical('Error while observing shadow root', e);
25292
+ logger$6.critical('Error while observing shadow root', e);
25265
25293
  }
25266
25294
  };
25267
25295
 
@@ -25272,7 +25300,7 @@ ShadowDOMObserver.prototype.start = function() {
25272
25300
  }
25273
25301
 
25274
25302
  if (!weakSetSupported()) {
25275
- logger$5.critical('Shadow DOM observation unavailable: WeakSet not supported');
25303
+ logger$6.critical('Shadow DOM observation unavailable: WeakSet not supported');
25276
25304
  return;
25277
25305
  }
25278
25306
 
@@ -25288,7 +25316,7 @@ ShadowDOMObserver.prototype.stop = function() {
25288
25316
  try {
25289
25317
  this.shadowObservers[i].disconnect();
25290
25318
  } catch (e) {
25291
- logger$5.critical('Error while disconnecting shadow DOM observer', e);
25319
+ logger$6.critical('Error while disconnecting shadow DOM observer', e);
25292
25320
  }
25293
25321
  }
25294
25322
  this.shadowObservers = [];
@@ -25476,7 +25504,7 @@ DeadClickTracker.prototype.startTracking = function() {
25476
25504
 
25477
25505
  this.mutationObserver.observe(document.body || document.documentElement, MUTATION_OBSERVER_CONFIG);
25478
25506
  } catch (e) {
25479
- logger$5.critical('Error while setting up mutation observer', e);
25507
+ logger$6.critical('Error while setting up mutation observer', e);
25480
25508
  }
25481
25509
  }
25482
25510
 
@@ -25491,7 +25519,7 @@ DeadClickTracker.prototype.startTracking = function() {
25491
25519
  );
25492
25520
  this.shadowDOMObserver.start();
25493
25521
  } catch (e) {
25494
- logger$5.critical('Error while setting up shadow DOM observer', e);
25522
+ logger$6.critical('Error while setting up shadow DOM observer', e);
25495
25523
  this.shadowDOMObserver = null;
25496
25524
  }
25497
25525
  }
@@ -25518,7 +25546,7 @@ DeadClickTracker.prototype.stopTracking = function() {
25518
25546
  try {
25519
25547
  listener.target.removeEventListener(listener.event, listener.handler, listener.options);
25520
25548
  } catch (e) {
25521
- logger$5.critical('Error while removing event listener', e);
25549
+ logger$6.critical('Error while removing event listener', e);
25522
25550
  }
25523
25551
  }
25524
25552
  this.eventListeners = [];
@@ -25527,7 +25555,7 @@ DeadClickTracker.prototype.stopTracking = function() {
25527
25555
  try {
25528
25556
  this.mutationObserver.disconnect();
25529
25557
  } catch (e) {
25530
- logger$5.critical('Error while disconnecting mutation observer', e);
25558
+ logger$6.critical('Error while disconnecting mutation observer', e);
25531
25559
  }
25532
25560
  this.mutationObserver = null;
25533
25561
  }
@@ -25536,7 +25564,7 @@ DeadClickTracker.prototype.stopTracking = function() {
25536
25564
  try {
25537
25565
  this.shadowDOMObserver.stop();
25538
25566
  } catch (e) {
25539
- logger$5.critical('Error while stopping shadow DOM observer', e);
25567
+ logger$6.critical('Error while stopping shadow DOM observer', e);
25540
25568
  }
25541
25569
  this.shadowDOMObserver = null;
25542
25570
  }
@@ -25614,7 +25642,7 @@ var Autocapture = function(mp) {
25614
25642
 
25615
25643
  Autocapture.prototype.init = function() {
25616
25644
  if (!minDOMApisSupported()) {
25617
- logger$5.critical('Autocapture unavailable: missing required DOM APIs');
25645
+ logger$6.critical('Autocapture unavailable: missing required DOM APIs');
25618
25646
  return;
25619
25647
  }
25620
25648
  this.initPageListeners();
@@ -25654,7 +25682,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
25654
25682
  try {
25655
25683
  return !urlMatchesRegexList(currentUrl, allowUrlRegexes);
25656
25684
  } catch (err) {
25657
- logger$5.critical('Error while checking block URL regexes: ', err);
25685
+ logger$6.critical('Error while checking block URL regexes: ', err);
25658
25686
  return true;
25659
25687
  }
25660
25688
  }
@@ -25667,7 +25695,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
25667
25695
  try {
25668
25696
  return urlMatchesRegexList(currentUrl, blockUrlRegexes);
25669
25697
  } catch (err) {
25670
- logger$5.critical('Error while checking block URL regexes: ', err);
25698
+ logger$6.critical('Error while checking block URL regexes: ', err);
25671
25699
  return true;
25672
25700
  }
25673
25701
  };
@@ -25805,7 +25833,7 @@ Autocapture.prototype._initScrollDepthTracking = function() {
25805
25833
  return;
25806
25834
  }
25807
25835
 
25808
- logger$5.log('Initializing scroll depth tracking');
25836
+ logger$6.log('Initializing scroll depth tracking');
25809
25837
 
25810
25838
  this.maxScrollViewDepth = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
25811
25839
 
@@ -25826,12 +25854,12 @@ Autocapture.prototype._initScrollDepthTracking = function() {
25826
25854
  };
25827
25855
 
25828
25856
  Autocapture.prototype.initClickTracking = function() {
25829
- win.removeEventListener(EV_CLICK, this.listenerClick);
25857
+ win.removeEventListener(EV_CLICK, this.listenerClick, true);
25830
25858
 
25831
25859
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
25832
25860
  return;
25833
25861
  }
25834
- logger$5.log('Initializing click tracking');
25862
+ logger$6.log('Initializing click tracking');
25835
25863
 
25836
25864
  this.listenerClick = function(ev) {
25837
25865
  if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
@@ -25839,7 +25867,7 @@ Autocapture.prototype.initClickTracking = function() {
25839
25867
  }
25840
25868
  this.trackDomEvent(ev, MP_EV_CLICK);
25841
25869
  }.bind(this);
25842
- win.addEventListener(EV_CLICK, this.listenerClick);
25870
+ win.addEventListener(EV_CLICK, this.listenerClick, true);
25843
25871
  };
25844
25872
 
25845
25873
  Autocapture.prototype.initDeadClickTracking = function() {
@@ -25850,7 +25878,7 @@ Autocapture.prototype.initDeadClickTracking = function() {
25850
25878
  return;
25851
25879
  }
25852
25880
 
25853
- logger$5.log('Initializing dead click tracking');
25881
+ logger$6.log('Initializing dead click tracking');
25854
25882
  if (!this._deadClickTracker) {
25855
25883
  this._deadClickTracker = new DeadClickTracker(function(deadClickEvent) {
25856
25884
  this.trackDomEvent(deadClickEvent, MP_EV_DEAD_CLICK);
@@ -25874,17 +25902,17 @@ Autocapture.prototype.initDeadClickTracking = function() {
25874
25902
  }
25875
25903
  this._deadClickTracker.trackClick(ev, normalizedConfig);
25876
25904
  }.bind(this);
25877
- win.addEventListener(EV_CLICK, this.listenerDeadClick);
25905
+ win.addEventListener(EV_CLICK, this.listenerDeadClick, true);
25878
25906
  }
25879
25907
  };
25880
25908
 
25881
25909
  Autocapture.prototype.initInputTracking = function() {
25882
- win.removeEventListener(EV_CHANGE, this.listenerChange);
25910
+ win.removeEventListener(EV_CHANGE, this.listenerChange, true);
25883
25911
 
25884
25912
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
25885
25913
  return;
25886
25914
  }
25887
- logger$5.log('Initializing input tracking');
25915
+ logger$6.log('Initializing input tracking');
25888
25916
 
25889
25917
  this.listenerChange = function(ev) {
25890
25918
  if (!this.getConfig(CONFIG_TRACK_INPUT)) {
@@ -25892,20 +25920,21 @@ Autocapture.prototype.initInputTracking = function() {
25892
25920
  }
25893
25921
  this.trackDomEvent(ev, MP_EV_INPUT);
25894
25922
  }.bind(this);
25895
- win.addEventListener(EV_CHANGE, this.listenerChange);
25923
+ win.addEventListener(EV_CHANGE, this.listenerChange, true);
25896
25924
  };
25897
25925
 
25898
25926
  Autocapture.prototype.initPageviewTracking = function() {
25899
25927
  win.removeEventListener(EV_MP_LOCATION_CHANGE, this.listenerLocationchange);
25900
25928
 
25901
- if (!this.pageviewTrackingConfig()) {
25929
+ if (!this.pageviewTrackingConfig() && !this.mp.get_config('record_heatmap_data')) {
25902
25930
  return;
25903
25931
  }
25904
- logger$5.log('Initializing pageview tracking');
25932
+ logger$6.log('Initializing pageview tracking');
25905
25933
 
25906
25934
  var previousTrackedUrl = '';
25907
25935
  var tracked = false;
25908
- if (!this.currentUrlBlocked()) {
25936
+ // Track initial pageview if pageview tracking enabled OR heatmap recording is active
25937
+ if ((this.pageviewTrackingConfig() || this.mp.is_recording_heatmap_data()) && !this.currentUrlBlocked()) {
25909
25938
  tracked = this.mp.track_pageview(DEFAULT_PROPS);
25910
25939
  }
25911
25940
  if (tracked) {
@@ -25921,6 +25950,10 @@ Autocapture.prototype.initPageviewTracking = function() {
25921
25950
  var shouldTrack = false;
25922
25951
  var didPathChange = currentUrl.split('#')[0].split('?')[0] !== previousTrackedUrl.split('#')[0].split('?')[0];
25923
25952
  var trackPageviewOption = this.pageviewTrackingConfig();
25953
+ if (!trackPageviewOption && this.mp.is_recording_heatmap_data()) {
25954
+ trackPageviewOption = PAGEVIEW_OPTION_FULL_URL;
25955
+ }
25956
+
25924
25957
  if (trackPageviewOption === PAGEVIEW_OPTION_FULL_URL) {
25925
25958
  shouldTrack = currentUrl !== previousTrackedUrl;
25926
25959
  } else if (trackPageviewOption === PAGEVIEW_OPTION_URL_WITH_PATH_AND_QUERY_STRING) {
@@ -25936,7 +25969,7 @@ Autocapture.prototype.initPageviewTracking = function() {
25936
25969
  }
25937
25970
  if (didPathChange) {
25938
25971
  this.lastScrollCheckpoint = 0;
25939
- logger$5.log('Path change: re-initializing scroll depth checkpoints');
25972
+ logger$6.log('Path change: re-initializing scroll depth checkpoints');
25940
25973
  }
25941
25974
  }
25942
25975
  }.bind(this));
@@ -25944,14 +25977,14 @@ Autocapture.prototype.initPageviewTracking = function() {
25944
25977
  };
25945
25978
 
25946
25979
  Autocapture.prototype.initRageClickTracking = function() {
25947
- win.removeEventListener(EV_CLICK, this.listenerRageClick);
25980
+ win.removeEventListener(EV_CLICK, this.listenerRageClick, true);
25948
25981
 
25949
25982
  var rageClickConfig = this._getClickTrackingConfig(CONFIG_TRACK_RAGE_CLICK);
25950
25983
  if (!rageClickConfig && !this.mp.get_config('record_heatmap_data')) {
25951
25984
  return;
25952
25985
  }
25953
25986
 
25954
- logger$5.log('Initializing rage click tracking');
25987
+ logger$6.log('Initializing rage click tracking');
25955
25988
  if (!this._rageClickTracker) {
25956
25989
  this._rageClickTracker = new RageClickTracker();
25957
25990
  }
@@ -25970,7 +26003,7 @@ Autocapture.prototype.initRageClickTracking = function() {
25970
26003
  this.trackDomEvent(ev, MP_EV_RAGE_CLICK);
25971
26004
  }
25972
26005
  }.bind(this);
25973
- win.addEventListener(EV_CLICK, this.listenerRageClick);
26006
+ win.addEventListener(EV_CLICK, this.listenerRageClick, true);
25974
26007
  };
25975
26008
 
25976
26009
  Autocapture.prototype.initScrollTracking = function() {
@@ -25981,7 +26014,7 @@ Autocapture.prototype.initScrollTracking = function() {
25981
26014
  if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
25982
26015
  return;
25983
26016
  }
25984
- logger$5.log('Initializing scroll tracking');
26017
+ logger$6.log('Initializing scroll tracking');
25985
26018
  this.lastScrollCheckpoint = 0;
25986
26019
 
25987
26020
  var scrollTrackFunction = function() {
@@ -26018,7 +26051,7 @@ Autocapture.prototype.initScrollTracking = function() {
26018
26051
  }
26019
26052
  }
26020
26053
  } catch (err) {
26021
- logger$5.critical('Error while calculating scroll percentage', err);
26054
+ logger$6.critical('Error while calculating scroll percentage', err);
26022
26055
  }
26023
26056
  if (shouldTrack) {
26024
26057
  this.mp.track(MP_EV_SCROLL, props);
@@ -26031,12 +26064,12 @@ Autocapture.prototype.initScrollTracking = function() {
26031
26064
  };
26032
26065
 
26033
26066
  Autocapture.prototype.initSubmitTracking = function() {
26034
- win.removeEventListener(EV_SUBMIT, this.listenerSubmit);
26067
+ win.removeEventListener(EV_SUBMIT, this.listenerSubmit, true);
26035
26068
 
26036
26069
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
26037
26070
  return;
26038
26071
  }
26039
- logger$5.log('Initializing submit tracking');
26072
+ logger$6.log('Initializing submit tracking');
26040
26073
 
26041
26074
  this.listenerSubmit = function(ev) {
26042
26075
  if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
@@ -26044,7 +26077,7 @@ Autocapture.prototype.initSubmitTracking = function() {
26044
26077
  }
26045
26078
  this.trackDomEvent(ev, MP_EV_SUBMIT);
26046
26079
  }.bind(this);
26047
- win.addEventListener(EV_SUBMIT, this.listenerSubmit);
26080
+ win.addEventListener(EV_SUBMIT, this.listenerSubmit, true);
26048
26081
  };
26049
26082
 
26050
26083
  Autocapture.prototype.initPageLeaveTracking = function() {
@@ -26058,7 +26091,7 @@ Autocapture.prototype.initPageLeaveTracking = function() {
26058
26091
  return;
26059
26092
  }
26060
26093
 
26061
- logger$5.log('Initializing page visibility tracking.');
26094
+ logger$6.log('Initializing page visibility tracking.');
26062
26095
  this._initScrollDepthTracking();
26063
26096
  var previousTrackedUrl = _.info.currentUrl();
26064
26097
 
@@ -26100,7 +26133,7 @@ Autocapture.prototype.initPageLeaveTracking = function() {
26100
26133
 
26101
26134
  Autocapture.prototype.stopDeadClickTracking = function() {
26102
26135
  if (this.listenerDeadClick) {
26103
- win.removeEventListener(EV_CLICK, this.listenerDeadClick);
26136
+ win.removeEventListener(EV_CLICK, this.listenerDeadClick, true);
26104
26137
  this.listenerDeadClick = null;
26105
26138
  }
26106
26139
 
@@ -26143,10 +26176,183 @@ var getTargetingPromise = function(loadExtraBundle, targetingSrc) {
26143
26176
  return win[TARGETING_GLOBAL_NAME];
26144
26177
  };
26145
26178
 
26179
+ var logger$2 = console_with_prefix('flags');
26180
+
26181
+ var MIXPANEL_FLAGS_DB_NAME = 'mixpanelFlagsDb';
26182
+ var FLAGS_STORE_NAME = 'mixpanelFlags';
26183
+
26184
+ // Keeping these two properties closeby, as adding additional stores to a DB in IndexedDB requires a version increment
26185
+ var FLAGS_VERSION_DATA = { version: 1, storeNames: [FLAGS_STORE_NAME] };
26186
+
26187
+ var PERSISTED_VARIANTS_KEY_PREFIX = 'persisted_variants_for_';
26188
+ var DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
26189
+
26190
+ var VariantLookupPolicy = Object.freeze({
26191
+ NETWORK_ONLY: 'networkOnly',
26192
+ NETWORK_FIRST: 'networkFirst',
26193
+ PERSISTENCE_UNTIL_NETWORK_SUCCESS: 'persistenceUntilNetworkSuccess'
26194
+ });
26195
+
26196
+ var VALID_POLICIES = [
26197
+ VariantLookupPolicy.NETWORK_ONLY,
26198
+ VariantLookupPolicy.NETWORK_FIRST,
26199
+ VariantLookupPolicy.PERSISTENCE_UNTIL_NETWORK_SUCCESS
26200
+ ];
26201
+
26202
+ /**
26203
+ * Module for handling the storage and retrieval of persisted feature flag variants.
26204
+ */
26205
+ var FeatureFlagPersistence = function(persistenceConfig, token, isGloballyDisabled) {
26206
+ this.idb = new IDBStorageWrapper(MIXPANEL_FLAGS_DB_NAME, FLAGS_STORE_NAME, FLAGS_VERSION_DATA);
26207
+ this.persistenceConfig = persistenceConfig;
26208
+ this.persistedVariantsKey = PERSISTED_VARIANTS_KEY_PREFIX + token;
26209
+ this.isGloballyDisabled = isGloballyDisabled || function() { return false; };
26210
+ };
26211
+
26212
+ FeatureFlagPersistence.prototype.getPolicy = function() {
26213
+ if (this.isGloballyDisabled() || !this._isConfigValid()) {
26214
+ return VariantLookupPolicy.NETWORK_ONLY;
26215
+ }
26216
+ return this.persistenceConfig['variantLookupPolicy'];
26217
+ };
26218
+
26219
+ FeatureFlagPersistence.prototype.getTtlMs = function() {
26220
+ if (!this._isConfigValid()) {
26221
+ return DEFAULT_TTL_MS;
26222
+ }
26223
+ var configuredTtl = this.persistenceConfig['persistenceTtlMs'];
26224
+ return (configuredTtl === undefined || configuredTtl === null) ? DEFAULT_TTL_MS : configuredTtl;
26225
+ };
26226
+
26227
+ FeatureFlagPersistence.prototype._isConfigValid = function() {
26228
+ var config = this.persistenceConfig;
26229
+ if (!config) {
26230
+ return false;
26231
+ }
26232
+
26233
+ if (VALID_POLICIES.indexOf(config['variantLookupPolicy']) === -1) {
26234
+ logger$2.error('Invalid variantLookupPolicy:', config['variantLookupPolicy']);
26235
+ return false;
26236
+ }
26237
+
26238
+ if (config['persistenceTtlMs'] !== undefined &&
26239
+ config['persistenceTtlMs'] !== null &&
26240
+ config['persistenceTtlMs'] <= 0) {
26241
+ logger$2.error('If provided, persistenceTtlMs must be a positive number. Provided value:', config['persistenceTtlMs']);
26242
+ return false;
26243
+ }
26244
+
26245
+ return true;
26246
+ };
26247
+
26248
+ FeatureFlagPersistence.prototype.loadFlagsFromStorage = function(context) {
26249
+ var clearAndReturnNull = _.bind(function() {
26250
+ return this.clear().then(function() { return null; }).catch(function() { return null; });
26251
+ }, this);
26252
+
26253
+ if (this.getPolicy() === VariantLookupPolicy.NETWORK_ONLY) {
26254
+ return clearAndReturnNull();
26255
+ }
26256
+
26257
+ var ttlMs = this.getTtlMs();
26258
+
26259
+ return this.idb.init().then(_.bind(function() {
26260
+ return this.idb.getItem(this.persistedVariantsKey);
26261
+ }, this)).then(_.bind(function(data) {
26262
+ if (!data) {
26263
+ logger$2.log('No persisted variants found in IndexedDB');
26264
+ return null;
26265
+ }
26266
+
26267
+ if (ttlMs && Date.now() - data['persistedAt'] >= ttlMs) {
26268
+ logger$2.log('Persisted variants are expiring');
26269
+ return null;
26270
+ }
26271
+
26272
+ if (!context || data['distinctId'] !== context['distinct_id']) {
26273
+ logger$2.log('Persisted variants found, but for a different distinct_id so clearing.');
26274
+ return clearAndReturnNull();
26275
+ }
26276
+
26277
+ var persistedFlags = new Map();
26278
+ _.each(data['flagVariants'], function(variantData, key) {
26279
+ persistedFlags.set(key, {
26280
+ 'key': variantData['variant_key'],
26281
+ 'value': variantData['variant_value'],
26282
+ 'experiment_id': variantData['experiment_id'],
26283
+ 'is_experiment_active': variantData['is_experiment_active'],
26284
+ 'is_qa_tester': variantData['is_qa_tester'],
26285
+ 'variant_source': 'persistence',
26286
+ 'persisted_at_in_ms': data['persistedAt'],
26287
+ 'ttl_in_ms': ttlMs
26288
+ });
26289
+ });
26290
+
26291
+ logger$2.log('Loaded', persistedFlags.size, 'variants from IndexedDB for distinct_id', data['distinctId']);
26292
+
26293
+ return {
26294
+ flags: persistedFlags,
26295
+ pendingFirstTimeEvents: data['pendingFirstTimeEvents'] || {},
26296
+ persistedAtMs: data['persistedAt'],
26297
+ ttlMs: ttlMs
26298
+ };
26299
+ }, this)).catch(_.bind(function(error) {
26300
+ logger$2.error('Failed to load persisted variants from IndexedDB, so clearing', error);
26301
+ return clearAndReturnNull();
26302
+ }, this));
26303
+ };
26304
+
26305
+ FeatureFlagPersistence.prototype.save = function(context, flagsMap, pendingFirstTimeEvents) {
26306
+ if (this.getPolicy() === VariantLookupPolicy.NETWORK_ONLY) {
26307
+ return Promise.resolve();
26308
+ }
26309
+
26310
+ var flagVariants = {};
26311
+ flagsMap.forEach(function(variant, key) {
26312
+ flagVariants[key] = {
26313
+ 'variant_key': variant['key'],
26314
+ 'variant_value': variant['value'],
26315
+ 'experiment_id': variant['experiment_id'],
26316
+ 'is_experiment_active': variant['is_experiment_active'],
26317
+ 'is_qa_tester': variant['is_qa_tester']
26318
+ };
26319
+ });
26320
+
26321
+ var data = {
26322
+ 'persistedAt': Date.now(),
26323
+ 'distinctId': context && context['distinct_id'],
26324
+ 'context': context,
26325
+ 'flagVariants': flagVariants,
26326
+ 'pendingFirstTimeEvents': pendingFirstTimeEvents || {}
26327
+ };
26328
+
26329
+ return this.idb.init().then(_.bind(function() {
26330
+ return this.idb.setItem(this.persistedVariantsKey, data);
26331
+ }, this)).then(function() {
26332
+ logger$2.log('Saved', flagsMap.size, 'variants to IndexedDB for distinct_id', data['distinctId']);
26333
+ }).catch(function(error) {
26334
+ logger$2.error('Failed to persist variants to IndexedDB:', error);
26335
+ });
26336
+ };
26337
+
26338
+ FeatureFlagPersistence.prototype.clear = function() {
26339
+ if (this.isGloballyDisabled()) {
26340
+ return Promise.resolve();
26341
+ }
26342
+ return this.idb.init().then(_.bind(function() {
26343
+ return this.idb.removeItem(this.persistedVariantsKey);
26344
+ }, this)).then(function() {
26345
+ logger$2.log('Cleared persisted variants from IndexedDB');
26346
+ }).catch(function(error) {
26347
+ logger$2.error('Failed to clear persisted variants from IndexedDB:', error);
26348
+ });
26349
+ };
26350
+
26146
26351
  var logger$1 = console_with_prefix('flags');
26147
26352
  var FLAGS_CONFIG_KEY = 'flags';
26148
26353
 
26149
26354
  var CONFIG_CONTEXT = 'context';
26355
+ var CONFIG_PERSISTENCE = 'persistence';
26150
26356
  var CONFIG_DEFAULTS = {};
26151
26357
  CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
26152
26358
 
@@ -26169,6 +26375,13 @@ var getFlagKeyFromPendingEventKey = function(eventKey) {
26169
26375
  return eventKey.split(':')[0];
26170
26376
  };
26171
26377
 
26378
+ var withFallbackSource = function(fallback) {
26379
+ if (_.isObject(fallback)) {
26380
+ return _.extend({}, fallback, {'variant_source': 'fallback'});
26381
+ }
26382
+ return {'value': fallback, 'variant_source': 'fallback'};
26383
+ };
26384
+
26172
26385
  /**
26173
26386
  * FeatureFlagManager: support for Mixpanel's feature flagging product
26174
26387
  * @constructor
@@ -26191,13 +26404,63 @@ FeatureFlagManager.prototype.init = function() {
26191
26404
  }
26192
26405
 
26193
26406
  this.flags = null;
26194
- this.fetchFlags().catch(function() {
26195
- logger$1.error('Error fetching flags during init');
26196
- });
26197
-
26198
26407
  this.trackedFeatures = new Set();
26199
26408
  this.pendingFirstTimeEvents = {};
26200
26409
  this.activatedFirstTimeEvents = {};
26410
+ this._loadedPersistedAtMs = null;
26411
+ this._loadedTtlMs = null;
26412
+
26413
+ this.persistence = new FeatureFlagPersistence(
26414
+ this.getConfig(CONFIG_PERSISTENCE),
26415
+ this.getMpConfig('token'),
26416
+ _.bind(function() { return this.getMpConfig('disable_persistence'); }, this)
26417
+ );
26418
+
26419
+ this.persistenceLoadedPromise = this.persistence.loadFlagsFromStorage(this._buildContext())
26420
+ .then(_.bind(function(loaded) {
26421
+ if (loaded) {
26422
+ this.flags = loaded.flags;
26423
+ this.pendingFirstTimeEvents = loaded.pendingFirstTimeEvents;
26424
+ this._loadedPersistedAtMs = loaded.persistedAtMs;
26425
+ this._loadedTtlMs = loaded.ttlMs;
26426
+ }
26427
+ }, this));
26428
+
26429
+ return this.persistenceLoadedPromise
26430
+ .then(_.bind(function() {
26431
+ return this.fetchFlags();
26432
+ }, this))
26433
+ .catch(function() {
26434
+ logger$1.error('Error initializing feature flags');
26435
+ });
26436
+ };
26437
+
26438
+ FeatureFlagManager.prototype._buildContext = function() {
26439
+ return _.extend(
26440
+ {'distinct_id': this.getMpProperty('distinct_id'), 'device_id': this.getMpProperty('$device_id')},
26441
+ this.getConfig(CONFIG_CONTEXT)
26442
+ );
26443
+ };
26444
+
26445
+ FeatureFlagManager.prototype.reset = function() {
26446
+ if (!this.persistence) {
26447
+ return Promise.resolve();
26448
+ }
26449
+
26450
+ this.flags = null;
26451
+ this.pendingFirstTimeEvents = {};
26452
+ this.activatedFirstTimeEvents = {};
26453
+ this.trackedFeatures = new Set();
26454
+ this.fetchPromise = null;
26455
+ this._fetchInProgressStartTime = null;
26456
+ this._loadedPersistedAtMs = null;
26457
+ this._loadedTtlMs = null;
26458
+
26459
+ return this.persistence.clear().then(_.bind(function() {
26460
+ return this.fetchFlags();
26461
+ }, this)).catch(function() {
26462
+ logger$1.error('Error during flags reset');
26463
+ });
26201
26464
  };
26202
26465
 
26203
26466
  FeatureFlagManager.prototype.getFullConfig = function() {
@@ -26254,12 +26517,11 @@ FeatureFlagManager.prototype.fetchFlags = function() {
26254
26517
  return Promise.resolve();
26255
26518
  }
26256
26519
 
26257
- var distinctId = this.getMpProperty('distinct_id');
26258
- var deviceId = this.getMpProperty('$device_id');
26520
+ var context = this._buildContext();
26521
+ var distinctId = context['distinct_id'];
26259
26522
  var traceparent = generateTraceparent();
26260
26523
  logger$1.log('Fetching flags for distinct ID: ' + distinctId);
26261
26524
 
26262
- var context = _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT));
26263
26525
  var searchParams = new URLSearchParams();
26264
26526
  searchParams.set('context', JSON.stringify(context));
26265
26527
  searchParams.set('token', this.getMpConfig('token'));
@@ -26309,7 +26571,8 @@ FeatureFlagManager.prototype.fetchFlags = function() {
26309
26571
  'value': data['variant_value'],
26310
26572
  'experiment_id': data['experiment_id'],
26311
26573
  'is_experiment_active': data['is_experiment_active'],
26312
- 'is_qa_tester': data['is_qa_tester']
26574
+ 'is_qa_tester': data['is_qa_tester'],
26575
+ 'variant_source': 'network'
26313
26576
  });
26314
26577
  }
26315
26578
  }, this);
@@ -26351,10 +26614,15 @@ FeatureFlagManager.prototype.fetchFlags = function() {
26351
26614
  }
26352
26615
 
26353
26616
  this.flags = flags;
26617
+ this.trackedFeatures = new Set();
26354
26618
  this.pendingFirstTimeEvents = pendingFirstTimeEvents;
26619
+ this._loadedPersistedAtMs = null;
26620
+ this._loadedTtlMs = null;
26355
26621
  this._traceparent = traceparent;
26356
26622
 
26357
26623
  this._loadTargetingIfNeeded();
26624
+
26625
+ this.persistence.save(context, this.flags, this.pendingFirstTimeEvents);
26358
26626
  }.bind(this)).catch(function(error) {
26359
26627
  if (this._fetchInProgressStartTime) {
26360
26628
  this.markFetchComplete();
@@ -26514,6 +26782,7 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
26514
26782
  };
26515
26783
 
26516
26784
  this.flags.set(flagKey, newVariant);
26785
+ this.trackedFeatures.delete(flagKey);
26517
26786
  this.activatedFirstTimeEvents[eventKey] = true;
26518
26787
 
26519
26788
  this.recordFirstTimeEvent(
@@ -26525,8 +26794,8 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
26525
26794
  };
26526
26795
 
26527
26796
  FeatureFlagManager.prototype.getFirstTimeEventApiRoute = function(flagId) {
26528
- // Construct URL: {api_host}/flags/{flagId}/first-time-events
26529
- return this.getFullApiRoute() + '/' + flagId + '/first-time-events';
26797
+ var base = this.getFullApiRoute().replace(/\/$/, '');
26798
+ return base + '/' + flagId + '/first-time-events';
26530
26799
  };
26531
26800
 
26532
26801
  FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId, firstTimeEventHash) {
@@ -26563,35 +26832,106 @@ FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId,
26563
26832
  };
26564
26833
 
26565
26834
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
26566
- if (!this.fetchPromise) {
26835
+ if (!this.persistenceLoadedPromise) {
26567
26836
  return new Promise(function(resolve) {
26568
26837
  logger$1.critical('Feature Flags not initialized');
26569
- resolve(fallback);
26838
+ resolve(withFallbackSource(fallback));
26570
26839
  });
26571
26840
  }
26572
26841
 
26573
- return this.fetchPromise.then(function() {
26574
- return this.getVariantSync(featureName, fallback);
26575
- }.bind(this)).catch(function(error) {
26576
- logger$1.error(error);
26577
- return fallback;
26578
- });
26842
+ var policy = this.persistence.getPolicy();
26843
+
26844
+ return this.persistenceLoadedPromise.then(_.bind(function() {
26845
+ // Serve from persistence until the network completes a successful fetch. If a non-expired cached value is available, return it without waiting on the in-flight fetch.
26846
+ if (policy === VariantLookupPolicy.PERSISTENCE_UNTIL_NETWORK_SUCCESS) {
26847
+ if (this.areFlagsReady() && !this._loadedPersistenceIsStale()) {
26848
+ return this.getVariantSync(featureName, fallback);
26849
+ }
26850
+ if (!this.fetchPromise) {
26851
+ return withFallbackSource(fallback);
26852
+ }
26853
+ return this.fetchPromise.then(_.bind(function() {
26854
+ return this.getVariantSync(featureName, fallback);
26855
+ }, this)).catch(function(error) {
26856
+ logger$1.error(error);
26857
+ return withFallbackSource(fallback);
26858
+ });
26859
+ }
26860
+
26861
+ var serve = _.bind(function() { return this.getVariantSync(featureName, fallback); }, this);
26862
+ if (!this.fetchPromise) {
26863
+ return withFallbackSource(fallback);
26864
+ }
26865
+ return this.fetchPromise.then(serve).catch(serve);
26866
+ }, this));
26867
+ };
26868
+
26869
+ FeatureFlagManager.prototype._loadedPersistenceIsStale = function() {
26870
+ if (!this._loadedPersistedAtMs || !this._loadedTtlMs) {
26871
+ return false;
26872
+ }
26873
+ return Date.now() - this._loadedPersistedAtMs >= this._loadedTtlMs;
26579
26874
  };
26580
26875
 
26581
26876
  FeatureFlagManager.prototype.getVariantSync = function(featureName, fallback) {
26877
+ if (this._loadedPersistenceIsStale()) {
26878
+ logger$1.log('Loaded persisted variants are past TTL so returning fallback for "' + featureName + '"');
26879
+ return withFallbackSource(fallback);
26880
+ }
26582
26881
  if (!this.areFlagsReady()) {
26583
26882
  logger$1.log('Flags not loaded yet');
26584
- return fallback;
26883
+ return withFallbackSource(fallback);
26585
26884
  }
26586
26885
  var feature = this.flags.get(featureName);
26587
26886
  if (!feature) {
26588
26887
  logger$1.log('No flag found: "' + featureName + '"');
26589
- return fallback;
26888
+ return withFallbackSource(fallback);
26590
26889
  }
26591
26890
  this.trackFeatureCheck(featureName, feature);
26592
26891
  return feature;
26593
26892
  };
26594
26893
 
26894
+ FeatureFlagManager.prototype.getAllVariants = function() {
26895
+ if (!this.persistenceLoadedPromise) {
26896
+ logger$1.critical('Feature Flags not initialized');
26897
+ return Promise.resolve(new Map());
26898
+ }
26899
+
26900
+ var policy = this.persistence.getPolicy();
26901
+
26902
+ return this.persistenceLoadedPromise.then(_.bind(function() {
26903
+ // Serve from persistence until the network completes a successful fetch. If a non-expired cached value is available, return it without waiting on the in-flight fetch.
26904
+ if (policy === VariantLookupPolicy.PERSISTENCE_UNTIL_NETWORK_SUCCESS) {
26905
+ if (this.areFlagsReady() && !this._loadedPersistenceIsStale()) {
26906
+ return this.getAllVariantsSync();
26907
+ }
26908
+ if (!this.fetchPromise) {
26909
+ return new Map();
26910
+ }
26911
+ return this.fetchPromise.then(_.bind(function() {
26912
+ return this.getAllVariantsSync();
26913
+ }, this)).catch(function(error) {
26914
+ logger$1.error(error);
26915
+ return new Map();
26916
+ });
26917
+ }
26918
+
26919
+ var serve = _.bind(this.getAllVariantsSync, this);
26920
+ if (!this.fetchPromise) {
26921
+ return new Map();
26922
+ }
26923
+ return this.fetchPromise.then(serve).catch(serve);
26924
+ }, this));
26925
+ };
26926
+
26927
+ FeatureFlagManager.prototype.getAllVariantsSync = function() {
26928
+ if (this._loadedPersistenceIsStale()) {
26929
+ logger$1.log('Loaded persisted variants are past TTL so returning empty Map');
26930
+ return new Map();
26931
+ }
26932
+ return this.flags || new Map();
26933
+ };
26934
+
26595
26935
  FeatureFlagManager.prototype.getVariantValue = function(featureName, fallbackValue) {
26596
26936
  return this.getVariant(featureName, {'value': fallbackValue}).then(function(feature) {
26597
26937
  return feature['value'];
@@ -26630,6 +26970,10 @@ FeatureFlagManager.prototype.isEnabledSync = function(featureName, fallbackValue
26630
26970
  return val;
26631
26971
  };
26632
26972
 
26973
+ function isPresent(v) {
26974
+ return v !== undefined && v !== null;
26975
+ }
26976
+
26633
26977
  FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature) {
26634
26978
  if (this.trackedFeatures.has(featureName)) {
26635
26979
  return;
@@ -26640,21 +26984,30 @@ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature)
26640
26984
  'Experiment name': featureName,
26641
26985
  'Variant name': feature['key'],
26642
26986
  '$experiment_type': 'feature_flag',
26643
- 'Variant fetch start time': new Date(this._fetchStartTime).toISOString(),
26644
- 'Variant fetch complete time': new Date(this._fetchCompleteTime).toISOString(),
26987
+ 'Variant fetch start time': isPresent(this._fetchStartTime) ? new Date(this._fetchStartTime).toISOString() : null,
26988
+ 'Variant fetch complete time': isPresent(this._fetchCompleteTime) ? new Date(this._fetchCompleteTime).toISOString() : null,
26645
26989
  'Variant fetch latency (ms)': this._fetchLatency,
26646
26990
  'Variant fetch traceparent': this._traceparent,
26647
26991
  };
26648
26992
 
26649
- if (feature['experiment_id'] !== 'undefined') {
26993
+ if (isPresent(feature['experiment_id'])) {
26650
26994
  trackingProperties['$experiment_id'] = feature['experiment_id'];
26651
26995
  }
26652
- if (feature['is_experiment_active'] !== 'undefined') {
26996
+ if (isPresent(feature['is_experiment_active'])) {
26653
26997
  trackingProperties['$is_experiment_active'] = feature['is_experiment_active'];
26654
26998
  }
26655
- if (feature['is_qa_tester'] !== 'undefined') {
26999
+ if (isPresent(feature['is_qa_tester'])) {
26656
27000
  trackingProperties['$is_qa_tester'] = feature['is_qa_tester'];
26657
27001
  }
27002
+ if (isPresent(feature['variant_source'])) {
27003
+ trackingProperties['$variant_source'] = feature['variant_source'];
27004
+ }
27005
+ if (isPresent(feature['persisted_at_in_ms'])) {
27006
+ trackingProperties['$persisted_at_in_ms'] = feature['persisted_at_in_ms'];
27007
+ }
27008
+ if (isPresent(feature['ttl_in_ms'])) {
27009
+ trackingProperties['$ttl_in_ms'] = feature['ttl_in_ms'];
27010
+ }
26658
27011
 
26659
27012
  this.track('$experiment_started', trackingProperties);
26660
27013
  };
@@ -26678,6 +27031,8 @@ safewrapClass(FeatureFlagManager);
26678
27031
  FeatureFlagManager.prototype['are_flags_ready'] = FeatureFlagManager.prototype.areFlagsReady;
26679
27032
  FeatureFlagManager.prototype['get_variant'] = FeatureFlagManager.prototype.getVariant;
26680
27033
  FeatureFlagManager.prototype['get_variant_sync'] = FeatureFlagManager.prototype.getVariantSync;
27034
+ FeatureFlagManager.prototype['get_all_variants'] = FeatureFlagManager.prototype.getAllVariants;
27035
+ FeatureFlagManager.prototype['get_all_variants_sync'] = FeatureFlagManager.prototype.getAllVariantsSync;
26681
27036
  FeatureFlagManager.prototype['get_variant_value'] = FeatureFlagManager.prototype.getVariantValue;
26682
27037
  FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
26683
27038
  FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
@@ -26730,7 +27085,7 @@ RecorderManager.prototype.shouldLoadRecorder = function() {
26730
27085
  return PromisePolyfill.resolve(false);
26731
27086
  }
26732
27087
 
26733
- var recording_registry_idb = new IDBStorageWrapper(RECORDING_REGISTRY_STORE_NAME);
27088
+ var recording_registry_idb = new IDBStorageWrapper(MIXPANEL_BROWSER_DB_NAME, RECORDING_REGISTRY_STORE_NAME, RECORDER_VERSION_DATA);
26734
27089
  var tab_id = this.getTabId();
26735
27090
  return recording_registry_idb.init()
26736
27091
  .then(function () {
@@ -28657,6 +29012,7 @@ MixpanelLib.prototype._init = function(token, config, name) {
28657
29012
  'disable_all_events': false,
28658
29013
  'identify_called': false
28659
29014
  };
29015
+ this._remote_settings_strict_disabled = false;
28660
29016
 
28661
29017
  // set up request queueing/batching
28662
29018
  this.request_batchers = {};
@@ -28731,9 +29087,6 @@ MixpanelLib.prototype._init = function(token, config, name) {
28731
29087
  this.flags.init();
28732
29088
  this['flags'] = this.flags;
28733
29089
 
28734
- this.autocapture = new Autocapture(this);
28735
- this.autocapture.init();
28736
-
28737
29090
  this._init_tab_id();
28738
29091
 
28739
29092
  // Based on remote_settings_mode, fetch remote settings and then start session recording if applicable
@@ -28745,6 +29098,9 @@ MixpanelLib.prototype._init = function(token, config, name) {
28745
29098
  } else {
28746
29099
  this.__session_recording_init_promise = this._check_and_start_session_recording();
28747
29100
  }
29101
+
29102
+ this.autocapture = new Autocapture(this);
29103
+ this.autocapture.init();
28748
29104
  };
28749
29105
 
28750
29106
  /**
@@ -28791,9 +29147,19 @@ MixpanelLib.prototype._check_and_start_session_recording = addOptOutCheckMixpane
28791
29147
  return this.recorderManager.checkAndStartSessionRecording(force_start);
28792
29148
  });
28793
29149
 
28794
- MixpanelLib.prototype._start_recording_on_event = function(event_name, properties) {
28795
- return this.recorderManager.startRecordingOnEvent(event_name, properties);
28796
- };
29150
+ MixpanelLib.prototype._start_recording_on_event = safewrap(function(event_name, properties) {
29151
+ // Wait for recording init to complete before evaluating event triggers.
29152
+ // This ensures recording_event_triggers config is fully loaded when remote settings are used.
29153
+ if (this.__session_recording_init_promise) {
29154
+ this.__session_recording_init_promise.then(_.bind(function() {
29155
+ // In strict mode, skip recording if remote settings failed
29156
+ if (this._remote_settings_strict_disabled) {
29157
+ return;
29158
+ }
29159
+ return this.recorderManager.startRecordingOnEvent(event_name, properties);
29160
+ }, this));
29161
+ }
29162
+ });
28797
29163
 
28798
29164
  MixpanelLib.prototype.start_session_recording = function () {
28799
29165
  return this._check_and_start_session_recording(true);
@@ -29092,6 +29458,7 @@ MixpanelLib.prototype._fetch_remote_settings = function(mode) {
29092
29458
  var disableRecordingIfStrict = function() {
29093
29459
  if (mode === 'strict') {
29094
29460
  self.set_config({'record_sessions_percent': 0});
29461
+ self._remote_settings_strict_disabled = true;
29095
29462
  }
29096
29463
  };
29097
29464
 
@@ -29717,6 +30084,10 @@ MixpanelLib.prototype.track_pageview = addOptOutCheckMixpanelLib(function(proper
29717
30084
  properties
29718
30085
  );
29719
30086
 
30087
+ if (this.is_recording_heatmap_data()) {
30088
+ event_properties['$captured_for_heatmap'] = true;
30089
+ }
30090
+
29720
30091
  return this.track(event_name, event_properties);
29721
30092
  });
29722
30093
 
@@ -30060,6 +30431,7 @@ MixpanelLib.prototype.reset = function() {
30060
30431
  '$device_id': uuid
30061
30432
  }, '');
30062
30433
  this._check_and_start_session_recording();
30434
+ this.flags.reset();
30063
30435
  };
30064
30436
 
30065
30437
  /**