mixpanel-browser 2.75.0 → 2.76.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/.claude/settings.local.json +14 -0
- package/.github/workflows/integration-tests.yml +2 -2
- package/.github/workflows/unit-tests.yml +2 -2
- package/CHANGELOG.md +10 -0
- package/build.sh +10 -8
- package/dist/async-modules/mixpanel-recorder-bIS4LMGd.js +23595 -0
- package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js +2 -0
- package/dist/async-modules/mixpanel-recorder-hFoTniVR.min.js.map +1 -0
- package/dist/async-modules/mixpanel-targeting-BcAPS-Mz.js +2520 -0
- package/dist/async-modules/mixpanel-targeting-VOeN7RWY.min.js +2 -0
- package/dist/async-modules/mixpanel-targeting-VOeN7RWY.min.js.map +1 -0
- package/dist/mixpanel-core.cjs.d.ts +68 -0
- package/dist/mixpanel-core.cjs.js +550 -383
- package/dist/mixpanel-recorder.js +708 -32
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-targeting.js +6 -62
- package/dist/mixpanel-targeting.min.js +1 -1
- package/dist/mixpanel-targeting.min.js.map +1 -1
- package/dist/mixpanel-with-async-modules.cjs.d.ts +68 -0
- package/dist/mixpanel-with-async-modules.cjs.js +550 -383
- package/dist/mixpanel-with-async-recorder.cjs.d.ts +68 -0
- package/dist/mixpanel-with-async-recorder.cjs.js +550 -383
- package/dist/mixpanel-with-recorder.d.ts +68 -0
- package/dist/mixpanel-with-recorder.js +1036 -197
- package/dist/mixpanel-with-recorder.min.d.ts +68 -0
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.d.ts +68 -0
- package/dist/mixpanel.amd.js +1038 -251
- package/dist/mixpanel.cjs.d.ts +68 -0
- package/dist/mixpanel.cjs.js +1038 -251
- package/dist/mixpanel.globals.js +550 -383
- package/dist/mixpanel.min.js +184 -181
- package/dist/mixpanel.module.d.ts +68 -0
- package/dist/mixpanel.module.js +1038 -251
- package/dist/mixpanel.umd.d.ts +68 -0
- package/dist/mixpanel.umd.js +1038 -251
- package/logo.svg +5 -0
- package/package.json +2 -1
- package/rollup.config.mjs +163 -46
- package/src/autocapture/index.js +10 -27
- package/src/config.js +9 -3
- package/src/flags/index.js +1 -2
- package/src/index.d.ts +68 -0
- package/src/mixpanel-core.js +76 -111
- package/src/recorder/index.js +1 -1
- package/src/recorder/recorder.js +5 -1
- package/src/recorder/rrweb-network-plugin.js +649 -0
- package/src/recorder/session-recording.js +31 -11
- package/src/recorder-manager.js +216 -0
- package/src/request-batcher.js +1 -1
- package/src/targeting/event-matcher.js +2 -57
- package/src/targeting/index.js +1 -1
- package/src/targeting/loader.js +1 -1
- package/src/utils.js +13 -1
- package/testServer.js +55 -0
- package/src/globals.js +0 -14
|
@@ -26,12 +26,9 @@
|
|
|
26
26
|
win = window;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Recorder library global (used by recorder and mixpanel-core)
|
|
29
|
+
var Config = {
|
|
30
|
+
LIB_VERSION: '2.76.0'
|
|
31
|
+
};
|
|
35
32
|
var RECORDER_GLOBAL_NAME = '__mp_recorder';
|
|
36
33
|
|
|
37
34
|
function _array_like_to_array(arr, len) {
|
|
@@ -18133,7 +18130,7 @@
|
|
|
18133
18130
|
var __publicField = function(obj, key, value) {
|
|
18134
18131
|
return __defNormalProp(obj, (typeof key === "undefined" ? "undefined" : _type_of(key)) !== "symbol" ? key + "" : key, value);
|
|
18135
18132
|
};
|
|
18136
|
-
function patch(source, name, replacement) {
|
|
18133
|
+
function patch$3(source, name, replacement) {
|
|
18137
18134
|
try {
|
|
18138
18135
|
if (!(name in source)) {
|
|
18139
18136
|
return function() {};
|
|
@@ -18550,7 +18547,7 @@
|
|
|
18550
18547
|
if (!_logger[level]) {
|
|
18551
18548
|
return function() {};
|
|
18552
18549
|
}
|
|
18553
|
-
return patch(_logger, level, function(original) {
|
|
18550
|
+
return patch$3(_logger, level, function(original) {
|
|
18554
18551
|
var _this1 = _this;
|
|
18555
18552
|
return function() {
|
|
18556
18553
|
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
@@ -18971,10 +18968,6 @@
|
|
|
18971
18968
|
PromisePolyfill = NpoPromise;
|
|
18972
18969
|
}
|
|
18973
18970
|
|
|
18974
|
-
var Config = {
|
|
18975
|
-
LIB_VERSION: '2.75.0'
|
|
18976
|
-
};
|
|
18977
|
-
|
|
18978
18971
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
18979
18972
|
|
|
18980
18973
|
// Maximum allowed session recording length
|
|
@@ -20640,6 +20633,17 @@
|
|
|
20640
20633
|
|
|
20641
20634
|
var NOOP_FUNC = function () {};
|
|
20642
20635
|
|
|
20636
|
+
var urlMatchesRegexList = function (url, regexList) {
|
|
20637
|
+
var matches = false;
|
|
20638
|
+
for (var i = 0; i < regexList.length; i++) {
|
|
20639
|
+
if (url.match(regexList[i])) {
|
|
20640
|
+
matches = true;
|
|
20641
|
+
break;
|
|
20642
|
+
}
|
|
20643
|
+
}
|
|
20644
|
+
return matches;
|
|
20645
|
+
};
|
|
20646
|
+
|
|
20643
20647
|
var JSONStringify = null, JSONParse = null;
|
|
20644
20648
|
if (typeof JSON !== 'undefined') {
|
|
20645
20649
|
JSONStringify = JSON.stringify;
|
|
@@ -20979,7 +20983,7 @@
|
|
|
20979
20983
|
};
|
|
20980
20984
|
}
|
|
20981
20985
|
|
|
20982
|
-
var logger$
|
|
20986
|
+
var logger$5 = console_with_prefix('lock');
|
|
20983
20987
|
|
|
20984
20988
|
/**
|
|
20985
20989
|
* SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
|
|
@@ -21031,7 +21035,7 @@
|
|
|
21031
21035
|
|
|
21032
21036
|
var delay = function(cb) {
|
|
21033
21037
|
if (new Date().getTime() - startTime > timeoutMS) {
|
|
21034
|
-
logger$
|
|
21038
|
+
logger$5.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
|
|
21035
21039
|
storage.removeItem(keyZ);
|
|
21036
21040
|
storage.removeItem(keyY);
|
|
21037
21041
|
loop();
|
|
@@ -21178,7 +21182,7 @@
|
|
|
21178
21182
|
}, this));
|
|
21179
21183
|
};
|
|
21180
21184
|
|
|
21181
|
-
var logger$
|
|
21185
|
+
var logger$4 = console_with_prefix('batch');
|
|
21182
21186
|
|
|
21183
21187
|
/**
|
|
21184
21188
|
* RequestQueue: queue for batching API requests with localStorage backup for retries.
|
|
@@ -21207,7 +21211,7 @@
|
|
|
21207
21211
|
timeoutMS: options.sharedLockTimeoutMS,
|
|
21208
21212
|
});
|
|
21209
21213
|
}
|
|
21210
|
-
this.reportError = options.errorReporter || _.bind(logger$
|
|
21214
|
+
this.reportError = options.errorReporter || _.bind(logger$4.error, logger$4);
|
|
21211
21215
|
|
|
21212
21216
|
this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
|
|
21213
21217
|
|
|
@@ -21540,7 +21544,7 @@
|
|
|
21540
21544
|
// maximum interval between request retries after exponential backoff
|
|
21541
21545
|
var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
|
21542
21546
|
|
|
21543
|
-
var logger$
|
|
21547
|
+
var logger$3 = console_with_prefix('batch');
|
|
21544
21548
|
|
|
21545
21549
|
/**
|
|
21546
21550
|
* RequestBatcher: manages the queueing, flushing, retry etc of requests of one
|
|
@@ -21668,7 +21672,7 @@
|
|
|
21668
21672
|
*/
|
|
21669
21673
|
RequestBatcher.prototype.flush = function(options) {
|
|
21670
21674
|
if (this.requestInProgress) {
|
|
21671
|
-
logger$
|
|
21675
|
+
logger$3.log('Flush: Request already in progress');
|
|
21672
21676
|
return PromisePolyfill.resolve();
|
|
21673
21677
|
}
|
|
21674
21678
|
|
|
@@ -21845,7 +21849,7 @@
|
|
|
21845
21849
|
if (options.unloading) {
|
|
21846
21850
|
requestOptions.transport = 'sendBeacon';
|
|
21847
21851
|
}
|
|
21848
|
-
logger$
|
|
21852
|
+
logger$3.log('MIXPANEL REQUEST:', dataForRequest);
|
|
21849
21853
|
return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
|
|
21850
21854
|
}, this))
|
|
21851
21855
|
.catch(_.bind(function(err) {
|
|
@@ -21858,7 +21862,7 @@
|
|
|
21858
21862
|
* Log error to global logger and optional user-defined logger.
|
|
21859
21863
|
*/
|
|
21860
21864
|
RequestBatcher.prototype.reportError = function(msg, err) {
|
|
21861
|
-
logger$
|
|
21865
|
+
logger$3.error.apply(logger$3.error, arguments);
|
|
21862
21866
|
if (this.errorReporter) {
|
|
21863
21867
|
try {
|
|
21864
21868
|
if (!(err instanceof Error)) {
|
|
@@ -21866,7 +21870,7 @@
|
|
|
21866
21870
|
}
|
|
21867
21871
|
this.errorReporter(msg, err);
|
|
21868
21872
|
} catch(err) {
|
|
21869
|
-
logger$
|
|
21873
|
+
logger$3.error(err);
|
|
21870
21874
|
}
|
|
21871
21875
|
}
|
|
21872
21876
|
};
|
|
@@ -22084,6 +22088,655 @@
|
|
|
22084
22088
|
}
|
|
22085
22089
|
}
|
|
22086
22090
|
|
|
22091
|
+
/**
|
|
22092
|
+
* This is a port of the open rrweb network plugin in this PR https://github.com/rrweb-io/rrweb/pull/1105
|
|
22093
|
+
* the hope is that eventually this can be replaced with the official plugin once it's published (and we sync the mixpanel rrweb fork)
|
|
22094
|
+
*
|
|
22095
|
+
* This plugin incorporates some important fixes for fetch/XHR body recording that are not yet in the main rrweb repo, as well as makes
|
|
22096
|
+
* header and body recording more restrictive by requiring an allowlist instead of content type / blocklist.
|
|
22097
|
+
*
|
|
22098
|
+
*/
|
|
22099
|
+
|
|
22100
|
+
var logger$2 = console_with_prefix('network-plugin');
|
|
22101
|
+
|
|
22102
|
+
/**
|
|
22103
|
+
* Get the time origin for converting performance timestamps to absolute timestamps.
|
|
22104
|
+
* Uses Date.now() - performance.now() instead of performance.timeOrigin because
|
|
22105
|
+
* browsers can report timeOrigin values that are skewed from actual time, and some
|
|
22106
|
+
* browsers (notably older Safari versions) don't implement timeOrigin at all.
|
|
22107
|
+
* See: https://github.com/getsentry/sentry-javascript/blob/e856e40b6e71a73252e788cd42b5260f81c9c88e/packages/utils/src/time.ts#L49-L70
|
|
22108
|
+
* @param {Window} win
|
|
22109
|
+
* @returns {number}
|
|
22110
|
+
*/
|
|
22111
|
+
function getTimeOrigin(win) {
|
|
22112
|
+
return Math.round(Date.now() - win.performance.now());
|
|
22113
|
+
}
|
|
22114
|
+
|
|
22115
|
+
/**
|
|
22116
|
+
* @typedef {import('../index.d.ts').InitiatorType} InitiatorType
|
|
22117
|
+
* @typedef {import('../index.d.ts').NetworkRequest} NetworkRequest
|
|
22118
|
+
* @typedef {import('../index.d.ts').NetworkRecordOptions} NetworkRecordOptions
|
|
22119
|
+
* @typedef {import('../index.d.ts').NetworkData} NetworkData
|
|
22120
|
+
*/
|
|
22121
|
+
|
|
22122
|
+
/**
|
|
22123
|
+
* @typedef {Record<string, string>} Headers
|
|
22124
|
+
*/
|
|
22125
|
+
|
|
22126
|
+
/**
|
|
22127
|
+
* @typedef {string | Document | Blob | ArrayBufferView | ArrayBuffer | FormData | URLSearchParams | ReadableStream<Uint8Array> | null} Body
|
|
22128
|
+
*/
|
|
22129
|
+
|
|
22130
|
+
/**
|
|
22131
|
+
* @callback networkCallback
|
|
22132
|
+
* @param {NetworkData} data
|
|
22133
|
+
* @returns {void}
|
|
22134
|
+
*/
|
|
22135
|
+
|
|
22136
|
+
/**
|
|
22137
|
+
* @callback listenerHandler
|
|
22138
|
+
* @returns {void}
|
|
22139
|
+
*/
|
|
22140
|
+
|
|
22141
|
+
/**
|
|
22142
|
+
* @typedef {(PerformanceNavigationTiming | PerformanceResourceTiming) & { responseStatus?: number }} ObservedPerformanceEntry
|
|
22143
|
+
*/
|
|
22144
|
+
|
|
22145
|
+
/**
|
|
22146
|
+
* @typedef {Object} RecordPlugin
|
|
22147
|
+
* @property {string} name
|
|
22148
|
+
* @property {(callback: networkCallback, win: Window, options: NetworkRecordOptions) => listenerHandler} observer
|
|
22149
|
+
* @property {NetworkRecordOptions} [options]
|
|
22150
|
+
*/
|
|
22151
|
+
|
|
22152
|
+
/** @type {Required<NetworkRecordOptions>} */
|
|
22153
|
+
var defaultNetworkOptions = {
|
|
22154
|
+
initiatorTypes: [
|
|
22155
|
+
'audio',
|
|
22156
|
+
'beacon',
|
|
22157
|
+
'body',
|
|
22158
|
+
'css',
|
|
22159
|
+
'early-hint',
|
|
22160
|
+
'embed',
|
|
22161
|
+
'fetch',
|
|
22162
|
+
'frame',
|
|
22163
|
+
'iframe',
|
|
22164
|
+
'icon',
|
|
22165
|
+
'image',
|
|
22166
|
+
'img',
|
|
22167
|
+
'input',
|
|
22168
|
+
'link',
|
|
22169
|
+
'navigation',
|
|
22170
|
+
'object',
|
|
22171
|
+
'ping',
|
|
22172
|
+
'script',
|
|
22173
|
+
'track',
|
|
22174
|
+
'video',
|
|
22175
|
+
'xmlhttprequest',
|
|
22176
|
+
],
|
|
22177
|
+
ignoreRequestFn: function() { return false; },
|
|
22178
|
+
recordHeaders: {
|
|
22179
|
+
request: [],
|
|
22180
|
+
response: [],
|
|
22181
|
+
},
|
|
22182
|
+
recordBodyUrls: {
|
|
22183
|
+
request: [],
|
|
22184
|
+
response: [],
|
|
22185
|
+
},
|
|
22186
|
+
recordInitialRequests: false,
|
|
22187
|
+
};
|
|
22188
|
+
|
|
22189
|
+
/**
|
|
22190
|
+
* @param {PerformanceEntry} entry
|
|
22191
|
+
* @returns {entry is PerformanceNavigationTiming}
|
|
22192
|
+
*/
|
|
22193
|
+
function isNavigationTiming(entry) {
|
|
22194
|
+
return entry.entryType === 'navigation';
|
|
22195
|
+
}
|
|
22196
|
+
|
|
22197
|
+
/**
|
|
22198
|
+
* @param {PerformanceEntry} entry
|
|
22199
|
+
* @returns {entry is PerformanceResourceTiming}
|
|
22200
|
+
*/
|
|
22201
|
+
function isResourceTiming (entry) {
|
|
22202
|
+
return entry.entryType === 'resource';
|
|
22203
|
+
}
|
|
22204
|
+
|
|
22205
|
+
function findLast(array, predicate) {
|
|
22206
|
+
var length = array.length;
|
|
22207
|
+
for (var i = length - 1; i >= 0; i -= 1) {
|
|
22208
|
+
if (predicate(array[i])) {
|
|
22209
|
+
return array[i];
|
|
22210
|
+
}
|
|
22211
|
+
}
|
|
22212
|
+
}
|
|
22213
|
+
|
|
22214
|
+
/**
|
|
22215
|
+
* Monkey-patches a method on an object with a wrapped version, returning a function that restores the original.
|
|
22216
|
+
* Adapted from Sentry's `fill` utility:
|
|
22217
|
+
* https://github.com/getsentry/sentry-javascript/blob/de5c5cbe177b4334386e747857225eec36a91ea1/packages/core/src/utils/object.ts#L67-L95
|
|
22218
|
+
*
|
|
22219
|
+
* @param {object} source - The object containing the method to patch
|
|
22220
|
+
* @param {string} name - The method name to patch
|
|
22221
|
+
* @param {function} replacementFactory - A function that receives the original method and returns the replacement
|
|
22222
|
+
* @returns {function} A function that restores the original method
|
|
22223
|
+
*/
|
|
22224
|
+
function patch(source, name, replacementFactory) {
|
|
22225
|
+
if (!(name in source) || typeof source[name] !== 'function') {
|
|
22226
|
+
return function() {};
|
|
22227
|
+
}
|
|
22228
|
+
var original = source[name];
|
|
22229
|
+
var wrapped = replacementFactory(original);
|
|
22230
|
+
source[name] = wrapped;
|
|
22231
|
+
return function() {
|
|
22232
|
+
source[name] = original;
|
|
22233
|
+
};
|
|
22234
|
+
}
|
|
22235
|
+
|
|
22236
|
+
|
|
22237
|
+
/**
|
|
22238
|
+
* Maximum body size to record (1MB)
|
|
22239
|
+
*/
|
|
22240
|
+
var MAX_BODY_SIZE = 1024 * 1024;
|
|
22241
|
+
|
|
22242
|
+
/**
|
|
22243
|
+
* Truncate string if it exceeds max size
|
|
22244
|
+
* @param {string} str
|
|
22245
|
+
* @returns {string}
|
|
22246
|
+
*/
|
|
22247
|
+
function truncateBody(str) {
|
|
22248
|
+
if (!str || typeof str !== 'string') {
|
|
22249
|
+
return str;
|
|
22250
|
+
}
|
|
22251
|
+
if (str.length > MAX_BODY_SIZE) {
|
|
22252
|
+
logger$2.error('Body truncated from ' + str.length + ' to ' + MAX_BODY_SIZE + ' characters');
|
|
22253
|
+
return str.substring(0, MAX_BODY_SIZE) + '... [truncated]';
|
|
22254
|
+
}
|
|
22255
|
+
return str;
|
|
22256
|
+
}
|
|
22257
|
+
|
|
22258
|
+
/**
|
|
22259
|
+
* @param {networkCallback} cb
|
|
22260
|
+
* @param {Window} win
|
|
22261
|
+
* @param {Required<NetworkRecordOptions>} options
|
|
22262
|
+
* @returns {listenerHandler}
|
|
22263
|
+
*/
|
|
22264
|
+
function initPerformanceObserver(cb, win, options) {
|
|
22265
|
+
if (!win.PerformanceObserver) {
|
|
22266
|
+
logger$2.error('PerformanceObserver not supported');
|
|
22267
|
+
return function() {
|
|
22268
|
+
//
|
|
22269
|
+
};
|
|
22270
|
+
}
|
|
22271
|
+
if (options.recordInitialRequests) {
|
|
22272
|
+
var initialPerformanceEntries = win.performance
|
|
22273
|
+
.getEntries()
|
|
22274
|
+
.filter(function(entry) {
|
|
22275
|
+
return isNavigationTiming(entry) ||
|
|
22276
|
+
(isResourceTiming(entry) &&
|
|
22277
|
+
options.initiatorTypes.includes(entry.initiatorType));
|
|
22278
|
+
});
|
|
22279
|
+
cb({
|
|
22280
|
+
requests: initialPerformanceEntries.map(function(entry) {
|
|
22281
|
+
return {
|
|
22282
|
+
url: entry.name,
|
|
22283
|
+
initiatorType: /** @type {InitiatorType} */ (entry.initiatorType),
|
|
22284
|
+
status: 'responseStatus' in entry ? entry.responseStatus : undefined,
|
|
22285
|
+
startTime: Math.round(entry.startTime),
|
|
22286
|
+
endTime: Math.round(entry.responseEnd),
|
|
22287
|
+
timeOrigin: getTimeOrigin(win),
|
|
22288
|
+
};
|
|
22289
|
+
}),
|
|
22290
|
+
isInitial: true,
|
|
22291
|
+
});
|
|
22292
|
+
}
|
|
22293
|
+
var observer = new win.PerformanceObserver(function(entries) {
|
|
22294
|
+
var performanceEntries = entries
|
|
22295
|
+
.getEntries()
|
|
22296
|
+
.filter(function(entry) {
|
|
22297
|
+
return isResourceTiming(entry) &&
|
|
22298
|
+
options.initiatorTypes.includes(entry.initiatorType) &&
|
|
22299
|
+
entry.initiatorType !== 'xmlhttprequest' &&
|
|
22300
|
+
entry.initiatorType !== 'fetch';
|
|
22301
|
+
});
|
|
22302
|
+
cb({
|
|
22303
|
+
requests: performanceEntries.map(function(entry) {
|
|
22304
|
+
return {
|
|
22305
|
+
url: entry.name,
|
|
22306
|
+
initiatorType: /** @type {InitiatorType} */ (entry.initiatorType),
|
|
22307
|
+
status: 'responseStatus' in entry ? entry.responseStatus : undefined,
|
|
22308
|
+
startTime: Math.round(entry.startTime),
|
|
22309
|
+
endTime: Math.round(entry.responseEnd),
|
|
22310
|
+
timeOrigin: getTimeOrigin(win),
|
|
22311
|
+
};
|
|
22312
|
+
}),
|
|
22313
|
+
});
|
|
22314
|
+
});
|
|
22315
|
+
observer.observe({ entryTypes: ['navigation', 'resource'] });
|
|
22316
|
+
return function() {
|
|
22317
|
+
observer.disconnect();
|
|
22318
|
+
};
|
|
22319
|
+
}
|
|
22320
|
+
|
|
22321
|
+
/**
|
|
22322
|
+
* Variation of the original rrweb function that requires an allowlist for headers instead of supporting boolean options
|
|
22323
|
+
* @param {'request' | 'response'} type
|
|
22324
|
+
* @param {NetworkRecordOptions['recordHeaders']} recordHeaders
|
|
22325
|
+
* @param {string} headerName
|
|
22326
|
+
* @returns {boolean}
|
|
22327
|
+
*/
|
|
22328
|
+
function shouldRecordHeader(type, recordHeaders, headerName) {
|
|
22329
|
+
if (!recordHeaders[type] || recordHeaders[type].length === 0) {
|
|
22330
|
+
return false;
|
|
22331
|
+
}
|
|
22332
|
+
|
|
22333
|
+
return recordHeaders[type].includes(headerName.toLowerCase());
|
|
22334
|
+
}
|
|
22335
|
+
|
|
22336
|
+
/**
|
|
22337
|
+
* Variation of the original rrweb function that requires an allowlist for URLs instead of supporting boolean options or by content type
|
|
22338
|
+
* @param {'request' | 'response'} type
|
|
22339
|
+
* @param {NetworkRecordOptions['recordBodyUrls']} recordBodyUrls
|
|
22340
|
+
* @param {string} url
|
|
22341
|
+
* @returns {boolean}
|
|
22342
|
+
*/
|
|
22343
|
+
function shouldRecordBody(type, recordBodyUrls, url) {
|
|
22344
|
+
if (!recordBodyUrls[type] || recordBodyUrls[type].length === 0) {
|
|
22345
|
+
return false;
|
|
22346
|
+
}
|
|
22347
|
+
|
|
22348
|
+
return urlMatchesRegexList(url, recordBodyUrls[type]);
|
|
22349
|
+
}
|
|
22350
|
+
|
|
22351
|
+
function tryReadXHRBody(body) {
|
|
22352
|
+
if (body === null || body === undefined) {
|
|
22353
|
+
return null;
|
|
22354
|
+
}
|
|
22355
|
+
|
|
22356
|
+
var result;
|
|
22357
|
+
if (typeof body === 'string') {
|
|
22358
|
+
result = body;
|
|
22359
|
+
} else if (body instanceof Document) {
|
|
22360
|
+
result = body.textContent;
|
|
22361
|
+
} else if (body instanceof FormData) {
|
|
22362
|
+
result = _.HTTPBuildQuery(body);
|
|
22363
|
+
} else if (_.isObject(body)) {
|
|
22364
|
+
try {
|
|
22365
|
+
result = JSON.stringify(body);
|
|
22366
|
+
} catch (e) {
|
|
22367
|
+
return 'Failed to stringify response object';
|
|
22368
|
+
}
|
|
22369
|
+
} else {
|
|
22370
|
+
return 'Cannot read body of type ' + typeof body;
|
|
22371
|
+
}
|
|
22372
|
+
|
|
22373
|
+
return truncateBody(result);
|
|
22374
|
+
}
|
|
22375
|
+
|
|
22376
|
+
/**
|
|
22377
|
+
* @param {Request | Response} r
|
|
22378
|
+
* @returns {Promise<string>}
|
|
22379
|
+
*/
|
|
22380
|
+
function tryReadFetchBody(r) {
|
|
22381
|
+
return new Promise(function(resolve) {
|
|
22382
|
+
var timeout = setTimeout(function() {
|
|
22383
|
+
resolve('Timeout while trying to read body');
|
|
22384
|
+
}, 500);
|
|
22385
|
+
try {
|
|
22386
|
+
r.clone()
|
|
22387
|
+
.text()
|
|
22388
|
+
.then(
|
|
22389
|
+
function(txt) {
|
|
22390
|
+
clearTimeout(timeout);
|
|
22391
|
+
resolve(truncateBody(txt));
|
|
22392
|
+
},
|
|
22393
|
+
function(reason) {
|
|
22394
|
+
clearTimeout(timeout);
|
|
22395
|
+
resolve('Failed to read body: ' + String(reason));
|
|
22396
|
+
}
|
|
22397
|
+
);
|
|
22398
|
+
} catch (e) {
|
|
22399
|
+
clearTimeout(timeout);
|
|
22400
|
+
resolve('Failed to read body: ' + String(e));
|
|
22401
|
+
}
|
|
22402
|
+
});
|
|
22403
|
+
}
|
|
22404
|
+
|
|
22405
|
+
/**
|
|
22406
|
+
* @param {Window} win
|
|
22407
|
+
* @param {string} initiatorType
|
|
22408
|
+
* @param {string} url
|
|
22409
|
+
* @param {number} [after]
|
|
22410
|
+
* @param {number} [before]
|
|
22411
|
+
* @param {number} [attempt]
|
|
22412
|
+
* @returns {Promise<PerformanceResourceTiming>}
|
|
22413
|
+
*/
|
|
22414
|
+
function getRequestPerformanceEntry(win, initiatorType, url, after, before, attempt) {
|
|
22415
|
+
if (attempt === undefined) {
|
|
22416
|
+
attempt = 0;
|
|
22417
|
+
}
|
|
22418
|
+
if (attempt > 10) {
|
|
22419
|
+
logger$2.error('Cannot find performance entry');
|
|
22420
|
+
return Promise.resolve(null);
|
|
22421
|
+
}
|
|
22422
|
+
var urlPerformanceEntries = /** @type {PerformanceResourceTiming[]} */ (
|
|
22423
|
+
win.performance.getEntriesByName(url)
|
|
22424
|
+
);
|
|
22425
|
+
var performanceEntry = findLast(
|
|
22426
|
+
urlPerformanceEntries,
|
|
22427
|
+
function(entry) {
|
|
22428
|
+
return isResourceTiming(entry) &&
|
|
22429
|
+
entry.initiatorType === initiatorType &&
|
|
22430
|
+
(!after || entry.startTime >= after) &&
|
|
22431
|
+
(!before || entry.startTime <= before);
|
|
22432
|
+
}
|
|
22433
|
+
);
|
|
22434
|
+
if (!performanceEntry) {
|
|
22435
|
+
return new Promise(function(resolve) {
|
|
22436
|
+
setTimeout(resolve, 50 * attempt);
|
|
22437
|
+
}).then(function() {
|
|
22438
|
+
return getRequestPerformanceEntry(
|
|
22439
|
+
win,
|
|
22440
|
+
initiatorType,
|
|
22441
|
+
url,
|
|
22442
|
+
after,
|
|
22443
|
+
before,
|
|
22444
|
+
attempt + 1
|
|
22445
|
+
);
|
|
22446
|
+
});
|
|
22447
|
+
}
|
|
22448
|
+
return Promise.resolve(performanceEntry);
|
|
22449
|
+
}
|
|
22450
|
+
|
|
22451
|
+
/**
|
|
22452
|
+
* @param {networkCallback} cb
|
|
22453
|
+
* @param {Window} win
|
|
22454
|
+
* @param {Required<NetworkRecordOptions>} options
|
|
22455
|
+
* @returns {listenerHandler}
|
|
22456
|
+
*/
|
|
22457
|
+
function initXhrObserver(cb, win, options) {
|
|
22458
|
+
if (!options.initiatorTypes.includes('xmlhttprequest')) {
|
|
22459
|
+
return function() {
|
|
22460
|
+
//
|
|
22461
|
+
};
|
|
22462
|
+
}
|
|
22463
|
+
var restorePatch = patch(
|
|
22464
|
+
win.XMLHttpRequest.prototype,
|
|
22465
|
+
'open',
|
|
22466
|
+
function(/** @type {typeof XMLHttpRequest.prototype.open} */ originalOpen) {
|
|
22467
|
+
return function(
|
|
22468
|
+
/** @type {string} */ method,
|
|
22469
|
+
/** @type {string | URL} */ url,
|
|
22470
|
+
/** @type {boolean} */ async,
|
|
22471
|
+
username, password
|
|
22472
|
+
) {
|
|
22473
|
+
if (async === undefined) {
|
|
22474
|
+
async = true;
|
|
22475
|
+
}
|
|
22476
|
+
var xhr = /** @type {XMLHttpRequest} */ (this);
|
|
22477
|
+
var req = new Request(url, { method: method });
|
|
22478
|
+
/** @type {Partial<NetworkRequest>} */
|
|
22479
|
+
var networkRequest = {};
|
|
22480
|
+
/** @type {number | undefined} */
|
|
22481
|
+
var after;
|
|
22482
|
+
/** @type {number | undefined} */
|
|
22483
|
+
var before;
|
|
22484
|
+
|
|
22485
|
+
/** @type {Headers} */
|
|
22486
|
+
var requestHeaders = {};
|
|
22487
|
+
var originalSetRequestHeader = xhr.setRequestHeader.bind(xhr);
|
|
22488
|
+
xhr.setRequestHeader = function(/** @type {string} */ header, /** @type {string} */ value) {
|
|
22489
|
+
if (shouldRecordHeader('request', options.recordHeaders, header)) {
|
|
22490
|
+
requestHeaders[header] = value;
|
|
22491
|
+
}
|
|
22492
|
+
return originalSetRequestHeader(header, value);
|
|
22493
|
+
};
|
|
22494
|
+
networkRequest.requestHeaders = requestHeaders;
|
|
22495
|
+
|
|
22496
|
+
var originalSend = xhr.send.bind(xhr);
|
|
22497
|
+
xhr.send = function(/** @type {Body} */ body) {
|
|
22498
|
+
if (shouldRecordBody('request', options.recordBodyUrls, req.url)) {
|
|
22499
|
+
networkRequest.requestBody = tryReadXHRBody(body);
|
|
22500
|
+
}
|
|
22501
|
+
after = win.performance.now();
|
|
22502
|
+
return originalSend(body);
|
|
22503
|
+
};
|
|
22504
|
+
xhr.addEventListener('readystatechange', function() {
|
|
22505
|
+
if (xhr.readyState !== xhr.DONE) {
|
|
22506
|
+
return;
|
|
22507
|
+
}
|
|
22508
|
+
before = win.performance.now();
|
|
22509
|
+
/** @type {Headers} */
|
|
22510
|
+
var responseHeaders = {};
|
|
22511
|
+
var rawHeaders = xhr.getAllResponseHeaders();
|
|
22512
|
+
if (rawHeaders) {
|
|
22513
|
+
var headers = rawHeaders.trim().split(/[\r\n]+/);
|
|
22514
|
+
headers.forEach(function(line) {
|
|
22515
|
+
if (!line) return;
|
|
22516
|
+
var colonIndex = line.indexOf(': ');
|
|
22517
|
+
if (colonIndex === -1) return;
|
|
22518
|
+
var header = line.substring(0, colonIndex);
|
|
22519
|
+
var value = line.substring(colonIndex + 2);
|
|
22520
|
+
if (header && shouldRecordHeader('response', options.recordHeaders, header)) {
|
|
22521
|
+
responseHeaders[header] = value;
|
|
22522
|
+
}
|
|
22523
|
+
});
|
|
22524
|
+
}
|
|
22525
|
+
networkRequest.responseHeaders = responseHeaders;
|
|
22526
|
+
if (
|
|
22527
|
+
shouldRecordBody('response', options.recordBodyUrls, req.url)
|
|
22528
|
+
) {
|
|
22529
|
+
networkRequest.responseBody = tryReadXHRBody(xhr.response);
|
|
22530
|
+
}
|
|
22531
|
+
getRequestPerformanceEntry(
|
|
22532
|
+
win,
|
|
22533
|
+
'xmlhttprequest',
|
|
22534
|
+
req.url,
|
|
22535
|
+
after,
|
|
22536
|
+
before
|
|
22537
|
+
)
|
|
22538
|
+
.then(function(entry) {
|
|
22539
|
+
if (!entry) {
|
|
22540
|
+
logger$2.error('Failed to get performance entry for XHR request to ' + req.url);
|
|
22541
|
+
return;
|
|
22542
|
+
}
|
|
22543
|
+
/** @type {NetworkRequest} */
|
|
22544
|
+
var request = {
|
|
22545
|
+
url: entry.name,
|
|
22546
|
+
method: req.method,
|
|
22547
|
+
initiatorType: /** @type {InitiatorType} */ (entry.initiatorType),
|
|
22548
|
+
status: xhr.status,
|
|
22549
|
+
startTime: Math.round(entry.startTime),
|
|
22550
|
+
endTime: Math.round(entry.responseEnd),
|
|
22551
|
+
timeOrigin: getTimeOrigin(win),
|
|
22552
|
+
requestHeaders: networkRequest.requestHeaders,
|
|
22553
|
+
requestBody: networkRequest.requestBody,
|
|
22554
|
+
responseHeaders: networkRequest.responseHeaders,
|
|
22555
|
+
responseBody: networkRequest.responseBody,
|
|
22556
|
+
};
|
|
22557
|
+
cb({ requests: [request] });
|
|
22558
|
+
})
|
|
22559
|
+
.catch(function(e) {
|
|
22560
|
+
logger$2.error('Error recording XHR request to ' + req.url + ': ' + String(e));
|
|
22561
|
+
});
|
|
22562
|
+
});
|
|
22563
|
+
|
|
22564
|
+
originalOpen.call(xhr, method, url, async, username, password);
|
|
22565
|
+
};
|
|
22566
|
+
}
|
|
22567
|
+
);
|
|
22568
|
+
return function() {
|
|
22569
|
+
restorePatch();
|
|
22570
|
+
};
|
|
22571
|
+
}
|
|
22572
|
+
|
|
22573
|
+
/**
|
|
22574
|
+
* @param {networkCallback} cb
|
|
22575
|
+
* @param {Window} win
|
|
22576
|
+
* @param {Required<NetworkRecordOptions>} options
|
|
22577
|
+
* @returns {listenerHandler}
|
|
22578
|
+
*/
|
|
22579
|
+
function initFetchObserver(cb, win, options) {
|
|
22580
|
+
if (!options.initiatorTypes.includes('fetch')) {
|
|
22581
|
+
return function() {
|
|
22582
|
+
//
|
|
22583
|
+
};
|
|
22584
|
+
}
|
|
22585
|
+
|
|
22586
|
+
var restorePatch = patch(win, 'fetch', function(/** @type {typeof fetch} */ originalFetch) {
|
|
22587
|
+
return function() {
|
|
22588
|
+
var req = new Request(arguments[0], arguments[1]);
|
|
22589
|
+
/** @type {Response | undefined} */
|
|
22590
|
+
var res;
|
|
22591
|
+
/** @type {Partial<NetworkRequest>} */
|
|
22592
|
+
var networkRequest = {};
|
|
22593
|
+
/** @type {number | undefined} */
|
|
22594
|
+
var after;
|
|
22595
|
+
/** @type {number | undefined} */
|
|
22596
|
+
var before;
|
|
22597
|
+
|
|
22598
|
+
var originalFetchPromise;
|
|
22599
|
+
var requestBodyPromise = Promise.resolve(undefined);
|
|
22600
|
+
var responseBodyPromise = Promise.resolve(undefined);
|
|
22601
|
+
try {
|
|
22602
|
+
/** @type {Headers} */
|
|
22603
|
+
var requestHeaders = {};
|
|
22604
|
+
req.headers.forEach(function(value, header) {
|
|
22605
|
+
if (shouldRecordHeader('request', options.recordHeaders, header)) {
|
|
22606
|
+
requestHeaders[header] = value;
|
|
22607
|
+
}
|
|
22608
|
+
});
|
|
22609
|
+
networkRequest.requestHeaders = requestHeaders;
|
|
22610
|
+
|
|
22611
|
+
if (shouldRecordBody('request', options.recordBodyUrls, req.url)) {
|
|
22612
|
+
requestBodyPromise = tryReadFetchBody(req)
|
|
22613
|
+
.then(function(body) {
|
|
22614
|
+
networkRequest.requestBody = body;
|
|
22615
|
+
});
|
|
22616
|
+
}
|
|
22617
|
+
|
|
22618
|
+
after = win.performance.now();
|
|
22619
|
+
originalFetchPromise = originalFetch.apply(win, arguments).then(function(response) {
|
|
22620
|
+
res = response;
|
|
22621
|
+
before = win.performance.now();
|
|
22622
|
+
|
|
22623
|
+
/** @type {Headers} */
|
|
22624
|
+
var responseHeaders = {};
|
|
22625
|
+
res.headers.forEach(function(value, header) {
|
|
22626
|
+
if (shouldRecordHeader('response', options.recordHeaders, header)) {
|
|
22627
|
+
responseHeaders[header] = value;
|
|
22628
|
+
}
|
|
22629
|
+
});
|
|
22630
|
+
networkRequest.responseHeaders = responseHeaders;
|
|
22631
|
+
|
|
22632
|
+
if (shouldRecordBody('response', options.recordBodyUrls, req.url)) {
|
|
22633
|
+
responseBodyPromise = tryReadFetchBody(res)
|
|
22634
|
+
.then(function(body) {
|
|
22635
|
+
networkRequest.responseBody = body;
|
|
22636
|
+
});
|
|
22637
|
+
}
|
|
22638
|
+
|
|
22639
|
+
return res;
|
|
22640
|
+
});
|
|
22641
|
+
} catch (e) {
|
|
22642
|
+
originalFetchPromise = Promise.reject(e);
|
|
22643
|
+
}
|
|
22644
|
+
|
|
22645
|
+
// await concurrently so we don't delay the fetch response
|
|
22646
|
+
Promise.all([requestBodyPromise, responseBodyPromise, originalFetchPromise])
|
|
22647
|
+
.then(function () {
|
|
22648
|
+
return getRequestPerformanceEntry(win, 'fetch', req.url, after, before);
|
|
22649
|
+
})
|
|
22650
|
+
.then(function(entry) {
|
|
22651
|
+
if (!entry) {
|
|
22652
|
+
logger$2.error('Failed to get performance entry for fetch request to ' + req.url);
|
|
22653
|
+
return;
|
|
22654
|
+
}
|
|
22655
|
+
/** @type {NetworkRequest} */
|
|
22656
|
+
var request = {
|
|
22657
|
+
url: entry.name,
|
|
22658
|
+
method: req.method,
|
|
22659
|
+
initiatorType: /** @type {InitiatorType} */ (entry.initiatorType),
|
|
22660
|
+
status: res ? res.status : undefined,
|
|
22661
|
+
startTime: Math.round(entry.startTime),
|
|
22662
|
+
endTime: Math.round(entry.responseEnd),
|
|
22663
|
+
timeOrigin: getTimeOrigin(win),
|
|
22664
|
+
requestHeaders: networkRequest.requestHeaders,
|
|
22665
|
+
requestBody: networkRequest.requestBody,
|
|
22666
|
+
responseHeaders: networkRequest.responseHeaders,
|
|
22667
|
+
responseBody: networkRequest.responseBody,
|
|
22668
|
+
};
|
|
22669
|
+
cb({ requests: [request] });
|
|
22670
|
+
})
|
|
22671
|
+
.catch(function (e) {
|
|
22672
|
+
logger$2.error('Error recording fetch request to ' + req.url + ': ' + String(e));
|
|
22673
|
+
});
|
|
22674
|
+
|
|
22675
|
+
return originalFetchPromise;
|
|
22676
|
+
};
|
|
22677
|
+
});
|
|
22678
|
+
return function() {
|
|
22679
|
+
restorePatch();
|
|
22680
|
+
};
|
|
22681
|
+
}
|
|
22682
|
+
|
|
22683
|
+
/**
|
|
22684
|
+
* @param {networkCallback} callback
|
|
22685
|
+
* @param {Window} win
|
|
22686
|
+
* @param {NetworkRecordOptions} options
|
|
22687
|
+
* @returns {listenerHandler}
|
|
22688
|
+
*/
|
|
22689
|
+
function initNetworkObserver(callback, win, options) {
|
|
22690
|
+
if (!('performance' in win)) {
|
|
22691
|
+
return function() {
|
|
22692
|
+
//
|
|
22693
|
+
};
|
|
22694
|
+
}
|
|
22695
|
+
|
|
22696
|
+
var recordHeaders = Object.assign({}, defaultNetworkOptions.recordHeaders, options.recordHeaders || {});
|
|
22697
|
+
var recordBodyUrls = Object.assign({}, defaultNetworkOptions.recordBodyUrls, options.recordBodyUrls || {});
|
|
22698
|
+
options = Object.assign({}, options, {
|
|
22699
|
+
recordHeaders: recordHeaders,
|
|
22700
|
+
recordBodyUrls: recordBodyUrls,
|
|
22701
|
+
});
|
|
22702
|
+
var networkOptions = /** @type {Required<NetworkRecordOptions>} */ Object.assign({}, defaultNetworkOptions, options);
|
|
22703
|
+
|
|
22704
|
+
/** @type {networkCallback} */
|
|
22705
|
+
var cb = function(data) {
|
|
22706
|
+
var requests = data.requests.filter(function(request) {
|
|
22707
|
+
var shouldIgnoreUrl = urlMatchesRegexList(request.url, networkOptions.ignoreRequestUrls || []);
|
|
22708
|
+
return !shouldIgnoreUrl && !networkOptions.ignoreRequestFn(request);
|
|
22709
|
+
});
|
|
22710
|
+
if (requests.length > 0 || data.isInitial) {
|
|
22711
|
+
callback(Object.assign({}, data, { requests: requests }));
|
|
22712
|
+
}
|
|
22713
|
+
};
|
|
22714
|
+
var performanceObserver = initPerformanceObserver(cb, win, networkOptions);
|
|
22715
|
+
var xhrObserver = initXhrObserver(cb, win, networkOptions);
|
|
22716
|
+
var fetchObserver = initFetchObserver(cb, win, networkOptions);
|
|
22717
|
+
return function() {
|
|
22718
|
+
performanceObserver();
|
|
22719
|
+
xhrObserver();
|
|
22720
|
+
fetchObserver();
|
|
22721
|
+
};
|
|
22722
|
+
}
|
|
22723
|
+
|
|
22724
|
+
// arbitrary .mp suffix in case rrweb does publish this plugin later and we use it but need to handle
|
|
22725
|
+
// a changed format in the mixpanel product.
|
|
22726
|
+
var NETWORK_PLUGIN_NAME = 'rrweb/network@1.mp';
|
|
22727
|
+
|
|
22728
|
+
/**
|
|
22729
|
+
* @param {NetworkRecordOptions} [options]
|
|
22730
|
+
* @returns {RecordPlugin}
|
|
22731
|
+
*/
|
|
22732
|
+
var getRecordNetworkPlugin = function(options) {
|
|
22733
|
+
return {
|
|
22734
|
+
name: NETWORK_PLUGIN_NAME,
|
|
22735
|
+
observer: initNetworkObserver,
|
|
22736
|
+
options: options,
|
|
22737
|
+
};
|
|
22738
|
+
};
|
|
22739
|
+
|
|
22087
22740
|
/**
|
|
22088
22741
|
* @typedef {import('../index').RecordPrivacyConfig} RecordPrivacyConfig
|
|
22089
22742
|
*/
|
|
@@ -22317,6 +22970,29 @@
|
|
|
22317
22970
|
|
|
22318
22971
|
var privacyConfig = getPrivacyConfig(this._mixpanel);
|
|
22319
22972
|
|
|
22973
|
+
var plugins = [];
|
|
22974
|
+
if (this.getConfig('record_network')) {
|
|
22975
|
+
var options = this.getConfig('record_network_options') || {};
|
|
22976
|
+
// don't track requests to Mixpanel /record API
|
|
22977
|
+
var ignoreRequestUrls = (options.ignoreRequestUrls || []).slice();
|
|
22978
|
+
ignoreRequestUrls.push(this._getApiRoute());
|
|
22979
|
+
options.ignoreRequestUrls = ignoreRequestUrls;
|
|
22980
|
+
|
|
22981
|
+
plugins.push(getRecordNetworkPlugin(options));
|
|
22982
|
+
}
|
|
22983
|
+
|
|
22984
|
+
if (this.getConfig('record_console')) {
|
|
22985
|
+
plugins.push(
|
|
22986
|
+
getRecordConsolePlugin({
|
|
22987
|
+
stringifyOptions: {
|
|
22988
|
+
stringLengthLimit: 1000,
|
|
22989
|
+
numOfKeysLimit: 50,
|
|
22990
|
+
depthOfLimit: 2
|
|
22991
|
+
}
|
|
22992
|
+
})
|
|
22993
|
+
);
|
|
22994
|
+
}
|
|
22995
|
+
|
|
22320
22996
|
try {
|
|
22321
22997
|
this._stopRecording = this._rrwebRecord({
|
|
22322
22998
|
'emit': function (ev) {
|
|
@@ -22355,15 +23031,7 @@
|
|
|
22355
23031
|
'sampling': {
|
|
22356
23032
|
'canvas': 15
|
|
22357
23033
|
},
|
|
22358
|
-
'plugins':
|
|
22359
|
-
getRecordConsolePlugin({
|
|
22360
|
-
stringifyOptions: {
|
|
22361
|
-
stringLengthLimit: 1000,
|
|
22362
|
-
numOfKeysLimit: 50,
|
|
22363
|
-
depthOfLimit: 2
|
|
22364
|
-
}
|
|
22365
|
-
})
|
|
22366
|
-
] : []
|
|
23034
|
+
'plugins': plugins,
|
|
22367
23035
|
});
|
|
22368
23036
|
} catch (err) {
|
|
22369
23037
|
this.reportError('Unexpected error when starting rrweb recording.', err);
|
|
@@ -22478,6 +23146,10 @@
|
|
|
22478
23146
|
return recording;
|
|
22479
23147
|
};
|
|
22480
23148
|
|
|
23149
|
+
SessionRecording.prototype._getApiRoute = function () {
|
|
23150
|
+
return this.getConfig('api_routes')['record'];
|
|
23151
|
+
};
|
|
23152
|
+
|
|
22481
23153
|
SessionRecording.prototype._sendRequest = function(currentReplayId, reqParams, reqBody, callback) {
|
|
22482
23154
|
var onSuccess = function (response, responseBody) {
|
|
22483
23155
|
// Update batch specific props only if the request was successful to guarantee ordering.
|
|
@@ -22497,7 +23169,7 @@
|
|
|
22497
23169
|
});
|
|
22498
23170
|
}.bind(this);
|
|
22499
23171
|
var apiHost = (this._mixpanel.get_api_host && this._mixpanel.get_api_host('record')) || this.getConfig('api_host');
|
|
22500
|
-
win['fetch'](apiHost + '/' + this.
|
|
23172
|
+
win['fetch'](apiHost + '/' + this._getApiRoute() + '?' + new URLSearchParams(reqParams), {
|
|
22501
23173
|
'method': 'POST',
|
|
22502
23174
|
'headers': {
|
|
22503
23175
|
'Authorization': 'Basic ' + btoa(this.getConfig('token') + ':'),
|
|
@@ -22898,8 +23570,12 @@
|
|
|
22898
23570
|
this.startRecording({shouldStopBatcher: true});
|
|
22899
23571
|
};
|
|
22900
23572
|
|
|
23573
|
+
MixpanelRecorder.prototype.isRecording = function () {
|
|
23574
|
+
return this.activeRecording && !this.activeRecording.isRrwebStopped();
|
|
23575
|
+
};
|
|
23576
|
+
|
|
22901
23577
|
MixpanelRecorder.prototype.getActiveReplayId = function () {
|
|
22902
|
-
if (this.
|
|
23578
|
+
if (this.isRecording()) {
|
|
22903
23579
|
return this.activeRecording.replayId;
|
|
22904
23580
|
} else {
|
|
22905
23581
|
return null;
|