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.
@@ -6,7 +6,7 @@
6
6
 
7
7
  var Config = {
8
8
  DEBUG: false,
9
- LIB_VERSION: '2.45.0'
9
+ LIB_VERSION: '2.46.0'
10
10
  };
11
11
 
12
12
  // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
@@ -2299,6 +2299,9 @@
2299
2299
 
2300
2300
  this.stopped = !this.libConfig['batch_autostart'];
2301
2301
  this.consecutiveRemovalFailures = 0;
2302
+
2303
+ // extra client-side dedupe
2304
+ this.itemIdsSentSuccessfully = {};
2302
2305
  };
2303
2306
 
2304
2307
  /**
@@ -2391,7 +2394,34 @@
2391
2394
  payload = this.beforeSendHook(payload);
2392
2395
  }
2393
2396
  if (payload) {
2394
- dataForRequest.push(payload);
2397
+ // mp_sent_by_lib_version prop captures which lib version actually
2398
+ // sends each event (regardless of which version originally queued
2399
+ // it for sending)
2400
+ if (payload['event'] && payload['properties']) {
2401
+ payload['properties'] = _.extend(
2402
+ {},
2403
+ payload['properties'],
2404
+ {'mp_sent_by_lib_version': Config.LIB_VERSION}
2405
+ );
2406
+ }
2407
+ var addPayload = true;
2408
+ var itemId = item['id'];
2409
+ if (itemId) {
2410
+ if ((this.itemIdsSentSuccessfully[itemId] || 0) > 5) {
2411
+ this.reportError('[dupe] item ID sent too many times, not sending', {
2412
+ item: item,
2413
+ batchSize: batch.length,
2414
+ timesSent: this.itemIdsSentSuccessfully[itemId]
2415
+ });
2416
+ addPayload = false;
2417
+ }
2418
+ } else {
2419
+ this.reportError('[dupe] found item with no ID', {item: item});
2420
+ }
2421
+
2422
+ if (addPayload) {
2423
+ dataForRequest.push(payload);
2424
+ }
2395
2425
  }
2396
2426
  transformedItems[item['id']] = payload;
2397
2427
  }, this);
@@ -2474,6 +2504,24 @@
2474
2504
  }
2475
2505
  }, this)
2476
2506
  );
2507
+
2508
+ // client-side dedupe
2509
+ _.each(batch, _.bind(function(item) {
2510
+ var itemId = item['id'];
2511
+ if (itemId) {
2512
+ this.itemIdsSentSuccessfully[itemId] = this.itemIdsSentSuccessfully[itemId] || 0;
2513
+ this.itemIdsSentSuccessfully[itemId]++;
2514
+ if (this.itemIdsSentSuccessfully[itemId] > 5) {
2515
+ this.reportError('[dupe] item ID sent too many times', {
2516
+ item: item,
2517
+ batchSize: batch.length,
2518
+ timesSent: this.itemIdsSentSuccessfully[itemId]
2519
+ });
2520
+ }
2521
+ } else {
2522
+ this.reportError('[dupe] found item with no ID while removing', {item: item});
2523
+ }
2524
+ }, this));
2477
2525
  }
2478
2526
 
2479
2527
  } catch(err) {
@@ -3319,24 +3367,25 @@
3319
3367
  });
3320
3368
 
3321
3369
  /*
3322
- * Record that you have charged the current user a certain amount
3323
- * of money. Charges recorded with track_charge() will appear in the
3324
- * Mixpanel revenue report.
3325
- *
3326
- * ### Usage:
3327
- *
3328
- * // charge a user $50
3329
- * mixpanel.people.track_charge(50);
3330
- *
3331
- * // charge a user $30.50 on the 2nd of january
3332
- * mixpanel.people.track_charge(30.50, {
3333
- * '$time': new Date('jan 1 2012')
3334
- * });
3335
- *
3336
- * @param {Number} amount The amount of money charged to the current user
3337
- * @param {Object} [properties] An associative array of properties associated with the charge
3338
- * @param {Function} [callback] If provided, the callback will be called when the server responds
3339
- */
3370
+ * Record that you have charged the current user a certain amount
3371
+ * of money. Charges recorded with track_charge() will appear in the
3372
+ * Mixpanel revenue report.
3373
+ *
3374
+ * ### Usage:
3375
+ *
3376
+ * // charge a user $50
3377
+ * mixpanel.people.track_charge(50);
3378
+ *
3379
+ * // charge a user $30.50 on the 2nd of january
3380
+ * mixpanel.people.track_charge(30.50, {
3381
+ * '$time': new Date('jan 1 2012')
3382
+ * });
3383
+ *
3384
+ * @param {Number} amount The amount of money charged to the current user
3385
+ * @param {Object} [properties] An associative array of properties associated with the charge
3386
+ * @param {Function} [callback] If provided, the callback will be called when the server responds
3387
+ * @deprecated
3388
+ */
3340
3389
  MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(amount, properties, callback) {
3341
3390
  if (!_.isNumber(amount)) {
3342
3391
  amount = parseFloat(amount);
@@ -3352,15 +3401,16 @@
3352
3401
  });
