@sentry/replay 7.53.1 → 7.54.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/cjs/index.js +235 -128
- package/cjs/index.js.map +1 -1
- package/esm/index.js +237 -130
- package/esm/index.js.map +1 -1
- package/package.json +5 -5
- package/types/coreHandlers/handleGlobalEvent.d.ts.map +1 -1
- package/types/coreHandlers/util/addBreadcrumbEvent.d.ts.map +1 -1
- package/types/integration.d.ts +1 -1
- package/types/integration.d.ts.map +1 -1
- package/types/replay.d.ts +20 -2
- package/types/replay.d.ts.map +1 -1
- package/types/types.d.ts +17 -3
- package/types/types.d.ts.map +1 -1
- package/types/util/addGlobalListeners.d.ts.map +1 -1
- package/types/util/createPerformanceSpans.d.ts +1 -1
- package/types/util/createPerformanceSpans.d.ts.map +1 -1
- package/types/util/throttle.d.ts +12 -0
- package/types/util/throttle.d.ts.map +1 -0
package/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getCurrentHub,
|
|
2
|
-
import { GLOBAL_OBJ,
|
|
1
|
+
import { getCurrentHub, addGlobalEventProcessor, prepareEvent, setContext, captureException } from '@sentry/core';
|
|
2
|
+
import { GLOBAL_OBJ, normalize, htmlTreeAsString, logger, uuid4, SENTRY_XHR_DATA_KEY, dropUndefinedKeys, stringMatchesSomePattern, addInstrumentationHandler, browserPerformanceTimeOrigin, createEnvelope, createEventEnvelopeHeaders, getSdkMetadataForEnvelopeHeader, isNodeEnv } from '@sentry/utils';
|
|
3
3
|
|
|
4
4
|
// exporting a separate copy of `WINDOW` rather than exporting the one from `@sentry/browser`
|
|
5
5
|
// prevents the browser package from being bundled in the CDN bundle, and avoids a
|
|
@@ -1133,7 +1133,7 @@ if (typeof window !== 'undefined' && window.Proxy && window.Reflect) {
|
|
|
1133
1133
|
},
|
|
1134
1134
|
});
|
|
1135
1135
|
}
|
|
1136
|
-
function throttle(func, wait, options = {}) {
|
|
1136
|
+
function throttle$1(func, wait, options = {}) {
|
|
1137
1137
|
let timeout = null;
|
|
1138
1138
|
let previous = 0;
|
|
1139
1139
|
return function (arg) {
|
|
@@ -1868,7 +1868,7 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
|
|
|
1868
1868
|
: 500;
|
|
1869
1869
|
let positions = [];
|
|
1870
1870
|
let timeBaseline;
|
|
1871
|
-
const wrappedCb = throttle((source) => {
|
|
1871
|
+
const wrappedCb = throttle$1((source) => {
|
|
1872
1872
|
const totalOffset = Date.now() - timeBaseline;
|
|
1873
1873
|
callbackWrapper(mousemoveCb)(positions.map((p) => {
|
|
1874
1874
|
p.timeOffset -= totalOffset;
|
|
@@ -1877,7 +1877,7 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
|
|
|
1877
1877
|
positions = [];
|
|
1878
1878
|
timeBaseline = null;
|
|
1879
1879
|
}, callbackThreshold);
|
|
1880
|
-
const updatePosition = throttle((evt) => {
|
|
1880
|
+
const updatePosition = throttle$1((evt) => {
|
|
1881
1881
|
const target = getEventTarget(evt);
|
|
1882
1882
|
const { clientX, clientY } = isTouchEvent(evt)
|
|
1883
1883
|
? evt.changedTouches[0]
|
|
@@ -1951,7 +1951,7 @@ function initMouseInteractionObserver({ mouseInteractionCb, doc, mirror, blockCl
|
|
|
1951
1951
|
});
|
|
1952
1952
|
}
|
|
1953
1953
|
function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector, unblockSelector, sampling, }) {
|
|
1954
|
-
const updatePosition = throttle((evt) => {
|
|
1954
|
+
const updatePosition = throttle$1((evt) => {
|
|
1955
1955
|
const target = getEventTarget(evt);
|
|
1956
1956
|
if (!target ||
|
|
1957
1957
|
isBlocked(target, blockClass, blockSelector, unblockSelector)) {
|
|
@@ -1979,7 +1979,7 @@ function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector,
|
|
|
1979
1979
|
function initViewportResizeObserver({ viewportResizeCb, }) {
|
|
1980
1980
|
let lastH = -1;
|
|
1981
1981
|
let lastW = -1;
|
|
1982
|
-
const updateDimension = throttle(() => {
|
|
1982
|
+
const updateDimension = throttle$1(() => {
|
|
1983
1983
|
const height = getWindowHeight();
|
|
1984
1984
|
const width = getWindowWidth();
|
|
1985
1985
|
if (lastH !== height || lastW !== width) {
|
|
@@ -2268,7 +2268,7 @@ function initStyleDeclarationObserver({ styleDeclarationCb, mirror }, { win }) {
|
|
|
2268
2268
|
});
|
|
2269
2269
|
}
|
|
2270
2270
|
function initMediaInteractionObserver({ mediaInteractionCb, blockClass, blockSelector, unblockSelector, mirror, sampling, }) {
|
|
2271
|
-
const handler = (type) => throttle(callbackWrapper((event) => {
|
|
2271
|
+
const handler = (type) => throttle$1(callbackWrapper((event) => {
|
|
2272
2272
|
const target = getEventTarget(event);
|
|
2273
2273
|
if (!target ||
|
|
2274
2274
|
isBlocked(target, blockClass, blockSelector, unblockSelector)) {
|
|
@@ -3285,89 +3285,6 @@ var NodeType;
|
|
|
3285
3285
|
NodeType[NodeType["Comment"] = 5] = "Comment";
|
|
3286
3286
|
})(NodeType || (NodeType = {}));
|
|
3287
3287
|
|
|
3288
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
3289
|
-
|
|
3290
|
-
var EventType; (function (EventType) {
|
|
3291
|
-
const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
|
|
3292
|
-
const Load = 1; EventType[EventType["Load"] = Load] = "Load";
|
|
3293
|
-
const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
|
|
3294
|
-
const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
|
|
3295
|
-
const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
|
|
3296
|
-
const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
|
|
3297
|
-
const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
|
|
3298
|
-
})(EventType || (EventType = {}));
|
|
3299
|
-
|
|
3300
|
-
/**
|
|
3301
|
-
* This is a partial copy of rrweb's eventWithTime type which only contains the properties
|
|
3302
|
-
* we specifcally need in the SDK.
|
|
3303
|
-
*/
|
|
3304
|
-
|
|
3305
|
-
/**
|
|
3306
|
-
* Converts a timestamp to ms, if it was in s, or keeps it as ms.
|
|
3307
|
-
*/
|
|
3308
|
-
function timestampToMs(timestamp) {
|
|
3309
|
-
const isMs = timestamp > 9999999999;
|
|
3310
|
-
return isMs ? timestamp : timestamp * 1000;
|
|
3311
|
-
}
|
|
3312
|
-
|
|
3313
|
-
/**
|
|
3314
|
-
* Add an event to the event buffer.
|
|
3315
|
-
* `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
|
|
3316
|
-
*/
|
|
3317
|
-
async function addEvent(
|
|
3318
|
-
replay,
|
|
3319
|
-
event,
|
|
3320
|
-
isCheckout,
|
|
3321
|
-
) {
|
|
3322
|
-
if (!replay.eventBuffer) {
|
|
3323
|
-
// This implies that `_isEnabled` is false
|
|
3324
|
-
return null;
|
|
3325
|
-
}
|
|
3326
|
-
|
|
3327
|
-
if (replay.isPaused()) {
|
|
3328
|
-
// Do not add to event buffer when recording is paused
|
|
3329
|
-
return null;
|
|
3330
|
-
}
|
|
3331
|
-
|
|
3332
|
-
const timestampInMs = timestampToMs(event.timestamp);
|
|
3333
|
-
|
|
3334
|
-
// Throw out events that happen more than 5 minutes ago. This can happen if
|
|
3335
|
-
// page has been left open and idle for a long period of time and user
|
|
3336
|
-
// comes back to trigger a new session. The performance entries rely on
|
|
3337
|
-
// `performance.timeOrigin`, which is when the page first opened.
|
|
3338
|
-
if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
|
|
3339
|
-
return null;
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
|
-
try {
|
|
3343
|
-
if (isCheckout) {
|
|
3344
|
-
replay.eventBuffer.clear();
|
|
3345
|
-
}
|
|
3346
|
-
|
|
3347
|
-
const replayOptions = replay.getOptions();
|
|
3348
|
-
|
|
3349
|
-
const eventAfterPossibleCallback =
|
|
3350
|
-
typeof replayOptions.beforeAddRecordingEvent === 'function' && event.type === EventType.Custom
|
|
3351
|
-
? replayOptions.beforeAddRecordingEvent(event)
|
|
3352
|
-
: event;
|
|
3353
|
-
|
|
3354
|
-
if (!eventAfterPossibleCallback) {
|
|
3355
|
-
return;
|
|
3356
|
-
}
|
|
3357
|
-
|
|
3358
|
-
return await replay.eventBuffer.addEvent(eventAfterPossibleCallback);
|
|
3359
|
-
} catch (error) {
|
|
3360
|
-
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
|
|
3361
|
-
await replay.stop('addEvent');
|
|
3362
|
-
|
|
3363
|
-
const client = getCurrentHub().getClient();
|
|
3364
|
-
|
|
3365
|
-
if (client) {
|
|
3366
|
-
client.recordDroppedEvent('internal_sdk_error', 'replay');
|
|
3367
|
-
}
|
|
3368
|
-
}
|
|
3369
|
-
}
|
|
3370
|
-
|
|
3371
3288
|
/**
|
|
3372
3289
|
* Add a breadcrumb event to replay.
|
|
3373
3290
|
*/
|
|
@@ -3383,7 +3300,7 @@ function addBreadcrumbEvent(replay, breadcrumb) {
|
|
|
3383
3300
|
}
|
|
3384
3301
|
|
|
3385
3302
|
replay.addUpdate(() => {
|
|
3386
|
-
void
|
|
3303
|
+
void replay.throttledAddEvent({
|
|
3387
3304
|
type: EventType$1.Custom,
|
|
3388
3305
|
// TODO: We were converting from ms to seconds for breadcrumbs, spans,
|
|
3389
3306
|
// but maybe we should just keep them as milliseconds
|
|
@@ -3724,7 +3641,7 @@ function getKeyboardBreadcrumb(event) {
|
|
|
3724
3641
|
const { metaKey, shiftKey, ctrlKey, altKey, key, target } = event;
|
|
3725
3642
|
|
|
3726
3643
|
// never capture for input fields
|
|
3727
|
-
if (!target || isInputElement(target )) {
|
|
3644
|
+
if (!target || isInputElement(target ) || !key) {
|
|
3728
3645
|
return null;
|
|
3729
3646
|
}
|
|
3730
3647
|
|
|
@@ -3896,6 +3813,14 @@ function t(t){let e=t.length;for(;--e>=0;)t[e]=0}const e=new Uint8Array([0,0,0,0
|
|
|
3896
3813
|
|
|
3897
3814
|
function e(){const e=new Blob([r]);return URL.createObjectURL(e)}
|
|
3898
3815
|
|
|
3816
|
+
/**
|
|
3817
|
+
* Converts a timestamp to ms, if it was in s, or keeps it as ms.
|
|
3818
|
+
*/
|
|
3819
|
+
function timestampToMs(timestamp) {
|
|
3820
|
+
const isMs = timestamp > 9999999999;
|
|
3821
|
+
return isMs ? timestamp : timestamp * 1000;
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3899
3824
|
/**
|
|
3900
3825
|
* A basic event buffer that does not do any compression.
|
|
3901
3826
|
* Used as fallback if the compression worker cannot be loaded or is disabled.
|
|
@@ -4473,6 +4398,81 @@ function getSession({
|
|
|
4473
4398
|
return { type: 'new', session: newSession };
|
|
4474
4399
|
}
|
|
4475
4400
|
|
|
4401
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
4402
|
+
|
|
4403
|
+
var EventType; (function (EventType) {
|
|
4404
|
+
const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
|
|
4405
|
+
const Load = 1; EventType[EventType["Load"] = Load] = "Load";
|
|
4406
|
+
const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
|
|
4407
|
+
const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
|
|
4408
|
+
const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
|
|
4409
|
+
const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
|
|
4410
|
+
const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
|
|
4411
|
+
})(EventType || (EventType = {}));
|
|
4412
|
+
|
|
4413
|
+
/**
|
|
4414
|
+
* This is a partial copy of rrweb's eventWithTime type which only contains the properties
|
|
4415
|
+
* we specifcally need in the SDK.
|
|
4416
|
+
*/
|
|
4417
|
+
|
|
4418
|
+
/**
|
|
4419
|
+
* Add an event to the event buffer.
|
|
4420
|
+
* `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
|
|
4421
|
+
*/
|
|
4422
|
+
async function addEvent(
|
|
4423
|
+
replay,
|
|
4424
|
+
event,
|
|
4425
|
+
isCheckout,
|
|
4426
|
+
) {
|
|
4427
|
+
if (!replay.eventBuffer) {
|
|
4428
|
+
// This implies that `_isEnabled` is false
|
|
4429
|
+
return null;
|
|
4430
|
+
}
|
|
4431
|
+
|
|
4432
|
+
if (replay.isPaused()) {
|
|
4433
|
+
// Do not add to event buffer when recording is paused
|
|
4434
|
+
return null;
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4437
|
+
const timestampInMs = timestampToMs(event.timestamp);
|
|
4438
|
+
|
|
4439
|
+
// Throw out events that happen more than 5 minutes ago. This can happen if
|
|
4440
|
+
// page has been left open and idle for a long period of time and user
|
|
4441
|
+
// comes back to trigger a new session. The performance entries rely on
|
|
4442
|
+
// `performance.timeOrigin`, which is when the page first opened.
|
|
4443
|
+
if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
|
|
4444
|
+
return null;
|
|
4445
|
+
}
|
|
4446
|
+
|
|
4447
|
+
try {
|
|
4448
|
+
if (isCheckout) {
|
|
4449
|
+
replay.eventBuffer.clear();
|
|
4450
|
+
}
|
|
4451
|
+
|
|
4452
|
+
const replayOptions = replay.getOptions();
|
|
4453
|
+
|
|
4454
|
+
const eventAfterPossibleCallback =
|
|
4455
|
+
typeof replayOptions.beforeAddRecordingEvent === 'function' && event.type === EventType.Custom
|
|
4456
|
+
? replayOptions.beforeAddRecordingEvent(event)
|
|
4457
|
+
: event;
|
|
4458
|
+
|
|
4459
|
+
if (!eventAfterPossibleCallback) {
|
|
4460
|
+
return;
|
|
4461
|
+
}
|
|
4462
|
+
|
|
4463
|
+
return await replay.eventBuffer.addEvent(eventAfterPossibleCallback);
|
|
4464
|
+
} catch (error) {
|
|
4465
|
+
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.error(error);
|
|
4466
|
+
await replay.stop('addEvent');
|
|
4467
|
+
|
|
4468
|
+
const client = getCurrentHub().getClient();
|
|
4469
|
+
|
|
4470
|
+
if (client) {
|
|
4471
|
+
client.recordDroppedEvent('internal_sdk_error', 'replay');
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
4475
|
+
|
|
4476
4476
|
/** If the event is an error event */
|
|
4477
4477
|
function isErrorEvent(event) {
|
|
4478
4478
|
return !event.type;
|
|
@@ -4647,13 +4647,6 @@ function handleGlobalEventListener(
|
|
|
4647
4647
|
event.tags = { ...event.tags, replayId: replay.getSessionId() };
|
|
4648
4648
|
}
|
|
4649
4649
|
|
|
4650
|
-
if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && replay.getOptions()._experiments.traceInternals && isErrorEvent(event)) {
|
|
4651
|
-
const exc = getEventExceptionValues(event);
|
|
4652
|
-
addInternalBreadcrumb({
|
|
4653
|
-
message: `Tagging event (${event.event_id}) - ${event.message} - ${exc.type}: ${exc.value}`,
|
|
4654
|
-
});
|
|
4655
|
-
}
|
|
4656
|
-
|
|
4657
4650
|
// In cases where a custom client is used that does not support the new hooks (yet),
|
|
4658
4651
|
// we manually call this hook method here
|
|
4659
4652
|
if (afterSendHandler) {
|
|
@@ -4665,34 +4658,15 @@ function handleGlobalEventListener(
|
|
|
4665
4658
|
};
|
|
4666
4659
|
}
|
|
4667
4660
|
|
|
4668
|
-
function addInternalBreadcrumb(arg) {
|
|
4669
|
-
const { category, level, message, ...rest } = arg;
|
|
4670
|
-
|
|
4671
|
-
addBreadcrumb({
|
|
4672
|
-
category: category || 'console',
|
|
4673
|
-
level: level || 'debug',
|
|
4674
|
-
message: `[debug]: ${message}`,
|
|
4675
|
-
...rest,
|
|
4676
|
-
});
|
|
4677
|
-
}
|
|
4678
|
-
|
|
4679
|
-
function getEventExceptionValues(event) {
|
|
4680
|
-
return {
|
|
4681
|
-
type: 'Unknown',
|
|
4682
|
-
value: 'n/a',
|
|
4683
|
-
...(event.exception && event.exception.values && event.exception.values[0]),
|
|
4684
|
-
};
|
|
4685
|
-
}
|
|
4686
|
-
|
|
4687
4661
|
/**
|
|
4688
|
-
* Create a "span" for each performance entry.
|
|
4662
|
+
* Create a "span" for each performance entry.
|
|
4689
4663
|
*/
|
|
4690
4664
|
function createPerformanceSpans(
|
|
4691
4665
|
replay,
|
|
4692
4666
|
entries,
|
|
4693
4667
|
) {
|
|
4694
|
-
return entries.map(({ type, start, end, name, data }) =>
|
|
4695
|
-
|
|
4668
|
+
return entries.map(({ type, start, end, name, data }) => {
|
|
4669
|
+
const response = replay.throttledAddEvent({
|
|
4696
4670
|
type: EventType$1.Custom,
|
|
4697
4671
|
timestamp: start,
|
|
4698
4672
|
data: {
|
|
@@ -4705,8 +4679,11 @@ function createPerformanceSpans(
|
|
|
4705
4679
|
data,
|
|
4706
4680
|
},
|
|
4707
4681
|
},
|
|
4708
|
-
})
|
|
4709
|
-
|
|
4682
|
+
});
|
|
4683
|
+
|
|
4684
|
+
// If response is a string, it means its either THROTTLED or SKIPPED
|
|
4685
|
+
return typeof response === 'string' ? Promise.resolve(null) : response;
|
|
4686
|
+
});
|
|
4710
4687
|
}
|
|
4711
4688
|
|
|
4712
4689
|
function handleHistory(handlerData) {
|
|
@@ -6055,6 +6032,16 @@ function addGlobalListeners(replay) {
|
|
|
6055
6032
|
dsc.replay_id = replayId;
|
|
6056
6033
|
}
|
|
6057
6034
|
});
|
|
6035
|
+
|
|
6036
|
+
client.on('startTransaction', transaction => {
|
|
6037
|
+
replay.lastTransaction = transaction;
|
|
6038
|
+
});
|
|
6039
|
+
|
|
6040
|
+
// We may be missing the initial startTransaction due to timing issues,
|
|
6041
|
+
// so we capture it on finish again.
|
|
6042
|
+
client.on('finishTransaction', transaction => {
|
|
6043
|
+
replay.lastTransaction = transaction;
|
|
6044
|
+
});
|
|
6058
6045
|
}
|
|
6059
6046
|
}
|
|
6060
6047
|
|
|
@@ -6778,6 +6765,62 @@ async function sendReplay(
|
|
|
6778
6765
|
}
|
|
6779
6766
|
}
|
|
6780
6767
|
|
|
6768
|
+
const THROTTLED = '__THROTTLED';
|
|
6769
|
+
const SKIPPED = '__SKIPPED';
|
|
6770
|
+
|
|
6771
|
+
/**
|
|
6772
|
+
* Create a throttled function off a given function.
|
|
6773
|
+
* When calling the throttled function, it will call the original function only
|
|
6774
|
+
* if it hasn't been called more than `maxCount` times in the last `durationSeconds`.
|
|
6775
|
+
*
|
|
6776
|
+
* Returns `THROTTLED` if throttled for the first time, after that `SKIPPED`,
|
|
6777
|
+
* or else the return value of the original function.
|
|
6778
|
+
*/
|
|
6779
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6780
|
+
function throttle(
|
|
6781
|
+
fn,
|
|
6782
|
+
maxCount,
|
|
6783
|
+
durationSeconds,
|
|
6784
|
+
) {
|
|
6785
|
+
const counter = new Map();
|
|
6786
|
+
|
|
6787
|
+
const _cleanup = (now) => {
|
|
6788
|
+
const threshold = now - durationSeconds;
|
|
6789
|
+
counter.forEach((_value, key) => {
|
|
6790
|
+
if (key < threshold) {
|
|
6791
|
+
counter.delete(key);
|
|
6792
|
+
}
|
|
6793
|
+
});
|
|
6794
|
+
};
|
|
6795
|
+
|
|
6796
|
+
const _getTotalCount = () => {
|
|
6797
|
+
return [...counter.values()].reduce((a, b) => a + b, 0);
|
|
6798
|
+
};
|
|
6799
|
+
|
|
6800
|
+
let isThrottled = false;
|
|
6801
|
+
|
|
6802
|
+
return (...rest) => {
|
|
6803
|
+
// Date in second-precision, which we use as basis for the throttling
|
|
6804
|
+
const now = Math.floor(Date.now() / 1000);
|
|
6805
|
+
|
|
6806
|
+
// First, make sure to delete any old entries
|
|
6807
|
+
_cleanup(now);
|
|
6808
|
+
|
|
6809
|
+
// If already over limit, do nothing
|
|
6810
|
+
if (_getTotalCount() >= maxCount) {
|
|
6811
|
+
const wasThrottled = isThrottled;
|
|
6812
|
+
isThrottled = true;
|
|
6813
|
+
return wasThrottled ? SKIPPED : THROTTLED;
|
|
6814
|
+
}
|
|
6815
|
+
|
|
6816
|
+
isThrottled = false;
|
|
6817
|
+
const count = counter.get(now) || 0;
|
|
6818
|
+
counter.set(now, count + 1);
|
|
6819
|
+
|
|
6820
|
+
return fn(...rest);
|
|
6821
|
+
};
|
|
6822
|
+
}
|
|
6823
|
+
|
|
6781
6824
|
/* eslint-disable max-lines */ // TODO: We might want to split this file up
|
|
6782
6825
|
|
|
6783
6826
|
/**
|
|
@@ -6800,6 +6843,11 @@ class ReplayContainer {
|
|
|
6800
6843
|
*/
|
|
6801
6844
|
__init3() {this.recordingMode = 'session';}
|
|
6802
6845
|
|
|
6846
|
+
/**
|
|
6847
|
+
* The current or last active transcation.
|
|
6848
|
+
* This is only available when performance is enabled.
|
|
6849
|
+
*/
|
|
6850
|
+
|
|
6803
6851
|
/**
|
|
6804
6852
|
* These are here so we can overwrite them in tests etc.
|
|
6805
6853
|
* @hidden
|
|
@@ -6866,6 +6914,14 @@ class ReplayContainer {
|
|
|
6866
6914
|
this._debouncedFlush = debounce(() => this._flush(), this._options.flushMinDelay, {
|
|
6867
6915
|
maxWait: this._options.flushMaxDelay,
|
|
6868
6916
|
});
|
|
6917
|
+
|
|
6918
|
+
this._throttledAddEvent = throttle(
|
|
6919
|
+
(event, isCheckout) => addEvent(this, event, isCheckout),
|
|
6920
|
+
// Max 300 events...
|
|
6921
|
+
300,
|
|
6922
|
+
// ... per 5s
|
|
6923
|
+
5,
|
|
6924
|
+
);
|
|
6869
6925
|
}
|
|
6870
6926
|
|
|
6871
6927
|
/** Get the event context. */
|
|
@@ -7295,6 +7351,52 @@ class ReplayContainer {
|
|
|
7295
7351
|
this._context.urls.push(url);
|
|
7296
7352
|
}
|
|
7297
7353
|
|
|
7354
|
+
/**
|
|
7355
|
+
* Add a breadcrumb event, that may be throttled.
|
|
7356
|
+
* If it was throttled, we add a custom breadcrumb to indicate that.
|
|
7357
|
+
*/
|
|
7358
|
+
throttledAddEvent(
|
|
7359
|
+
event,
|
|
7360
|
+
isCheckout,
|
|
7361
|
+
) {
|
|
7362
|
+
const res = this._throttledAddEvent(event, isCheckout);
|
|
7363
|
+
|
|
7364
|
+
// If this is THROTTLED, it means we have throttled the event for the first time
|
|
7365
|
+
// In this case, we want to add a breadcrumb indicating that something was skipped
|
|
7366
|
+
if (res === THROTTLED) {
|
|
7367
|
+
const breadcrumb = createBreadcrumb({
|
|
7368
|
+
category: 'replay.throttled',
|
|
7369
|
+
});
|
|
7370
|
+
|
|
7371
|
+
this.addUpdate(() => {
|
|
7372
|
+
void addEvent(this, {
|
|
7373
|
+
type: EventType$1.Custom,
|
|
7374
|
+
timestamp: breadcrumb.timestamp || 0,
|
|
7375
|
+
data: {
|
|
7376
|
+
tag: 'breadcrumb',
|
|
7377
|
+
payload: breadcrumb,
|
|
7378
|
+
metric: true,
|
|
7379
|
+
},
|
|
7380
|
+
});
|
|
7381
|
+
});
|
|
7382
|
+
}
|
|
7383
|
+
|
|
7384
|
+
return res;
|
|
7385
|
+
}
|
|
7386
|
+
|
|
7387
|
+
/**
|
|
7388
|
+
* This will get the parametrized route name of the current page.
|
|
7389
|
+
* This is only available if performance is enabled, and if an instrumented router is used.
|
|
7390
|
+
*/
|
|
7391
|
+
getCurrentRoute() {
|
|
7392
|
+
const lastTransaction = this.lastTransaction || getCurrentHub().getScope().getTransaction();
|
|
7393
|
+
if (!lastTransaction || !['route', 'custom'].includes(lastTransaction.metadata.source)) {
|
|
7394
|
+
return undefined;
|
|
7395
|
+
}
|
|
7396
|
+
|
|
7397
|
+
return lastTransaction.name;
|
|
7398
|
+
}
|
|
7399
|
+
|
|
7298
7400
|
/**
|
|
7299
7401
|
* Initialize and start all listeners to varying events (DOM,
|
|
7300
7402
|
* Performance Observer, Recording, Sentry SDK, etc)
|
|
@@ -7533,7 +7635,7 @@ class ReplayContainer {
|
|
|
7533
7635
|
*/
|
|
7534
7636
|
_createCustomBreadcrumb(breadcrumb) {
|
|
7535
7637
|
this.addUpdate(() => {
|
|
7536
|
-
void
|
|
7638
|
+
void this.throttledAddEvent({
|
|
7537
7639
|
type: EventType$1.Custom,
|
|
7538
7640
|
timestamp: breadcrumb.timestamp || 0,
|
|
7539
7641
|
data: {
|
|
@@ -7732,8 +7834,8 @@ class ReplayContainer {
|
|
|
7732
7834
|
__init18() {this._onMutationHandler = (mutations) => {
|
|
7733
7835
|
const count = mutations.length;
|
|
7734
7836
|
|
|
7735
|
-
const mutationLimit = this._options.
|
|
7736
|
-
const mutationBreadcrumbLimit = this._options.
|
|
7837
|
+
const mutationLimit = this._options.mutationLimit;
|
|
7838
|
+
const mutationBreadcrumbLimit = this._options.mutationBreadcrumbLimit;
|
|
7737
7839
|
const overMutationLimit = mutationLimit && count > mutationLimit;
|
|
7738
7840
|
|
|
7739
7841
|
// Create a breadcrumb if a lot of mutations happen at the same time
|
|
@@ -7743,15 +7845,15 @@ class ReplayContainer {
|
|
|
7743
7845
|
category: 'replay.mutations',
|
|
7744
7846
|
data: {
|
|
7745
7847
|
count,
|
|
7848
|
+
limit: overMutationLimit,
|
|
7746
7849
|
},
|
|
7747
7850
|
});
|
|
7748
7851
|
this._createCustomBreadcrumb(breadcrumb);
|
|
7749
7852
|
}
|
|
7750
7853
|
|
|
7854
|
+
// Stop replay if over the mutation limit
|
|
7751
7855
|
if (overMutationLimit) {
|
|
7752
|
-
|
|
7753
|
-
// Instead, we do a full snapshot
|
|
7754
|
-
this._triggerFullSnapshot(false);
|
|
7856
|
+
void this.stop('mutationLimit');
|
|
7755
7857
|
return false;
|
|
7756
7858
|
}
|
|
7757
7859
|
|
|
@@ -7905,6 +8007,9 @@ class Replay {
|
|
|
7905
8007
|
maskAllInputs = true,
|
|
7906
8008
|
blockAllMedia = true,
|
|
7907
8009
|
|
|
8010
|
+
mutationBreadcrumbLimit = 750,
|
|
8011
|
+
mutationLimit = 10000,
|
|
8012
|
+
|
|
7908
8013
|
networkDetailAllowUrls = [],
|
|
7909
8014
|
networkCaptureBodies = true,
|
|
7910
8015
|
networkRequestHeaders = [],
|
|
@@ -7972,6 +8077,8 @@ class Replay {
|
|
|
7972
8077
|
blockAllMedia,
|
|
7973
8078
|
maskAllInputs,
|
|
7974
8079
|
maskAllText,
|
|
8080
|
+
mutationBreadcrumbLimit,
|
|
8081
|
+
mutationLimit,
|
|
7975
8082
|
networkDetailAllowUrls,
|
|
7976
8083
|
networkCaptureBodies,
|
|
7977
8084
|
networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
|