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