mixpanel-browser 2.65.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.65.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();
@@ -17351,8 +17385,8 @@ define((function () { 'use strict';
17351
17385
  retryAfter: response.headers.get('Retry-After')
17352
17386
  });
17353
17387
  }.bind(this);
17354
-
17355
- win['fetch'](this.getConfig('api_host') + '/' + this.getConfig('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
17388
+ var apiHost = (this._mixpanel.get_api_host && this._mixpanel.get_api_host('record')) || this.getConfig('api_host');
17389
+ win['fetch'](apiHost + '/' + this.getConfig('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
17356
17390
  'method': 'POST',
17357
17391
  'headers': {
17358
17392
  'Authorization': 'Basic ' + btoa(this.getConfig('token') + ':'),
@@ -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();
@@ -17569,6 +17593,7 @@ define((function () { 'use strict';
17569
17593
  this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
17570
17594
 
17571
17595
  this.activeRecording = null;
17596
+ this.stopRecordingInProgress = false;
17572
17597
  };
17573
17598
 
17574
17599
  MixpanelRecorder.prototype.startRecording = function(options) {
@@ -17617,19 +17642,26 @@ define((function () { 'use strict';
17617
17642
  };
17618
17643
 
17619
17644
  MixpanelRecorder.prototype.stopRecording = function() {
17620
- var stopPromise = this._stopCurrentRecording(false);
17621
- this.recordingRegistry.clearActiveRecording();
17622
- this.activeRecording = null;
17623
- return stopPromise;
17645
+ // Prevents activeSerializedRecording from being reused when stopping the recording.
17646
+ this.stopRecordingInProgress = true;
17647
+ return this._stopCurrentRecording(false, true).then(function() {
17648
+ return this.recordingRegistry.clearActiveRecording();
17649
+ }.bind(this)).then(function() {
17650
+ this.stopRecordingInProgress = false;
17651
+ }.bind(this));
17624
17652
  };
17625
17653
 
17626
17654
  MixpanelRecorder.prototype.pauseRecording = function() {
17627
17655
  return this._stopCurrentRecording(false);
17628
17656
  };
17629
17657
 
17630
- MixpanelRecorder.prototype._stopCurrentRecording = function(skipFlush) {
17658
+ MixpanelRecorder.prototype._stopCurrentRecording = function(skipFlush, disableActiveRecording) {
17631
17659
  if (this.activeRecording) {
17632
- return this.activeRecording.stopRecording(skipFlush);
17660
+ var stopRecordingPromise = this.activeRecording.stopRecording(skipFlush);
17661
+ if (disableActiveRecording) {
17662
+ this.activeRecording = null;
17663
+ }
17664
+ return stopRecordingPromise;
17633
17665
  }
17634
17666
  return PromisePolyfill.resolve();
17635
17667
  };
@@ -17642,7 +17674,7 @@ define((function () { 'use strict';
17642
17674
 
17643
17675
  return this.recordingRegistry.getActiveRecording()
17644
17676
  .then(function (activeSerializedRecording) {
17645
- if (activeSerializedRecording) {
17677
+ if (activeSerializedRecording && !this.stopRecordingInProgress) {
17646
17678
  return this.startRecording({activeSerializedRecording: activeSerializedRecording});
17647
17679
  } else if (startNewIfInactive) {
17648
17680
  return this.startRecording({shouldStopBatcher: false});
@@ -18545,8 +18577,9 @@ define((function () { 'use strict';
18545
18577
  * @constructor
18546
18578
  */
18547
18579
  var FeatureFlagManager = function(initOptions) {
18580
+ this.getFullApiRoute = initOptions.getFullApiRoute;
18548
18581
  this.getMpConfig = initOptions.getConfigFunc;
18549
- this.getDistinctId = initOptions.getDistinctIdFunc;
18582
+ this.getMpProperty = initOptions.getPropertyFunc;
18550
18583
  this.track = initOptions.trackingFunc;
18551
18584
  };
18552
18585
 
@@ -18595,12 +18628,14 @@ define((function () { 'use strict';
18595
18628
  return;
18596
18629
  }
18597
18630
 
18598
- var distinctId = this.getDistinctId();
18631
+ var distinctId = this.getMpProperty('distinct_id');
18632
+ var deviceId = this.getMpProperty('$device_id');
18599
18633
  logger.log('Fetching flags for distinct ID: ' + distinctId);
18600
18634
  var reqParams = {
18601
- 'context': _.extend({'distinct_id': distinctId}, this.getConfig(CONFIG_CONTEXT))
18635
+ 'context': _.extend({'distinct_id': distinctId, 'device_id': deviceId}, this.getConfig(CONFIG_CONTEXT))
18602
18636
  };
18603
- 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(), {
18604
18639
  'method': 'POST',
18605
18640
  'headers': {
18606
18641
  'Authorization': 'Basic ' + btoa(this.getMpConfig('token') + ':'),
@@ -18608,6 +18643,7 @@ define((function () { 'use strict';
18608
18643
  },
18609
18644
  'body': JSON.stringify(reqParams)
18610
18645
  }).then(function(response) {
18646
+ this.markFetchComplete();
18611
18647
  return response.json().then(function(responseBody) {
18612
18648
  var responseFlags = responseBody['flags'];
18613
18649
  if (!responseFlags) {
@@ -18622,9 +18658,24 @@ define((function () { 'use strict';
18622
18658
  });
18623
18659
  this.flags = flags;
18624
18660
  }.bind(this)).catch(function(error) {
18661
+ this.markFetchComplete();
18625
18662
  logger.error(error);
18626
- });
18627
- }.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;
18628
18679
  };
18629
18680
 
18630
18681
  FeatureFlagManager.prototype.getVariant = function(featureName, fallback) {
@@ -18703,7 +18754,10 @@ define((function () { 'use strict';
18703
18754
  this.track('$experiment_started', {
18704
18755
  'Experiment name': featureName,
18705
18756
  'Variant name': feature['key'],
18706
- '$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
18707
18761
  });
18708
18762
  };
18709
18763
 
@@ -19144,7 +19198,7 @@ define((function () { 'use strict';
19144
19198
  return this._mixpanel._track_or_batch({
19145
19199
  type: 'groups',
19146
19200
  data: date_encoded_data,
19147
- endpoint: this._get_config('api_host') + '/' + this._get_config('api_routes')['groups'],
19201
+ endpoint: this._mixpanel.get_api_host('groups') + '/' + this._get_config('api_routes')['groups'],
19148
19202
  batcher: this._mixpanel.request_batchers.groups
19149
19203
  }, callback);
19150
19204
  };
@@ -19496,7 +19550,7 @@ define((function () { 'use strict';
19496
19550
  return this._mixpanel._track_or_batch({
19497
19551
  type: 'people',
19498
19552
  data: date_encoded_data,
19499
- endpoint: this._get_config('api_host') + '/' + this._get_config('api_routes')['engage'],
19553
+ endpoint: this._mixpanel.get_api_host('people') + '/' + this._get_config('api_routes')['engage'],
19500
19554
  batcher: this._mixpanel.request_batchers.people
19501
19555
  }, callback);
19502
19556
  };
@@ -20133,6 +20187,7 @@ define((function () { 'use strict';
20133
20187
  */
20134
20188
  var DEFAULT_CONFIG = {
20135
20189
  'api_host': 'https://api-js.mixpanel.com',
20190
+ 'api_hosts': {},
20136
20191
  'api_routes': DEFAULT_API_ROUTES,
20137
20192
  'api_extra_query_params': {},
20138
20193
  'api_method': 'POST',
@@ -20402,8 +20457,11 @@ define((function () { 'use strict';
20402
20457
  }
20403
20458
 
20404
20459
  this.flags = new FeatureFlagManager({
20460
+ getFullApiRoute: _.bind(function() {
20461
+ return this.get_api_host('flags') + '/' + this.get_config('api_routes')['flags'];
20462
+ }, this),
20405
20463
  getConfigFunc: _.bind(this.get_config, this),
20406
- getDistinctIdFunc: _.bind(this.get_distinct_id, this),
20464
+ getPropertyFunc: _.bind(this.get_property, this),
20407
20465
  trackingFunc: _.bind(this.track, this)
20408
20466
  });
20409
20467
  this.flags.init();
@@ -20518,20 +20576,23 @@ define((function () { 'use strict';
20518
20576
 
20519
20577
  MixpanelLib.prototype.stop_session_recording = function () {
20520
20578
  if (this._recorder) {
20521
- this._recorder['stopRecording']();
20579
+ return this._recorder['stopRecording']();
20522
20580
  }
20581
+ return Promise.resolve();
20523
20582
  };
20524
20583
 
20525
20584
  MixpanelLib.prototype.pause_session_recording = function () {
20526
20585
  if (this._recorder) {
20527
- this._recorder['pauseRecording']();
20586
+ return this._recorder['pauseRecording']();
20528
20587
  }
20588
+ return Promise.resolve();
20529
20589
  };
20530
20590
 
20531
20591
  MixpanelLib.prototype.resume_session_recording = function () {
20532
20592
  if (this._recorder) {
20533
- this._recorder['resumeRecording']();
20593
+ return this._recorder['resumeRecording']();
20534
20594
  }
20595
+ return Promise.resolve();
20535
20596
  };
20536
20597
 
20537
20598
  MixpanelLib.prototype.is_recording_heatmap_data = function () {
@@ -20886,11 +20947,10 @@ define((function () { 'use strict';
20886
20947
 
20887
20948
  MixpanelLib.prototype.get_batcher_configs = function() {
20888
20949
  var queue_prefix = '__mpq_' + this.get_config('token');
20889
- var api_routes = this.get_config('api_routes');
20890
20950
  this._batcher_configs = this._batcher_configs || {
20891
- events: {type: 'events', endpoint: '/' + api_routes['track'], queue_key: queue_prefix + '_ev'},
20892
- people: {type: 'people', endpoint: '/' + api_routes['engage'], queue_key: queue_prefix + '_pp'},
20893
- 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'}
20894
20954
  };
20895
20955
  return this._batcher_configs;
20896
20956
  };
@@ -20904,8 +20964,9 @@ define((function () { 'use strict';
20904
20964
  libConfig: this['config'],
20905
20965
  errorReporter: this.get_config('error_reporter'),
20906
20966
  sendRequestFunc: _.bind(function(data, options, cb) {
20967
+ var api_routes = this.get_config('api_routes');
20907
20968
  this._send_request(
20908
- this.get_config('api_host') + attrs.endpoint,
20969
+ this.get_api_host(attrs.api_name) + '/' + api_routes[attrs.api_name],
20909
20970
  this._encode_data_for_request(data),
20910
20971
  options,
20911
20972
  this._prepare_callback(cb, data)
@@ -21131,7 +21192,7 @@ define((function () { 'use strict';
21131
21192
  var ret = this._track_or_batch({
21132
21193
  type: 'events',
21133
21194
  data: data,
21134
- endpoint: this.get_config('api_host') + '/' + this.get_config('api_routes')['track'],
21195
+ endpoint: this.get_api_host('events') + '/' + this.get_config('api_routes')['track'],
21135
21196
  batcher: this.request_batchers.events,
21136
21197
  should_send_immediately: should_send_immediately,
21137
21198
  send_request_options: options
@@ -21633,6 +21694,7 @@ define((function () { 'use strict';
21633
21694
  * Useful for clearing data when a user logs out.
21634
21695
  */
21635
21696
  MixpanelLib.prototype.reset = function() {
21697
+ this.stop_session_recording();
21636
21698
  this['persistence'].clear();
21637
21699
  this._flags.identify_called = false;
21638
21700
  var uuid = _.UUID();
@@ -21640,7 +21702,6 @@ define((function () { 'use strict';
21640
21702
  'distinct_id': DEVICE_ID_PREFIX + uuid,
21641
21703
  '$device_id': uuid
21642
21704
  }, '');
21643
- this.stop_session_recording();
21644
21705
  this._check_and_start_session_recording();
21645
21706
  };
21646
21707
 
@@ -21952,6 +22013,16 @@ define((function () { 'use strict';
21952
22013
  return this['persistence'].load_prop([property_name]);
21953
22014
  };
21954
22015
 
22016
+ /**
22017
+ * Get the API host for a specific endpoint type, falling back to the default api_host if not specified
22018
+ *
22019
+ * @param {String} endpoint_type The type of endpoint (e.g., "events", "people", "groups")
22020
+ * @returns {String} The API host to use for this endpoint
22021
+ */
22022
+ MixpanelLib.prototype.get_api_host = function(endpoint_type) {
22023
+ return this.get_config('api_hosts')[endpoint_type] || this.get_config('api_host');
22024
+ };
22025
+
21955
22026
  MixpanelLib.prototype.toString = function() {
21956
22027
  var name = this.get_config('name');
21957
22028
  if (name !== PRIMARY_INSTANCE_NAME) {
@@ -22247,6 +22318,7 @@ define((function () { 'use strict';
22247
22318
  MixpanelLib.prototype['name_tag'] = MixpanelLib.prototype.name_tag;
22248
22319
  MixpanelLib.prototype['set_config'] = MixpanelLib.prototype.set_config;
22249
22320
  MixpanelLib.prototype['get_config'] = MixpanelLib.prototype.get_config;
22321
+ MixpanelLib.prototype['get_api_host'] = MixpanelLib.prototype.get_api_host;
22250
22322
  MixpanelLib.prototype['get_property'] = MixpanelLib.prototype.get_property;
22251
22323
  MixpanelLib.prototype['get_distinct_id'] = MixpanelLib.prototype.get_distinct_id;
22252
22324
  MixpanelLib.prototype['toString'] = MixpanelLib.prototype.toString;