mixpanel-browser 2.45.0 → 2.46.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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ **2.46.0** (20 Mar 2023)
2
+ - Updates for new identity management system
3
+ - More aggressive deduplication within batch sender
4
+
1
5
  **2.45.0** (17 Feb 2022)
2
6
  - Remove all code related to in-app messaging feature
3
7
  - Add `error_reporter` config option for user-configurable handling of errors
@@ -5,7 +9,7 @@
5
9
  - Fixes for some batch/retry edge cases where localStorage write failures resulted in duplicate sends
6
10
 
7
11
  **2.43.0** (5 Jan 2022)
8
- - Support plain JSON tracking payloads (no base64-encoding) and use as default when sendinig to *.mixpanel.com API hosts
12
+ - Support plain JSON tracking payloads (no base64-encoding) and use as default when sending to *.mixpanel.com API hosts
9
13
 
10
14
  **2.42.1** (20 Dec 2021)
11
15
  - Add new crawler user agents to blocked list (ahrefsbot, petalbot)
package/README.md CHANGED
@@ -72,7 +72,7 @@ Mixpanel production releases are tested against a large matrix of browsers and o
72
72
  ## Generating and publishing documentation
73
73
  - Create bundled source build: `npm run build-dist`
74
74
  - Generate Markdown: `npm run dox` (result is at `doc/readme.io/javascript-full-api-reference.md`)
