mixpanel-browser 2.55.1 → 2.56.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/.eslintrc.json +31 -1
- package/CHANGELOG.md +8 -0
- package/dist/mixpanel-core.cjs.js +32 -10
- package/dist/mixpanel-recorder.js +162 -74
- package/dist/mixpanel-recorder.min.js +8 -7
- package/dist/mixpanel-recorder.min.js.map +1 -0
- package/dist/mixpanel-with-async-recorder.cjs.js +32 -10
- package/dist/mixpanel.amd.js +188 -80
- package/dist/mixpanel.cjs.js +188 -80
- package/dist/mixpanel.globals.js +32 -10
- package/dist/mixpanel.min.js +106 -105
- package/dist/mixpanel.min.js.map +8 -0
- package/dist/mixpanel.module.js +188 -80
- package/dist/mixpanel.umd.js +188 -80
- package/package.json +1 -1
- package/src/config.js +1 -1
- package/src/mixpanel-core.js +26 -6
- package/src/recorder/index.js +39 -252
- package/src/recorder/rollup.config.js +2 -1
- package/src/recorder/session-recording.js +305 -0
- package/src/request-queue.js +5 -3
package/dist/mixpanel.umd.js
CHANGED
|
@@ -4513,7 +4513,7 @@
|
|
|
4513
4513
|
|
|
4514
4514
|
var Config = {
|
|
4515
4515
|
DEBUG: false,
|
|
4516
|
-
LIB_VERSION: '2.
|
|
4516
|
+
LIB_VERSION: '2.56.0'
|
|
4517
4517
|
};
|
|
4518
4518
|
|
|
4519
4519
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -6550,7 +6550,7 @@
|
|
|
6550
6550
|
};
|
|
6551
6551
|
}
|
|
6552
6552
|
|
|
6553
|
-
var logger$
|
|
6553
|
+
var logger$4 = console_with_prefix('lock');
|
|
6554
6554
|
|
|
6555
6555
|
/**
|
|
6556
6556
|
* SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
|
|
@@ -6607,7 +6607,7 @@
|
|
|
6607
6607
|
|
|
6608
6608
|
var delay = function(cb) {
|
|
6609
6609
|
if (new Date().getTime() - startTime > timeoutMS) {
|
|
6610
|
-
logger$
|
|
6610
|
+
logger$4.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
|
|
6611
6611
|
storage.removeItem(keyZ);
|
|
6612
6612
|
storage.removeItem(keyY);
|
|
6613
6613
|
loop();
|
|
@@ -6696,7 +6696,7 @@
|
|
|
6696
6696
|
}
|
|
6697
6697
|
};
|
|
6698
6698
|
|
|
6699
|
-
var logger$
|
|
6699
|
+
var logger$3 = console_with_prefix('batch');
|
|
6700
6700
|
|
|
6701
6701
|
/**
|
|
6702
6702
|
* RequestQueue: queue for batching API requests with localStorage backup for retries.
|
|
@@ -6717,11 +6717,13 @@
|
|
|
6717
6717
|
var RequestQueue = function(storageKey, options) {
|
|
6718
6718
|
options = options || {};
|
|
6719
6719
|
this.storageKey = storageKey;
|
|
6720
|
-
this.storage = options.storage || window.localStorage;
|
|
6721
|
-
this.reportError = options.errorReporter || _.bind(logger$2.error, logger$2);
|
|
6722
|
-
this.lock = new SharedLock(storageKey, {storage: this.storage});
|
|
6723
|
-
|
|
6724
6720
|
this.usePersistence = options.usePersistence;
|
|
6721
|
+
if (this.usePersistence) {
|
|
6722
|
+
this.storage = options.storage || window.localStorage;
|
|
6723
|
+
this.lock = new SharedLock(storageKey, {storage: this.storage});
|
|
6724
|
+
}
|
|
6725
|
+
this.reportError = options.errorReporter || _.bind(logger$3.error, logger$3);
|
|
6726
|
+
|
|
6725
6727
|
this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
|
|
6726
6728
|
|
|
6727
6729
|
this.memQueue = [];
|
|
@@ -7000,7 +7002,7 @@
|
|
|
7000
7002
|
// maximum interval between request retries after exponential backoff
|
|
7001
7003
|
var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
|
7002
7004
|
|
|
7003
|
-
var logger$
|
|
7005
|
+
var logger$2 = console_with_prefix('batch');
|
|
7004
7006
|
|
|
7005
7007
|
/**
|
|
7006
7008
|
* RequestBatcher: manages the queueing, flushing, retry etc of requests of one
|
|
@@ -7114,7 +7116,7 @@
|
|
|
7114
7116
|
try {
|
|
7115
7117
|
|
|
7116
7118
|
if (this.requestInProgress) {
|
|
7117
|
-
logger$
|
|
7119
|
+
logger$2.log('Flush: Request already in progress');
|
|
7118
7120
|
return;
|
|
7119
7121
|
}
|
|
7120
7122
|
|
|
@@ -7282,7 +7284,7 @@
|
|
|
7282
7284
|
if (options.unloading) {
|
|
7283
7285
|
requestOptions.transport = 'sendBeacon';
|
|
7284
7286
|
}
|
|
7285
|
-
logger$
|
|
7287
|
+
logger$2.log('MIXPANEL REQUEST:', dataForRequest);
|
|
7286
7288
|
this.sendRequest(dataForRequest, requestOptions, batchSendCallback);
|
|
7287
7289
|
} catch(err) {
|
|
7288
7290
|
this.reportError('Error flushing request queue', err);
|
|
@@ -7294,7 +7296,7 @@
|
|
|
7294
7296
|
* Log error to global logger and optional user-defined logger.
|
|
7295
7297
|
*/
|
|
7296
7298
|
RequestBatcher.prototype.reportError = function(msg, err) {
|
|
7297
|
-
logger$
|
|
7299
|
+
logger$2.error.apply(logger$2.error, arguments);
|
|
7298
7300
|
if (this.errorReporter) {
|
|
7299
7301
|
try {
|
|
7300
7302
|
if (!(err instanceof Error)) {
|
|
@@ -7302,12 +7304,12 @@
|
|
|
7302
7304
|
}
|
|
7303
7305
|
this.errorReporter(msg, err);
|
|
7304
7306
|
} catch(err) {
|
|
7305
|
-
logger$
|
|
7307
|
+
logger$2.error(err);
|
|
7306
7308
|
}
|
|
7307
7309
|
}
|
|
7308
7310
|
};
|
|
7309
7311
|
|
|
7310
|
-
var logger = console_with_prefix('recorder');
|
|
7312
|
+
var logger$1 = console_with_prefix('recorder');
|
|
7311
7313
|
var CompressionStream = win['CompressionStream'];
|
|
7312
7314
|
|
|
7313
7315
|
var RECORDER_BATCHER_LIB_CONFIG = {
|
|
@@ -7333,65 +7335,78 @@
|
|
|
7333
7335
|
return ev.type === EventType.IncrementalSnapshot && ACTIVE_SOURCES.has(ev.data.source);
|
|
7334
7336
|
}
|
|
7335
7337
|
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
+
/**
|
|
7339
|
+
* This class encapsulates a single session recording and its lifecycle.
|
|
7340
|
+
* @param {Object} [options.mixpanelInstance] - reference to the core MixpanelLib
|
|
7341
|
+
* @param {String} [options.replayId] - unique uuid for a single replay
|
|
7342
|
+
* @param {Function} [options.onIdleTimeout] - callback when a recording reaches idle timeout
|
|
7343
|
+
* @param {Function} [options.onMaxLengthReached] - callback when a recording reaches its maximum length
|
|
7344
|
+
* @param {Function} [options.rrwebRecord] - rrweb's `record` function
|
|
7345
|
+
*/
|
|
7346
|
+
var SessionRecording = function(options) {
|
|
7347
|
+
this._mixpanel = options.mixpanelInstance;
|
|
7348
|
+
this._onIdleTimeout = options.onIdleTimeout;
|
|
7349
|
+
this._onMaxLengthReached = options.onMaxLengthReached;
|
|
7350
|
+
this._rrwebRecord = options.rrwebRecord;
|
|
7351
|
+
|
|
7352
|
+
this.replayId = options.replayId;
|
|
7338
7353
|
|
|
7339
7354
|
// internal rrweb stopRecording function
|
|
7340
7355
|
this._stopRecording = null;
|
|
7341
7356
|
|
|
7342
|
-
this.recEvents = [];
|
|
7343
7357
|
this.seqNo = 0;
|
|
7344
|
-
this.replayId = null;
|
|
7345
7358
|
this.replayStartTime = null;
|
|
7346
|
-
this.
|
|
7359
|
+
this.batchStartUrl = null;
|
|
7347
7360
|
|
|
7348
7361
|
this.idleTimeoutId = null;
|
|
7349
7362
|
this.maxTimeoutId = null;
|
|
7350
7363
|
|
|
7351
7364
|
this.recordMaxMs = MAX_RECORDING_MS;
|
|
7352
7365
|
this.recordMinMs = 0;
|
|
7353
|
-
this._initBatcher();
|
|
7354
|
-
};
|
|
7355
7366
|
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
sendRequestFunc: _.bind(this.flushEventsWithOptOut, this),
|
|
7367
|
+
// each replay has its own batcher key to avoid conflicts between rrweb events of different recordings
|
|
7368
|
+
// this will be important when persistence is introduced
|
|
7369
|
+
var batcherKey = '__mprec_' + this.getConfig('token') + '_' + this.replayId;
|
|
7370
|
+
this.batcher = new RequestBatcher(batcherKey, {
|
|
7361
7371
|
errorReporter: _.bind(this.reportError, this),
|
|
7362
7372
|
flushOnlyOnInterval: true,
|
|
7373
|
+
libConfig: RECORDER_BATCHER_LIB_CONFIG,
|
|
7374
|
+
sendRequestFunc: _.bind(this.flushEventsWithOptOut, this),
|
|
7363
7375
|
usePersistence: false
|
|
7364
7376
|
});
|
|
7365
7377
|
};
|
|
7366
7378
|
|
|
7367
|
-
|
|
7368
|
-
MixpanelRecorder.prototype.get_config = function(configVar) {
|
|
7379
|
+
SessionRecording.prototype.getConfig = function(configVar) {
|
|
7369
7380
|
return this._mixpanel.get_config(configVar);
|
|
7370
7381
|
};
|
|
7371
7382
|
|
|
7372
|
-
|
|
7383
|
+
// Alias for getConfig, used by the common addOptOutCheckMixpanelLib function which
|
|
7384
|
+
// reaches into this class instance and expects the snake case version of the function.
|
|
7385
|
+
// eslint-disable-next-line camelcase
|
|
7386
|
+
SessionRecording.prototype.get_config = function(configVar) {
|
|
7387
|
+
return this.getConfig(configVar);
|
|
7388
|
+
};
|
|
7389
|
+
|
|
7390
|
+
SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
|
|
7373
7391
|
if (this._stopRecording !== null) {
|
|
7374
|
-
logger.log('Recording already in progress, skipping startRecording.');
|
|
7392
|
+
logger$1.log('Recording already in progress, skipping startRecording.');
|
|
7375
7393
|
return;
|
|
7376
7394
|
}
|
|
7377
7395
|
|
|
7378
|
-
this.recordMaxMs = this.
|
|
7396
|
+
this.recordMaxMs = this.getConfig('record_max_ms');
|
|
7379
7397
|
if (this.recordMaxMs > MAX_RECORDING_MS) {
|
|
7380
7398
|
this.recordMaxMs = MAX_RECORDING_MS;
|
|
7381
|
-
logger.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
|
|
7399
|
+
logger$1.critical('record_max_ms cannot be greater than ' + MAX_RECORDING_MS + 'ms. Capping value.');
|
|
7382
7400
|
}
|
|
7383
7401
|
|
|
7384
|
-
this.recordMinMs = this.
|
|
7402
|
+
this.recordMinMs = this.getConfig('record_min_ms');
|
|
7385
7403
|
if (this.recordMinMs > MAX_VALUE_FOR_MIN_RECORDING_MS) {
|
|
7386
7404
|
this.recordMinMs = MAX_VALUE_FOR_MIN_RECORDING_MS;
|
|
7387
|
-
logger.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
|
|
7405
|
+
logger$1.critical('record_min_ms cannot be greater than ' + MAX_VALUE_FOR_MIN_RECORDING_MS + 'ms. Capping value.');
|
|
7388
7406
|
}
|
|
7389
7407
|
|
|
7390
|
-
this.recEvents = [];
|
|
7391
|
-
this.seqNo = 0;
|
|
7392
7408
|
this.replayStartTime = new Date().getTime();
|
|
7393
|
-
|
|
7394
|
-
this.replayId = _.UUID();
|
|
7409
|
+
this.batchStartUrl = _.info.currentUrl();
|
|
7395
7410
|
|
|
7396
7411
|
if (shouldStopBatcher || this.recordMinMs > 0) {
|
|
7397
7412
|
// the primary case for shouldStopBatcher is when we're starting recording after a reset
|
|
@@ -7406,18 +7421,15 @@
|
|
|
7406
7421
|
|
|
7407
7422
|
var resetIdleTimeout = _.bind(function () {
|
|
7408
7423
|
clearTimeout(this.idleTimeoutId);
|
|
7409
|
-
this.idleTimeoutId = setTimeout(
|
|
7410
|
-
logger.log('Idle timeout reached, restarting recording.');
|
|
7411
|
-
this.resetRecording();
|
|
7412
|
-
}, this), this.get_config('record_idle_timeout_ms'));
|
|
7424
|
+
this.idleTimeoutId = setTimeout(this._onIdleTimeout, this.getConfig('record_idle_timeout_ms'));
|
|
7413
7425
|
}, this);
|
|
7414
7426
|
|
|
7415
|
-
var blockSelector = this.
|
|
7427
|
+
var blockSelector = this.getConfig('record_block_selector');
|
|
7416
7428
|
if (blockSelector === '' || blockSelector === null) {
|
|
7417
7429
|
blockSelector = undefined;
|
|
7418
7430
|
}
|
|
7419
7431
|
|
|
7420
|
-
this._stopRecording =
|
|
7432
|
+
this._stopRecording = this._rrwebRecord({
|
|
7421
7433
|
'emit': _.bind(function (ev) {
|
|
7422
7434
|
this.batcher.enqueue(ev);
|
|
7423
7435
|
if (isUserEvent(ev)) {
|
|
@@ -7428,28 +7440,33 @@
|
|
|
7428
7440
|
resetIdleTimeout();
|
|
7429
7441
|
}
|
|
7430
7442
|
}, this),
|
|
7431
|
-
'blockClass': this.
|
|
7443
|
+
'blockClass': this.getConfig('record_block_class'),
|
|
7432
7444
|
'blockSelector': blockSelector,
|
|
7433
|
-
'collectFonts': this.
|
|
7434
|
-
'inlineImages': this.get_config('record_inline_images'),
|
|
7445
|
+
'collectFonts': this.getConfig('record_collect_fonts'),
|
|
7435
7446
|
'maskAllInputs': true,
|
|
7436
|
-
'maskTextClass': this.
|
|
7437
|
-
'maskTextSelector': this.
|
|
7447
|
+
'maskTextClass': this.getConfig('record_mask_text_class'),
|
|
7448
|
+
'maskTextSelector': this.getConfig('record_mask_text_selector')
|
|
7438
7449
|
});
|
|
7439
7450
|
|
|
7440
|
-
|
|
7451
|
+
if (typeof this._stopRecording !== 'function') {
|
|
7452
|
+
this.reportError('rrweb failed to start, skipping this recording.');
|
|
7453
|
+
this._stopRecording = null;
|
|
7454
|
+
this.stopRecording(); // stop batcher looping and any timeouts
|
|
7455
|
+
return;
|
|
7456
|
+
}
|
|
7441
7457
|
|
|
7442
|
-
|
|
7443
|
-
};
|
|
7458
|
+
resetIdleTimeout();
|
|
7444
7459
|
|
|
7445
|
-
|
|
7446
|
-
this.stopRecording();
|
|
7447
|
-
this.startRecording(true);
|
|
7460
|
+
this.maxTimeoutId = setTimeout(_.bind(this._onMaxLengthReached, this), this.recordMaxMs);
|
|
7448
7461
|
};
|
|
7449
7462
|
|
|
7450
|
-
|
|
7451
|
-
if (this.
|
|
7452
|
-
|
|
7463
|
+
SessionRecording.prototype.stopRecording = function () {
|
|
7464
|
+
if (!this.isRrwebStopped()) {
|
|
7465
|
+
try {
|
|
7466
|
+
this._stopRecording();
|
|
7467
|
+
} catch (err) {
|
|
7468
|
+
this.reportError('Error with rrweb stopRecording', err);
|
|
7469
|
+
}
|
|
7453
7470
|
this._stopRecording = null;
|
|
7454
7471
|
}
|
|
7455
7472
|
|
|
@@ -7461,35 +7478,38 @@
|
|
|
7461
7478
|
this.batcher.flush();
|
|
7462
7479
|
this.batcher.stop();
|
|
7463
7480
|
}
|
|
7464
|
-
this.replayId = null;
|
|
7465
7481
|
|
|
7466
7482
|
clearTimeout(this.idleTimeoutId);
|
|
7467
7483
|
clearTimeout(this.maxTimeoutId);
|
|
7468
7484
|
};
|
|
7469
7485
|
|
|
7486
|
+
SessionRecording.prototype.isRrwebStopped = function () {
|
|
7487
|
+
return this._stopRecording === null;
|
|
7488
|
+
};
|
|
7489
|
+
|
|
7470
7490
|
/**
|
|
7471
7491
|
* Flushes the current batch of events to the server, but passes an opt-out callback to make sure
|
|
7472
7492
|
* we stop recording and dump any queued events if the user has opted out.
|
|
7473
7493
|
*/
|
|
7474
|
-
|
|
7494
|
+
SessionRecording.prototype.flushEventsWithOptOut = function (data, options, cb) {
|
|
7475
7495
|
this._flushEvents(data, options, cb, _.bind(this._onOptOut, this));
|
|
7476
7496
|
};
|
|
7477
7497
|
|
|
7478
|
-
|
|
7498
|
+
SessionRecording.prototype._onOptOut = function (code) {
|
|
7479
7499
|
// addOptOutCheckMixpanelLib invokes this function with code=0 when the user has opted out
|
|
7480
7500
|
if (code === 0) {
|
|
7481
|
-
this.recEvents = [];
|
|
7482
7501
|
this.stopRecording();
|
|
7483
7502
|
}
|
|
7484
7503
|
};
|
|
7485
7504
|
|
|
7486
|
-
|
|
7505
|
+
SessionRecording.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
|
|
7487
7506
|
var onSuccess = _.bind(function (response, responseBody) {
|
|
7488
|
-
//
|
|
7507
|
+
// Update batch specific props only if the request was successful to guarantee ordering.
|
|
7489
7508
|
// RequestBatcher will always flush the next batch after the previous one succeeds.
|
|
7490
7509
|
// extra check to see if the replay ID has changed so that we don't increment the seqNo on the wrong replay
|
|
7491
7510
|
if (response.status === 200 && this.replayId === currentReplayId) {
|
|
7492
7511
|
this.seqNo++;
|
|
7512
|
+
this.batchStartUrl = _.info.currentUrl();
|
|
7493
7513
|
}
|
|
7494
7514
|
callback({
|
|
7495
7515
|
status: 0,
|
|
@@ -7499,10 +7519,10 @@
|
|
|
7499
7519
|
});
|
|
7500
7520
|
}, this);
|
|
7501
7521
|
|
|
7502
|
-
win['fetch'](this.
|
|
7522
|
+
win['fetch'](this.getConfig('api_host') + '/' + this.getConfig('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
|
|
7503
7523
|
'method': 'POST',
|
|
7504
7524
|
'headers': {
|
|
7505
|
-
'Authorization': 'Basic ' + btoa(this.
|
|
7525
|
+
'Authorization': 'Basic ' + btoa(this.getConfig('token') + ':'),
|
|
7506
7526
|
'Content-Type': 'application/octet-stream'
|
|
7507
7527
|
},
|
|
7508
7528
|
'body': reqBody,
|
|
@@ -7517,7 +7537,7 @@
|
|
|
7517
7537
|
});
|
|
7518
7538
|
};
|
|
7519
7539
|
|
|
7520
|
-
|
|
7540
|
+
SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (data, options, callback) {
|
|
7521
7541
|
const numEvents = data.length;
|
|
7522
7542
|
|
|
7523
7543
|
if (numEvents > 0) {
|
|
@@ -7535,12 +7555,15 @@
|
|
|
7535
7555
|
var replayLengthMs = data[numEvents - 1].timestamp - this.replayStartTime;
|
|
7536
7556
|
|
|
7537
7557
|
var reqParams = {
|
|
7538
|
-
'
|
|
7539
|
-
'
|
|
7558
|
+
'$current_url': this.batchStartUrl,
|
|
7559
|
+
'$lib_version': Config.LIB_VERSION,
|
|
7540
7560
|
'batch_start_time': batchStartTime / 1000,
|
|
7561
|
+
'distinct_id': String(this._mixpanel.get_distinct_id()),
|
|
7562
|
+
'mp_lib': 'web',
|
|
7541
7563
|
'replay_id': replayId,
|
|
7542
7564
|
'replay_length_ms': replayLengthMs,
|
|
7543
|
-
'replay_start_time': this.replayStartTime / 1000
|
|
7565
|
+
'replay_start_time': this.replayStartTime / 1000,
|
|
7566
|
+
'seq': this.seqNo
|
|
7544
7567
|
};
|
|
7545
7568
|
var eventsJson = _.JSONEncode(data);
|
|
7546
7569
|
|
|
@@ -7571,18 +7594,83 @@
|
|
|
7571
7594
|
});
|
|
7572
7595
|
|
|
7573
7596
|
|
|
7574
|
-
|
|
7575
|
-
logger.error.apply(logger.error, arguments);
|
|
7597
|
+
SessionRecording.prototype.reportError = function(msg, err) {
|
|
7598
|
+
logger$1.error.apply(logger$1.error, arguments);
|
|
7576
7599
|
try {
|
|
7577
7600
|
if (!err && !(msg instanceof Error)) {
|
|
7578
7601
|
msg = new Error(msg);
|
|
7579
7602
|
}
|
|
7580
|
-
this.
|
|
7603
|
+
this.getConfig('error_reporter')(msg, err);
|
|
7581
7604
|
} catch(err) {
|
|
7582
|
-
logger.error(err);
|
|
7605
|
+
logger$1.error(err);
|
|
7606
|
+
}
|
|
7607
|
+
};
|
|
7608
|
+
|
|
7609
|
+
var logger = console_with_prefix('recorder');
|
|
7610
|
+
|
|
7611
|
+
/**
|
|
7612
|
+
* Recorder API: manages recordings and exposes methods public to the core Mixpanel library.
|
|
7613
|
+
* @param {Object} [options.mixpanelInstance] - reference to the core MixpanelLib
|
|
7614
|
+
*/
|
|
7615
|
+
var MixpanelRecorder = function(mixpanelInstance) {
|
|
7616
|
+
this._mixpanel = mixpanelInstance;
|
|
7617
|
+
this.activeRecording = null;
|
|
7618
|
+
};
|
|
7619
|
+
|
|
7620
|
+
MixpanelRecorder.prototype.startRecording = function(shouldStopBatcher) {
|
|
7621
|
+
if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
|
|
7622
|
+
logger.log('Recording already in progress, skipping startRecording.');
|
|
7623
|
+
return;
|
|
7624
|
+
}
|
|
7625
|
+
|
|
7626
|
+
var onIdleTimeout = _.bind(function () {
|
|
7627
|
+
logger.log('Idle timeout reached, restarting recording.');
|
|
7628
|
+
this.resetRecording();
|
|
7629
|
+
}, this);
|
|
7630
|
+
|
|
7631
|
+
var onMaxLengthReached = _.bind(function () {
|
|
7632
|
+
logger.log('Max recording length reached, stopping recording.');
|
|
7633
|
+
this.resetRecording();
|
|
7634
|
+
}, this);
|
|
7635
|
+
|
|
7636
|
+
this.activeRecording = new SessionRecording({
|
|
7637
|
+
mixpanelInstance: this._mixpanel,
|
|
7638
|
+
onIdleTimeout: onIdleTimeout,
|
|
7639
|
+
onMaxLengthReached: onMaxLengthReached,
|
|
7640
|
+
replayId: _.UUID(),
|
|
7641
|
+
rrwebRecord: record
|
|
7642
|
+
});
|
|
7643
|
+
|
|
7644
|
+
this.activeRecording.startRecording(shouldStopBatcher);
|
|
7645
|
+
};
|
|
7646
|
+
|
|
7647
|
+
MixpanelRecorder.prototype.stopRecording = function() {
|
|
7648
|
+
if (this.activeRecording) {
|
|
7649
|
+
this.activeRecording.stopRecording();
|
|
7650
|
+
this.activeRecording = null;
|
|
7583
7651
|
}
|
|
7584
7652
|
};
|
|
7585
7653
|
|
|
7654
|
+
MixpanelRecorder.prototype.resetRecording = function () {
|
|
7655
|
+
this.stopRecording();
|
|
7656
|
+
this.startRecording(true);
|
|
7657
|
+
};
|
|
7658
|
+
|
|
7659
|
+
MixpanelRecorder.prototype.getActiveReplayId = function () {
|
|
7660
|
+
if (this.activeRecording && !this.activeRecording.isRrwebStopped()) {
|
|
7661
|
+
return this.activeRecording.replayId;
|
|
7662
|
+
} else {
|
|
7663
|
+
return null;
|
|
7664
|
+
}
|
|
7665
|
+
};
|
|
7666
|
+
|
|
7667
|
+
// getter so that older mixpanel-core versions can still retrieve the replay ID
|
|
7668
|
+
// when pulling the latest recorder bundle from the CDN
|
|
7669
|
+
Object.defineProperty(MixpanelRecorder.prototype, 'replayId', {
|
|
7670
|
+
get: function () {
|
|
7671
|
+
return this.getActiveReplayId();
|
|
7672
|
+
}
|
|
7673
|
+
});
|
|
7586
7674
|
|
|
7587
7675
|
win['__mp_recorder'] = MixpanelRecorder;
|
|
7588
7676
|
|
|
@@ -9049,7 +9137,6 @@
|
|
|
9049
9137
|
'record_block_selector': 'img, video',
|
|
9050
9138
|
'record_collect_fonts': false,
|
|
9051
9139
|
'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
|
|
9052
|
-
'record_inline_images': false,
|
|
9053
9140
|
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
|
|
9054
9141
|
'record_mask_text_selector': '*',
|
|
9055
9142
|
'record_max_ms': MAX_RECORDING_MS,
|
|
@@ -9302,15 +9389,35 @@
|
|
|
9302
9389
|
|
|
9303
9390
|
MixpanelLib.prototype.get_session_recording_properties = function () {
|
|
9304
9391
|
var props = {};
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
props['$mp_replay_id'] = replay_id;
|
|
9309
|
-
}
|
|
9392
|
+
var replay_id = this._get_session_replay_id();
|
|
9393
|
+
if (replay_id) {
|
|
9394
|
+
props['$mp_replay_id'] = replay_id;
|
|
9310
9395
|
}
|
|
9311
9396
|
return props;
|
|
9312
9397
|
};
|
|
9313
9398
|
|
|
9399
|
+
MixpanelLib.prototype.get_session_replay_url = function () {
|
|
9400
|
+
var replay_url = null;
|
|
9401
|
+
var replay_id = this._get_session_replay_id();
|
|
9402
|
+
if (replay_id) {
|
|
9403
|
+
var query_params = _.HTTPBuildQuery({
|
|
9404
|
+
'replay_id': replay_id,
|
|
9405
|
+
'distinct_id': this.get_distinct_id(),
|
|
9406
|
+
'token': this.get_config('token')
|
|
9407
|
+
});
|
|
9408
|
+
replay_url = 'https://mixpanel.com/projects/replay-redirect?' + query_params;
|
|
9409
|
+
}
|
|
9410
|
+
return replay_url;
|
|
9411
|
+
};
|
|
9412
|
+
|
|
9413
|
+
MixpanelLib.prototype._get_session_replay_id = function () {
|
|
9414
|
+
var replay_id = null;
|
|
9415
|
+
if (this._recorder) {
|
|
9416
|
+
replay_id = this._recorder['replayId'];
|
|
9417
|
+
}
|
|
9418
|
+
return replay_id || null;
|
|
9419
|
+
};
|
|
9420
|
+
|
|
9314
9421
|
// Private methods
|
|
9315
9422
|
|
|
9316
9423
|
MixpanelLib.prototype._loaded = function() {
|
|
@@ -11037,6 +11144,7 @@
|
|
|
11037
11144
|
MixpanelLib.prototype['start_session_recording'] = MixpanelLib.prototype.start_session_recording;
|
|
11038
11145
|
MixpanelLib.prototype['stop_session_recording'] = MixpanelLib.prototype.stop_session_recording;
|
|
11039
11146
|
MixpanelLib.prototype['get_session_recording_properties'] = MixpanelLib.prototype.get_session_recording_properties;
|
|
11147
|
+
MixpanelLib.prototype['get_session_replay_url'] = MixpanelLib.prototype.get_session_replay_url;
|
|
11040
11148
|
MixpanelLib.prototype['DEFAULT_API_ROUTES'] = DEFAULT_API_ROUTES;
|
|
11041
11149
|
|
|
11042
11150
|
// MixpanelPersistence Exports
|
package/package.json
CHANGED
package/src/config.js
CHANGED
package/src/mixpanel-core.js
CHANGED
|
@@ -148,7 +148,6 @@ var DEFAULT_CONFIG = {
|
|
|
148
148
|
'record_block_selector': 'img, video',
|
|
149
149
|
'record_collect_fonts': false,
|
|
150
150
|
'record_idle_timeout_ms': 30 * 60 * 1000, // 30 minutes
|
|
151
|
-
'record_inline_images': false,
|
|
152
151
|
'record_mask_text_class': new RegExp('^(mp-mask|fs-mask|amp-mask|rr-mask|ph-mask)$'),
|
|
153
152
|
'record_mask_text_selector': '*',
|
|
154
153
|
'record_max_ms': MAX_RECORDING_MS,
|
|
@@ -401,15 +400,35 @@ MixpanelLib.prototype.stop_session_recording = function () {
|
|
|
401
400
|
|
|
402
401
|
MixpanelLib.prototype.get_session_recording_properties = function () {
|
|
403
402
|
var props = {};
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
props['$mp_replay_id'] = replay_id;
|
|
408
|
-
}
|
|
403
|
+
var replay_id = this._get_session_replay_id();
|
|
404
|
+
if (replay_id) {
|
|
405
|
+
props['$mp_replay_id'] = replay_id;
|
|
409
406
|
}
|
|
410
407
|
return props;
|
|
411
408
|
};
|
|
412
409
|
|
|
410
|
+
MixpanelLib.prototype.get_session_replay_url = function () {
|
|
411
|
+
var replay_url = null;
|
|
412
|
+
var replay_id = this._get_session_replay_id();
|
|
413
|
+
if (replay_id) {
|
|
414
|
+
var query_params = _.HTTPBuildQuery({
|
|
415
|
+
'replay_id': replay_id,
|
|
416
|
+
'distinct_id': this.get_distinct_id(),
|
|
417
|
+
'token': this.get_config('token')
|
|
418
|
+
});
|
|
419
|
+
replay_url = 'https://mixpanel.com/projects/replay-redirect?' + query_params;
|
|
420
|
+
}
|
|
421
|
+
return replay_url;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
MixpanelLib.prototype._get_session_replay_id = function () {
|
|
425
|
+
var replay_id = null;
|
|
426
|
+
if (this._recorder) {
|
|
427
|
+
replay_id = this._recorder['replayId'];
|
|
428
|
+
}
|
|
429
|
+
return replay_id || null;
|
|
430
|
+
};
|
|
431
|
+
|
|
413
432
|
// Private methods
|
|
414
433
|
|
|
415
434
|
MixpanelLib.prototype._loaded = function() {
|
|
@@ -2136,6 +2155,7 @@ MixpanelLib.prototype['stop_batch_senders'] = MixpanelLib.protot
|
|
|
2136
2155
|
MixpanelLib.prototype['start_session_recording'] = MixpanelLib.prototype.start_session_recording;
|
|
2137
2156
|
MixpanelLib.prototype['stop_session_recording'] = MixpanelLib.prototype.stop_session_recording;
|
|
2138
2157
|
MixpanelLib.prototype['get_session_recording_properties'] = MixpanelLib.prototype.get_session_recording_properties;
|
|
2158
|
+
MixpanelLib.prototype['get_session_replay_url'] = MixpanelLib.prototype.get_session_replay_url;
|
|
2139
2159
|
MixpanelLib.prototype['DEFAULT_API_ROUTES'] = DEFAULT_API_ROUTES;
|
|
2140
2160
|
|
|
2141
2161
|
// MixpanelPersistence Exports
|