Skip to content

Commit 2996c62

Browse files
authored
ref(browser): Improve type safety of breadcrumbs integration (#7382)
1 parent 2b44452 commit 2996c62

File tree

4 files changed

+47
-35
lines changed

4 files changed

+47
-35
lines changed

packages/browser/src/integrations/breadcrumbs.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
22
/* eslint-disable max-lines */
33
import { getCurrentHub } from '@sentry/core';
4-
import type { Event, Integration } from '@sentry/types';
4+
import type { Event as SentryEvent, HandlerDataFetch, Integration, SentryWrappedXMLHttpRequest } from '@sentry/types';
55
import {
66
addInstrumentationHandler,
77
getEventDescription,
@@ -14,6 +14,8 @@ import {
1414

1515
import { WINDOW } from '../helpers';
1616

17+
type HandlerData = Record<string, unknown>;
18+
1719
/** JSDoc */
1820
interface BreadcrumbsOptions {
1921
console: boolean;
@@ -99,7 +101,7 @@ export class Breadcrumbs implements Integration {
99101
/**
100102
* Adds a breadcrumb for Sentry events or transactions if this option is enabled.
101103
*/
102-
public addSentryBreadcrumb(event: Event): void {
104+
public addSentryBreadcrumb(event: SentryEvent): void {
103105
if (this.options.sentry) {
104106
getCurrentHub().addBreadcrumb(
105107
{
@@ -120,10 +122,8 @@ export class Breadcrumbs implements Integration {
120122
* A HOC that creaes a function that creates breadcrumbs from DOM API calls.
121123
* This is a HOC so that we get access to dom options in the closure.
122124
*/
123-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
124-
function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: { [key: string]: any }) => void {
125-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
126-
function _innerDomBreadcrumb(handlerData: { [key: string]: any }): void {
125+
function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: HandlerData) => void {
126+
function _innerDomBreadcrumb(handlerData: HandlerData): void {
127127
let target;
128128
let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;
129129

@@ -143,9 +143,10 @@ function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: { [key: s
143143

144144
// Accessing event.target can throw (see getsentry/raven-js#838, #768)
145145
try {
146-
target = handlerData.event.target
147-
? htmlTreeAsString(handlerData.event.target as Node, { keyAttrs, maxStringLength })
148-
: htmlTreeAsString(handlerData.event as unknown as Node, { keyAttrs, maxStringLength });
146+
const event = handlerData.event as Event | Node;
147+
target = _isEvent(event)
148+
? htmlTreeAsString(event.target, { keyAttrs, maxStringLength })
149+
: htmlTreeAsString(event, { keyAttrs, maxStringLength });
149150
} catch (e) {
150151
target = '<unknown>';
151152
}
@@ -173,8 +174,7 @@ function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: { [key: s
173174
/**
174175
* Creates breadcrumbs from console API calls
175176
*/
176-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
177-
function _consoleBreadcrumb(handlerData: { [key: string]: any }): void {
177+
function _consoleBreadcrumb(handlerData: HandlerData & { args: unknown[]; level: string }): void {
178178
// This is a hack to fix a Vue3-specific bug that causes an infinite loop of
179179
// console warnings. This happens when a Vue template is rendered with
180180
// an undeclared variable, which we try to stringify, ultimately causing
@@ -216,8 +216,7 @@ function _consoleBreadcrumb(handlerData: { [key: string]: any }): void {
216216
/**
217217
* Creates breadcrumbs from XHR API calls
218218
*/
219-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
220-
function _xhrBreadcrumb(handlerData: { [key: string]: any }): void {
219+
function _xhrBreadcrumb(handlerData: HandlerData & { xhr: SentryWrappedXMLHttpRequest }): void {
221220
if (handlerData.endTimestamp) {
222221
// We only capture complete, non-sentry requests
223222
if (handlerData.xhr.__sentry_own_request__) {
@@ -249,8 +248,7 @@ function _xhrBreadcrumb(handlerData: { [key: string]: any }): void {
249248
/**
250249
* Creates breadcrumbs from fetch API calls
251250
*/
252-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
253-
function _fetchBreadcrumb(handlerData: { [key: string]: any }): void {
251+
function _fetchBreadcrumb(handlerData: HandlerData & HandlerDataFetch): void {
254252
// We only capture complete fetch requests
255253
if (!handlerData.endTimestamp) {
256254
return;
@@ -280,7 +278,7 @@ function _fetchBreadcrumb(handlerData: { [key: string]: any }): void {
280278
category: 'fetch',
281279
data: {
282280
...handlerData.fetchData,
283-
status_code: handlerData.response.status,
281+
status_code: handlerData.response && handlerData.response.status,
284282
},
285283
type: 'http',
286284
},
@@ -295,10 +293,9 @@ function _fetchBreadcrumb(handlerData: { [key: string]: any }): void {
295293
/**
296294
* Creates breadcrumbs from history API calls
297295
*/
298-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
299-
function _historyBreadcrumb(handlerData: { [key: string]: any }): void {
300-
let from = handlerData.from;
301-
let to = handlerData.to;
296+
function _historyBreadcrumb(handlerData: HandlerData & { from: string; to: string }): void {
297+
let from: string | undefined = handlerData.from;
298+
let to: string | undefined = handlerData.to;
302299
const parsedLoc = parseUrl(WINDOW.location.href);
303300
let parsedFrom = parseUrl(from);
304301
const parsedTo = parseUrl(to);
@@ -325,3 +322,7 @@ function _historyBreadcrumb(handlerData: { [key: string]: any }): void {
325322
},
326323
});
327324
}
325+
326+
function _isEvent(event: unknown): event is Event {
327+
return event && !!(event as Record<string, unknown>).target;
328+
}

packages/types/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,6 @@ export type {
9494
export type { User, UserFeedback } from './user';
9595
export type { WrappedFunction } from './wrappedfunction';
9696
export type { Instrumenter } from './instrumenter';
97+
export type { HandlerDataFetch, SentryWrappedXMLHttpRequest } from './instrument';
9798

9899
export type { BrowserClientReplayOptions } from './browseroptions';

packages/types/src/instrument.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
type XHRSendInput = null | Blob | BufferSource | FormData | URLSearchParams | string;
2+
3+
export interface SentryWrappedXMLHttpRequest extends XMLHttpRequest {
4+
[key: string]: any;
5+
__sentry_xhr__?: {
6+
method?: string;
7+
url?: string;
8+
status_code?: number;
9+
body?: XHRSendInput;
10+
};
11+
}
12+
13+
interface SentryFetchData {
14+
method: string;
15+
url: string;
16+
}
17+
18+
export interface HandlerDataFetch {
19+
args: any[];
20+
fetchData: SentryFetchData;
21+
startTimestamp: number;
22+
response?: Response;
23+
}

packages/utils/src/instrument.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable max-lines */
22
/* eslint-disable @typescript-eslint/no-explicit-any */
33
/* eslint-disable @typescript-eslint/ban-types */
4-
import type { WrappedFunction } from '@sentry/types';
4+
import type { HandlerDataFetch, SentryWrappedXMLHttpRequest, WrappedFunction } from '@sentry/types';
55

66
import { isInstanceOf, isString } from './is';
77
import { CONSOLE_LEVELS, logger } from './logger';
@@ -136,7 +136,7 @@ function instrumentFetch(): void {
136136

137137
fill(WINDOW, 'fetch', function (originalFetch: () => void): () => void {
138138
return function (...args: any[]): void {
139-
const handlerData = {
139+
const handlerData: HandlerDataFetch = {
140140
args,
141141
fetchData: {
142142
method: getFetchMethod(args),
@@ -175,19 +175,6 @@ function instrumentFetch(): void {
175175
});
176176
}
177177

178-
type XHRSendInput = null | Blob | BufferSource | FormData | URLSearchParams | string;
179-
180-
/** JSDoc */
181-
interface SentryWrappedXMLHttpRequest extends XMLHttpRequest {
182-
[key: string]: any;
183-
__sentry_xhr__?: {
184-
method?: string;
185-
url?: string;
186-
status_code?: number;
187-
body?: XHRSendInput;
188-
};
189-
}
190-
191178
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
192179
/** Extract `method` from fetch call arguments */
193180
function getFetchMethod(fetchArgs: any[] = []): string {

0 commit comments

Comments
 (0)