75
- - Publish to readme.io via the [rdme](https://www.npmjs.com/package/rdme) util: `RDME_API_KEY=<API_KEY> npm run dox-publish`
75
+ - Publish to readme.io via the [rdme](https://www.npmjs.com/package/rdme) util: `RDME_API_KEY=<API_KEY> RDME_DOC_VERSION=<version> npm run dox-publish`
76
76
 
77
77
  ## Thanks
78
78
  For patches and support: @bohanyang, @dehau, @drubin, @D1plo1d, @feychenie, @mogstad, @pfhayes, @sandorfr, @stefansedich, @gfx, @pkaminski, @austince, @danielbaker, @mkdai, @wolever, @dpraul, @chriszamierowski, @JoaoGomesTW
package/build.sh CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/bin/bash
2
2
 
3
+ set -e
4
+
3
5
  # building with $DIST=1 also implies $FULL=1
4
6
  if [ ! -z "$DIST" ]; then
5
7
  export FULL=1
@@ -2,7 +2,7 @@ define(function () { 'use strict';
2
2
 
3
3
  var Config = {
4
4
  DEBUG: false,
5
- LIB_VERSION: '2.45.0'
5
+ LIB_VERSION: '2.46.0'
6
6
  };
7
7
 
8
8
  // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
@@ -2295,6 +2295,9 @@ define(function () { 'use strict';
2295
2295
 
2296
2296
  this.stopped = !this.libConfig['batch_autostart'];
2297
2297
  this.consecutiveRemovalFailures = 0;
2298
+
2299
+ // extra client-side dedupe
2300
+ this.itemIdsSentSuccessfully = {};
2298
2301
  };
2299
2302
 
2300
2303
  /**
@@ -2387,7 +2390,34 @@ define(function () { 'use strict';
2387
2390
  payload = this.beforeSendHook(payload);
2388
2391
  }
2389
2392
  if (payload) {
2390
- dataForRequest.push(payload);
2393
+ // mp_sent_by_lib_version prop captures which lib version actually
2394
+ // sends each event (regardless of which version originally queued
2395
+ // it for sending)
2396
+ if (payload['event'] && payload['properties']) {
2397
+ payload['properties'] = _.extend(
2398
+ {},
2399
+ payload['properties'],
2400
+ {'mp_sent_by_lib_version': Config.LIB_VERSION}
2401
+ );
2402
+ }
2403
+ var addPayload = true;
2404
+ var itemId = item['id'];
2405
+ if (itemId) {
2406
+ if ((this.itemIdsSentSuccessfully[itemId] || 0) > 5) {
2407
+ this.reportError('[dupe] item ID sent too many times, not sending', {
2408
+ item: item,
2409
+ batchSize: batch.length,
2410
+ timesSent: this.itemIdsSentSuccessfully[itemId]
2411
+ });
2412
+ addPayload = false;
2413
+ }
2414
+ } else {
2415
+ this.reportError('[dupe] found item with no ID', {item: item});
2416
+ }
2417
+
2418
+ if (addPayload) {
2419
+ dataForRequest.push(payload);
2420
+ }
2391
2421
  }
2392
2422
  transformedItems[item['id']] = payload;
2393
2423
  }, this);
@@ -2470,6 +2500,24 @@ define(function () { 'use strict';
2470
2500
  }
2471
2501
  }, this)
2472
2502
  );
2503
+
2504
+ // client-side dedupe
2505
+ _.each(batch, _.bind(function(item) {
2506
+ var itemId = item['id'];
2507
+ if (itemId) {
2508
+ this.itemIdsSentSuccessfully[itemId] = this.itemIdsSentSuccessfully[itemId] || 0;
2509
+ this.itemIdsSentSuccessfully[itemId]++;
2510
+ if (this.itemIdsSentSuccessfully[itemId] > 5) {
2511
+ this.reportError('[dupe] item ID sent too many times', {
2512
+ item: item,
2513
+ batchSize: batch.length,
2514
+ timesSent: this.itemIdsSentSuccessfully[itemId]
2515
+ });
2516
+ }
2517
+ } else {
2518
+ this.reportError('[dupe] found item with no ID while removing', {item: item});
2519
+ }
2520
+ }, this));
2473
2521
  }
2474
2522
 
2475
2523
  } catch(err) {
@@ -3315,24 +3363,25 @@ define(function () { 'use strict';
3315
3363
  });
3316
3364
 
3317
3365
  /*
3318
- * Record that you have charged the current user a certain amount
3319
- * of money. Charges recorded with track_charge() will appear in the
3320
- * Mixpanel revenue report.
3321
- *
3322
- * ### Usage:
3323
- *
3324
- * // charge a user $50
3325
- * mixpanel.people.track_charge(50);
3326
- *
3327
- * // charge a user $30.50 on the 2nd of january
3328
- * mixpanel.people.track_charge(30.50, {
3329
- * '$time': new Date('jan 1 2012')
3330
- * });
3331
- *
3332
- * @param {Number} amount The amount of money charged to the current user
3333
- * @param {Object} [properties] An associative array of properties associated with the charge
3334
- * @param {Function} [callback] If provided, the callback will be called when the server responds
3335
- */
3366
+ * Record that you have charged the current user a certain amount
3367
+ * of money. Charges recorded with track_charge() will appear in the
3368
+ * Mixpanel revenue report.
3369
+ *
3370
+ * ### Usage:
3371
+ *
3372
+ * // charge a user $50
3373
+ * mixpanel.people.track_charge(50);
3374
+ *
3375
+ * // charge a user $30.50 on the 2nd of january
3376
+ * mixpanel.people.track_charge(30.50, {
3377
+ * '$time': new Date('jan 1 2012')
3378
+ * });
3379
+ *
3380
+ * @param {Number} amount The amount of money charged to the current user
3381
+ * @param {Object} [properties] An associative array of properties associated with the charge
3382
+ * @param {Function} [callback] If provided, the callback will be called when the server responds
3383
+ * @deprecated
3384
+ */
3336
3385
  MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(amount, properties, callback) {
3337
3386
  if (!_.isNumber(amount)) {
3338
3387
  amount = parseFloat(amount);
@@ -3348,15 +3397,16 @@ define(function () { 'use strict';
3348
3397
  });
3349
3398
 
3350
3399
  /*
3351
- * Permanently clear all revenue report transactions from the
3352
- * current user's people analytics profile.
3353
- *
3354
- * ### Usage:
3355
- *
3356
- * mixpanel.people.clear_charges();
3357
- *
3358
- * @param {Function} [callback] If provided, the callback will be called after tracking the event.
3359
- */
3400
+ * Permanently clear all revenue report transactions from the
3401
+ * current user's people analytics profile.
3402
+ *
3403
+ * ### Usage:
3404
+ *
3405
+ * mixpanel.people.clear_charges();
3406
+ *
3407
+ * @param {Function} [callback] If provided, the callback will be called after tracking the event.
3408
+ * @deprecated
3409
+ */
3360
3410
  MixpanelPeople.prototype.clear_charges = function(callback) {
3361
3411
  return this.set('$transactions', [], callback);
3362
3412
  };
@@ -4045,6 +4095,7 @@ define(function () { 'use strict';
4045
4095
  /** @const */ var PRIMARY_INSTANCE_NAME = 'mixpanel';
4046
4096
  /** @const */ var PAYLOAD_TYPE_BASE64 = 'base64';
4047
4097
  /** @const */ var PAYLOAD_TYPE_JSON = 'json';
4098
+ /** @const */ var DEVICE_ID_PREFIX = '$device:';
4048
4099
 
4049
4100
 
4050
4101
  /*
@@ -4292,7 +4343,7 @@ define(function () { 'use strict';
4292
4343
  // or the device id if something was already stored
4293
4344
  // in the persitence
4294
4345
  this.register_once({
4295
- 'distinct_id': uuid,
4346
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
4296
4347
  '$device_id': uuid
4297
4348
  }, '');
4298
4349
  }
@@ -5218,7 +5269,15 @@ define(function () { 'use strict';
5218
5269
  // _unset_callback:function A callback to be run if and when the People unset queue is flushed
5219
5270
 
5220
5271
  var previous_distinct_id = this.get_distinct_id();
5221
- this.register({'$user_id': new_distinct_id});
5272
+ if (new_distinct_id && previous_distinct_id !== new_distinct_id) {
5273
+ // we allow the following condition if previous distinct_id is same as new_distinct_id
5274
+ // so that you can force flush people updates for anonymous profiles.
5275
+ if (typeof new_distinct_id === 'string' && new_distinct_id.indexOf(DEVICE_ID_PREFIX) === 0) {
5276
+ this.report_error('distinct_id cannot have $device: prefix');
5277
+ return -1;
5278
+ }
5279
+ this.register({'$user_id': new_distinct_id});
5280
+ }
5222
5281
 
5223
5282
  if (!this.get_property('$device_id')) {
5224
5283
  // The persisted distinct id might not actually be a device id at all
@@ -5259,7 +5318,7 @@ define(function () { 'use strict';
5259
5318
  this._flags.identify_called = false;
5260
5319
  var uuid = _.UUID();
5261
5320
  this.register_once({
5262
- 'distinct_id': uuid,
5321
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
5263
5322
  '$device_id': uuid
5264
5323
  }, '');
5265
5324
  };
@@ -5384,8 +5443,8 @@ define(function () { 'use strict';
5384
5443
  * // batching or retry mechanisms.
5385
5444
  * api_transport: 'XHR'
5386
5445
  *
5387
- * // turn on request-batching/queueing/retry
5388
- * batch_requests: false,
5446
+ * // request-batching/queueing/retry
5447
+ * batch_requests: true,
5389
5448
  *
5390
5449
  * // maximum number of events/updates to send in a single
5391
5450
  * // network request
@@ -2,7 +2,7 @@
2
2
 
3
3
  var Config = {
4
4
  DEBUG: false,
5
- LIB_VERSION: '2.45.0'
5
+ LIB_VERSION: '2.46.0'
6
6
  };
7
7
 
8
8
  // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
@@ -2295,6 +2295,9 @@ var RequestBatcher = function(storageKey, options) {
2295
2295
 
2296
2296
  this.stopped = !this.libConfig['batch_autostart'];
2297
2297
  this.consecutiveRemovalFailures = 0;
2298
+
2299
+ // extra client-side dedupe
2300
+ this.itemIdsSentSuccessfully = {};
2298
2301
  };
2299
2302
 
2300
2303
  /**
@@ -2387,7 +2390,34 @@ RequestBatcher.prototype.flush = function(options) {
2387
2390
  payload = this.beforeSendHook(payload);
2388
2391
  }
2389
2392
  if (payload) {
2390
- dataForRequest.push(payload);
2393
+ // mp_sent_by_lib_version prop captures which lib version actually
2394
+ // sends each event (regardless of which version originally queued
2395
+ // it for sending)
2396
+ if (payload['event'] && payload['properties']) {
2397
+ payload['properties'] = _.extend(
2398
+ {},
2399
+ payload['properties'],
2400
+ {'mp_sent_by_lib_version': Config.LIB_VERSION}
2401
+ );
2402
+ }
2403
+ var addPayload = true;
2404
+ var itemId = item['id'];
2405
+ if (itemId) {
2406
+ if ((this.itemIdsSentSuccessfully[itemId] || 0) > 5) {
2407
+ this.reportError('[dupe] item ID sent too many times, not sending', {
2408
+ item: item,
2409
+ batchSize: batch.length,
2410
+ timesSent: this.itemIdsSentSuccessfully[itemId]
2411
+ });
2412
+ addPayload = false;
2413
+ }
2414
+ } else {
2415
+ this.reportError('[dupe] found item with no ID', {item: item});
2416
+ }
2417
+
2418
+ if (addPayload) {
2419
+ dataForRequest.push(payload);
2420
+ }
2391
2421
  }
2392
2422
  transformedItems[item['id']] = payload;
2393
2423
  }, this);
@@ -2470,6 +2500,24 @@ RequestBatcher.prototype.flush = function(options) {
2470
2500
  }
2471
2501
  }, this)
2472
2502
  );
2503
+
2504
+ // client-side dedupe
2505
+ _.each(batch, _.bind(function(item) {
2506
+ var itemId = item['id'];
2507
+ if (itemId) {
2508
+ this.itemIdsSentSuccessfully[itemId] = this.itemIdsSentSuccessfully[itemId] || 0;
2509
+ this.itemIdsSentSuccessfully[itemId]++;
2510
+ if (this.itemIdsSentSuccessfully[itemId] > 5) {
2511
+ this.reportError('[dupe] item ID sent too many times', {
2512
+ item: item,
2513
+ batchSize: batch.length,
2514
+ timesSent: this.itemIdsSentSuccessfully[itemId]
2515
+ });
2516
+ }
2517
+ } else {
2518
+ this.reportError('[dupe] found item with no ID while removing', {item: item});
2519
+ }
2520
+ }, this));
2473
2521
  }
2474
2522
 
2475
2523
  } catch(err) {
@@ -3315,24 +3363,25 @@ MixpanelPeople.prototype.union = addOptOutCheckMixpanelPeople(function(list_name
3315
3363
  });
3316
3364
 
3317
3365
  /*
3318
- * Record that you have charged the current user a certain amount
3319
- * of money. Charges recorded with track_charge() will appear in the
3320
- * Mixpanel revenue report.
3321
- *
3322
- * ### Usage:
3323
- *
3324
- * // charge a user $50
3325
- * mixpanel.people.track_charge(50);
3326
- *
3327
- * // charge a user $30.50 on the 2nd of january
3328
- * mixpanel.people.track_charge(30.50, {
3329
- * '$time': new Date('jan 1 2012')
3330
- * });
3331
- *
3332
- * @param {Number} amount The amount of money charged to the current user
3333
- * @param {Object} [properties] An associative array of properties associated with the charge
3334
- * @param {Function} [callback] If provided, the callback will be called when the server responds
3335
- */
3366
+ * Record that you have charged the current user a certain amount
3367
+ * of money. Charges recorded with track_charge() will appear in the
3368
+ * Mixpanel revenue report.
3369
+ *
3370
+ * ### Usage:
3371
+ *
3372
+ * // charge a user $50
3373
+ * mixpanel.people.track_charge(50);
3374
+ *
3375
+ * // charge a user $30.50 on the 2nd of january
3376
+ * mixpanel.people.track_charge(30.50, {
3377
+ * '$time': new Date('jan 1 2012')
3378
+ * });
3379
+ *
3380
+ * @param {Number} amount The amount of money charged to the current user
3381
+ * @param {Object} [properties] An associative array of properties associated with the charge
3382
+ * @param {Function} [callback] If provided, the callback will be called when the server responds
3383
+ * @deprecated
3384
+ */
3336
3385
  MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(amount, properties, callback) {
3337
3386
  if (!_.isNumber(amount)) {
3338
3387
  amount = parseFloat(amount);
@@ -3348,15 +3397,16 @@ MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(am
3348
3397
  });
3349
3398
 
3350
3399
  /*
3351
- * Permanently clear all revenue report transactions from the
3352
- * current user's people analytics profile.
3353
- *
3354
- * ### Usage:
3355
- *
3356
- * mixpanel.people.clear_charges();
3357
- *
3358
- * @param {Function} [callback] If provided, the callback will be called after tracking the event.
3359
- */
3400
+ * Permanently clear all revenue report transactions from the
3401
+ * current user's people analytics profile.
3402
+ *
3403
+ * ### Usage:
3404
+ *
3405
+ * mixpanel.people.clear_charges();
3406
+ *
3407
+ * @param {Function} [callback] If provided, the callback will be called after tracking the event.
3408
+ * @deprecated
3409
+ */
3360
3410
  MixpanelPeople.prototype.clear_charges = function(callback) {
3361
3411
  return this.set('$transactions', [], callback);
3362
3412
  };
@@ -4045,6 +4095,7 @@ var NOOP_FUNC = function() {};
4045
4095
  /** @const */ var PRIMARY_INSTANCE_NAME = 'mixpanel';
4046
4096
  /** @const */ var PAYLOAD_TYPE_BASE64 = 'base64';
4047
4097
  /** @const */ var PAYLOAD_TYPE_JSON = 'json';
4098
+ /** @const */ var DEVICE_ID_PREFIX = '$device:';
4048
4099
 
4049
4100
 
4050
4101
  /*
@@ -4292,7 +4343,7 @@ MixpanelLib.prototype._init = function(token, config, name) {
4292
4343
  // or the device id if something was already stored
4293
4344
  // in the persitence
4294
4345
  this.register_once({
4295
- 'distinct_id': uuid,
4346
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
4296
4347
  '$device_id': uuid
4297
4348
  }, '');
4298
4349
  }
@@ -5218,7 +5269,15 @@ MixpanelLib.prototype.identify = function(
5218
5269
  // _unset_callback:function A callback to be run if and when the People unset queue is flushed
5219
5270
 
5220
5271
  var previous_distinct_id = this.get_distinct_id();
5221
- this.register({'$user_id': new_distinct_id});
5272
+ if (new_distinct_id && previous_distinct_id !== new_distinct_id) {
5273
+ // we allow the following condition if previous distinct_id is same as new_distinct_id
5274
+ // so that you can force flush people updates for anonymous profiles.
5275
+ if (typeof new_distinct_id === 'string' && new_distinct_id.indexOf(DEVICE_ID_PREFIX) === 0) {
5276
+ this.report_error('distinct_id cannot have $device: prefix');
5277
+ return -1;
5278
+ }
5279
+ this.register({'$user_id': new_distinct_id});
5280
+ }
5222
5281
 
5223
5282
  if (!this.get_property('$device_id')) {
5224
5283
  // The persisted distinct id might not actually be a device id at all
@@ -5259,7 +5318,7 @@ MixpanelLib.prototype.reset = function() {
5259
5318
  this._flags.identify_called = false;
5260
5319
  var uuid = _.UUID();
5261
5320
  this.register_once({
5262
- 'distinct_id': uuid,
5321
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
5263
5322
  '$device_id': uuid
5264
5323
  }, '');
5265
5324
  };
@@ -5384,8 +5443,8 @@ MixpanelLib.prototype.name_tag = function(name_tag) {
5384
5443
  * // batching or retry mechanisms.
5385
5444
  * api_transport: 'XHR'
5386
5445
  *
5387
- * // turn on request-batching/queueing/retry
5388
- * batch_requests: false,
5446
+ * // request-batching/queueing/retry
5447
+ * batch_requests: true,
5389
5448
  *
5390
5449
  * // maximum number of events/updates to send in a single
5391
5450
  * // network request
@@ -3,7 +3,7 @@
3
3
 
4
4
  var Config = {
5
5
  DEBUG: false,
6
- LIB_VERSION: '2.45.0'
6
+ LIB_VERSION: '2.46.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
@@ -2296,6 +2296,9 @@
2296
2296
 
2297
2297
  this.stopped = !this.libConfig['batch_autostart'];
2298
2298
  this.consecutiveRemovalFailures = 0;
2299
+
2300
+ // extra client-side dedupe
2301
+ this.itemIdsSentSuccessfully = {};
2299
2302
  };
2300
2303
 
2301
2304
  /**
@@ -2388,7 +2391,34 @@
2388
2391
  payload = this.beforeSendHook(payload);
2389
2392
  }
2390
2393
  if (payload) {
2391
- dataForRequest.push(payload);
2394
+ // mp_sent_by_lib_version prop captures which lib version actually
2395
+ // sends each event (regardless of which version originally queued
2396
+ // it for sending)
2397
+ if (payload['event'] && payload['properties']) {
2398
+ payload['properties'] = _.extend(
2399
+ {},
2400
+ payload['properties'],
2401
+ {'mp_sent_by_lib_version': Config.LIB_VERSION}
2402
+ );
2403
+ }
2404
+ var addPayload = true;
2405
+ var itemId = item['id'];
2406
+ if (itemId) {
2407
+ if ((this.itemIdsSentSuccessfully[itemId] || 0) > 5) {
2408
+ this.reportError('[dupe] item ID sent too many times, not sending', {
2409
+ item: item,
2410
+ batchSize: batch.length,
2411
+ timesSent: this.itemIdsSentSuccessfully[itemId]
2412
+ });
2413
+ addPayload = false;
2414
+ }
2415
+ } else {
2416
+ this.reportError('[dupe] found item with no ID', {item: item});
2417
+ }
2418
+
2419
+ if (addPayload) {
2420
+ dataForRequest.push(payload);
2421
+ }
2392
2422
  }
2393
2423
  transformedItems[item['id']] = payload;
2394
2424
  }, this);
@@ -2471,6 +2501,24 @@
2471
2501
  }
2472
2502
  }, this)
2473
2503
  );
2504
+
2505
+ // client-side dedupe
2506
+ _.each(batch, _.bind(function(item) {
2507
+ var itemId = item['id'];
2508
+ if (itemId) {
2509
+ this.itemIdsSentSuccessfully[itemId] = this.itemIdsSentSuccessfully[itemId] || 0;
2510
+ this.itemIdsSentSuccessfully[itemId]++;
2511
+ if (this.itemIdsSentSuccessfully[itemId] > 5) {
2512
+ this.reportError('[dupe] item ID sent too many times', {
2513
+ item: item,
2514
+ batchSize: batch.length,
2515
+ timesSent: this.itemIdsSentSuccessfully[itemId]
2516
+ });
2517
+ }
2518
+ } else {
2519
+ this.reportError('[dupe] found item with no ID while removing', {item: item});
2520
+ }
2521
+ }, this));
2474
2522
  }
2475
2523
 
2476
2524
  } catch(err) {
@@ -3316,24 +3364,25 @@
3316
3364
  });
3317
3365
 
3318
3366
  /*
3319
- * Record that you have charged the current user a certain amount
3320
- * of money. Charges recorded with track_charge() will appear in the
3321
- * Mixpanel revenue report.
3322
- *
3323
- * ### Usage:
3324
- *
3325
- * // charge a user $50
3326
- * mixpanel.people.track_charge(50);
3327
- *
3328
- * // charge a user $30.50 on the 2nd of january
3329
- * mixpanel.people.track_charge(30.50, {
3330
- * '$time': new Date('jan 1 2012')
3331
- * });
3332
- *
3333
- * @param {Number} amount The amount of money charged to the current user
3334
- * @param {Object} [properties] An associative array of properties associated with the charge
3335
- * @param {Function} [callback] If provided, the callback will be called when the server responds
3336
- */
3367
+ * Record that you have charged the current user a certain amount
3368
+ * of money. Charges recorded with track_charge() will appear in the
3369
+ * Mixpanel revenue report.
3370
+ *
3371
+ * ### Usage:
3372
+ *
3373
+ * // charge a user $50
3374
+ * mixpanel.people.track_charge(50);
3375
+ *
3376
+ * // charge a user $30.50 on the 2nd of january
3377
+ * mixpanel.people.track_charge(30.50, {
3378
+ * '$time': new Date('jan 1 2012')
3379
+ * });
3380
+ *
3381
+ * @param {Number} amount The amount of money charged to the current user
3382
+ * @param {Object} [properties] An associative array of properties associated with the charge
3383
+ * @param {Function} [callback] If provided, the callback will be called when the server responds
3384
+ * @deprecated
3385
+ */
3337
3386
  MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(amount, properties, callback) {
3338
3387
  if (!_.isNumber(amount)) {
3339
3388
  amount = parseFloat(amount);
@@ -3349,15 +3398,16 @@
3349
3398
  });
3350
3399
 
3351
3400
  /*
3352
- * Permanently clear all revenue report transactions from the
3353
- * current user's people analytics profile.
3354
- *
3355
- * ### Usage:
3356
- *
3357
- * mixpanel.people.clear_charges();
3358
- *
3359
- * @param {Function} [callback] If provided, the callback will be called after tracking the event.
3360
- */
3401
+ * Permanently clear all revenue report transactions from the
3402
+ * current user's people analytics profile.
3403
+ *
3404
+ * ### Usage:
3405
+ *
3406
+ * mixpanel.people.clear_charges();
3407
+ *
3408
+ * @param {Function} [callback] If provided, the callback will be called after tracking the event.
3409
+ * @deprecated
3410
+ */
3361
3411
  MixpanelPeople.prototype.clear_charges = function(callback) {
3362
3412
  return this.set('$transactions', [], callback);
3363
3413
  };
@@ -4046,6 +4096,7 @@
4046
4096
  /** @const */ var PRIMARY_INSTANCE_NAME = 'mixpanel';
4047
4097
  /** @const */ var PAYLOAD_TYPE_BASE64 = 'base64';
4048
4098
  /** @const */ var PAYLOAD_TYPE_JSON = 'json';
4099
+ /** @const */ var DEVICE_ID_PREFIX = '$device:';
4049
4100
 
4050
4101
 
4051
4102
  /*
@@ -4293,7 +4344,7 @@
4293
4344
  // or the device id if something was already stored
4294
4345
  // in the persitence
4295
4346
  this.register_once({
4296
- 'distinct_id': uuid,
4347
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
4297
4348
  '$device_id': uuid
4298
4349
  }, '');
4299
4350
  }
@@ -5219,7 +5270,15 @@
5219
5270
  // _unset_callback:function A callback to be run if and when the People unset queue is flushed
5220
5271
 
5221
5272
  var previous_distinct_id = this.get_distinct_id();
5222
- this.register({'$user_id': new_distinct_id});
5273
+ if (new_distinct_id && previous_distinct_id !== new_distinct_id) {
5274
+ // we allow the following condition if previous distinct_id is same as new_distinct_id
5275
+ // so that you can force flush people updates for anonymous profiles.
5276
+ if (typeof new_distinct_id === 'string' && new_distinct_id.indexOf(DEVICE_ID_PREFIX) === 0) {
5277
+ this.report_error('distinct_id cannot have $device: prefix');
5278
+ return -1;
5279
+ }
5280
+ this.register({'$user_id': new_distinct_id});
5281
+ }
5223
5282
 
5224
5283
  if (!this.get_property('$device_id')) {
5225
5284
  // The persisted distinct id might not actually be a device id at all
@@ -5260,7 +5319,7 @@
5260
5319
  this._flags.identify_called = false;
5261
5320
  var uuid = _.UUID();
5262
5321
  this.register_once({
5263
- 'distinct_id': uuid,
5322
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
5264
5323
  '$device_id': uuid
5265
5324
  }, '');
5266
5325
  };
@@ -5385,8 +5444,8 @@
5385
5444
  * // batching or retry mechanisms.
5386
5445
  * api_transport: 'XHR'
5387
5446
  *
5388
- * // turn on request-batching/queueing/retry
5389
- * batch_requests: false,
5447
+ * // request-batching/queueing/retry
5448
+ * batch_requests: true,
5390
5449
  *
5391
5450
  * // maximum number of events/updates to send in a single
5392
5451
  * // network request