3353
3402
 
3354
3403
  /*
3355
- * Permanently clear all revenue report transactions from the
3356
- * current user's people analytics profile.
3357
- *
3358
- * ### Usage:
3359
- *
3360
- * mixpanel.people.clear_charges();
3361
- *
3362
- * @param {Function} [callback] If provided, the callback will be called after tracking the event.
3363
- */
3404
+ * Permanently clear all revenue report transactions from the
3405
+ * current user's people analytics profile.
3406
+ *
3407
+ * ### Usage:
3408
+ *
3409
+ * mixpanel.people.clear_charges();
3410
+ *
3411
+ * @param {Function} [callback] If provided, the callback will be called after tracking the event.
3412
+ * @deprecated
3413
+ */
3364
3414
  MixpanelPeople.prototype.clear_charges = function(callback) {
3365
3415
  return this.set('$transactions', [], callback);
3366
3416
  };
@@ -4049,6 +4099,7 @@
4049
4099
  /** @const */ var PRIMARY_INSTANCE_NAME = 'mixpanel';
4050
4100
  /** @const */ var PAYLOAD_TYPE_BASE64 = 'base64';
4051
4101
  /** @const */ var PAYLOAD_TYPE_JSON = 'json';
4102
+ /** @const */ var DEVICE_ID_PREFIX = '$device:';
4052
4103
 
4053
4104
 
