Skip to content

Commit 258de3f

Browse files
committed
feat(replay): Improve types for replay recording events
WIP
1 parent 78454aa commit 258de3f

File tree

11 files changed

+339
-184
lines changed

11 files changed

+339
-184
lines changed

packages/replay/src/coreHandlers/handleScope.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import { normalize } from '@sentry/utils';
33

44
import { CONSOLE_ARG_MAX_SIZE } from '../constants';
55
import type { ReplayContainer } from '../types';
6+
import type { ReplayFrame } from '../types/replayFrame';
67
import { createBreadcrumb } from '../util/createBreadcrumb';
78
import { fixJson } from '../util/truncateJson/fixJson';
89
import { addBreadcrumbEvent } from './util/addBreadcrumbEvent';
910

1011
let _LAST_BREADCRUMB: null | Breadcrumb = null;
1112

13+
type BreadcrumbWithCategory = Required<Pick<Breadcrumb, 'category'>>;
14+
1215
export const handleScopeListener: (replay: ReplayContainer) => (scope: Scope) => void =
1316
(replay: ReplayContainer) =>
1417
(scope: Scope): void => {
@@ -44,22 +47,24 @@ export function handleScope(scope: Scope): Breadcrumb | null {
4447
_LAST_BREADCRUMB = newBreadcrumb;
4548

4649
if (
47-
newBreadcrumb.category &&
48-
(['fetch', 'xhr', 'sentry.event', 'sentry.transaction'].includes(newBreadcrumb.category) ||
49-
newBreadcrumb.category.startsWith('ui.'))
50+
typeof newBreadcrumb.category !== 'string' ||
51+
['fetch', 'xhr', 'sentry.event', 'sentry.transaction'].includes(newBreadcrumb.category) ||
52+
newBreadcrumb.category.startsWith('ui.')
5053
) {
5154
return null;
5255
}
5356

5457
if (newBreadcrumb.category === 'console') {
55-
return normalizeConsoleBreadcrumb(newBreadcrumb);
58+
return normalizeConsoleBreadcrumb(newBreadcrumb as BreadcrumbWithCategory);
5659
}
5760

58-
return createBreadcrumb(newBreadcrumb);
61+
return createBreadcrumb(newBreadcrumb as BreadcrumbWithCategory);
5962
}
6063

6164
/** exported for tests only */
62-
export function normalizeConsoleBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb {
65+
export function normalizeConsoleBreadcrumb(
66+
breadcrumb: Omit<Breadcrumb, 'category'> & BreadcrumbWithCategory,
67+
): ReplayFrame {
6368
const args = breadcrumb.data && breadcrumb.data.arguments;
6469

6570
if (!Array.isArray(args) || args.length === 0) {

packages/replay/src/coreHandlers/handleSlowClick.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ function handleSlowClick(
101101
const breadcrumb = {
102102
message: clickBreadcrumb.message,
103103
timestamp: clickBreadcrumb.timestamp,
104-
category: 'ui.slowClickDetected',
104+
category: 'ui.slowClickDetected' as const,
105105
data: {
106106
...clickBreadcrumb.data,
107107
url: WINDOW.location.href,

packages/replay/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
11
export { Replay } from './integration';
2+
export type {
3+
ReplayFrame,
4+
ReplayFrameEvent,
5+
CrumbFrame,
6+
CrumbFrameEvent,
7+
SpanFrame,
8+
SpanFrameEvent,
9+
} from './types/replayFrame';

packages/replay/src/replay.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@ export class ReplayContainer implements ReplayContainerInterface {
748748
*/
749749
private _handleWindowBlur: () => void = () => {
750750
const breadcrumb = createBreadcrumb({
751-
category: 'ui.blur',
751+
category: 'ui.blur' as const,
752752
});
753753

754754
// Do not count blur as a user action -- it's part of the process of them
@@ -761,7 +761,7 @@ export class ReplayContainer implements ReplayContainerInterface {
761761
*/
762762
private _handleWindowFocus: () => void = () => {
763763
const breadcrumb = createBreadcrumb({
764-
category: 'ui.focus',
764+
category: 'ui.focus' as const,
765765
});
766766

767767
// Do not count focus as a user action -- instead wait until they focus and
@@ -1058,15 +1058,16 @@ export class ReplayContainer implements ReplayContainerInterface {
10581058

10591059
const mutationLimit = this._options._experiments.mutationLimit || 0;
10601060
const mutationBreadcrumbLimit = this._options._experiments.mutationBreadcrumbLimit || 1000;
1061-
const overMutationLimit = mutationLimit && count > mutationLimit;
1061+
const overMutationLimit = Boolean(mutationLimit && count > mutationLimit);
10621062

10631063
// Create a breadcrumb if a lot of mutations happen at the same time
10641064
// We can show this in the UI as an information with potential performance improvements
10651065
if (count > mutationBreadcrumbLimit || overMutationLimit) {
10661066
const breadcrumb = createBreadcrumb({
1067-
category: 'replay.mutations',
1067+
category: 'replay.mutations' as const,
10681068
data: {
10691069
count,
1070+
limit: overMutationLimit,
10701071
},
10711072
});
10721073
this._createCustomBreadcrumb(breadcrumb);

packages/replay/src/types/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './performance';
2+
export * from './replay';
3+
export * from './replayFrame';
4+
export * from './rrweb';
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
export type AllPerformanceEntry = PerformancePaintTiming | PerformanceResourceTiming | PerformanceNavigationTiming;
2+
3+
// PerformancePaintTiming and PerformanceNavigationTiming are only available with TS 4.4 and newer
4+
// Therefore, we're exporting them here to make them available in older TS versions
5+
export type PerformancePaintTiming = PerformanceEntry;
6+
export type PerformanceNavigationTiming = PerformanceEntry &
7+
PerformanceResourceTiming & {
8+
type: string;
9+
transferSize: number;
10+
11+
/**
12+
* A DOMHighResTimeStamp representing the time immediately before the user agent
13+
* sets the document's readyState to "interactive".
14+
*/
15+
domInteractive: number;
16+
17+
/**
18+
* A DOMHighResTimeStamp representing the time immediately before the current
19+
* document's DOMContentLoaded event handler starts.
20+
*/
21+
domContentLoadedEventStart: number;
22+
/**
23+
* A DOMHighResTimeStamp representing the time immediately after the current
24+
* document's DOMContentLoaded event handler completes.
25+
*/
26+
domContentLoadedEventEnd: number;
27+
28+
/**
29+
* A DOMHighResTimeStamp representing the time immediately before the current
30+
* document's load event handler starts.
31+
*/
32+
loadEventStart: number;
33+
34+
/**
35+
* A DOMHighResTimeStamp representing the time immediately after the current
36+
* document's load event handler completes.
37+
*/
38+
loadEventEnd: number;
39+
40+
/**
41+
* A DOMHighResTimeStamp representing the time immediately before the user agent
42+
* sets the document's readyState to "complete".
43+
*/
44+
domComplete: number;
45+
46+
/**
47+
* A number representing the number of redirects since the last non-redirect
48+
* navigation in the current browsing context.
49+
*/
50+
redirectCount: number;
51+
};
52+
export type ExperimentalPerformanceResourceTiming = PerformanceResourceTiming & {
53+
// Experimental, see: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus
54+
// Requires Chrome 109
55+
responseStatus?: number;
56+
};
57+
58+
export type PaintData = undefined;
59+
60+
/**
61+
* See https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming
62+
*
63+
* Note `navigation.push` will not have any data
64+
*/
65+
export type NavigationData = Partial<
66+
Pick<
67+
PerformanceNavigationTiming,
68+
| 'decodedBodySize'
69+
| 'encodedBodySize'
70+
| 'duration'
71+
| 'domInteractive'
72+
| 'domContentLoadedEventEnd'
73+
| 'domContentLoadedEventStart'
74+
| 'loadEventStart'
75+
| 'loadEventEnd'
76+
| 'domComplete'
77+
| 'redirectCount'
78+
>
79+
> & {
80+
/**
81+
* Transfer size of resource
82+
*/
83+
size?: number;
84+
};
85+
86+
export type ResourceData = Pick<PerformanceResourceTiming, 'decodedBodySize' | 'encodedBodySize'> & {
87+
/**
88+
* Transfer size of resource
89+
*/
90+
size: number;
91+
/**
92+
* HTTP status code. Note this is experimental and not available on all browsers.
93+
*/
94+
statusCode?: number;
95+
};
96+
97+
export interface LargestContentfulPaintData {
98+
/**
99+
* Render time (in ms) of the LCP
100+
*/
101+
value: number;
102+
size: number;
103+
/**
104+
* The recording id of the LCP node. -1 if not found
105+
*/
106+
nodeId?: number;
107+
}
108+
109+
/**
110+
* Entries that come from window.performance
111+
*/
112+
export type AllPerformanceEntryData = PaintData | NavigationData | ResourceData | LargestContentfulPaintData;
113+
114+
export interface MemoryData {
115+
memory: {
116+
jsHeapSizeLimit: number;
117+
totalJSHeapSize: number;
118+
usedJSHeapSize: number;
119+
};
120+
}
121+
122+
export interface NetworkRequestData {
123+
method?: string;
124+
statusCode?: number;
125+
requestBodySize?: number;
126+
responseBodySize?: number;
127+
}
128+
129+
export interface HistoryData {
130+
previous: string;
131+
}
132+
133+
export type AllEntryData = AllPerformanceEntryData | MemoryData | NetworkRequestData | HistoryData;
134+
135+
export interface ReplayPerformanceEntry<T> {
136+
/**
137+
* One of these types https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry/entryType
138+
*/
139+
type: string;
140+
141+
/**
142+
* A more specific description of the performance entry
143+
*/
144+
name: string;
145+
146+
/**
147+
* The start timestamp in seconds
148+
*/
149+
start: number;
150+
151+
/**
152+
* The end timestamp in seconds
153+
*/
154+
end: number;
155+
156+
/**
157+
* Additional unstructured data to be included
158+
*/
159+
data: T;
160+
}

0 commit comments

Comments
 (0)