mixpanel-browser 2.61.0 → 2.61.2

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.
@@ -4897,7 +4897,7 @@
4897
4897
 
4898
4898
  var Config = {
4899
4899
  DEBUG: false,
4900
- LIB_VERSION: '2.61.0'
4900
+ LIB_VERSION: '2.61.2'
4901
4901
  };
4902
4902
 
4903
4903
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -6760,7 +6760,7 @@
6760
6760
  return this.dbPromise
6761
6761
  .then(doTransaction)
6762
6762
  .catch(function (err) {
6763
- if (err['name'] === 'InvalidStateError') {
6763
+ if (err && err['name'] === 'InvalidStateError') {
6764
6764
  // try reopening the DB if the connection is closed
6765
6765
  this.dbPromise = this._openDb();
6766
6766
  return this.dbPromise.then(doTransaction);
@@ -8184,34 +8184,37 @@
8184
8184
  blockSelector = undefined;
8185
8185
  }
8186
8186
 
8187
- this._stopRecording = this._rrwebRecord({
8188
- 'emit': addOptOutCheckMixpanelLib(function (ev) {
8189
- if (isUserEvent(ev)) {
8190
- if (this.batcher.stopped && new Date().getTime() - this.replayStartTime >= this.recordMinMs) {
8191
- // start flushing again after user activity
8192
- this.batcher.start();
8187
+ try {
8188
+ this._stopRecording = this._rrwebRecord({
8189
+ 'emit': function (ev) {
8190
+ if (isUserEvent(ev)) {
8191
+ if (this.batcher.stopped && new Date().getTime() - this.replayStartTime >= this.recordMinMs) {
8192
+ // start flushing again after user activity
8193
+ this.batcher.start();
8194
+ }
8195
+ resetIdleTimeout();
8193
8196
  }
8194
- resetIdleTimeout();
8197
+ // promise only used to await during tests
8198
+ this.__enqueuePromise = this.batcher.enqueue(ev);
8199
+ }.bind(this),
8200
+ 'blockClass': this.getConfig('record_block_class'),
8201
+ 'blockSelector': blockSelector,
8202
+ 'collectFonts': this.getConfig('record_collect_fonts'),
8203
+ 'dataURLOptions': { // canvas image options (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL)
8204
+ 'type': 'image/webp',
8205
+ 'quality': 0.6
8206
+ },
8207
+ 'maskAllInputs': true,
8208
+ 'maskTextClass': this.getConfig('record_mask_text_class'),
8209
+ 'maskTextSelector': this.getConfig('record_mask_text_selector'),
8210
+ 'recordCanvas': this.getConfig('record_canvas'),
8211
+ 'sampling': {
8212
+ 'canvas': 15
8195
8213
  }
8196
-
8197
- // promise only used to await during tests
8198
- this.__enqueuePromise = this.batcher.enqueue(ev);
8199
- }.bind(this)),
8200
- 'blockClass': this.getConfig('record_block_class'),
8201
- 'blockSelector': blockSelector,
8202
- 'collectFonts': this.getConfig('record_collect_fonts'),
8203
- 'dataURLOptions': { // canvas image options (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL)
8204
- 'type': 'image/webp',
8205
- 'quality': 0.6
8206
- },
8207
- 'maskAllInputs': true,
8208
- 'maskTextClass': this.getConfig('record_mask_text_class'),
8209
- 'maskTextSelector': this.getConfig('record_mask_text_selector'),
8210
- 'recordCanvas': this.getConfig('record_canvas'),
8211
- 'sampling': {
8212
- 'canvas': 15
8213
- }
8214
- });
8214
+ });
8215
+ } catch (err) {
8216
+ this.reportError('Unexpected error when starting rrweb recording.', err);
8217
+ }
8215
8218
 
8216
8219
  if (typeof this._stopRecording !== 'function') {
8217
8220
  this.reportError('rrweb failed to start, skipping this recording.');
@@ -8255,12 +8258,21 @@
8255
8258
  return this._stopRecording === null;
8256
8259
  };
8257
8260
 
8261
+
8258
8262
  /**
8259
8263
  * Flushes the current batch of events to the server, but passes an opt-out callback to make sure
8260
8264
  * we stop recording and dump any queued events if the user has opted out.
8261
8265
  */
8262
8266
  SessionRecording.prototype.flushEventsWithOptOut = function (data, options, cb) {
8263
- this._flushEvents(data, options, cb, this._onOptOut.bind(this));
8267
+ var onOptOut = function (code) {
8268
+ // addOptOutCheckMixpanelLib invokes this function with code=0 when the user has opted out
8269
+ if (code === 0) {
8270
+ this.stopRecording();
8271
+ cb({error: 'Tracking has been opted out, stopping recording.'});
8272
+ }
8273
+ }.bind(this);
8274
+
8275
+ this._flushEvents(data, options, cb, onOptOut);
8264
8276
  };
8265
8277
 
8266
8278
  /**
@@ -8310,13 +8322,6 @@
8310
8322
  return recording;
8311
8323
  };
8312
8324
 
8313
- SessionRecording.prototype._onOptOut = function (code) {
8314
- // addOptOutCheckMixpanelLib invokes this function with code=0 when the user has opted out
8315
- if (code === 0) {
8316
- this.stopRecording();
8317
- }
8318
- };
8319
-
8320
8325
  SessionRecording.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
8321
8326
  var onSuccess = function (response, responseBody) {
8322
8327
  // Update batch specific props only if the request was successful to guarantee ordering.
@@ -8359,17 +8364,32 @@
8359
8364
 
8360
8365
  if (numEvents > 0) {
8361
8366
  var replayId = this.replayId;
8367
+
8362
8368
  // each rrweb event has a timestamp - leverage those to get time properties
8363
- var batchStartTime = data[0].timestamp;
8364
- if (this.seqNo === 0 || !this.replayStartTime) {
8365
- // extra safety net so that we don't send a null replay start time
8366
- if (this.seqNo !== 0) {
8367
- this.reportError('Replay start time not set but seqNo is not 0. Using current batch start time as a fallback.');
8369
+ var batchStartTime = Infinity;
8370
+ var batchEndTime = -Infinity;
8371
+ var hasFullSnapshot = false;
8372
+ for (var i = 0; i < numEvents; i++) {
8373
+ batchStartTime = Math.min(batchStartTime, data[i].timestamp);
8374
+ batchEndTime = Math.max(batchEndTime, data[i].timestamp);
8375
+ if (data[i].type === EventType.FullSnapshot) {
8376
+ hasFullSnapshot = true;
8368
8377
  }
8378
+ }
8369
8379
 
8380
+ if (this.seqNo === 0) {
8381
+ if (!hasFullSnapshot) {
8382
+ callback({error: 'First batch does not contain a full snapshot. Aborting recording.'});
8383
+ this.stopRecording(true);
8384
+ return;
8385
+ }
8386
+ this.replayStartTime = batchStartTime;
8387
+ } else if (!this.replayStartTime) {
8388
+ this.reportError('Replay start time not set but seqNo is not 0. Using current batch start time as a fallback.');
8370
8389
  this.replayStartTime = batchStartTime;
8371
8390
  }
8372
- var replayLengthMs = data[numEvents - 1].timestamp - this.replayStartTime;
8391
+
8392
+ var replayLengthMs = batchEndTime - this.replayStartTime;
8373
8393
 
8374
8394
  var reqParams = {
8375
8395
  '$current_url': this.batchStartUrl,
@@ -11669,7 +11689,6 @@
11669
11689
  }, this),
11670
11690
  stopAllBatchingFunc: _.bind(this.stop_batch_senders, this),
11671
11691
  usePersistence: true,
11672
- enqueueThrottleMs: 10,
11673
11692
  }
11674
11693
  );
11675
11694
  }, this);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.61.0",
3
+ "version": "2.61.2",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "module": "dist/mixpanel.module.js",
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  var Config = {
2
2
  DEBUG: false,
3
- LIB_VERSION: '2.61.0'
3
+ LIB_VERSION: '2.61.2'
4
4
  };
5
5
 
6
6
  export default Config;
@@ -863,7 +863,6 @@ MixpanelLib.prototype.init_batchers = function() {
863
863
  }, this),
864
864
  stopAllBatchingFunc: _.bind(this.stop_batch_senders, this),
865
865
  usePersistence: true,
866
- enqueueThrottleMs: 10,
867
866
  }
868
867
  );
869
868
  }, this);
@@ -193,34 +193,37 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
193
193
  blockSelector = undefined;
194
194
  }
195
195
 
196
- this._stopRecording = this._rrwebRecord({
197
- 'emit': addOptOutCheckMixpanelLib(function (ev) {
198
- if (isUserEvent(ev)) {
199
- if (this.batcher.stopped && new Date().getTime() - this.replayStartTime >= this.recordMinMs) {
200
- // start flushing again after user activity
201
- this.batcher.start();
196
+ try {
197
+ this._stopRecording = this._rrwebRecord({
198
+ 'emit': function (ev) {
199
+ if (isUserEvent(ev)) {
200
+ if (this.batcher.stopped && new Date().getTime() - this.replayStartTime >= this.recordMinMs) {
201
+ // start flushing again after user activity
202
+ this.batcher.start();
203
+ }
204
+ resetIdleTimeout();
202
205
  }
203
- resetIdleTimeout();
206
+ // promise only used to await during tests
207
+ this.__enqueuePromise = this.batcher.enqueue(ev);
208
+ }.bind(this),
209
+ 'blockClass': this.getConfig('record_block_class'),
210
+ 'blockSelector': blockSelector,
211
+ 'collectFonts': this.getConfig('record_collect_fonts'),
212
+ 'dataURLOptions': { // canvas image options (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL)
213
+ 'type': 'image/webp',
214
+ 'quality': 0.6
215
+ },
216
+ 'maskAllInputs': true,
217
+ 'maskTextClass': this.getConfig('record_mask_text_class'),
218
+ 'maskTextSelector': this.getConfig('record_mask_text_selector'),
219
+ 'recordCanvas': this.getConfig('record_canvas'),
220
+ 'sampling': {
221
+ 'canvas': 15
204
222
  }
205
-
206
- // promise only used to await during tests
207
- this.__enqueuePromise = this.batcher.enqueue(ev);
208
- }.bind(this)),
209
- 'blockClass': this.getConfig('record_block_class'),
210
- 'blockSelector': blockSelector,
211
- 'collectFonts': this.getConfig('record_collect_fonts'),
212
- 'dataURLOptions': { // canvas image options (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL)
213
- 'type': 'image/webp',
214
- 'quality': 0.6
215
- },
216
- 'maskAllInputs': true,
217
- 'maskTextClass': this.getConfig('record_mask_text_class'),
218
- 'maskTextSelector': this.getConfig('record_mask_text_selector'),
219
- 'recordCanvas': this.getConfig('record_canvas'),
220
- 'sampling': {
221
- 'canvas': 15
222
- }
223
- });
223
+ });
224
+ } catch (err) {
225
+ this.reportError('Unexpected error when starting rrweb recording.', err);
226
+ }
224
227
 
225
228
  if (typeof this._stopRecording !== 'function') {
226
229
  this.reportError('rrweb failed to start, skipping this recording.');
@@ -264,12 +267,21 @@ SessionRecording.prototype.isRrwebStopped = function () {
264
267
  return this._stopRecording === null;
265
268
  };
266
269
 
270
+
267
271
  /**
268
272
  * Flushes the current batch of events to the server, but passes an opt-out callback to make sure
269
273
  * we stop recording and dump any queued events if the user has opted out.
270
274
  */
271
275
  SessionRecording.prototype.flushEventsWithOptOut = function (data, options, cb) {
272
- this._flushEvents(data, options, cb, this._onOptOut.bind(this));
276
+ var onOptOut = function (code) {
277
+ // addOptOutCheckMixpanelLib invokes this function with code=0 when the user has opted out
278
+ if (code === 0) {
279
+ this.stopRecording();
280
+ cb({error: 'Tracking has been opted out, stopping recording.'});
281
+ }
282
+ }.bind(this);
283
+
284
+ this._flushEvents(data, options, cb, onOptOut);
273
285
  };
274
286
 
275
287
  /**
@@ -319,13 +331,6 @@ SessionRecording.deserialize = function (serializedRecording, options) {
319
331
  return recording;
320
332
  };
321
333
 
322
- SessionRecording.prototype._onOptOut = function (code) {
323
- // addOptOutCheckMixpanelLib invokes this function with code=0 when the user has opted out
324
- if (code === 0) {
325
- this.stopRecording();
326
- }
327
- };
328
-
329
334
  SessionRecording.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
330
335
  var onSuccess = function (response, responseBody) {
331
336
  // Update batch specific props only if the request was successful to guarantee ordering.
@@ -368,17 +373,32 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
368
373
 
369
374
  if (numEvents > 0) {
370
375
  var replayId = this.replayId;
376
+
371
377
  // each rrweb event has a timestamp - leverage those to get time properties
372
- var batchStartTime = data[0].timestamp;
373
- if (this.seqNo === 0 || !this.replayStartTime) {
374
- // extra safety net so that we don't send a null replay start time
375
- if (this.seqNo !== 0) {
376
- this.reportError('Replay start time not set but seqNo is not 0. Using current batch start time as a fallback.');
378
+ var batchStartTime = Infinity;
379
+ var batchEndTime = -Infinity;
380
+ var hasFullSnapshot = false;
381
+ for (var i = 0; i < numEvents; i++) {
382
+ batchStartTime = Math.min(batchStartTime, data[i].timestamp);
383
+ batchEndTime = Math.max(batchEndTime, data[i].timestamp);
384
+ if (data[i].type === EventType.FullSnapshot) {
385
+ hasFullSnapshot = true;
377
386
  }
387
+ }
378
388
 
389
+ if (this.seqNo === 0) {
390
+ if (!hasFullSnapshot) {
391
+ callback({error: 'First batch does not contain a full snapshot. Aborting recording.'});
392
+ this.stopRecording(true);
393
+ return;
394
+ }
395
+ this.replayStartTime = batchStartTime;
396
+ } else if (!this.replayStartTime) {
397
+ this.reportError('Replay start time not set but seqNo is not 0. Using current batch start time as a fallback.');
379
398
  this.replayStartTime = batchStartTime;
380
399
  }
381
- var replayLengthMs = data[numEvents - 1].timestamp - this.replayStartTime;
400
+
401
+ var replayLengthMs = batchEndTime - this.replayStartTime;
382
402
 
383
403
  var reqParams = {
384
404
  '$current_url': this.batchStartUrl,
@@ -84,7 +84,7 @@ IDBStorageWrapper.prototype.makeTransaction = function (mode, storeCb) {
84
84
  return this.dbPromise
85
85
  .then(doTransaction)
86
86
  .catch(function (err) {
87
- if (err['name'] === 'InvalidStateError') {
87
+ if (err && err['name'] === 'InvalidStateError') {
88
88
  // try reopening the DB if the connection is closed
89
89
  this.dbPromise = this._openDb();
90
90
  return this.dbPromise.then(doTransaction);