mixpanel-browser 2.77.0 → 2.79.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.
- package/.claude/settings.local.json +6 -9
- package/.eslintrc.json +12 -0
- package/.github/workflows/openfeature-provider-tests.yml +31 -0
- package/CHANGELOG.md +11 -0
- package/build.sh +2 -2
- package/dist/async-modules/{mixpanel-recorder-wIWnMDLA.min.js → mixpanel-recorder-D5HJyV2E.min.js} +2 -2
- package/dist/async-modules/mixpanel-recorder-D5HJyV2E.min.js.map +1 -0
- package/dist/async-modules/{mixpanel-recorder-DLKbUIEE.js → mixpanel-recorder-P6SEnnPV.js} +57 -33
- package/dist/async-modules/mixpanel-targeting-1L9FyetZ.min.js +2 -0
- package/dist/async-modules/mixpanel-targeting-1L9FyetZ.min.js.map +1 -0
- package/dist/async-modules/{mixpanel-targeting-CmVvUyFM.js → mixpanel-targeting-BBMVbgJF.js} +24 -13
- package/dist/mixpanel-core.cjs.d.ts +46 -1
- package/dist/mixpanel-core.cjs.js +671 -272
- package/dist/mixpanel-recorder.js +57 -33
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-targeting.js +24 -13
- package/dist/mixpanel-targeting.min.js +1 -1
- package/dist/mixpanel-targeting.min.js.map +1 -1
- package/dist/mixpanel-with-async-modules.cjs.d.ts +46 -1
- package/dist/mixpanel-with-async-modules.cjs.js +673 -274
- package/dist/mixpanel-with-async-recorder.cjs.d.ts +46 -1
- package/dist/mixpanel-with-async-recorder.cjs.js +673 -274
- package/dist/mixpanel-with-recorder.d.ts +46 -1
- package/dist/mixpanel-with-recorder.js +596 -197
- package/dist/mixpanel-with-recorder.min.d.ts +46 -1
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.d.ts +46 -1
- package/dist/mixpanel.amd.js +596 -197
- package/dist/mixpanel.cjs.d.ts +46 -1
- package/dist/mixpanel.cjs.js +596 -197
- package/dist/mixpanel.globals.js +673 -274
- package/dist/mixpanel.min.js +200 -189
- package/dist/mixpanel.module.d.ts +46 -1
- package/dist/mixpanel.module.js +596 -197
- package/dist/mixpanel.umd.d.ts +46 -1
- package/dist/mixpanel.umd.js +596 -197
- package/package.json +1 -1
- package/packages/openfeature-web-provider/README.md +357 -0
- package/packages/openfeature-web-provider/package-lock.json +1636 -0
- package/packages/openfeature-web-provider/package.json +51 -0
- package/packages/openfeature-web-provider/rollup.config.browser.mjs +26 -0
- package/packages/openfeature-web-provider/src/MixpanelProvider.ts +302 -0
- package/packages/openfeature-web-provider/src/index.ts +1 -0
- package/packages/openfeature-web-provider/src/types.ts +72 -0
- package/packages/openfeature-web-provider/test/MixpanelProvider.spec.ts +484 -0
- package/packages/openfeature-web-provider/tsconfig.json +15 -0
- package/src/autocapture/index.js +7 -2
- package/src/config.js +1 -1
- package/src/flags/CLAUDE.md +24 -0
- package/src/flags/flags-persistence.js +176 -0
- package/src/flags/index.js +278 -98
- package/src/index.d.ts +46 -1
- package/src/mixpanel-core.js +27 -8
- package/src/recorder/idb-config.js +16 -0
- package/src/recorder/recording-registry.js +7 -2
- package/src/recorder/session-recording.js +9 -4
- package/src/recorder-manager.js +7 -2
- package/src/request-queue.js +1 -2
- package/src/shared-lock.js +2 -3
- package/src/storage/indexed-db.js +16 -15
- package/src/storage/local-storage.js +5 -3
- package/src/utils.js +25 -12
- package/testServer.js +2 -0
- package/tsconfig.base.json +9 -0
- package/dist/async-modules/mixpanel-recorder-wIWnMDLA.min.js.map +0 -1
- package/dist/async-modules/mixpanel-targeting-CTcftSJC.min.js +0 -2
- package/dist/async-modules/mixpanel-targeting-CTcftSJC.min.js.map +0 -1
package/dist/mixpanel.module.js
CHANGED
|
@@ -25,7 +25,7 @@ if (typeof(window) === 'undefined') {
|
|
|
25
25
|
|
|
26
26
|
var Config = {
|
|
27
27
|
DEBUG: false,
|
|
28
|
-
LIB_VERSION: '2.
|
|
28
|
+
LIB_VERSION: '2.79.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
|
-
|
|
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
|
-
|
|
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
|
|
20131
|
-
//
|
|
20132
|
-
var
|
|
20133
|
-
try {
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
}
|
|
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
|
+
};
|
|
20138
20142
|
|
|
20139
|
-
|
|
20140
|
-
|
|
20143
|
+
var getSessionStorage = function() {
|
|
20144
|
+
try {
|
|
20145
|
+
return win.sessionStorage; // eslint-disable-line no-restricted-properties
|
|
20146
|
+
} catch (_err) {
|
|
20147
|
+
return null;
|
|
20148
|
+
}
|
|
20149
|
+
};
|
|
20150
|
+
|
|
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(
|
|
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
|
-
|
|
20844
|
-
db.
|
|
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$
|
|
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 ||
|
|
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$
|
|
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 ||
|
|
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$
|
|
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
|
|
21484
|
+
storage: options.sharedLockStorage,
|
|
21461
21485
|
timeoutMS: options.sharedLockTimeoutMS,
|
|
21462
21486
|
});
|
|
21463
21487
|
}
|
|
21464
|
-
this.reportError = options.errorReporter || _.bind(logger$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
23803
|
+
var logger$4 = console_with_prefix('recorder');
|
|
23780
23804
|
var CompressionStream = win['CompressionStream'];
|
|
23781
23805
|
|
|
23782
23806
|
var RECORDER_BATCHER_LIB_CONFIG = {
|
|
@@ -23873,11 +23897,11 @@ var SessionRecording = function(options) {
|
|
|
23873
23897
|
|
|
23874
23898
|
// disable persistence if localStorage is not supported
|
|
23875
23899
|
// request-queue will automatically disable persistence if indexedDB fails to initialize
|
|
23876
|
-
var usePersistence = localStorageSupported(options.sharedLockStorage, true) && !this.getConfig('disable_persistence');
|
|
23900
|
+
var usePersistence = localStorageSupported(options.sharedLockStorage || getLocalStorage(), true) && !this.getConfig('disable_persistence');
|
|
23877
23901
|
|
|
23878
23902
|
// each replay has its own batcher key to avoid conflicts between rrweb events of different recordings
|
|
23879
23903
|
this.batcherKey = '__mprec_' + this.getConfig('name') + '_' + this.getConfig('token') + '_' + this.replayId;
|
|
23880
|
-
this.queueStorage = new IDBStorageWrapper(RECORDING_EVENTS_STORE_NAME);
|
|
23904
|
+
this.queueStorage = new IDBStorageWrapper(MIXPANEL_BROWSER_DB_NAME, RECORDING_EVENTS_STORE_NAME, RECORDER_VERSION_DATA);
|
|
23881
23905
|
this.batcher = new RequestBatcher(this.batcherKey, {
|
|
23882
23906
|
errorReporter: this.reportError.bind(this),
|
|
23883
23907
|
flushOnlyOnInterval: true,
|
|
@@ -23956,14 +23980,14 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
|
|
|
23956
23980
|
}
|
|
23957
23981
|
|
|
23958
23982
|
if (this._stopRecording !== null) {
|
|
23959
|
-
logger$
|
|
23983
|
+
logger$4.log('Recording already in progress, skipping startRecording.');
|
|
23960
23984
|
return;
|
|
23961
23985
|
}
|
|
23962
23986
|
|
|
23963
23987
|
this.recordMaxMs = this.getConfig('record_max_ms');
|
|
23964
23988
|
if (this.recordMaxMs > MAX_RECORDING_MS) {
|
|
23965
23989
|
this.recordMaxMs = MAX_RECORDING_MS;
|
|
23966
|
-
logger$
|
|
23990
|
+
logger$4.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
|
|
23967
23991
|
}
|
|
23968
23992
|
|
|
23969
23993
|
if (!this.maxExpires) {
|
|
@@ -24027,7 +24051,7 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
|
|
|
24027
24051
|
);
|
|
24028
24052
|
}
|
|
24029
24053
|
|
|
24030
|
-
var validatedOrigins = validateAllowedOrigins(this.getConfig('record_allowed_iframe_origins'), logger$
|
|
24054
|
+
var validatedOrigins = validateAllowedOrigins(this.getConfig('record_allowed_iframe_origins'), logger$4);
|
|
24031
24055
|
|
|
24032
24056
|
try {
|
|
24033
24057
|
this._stopRecording = this._rrwebRecord({
|
|
@@ -24289,14 +24313,14 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
|
|
|
24289
24313
|
|
|
24290
24314
|
|
|
24291
24315
|
SessionRecording.prototype.reportError = function(msg, err) {
|
|
24292
|
-
logger$
|
|
24316
|
+
logger$4.error.apply(logger$4.error, arguments);
|
|
24293
24317
|
try {
|
|
24294
24318
|
if (!err && !(msg instanceof Error)) {
|
|
24295
24319
|
msg = new Error(msg);
|
|
24296
24320
|
}
|
|
24297
24321
|
this.getConfig('error_reporter')(msg, err);
|
|
24298
24322
|
} catch(err) {
|
|
24299
|
-
logger$
|
|
24323
|
+
logger$4.error(err);
|
|
24300
24324
|
}
|
|
24301
24325
|
};
|
|
24302
24326
|
|
|
@@ -24325,7 +24349,7 @@ SessionRecording.prototype._getRecordMinMs = function() {
|
|
|
24325
24349
|
var configValue = this.getConfig('record_min_ms');
|
|
24326
24350
|
|
|
24327
24351
|
if (configValue > MAX_VALUE_FOR_MIN_RECORDING_MS) {
|
|
24328
|
-
logger$
|
|
24352
|
+
logger$4.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
|
|
24329
24353
|
return MAX_VALUE_FOR_MIN_RECORDING_MS;
|
|
24330
24354
|
}
|
|
24331
24355
|
|
|
@@ -24367,7 +24391,7 @@ SessionRecording.prototype._getMaskFn = function(shouldMaskFn, privacyConfig) {
|
|
|
24367
24391
|
*/
|
|
24368
24392
|
var RecordingRegistry = function (options) {
|
|
24369
24393
|
/** @type {IDBStorageWrapper} */
|
|
24370
|
-
this.idb = new IDBStorageWrapper(RECORDING_REGISTRY_STORE_NAME);
|
|
24394
|
+
this.idb = new IDBStorageWrapper(MIXPANEL_BROWSER_DB_NAME, RECORDING_REGISTRY_STORE_NAME, RECORDER_VERSION_DATA);
|
|
24371
24395
|
this.errorReporter = options.errorReporter;
|
|
24372
24396
|
this.mixpanelInstance = options.mixpanelInstance;
|
|
24373
24397
|
this.sharedLockStorage = options.sharedLockStorage;
|
|
@@ -24488,7 +24512,7 @@ RecordingRegistry.prototype.flushInactiveRecordings = function () {
|
|
|
24488
24512
|
.catch(this.handleError.bind(this));
|
|
24489
24513
|
};
|
|
24490
24514
|
|
|
24491
|
-
var logger$
|
|
24515
|
+
var logger$3 = console_with_prefix('recorder');
|
|
24492
24516
|
|
|
24493
24517
|
/**
|
|
24494
24518
|
* Recorder API: bundles rrweb and and exposes methods to start and stop recordings.
|
|
@@ -24504,7 +24528,7 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
|
|
|
24504
24528
|
*/
|
|
24505
24529
|
this.recordingRegistry = new RecordingRegistry({
|
|
24506
24530
|
mixpanelInstance: this.mixpanelInstance,
|
|
24507
|
-
errorReporter: logger$
|
|
24531
|
+
errorReporter: logger$3.error,
|
|
24508
24532
|
sharedLockStorage: sharedLockStorage
|
|
24509
24533
|
});
|
|
24510
24534
|
this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
|
|
@@ -24516,17 +24540,17 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
|
|
|
24516
24540
|
MixpanelRecorder.prototype.startRecording = function(options) {
|
|
24517
24541
|
options = options || {};
|
|
24518
24542
|
if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
|
|
24519
|
-
logger$
|
|
24543
|
+
logger$3.log('Recording already in progress, skipping startRecording.');
|
|
24520
24544
|
return;
|
|
24521
24545
|
}
|
|
24522
24546
|
|
|
24523
24547
|
var onIdleTimeout = function () {
|
|
24524
|
-
logger$
|
|
24548
|
+
logger$3.log('Idle timeout reached, restarting recording.');
|
|
24525
24549
|
this.resetRecording();
|
|
24526
24550
|
}.bind(this);
|
|
24527
24551
|
|
|
24528
24552
|
var onMaxLengthReached = function () {
|
|
24529
|
-
logger$
|
|
24553
|
+
logger$3.log('Max recording length reached, stopping recording.');
|
|
24530
24554
|
this.resetRecording();
|
|
24531
24555
|
}.bind(this);
|
|
24532
24556
|
|
|
@@ -24596,7 +24620,7 @@ MixpanelRecorder.prototype.resumeRecording = function (startNewIfInactive) {
|
|
|
24596
24620
|
} else if (startNewIfInactive) {
|
|
24597
24621
|
return this.startRecording({shouldStopBatcher: false});
|
|
24598
24622
|
} else {
|
|
24599
|
-
logger$
|
|
24623
|
+
logger$3.log('No resumable recording found.');
|
|
24600
24624
|
return null;
|
|
24601
24625
|
}
|
|
24602
24626
|
}.bind(this));
|
|
@@ -25261,7 +25285,7 @@ ShadowDOMObserver.prototype.observeShadowRoot = function(shadowRoot) {
|
|
|
25261
25285
|
observer.observe(shadowRoot, this.observerConfig);
|
|
25262
25286
|
this.shadowObservers.push(observer);
|
|
25263
25287
|
} catch (e) {
|
|
25264
|
-
logger$
|
|
25288
|
+
logger$6.critical('Error while observing shadow root', e);
|
|
25265
25289
|
}
|
|
25266
25290
|
};
|
|
25267
25291
|
|
|
@@ -25272,7 +25296,7 @@ ShadowDOMObserver.prototype.start = function() {
|
|
|
25272
25296
|
}
|
|
25273
25297
|
|
|
25274
25298
|
if (!weakSetSupported()) {
|
|
25275
|
-
logger$
|
|
25299
|
+
logger$6.critical('Shadow DOM observation unavailable: WeakSet not supported');
|
|
25276
25300
|
return;
|
|
25277
25301
|
}
|
|
25278
25302
|
|
|
@@ -25288,7 +25312,7 @@ ShadowDOMObserver.prototype.stop = function() {
|
|
|
25288
25312
|
try {
|
|
25289
25313
|
this.shadowObservers[i].disconnect();
|
|
25290
25314
|
} catch (e) {
|
|
25291
|
-
logger$
|
|
25315
|
+
logger$6.critical('Error while disconnecting shadow DOM observer', e);
|
|
25292
25316
|
}
|
|
25293
25317
|
}
|
|
25294
25318
|
this.shadowObservers = [];
|
|
@@ -25476,7 +25500,7 @@ DeadClickTracker.prototype.startTracking = function() {
|
|
|
25476
25500
|
|
|
25477
25501
|
this.mutationObserver.observe(document.body || document.documentElement, MUTATION_OBSERVER_CONFIG);
|
|
25478
25502
|
} catch (e) {
|
|
25479
|
-
logger$
|
|
25503
|
+
logger$6.critical('Error while setting up mutation observer', e);
|
|
25480
25504
|
}
|
|
25481
25505
|
}
|
|
25482
25506
|
|
|
@@ -25491,7 +25515,7 @@ DeadClickTracker.prototype.startTracking = function() {
|
|
|
25491
25515
|
);
|
|
25492
25516
|
this.shadowDOMObserver.start();
|
|
25493
25517
|
} catch (e) {
|
|
25494
|
-
logger$
|
|
25518
|
+
logger$6.critical('Error while setting up shadow DOM observer', e);
|
|
25495
25519
|
this.shadowDOMObserver = null;
|
|
25496
25520
|
}
|
|
25497
25521
|
}
|
|
@@ -25518,7 +25542,7 @@ DeadClickTracker.prototype.stopTracking = function() {
|
|
|
25518
25542
|
try {
|
|
25519
25543
|
listener.target.removeEventListener(listener.event, listener.handler, listener.options);
|
|
25520
25544
|
} catch (e) {
|
|
25521
|
-
logger$
|
|
25545
|
+
logger$6.critical('Error while removing event listener', e);
|
|
25522
25546
|
}
|
|
25523
25547
|
}
|
|
25524
25548
|
this.eventListeners = [];
|
|
@@ -25527,7 +25551,7 @@ DeadClickTracker.prototype.stopTracking = function() {
|
|
|
25527
25551
|
try {
|
|
25528
25552
|
this.mutationObserver.disconnect();
|
|
25529
25553
|
} catch (e) {
|
|
25530
|
-
logger$
|
|
25554
|
+
logger$6.critical('Error while disconnecting mutation observer', e);
|
|
25531
25555
|
}
|
|
25532
25556
|
this.mutationObserver = null;
|
|
25533
25557
|
}
|
|
@@ -25536,7 +25560,7 @@ DeadClickTracker.prototype.stopTracking = function() {
|
|
|
25536
25560
|
try {
|
|
25537
25561
|
this.shadowDOMObserver.stop();
|
|
25538
25562
|
} catch (e) {
|
|
25539
|
-
logger$
|
|
25563
|
+
logger$6.critical('Error while stopping shadow DOM observer', e);
|
|
25540
25564
|
}
|
|
25541
25565
|
this.shadowDOMObserver = null;
|
|
25542
25566
|
}
|
|
@@ -25614,7 +25638,7 @@ var Autocapture = function(mp) {
|
|
|
25614
25638
|
|
|
25615
25639
|
Autocapture.prototype.init = function() {
|
|
25616
25640
|
if (!minDOMApisSupported()) {
|
|
25617
|
-
logger$
|
|
25641
|
+
logger$6.critical('Autocapture unavailable: missing required DOM APIs');
|
|
25618
25642
|
return;
|
|
25619
25643
|
}
|
|
25620
25644
|
this.initPageListeners();
|
|
@@ -25654,7 +25678,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
|
|
|
25654
25678
|
try {
|
|
25655
25679
|
return !urlMatchesRegexList(currentUrl, allowUrlRegexes);
|
|
25656
25680
|
} catch (err) {
|
|
25657
|
-
logger$
|
|
25681
|
+
logger$6.critical('Error while checking block URL regexes: ', err);
|
|
25658
25682
|
return true;
|
|
25659
25683
|
}
|
|
25660
25684
|
}
|
|
@@ -25667,7 +25691,7 @@ Autocapture.prototype.currentUrlBlocked = function() {
|
|
|
25667
25691
|
try {
|
|
25668
25692
|
return urlMatchesRegexList(currentUrl, blockUrlRegexes);
|
|
25669
25693
|
} catch (err) {
|
|
25670
|
-
logger$
|
|
25694
|
+
logger$6.critical('Error while checking block URL regexes: ', err);
|
|
25671
25695
|
return true;
|
|
25672
25696
|
}
|
|
25673
25697
|
};
|
|
@@ -25805,7 +25829,7 @@ Autocapture.prototype._initScrollDepthTracking = function() {
|
|
|
25805
25829
|
return;
|
|
25806
25830
|
}
|
|
25807
25831
|
|
|
25808
|
-
logger$
|
|
25832
|
+
logger$6.log('Initializing scroll depth tracking');
|
|
25809
25833
|
|
|
25810
25834
|
this.maxScrollViewDepth = Math.max(document$1.documentElement.clientHeight, win.innerHeight || 0);
|
|
25811
25835
|
|
|
@@ -25831,7 +25855,7 @@ Autocapture.prototype.initClickTracking = function() {
|
|
|
25831
25855
|
if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.get_config('record_heatmap_data')) {
|
|
25832
25856
|
return;
|
|
25833
25857
|
}
|
|
25834
|
-
logger$
|
|
25858
|
+
logger$6.log('Initializing click tracking');
|
|
25835
25859
|
|
|
25836
25860
|
this.listenerClick = function(ev) {
|
|
25837
25861
|
if (!this.getConfig(CONFIG_TRACK_CLICK) && !this.mp.is_recording_heatmap_data()) {
|
|
@@ -25850,7 +25874,7 @@ Autocapture.prototype.initDeadClickTracking = function() {
|
|
|
25850
25874
|
return;
|
|
25851
25875
|
}
|
|
25852
25876
|
|
|
25853
|
-
logger$
|
|
25877
|
+
logger$6.log('Initializing dead click tracking');
|
|
25854
25878
|
if (!this._deadClickTracker) {
|
|
25855
25879
|
this._deadClickTracker = new DeadClickTracker(function(deadClickEvent) {
|
|
25856
25880
|
this.trackDomEvent(deadClickEvent, MP_EV_DEAD_CLICK);
|
|
@@ -25884,7 +25908,7 @@ Autocapture.prototype.initInputTracking = function() {
|
|
|
25884
25908
|
if (!this.getConfig(CONFIG_TRACK_INPUT)) {
|
|
25885
25909
|
return;
|
|
25886
25910
|
}
|
|
25887
|
-
logger$
|
|
25911
|
+
logger$6.log('Initializing input tracking');
|
|
25888
25912
|
|
|
25889
25913
|
this.listenerChange = function(ev) {
|
|
25890
25914
|
if (!this.getConfig(CONFIG_TRACK_INPUT)) {
|
|
@@ -25898,14 +25922,15 @@ Autocapture.prototype.initInputTracking = function() {
|
|
|
25898
25922
|
Autocapture.prototype.initPageviewTracking = function() {
|
|
25899
25923
|
win.removeEventListener(EV_MP_LOCATION_CHANGE, this.listenerLocationchange);
|
|
25900
25924
|
|
|
25901
|
-
if (!this.pageviewTrackingConfig()) {
|
|
25925
|
+
if (!this.pageviewTrackingConfig() && !this.mp.get_config('record_heatmap_data')) {
|
|
25902
25926
|
return;
|
|
25903
25927
|
}
|
|
25904
|
-
logger$
|
|
25928
|
+
logger$6.log('Initializing pageview tracking');
|
|
25905
25929
|
|
|
25906
25930
|
var previousTrackedUrl = '';
|
|
25907
25931
|
var tracked = false;
|
|
25908
|
-
if
|
|
25932
|
+
// Track initial pageview if pageview tracking enabled OR heatmap recording is active
|
|
25933
|
+
if ((this.pageviewTrackingConfig() || this.mp.is_recording_heatmap_data()) && !this.currentUrlBlocked()) {
|
|
25909
25934
|
tracked = this.mp.track_pageview(DEFAULT_PROPS);
|
|
25910
25935
|
}
|
|
25911
25936
|
if (tracked) {
|
|
@@ -25921,6 +25946,10 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
25921
25946
|
var shouldTrack = false;
|
|
25922
25947
|
var didPathChange = currentUrl.split('#')[0].split('?')[0] !== previousTrackedUrl.split('#')[0].split('?')[0];
|
|
25923
25948
|
var trackPageviewOption = this.pageviewTrackingConfig();
|
|
25949
|
+
if (!trackPageviewOption && this.mp.is_recording_heatmap_data()) {
|
|
25950
|
+
trackPageviewOption = PAGEVIEW_OPTION_FULL_URL;
|
|
25951
|
+
}
|
|
25952
|
+
|
|
25924
25953
|
if (trackPageviewOption === PAGEVIEW_OPTION_FULL_URL) {
|
|
25925
25954
|
shouldTrack = currentUrl !== previousTrackedUrl;
|
|
25926
25955
|
} else if (trackPageviewOption === PAGEVIEW_OPTION_URL_WITH_PATH_AND_QUERY_STRING) {
|
|
@@ -25936,7 +25965,7 @@ Autocapture.prototype.initPageviewTracking = function() {
|
|
|
25936
25965
|
}
|
|
25937
25966
|
if (didPathChange) {
|
|
25938
25967
|
this.lastScrollCheckpoint = 0;
|
|
25939
|
-
logger$
|
|
25968
|
+
logger$6.log('Path change: re-initializing scroll depth checkpoints');
|
|
25940
25969
|
}
|
|
25941
25970
|
}
|
|
25942
25971
|
}.bind(this));
|
|
@@ -25951,7 +25980,7 @@ Autocapture.prototype.initRageClickTracking = function() {
|
|
|
25951
25980
|
return;
|
|
25952
25981
|
}
|
|
25953
25982
|
|
|
25954
|
-
logger$
|
|
25983
|
+
logger$6.log('Initializing rage click tracking');
|
|
25955
25984
|
if (!this._rageClickTracker) {
|
|
25956
25985
|
this._rageClickTracker = new RageClickTracker();
|
|
25957
25986
|
}
|
|
@@ -25981,7 +26010,7 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
25981
26010
|
if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
|
|
25982
26011
|
return;
|
|
25983
26012
|
}
|
|
25984
|
-
logger$
|
|
26013
|
+
logger$6.log('Initializing scroll tracking');
|
|
25985
26014
|
this.lastScrollCheckpoint = 0;
|
|
25986
26015
|
|
|
25987
26016
|
var scrollTrackFunction = function() {
|
|
@@ -26018,7 +26047,7 @@ Autocapture.prototype.initScrollTracking = function() {
|
|
|
26018
26047
|
}
|
|
26019
26048
|
}
|
|
26020
26049
|
} catch (err) {
|
|
26021
|
-
logger$
|
|
26050
|
+
logger$6.critical('Error while calculating scroll percentage', err);
|
|
26022
26051
|
}
|
|
26023
26052
|
if (shouldTrack) {
|
|
26024
26053
|
this.mp.track(MP_EV_SCROLL, props);
|
|
@@ -26036,7 +26065,7 @@ Autocapture.prototype.initSubmitTracking = function() {
|
|
|
26036
26065
|
if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
|
|
26037
26066
|
return;
|
|
26038
26067
|
}
|
|
26039
|
-
logger$
|
|
26068
|
+
logger$6.log('Initializing submit tracking');
|
|
26040
26069
|
|
|
26041
26070
|
this.listenerSubmit = function(ev) {
|
|
26042
26071
|
if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
|
|
@@ -26058,7 +26087,7 @@ Autocapture.prototype.initPageLeaveTracking = function() {
|
|
|
26058
26087
|
return;
|
|
26059
26088
|
}
|
|
26060
26089
|
|
|
26061
|
-
logger$
|
|
26090
|
+
logger$6.log('Initializing page visibility tracking.');
|
|
26062
26091
|
this._initScrollDepthTracking();
|
|
26063
26092
|
var previousTrackedUrl = _.info.currentUrl();
|
|
26064
26093
|
|
|
@@ -26143,10 +26172,183 @@ var getTargetingPromise = function(loadExtraBundle, targetingSrc) {
|
|
|
26143
26172
|
return win[TARGETING_GLOBAL_NAME];
|
|
26144
26173
|
};
|
|
26145
26174
|
|
|
26175
|
+
var logger$2 = console_with_prefix('flags');
|
|
26176
|
+
|
|
26177
|
+
var MIXPANEL_FLAGS_DB_NAME = 'mixpanelFlagsDb';
|
|
26178
|
+
var FLAGS_STORE_NAME = 'mixpanelFlags';
|
|
26179
|
+
|
|
26180
|
+
// Keeping these two properties closeby, as adding additional stores to a DB in IndexedDB requires a version increment
|
|
26181
|
+
var FLAGS_VERSION_DATA = { version: 1, storeNames: [FLAGS_STORE_NAME] };
|
|
26182
|
+
|
|
26183
|
+
var PERSISTED_VARIANTS_KEY_PREFIX = 'persisted_variants_for_';
|
|
26184
|
+
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
26185
|
+
|
|
26186
|
+
var VariantLookupPolicy = Object.freeze({
|
|
26187
|
+
NETWORK_ONLY: 'networkOnly',
|
|
26188
|
+
NETWORK_FIRST: 'networkFirst',
|
|
26189
|
+
PERSISTENCE_UNTIL_NETWORK_SUCCESS: 'persistenceUntilNetworkSuccess'
|
|
26190
|
+
});
|
|
26191
|
+
|
|
26192
|
+
var VALID_POLICIES = [
|
|
26193
|
+
VariantLookupPolicy.NETWORK_ONLY,
|
|
26194
|
+
VariantLookupPolicy.NETWORK_FIRST,
|
|
26195
|
+
VariantLookupPolicy.PERSISTENCE_UNTIL_NETWORK_SUCCESS
|
|
26196
|
+
];
|
|
26197
|
+
|
|
26198
|
+
/**
|
|
26199
|
+
* Module for handling the storage and retrieval of persisted feature flag variants.
|
|
26200
|
+
*/
|
|
26201
|
+
var FeatureFlagPersistence = function(persistenceConfig, token, isGloballyDisabled) {
|
|
26202
|
+
this.idb = new IDBStorageWrapper(MIXPANEL_FLAGS_DB_NAME, FLAGS_STORE_NAME, FLAGS_VERSION_DATA);
|
|
26203
|
+
this.persistenceConfig = persistenceConfig;
|
|
26204
|
+
this.persistedVariantsKey = PERSISTED_VARIANTS_KEY_PREFIX + token;
|
|
26205
|
+
this.isGloballyDisabled = isGloballyDisabled || function() { return false; };
|
|
26206
|
+
};
|
|
26207
|
+
|
|
26208
|
+
FeatureFlagPersistence.prototype.getPolicy = function() {
|
|
26209
|
+
if (this.isGloballyDisabled() || !this._isConfigValid()) {
|
|
26210
|
+
return VariantLookupPolicy.NETWORK_ONLY;
|
|
26211
|
+
}
|
|
26212
|
+
return this.persistenceConfig['variantLookupPolicy'];
|
|
26213
|
+
};
|
|
26214
|
+
|
|
26215
|
+
FeatureFlagPersistence.prototype.getTtlMs = function() {
|
|
26216
|
+
if (!this._isConfigValid()) {
|
|
26217
|
+
return DEFAULT_TTL_MS;
|
|
26218
|
+
}
|
|
26219
|
+
var configuredTtl = this.persistenceConfig['persistenceTtlMs'];
|
|
26220
|
+
return (configuredTtl === undefined || configuredTtl === null) ? DEFAULT_TTL_MS : configuredTtl;
|
|
26221
|
+
};
|
|
26222
|
+
|
|
26223
|
+
FeatureFlagPersistence.prototype._isConfigValid = function() {
|
|
26224
|
+
var config = this.persistenceConfig;
|
|
26225
|
+
if (!config) {
|
|
26226
|
+
return false;
|
|
26227
|
+
}
|
|
26228
|
+
|
|
26229
|
+
if (VALID_POLICIES.indexOf(config['variantLookupPolicy']) === -1) {
|
|
26230
|
+
logger$2.error('Invalid variantLookupPolicy:', config['variantLookupPolicy']);
|
|
26231
|
+
return false;
|
|
26232
|
+
}
|
|
26233
|
+
|
|
26234
|
+
if (config['persistenceTtlMs'] !== undefined &&
|
|
26235
|
+
config['persistenceTtlMs'] !== null &&
|
|
26236
|
+
config['persistenceTtlMs'] <= 0) {
|
|
26237
|
+
logger$2.error('If provided, persistenceTtlMs must be a positive number. Provided value:', config['persistenceTtlMs']);
|
|
26238
|
+
return false;
|
|
26239
|
+
}
|
|
26240
|
+
|
|
26241
|
+
return true;
|
|
26242
|
+
};
|
|
26243
|
+
|
|
26244
|
+
FeatureFlagPersistence.prototype.loadFlagsFromStorage = function(context) {
|
|
26245
|
+
var clearAndReturnNull = _.bind(function() {
|
|
26246
|
+
return this.clear().then(function() { return null; }).catch(function() { return null; });
|
|
26247
|
+
}, this);
|
|
26248
|
+
|
|
26249
|
+
if (this.getPolicy() === VariantLookupPolicy.NETWORK_ONLY) {
|
|
26250
|
+
return clearAndReturnNull();
|
|
26251
|
+
}
|
|
26252
|
+
|
|
26253
|
+
var ttlMs = this.getTtlMs();
|
|
26254
|
+
|
|
26255
|
+
return this.idb.init().then(_.bind(function() {
|
|
26256
|
+
return this.idb.getItem(this.persistedVariantsKey);
|
|
26257
|
+
}, this)).then(_.bind(function(data) {
|
|
26258
|
+
if (!data) {
|
|
26259
|
+
logger$2.log('No persisted variants found in IndexedDB');
|
|
26260
|
+
return null;
|
|
26261
|
+
}
|
|
26262
|
+
|
|
26263
|
+
if (ttlMs && Date.now() - data['persistedAt'] >= ttlMs) {
|
|
26264
|
+
logger$2.log('Persisted variants are expiring');
|
|
26265
|
+
return null;
|
|
26266
|
+
}
|
|
26267
|
+
|
|
26268
|
+
if (!context || data['distinctId'] !== context['distinct_id']) {
|
|
26269
|
+
logger$2.log('Persisted variants found, but for a different distinct_id so clearing.');
|
|
26270
|
+
return clearAndReturnNull();
|
|
26271
|
+
}
|
|
26272
|
+
|
|
26273
|
+
var persistedFlags = new Map();
|
|
26274
|
+
_.each(data['flagVariants'], function(variantData, key) {
|
|
26275
|
+
persistedFlags.set(key, {
|
|
26276
|
+
'key': variantData['variant_key'],
|
|
26277
|
+
'value': variantData['variant_value'],
|
|
26278
|
+
'experiment_id': variantData['experiment_id'],
|
|
26279
|
+
'is_experiment_active': variantData['is_experiment_active'],
|
|
26280
|
+
'is_qa_tester': variantData['is_qa_tester'],
|
|
26281
|
+
'variant_source': 'persistence',
|
|
26282
|
+
'persisted_at_in_ms': data['persistedAt'],
|
|
26283
|
+
'ttl_in_ms': ttlMs
|
|
26284
|
+
});
|
|
26285
|
+
});
|
|
26286
|
+
|
|
26287
|
+
logger$2.log('Loaded', persistedFlags.size, 'variants from IndexedDB for distinct_id', data['distinctId']);
|
|
26288
|
+
|
|
26289
|
+
return {
|
|
26290
|
+
flags: persistedFlags,
|
|
26291
|
+
pendingFirstTimeEvents: data['pendingFirstTimeEvents'] || {},
|
|
26292
|
+
persistedAtMs: data['persistedAt'],
|
|
26293
|
+
ttlMs: ttlMs
|
|
26294
|
+
};
|
|
26295
|
+
}, this)).catch(_.bind(function(error) {
|
|
26296
|
+
logger$2.error('Failed to load persisted variants from IndexedDB, so clearing', error);
|
|
26297
|
+
return clearAndReturnNull();
|
|
26298
|
+
}, this));
|
|
26299
|
+
};
|
|
26300
|
+
|
|
26301
|
+
FeatureFlagPersistence.prototype.save = function(context, flagsMap, pendingFirstTimeEvents) {
|
|
26302
|
+
if (this.getPolicy() === VariantLookupPolicy.NETWORK_ONLY) {
|
|
26303
|
+
return Promise.resolve();
|
|
26304
|
+
}
|
|
26305
|
+
|
|
26306
|
+
var flagVariants = {};
|
|
26307
|
+
flagsMap.forEach(function(variant, key) {
|
|
26308
|
+
flagVariants[key] = {
|
|
26309
|
+
'variant_key': variant['key'],
|
|
26310
|
+
'variant_value': variant['value'],
|
|
26311
|
+
'experiment_id': variant['experiment_id'],
|
|
26312
|
+
'is_experiment_active': variant['is_experiment_active'],
|
|
26313
|
+
'is_qa_tester': variant['is_qa_tester']
|
|
26314
|
+
};
|
|
26315
|
+
});
|
|
26316
|
+
|
|
26317
|
+
var data = {
|
|
26318
|
+
'persistedAt': Date.now(),
|
|
26319
|
+
'distinctId': context && context['distinct_id'],
|
|
26320
|
+
'context': context,
|
|
26321
|
+
'flagVariants': flagVariants,
|
|
26322
|
+
'pendingFirstTimeEvents': pendingFirstTimeEvents || {}
|
|
26323
|
+
};
|
|
26324
|
+
|
|
26325
|
+
return this.idb.init().then(_.bind(function() {
|
|
26326
|
+
return this.idb.setItem(this.persistedVariantsKey, data);
|
|
26327
|
+
}, this)).then(function() {
|
|
26328
|
+
logger$2.log('Saved', flagsMap.size, 'variants to IndexedDB for distinct_id', data['distinctId']);
|
|
26329
|
+
}).catch(function(error) {
|
|
26330
|
+
logger$2.error('Failed to persist variants to IndexedDB:', error);
|
|
26331
|
+
});
|
|
26332
|
+
};
|
|
26333
|
+
|
|
26334
|
+
FeatureFlagPersistence.prototype.clear = function() {
|
|
26335
|
+
if (this.isGloballyDisabled()) {
|
|
26336
|
+
return Promise.resolve();
|
|
26337
|
+
}
|
|
26338
|
+
return this.idb.init().then(_.bind(function() {
|
|
26339
|
+
return this.idb.removeItem(this.persistedVariantsKey);
|
|
26340
|
+
}, this)).then(function() {
|
|
26341
|
+
logger$2.log('Cleared persisted variants from IndexedDB');
|
|
26342
|
+
}).catch(function(error) {
|
|
26343
|
+
logger$2.error('Failed to clear persisted variants from IndexedDB:', error);
|
|
26344
|
+
});
|
|
26345
|
+
};
|
|
26346
|
+
|
|
26146
26347
|
var logger$1 = console_with_prefix('flags');
|
|
26147
26348
|
var FLAGS_CONFIG_KEY = 'flags';
|
|
26148
26349
|
|
|
26149
26350
|
var CONFIG_CONTEXT = 'context';
|
|
26351
|
+
var CONFIG_PERSISTENCE = 'persistence';
|
|
26150
26352
|
var CONFIG_DEFAULTS = {};
|
|
26151
26353
|
CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
|
|
26152
26354
|
|
|
@@ -26169,6 +26371,13 @@ var getFlagKeyFromPendingEventKey = function(eventKey) {
|
|
|
26169
26371
|
return eventKey.split(':')[0];
|
|
26170
26372
|
};
|
|
26171
26373
|
|
|
26374
|
+
var withFallbackSource = function(fallback) {
|
|
26375
|
+
if (_.isObject(fallback)) {
|
|
26376
|
+
return _.extend({}, fallback, {'variant_source': 'fallback'});
|
|
26377
|
+
}
|
|
26378
|
+
return {'value': fallback, 'variant_source': 'fallback'};
|
|
26379
|
+
};
|
|
26380
|
+
|
|
26172
26381
|
/**
|
|
26173
26382
|
* FeatureFlagManager: support for Mixpanel's feature flagging product
|
|
26174
26383
|
* @constructor
|
|
@@ -26191,11 +26400,63 @@ FeatureFlagManager.prototype.init = function() {
|
|
|
26191
26400
|
}
|
|
26192
26401
|
|
|
26193
26402
|
this.flags = null;
|
|
26194
|
-
this.fetchFlags();
|
|
26195
|
-
|
|
26196
26403
|
this.trackedFeatures = new Set();
|
|
26197
26404
|
this.pendingFirstTimeEvents = {};
|
|
26198
26405
|
this.activatedFirstTimeEvents = {};
|
|
26406
|
+
this._loadedPersistedAtMs = null;
|
|
26407
|
+
this._loadedTtlMs = null;
|
|
26408
|
+
|
|
26409
|
+
this.persistence = new FeatureFlagPersistence(
|
|
26410
|
+
this.getConfig(CONFIG_PERSISTENCE),
|
|
26411
|
+
this.getMpConfig('token'),
|
|
26412
|
+
_.bind(function() { return this.getMpConfig('disable_persistence'); }, this)
|
|
26413
|
+
);
|
|
26414
|
+
|
|
26415
|
+
this.persistenceLoadedPromise = this.persistence.loadFlagsFromStorage(this._buildContext())
|
|
26416
|
+
.then(_.bind(function(loaded) {
|
|
26417
|
+
if (loaded) {
|
|
26418
|
+
this.flags = loaded.flags;
|
|
26419
|
+
this.pendingFirstTimeEvents = loaded.pendingFirstTimeEvents;
|
|
26420
|
+
this._loadedPersistedAtMs = loaded.persistedAtMs;
|
|
26421
|
+
this._loadedTtlMs = loaded.ttlMs;
|
|
26422
|
+
}
|
|
26423
|
+
}, this));
|
|
26424
|
+
|
|
26425
|
+
return this.persistenceLoadedPromise
|
|
26426
|
+
.then(_.bind(function() {
|
|
26427
|
+
return this.fetchFlags();
|
|
26428
|
+
}, this))
|
|
26429
|
+
.catch(function() {
|
|
26430
|
+
logger$1.error('Error initializing feature flags');
|
|
26431
|
+
});
|
|
26432
|
+
};
|
|
26433
|
+
|
|
26434
|
+
FeatureFlagManager.prototype._buildContext = function() {
|
|
26435
|
+
return _.extend(
|
|
26436
|
+
{'distinct_id': this.getMpProperty('distinct_id'), 'device_id': this.getMpProperty('$device_id')},
|
|
26437
|
+
this.getConfig(CONFIG_CONTEXT)
|
|
26438
|
+
);
|
|
26439
|
+
};
|
|
26440
|
+
|
|
26441
|
+
FeatureFlagManager.prototype.reset = function() {
|
|
26442
|
+
if (!this.persistence) {
|
|
26443
|
+
return Promise.resolve();
|
|
26444
|
+
}
|
|
26445
|
+
|
|
26446
|
+
this.flags = null;
|
|
26447
|
+
this.pendingFirstTimeEvents = {};
|
|
26448
|
+
this.activatedFirstTimeEvents = {};
|
|
26449
|
+
this.trackedFeatures = new Set();
|
|
26450
|
+
this.fetchPromise = null;
|
|
26451
|
+
this._fetchInProgressStartTime = null;
|
|
26452
|
+
this._loadedPersistedAtMs = null;
|
|
26453
|
+
this._loadedTtlMs = null;
|
|
26454
|
+
|
|
26455
|
+
return this.persistence.clear().then(_.bind(function() {
|
|
26456
|
+
return this.fetchFlags();
|
|
26457
|
+
}, this)).catch(function() {
|
|
26458
|
+
logger$1.error('Error during flags reset');
|
|
26459
|
+
});
|
|
26199
26460
|
};
|
|
26200
26461
|
|
|
26201
26462
|
FeatureFlagManager.prototype.getFullConfig = function() {
|
|
@@ -26232,8 +26493,12 @@ FeatureFlagManager.prototype.updateContext = function(newContext, options) {
|
|
|
26232
26493
|
var oldContext = (options && options['replace']) ? {} : this.getConfig(CONFIG_CONTEXT);
|
|
26233
26494
|
ffConfig[CONFIG_CONTEXT] = _.extend({}, oldContext, newContext);
|
|
26234
26495
|
|
|
26235
|
-
|
|
26236
|
-
|
|
26496
|
+
var configUpdate = {};
|
|
26497
|
+
configUpdate[FLAGS_CONFIG_KEY] = ffConfig;
|
|
26498
|
+
this.setMpConfig(configUpdate);
|
|
26499
|
+
return this.fetchFlags().catch(function() {
|
|
26500
|
+
logger$1.error('Error fetching flags during updateContext');
|
|
26501
|
+
});
|
|
26237
26502
|
};
|
|
26238
26503
|
|
|
26239
26504
|
FeatureFlagManager.prototype.areFlagsReady = function() {
|
|
@@ -26248,12 +26513,11 @@ FeatureFlagManager.prototype.fetchFlags = function() {
|
|
|
26248
26513
|
return Promise.resolve();
|
|
26249
26514
|
}
|
|
26250
26515
|
|
|
26251
|
-
var
|
|
26252
|
-
var
|
|
26516
|
+
var context = this._buildContext();
|
|
26517
|
+
var distinctId = context['distinct_id'];
|
|
26253
26518
|
var traceparent = generateTraceparent();
|
|
26254
26519
|
logger$1.log('Fetching flags for distinct ID: ' + distinctId);
|
|
26255
26520
|
|
|
26256
|
-
var context = _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT));
|
|
26257
26521
|
var searchParams = new URLSearchParams();
|
|
26258
26522
|
searchParams.set('context', JSON.stringify(context));
|
|
26259
26523
|
searchParams.set('token', this.getMpConfig('token'));
|
|
@@ -26270,96 +26534,116 @@ FeatureFlagManager.prototype.fetchFlags = function() {
|
|
|
26270
26534
|
}
|
|
26271
26535
|
}).then(function(response) {
|
|
26272
26536
|
this.markFetchComplete();
|
|
26273
|
-
return response.json()
|
|
26274
|
-
|
|
26275
|
-
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
|
|
26279
|
-
|
|
26280
|
-
|
|
26281
|
-
|
|
26282
|
-
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
|
|
26286
|
-
|
|
26287
|
-
|
|
26288
|
-
|
|
26289
|
-
|
|
26290
|
-
}
|
|
26537
|
+
return response.json();
|
|
26538
|
+
}.bind(this)).then(function(responseBody) {
|
|
26539
|
+
var responseFlags = responseBody['flags'];
|
|
26540
|
+
if (!responseFlags) {
|
|
26541
|
+
throw new Error('No flags in API response');
|
|
26542
|
+
}
|
|
26543
|
+
var flags = new Map();
|
|
26544
|
+
var pendingFirstTimeEvents = {};
|
|
26545
|
+
|
|
26546
|
+
// Process flags from response
|
|
26547
|
+
_.each(responseFlags, function(data, key) {
|
|
26548
|
+
// Check if this flag has any activated first-time events this session
|
|
26549
|
+
var hasActivatedEvent = false;
|
|
26550
|
+
var prefix = key + ':';
|
|
26551
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
26552
|
+
if (eventKey.startsWith(prefix)) {
|
|
26553
|
+
hasActivatedEvent = true;
|
|
26554
|
+
}
|
|
26555
|
+
});
|
|
26291
26556
|
|
|
26292
|
-
|
|
26293
|
-
|
|
26294
|
-
|
|
26295
|
-
|
|
26296
|
-
|
|
26297
|
-
}
|
|
26298
|
-
} else {
|
|
26299
|
-
// Use server's current variant
|
|
26300
|
-
flags.set(key, {
|
|
26301
|
-
'key': data['variant_key'],
|
|
26302
|
-
'value': data['variant_value'],
|
|
26303
|
-
'experiment_id': data['experiment_id'],
|
|
26304
|
-
'is_experiment_active': data['is_experiment_active'],
|
|
26305
|
-
'is_qa_tester': data['is_qa_tester']
|
|
26306
|
-
});
|
|
26557
|
+
if (hasActivatedEvent) {
|
|
26558
|
+
// Preserve the activated variant, don't overwrite with server's current variant
|
|
26559
|
+
var currentFlag = this.flags && this.flags.get(key);
|
|
26560
|
+
if (currentFlag) {
|
|
26561
|
+
flags.set(key, currentFlag);
|
|
26307
26562
|
}
|
|
26308
|
-
}
|
|
26563
|
+
} else {
|
|
26564
|
+
// Use server's current variant
|
|
26565
|
+
flags.set(key, {
|
|
26566
|
+
'key': data['variant_key'],
|
|
26567
|
+
'value': data['variant_value'],
|
|
26568
|
+
'experiment_id': data['experiment_id'],
|
|
26569
|
+
'is_experiment_active': data['is_experiment_active'],
|
|
26570
|
+
'is_qa_tester': data['is_qa_tester'],
|
|
26571
|
+
'variant_source': 'network'
|
|
26572
|
+
});
|
|
26573
|
+
}
|
|
26574
|
+
}, this);
|
|
26309
26575
|
|
|
26310
|
-
|
|
26311
|
-
|
|
26312
|
-
|
|
26313
|
-
|
|
26314
|
-
|
|
26315
|
-
|
|
26576
|
+
// Process top-level pending_first_time_events array
|
|
26577
|
+
var topLevelDefinitions = responseBody['pending_first_time_events'];
|
|
26578
|
+
if (topLevelDefinitions && topLevelDefinitions.length > 0) {
|
|
26579
|
+
_.each(topLevelDefinitions, function(def) {
|
|
26580
|
+
var flagKey = def['flag_key'];
|
|
26581
|
+
var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
|
|
26316
26582
|
|
|
26317
|
-
|
|
26318
|
-
|
|
26319
|
-
|
|
26320
|
-
|
|
26583
|
+
// Skip if this specific event has already been activated this session
|
|
26584
|
+
if (this.activatedFirstTimeEvents[eventKey]) {
|
|
26585
|
+
return;
|
|
26586
|
+
}
|
|
26321
26587
|
|
|
26322
|
-
|
|
26323
|
-
|
|
26324
|
-
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
|
|
26328
|
-
|
|
26329
|
-
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26588
|
+
// Store pending event definition using composite key
|
|
26589
|
+
pendingFirstTimeEvents[eventKey] = {
|
|
26590
|
+
'flag_key': flagKey,
|
|
26591
|
+
'flag_id': def['flag_id'],
|
|
26592
|
+
'project_id': def['project_id'],
|
|
26593
|
+
'first_time_event_hash': def['first_time_event_hash'],
|
|
26594
|
+
'event_name': def['event_name'],
|
|
26595
|
+
'property_filters': def['property_filters'],
|
|
26596
|
+
'pending_variant': def['pending_variant']
|
|
26597
|
+
};
|
|
26598
|
+
}, this);
|
|
26599
|
+
}
|
|
26334
26600
|
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26339
|
-
|
|
26340
|
-
|
|
26341
|
-
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
|
|
26601
|
+
// Preserve any activated orphaned flags (flags that were activated but are no longer in response)
|
|
26602
|
+
if (this.activatedFirstTimeEvents) {
|
|
26603
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
26604
|
+
var flagKey = getFlagKeyFromPendingEventKey(eventKey);
|
|
26605
|
+
if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
|
|
26606
|
+
// Keep the activated flag even though it's not in the new response
|
|
26607
|
+
flags.set(flagKey, this.flags.get(flagKey));
|
|
26608
|
+
}
|
|
26609
|
+
}, this);
|
|
26610
|
+
}
|
|
26345
26611
|
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26612
|
+
this.flags = flags;
|
|
26613
|
+
this.trackedFeatures = new Set();
|
|
26614
|
+
this.pendingFirstTimeEvents = pendingFirstTimeEvents;
|
|
26615
|
+
this._loadedPersistedAtMs = null;
|
|
26616
|
+
this._loadedTtlMs = null;
|
|
26617
|
+
this._traceparent = traceparent;
|
|
26349
26618
|
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
logger$1.error(error);
|
|
26354
|
-
}.bind(this));
|
|
26619
|
+
this._loadTargetingIfNeeded();
|
|
26620
|
+
|
|
26621
|
+
this.persistence.save(context, this.flags, this.pendingFirstTimeEvents);
|
|
26355
26622
|
}.bind(this)).catch(function(error) {
|
|
26356
|
-
this.
|
|
26623
|
+
if (this._fetchInProgressStartTime) {
|
|
26624
|
+
this.markFetchComplete();
|
|
26625
|
+
}
|
|
26357
26626
|
logger$1.error(error);
|
|
26627
|
+
throw error;
|
|
26358
26628
|
}.bind(this));
|
|
26359
26629
|
|
|
26360
26630
|
return this.fetchPromise;
|
|
26361
26631
|
};
|
|
26362
26632
|
|
|
26633
|
+
FeatureFlagManager.prototype.loadFlags = function() {
|
|
26634
|
+
if (!this.isSystemEnabled()) {
|
|
26635
|
+
return Promise.resolve();
|
|
26636
|
+
}
|
|
26637
|
+
if (!this.trackedFeatures) {
|
|
26638
|
+
logger$1.error('loadFlags called before init');
|
|
26639
|
+
return Promise.resolve();
|
|
26640
|
+
}
|
|
26641
|
+
if (this._fetchInProgressStartTime) {
|
|
26642
|
+
return this.fetchPromise;
|
|
26643
|
+
}
|
|
26644
|
+
return this.fetchFlags();
|
|
26645
|
+
};
|
|
26646
|
+
|
|
26363
26647
|
FeatureFlagManager.prototype.markFetchComplete = function() {
|
|
26364
26648
|
if (!this._fetchInProgressStartTime) {
|
|
26365
26649
|
logger$1.error('Fetch in progress started time not set, cannot mark fetch complete');
|
|
@@ -26494,6 +26778,7 @@ FeatureFlagManager.prototype._processFirstTimeEventCheck = function(eventName, p
|
|
|
26494
26778
|
};
|
|
26495
26779
|
|
|
26496
26780
|
this.flags.set(flagKey, newVariant);
|
|
26781
|
+
this.trackedFeatures.delete(flagKey);
|
|
26497
26782
|
this.activatedFirstTimeEvents[eventKey] = true;
|
|
26498
26783
|
|
|
26499
26784
|
this.recordFirstTimeEvent(
|
|
@@ -26543,35 +26828,106 @@ FeatureFlagManager.prototype.recordFirstTimeEvent = function(flagId, projectId,
|
|
|
26543
26828
|
};
|
|
26544
26829
|
|
|
26545
26830
|
FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
|
|
26546
|
-
if (!this.
|
|
26831
|
+
if (!this.persistenceLoadedPromise) {
|
|
26547
26832
|
return new Promise(function(resolve) {
|
|
26548
26833
|
logger$1.critical('Feature Flags not initialized');
|
|
26549
|
-
resolve(fallback);
|
|
26834
|
+
resolve(withFallbackSource(fallback));
|
|
26550
26835
|
});
|
|
26551
26836
|
}
|
|
26552
26837
|
|
|
26553
|
-
|
|
26554
|
-
|
|
26555
|
-
|
|
26556
|
-
|
|
26557
|
-
|
|
26558
|
-
|
|
26838
|
+
var policy = this.persistence.getPolicy();
|
|
26839
|
+
|
|
26840
|
+
return this.persistenceLoadedPromise.then(_.bind(function() {
|
|
26841
|
+
// 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.
|
|
26842
|
+
if (policy === VariantLookupPolicy.PERSISTENCE_UNTIL_NETWORK_SUCCESS) {
|
|
26843
|
+
if (this.areFlagsReady() && !this._loadedPersistenceIsStale()) {
|
|
26844
|
+
return this.getVariantSync(featureName, fallback);
|
|
26845
|
+
}
|
|
26846
|
+
if (!this.fetchPromise) {
|
|
26847
|
+
return withFallbackSource(fallback);
|
|
26848
|
+
}
|
|
26849
|
+
return this.fetchPromise.then(_.bind(function() {
|
|
26850
|
+
return this.getVariantSync(featureName, fallback);
|
|
26851
|
+
}, this)).catch(function(error) {
|
|
26852
|
+
logger$1.error(error);
|
|
26853
|
+
return withFallbackSource(fallback);
|
|
26854
|
+
});
|
|
26855
|
+
}
|
|
26856
|
+
|
|
26857
|
+
var serve = _.bind(function() { return this.getVariantSync(featureName, fallback); }, this);
|
|
26858
|
+
if (!this.fetchPromise) {
|
|
26859
|
+
return withFallbackSource(fallback);
|
|
26860
|
+
}
|
|
26861
|
+
return this.fetchPromise.then(serve).catch(serve);
|
|
26862
|
+
}, this));
|
|
26863
|
+
};
|
|
26864
|
+
|
|
26865
|
+
FeatureFlagManager.prototype._loadedPersistenceIsStale = function() {
|
|
26866
|
+
if (!this._loadedPersistedAtMs || !this._loadedTtlMs) {
|
|
26867
|
+
return false;
|
|
26868
|
+
}
|
|
26869
|
+
return Date.now() - this._loadedPersistedAtMs >= this._loadedTtlMs;
|
|
26559
26870
|
};
|
|
26560
26871
|
|
|
26561
26872
|
FeatureFlagManager.prototype.getVariantSync = function(featureName, fallback) {
|
|
26873
|
+
if (this._loadedPersistenceIsStale()) {
|
|
26874
|
+
logger$1.log('Loaded persisted variants are past TTL so returning fallback for "' + featureName + '"');
|
|
26875
|
+
return withFallbackSource(fallback);
|
|
26876
|
+
}
|
|
26562
26877
|
if (!this.areFlagsReady()) {
|
|
26563
26878
|
logger$1.log('Flags not loaded yet');
|
|
26564
|
-
return fallback;
|
|
26879
|
+
return withFallbackSource(fallback);
|
|
26565
26880
|
}
|
|
26566
26881
|
var feature = this.flags.get(featureName);
|
|
26567
26882
|
if (!feature) {
|
|
26568
26883
|
logger$1.log('No flag found: "' + featureName + '"');
|
|
26569
|
-
return fallback;
|
|
26884
|
+
return withFallbackSource(fallback);
|
|
26570
26885
|
}
|
|
26571
26886
|
this.trackFeatureCheck(featureName, feature);
|
|
26572
26887
|
return feature;
|
|
26573
26888
|
};
|
|
26574
26889
|
|
|
26890
|
+
FeatureFlagManager.prototype.getAllVariants = function() {
|
|
26891
|
+
if (!this.persistenceLoadedPromise) {
|
|
26892
|
+
logger$1.critical('Feature Flags not initialized');
|
|
26893
|
+
return Promise.resolve(new Map());
|
|
26894
|
+
}
|
|
26895
|
+
|
|
26896
|
+
var policy = this.persistence.getPolicy();
|
|
26897
|
+
|
|
26898
|
+
return this.persistenceLoadedPromise.then(_.bind(function() {
|
|
26899
|
+
// 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.
|
|
26900
|
+
if (policy === VariantLookupPolicy.PERSISTENCE_UNTIL_NETWORK_SUCCESS) {
|
|
26901
|
+
if (this.areFlagsReady() && !this._loadedPersistenceIsStale()) {
|
|
26902
|
+
return this.getAllVariantsSync();
|
|
26903
|
+
}
|
|
26904
|
+
if (!this.fetchPromise) {
|
|
26905
|
+
return new Map();
|
|
26906
|
+
}
|
|
26907
|
+
return this.fetchPromise.then(_.bind(function() {
|
|
26908
|
+
return this.getAllVariantsSync();
|
|
26909
|
+
}, this)).catch(function(error) {
|
|
26910
|
+
logger$1.error(error);
|
|
26911
|
+
return new Map();
|
|
26912
|
+
});
|
|
26913
|
+
}
|
|
26914
|
+
|
|
26915
|
+
var serve = _.bind(this.getAllVariantsSync, this);
|
|
26916
|
+
if (!this.fetchPromise) {
|
|
26917
|
+
return new Map();
|
|
26918
|
+
}
|
|
26919
|
+
return this.fetchPromise.then(serve).catch(serve);
|
|
26920
|
+
}, this));
|
|
26921
|
+
};
|
|
26922
|
+
|
|
26923
|
+
FeatureFlagManager.prototype.getAllVariantsSync = function() {
|
|
26924
|
+
if (this._loadedPersistenceIsStale()) {
|
|
26925
|
+
logger$1.log('Loaded persisted variants are past TTL so returning empty Map');
|
|
26926
|
+
return new Map();
|
|
26927
|
+
}
|
|
26928
|
+
return this.flags || new Map();
|
|
26929
|
+
};
|
|
26930
|
+
|
|
26575
26931
|
FeatureFlagManager.prototype.getVariantValue = function(featureName, fallbackValue) {
|
|
26576
26932
|
return this.getVariant(featureName, {'value': fallbackValue}).then(function(feature) {
|
|
26577
26933
|
return feature['value'];
|
|
@@ -26610,6 +26966,10 @@ FeatureFlagManager.prototype.isEnabledSync = function(featureName, fallbackValue
|
|
|
26610
26966
|
return val;
|
|
26611
26967
|
};
|
|
26612
26968
|
|
|
26969
|
+
function isPresent(v) {
|
|
26970
|
+
return v !== undefined && v !== null;
|
|
26971
|
+
}
|
|
26972
|
+
|
|
26613
26973
|
FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature) {
|
|
26614
26974
|
if (this.trackedFeatures.has(featureName)) {
|
|
26615
26975
|
return;
|
|
@@ -26620,25 +26980,41 @@ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature)
|
|
|
26620
26980
|
'Experiment name': featureName,
|
|
26621
26981
|
'Variant name': feature['key'],
|
|
26622
26982
|
'$experiment_type': 'feature_flag',
|
|
26623
|
-
'Variant fetch start time': new Date(this._fetchStartTime).toISOString(),
|
|
26624
|
-
'Variant fetch complete time': new Date(this._fetchCompleteTime).toISOString(),
|
|
26983
|
+
'Variant fetch start time': isPresent(this._fetchStartTime) ? new Date(this._fetchStartTime).toISOString() : null,
|
|
26984
|
+
'Variant fetch complete time': isPresent(this._fetchCompleteTime) ? new Date(this._fetchCompleteTime).toISOString() : null,
|
|
26625
26985
|
'Variant fetch latency (ms)': this._fetchLatency,
|
|
26626
26986
|
'Variant fetch traceparent': this._traceparent,
|
|
26627
26987
|
};
|
|
26628
26988
|
|
|
26629
|
-
if (feature['experiment_id']
|
|
26989
|
+
if (isPresent(feature['experiment_id'])) {
|
|
26630
26990
|
trackingProperties['$experiment_id'] = feature['experiment_id'];
|
|
26631
26991
|
}
|
|
26632
|
-
if (feature['is_experiment_active']
|
|
26992
|
+
if (isPresent(feature['is_experiment_active'])) {
|
|
26633
26993
|
trackingProperties['$is_experiment_active'] = feature['is_experiment_active'];
|
|
26634
26994
|
}
|
|
26635
|
-
if (feature['is_qa_tester']
|
|
26995
|
+
if (isPresent(feature['is_qa_tester'])) {
|
|
26636
26996
|
trackingProperties['$is_qa_tester'] = feature['is_qa_tester'];
|
|
26637
26997
|
}
|
|
26998
|
+
if (isPresent(feature['variant_source'])) {
|
|
26999
|
+
trackingProperties['$variant_source'] = feature['variant_source'];
|
|
27000
|
+
}
|
|
27001
|
+
if (isPresent(feature['persisted_at_in_ms'])) {
|
|
27002
|
+
trackingProperties['$persisted_at_in_ms'] = feature['persisted_at_in_ms'];
|
|
27003
|
+
}
|
|
27004
|
+
if (isPresent(feature['ttl_in_ms'])) {
|
|
27005
|
+
trackingProperties['$ttl_in_ms'] = feature['ttl_in_ms'];
|
|
27006
|
+
}
|
|
26638
27007
|
|
|
26639
27008
|
this.track('$experiment_started', trackingProperties);
|
|
26640
27009
|
};
|
|
26641
27010
|
|
|
27011
|
+
FeatureFlagManager.prototype.whenReady = function() {
|
|
27012
|
+
if (this.fetchPromise) {
|
|
27013
|
+
return this.fetchPromise;
|
|
27014
|
+
}
|
|
27015
|
+
return Promise.resolve();
|
|
27016
|
+
};
|
|
27017
|
+
|
|
26642
27018
|
FeatureFlagManager.prototype.minApisSupported = function() {
|
|
26643
27019
|
return !!this.fetch &&
|
|
26644
27020
|
typeof Promise !== 'undefined' &&
|
|
@@ -26651,11 +27027,15 @@ safewrapClass(FeatureFlagManager);
|
|
|
26651
27027
|
FeatureFlagManager.prototype['are_flags_ready'] = FeatureFlagManager.prototype.areFlagsReady;
|
|
26652
27028
|
FeatureFlagManager.prototype['get_variant'] = FeatureFlagManager.prototype.getVariant;
|
|
26653
27029
|
FeatureFlagManager.prototype['get_variant_sync'] = FeatureFlagManager.prototype.getVariantSync;
|
|
27030
|
+
FeatureFlagManager.prototype['get_all_variants'] = FeatureFlagManager.prototype.getAllVariants;
|
|
27031
|
+
FeatureFlagManager.prototype['get_all_variants_sync'] = FeatureFlagManager.prototype.getAllVariantsSync;
|
|
26654
27032
|
FeatureFlagManager.prototype['get_variant_value'] = FeatureFlagManager.prototype.getVariantValue;
|
|
26655
27033
|
FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
|
|
26656
27034
|
FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
|
|
26657
27035
|
FeatureFlagManager.prototype['is_enabled_sync'] = FeatureFlagManager.prototype.isEnabledSync;
|
|
27036
|
+
FeatureFlagManager.prototype['load_flags'] = FeatureFlagManager.prototype.loadFlags;
|
|
26658
27037
|
FeatureFlagManager.prototype['update_context'] = FeatureFlagManager.prototype.updateContext;
|
|
27038
|
+
FeatureFlagManager.prototype['when_ready'] = FeatureFlagManager.prototype.whenReady;
|
|
26659
27039
|
|
|
26660
27040
|
// Deprecated method
|
|
26661
27041
|
FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
|
|
@@ -26701,7 +27081,7 @@ RecorderManager.prototype.shouldLoadRecorder = function() {
|
|
|
26701
27081
|
return PromisePolyfill.resolve(false);
|
|
26702
27082
|
}
|
|
26703
27083
|
|
|
26704
|
-
var recording_registry_idb = new IDBStorageWrapper(RECORDING_REGISTRY_STORE_NAME);
|
|
27084
|
+
var recording_registry_idb = new IDBStorageWrapper(MIXPANEL_BROWSER_DB_NAME, RECORDING_REGISTRY_STORE_NAME, RECORDER_VERSION_DATA);
|
|
26705
27085
|
var tab_id = this.getTabId();
|
|
26706
27086
|
return recording_registry_idb.init()
|
|
26707
27087
|
.then(function () {
|
|
@@ -28628,6 +29008,7 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
28628
29008
|
'disable_all_events': false,
|
|
28629
29009
|
'identify_called': false
|
|
28630
29010
|
};
|
|
29011
|
+
this._remote_settings_strict_disabled = false;
|
|
28631
29012
|
|
|
28632
29013
|
// set up request queueing/batching
|
|
28633
29014
|
this.request_batchers = {};
|
|
@@ -28702,9 +29083,6 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
28702
29083
|
this.flags.init();
|
|
28703
29084
|
this['flags'] = this.flags;
|
|
28704
29085
|
|
|
28705
|
-
this.autocapture = new Autocapture(this);
|
|
28706
|
-
this.autocapture.init();
|
|
28707
|
-
|
|
28708
29086
|
this._init_tab_id();
|
|
28709
29087
|
|
|
28710
29088
|
// Based on remote_settings_mode, fetch remote settings and then start session recording if applicable
|
|
@@ -28716,6 +29094,9 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
28716
29094
|
} else {
|
|
28717
29095
|
this.__session_recording_init_promise = this._check_and_start_session_recording();
|
|
28718
29096
|
}
|
|
29097
|
+
|
|
29098
|
+
this.autocapture = new Autocapture(this);
|
|
29099
|
+
this.autocapture.init();
|
|
28719
29100
|
};
|
|
28720
29101
|
|
|
28721
29102
|
/**
|
|
@@ -28762,9 +29143,19 @@ MixpanelLib.prototype._check_and_start_session_recording = addOptOutCheckMixpane
|
|
|
28762
29143
|
return this.recorderManager.checkAndStartSessionRecording(force_start);
|
|
28763
29144
|
});
|
|
28764
29145
|
|
|
28765
|
-
MixpanelLib.prototype._start_recording_on_event = function(event_name, properties) {
|
|
28766
|
-
|
|
28767
|
-
|
|
29146
|
+
MixpanelLib.prototype._start_recording_on_event = safewrap(function(event_name, properties) {
|
|
29147
|
+
// Wait for recording init to complete before evaluating event triggers.
|
|
29148
|
+
// This ensures recording_event_triggers config is fully loaded when remote settings are used.
|
|
29149
|
+
if (this.__session_recording_init_promise) {
|
|
29150
|
+
this.__session_recording_init_promise.then(_.bind(function() {
|
|
29151
|
+
// In strict mode, skip recording if remote settings failed
|
|
29152
|
+
if (this._remote_settings_strict_disabled) {
|
|
29153
|
+
return;
|
|
29154
|
+
}
|
|
29155
|
+
return this.recorderManager.startRecordingOnEvent(event_name, properties);
|
|
29156
|
+
}, this));
|
|
29157
|
+
}
|
|
29158
|
+
});
|
|
28768
29159
|
|
|
28769
29160
|
MixpanelLib.prototype.start_session_recording = function () {
|
|
28770
29161
|
return this._check_and_start_session_recording(true);
|
|
@@ -29063,6 +29454,7 @@ MixpanelLib.prototype._fetch_remote_settings = function(mode) {
|
|
|
29063
29454
|
var disableRecordingIfStrict = function() {
|
|
29064
29455
|
if (mode === 'strict') {
|
|
29065
29456
|
self.set_config({'record_sessions_percent': 0});
|
|
29457
|
+
self._remote_settings_strict_disabled = true;
|
|
29066
29458
|
}
|
|
29067
29459
|
};
|
|
29068
29460
|
|
|
@@ -29688,6 +30080,10 @@ MixpanelLib.prototype.track_pageview = addOptOutCheckMixpanelLib(function(proper
|
|
|
29688
30080
|
properties
|
|
29689
30081
|
);
|
|
29690
30082
|
|
|
30083
|
+
if (this.is_recording_heatmap_data()) {
|
|
30084
|
+
event_properties['$captured_for_heatmap'] = true;
|
|
30085
|
+
}
|
|
30086
|
+
|
|
29691
30087
|
return this.track(event_name, event_properties);
|
|
29692
30088
|
});
|
|
29693
30089
|
|
|
@@ -30011,7 +30407,9 @@ MixpanelLib.prototype.identify = function(
|
|
|
30011
30407
|
|
|
30012
30408
|
// check feature flags again if distinct id has changed
|
|
30013
30409
|
if (new_distinct_id !== previous_distinct_id) {
|
|
30014
|
-
this.flags.fetchFlags()
|
|
30410
|
+
this.flags.fetchFlags().catch(function() {
|
|
30411
|
+
console$1.error('[flags] Error fetching flags during identify');
|
|
30412
|
+
});
|
|
30015
30413
|
}
|
|
30016
30414
|
};
|
|
30017
30415
|
|
|
@@ -30029,6 +30427,7 @@ MixpanelLib.prototype.reset = function() {
|
|
|
30029
30427
|
'$device_id': uuid
|
|
30030
30428
|
}, '');
|
|
30031
30429
|
this._check_and_start_session_recording();
|
|
30430
|
+
this.flags.reset();
|
|
30032
30431
|
};
|
|
30033
30432
|
|
|
30034
30433
|
/**
|