@sentry/react 10.7.0 → 10.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/{reactrouterv6-compat-utils.js → reactrouter-compat-utils/instrumentation.js} +271 -310
- package/build/cjs/reactrouter-compat-utils/instrumentation.js.map +1 -0
- package/build/cjs/reactrouter-compat-utils/lazy-routes.js +80 -0
- package/build/cjs/reactrouter-compat-utils/lazy-routes.js.map +1 -0
- package/build/cjs/reactrouter-compat-utils/utils.js +297 -0
- package/build/cjs/reactrouter-compat-utils/utils.js.map +1 -0
- package/build/cjs/reactrouterv6.js +7 -6
- package/build/cjs/reactrouterv6.js.map +1 -1
- package/build/cjs/reactrouterv7.js +7 -6
- package/build/cjs/reactrouterv7.js.map +1 -1
- package/build/cjs/redux.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/{reactrouterv6-compat-utils.js → reactrouter-compat-utils/instrumentation.js} +265 -307
- package/build/esm/reactrouter-compat-utils/instrumentation.js.map +1 -0
- package/build/esm/reactrouter-compat-utils/lazy-routes.js +76 -0
- package/build/esm/reactrouter-compat-utils/lazy-routes.js.map +1 -0
- package/build/esm/reactrouter-compat-utils/utils.js +286 -0
- package/build/esm/reactrouter-compat-utils/utils.js.map +1 -0
- package/build/esm/reactrouterv6.js +2 -1
- package/build/esm/reactrouterv6.js.map +1 -1
- package/build/esm/reactrouterv7.js +2 -1
- package/build/esm/reactrouterv7.js.map +1 -1
- package/build/esm/redux.js.map +1 -1
- package/build/types/reactrouter-compat-utils/index.d.ts +5 -0
- package/build/types/reactrouter-compat-utils/index.d.ts.map +1 -0
- package/build/types/{reactrouterv6-compat-utils.d.ts → reactrouter-compat-utils/instrumentation.d.ts} +21 -7
- package/build/types/reactrouter-compat-utils/instrumentation.d.ts.map +1 -0
- package/build/types/reactrouter-compat-utils/lazy-routes.d.ts +14 -0
- package/build/types/reactrouter-compat-utils/lazy-routes.d.ts.map +1 -0
- package/build/types/reactrouter-compat-utils/utils.d.ts +50 -0
- package/build/types/reactrouter-compat-utils/utils.d.ts.map +1 -0
- package/build/types/reactrouterv6.d.ts +1 -1
- package/build/types/reactrouterv6.d.ts.map +1 -1
- package/build/types/reactrouterv7.d.ts +1 -1
- package/build/types/reactrouterv7.d.ts.map +1 -1
- package/build/types/redux.d.ts.map +1 -1
- package/build/types-ts3.8/reactrouter-compat-utils/index.d.ts +5 -0
- package/build/types-ts3.8/{reactrouterv6-compat-utils.d.ts → reactrouter-compat-utils/instrumentation.d.ts} +21 -7
- package/build/types-ts3.8/reactrouter-compat-utils/lazy-routes.d.ts +14 -0
- package/build/types-ts3.8/reactrouter-compat-utils/utils.d.ts +56 -0
- package/build/types-ts3.8/reactrouterv6.d.ts +1 -1
- package/build/types-ts3.8/reactrouterv7.d.ts +1 -1
- package/package.json +3 -3
- package/build/cjs/reactrouterv6-compat-utils.js.map +0 -1
- package/build/esm/reactrouterv6-compat-utils.js.map +0 -1
- package/build/types/reactrouterv6-compat-utils.d.ts.map +0 -1
package/build/esm/{reactrouterv6-compat-utils.js → reactrouter-compat-utils/instrumentation.js}
RENAMED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { browserTracingIntegration, WINDOW, startBrowserTracingPageLoadSpan, startBrowserTracingNavigationSpan } from '@sentry/browser';
|
|
2
|
-
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, debug, getActiveSpan, getRootSpan,
|
|
2
|
+
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, debug, spanToJSON, getActiveSpan, getRootSpan, getCurrentScope, getClient, addNonEnumerableProperty } from '@sentry/core';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { DEBUG_BUILD } from '
|
|
5
|
-
import { hoistNonReactStatics } from '
|
|
4
|
+
import { DEBUG_BUILD } from '../debug-build.js';
|
|
5
|
+
import { hoistNonReactStatics } from '../hoist-non-react-statics.js';
|
|
6
|
+
import { checkRouteForAsyncHandler } from './lazy-routes.js';
|
|
7
|
+
import { locationIsInsideDescendantRoute, prefixWithSlash, rebuildRoutePathFromAllRoutes, getNormalizedName, resolveRouteNameAndSource, isLikelyLazyRouteContext, initializeRouterUtils } from './utils.js';
|
|
6
8
|
|
|
7
9
|
/* eslint-disable max-lines */
|
|
8
10
|
// Inspired from Donnie McNeal's solution:
|
|
@@ -14,14 +16,10 @@ let _useLocation;
|
|
|
14
16
|
let _useNavigationType;
|
|
15
17
|
let _createRoutesFromChildren;
|
|
16
18
|
let _matchRoutes;
|
|
17
|
-
let _stripBasename = false;
|
|
18
19
|
let _enableAsyncRouteHandlers = false;
|
|
19
20
|
|
|
20
21
|
const CLIENTS_WITH_INSTRUMENT_NAVIGATION = new WeakSet();
|
|
21
22
|
|
|
22
|
-
// Keeping as a global variable for cross-usage in multiple functions
|
|
23
|
-
const allRoutes = new Set();
|
|
24
|
-
|
|
25
23
|
/**
|
|
26
24
|
* Adds resolved routes as children to the parent route.
|
|
27
25
|
* Prevents duplicate routes by checking if they already exist.
|
|
@@ -44,39 +42,22 @@ function addResolvedRoutesToParent(resolvedRoutes, parentRoute) {
|
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
*/
|
|
50
|
-
function handleAsyncHandlerResult(result, route, handlerKey) {
|
|
51
|
-
if (
|
|
52
|
-
result &&
|
|
53
|
-
typeof result === 'object' &&
|
|
54
|
-
'then' in result &&
|
|
55
|
-
typeof (result ).then === 'function'
|
|
56
|
-
) {
|
|
57
|
-
(result )
|
|
58
|
-
.then((resolvedRoutes) => {
|
|
59
|
-
if (Array.isArray(resolvedRoutes)) {
|
|
60
|
-
processResolvedRoutes(resolvedRoutes, route);
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
.catch((e) => {
|
|
64
|
-
DEBUG_BUILD && debug.warn(`Error resolving async handler '${handlerKey}' for route`, route, e);
|
|
65
|
-
});
|
|
66
|
-
} else if (Array.isArray(result)) {
|
|
67
|
-
processResolvedRoutes(result, route);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
45
|
+
// Keeping as a global variable for cross-usage in multiple functions
|
|
46
|
+
const allRoutes = new Set();
|
|
70
47
|
|
|
71
48
|
/**
|
|
72
49
|
* Processes resolved routes by adding them to allRoutes and checking for nested async handlers.
|
|
73
50
|
*/
|
|
74
|
-
function processResolvedRoutes(
|
|
51
|
+
function processResolvedRoutes(
|
|
52
|
+
resolvedRoutes,
|
|
53
|
+
parentRoute,
|
|
54
|
+
currentLocation = null,
|
|
55
|
+
) {
|
|
75
56
|
resolvedRoutes.forEach(child => {
|
|
76
57
|
allRoutes.add(child);
|
|
77
58
|
// Only check for async handlers if the feature is enabled
|
|
78
59
|
if (_enableAsyncRouteHandlers) {
|
|
79
|
-
checkRouteForAsyncHandler(child);
|
|
60
|
+
checkRouteForAsyncHandler(child, processResolvedRoutes);
|
|
80
61
|
}
|
|
81
62
|
});
|
|
82
63
|
|
|
@@ -85,63 +66,81 @@ function processResolvedRoutes(resolvedRoutes, parentRoute) {
|
|
|
85
66
|
addResolvedRoutesToParent(resolvedRoutes, parentRoute);
|
|
86
67
|
}
|
|
87
68
|
|
|
88
|
-
// After processing lazy routes, check if we need to update an active
|
|
69
|
+
// After processing lazy routes, check if we need to update an active transaction
|
|
89
70
|
const activeRootSpan = getActiveRootSpan();
|
|
90
|
-
if (activeRootSpan
|
|
91
|
-
const
|
|
71
|
+
if (activeRootSpan) {
|
|
72
|
+
const spanOp = spanToJSON(activeRootSpan).op;
|
|
73
|
+
|
|
74
|
+
// Try to use the provided location first, then fall back to global window location if needed
|
|
75
|
+
let location = currentLocation;
|
|
76
|
+
if (!location) {
|
|
77
|
+
if (typeof WINDOW !== 'undefined') {
|
|
78
|
+
const globalLocation = WINDOW.location;
|
|
79
|
+
if (globalLocation) {
|
|
80
|
+
location = { pathname: globalLocation.pathname };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
92
85
|
if (location) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
86
|
+
if (spanOp === 'pageload') {
|
|
87
|
+
// Re-run the pageload transaction update with the newly loaded routes
|
|
88
|
+
updatePageloadTransaction({
|
|
89
|
+
activeRootSpan,
|
|
90
|
+
location: { pathname: location.pathname },
|
|
91
|
+
routes: Array.from(allRoutes),
|
|
92
|
+
allRoutes: Array.from(allRoutes),
|
|
93
|
+
});
|
|
94
|
+
} else if (spanOp === 'navigation') {
|
|
95
|
+
// For navigation spans, update the name with the newly loaded routes
|
|
96
|
+
updateNavigationSpan(activeRootSpan, location, Array.from(allRoutes), false, _matchRoutes);
|
|
97
|
+
}
|
|
102
98
|
}
|
|
103
99
|
}
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
/**
|
|
107
|
-
*
|
|
103
|
+
* Updates a navigation span with the correct route name after lazy routes have been loaded.
|
|
108
104
|
*/
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
function updateNavigationSpan(
|
|
106
|
+
activeRootSpan,
|
|
107
|
+
location,
|
|
108
|
+
allRoutes,
|
|
109
|
+
forceUpdate = false,
|
|
110
|
+
matchRoutes,
|
|
113
111
|
) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
112
|
+
// Check if this span has already been named to avoid multiple updates
|
|
113
|
+
// But allow updates if this is a forced update (e.g., when lazy routes are loaded)
|
|
114
|
+
const hasBeenNamed =
|
|
115
|
+
!forceUpdate &&
|
|
116
|
+
(
|
|
117
|
+
activeRootSpan
|
|
118
|
+
|
|
119
|
+
)?.__sentry_navigation_name_set__;
|
|
120
|
+
|
|
121
|
+
if (!hasBeenNamed) {
|
|
122
|
+
// Get fresh branches for the current location with all loaded routes
|
|
123
|
+
const currentBranches = matchRoutes(allRoutes, location);
|
|
124
|
+
const [name, source] = resolveRouteNameAndSource(
|
|
125
|
+
location,
|
|
126
|
+
allRoutes,
|
|
127
|
+
allRoutes,
|
|
128
|
+
(currentBranches ) || [],
|
|
129
|
+
'',
|
|
130
|
+
);
|
|
126
131
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (route.handle && typeof route.handle === 'object') {
|
|
133
|
-
for (const key of Object.keys(route.handle)) {
|
|
134
|
-
const maybeFn = route.handle[key];
|
|
135
|
-
if (typeof maybeFn === 'function' && !(maybeFn ).__sentry_proxied__) {
|
|
136
|
-
route.handle[key] = createAsyncHandlerProxy(maybeFn, route, key);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
132
|
+
// Only update if we have a valid name and the span hasn't finished
|
|
133
|
+
const spanJson = spanToJSON(activeRootSpan);
|
|
134
|
+
if (name && !spanJson.timestamp) {
|
|
135
|
+
activeRootSpan.updateName(name);
|
|
136
|
+
activeRootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
138
|
+
// Mark this span as having its name set to prevent future updates
|
|
139
|
+
addNonEnumerableProperty(
|
|
140
|
+
activeRootSpan ,
|
|
141
|
+
'__sentry_navigation_name_set__',
|
|
142
|
+
true,
|
|
143
|
+
);
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
146
|
}
|
|
@@ -170,11 +169,14 @@ function createV6CompatibleWrapCreateBrowserRouter
|
|
|
170
169
|
// Check for async handlers that might contain sub-route declarations (only if enabled)
|
|
171
170
|
if (_enableAsyncRouteHandlers) {
|
|
172
171
|
for (const route of routes) {
|
|
173
|
-
checkRouteForAsyncHandler(route);
|
|
172
|
+
checkRouteForAsyncHandler(route, processResolvedRoutes);
|
|
174
173
|
}
|
|
175
174
|
}
|
|
176
175
|
|
|
177
|
-
|
|
176
|
+
// Wrap patchRoutesOnNavigation to detect when lazy routes are loaded
|
|
177
|
+
const wrappedOpts = wrapPatchRoutesOnNavigation(opts);
|
|
178
|
+
|
|
179
|
+
const router = createRouterFunction(routes, wrappedOpts);
|
|
178
180
|
const basename = opts?.basename;
|
|
179
181
|
|
|
180
182
|
const activeRootSpan = getActiveRootSpan();
|
|
@@ -183,14 +185,13 @@ function createV6CompatibleWrapCreateBrowserRouter
|
|
|
183
185
|
// This is the earliest convenient time to update the transaction name.
|
|
184
186
|
// Callbacks to `router.subscribe` are not called for the initial load.
|
|
185
187
|
if (router.state.historyAction === 'POP' && activeRootSpan) {
|
|
186
|
-
updatePageloadTransaction(
|
|
188
|
+
updatePageloadTransaction({
|
|
187
189
|
activeRootSpan,
|
|
188
|
-
router.state.location,
|
|
190
|
+
location: router.state.location,
|
|
189
191
|
routes,
|
|
190
|
-
undefined,
|
|
191
192
|
basename,
|
|
192
|
-
Array.from(allRoutes),
|
|
193
|
-
);
|
|
193
|
+
allRoutes: Array.from(allRoutes),
|
|
194
|
+
});
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
router.subscribe((state) => {
|
|
@@ -253,11 +254,14 @@ function createV6CompatibleWrapCreateMemoryRouter
|
|
|
253
254
|
// Check for async handlers that might contain sub-route declarations (only if enabled)
|
|
254
255
|
if (_enableAsyncRouteHandlers) {
|
|
255
256
|
for (const route of routes) {
|
|
256
|
-
checkRouteForAsyncHandler(route);
|
|
257
|
+
checkRouteForAsyncHandler(route, processResolvedRoutes);
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
|
|
260
|
-
|
|
261
|
+
// Wrap patchRoutesOnNavigation to detect when lazy routes are loaded
|
|
262
|
+
const wrappedOpts = wrapPatchRoutesOnNavigation(opts, true);
|
|
263
|
+
|
|
264
|
+
const router = createRouterFunction(routes, wrappedOpts);
|
|
261
265
|
const basename = opts?.basename;
|
|
262
266
|
|
|
263
267
|
const activeRootSpan = getActiveRootSpan();
|
|
@@ -282,7 +286,13 @@ function createV6CompatibleWrapCreateMemoryRouter
|
|
|
282
286
|
: router.state.location;
|
|
283
287
|
|
|
284
288
|
if (router.state.historyAction === 'POP' && activeRootSpan) {
|
|
285
|
-
updatePageloadTransaction(
|
|
289
|
+
updatePageloadTransaction({
|
|
290
|
+
activeRootSpan,
|
|
291
|
+
location,
|
|
292
|
+
routes,
|
|
293
|
+
basename,
|
|
294
|
+
allRoutes: Array.from(allRoutes),
|
|
295
|
+
});
|
|
286
296
|
}
|
|
287
297
|
|
|
288
298
|
router.subscribe((state) => {
|
|
@@ -338,8 +348,10 @@ function createReactRouterV6CompatibleTracingIntegration(
|
|
|
338
348
|
_useNavigationType = useNavigationType;
|
|
339
349
|
_matchRoutes = matchRoutes;
|
|
340
350
|
_createRoutesFromChildren = createRoutesFromChildren;
|
|
341
|
-
_stripBasename = stripBasename || false;
|
|
342
351
|
_enableAsyncRouteHandlers = enableAsyncRouteHandlers;
|
|
352
|
+
|
|
353
|
+
// Initialize the router utils with the required dependencies
|
|
354
|
+
initializeRouterUtils(matchRoutes, stripBasename || false);
|
|
343
355
|
},
|
|
344
356
|
afterAllSetup(client) {
|
|
345
357
|
integration.afterAllSetup(client);
|
|
@@ -395,14 +407,12 @@ function createV6CompatibleWrapUseRoutes(origUseRoutes, version) {
|
|
|
395
407
|
if (isMountRenderPass.current) {
|
|
396
408
|
addRoutesToAllRoutes(routes);
|
|
397
409
|
|
|
398
|
-
updatePageloadTransaction(
|
|
399
|
-
getActiveRootSpan(),
|
|
400
|
-
normalizedLocation,
|
|
410
|
+
updatePageloadTransaction({
|
|
411
|
+
activeRootSpan: getActiveRootSpan(),
|
|
412
|
+
location: normalizedLocation,
|
|
401
413
|
routes,
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
Array.from(allRoutes),
|
|
405
|
-
);
|
|
414
|
+
allRoutes: Array.from(allRoutes),
|
|
415
|
+
});
|
|
406
416
|
isMountRenderPass.current = false;
|
|
407
417
|
} else {
|
|
408
418
|
handleNavigation({
|
|
@@ -424,6 +434,79 @@ function createV6CompatibleWrapUseRoutes(origUseRoutes, version) {
|
|
|
424
434
|
};
|
|
425
435
|
}
|
|
426
436
|
|
|
437
|
+
function wrapPatchRoutesOnNavigation(
|
|
438
|
+
opts,
|
|
439
|
+
isMemoryRouter = false,
|
|
440
|
+
) {
|
|
441
|
+
if (!opts || !('patchRoutesOnNavigation' in opts) || typeof opts.patchRoutesOnNavigation !== 'function') {
|
|
442
|
+
return opts || {};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const originalPatchRoutes = opts.patchRoutesOnNavigation;
|
|
446
|
+
return {
|
|
447
|
+
...opts,
|
|
448
|
+
patchRoutesOnNavigation: async (args) => {
|
|
449
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
450
|
+
const targetPath = (args )?.path;
|
|
451
|
+
|
|
452
|
+
// For browser router, wrap the patch function to update span during patching
|
|
453
|
+
if (!isMemoryRouter) {
|
|
454
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
455
|
+
const originalPatch = (args )?.patch;
|
|
456
|
+
if (originalPatch) {
|
|
457
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
458
|
+
(args ).patch = (routeId, children) => {
|
|
459
|
+
addRoutesToAllRoutes(children);
|
|
460
|
+
const activeRootSpan = getActiveRootSpan();
|
|
461
|
+
if (activeRootSpan && (spanToJSON(activeRootSpan) ).op === 'navigation') {
|
|
462
|
+
updateNavigationSpan(
|
|
463
|
+
activeRootSpan,
|
|
464
|
+
{
|
|
465
|
+
pathname: targetPath,
|
|
466
|
+
search: '',
|
|
467
|
+
hash: '',
|
|
468
|
+
state: null,
|
|
469
|
+
key: 'default',
|
|
470
|
+
},
|
|
471
|
+
Array.from(allRoutes),
|
|
472
|
+
true, // forceUpdate = true since we're loading lazy routes
|
|
473
|
+
_matchRoutes,
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
return originalPatch(routeId, children);
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const result = await originalPatchRoutes(args);
|
|
482
|
+
|
|
483
|
+
// Update navigation span after routes are patched
|
|
484
|
+
const activeRootSpan = getActiveRootSpan();
|
|
485
|
+
if (activeRootSpan && (spanToJSON(activeRootSpan) ).op === 'navigation') {
|
|
486
|
+
// For memory routers, we should not access window.location; use targetPath only
|
|
487
|
+
const pathname = isMemoryRouter ? targetPath : targetPath || WINDOW.location?.pathname;
|
|
488
|
+
if (pathname) {
|
|
489
|
+
updateNavigationSpan(
|
|
490
|
+
activeRootSpan,
|
|
491
|
+
{
|
|
492
|
+
pathname,
|
|
493
|
+
search: '',
|
|
494
|
+
hash: '',
|
|
495
|
+
state: null,
|
|
496
|
+
key: 'default',
|
|
497
|
+
},
|
|
498
|
+
Array.from(allRoutes),
|
|
499
|
+
false, // forceUpdate = false since this is after lazy routes are loaded
|
|
500
|
+
_matchRoutes,
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return result;
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
427
510
|
function handleNavigation(opts
|
|
428
511
|
|
|
429
512
|
) {
|
|
@@ -436,105 +519,28 @@ function handleNavigation(opts
|
|
|
436
519
|
}
|
|
437
520
|
|
|
438
521
|
if ((navigationType === 'PUSH' || navigationType === 'POP') && branches) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
522
|
+
const [name, source] = resolveRouteNameAndSource(
|
|
523
|
+
location,
|
|
524
|
+
routes,
|
|
525
|
+
allRoutes || routes,
|
|
526
|
+
branches ,
|
|
527
|
+
basename,
|
|
528
|
+
);
|
|
447
529
|
|
|
448
|
-
if
|
|
449
|
-
|
|
450
|
-
}
|
|
530
|
+
// Check if this might be a lazy route context
|
|
531
|
+
const isLazyRouteContext = isLikelyLazyRouteContext(allRoutes || routes, location);
|
|
451
532
|
|
|
452
533
|
const activeSpan = getActiveSpan();
|
|
453
|
-
const
|
|
534
|
+
const spanJson = activeSpan && spanToJSON(activeSpan);
|
|
535
|
+
const isAlreadyInNavigationSpan = spanJson?.op === 'navigation';
|
|
454
536
|
|
|
455
537
|
// Cross usage can result in multiple navigation spans being created without this check
|
|
456
|
-
if (isAlreadyInNavigationSpan) {
|
|
457
|
-
activeSpan
|
|
458
|
-
activeSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
|
|
538
|
+
if (isAlreadyInNavigationSpan && activeSpan && spanJson) {
|
|
539
|
+
handleExistingNavigationSpan(activeSpan, spanJson, name, source, isLazyRouteContext);
|
|
459
540
|
} else {
|
|
460
|
-
|
|
461
|
-
name,
|
|
462
|
-
attributes: {
|
|
463
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
|
|
464
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
|
|
465
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter_v${version}`,
|
|
466
|
-
},
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Strip the basename from a pathname if exists.
|
|
474
|
-
*
|
|
475
|
-
* Vendored and modified from `react-router`
|
|
476
|
-
* https://github.com/remix-run/react-router/blob/462bb712156a3f739d6139a0f14810b76b002df6/packages/router/utils.ts#L1038
|
|
477
|
-
*/
|
|
478
|
-
function stripBasenameFromPathname(pathname, basename) {
|
|
479
|
-
if (!basename || basename === '/') {
|
|
480
|
-
return pathname;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {
|
|
484
|
-
return pathname;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// We want to leave trailing slash behavior in the user's control, so if they
|
|
488
|
-
// specify a basename with a trailing slash, we should support it
|
|
489
|
-
const startIndex = basename.endsWith('/') ? basename.length - 1 : basename.length;
|
|
490
|
-
const nextChar = pathname.charAt(startIndex);
|
|
491
|
-
if (nextChar && nextChar !== '/') {
|
|
492
|
-
// pathname does not start with basename/
|
|
493
|
-
return pathname;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return pathname.slice(startIndex) || '/';
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function sendIndexPath(pathBuilder, pathname, basename) {
|
|
500
|
-
const reconstructedPath = pathBuilder || _stripBasename ? stripBasenameFromPathname(pathname, basename) : pathname;
|
|
501
|
-
|
|
502
|
-
const formattedPath =
|
|
503
|
-
// If the path ends with a slash, remove it
|
|
504
|
-
reconstructedPath[reconstructedPath.length - 1] === '/'
|
|
505
|
-
? reconstructedPath.slice(0, -1)
|
|
506
|
-
: // If the path ends with a wildcard, remove it
|
|
507
|
-
reconstructedPath.slice(-2) === '/*'
|
|
508
|
-
? reconstructedPath.slice(0, -1)
|
|
509
|
-
: reconstructedPath;
|
|
510
|
-
|
|
511
|
-
return [formattedPath, 'route'];
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function pathEndsWithWildcard(path) {
|
|
515
|
-
return path.endsWith('*');
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function pathIsWildcardAndHasChildren(path, branch) {
|
|
519
|
-
return (pathEndsWithWildcard(path) && !!branch.route.children?.length) || false;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function routeIsDescendant(route) {
|
|
523
|
-
return !!(!route.children && route.element && route.path?.endsWith('/*'));
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function locationIsInsideDescendantRoute(location, routes) {
|
|
527
|
-
const matchedRoutes = _matchRoutes(routes, location) ;
|
|
528
|
-
|
|
529
|
-
if (matchedRoutes) {
|
|
530
|
-
for (const match of matchedRoutes) {
|
|
531
|
-
if (routeIsDescendant(match.route) && pickSplat(match)) {
|
|
532
|
-
return true;
|
|
533
|
-
}
|
|
541
|
+
createNewNavigationSpan(client, name, source, version, isLazyRouteContext);
|
|
534
542
|
}
|
|
535
543
|
}
|
|
536
|
-
|
|
537
|
-
return false;
|
|
538
544
|
}
|
|
539
545
|
|
|
540
546
|
function addRoutesToAllRoutes(routes) {
|
|
@@ -565,125 +571,15 @@ function getChildRoutesRecursively(route, allRoutes = new Set()) {
|
|
|
565
571
|
return allRoutes;
|
|
566
572
|
}
|
|
567
573
|
|
|
568
|
-
function
|
|
569
|
-
return trimWildcard(match.route.path || '');
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function pickSplat(match) {
|
|
573
|
-
return match.params['*'] || '';
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
function trimWildcard(path) {
|
|
577
|
-
return path[path.length - 1] === '*' ? path.slice(0, -1) : path;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
function trimSlash(path) {
|
|
581
|
-
return path[path.length - 1] === '/' ? path.slice(0, -1) : path;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
function prefixWithSlash(path) {
|
|
585
|
-
return path[0] === '/' ? path : `/${path}`;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
function rebuildRoutePathFromAllRoutes(allRoutes, location) {
|
|
589
|
-
const matchedRoutes = _matchRoutes(allRoutes, location) ;
|
|
590
|
-
|
|
591
|
-
if (!matchedRoutes || matchedRoutes.length === 0) {
|
|
592
|
-
return '';
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
for (const match of matchedRoutes) {
|
|
596
|
-
if (match.route.path && match.route.path !== '*') {
|
|
597
|
-
const path = pickPath(match);
|
|
598
|
-
const strippedPath = stripBasenameFromPathname(location.pathname, prefixWithSlash(match.pathnameBase));
|
|
599
|
-
|
|
600
|
-
if (location.pathname === strippedPath) {
|
|
601
|
-
return trimSlash(strippedPath);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
return trimSlash(
|
|
605
|
-
trimSlash(path || '') +
|
|
606
|
-
prefixWithSlash(
|
|
607
|
-
rebuildRoutePathFromAllRoutes(
|
|
608
|
-
allRoutes.filter(route => route !== match.route),
|
|
609
|
-
{
|
|
610
|
-
pathname: strippedPath,
|
|
611
|
-
},
|
|
612
|
-
),
|
|
613
|
-
),
|
|
614
|
-
);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
return '';
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
function getNormalizedName(
|
|
622
|
-
routes,
|
|
623
|
-
location,
|
|
624
|
-
branches,
|
|
625
|
-
basename = '',
|
|
626
|
-
) {
|
|
627
|
-
if (!routes || routes.length === 0) {
|
|
628
|
-
return [_stripBasename ? stripBasenameFromPathname(location.pathname, basename) : location.pathname, 'url'];
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
let pathBuilder = '';
|
|
632
|
-
|
|
633
|
-
if (branches) {
|
|
634
|
-
for (const branch of branches) {
|
|
635
|
-
const route = branch.route;
|
|
636
|
-
if (route) {
|
|
637
|
-
// Early return if index route
|
|
638
|
-
if (route.index) {
|
|
639
|
-
return sendIndexPath(pathBuilder, branch.pathname, basename);
|
|
640
|
-
}
|
|
641
|
-
const path = route.path;
|
|
642
|
-
|
|
643
|
-
// If path is not a wildcard and has no child routes, append the path
|
|
644
|
-
if (path && !pathIsWildcardAndHasChildren(path, branch)) {
|
|
645
|
-
const newPath = path[0] === '/' || pathBuilder[pathBuilder.length - 1] === '/' ? path : `/${path}`;
|
|
646
|
-
pathBuilder = trimSlash(pathBuilder) + prefixWithSlash(newPath);
|
|
647
|
-
|
|
648
|
-
// If the path matches the current location, return the path
|
|
649
|
-
if (trimSlash(location.pathname) === trimSlash(basename + branch.pathname)) {
|
|
650
|
-
if (
|
|
651
|
-
// If the route defined on the element is something like
|
|
652
|
-
// <Route path="/stores/:storeId/products/:productId" element={<div>Product</div>} />
|
|
653
|
-
// We should check against the branch.pathname for the number of / separators
|
|
654
|
-
getNumberOfUrlSegments(pathBuilder) !== getNumberOfUrlSegments(branch.pathname) &&
|
|
655
|
-
// We should not count wildcard operators in the url segments calculation
|
|
656
|
-
!pathEndsWithWildcard(pathBuilder)
|
|
657
|
-
) {
|
|
658
|
-
return [(_stripBasename ? '' : basename) + newPath, 'route'];
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// if the last character of the pathbuilder is a wildcard and there are children, remove the wildcard
|
|
662
|
-
if (pathIsWildcardAndHasChildren(pathBuilder, branch)) {
|
|
663
|
-
pathBuilder = pathBuilder.slice(0, -1);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
return [(_stripBasename ? '' : basename) + pathBuilder, 'route'];
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
const fallbackTransactionName = _stripBasename
|
|
674
|
-
? stripBasenameFromPathname(location.pathname, basename)
|
|
675
|
-
: location.pathname || '/';
|
|
676
|
-
|
|
677
|
-
return [fallbackTransactionName, 'url'];
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
function updatePageloadTransaction(
|
|
574
|
+
function updatePageloadTransaction({
|
|
681
575
|
activeRootSpan,
|
|
682
576
|
location,
|
|
683
577
|
routes,
|
|
684
578
|
matches,
|
|
685
579
|
basename,
|
|
686
580
|
allRoutes,
|
|
581
|
+
}
|
|
582
|
+
|
|
687
583
|
) {
|
|
688
584
|
const branches = Array.isArray(matches)
|
|
689
585
|
? matches
|
|
@@ -740,7 +636,12 @@ function createV6CompatibleWithSentryReactRouterRouting(
|
|
|
740
636
|
if (isMountRenderPass.current) {
|
|
741
637
|
addRoutesToAllRoutes(routes);
|
|
742
638
|
|
|
743
|
-
updatePageloadTransaction(
|
|
639
|
+
updatePageloadTransaction({
|
|
640
|
+
activeRootSpan: getActiveRootSpan(),
|
|
641
|
+
location,
|
|
642
|
+
routes,
|
|
643
|
+
allRoutes: Array.from(allRoutes),
|
|
644
|
+
});
|
|
744
645
|
isMountRenderPass.current = false;
|
|
745
646
|
} else {
|
|
746
647
|
handleNavigation({
|
|
@@ -784,12 +685,69 @@ function getActiveRootSpan() {
|
|
|
784
685
|
}
|
|
785
686
|
|
|
786
687
|
/**
|
|
787
|
-
*
|
|
688
|
+
* Handles updating an existing navigation span
|
|
689
|
+
*/
|
|
690
|
+
function handleExistingNavigationSpan(
|
|
691
|
+
activeSpan,
|
|
692
|
+
spanJson,
|
|
693
|
+
name,
|
|
694
|
+
source,
|
|
695
|
+
isLikelyLazyRoute,
|
|
696
|
+
) {
|
|
697
|
+
// Check if we've already set the name for this span using a custom property
|
|
698
|
+
const hasBeenNamed = (
|
|
699
|
+
activeSpan
|
|
700
|
+
|
|
701
|
+
)?.__sentry_navigation_name_set__;
|
|
702
|
+
|
|
703
|
+
if (!hasBeenNamed) {
|
|
704
|
+
// This is the first time we're setting the name for this span
|
|
705
|
+
if (!spanJson.timestamp) {
|
|
706
|
+
activeSpan?.updateName(name);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// For lazy routes, don't mark as named yet so it can be updated later
|
|
710
|
+
if (!isLikelyLazyRoute) {
|
|
711
|
+
addNonEnumerableProperty(
|
|
712
|
+
activeSpan ,
|
|
713
|
+
'__sentry_navigation_name_set__',
|
|
714
|
+
true,
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Always set the source attribute to keep it consistent with the current route
|
|
720
|
+
activeSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Creates a new navigation span
|
|
788
725
|
*/
|
|
789
|
-
function
|
|
790
|
-
|
|
791
|
-
|
|
726
|
+
function createNewNavigationSpan(
|
|
727
|
+
client,
|
|
728
|
+
name,
|
|
729
|
+
source,
|
|
730
|
+
version,
|
|
731
|
+
isLikelyLazyRoute,
|
|
732
|
+
) {
|
|
733
|
+
const newSpan = startBrowserTracingNavigationSpan(client, {
|
|
734
|
+
name,
|
|
735
|
+
attributes: {
|
|
736
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
|
|
737
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
|
|
738
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter_v${version}`,
|
|
739
|
+
},
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
// For lazy routes, don't mark as named yet so it can be updated later when the route loads
|
|
743
|
+
if (!isLikelyLazyRoute && newSpan) {
|
|
744
|
+
addNonEnumerableProperty(
|
|
745
|
+
newSpan ,
|
|
746
|
+
'__sentry_navigation_name_set__',
|
|
747
|
+
true,
|
|
748
|
+
);
|
|
749
|
+
}
|
|
792
750
|
}
|
|
793
751
|
|
|
794
|
-
export {
|
|
795
|
-
//# sourceMappingURL=
|
|
752
|
+
export { addResolvedRoutesToParent, createNewNavigationSpan, createReactRouterV6CompatibleTracingIntegration, createV6CompatibleWithSentryReactRouterRouting, createV6CompatibleWrapCreateBrowserRouter, createV6CompatibleWrapCreateMemoryRouter, createV6CompatibleWrapUseRoutes, handleExistingNavigationSpan, handleNavigation, processResolvedRoutes, updateNavigationSpan };
|
|
753
|
+
//# sourceMappingURL=instrumentation.js.map
|