4054
4105
  /*
@@ -4296,7 +4347,7 @@
4296
4347
  // or the device id if something was already stored
4297
4348
  // in the persitence
4298
4349
  this.register_once({
4299
- 'distinct_id': uuid,
4350
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
4300
4351
  '$device_id': uuid
4301
4352
  }, '');
4302
4353
  }
@@ -5222,7 +5273,15 @@
5222
5273
  // _unset_callback:function A callback to be run if and when the People unset queue is flushed
5223
5274
 
5224
5275
  var previous_distinct_id = this.get_distinct_id();
5225
- this.register({'$user_id': new_distinct_id});
5276
+ if (new_distinct_id && previous_distinct_id !== new_distinct_id) {
5277
+ // we allow the following condition if previous distinct_id is same as new_distinct_id
5278
+ // so that you can force flush people updates for anonymous profiles.
5279
+ if (typeof new_distinct_id === 'string' && new_distinct_id.indexOf(DEVICE_ID_PREFIX) === 0) {
5280
+ this.report_error('distinct_id cannot have $device: prefix');
5281
+ return -1;
5282
+ }
5283
+ this.register({'$user_id': new_distinct_id});
5284
+ }
5226
5285
 
5227
5286
  if (!this.get_property('$device_id')) {
5228
5287
  // The persisted distinct id might not actually be a device id at all
@@ -5263,7 +5322,7 @@
5263
5322
  this._flags.identify_called = false;
5264
5323
  var uuid = _.UUID();
5265
5324
  this.register_once({
5266
- 'distinct_id': uuid,
5325
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
5267
5326
  '$device_id': uuid
5268
5327
  }, '');
5269
5328
  };
@@ -5388,8 +5447,8 @@
5388
5447
  * // batching or retry mechanisms.
5389
5448
  * api_transport: 'XHR'
5390
5449
  *
5391
- * // turn on request-batching/queueing/retry
5392
- * batch_requests: false,
5450
+ * // request-batching/queueing/retry
5451
+ * batch_requests: true,
5393
5452
  *
5394
5453
  * // maximum number of events/updates to send in a single
5395
5454
  * // network request
@@ -1,5 +1,5 @@
1
1
  ---
2
- category: 5ae0c2c9fa0ec6000345c0ac
2
+ category: 62ec0192a94ae90a45602b13
3
3
  title: JavaScript Full API Reference
4
4
  ---
5
5
 
@@ -450,8 +450,8 @@ The default config is:
450
450
  // batching or retry mechanisms.
451
451
  api_transport: 'XHR'
452
452
 
453
- // turn on request-batching/queueing/retry
454
- batch_requests: false,
453
+ // request-batching/queueing/retry
454
+ batch_requests: true,
455
455
 
456
456
  // maximum number of events/updates to send in a single
457
457
  // network request
@@ -538,14 +538,6 @@ The default config is:
538
538
  // the format {'Header-Name': value}
539
539
  xhr_headers: {}
540
540
 
541
- // protocol for fetching in-app message resources, e.g.
542
- // 'https://' or 'http://'; defaults to '//' (which defers to the
543
- // current page's protocol)
544
- inapp_protocol: '//'
545
-
546
- // whether to open in-app message link in new tab/window
547
- inapp_link_new_window: false
548
-
549
541
  // whether to ignore or respect the web browser's Do Not Track setting
550
542
  ignore_dnt: false
551
543
  }
@@ -761,24 +753,6 @@ mixpanel.people.append({
761
753
  | **callback** | <span class="mp-arg-type">Function</span></br></span><span class="mp-arg-optional">optional</span> | If provided, the callback will be called after tracking the event. |
762
754
 
763
755
 
764
- ___
765
- ## mixpanel.people.clear_charges
766
- Permanently clear all revenue report transactions from the current user's people analytics profile.
767
-
768
-
769
- ### Usage:
770
-
771
- ```javascript
772
- mixpanel.people.clear_charges();
773
- ```
774
-
775
-
776
-
777
- | Argument | Type | Description |
778
- | ------------- | ------------- | ----- |
779
- | **callback** | <span class="mp-arg-type">Function</span></br></span><span class="mp-arg-optional">optional</span> | If provided, the callback will be called after tracking the event. |
780
-
781
-
782
756
  ___
783
757
  ## mixpanel.people.delete_user
784
758
  Permanently deletes the current people analytics profile from Mixpanel (using the current distinct_id).
@@ -904,32 +878,6 @@ mixpanel.people.set_once({
904
878
  | **callback** | <span class="mp-arg-type">Function</span></br></span><span class="mp-arg-optional">optional</span> | If provided, the callback will be called after tracking the event. |
905
879
 
906
880
 
907
- ___
908
- ## mixpanel.people.track_charge
909
- Record that you have charged the current user a certain amount of money. Charges recorded with track_charge() will appear in the Mixpanel revenue report.
910
-
911
-
912
- ### Usage:
913
-
914
- ```javascript
915
- // charge a user $50
916
- mixpanel.people.track_charge(50);
917
-
918
- // charge a user $30.50 on the 2nd of january
919
- mixpanel.people.track_charge(30.50, {
920
- '$time': new Date('jan 1 2012')
921
- });
922
- ```
923
-
924
-
925
-
926
- | Argument | Type | Description |
927
- | ------------- | ------------- | ----- |
928
- | **amount** | <span class="mp-arg-type">Number</span></br></span><span class="mp-arg-required">required</span> | The amount of money charged to the current user |
929
- | **properties** | <span class="mp-arg-type">Object</span></br></span><span class="mp-arg-optional">optional</span> | An associative array of properties associated with the charge |
930
- | **callback** | <span class="mp-arg-type">Function</span></br></span><span class="mp-arg-optional">optional</span> | If provided, the callback will be called when the server responds |
931
-
932
-
933
881
  ___
934
882
  ## mixpanel.people.union
935
883
  Merge a given list with a list-valued people analytics property, excluding duplicate values.
package/doc/template.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- category: 5ae0c2c9fa0ec6000345c0ac
2
+ category: 62ec0192a94ae90a45602b13
3
3
  title: JavaScript Full API Reference
4
4
  ---
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.45.0",
3
+ "version": "2.46.0",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "directories": {
@@ -12,7 +12,7 @@
12
12
  "build-full": "FULL=1 ./build.sh",
13
13
  "build-test-polyfill": "webpack tests/vendor/core-js-polyfills.src.js tests/vendor/core-js-polyfills.js",
14
14
  "dox": "node doc/build-docs.js",
15
- "dox-publish": "rdme docs doc/readme.io --key=$RDME_API_KEY --version=1.0",
15
+ "dox-publish": "rdme docs:single doc/readme.io/javascript-full-api-reference.md --key=$RDME_API_KEY --version=$RDME_DOC_VERSION",
16
16
  "integration_test": "echo 'Browse to localhost:3000/tests' && node testServer.js",
17
17
  "lint": "eslint ./src",
18
18
  "prepublishOnly": "npm run build-dist",
@@ -37,19 +37,19 @@
37
37
  "babel-preset-es2015": "6.6.0",
38
38
  "babelify": "6.1.2",
39
39
  "browserify": "10.2.4",
40
- "chai": "3.5.0",
40
+ "chai": "4.0.0",
41
41
  "cookie-parser": "1.3.4",
42
42
  "core-js": "3.6.5",
43
43
  "dox": "0.9.0",
44
44
  "eslint": "4.18.2",
45
45
  "express": "4.12.2",
46
- "jsdom": "11.12.0",
46
+ "jsdom": "16.5.0",
47
47
  "jsdom-global": "3.0.2",
48
48
  "localStorage": "1.0.4",
49
49
  "lodash": "4.17.21",
50
50
  "mocha": "7.1.1",
51
51
  "morgan": "1.9.1",
52
- "rdme": "4.0.0",
52
+ "rdme": "7.5.0",
53
53
  "request": "2.88.0",
54
54
  "rollup": "0.25.8",
55
55
  "rollup-plugin-npm": "1.4.0",
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  var Config = {
2
2
  DEBUG: false,
3
- LIB_VERSION: '2.45.0'
3
+ LIB_VERSION: '2.46.0'
4
4
  };
5
5
 
6
6
  export default Config;
@@ -57,6 +57,7 @@ var NOOP_FUNC = function() {};
57
57
  /** @const */ var PRIMARY_INSTANCE_NAME = 'mixpanel';
