mixpanel-browser 2.54.1 → 2.55.1
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 +9 -0
- package/README.md +20 -29
- package/build.sh +1 -0
- package/dist/mixpanel-core.cjs.js +21 -6
- package/dist/mixpanel-recorder.js +55 -19
- package/dist/mixpanel-recorder.min.js +9 -9
- package/dist/mixpanel-with-async-recorder.cjs.js +21 -6
- package/dist/mixpanel.amd.js +56 -19
- package/dist/mixpanel.cjs.js +56 -19
- package/dist/mixpanel.globals.js +21 -6
- package/dist/mixpanel.min.js +98 -97
- package/dist/mixpanel.module.js +11168 -0
- package/dist/mixpanel.umd.js +56 -19
- package/package.json +2 -1
- package/src/config.js +1 -1
- package/src/mixpanel-core.js +1 -0
- package/src/recorder/index.js +34 -14
- package/src/request-batcher.js +7 -2
- package/src/utils.js +26 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
**2.55.1** (27 Aug 2024)
|
|
2
|
+
- Adds a minimum recording length option for session recording
|
|
3
|
+
- Fixes and improvements for session recording batcher to support offline queueing and retry
|
|
4
|
+
- Fix for query param parsing/escaping
|
|
5
|
+
- Support for more UTM tags / click IDs (thanks @aliyalcinkaya)
|
|
6
|
+
|
|
7
|
+
**2.55.0** (2 Aug 2024)
|
|
8
|
+
- Added new build to support native JavaScript modules
|
|
9
|
+
|
|
1
10
|
**2.54.1** (30 Jul 2024)
|
|
2
11
|
- Fixes and improvements for user-idleness detection in session recording
|
|
3
12
|
|
package/README.md
CHANGED
|
@@ -8,16 +8,16 @@ intended to be used by websites wishing to send data to Mixpanel projects. A ful
|
|
|
8
8
|
is available [here](https://developer.mixpanel.com/docs/javascript-full-api-reference).
|
|
9
9
|
|
|
10
10
|
## Alternative installation via NPM
|
|
11
|
-
This library is available as a [package on NPM](https://www.npmjs.com/package/mixpanel-browser) (named `mixpanel-browser` to distinguish it from Mixpanel's server-side Node.js library, available on NPM as `mixpanel`). To install into a project using NPM with a front-end packager such as [
|
|
11
|
+
This library is available as a [package on NPM](https://www.npmjs.com/package/mixpanel-browser) (named `mixpanel-browser` to distinguish it from Mixpanel's server-side Node.js library, available on NPM as `mixpanel`). To install into a project using NPM with a front-end packager such as [Vite](https://vitejs.dev/) or [Webpack](https://webpack.github.io/):
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
14
|
npm install --save mixpanel-browser
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
You can then
|
|
17
|
+
You can then import the lib:
|
|
18
18
|
|
|
19
19
|
```javascript
|
|
20
|
-
|
|
20
|
+
import mixpanel from 'mixpanel-browser';
|
|
21
21
|
|
|
22
22
|
mixpanel.init("YOUR_TOKEN");
|
|
23
23
|
mixpanel.track("An event");
|
|
@@ -35,38 +35,29 @@ To load the core SDK and optionally load session recording bundle asynchronously
|
|
|
35
35
|
import mixpanel from 'mixpanel-browser/src/loaders/loader-module-with-async-recorder';
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
##
|
|
39
|
-
`mixpanel-js` is also available via front-end package manager [Bower](http://bower.io/). After installing Bower, fetch into your project's `bower_components` dir with:
|
|
40
|
-
```sh
|
|
41
|
-
bower install mixpanel
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### Using Bower to load the snippet
|
|
45
|
-
You can then load the lib via the embed code (snippet) with a script reference:
|
|
46
|
-
```html
|
|
47
|
-
<script src="bower_components/mixpanel/mixpanel-jslib-snippet.min.js"></script>
|
|
48
|
-
```
|
|
49
|
-
which loads the _latest_ library version from the Mixpanel CDN ([http://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js](http://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js)).
|
|
38
|
+
## Use as a browser JavaScript module
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
If you wish to load the specific version downloaded in your Bower package, there are two options.
|
|
40
|
+
If you are leveraging [browser JavaScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), you can use [`importmap`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) to pull in this library.
|
|
53
41
|
|
|
54
|
-
1) Override the CDN library location with the global `MIXPANEL_CUSTOM_LIB_URL` var:
|
|
55
42
|
```html
|
|
56
|
-
<script>
|
|
57
|
-
|
|
43
|
+
<script type="importmap">
|
|
44
|
+
{
|
|
45
|
+
"imports": {
|
|
46
|
+
"mixpanel-browser": "https://cdn.mxpnl.com/libs/mixpanel-js/dist/mixpanel.module.js"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
58
49
|
</script>
|
|
59
|
-
<script src="
|
|
50
|
+
<script type="module" src="main.js"></script>
|
|
60
51
|
```
|
|
61
|
-
or
|
|
62
52
|
|
|
63
|
-
|
|
64
|
-
```sh
|
|
65
|
-
java -jar compiler.jar --js src/loaders/mixpanel-jslib-snippet.js --js_output_file mixpanel-jslib-snippet.min.js --compilation_level ADVANCED_OPTIMIZATIONS --define='MIXPANEL_LIB_URL="bower_components/mixpanel/mixpanel.js"'
|
|
66
|
-
```
|
|
53
|
+
Then you are free to import `mixpanel-browser` in your javascript modules.
|
|
67
54
|
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
```js
|
|
56
|
+
// main.js
|
|
57
|
+
import mixpanel from 'mixpanel-browser';
|
|
58
|
+
|
|
59
|
+
mixpanel.init('YOUR_TOKEN', {debug: true, track_pageview: true, persistence: 'localStorage'});
|
|
60
|
+
```
|
|
70
61
|
|
|
71
62
|
## Building bundles for release
|
|
72
63
|
- Install development dependencies: `npm install`
|
|
@@ -87,4 +78,4 @@ Mixpanel production releases are tested against a large matrix of browsers and o
|
|
|
87
78
|
- 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`
|
|
88
79
|
|
|
89
80
|
## Thanks
|
|
90
|
-
For patches and support: @bohanyang, @dehau, @drubin, @D1plo1d, @feychenie, @mogstad, @pfhayes, @sandorfr, @stefansedich, @gfx, @pkaminski, @austince, @danielbaker, @mkdai, @wolever, @dpraul, @chriszamierowski, @JoaoGomesTW
|
|
81
|
+
For patches and support: @bohanyang, @dehau, @drubin, @D1plo1d, @feychenie, @mogstad, @pfhayes, @sandorfr, @stefansedich, @gfx, @pkaminski, @austince, @danielbaker, @mkdai, @wolever, @dpraul, @chriszamierowski, @JoaoGomesTW, @@aliyalcinkaya
|
package/build.sh
CHANGED
|
@@ -27,6 +27,7 @@ if [ ! -z "$FULL" ]; then
|
|
|
27
27
|
echo 'Building module bundles'
|
|
28
28
|
npx rollup -i src/loaders/loader-module.js -f amd -o build/mixpanel.amd.js -c rollup.config.js
|
|
29
29
|
npx rollup -i src/loaders/loader-module.js -f cjs -o build/mixpanel.cjs.js -c rollup.config.js
|
|
30
|
+
npx rollup -i src/loaders/loader-module.js -f es -o build/mixpanel.module.js -c rollup.config.js
|
|
30
31
|
npx rollup -i src/loaders/loader-module-core.js -f cjs -o build/mixpanel-core.cjs.js -c rollup.config.js
|
|
31
32
|
npx rollup -i src/loaders/loader-module-with-async-recorder.js -f cjs -o build/mixpanel-with-async-recorder.cjs.js -c rollup.config.js
|
|
32
33
|
npx rollup -i src/loaders/loader-module.js -f umd -o build/mixpanel.umd.js -n mixpanel -c rollup.config.js
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var Config = {
|
|
4
4
|
DEBUG: false,
|
|
5
|
-
LIB_VERSION: '2.
|
|
5
|
+
LIB_VERSION: '2.55.1'
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -14,7 +14,7 @@ if (typeof(window) === 'undefined') {
|
|
|
14
14
|
hostname: ''
|
|
15
15
|
};
|
|
16
16
|
win = {
|
|
17
|
-
navigator: { userAgent: '' },
|
|
17
|
+
navigator: { userAgent: '', onLine: true },
|
|
18
18
|
document: {
|
|
19
19
|
location: loc,
|
|
20
20
|
referrer: ''
|
|
@@ -968,7 +968,7 @@ _.HTTPBuildQuery = function(formdata, arg_separator) {
|
|
|
968
968
|
_.getQueryParam = function(url, param) {
|
|
969
969
|
// Expects a raw URL
|
|
970
970
|
|
|
971
|
-
param = param.replace(/[[]
|
|
971
|
+
param = param.replace(/[[]/g, '\\[').replace(/[\]]/g, '\\]');
|
|
972
972
|
var regexS = '[\\?&]' + param + '=([^&#]*)',
|
|
973
973
|
regex = new RegExp(regexS),
|
|
974
974
|
results = regex.exec(url);
|
|
@@ -1425,8 +1425,8 @@ _.dom_query = (function() {
|
|
|
1425
1425
|
};
|
|
1426
1426
|
})();
|
|
1427
1427
|
|
|
1428
|
-
var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
|
|
1429
|
-
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'ttclid', 'twclid', 'wbraid'];
|
|
1428
|
+
var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'utm_id', 'utm_source_platform','utm_campaign_id', 'utm_creative_format', 'utm_marketing_tactic'];
|
|
1429
|
+
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'sccid', 'ttclid', 'twclid', 'wbraid'];
|
|
1430
1430
|
|
|
1431
1431
|
_.info = {
|
|
1432
1432
|
campaignParams: function(default_value) {
|
|
@@ -1708,6 +1708,15 @@ var extract_domain = function(hostname) {
|
|
|
1708
1708
|
return matches ? matches[0] : '';
|
|
1709
1709
|
};
|
|
1710
1710
|
|
|
1711
|
+
/**
|
|
1712
|
+
* Check whether we have network connection. default to true for browsers that don't support navigator.onLine (IE)
|
|
1713
|
+
* @returns {boolean}
|
|
1714
|
+
*/
|
|
1715
|
+
var isOnline = function() {
|
|
1716
|
+
var onLine = win.navigator['onLine'];
|
|
1717
|
+
return _.isUndefined(onLine) || onLine;
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1711
1720
|
var JSONStringify = null, JSONParse = null;
|
|
1712
1721
|
if (typeof JSON !== 'undefined') {
|
|
1713
1722
|
JSONStringify = JSON.stringify;
|
|
@@ -2523,7 +2532,12 @@ RequestBatcher.prototype.flush = function(options) {
|
|
|
2523
2532
|
this.flush();
|
|
2524
2533
|
} else if (
|
|
2525
2534
|
_.isObject(res) &&
|
|
2526
|
-
(
|
|
2535
|
+
(
|
|
2536
|
+
res.httpStatusCode >= 500
|
|
2537
|
+
|| res.httpStatusCode === 429
|
|
2538
|
+
|| (res.httpStatusCode <= 0 && !isOnline())
|
|
2539
|
+
|| res.error === 'timeout'
|
|
2540
|
+
)
|
|
2527
2541
|
) {
|
|
2528
2542
|
// network or API error, or 429 Too Many Requests, retry
|
|
2529
2543
|
var retryMS = this.flushInterval * 2;
|
|
@@ -4247,6 +4261,7 @@ var DEFAULT_CONFIG = {
|
|
|
4247
4261
|
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
|
|
4248
4262
|
'record_mask_text_selector': '*',
|
|
4249
4263
|
'record_max_ms': MAX_RECORDING_MS,
|
|
4264
|
+
'record_min_ms': 0,
|
|
4250
4265
|
'record_sessions_percent': 0,
|
|
4251
4266
|
'recorder_src': 'https://cdn.mxpnl.com/libs/mixpanel-recorder.min.js'
|
|
4252
4267
|
};
|
|
@@ -4510,7 +4510,7 @@
|
|
|
4510
4510
|
|
|
4511
4511
|
var Config = {
|
|
4512
4512
|
DEBUG: false,
|
|
4513
|
-
LIB_VERSION: '2.
|
|
4513
|
+
LIB_VERSION: '2.55.1'
|
|
4514
4514
|
};
|
|
4515
4515
|
|
|
4516
4516
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -4522,7 +4522,7 @@
|
|
|
4522
4522
|
hostname: ''
|
|
4523
4523
|
};
|
|
4524
4524
|
win = {
|
|
4525
|
-
navigator: { userAgent: '' },
|
|
4525
|
+
navigator: { userAgent: '', onLine: true },
|
|
4526
4526
|
document: {
|
|
4527
4527
|
location: loc,
|
|
4528
4528
|
referrer: ''
|
|
@@ -4536,6 +4536,8 @@
|
|
|
4536
4536
|
|
|
4537
4537
|
// Maximum allowed session recording length
|
|
4538
4538
|
var MAX_RECORDING_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
4539
|
+
// Maximum allowed value for minimum session recording length
|
|
4540
|
+
var MAX_VALUE_FOR_MIN_RECORDING_MS = 8 * 1000; // 8 seconds
|
|
4539
4541
|
|
|
4540
4542
|
/*
|
|
4541
4543
|
* Saved references to long variable names, so that closure compiler can
|
|
@@ -5447,7 +5449,7 @@
|
|
|
5447
5449
|
_.getQueryParam = function(url, param) {
|
|
5448
5450
|
// Expects a raw URL
|
|
5449
5451
|
|
|
5450
|
-
param = param.replace(/[[]
|
|
5452
|
+
param = param.replace(/[[]/g, '\\[').replace(/[\]]/g, '\\]');
|
|
5451
5453
|
var regexS = '[\\?&]' + param + '=([^&#]*)',
|
|
5452
5454
|
regex = new RegExp(regexS),
|
|
5453
5455
|
results = regex.exec(url);
|
|
@@ -5904,8 +5906,8 @@
|
|
|
5904
5906
|
};
|
|
5905
5907
|
})();
|
|
5906
5908
|
|
|
5907
|
-
var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
|
|
5908
|
-
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'ttclid', 'twclid', 'wbraid'];
|
|
5909
|
+
var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'utm_id', 'utm_source_platform','utm_campaign_id', 'utm_creative_format', 'utm_marketing_tactic'];
|
|
5910
|
+
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'sccid', 'ttclid', 'twclid', 'wbraid'];
|
|
5909
5911
|
|
|
5910
5912
|
_.info = {
|
|
5911
5913
|
campaignParams: function(default_value) {
|
|
@@ -6187,6 +6189,15 @@
|
|
|
6187
6189
|
return matches ? matches[0] : '';
|
|
6188
6190
|
};
|
|
6189
6191
|
|
|
6192
|
+
/**
|
|
6193
|
+
* Check whether we have network connection. default to true for browsers that don't support navigator.onLine (IE)
|
|
6194
|
+
* @returns {boolean}
|
|
6195
|
+
*/
|
|
6196
|
+
var isOnline = function() {
|
|
6197
|
+
var onLine = win.navigator['onLine'];
|
|
6198
|
+
return _.isUndefined(onLine) || onLine;
|
|
6199
|
+
};
|
|
6200
|
+
|
|
6190
6201
|
var JSONStringify = null, JSONParse = null;
|
|
6191
6202
|
if (typeof JSON !== 'undefined') {
|
|
6192
6203
|
JSONStringify = JSON.stringify;
|
|
@@ -7018,7 +7029,12 @@
|
|
|
7018
7029
|
this.flush();
|
|
7019
7030
|
} else if (
|
|
7020
7031
|
_.isObject(res) &&
|
|
7021
|
-
(
|
|
7032
|
+
(
|
|
7033
|
+
res.httpStatusCode >= 500
|
|
7034
|
+
|| res.httpStatusCode === 429
|
|
7035
|
+
|| (res.httpStatusCode <= 0 && !isOnline())
|
|
7036
|
+
|| res.error === 'timeout'
|
|
7037
|
+
)
|
|
7022
7038
|
) {
|
|
7023
7039
|
// network or API error, or 429 Too Many Requests, retry
|
|
7024
7040
|
var retryMS = this.flushInterval * 2;
|
|
@@ -7169,6 +7185,7 @@
|
|
|
7169
7185
|
this.maxTimeoutId = null;
|
|
7170
7186
|
|
|
7171
7187
|
this.recordMaxMs = MAX_RECORDING_MS;
|
|
7188
|
+
this.recordMinMs = 0;
|
|
7172
7189
|
this._initBatcher();
|
|
7173
7190
|
};
|
|
7174
7191
|
|
|
@@ -7200,16 +7217,24 @@
|
|
|
7200
7217
|
logger.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
|
|
7201
7218
|
}
|
|
7202
7219
|
|
|
7220
|
+
this.recordMinMs = this.get_config('record_min_ms');
|
|
7221
|
+
if (this.recordMinMs > MAX_VALUE_FOR_MIN_RECORDING_MS) {
|
|
7222
|
+
this.recordMinMs = MAX_VALUE_FOR_MIN_RECORDING_MS;
|
|
7223
|
+
logger.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
|
|
7224
|
+
}
|
|
7225
|
+
|
|
7203
7226
|
this.recEvents = [];
|
|
7204
7227
|
this.seqNo = 0;
|
|
7205
|
-
this.replayStartTime =
|
|
7228
|
+
this.replayStartTime = new Date().getTime();
|
|
7206
7229
|
|
|
7207
7230
|
this.replayId = _.UUID();
|
|
7208
7231
|
|
|
7209
|
-
if (shouldStopBatcher) {
|
|
7210
|
-
//
|
|
7232
|
+
if (shouldStopBatcher || this.recordMinMs > 0) {
|
|
7233
|
+
// the primary case for shouldStopBatcher is when we're starting recording after a reset
|
|
7211
7234
|
// and don't want to send anything over the network until there's
|
|
7212
7235
|
// actual user activity
|
|
7236
|
+
// this also applies if the minimum recording length has not been hit yet
|
|
7237
|
+
// so that we don't send data until we know the recording will be long enough
|
|
7213
7238
|
this.batcher.stop();
|
|
7214
7239
|
} else {
|
|
7215
7240
|
this.batcher.start();
|
|
@@ -7223,11 +7248,16 @@
|
|
|
7223
7248
|
}, this), this.get_config('record_idle_timeout_ms'));
|
|
7224
7249
|
}, this);
|
|
7225
7250
|
|
|
7251
|
+
var blockSelector = this.get_config('record_block_selector');
|
|
7252
|
+
if (blockSelector === '' || blockSelector === null) {
|
|
7253
|
+
blockSelector = undefined;
|
|
7254
|
+
}
|
|
7255
|
+
|
|
7226
7256
|
this._stopRecording = record({
|
|
7227
7257
|
'emit': _.bind(function (ev) {
|
|
7228
7258
|
this.batcher.enqueue(ev);
|
|
7229
7259
|
if (isUserEvent(ev)) {
|
|
7230
|
-
if (this.batcher.stopped) {
|
|
7260
|
+
if (this.batcher.stopped && new Date().getTime() - this.replayStartTime >= this.recordMinMs) {
|
|
7231
7261
|
// start flushing again after user activity
|
|
7232
7262
|
this.batcher.start();
|
|
7233
7263
|
}
|
|
@@ -7235,7 +7265,7 @@
|
|
|
7235
7265
|
}
|
|
7236
7266
|
}, this),
|
|
7237
7267
|
'blockClass': this.get_config('record_block_class'),
|
|
7238
|
-
'blockSelector':
|
|
7268
|
+
'blockSelector': blockSelector,
|
|
7239
7269
|
'collectFonts': this.get_config('record_collect_fonts'),
|
|
7240
7270
|
'inlineImages': this.get_config('record_inline_images'),
|
|
7241
7271
|
'maskAllInputs': true,
|
|
@@ -7289,14 +7319,14 @@
|
|
|
7289
7319
|
}
|
|
7290
7320
|
};
|
|
7291
7321
|
|
|
7292
|
-
MixpanelRecorder.prototype._sendRequest = function(reqParams, reqBody, callback) {
|
|
7322
|
+
MixpanelRecorder.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
|
|
7293
7323
|
var onSuccess = _.bind(function (response, responseBody) {
|
|
7294
7324
|
// Increment sequence counter only if the request was successful to guarantee ordering.
|
|
7295
7325
|
// RequestBatcher will always flush the next batch after the previous one succeeds.
|
|
7296
|
-
if
|
|
7326
|
+
// extra check to see if the replay ID has changed so that we don't increment the seqNo on the wrong replay
|
|
7327
|
+
if (response.status === 200 && this.replayId === currentReplayId) {
|
|
7297
7328
|
this.seqNo++;
|
|
7298
7329
|
}
|
|
7299
|
-
|
|
7300
7330
|
callback({
|
|
7301
7331
|
status: 0,
|
|
7302
7332
|
httpStatusCode: response.status,
|
|
@@ -7319,7 +7349,7 @@
|
|
|
7319
7349
|
callback({error: error});
|
|
7320
7350
|
});
|
|
7321
7351
|
}).catch(function (error) {
|
|
7322
|
-
callback({error: error});
|
|
7352
|
+
callback({error: error, httpStatusCode: 0});
|
|
7323
7353
|
});
|
|
7324
7354
|
};
|
|
7325
7355
|
|
|
@@ -7327,9 +7357,15 @@
|
|
|
7327
7357
|
const numEvents = data.length;
|
|
7328
7358
|
|
|
7329
7359
|
if (numEvents > 0) {
|
|
7360
|
+
var replayId = this.replayId;
|
|
7330
7361
|
// each rrweb event has a timestamp - leverage those to get time properties
|
|
7331
7362
|
var batchStartTime = data[0].timestamp;
|
|
7332
|
-
if (this.seqNo === 0) {
|
|
7363
|
+
if (this.seqNo === 0 || !this.replayStartTime) {
|
|
7364
|
+
// extra safety net so that we don't send a null replay start time
|
|
7365
|
+
if (this.seqNo !== 0) {
|
|
7366
|
+
this.reportError('Replay start time not set but seqNo is not 0. Using current batch start time as a fallback.');
|
|
7367
|
+
}
|
|
7368
|
+
|
|
7333
7369
|
this.replayStartTime = batchStartTime;
|
|
7334
7370
|
}
|
|
7335
7371
|
var replayLengthMs = data[numEvents - 1].timestamp - this.replayStartTime;
|
|
@@ -7338,7 +7374,7 @@
|
|
|
7338
7374
|
'distinct_id': String(this._mixpanel.get_distinct_id()),
|
|
7339
7375
|
'seq': this.seqNo,
|
|
7340
7376
|
'batch_start_time': batchStartTime / 1000,
|
|
7341
|
-
'replay_id':
|
|
7377
|
+
'replay_id': replayId,
|
|
7342
7378
|
'replay_length_ms': replayLengthMs,
|
|
7343
7379
|
'replay_start_time': this.replayStartTime / 1000
|
|
7344
7380
|
};
|
|
@@ -7361,11 +7397,11 @@
|
|
|
7361
7397
|
.blob()
|
|
7362
7398
|
.then(_.bind(function(compressedBlob) {
|
|
7363
7399
|
reqParams['format'] = 'gzip';
|
|
7364
|
-
this._sendRequest(reqParams, compressedBlob, callback);
|
|
7400
|
+
this._sendRequest(replayId, reqParams, compressedBlob, callback);
|
|
7365
7401
|
}, this));
|
|
7366
7402
|
} else {
|
|
7367
7403
|
reqParams['format'] = 'body';
|
|
7368
|
-
this._sendRequest(reqParams, eventsJson, callback);
|
|
7404
|
+
this._sendRequest(replayId, reqParams, eventsJson, callback);
|
|
7369
7405
|
}
|
|
7370
7406
|
}
|
|
7371
7407
|
});
|