@sentry/react 10.63.0 → 10.65.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.
@@ -1 +1 @@
1
- {"version":3,"file":"reactrouterv3.js","sources":["../../src/reactrouterv3.ts"],"sourcesContent":["import {\n browserTracingIntegration,\n startBrowserTracingNavigationSpan,\n startBrowserTracingPageLoadSpan,\n WINDOW,\n} from '@sentry/browser';\nimport type { Integration, TransactionSource } from '@sentry/core/browser';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n} from '@sentry/core/browser';\nimport type { Location } from './types';\n\n// Many of the types below had to be mocked out to prevent typescript issues\n// these types are required for correct functionality.\n\ntype HistoryV3 = {\n location?: Location;\n listen?(cb: (location: Location) => void): void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n} & Record<string, any>;\n\nexport type Route = { path?: string; childRoutes?: Route[] };\n\nexport type Match = (\n props: { location: Location; routes: Route[] },\n cb: (error?: Error, _redirectLocation?: Location, renderProps?: { routes?: Route[] }) => void,\n) => void;\n\ntype ReactRouterV3TransactionSource = Extract<TransactionSource, 'url' | 'route'>;\n\ninterface ReactRouterOptions {\n history: HistoryV3;\n routes: Route[];\n match: Match;\n}\n\n/**\n * A browser tracing integration that uses React Router v3 to instrument navigations.\n * Expects `history` (and optionally `routes` and `matchPath`) to be passed as options.\n */\nexport function reactRouterV3BrowserTracingIntegration(\n options: Parameters<typeof browserTracingIntegration>[0] & ReactRouterOptions,\n): Integration {\n const integration = browserTracingIntegration({\n ...options,\n instrumentPageLoad: false,\n instrumentNavigation: false,\n });\n\n const { history, routes, match, instrumentPageLoad = true, instrumentNavigation = true } = options;\n\n return {\n ...integration,\n afterAllSetup(client) {\n integration.afterAllSetup(client);\n\n if (instrumentPageLoad && WINDOW.location) {\n normalizeTransactionName(\n routes,\n WINDOW.location as unknown as Location,\n match,\n (localName: string, source: ReactRouterV3TransactionSource = 'url') => {\n startBrowserTracingPageLoadSpan(client, {\n name: localName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v3',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,\n },\n });\n },\n );\n }\n\n if (instrumentNavigation && history.listen) {\n history.listen(location => {\n if (location.action === 'PUSH' || location.action === 'POP') {\n normalizeTransactionName(\n routes,\n location,\n match,\n (localName: string, source: TransactionSource = 'url') => {\n startBrowserTracingNavigationSpan(client, {\n name: localName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,\n },\n });\n },\n );\n }\n });\n }\n },\n };\n}\n\n/**\n * Normalize transaction names using `Router.match`\n */\nfunction normalizeTransactionName(\n appRoutes: Route[],\n location: Location,\n match: Match,\n callback: (pathname: string, source?: ReactRouterV3TransactionSource) => void,\n): void {\n let name = location.pathname;\n match(\n {\n location,\n routes: appRoutes,\n },\n (error, _redirectLocation, renderProps) => {\n if (error || !renderProps) {\n return callback(name);\n }\n\n const routePath = getRouteStringFromRoutes(renderProps.routes || []);\n if (routePath.length === 0 || routePath === '/*') {\n return callback(name);\n }\n\n name = routePath;\n return callback(name, 'route');\n },\n );\n}\n\n/**\n * Generate route name from array of routes\n */\nfunction getRouteStringFromRoutes(routes: Route[]): string {\n if (!Array.isArray(routes) || routes.length === 0) {\n return '';\n }\n\n const routesWithPaths: Route[] = routes.filter((route: Route) => !!route.path);\n\n let index = -1;\n for (let x = routesWithPaths.length - 1; x >= 0; x--) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const route = routesWithPaths[x]!;\n if (route.path?.startsWith('/')) {\n index = x;\n break;\n }\n }\n\n return routesWithPaths.slice(index).reduce((acc, { path }) => {\n const pathSegment = acc === '/' || acc === '' ? path : `/${path}`;\n return `${acc}${pathSegment}`;\n }, '');\n}\n"],"names":["browserTracingIntegration","WINDOW","startBrowserTracingPageLoadSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","startBrowserTracingNavigationSpan"],"mappings":"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"}
1
+ {"version":3,"file":"reactrouterv3.js","sources":["../../src/reactrouterv3.ts"],"sourcesContent":["import {\n browserTracingIntegration,\n startBrowserTracingNavigationSpan,\n startBrowserTracingPageLoadSpan,\n WINDOW,\n} from '@sentry/browser';\nimport type { Integration, TransactionSource } from '@sentry/core/browser';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n} from '@sentry/core/browser';\nimport type { Location } from './types';\nimport { URL_TEMPLATE } from '@sentry/conventions/attributes';\n\n// Many of the types below had to be mocked out to prevent typescript issues\n// these types are required for correct functionality.\n\ntype HistoryV3 = {\n location?: Location;\n listen?(cb: (location: Location) => void): void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n} & Record<string, any>;\n\nexport type Route = { path?: string; childRoutes?: Route[] };\n\nexport type Match = (\n props: { location: Location; routes: Route[] },\n cb: (error?: Error, _redirectLocation?: Location, renderProps?: { routes?: Route[] }) => void,\n) => void;\n\ntype ReactRouterV3TransactionSource = Extract<TransactionSource, 'url' | 'route'>;\n\ninterface ReactRouterOptions {\n history: HistoryV3;\n routes: Route[];\n match: Match;\n}\n\n/**\n * A browser tracing integration that uses React Router v3 to instrument navigations.\n * Expects `history` (and optionally `routes` and `matchPath`) to be passed as options.\n */\nexport function reactRouterV3BrowserTracingIntegration(\n options: Parameters<typeof browserTracingIntegration>[0] & ReactRouterOptions,\n): Integration {\n const integration = browserTracingIntegration({\n ...options,\n instrumentPageLoad: false,\n instrumentNavigation: false,\n });\n\n const { history, routes, match, instrumentPageLoad = true, instrumentNavigation = true } = options;\n\n return {\n ...integration,\n afterAllSetup(client) {\n integration.afterAllSetup(client);\n\n if (instrumentPageLoad && WINDOW.location) {\n normalizeTransactionName(\n routes,\n WINDOW.location as unknown as Location,\n match,\n (localName: string, source: ReactRouterV3TransactionSource = 'url') => {\n startBrowserTracingPageLoadSpan(client, {\n name: localName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v3',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,\n ...(source === 'route' && { [URL_TEMPLATE]: localName }),\n },\n });\n },\n );\n }\n\n if (instrumentNavigation && history.listen) {\n history.listen(location => {\n if (location.action === 'PUSH' || location.action === 'POP') {\n normalizeTransactionName(\n routes,\n location,\n match,\n (localName: string, source: TransactionSource = 'url') => {\n startBrowserTracingNavigationSpan(client, {\n name: localName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,\n ...(source === 'route' && { [URL_TEMPLATE]: localName }),\n },\n });\n },\n );\n }\n });\n }\n },\n };\n}\n\n/**\n * Normalize transaction names using `Router.match`\n */\nfunction normalizeTransactionName(\n appRoutes: Route[],\n location: Location,\n match: Match,\n callback: (pathname: string, source?: ReactRouterV3TransactionSource) => void,\n): void {\n let name = location.pathname;\n match(\n {\n location,\n routes: appRoutes,\n },\n (error, _redirectLocation, renderProps) => {\n if (error || !renderProps) {\n return callback(name);\n }\n\n const routePath = getRouteStringFromRoutes(renderProps.routes || []);\n if (routePath.length === 0 || routePath === '/*') {\n return callback(name);\n }\n\n name = routePath;\n return callback(name, 'route');\n },\n );\n}\n\n/**\n * Generate route name from array of routes\n */\nfunction getRouteStringFromRoutes(routes: Route[]): string {\n if (!Array.isArray(routes) || routes.length === 0) {\n return '';\n }\n\n const routesWithPaths: Route[] = routes.filter((route: Route) => !!route.path);\n\n let index = -1;\n for (let x = routesWithPaths.length - 1; x >= 0; x--) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const route = routesWithPaths[x]!;\n if (route.path?.startsWith('/')) {\n index = x;\n break;\n }\n }\n\n return routesWithPaths.slice(index).reduce((acc, { path }) => {\n const pathSegment = acc === '/' || acc === '' ? path : `/${path}`;\n return `${acc}${pathSegment}`;\n }, '');\n}\n"],"names":["browserTracingIntegration","WINDOW","startBrowserTracingPageLoadSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","URL_TEMPLATE","startBrowserTracingNavigationSpan"],"mappings":"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"}
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const browser = require('@sentry/browser');
4
4
  const browser$1 = require('@sentry/core/browser');
