mixpanel-browser 2.66.0 → 2.67.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.
@@ -13944,7 +13944,7 @@ define((function () { 'use strict';
13944
13944
 
13945
13945
  var Config = {
13946
13946
  DEBUG: false,
13947
- LIB_VERSION: '2.66.0'
13947
+ LIB_VERSION: '2.67.0'
13948
13948
  };
13949
13949
 
13950
13950
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -17062,6 +17062,13 @@ define((function () { 'use strict';
17062
17062
  * @property {string} replayStartUrl
17063
17063
  */
17064
17064
 
17065
+ /**
17066
+ * @typedef {Object} UserIdInfo
17067
+ * @property {string} distinct_id
17068
+ * @property {string} user_id
17069
+ * @property {string} device_id
17070
+ */
17071
+
17065
17072
 
17066
17073
  /**
17067
17074
  * This class encapsulates a single session recording and its lifecycle.
@@ -17117,6 +17124,30 @@ define((function () { 'use strict';
17117
17124
  });
17118
17125
  };
17119
17126
 
17127
+ /**
17128
+ * @returns {UserIdInfo}
17129
+ */
17130
+ SessionRecording.prototype.getUserIdInfo = function () {
17131
+ if (this.finalFlushUserIdInfo) {
17132
+ return this.finalFlushUserIdInfo;
17133
+ }
17134
+
17135
+ var userIdInfo = {
17136
+ 'distinct_id': String(this._mixpanel.get_distinct_id()),
17137
+ };
17138
+
17139
+ // send ID management props if they exist
17140
+ var deviceId = this._mixpanel.get_property('$device_id');
17141
+ if (deviceId) {
17142
+ userIdInfo['$device_id'] = deviceId;
17143
+ }
17144
+ var userId = this._mixpanel.get_property('$user_id');
17145
+ if (userId) {
17146
+ userIdInfo['$user_id'] = userId;
17147
+ }
17148
+ return userIdInfo;
17149
+ };
17150
+
17120
17151
  SessionRecording.prototype.unloadPersistedData = function () {
17121
17152
  this.batcher.stop();
17122
17153
  return this.batcher.flush()
@@ -17241,6 +17272,9 @@ define((function () { 'use strict';
17241
17272
  };
17242
17273
 
17243
17274
  SessionRecording.prototype.stopRecording = function (skipFlush) {
17275
+ // store the user ID info in case this is getting called in mixpanel.reset()
17276
+ this.finalFlushUserIdInfo = this.getUserIdInfo();
17277
+
17244
17278
  if (!this.isRrwebStopped()) {
17245
17279
  try {
17246
17280
  this._stopRecording();
@@ -17406,7 +17440,6 @@ define((function () { 'use strict';
17406
17440
  '$current_url': this.batchStartUrl,
17407
17441
  '$lib_version': Config.LIB_VERSION,
17408
17442
  'batch_start_time': batchStartTime / 1000,
17409
- 'distinct_id': String(this._mixpanel.get_distinct_id()),
17410
17443
  'mp_lib': 'web',
17411
17444
  'replay_id': replayId,
17412
17445
  'replay_length_ms': replayLengthMs,
@@ -17415,16 +17448,7 @@ define((function () { 'use strict';
17415
17448
  'seq': this.seqNo
17416
17449
  };
17417
17450
  var eventsJson = JSON.stringify(data);
17418
-
17419
- // send ID management props if they exist
17420
- var deviceId = this._mixpanel.get_property('$device_id');
17421
- if (deviceId) {
17422
- reqParams['$device_id'] = deviceId;
17423
- }
17424
- var userId = this._mixpanel.get_property('$user_id');
17425
- if (userId) {
17426
- reqParams['$user_id'] = userId;
17427
- }
17451
+ Object.assign(reqParams, this.getUserIdInfo());
17428
17452
 
17429
17453
  if (CompressionStream) {
17430
17454
  var jsonStream = new Blob([eventsJson], {type: 'application/json'}).stream();
@@ -18553,8 +18577,9 @@ define((function () { 'use strict';
18553
18577
  * @constructor
18554
18578
  */
18555
18579
  var FeatureFlagManager = function(initOptions) {
18580
+ this.getFullApiRoute = initOptions.getFullApiRoute;
18556
18581
  this.getMpConfig = initOptions.getConfigFunc;
18557
- this.getDistinctId = initOptions.getDistinctIdFunc;
18582
+ this.getMpProperty = initOptions.getPropertyFunc;
18558
18583
  this.track = initOptions.trackingFunc;
18559
18584
  };
18560
18585
 
@@ -18603,12 +18628,14 @@ define((function () { 'use strict';
18603
18628
  return;
18604
18629
  }
18605
18630
 
18606
- var distinctId = this.getDistinctId();
18631
+ var distinctId = this.getMpProperty('distinct_id');
18632
+ var deviceId = this.getMpProperty('$device_id');
18607
18633
  logger.log('Fetching flags for distinct ID: ' + distinctId);
18608
18634
  var reqParams = {
18609
- 'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
18635
+ 'context': _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT))
18610
18636
  };
18611
- this.fetchPromise = win['fetch'](this.getMpConfig('api_host') + '/' + this.getMpConfig('api_routes')['flags'], {
18637
+ this._fetchInProgressStartTime = Date.now();
18638
+ this.fetchPromise = win['fetch'](this.getFullApiRoute(), {
18612
18639
  'method': 'POST',
18613
18640
  'headers': {
18614
18641
  'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
@@ -18616,6 +18643,7 @@ define((function () { 'use strict';
18616
18643
  },
18617
18644
  'body': JSON.stringify(reqParams)
18618
18645
  }).then(function(response) {
18646
+ this.markFetchComplete();
18619
18647
  return response.json().then(function(responseBody) {
18620
18648
  var responseFlags = responseBody['flags'];
18621
18649
  if (!responseFlags) {
@@ -18630,9 +18658,24 @@ define((function () { 'use strict';
18630
18658
  });
18631
18659
  this.flags = flags;
18632
18660
  }.bind(this)).catch(function(error) {
18661
+ this.markFetchComplete();
18633
18662
  logger.error(error);
18634
- });
18635
- }.bind(this)).catch(function() {});
18663
+ }.bind(this));
18664
+ }.bind(this)).catch(function(error) {
18665
+ this.markFetchComplete();
18666
+ logger.error(error);
18667
+ }.bind(this));
18668
+ };
18669
+
18670
+ FeatureFlagManager.prototype.markFetchComplete = function() {
18671
+ if (!this._fetchInProgressStartTime) {
18672
+ logger.error('Fetch in progress started time not set, cannot mark fetch complete');
18673
+ return;
18674
+ }
18675
+ this._fetchStartTime = this._fetchInProgressStartTime;
18676
+ this._fetchCompleteTime = Date.now();
18677
+ this._fetchLatency = this._fetchCompleteTime - this._fetchStartTime;
18678
+ this._fetchInProgressStartTime = null;
18636
18679
  };
18637
18680
 
18638
18681
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
@@ -18711,7 +18754,10 @@ define((function () { 'use strict';
18711
18754
  this.track('$experiment_started', {
18712
18755
  'Experiment name': featureName,
18713
18756
  'Variant name': feature['key'],
18714
- '$experiment_type': 'feature_flag'
18757
+ '$experiment_type': 'feature_flag',
18758
+ 'Variant fetch start time': new Date(this._fetchStartTime).toISOString(),
18759
+ 'Variant fetch complete time': new Date(this._fetchCompleteTime).toISOString(),
18760
+ 'Variant fetch latency (ms)': this._fetchLatency
18715
18761
  });
18716
18762
  };
18717
18763
 
@@ -20411,8 +20457,11 @@ define((function () { 'use strict';
20411
20457
  }
20412
20458
 
20413
20459
  this.flags = new FeatureFlagManager({
20460
+ getFullApiRoute: _.bind(function() {
20461
+ return this.get_api_host('flags') + '/' + this.get_config('api_routes')['flags'];
20462
+ }, this),
20414
20463
  getConfigFunc: _.bind(this.get_config, this),
20415
- getDistinctIdFunc: _.bind(this.get_distinct_id, this),
20464
+ getPropertyFunc: _.bind(this.get_property, this),
20416
20465
  trackingFunc: _.bind(this.track, this)
20417
20466
  });
20418
20467
  this.flags.init();
@@ -20898,11 +20947,10 @@ define((function () { 'use strict';
20898
20947
 
20899
20948
  MixpanelLib.prototype.get_batcher_configs = function() {
20900
20949
  var queue_prefix = '__mpq_' + this.get_config('token');
20901
- var api_routes = this.get_config('api_routes');
20902
20950
  this._batcher_configs = this._batcher_configs || {
20903
- events: {type: 'events', endpoint: '/' + api_routes['track'], queue_key: queue_prefix + '_ev'},
20904
- people: {type: 'people', endpoint: '/' + api_routes['engage'], queue_key: queue_prefix + '_pp'},
20905
- groups: {type: 'groups', endpoint: '/' + api_routes['groups'], queue_key: queue_prefix + '_gr'}
20951
+ events: {type: 'events', api_name: 'track', queue_key: queue_prefix + '_ev'},
20952
+ people: {type: 'people', api_name: 'engage', queue_key: queue_prefix + '_pp'},
20953
+ groups: {type: 'groups', api_name: 'groups', queue_key: queue_prefix + '_gr'}
20906
20954
  };
20907
20955
  return this._batcher_configs;
20908
20956
  };
@@ -20916,8 +20964,9 @@ define((function () { 'use strict';
20916
20964
  libConfig: this['config'],
20917
20965
  errorReporter: this.get_config('error_reporter'),
20918
20966
  sendRequestFunc: _.bind(function(data, options, cb) {
20967
+ var api_routes = this.get_config('api_routes');
20919
20968
  this._send_request(
20920
- this.get_config('api_host') + attrs.endpoint,
20969
+ this.get_api_host(attrs.api_name) + '/' + api_routes[attrs.api_name],
20921
20970
  this._encode_data_for_request(data),
20922
20971
  options,
20923
20972
  this._prepare_callback(cb, data)
@@ -21645,31 +21694,15 @@ define((function () { 'use strict';
21645
21694
  * Useful for clearing data when a user logs out.
21646
21695
  */
21647
21696
  MixpanelLib.prototype.reset = function() {
21648
- var self = this;
21649
-
21650
- var reset = function () {
21651
- self['persistence'].clear();
21652
- self._flags.identify_called = false;
21653
- var uuid = _.UUID();
21654
- self.register_once({
21655
- 'distinct_id': DEVICE_ID_PREFIX + uuid,
21656
- '$device_id': uuid
21657
- }, '');
21658
- };
21659
-
21660
- if (self._recorder) {
21661
- self.stop_session_recording()
21662
- .then(function () {
21663
- reset();
21664
- self._check_and_start_session_recording();
21665
- })
21666
- .catch(_.bind(function (err) {
21667
- reset();
21668
- this.report_error('Error restarting recording session', err);
21669
- }, this));
21670
- } else {
21671
- reset();
21672
- }
21697
+ this.stop_session_recording();
21698
+ this['persistence'].clear();
21699
+ this._flags.identify_called = false;
21700
+ var uuid = _.UUID();
21701
+ this.register_once({
21702
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
21703
+ '$device_id': uuid
21704
+ }, '');
21705
+ this._check_and_start_session_recording();
21673
21706
  };
21674
21707
 
21675
21708
  /**
@@ -13944,7 +13944,7 @@ if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]'
13944
13944
 
13945
13945
  var Config = {
13946
13946
  DEBUG: false,
13947
- LIB_VERSION: '2.66.0'
13947
+ LIB_VERSION: '2.67.0'
13948
13948
  };
13949
13949
 
13950
13950
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -17062,6 +17062,13 @@ function isUserEvent(ev) {
17062
17062
  * @property {string} replayStartUrl
17063
17063
  */
17064
17064
 
17065
+ /**
17066
+ * @typedef {Object} UserIdInfo
17067
+ * @property {string} distinct_id
17068
+ * @property {string} user_id
17069
+ * @property {string} device_id
17070
+ */
17071
+
17065
17072
 
17066
17073
  /**
17067
17074
  * This class encapsulates a single session recording and its lifecycle.
@@ -17117,6 +17124,30 @@ var SessionRecording = function(options) {
17117
17124
  });
17118
17125
  };
17119
17126
 
17127
+ /**
17128
+ * @returns {UserIdInfo}
17129
+ */
17130
+ SessionRecording.prototype.getUserIdInfo = function () {
17131
+ if (this.finalFlushUserIdInfo) {
17132
+ return this.finalFlushUserIdInfo;
17133
+ }
17134
+
17135
+ var userIdInfo = {
17136
+ 'distinct_id': String(this._mixpanel.get_distinct_id()),
17137
+ };
17138
+
17139
+ // send ID management props if they exist
17140
+ var deviceId = this._mixpanel.get_property('$device_id');
17141
+ if (deviceId) {
17142
+ userIdInfo['$device_id'] = deviceId;
17143
+ }
17144
+ var userId = this._mixpanel.get_property('$user_id');
17145
+ if (userId) {
17146
+ userIdInfo['$user_id'] = userId;
17147
+ }
17148
+ return userIdInfo;
17149
+ };
17150
+
17120
17151
  SessionRecording.prototype.unloadPersistedData = function () {
17121
17152
  this.batcher.stop();
17122
17153
  return this.batcher.flush()
@@ -17241,6 +17272,9 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
17241
17272
  };
17242
17273
 
17243
17274
  SessionRecording.prototype.stopRecording = function (skipFlush) {
17275
+ // store the user ID info in case this is getting called in mixpanel.reset()
17276
+ this.finalFlushUserIdInfo = this.getUserIdInfo();
17277
+
17244
17278
  if (!this.isRrwebStopped()) {
17245
17279
  try {
17246
17280
  this._stopRecording();
@@ -17406,7 +17440,6 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
17406
17440
  '$current_url': this.batchStartUrl,
17407
17441
  '$lib_version': Config.LIB_VERSION,
17408
17442
  'batch_start_time': batchStartTime / 1000,
17409
- 'distinct_id': String(this._mixpanel.get_distinct_id()),
17410
17443
  'mp_lib': 'web',
17411
17444
  'replay_id': replayId,
17412
17445
  'replay_length_ms': replayLengthMs,
@@ -17415,16 +17448,7 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
17415
17448
  'seq': this.seqNo
17416
17449
  };
17417
17450
  var eventsJson = JSON.stringify(data);
17418
-
17419
- // send ID management props if they exist
17420
- var deviceId = this._mixpanel.get_property('$device_id');
17421
- if (deviceId) {
17422
- reqParams['$device_id'] = deviceId;
17423
- }
17424
- var userId = this._mixpanel.get_property('$user_id');
17425
- if (userId) {
17426
- reqParams['$user_id'] = userId;
17427
- }
17451
+ Object.assign(reqParams, this.getUserIdInfo());
17428
17452
 
17429
17453
  if (CompressionStream) {
17430
17454
  var jsonStream = new Blob([eventsJson], {type: 'application/json'}).stream();
@@ -18553,8 +18577,9 @@ CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
18553
18577
  * @constructor
18554
18578
  */
18555
18579
  var FeatureFlagManager = function(initOptions) {
18580
+ this.getFullApiRoute = initOptions.getFullApiRoute;
18556
18581
  this.getMpConfig = initOptions.getConfigFunc;
18557
- this.getDistinctId = initOptions.getDistinctIdFunc;
18582
+ this.getMpProperty = initOptions.getPropertyFunc;
18558
18583
  this.track = initOptions.trackingFunc;
18559
18584
  };
18560
18585
 
@@ -18603,12 +18628,14 @@ FeatureFlagManager.prototype.fetchFlags = function() {
18603
18628
  return;
18604
18629
  }
18605
18630
 
18606
- var distinctId = this.getDistinctId();
18631
+ var distinctId = this.getMpProperty('distinct_id');
18632
+ var deviceId = this.getMpProperty('$device_id');
18607
18633
  logger.log('Fetching flags for distinct ID: ' + distinctId);
18608
18634
  var reqParams = {
18609
- 'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
18635
+ 'context': _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT))
18610
18636
  };
18611
- this.fetchPromise = win['fetch'](this.getMpConfig('api_host') + '/' + this.getMpConfig('api_routes')['flags'], {
18637
+ this._fetchInProgressStartTime = Date.now();
18638
+ this.fetchPromise = win['fetch'](this.getFullApiRoute(), {
18612
18639
  'method': 'POST',
18613
18640
  'headers': {
18614
18641
  'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
@@ -18616,6 +18643,7 @@ FeatureFlagManager.prototype.fetchFlags = function() {
18616
18643
  },
18617
18644
  'body': JSON.stringify(reqParams)
18618
18645
  }).then(function(response) {
18646
+ this.markFetchComplete();
18619
18647
  return response.json().then(function(responseBody) {
18620
18648
  var responseFlags = responseBody['flags'];
18621
18649
  if (!responseFlags) {
@@ -18630,9 +18658,24 @@ FeatureFlagManager.prototype.fetchFlags = function() {
18630
18658
  });
18631
18659
  this.flags = flags;
18632
18660
  }.bind(this)).catch(function(error) {
18661
+ this.markFetchComplete();
18633
18662
  logger.error(error);
18634
- });
18635
- }.bind(this)).catch(function() {});
18663
+ }.bind(this));
18664
+ }.bind(this)).catch(function(error) {
18665
+ this.markFetchComplete();
18666
+ logger.error(error);
18667
+ }.bind(this));
18668
+ };
18669
+
18670
+ FeatureFlagManager.prototype.markFetchComplete = function() {
18671
+ if (!this._fetchInProgressStartTime) {
18672
+ logger.error('Fetch in progress started time not set, cannot mark fetch complete');
18673
+ return;
18674
+ }
18675
+ this._fetchStartTime = this._fetchInProgressStartTime;
18676
+ this._fetchCompleteTime = Date.now();
18677
+ this._fetchLatency = this._fetchCompleteTime - this._fetchStartTime;
18678
+ this._fetchInProgressStartTime = null;
18636
18679
  };
18637
18680
 
18638
18681
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
@@ -18711,7 +18754,10 @@ FeatureFlagManager.prototype.trackFeatureCheck = function(featureName, feature)
18711
18754
  this.track('$experiment_started', {
18712
18755
  'Experiment name': featureName,
18713
18756
  'Variant name': feature['key'],
18714
- '$experiment_type': 'feature_flag'
18757
+ '$experiment_type': 'feature_flag',
18758
+ 'Variant fetch start time': new Date(this._fetchStartTime).toISOString(),
18759
+ 'Variant fetch complete time': new Date(this._fetchCompleteTime).toISOString(),
18760
+ 'Variant fetch latency (ms)': this._fetchLatency
18715
18761
  });
18716
18762
  };
18717
18763
 
@@ -20411,8 +20457,11 @@ MixpanelLib.prototype._init = function(token, config, name) {
20411
20457
  }
20412
20458
 
20413
20459
  this.flags = new FeatureFlagManager({
20460
+ getFullApiRoute: _.bind(function() {
20461
+ return this.get_api_host('flags') + '/' + this.get_config('api_routes')['flags'];
20462
+ }, this),
20414
20463
  getConfigFunc: _.bind(this.get_config, this),
20415
- getDistinctIdFunc: _.bind(this.get_distinct_id, this),
20464
+ getPropertyFunc: _.bind(this.get_property, this),
20416
20465
  trackingFunc: _.bind(this.track, this)
20417
20466
  });
20418
20467
  this.flags.init();
@@ -20898,11 +20947,10 @@ MixpanelLib.prototype.are_batchers_initialized = function() {
20898
20947
 
20899
20948
  MixpanelLib.prototype.get_batcher_configs = function() {
20900
20949
  var queue_prefix = '__mpq_' + this.get_config('token');
20901
- var api_routes = this.get_config('api_routes');
20902
20950
  this._batcher_configs = this._batcher_configs || {
20903
- events: {type: 'events', endpoint: '/' + api_routes['track'], queue_key: queue_prefix + '_ev'},
20904
- people: {type: 'people', endpoint: '/' + api_routes['engage'], queue_key: queue_prefix + '_pp'},
20905
- groups: {type: 'groups', endpoint: '/' + api_routes['groups'], queue_key: queue_prefix + '_gr'}
20951
+ events: {type: 'events', api_name: 'track', queue_key: queue_prefix + '_ev'},
20952
+ people: {type: 'people', api_name: 'engage', queue_key: queue_prefix + '_pp'},
20953
+ groups: {type: 'groups', api_name: 'groups', queue_key: queue_prefix + '_gr'}
20906
20954
  };
20907
20955
  return this._batcher_configs;
20908
20956
  };
@@ -20916,8 +20964,9 @@ MixpanelLib.prototype.init_batchers = function() {
20916
20964
  libConfig: this['config'],
20917
20965
  errorReporter: this.get_config('error_reporter'),
20918
20966
  sendRequestFunc: _.bind(function(data, options, cb) {
20967
+ var api_routes = this.get_config('api_routes');
20919
20968
  this._send_request(
20920
- this.get_config('api_host') + attrs.endpoint,
20969
+ this.get_api_host(attrs.api_name) + '/' + api_routes[attrs.api_name],
20921
20970
  this._encode_data_for_request(data),
20922
20971
  options,
20923
20972
  this._prepare_callback(cb, data)
@@ -21645,31 +21694,15 @@ MixpanelLib.prototype.identify = function(
21645
21694
  * Useful for clearing data when a user logs out.
21646
21695
  */
21647
21696
  MixpanelLib.prototype.reset = function() {
21648
- var self = this;
21649
-
21650
- var reset = function () {
21651
- self['persistence'].clear();
21652
- self._flags.identify_called = false;
21653
- var uuid = _.UUID();
21654
- self.register_once({
21655
- 'distinct_id': DEVICE_ID_PREFIX + uuid,
21656
- '$device_id': uuid
21657
- }, '');
21658
- };
21659
-
21660
- if (self._recorder) {
21661
- self.stop_session_recording()
21662
- .then(function () {
21663
- reset();
21664
- self._check_and_start_session_recording();
21665
- })
21666
- .catch(_.bind(function (err) {
21667
- reset();
21668
- this.report_error('Error restarting recording session', err);
21669
- }, this));
21670
- } else {
21671
- reset();
21672
- }
21697
+ this.stop_session_recording();
21698
+ this['persistence'].clear();
21699
+ this._flags.identify_called = false;
21700
+ var uuid = _.UUID();
21701
+ this.register_once({
21702
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
21703
+ '$device_id': uuid
21704
+ }, '');
21705
+ this._check_and_start_session_recording();
21673
21706
  };
21674
21707
 
21675
21708
  /**
@@ -3,7 +3,7 @@
3
3
 
4
4
  var Config = {
5
5
  DEBUG: false,
6
- LIB_VERSION: '2.66.0'
6
+ LIB_VERSION: '2.67.0'
7
7
  };
8
8
 
9
9
  // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
@@ -3003,8 +3003,9 @@
3003
3003
  * @constructor
3004
3004
  */
3005
3005
  var FeatureFlagManager = function(initOptions) {
3006
+ this.getFullApiRoute = initOptions.getFullApiRoute;
3006
3007
  this.getMpConfig = initOptions.getConfigFunc;
3007
- this.getDistinctId = initOptions.getDistinctIdFunc;
3008
+ this.getMpProperty = initOptions.getPropertyFunc;
3008
3009
  this.track = initOptions.trackingFunc;
3009
3010
  };
3010
3011
 
@@ -3053,12 +3054,14 @@
3053
3054
  return;
3054
3055
  }
3055
3056
 
3056
- var distinctId = this.getDistinctId();
3057
+ var distinctId = this.getMpProperty('distinct_id');
3058
+ var deviceId = this.getMpProperty('$device_id');
3057
3059
  logger$3.log('Fetching flags for distinct ID: ' + distinctId);
3058
3060
  var reqParams = {
3059
- 'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
3061
+ 'context': _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT))
3060
3062
  };
3061
- this.fetchPromise = win['fetch'](this.getMpConfig('api_host') + '/' + this.getMpConfig('api_routes')['flags'], {
3063
+ this._fetchInProgressStartTime = Date.now();
3064
+ this.fetchPromise = win['fetch'](this.getFullApiRoute(), {
3062
3065
  'method': 'POST',
3063
3066
  'headers': {
3064
3067
  'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
@@ -3066,6 +3069,7 @@
3066
3069
  },
3067
3070
  'body': JSON.stringify(reqParams)
3068
3071
  }).then(function(response) {
3072
+ this.markFetchComplete();
3069
3073
  return response.json().then(function(responseBody) {
3070
3074
  var responseFlags = responseBody['flags'];
3071
3075
  if (!responseFlags) {
@@ -3080,9 +3084,24 @@
3080
3084
  });
3081
3085
  this.flags = flags;
3082
3086
  }.bind(this)).catch(function(error) {
3087
+ this.markFetchComplete();
3083
3088
  logger$3.error(error);
3084
- });
3085
- }.bind(this)).catch(function() {});
3089
+ }.bind(this));
3090
+ }.bind(this)).catch(function(error) {
3091
+ this.markFetchComplete();
3092
+ logger$3.error(error);
3093
+ }.bind(this));
3094
+ };
3095
+
3096
+ FeatureFlagManager.prototype.markFetchComplete = function() {
3097
+ if (!this._fetchInProgressStartTime) {
3098
+ logger$3.error('Fetch in progress started time not set, cannot mark fetch complete');
3099
+ return;
3100
+ }
3101
+ this._fetchStartTime = this._fetchInProgressStartTime;
3102
+ this._fetchCompleteTime = Date.now();
3103
+ this._fetchLatency = this._fetchCompleteTime - this._fetchStartTime;
3104
+ this._fetchInProgressStartTime = null;
3086
3105
  };
3087
3106
 
3088
3107
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
@@ -3161,7 +3180,10 @@
3161
3180
  this.track('$experiment_started', {
3162
3181
  'Experiment name': featureName,
3163
3182
  'Variant name': feature['key'],
3164
- '$experiment_type': 'feature_flag'
3183
+ '$experiment_type': 'feature_flag',
3184
+ 'Variant fetch start time': new Date(this._fetchStartTime).toISOString(),
3185
+ 'Variant fetch complete time': new Date(this._fetchCompleteTime).toISOString(),
3186
+ 'Variant fetch latency (ms)': this._fetchLatency
3165
3187
  });
3166
3188
  };
3167
3189
 
@@ -6172,8 +6194,11 @@
6172
6194
  }
6173
6195
 
6174
6196
  this.flags = new FeatureFlagManager({
6197
+ getFullApiRoute: _.bind(function() {
6198
+ return this.get_api_host('flags') + '/' + this.get_config('api_routes')['flags'];
6199
+ }, this),
6175
6200
  getConfigFunc: _.bind(this.get_config, this),
6176
- getDistinctIdFunc: _.bind(this.get_distinct_id, this),
6201
+ getPropertyFunc: _.bind(this.get_property, this),
6177
6202
  trackingFunc: _.bind(this.track, this)
6178
6203
  });
6179
6204
  this.flags.init();
@@ -6659,11 +6684,10 @@
6659
6684
 
6660
6685
  MixpanelLib.prototype.get_batcher_configs = function() {
6661
6686
  var queue_prefix = '__mpq_' + this.get_config('token');
6662
- var api_routes = this.get_config('api_routes');
6663
6687
  this._batcher_configs = this._batcher_configs || {
6664
- events: {type: 'events', endpoint: '/' + api_routes['track'], queue_key: queue_prefix + '_ev'},
6665
- people: {type: 'people', endpoint: '/' + api_routes['engage'], queue_key: queue_prefix + '_pp'},
6666
- groups: {type: 'groups', endpoint: '/' + api_routes['groups'], queue_key: queue_prefix + '_gr'}
6688
+ events: {type: 'events', api_name: 'track', queue_key: queue_prefix + '_ev'},
6689
+ people: {type: 'people', api_name: 'engage', queue_key: queue_prefix + '_pp'},
6690
+ groups: {type: 'groups', api_name: 'groups', queue_key: queue_prefix + '_gr'}
6667
6691
  };
6668
6692
  return this._batcher_configs;
6669
6693
  };
@@ -6677,8 +6701,9 @@
6677
6701
  libConfig: this['config'],
6678
6702
  errorReporter: this.get_config('error_reporter'),
6679
6703
  sendRequestFunc: _.bind(function(data, options, cb) {
6704
+ var api_routes = this.get_config('api_routes');
6680
6705
  this._send_request(
6681
- this.get_config('api_host') + attrs.endpoint,
6706
+ this.get_api_host(attrs.api_name) + '/' + api_routes[attrs.api_name],
6682
6707
  this._encode_data_for_request(data),
6683
6708
  options,
6684
6709
  this._prepare_callback(cb, data)
@@ -7406,31 +7431,15 @@
7406
7431
  * Useful for clearing data when a user logs out.
7407
7432
  */
7408
7433
  MixpanelLib.prototype.reset = function() {
7409
- var self = this;
7410
-
7411
- var reset = function () {
7412
- self['persistence'].clear();
7413
- self._flags.identify_called = false;
7414
- var uuid = _.UUID();
7415
- self.register_once({
7416
- 'distinct_id': DEVICE_ID_PREFIX + uuid,
7417
- '$device_id': uuid
7418
- }, '');
7419
- };
7420
-
7421
- if (self._recorder) {
7422
- self.stop_session_recording()
7423
- .then(function () {
7424
- reset();
7425
- self._check_and_start_session_recording();
7426
- })
7427
- .catch(_.bind(function (err) {
7428
- reset();
7429
- this.report_error('Error restarting recording session', err);
7430
- }, this));
7431
- } else {
7432
- reset();
7433
- }
7434
+ this.stop_session_recording();
7435
+ this['persistence'].clear();
7436
+ this._flags.identify_called = false;
7437
+ var uuid = _.UUID();
7438
+ this.register_once({
7439
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
7440
+ '$device_id': uuid
7441
+ }, '');
7442
+ this._check_and_start_session_recording();
7434
7443
  };
7435
7444
 
7436
7445
  /**