@sentry/react-router 11.0.0-alpha.1 → 11.0.0-alpha.2
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/README.md +2 -2
- package/build/cjs/client/createClientInstrumentation.js +30 -23
- package/build/cjs/client/createClientInstrumentation.js.map +1 -1
- package/build/cjs/client/hydratedRouter.js +11 -19
- package/build/cjs/client/hydratedRouter.js.map +1 -1
- package/build/cjs/client/tracingIntegration.js.map +1 -1
- package/build/cjs/client/utils.js +32 -12
- package/build/cjs/client/utils.js.map +1 -1
- package/build/cjs/cloudflare/index.js +3 -2
- package/build/cjs/cloudflare/index.js.map +1 -1
- package/build/cjs/common/utils.js.map +1 -1
- package/build/cjs/index.server.js +0 -6
- package/build/cjs/index.server.js.map +1 -1
- package/build/cjs/server/createServerInstrumentation.js +22 -15
- package/build/cjs/server/createServerInstrumentation.js.map +1 -1
- package/build/cjs/server/getMetaTagTransformer.js +3 -2
- package/build/cjs/server/getMetaTagTransformer.js.map +1 -1
- package/build/cjs/server/integration/lowQualityTransactionsFilterIntegration.js +3 -2
- package/build/cjs/server/integration/lowQualityTransactionsFilterIntegration.js.map +1 -1
- package/build/cjs/server/wrapSentryHandleRequest.js +2 -2
- package/build/cjs/server/wrapSentryHandleRequest.js.map +1 -1
- package/build/cjs/vite/buildEnd/handleOnBuildEnd.js +17 -29
- package/build/cjs/vite/buildEnd/handleOnBuildEnd.js.map +1 -1
- package/build/cjs/vite/index.js +12 -0
- package/build/cjs/vite/index.js.map +1 -0
- package/build/cjs/vite/makeCustomSentryVitePlugins.js +21 -32
- package/build/cjs/vite/makeCustomSentryVitePlugins.js.map +1 -1
- package/build/esm/client/createClientInstrumentation.js +22 -15
- package/build/esm/client/createClientInstrumentation.js.map +1 -1
- package/build/esm/client/hydratedRouter.js +14 -22
- package/build/esm/client/hydratedRouter.js.map +1 -1
- package/build/esm/client/tracingIntegration.js.map +1 -1
- package/build/esm/client/utils.js +34 -14
- package/build/esm/client/utils.js.map +1 -1
- package/build/esm/cloudflare/index.js +3 -2
- package/build/esm/cloudflare/index.js.map +1 -1
- package/build/esm/common/utils.js.map +1 -1
- package/build/esm/index.server.js +0 -3
- package/build/esm/index.server.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/server/createServerInstrumentation.js +25 -18
- package/build/esm/server/createServerInstrumentation.js.map +1 -1
- package/build/esm/server/getMetaTagTransformer.js +3 -2
- package/build/esm/server/getMetaTagTransformer.js.map +1 -1
- package/build/esm/server/integration/lowQualityTransactionsFilterIntegration.js +3 -2
- package/build/esm/server/integration/lowQualityTransactionsFilterIntegration.js.map +1 -1
- package/build/esm/server/wrapSentryHandleRequest.js +4 -4
- package/build/esm/server/wrapSentryHandleRequest.js.map +1 -1
- package/build/esm/vite/buildEnd/handleOnBuildEnd.js +17 -29
- package/build/esm/vite/buildEnd/handleOnBuildEnd.js.map +1 -1
- package/build/esm/vite/index.js +4 -0
- package/build/esm/vite/index.js.map +1 -0
- package/build/esm/vite/makeCustomSentryVitePlugins.js +21 -32
- package/build/esm/vite/makeCustomSentryVitePlugins.js.map +1 -1
- package/build/types/client/createClientInstrumentation.d.ts +1 -1
- package/build/types/client/createClientInstrumentation.d.ts.map +1 -1
- package/build/types/client/hydratedRouter.d.ts.map +1 -1
- package/build/types/client/tracingIntegration.d.ts +1 -1
- package/build/types/client/tracingIntegration.d.ts.map +1 -1
- package/build/types/client/utils.d.ts +7 -1
- package/build/types/client/utils.d.ts.map +1 -1
- package/build/types/cloudflare/index.d.ts.map +1 -1
- package/build/types/common/utils.d.ts +3 -2
- package/build/types/common/utils.d.ts.map +1 -1
- package/build/types/index.server.d.ts +0 -1
- package/build/types/index.server.d.ts.map +1 -1
- package/build/types/index.types.d.ts +3 -1
- package/build/types/index.types.d.ts.map +1 -1
- package/build/types/server/createServerInstrumentation.d.ts +1 -1
- package/build/types/server/createServerInstrumentation.d.ts.map +1 -1
- package/build/types/server/getMetaTagTransformer.d.ts.map +1 -1
- package/build/types/server/integration/lowQualityTransactionsFilterIntegration.d.ts.map +1 -1
- package/build/types/server/wrapSentryHandleRequest.d.ts.map +1 -1
- package/build/types/vite/buildEnd/handleOnBuildEnd.d.ts.map +1 -1
- package/build/types/vite/makeCustomSentryVitePlugins.d.ts.map +1 -1
- package/build/types/vite/types.d.ts +15 -13
- package/build/types/vite/types.d.ts.map +1 -1
- package/package.json +16 -10
- package/build/types/common/types.d.ts +0 -86
- package/build/types/common/types.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ Update your vite.config.ts file to include the `sentryReactRouter` plugin and al
|
|
|
155
155
|
|
|
156
156
|
```ts
|
|
157
157
|
import { reactRouter } from '@react-router/dev/vite';
|
|
158
|
-
import { sentryReactRouter } from '@sentry/react-router';
|
|
158
|
+
import { sentryReactRouter } from '@sentry/react-router/vite';
|
|
159
159
|
import { defineConfig } from 'vite';
|
|
160
160
|
|
|
161
161
|
const sentryConfig = {
|
|
@@ -177,7 +177,7 @@ Next, in your `react-router.config.ts` file, include the `sentryOnBuildEnd` hook
|
|
|
177
177
|
|
|
178
178
|
```ts
|
|
179
179
|
import type { Config } from '@react-router/dev/config';
|
|
180
|
-
import { sentryOnBuildEnd } from '@sentry/react-router';
|
|
180
|
+
import { sentryOnBuildEnd } from '@sentry/react-router/vite';
|
|
181
181
|
|
|
182
182
|
export default {
|
|
183
183
|
ssr: true,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const browser = require('@sentry/browser');
|
|
3
|
+
const browser$1 = require('@sentry/browser');
|
|
4
4
|
const core = require('@sentry/core');
|
|
5
|
+
const browser = require('@sentry/core/browser');
|
|
5
6
|
const debugBuild = require('../common/debug-build.js');
|
|
6
7
|
const utils = require('../common/utils.js');
|
|
7
8
|
const utils$1 = require('./utils.js');
|
|
@@ -38,18 +39,20 @@ function createSentryClientInstrumentation(options = {}) {
|
|
|
38
39
|
currentNumericNavigationSpan = void 0;
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
|
-
browser.startBrowserTracingNavigationSpan(
|
|
42
|
+
browser$1.startBrowserTracingNavigationSpan(
|
|
42
43
|
client,
|
|
43
44
|
{
|
|
44
|
-
|
|
45
|
+
// With span streaming, span names have to be low cardinality, so we can't fall back to
|
|
46
|
+
// the URL. The route hooks parameterize the span once they resolve.
|
|
47
|
+
name: core.hasSpanStreamingEnabled(client) ? core.NAVIGATION_SPAN_NAME_FALLBACK : pathname,
|
|
45
48
|
attributes: {
|
|
46
|
-
[
|
|
49
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: "url",
|
|
47
50
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
|
|
48
51
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.react_router.instrumentation_api",
|
|
49
52
|
"navigation.type": "browser.popstate"
|
|
50
53
|
}
|
|
51
54
|
},
|
|
52
|
-
{ url: browser.getAbsoluteUrl(pathname) }
|
|
55
|
+
{ url: browser$1.getAbsoluteUrl(pathname) }
|
|
53
56
|
);
|
|
54
57
|
});
|
|
55
58
|
debugBuild.DEBUG_BUILD && core.debug.log("React Router popstate listener registered for browser back/forward navigation.");
|
|
@@ -72,18 +75,20 @@ function createSentryClientInstrumentation(options = {}) {
|
|
|
72
75
|
if (client2) {
|
|
73
76
|
const navigationType = info.to < 0 ? "router.back" : "router.forward";
|
|
74
77
|
const currentPathname = WINDOW.location?.pathname || info.currentUrl;
|
|
75
|
-
navigationSpan2 = browser.startBrowserTracingNavigationSpan(
|
|
78
|
+
navigationSpan2 = browser$1.startBrowserTracingNavigationSpan(
|
|
76
79
|
client2,
|
|
77
80
|
{
|
|
78
|
-
|
|
81
|
+
// With span streaming, span names have to be low cardinality, so we can't fall back
|
|
82
|
+
// to the URL. The route is resolved once the navigation settles.
|
|
83
|
+
name: core.hasSpanStreamingEnabled(client2) ? core.NAVIGATION_SPAN_NAME_FALLBACK : currentPathname,
|
|
79
84
|
attributes: {
|
|
80
|
-
[
|
|
85
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: "url",
|
|
81
86
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
|
|
82
87
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.react_router.instrumentation_api",
|
|
83
88
|
"navigation.type": navigationType
|
|
84
89
|
}
|
|
85
90
|
},
|
|
86
|
-
{ url: browser.getAbsoluteUrl(currentPathname) }
|
|
91
|
+
{ url: browser$1.getAbsoluteUrl(currentPathname) }
|
|
87
92
|
);
|
|
88
93
|
currentNumericNavigationSpan = navigationSpan2;
|
|
89
94
|
}
|
|
@@ -109,12 +114,14 @@ function createSentryClientInstrumentation(options = {}) {
|
|
|
109
114
|
const toPath = utils$1.resolveNavigateArg(info.to);
|
|
110
115
|
let navigationSpan;
|
|
111
116
|
if (client) {
|
|
112
|
-
navigationSpan = browser.startBrowserTracingNavigationSpan(
|
|
117
|
+
navigationSpan = browser$1.startBrowserTracingNavigationSpan(
|
|
113
118
|
client,
|
|
114
119
|
{
|
|
115
|
-
|
|
120
|
+
// With span streaming, span names have to be low cardinality, so we can't fall back to
|
|
121
|
+
// the URL. The route hooks parameterize the span once they resolve.
|
|
122
|
+
name: core.hasSpanStreamingEnabled(client) ? core.NAVIGATION_SPAN_NAME_FALLBACK : toPath,
|
|
116
123
|
attributes: {
|
|
117
|
-
[
|
|
124
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: "url",
|
|
118
125
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
|
|
119
126
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.react_router.instrumentation_api",
|
|
120
127
|
"navigation.type": "router.navigate"
|
|
@@ -135,11 +142,11 @@ function createSentryClientInstrumentation(options = {}) {
|
|
|
135
142
|
return;
|
|
136
143
|
},
|
|
137
144
|
async fetch(callFetch, info) {
|
|
138
|
-
await
|
|
145
|
+
await browser.startSpan(
|
|
139
146
|
{
|
|
140
147
|
name: `Fetcher ${info.fetcherKey}`,
|
|
141
148
|
attributes: {
|
|
142
|
-
[attributes.SENTRY_OP]: op.
|
|
149
|
+
[attributes.SENTRY_OP]: op.FUNCTION,
|
|
143
150
|
[attributes.CODE_FUNCTION_NAME]: "fetcher",
|
|
144
151
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api"
|
|
145
152
|
}
|
|
@@ -165,11 +172,11 @@ function createSentryClientInstrumentation(options = {}) {
|
|
|
165
172
|
const pattern = utils.normalizeRoutePath(utils.getPattern(info));
|
|
166
173
|
const routePattern = pattern || urlPath;
|
|
167
174
|
updateRootSpanRoute(routePattern, !!pattern);
|
|
168
|
-
await
|
|
175
|
+
await browser.startSpan(
|
|
169
176
|
{
|
|
170
177
|
name: routePattern,
|
|
171
178
|
attributes: {
|
|
172
|
-
[attributes.SENTRY_OP]: op.
|
|
179
|
+
[attributes.SENTRY_OP]: op.FUNCTION,
|
|
173
180
|
[attributes.CODE_FUNCTION_NAME]: "clientLoader",
|
|
174
181
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api"
|
|
175
182
|
}
|
|
@@ -190,11 +197,11 @@ function createSentryClientInstrumentation(options = {}) {
|
|
|
190
197
|
const pattern = utils.normalizeRoutePath(utils.getPattern(info));
|
|
191
198
|
const routePattern = pattern || urlPath;
|
|
192
199
|
updateRootSpanRoute(routePattern, !!pattern);
|
|
193
|
-
await
|
|
200
|
+
await browser.startSpan(
|
|
194
201
|
{
|
|
195
202
|
name: routePattern,
|
|
196
203
|
attributes: {
|
|
197
|
-
[attributes.SENTRY_OP]: op.
|
|
204
|
+
[attributes.SENTRY_OP]: op.FUNCTION,
|
|
198
205
|
[attributes.CODE_FUNCTION_NAME]: "clientAction",
|
|
199
206
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api"
|
|
200
207
|
}
|
|
@@ -220,11 +227,11 @@ function createSentryClientInstrumentation(options = {}) {
|
|
|
220
227
|
}
|
|
221
228
|
const middlewareIndex = counters[routeId] ?? 0;
|
|
222
229
|
counters[routeId] = middlewareIndex + 1;
|
|
223
|
-
await
|
|
230
|
+
await browser.startSpan(
|
|
224
231
|
{
|
|
225
232
|
name: `middleware ${routeId}`,
|
|
226
233
|
attributes: {
|
|
227
|
-
[attributes.SENTRY_OP]: op.
|
|
234
|
+
[attributes.SENTRY_OP]: op.MIDDLEWARE,
|
|
228
235
|
[attributes.CODE_FUNCTION_NAME]: "clientMiddleware",
|
|
229
236
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api",
|
|
230
237
|
"react_router.route.id": routeId,
|
|
@@ -244,11 +251,11 @@ function createSentryClientInstrumentation(options = {}) {
|
|
|
244
251
|
);
|
|
245
252
|
},
|
|
246
253
|
async lazy(callLazy) {
|
|
247
|
-
await
|
|
254
|
+
await browser.startSpan(
|
|
248
255
|
{
|
|
249
256
|
name: "Lazy Route Load",
|
|
250
257
|
attributes: {
|
|
251
|
-
[attributes.SENTRY_OP]: op.
|
|
258
|
+
[attributes.SENTRY_OP]: op.FUNCTION,
|
|
252
259
|
[attributes.CODE_FUNCTION_NAME]: "lazy",
|
|
253
260
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api"
|
|
254
261
|
}
|
|
@@ -281,7 +288,7 @@ function updateRootSpanRoute(routeName, hasPattern) {
|
|
|
281
288
|
return;
|
|
282
289
|
}
|
|
283
290
|
core.updateSpanName(rootSpan, routeName);
|
|
284
|
-
rootSpan.setAttributes({ [
|
|
291
|
+
rootSpan.setAttributes({ [attributes.SENTRY_SEGMENT_NAME_SOURCE]: "route", [attributes.URL_TEMPLATE]: routeName });
|
|
285
292
|
}
|
|
286
293
|
function isClientInstrumentationApiUsed() {
|
|
287
294
|
return !!GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createClientInstrumentation.js","sources":["../../../src/client/createClientInstrumentation.ts"],"sourcesContent":["// oxlint-disable max-lines\nimport { getAbsoluteUrl, startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getClient,\n getRootSpan,\n GLOBAL_OBJ,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n SPAN_STATUS_ERROR,\n startSpan,\n updateSpanName,\n filterCollectedUrl,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport type { ClientInstrumentation, InstrumentableRoute, InstrumentableRouter } from '../common/types';\nimport { captureInstrumentationError, getPathFromRequest, getPattern, normalizeRoutePath } from '../common/utils';\nimport {\n resolveNavigateAbsoluteUrl,\n resolveNavigateArg,\n finalizeNavigationSpanFromHydratedRouter,\n updateNavigationSpanUrlFromLocation,\n} from './utils';\nimport { CODE_FUNCTION_NAME, SENTRY_OP, URL_FULL, URL_TEMPLATE } from '@sentry/conventions/attributes';\nimport { GENERAL_FUNCTION_SPAN_OP, WEB_SERVER_MIDDLEWARE_SPAN_OP } from '@sentry/conventions/op';\n\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;\n\n// Tracks active numeric navigation span to prevent duplicate spans when popstate fires\nlet currentNumericNavigationSpan: Span | undefined;\n\n// Per-request middleware counters, keyed by Request\nconst middlewareCountersMap = new WeakMap<object, Record<string, number>>();\n\nconst SENTRY_CLIENT_INSTRUMENTATION_FLAG = '__sentryReactRouterClientInstrumentationUsed';\n// Intentionally never reset - once set, instrumentation API handles all navigations for the session.\nconst SENTRY_NAVIGATE_HOOK_INVOKED_FLAG = '__sentryReactRouterNavigateHookInvoked';\nconst SENTRY_POPSTATE_LISTENER_ADDED_FLAG = '__sentryReactRouterPopstateListenerAdded';\n\ntype GlobalObjWithFlags = typeof GLOBAL_OBJ & {\n [SENTRY_CLIENT_INSTRUMENTATION_FLAG]?: boolean;\n [SENTRY_NAVIGATE_HOOK_INVOKED_FLAG]?: boolean;\n [SENTRY_POPSTATE_LISTENER_ADDED_FLAG]?: boolean;\n};\n\nconst GLOBAL_WITH_FLAGS = GLOBAL_OBJ as GlobalObjWithFlags;\n\n/**\n * Options for creating Sentry client instrumentation.\n */\nexport interface CreateSentryClientInstrumentationOptions {\n /**\n * Whether to capture errors from loaders/actions automatically.\n * Set to `false` to avoid duplicates if using custom error handlers.\n * @default true\n */\n captureErrors?: boolean;\n}\n\n/**\n * Creates a Sentry client instrumentation for React Router's instrumentation API.\n */\nexport function createSentryClientInstrumentation(\n options: CreateSentryClientInstrumentationOptions = {},\n): ClientInstrumentation {\n const { captureErrors = true } = options;\n\n DEBUG_BUILD && debug.log('React Router client instrumentation API created.');\n\n return {\n router(router: InstrumentableRouter) {\n // Set the flag when React Router actually invokes our instrumentation.\n // This ensures the flag is only set in Library Mode (where hooks run),\n // not in Framework Mode (where hooks are never called).\n // See: https://github.com/remix-run/react-router/discussions/13749\n GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG] = true;\n DEBUG_BUILD && debug.log('React Router client instrumentation API router hook registered.');\n\n // Add popstate listener for browser back/forward navigation (persists for session, one listener only)\n if (!GLOBAL_WITH_FLAGS[SENTRY_POPSTATE_LISTENER_ADDED_FLAG] && WINDOW.addEventListener) {\n GLOBAL_WITH_FLAGS[SENTRY_POPSTATE_LISTENER_ADDED_FLAG] = true;\n\n WINDOW.addEventListener('popstate', () => {\n const client = getClient();\n if (!client) {\n currentNumericNavigationSpan = undefined;\n return;\n }\n\n const pathname = WINDOW.location?.pathname || '/';\n\n // If there's an active numeric navigation span, update it instead of creating a duplicate\n if (currentNumericNavigationSpan) {\n if (currentNumericNavigationSpan.isRecording()) {\n updateNavigationSpanUrlFromLocation(currentNumericNavigationSpan);\n }\n currentNumericNavigationSpan = undefined;\n return;\n }\n\n // Only create a new span for actual browser back/forward button clicks\n startBrowserTracingNavigationSpan(\n client,\n {\n name: pathname,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': 'browser.popstate',\n },\n },\n { url: getAbsoluteUrl(pathname) },\n );\n });\n\n DEBUG_BUILD && debug.log('React Router popstate listener registered for browser back/forward navigation.');\n }\n\n router.instrument({\n async navigate(callNavigate, info) {\n // navigate(0) triggers a page reload - skip span creation, but still capture errors\n // (navigation can be rejected before reload, e.g., by a navigation guard)\n if (info.to === 0) {\n const result = await callNavigate();\n if (result.status === 'error' && result.error instanceof Error) {\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n [URL_FULL]: filterCollectedUrl(info.currentUrl),\n });\n }\n return;\n }\n\n GLOBAL_WITH_FLAGS[SENTRY_NAVIGATE_HOOK_INVOKED_FLAG] = true;\n\n // Handle numeric navigations (navigate(-1), navigate(1), etc.)\n if (typeof info.to === 'number') {\n const client = getClient();\n let navigationSpan;\n\n if (client) {\n const navigationType = info.to < 0 ? 'router.back' : 'router.forward';\n const currentPathname = WINDOW.location?.pathname || info.currentUrl;\n\n navigationSpan = startBrowserTracingNavigationSpan(\n client,\n {\n name: currentPathname,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': navigationType,\n },\n },\n { url: getAbsoluteUrl(currentPathname) },\n );\n\n // Store ref so popstate listener can update it instead of creating a duplicate\n currentNumericNavigationSpan = navigationSpan;\n }\n\n try {\n const result = await callNavigate();\n\n if (navigationSpan && WINDOW.location) {\n finalizeNavigationSpanFromHydratedRouter(navigationSpan);\n }\n\n if (result.status === 'error' && result.error instanceof Error) {\n if (navigationSpan) {\n navigationSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n [URL_FULL]: WINDOW.location?.pathname || filterCollectedUrl(info.currentUrl),\n });\n }\n } finally {\n currentNumericNavigationSpan = undefined;\n }\n return;\n }\n\n // Handle string/object navigations (e.g., navigate('/about') or navigate({ pathname: '/about' }))\n const client = getClient();\n const toPath = resolveNavigateArg(info.to);\n let navigationSpan;\n\n if (client) {\n navigationSpan = startBrowserTracingNavigationSpan(\n client,\n {\n name: toPath,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': 'router.navigate',\n },\n },\n { url: resolveNavigateAbsoluteUrl(info.to, info.currentUrl) },\n );\n }\n\n const result = await callNavigate();\n if (result.status === 'error' && result.error instanceof Error) {\n if (navigationSpan) {\n navigationSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n [URL_FULL]: filterCollectedUrl(toPath),\n });\n }\n return;\n },\n\n async fetch(callFetch, info) {\n await startSpan(\n {\n name: `Fetcher ${info.fetcherKey}`,\n attributes: {\n [SENTRY_OP]: GENERAL_FUNCTION_SPAN_OP,\n [CODE_FUNCTION_NAME]: 'fetcher',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callFetch();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.fetcher', {\n [URL_FULL]: filterCollectedUrl(info.href),\n });\n }\n },\n );\n },\n });\n },\n\n route(route: InstrumentableRoute) {\n const routeId = route.id;\n\n route.instrument({\n async loader(callLoader, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = normalizeRoutePath(getPattern(info));\n const routePattern = pattern || urlPath;\n // Parameterize the active navigation root span. (Route hooks don't fire on initial\n // pageload, so this only affects navigations.)\n updateRootSpanRoute(routePattern, !!pattern);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SENTRY_OP]: GENERAL_FUNCTION_SPAN_OP,\n [CODE_FUNCTION_NAME]: 'clientLoader',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLoader();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.client_loader', {\n [URL_FULL]: urlPath,\n });\n }\n },\n );\n },\n\n async action(callAction, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = normalizeRoutePath(getPattern(info));\n const routePattern = pattern || urlPath;\n updateRootSpanRoute(routePattern, !!pattern);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SENTRY_OP]: GENERAL_FUNCTION_SPAN_OP,\n [CODE_FUNCTION_NAME]: 'clientAction',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callAction();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.client_action', {\n [URL_FULL]: urlPath,\n });\n }\n },\n );\n },\n\n async middleware(callMiddleware, info) {\n const urlPath = getPathFromRequest(info.request);\n const routePattern = normalizeRoutePath(getPattern(info)) || urlPath;\n\n let counters = middlewareCountersMap.get(info.request);\n if (!counters) {\n counters = {};\n middlewareCountersMap.set(info.request, counters);\n }\n\n const middlewareIndex = counters[routeId] ?? 0;\n counters[routeId] = middlewareIndex + 1;\n\n await startSpan(\n {\n name: `middleware ${routeId}`,\n attributes: {\n [SENTRY_OP]: WEB_SERVER_MIDDLEWARE_SPAN_OP,\n [CODE_FUNCTION_NAME]: 'clientMiddleware',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n 'react_router.route.id': routeId,\n 'http.route': routePattern,\n 'react_router.middleware.index': middlewareIndex,\n },\n },\n async span => {\n const result = await callMiddleware();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.client_middleware', {\n [URL_FULL]: urlPath,\n });\n }\n },\n );\n },\n\n async lazy(callLazy) {\n await startSpan(\n {\n name: 'Lazy Route Load',\n attributes: {\n [SENTRY_OP]: GENERAL_FUNCTION_SPAN_OP,\n [CODE_FUNCTION_NAME]: 'lazy',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLazy();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.client_lazy', {});\n }\n },\n );\n },\n });\n },\n };\n}\n\n/**\n * Updates the active navigation/pageload root span name with the parameterized route, so the\n * transaction reflects the parameterized route pattern (e.g. `/users/:id`).\n */\nfunction updateRootSpanRoute(routeName: string, hasPattern: boolean): void {\n if (!hasPattern) {\n return;\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n if (!rootSpan) {\n return;\n }\n\n const { attributes } = spanToJSON(rootSpan);\n const op = attributes[SENTRY_OP];\n if (op !== 'navigation' && op !== 'pageload') {\n return;\n }\n\n updateSpanName(rootSpan, routeName);\n rootSpan.setAttributes({ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', [URL_TEMPLATE]: routeName });\n}\n\n/**\n * Check if React Router's instrumentation API is being used on the client.\n */\nexport function isClientInstrumentationApiUsed(): boolean {\n return !!GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG];\n}\n\n/**\n * Check if React Router's instrumentation API's navigate hook was invoked.\n */\nexport function isNavigateHookInvoked(): boolean {\n return !!GLOBAL_WITH_FLAGS[SENTRY_NAVIGATE_HOOK_INVOKED_FLAG];\n}\n"],"names":["GLOBAL_OBJ","DEBUG_BUILD","debug","getClient","updateNavigationSpanUrlFromLocation","startBrowserTracingNavigationSpan","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","getAbsoluteUrl","result","captureInstrumentationError","URL_FULL","filterCollectedUrl","client","navigationSpan","finalizeNavigationSpanFromHydratedRouter","SPAN_STATUS_ERROR","resolveNavigateArg","resolveNavigateAbsoluteUrl","startSpan","SENTRY_OP","GENERAL_FUNCTION_SPAN_OP","CODE_FUNCTION_NAME","getPathFromRequest","normalizeRoutePath","getPattern","WEB_SERVER_MIDDLEWARE_SPAN_OP","getActiveSpan","getRootSpan","attributes","spanToJSON","updateSpanName","URL_TEMPLATE"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createClientInstrumentation.js","sources":["../../../src/client/createClientInstrumentation.ts"],"sourcesContent":["// oxlint-disable max-lines\nimport { getAbsoluteUrl, startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getClient,\n getRootSpan,\n GLOBAL_OBJ,\n hasSpanStreamingEnabled,\n NAVIGATION_SPAN_NAME_FALLBACK,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n SPAN_STATUS_ERROR,\n updateSpanName,\n filterCollectedUrl,\n} from '@sentry/core';\nimport { startSpan } from '@sentry/core/browser';\nimport type { ClientInstrumentation } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { captureInstrumentationError, getPathFromRequest, getPattern, normalizeRoutePath } from '../common/utils';\nimport {\n resolveNavigateAbsoluteUrl,\n resolveNavigateArg,\n finalizeNavigationSpanFromHydratedRouter,\n updateNavigationSpanUrlFromLocation,\n} from './utils';\nimport {\n SENTRY_SEGMENT_NAME_SOURCE,\n CODE_FUNCTION_NAME,\n SENTRY_OP,\n URL_FULL,\n URL_TEMPLATE,\n} from '@sentry/conventions/attributes';\nimport { FUNCTION, MIDDLEWARE } from '@sentry/conventions/op';\n\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;\n\n// Tracks active numeric navigation span to prevent duplicate spans when popstate fires\nlet currentNumericNavigationSpan: Span | undefined;\n\n// Per-request middleware counters, keyed by Request\nconst middlewareCountersMap = new WeakMap<object, Record<string, number>>();\n\nconst SENTRY_CLIENT_INSTRUMENTATION_FLAG = '__sentryReactRouterClientInstrumentationUsed';\n// Intentionally never reset - once set, instrumentation API handles all navigations for the session.\nconst SENTRY_NAVIGATE_HOOK_INVOKED_FLAG = '__sentryReactRouterNavigateHookInvoked';\nconst SENTRY_POPSTATE_LISTENER_ADDED_FLAG = '__sentryReactRouterPopstateListenerAdded';\n\ntype GlobalObjWithFlags = typeof GLOBAL_OBJ & {\n [SENTRY_CLIENT_INSTRUMENTATION_FLAG]?: boolean;\n [SENTRY_NAVIGATE_HOOK_INVOKED_FLAG]?: boolean;\n [SENTRY_POPSTATE_LISTENER_ADDED_FLAG]?: boolean;\n};\n\nconst GLOBAL_WITH_FLAGS = GLOBAL_OBJ as GlobalObjWithFlags;\n\n/**\n * Options for creating Sentry client instrumentation.\n */\nexport interface CreateSentryClientInstrumentationOptions {\n /**\n * Whether to capture errors from loaders/actions automatically.\n * Set to `false` to avoid duplicates if using custom error handlers.\n * @default true\n */\n captureErrors?: boolean;\n}\n\n/**\n * Creates a Sentry client instrumentation for React Router's instrumentation API.\n */\nexport function createSentryClientInstrumentation(\n options: CreateSentryClientInstrumentationOptions = {},\n): ClientInstrumentation {\n const { captureErrors = true } = options;\n\n DEBUG_BUILD && debug.log('React Router client instrumentation API created.');\n\n return {\n router(router) {\n // Set the flag when React Router actually invokes our instrumentation.\n // This ensures the flag is only set in Library Mode (where hooks run),\n // not in Framework Mode (where hooks are never called).\n // See: https://github.com/remix-run/react-router/discussions/13749\n GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG] = true;\n DEBUG_BUILD && debug.log('React Router client instrumentation API router hook registered.');\n\n // Add popstate listener for browser back/forward navigation (persists for session, one listener only)\n if (!GLOBAL_WITH_FLAGS[SENTRY_POPSTATE_LISTENER_ADDED_FLAG] && WINDOW.addEventListener) {\n GLOBAL_WITH_FLAGS[SENTRY_POPSTATE_LISTENER_ADDED_FLAG] = true;\n\n WINDOW.addEventListener('popstate', () => {\n const client = getClient();\n if (!client) {\n currentNumericNavigationSpan = undefined;\n return;\n }\n\n const pathname = WINDOW.location?.pathname || '/';\n\n // If there's an active numeric navigation span, update it instead of creating a duplicate\n if (currentNumericNavigationSpan) {\n if (currentNumericNavigationSpan.isRecording()) {\n updateNavigationSpanUrlFromLocation(currentNumericNavigationSpan);\n }\n currentNumericNavigationSpan = undefined;\n return;\n }\n\n // Only create a new span for actual browser back/forward button clicks\n startBrowserTracingNavigationSpan(\n client,\n {\n // With span streaming, span names have to be low cardinality, so we can't fall back to\n // the URL. The route hooks parameterize the span once they resolve.\n name: hasSpanStreamingEnabled(client) ? NAVIGATION_SPAN_NAME_FALLBACK : pathname,\n attributes: {\n [SENTRY_SEGMENT_NAME_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': 'browser.popstate',\n },\n },\n { url: getAbsoluteUrl(pathname) },\n );\n });\n\n DEBUG_BUILD && debug.log('React Router popstate listener registered for browser back/forward navigation.');\n }\n\n router.instrument({\n async navigate(callNavigate, info) {\n // navigate(0) triggers a page reload - skip span creation, but still capture errors\n // (navigation can be rejected before reload, e.g., by a navigation guard)\n if (info.to === 0) {\n const result = await callNavigate();\n if (result.status === 'error' && result.error instanceof Error) {\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n [URL_FULL]: filterCollectedUrl(info.currentUrl),\n });\n }\n return;\n }\n\n GLOBAL_WITH_FLAGS[SENTRY_NAVIGATE_HOOK_INVOKED_FLAG] = true;\n\n // Handle numeric navigations (navigate(-1), navigate(1), etc.)\n if (typeof info.to === 'number') {\n const client = getClient();\n let navigationSpan;\n\n if (client) {\n const navigationType = info.to < 0 ? 'router.back' : 'router.forward';\n const currentPathname = WINDOW.location?.pathname || info.currentUrl;\n\n navigationSpan = startBrowserTracingNavigationSpan(\n client,\n {\n // With span streaming, span names have to be low cardinality, so we can't fall back\n // to the URL. The route is resolved once the navigation settles.\n name: hasSpanStreamingEnabled(client) ? NAVIGATION_SPAN_NAME_FALLBACK : currentPathname,\n attributes: {\n [SENTRY_SEGMENT_NAME_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': navigationType,\n },\n },\n { url: getAbsoluteUrl(currentPathname) },\n );\n\n // Store ref so popstate listener can update it instead of creating a duplicate\n currentNumericNavigationSpan = navigationSpan;\n }\n\n try {\n const result = await callNavigate();\n\n if (navigationSpan && WINDOW.location) {\n finalizeNavigationSpanFromHydratedRouter(navigationSpan);\n }\n\n if (result.status === 'error' && result.error instanceof Error) {\n if (navigationSpan) {\n navigationSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n [URL_FULL]: WINDOW.location?.pathname || filterCollectedUrl(info.currentUrl),\n });\n }\n } finally {\n currentNumericNavigationSpan = undefined;\n }\n return;\n }\n\n // Handle string/object navigations (e.g., navigate('/about') or navigate({ pathname: '/about' }))\n const client = getClient();\n const toPath = resolveNavigateArg(info.to);\n let navigationSpan;\n\n if (client) {\n navigationSpan = startBrowserTracingNavigationSpan(\n client,\n {\n // With span streaming, span names have to be low cardinality, so we can't fall back to\n // the URL. The route hooks parameterize the span once they resolve.\n name: hasSpanStreamingEnabled(client) ? NAVIGATION_SPAN_NAME_FALLBACK : toPath,\n attributes: {\n [SENTRY_SEGMENT_NAME_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': 'router.navigate',\n },\n },\n { url: resolveNavigateAbsoluteUrl(info.to, info.currentUrl) },\n );\n }\n\n const result = await callNavigate();\n if (result.status === 'error' && result.error instanceof Error) {\n if (navigationSpan) {\n navigationSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n [URL_FULL]: filterCollectedUrl(toPath),\n });\n }\n return;\n },\n\n async fetch(callFetch, info) {\n await startSpan(\n {\n name: `Fetcher ${info.fetcherKey}`,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n [CODE_FUNCTION_NAME]: 'fetcher',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callFetch();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.fetcher', {\n [URL_FULL]: filterCollectedUrl(info.href),\n });\n }\n },\n );\n },\n });\n },\n\n route(route) {\n const routeId = route.id;\n\n route.instrument({\n async loader(callLoader, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = normalizeRoutePath(getPattern(info));\n const routePattern = pattern || urlPath;\n // Parameterize the active navigation root span. (Route hooks don't fire on initial\n // pageload, so this only affects navigations.)\n updateRootSpanRoute(routePattern, !!pattern);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n [CODE_FUNCTION_NAME]: 'clientLoader',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLoader();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.client_loader', {\n [URL_FULL]: urlPath,\n });\n }\n },\n );\n },\n\n async action(callAction, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = normalizeRoutePath(getPattern(info));\n const routePattern = pattern || urlPath;\n updateRootSpanRoute(routePattern, !!pattern);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SENTRY_OP]: FUNCTION,\n [CODE_FUNCTION_NAME]: 'clientAction',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callAction();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.client_action', {\n [URL_FULL]: urlPath,\n });\n }\n },\n );\n },\n\n async middleware(callMiddleware, info) {\n const urlPath = getPathFromRequest(info.request);\n const routePattern = normalizeRoutePath(getPattern(info)) || urlPath;\n\n let counters = middlewareCountersMap.get(info.request);\n if (!counters) {\n counters = {};\n middlewareCountersMap.set(info.request, counters);\n }\n\n const middlewareIndex = counters[routeId] ?? 0;\n counters[routeId] = middlewareIndex + 1;\n\n await startSpan(\n {\n name: `middleware ${routeId}`,\n attributes: {\n [SENTRY_OP]: MIDDLEWARE,\n [CODE_FUNCTION_NAME]: 'clientMiddleware',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n 'react_router.route.id': routeId,\n 'http.route': routePattern,\n 'react_router.middleware.index': middlewareIndex,\n },\n },\n async span => {\n const result = await callMiddleware();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.client_middleware', {\n [URL_FULL]: urlPath,\n });\n }\n },\n );\n },\n\n async lazy(callLazy) {\n await startSpan(\n {\n name: 'Lazy Route Load',\n attributes: {\n [SENTRY_OP]: FUNCTION,\n [CODE_FUNCTION_NAME]: 'lazy',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLazy();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.client_lazy', {});\n }\n },\n );\n },\n });\n },\n };\n}\n\n/**\n * Updates the active navigation/pageload root span name with the parameterized route, so the\n * transaction reflects the parameterized route pattern (e.g. `/users/:id`).\n */\nfunction updateRootSpanRoute(routeName: string, hasPattern: boolean): void {\n if (!hasPattern) {\n return;\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n if (!rootSpan) {\n return;\n }\n\n const { attributes } = spanToJSON(rootSpan);\n const op = attributes[SENTRY_OP];\n if (op !== 'navigation' && op !== 'pageload') {\n return;\n }\n\n updateSpanName(rootSpan, routeName);\n rootSpan.setAttributes({ [SENTRY_SEGMENT_NAME_SOURCE]: 'route', [URL_TEMPLATE]: routeName });\n}\n\n/**\n * Check if React Router's instrumentation API is being used on the client.\n */\nexport function isClientInstrumentationApiUsed(): boolean {\n return !!GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG];\n}\n\n/**\n * Check if React Router's instrumentation API's navigate hook was invoked.\n */\nexport function isNavigateHookInvoked(): boolean {\n return !!GLOBAL_WITH_FLAGS[SENTRY_NAVIGATE_HOOK_INVOKED_FLAG];\n}\n"],"names":["GLOBAL_OBJ","DEBUG_BUILD","debug","getClient","updateNavigationSpanUrlFromLocation","startBrowserTracingNavigationSpan","hasSpanStreamingEnabled","NAVIGATION_SPAN_NAME_FALLBACK","SENTRY_SEGMENT_NAME_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","getAbsoluteUrl","result","captureInstrumentationError","URL_FULL","filterCollectedUrl","client","navigationSpan","finalizeNavigationSpanFromHydratedRouter","SPAN_STATUS_ERROR","resolveNavigateArg","resolveNavigateAbsoluteUrl","startSpan","SENTRY_OP","FUNCTION","CODE_FUNCTION_NAME","getPathFromRequest","normalizeRoutePath","getPattern","MIDDLEWARE","getActiveSpan","getRootSpan","attributes","spanToJSON","updateSpanName","URL_TEMPLATE"],"mappings":"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"}
|
|
@@ -16,16 +16,11 @@ function instrumentHydratedRouter() {
|
|
|
16
16
|
if (router) {
|
|
17
17
|
const pageloadSpan = getActiveRootSpan();
|
|
18
18
|
if (pageloadSpan) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
pageloadSpan.
|
|
24
|
-
pageloadSpan.setAttributes({
|
|
25
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
|
|
26
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.pageload.react_router",
|
|
27
|
-
[attributes.URL_TEMPLATE]: parameterizePageloadRoute
|
|
28
|
-
});
|
|
19
|
+
const pageloadPath = core.spanToJSON(pageloadSpan).attributes[attributes.URL_PATH];
|
|
20
|
+
if (typeof pageloadPath === "string" && // this event is for the currently active pageload
|
|
21
|
+
utils.normalizePathname(router.state.location.pathname) === utils.normalizePathname(pageloadPath)) {
|
|
22
|
+
pageloadSpan.setAttribute(core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, "auto.pageload.react_router");
|
|
23
|
+
utils.updateSpanWithParameterizedRoute(pageloadSpan, router.state);
|
|
29
24
|
}
|
|
30
25
|
}
|
|
31
26
|
if (typeof router.navigate === "function") {
|
|
@@ -72,21 +67,16 @@ function instrumentHydratedRouter() {
|
|
|
72
67
|
}
|
|
73
68
|
const rootSpanJson = core.spanToJSON(rootSpan);
|
|
74
69
|
const rootSpanAttributes = rootSpanJson.attributes;
|
|
75
|
-
if (rootSpanAttributes[attributes.SENTRY_OP] === "navigation" && createClientInstrumentation.isClientInstrumentationApiUsed() && rootSpanAttributes[
|
|
70
|
+
if (rootSpanAttributes[attributes.SENTRY_OP] === "navigation" && createClientInstrumentation.isClientInstrumentationApiUsed() && rootSpanAttributes[attributes.SENTRY_SEGMENT_NAME_SOURCE] === "route") {
|
|
76
71
|
return;
|
|
77
72
|
}
|
|
78
73
|
const rootSpanName = rootSpanJson.name;
|
|
79
|
-
const parameterizedRoute = utils.getParameterizedRoute(newState);
|
|
80
74
|
const spanPathname = rootSpanAttributes[attributes.URL_PATH];
|
|
81
75
|
const destinationPathname = utils.normalizePathname(newState.location.pathname);
|
|
82
76
|
if (rootSpanName && newState.navigation.state === "idle" && // navigation has completed
|
|
83
77
|
// this event is for the currently active root span
|
|
84
78
|
(destinationPathname === utils.normalizePathname(rootSpanName) || spanPathname && destinationPathname === utils.normalizePathname(spanPathname))) {
|
|
85
|
-
|
|
86
|
-
rootSpan.setAttributes({
|
|
87
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
|
|
88
|
-
[attributes.URL_TEMPLATE]: parameterizedRoute
|
|
89
|
-
});
|
|
79
|
+
utils.updateSpanWithParameterizedRoute(rootSpan, newState);
|
|
90
80
|
}
|
|
91
81
|
});
|
|
92
82
|
return true;
|
|
@@ -114,9 +104,11 @@ function maybeCreateNavigationTransaction(name, url, source) {
|
|
|
114
104
|
return browser.startBrowserTracingNavigationSpan(
|
|
115
105
|
client,
|
|
116
106
|
{
|
|
117
|
-
|
|
107
|
+
// With span streaming, span names have to be low cardinality, so we can't fall back to the URL.
|
|
108
|
+
// The route is resolved once the router settles, which updates the span name then.
|
|
109
|
+
name: !core.hasSpanStreamingEnabled(client) ? name : core.NAVIGATION_SPAN_NAME_FALLBACK,
|
|
118
110
|
attributes: {
|
|
119
|
-
[
|
|
111
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: source,
|
|
120
112
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
|
|
121
113
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.react_router",
|
|
122
114
|
...{}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydratedRouter.js","sources":["../../../src/client/hydratedRouter.ts"],"sourcesContent":["import { getAbsoluteUrl, startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getClient,\n getRootSpan,\n GLOBAL_OBJ,\n isThenable,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n} from '@sentry/core';\nimport type { DataRouter } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { isClientInstrumentationApiUsed } from './createClientInstrumentation';\nimport {\n finalizeNavigationSpanFromRouterState,\n getParameterizedRoute,\n normalizePathname,\n resolveNavigateAbsoluteUrl,\n resolveNavigateArg,\n} from './utils';\nimport { SENTRY_OP, URL_PATH, URL_TEMPLATE } from '@sentry/conventions/attributes';\n\nconst GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __reactRouterDataRouter?: DataRouter;\n};\n\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;\n\nconst MAX_RETRIES = 40; // 2 seconds at 50ms interval\n\n/**\n * Instruments the React Router Data Router for pageloads and navigation.\n *\n * This function waits for the router to be available after hydration, then:\n * 1. Updates the pageload transaction with parameterized route info\n * 2. Patches router.navigate() to create navigation transactions\n * 3. Subscribes to router state changes to update navigation transactions with parameterized routes\n */\nexport function instrumentHydratedRouter(): void {\n function trySubscribe(): boolean {\n const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;\n\n if (router) {\n // The first time we hit the router, we try to update the pageload transaction\n const pageloadSpan = getActiveRootSpan();\n\n if (pageloadSpan) {\n const pageloadName = spanToJSON(pageloadSpan).name;\n const parameterizePageloadRoute = getParameterizedRoute(router.state);\n if (\n pageloadName &&\n // this event is for the currently active pageload\n normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName)\n ) {\n pageloadSpan.updateName(parameterizePageloadRoute);\n pageloadSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react_router',\n [URL_TEMPLATE]: parameterizePageloadRoute,\n });\n }\n }\n\n // Patching navigate for creating accurate navigation transactions\n if (typeof router.navigate === 'function') {\n const originalNav = router.navigate.bind(router);\n router.navigate = function sentryPatchedNavigate(...args) {\n // Skip if instrumentation API is enabled (it handles navigation spans itself)\n if (!isClientInstrumentationApiUsed()) {\n const target = args[0];\n\n if (typeof target === 'number') {\n // navigate(0) triggers a reload, not a route change — skip span creation\n if (target !== 0) {\n const currentPathname = WINDOW.location?.pathname || '/';\n const navigationSpan = maybeCreateNavigationTransaction(\n currentPathname,\n getAbsoluteUrl(currentPathname),\n 'url',\n );\n\n const result = originalNav(...args);\n\n // Numeric navigations (`navigate(-1)`/`navigate(1)`) don't carry a destination\n // path, so we can only resolve the real URL/route once the router has settled.\n if (navigationSpan) {\n // Finalize from the (updated) router state after navigation completes, in both\n // the resolve and reject paths, so a rejected navigation still ends up with the\n // correct URL attributes instead of the placeholder start pathname.\n if (isThenable(result)) {\n result.then(\n () => finalizeNavigationSpanFromRouterState(navigationSpan, router.state),\n () => finalizeNavigationSpanFromRouterState(navigationSpan, router.state),\n );\n } else {\n finalizeNavigationSpanFromRouterState(navigationSpan, router.state);\n }\n }\n\n return result;\n }\n } else {\n maybeCreateNavigationTransaction(\n resolveNavigateArg(target) || '<unknown route>',\n resolveNavigateAbsoluteUrl(target),\n 'url',\n );\n }\n }\n return originalNav(...args);\n };\n }\n\n // Subscribe to router state changes to update navigation transactions (and any pageload\n // whose route info only became available after `trySubscribe`, e.g. lazy routes) with the\n // parameterized route.\n router.subscribe(newState => {\n const rootSpan = getActiveRootSpan();\n\n if (!rootSpan) {\n return;\n }\n\n const rootSpanJson = spanToJSON(rootSpan);\n const rootSpanAttributes = rootSpanJson.attributes;\n\n // When the instrumentation API is active, navigation roots are parameterized\n // by the native route hooks\n if (\n rootSpanAttributes[SENTRY_OP] === 'navigation' &&\n isClientInstrumentationApiUsed() &&\n rootSpanAttributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] === 'route'\n ) {\n return;\n }\n\n const rootSpanName = rootSpanJson.name;\n const parameterizedRoute = getParameterizedRoute(newState);\n const spanPathname = rootSpanAttributes[URL_PATH] as string | undefined;\n const destinationPathname = normalizePathname(newState.location.pathname);\n\n if (\n rootSpanName &&\n newState.navigation.state === 'idle' && // navigation has completed\n // this event is for the currently active root span\n (destinationPathname === normalizePathname(rootSpanName) ||\n (spanPathname && destinationPathname === normalizePathname(spanPathname)))\n ) {\n rootSpan.updateName(parameterizedRoute);\n rootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [URL_TEMPLATE]: parameterizedRoute,\n });\n }\n });\n return true;\n }\n return false;\n }\n\n // Wait until the router is available (since the SDK loads before hydration)\n if (!trySubscribe()) {\n let retryCount = 0;\n // Retry until the router is available or max retries reached\n const interval = setInterval(() => {\n if (trySubscribe() || retryCount >= MAX_RETRIES) {\n if (retryCount >= MAX_RETRIES) {\n DEBUG_BUILD && debug.warn('Unable to instrument React Router: router not found after hydration.');\n }\n clearInterval(interval);\n }\n retryCount++;\n }, 50);\n }\n}\n\nfunction maybeCreateNavigationTransaction(name: string, url: string, source: 'url' | 'route'): Span | undefined {\n const client = getClient();\n\n if (!client) {\n return undefined;\n }\n\n return startBrowserTracingNavigationSpan(\n client,\n {\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router',\n ...(source === 'route' ? { [URL_TEMPLATE]: name } : {}),\n },\n },\n { url },\n );\n}\n\nfunction getActiveRootSpan(): Span | undefined {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n\n const rootSpan = getRootSpan(activeSpan);\n\n const op = spanToJSON(rootSpan).attributes[SENTRY_OP];\n\n // Only use this root span if it is a pageload or navigation span\n return op === 'navigation' || op === 'pageload' ? rootSpan : undefined;\n}\n"],"names":["GLOBAL_OBJ","spanToJSON","getParameterizedRoute","normalizePathname","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","URL_TEMPLATE","isClientInstrumentationApiUsed","getAbsoluteUrl","isThenable","finalizeNavigationSpanFromRouterState","resolveNavigateArg","resolveNavigateAbsoluteUrl","SENTRY_OP","URL_PATH","DEBUG_BUILD","debug","getClient","startBrowserTracingNavigationSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","getActiveSpan","getRootSpan"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hydratedRouter.js","sources":["../../../src/client/hydratedRouter.ts"],"sourcesContent":["import { getAbsoluteUrl, startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getClient,\n getRootSpan,\n GLOBAL_OBJ,\n hasSpanStreamingEnabled,\n isThenable,\n NAVIGATION_SPAN_NAME_FALLBACK,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n} from '@sentry/core';\nimport type { DataRouter } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { isClientInstrumentationApiUsed } from './createClientInstrumentation';\nimport {\n finalizeNavigationSpanFromRouterState,\n normalizePathname,\n resolveNavigateAbsoluteUrl,\n resolveNavigateArg,\n updateSpanWithParameterizedRoute,\n} from './utils';\nimport { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, URL_PATH, URL_TEMPLATE } from '@sentry/conventions/attributes';\n\nconst GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __reactRouterDataRouter?: DataRouter;\n};\n\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;\n\nconst MAX_RETRIES = 40; // 2 seconds at 50ms interval\n\n/**\n * Instruments the React Router Data Router for pageloads and navigation.\n *\n * This function waits for the router to be available after hydration, then:\n * 1. Updates the pageload transaction with parameterized route info\n * 2. Patches router.navigate() to create navigation transactions\n * 3. Subscribes to router state changes to update navigation transactions with parameterized routes\n */\nexport function instrumentHydratedRouter(): void {\n function trySubscribe(): boolean {\n const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;\n\n if (router) {\n // The first time we hit the router, we try to update the pageload transaction\n const pageloadSpan = getActiveRootSpan();\n\n if (pageloadSpan) {\n // Matched against `url.path` rather than the span name: with span streaming, the pageload\n // span is named `Pageload` until a route is resolved, so the name may not hold the pathname.\n const pageloadPath = spanToJSON(pageloadSpan).attributes[URL_PATH];\n if (\n typeof pageloadPath === 'string' &&\n // this event is for the currently active pageload\n normalizePathname(router.state.location.pathname) === normalizePathname(pageloadPath)\n ) {\n pageloadSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.pageload.react_router');\n updateSpanWithParameterizedRoute(pageloadSpan, router.state);\n }\n }\n\n // Patching navigate for creating accurate navigation transactions\n if (typeof router.navigate === 'function') {\n const originalNav = router.navigate.bind(router);\n router.navigate = function sentryPatchedNavigate(...args) {\n // Skip if instrumentation API is enabled (it handles navigation spans itself)\n if (!isClientInstrumentationApiUsed()) {\n const target = args[0];\n\n if (typeof target === 'number') {\n // navigate(0) triggers a reload, not a route change — skip span creation\n if (target !== 0) {\n const currentPathname = WINDOW.location?.pathname || '/';\n const navigationSpan = maybeCreateNavigationTransaction(\n currentPathname,\n getAbsoluteUrl(currentPathname),\n 'url',\n );\n\n const result = originalNav(...args);\n\n // Numeric navigations (`navigate(-1)`/`navigate(1)`) don't carry a destination\n // path, so we can only resolve the real URL/route once the router has settled.\n if (navigationSpan) {\n // Finalize from the (updated) router state after navigation completes, in both\n // the resolve and reject paths, so a rejected navigation still ends up with the\n // correct URL attributes instead of the placeholder start pathname.\n if (isThenable(result)) {\n result.then(\n () => finalizeNavigationSpanFromRouterState(navigationSpan, router.state),\n () => finalizeNavigationSpanFromRouterState(navigationSpan, router.state),\n );\n } else {\n finalizeNavigationSpanFromRouterState(navigationSpan, router.state);\n }\n }\n\n return result;\n }\n } else {\n maybeCreateNavigationTransaction(\n resolveNavigateArg(target) || '<unknown route>',\n resolveNavigateAbsoluteUrl(target),\n 'url',\n );\n }\n }\n return originalNav(...args);\n };\n }\n\n // Subscribe to router state changes to update navigation transactions (and any pageload\n // whose route info only became available after `trySubscribe`, e.g. lazy routes) with the\n // parameterized route.\n router.subscribe(newState => {\n const rootSpan = getActiveRootSpan();\n\n if (!rootSpan) {\n return;\n }\n\n const rootSpanJson = spanToJSON(rootSpan);\n const rootSpanAttributes = rootSpanJson.attributes;\n\n // When the instrumentation API is active, navigation roots are parameterized\n // by the native route hooks\n if (\n rootSpanAttributes[SENTRY_OP] === 'navigation' &&\n isClientInstrumentationApiUsed() &&\n rootSpanAttributes[SENTRY_SEGMENT_NAME_SOURCE] === 'route'\n ) {\n return;\n }\n\n const rootSpanName = rootSpanJson.name;\n const spanPathname = rootSpanAttributes[URL_PATH] as string | undefined;\n const destinationPathname = normalizePathname(newState.location.pathname);\n\n if (\n rootSpanName &&\n newState.navigation.state === 'idle' && // navigation has completed\n // this event is for the currently active root span\n (destinationPathname === normalizePathname(rootSpanName) ||\n (spanPathname && destinationPathname === normalizePathname(spanPathname)))\n ) {\n updateSpanWithParameterizedRoute(rootSpan, newState);\n }\n });\n return true;\n }\n return false;\n }\n\n // Wait until the router is available (since the SDK loads before hydration)\n if (!trySubscribe()) {\n let retryCount = 0;\n // Retry until the router is available or max retries reached\n const interval = setInterval(() => {\n if (trySubscribe() || retryCount >= MAX_RETRIES) {\n if (retryCount >= MAX_RETRIES) {\n DEBUG_BUILD && debug.warn('Unable to instrument React Router: router not found after hydration.');\n }\n clearInterval(interval);\n }\n retryCount++;\n }, 50);\n }\n}\n\nfunction maybeCreateNavigationTransaction(name: string, url: string, source: 'url' | 'route'): Span | undefined {\n const client = getClient();\n\n if (!client) {\n return undefined;\n }\n\n return startBrowserTracingNavigationSpan(\n client,\n {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n // The route is resolved once the router settles, which updates the span name then.\n name: source === 'route' || !hasSpanStreamingEnabled(client) ? name : NAVIGATION_SPAN_NAME_FALLBACK,\n attributes: {\n [SENTRY_SEGMENT_NAME_SOURCE]: source,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router',\n ...(source === 'route' ? { [URL_TEMPLATE]: name } : {}),\n },\n },\n { url },\n );\n}\n\nfunction getActiveRootSpan(): Span | undefined {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n\n const rootSpan = getRootSpan(activeSpan);\n\n const op = spanToJSON(rootSpan).attributes[SENTRY_OP];\n\n // Only use this root span if it is a pageload or navigation span\n return op === 'navigation' || op === 'pageload' ? rootSpan : undefined;\n}\n"],"names":["GLOBAL_OBJ","spanToJSON","URL_PATH","normalizePathname","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","updateSpanWithParameterizedRoute","isClientInstrumentationApiUsed","getAbsoluteUrl","isThenable","finalizeNavigationSpanFromRouterState","resolveNavigateArg","resolveNavigateAbsoluteUrl","SENTRY_OP","SENTRY_SEGMENT_NAME_SOURCE","DEBUG_BUILD","debug","getClient","startBrowserTracingNavigationSpan","hasSpanStreamingEnabled","NAVIGATION_SPAN_NAME_FALLBACK","SEMANTIC_ATTRIBUTE_SENTRY_OP","getActiveSpan","getRootSpan"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracingIntegration.js","sources":["../../../src/client/tracingIntegration.ts"],"sourcesContent":["import { browserTracingIntegration as originalBrowserTracingIntegration } from '@sentry/browser';\nimport type { Integration } from '@sentry/core';\nimport type { ClientInstrumentation } from '
|
|
1
|
+
{"version":3,"file":"tracingIntegration.js","sources":["../../../src/client/tracingIntegration.ts"],"sourcesContent":["import { browserTracingIntegration as originalBrowserTracingIntegration } from '@sentry/browser';\nimport type { Integration } from '@sentry/core';\nimport type { ClientInstrumentation } from 'react-router';\nimport {\n createSentryClientInstrumentation,\n type CreateSentryClientInstrumentationOptions,\n} from './createClientInstrumentation';\nimport { instrumentHydratedRouter } from './hydratedRouter';\n\n/**\n * Options for the React Router tracing integration.\n */\nexport interface ReactRouterTracingIntegrationOptions {\n /**\n * Options for React Router's instrumentation API.\n */\n instrumentationOptions?: CreateSentryClientInstrumentationOptions;\n\n /** @deprecated `clientInstrumentation` is always built, so this flag is a no-op. Will be removed in a future major. */\n useInstrumentationAPI?: boolean;\n}\n\n/**\n * React Router tracing integration with support for the instrumentation API.\n */\nexport interface ReactRouterTracingIntegration extends Integration {\n /**\n * Client instrumentation to pass to `HydratedRouter`'s `instrumentations` prop.\n *\n * @deprecated Use the standalone `createSentryClientInstrumentation()` export instead and pass its\n * result to `HydratedRouter`'s `instrumentations` prop. This mirrors the server-side\n * `createSentryServerInstrumentation()` API. Will be removed in a future major.\n */\n readonly clientInstrumentation: ClientInstrumentation;\n}\n\n/**\n * Browser tracing integration for React Router (Framework) applications.\n * This integration will create navigation spans and enhance transaction names with parameterized routes.\n */\nexport function reactRouterTracingIntegration(\n options: ReactRouterTracingIntegrationOptions = {},\n): ReactRouterTracingIntegration {\n const browserTracingIntegrationInstance = originalBrowserTracingIntegration({\n // Navigation transactions are started within the hydrated router instrumentation\n instrumentNavigation: false,\n });\n\n // Built eagerly so it can be passed to HydratedRouter's `instrumentations` prop. This has no\n // side effects - the API is only marked \"used\" once React Router invokes the `router()` hook.\n const clientInstrumentationInstance = createSentryClientInstrumentation(options.instrumentationOptions);\n\n return {\n ...browserTracingIntegrationInstance,\n name: 'ReactRouterTracingIntegration',\n afterAllSetup(client) {\n browserTracingIntegrationInstance.afterAllSetup(client);\n instrumentHydratedRouter();\n },\n // oxlint-disable-next-line typescript/no-deprecated -- exposes the deprecated getter for back-compat until it is removed\n get clientInstrumentation(): ClientInstrumentation {\n return clientInstrumentationInstance;\n },\n };\n}\n"],"names":["originalBrowserTracingIntegration","createSentryClientInstrumentation","instrumentHydratedRouter"],"mappings":";;;;;;AAwCO,SAAS,6BAAA,CACd,OAAA,GAAgD,EAAC,EAClB;AAC/B,EAAA,MAAM,oCAAoCA,iCAAA,CAAkC;AAAA;AAAA,IAE1E,oBAAA,EAAsB;AAAA,GACvB,CAAA;AAID,EAAA,MAAM,6BAAA,GAAgCC,6DAAA,CAAkC,OAAA,CAAQ,sBAAsB,CAAA;AAEtG,EAAA,OAAO;AAAA,IACL,GAAG,iCAAA;AAAA,IACH,IAAA,EAAM,+BAAA;AAAA,IACN,cAAc,MAAA,EAAQ;AACpB,MAAA,iCAAA,CAAkC,cAAc,MAAM,CAAA;AACtD,MAAAC,uCAAA,EAAyB;AAAA,IAC3B,CAAA;AAAA;AAAA,IAEA,IAAI,qBAAA,GAA+C;AACjD,MAAA,OAAO,6BAAA;AAAA,IACT;AAAA,GACF;AACF;;;;"}
|
|
@@ -53,9 +53,10 @@ function updateNavigationSpanUrlFromLocation(span) {
|
|
|
53
53
|
}
|
|
54
54
|
const { pathname, search = "", hash = "" } = WINDOW.location;
|
|
55
55
|
const destinationUrl = browser.getAbsoluteUrl(`${pathname}${search}${hash}`);
|
|
56
|
-
|
|
56
|
+
const client = core.getClient();
|
|
57
|
+
span.updateName(client && core.hasSpanStreamingEnabled(client) ? core.NAVIGATION_SPAN_NAME_FALLBACK : pathname);
|
|
57
58
|
span.setAttributes({
|
|
58
|
-
[
|
|
59
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: "url",
|
|
59
60
|
[attributes.URL_PATH]: pathname,
|
|
60
61
|
[attributes.URL_FULL]: core.filterCollectedUrl(destinationUrl)
|
|
61
62
|
});
|
|
@@ -67,9 +68,33 @@ function normalizePathname(pathname) {
|
|
|
67
68
|
}
|
|
68
69
|
return normalized;
|
|
69
70
|
}
|
|
70
|
-
function
|
|
71
|
-
const
|
|
72
|
-
|
|
71
|
+
function getRouteTemplate(routerState) {
|
|
72
|
+
const { matches } = routerState;
|
|
73
|
+
if (!matches.length) {
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
let template = "";
|
|
77
|
+
for (const match of matches) {
|
|
78
|
+
const routePath = match.route.path;
|
|
79
|
+
if (!routePath) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
template = routePath.startsWith("/") ? routePath : `${template.replace(/\/$/, "")}/${routePath}`;
|
|
83
|
+
}
|
|
84
|
+
return normalizePathname(template);
|
|
85
|
+
}
|
|
86
|
+
function updateSpanWithParameterizedRoute(span, routerState) {
|
|
87
|
+
const routeTemplate = getRouteTemplate(routerState);
|
|
88
|
+
const client = core.getClient();
|
|
89
|
+
if (!routeTemplate && client && core.hasSpanStreamingEnabled(client)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const parameterizedRoute = routeTemplate || normalizePathname(routerState.location.pathname);
|
|
93
|
+
span.updateName(parameterizedRoute);
|
|
94
|
+
span.setAttributes({
|
|
95
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: "route",
|
|
96
|
+
[attributes.URL_TEMPLATE]: parameterizedRoute
|
|
97
|
+
});
|
|
73
98
|
}
|
|
74
99
|
function finalizeNavigationSpanFromRouterState(span, routerState) {
|
|
75
100
|
updateNavigationSpanUrlFromLocation(span);
|
|
@@ -78,12 +103,7 @@ function finalizeNavigationSpanFromRouterState(span, routerState) {
|
|
|
78
103
|
}
|
|
79
104
|
const { pathname } = WINDOW.location;
|
|
80
105
|
if (routerState.navigation?.state === "idle" && normalizePathname(routerState.location.pathname) === normalizePathname(pathname)) {
|
|
81
|
-
|
|
82
|
-
span.updateName(parameterizedRoute);
|
|
83
|
-
span.setAttributes({
|
|
84
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
|
|
85
|
-
[attributes.URL_TEMPLATE]: parameterizedRoute
|
|
86
|
-
});
|
|
106
|
+
updateSpanWithParameterizedRoute(span, routerState);
|
|
87
107
|
}
|
|
88
108
|
}
|
|
89
109
|
function finalizeNavigationSpanFromHydratedRouter(span) {
|
|
@@ -97,10 +117,10 @@ function finalizeNavigationSpanFromHydratedRouter(span) {
|
|
|
97
117
|
|
|
98
118
|
exports.finalizeNavigationSpanFromHydratedRouter = finalizeNavigationSpanFromHydratedRouter;
|
|
99
119
|
exports.finalizeNavigationSpanFromRouterState = finalizeNavigationSpanFromRouterState;
|
|
100
|
-
exports.getParameterizedRoute = getParameterizedRoute;
|
|
101
120
|
exports.normalizePathname = normalizePathname;
|
|
102
121
|
exports.resolveNavigateAbsoluteUrl = resolveNavigateAbsoluteUrl;
|
|
103
122
|
exports.resolveNavigateArg = resolveNavigateArg;
|
|
104
123
|
exports.resolveNavigateUrl = resolveNavigateUrl;
|
|
105
124
|
exports.updateNavigationSpanUrlFromLocation = updateNavigationSpanUrlFromLocation;
|
|
125
|
+
exports.updateSpanWithParameterizedRoute = updateSpanWithParameterizedRoute;
|
|
106
126
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../src/client/utils.ts"],"sourcesContent":["import { getAbsoluteUrl } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport { GLOBAL_OBJ, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, filterCollectedUrl } from '@sentry/core';\nimport { URL_FULL, URL_PATH, URL_TEMPLATE } from '@sentry/conventions/attributes';\nimport type { DataRouter, RouterState } from 'react-router';\n\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;\n\nconst GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __reactRouterDataRouter?: DataRouter;\n};\n\n/**\n * Resolves a navigate argument to a pathname string.\n *\n * React Router's navigate() accepts a string, number, or a To object ({ pathname, search, hash }).\n * All fields in the To object are optional (Partial<Path>), so we need to detect object args\n * to avoid \"[object Object]\" transaction names.\n */\nexport function resolveNavigateArg(target: unknown): string {\n if (typeof target !== 'object' || target === null) {\n // string or number\n return String(target);\n }\n\n // Object `to` with pathname\n const pathname = (target as Record<string, unknown>).pathname;\n if (typeof pathname === 'string') {\n return pathname || '/';\n }\n\n // Object `to` without pathname - navigation stays on current path\n return WINDOW.location?.pathname || '/';\n}\n\n/**\n * Resolves a navigate argument to the full destination path, preserving `search`/`hash` from a\n * To object. Unlike `resolveNavigateArg` (used for span/route naming, which should stay a bare\n * path), this is used to derive `url.full`/`url.path`, which should reflect the actual\n * destination the user is navigating to, including any query string.\n */\nexport function resolveNavigateUrl(target: unknown): string {\n if (typeof target !== 'object' || target === null) {\n // string or number\n return String(target);\n }\n\n const { pathname, search, hash } = target as Record<string, unknown>;\n const path = typeof pathname === 'string' && pathname !== '' ? pathname : WINDOW.location?.pathname || '/';\n\n return `${path}${typeof search === 'string' ? search : ''}${typeof hash === 'string' ? hash : ''}`;\n}\n\nfunction getNavigateBaseUrl(currentUrl?: string): string {\n const origin = WINDOW.location?.origin || 'http://localhost';\n\n // Prefer the live pathname, but fall back to the hook-provided `currentUrl` (which may be a full\n // URL or a bare path) when `location` is unavailable (e.g. SSR/non-browser environments).\n let pathname = WINDOW.location?.pathname;\n if (!pathname && currentUrl) {\n try {\n pathname = new URL(currentUrl, origin).pathname;\n } catch {\n pathname = currentUrl.startsWith('/') ? currentUrl : `/${currentUrl}`;\n }\n }\n pathname = pathname || '/';\n\n // React Router resolves relative navigate targets against the current path treated as a\n // *directory* (it appends segments), whereas the WHATWG `URL` parser treats the last path\n // segment as a file and drops it. Enforce a trailing slash so relative resolution matches\n // React Router, e.g. `navigate('ssr')` from `/performance` -> `/performance/ssr` (not `/ssr`).\n const directory = pathname.endsWith('/') ? pathname : `${pathname}/`;\n\n return `${origin}${directory}`;\n}\n\n/**\n * Resolves a navigate argument to an absolute URL for `url.full`/`url.path`, correctly handling\n * relative targets (no leading slash) by resolving them against the current URL instead of the\n * document origin alone.\n */\nexport function resolveNavigateAbsoluteUrl(target: unknown, currentUrl?: string): string {\n const destination = resolveNavigateUrl(target);\n\n try {\n const resolved = new URL(destination, getNavigateBaseUrl(currentUrl));\n return getAbsoluteUrl(`${resolved.pathname}${resolved.search}${resolved.hash}`);\n } catch {\n return getAbsoluteUrl(destination);\n }\n}\n\n/**\n * Updates a navigation span's name and `url.path`/`url.full` from the current `location`.\n */\nexport function updateNavigationSpanUrlFromLocation(span: Span): void {\n if (!WINDOW.location) {\n return;\n }\n\n const { pathname, search = '', hash = '' } = WINDOW.location;\n const destinationUrl = getAbsoluteUrl(`${pathname}${search}${hash}`);\n\n span.updateName(pathname);\n span.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [URL_PATH]: pathname,\n [URL_FULL]: filterCollectedUrl(destinationUrl),\n });\n}\n\nexport function normalizePathname(pathname: string): string {\n let normalized = pathname.startsWith('/') ? pathname : `/${pathname}`;\n if (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function getParameterizedRoute(routerState: RouterState): string {\n const lastMatch = routerState.matches[routerState.matches.length - 1];\n return normalizePathname(lastMatch?.route.path || routerState.location.pathname);\n}\n\n/**\n * Updates a navigation span's URL attributes and parameterizes its name from the router state.\n * Used after numeric navigations (`navigate(-1)` / `navigate(1)`) where route hooks may not\n * supply a pattern (e.g. index routes).\n *\n * `url.path` reflects raw `location.pathname` (may include a trailing slash when the server\n * redirects index routes), while `url.template` is normalized without trailing slashes.\n */\nexport function finalizeNavigationSpanFromRouterState(span: Span, routerState: RouterState): void {\n updateNavigationSpanUrlFromLocation(span);\n\n if (!WINDOW.location) {\n return;\n }\n\n const { pathname } = WINDOW.location;\n\n if (\n routerState.navigation?.state === 'idle' &&\n normalizePathname(routerState.location.pathname) === normalizePathname(pathname)\n ) {\n const parameterizedRoute = getParameterizedRoute(routerState);\n span.updateName(parameterizedRoute);\n span.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [URL_TEMPLATE]: parameterizedRoute,\n });\n }\n}\n\n/**\n * Finalizes a navigation span after numeric navigation using the hydrated data router when\n * available, otherwise falls back to URL attributes from `location` alone.\n */\nexport function finalizeNavigationSpanFromHydratedRouter(span: Span): void {\n const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;\n if (router) {\n finalizeNavigationSpanFromRouterState(span, router.state);\n } else {\n updateNavigationSpanUrlFromLocation(span);\n }\n}\n"],"names":["GLOBAL_OBJ","getAbsoluteUrl","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","URL_PATH","URL_FULL","filterCollectedUrl","URL_TEMPLATE"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../src/client/utils.ts"],"sourcesContent":["import { getAbsoluteUrl } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n getClient,\n GLOBAL_OBJ,\n hasSpanStreamingEnabled,\n NAVIGATION_SPAN_NAME_FALLBACK,\n filterCollectedUrl,\n} from '@sentry/core';\nimport { SENTRY_SEGMENT_NAME_SOURCE, URL_FULL, URL_PATH, URL_TEMPLATE } from '@sentry/conventions/attributes';\nimport type { DataRouter, RouterState } from 'react-router';\n\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;\n\nconst GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __reactRouterDataRouter?: DataRouter;\n};\n\n/**\n * Resolves a navigate argument to a pathname string.\n *\n * React Router's navigate() accepts a string, number, or a To object ({ pathname, search, hash }).\n * All fields in the To object are optional (Partial<Path>), so we need to detect object args\n * to avoid \"[object Object]\" transaction names.\n */\nexport function resolveNavigateArg(target: unknown): string {\n if (typeof target !== 'object' || target === null) {\n // string or number\n return String(target);\n }\n\n // Object `to` with pathname\n const pathname = (target as Record<string, unknown>).pathname;\n if (typeof pathname === 'string') {\n return pathname || '/';\n }\n\n // Object `to` without pathname - navigation stays on current path\n return WINDOW.location?.pathname || '/';\n}\n\n/**\n * Resolves a navigate argument to the full destination path, preserving `search`/`hash` from a\n * To object. Unlike `resolveNavigateArg` (used for span/route naming, which should stay a bare\n * path), this is used to derive `url.full`/`url.path`, which should reflect the actual\n * destination the user is navigating to, including any query string.\n */\nexport function resolveNavigateUrl(target: unknown): string {\n if (typeof target !== 'object' || target === null) {\n // string or number\n return String(target);\n }\n\n const { pathname, search, hash } = target as Record<string, unknown>;\n const path = typeof pathname === 'string' && pathname !== '' ? pathname : WINDOW.location?.pathname || '/';\n\n return `${path}${typeof search === 'string' ? search : ''}${typeof hash === 'string' ? hash : ''}`;\n}\n\nfunction getNavigateBaseUrl(currentUrl?: string): string {\n const origin = WINDOW.location?.origin || 'http://localhost';\n\n // Prefer the live pathname, but fall back to the hook-provided `currentUrl` (which may be a full\n // URL or a bare path) when `location` is unavailable (e.g. SSR/non-browser environments).\n let pathname = WINDOW.location?.pathname;\n if (!pathname && currentUrl) {\n try {\n pathname = new URL(currentUrl, origin).pathname;\n } catch {\n pathname = currentUrl.startsWith('/') ? currentUrl : `/${currentUrl}`;\n }\n }\n pathname = pathname || '/';\n\n // React Router resolves relative navigate targets against the current path treated as a\n // *directory* (it appends segments), whereas the WHATWG `URL` parser treats the last path\n // segment as a file and drops it. Enforce a trailing slash so relative resolution matches\n // React Router, e.g. `navigate('ssr')` from `/performance` -> `/performance/ssr` (not `/ssr`).\n const directory = pathname.endsWith('/') ? pathname : `${pathname}/`;\n\n return `${origin}${directory}`;\n}\n\n/**\n * Resolves a navigate argument to an absolute URL for `url.full`/`url.path`, correctly handling\n * relative targets (no leading slash) by resolving them against the current URL instead of the\n * document origin alone.\n */\nexport function resolveNavigateAbsoluteUrl(target: unknown, currentUrl?: string): string {\n const destination = resolveNavigateUrl(target);\n\n try {\n const resolved = new URL(destination, getNavigateBaseUrl(currentUrl));\n return getAbsoluteUrl(`${resolved.pathname}${resolved.search}${resolved.hash}`);\n } catch {\n return getAbsoluteUrl(destination);\n }\n}\n\n/**\n * Updates a navigation span's name and `url.path`/`url.full` from the current `location`.\n */\nexport function updateNavigationSpanUrlFromLocation(span: Span): void {\n if (!WINDOW.location) {\n return;\n }\n\n const { pathname, search = '', hash = '' } = WINDOW.location;\n const destinationUrl = getAbsoluteUrl(`${pathname}${search}${hash}`);\n\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n const client = getClient();\n span.updateName(client && hasSpanStreamingEnabled(client) ? NAVIGATION_SPAN_NAME_FALLBACK : pathname);\n span.setAttributes({\n [SENTRY_SEGMENT_NAME_SOURCE]: 'url',\n [URL_PATH]: pathname,\n [URL_FULL]: filterCollectedUrl(destinationUrl),\n });\n}\n\nexport function normalizePathname(pathname: string): string {\n let normalized = pathname.startsWith('/') ? pathname : `/${pathname}`;\n if (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * The route template for the current match, or `undefined` when nothing matched.\n *\n * Built from the whole matched chain rather than the leaf alone: nested routes carry paths\n * relative to their parent, and index and layout routes carry none at all, so the leaf on its own\n * is either a fragment of the route (`edit`) or missing entirely.\n */\nfunction getRouteTemplate(routerState: RouterState): string | undefined {\n const { matches } = routerState;\n\n if (!matches.length) {\n return undefined;\n }\n\n let template = '';\n for (const match of matches) {\n const routePath = match.route.path;\n if (!routePath) {\n continue;\n }\n\n template = routePath.startsWith('/') ? routePath : `${template.replace(/\\/$/, '')}/${routePath}`;\n }\n\n // A chain without any path only ever matches the root.\n return normalizePathname(template);\n}\n\n/**\n * Names a root span after the route the router matched.\n *\n * With span streaming there is nothing low cardinality to fall back to when nothing matched, so\n * the span keeps the name it started with rather than taking on the raw URL.\n */\nexport function updateSpanWithParameterizedRoute(span: Span, routerState: RouterState): void {\n const routeTemplate = getRouteTemplate(routerState);\n const client = getClient();\n\n if (!routeTemplate && client && hasSpanStreamingEnabled(client)) {\n return;\n }\n\n const parameterizedRoute = routeTemplate || normalizePathname(routerState.location.pathname);\n\n span.updateName(parameterizedRoute);\n span.setAttributes({\n [SENTRY_SEGMENT_NAME_SOURCE]: 'route',\n [URL_TEMPLATE]: parameterizedRoute,\n });\n}\n\n/**\n * Updates a navigation span's URL attributes and parameterizes its name from the router state.\n * Used after numeric navigations (`navigate(-1)` / `navigate(1)`) where route hooks may not\n * supply a pattern (e.g. index routes).\n *\n * `url.path` reflects raw `location.pathname` (may include a trailing slash when the server\n * redirects index routes), while `url.template` is normalized without trailing slashes.\n */\nexport function finalizeNavigationSpanFromRouterState(span: Span, routerState: RouterState): void {\n updateNavigationSpanUrlFromLocation(span);\n\n if (!WINDOW.location) {\n return;\n }\n\n const { pathname } = WINDOW.location;\n\n if (\n routerState.navigation?.state === 'idle' &&\n normalizePathname(routerState.location.pathname) === normalizePathname(pathname)\n ) {\n updateSpanWithParameterizedRoute(span, routerState);\n }\n}\n\n/**\n * Finalizes a navigation span after numeric navigation using the hydrated data router when\n * available, otherwise falls back to URL attributes from `location` alone.\n */\nexport function finalizeNavigationSpanFromHydratedRouter(span: Span): void {\n const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;\n if (router) {\n finalizeNavigationSpanFromRouterState(span, router.state);\n } else {\n updateNavigationSpanUrlFromLocation(span);\n }\n}\n"],"names":["GLOBAL_OBJ","getAbsoluteUrl","getClient","hasSpanStreamingEnabled","NAVIGATION_SPAN_NAME_FALLBACK","SENTRY_SEGMENT_NAME_SOURCE","URL_PATH","URL_FULL","filterCollectedUrl","URL_TEMPLATE"],"mappings":"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"}
|
|
@@ -12,6 +12,8 @@ const wrapSentryHandleRequest = require('../server/wrapSentryHandleRequest.js');
|
|
|
12
12
|
function injectTraceMetaTags(body) {
|
|
13
13
|
const headClosingTag = "</head>";
|
|
14
14
|
const reader = body.getReader();
|
|
15
|
+
const encoder = new TextEncoder();
|
|
16
|
+
const decoder = new TextDecoder();
|
|
15
17
|
const stream = new ReadableStream({
|
|
16
18
|
async pull(controller) {
|
|
17
19
|
const { done, value } = await reader.read();
|
|
@@ -19,8 +21,7 @@ function injectTraceMetaTags(body) {
|
|
|
19
21
|
controller.close();
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
22
|
-
const
|
|
23
|
-
const html = value instanceof Uint8Array ? new TextDecoder().decode(value) : String(value);
|
|
24
|
+
const html = value instanceof Uint8Array ? decoder.decode(value, { stream: true }) : String(value);
|
|
24
25
|
if (html.includes(headClosingTag)) {
|
|
25
26
|
const modifiedHtml = html.replace(headClosingTag, `${core.getTraceMetaTags()}${headClosingTag}`);
|
|
26
27
|
controller.enqueue(encoder.encode(modifiedHtml));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/cloudflare/index.ts"],"sourcesContent":["import { getTraceMetaTags } from '@sentry/core';\n\nexport * from '../client';\n\nexport { wrapSentryHandleRequest } from '../server/wrapSentryHandleRequest';\n\n/**\n * Injects Sentry trace meta tags into the HTML response by transforming the ReadableStream.\n * This enables distributed tracing by adding trace context to the HTML document head.\n * @param body - ReadableStream containing the HTML response body to modify\n * @returns A new ReadableStream with Sentry trace meta tags injected into the head section\n */\nexport function injectTraceMetaTags(body: ReadableStream): ReadableStream {\n const headClosingTag = '</head>';\n\n const reader = body.getReader();\n const stream = new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read();\n\n if (done) {\n controller.close();\n return;\n }\n\n const
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/cloudflare/index.ts"],"sourcesContent":["import { getTraceMetaTags } from '@sentry/core';\n\nexport * from '../client';\n\nexport { wrapSentryHandleRequest } from '../server/wrapSentryHandleRequest';\n\n/**\n * Injects Sentry trace meta tags into the HTML response by transforming the ReadableStream.\n * This enables distributed tracing by adding trace context to the HTML document head.\n * @param body - ReadableStream containing the HTML response body to modify\n * @returns A new ReadableStream with Sentry trace meta tags injected into the head section\n */\nexport function injectTraceMetaTags(body: ReadableStream): ReadableStream {\n const headClosingTag = '</head>';\n\n const reader = body.getReader();\n const encoder = new TextEncoder();\n // A single streaming decoder carries incomplete multi-byte sequences across chunk\n // boundaries. A fresh, non-streaming decoder per chunk would flush a split character\n // as U+FFFD, corrupting the response (see https://github.com/whatwg/encoding/issues/184).\n const decoder = new TextDecoder();\n const stream = new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read();\n\n if (done) {\n controller.close();\n return;\n }\n\n const html = value instanceof Uint8Array ? decoder.decode(value, { stream: true }) : String(value);\n\n if (html.includes(headClosingTag)) {\n const modifiedHtml = html.replace(headClosingTag, `${getTraceMetaTags()}${headClosingTag}`);\n\n controller.enqueue(encoder.encode(modifiedHtml));\n return;\n }\n\n controller.enqueue(encoder.encode(html));\n },\n });\n\n return stream;\n}\n"],"names":["getTraceMetaTags"],"mappings":";;;;;;;;;;;AAYO,SAAS,oBAAoB,IAAA,EAAsC;AACxE,EAAA,MAAM,cAAA,GAAiB,SAAA;AAEvB,EAAA,MAAM,MAAA,GAAS,KAAK,SAAA,EAAU;AAC9B,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAIhC,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,MAAM,MAAA,GAAS,IAAI,cAAA,CAAe;AAAA,IAChC,MAAM,KAAK,UAAA,EAAY;AACrB,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,OAAO,IAAA,EAAK;AAE1C,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,UAAA,CAAW,KAAA,EAAM;AACjB,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,IAAA,GAAO,KAAA,YAAiB,UAAA,GAAa,OAAA,CAAQ,MAAA,CAAO,KAAA,EAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA,GAAI,MAAA,CAAO,KAAK,CAAA;AAEjG,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,cAAc,CAAA,EAAG;AACjC,QAAA,MAAM,YAAA,GAAe,KAAK,OAAA,CAAQ,cAAA,EAAgB,GAAGA,qBAAA,EAAkB,CAAA,EAAG,cAAc,CAAA,CAAE,CAAA;AAE1F,QAAA,UAAA,CAAW,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,YAAY,CAAC,CAAA;AAC/C,QAAA;AAAA,MACF;AAEA,MAAA,UAAA,CAAW,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,IACzC;AAAA,GACD,CAAA;AAED,EAAA,OAAO,MAAA;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|