5
+ const attributes = require('@sentry/conventions/attributes');
5
6
 
6
7
  function tanstackRouterBrowserTracingIntegration(router, options = {}) {
7
8
  const castRouterInstance = router;
@@ -15,68 +16,105 @@ function tanstackRouterBrowserTracingIntegration(router, options = {}) {
15
16
  ...browserTracingIntegrationInstance,
16
17
  afterAllSetup(client) {
17
18
  browserTracingIntegrationInstance.afterAllSetup(client);
19
+ const resolveRouteMatch = (pathname, search) => {
20
+ const matchedRoutes = castRouterInstance.matchRoutes(pathname, search, {
21
+ preload: false,
22
+ throwOnError: false
23
+ });
24
+ const lastMatch = matchedRoutes[matchedRoutes.length - 1];
25
+ return lastMatch?.routeId !== "__root__" ? lastMatch : void 0;
26
+ };
27
+ const applyRouteMatch = (span, match, toLocation, fallbackName) => {
28
+ span.updateName(match ? match.routeId : fallbackName);
29
+ span.setAttribute(browser$1.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, match ? "route" : "url");
30
+ span.setAttributes({
31
+ ...match && { [attributes.URL_TEMPLATE]: match.routeId },
32
+ ...locationToSpanUrlAttributes(castRouterInstance, toLocation),
33
+ ...routeMatchToParamSpanAttributes(match)
34
+ });
35
+ };
18
36
  const initialWindowLocation = browser.WINDOW.location;
19
37
  if (instrumentPageLoad && initialWindowLocation) {
20
- const matchedRoutes = castRouterInstance.matchRoutes(
38
+ const routeMatch = resolveRouteMatch(
21
39
  initialWindowLocation.pathname,
22
- castRouterInstance.options.parseSearch(initialWindowLocation.search),
23
- { preload: false, throwOnError: false }
40
+ castRouterInstance.options.parseSearch(initialWindowLocation.search)
24
41
  );
25
- const lastMatch = matchedRoutes[matchedRoutes.length - 1];
26
- const routeMatch = lastMatch?.routeId !== "__root__" ? lastMatch : void 0;
27
- browser.startBrowserTracingPageLoadSpan(client, {
42
+ const pageloadSpan = browser.startBrowserTracingPageLoadSpan(client, {
28
43
  name: routeMatch ? routeMatch.routeId : initialWindowLocation.pathname,
29
44
  attributes: {
30
45
  [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "pageload",
31
46
  [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.pageload.react.tanstack_router",
32
47
  [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeMatch ? "route" : "url",
48
+ ...routeMatch && { [attributes.URL_TEMPLATE]: routeMatch.routeId },
33
49
  ...routeMatchToParamSpanAttributes(routeMatch)
34
50
  }
35
51
  });
52
+ const unsubscribePageloadResolved = castRouterInstance.subscribe("onResolved", (onResolvedArgs) => {
53
+ unsubscribePageloadResolved();
54
+ if (!pageloadSpan) {
55
+ return;
56
+ }
57
+ const { toLocation } = onResolvedArgs;
58
+ const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);
59
+ applyRouteMatch(pageloadSpan, resolvedMatch, toLocation, toLocation.pathname);
60
+ });
36
61
  }
37
62
  if (instrumentNavigation) {
38
- castRouterInstance.subscribe("onBeforeNavigate", (onBeforeNavigateArgs) => {
39
- if (!onBeforeNavigateArgs.fromLocation || onBeforeNavigateArgs.toLocation.state === onBeforeNavigateArgs.fromLocation.state) {
63
+ let inFlightNavigationSpan;
64
+ castRouterInstance.subscribe("onBeforeLoad", (onBeforeLoadArgs) => {
65
+ const { toLocation, fromLocation } = onBeforeLoadArgs;
66
+ if (!fromLocation || toLocation.state === fromLocation.state) {
40
67
  return;
41
68
  }
42
- const matchedRoutesOnBeforeNavigate = castRouterInstance.matchRoutes(
43
- onBeforeNavigateArgs.toLocation.pathname,
44
- onBeforeNavigateArgs.toLocation.search,
45
- { preload: false, throwOnError: false }
46
- );
47
- const onBeforeNavigateLastMatch = matchedRoutesOnBeforeNavigate[matchedRoutesOnBeforeNavigate.length - 1];
48
- const onBeforeNavigateRouteMatch = onBeforeNavigateLastMatch?.routeId !== "__root__" ? onBeforeNavigateLastMatch : void 0;
49
- const navigationLocation = browser.WINDOW.location;
50
- const navigationSpan = browser.startBrowserTracingNavigationSpan(client, {
51
- name: onBeforeNavigateRouteMatch ? onBeforeNavigateRouteMatch.routeId : navigationLocation.pathname,
52
- attributes: {
53
- [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
54
- [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.react.tanstack_router",
55
- [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: onBeforeNavigateRouteMatch ? "route" : "url"
56
- }
57
- });
58
- const unsubscribeOnResolved = castRouterInstance.subscribe("onResolved", (onResolvedArgs) => {
59
- unsubscribeOnResolved();
60
- if (navigationSpan) {
61
- const matchedRoutesOnResolved = castRouterInstance.matchRoutes(
62
- onResolvedArgs.toLocation.pathname,
63
- onResolvedArgs.toLocation.search,
64
- { preload: false, throwOnError: false }
65
- );
66
- const onResolvedLastMatch = matchedRoutesOnResolved[matchedRoutesOnResolved.length - 1];
67
- const onResolvedRouteMatch = onResolvedLastMatch?.routeId !== "__root__" ? onResolvedLastMatch : void 0;
68
- if (onResolvedRouteMatch) {
69
- navigationSpan.updateName(onResolvedRouteMatch.routeId);
70
- navigationSpan.setAttribute(browser$1.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, "route");
71
- navigationSpan.setAttributes(routeMatchToParamSpanAttributes(onResolvedRouteMatch));
69
+ const routeMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);
70
+ const fallbackName = browser.WINDOW.location?.pathname || toLocation.pathname;
71
+ if (inFlightNavigationSpan) {
72
+ applyRouteMatch(inFlightNavigationSpan, routeMatch, toLocation, fallbackName);
73
+ return;
74
+ }
75
+ inFlightNavigationSpan = browser.startBrowserTracingNavigationSpan(
76
+ client,
77
+ {
78
+ name: routeMatch ? routeMatch.routeId : fallbackName,
79
+ attributes: {
80
+ [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
81
+ [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.react.tanstack_router",
82
+ [browser$1.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeMatch ? "route" : "url",
83
+ ...routeMatch && { [attributes.URL_TEMPLATE]: routeMatch.routeId },
84
+ ...routeMatchToParamSpanAttributes(routeMatch)
72
85
  }
73
- }
74
- });
86
+ },
87
+ { url: locationToAbsoluteUrl(castRouterInstance, toLocation) }
88
+ );
89
+ });
90
+ castRouterInstance.subscribe("onResolved", (onResolvedArgs) => {
91
+ const span = inFlightNavigationSpan;
92
+ inFlightNavigationSpan = void 0;
93
+ if (!span) {
94
+ return;
95
+ }
96
+ const { toLocation } = onResolvedArgs;
97
+ const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);
98
+ if (resolvedMatch) {
99
+ applyRouteMatch(span, resolvedMatch, toLocation, browser.WINDOW.location?.pathname || toLocation.pathname);
100
+ }
75
101
  });
76
102
  }
77
103
  }
78
104
  };
79
105
  }
106
+ function locationToAbsoluteUrl(router, location) {
107
+ const search = router.options.stringifySearch?.(location.search) ?? "";
108
+ const pathWithSearch = `${location.pathname}${search && search !== "?" ? search : ""}`;
109
+ return browser.getAbsoluteUrl(pathWithSearch);
110
+ }
111
+ function locationToSpanUrlAttributes(router, location) {
112
+ const absoluteUrl = locationToAbsoluteUrl(router, location);
113
+ return {
114
+ [attributes.URL_PATH]: location.pathname,
115
+ [attributes.URL_FULL]: absoluteUrl
116
+ };
117
+ }
80
118
  function routeMatchToParamSpanAttributes(match) {
81
119
  if (!match) {
82
120
  return {};
@@ -84,8 +122,8 @@ function routeMatchToParamSpanAttributes(match) {
84
122
  const paramAttributes = {};
85
123
  Object.entries(match.params).forEach(([key, value]) => {
86
124
  paramAttributes[`url.path.params.${key}`] = value;
87
- paramAttributes[`url.path.parameter.${key}`] = value;
88
- paramAttributes[`params.${key}`] = value;
125
+ paramAttributes[attributes.URL_PATH_PARAMETER_KEY.replace("<key>", key)] = value;
126
+ paramAttributes[attributes.PARAMS_KEY.replace("<key>", key)] = value;
89
127
  });
90
128
  return paramAttributes;
91
129
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tanstackrouter.js","sources":["../../src/tanstackrouter.ts"],"sourcesContent":["import {\n browserTracingIntegration as originalBrowserTracingIntegration,\n startBrowserTracingNavigationSpan,\n startBrowserTracingPageLoadSpan,\n WINDOW,\n} from '@sentry/browser';\nimport type { Integration } from '@sentry/core/browser';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n} from '@sentry/core/browser';\nimport type { VendoredTanstackRouter, VendoredTanstackRouterRouteMatch } from './vendor/tanstackrouter-types';\n\n/**\n * A custom browser tracing integration for TanStack Router.\n *\n * The minimum compatible version of `@tanstack/react-router` is `1.64.0`.\n *\n * @param router A TanStack Router `Router` instance that should be used for routing instrumentation.\n * @param options Sentry browser tracing configuration.\n */\nexport function tanstackRouterBrowserTracingIntegration(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n router: any, // This is `any` because we don't want any type mismatches if TanStack Router changes their types\n options: Parameters<typeof originalBrowserTracingIntegration>[0] = {},\n): Integration {\n const castRouterInstance: VendoredTanstackRouter = router;\n\n const browserTracingIntegrationInstance = originalBrowserTracingIntegration({\n ...options,\n instrumentNavigation: false,\n instrumentPageLoad: false,\n });\n\n const { instrumentPageLoad = true, instrumentNavigation = true } = options;\n\n return {\n ...browserTracingIntegrationInstance,\n afterAllSetup(client) {\n browserTracingIntegrationInstance.afterAllSetup(client);\n\n const initialWindowLocation = WINDOW.location;\n if (instrumentPageLoad && initialWindowLocation) {\n const matchedRoutes = castRouterInstance.matchRoutes(\n initialWindowLocation.pathname,\n castRouterInstance.options.parseSearch(initialWindowLocation.search),\n { preload: false, throwOnError: false },\n );\n\n const lastMatch = matchedRoutes[matchedRoutes.length - 1];\n // If we only match __root__, we ended up not matching any route at all, so\n // we fall back to the pathname.\n const routeMatch = lastMatch?.routeId !== '__root__' ? lastMatch : undefined;\n\n startBrowserTracingPageLoadSpan(client, {\n name: routeMatch ? routeMatch.routeId : initialWindowLocation.pathname,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.tanstack_router',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeMatch ? 'route' : 'url',\n ...routeMatchToParamSpanAttributes(routeMatch),\n },\n });\n }\n\n if (instrumentNavigation) {\n // The onBeforeNavigate hook is called at the very beginning of a navigation and is only called once per navigation, even when the user is redirected\n castRouterInstance.subscribe('onBeforeNavigate', onBeforeNavigateArgs => {\n // onBeforeNavigate is called during pageloads. We can avoid creating navigation spans by:\n // 1. Checking if there's no fromLocation (initial pageload)\n // 2. Comparing the states of the to and from arguments\n if (\n !onBeforeNavigateArgs.fromLocation ||\n onBeforeNavigateArgs.toLocation.state === onBeforeNavigateArgs.fromLocation.state\n ) {\n return;\n }\n\n const matchedRoutesOnBeforeNavigate = castRouterInstance.matchRoutes(\n onBeforeNavigateArgs.toLocation.pathname,\n onBeforeNavigateArgs.toLocation.search,\n { preload: false, throwOnError: false },\n );\n\n const onBeforeNavigateLastMatch = matchedRoutesOnBeforeNavigate[matchedRoutesOnBeforeNavigate.length - 1];\n const onBeforeNavigateRouteMatch =\n onBeforeNavigateLastMatch?.routeId !== '__root__' ? onBeforeNavigateLastMatch : undefined;\n\n const navigationLocation = WINDOW.location;\n const navigationSpan = startBrowserTracingNavigationSpan(client, {\n name: onBeforeNavigateRouteMatch ? onBeforeNavigateRouteMatch.routeId : navigationLocation.pathname,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.tanstack_router',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: onBeforeNavigateRouteMatch ? 'route' : 'url',\n },\n });\n\n // In case the user is redirected during navigation we want to update the span with the right value.\n const unsubscribeOnResolved = castRouterInstance.subscribe('onResolved', onResolvedArgs => {\n unsubscribeOnResolved();\n if (navigationSpan) {\n const matchedRoutesOnResolved = castRouterInstance.matchRoutes(\n onResolvedArgs.toLocation.pathname,\n onResolvedArgs.toLocation.search,\n { preload: false, throwOnError: false },\n );\n\n const onResolvedLastMatch = matchedRoutesOnResolved[matchedRoutesOnResolved.length - 1];\n const onResolvedRouteMatch =\n onResolvedLastMatch?.routeId !== '__root__' ? onResolvedLastMatch : undefined;\n\n if (onResolvedRouteMatch) {\n navigationSpan.updateName(onResolvedRouteMatch.routeId);\n navigationSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');\n navigationSpan.setAttributes(routeMatchToParamSpanAttributes(onResolvedRouteMatch));\n }\n }\n });\n });\n }\n },\n };\n}\n\nfunction routeMatchToParamSpanAttributes(match: VendoredTanstackRouterRouteMatch | undefined): Record<string, string> {\n if (!match) {\n return {};\n }\n\n const paramAttributes: Record<string, string> = {};\n Object.entries(match.params).forEach(([key, value]) => {\n paramAttributes[`url.path.params.${key}`] = value; // TODO(v11): remove attribute which does not adhere to Sentry's semantic convention\n paramAttributes[`url.path.parameter.${key}`] = value;\n paramAttributes[`params.${key}`] = value; // params.[key] is an alias\n });\n\n return paramAttributes;\n}\n"],"names":["originalBrowserTracingIntegration","WINDOW","startBrowserTracingPageLoadSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","startBrowserTracingNavigationSpan"],"mappings":"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"}
1
+ {"version":3,"file":"tanstackrouter.js","sources":["../../src/tanstackrouter.ts"],"sourcesContent":["import {\n browserTracingIntegration as originalBrowserTracingIntegration,\n getAbsoluteUrl,\n startBrowserTracingNavigationSpan,\n startBrowserTracingPageLoadSpan,\n WINDOW,\n} from '@sentry/browser';\nimport type { Integration } from '@sentry/core/browser';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n} from '@sentry/core/browser';\nimport type { VendoredTanstackRouter, VendoredTanstackRouterRouteMatch } from './vendor/tanstackrouter-types';\nimport { PARAMS_KEY, URL_FULL, URL_PATH, URL_PATH_PARAMETER_KEY, URL_TEMPLATE } from '@sentry/conventions/attributes';\n\ninterface TanstackRouterLocation {\n pathname: string;\n search: Record<string, unknown>;\n state?: unknown;\n}\n\n/**\n * A custom browser tracing integration for TanStack Router.\n *\n * The minimum compatible version of `@tanstack/react-router` is `1.64.0`.\n *\n * @param router A TanStack Router `Router` instance that should be used for routing instrumentation.\n * @param options Sentry browser tracing configuration.\n */\nexport function tanstackRouterBrowserTracingIntegration(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n router: any, // This is `any` because we don't want any type mismatches if TanStack Router changes their types\n options: Parameters<typeof originalBrowserTracingIntegration>[0] = {},\n): Integration {\n const castRouterInstance: VendoredTanstackRouter = router;\n\n const browserTracingIntegrationInstance = originalBrowserTracingIntegration({\n ...options,\n instrumentNavigation: false,\n instrumentPageLoad: false,\n });\n\n const { instrumentPageLoad = true, instrumentNavigation = true } = options;\n\n return {\n ...browserTracingIntegrationInstance,\n afterAllSetup(client) {\n browserTracingIntegrationInstance.afterAllSetup(client);\n\n const resolveRouteMatch = (pathname: string, search: unknown): VendoredTanstackRouterRouteMatch | undefined => {\n const matchedRoutes = castRouterInstance.matchRoutes(pathname, search as {}, {\n preload: false,\n throwOnError: false,\n });\n const lastMatch = matchedRoutes[matchedRoutes.length - 1];\n // If we only match __root__, we ended up not matching any route at all, so\n // we fall back to the pathname.\n return lastMatch?.routeId !== '__root__' ? lastMatch : undefined;\n };\n\n const applyRouteMatch = (\n span: NonNullable<ReturnType<typeof startBrowserTracingPageLoadSpan>>,\n match: VendoredTanstackRouterRouteMatch | undefined,\n toLocation: TanstackRouterLocation,\n fallbackName: string,\n ): void => {\n span.updateName(match ? match.routeId : fallbackName);\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, match ? 'route' : 'url');\n span.setAttributes({\n ...(match && { [URL_TEMPLATE]: match.routeId }),\n ...locationToSpanUrlAttributes(castRouterInstance, toLocation),\n ...routeMatchToParamSpanAttributes(match),\n });\n };\n\n const initialWindowLocation = WINDOW.location;\n if (instrumentPageLoad && initialWindowLocation) {\n const routeMatch = resolveRouteMatch(\n initialWindowLocation.pathname,\n castRouterInstance.options.parseSearch(initialWindowLocation.search),\n );\n\n const pageloadSpan = startBrowserTracingPageLoadSpan(client, {\n name: routeMatch ? routeMatch.routeId : initialWindowLocation.pathname,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.tanstack_router',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeMatch ? 'route' : 'url',\n ...(routeMatch && { [URL_TEMPLATE]: routeMatch.routeId }),\n ...routeMatchToParamSpanAttributes(routeMatch),\n },\n });\n\n // A redirect thrown during the initial pageload leaves the span named after the pre-redirect\n // route, so correct it to the resolved route once.\n const unsubscribePageloadResolved = castRouterInstance.subscribe('onResolved', onResolvedArgs => {\n unsubscribePageloadResolved();\n if (!pageloadSpan) {\n return;\n }\n const { toLocation } = onResolvedArgs;\n const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n applyRouteMatch(pageloadSpan, resolvedMatch, toLocation, toLocation.pathname);\n });\n }\n\n if (instrumentNavigation) {\n // Navigation is driven by `onBeforeLoad` (accurate start) + `onResolved` (final route), not\n // `onBeforeNavigate`, which TanStack stops firing after any loader redirect (TanStack/router#3920).\n // A redirect chain emits one `onBeforeLoad` per load but a single `onResolved`, so we start the\n // span on the first `onBeforeLoad`, rename it on later ones, and clear it on `onResolved`.\n let inFlightNavigationSpan: ReturnType<typeof startBrowserTracingNavigationSpan> | undefined;\n\n castRouterInstance.subscribe('onBeforeLoad', onBeforeLoadArgs => {\n const { toLocation, fromLocation } = onBeforeLoadArgs;\n // Skip the initial pageload (no fromLocation) and no-op reloads (same state).\n if (!fromLocation || toLocation.state === fromLocation.state) {\n return;\n }\n\n const routeMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n const fallbackName = WINDOW.location?.pathname || toLocation.pathname;\n\n if (inFlightNavigationSpan) {\n // Redirect continuation within the same navigation: keep the span, update the target.\n applyRouteMatch(inFlightNavigationSpan, routeMatch, toLocation, fallbackName);\n return;\n }\n\n inFlightNavigationSpan = startBrowserTracingNavigationSpan(\n client,\n {\n name: routeMatch ? routeMatch.routeId : fallbackName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.tanstack_router',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeMatch ? 'route' : 'url',\n ...(routeMatch && { [URL_TEMPLATE]: routeMatch.routeId }),\n ...routeMatchToParamSpanAttributes(routeMatch),\n },\n },\n { url: locationToAbsoluteUrl(castRouterInstance, toLocation) },\n );\n });\n\n castRouterInstance.subscribe('onResolved', onResolvedArgs => {\n const span = inFlightNavigationSpan;\n inFlightNavigationSpan = undefined;\n if (!span) {\n return;\n }\n const { toLocation } = onResolvedArgs;\n const resolvedMatch = resolveRouteMatch(toLocation.pathname, toLocation.search);\n if (resolvedMatch) {\n applyRouteMatch(span, resolvedMatch, toLocation, WINDOW.location?.pathname || toLocation.pathname);\n }\n });\n }\n },\n };\n}\n\nfunction locationToAbsoluteUrl(router: VendoredTanstackRouter, location: TanstackRouterLocation): string {\n const search = router.options.stringifySearch?.(location.search) ?? '';\n const pathWithSearch = `${location.pathname}${search && search !== '?' ? search : ''}`;\n\n return getAbsoluteUrl(pathWithSearch);\n}\n\nfunction locationToSpanUrlAttributes(\n router: VendoredTanstackRouter,\n location: TanstackRouterLocation,\n): Record<string, string> {\n const absoluteUrl = locationToAbsoluteUrl(router, location);\n\n return {\n [URL_PATH]: location.pathname,\n [URL_FULL]: absoluteUrl,\n };\n}\n\nfunction routeMatchToParamSpanAttributes(match: VendoredTanstackRouterRouteMatch | undefined): Record<string, string> {\n if (!match) {\n return {};\n }\n\n const paramAttributes: Record<string, string> = {};\n Object.entries(match.params).forEach(([key, value]) => {\n paramAttributes[`url.path.params.${key}`] = value; // TODO(v11): remove attribute which does not adhere to Sentry's semantic convention\n paramAttributes[URL_PATH_PARAMETER_KEY.replace('<key>', key)] = value;\n paramAttributes[PARAMS_KEY.replace('<key>', key)] = value; // params.[key] is an alias\n });\n\n return paramAttributes;\n}\n"],"names":["originalBrowserTracingIntegration","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","URL_TEMPLATE","WINDOW","startBrowserTracingPageLoadSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startBrowserTracingNavigationSpan","getAbsoluteUrl","URL_PATH","URL_FULL","URL_PATH_PARAMETER_KEY","PARAMS_KEY"],"mappings":"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"}
@@ -1 +1 @@
1
- {"type":"module","version":"10.63.0","sideEffects":false}
1
+ {"type":"module","version":"10.65.0","sideEffects":false}
@@ -5,6 +5,7 @@ import { DEBUG_BUILD } from '../debug-build.js';
5
5
  import { hoistNonReactStatics } from '../hoist-non-react-statics.js';
6
6
  import { checkRouteForAsyncHandler } from './lazy-routes.js';
7
7
  import { getActiveRootSpan, setNavigationContext, clearNavigationContext, resolveRouteNameAndSource, transactionNameHasWildcard, initializeRouterUtils } from './utils.js';
8
+ import { URL_TEMPLATE } from '@sentry/conventions/attributes';
8
9
 
9
10
  let _useEffect;
10
11
  let _useLocation;
@@ -165,6 +166,9 @@ function updateNavigationSpan(activeRootSpan, location, allRoutes2, forceUpdate
165
166
  if (isImprovement) {
166
167
  activeRootSpan.updateName(name);
167
168
  activeRootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
169
+ if (source === "route") {
170
+ activeRootSpan.setAttribute(URL_TEMPLATE, name);
171
+ }
168
172
  if (!transactionNameHasWildcard(name) && source === "route") {
169
173
  addNonEnumerableProperty(
170
174
  activeRootSpan,
@@ -549,6 +553,9 @@ function handleNavigation(opts) {
549
553
  } else {
550
554
  trackedNav.span.updateName(name);
551
555
  trackedNav.span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
556
+ if (source === "route") {
557
+ trackedNav.span.setAttribute(URL_TEMPLATE, name);
558
+ }
552
559
  addNonEnumerableProperty(
553
560
  trackedNav.span,
554
561
  "__sentry_navigation_name_set__",
@@ -580,7 +587,8 @@ function handleNavigation(opts) {
580
587
  attributes: {
581
588
  [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
582
589
  [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
583
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter${version ? `_v${version}` : ""}`
590
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter${version ? `_v${version}` : ""}`,
591
+ ...source === "route" && { [URL_TEMPLATE]: placeholderEntry.routeName }
584
592
  }
585
593
  });
586
594
  } catch (e) {
@@ -646,6 +654,9 @@ function updatePageloadTransaction({
646
654
  if (activeRootSpan) {
647
655
  activeRootSpan.updateName(name);
648
656
  activeRootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
657
+ if (source === "route") {
658
+ activeRootSpan.setAttribute(URL_TEMPLATE, name);
659
+ }
649
660
  patchSpanEnd(activeRootSpan, location, routes, basename, "pageload");
650
661
  }
651
662
  } else if (activeRootSpan) {
@@ -694,6 +705,9 @@ function tryUpdateSpanNameBeforeEnd(span, spanJson, currentName, location, route
694
705
  if (isImprovement && spanNotEnded) {
695
706
  span.updateName(name);
696
707
  span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
708
+ if (source === "route") {
709
+ span.setAttribute(URL_TEMPLATE, name);
710
+ }
697
711
  }
698
712
  } catch (error) {
699
713
  DEBUG_BUILD && debug.warn(`Error updating span details before ending: ${error}`);