58
58
  /** @const */ var PAYLOAD_TYPE_BASE64 = 'base64';
59
59
  /** @const */ var PAYLOAD_TYPE_JSON = 'json';
60
+ /** @const */ var DEVICE_ID_PREFIX = '$device:';
60
61
 
61
62
 
62
63
  /*
@@ -304,7 +305,7 @@ MixpanelLib.prototype._init = function(token, config, name) {
304
305
  // or the device id if something was already stored
305
306
  // in the persitence
306
307
  this.register_once({
307
- 'distinct_id': uuid,
308
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
308
309
  '$device_id': uuid
309
310
  }, '');
310
311
  }
@@ -1230,7 +1231,15 @@ MixpanelLib.prototype.identify = function(
1230
1231
  // _unset_callback:function A callback to be run if and when the People unset queue is flushed
1231
1232
 
1232
1233
  var previous_distinct_id = this.get_distinct_id();
1233
- this.register({'$user_id': new_distinct_id});
1234
+ if (new_distinct_id && previous_distinct_id !== new_distinct_id) {
1235
+ // we allow the following condition if previous distinct_id is same as new_distinct_id
1236
+ // so that you can force flush people updates for anonymous profiles.
1237
+ if (typeof new_distinct_id === 'string' && new_distinct_id.indexOf(DEVICE_ID_PREFIX) === 0) {
1238
+ this.report_error('distinct_id cannot have $device: prefix');
1239
+ return -1;
1240
+ }
1241
+ this.register({'$user_id': new_distinct_id});
1242
+ }
1234
1243
 
1235
1244
  if (!this.get_property('$device_id')) {
1236
1245
  // The persisted distinct id might not actually be a device id at all
@@ -1271,7 +1280,7 @@ MixpanelLib.prototype.reset = function() {
1271
1280
  this._flags.identify_called = false;
1272
1281
  var uuid = _.UUID();
1273
1282
  this.register_once({
1274
- 'distinct_id': uuid,
1283
+ 'distinct_id': DEVICE_ID_PREFIX + uuid,
1275
1284
  '$device_id': uuid
1276
1285
  }, '');
1277
1286
  };
@@ -1396,8 +1405,8 @@ MixpanelLib.prototype.name_tag = function(name_tag) {
1396
1405
  * // batching or retry mechanisms.
1397
1406
  * api_transport: 'XHR'
1398
1407
  *
1399
- * // turn on request-batching/queueing/retry
1400
- * batch_requests: false,
1408
+ * // request-batching/queueing/retry
1409
+ * batch_requests: true,
1401
1410
  *
1402
1411
  * // maximum number of events/updates to send in a single
1403
1412
  * // network request
@@ -244,24 +244,25 @@ MixpanelPeople.prototype.union = addOptOutCheckMixpanelPeople(function(list_name
244
244
  });
245
245
 
246
246
  /*
247
- * Record that you have charged the current user a certain amount
248
- * of money. Charges recorded with track_charge() will appear in the
249
- * Mixpanel revenue report.
250
- *
251
- * ### Usage:
252
- *
253
- * // charge a user $50
254
- * mixpanel.people.track_charge(50);
255
- *
256
- * // charge a user $30.50 on the 2nd of january
257
- * mixpanel.people.track_charge(30.50, {
258
- * '$time': new Date('jan 1 2012')
259
- * });
260
- *
261
- * @param {Number} amount The amount of money charged to the current user
262
- * @param {Object} [properties] An associative array of properties associated with the charge
263
- * @param {Function} [callback] If provided, the callback will be called when the server responds
264
- */
247
+ * Record that you have charged the current user a certain amount
248
+ * of money. Charges recorded with track_charge() will appear in the
249
+ * Mixpanel revenue report.
250
+ *
251
+ * ### Usage:
252
+ *
253
+ * // charge a user $50
254
+ * mixpanel.people.track_charge(50);
255
+ *
256
+ * // charge a user $30.50 on the 2nd of january
257
+ * mixpanel.people.track_charge(30.50, {
258
+ * '$time': new Date('jan 1 2012')
259
+ * });
260
+ *
261
+ * @param {Number} amount The amount of money charged to the current user
262
+ * @param {Object} [properties] An associative array of properties associated with the charge
263
+ * @param {Function} [callback] If provided, the callback will be called when the server responds
264
+ * @deprecated
265
+ */
265
266
  MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(amount, properties, callback) {
266
267
  if (!_.isNumber(amount)) {
267
268
  amount = parseFloat(amount);
@@ -277,15 +278,16 @@ MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(am
277
278
  });
278
279
 
279
280
  /*
280
- * Permanently clear all revenue report transactions from the
281
- * current user's people analytics profile.
282
- *
283
- * ### Usage:
284
- *
285
- * mixpanel.people.clear_charges();
286
- *
287
- * @param {Function} [callback] If provided, the callback will be called after tracking the event.
288
- */
281
+ * Permanently clear all revenue report transactions from the
282
+ * current user's people analytics profile.
283
+ *
284
+ * ### Usage:
285
+ *
286
+ * mixpanel.people.clear_charges();
287
+ *
288
+ * @param {Function} [callback] If provided, the callback will be called after tracking the event.
289
+ * @deprecated
290
+ */
289
291
  MixpanelPeople.prototype.clear_charges = function(callback) {
290
292
  return this.set('$transactions', [], callback);
291
293
  };
@@ -1,3 +1,4 @@
1
+ import Config from './config';
1
2
  import { RequestQueue } from './request-queue';
2
3
  import { console_with_prefix, _ } from './utils'; // eslint-disable-line camelcase
3
4
 
@@ -30,6 +31,9 @@ var RequestBatcher = function(storageKey, options) {
30
31
 
31
32
  this.stopped = !this.libConfig['batch_autostart'];
32
33
  this.consecutiveRemovalFailures = 0;
34
+
35
+ // extra client-side dedupe
36
+ this.itemIdsSentSuccessfully = {};
33
37
  };
34
38
 
35
39
  /**
@@ -122,7 +126,34 @@ RequestBatcher.prototype.flush = function(options) {
122
126
  payload = this.beforeSendHook(payload);
123
127
  }
124
128
  if (payload) {
125
- dataForRequest.push(payload);
129
+ // mp_sent_by_lib_version prop captures which lib version actually
130
+ // sends each event (regardless of which version originally queued
131
+ // it for sending)
132
+ if (payload['event'] && payload['properties']) {
133
+ payload['properties'] = _.extend(
134
+ {},
135
+ payload['properties'],
136
+ {'mp_sent_by_lib_version': Config.LIB_VERSION}
137
+ );
138
+ }
139
+ var addPayload = true;
140
+ var itemId = item['id'];
141
+ if (itemId) {
142
+ if ((this.itemIdsSentSuccessfully[itemId] || 0) > 5) {
143
+ this.reportError('[dupe] item ID sent too many times, not sending', {
144
+ item: item,
145
+ batchSize: batch.length,
146
+ timesSent: this.itemIdsSentSuccessfully[itemId]
147
+ });
148
+ addPayload = false;
149
+ }
150
+ } else {
151
+ this.reportError('[dupe] found item with no ID', {item: item});
152
+ }
153
+
154
+ if (addPayload) {
155
+ dataForRequest.push(payload);
156
+ }
126
157
  }
127
158
  transformedItems[item['id']] = payload;
128
159
  }, this);
@@ -205,6 +236,24 @@ RequestBatcher.prototype.flush = function(options) {
205
236
  }
206
237
  }, this)
207
238
  );
239
+
240
+ // client-side dedupe
241
+ _.each(batch, _.bind(function(item) {
242
+ var itemId = item['id'];
243
+ if (itemId) {
244
+ this.itemIdsSentSuccessfully[itemId] = this.itemIdsSentSuccessfully[itemId] || 0;
245
+ this.itemIdsSentSuccessfully[itemId]++;
246
+ if (this.itemIdsSentSuccessfully[itemId] > 5) {
247
+ this.reportError('[dupe] item ID sent too many times', {
248
+ item: item,
249
+ batchSize: batch.length,
250
+ timesSent: this.itemIdsSentSuccessfully[itemId]
251
+ });
252
+ }
253
+ } else {
254
+ this.reportError('[dupe] found item with no ID while removing', {item: item});
255
+ }
256
+ }, this));
208
257
  }
209
258
 
210
259
  } catch(err) {