mixpanel-browser 2.41.0 → 2.42.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.41.0'
9
+ LIB_VERSION: '2.42.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
@@ -1688,28 +1688,6 @@
1688
1688
  return maxlen ? guid.substring(0, maxlen) : guid;
1689
1689
  };
1690
1690
 
1691
- /**
1692
- * Check deterministically whether to include or exclude from a feature rollout/test based on the
1693
- * given string and the desired percentage to include.
1694
- * @param {String} str - string to run the check against (for instance a project's token)
1695
- * @param {String} feature - name of feature (for inclusion in hash, to ensure different results
1696
- * for different features)
1697
- * @param {Number} percent_allowed - percentage chance that a given string will be included
1698
- * @returns {Boolean} whether the given string should be included
1699
- */
1700
- var determine_eligibility = _.safewrap(function(str, feature, percent_allowed) {
1701
- str = str + feature;
1702
-
1703
- // Bernstein's hash: http://www.cse.yorku.ca/~oz/hash.html#djb2
1704
- var hash = 5381;
1705
- for (var i = 0; i < str.length; i++) {
1706
- hash = ((hash << 5) + hash) + str.charCodeAt(i);
1707
- hash = hash & hash;
1708
- }
1709
- var dart = (hash >>> 0) % 100;
1710
- return dart < percent_allowed;
1711
- });
1712
-
1713
1691
  // naive way to extract domain name (example.com) from full hostname (my.sub.example.com)
1714
1692
  var SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
1715
1693
  // this next one attempts to account for some ccSLDs, e.g. extracting oxford.ac.uk from www.oxford.ac.uk
