mixpanel-browser 2.77.0 → 2.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/CHANGELOG.md +4 -0
- package/dist/async-modules/{mixpanel-recorder-wIWnMDLA.min.js → mixpanel-recorder-BjSlYaNJ.min.js} +2 -2
- package/dist/async-modules/{mixpanel-recorder-wIWnMDLA.min.js.map → mixpanel-recorder-BjSlYaNJ.min.js.map} +1 -1
- package/dist/async-modules/{mixpanel-recorder-DLKbUIEE.js → mixpanel-recorder-zMBXIyeG.js} +1 -1
- package/dist/async-modules/{mixpanel-targeting-CTcftSJC.min.js → mixpanel-targeting-BSHal4N9.min.js} +2 -2
- package/dist/async-modules/{mixpanel-targeting-CTcftSJC.min.js.map → mixpanel-targeting-BSHal4N9.min.js.map} +1 -1
- package/dist/async-modules/{mixpanel-targeting-CmVvUyFM.js → mixpanel-targeting-UHf4eBfC.js} +1 -1
- package/dist/mixpanel-core.cjs.d.ts +1 -0
- package/dist/mixpanel-core.cjs.js +111 -80
- package/dist/mixpanel-recorder.js +1 -1
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-targeting.js +1 -1
- 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 +1 -0
- package/dist/mixpanel-with-async-modules.cjs.js +113 -82
- package/dist/mixpanel-with-async-recorder.cjs.d.ts +1 -0
- package/dist/mixpanel-with-async-recorder.cjs.js +113 -82
- package/dist/mixpanel-with-recorder.d.ts +1 -0
- package/dist/mixpanel-with-recorder.js +111 -80
- package/dist/mixpanel-with-recorder.min.d.ts +1 -0
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.d.ts +1 -0
- package/dist/mixpanel.amd.js +111 -80
- package/dist/mixpanel.cjs.d.ts +1 -0
- package/dist/mixpanel.cjs.js +111 -80
- package/dist/mixpanel.globals.js +113 -82
- package/dist/mixpanel.min.js +180 -179
- package/dist/mixpanel.module.d.ts +1 -0
- package/dist/mixpanel.module.js +111 -80
- package/dist/mixpanel.umd.d.ts +1 -0
- package/dist/mixpanel.umd.js +111 -80
- package/package.json +1 -1
- package/src/config.js +1 -1
- package/src/flags/CLAUDE.md +24 -0
- package/src/flags/index.js +109 -80
- package/src/index.d.ts +1 -0
- package/src/mixpanel-core.js +3 -1
- package/testServer.js +2 -0
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.78.0'
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
// Window global names for async modules
|
|
@@ -26191,7 +26191,9 @@ FeatureFlagManager.prototype.init = function() {
|
|
|
26191
26191
|
}
|
|
26192
26192
|
|
|
26193
26193
|
this.flags = null;
|
|
26194
|
-
this.fetchFlags()
|
|
26194
|
+
this.fetchFlags().catch(function() {
|
|
26195
|
+
logger$1.error('Error fetching flags during init');
|
|
26196
|
+
});
|
|
26195
26197
|
|
|
26196
26198
|
this.trackedFeatures = new Set();
|
|
26197
26199
|
this.pendingFirstTimeEvents = {};
|
|
@@ -26232,8 +26234,12 @@ FeatureFlagManager.prototype.updateContext = function(newContext, options) {
|
|
|
26232
26234
|
var oldContext = (options && options['replace']) ? {} : this.getConfig(CONFIG_CONTEXT);
|
|
26233
26235
|
ffConfig[CONFIG_CONTEXT] = _.extend({}, oldContext, newContext);
|
|
26234
26236
|
|
|
26235
|
-
|
|
26236
|
-
|
|
26237
|
+
var configUpdate = {};
|
|
26238
|
+
configUpdate[FLAGS_CONFIG_KEY] = ffConfig;
|
|
26239
|
+
this.setMpConfig(configUpdate);
|
|
26240
|
+
return this.fetchFlags().catch(function() {
|
|
26241
|
+
logger$1.error('Error fetching flags during updateContext');
|
|
26242
|
+
});
|
|
26237
26243
|
};
|
|
26238
26244
|
|
|
26239
26245
|
FeatureFlagManager.prototype.areFlagsReady = function() {
|
|
@@ -26270,96 +26276,110 @@ FeatureFlagManager.prototype.fetchFlags = function() {
|
|
|
26270
26276
|
}
|
|
26271
26277
|
}).then(function(response) {
|
|
26272
26278
|
this.markFetchComplete();
|
|
26273
|
-
return response.json()
|
|
26274
|
-
|
|
26275
|
-
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
|
|
26279
|
-
|
|
26280
|
-
|
|
26281
|
-
|
|
26282
|
-
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
|
|
26286
|
-
|
|
26287
|
-
|
|
26288
|
-
|
|
26289
|
-
|
|
26290
|
-
}
|
|
26279
|
+
return response.json();
|
|
26280
|
+
}.bind(this)).then(function(responseBody) {
|
|
26281
|
+
var responseFlags = responseBody['flags'];
|
|
26282
|
+
if (!responseFlags) {
|
|
26283
|
+
throw new Error('No flags in API response');
|
|
26284
|
+
}
|
|
26285
|
+
var flags = new Map();
|
|
26286
|
+
var pendingFirstTimeEvents = {};
|
|
26287
|
+
|
|
26288
|
+
// Process flags from response
|
|
26289
|
+
_.each(responseFlags, function(data, key) {
|
|
26290
|
+
// Check if this flag has any activated first-time events this session
|
|
26291
|
+
var hasActivatedEvent = false;
|
|
26292
|
+
var prefix = key + ':';
|
|
26293
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
26294
|
+
if (eventKey.startsWith(prefix)) {
|
|
26295
|
+
hasActivatedEvent = true;
|
|
26296
|
+
}
|
|
26297
|
+
});
|
|
26291
26298
|
|
|
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
|
-
});
|
|
26299
|
+
if (hasActivatedEvent) {
|
|
26300
|
+
// Preserve the activated variant, don't overwrite with server's current variant
|
|
26301
|
+
var currentFlag = this.flags && this.flags.get(key);
|
|
26302
|
+
if (currentFlag) {
|
|
26303
|
+
flags.set(key, currentFlag);
|
|
26307
26304
|
}
|
|
26308
|
-
}
|
|
26305
|
+
} else {
|
|
26306
|
+
// Use server's current variant
|
|
26307
|
+
flags.set(key, {
|
|
26308
|
+
'key': data['variant_key'],
|
|
26309
|
+
'value': data['variant_value'],
|
|
26310
|
+
'experiment_id': data['experiment_id'],
|
|
26311
|
+
'is_experiment_active': data['is_experiment_active'],
|
|
26312
|
+
'is_qa_tester': data['is_qa_tester']
|
|
26313
|
+
});
|
|
26314
|
+
}
|
|
26315
|
+
}, this);
|
|
26309
26316
|
|
|
26310
|
-
|
|
26311
|
-
|
|
26312
|
-
|
|
26313
|
-
|
|
26314
|
-
|
|
26315
|
-
|
|
26317
|
+
// Process top-level pending_first_time_events array
|
|
26318
|
+
var topLevelDefinitions = responseBody['pending_first_time_events'];
|
|
26319
|
+
if (topLevelDefinitions && topLevelDefinitions.length > 0) {
|
|
26320
|
+
_.each(topLevelDefinitions, function(def) {
|
|
26321
|
+
var flagKey = def['flag_key'];
|
|
26322
|
+
var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
|
|
26316
26323
|
|
|
26317
|
-
|
|
26318
|
-
|
|
26319
|
-
|
|
26320
|
-
|
|
26324
|
+
// Skip if this specific event has already been activated this session
|
|
26325
|
+
if (this.activatedFirstTimeEvents[eventKey]) {
|
|
26326
|
+
return;
|
|
26327
|
+
}
|
|
26321
26328
|
|
|
26322
|
-
|
|
26323
|
-
|
|
26324
|
-
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
|
|
26328
|
-
|
|
26329
|
-
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26329
|
+
// Store pending event definition using composite key
|
|
26330
|
+
pendingFirstTimeEvents[eventKey] = {
|
|
26331
|
+
'flag_key': flagKey,
|
|
26332
|
+
'flag_id': def['flag_id'],
|
|
26333
|
+
'project_id': def['project_id'],
|
|
26334
|
+
'first_time_event_hash': def['first_time_event_hash'],
|
|
26335
|
+
'event_name': def['event_name'],
|
|
26336
|
+
'property_filters': def['property_filters'],
|
|
26337
|
+
'pending_variant': def['pending_variant']
|
|
26338
|
+
};
|
|
26339
|
+
}, this);
|
|
26340
|
+
}
|
|
26334
26341
|
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26339
|
-
|
|
26340
|
-
|
|
26341
|
-
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
|
|
26342
|
+
// Preserve any activated orphaned flags (flags that were activated but are no longer in response)
|
|
26343
|
+
if (this.activatedFirstTimeEvents) {
|
|
26344
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
26345
|
+
var flagKey = getFlagKeyFromPendingEventKey(eventKey);
|
|
26346
|
+
if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
|
|
26347
|
+
// Keep the activated flag even though it's not in the new response
|
|
26348
|
+
flags.set(flagKey, this.flags.get(flagKey));
|
|
26349
|
+
}
|
|
26350
|
+
}, this);
|
|
26351
|
+
}
|
|
26345
26352
|
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26353
|
+
this.flags = flags;
|
|
26354
|
+
this.pendingFirstTimeEvents = pendingFirstTimeEvents;
|
|
26355
|
+
this._traceparent = traceparent;
|
|
26349
26356
|
|
|
26350
|
-
|
|
26351
|
-
}.bind(this)).catch(function(error) {
|
|
26352
|
-
this.markFetchComplete();
|
|
26353
|
-
logger$1.error(error);
|
|
26354
|
-
}.bind(this));
|
|
26357
|
+
this._loadTargetingIfNeeded();
|
|
26355
26358
|
}.bind(this)).catch(function(error) {
|
|
26356
|
-
this.
|
|
26359
|
+
if (this._fetchInProgressStartTime) {
|
|
26360
|
+
this.markFetchComplete();
|
|
26361
|
+
}
|
|
26357
26362
|
logger$1.error(error);
|
|
26363
|
+
throw error;
|
|
26358
26364
|
}.bind(this));
|
|
26359
26365
|
|
|
26360
26366
|
return this.fetchPromise;
|
|
26361
26367
|
};
|
|
26362
26368
|
|
|
26369
|
+
FeatureFlagManager.prototype.loadFlags = function() {
|
|
26370
|
+
if (!this.isSystemEnabled()) {
|
|
26371
|
+
return Promise.resolve();
|
|
26372
|
+
}
|
|
26373
|
+
if (!this.trackedFeatures) {
|
|
26374
|
+
logger$1.error('loadFlags called before init');
|
|
26375
|
+
return Promise.resolve();
|
|
26376
|
+
}
|
|
26377
|
+
if (this._fetchInProgressStartTime) {
|
|
26378
|
+
return this.fetchPromise;
|
|
26379
|
+
}
|
|
26380
|
+
return this.fetchFlags();
|
|
26381
|
+
};
|
|
26382
|
+
|
|
26363
26383
|
FeatureFlagManager.prototype.markFetchComplete = function() {
|
|
26364
26384
|
if (!this._fetchInProgressStartTime) {
|
|
26365
26385
|
logger$1.error('Fetch in progress started time not set, cannot mark fetch complete');
|
|
@@ -26639,6 +26659,13 @@ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature)
|
|
|
26639
26659
|
this.track('$experiment_started', trackingProperties);
|
|
26640
26660
|
};
|
|
26641
26661
|
|
|
26662
|
+
FeatureFlagManager.prototype.whenReady = function() {
|
|
26663
|
+
if (this.fetchPromise) {
|
|
26664
|
+
return this.fetchPromise;
|
|
26665
|
+
}
|
|
26666
|
+
return Promise.resolve();
|
|
26667
|
+
};
|
|
26668
|
+
|
|
26642
26669
|
FeatureFlagManager.prototype.minApisSupported = function() {
|
|
26643
26670
|
return !!this.fetch &&
|
|
26644
26671
|
typeof Promise !== 'undefined' &&
|
|
@@ -26655,7 +26682,9 @@ FeatureFlagManager.prototype['get_variant_value'] = FeatureFlagManager.prototype
|
|
|
26655
26682
|
FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
|
|
26656
26683
|
FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
|
|
26657
26684
|
FeatureFlagManager.prototype['is_enabled_sync'] = FeatureFlagManager.prototype.isEnabledSync;
|
|
26685
|
+
FeatureFlagManager.prototype['load_flags'] = FeatureFlagManager.prototype.loadFlags;
|
|
26658
26686
|
FeatureFlagManager.prototype['update_context'] = FeatureFlagManager.prototype.updateContext;
|
|
26687
|
+
FeatureFlagManager.prototype['when_ready'] = FeatureFlagManager.prototype.whenReady;
|
|
26659
26688
|
|
|
26660
26689
|
// Deprecated method
|
|
26661
26690
|
FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
|
|
@@ -30011,7 +30040,9 @@ MixpanelLib.prototype.identify = function(
|
|
|
30011
30040
|
|
|
30012
30041
|
// check feature flags again if distinct id has changed
|
|
30013
30042
|
if (new_distinct_id !== previous_distinct_id) {
|
|
30014
|
-
this.flags.fetchFlags()
|
|
30043
|
+
this.flags.fetchFlags().catch(function() {
|
|
30044
|
+
console$1.error('[flags] Error fetching flags during identify');
|
|
30045
|
+
});
|
|
30015
30046
|
}
|
|
30016
30047
|
};
|
|
30017
30048
|
|
package/dist/mixpanel.umd.d.ts
CHANGED
package/dist/mixpanel.umd.js
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
var Config = {
|
|
33
33
|
DEBUG: false,
|
|
34
|
-
LIB_VERSION: '2.
|
|
34
|
+
LIB_VERSION: '2.78.0'
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
// Window global names for async modules
|
|
@@ -26197,7 +26197,9 @@
|
|
|
26197
26197
|
}
|
|
26198
26198
|
|
|
26199
26199
|
this.flags = null;
|
|
26200
|
-
this.fetchFlags()
|
|
26200
|
+
this.fetchFlags().catch(function() {
|
|
26201
|
+
logger$1.error('Error fetching flags during init');
|
|
26202
|
+
});
|
|
26201
26203
|
|
|
26202
26204
|
this.trackedFeatures = new Set();
|
|
26203
26205
|
this.pendingFirstTimeEvents = {};
|
|
@@ -26238,8 +26240,12 @@
|
|
|
26238
26240
|
var oldContext = (options && options['replace']) ? {} : this.getConfig(CONFIG_CONTEXT);
|
|
26239
26241
|
ffConfig[CONFIG_CONTEXT] = _.extend({}, oldContext, newContext);
|
|
26240
26242
|
|
|
26241
|
-
|
|
26242
|
-
|
|
26243
|
+
var configUpdate = {};
|
|
26244
|
+
configUpdate[FLAGS_CONFIG_KEY] = ffConfig;
|
|
26245
|
+
this.setMpConfig(configUpdate);
|
|
26246
|
+
return this.fetchFlags().catch(function() {
|
|
26247
|
+
logger$1.error('Error fetching flags during updateContext');
|
|
26248
|
+
});
|
|
26243
26249
|
};
|
|
26244
26250
|
|
|
26245
26251
|
FeatureFlagManager.prototype.areFlagsReady = function() {
|
|
@@ -26276,96 +26282,110 @@
|
|
|
26276
26282
|
}
|
|
26277
26283
|
}).then(function(response) {
|
|
26278
26284
|
this.markFetchComplete();
|
|
26279
|
-
return response.json()
|
|
26280
|
-
|
|
26281
|
-
|
|
26282
|
-
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
|
|
26286
|
-
|
|
26287
|
-
|
|
26288
|
-
|
|
26289
|
-
|
|
26290
|
-
|
|
26291
|
-
|
|
26292
|
-
|
|
26293
|
-
|
|
26294
|
-
|
|
26295
|
-
|
|
26296
|
-
}
|
|
26285
|
+
return response.json();
|
|
26286
|
+
}.bind(this)).then(function(responseBody) {
|
|
26287
|
+
var responseFlags = responseBody['flags'];
|
|
26288
|
+
if (!responseFlags) {
|
|
26289
|
+
throw new Error('No flags in API response');
|
|
26290
|
+
}
|
|
26291
|
+
var flags = new Map();
|
|
26292
|
+
var pendingFirstTimeEvents = {};
|
|
26293
|
+
|
|
26294
|
+
// Process flags from response
|
|
26295
|
+
_.each(responseFlags, function(data, key) {
|
|
26296
|
+
// Check if this flag has any activated first-time events this session
|
|
26297
|
+
var hasActivatedEvent = false;
|
|
26298
|
+
var prefix = key + ':';
|
|
26299
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
26300
|
+
if (eventKey.startsWith(prefix)) {
|
|
26301
|
+
hasActivatedEvent = true;
|
|
26302
|
+
}
|
|
26303
|
+
});
|
|
26297
26304
|
|
|
26298
|
-
|
|
26299
|
-
|
|
26300
|
-
|
|
26301
|
-
|
|
26302
|
-
|
|
26303
|
-
}
|
|
26304
|
-
} else {
|
|
26305
|
-
// Use server's current variant
|
|
26306
|
-
flags.set(key, {
|
|
26307
|
-
'key': data['variant_key'],
|
|
26308
|
-
'value': data['variant_value'],
|
|
26309
|
-
'experiment_id': data['experiment_id'],
|
|
26310
|
-
'is_experiment_active': data['is_experiment_active'],
|
|
26311
|
-
'is_qa_tester': data['is_qa_tester']
|
|
26312
|
-
});
|
|
26305
|
+
if (hasActivatedEvent) {
|
|
26306
|
+
// Preserve the activated variant, don't overwrite with server's current variant
|
|
26307
|
+
var currentFlag = this.flags && this.flags.get(key);
|
|
26308
|
+
if (currentFlag) {
|
|
26309
|
+
flags.set(key, currentFlag);
|
|
26313
26310
|
}
|
|
26314
|
-
}
|
|
26311
|
+
} else {
|
|
26312
|
+
// Use server's current variant
|
|
26313
|
+
flags.set(key, {
|
|
26314
|
+
'key': data['variant_key'],
|
|
26315
|
+
'value': data['variant_value'],
|
|
26316
|
+
'experiment_id': data['experiment_id'],
|
|
26317
|
+
'is_experiment_active': data['is_experiment_active'],
|
|
26318
|
+
'is_qa_tester': data['is_qa_tester']
|
|
26319
|
+
});
|
|
26320
|
+
}
|
|
26321
|
+
}, this);
|
|
26315
26322
|
|
|
26316
|
-
|
|
26317
|
-
|
|
26318
|
-
|
|
26319
|
-
|
|
26320
|
-
|
|
26321
|
-
|
|
26323
|
+
// Process top-level pending_first_time_events array
|
|
26324
|
+
var topLevelDefinitions = responseBody['pending_first_time_events'];
|
|
26325
|
+
if (topLevelDefinitions && topLevelDefinitions.length > 0) {
|
|
26326
|
+
_.each(topLevelDefinitions, function(def) {
|
|
26327
|
+
var flagKey = def['flag_key'];
|
|
26328
|
+
var eventKey = getPendingEventKey(flagKey, def['first_time_event_hash']);
|
|
26322
26329
|
|
|
26323
|
-
|
|
26324
|
-
|
|
26325
|
-
|
|
26326
|
-
|
|
26330
|
+
// Skip if this specific event has already been activated this session
|
|
26331
|
+
if (this.activatedFirstTimeEvents[eventKey]) {
|
|
26332
|
+
return;
|
|
26333
|
+
}
|
|
26327
26334
|
|
|
26328
|
-
|
|
26329
|
-
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26334
|
-
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26339
|
-
|
|
26335
|
+
// Store pending event definition using composite key
|
|
26336
|
+
pendingFirstTimeEvents[eventKey] = {
|
|
26337
|
+
'flag_key': flagKey,
|
|
26338
|
+
'flag_id': def['flag_id'],
|
|
26339
|
+
'project_id': def['project_id'],
|
|
26340
|
+
'first_time_event_hash': def['first_time_event_hash'],
|
|
26341
|
+
'event_name': def['event_name'],
|
|
26342
|
+
'property_filters': def['property_filters'],
|
|
26343
|
+
'pending_variant': def['pending_variant']
|
|
26344
|
+
};
|
|
26345
|
+
}, this);
|
|
26346
|
+
}
|
|
26340
26347
|
|
|
26341
|
-
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26348
|
+
// Preserve any activated orphaned flags (flags that were activated but are no longer in response)
|
|
26349
|
+
if (this.activatedFirstTimeEvents) {
|
|
26350
|
+
_.each(this.activatedFirstTimeEvents, function(activated, eventKey) {
|
|
26351
|
+
var flagKey = getFlagKeyFromPendingEventKey(eventKey);
|
|
26352
|
+
if (activated && !flags.has(flagKey) && this.flags && this.flags.has(flagKey)) {
|
|
26353
|
+
// Keep the activated flag even though it's not in the new response
|
|
26354
|
+
flags.set(flagKey, this.flags.get(flagKey));
|
|
26355
|
+
}
|
|
26356
|
+
}, this);
|
|
26357
|
+
}
|
|
26351
26358
|
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26359
|
+
this.flags = flags;
|
|
26360
|
+
this.pendingFirstTimeEvents = pendingFirstTimeEvents;
|
|
26361
|
+
this._traceparent = traceparent;
|
|
26355
26362
|
|
|
26356
|
-
|
|
26357
|
-
}.bind(this)).catch(function(error) {
|
|
26358
|
-
this.markFetchComplete();
|
|
26359
|
-
logger$1.error(error);
|
|
26360
|
-
}.bind(this));
|
|
26363
|
+
this._loadTargetingIfNeeded();
|
|
26361
26364
|
}.bind(this)).catch(function(error) {
|
|
26362
|
-
this.
|
|
26365
|
+
if (this._fetchInProgressStartTime) {
|
|
26366
|
+
this.markFetchComplete();
|
|
26367
|
+
}
|
|
26363
26368
|
logger$1.error(error);
|
|
26369
|
+
throw error;
|
|
26364
26370
|
}.bind(this));
|
|
26365
26371
|
|
|
26366
26372
|
return this.fetchPromise;
|
|
26367
26373
|
};
|
|
26368
26374
|
|
|
26375
|
+
FeatureFlagManager.prototype.loadFlags = function() {
|
|
26376
|
+
if (!this.isSystemEnabled()) {
|
|
26377
|
+
return Promise.resolve();
|
|
26378
|
+
}
|
|
26379
|
+
if (!this.trackedFeatures) {
|
|
26380
|
+
logger$1.error('loadFlags called before init');
|
|
26381
|
+
return Promise.resolve();
|
|
26382
|
+
}
|
|
26383
|
+
if (this._fetchInProgressStartTime) {
|
|
26384
|
+
return this.fetchPromise;
|
|
26385
|
+
}
|
|
26386
|
+
return this.fetchFlags();
|
|
26387
|
+
};
|
|
26388
|
+
|
|
26369
26389
|
FeatureFlagManager.prototype.markFetchComplete = function() {
|
|
26370
26390
|
if (!this._fetchInProgressStartTime) {
|
|
26371
26391
|
logger$1.error('Fetch in progress started time not set, cannot mark fetch complete');
|
|
@@ -26645,6 +26665,13 @@
|
|
|
26645
26665
|
this.track('$experiment_started', trackingProperties);
|
|
26646
26666
|
};
|
|
26647
26667
|
|
|
26668
|
+
FeatureFlagManager.prototype.whenReady = function() {
|
|
26669
|
+
if (this.fetchPromise) {
|
|
26670
|
+
return this.fetchPromise;
|
|
26671
|
+
}
|
|
26672
|
+
return Promise.resolve();
|
|
26673
|
+
};
|
|
26674
|
+
|
|
26648
26675
|
FeatureFlagManager.prototype.minApisSupported = function() {
|
|
26649
26676
|
return !!this.fetch &&
|
|
26650
26677
|
typeof Promise !== 'undefined' &&
|
|
@@ -26661,7 +26688,9 @@
|
|
|
26661
26688
|
FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
|
|
26662
26689
|
FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
|
|
26663
26690
|
FeatureFlagManager.prototype['is_enabled_sync'] = FeatureFlagManager.prototype.isEnabledSync;
|
|
26691
|
+
FeatureFlagManager.prototype['load_flags'] = FeatureFlagManager.prototype.loadFlags;
|
|
26664
26692
|
FeatureFlagManager.prototype['update_context'] = FeatureFlagManager.prototype.updateContext;
|
|
26693
|
+
FeatureFlagManager.prototype['when_ready'] = FeatureFlagManager.prototype.whenReady;
|
|
26665
26694
|
|
|
26666
26695
|
// Deprecated method
|
|
26667
26696
|
FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;
|
|
@@ -30017,7 +30046,9 @@
|
|
|
30017
30046
|
|
|
30018
30047
|
// check feature flags again if distinct id has changed
|
|
30019
30048
|
if (new_distinct_id !== previous_distinct_id) {
|
|
30020
|
-
this.flags.fetchFlags()
|
|
30049
|
+
this.flags.fetchFlags().catch(function() {
|
|
30050
|
+
console$1.error('[flags] Error fetching flags during identify');
|
|
30051
|
+
});
|
|
30021
30052
|
}
|
|
30022
30053
|
};
|
|
30023
30054
|
|
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Flags Module
|
|
2
|
+
|
|
3
|
+
## Testing
|
|
4
|
+
- Test runner is **mocha** (not jest): `BABEL_ENV=test npx mocha --require babel-core/register tests/unit/flags.js`
|
|
5
|
+
- Unit tests live in `tests/unit/flags.js`
|
|
6
|
+
|
|
7
|
+
## Code style
|
|
8
|
+
- ES5 prototypal classes — no arrow functions, no ES6 classes
|
|
9
|
+
- Use `.bind(this)` for `this` context in promise `.then()` / `.catch()` callbacks
|
|
10
|
+
- Flat promise chains preferred over nested `.then()` inside `.then()`
|
|
11
|
+
|
|
12
|
+
## Public API pattern
|
|
13
|
+
- Snake-case aliases are registered at the bottom of `index.js` (e.g., `prototype['load_flags'] = prototype.loadFlags`)
|
|
14
|
+
- New public methods need both the camelCase implementation and a snake_case alias
|
|
15
|
+
|
|
16
|
+
## Error handling convention
|
|
17
|
+
- `fetchFlags()` always rejects on error (single `.catch` that logs and re-throws)
|
|
18
|
+
- Fire-and-forget callers (`init`, `updateContext`, `mixpanel-core.js` identify call) swallow errors at the call site with `.catch(function() {})`
|
|
19
|
+
- User-facing methods like `loadFlags` propagate rejections so the caller can handle them
|
|
20
|
+
|
|
21
|
+
## Key files
|
|
22
|
+
- `src/flags/index.js` — `FeatureFlagManager` class (fetch, load, variants, first-time events)
|
|
23
|
+
- `src/mixpanel-core.js` — calls `fetchFlags()` on distinct_id change (~line 1764)
|
|
24
|
+
- `tests/unit/flags.js` — all flag unit tests
|