@sentry/replay 7.54.0 → 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 +127 -86
- package/cjs/index.js.map +1 -1
- package/esm/index.js +105 -64
- 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/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/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 +8 -0
- 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} +21 -144
- 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/createBreadcrumb.d.ts +2 -4
- package/types/util/createBreadcrumb.d.ts.map +1 -1
- package/types/util/handleRecordingEmit.d.ts +2 -2
- package/types/util/handleRecordingEmit.d.ts.map +1 -1
- package/types/types.d.ts.map +0 -1
package/cjs/index.js
CHANGED
|
@@ -40,7 +40,12 @@ const NETWORK_BODY_MAX_SIZE = 150000;
|
|
|
40
40
|
/* The max size of a single console arg that is captured. Any arg larger than this will be truncated. */
|
|
41
41
|
const CONSOLE_ARG_MAX_SIZE = 5000;
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
/* Min. time to wait before we consider something a slow click. */
|
|
44
|
+
const SLOW_CLICK_THRESHOLD = 3000;
|
|
45
|
+
/* For scroll actions after a click, we only look for a very short time period to detect programmatic scrolling. */
|
|
46
|
+
const SLOW_CLICK_SCROLL_TIMEOUT = 300;
|
|
47
|
+
|
|
48
|
+
var NodeType;
|
|
44
49
|
(function (NodeType) {
|
|
45
50
|
NodeType[NodeType["Document"] = 0] = "Document";
|
|
46
51
|
NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
|
|
@@ -48,7 +53,7 @@ var NodeType$1;
|
|
|
48
53
|
NodeType[NodeType["Text"] = 3] = "Text";
|
|
49
54
|
NodeType[NodeType["CDATA"] = 4] = "CDATA";
|
|
50
55
|
NodeType[NodeType["Comment"] = 5] = "Comment";
|
|
51
|
-
})(NodeType
|
|
56
|
+
})(NodeType || (NodeType = {}));
|
|
52
57
|
|
|
53
58
|
function isElement(n) {
|
|
54
59
|
return n.nodeType === n.ELEMENT_NODE;
|
|
@@ -452,7 +457,7 @@ function serializeNode(n, options) {
|
|
|
452
457
|
case n.DOCUMENT_NODE:
|
|
453
458
|
if (n.compatMode !== 'CSS1Compat') {
|
|
454
459
|
return {
|
|
455
|
-
type: NodeType
|
|
460
|
+
type: NodeType.Document,
|
|
456
461
|
childNodes: [],
|
|
457
462
|
compatMode: n.compatMode,
|
|
458
463
|
rootId,
|
|
@@ -460,14 +465,14 @@ function serializeNode(n, options) {
|
|
|
460
465
|
}
|
|
461
466
|
else {
|
|
462
467
|
return {
|
|
463
|
-
type: NodeType
|
|
468
|
+
type: NodeType.Document,
|
|
464
469
|
childNodes: [],
|
|
465
470
|
rootId,
|
|
466
471
|
};
|
|
467
472
|
}
|
|
468
473
|
case n.DOCUMENT_TYPE_NODE:
|
|
469
474
|
return {
|
|
470
|
-
type: NodeType
|
|
475
|
+
type: NodeType.DocumentType,
|
|
471
476
|
name: n.name,
|
|
472
477
|
publicId: n.publicId,
|
|
473
478
|
systemId: n.systemId,
|
|
@@ -611,7 +616,7 @@ function serializeNode(n, options) {
|
|
|
611
616
|
delete attributes.src;
|
|
612
617
|
}
|
|
613
618
|
return {
|
|
614
|
-
type: NodeType
|
|
619
|
+
type: NodeType.Element,
|
|
615
620
|
tagName,
|
|
616
621
|
attributes,
|
|
617
622
|
childNodes: [],
|
|
@@ -665,20 +670,20 @@ function serializeNode(n, options) {
|
|
|
665
670
|
: defaultMaskFn(textContent);
|
|
666
671
|
}
|
|
667
672
|
return {
|
|
668
|
-
type: NodeType
|
|
673
|
+
type: NodeType.Text,
|
|
669
674
|
textContent: textContent || '',
|
|
670
675
|
isStyle,
|
|
671
676
|
rootId,
|
|
672
677
|
};
|
|
673
678
|
case n.CDATA_SECTION_NODE:
|
|
674
679
|
return {
|
|
675
|
-
type: NodeType
|
|
680
|
+
type: NodeType.CDATA,
|
|
676
681
|
textContent: '',
|
|
677
682
|
rootId,
|
|
678
683
|
};
|
|
679
684
|
case n.COMMENT_NODE:
|
|
680
685
|
return {
|
|
681
|
-
type: NodeType
|
|
686
|
+
type: NodeType.Comment,
|
|
682
687
|
textContent: n.textContent || '',
|
|
683
688
|
rootId,
|
|
684
689
|
};
|
|
@@ -695,10 +700,10 @@ function lowerIfExists(maybeAttr) {
|
|
|
695
700
|
}
|
|
696
701
|
}
|
|
697
702
|
function slimDOMExcluded(sn, slimDOMOptions) {
|
|
698
|
-
if (slimDOMOptions.comment && sn.type === NodeType
|
|
703
|
+
if (slimDOMOptions.comment && sn.type === NodeType.Comment) {
|
|
699
704
|
return true;
|
|
700
705
|
}
|
|
701
|
-
else if (sn.type === NodeType
|
|
706
|
+
else if (sn.type === NodeType.Element) {
|
|
702
707
|
if (slimDOMOptions.script &&
|
|
703
708
|
(sn.tagName === 'script' ||
|
|
704
709
|
(sn.tagName === 'link' &&
|
|
@@ -799,7 +804,7 @@ function serializeNodeWithId(n, options) {
|
|
|
799
804
|
}
|
|
800
805
|
else if (slimDOMExcluded(_serializedNode, slimDOMOptions) ||
|
|
801
806
|
(!preserveWhiteSpace &&
|
|
802
|
-
_serializedNode.type === NodeType
|
|
807
|
+
_serializedNode.type === NodeType.Text &&
|
|
803
808
|
!_serializedNode.isStyle &&
|
|
804
809
|
!_serializedNode.textContent.replace(/^\s+|\s+$/gm, '').length)) {
|
|
805
810
|
id = IGNORED_NODE;
|
|
@@ -817,17 +822,17 @@ function serializeNodeWithId(n, options) {
|
|
|
817
822
|
onSerialize(n);
|
|
818
823
|
}
|
|
819
824
|
let recordChild = !skipChild;
|
|
820
|
-
if (serializedNode.type === NodeType
|
|
825
|
+
if (serializedNode.type === NodeType.Element) {
|
|
821
826
|
recordChild = recordChild && !serializedNode.needBlock;
|
|
822
827
|
delete serializedNode.needBlock;
|
|
823
828
|
if (n.shadowRoot)
|
|
824
829
|
serializedNode.isShadowHost = true;
|
|
825
830
|
}
|
|
826
|
-
if ((serializedNode.type === NodeType
|
|
827
|
-
serializedNode.type === NodeType
|
|
831
|
+
if ((serializedNode.type === NodeType.Document ||
|
|
832
|
+
serializedNode.type === NodeType.Element) &&
|
|
828
833
|
recordChild) {
|
|
829
834
|
if (slimDOMOptions.headWhitespace &&
|
|
830
|
-
_serializedNode.type === NodeType
|
|
835
|
+
_serializedNode.type === NodeType.Element &&
|
|
831
836
|
_serializedNode.tagName === 'head') {
|
|
832
837
|
preserveWhiteSpace = false;
|
|
833
838
|
}
|
|
@@ -877,7 +882,7 @@ function serializeNodeWithId(n, options) {
|
|
|
877
882
|
if (n.parentNode && isShadowRoot(n.parentNode)) {
|
|
878
883
|
serializedNode.isShadow = true;
|
|
879
884
|
}
|
|
880
|
-
if (serializedNode.type === NodeType
|
|
885
|
+
if (serializedNode.type === NodeType.Element &&
|
|
881
886
|
serializedNode.tagName === 'iframe') {
|
|
882
887
|
onceIframeLoaded(n, () => {
|
|
883
888
|
const iframeDoc = n.contentDocument;
|
|
@@ -993,7 +998,7 @@ function skipAttribute(tagName, attributeName, value) {
|
|
|
993
998
|
return ((tagName === 'video' || tagName === 'audio') && attributeName === 'autoplay');
|
|
994
999
|
}
|
|
995
1000
|
|
|
996
|
-
|
|
1001
|
+
exports.EventType = void 0;
|
|
997
1002
|
(function (EventType) {
|
|
998
1003
|
EventType[EventType["DomContentLoaded"] = 0] = "DomContentLoaded";
|
|
999
1004
|
EventType[EventType["Load"] = 1] = "Load";
|
|
@@ -1002,7 +1007,7 @@ var EventType$1;
|
|
|
1002
1007
|
EventType[EventType["Meta"] = 4] = "Meta";
|
|
1003
1008
|
EventType[EventType["Custom"] = 5] = "Custom";
|
|
1004
1009
|
EventType[EventType["Plugin"] = 6] = "Plugin";
|
|
1005
|
-
})(EventType
|
|
1010
|
+
})(exports.EventType || (exports.EventType = {}));
|
|
1006
1011
|
var IncrementalSource;
|
|
1007
1012
|
(function (IncrementalSource) {
|
|
1008
1013
|
IncrementalSource[IncrementalSource["Mutation"] = 0] = "Mutation";
|
|
@@ -1302,7 +1307,7 @@ function polyfill(win = window) {
|
|
|
1302
1307
|
}
|
|
1303
1308
|
function isIframeINode(node) {
|
|
1304
1309
|
if ('__sn' in node) {
|
|
1305
|
-
return (node.__sn.type === NodeType
|
|
1310
|
+
return (node.__sn.type === NodeType.Element && node.__sn.tagName === 'iframe');
|
|
1306
1311
|
}
|
|
1307
1312
|
return false;
|
|
1308
1313
|
}
|
|
@@ -2975,17 +2980,17 @@ function record(options = {}) {
|
|
|
2975
2980
|
wrappedEmit = (e, isCheckout) => {
|
|
2976
2981
|
var _a;
|
|
2977
2982
|
if (((_a = mutationBuffers[0]) === null || _a === void 0 ? void 0 : _a.isFrozen()) &&
|
|
2978
|
-
e.type !== EventType
|
|
2979
|
-
!(e.type === EventType
|
|
2983
|
+
e.type !== exports.EventType.FullSnapshot &&
|
|
2984
|
+
!(e.type === exports.EventType.IncrementalSnapshot &&
|
|
2980
2985
|
e.data.source === IncrementalSource.Mutation)) {
|
|
2981
2986
|
mutationBuffers.forEach((buf) => buf.unfreeze());
|
|
2982
2987
|
}
|
|
2983
2988
|
emit(eventProcessor(e), isCheckout);
|
|
2984
|
-
if (e.type === EventType
|
|
2989
|
+
if (e.type === exports.EventType.FullSnapshot) {
|
|
2985
2990
|
lastFullSnapshotEvent = e;
|
|
2986
2991
|
incrementalSnapshotCount = 0;
|
|
2987
2992
|
}
|
|
2988
|
-
else if (e.type === EventType
|
|
2993
|
+
else if (e.type === exports.EventType.IncrementalSnapshot) {
|
|
2989
2994
|
if (e.data.source === IncrementalSource.Mutation &&
|
|
2990
2995
|
e.data.isAttachIframe) {
|
|
2991
2996
|
return;
|
|
@@ -3001,16 +3006,16 @@ function record(options = {}) {
|
|
|
3001
3006
|
};
|
|
3002
3007
|
const wrappedMutationEmit = (m) => {
|
|
3003
3008
|
wrappedEmit(wrapEvent({
|
|
3004
|
-
type: EventType
|
|
3009
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3005
3010
|
data: Object.assign({ source: IncrementalSource.Mutation }, m),
|
|
3006
3011
|
}));
|
|
3007
3012
|
};
|
|
3008
3013
|
const wrappedScrollEmit = (p) => wrappedEmit(wrapEvent({
|
|
3009
|
-
type: EventType
|
|
3014
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3010
3015
|
data: Object.assign({ source: IncrementalSource.Scroll }, p),
|
|
3011
3016
|
}));
|
|
3012
3017
|
const wrappedCanvasMutationEmit = (p) => wrappedEmit(wrapEvent({
|
|
3013
|
-
type: EventType
|
|
3018
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3014
3019
|
data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
|
|
3015
3020
|
}));
|
|
3016
3021
|
const iframeManager = new IframeManager({
|
|
@@ -3055,7 +3060,7 @@ function record(options = {}) {
|
|
|
3055
3060
|
takeFullSnapshot = (isCheckout = false) => {
|
|
3056
3061
|
var _a, _b, _c, _d;
|
|
3057
3062
|
wrappedEmit(wrapEvent({
|
|
3058
|
-
type: EventType
|
|
3063
|
+
type: exports.EventType.Meta,
|
|
3059
3064
|
data: {
|
|
3060
3065
|
href: window.location.href,
|
|
3061
3066
|
width: getWindowWidth(),
|
|
@@ -3098,7 +3103,7 @@ function record(options = {}) {
|
|
|
3098
3103
|
}
|
|
3099
3104
|
mirror.map = idNodeMap;
|
|
3100
3105
|
wrappedEmit(wrapEvent({
|
|
3101
|
-
type: EventType
|
|
3106
|
+
type: exports.EventType.FullSnapshot,
|
|
3102
3107
|
data: {
|
|
3103
3108
|
node,
|
|
3104
3109
|
initialOffset: {
|
|
@@ -3123,7 +3128,7 @@ function record(options = {}) {
|
|
|
3123
3128
|
const handlers = [];
|
|
3124
3129
|
handlers.push(on('DOMContentLoaded', () => {
|
|
3125
3130
|
wrappedEmit(wrapEvent({
|
|
3126
|
-
type: EventType
|
|
3131
|
+
type: exports.EventType.DomContentLoaded,
|
|
3127
3132
|
data: {},
|
|
3128
3133
|
}));
|
|
3129
3134
|
}));
|
|
@@ -3133,40 +3138,40 @@ function record(options = {}) {
|
|
|
3133
3138
|
onMutation,
|
|
3134
3139
|
mutationCb: wrappedMutationEmit,
|
|
3135
3140
|
mousemoveCb: (positions, source) => wrappedEmit(wrapEvent({
|
|
3136
|
-
type: EventType
|
|
3141
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3137
3142
|
data: {
|
|
3138
3143
|
source,
|
|
3139
3144
|
positions,
|
|
3140
3145
|
},
|
|
3141
3146
|
})),
|
|
3142
3147
|
mouseInteractionCb: (d) => wrappedEmit(wrapEvent({
|
|
3143
|
-
type: EventType
|
|
3148
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3144
3149
|
data: Object.assign({ source: IncrementalSource.MouseInteraction }, d),
|
|
3145
3150
|
})),
|
|
3146
3151
|
scrollCb: wrappedScrollEmit,
|
|
3147
3152
|
viewportResizeCb: (d) => wrappedEmit(wrapEvent({
|
|
3148
|
-
type: EventType
|
|
3153
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3149
3154
|
data: Object.assign({ source: IncrementalSource.ViewportResize }, d),
|
|
3150
3155
|
})),
|
|
3151
3156
|
inputCb: (v) => wrappedEmit(wrapEvent({
|
|
3152
|
-
type: EventType
|
|
3157
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3153
3158
|
data: Object.assign({ source: IncrementalSource.Input }, v),
|
|
3154
3159
|
})),
|
|
3155
3160
|
mediaInteractionCb: (p) => wrappedEmit(wrapEvent({
|
|
3156
|
-
type: EventType
|
|
3161
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3157
3162
|
data: Object.assign({ source: IncrementalSource.MediaInteraction }, p),
|
|
3158
3163
|
})),
|
|
3159
3164
|
styleSheetRuleCb: (r) => wrappedEmit(wrapEvent({
|
|
3160
|
-
type: EventType
|
|
3165
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3161
3166
|
data: Object.assign({ source: IncrementalSource.StyleSheetRule }, r),
|
|
3162
3167
|
})),
|
|
3163
3168
|
styleDeclarationCb: (r) => wrappedEmit(wrapEvent({
|
|
3164
|
-
type: EventType
|
|
3169
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3165
3170
|
data: Object.assign({ source: IncrementalSource.StyleDeclaration }, r),
|
|
3166
3171
|
})),
|
|
3167
3172
|
canvasMutationCb: wrappedCanvasMutationEmit,
|
|
3168
3173
|
fontCb: (p) => wrappedEmit(wrapEvent({
|
|
3169
|
-
type: EventType
|
|
3174
|
+
type: exports.EventType.IncrementalSnapshot,
|
|
3170
3175
|
data: Object.assign({ source: IncrementalSource.Font }, p),
|
|
3171
3176
|
})),
|
|
3172
3177
|
blockClass,
|
|
@@ -3199,7 +3204,7 @@ function record(options = {}) {
|
|
|
3199
3204
|
observer: p.observer,
|
|
3200
3205
|
options: p.options,
|
|
3201
3206
|
callback: (payload) => wrappedEmit(wrapEvent({
|
|
3202
|
-
type: EventType
|
|
3207
|
+
type: exports.EventType.Plugin,
|
|
3203
3208
|
data: {
|
|
3204
3209
|
plugin: p.name,
|
|
3205
3210
|
payload,
|
|
@@ -3227,7 +3232,7 @@ function record(options = {}) {
|
|
|
3227
3232
|
else {
|
|
3228
3233
|
handlers.push(on('load', () => {
|
|
3229
3234
|
wrappedEmit(wrapEvent({
|
|
3230
|
-
type: EventType
|
|
3235
|
+
type: exports.EventType.Load,
|
|
3231
3236
|
data: {},
|
|
3232
3237
|
}));
|
|
3233
3238
|
init();
|
|
@@ -3246,7 +3251,7 @@ record.addCustomEvent = (tag, payload) => {
|
|
|
3246
3251
|
throw new Error('please add custom event after start recording');
|
|
3247
3252
|
}
|
|
3248
3253
|
wrappedEmit(wrapEvent({
|
|
3249
|
-
type: EventType
|
|
3254
|
+
type: exports.EventType.Custom,
|
|
3250
3255
|
data: {
|
|
3251
3256
|
tag,
|
|
3252
3257
|
payload,
|
|
@@ -3277,7 +3282,7 @@ function createBreadcrumb(
|
|
|
3277
3282
|
};
|
|
3278
3283
|
}
|
|
3279
3284
|
|
|
3280
|
-
|
|
3285
|
+
exports.NodeType = void 0;
|
|
3281
3286
|
(function (NodeType) {
|
|
3282
3287
|
NodeType[NodeType["Document"] = 0] = "Document";
|
|
3283
3288
|
NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
|
|
@@ -3285,7 +3290,7 @@ var NodeType;
|
|
|
3285
3290
|
NodeType[NodeType["Text"] = 3] = "Text";
|
|
3286
3291
|
NodeType[NodeType["CDATA"] = 4] = "CDATA";
|
|
3287
3292
|
NodeType[NodeType["Comment"] = 5] = "Comment";
|
|
3288
|
-
})(NodeType || (NodeType = {}));
|
|
3293
|
+
})(exports.NodeType || (exports.NodeType = {}));
|
|
3289
3294
|
|
|
3290
3295
|
/**
|
|
3291
3296
|
* Add a breadcrumb event to replay.
|
|
@@ -3303,7 +3308,7 @@ function addBreadcrumbEvent(replay, breadcrumb) {
|
|
|
3303
3308
|
|
|
3304
3309
|
replay.addUpdate(() => {
|
|
3305
3310
|
void replay.throttledAddEvent({
|
|
3306
|
-
type: EventType
|
|
3311
|
+
type: exports.EventType.Custom,
|
|
3307
3312
|
// TODO: We were converting from ms to seconds for breadcrumbs, spans,
|
|
3308
3313
|
// but maybe we should just keep them as milliseconds
|
|
3309
3314
|
timestamp: (breadcrumb.timestamp || 0) * 1000,
|
|
@@ -3425,15 +3430,16 @@ function handleSlowClick(
|
|
|
3425
3430
|
addBreadcrumbEvent(replay, breadcrumb);
|
|
3426
3431
|
}
|
|
3427
3432
|
|
|
3428
|
-
const
|
|
3433
|
+
const SLOW_CLICK_TAGS = ['A', 'BUTTON', 'INPUT'];
|
|
3429
3434
|
|
|
3435
|
+
/** exported for tests only */
|
|
3430
3436
|
function ignoreElement(node, config) {
|
|
3431
|
-
|
|
3432
|
-
if (node.tagName === 'INPUT' && !['submit', 'button'].includes(node.getAttribute('type') || '')) {
|
|
3437
|
+
if (!SLOW_CLICK_TAGS.includes(node.tagName)) {
|
|
3433
3438
|
return true;
|
|
3434
3439
|
}
|
|
3435
3440
|
|
|
3436
|
-
|
|
3441
|
+
// If <input> tag, we only want to consider input[type='submit'] & input[type='button']
|
|
3442
|
+
if (node.tagName === 'INPUT' && !['submit', 'button'].includes(node.getAttribute('type') || '')) {
|
|
3437
3443
|
return true;
|
|
3438
3444
|
}
|
|
3439
3445
|
|
|
@@ -3466,6 +3472,8 @@ const ATTRIBUTES_TO_RECORD = new Set([
|
|
|
3466
3472
|
'title',
|
|
3467
3473
|
'data-test-id',
|
|
3468
3474
|
'data-testid',
|
|
3475
|
+
'disabled',
|
|
3476
|
+
'aria-disabled',
|
|
3469
3477
|
]);
|
|
3470
3478
|
|
|
3471
3479
|
/**
|
|
@@ -3491,14 +3499,14 @@ function getAttributesToRecord(attributes) {
|
|
|
3491
3499
|
const handleDomListener = (
|
|
3492
3500
|
replay,
|
|
3493
3501
|
) => {
|
|
3494
|
-
const
|
|
3502
|
+
const { slowClickTimeout, slowClickIgnoreSelectors } = replay.getOptions();
|
|
3495
3503
|
|
|
3496
|
-
const slowClickConfig =
|
|
3504
|
+
const slowClickConfig = slowClickTimeout
|
|
3497
3505
|
? {
|
|
3498
|
-
threshold:
|
|
3499
|
-
timeout:
|
|
3500
|
-
scrollTimeout:
|
|
3501
|
-
ignoreSelector:
|
|
3506
|
+
threshold: Math.min(SLOW_CLICK_THRESHOLD, slowClickTimeout),
|
|
3507
|
+
timeout: slowClickTimeout,
|
|
3508
|
+
scrollTimeout: SLOW_CLICK_SCROLL_TIMEOUT,
|
|
3509
|
+
ignoreSelector: slowClickIgnoreSelectors ? slowClickIgnoreSelectors.join(',') : '',
|
|
3502
3510
|
}
|
|
3503
3511
|
: undefined;
|
|
3504
3512
|
|
|
@@ -3532,7 +3540,7 @@ const handleDomListener = (
|
|
|
3532
3540
|
/** Get the base DOM breadcrumb. */
|
|
3533
3541
|
function getBaseDomBreadcrumb(target, message) {
|
|
3534
3542
|
// `__sn` property is the serialized node created by rrweb
|
|
3535
|
-
const serializedNode = target && isRrwebNode(target) && target.__sn.type === NodeType.Element ? target.__sn : null;
|
|
3543
|
+
const serializedNode = target && isRrwebNode(target) && target.__sn.type === exports.NodeType.Element ? target.__sn : null;
|
|
3536
3544
|
|
|
3537
3545
|
return {
|
|
3538
3546
|
message,
|
|
@@ -3545,7 +3553,7 @@ function getBaseDomBreadcrumb(target, message) {
|
|
|
3545
3553
|
textContent: target
|
|
3546
3554
|
? Array.from(target.childNodes)
|
|
3547
3555
|
.map(
|
|
3548
|
-
(node) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent,
|
|
3556
|
+
(node) => '__sn' in node && node.__sn.type === exports.NodeType.Text && node.__sn.textContent,
|
|
3549
3557
|
)
|
|
3550
3558
|
.filter(Boolean) // filter out empty values
|
|
3551
3559
|
.map(text => (text ).trim())
|
|
@@ -3627,7 +3635,10 @@ function handleKeyboardEvent(replay, event) {
|
|
|
3627
3635
|
return;
|
|
3628
3636
|
}
|
|
3629
3637
|
|
|
3630
|
-
|
|
3638
|
+
// Update user activity, but do not restart recording as it can create
|
|
3639
|
+
// noisy/low-value replays (e.g. user comes back from idle, hits alt-tab, new
|
|
3640
|
+
// session with a single "keydown" breadcrumb is created)
|
|
3641
|
+
replay.updateUserActivity();
|
|
3631
3642
|
|
|
3632
3643
|
const breadcrumb = getKeyboardBreadcrumb(event);
|
|
3633
3644
|
|
|
@@ -4378,11 +4389,13 @@ function getSession({
|
|
|
4378
4389
|
// within "max session time").
|
|
4379
4390
|
const isExpired = isSessionExpired(session, timeouts);
|
|
4380
4391
|
|
|
4381
|
-
if (!isExpired) {
|
|
4392
|
+
if (!isExpired || (allowBuffering && session.shouldRefresh)) {
|
|
4382
4393
|
return { type: 'saved', session };
|
|
4383
4394
|
} else if (!session.shouldRefresh) {
|
|
4384
|
-
//
|
|
4385
|
-
//
|
|
4395
|
+
// This is the case if we have an error session that is completed
|
|
4396
|
+
// (=triggered an error). Session will continue as session-based replay,
|
|
4397
|
+
// and when this session is expired, it will not be renewed until user
|
|
4398
|
+
// reloads.
|
|
4386
4399
|
const discardedSession = makeSession({ sampled: false });
|
|
4387
4400
|
return { type: 'new', session: discardedSession };
|
|
4388
4401
|
} else {
|
|
@@ -4400,22 +4413,9 @@ function getSession({
|
|
|
4400
4413
|
return { type: 'new', session: newSession };
|
|
4401
4414
|
}
|
|
4402
4415
|
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
|
|
4407
|
-
const Load = 1; EventType[EventType["Load"] = Load] = "Load";
|
|
4408
|
-
const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
|
|
4409
|
-
const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
|
|
4410
|
-
const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
|
|
4411
|
-
const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
|
|
4412
|
-
const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
|
|
4413
|
-
})(EventType || (EventType = {}));
|
|
4414
|
-
|
|
4415
|
-
/**
|
|
4416
|
-
* This is a partial copy of rrweb's eventWithTime type which only contains the properties
|
|
4417
|
-
* we specifcally need in the SDK.
|
|
4418
|
-
*/
|
|
4416
|
+
function isCustomEvent(event) {
|
|
4417
|
+
return event.type === exports.EventType.Custom;
|
|
4418
|
+
}
|
|
4419
4419
|
|
|
4420
4420
|
/**
|
|
4421
4421
|
* Add an event to the event buffer.
|
|
@@ -4454,7 +4454,7 @@ async function addEvent(
|
|
|
4454
4454
|
const replayOptions = replay.getOptions();
|
|
4455
4455
|
|
|
4456
4456
|
const eventAfterPossibleCallback =
|
|
4457
|
-
typeof replayOptions.beforeAddRecordingEvent === 'function' && event
|
|
4457
|
+
typeof replayOptions.beforeAddRecordingEvent === 'function' && isCustomEvent(event)
|
|
4458
4458
|
? replayOptions.beforeAddRecordingEvent(event)
|
|
4459
4459
|
: event;
|
|
4460
4460
|
|
|
@@ -4669,7 +4669,7 @@ function createPerformanceSpans(
|
|
|
4669
4669
|
) {
|
|
4670
4670
|
return entries.map(({ type, start, end, name, data }) => {
|
|
4671
4671
|
const response = replay.throttledAddEvent({
|
|
4672
|
-
type: EventType
|
|
4672
|
+
type: exports.EventType.Custom,
|
|
4673
4673
|
timestamp: start,
|
|
4674
4674
|
data: {
|
|
4675
4675
|
tag: 'performanceSpan',
|
|
@@ -5903,6 +5903,10 @@ function _isFetchHint(hint) {
|
|
|
5903
5903
|
|
|
5904
5904
|
let _LAST_BREADCRUMB = null;
|
|
5905
5905
|
|
|
5906
|
+
function isBreadcrumbWithCategory(breadcrumb) {
|
|
5907
|
+
return !!breadcrumb.category;
|
|
5908
|
+
}
|
|
5909
|
+
|
|
5906
5910
|
const handleScopeListener =
|
|
5907
5911
|
(replay) =>
|
|
5908
5912
|
(scope) => {
|
|
@@ -5938,9 +5942,9 @@ function handleScope(scope) {
|
|
|
5938
5942
|
_LAST_BREADCRUMB = newBreadcrumb;
|
|
5939
5943
|
|
|
5940
5944
|
if (
|
|
5941
|
-
newBreadcrumb
|
|
5942
|
-
|
|
5943
|
-
|
|
5945
|
+
!isBreadcrumbWithCategory(newBreadcrumb) ||
|
|
5946
|
+
['fetch', 'xhr', 'sentry.event', 'sentry.transaction'].includes(newBreadcrumb.category) ||
|
|
5947
|
+
newBreadcrumb.category.startsWith('ui.')
|
|
5944
5948
|
) {
|
|
5945
5949
|
return null;
|
|
5946
5950
|
}
|
|
@@ -5953,7 +5957,9 @@ function handleScope(scope) {
|
|
|
5953
5957
|
}
|
|
5954
5958
|
|
|
5955
5959
|
/** exported for tests only */
|
|
5956
|
-
function normalizeConsoleBreadcrumb(
|
|
5960
|
+
function normalizeConsoleBreadcrumb(
|
|
5961
|
+
breadcrumb,
|
|
5962
|
+
) {
|
|
5957
5963
|
const args = breadcrumb.data && breadcrumb.data.arguments;
|
|
5958
5964
|
|
|
5959
5965
|
if (!Array.isArray(args) || args.length === 0) {
|
|
@@ -6402,6 +6408,10 @@ function getHandleRecordingEmit(replay) {
|
|
|
6402
6408
|
// a previous session ID. In this case, we want to buffer events
|
|
6403
6409
|
// for a set amount of time before flushing. This can help avoid
|
|
6404
6410
|
// capturing replays of users that immediately close the window.
|
|
6411
|
+
// TODO: We should check `recordingMode` here and do nothing if it's
|
|
6412
|
+
// buffer, instead of checking inside of timeout, this will make our
|
|
6413
|
+
// tests a bit cleaner as we will need to wait on the delay in order to
|
|
6414
|
+
// do nothing.
|
|
6405
6415
|
setTimeout(() => replay.conditionalFlush(), options._experiments.delayFlushOnCheckout);
|
|
6406
6416
|
|
|
6407
6417
|
// Cancel any previously debounced flushes to ensure there are no [near]
|
|
@@ -6437,7 +6447,7 @@ function getHandleRecordingEmit(replay) {
|
|
|
6437
6447
|
function createOptionsEvent(replay) {
|
|
6438
6448
|
const options = replay.getOptions();
|
|
6439
6449
|
return {
|
|
6440
|
-
type: EventType.Custom,
|
|
6450
|
+
type: exports.EventType.Custom,
|
|
6441
6451
|
timestamp: Date.now(),
|
|
6442
6452
|
data: {
|
|
6443
6453
|
tag: 'options',
|
|
@@ -7168,6 +7178,8 @@ class ReplayContainer {
|
|
|
7168
7178
|
return this.flushImmediate();
|
|
7169
7179
|
}
|
|
7170
7180
|
|
|
7181
|
+
const activityTime = Date.now();
|
|
7182
|
+
|
|
7171
7183
|
// Allow flush to complete before resuming as a session recording, otherwise
|
|
7172
7184
|
// the checkout from `startRecording` may be included in the payload.
|
|
7173
7185
|
// Prefer to keep the error replay as a separate (and smaller) segment
|
|
@@ -7189,6 +7201,18 @@ class ReplayContainer {
|
|
|
7189
7201
|
// Once this session ends, we do not want to refresh it
|
|
7190
7202
|
if (this.session) {
|
|
7191
7203
|
this.session.shouldRefresh = false;
|
|
7204
|
+
|
|
7205
|
+
// It's possible that the session lifespan is > max session lifespan
|
|
7206
|
+
// because we have been buffering beyond max session lifespan (we ignore
|
|
7207
|
+
// expiration given that `shouldRefresh` is true). Since we flip
|
|
7208
|
+
// `shouldRefresh`, the session could be considered expired due to
|
|
7209
|
+
// lifespan, which is not what we want. Update session start date to be
|
|
7210
|
+
// the current timestamp, so that session is not considered to be
|
|
7211
|
+
// expired. This means that max replay duration can be MAX_SESSION_LIFE +
|
|
7212
|
+
// (length of buffer), which we are ok with.
|
|
7213
|
+
this._updateUserActivity(activityTime);
|
|
7214
|
+
this._updateSessionActivity(activityTime);
|
|
7215
|
+
this.session.started = activityTime;
|
|
7192
7216
|
this._maybeSaveSession();
|
|
7193
7217
|
}
|
|
7194
7218
|
|
|
@@ -7252,6 +7276,18 @@ class ReplayContainer {
|
|
|
7252
7276
|
this._updateSessionActivity();
|
|
7253
7277
|
}
|
|
7254
7278
|
|
|
7279
|
+
/**
|
|
7280
|
+
* Updates the user activity timestamp *without* resuming
|
|
7281
|
+
* recording. Some user events (e.g. keydown) can be create
|
|
7282
|
+
* low-value replays that only contain the keypress as a
|
|
7283
|
+
* breadcrumb. Instead this would require other events to
|
|
7284
|
+
* create a new replay after a session has expired.
|
|
7285
|
+
*/
|
|
7286
|
+
updateUserActivity() {
|
|
7287
|
+
this._updateUserActivity();
|
|
7288
|
+
this._updateSessionActivity();
|
|
7289
|
+
}
|
|
7290
|
+
|
|
7255
7291
|
/**
|
|
7256
7292
|
* Only flush if `this.recordingMode === 'session'`
|
|
7257
7293
|
*/
|
|
@@ -7372,7 +7408,7 @@ class ReplayContainer {
|
|
|
7372
7408
|
|
|
7373
7409
|
this.addUpdate(() => {
|
|
7374
7410
|
void addEvent(this, {
|
|
7375
|
-
type: EventType
|
|
7411
|
+
type: exports.EventType.Custom,
|
|
7376
7412
|
timestamp: breadcrumb.timestamp || 0,
|
|
7377
7413
|
data: {
|
|
7378
7414
|
tag: 'breadcrumb',
|
|
@@ -7442,7 +7478,7 @@ class ReplayContainer {
|
|
|
7442
7478
|
stickySession: Boolean(this._options.stickySession),
|
|
7443
7479
|
currentSession: this.session,
|
|
7444
7480
|
sessionSampleRate: this._options.sessionSampleRate,
|
|
7445
|
-
allowBuffering: this._options.errorSampleRate > 0,
|
|
7481
|
+
allowBuffering: this._options.errorSampleRate > 0 || this.recordingMode === 'buffer',
|
|
7446
7482
|
});
|
|
7447
7483
|
|
|
7448
7484
|
// If session was newly created (i.e. was not loaded from storage), then
|
|
@@ -7638,7 +7674,7 @@ class ReplayContainer {
|
|
|
7638
7674
|
_createCustomBreadcrumb(breadcrumb) {
|
|
7639
7675
|
this.addUpdate(() => {
|
|
7640
7676
|
void this.throttledAddEvent({
|
|
7641
|
-
type: EventType
|
|
7677
|
+
type: exports.EventType.Custom,
|
|
7642
7678
|
timestamp: breadcrumb.timestamp || 0,
|
|
7643
7679
|
data: {
|
|
7644
7680
|
tag: 'breadcrumb',
|
|
@@ -8012,6 +8048,9 @@ class Replay {
|
|
|
8012
8048
|
mutationBreadcrumbLimit = 750,
|
|
8013
8049
|
mutationLimit = 10000,
|
|
8014
8050
|
|
|
8051
|
+
slowClickTimeout = 7000,
|
|
8052
|
+
slowClickIgnoreSelectors = [],
|
|
8053
|
+
|
|
8015
8054
|
networkDetailAllowUrls = [],
|
|
8016
8055
|
networkCaptureBodies = true,
|
|
8017
8056
|
networkRequestHeaders = [],
|
|
@@ -8081,6 +8120,8 @@ class Replay {
|
|
|
8081
8120
|
maskAllText,
|
|
8082
8121
|
mutationBreadcrumbLimit,
|
|
8083
8122
|
mutationLimit,
|
|
8123
|
+
slowClickTimeout,
|
|
8124
|
+
slowClickIgnoreSelectors,
|
|
8084
8125
|
networkDetailAllowUrls,
|
|
8085
8126
|
networkCaptureBodies,
|
|
8086
8127
|
networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
|