@@ -2448,9 +2426,9 @@
2448
2426
  } else if (
2449
2427
  _.isObject(res) &&
2450
2428
  res.xhr_req &&
2451
- (res.xhr_req['status'] >= 500 || res.xhr_req['status'] <= 0)
2429
+ (res.xhr_req['status'] >= 500 || res.xhr_req['status'] === 429 || res.error === 'timeout')
2452
2430
  ) {
2453
- // network or API error, retry
2431
+ // network or API error, or 429 Too Many Requests, retry
2454
2432
  var retryMS = this.flushInterval * 2;
2455
2433
  var headers = res.xhr_req['responseHeaders'];
2456
2434
  if (headers) {
@@ -3020,9 +2998,13 @@
3020
2998
  * Permanently delete a group.
3021
2999
  *
3022
3000
  * ### Usage:
3001
+ *
3023
3002
  * mixpanel.get_group('company', 'mixpanel').delete();
3003
+ *
3004
+ * @param {Function} [callback] If provided, the callback will be called after the tracking event
3024
3005
  */
3025
3006
  MixpanelGroup.prototype['delete'] = addOptOutCheckMixpanelGroup(function(callback) {
3007
+ // bracket notation above prevents a minification error related to reserved words
3026
3008
  var data = this.delete_action();
3027
3009
  return this._send_request(data, callback);
3028
3010
  });
@@ -5924,7 +5906,7 @@
5924
5906
  'inapp_protocol': '//',
5925
5907
  'inapp_link_new_window': false,
5926
5908
  'ignore_dnt': false,
5927
- 'batch_requests': false, // for now
5909
+ 'batch_requests': true,
5928
5910
  'batch_size': 50,
5929
5911
  'batch_flush_interval_ms': 5000,
5930
5912
  'batch_request_timeout_ms': 90000,
@@ -6043,17 +6025,7 @@
6043
6025
  this['config'] = {};
6044
6026
  this['_triggered_notifs'] = [];
6045
6027
 
6046
- // rollout: enable batch_requests by default for 60% of projects
6047
- // (only if they have not specified a value in their init config
6048
- // and they aren't using a custom API host)
6049
- var variable_features = {};
6050
- var api_host = config['api_host'];
6051
- var is_custom_api = !!api_host && !api_host.match(/\.mixpanel\.com$/);
6052
- if (!('batch_requests' in config) && !is_custom_api && determine_eligibility(token, 'batch', 60)) {
6053
- variable_features['batch_requests'] = true;
6054
- }
6055
-
6056
- this.set_config(_.extend({}, DEFAULT_CONFIG, variable_features, config, {
6028
+ this.set_config(_.extend({}, DEFAULT_CONFIG, config, {
6057
6029
  'name': name,
6058
6030
  'token': token,
6059
6031
  'callback_fn': ((name === PRIMARY_INSTANCE_NAME) ? name : PRIMARY_INSTANCE_NAME + '.' + name) + '._jsc'
@@ -6079,15 +6051,32 @@
6079
6051
  } else {
6080
6052
  this.init_batchers();
6081
6053
  if (sendBeacon && window$1.addEventListener) {
6082
- window$1.addEventListener('unload', _.bind(function() {
6083
- // Before page closes, attempt to flush any events queued up via navigator.sendBeacon.
6084
- // Since sendBeacon doesn't report success/failure, events will not be removed from
6085
- // the persistent store; if the site is loaded again, the events will be flushed again
6086
- // on startup and deduplicated on the Mixpanel server side.
6054
+ // Before page closes or hides (user tabs away etc), attempt to flush any events
6055
+ // queued up via navigator.sendBeacon. Since sendBeacon doesn't report success/failure,
6056
+ // events will not be removed from the persistent store; if the site is loaded again,
6057
+ // the events will be flushed again on startup and deduplicated on the Mixpanel server
6058
+ // side.
6059
+ // There is no reliable way to capture only page close events, so we lean on the
6060
+ // visibilitychange and pagehide events as recommended at
6061
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes.
6062
+ // These events fire when the user clicks away from the current page/tab, so will occur
6063
+ // more frequently than page unload, but are the only mechanism currently for capturing
6064
+ // this scenario somewhat reliably.
6065
+ var flush_on_unload = _.bind(function() {
6087
6066
  if (!this.request_batchers.events.stopped) {
6088
6067
  this.request_batchers.events.flush({unloading: true});
6089
6068
  }
6090
- }, this));
6069
+ }, this);
6070
+ window$1.addEventListener('pagehide', function(ev) {
6071
+ if (ev['persisted']) {
6072
+ flush_on_unload();
6073
+ }
6074
+ });
6075
+ window$1.addEventListener('visibilitychange', function() {
6076
+ if (document$1['visibilityState'] === 'hidden') {
6077
+ flush_on_unload();
6078
+ }
6079
+ });
6091
6080
  }
6092
6081
  }
6093
6082
  }
package/doc/build-docs.js CHANGED
@@ -97,6 +97,22 @@ function doxToMD(items) {
97
97
  });
98
98
  }
99
99
 
100
+ // Captures prototype bracket notation property assignment, e.g.:
101
+ // `MixpanelGroup.prototype['delete'] = addOptOutCheckMixpanelGroup(function(callback) {`
102
+ // Based on https://github.com/tj/dox/blob/9fe92e17dfcd31c9b6512f6e5bf0b52c2b6b84d4/lib/dox.js#L592
103
+ dox.contextPatternMatchers.push(function (str) {
104
+ if (/^\s*([\w$.]+)\s*\.\s*prototype\s*\['\s*([\w$]+)'\]\s*=\s*([^\n;]+)/.exec(str)) {
105
+ return {
106
+ type: 'property'
107
+ , constructor: RegExp.$1
108
+ , cons: RegExp.$1
109
+ , name: RegExp.$2
110
+ , value: RegExp.$3.trim()
111
+ , string: RegExp.$1 + '.prototype.' + RegExp.$2
112
+ };
113
+ }
114
+ });
115
+
100
116
  const rawCode = fs.readFileSync(SOURCE_FILE).toString().trim();
101
117
  const parsed = dox.parseComments(rawCode);
102
118
 
@@ -990,6 +990,24 @@ mixpanel.people.unset(['gender', 'Company']);
990
990
  # mixpanel.group
991
991
 
992
992
 
993
+ ___
994
+ ## mixpanel.group.delete
995
+ Permanently delete a group.
996
+
997
+
998
+ ### Usage:
999
+
1000
+ ```javascript
1001
+ mixpanel.get_group('company', 'mixpanel').delete();
1002
+ ```
1003
+
1004
+
1005
+
1006
+ | Argument | Type | Description |
1007
+ | ------------- | ------------- | ----- |
1008
+ | **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 the tracking event |
1009
+
1010
+
993
1011
  ___
994
1012
  ## mixpanel.group.remove
995
1013
  Remove a property from a group. The value will be ignored if doesn't exist.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.41.0",
3
+ "version": "2.42.0",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "directories": {
@@ -46,10 +46,10 @@
46
46
  "jsdom": "11.12.0",
47
47
  "jsdom-global": "3.0.2",
48
48
  "localStorage": "1.0.4",
49
- "lodash": "4.17.19",
49
+ "lodash": "4.17.21",
50
50
  "mocha": "7.1.1",
51
51
  "morgan": "1.9.1",
52
- "rdme": "3.0.0",
52
+ "rdme": "4.0.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.41.0'
3
+ LIB_VERSION: '2.42.0'
4
4
  };
5
5
 
6
6
  export default Config;
@@ -1,6 +1,6 @@
1
1
  /* eslint camelcase: "off" */
2
2
  import Config from './config';
3
- import { _, console, userAgent, window, document, navigator, determine_eligibility, slice } from './utils';
3
+ import { _, console, userAgent, window, document, navigator, slice } from './utils';
4
4
  import { FormTracker, LinkTracker } from './dom-trackers';
5
5
  import { RequestBatcher } from './request-batcher';
6
6
  import { MixpanelGroup } from './mixpanel-group';
@@ -117,7 +117,7 @@ var DEFAULT_CONFIG = {
117
117
  'inapp_protocol': '//',
118
118
  'inapp_link_new_window': false,
119
119
  'ignore_dnt': false,
120
- 'batch_requests': false, // for now
120
+ 'batch_requests': true,
121
121
  'batch_size': 50,
122
122
  'batch_flush_interval_ms': 5000,
123
123
  'batch_request_timeout_ms': 90000,
@@ -236,17 +236,7 @@ MixpanelLib.prototype._init = function(token, config, name) {
236
236
  this['config'] = {};
237
237
  this['_triggered_notifs'] = [];
238
238
 
239
- // rollout: enable batch_requests by default for 60% of projects
240
- // (only if they have not specified a value in their init config
241
- // and they aren't using a custom API host)
242
- var variable_features = {};
243
- var api_host = config['api_host'];
244
- var is_custom_api = !!api_host && !api_host.match(/\.mixpanel\.com$/);
245
- if (!('batch_requests' in config) && !is_custom_api && determine_eligibility(token, 'batch', 60)) {
246
- variable_features['batch_requests'] = true;
247
- }
248
-
249
- this.set_config(_.extend({}, DEFAULT_CONFIG, variable_features, config, {
239
+ this.set_config(_.extend({}, DEFAULT_CONFIG, config, {
250
240
  'name': name,
251
241
  'token': token,
252
242
  'callback_fn': ((name === PRIMARY_INSTANCE_NAME) ? name : PRIMARY_INSTANCE_NAME + '.' + name) + '._jsc'
@@ -272,15 +262,32 @@ MixpanelLib.prototype._init = function(token, config, name) {
272
262
  } else {
273
263
  this.init_batchers();
274
264
  if (sendBeacon && window.addEventListener) {
275
- window.addEventListener('unload', _.bind(function() {
276
- // Before page closes, attempt to flush any events queued up via navigator.sendBeacon.
277
- // Since sendBeacon doesn't report success/failure, events will not be removed from
278
- // the persistent store; if the site is loaded again, the events will be flushed again
279
- // on startup and deduplicated on the Mixpanel server side.
265
+ // Before page closes or hides (user tabs away etc), attempt to flush any events
266
+ // queued up via navigator.sendBeacon. Since sendBeacon doesn't report success/failure,
267
+ // events will not be removed from the persistent store; if the site is loaded again,
268
+ // the events will be flushed again on startup and deduplicated on the Mixpanel server
269
+ // side.
270
+ // There is no reliable way to capture only page close events, so we lean on the
271
+ // visibilitychange and pagehide events as recommended at
272
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes.
273
+ // These events fire when the user clicks away from the current page/tab, so will occur
274
+ // more frequently than page unload, but are the only mechanism currently for capturing
275
+ // this scenario somewhat reliably.
276
+ var flush_on_unload = _.bind(function() {
280
277
  if (!this.request_batchers.events.stopped) {
281
278
  this.request_batchers.events.flush({unloading: true});
282
279
  }
283
- }, this));
280
+ }, this);
281
+ window.addEventListener('pagehide', function(ev) {
282
+ if (ev['persisted']) {
283
+ flush_on_unload();
284
+ }
285
+ });
286
+ window.addEventListener('visibilitychange', function() {
287
+ if (document['visibilityState'] === 'hidden') {
288
+ flush_on_unload();
289
+ }
290
+ });
284
291
  }
285
292
  }
286
293
  }
@@ -110,9 +110,13 @@ MixpanelGroup.prototype.union = addOptOutCheckMixpanelGroup(function(list_name,
110
110
  * Permanently delete a group.
111
111
  *
112
112
  * ### Usage:
113
+ *
113
114
  * mixpanel.get_group('company', 'mixpanel').delete();
115
+ *
116
+ * @param {Function} [callback] If provided, the callback will be called after the tracking event
114
117
  */
115
118
  MixpanelGroup.prototype['delete'] = addOptOutCheckMixpanelGroup(function(callback) {
119
+ // bracket notation above prevents a minification error related to reserved words
116
120
  var data = this.delete_action();
117
121
  return this._send_request(data, callback);
118
122
  });
@@ -148,9 +148,9 @@ RequestBatcher.prototype.flush = function(options) {
148
148
  } else if (
149
149
  _.isObject(res) &&
150
150
  res.xhr_req &&
151
- (res.xhr_req['status'] >= 500 || res.xhr_req['status'] <= 0)
151
+ (res.xhr_req['status'] >= 500 || res.xhr_req['status'] === 429 || res.error === 'timeout')
152
152
  ) {
153
- // network or API error, retry
153
+ // network or API error, or 429 Too Many Requests, retry
154
154
  var retryMS = this.flushInterval * 2;
155
155
  var headers = res.xhr_req['responseHeaders'];
156
156
  if (headers) {
package/src/utils.js CHANGED
@@ -1682,28 +1682,6 @@ var cheap_guid = function(maxlen) {
1682
1682
  return maxlen ? guid.substring(0, maxlen) : guid;
1683
1683
  };
1684
1684
 
1685
- /**
1686
- * Check deterministically whether to include or exclude from a feature rollout/test based on the
1687
- * given string and the desired percentage to include.
1688
- * @param {String} str - string to run the check against (for instance a project's token)
1689
- * @param {String} feature - name of feature (for inclusion in hash, to ensure different results
1690
- * for different features)
1691
- * @param {Number} percent_allowed - percentage chance that a given string will be included
1692
- * @returns {Boolean} whether the given string should be included
1693
- */
1694
- var determine_eligibility = _.safewrap(function(str, feature, percent_allowed) {
1695
- str = str + feature;
1696
-
1697
- // Bernstein's hash: http://www.cse.yorku.ca/~oz/hash.html#djb2
1698
- var hash = 5381;
1699
- for (var i = 0; i < str.length; i++) {
1700
- hash = ((hash << 5) + hash) + str.charCodeAt(i);
1701
- hash = hash & hash;
1702
- }
1703
- var dart = (hash >>> 0) % 100;
1704
- return dart < percent_allowed;
1705
- });
1706
-
1707
1685
  // naive way to extract domain name (example.com) from full hostname (my.sub.example.com)
1708
1686
  var SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
1709
1687
  // this next one attempts to account for some ccSLDs, e.g. extracting oxford.ac.uk from www.oxford.ac.uk
@@ -1762,7 +1740,6 @@ export {
1762
1740
  navigator,
1763
1741
  cheap_guid,
1764
1742
  console_with_prefix,
1765
- determine_eligibility,
1766
1743
  extract_domain,
1767
1744
  localStorageSupported,
1768
1745
  JSONStringify,
package/tunnel.log ADDED
File without changes
package/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- node_js:
2
- - '12'
3
- - '14'
4
- language: node_js
5
- script:
6
- - npm test