@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/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";
|
|
@@ -1135,7 +1140,7 @@ if (typeof window !== 'undefined' && window.Proxy && window.Reflect) {
|
|
|
1135
1140
|
},
|
|
1136
1141
|
});
|
|
1137
1142
|
}
|
|
1138
|
-
function throttle(func, wait, options = {}) {
|
|
1143
|
+
function throttle$1(func, wait, options = {}) {
|
|
1139
1144
|
let timeout = null;
|
|
1140
1145
|
let previous = 0;
|
|
1141
1146
|
return function (arg) {
|
|
@@ -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
|
}
|
|
@@ -1870,7 +1875,7 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
|
|
|
1870
1875
|
: 500;
|
|
1871
1876
|
let positions = [];
|
|
1872
1877
|
let timeBaseline;
|
|
1873
|
-
const wrappedCb = throttle((source) => {
|
|
1878
|
+
const wrappedCb = throttle$1((source) => {
|
|
1874
1879
|
const totalOffset = Date.now() - timeBaseline;
|
|
1875
1880
|
callbackWrapper(mousemoveCb)(positions.map((p) => {
|
|
1876
1881
|
p.timeOffset -= totalOffset;
|
|
@@ -1879,7 +1884,7 @@ function initMoveObserver({ mousemoveCb, sampling, doc, mirror, }) {
|
|
|
1879
1884
|
positions = [];
|
|
1880
1885
|
timeBaseline = null;
|
|
1881
1886
|
}, callbackThreshold);
|
|
1882
|
-
const updatePosition = throttle((evt) => {
|
|
1887
|
+
const updatePosition = throttle$1((evt) => {
|
|
1883
1888
|
const target = getEventTarget(evt);
|
|
1884
1889
|
const { clientX, clientY } = isTouchEvent(evt)
|
|
1885
1890
|
? evt.changedTouches[0]
|
|
@@ -1953,7 +1958,7 @@ function initMouseInteractionObserver({ mouseInteractionCb, doc, mirror, blockCl
|
|
|
1953
1958
|
});
|
|
1954
1959
|
}
|
|
1955
1960
|
function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector, unblockSelector, sampling, }) {
|
|
1956
|
-
const updatePosition = throttle((evt) => {
|
|
1961
|
+
const updatePosition = throttle$1((evt) => {
|
|
1957
1962
|
const target = getEventTarget(evt);
|
|
1958
1963
|
if (!target ||
|
|
1959
1964
|
isBlocked(target, blockClass, blockSelector, unblockSelector)) {
|
|
@@ -1981,7 +1986,7 @@ function initScrollObserver({ scrollCb, doc, mirror, blockClass, blockSelector,
|
|
|
1981
1986
|
function initViewportResizeObserver({ viewportResizeCb, }) {
|
|
1982
1987
|
let lastH = -1;
|
|
1983
1988
|
let lastW = -1;
|
|
1984
|
-
const updateDimension = throttle(() => {
|
|
1989
|
+
const updateDimension = throttle$1(() => {
|
|
1985
1990
|
const height = getWindowHeight();
|
|
1986
1991
|
const width = getWindowWidth();
|
|
1987
1992
|
if (lastH !== height || lastW !== width) {
|
|
@@ -2270,7 +2275,7 @@ function initStyleDeclarationObserver({ styleDeclarationCb, mirror }, { win }) {
|
|
|
2270
2275
|
});
|
|
2271
2276
|
}
|
|
2272
2277
|
function initMediaInteractionObserver({ mediaInteractionCb, blockClass, blockSelector, unblockSelector, mirror, sampling, }) {
|
|
2273
|
-
const handler = (type) => throttle(callbackWrapper((event) => {
|
|
2278
|
+
const handler = (type) => throttle$1(callbackWrapper((event) => {
|
|
2274
2279
|
const target = getEventTarget(event);
|
|
2275
2280
|
if (!target ||
|
|
2276
2281
|
isBlocked(target, blockClass, blockSelector, unblockSelector)) {
|
|
@@ -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,90 +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 = {}));
|
|
3289
|
-
|
|
3290
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
3291
|
-
|
|
3292
|
-
var EventType; (function (EventType) {
|
|
3293
|
-
const DomContentLoaded = 0; EventType[EventType["DomContentLoaded"] = DomContentLoaded] = "DomContentLoaded";
|
|
3294
|
-
const Load = 1; EventType[EventType["Load"] = Load] = "Load";
|
|
3295
|
-
const FullSnapshot = 2; EventType[EventType["FullSnapshot"] = FullSnapshot] = "FullSnapshot";
|
|
3296
|
-
const IncrementalSnapshot = 3; EventType[EventType["IncrementalSnapshot"] = IncrementalSnapshot] = "IncrementalSnapshot";
|
|
3297
|
-
const Meta = 4; EventType[EventType["Meta"] = Meta] = "Meta";
|
|
3298
|
-
const Custom = 5; EventType[EventType["Custom"] = Custom] = "Custom";
|
|
3299
|
-
const Plugin = 6; EventType[EventType["Plugin"] = Plugin] = "Plugin";
|
|
3300
|
-
})(EventType || (EventType = {}));
|
|
3301
|
-
|
|
3302
|
-
/**
|
|
3303
|
-
* This is a partial copy of rrweb's eventWithTime type which only contains the properties
|
|
3304
|
-
* we specifcally need in the SDK.
|
|
3305
|
-
*/
|
|
3306
|
-
|
|
3307
|
-
/**
|
|
3308
|
-
* Converts a timestamp to ms, if it was in s, or keeps it as ms.
|
|
3309
|
-
*/
|
|
3310
|
-
function timestampToMs(timestamp) {
|
|
3311
|
-
const isMs = timestamp > 9999999999;
|
|
3312
|
-
return isMs ? timestamp : timestamp * 1000;
|
|
3313
|
-
}
|
|
3314
|
-
|
|
3315
|
-
/**
|
|
3316
|
-
* Add an event to the event buffer.
|
|
3317
|
-
* `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
|
|
3318
|
-
*/
|
|
3319
|
-
async function addEvent(
|
|
3320
|
-
replay,
|
|
3321
|
-
event,
|
|
3322
|
-
isCheckout,
|
|
3323
|
-
) {
|
|
3324
|
-
if (!replay.eventBuffer) {
|
|
3325
|
-
// This implies that `_isEnabled` is false
|
|
3326
|
-
return null;
|
|
3327
|
-
}
|
|
3328
|
-
|
|
3329
|
-
if (replay.isPaused()) {
|
|
3330
|
-
// Do not add to event buffer when recording is paused
|
|
3331
|
-
return null;
|
|
3332
|
-
}
|
|
3333
|
-
|
|
3334
|
-
const timestampInMs = timestampToMs(event.timestamp);
|
|
3335
|
-
|
|
3336
|
-
// Throw out events that happen more than 5 minutes ago. This can happen if
|
|
3337
|
-
// page has been left open and idle for a long period of time and user
|
|
3338
|
-
// comes back to trigger a new session. The performance entries rely on
|
|
3339
|
-
// `performance.timeOrigin`, which is when the page first opened.
|
|
3340
|
-
if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
|
|
3341
|
-
return null;
|
|
3342
|
-
}
|
|
3343
|
-
|
|
3344
|
-
try {
|
|
3345
|
-
if (isCheckout) {
|
|
3346
|
-
replay.eventBuffer.clear();
|
|
3347
|
-
}
|
|
3348
|
-
|
|
3349
|
-
const replayOptions = replay.getOptions();
|
|
3350
|
-
|
|
3351
|
-
const eventAfterPossibleCallback =
|
|
3352
|
-
typeof replayOptions.beforeAddRecordingEvent === 'function' && event.type === EventType.Custom
|
|
3353
|
-
? replayOptions.beforeAddRecordingEvent(event)
|
|
3354
|
-
: event;
|
|
3355
|
-
|
|
3356
|
-
if (!eventAfterPossibleCallback) {
|
|
3357
|
-
return;
|
|
3358
|
-
}
|
|
3359
|
-
|
|
3360
|
-
return await replay.eventBuffer.addEvent(eventAfterPossibleCallback);
|
|
3361
|
-
} catch (error) {
|
|
3362
|
-
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.error(error);
|
|
3363
|
-
await replay.stop('addEvent');
|
|
3364
|
-
|
|
3365
|
-
const client = core.getCurrentHub().getClient();
|
|
3366
|
-
|
|
3367
|
-
if (client) {
|
|
3368
|
-
client.recordDroppedEvent('internal_sdk_error', 'replay');
|
|
3369
|
-
}
|
|
3370
|
-
}
|
|
3371
|
-
}
|
|
3293
|
+
})(exports.NodeType || (exports.NodeType = {}));
|
|
3372
3294
|
|
|
3373
3295
|
/**
|
|
3374
3296
|
* Add a breadcrumb event to replay.
|
|
@@ -3385,8 +3307,8 @@ function addBreadcrumbEvent(replay, breadcrumb) {
|
|
|
3385
3307
|
}
|
|
3386
3308
|
|
|
3387
3309
|
replay.addUpdate(() => {
|
|
3388
|
-
void
|
|
3389
|
-
type: EventType
|
|
3310
|
+
void replay.throttledAddEvent({
|
|
3311
|
+
type: exports.EventType.Custom,
|
|
3390
3312
|
// TODO: We were converting from ms to seconds for breadcrumbs, spans,
|
|
3391
3313
|
// but maybe we should just keep them as milliseconds
|
|
3392
3314
|
timestamp: (breadcrumb.timestamp || 0) * 1000,
|
|
@@ -3508,15 +3430,16 @@ function handleSlowClick(
|
|
|
3508
3430
|
addBreadcrumbEvent(replay, breadcrumb);
|
|
3509
3431
|
}
|
|
3510
3432
|
|
|
3511
|
-
const
|
|
3433
|
+
const SLOW_CLICK_TAGS = ['A', 'BUTTON', 'INPUT'];
|
|
3512
3434
|
|
|
3435
|
+
/** exported for tests only */
|
|
3513
3436
|
function ignoreElement(node, config) {
|
|
3514
|
-
|
|
3515
|
-
if (node.tagName === 'INPUT' && !['submit', 'button'].includes(node.getAttribute('type') || '')) {
|
|
3437
|
+
if (!SLOW_CLICK_TAGS.includes(node.tagName)) {
|
|
3516
3438
|
return true;
|
|
3517
3439
|
}
|
|
3518
3440
|
|
|
3519
|
-
|
|
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') || '')) {
|
|
3520
3443
|
return true;
|
|
3521
3444
|
}
|
|
3522
3445
|
|
|
@@ -3549,6 +3472,8 @@ const ATTRIBUTES_TO_RECORD = new Set([
|
|
|
3549
3472
|
'title',
|
|
3550
3473
|
'data-test-id',
|
|
3551
3474
|
'data-testid',
|
|
3475
|
+
'disabled',
|
|
3476
|
+
'aria-disabled',
|
|
3552
3477
|
]);
|
|
3553
3478
|
|
|
3554
3479
|
/**
|
|
@@ -3574,14 +3499,14 @@ function getAttributesToRecord(attributes) {
|
|
|
3574
3499
|
const handleDomListener = (
|
|
3575
3500
|
replay,
|
|
3576
3501
|
) => {
|
|
3577
|
-
const
|
|
3502
|
+
const { slowClickTimeout, slowClickIgnoreSelectors } = replay.getOptions();
|
|
3578
3503
|
|
|
3579
|
-
const slowClickConfig =
|
|
3504
|
+
const slowClickConfig = slowClickTimeout
|
|
3580
3505
|
? {
|
|
3581
|
-
threshold:
|
|
3582
|
-
timeout:
|
|
3583
|
-
scrollTimeout:
|
|
3584
|
-
ignoreSelector:
|
|
3506
|
+
threshold: Math.min(SLOW_CLICK_THRESHOLD, slowClickTimeout),
|
|
3507
|
+
timeout: slowClickTimeout,
|
|
3508
|
+
scrollTimeout: SLOW_CLICK_SCROLL_TIMEOUT,
|
|
3509
|
+
ignoreSelector: slowClickIgnoreSelectors ? slowClickIgnoreSelectors.join(',') : '',
|
|
3585
3510
|
}
|
|
3586
3511
|
: undefined;
|
|
3587
3512
|
|
|
@@ -3615,7 +3540,7 @@ const handleDomListener = (
|
|
|
3615
3540
|
/** Get the base DOM breadcrumb. */
|
|
3616
3541
|
function getBaseDomBreadcrumb(target, message) {
|
|
3617
3542
|
// `__sn` property is the serialized node created by rrweb
|
|
3618
|
-
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;
|
|
3619
3544
|
|
|
3620
3545
|
return {
|
|
3621
3546
|
message,
|
|
@@ -3628,7 +3553,7 @@ function getBaseDomBreadcrumb(target, message) {
|
|
|
3628
3553
|
textContent: target
|
|
3629
3554
|
? Array.from(target.childNodes)
|
|
3630
3555
|
.map(
|
|
3631
|
-
(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,
|
|
3632
3557
|
)
|
|
3633
3558
|
.filter(Boolean) // filter out empty values
|
|
3634
3559
|
.map(text => (text ).trim())
|
|
@@ -3710,7 +3635,10 @@ function handleKeyboardEvent(replay, event) {
|
|
|
3710
3635
|
return;
|
|
3711
3636
|
}
|
|
3712
3637
|
|
|
3713
|
-
|
|
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();
|
|
3714
3642
|
|
|
3715
3643
|
const breadcrumb = getKeyboardBreadcrumb(event);
|
|
3716
3644
|
|
|
@@ -3726,7 +3654,7 @@ function getKeyboardBreadcrumb(event) {
|
|
|
3726
3654
|
const { metaKey, shiftKey, ctrlKey, altKey, key, target } = event;
|
|
3727
3655
|
|
|
3728
3656
|
// never capture for input fields
|
|
3729
|
-
if (!target || isInputElement(target )) {
|
|
3657
|
+
if (!target || isInputElement(target ) || !key) {
|
|
3730
3658
|
return null;
|
|
3731
3659
|
}
|
|
3732
3660
|
|
|
@@ -3898,6 +3826,14 @@ function t(t){let e=t.length;for(;--e>=0;)t[e]=0}const e=new Uint8Array([0,0,0,0
|
|
|
3898
3826
|
|
|
3899
3827
|
function e(){const e=new Blob([r]);return URL.createObjectURL(e)}
|
|
3900
3828
|
|
|
3829
|
+
/**
|
|
3830
|
+
* Converts a timestamp to ms, if it was in s, or keeps it as ms.
|
|
3831
|
+
*/
|
|
3832
|
+
function timestampToMs(timestamp) {
|
|
3833
|
+
const isMs = timestamp > 9999999999;
|
|
3834
|
+
return isMs ? timestamp : timestamp * 1000;
|
|
3835
|
+
}
|
|
3836
|
+
|
|
3901
3837
|
/**
|
|
3902
3838
|
* A basic event buffer that does not do any compression.
|
|
3903
3839
|
* Used as fallback if the compression worker cannot be loaded or is disabled.
|
|
@@ -4453,11 +4389,13 @@ function getSession({
|
|
|
4453
4389
|
// within "max session time").
|
|
4454
4390
|
const isExpired = isSessionExpired(session, timeouts);
|
|
4455
4391
|
|
|
4456
|
-
if (!isExpired) {
|
|
4392
|
+
if (!isExpired || (allowBuffering && session.shouldRefresh)) {
|
|
4457
4393
|
return { type: 'saved', session };
|
|
4458
4394
|
} else if (!session.shouldRefresh) {
|
|
4459
|
-
//
|
|
4460
|
-
//
|
|
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.
|
|
4461
4399
|
const discardedSession = makeSession({ sampled: false });
|
|
4462
4400
|
return { type: 'new', session: discardedSession };
|
|
4463
4401
|
} else {
|
|
@@ -4475,6 +4413,68 @@ function getSession({
|
|
|
4475
4413
|
return { type: 'new', session: newSession };
|
|
4476
4414
|
}
|
|
4477
4415
|
|
|
4416
|
+
function isCustomEvent(event) {
|
|
4417
|
+
return event.type === exports.EventType.Custom;
|
|
4418
|
+
}
|
|
4419
|
+
|
|
4420
|
+
/**
|
|
4421
|
+
* Add an event to the event buffer.
|
|
4422
|
+
* `isCheckout` is true if this is either the very first event, or an event triggered by `checkoutEveryNms`.
|
|
4423
|
+
*/
|
|
4424
|
+
async function addEvent(
|
|
4425
|
+
replay,
|
|
4426
|
+
event,
|
|
4427
|
+
isCheckout,
|
|
4428
|
+
) {
|
|
4429
|
+
if (!replay.eventBuffer) {
|
|
4430
|
+
// This implies that `_isEnabled` is false
|
|
4431
|
+
return null;
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
if (replay.isPaused()) {
|
|
4435
|
+
// Do not add to event buffer when recording is paused
|
|
4436
|
+
return null;
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4439
|
+
const timestampInMs = timestampToMs(event.timestamp);
|
|
4440
|
+
|
|
4441
|
+
// Throw out events that happen more than 5 minutes ago. This can happen if
|
|
4442
|
+
// page has been left open and idle for a long period of time and user
|
|
4443
|
+
// comes back to trigger a new session. The performance entries rely on
|
|
4444
|
+
// `performance.timeOrigin`, which is when the page first opened.
|
|
4445
|
+
if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) {
|
|
4446
|
+
return null;
|
|
4447
|
+
}
|
|
4448
|
+
|
|
4449
|
+
try {
|
|
4450
|
+
if (isCheckout) {
|
|
4451
|
+
replay.eventBuffer.clear();
|
|
4452
|
+
}
|
|
4453
|
+
|
|
4454
|
+
const replayOptions = replay.getOptions();
|
|
4455
|
+
|
|
4456
|
+
const eventAfterPossibleCallback =
|
|
4457
|
+
typeof replayOptions.beforeAddRecordingEvent === 'function' && isCustomEvent(event)
|
|
4458
|
+
? replayOptions.beforeAddRecordingEvent(event)
|
|
4459
|
+
: event;
|
|
4460
|
+
|
|
4461
|
+
if (!eventAfterPossibleCallback) {
|
|
4462
|
+
return;
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
return await replay.eventBuffer.addEvent(eventAfterPossibleCallback);
|
|
4466
|
+
} catch (error) {
|
|
4467
|
+
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.error(error);
|
|
4468
|
+
await replay.stop('addEvent');
|
|
4469
|
+
|
|
4470
|
+
const client = core.getCurrentHub().getClient();
|
|
4471
|
+
|
|
4472
|
+
if (client) {
|
|
4473
|
+
client.recordDroppedEvent('internal_sdk_error', 'replay');
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4477
|
+
|
|
4478
4478
|
/** If the event is an error event */
|
|
4479
4479
|
function isErrorEvent(event) {
|
|
4480
4480
|
return !event.type;
|
|
@@ -4649,13 +4649,6 @@ function handleGlobalEventListener(
|
|
|
4649
4649
|
event.tags = { ...event.tags, replayId: replay.getSessionId() };
|
|
4650
4650
|
}
|
|
4651
4651
|
|
|
4652
|
-
if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && replay.getOptions()._experiments.traceInternals && isErrorEvent(event)) {
|
|
4653
|
-
const exc = getEventExceptionValues(event);
|
|
4654
|
-
addInternalBreadcrumb({
|
|
4655
|
-
message: `Tagging event (${event.event_id}) - ${event.message} - ${exc.type}: ${exc.value}`,
|
|
4656
|
-
});
|
|
4657
|
-
}
|
|
4658
|
-
|
|
4659
4652
|
// In cases where a custom client is used that does not support the new hooks (yet),
|
|
4660
4653
|
// we manually call this hook method here
|
|
4661
4654
|
if (afterSendHandler) {
|
|
@@ -4667,35 +4660,16 @@ function handleGlobalEventListener(
|
|
|
4667
4660
|
};
|
|
4668
4661
|
}
|
|
4669
4662
|
|
|
4670
|
-
function addInternalBreadcrumb(arg) {
|
|
4671
|
-
const { category, level, message, ...rest } = arg;
|
|
4672
|
-
|
|
4673
|
-
core.addBreadcrumb({
|
|
4674
|
-
category: category || 'console',
|
|
4675
|
-
level: level || 'debug',
|
|
4676
|
-
message: `[debug]: ${message}`,
|
|
4677
|
-
...rest,
|
|
4678
|
-
});
|
|
4679
|
-
}
|
|
4680
|
-
|
|
4681
|
-
function getEventExceptionValues(event) {
|
|
4682
|
-
return {
|
|
4683
|
-
type: 'Unknown',
|
|
4684
|
-
value: 'n/a',
|
|
4685
|
-
...(event.exception && event.exception.values && event.exception.values[0]),
|
|
4686
|
-
};
|
|
4687
|
-
}
|
|
4688
|
-
|
|
4689
4663
|
/**
|
|
4690
|
-
* Create a "span" for each performance entry.
|
|
4664
|
+
* Create a "span" for each performance entry.
|
|
4691
4665
|
*/
|
|
4692
4666
|
function createPerformanceSpans(
|
|
4693
4667
|
replay,
|
|
4694
4668
|
entries,
|
|
4695
4669
|
) {
|
|
4696
|
-
return entries.map(({ type, start, end, name, data }) =>
|
|
4697
|
-
|
|
4698
|
-
type: EventType
|
|
4670
|
+
return entries.map(({ type, start, end, name, data }) => {
|
|
4671
|
+
const response = replay.throttledAddEvent({
|
|
4672
|
+
type: exports.EventType.Custom,
|
|
4699
4673
|
timestamp: start,
|
|
4700
4674
|
data: {
|
|
4701
4675
|
tag: 'performanceSpan',
|
|
@@ -4707,8 +4681,11 @@ function createPerformanceSpans(
|
|
|
4707
4681
|
data,
|
|
4708
4682
|
},
|
|
4709
4683
|
},
|
|
4710
|
-
})
|
|
4711
|
-
|
|
4684
|
+
});
|
|
4685
|
+
|
|
4686
|
+
// If response is a string, it means its either THROTTLED or SKIPPED
|
|
4687
|
+
return typeof response === 'string' ? Promise.resolve(null) : response;
|
|
4688
|
+
});
|
|
4712
4689
|
}
|
|
4713
4690
|
|
|
4714
4691
|
function handleHistory(handlerData) {
|
|
@@ -5926,6 +5903,10 @@ function _isFetchHint(hint) {
|
|
|
5926
5903
|
|
|
5927
5904
|
let _LAST_BREADCRUMB = null;
|
|
5928
5905
|
|
|
5906
|
+
function isBreadcrumbWithCategory(breadcrumb) {
|
|
5907
|
+
return !!breadcrumb.category;
|
|
5908
|
+
}
|
|
5909
|
+
|
|
5929
5910
|
const handleScopeListener =
|
|
5930
5911
|
(replay) =>
|
|
5931
5912
|
(scope) => {
|
|
@@ -5961,9 +5942,9 @@ function handleScope(scope) {
|
|
|
5961
5942
|
_LAST_BREADCRUMB = newBreadcrumb;
|
|
5962
5943
|
|
|
5963
5944
|
if (
|
|
5964
|
-
newBreadcrumb
|
|
5965
|
-
|
|
5966
|
-
|
|
5945
|
+
!isBreadcrumbWithCategory(newBreadcrumb) ||
|
|
5946
|
+
['fetch', 'xhr', 'sentry.event', 'sentry.transaction'].includes(newBreadcrumb.category) ||
|
|
5947
|
+
newBreadcrumb.category.startsWith('ui.')
|
|
5967
5948
|
) {
|
|
5968
5949
|
return null;
|
|
5969
5950
|
}
|
|
@@ -5976,7 +5957,9 @@ function handleScope(scope) {
|
|
|
5976
5957
|
}
|
|
5977
5958
|
|
|
5978
5959
|
/** exported for tests only */
|
|
5979
|
-
function normalizeConsoleBreadcrumb(
|
|
5960
|
+
function normalizeConsoleBreadcrumb(
|
|
5961
|
+
breadcrumb,
|
|
5962
|
+
) {
|
|
5980
5963
|
const args = breadcrumb.data && breadcrumb.data.arguments;
|
|
5981
5964
|
|
|
5982
5965
|
if (!Array.isArray(args) || args.length === 0) {
|
|
@@ -6057,6 +6040,16 @@ function addGlobalListeners(replay) {
|
|
|
6057
6040
|
dsc.replay_id = replayId;
|
|
6058
6041
|
}
|
|
6059
6042
|
});
|
|
6043
|
+
|
|
6044
|
+
client.on('startTransaction', transaction => {
|
|
6045
|
+
replay.lastTransaction = transaction;
|
|
6046
|
+
});
|
|
6047
|
+
|
|
6048
|
+
// We may be missing the initial startTransaction due to timing issues,
|
|
6049
|
+
// so we capture it on finish again.
|
|
6050
|
+
client.on('finishTransaction', transaction => {
|
|
6051
|
+
replay.lastTransaction = transaction;
|
|
6052
|
+
});
|
|
6060
6053
|
}
|
|
6061
6054
|
}
|
|
6062
6055
|
|
|
@@ -6415,6 +6408,10 @@ function getHandleRecordingEmit(replay) {
|
|
|
6415
6408
|
// a previous session ID. In this case, we want to buffer events
|
|
6416
6409
|
// for a set amount of time before flushing. This can help avoid
|
|
6417
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.
|
|
6418
6415
|
setTimeout(() => replay.conditionalFlush(), options._experiments.delayFlushOnCheckout);
|
|
6419
6416
|
|
|
6420
6417
|
// Cancel any previously debounced flushes to ensure there are no [near]
|
|
@@ -6450,7 +6447,7 @@ function getHandleRecordingEmit(replay) {
|
|
|
6450
6447
|
function createOptionsEvent(replay) {
|
|
6451
6448
|
const options = replay.getOptions();
|
|
6452
6449
|
return {
|
|
6453
|
-
type: EventType.Custom,
|
|
6450
|
+
type: exports.EventType.Custom,
|
|
6454
6451
|
timestamp: Date.now(),
|
|
6455
6452
|
data: {
|
|
6456
6453
|
tag: 'options',
|
|
@@ -6780,6 +6777,62 @@ async function sendReplay(
|
|
|
6780
6777
|
}
|
|
6781
6778
|
}
|
|
6782
6779
|
|
|
6780
|
+
const THROTTLED = '__THROTTLED';
|
|
6781
|
+
const SKIPPED = '__SKIPPED';
|
|
6782
|
+
|
|
6783
|
+
/**
|
|
6784
|
+
* Create a throttled function off a given function.
|
|
6785
|
+
* When calling the throttled function, it will call the original function only
|
|
6786
|
+
* if it hasn't been called more than `maxCount` times in the last `durationSeconds`.
|
|
6787
|
+
*
|
|
6788
|
+
* Returns `THROTTLED` if throttled for the first time, after that `SKIPPED`,
|
|
6789
|
+
* or else the return value of the original function.
|
|
6790
|
+
*/
|
|
6791
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6792
|
+
function throttle(
|
|
6793
|
+
fn,
|
|
6794
|
+
maxCount,
|
|
6795
|
+
durationSeconds,
|
|
6796
|
+
) {
|
|
6797
|
+
const counter = new Map();
|
|
6798
|
+
|
|
6799
|
+
const _cleanup = (now) => {
|
|
6800
|
+
const threshold = now - durationSeconds;
|
|
6801
|
+
counter.forEach((_value, key) => {
|
|
6802
|
+
if (key < threshold) {
|
|
6803
|
+
counter.delete(key);
|
|
6804
|
+
}
|
|
6805
|
+
});
|
|
6806
|
+
};
|
|
6807
|
+
|
|
6808
|
+
const _getTotalCount = () => {
|
|
6809
|
+
return [...counter.values()].reduce((a, b) => a + b, 0);
|
|
6810
|
+
};
|
|
6811
|
+
|
|
6812
|
+
let isThrottled = false;
|
|
6813
|
+
|
|
6814
|
+
return (...rest) => {
|
|
6815
|
+
// Date in second-precision, which we use as basis for the throttling
|
|
6816
|
+
const now = Math.floor(Date.now() / 1000);
|
|
6817
|
+
|
|
6818
|
+
// First, make sure to delete any old entries
|
|
6819
|
+
_cleanup(now);
|
|
6820
|
+
|
|
6821
|
+
// If already over limit, do nothing
|
|
6822
|
+
if (_getTotalCount() >= maxCount) {
|
|
6823
|
+
const wasThrottled = isThrottled;
|
|
6824
|
+
isThrottled = true;
|
|
6825
|
+
return wasThrottled ? SKIPPED : THROTTLED;
|
|
6826
|
+
}
|
|
6827
|
+
|
|
6828
|
+
isThrottled = false;
|
|
6829
|
+
const count = counter.get(now) || 0;
|
|
6830
|
+
counter.set(now, count + 1);
|
|
6831
|
+
|
|
6832
|
+
return fn(...rest);
|
|
6833
|
+
};
|
|
6834
|
+
}
|
|
6835
|
+
|
|
6783
6836
|
/* eslint-disable max-lines */ // TODO: We might want to split this file up
|
|
6784
6837
|
|
|
6785
6838
|
/**
|
|
@@ -6802,6 +6855,11 @@ class ReplayContainer {
|
|
|
6802
6855
|
*/
|
|
6803
6856
|
__init3() {this.recordingMode = 'session';}
|
|
6804
6857
|
|
|
6858
|
+
/**
|
|
6859
|
+
* The current or last active transcation.
|
|
6860
|
+
* This is only available when performance is enabled.
|
|
6861
|
+
*/
|
|
6862
|
+
|
|
6805
6863
|
/**
|
|
6806
6864
|
* These are here so we can overwrite them in tests etc.
|
|
6807
6865
|
* @hidden
|
|
@@ -6868,6 +6926,14 @@ class ReplayContainer {
|
|
|
6868
6926
|
this._debouncedFlush = debounce(() => this._flush(), this._options.flushMinDelay, {
|
|
6869
6927
|
maxWait: this._options.flushMaxDelay,
|
|
6870
6928
|
});
|
|
6929
|
+
|
|
6930
|
+
this._throttledAddEvent = throttle(
|
|
6931
|
+
(event, isCheckout) => addEvent(this, event, isCheckout),
|
|
6932
|
+
// Max 300 events...
|
|
6933
|
+
300,
|
|
6934
|
+
// ... per 5s
|
|
6935
|
+
5,
|
|
6936
|
+
);
|
|
6871
6937
|
}
|
|
6872
6938
|
|
|
6873
6939
|
/** Get the event context. */
|
|
@@ -7112,6 +7178,8 @@ class ReplayContainer {
|
|
|
7112
7178
|
return this.flushImmediate();
|
|
7113
7179
|
}
|
|
7114
7180
|
|
|
7181
|
+
const activityTime = Date.now();
|
|
7182
|
+
|
|
7115
7183
|
// Allow flush to complete before resuming as a session recording, otherwise
|
|
7116
7184
|
// the checkout from `startRecording` may be included in the payload.
|
|
7117
7185
|
// Prefer to keep the error replay as a separate (and smaller) segment
|
|
@@ -7133,6 +7201,18 @@ class ReplayContainer {
|
|
|
7133
7201
|
// Once this session ends, we do not want to refresh it
|
|
7134
7202
|
if (this.session) {
|
|
7135
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;
|
|
7136
7216
|
this._maybeSaveSession();
|
|
7137
7217
|
}
|
|
7138
7218
|
|
|
@@ -7196,6 +7276,18 @@ class ReplayContainer {
|
|
|
7196
7276
|
this._updateSessionActivity();
|
|
7197
7277
|
}
|
|
7198
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
|
+
|
|
7199
7291
|
/**
|
|
7200
7292
|
* Only flush if `this.recordingMode === 'session'`
|
|
7201
7293
|
*/
|
|
@@ -7297,6 +7389,52 @@ class ReplayContainer {
|
|
|
7297
7389
|
this._context.urls.push(url);
|
|
7298
7390
|
}
|
|
7299
7391
|
|
|
7392
|
+
/**
|
|
7393
|
+
* Add a breadcrumb event, that may be throttled.
|
|
7394
|
+
* If it was throttled, we add a custom breadcrumb to indicate that.
|
|
7395
|
+
*/
|
|
7396
|
+
throttledAddEvent(
|
|
7397
|
+
event,
|
|
7398
|
+
isCheckout,
|
|
7399
|
+
) {
|
|
7400
|
+
const res = this._throttledAddEvent(event, isCheckout);
|
|
7401
|
+
|
|
7402
|
+
// If this is THROTTLED, it means we have throttled the event for the first time
|
|
7403
|
+
// In this case, we want to add a breadcrumb indicating that something was skipped
|
|
7404
|
+
if (res === THROTTLED) {
|
|
7405
|
+
const breadcrumb = createBreadcrumb({
|
|
7406
|
+
category: 'replay.throttled',
|
|
7407
|
+
});
|
|
7408
|
+
|
|
7409
|
+
this.addUpdate(() => {
|
|
7410
|
+
void addEvent(this, {
|
|
7411
|
+
type: exports.EventType.Custom,
|
|
7412
|
+
timestamp: breadcrumb.timestamp || 0,
|
|
7413
|
+
data: {
|
|
7414
|
+
tag: 'breadcrumb',
|
|
7415
|
+
payload: breadcrumb,
|
|
7416
|
+
metric: true,
|
|
7417
|
+
},
|
|
7418
|
+
});
|
|
7419
|
+
});
|
|
7420
|
+
}
|
|
7421
|
+
|
|
7422
|
+
return res;
|
|
7423
|
+
}
|
|
7424
|
+
|
|
7425
|
+
/**
|
|
7426
|
+
* This will get the parametrized route name of the current page.
|
|
7427
|
+
* This is only available if performance is enabled, and if an instrumented router is used.
|
|
7428
|
+
*/
|
|
7429
|
+
getCurrentRoute() {
|
|
7430
|
+
const lastTransaction = this.lastTransaction || core.getCurrentHub().getScope().getTransaction();
|
|
7431
|
+
if (!lastTransaction || !['route', 'custom'].includes(lastTransaction.metadata.source)) {
|
|
7432
|
+
return undefined;
|
|
7433
|
+
}
|
|
7434
|
+
|
|
7435
|
+
return lastTransaction.name;
|
|
7436
|
+
}
|
|
7437
|
+
|
|
7300
7438
|
/**
|
|
7301
7439
|
* Initialize and start all listeners to varying events (DOM,
|
|
7302
7440
|
* Performance Observer, Recording, Sentry SDK, etc)
|
|
@@ -7340,7 +7478,7 @@ class ReplayContainer {
|
|
|
7340
7478
|
stickySession: Boolean(this._options.stickySession),
|
|
7341
7479
|
currentSession: this.session,
|
|
7342
7480
|
sessionSampleRate: this._options.sessionSampleRate,
|
|
7343
|
-
allowBuffering: this._options.errorSampleRate > 0,
|
|
7481
|
+
allowBuffering: this._options.errorSampleRate > 0 || this.recordingMode === 'buffer',
|
|
7344
7482
|
});
|
|
7345
7483
|
|
|
7346
7484
|
// If session was newly created (i.e. was not loaded from storage), then
|
|
@@ -7535,8 +7673,8 @@ class ReplayContainer {
|
|
|
7535
7673
|
*/
|
|
7536
7674
|
_createCustomBreadcrumb(breadcrumb) {
|
|
7537
7675
|
this.addUpdate(() => {
|
|
7538
|
-
void
|
|
7539
|
-
type: EventType
|
|
7676
|
+
void this.throttledAddEvent({
|
|
7677
|
+
type: exports.EventType.Custom,
|
|
7540
7678
|
timestamp: breadcrumb.timestamp || 0,
|
|
7541
7679
|
data: {
|
|
7542
7680
|
tag: 'breadcrumb',
|
|
@@ -7734,8 +7872,8 @@ class ReplayContainer {
|
|
|
7734
7872
|
__init18() {this._onMutationHandler = (mutations) => {
|
|
7735
7873
|
const count = mutations.length;
|
|
7736
7874
|
|
|
7737
|
-
const mutationLimit = this._options.
|
|
7738
|
-
const mutationBreadcrumbLimit = this._options.
|
|
7875
|
+
const mutationLimit = this._options.mutationLimit;
|
|
7876
|
+
const mutationBreadcrumbLimit = this._options.mutationBreadcrumbLimit;
|
|
7739
7877
|
const overMutationLimit = mutationLimit && count > mutationLimit;
|
|
7740
7878
|
|
|
7741
7879
|
// Create a breadcrumb if a lot of mutations happen at the same time
|
|
@@ -7745,15 +7883,15 @@ class ReplayContainer {
|
|
|
7745
7883
|
category: 'replay.mutations',
|
|
7746
7884
|
data: {
|
|
7747
7885
|
count,
|
|
7886
|
+
limit: overMutationLimit,
|
|
7748
7887
|
},
|
|
7749
7888
|
});
|
|
7750
7889
|
this._createCustomBreadcrumb(breadcrumb);
|
|
7751
7890
|
}
|
|
7752
7891
|
|
|
7892
|
+
// Stop replay if over the mutation limit
|
|
7753
7893
|
if (overMutationLimit) {
|
|
7754
|
-
|
|
7755
|
-
// Instead, we do a full snapshot
|
|
7756
|
-
this._triggerFullSnapshot(false);
|
|
7894
|
+
void this.stop('mutationLimit');
|
|
7757
7895
|
return false;
|
|
7758
7896
|
}
|
|
7759
7897
|
|
|
@@ -7907,6 +8045,12 @@ class Replay {
|
|
|
7907
8045
|
maskAllInputs = true,
|
|
7908
8046
|
blockAllMedia = true,
|
|
7909
8047
|
|
|
8048
|
+
mutationBreadcrumbLimit = 750,
|
|
8049
|
+
mutationLimit = 10000,
|
|
8050
|
+
|
|
8051
|
+
slowClickTimeout = 7000,
|
|
8052
|
+
slowClickIgnoreSelectors = [],
|
|
8053
|
+
|
|
7910
8054
|
networkDetailAllowUrls = [],
|
|
7911
8055
|
networkCaptureBodies = true,
|
|
7912
8056
|
networkRequestHeaders = [],
|
|
@@ -7974,6 +8118,10 @@ class Replay {
|
|
|
7974
8118
|
blockAllMedia,
|
|
7975
8119
|
maskAllInputs,
|
|
7976
8120
|
maskAllText,
|
|
8121
|
+
mutationBreadcrumbLimit,
|
|
8122
|
+
mutationLimit,
|
|
8123
|
+
slowClickTimeout,
|
|
8124
|
+
slowClickIgnoreSelectors,
|
|
7977
8125
|
networkDetailAllowUrls,
|
|
7978
8126
|
networkCaptureBodies,
|
|
7979
8127
|
networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
|