@sentry/replay 7.53.1 → 7.55.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 +344 -196
- package/cjs/index.js.map +1 -1
- package/esm/index.js +324 -176
- package/esm/index.js.map +1 -1
- package/package.json +5 -5
- package/types/constants.d.ts +2 -0
- package/types/constants.d.ts.map +1 -1
- package/types/coreHandlers/handleDom.d.ts.map +1 -1
- package/types/coreHandlers/handleGlobalEvent.d.ts.map +1 -1
- package/types/coreHandlers/handleKeyboardEvent.d.ts.map +1 -1
- package/types/coreHandlers/handleScope.d.ts +4 -1
- package/types/coreHandlers/handleScope.d.ts.map +1 -1
- package/types/coreHandlers/handleSlowClick.d.ts +2 -0
- package/types/coreHandlers/handleSlowClick.d.ts.map +1 -1
- package/types/coreHandlers/util/addBreadcrumbEvent.d.ts.map +1 -1
- package/types/coreHandlers/util/getAttributesToRecord.d.ts.map +1 -1
- package/types/index.d.ts +5 -0
- package/types/index.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 +28 -2
- package/types/replay.d.ts.map +1 -1
- package/types/session/getSession.d.ts.map +1 -1
- package/types/types/index.d.ts +6 -0
- package/types/types/index.d.ts.map +1 -0
- package/types/types/performance.d.ts +124 -0
- package/types/types/performance.d.ts.map +1 -0
- package/types/{types.d.ts → types/replay.d.ts} +37 -146
- package/types/types/replay.d.ts.map +1 -0
- package/types/types/replayFrame.d.ts +160 -0
- package/types/types/replayFrame.d.ts.map +1 -0
- package/types/types/request.d.ts +15 -0
- package/types/types/request.d.ts.map +1 -0
- package/types/types/rrweb.d.ts +1 -9
- package/types/types/rrweb.d.ts.map +1 -1
- package/types/util/addEvent.d.ts.map +1 -1
- package/types/util/addGlobalListeners.d.ts.map +1 -1
- package/types/util/createBreadcrumb.d.ts +2 -4
- package/types/util/createBreadcrumb.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/handleRecordingEmit.d.ts +2 -2
- package/types/util/handleRecordingEmit.d.ts.map +1 -1
- package/types/util/throttle.d.ts +12 -0
- package/types/util/throttle.d.ts.map +1 -0
- package/types/types.d.ts.map +0 -1
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
|
|
@@ -38,6 +38,11 @@ const NETWORK_BODY_MAX_SIZE = 150000;
|
|
|
38
38
|
/* The max size of a single console arg that is captured. Any arg larger than this will be truncated. */
|
|
39
39
|
const CONSOLE_ARG_MAX_SIZE = 5000;
|
|
40
40
|
|
|
41
|
+
/* Min. time to wait before we consider something a slow click. */
|
|
42
|
+
const SLOW_CLICK_THRESHOLD = 3000;
|
|
43
|
+
/* For scroll actions after a click, we only look for a very short time period to detect programmatic scrolling. */
|
|
44
|
+
const SLOW_CLICK_SCROLL_TIMEOUT = 300;
|
|
45
|
+
|
|
41
46
|
var NodeType$1;
|
|
42
47
|
(function (NodeType) {
|
|
43
48
|
NodeType[NodeType["Document"] = 0] = "Document";
|
|
@@ -991,7 +996,7 @@ function skipAttribute(tagName, attributeName, value) {
|
|
|
991
996
|
return ((tagName === 'video' || tagName === 'audio') && attributeName === 'autoplay');
|
|
992
997
|
}
|
|
993
998
|
|
|
994
|
-
var EventType
|
|
999
|
+
var EventType;
|
|
995
1000
|
(function (EventType) {
|
|
996
1001
|
EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
|
|
997
1002
|
EventType[EventType["Load"] = 1] = "Load";
|
|
@@ -1000,7 +1005,7 @@ var EventType$1;
|
|
|
1000
1005
|
EventType[EventType["Meta"] = 4] = "Meta";
|
|
1001
1006
|
EventType[EventType["Custom"] = 5] = "Custom";
|
|
1002
1007
|
EventType[EventType["Plugin"] = 6] = "Plugin";
|
|
1003
|
-
})(EventType
|
|
1008
|
+
})(EventType || (EventType = {}));
|
|
1004
1009
|
var IncrementalSource;
|
|
1005
1010
|
(function (IncrementalSource) {
|
|
1006
1011
|
IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
|
|
@@ -1133,7 +1138,7 @@ if (typeof window !== 'undefined' && window.Proxy && window.Reflect) {
|
|
|
1133
1138
|
},
|
|
1134
1139
|
});
|
|
1135
1140
|
}
|
|
1136
|
-
function throttle(func, wait, options = {}) {
|
|
1141
|
+
function throttle$1(func, wait, options = {}) {
|
|
1137
1142
|
let timeout = null;
|
|
1138
1143
|
let previous = 0;
|
|
1139
1144
|
return function (arg) {
|
|
@@ -1868,7 +1873,7 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
|
|
|
1868
1873
|
: 500;
|
|
1869
1874
|
let positions = [];
|
|
1870
1875
|
let timeBaseline;
|
|
1871
|
-
const wrappedCb = throttle((source) => {
|
|
1876
|
+
const wrappedCb = throttle$1((source) => {
|
|
1872
1877
|
const totalOffset = Date.now() - timeBaseline;
|
|
1873
1878
|
callbackWrapper(mousemoveCb)(positions.map((p) => {
|
|
1874
1879
|
p.timeOffset -= totalOffset;
|
|
@@ -1877,7 +1882,7 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
|
|
|
1877
1882
|
positions = [];
|
|
1878
1883
|
timeBaseline = null;
|
|
1879
1884
|
}, callbackThreshold);
|
|
1880
|
-
const updatePosition = throttle((evt) => {
|
|
1885
|
+
const updatePosition = throttle$1((evt) => {
|
|
1881
1886
|
const target = getEventTarget(evt);
|
|
1882
1887
|
const { clientX, clientY } = isTouchEvent(evt)
|
|
1883
1888
|
? evt.changedTouches[0]
|
|
@@ -1951,7 +1956,7 @@ function initMouseInteractionObserver({ mouseInteractionCb, doc, mirror, blockCl
|
|
|
1951
1956
|
});
|
|
1952
1957
|
}
|
|
1953
1958
|
function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector, unblockSelector, sampling, }) {
|
|
1954
|
-
const updatePosition = throttle((evt) => {
|
|
1959
|
+
const updatePosition = throttle$1((evt) => {
|
|
1955
1960
|
const target = getEventTarget(evt);
|
|
1956
1961
|
if (!target ||
|
|
1957
1962
|
isBlocked(target, blockClass, blockSelector, unblockSelector)) {
|
|
@@ -1979,7 +1984,7 @@ function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector,
|
|
|
1979
1984
|
function initViewportResizeObserver({ viewportResizeCb, }) {
|
|
1980
1985
|
let lastH = -1;
|
|
1981
1986
|
let lastW = -1;
|
|
1982
|
-
const updateDimension = throttle(() => {
|
|
1987
|
+
const updateDimension = throttle$1(() => {
|
|
1983
1988
|
const height = getWindowHeight();
|
|
1984
1989
|
const width = getWindowWidth();
|
|
1985
1990
|
if (lastH !== height || lastW !== width) {
|
|
@@ -2268,7 +2273,7 @@ function initStyleDeclarationObserver({ styleDeclarationCb, mirror }, { win }) {
|
|
|
2268
2273
|
});
|
|
2269
2274
|
}
|
|
2270
2275
|
function initMediaInteractionObserver({ mediaInteractionCb, blockClass, blockSelector, unblockSelector, mirror, sampling, }) {
|
|
2271
|
-
const handler = (type) => throttle(callbackWrapper((event) => {
|
|
2276
|
+
const handler = (type) => throttle$1(callbackWrapper((event) => {
|
|
2272
2277
|
const target = getEventTarget(event);
|
|
2273
2278
|
if (!target ||
|
|
2274
2279
|
isBlocked(target, blockClass, blockSelector, unblockSelector)) {
|
|
@@ -2973,17 +2978,17 @@ function record(options = {}) {
|
|
|
2973
2978
|
wrappedEmit = (e, isCheckout) => {
|
|
2974
2979
|
var _a;
|
|
2975
2980
|
if (((_a = mutationBuffers[0]) === null || _a === void 0 ? void 0 : _a.isFrozen()) &&
|
|
2976
|
-
e.type !== EventType
|
|
2977
|
-
!(e.type === EventType
|
|
2981
|
+
e.type !== EventType.FullSnapshot &&
|
|
2982
|
+
!(e.type === EventType.IncrementalSnapshot &&
|
|
2978
2983
|
e.data.source === IncrementalSource.Mutation)) {
|
|
2979
2984
|
mutationBuffers.forEach((buf) => buf.unfreeze());
|
|
2980
2985
|
}
|
|
2981
2986
|
emit(eventProcessor(e), isCheckout);
|
|
2982
|
-
if (e.type === EventType
|
|
2987
|
+
if (e.type === EventType.FullSnapshot) {
|
|
2983
2988
|
lastFullSnapshotEvent = e;
|
|
2984
2989
|
incrementalSnapshotCount = 0;
|
|
2985
2990
|
}
|
|
2986
|
-
else if (e.type === EventType
|
|
2991
|
+
else if (e.type === EventType.IncrementalSnapshot) {
|
|
2987
2992
|
if (e.data.source === IncrementalSource.Mutation &&
|
|
2988
2993
|
e.data.isAttachIframe) {
|
|
2989
2994
|
return;
|
|
@@ -2999,16 +3004,16 @@ function record(options = {}) {
|
|
|
2999
3004
|
};
|
|
3000
3005
|
const wrappedMutationEmit = (m) => {
|
|
3001
3006
|
wrappedEmit(wrapEvent({
|
|
3002
|
-
type: EventType
|
|
3007
|
+
type: EventType.IncrementalSnapshot,
|
|
3003
3008
|
data: Object.assign({ source: IncrementalSource.Mutation }, m),
|
|
3004
3009
|
}));
|
|
3005
3010
|
};
|
|
3006
3011
|
const wrappedScrollEmit = (p) => wrappedEmit(wrapEvent({
|
|
3007
|
-
type: EventType
|
|
3012
|
+
type: EventType.IncrementalSnapshot,
|
|
3008
3013
|
data: Object.assign({ source: IncrementalSource.Scroll }, p),
|
|
3009
3014
|
}));
|
|
3010
3015
|
const wrappedCanvasMutationEmit = (p) => wrappedEmit(wrapEvent({
|
|
3011
|
-
type: EventType
|
|
3016
|
+
type: EventType.IncrementalSnapshot,
|
|
3012
3017
|
data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
|
|
3013
3018
|
}));
|
|
3014
3019
|
const iframeManager = new IframeManager({
|
|
@@ -3053,7 +3058,7 @@ function record(options = {}) {
|
|
|
3053
3058
|
takeFullSnapshot = (isCheckout = false) => {
|
|
3054
3059
|
var _a, _b, _c, _d;
|
|
3055
3060
|
wrappedEmit(wrapEvent({
|
|
3056
|
-
type: EventType
|
|
3061
|
+
type: EventType.Meta,
|
|
3057
3062
|
data: {
|
|
3058
3063
|
href: window.location.href,
|
|
3059
3064
|
width: getWindowWidth(),
|
|
@@ -3096,7 +3101,7 @@ function record(options = {}) {
|
|
|
3096
3101
|
}
|
|
3097
3102
|
mirror.map = idNodeMap;
|
|
3098
3103
|
wrappedEmit(wrapEvent({
|
|
3099
|
-
type: EventType
|
|
3104
|
+
type: EventType.FullSnapshot,
|
|
3100
3105
|
data: {
|
|
3101
3106
|
node,
|
|
3102
3107
|
initialOffset: {
|
|
@@ -3121,7 +3126,7 @@ function record(options = {}) {
|
|
|
3121
3126
|
const handlers = [];
|
|
3122
3127
|
handlers.push(on('DOMContentLoaded', () => {
|
|
3123
3128
|
wrappedEmit(wrapEvent({
|
|
3124
|
-
type: EventType
|
|
3129
|
+
type: EventType.DomContentLoaded,
|
|
3125
3130
|
data: {},
|
|
3126
3131
|
}));
|
|
3127
3132
|
}));
|
|
@@ -3131,40 +3136,40 @@ function record(options = {}) {
|
|
|
3131
3136
|
onMutation,
|
|
3132
3137
|
mutationCb: wrappedMutationEmit,
|
|
3133
3138
|
mousemoveCb: (positions, source) => wrappedEmit(wrapEvent({
|
|
3134
|
-
type: EventType
|
|
3139
|
+
type: EventType.IncrementalSnapshot,
|
|
3135
3140
|
data: {
|
|
3136
3141
|
source,
|
|
3137
3142
|
positions,
|
|
3138
3143
|
},
|
|
3139
3144
|
})),
|
|
3140
3145
|
mouseInteractionCb: (d) => wrappedEmit(wrapEvent({
|
|
3141
|
-
type: EventType
|
|
3146
|
+
type: EventType.IncrementalSnapshot,
|
|
3142
3147
|
data: Object.assign({ source: IncrementalSource.MouseInteraction }, d),
|
|
3143
3148
|
})),
|
|
3144
3149
|
scrollCb: wrappedScrollEmit,
|
|
3145
3150
|
viewportResizeCb: (d) => wrappedEmit(wrapEvent({
|
|
3146
|
-
type: EventType
|
|
3151
|
+
type: EventType.IncrementalSnapshot,
|
|
3147
3152
|
data: Object.assign({ source: IncrementalSource.ViewportResize }, d),
|
|
3148
3153
|
})),
|
|
3149
3154
|
inputCb: (v) => wrappedEmit(wrapEvent({
|
|
3150
|
-
type: EventType
|
|
3155
|
+
type: EventType.IncrementalSnapshot,
|
|
3151
3156
|
data: Object.assign({ source: IncrementalSource.Input }, v),
|
|
3152
3157
|
})),
|
|
3153
3158
|
mediaInteractionCb: (p) => wrappedEmit(wrapEvent({
|
|
3154
|
-
type: EventType
|
|
3159
|
+
type: EventType.IncrementalSnapshot,
|
|
3155
3160
|
data: Object.assign({ source: IncrementalSource.MediaInteraction }, p),
|
|
3156
3161
|
})),
|
|
3157
3162
|
styleSheetRuleCb: (r) => wrappedEmit(wrapEvent({
|
|
3158
|
-
type: EventType
|
|
3163
|
+
type: EventType.IncrementalSnapshot,
|
|
3159
3164
|
data: Object.assign({ source: IncrementalSource.StyleSheetRule }, r),
|
|
3160
3165
|
})),
|
|
3161
3166
|
styleDeclarationCb: (r) => wrappedEmit(wrapEvent({
|
|
3162
|
-
type: EventType
|
|
3167
|
+
type: EventType.IncrementalSnapshot,
|
|
3163
3168
|
data: Object.assign({ source: IncrementalSource.StyleDeclaration }, r),
|
|
3164
3169
|
})),
|
|
3165
3170
|
canvasMutationCb: wrappedCanvasMutationEmit,
|
|
3166
3171
|
fontCb: (p) => wrappedEmit(wrapEvent({
|
|
3167
|
-
type: EventType
|
|
3172
|
+
type: EventType.IncrementalSnapshot,
|
|
3168
3173
|
data: Object.assign({ source: IncrementalSource.Font }, p),
|
|
3169
3174
|
})),
|
|
3170
3175
|
blockClass,
|
|
@@ -3197,7 +3202,7 @@ function record(options = {}) {
|
|
|
3197
3202
|
observer: p.observer,
|
|
3198
3203
|
options: p.options,
|
|
3199
3204
|
callback: (payload) => wrappedEmit(wrapEvent({
|
|
3200
|
-
type: EventType
|
|
3205
|
+
type: EventType.Plugin,
|
|
3201
3206
|
data: {
|
|
3202
3207
|
plugin: p.name,
|
|
3203
3208
|
payload,
|
|
@@ -3225,7 +3230,7 @@ function record(options = {}) {
|
|
|
3225
3230
|
else {
|
|
3226
3231
|
handlers.push(on('load', () => {
|
|
3227
3232
|
wrappedEmit(wrapEvent({
|
|
3228
|
-
type: EventType
|
|
3233
|
+
type: EventType.Load,
|
|
3229
3234
|
data: {},
|
|
3230
3235
|
}));
|
|
3231
3236
|
init();
|
|
@@ -3244,7 +3249,7 @@ record.addCustomEvent = (tag, payload) => {
|
|
|
3244
3249
|
throw new Error('please add custom event after start recording');
|
|
3245
3250
|
}
|
|
3246
3251
|
wrappedEmit(wrapEvent({
|
|
3247
|
-
type: EventType
|
|
3252
|
+
type: EventType.Custom,
|
|
3248
3253
|
data: {
|
|
3249
3254
|
tag,
|
|
3250
3255
|
payload,
|
|
@@ -3285,89 +3290,6 @@ var NodeType;
|
|
|
3285
3290
|
NodeType[NodeType["Comment"] = 5] = "Comment";
|
|
3286
3291
|
})(NodeType || (NodeType = {}));
|
|
3287
3292
|
|
|
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
3293
|
/**
|
|
3372
3294
|
* Add a breadcrumb event to replay.
|
|
3373
3295
|
*/
|
|
@@ -3383,8 +3305,8 @@ function addBreadcrumbEvent(replay, breadcrumb) {
|
|
|
3383
3305
|
}
|
|
3384
3306
|
|
|
3385
3307
|
replay.addUpdate(() => {
|
|
3386
|
-
void
|
|
3387
|
-
type: EventType
|
|
3308
|
+
void replay.throttledAddEvent({
|
|
3309
|
+
type: EventType.Custom,
|
|
3388
3310
|
// TODO: We were converting from ms to seconds for breadcrumbs, spans,
|
|
3389
3311
|
// but maybe we should just keep them as milliseconds
|
|
3390
3312
|
timestamp: (breadcrumb.timestamp || 0) * 1000,
|
|
@@ -3506,15 +3428,16 @@ function handleSlowClick(
|
|
|
3506
3428
|
addBreadcrumbEvent(replay, breadcrumb);
|
|
3507
3429
|
}
|
|
3508
3430
|
|
|
3509
|
-
const
|
|
3431
|
+
const SLOW_CLICK_TAGS = ['A', 'BUTTON', 'INPUT'];
|
|
3510
3432
|
|
|
3433
|
+
/** exported for tests only */
|
|
3511
3434
|
function ignoreElement(node, config) {
|
|
3512
|
-
|
|
3513
|
-
if (node.tagName === 'INPUT' && !['submit', 'button'].includes(node.getAttribute('type') || '')) {
|
|
3435
|
+
if (!SLOW_CLICK_TAGS.includes(node.tagName)) {
|
|
3514
3436
|
return true;
|
|
3515
3437
|
}
|
|
3516
3438
|
|
|
3517
|
-
|
|
3439
|
+
// If <input> tag, we only want to consider input[type='submit'] & input[type='button']
|
|
3440
|
+
if (node.tagName === 'INPUT' && !['submit', 'button'].includes(node.getAttribute('type') || '')) {
|
|
3518
3441
|
return true;
|
|
3519
3442
|
}
|
|
3520
3443
|
|
|
@@ -3547,6 +3470,8 @@ const ATTRIBUTES_TO_RECORD = new Set([
|
|
|
3547
3470
|
'title',
|
|
3548
3471
|
'data-test-id',
|
|
3549
3472
|
'data-testid',
|
|
3473
|
+
'disabled',
|
|
3474
|
+
'aria-disabled',
|
|
3550
3475
|
]);
|
|
3551
3476
|
|
|
3552
3477
|
/**
|
|
@@ -3572,14 +3497,14 @@ function getAttributesToRecord(attributes) {
|
|
|
3572
3497
|
const handleDomListener = (
|
|
3573
3498
|
replay,
|
|
3574
3499
|
) => {
|
|
3575
|
-
const
|
|
3500
|
+
const { slowClickTimeout, slowClickIgnoreSelectors } = replay.getOptions();
|
|
3576
3501
|
|
|
3577
|
-
const slowClickConfig =
|
|
3502
|
+
const slowClickConfig = slowClickTimeout
|
|
3578
3503
|
? {
|
|
3579
|
-
threshold:
|
|
3580
|
-
timeout:
|
|
3581
|
-
scrollTimeout:
|
|
3582
|
-
ignoreSelector:
|
|
3504
|
+
threshold: Math.min(SLOW_CLICK_THRESHOLD, slowClickTimeout),
|
|
3505
|
+
timeout: slowClickTimeout,
|
|
3506
|
+
scrollTimeout: SLOW_CLICK_SCROLL_TIMEOUT,
|
|
3507
|
+
ignoreSelector: slowClickIgnoreSelectors ? slowClickIgnoreSelectors.join(',') : '',
|
|
3583
3508
|
}
|
|
3584
3509
|
: undefined;
|
|
3585
3510
|
|
|
@@ -3708,7 +3633,10 @@ function handleKeyboardEvent(replay, event) {
|
|
|
3708
3633
|
return;
|
|
3709
3634
|
}
|
|
3710
3635
|
|
|
3711
|
-
|
|
3636
|
+
// Update user activity, but do not restart recording as it can create
|
|
3637
|
+
// noisy/low-value replays (e.g. user comes back from idle, hits alt-tab, new
|
|
3638
|
+
// session with a single "keydown" breadcrumb is created)
|
|
3639
|
+
replay.updateUserActivity();
|
|
3712
3640
|
|
|
3713
3641
|
const breadcrumb = getKeyboardBreadcrumb(event);
|
|
3714
3642
|
|
|
@@ -3724,7 +3652,7 @@ function getKeyboardBreadcrumb(event) {
|
|
|
3724
3652
|
const { metaKey, shiftKey, ctrlKey, altKey, key, target } = event;
|
|
3725
3653
|
|
|
3726
3654
|
// never capture for input fields
|
|
3727
|
-
if (!target || isInputElement(target )) {
|
|
3655
|
+
if (!target || isInputElement(target ) || !key) {
|
|
3728
3656
|
return null;
|
|
3729
3657
|
}
|
|
3730
3658
|
|
|
@@ -3896,6 +3824,14 @@ function t(t){let e=t.length;for(;--e>=0;)t[e]=0}const e=new Uint8Array([0,0,0,0
|
|
|
3896
3824
|
|
|
3897
3825
|
function e(){const e=new Blob([r]);return URL.createObjectURL(e)}
|
|
3898
3826
|
|
|
3827
|
+
/**
|
|
3828
|
+
* Converts a timestamp to ms, if it was in s, or keeps it as ms.
|
|
3829
|
+
*/
|
|
3830
|
+
function timestampToMs(timestamp) {
|
|
3831
|
+
const isMs = timestamp > 9999999999;
|
|
3832
|
+
return isMs ? timestamp : timestamp * 1000;
|
|
3833
|
+
}
|
|
3834
|
+
|
|
3899
3835
|
/**
|
|
3900
3836
|
* A basic event buffer that does not do any compression.
|
|
3901
3837
|
* Used as fallback if the compression worker cannot be loaded or is disabled.
|
|
@@ -4451,11 +4387,13 @@ function getSession({
|
|
|
4451
4387
|
// within "max session time").
|
|
4452
4388
|
const isExpired = isSessionExpired(session, timeouts);
|
|
4453
4389
|
|
|
4454
|
-
if (!isExpired) {
|
|
4390
|
+
if (!isExpired || (allowBuffering && session.shouldRefresh)) {
|
|
4455
4391
|
return { type: 'saved', session };
|
|
4456
4392
|
} else if (!session.shouldRefresh) {
|
|
4457
|
-
//
|
|
4458
|
-
//
|
|
4393
|
+
// This is the case if we have an error session that is completed
|
|
4394
|
+
// (=triggered an error). Session will continue as session-based replay,
|
|
4395
|
+
// and when this session is expired, it will not be renewed until user
|
|
4396
|
+
// reloads.
|
|
4459
4397
|
const discardedSession = makeSession({ sampled: false });
|
|
4460
4398
|
return { type: 'new', session: discardedSession };
|
|
4461
4399
|
} else {
|
|
@@ -4473,6 +4411,68 @@ function getSession({
|
|
|
4473
4411
|
return { type: 'new', session: newSession };
|
|
4474
4412
|
}
|
|
4475
4413
|
|
|
4414
|
+
function isCustomEvent(event) {
|
|
4415
|
+
return event.type === EventType.Custom;
|
|
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' && isCustomEvent(event)
|
|
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,35 +4658,16 @@ 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
|
-
|
|
4696
|
-
type: EventType
|
|
4668
|
+
return entries.map(({ type, start, end, name, data }) => {
|
|
4669
|
+
const response = replay.throttledAddEvent({
|
|
4670
|
+
type: EventType.Custom,
|
|
4697
4671
|
timestamp: start,
|
|
4698
4672
|
data: {
|
|
4699
4673
|
tag: 'performanceSpan',
|
|
@@ -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) {
|
|
@@ -5924,6 +5901,10 @@ function _isFetchHint(hint) {
|
|
|
5924
5901
|
|
|
5925
5902
|
let _LAST_BREADCRUMB = null;
|
|
5926
5903
|
|
|
5904
|
+
function isBreadcrumbWithCategory(breadcrumb) {
|
|
5905
|
+
return !!breadcrumb.category;
|
|
5906
|
+
}
|
|
5907
|
+
|
|
5927
5908
|
const handleScopeListener =
|
|
5928
5909
|
(replay) =>
|
|
5929
5910
|
(scope) => {
|
|
@@ -5959,9 +5940,9 @@ function handleScope(scope) {
|
|
|
5959
5940
|
_LAST_BREADCRUMB = newBreadcrumb;
|
|
5960
5941
|
|
|
5961
5942
|
if (
|
|
5962
|
-
newBreadcrumb
|
|
5963
|
-
|
|
5964
|
-
|
|
5943
|
+
!isBreadcrumbWithCategory(newBreadcrumb) ||
|
|
5944
|
+
['fetch', 'xhr', 'sentry.event', 'sentry.transaction'].includes(newBreadcrumb.category) ||
|
|
5945
|
+
newBreadcrumb.category.startsWith('ui.')
|
|
5965
5946
|
) {
|
|
5966
5947
|
return null;
|
|
5967
5948
|
}
|
|
@@ -5974,7 +5955,9 @@ function handleScope(scope) {
|
|
|
5974
5955
|
}
|
|
5975
5956
|
|
|
5976
5957
|
/** exported for tests only */
|
|
5977
|
-
function normalizeConsoleBreadcrumb(
|
|
5958
|
+
function normalizeConsoleBreadcrumb(
|
|
5959
|
+
breadcrumb,
|
|
5960
|
+
) {
|
|
5978
5961
|
const args = breadcrumb.data && breadcrumb.data.arguments;
|
|
5979
5962
|
|
|
5980
5963
|
if (!Array.isArray(args) || args.length === 0) {
|
|
@@ -6055,6 +6038,16 @@ function addGlobalListeners(replay) {
|
|
|
6055
6038
|
dsc.replay_id = replayId;
|
|
6056
6039
|
}
|
|
6057
6040
|
});
|
|
6041
|
+
|
|
6042
|
+
client.on('startTransaction', transaction => {
|
|
6043
|
+
replay.lastTransaction = transaction;
|
|
6044
|
+
});
|
|
6045
|
+
|
|
6046
|
+
// We may be missing the initial startTransaction due to timing issues,
|
|
6047
|
+
// so we capture it on finish again.
|
|
6048
|
+
client.on('finishTransaction', transaction => {
|
|
6049
|
+
replay.lastTransaction = transaction;
|
|
6050
|
+
});
|
|
6058
6051
|
}
|
|
6059
6052
|
}
|
|
6060
6053
|
|
|
@@ -6413,6 +6406,10 @@ function getHandleRecordingEmit(replay) {
|
|
|
6413
6406
|
// a previous session ID. In this case, we want to buffer events
|
|
6414
6407
|
// for a set amount of time before flushing. This can help avoid
|
|
6415
6408
|
// capturing replays of users that immediately close the window.
|
|
6409
|
+
// TODO: We should check `recordingMode` here and do nothing if it's
|
|
6410
|
+
// buffer, instead of checking inside of timeout, this will make our
|
|
6411
|
+
// tests a bit cleaner as we will need to wait on the delay in order to
|
|
6412
|
+
// do nothing.
|
|
6416
6413
|
setTimeout(() => replay.conditionalFlush(), options._experiments.delayFlushOnCheckout);
|
|
6417
6414
|
|
|
6418
6415
|
// Cancel any previously debounced flushes to ensure there are no [near]
|
|
@@ -6778,6 +6775,62 @@ async function sendReplay(
|
|
|
6778
6775
|
}
|
|
6779
6776
|
}
|
|
6780
6777
|
|
|
6778
|
+
const THROTTLED = '__THROTTLED';
|
|
6779
|
+
const SKIPPED = '__SKIPPED';
|
|
6780
|
+
|
|
6781
|
+
/**
|
|
6782
|
+
* Create a throttled function off a given function.
|
|
6783
|
+
* When calling the throttled function, it will call the original function only
|
|
6784
|
+
* if it hasn't been called more than `maxCount` times in the last `durationSeconds`.
|
|
6785
|
+
*
|
|
6786
|
+
* Returns `THROTTLED` if throttled for the first time, after that `SKIPPED`,
|
|
6787
|
+
* or else the return value of the original function.
|
|
6788
|
+
*/
|
|
6789
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6790
|
+
function throttle(
|
|
6791
|
+
fn,
|
|
6792
|
+
maxCount,
|
|
6793
|
+
durationSeconds,
|
|
6794
|
+
) {
|
|
6795
|
+
const counter = new Map();
|
|
6796
|
+
|
|
6797
|
+
const _cleanup = (now) => {
|
|
6798
|
+
const threshold = now - durationSeconds;
|
|
6799
|
+
counter.forEach((_value, key) => {
|
|
6800
|
+
if (key < threshold) {
|
|
6801
|
+
counter.delete(key);
|
|
6802
|
+
}
|
|
6803
|
+
});
|
|
6804
|
+
};
|
|
6805
|
+
|
|
6806
|
+
const _getTotalCount = () => {
|
|
6807
|
+
return [...counter.values()].reduce((a, b) => a + b, 0);
|
|
6808
|
+
};
|
|
6809
|
+
|
|
6810
|
+
let isThrottled = false;
|
|
6811
|
+
|
|
6812
|
+
return (...rest) => {
|
|
6813
|
+
// Date in second-precision, which we use as basis for the throttling
|
|
6814
|
+
const now = Math.floor(Date.now() / 1000);
|
|
6815
|
+
|
|
6816
|
+
// First, make sure to delete any old entries
|
|
6817
|
+
_cleanup(now);
|
|
6818
|
+
|
|
6819
|
+
// If already over limit, do nothing
|
|
6820
|
+
if (_getTotalCount() >= maxCount) {
|
|
6821
|
+
const wasThrottled = isThrottled;
|
|
6822
|
+
isThrottled = true;
|
|
6823
|
+
return wasThrottled ? SKIPPED : THROTTLED;
|
|
6824
|
+
}
|
|
6825
|
+
|
|
6826
|
+
isThrottled = false;
|
|
6827
|
+
const count = counter.get(now) || 0;
|
|
6828
|
+
counter.set(now, count + 1);
|
|
6829
|
+
|
|
6830
|
+
return fn(...rest);
|
|
6831
|
+
};
|
|
6832
|
+
}
|
|
6833
|
+
|
|
6781
6834
|
/* eslint-disable max-lines */ // TODO: We might want to split this file up
|
|
6782
6835
|
|
|
6783
6836
|
/**
|
|
@@ -6800,6 +6853,11 @@ class ReplayContainer {
|
|
|
6800
6853
|
*/
|
|
6801
6854
|
__init3() {this.recordingMode = 'session';}
|
|
6802
6855
|
|
|
6856
|
+
/**
|
|
6857
|
+
* The current or last active transcation.
|
|
6858
|
+
* This is only available when performance is enabled.
|
|
6859
|
+
*/
|
|
6860
|
+
|
|
6803
6861
|
/**
|
|
6804
6862
|
* These are here so we can overwrite them in tests etc.
|
|
6805
6863
|
* @hidden
|
|
@@ -6866,6 +6924,14 @@ class ReplayContainer {
|
|
|
6866
6924
|
this._debouncedFlush = debounce(() => this._flush(), this._options.flushMinDelay, {
|
|
6867
6925
|
maxWait: this._options.flushMaxDelay,
|
|
6868
6926
|
});
|
|
6927
|
+
|
|
6928
|
+
this._throttledAddEvent = throttle(
|
|
6929
|
+
(event, isCheckout) => addEvent(this, event, isCheckout),
|
|
6930
|
+
// Max 300 events...
|
|
6931
|
+
300,
|
|
6932
|
+
// ... per 5s
|
|
6933
|
+
5,
|
|
6934
|
+
);
|
|
6869
6935
|
}
|
|
6870
6936
|
|
|
6871
6937
|
/** Get the event context. */
|
|
@@ -7110,6 +7176,8 @@ class ReplayContainer {
|
|
|
7110
7176
|
return this.flushImmediate();
|
|
7111
7177
|
}
|
|
7112
7178
|
|
|
7179
|
+
const activityTime = Date.now();
|
|
7180
|
+
|
|
7113
7181
|
// Allow flush to complete before resuming as a session recording, otherwise
|
|
7114
7182
|
// the checkout from `startRecording` may be included in the payload.
|
|
7115
7183
|
// Prefer to keep the error replay as a separate (and smaller) segment
|
|
@@ -7131,6 +7199,18 @@ class ReplayContainer {
|
|
|
7131
7199
|
// Once this session ends, we do not want to refresh it
|
|
7132
7200
|
if (this.session) {
|
|
7133
7201
|
this.session.shouldRefresh = false;
|
|
7202
|
+
|
|
7203
|
+
// It's possible that the session lifespan is > max session lifespan
|
|
7204
|
+
// because we have been buffering beyond max session lifespan (we ignore
|
|
7205
|
+
// expiration given that `shouldRefresh` is true). Since we flip
|
|
7206
|
+
// `shouldRefresh`, the session could be considered expired due to
|
|
7207
|
+
// lifespan, which is not what we want. Update session start date to be
|
|
7208
|
+
// the current timestamp, so that session is not considered to be
|
|
7209
|
+
// expired. This means that max replay duration can be MAX_SESSION_LIFE +
|
|
7210
|
+
// (length of buffer), which we are ok with.
|
|
7211
|
+
this._updateUserActivity(activityTime);
|
|
7212
|
+
this._updateSessionActivity(activityTime);
|
|
7213
|
+
this.session.started = activityTime;
|
|
7134
7214
|
this._maybeSaveSession();
|
|
7135
7215
|
}
|
|
7136
7216
|
|
|
@@ -7194,6 +7274,18 @@ class ReplayContainer {
|
|
|
7194
7274
|
this._updateSessionActivity();
|
|
7195
7275
|
}
|
|
7196
7276
|
|
|
7277
|
+
/**
|
|
7278
|
+
* Updates the user activity timestamp *without* resuming
|
|
7279
|
+
* recording. Some user events (e.g. keydown) can be create
|
|
7280
|
+
* low-value replays that only contain the keypress as a
|
|
7281
|
+
* breadcrumb. Instead this would require other events to
|
|
7282
|
+
* create a new replay after a session has expired.
|
|
7283
|
+
*/
|
|
7284
|
+
updateUserActivity() {
|
|
7285
|
+
this._updateUserActivity();
|
|
7286
|
+
this._updateSessionActivity();
|
|
7287
|
+
}
|
|
7288
|
+
|
|
7197
7289
|
/**
|
|
7198
7290
|
* Only flush if `this.recordingMode === 'session'`
|
|
7199
7291
|
*/
|
|
@@ -7295,6 +7387,52 @@ class ReplayContainer {
|
|
|
7295
7387
|
this._context.urls.push(url);
|
|
7296
7388
|
}
|
|
7297
7389
|
|
|
7390
|
+
/**
|
|
7391
|
+
* Add a breadcrumb event, that may be throttled.
|
|
7392
|
+
* If it was throttled, we add a custom breadcrumb to indicate that.
|
|
7393
|
+
*/
|
|
7394
|
+
throttledAddEvent(
|
|
7395
|
+
event,
|
|
7396
|
+
isCheckout,
|
|
7397
|
+
) {
|
|
7398
|
+
const res = this._throttledAddEvent(event, isCheckout);
|
|
7399
|
+
|
|
7400
|
+
// If this is THROTTLED, it means we have throttled the event for the first time
|
|
7401
|
+
// In this case, we want to add a breadcrumb indicating that something was skipped
|
|
7402
|
+
if (res === THROTTLED) {
|
|
7403
|
+
const breadcrumb = createBreadcrumb({
|
|
7404
|
+
category: 'replay.throttled',
|
|
7405
|
+
});
|
|
7406
|
+
|
|
7407
|
+
this.addUpdate(() => {
|
|
7408
|
+
void addEvent(this, {
|
|
7409
|
+
type: EventType.Custom,
|
|
7410
|
+
timestamp: breadcrumb.timestamp || 0,
|
|
7411
|
+
data: {
|
|
7412
|
+
tag: 'breadcrumb',
|
|
7413
|
+
payload: breadcrumb,
|
|
7414
|
+
metric: true,
|
|
7415
|
+
},
|
|
7416
|
+
});
|
|
7417
|
+
});
|
|
7418
|
+
}
|
|
7419
|
+
|
|
7420
|
+
return res;
|
|
7421
|
+
}
|
|
7422
|
+
|
|
7423
|
+
/**
|
|
7424
|
+
* This will get the parametrized route name of the current page.
|
|
7425
|
+
* This is only available if performance is enabled, and if an instrumented router is used.
|
|
7426
|
+
*/
|
|
7427
|
+
getCurrentRoute() {
|
|
7428
|
+
const lastTransaction = this.lastTransaction || getCurrentHub().getScope().getTransaction();
|
|
7429
|
+
if (!lastTransaction || !['route', 'custom'].includes(lastTransaction.metadata.source)) {
|
|
7430
|
+
return undefined;
|
|
7431
|
+
}
|
|
7432
|
+
|
|
7433
|
+
return lastTransaction.name;
|
|
7434
|
+
}
|
|
7435
|
+
|
|
7298
7436
|
/**
|
|
7299
7437
|
* Initialize and start all listeners to varying events (DOM,
|
|
7300
7438
|
* Performance Observer, Recording, Sentry SDK, etc)
|
|
@@ -7338,7 +7476,7 @@ class ReplayContainer {
|
|
|
7338
7476
|
stickySession: Boolean(this._options.stickySession),
|
|
7339
7477
|
currentSession: this.session,
|
|
7340
7478
|
sessionSampleRate: this._options.sessionSampleRate,
|
|
7341
|
-
allowBuffering: this._options.errorSampleRate > 0,
|
|
7479
|
+
allowBuffering: this._options.errorSampleRate > 0 || this.recordingMode === 'buffer',
|
|
7342
7480
|
});
|
|
7343
7481
|
|
|
7344
7482
|
// If session was newly created (i.e. was not loaded from storage), then
|
|
@@ -7533,8 +7671,8 @@ class ReplayContainer {
|
|
|
7533
7671
|
*/
|
|
7534
7672
|
_createCustomBreadcrumb(breadcrumb) {
|
|
7535
7673
|
this.addUpdate(() => {
|
|
7536
|
-
void
|
|
7537
|
-
type: EventType
|
|
7674
|
+
void this.throttledAddEvent({
|
|
7675
|
+
type: EventType.Custom,
|
|
7538
7676
|
timestamp: breadcrumb.timestamp || 0,
|
|
7539
7677
|
data: {
|
|
7540
7678
|
tag: 'breadcrumb',
|
|
@@ -7732,8 +7870,8 @@ class ReplayContainer {
|
|
|
7732
7870
|
__init18() {this._onMutationHandler = (mutations) => {
|
|
7733
7871
|
const count = mutations.length;
|
|
7734
7872
|
|
|
7735
|
-
const mutationLimit = this._options.
|
|
7736
|
-
const mutationBreadcrumbLimit = this._options.
|
|
7873
|
+
const mutationLimit = this._options.mutationLimit;
|
|
7874
|
+
const mutationBreadcrumbLimit = this._options.mutationBreadcrumbLimit;
|
|
7737
7875
|
const overMutationLimit = mutationLimit && count > mutationLimit;
|
|
7738
7876
|
|
|
7739
7877
|
// Create a breadcrumb if a lot of mutations happen at the same time
|
|
@@ -7743,15 +7881,15 @@ class ReplayContainer {
|
|
|
7743
7881
|
category: 'replay.mutations',
|
|
7744
7882
|
data: {
|
|
7745
7883
|
count,
|
|
7884
|
+
limit: overMutationLimit,
|
|
7746
7885
|
},
|
|
7747
7886
|
});
|
|
7748
7887
|
this._createCustomBreadcrumb(breadcrumb);
|
|
7749
7888
|
}
|
|
7750
7889
|
|
|
7890
|
+
// Stop replay if over the mutation limit
|
|
7751
7891
|
if (overMutationLimit) {
|
|
7752
|
-
|
|
7753
|
-
// Instead, we do a full snapshot
|
|
7754
|
-
this._triggerFullSnapshot(false);
|
|
7892
|
+
void this.stop('mutationLimit');
|
|
7755
7893
|
return false;
|
|
7756
7894
|
}
|
|
7757
7895
|
|
|
@@ -7905,6 +8043,12 @@ class Replay {
|
|
|
7905
8043
|
maskAllInputs = true,
|
|
7906
8044
|
blockAllMedia = true,
|
|
7907
8045
|
|
|
8046
|
+
mutationBreadcrumbLimit = 750,
|
|
8047
|
+
mutationLimit = 10000,
|
|
8048
|
+
|
|
8049
|
+
slowClickTimeout = 7000,
|
|
8050
|
+
slowClickIgnoreSelectors = [],
|
|
8051
|
+
|
|
7908
8052
|
networkDetailAllowUrls = [],
|
|
7909
8053
|
networkCaptureBodies = true,
|
|
7910
8054
|
networkRequestHeaders = [],
|
|
@@ -7972,6 +8116,10 @@ class Replay {
|
|
|
7972
8116
|
blockAllMedia,
|
|
7973
8117
|
maskAllInputs,
|
|
7974
8118
|
maskAllText,
|
|
8119
|
+
mutationBreadcrumbLimit,
|
|
8120
|
+
mutationLimit,
|
|
8121
|
+
slowClickTimeout,
|
|
8122
|
+
slowClickIgnoreSelectors,
|
|
7975
8123
|
networkDetailAllowUrls,
|
|
7976
8124
|
networkCaptureBodies,
|
|
7977
8125
|
networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
|
|
@@ -8178,5 +8326,5 @@ function _getMergedNetworkHeaders(headers) {
|
|
|
8178
8326
|
return [...DEFAULT_NETWORK_HEADERS, ...headers.map(header => header.toLowerCase())];
|
|
8179
8327
|
}
|
|
8180
8328
|
|
|
8181
|
-
export { Replay };
|
|
8329
|
+
export { EventType, NodeType, Replay };
|
|
8182
8330
|
//# sourceMappingURL=index.js.map
|