@sentry/react 10.53.1 → 10.55.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.
Files changed (78) hide show
  1. package/build/cjs/constants.js +3 -5
  2. package/build/cjs/constants.js.map +1 -1
  3. package/build/cjs/debug-build.js +0 -5
  4. package/build/cjs/debug-build.js.map +1 -1
  5. package/build/cjs/error.js +12 -72
  6. package/build/cjs/error.js.map +1 -1
  7. package/build/cjs/errorboundary.js +22 -77
  8. package/build/cjs/errorboundary.js.map +1 -1
  9. package/build/cjs/hoist-non-react-statics.js +16 -84
  10. package/build/cjs/hoist-non-react-statics.js.map +1 -1
  11. package/build/cjs/isSyntheticEvent.js +10 -0
  12. package/build/cjs/isSyntheticEvent.js.map +1 -0
  13. package/build/cjs/profiler.js +35 -110
  14. package/build/cjs/profiler.js.map +1 -1
  15. package/build/cjs/reactrouter-compat-utils/instrumentation.js +219 -690
  16. package/build/cjs/reactrouter-compat-utils/instrumentation.js.map +1 -1
  17. package/build/cjs/reactrouter-compat-utils/lazy-routes.js +24 -85
  18. package/build/cjs/reactrouter-compat-utils/lazy-routes.js.map +1 -1
  19. package/build/cjs/reactrouter-compat-utils/route-manifest.js +17 -95
  20. package/build/cjs/reactrouter-compat-utils/route-manifest.js.map +1 -1
  21. package/build/cjs/reactrouter-compat-utils/utils.js +48 -190
  22. package/build/cjs/reactrouter-compat-utils/utils.js.map +1 -1
  23. package/build/cjs/reactrouter.js +33 -113
  24. package/build/cjs/reactrouter.js.map +1 -1
  25. package/build/cjs/reactrouterv3.js +27 -59
  26. package/build/cjs/reactrouterv3.js.map +1 -1
  27. package/build/cjs/reactrouterv6.js +8 -41
  28. package/build/cjs/reactrouterv6.js.map +1 -1
  29. package/build/cjs/reactrouterv7.js +8 -41
  30. package/build/cjs/reactrouterv7.js.map +1 -1
  31. package/build/cjs/redux.js +60 -88
  32. package/build/cjs/redux.js.map +1 -1
  33. package/build/cjs/sdk.js +13 -8
  34. package/build/cjs/sdk.js.map +1 -1
  35. package/build/cjs/tanstackrouter.js +24 -64
  36. package/build/cjs/tanstackrouter.js.map +1 -1
  37. package/build/esm/constants.js +3 -5
  38. package/build/esm/constants.js.map +1 -1
  39. package/build/esm/debug-build.js +0 -5
  40. package/build/esm/debug-build.js.map +1 -1
  41. package/build/esm/error.js +12 -72
  42. package/build/esm/error.js.map +1 -1
  43. package/build/esm/errorboundary.js +22 -77
  44. package/build/esm/errorboundary.js.map +1 -1
  45. package/build/esm/hoist-non-react-statics.js +16 -84
  46. package/build/esm/hoist-non-react-statics.js.map +1 -1
  47. package/build/esm/isSyntheticEvent.js +8 -0
  48. package/build/esm/isSyntheticEvent.js.map +1 -0
  49. package/build/esm/package.json +1 -1
  50. package/build/esm/profiler.js +35 -110
  51. package/build/esm/profiler.js.map +1 -1
  52. package/build/esm/reactrouter-compat-utils/instrumentation.js +219 -690
  53. package/build/esm/reactrouter-compat-utils/instrumentation.js.map +1 -1
  54. package/build/esm/reactrouter-compat-utils/lazy-routes.js +24 -85
  55. package/build/esm/reactrouter-compat-utils/lazy-routes.js.map +1 -1
  56. package/build/esm/reactrouter-compat-utils/route-manifest.js +17 -95
  57. package/build/esm/reactrouter-compat-utils/route-manifest.js.map +1 -1
  58. package/build/esm/reactrouter-compat-utils/utils.js +48 -190
  59. package/build/esm/reactrouter-compat-utils/utils.js.map +1 -1
  60. package/build/esm/reactrouter.js +33 -113
  61. package/build/esm/reactrouter.js.map +1 -1
  62. package/build/esm/reactrouterv3.js +27 -59
  63. package/build/esm/reactrouterv3.js.map +1 -1
  64. package/build/esm/reactrouterv6.js +8 -41
  65. package/build/esm/reactrouterv6.js.map +1 -1
  66. package/build/esm/reactrouterv7.js +8 -41
  67. package/build/esm/reactrouterv7.js.map +1 -1
  68. package/build/esm/redux.js +60 -88
  69. package/build/esm/redux.js.map +1 -1
  70. package/build/esm/sdk.js +15 -10
  71. package/build/esm/sdk.js.map +1 -1
  72. package/build/esm/tanstackrouter.js +24 -64
  73. package/build/esm/tanstackrouter.js.map +1 -1
  74. package/build/types/isSyntheticEvent.d.ts +10 -0
  75. package/build/types/isSyntheticEvent.d.ts.map +1 -0
  76. package/build/types/sdk.d.ts.map +1 -1
  77. package/build/types-ts3.8/isSyntheticEvent.d.ts +10 -0
  78. package/package.json +3 -3
@@ -6,52 +6,26 @@ import { hoistNonReactStatics } from '../hoist-non-react-statics.js';
6
6
  import { checkRouteForAsyncHandler } from './lazy-routes.js';
7
7
  import { getActiveRootSpan, setNavigationContext, clearNavigationContext, resolveRouteNameAndSource, transactionNameHasWildcard, initializeRouterUtils } from './utils.js';
8
8
 
9
- /* eslint-disable max-lines */
10
- // Inspired from Donnie McNeal's solution:
11
- // https://gist.github.com/wontondon/e8c4bdf2888875e4c755712e99279536
12
-
13
-
14
9
  let _useEffect;
15
10
  let _useLocation;
16
11
  let _useNavigationType;
17
12
  let _createRoutesFromChildren;
18
13
  let _matchRoutes;
19
-
20
14
  let _enableAsyncRouteHandlers = false;
21
- let _lazyRouteTimeout = 3000;
15
+ let _lazyRouteTimeout = 3e3;
22
16
  let _lazyRouteManifest;
23
- let _basename = '';
24
-
25
- const CLIENTS_WITH_INSTRUMENT_NAVIGATION = new WeakSet();
26
-
27
- // Prevents duplicate spans when router.subscribe fires multiple times
28
- const activeNavigationSpans = new WeakMap
29
-
30
- ();
31
-
32
- // Exported for testing only
33
- const allRoutes = new Set();
34
-
35
- // Tracks lazy route loads to wait before finalizing span names
36
- const pendingLazyRouteLoads = new WeakMap();
37
-
38
- // Tracks deferred lazy route promises that can be resolved when patchRoutesOnNavigation is called
39
- const deferredLazyRouteResolvers = new WeakMap();
40
-
41
- /**
42
- * Schedules a callback using requestAnimationFrame when available (browser),
43
- * or falls back to setTimeout for SSR environments (Node.js, createMemoryRouter tests).
44
- */
17
+ let _basename = "";
18
+ const CLIENTS_WITH_INSTRUMENT_NAVIGATION = /* @__PURE__ */ new WeakSet();
19
+ const activeNavigationSpans = /* @__PURE__ */ new WeakMap();
20
+ const allRoutes = /* @__PURE__ */ new Set();
21
+ const pendingLazyRouteLoads = /* @__PURE__ */ new WeakMap();
22
+ const deferredLazyRouteResolvers = /* @__PURE__ */ new WeakMap();
45
23
  function scheduleCallback(callback) {
46
24
  if (WINDOW?.requestAnimationFrame) {
47
25
  return WINDOW.requestAnimationFrame(callback);
48
26
  }
49
- return setTimeout(callback, 0) ;
27
+ return setTimeout(callback, 0);
50
28
  }
51
-
52
- /**
53
- * Cancels a scheduled callback, handling both RAF (browser) and timeout (SSR) IDs.
54
- */
55
29
  function cancelScheduledCallback(id) {
56
30
  if (WINDOW?.cancelAnimationFrame) {
57
31
  WINDOW.cancelAnimationFrame(id);
@@ -59,103 +33,48 @@ function cancelScheduledCallback(id) {
59
33
  clearTimeout(id);
60
34
  }
61
35
  }
62
-
63
- /**
64
- * Computes location key for duplicate detection. Normalizes undefined/null to empty strings.
65
- * Exported for testing.
66
- */
67
36
  function computeLocationKey(location) {
68
- return `${location.pathname}${location.search || ''}${location.hash || ''}`;
37
+ return `${location.pathname}${location.search || ""}${location.hash || ""}`;
69
38
  }
70
-
71
- /**
72
- * Checks if a route name is parameterized (contains route parameters like :id or wildcards like *)
73
- * vs a raw URL path.
74
- */
75
39
  function isParameterizedRoute(routeName) {
76
- return routeName.includes(':') || routeName.includes('*');
40
+ return routeName.includes(":") || routeName.includes("*");
77
41
  }
78
-
79
- /**
80
- * Determines if a navigation should be skipped as a duplicate, and if an existing span should be updated.
81
- * Exported for testing.
82
- *
83
- * @returns An object with:
84
- * - skip: boolean - Whether to skip creating a new span
85
- * - shouldUpdate: boolean - Whether to update the existing span name (wildcard upgrade)
86
- */
87
- function shouldSkipNavigation(
88
- trackedNav
89
-
90
- ,
91
- locationKey,
92
- proposedName,
93
- spanHasEnded,
94
- ) {
42
+ function shouldSkipNavigation(trackedNav, locationKey, proposedName, spanHasEnded) {
95
43
  if (!trackedNav) {
96
44
  return { skip: false, shouldUpdate: false };
97
45
  }
98
-
99
- // Check if this is a duplicate navigation (same location)
100
- // 1. If it's a placeholder, it's always a duplicate (we're waiting for the real one)
101
- // 2. If it's a real span, it's a duplicate only if it hasn't ended yet
102
46
  const isDuplicate = trackedNav.locationKey === locationKey && (trackedNav.isPlaceholder || !spanHasEnded);
103
-
104
47
  if (isDuplicate) {
105
- // Check if we should update the span name with a better route
106
- // Allow updates if:
107
- // 1. Current has wildcard and new doesn't (wildcard → parameterized upgrade)
108
- // 2. Current is raw path and new is parameterized (raw → parameterized upgrade)
109
- // 3. New name is different and more specific (longer, indicating nested routes resolved)
110
48
  const currentHasWildcard = !!trackedNav.routeName && transactionNameHasWildcard(trackedNav.routeName);
111
49
  const proposedHasWildcard = transactionNameHasWildcard(proposedName);
112
50
  const currentIsParameterized = !!trackedNav.routeName && isParameterizedRoute(trackedNav.routeName);
113
51
  const proposedIsParameterized = isParameterizedRoute(proposedName);
114
-
115
52
  const isWildcardUpgrade = currentHasWildcard && !proposedHasWildcard;
116
53
  const isRawToParameterized = !currentIsParameterized && proposedIsParameterized;
117
- const isMoreSpecific =
118
- proposedName !== trackedNav.routeName &&
119
- proposedName.length > (trackedNav.routeName?.length || 0) &&
120
- !proposedHasWildcard;
121
-
54
+ const isMoreSpecific = proposedName !== trackedNav.routeName && proposedName.length > (trackedNav.routeName?.length || 0) && !proposedHasWildcard;
122
55
  const shouldUpdate = !!(trackedNav.routeName && (isWildcardUpgrade || isRawToParameterized || isMoreSpecific));
123
-
124
56
  return { skip: true, shouldUpdate };
125
57
  }
126
-
127
58
  return { skip: false, shouldUpdate: false };
128
59
  }
129
-
130
60
  function addResolvedRoutesToParent(resolvedRoutes, parentRoute) {
131
61
  const existingChildren = parentRoute.children || [];
132
-
133
62
  const newRoutes = resolvedRoutes.filter(
134
- newRoute =>
135
- !existingChildren.some(
136
- existing =>
137
- existing === newRoute ||
138
- (newRoute.path && existing.path === newRoute.path) ||
139
- (newRoute.id && existing.id === newRoute.id),
140
- ),
63
+ (newRoute) => !existingChildren.some(
64
+ (existing) => existing === newRoute || newRoute.path && existing.path === newRoute.path || newRoute.id && existing.id === newRoute.id
65
+ )
141
66
  );
142
-
143
67
  if (newRoutes.length > 0) {
144
68
  parentRoute.children = [...existingChildren, ...newRoutes];
145
69
  }
146
70
  }
147
-
148
- /** Registers a pending lazy route load promise for a span. */
149
71
  function trackLazyRouteLoad(span, promise) {
150
72
  let promises = pendingLazyRouteLoads.get(span);
151
73
  if (!promises) {
152
- promises = new Set();
74
+ promises = /* @__PURE__ */ new Set();
153
75
  pendingLazyRouteLoads.set(span, promises);
154
76
  }
155
77
  promises.add(promise);
156
-
157
- // Clean up when promise resolves/rejects
158
- // oxlint-disable-next-line typescript/no-floating-promises
159
78
  promise.finally(() => {
160
79
  const currentPromises = pendingLazyRouteLoads.get(span);
161
80
  if (currentPromises) {
@@ -163,192 +82,122 @@ function trackLazyRouteLoad(span, promise) {
163
82
  }
164
83
  });
165
84
  }
166
-
167
- /**
168
- * Creates a deferred promise for a span that will be resolved when patchRoutesOnNavigation is called.
169
- * This ensures that patchedEnd waits for patchRoutesOnNavigation to be called before ending the span.
170
- */
171
85
  function createDeferredLazyRoutePromise(span) {
172
- const deferredPromise = new Promise(resolve => {
86
+ const deferredPromise = new Promise((resolve) => {
173
87
  deferredLazyRouteResolvers.set(span, resolve);
174
88
  });
175
-
176
89
  trackLazyRouteLoad(span, deferredPromise);
177
90
  }
178
-
179
- /**
180
- * Resolves the deferred lazy route promise for a span.
181
- * Called when patchRoutesOnNavigation is invoked.
182
- */
183
91
  function resolveDeferredLazyRoutePromise(span) {
184
92
  const resolver = deferredLazyRouteResolvers.get(span);
185
93
  if (resolver) {
186
94
  resolver();
187
95
  deferredLazyRouteResolvers.delete(span);
188
- // Clear the flag so patchSpanEnd doesn't wait unnecessarily for routes that have already loaded
189
- if ((span ).__sentry_may_have_lazy_routes__) {
190
- (span ).__sentry_may_have_lazy_routes__ = false;
96
+ if (span.__sentry_may_have_lazy_routes__) {
97
+ span.__sentry_may_have_lazy_routes__ = false;
191
98
  }
192
99
  }
193
100
  }
194
-
195
- /**
196
- * Processes resolved routes by adding them to allRoutes and checking for nested async handlers.
197
- * When capturedSpan is provided, updates that specific span instead of the current active span.
198
- * This prevents race conditions where a lazy handler resolves after the user has navigated away.
199
- */
200
- function processResolvedRoutes(
201
- resolvedRoutes,
202
- parentRoute,
203
- currentLocation = null,
204
- capturedSpan,
205
- ) {
206
- resolvedRoutes.forEach(child => {
101
+ function processResolvedRoutes(resolvedRoutes, parentRoute, currentLocation = null, capturedSpan) {
102
+ resolvedRoutes.forEach((child) => {
207
103
  allRoutes.add(child);
208
- // Only check for async handlers if the feature is enabled
209
104
  if (_enableAsyncRouteHandlers) {
210
105
  checkRouteForAsyncHandler(child, processResolvedRoutes);
211
106
  }
212
107
  });
213
-
214
108
  if (parentRoute) {
215
- // If a parent route is provided, add the resolved routes as children to the parent route
216
109
  addResolvedRoutesToParent(resolvedRoutes, parentRoute);
217
110
  }
218
-
219
- // Use captured span if provided, otherwise fall back to current active span
220
111
  const targetSpan = capturedSpan ?? getActiveRootSpan();
221
112
  if (targetSpan) {
222
113
  const spanJson = spanToJSON(targetSpan);
223
-
224
- // Skip update if span has already ended (timestamp is set when span.end() is called)
225
114
  if (spanJson.timestamp) {
226
- DEBUG_BUILD && debug.warn('[React Router] Lazy handler resolved after span ended - skipping update');
115
+ DEBUG_BUILD && debug.warn("[React Router] Lazy handler resolved after span ended - skipping update");
227
116
  return;
228
117
  }
229
-
230
118
  const spanOp = spanJson.op;
231
-
232
- // Use captured location for route matching (ensures we match against the correct route)
233
- // Fall back to window.location only if no captured location and no captured span
234
- // (i.e., this is not from an async handler)
235
119
  let location = currentLocation;
236
120
  if (!location && !capturedSpan) {
237
- if (typeof WINDOW !== 'undefined') {
121
+ if (typeof WINDOW !== "undefined") {
238
122
  const globalLocation = WINDOW.location;
239
123
  if (globalLocation?.pathname) {
240
124
  location = { pathname: globalLocation.pathname };
241
125
  }
242
126
  }
243
127
  }
244
-
245
128
  if (location) {
246
- if (spanOp === 'pageload') {
247
- // Re-run the pageload transaction update with the newly loaded routes
129
+ if (spanOp === "pageload") {
248
130
  updatePageloadTransaction({
249
131
  activeRootSpan: targetSpan,
250
132
  location: { pathname: location.pathname },
251
133
  routes: Array.from(allRoutes),
252
- allRoutes: Array.from(allRoutes),
134
+ allRoutes: Array.from(allRoutes)
253
135
  });
254
- } else if (spanOp === 'navigation') {
255
- // For navigation spans, update the name with the newly loaded routes
136
+ } else if (spanOp === "navigation") {
256
137
  updateNavigationSpan(targetSpan, location, Array.from(allRoutes), false, _matchRoutes);
257
138
  }
258
139
  }
259
140
  }
260
141
  }
261
-
262
- /**
263
- * Updates a navigation span with the correct route name after lazy routes have been loaded.
264
- */
265
- function updateNavigationSpan(
266
- activeRootSpan,
267
- location,
268
- allRoutes,
269
- forceUpdate = false,
270
- matchRoutes,
271
- ) {
142
+ function updateNavigationSpan(activeRootSpan, location, allRoutes2, forceUpdate = false, matchRoutes) {
272
143
  const spanJson = spanToJSON(activeRootSpan);
273
144
  const currentName = spanJson.description;
274
-
275
- const hasBeenNamed = (activeRootSpan )?.__sentry_navigation_name_set__;
145
+ const hasBeenNamed = activeRootSpan?.__sentry_navigation_name_set__;
276
146
  const currentNameHasWildcard = currentName && transactionNameHasWildcard(currentName);
277
147
  const shouldUpdate = !hasBeenNamed || forceUpdate || currentNameHasWildcard;
278
-
279
148
  if (shouldUpdate && !spanJson.timestamp) {
280
- const currentBranches = matchRoutes(allRoutes, location);
149
+ const currentBranches = matchRoutes(allRoutes2, location);
281
150
  const [name, source] = resolveRouteNameAndSource(
282
151
  location,
283
- allRoutes,
284
- allRoutes,
285
- (currentBranches ) || [],
152
+ allRoutes2,
153
+ allRoutes2,
154
+ currentBranches || [],
286
155
  _basename,
287
156
  _lazyRouteManifest,
288
- _enableAsyncRouteHandlers,
157
+ _enableAsyncRouteHandlers
289
158
  );
290
-
291
159
  const currentSource = spanJson.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
292
- const isImprovement =
293
- name &&
294
- (!currentName || // No current name - always set
295
- (!hasBeenNamed && (currentSource !== 'route' || source === 'route')) || // Not finalized - allow unless downgrading route→url
296
- (currentSource !== 'route' && source === 'route') || // URL → route upgrade
297
- (currentSource === 'route' && source === 'route' && currentNameHasWildcard)); // Route → better route (only if current has wildcard)
160
+ const isImprovement = name && (!currentName || // No current name - always set
161
+ !hasBeenNamed && (currentSource !== "route" || source === "route") || // Not finalized - allow unless downgrading route→url
162
+ currentSource !== "route" && source === "route" || // URL route upgrade
163
+ currentSource === "route" && source === "route" && currentNameHasWildcard);
298
164
  if (isImprovement) {
299
165
  activeRootSpan.updateName(name);
300
166
  activeRootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
301
-
302
- // Only mark as finalized for non-wildcard route names (allows URL→route upgrades).
303
- if (!transactionNameHasWildcard(name) && source === 'route') {
167
+ if (!transactionNameHasWildcard(name) && source === "route") {
304
168
  addNonEnumerableProperty(
305
- activeRootSpan ,
306
- '__sentry_navigation_name_set__',
307
- true,
169
+ activeRootSpan,
170
+ "__sentry_navigation_name_set__",
171
+ true
308
172
  );
309
173
  }
310
174
  }
311
175
  }
312
176
  }
313
-
314
- function setupRouterSubscription(
315
- router,
316
- routes,
317
- version,
318
- basename,
319
- activeRootSpan,
320
- ) {
177
+ function setupRouterSubscription(router, routes, version, basename, activeRootSpan) {
321
178
  let isInitialPageloadComplete = false;
322
- let hasSeenPageloadSpan = !!activeRootSpan && spanToJSON(activeRootSpan).op === 'pageload';
179
+ let hasSeenPageloadSpan = !!activeRootSpan && spanToJSON(activeRootSpan).op === "pageload";
323
180
  let hasSeenPopAfterPageload = false;
324
181
  let scheduledNavigationHandler = null;
325
182
  let lastHandledPathname = null;
326
-
327
183
  router.subscribe((state) => {
328
184
  if (!isInitialPageloadComplete) {
329
185
  const currentRootSpan = getActiveRootSpan();
330
- const isCurrentlyInPageload = currentRootSpan && spanToJSON(currentRootSpan).op === 'pageload';
331
-
186
+ const isCurrentlyInPageload = currentRootSpan && spanToJSON(currentRootSpan).op === "pageload";
332
187
  if (isCurrentlyInPageload) {
333
188
  hasSeenPageloadSpan = true;
334
189
  } else if (hasSeenPageloadSpan) {
335
- if (state.historyAction === 'POP' && !hasSeenPopAfterPageload) {
190
+ if (state.historyAction === "POP" && !hasSeenPopAfterPageload) {
336
191
  hasSeenPopAfterPageload = true;
337
192
  } else {
338
193
  isInitialPageloadComplete = true;
339
194
  }
340
195
  }
341
196
  }
342
-
343
- const shouldHandleNavigation =
344
- state.historyAction === 'PUSH' || (state.historyAction === 'POP' && isInitialPageloadComplete);
345
-
197
+ const shouldHandleNavigation = state.historyAction === "PUSH" || state.historyAction === "POP" && isInitialPageloadComplete;
346
198
  if (shouldHandleNavigation) {
347
- // Include search and hash to allow query/hash-only navigations
348
- // Use computeLocationKey() to ensure undefined/null values are normalized to empty strings
349
199
  const currentLocationKey = computeLocationKey(state.location);
350
200
  const navigationHandler = () => {
351
- // Prevent multiple calls for the same location within the same navigation cycle
352
201
  if (lastHandledPathname === currentLocationKey) {
353
202
  return;
354
203
  }
@@ -360,207 +209,119 @@ function setupRouterSubscription(
360
209
  navigationType: state.historyAction,
361
210
  version,
362
211
  basename,
363
- allRoutes: Array.from(allRoutes),
212
+ allRoutes: Array.from(allRoutes)
364
213
  });
365
214
  };
366
-
367
- if (state.navigation.state !== 'idle') {
368
- // Navigation in progress - reset if location changed
215
+ if (state.navigation.state !== "idle") {
369
216
  if (lastHandledPathname !== currentLocationKey) {
370
217
  lastHandledPathname = null;
371
218
  }
372
- // Cancel any previously scheduled handler to avoid duplicates
373
219
  if (scheduledNavigationHandler !== null) {
374
220
  cancelScheduledCallback(scheduledNavigationHandler);
375
221
  }
376
222
  scheduledNavigationHandler = scheduleCallback(navigationHandler);
377
223
  } else {
378
- // Navigation completed - cancel scheduled handler if any, then call immediately
379
224
  if (scheduledNavigationHandler !== null) {
380
225
  cancelScheduledCallback(scheduledNavigationHandler);
381
226
  scheduledNavigationHandler = null;
382
227
  }
383
228
  navigationHandler();
384
- // Don't reset - next navigation cycle resets to prevent duplicates within same cycle.
385
229
  }
386
230
  }
387
231
  });
388
232
  }
389
-
390
- /**
391
- * Creates a wrapCreateBrowserRouter function that can be used with all React Router v6 compatible versions.
392
- */
393
- function createV6CompatibleWrapCreateBrowserRouter
394
-
395
- (
396
- createRouterFunction,
397
- version,
398
- ) {
233
+ function createV6CompatibleWrapCreateBrowserRouter(createRouterFunction, version) {
399
234
  if (!_useEffect || !_useLocation || !_useNavigationType || !_matchRoutes) {
400
- DEBUG_BUILD &&
401
- debug.warn(
402
- `reactRouterV${version}Instrumentation was unable to wrap the \`createRouter\` function because of one or more missing parameters.`,
403
- );
404
-
235
+ DEBUG_BUILD && debug.warn(
236
+ `reactRouterV${version}Instrumentation was unable to wrap the \`createRouter\` function because of one or more missing parameters.`
237
+ );
405
238
  return createRouterFunction;
406
239
  }
407
-
408
- return function (routes, opts) {
240
+ return function(routes, opts) {
409
241
  addRoutesToAllRoutes(routes);
410
-
411
242
  if (_enableAsyncRouteHandlers) {
412
243
  for (const route of routes) {
413
244
  checkRouteForAsyncHandler(route, processResolvedRoutes);
414
245
  }
415
246
  }
416
-
417
- // Capture the active span BEFORE creating the router.
418
- // This is important because the span might end (due to idle timeout) before
419
- // patchRoutesOnNavigation is called by React Router.
420
247
  const activeRootSpan = getActiveRootSpan();
421
-
422
- // If patchRoutesOnNavigation is provided and we have an active span,
423
- // mark the span as having potential lazy routes and create a deferred promise.
424
- const hasPatchRoutesOnNavigation =
425
- opts && 'patchRoutesOnNavigation' in opts && typeof opts.patchRoutesOnNavigation === 'function';
248
+ const hasPatchRoutesOnNavigation = opts && "patchRoutesOnNavigation" in opts && typeof opts.patchRoutesOnNavigation === "function";
426
249
  if (hasPatchRoutesOnNavigation && activeRootSpan) {
427
- // Mark the span as potentially having lazy routes
428
250
  addNonEnumerableProperty(
429
- activeRootSpan ,
430
- '__sentry_may_have_lazy_routes__',
431
- true,
251
+ activeRootSpan,
252
+ "__sentry_may_have_lazy_routes__",
253
+ true
432
254
  );
433
255
  createDeferredLazyRoutePromise(activeRootSpan);
434
256
  }
435
-
436
- // Pass the captured span to wrapPatchRoutesOnNavigation so it uses the same span
437
- // even if the span has ended by the time patchRoutesOnNavigation is called.
438
257
  const wrappedOpts = wrapPatchRoutesOnNavigation(opts, false, activeRootSpan);
439
258
  const router = createRouterFunction(routes, wrappedOpts);
440
259
  const basename = opts?.basename;
441
-
442
- if (router.state.historyAction === 'POP' && activeRootSpan) {
260
+ if (router.state.historyAction === "POP" && activeRootSpan) {
443
261
  updatePageloadTransaction({
444
262
  activeRootSpan,
445
263
  location: router.state.location,
446
264
  routes,
447
265
  basename,
448
- allRoutes: Array.from(allRoutes),
266
+ allRoutes: Array.from(allRoutes)
449
267
  });
450
268
  }
451
-
452
- // Store basename for use in updateNavigationSpan
453
- _basename = basename || '';
454
-
269
+ _basename = basename || "";
455
270
  setupRouterSubscription(router, routes, version, basename, activeRootSpan);
456
-
457
271
  return router;
458
272
  };
459
273
  }
460
-
461
- /**
462
- * Creates a wrapCreateMemoryRouter function that can be used with all React Router v6 compatible versions.
463
- */
464
- function createV6CompatibleWrapCreateMemoryRouter
465
-
466
- (
467
- createRouterFunction,
468
- version,
469
- ) {
274
+ function createV6CompatibleWrapCreateMemoryRouter(createRouterFunction, version) {
470
275
  if (!_useEffect || !_useLocation || !_useNavigationType || !_matchRoutes) {
471
- DEBUG_BUILD &&
472
- debug.warn(
473
- `reactRouterV${version}Instrumentation was unable to wrap the \`createMemoryRouter\` function because of one or more missing parameters.`,
474
- );
475
-
276
+ DEBUG_BUILD && debug.warn(
277
+ `reactRouterV${version}Instrumentation was unable to wrap the \`createMemoryRouter\` function because of one or more missing parameters.`
278
+ );
476
279
  return createRouterFunction;
477
280
  }
478
-
479
- return function (
480
- routes,
481
- opts
482
-
483
- ,
484
- ) {
281
+ return function(routes, opts) {
485
282
  addRoutesToAllRoutes(routes);
486
-
487
283
  if (_enableAsyncRouteHandlers) {
488
284
  for (const route of routes) {
489
285
  checkRouteForAsyncHandler(route, processResolvedRoutes);
490
286
  }
491
287
  }
492
-
493
- // Capture the active span BEFORE creating the router (same as browser router)
494
288
  const memoryActiveRootSpanEarly = getActiveRootSpan();
495
-
496
- // If patchRoutesOnNavigation is provided and we have an active span,
497
- // mark the span as having potential lazy routes and create a deferred promise.
498
- const hasPatchRoutesOnNavigation =
499
- opts && 'patchRoutesOnNavigation' in opts && typeof opts.patchRoutesOnNavigation === 'function';
289
+ const hasPatchRoutesOnNavigation = opts && "patchRoutesOnNavigation" in opts && typeof opts.patchRoutesOnNavigation === "function";
500
290
  if (hasPatchRoutesOnNavigation && memoryActiveRootSpanEarly) {
501
291
  addNonEnumerableProperty(
502
- memoryActiveRootSpanEarly ,
503
- '__sentry_may_have_lazy_routes__',
504
- true,
292
+ memoryActiveRootSpanEarly,
293
+ "__sentry_may_have_lazy_routes__",
294
+ true
505
295
  );
506
296
  createDeferredLazyRoutePromise(memoryActiveRootSpanEarly);
507
297
  }
508
-
509
298
  const wrappedOpts = wrapPatchRoutesOnNavigation(opts, true, memoryActiveRootSpanEarly);
510
-
511
299
  const router = createRouterFunction(routes, wrappedOpts);
512
300
  const basename = opts?.basename;
513
-
514
- let initialEntry = undefined;
515
-
301
+ let initialEntry = void 0;
516
302
  const initialEntries = opts?.initialEntries;
517
303
  const initialIndex = opts?.initialIndex;
518
-
519
304
  const hasOnlyOneInitialEntry = initialEntries?.length === 1;
520
- const hasIndexedEntry = initialIndex !== undefined && initialEntries?.[initialIndex];
521
-
522
- initialEntry = hasOnlyOneInitialEntry
523
- ? initialEntries[0]
524
- : hasIndexedEntry
525
- ? initialEntries[initialIndex]
526
- : undefined;
527
-
528
- const location = initialEntry
529
- ? typeof initialEntry === 'string'
530
- ? { pathname: initialEntry }
531
- : initialEntry
532
- : router.state.location;
533
-
305
+ const hasIndexedEntry = initialIndex !== void 0 && initialEntries?.[initialIndex];
306
+ initialEntry = hasOnlyOneInitialEntry ? initialEntries[0] : hasIndexedEntry ? initialEntries[initialIndex] : void 0;
307
+ const location = initialEntry ? typeof initialEntry === "string" ? { pathname: initialEntry } : initialEntry : router.state.location;
534
308
  const memoryActiveRootSpan = getActiveRootSpan();
535
-
536
- if (router.state.historyAction === 'POP' && memoryActiveRootSpan) {
309
+ if (router.state.historyAction === "POP" && memoryActiveRootSpan) {
537
310
  updatePageloadTransaction({
538
311
  activeRootSpan: memoryActiveRootSpan,
539
312
  location,
540
313
  routes,
541
314
  basename,
542
- allRoutes: Array.from(allRoutes),
315
+ allRoutes: Array.from(allRoutes)
543
316
  });
544
317
  }
545
-
546
- // Store basename for use in updateNavigationSpan
547
- _basename = basename || '';
548
-
318
+ _basename = basename || "";
549
319
  setupRouterSubscription(router, routes, version, basename, memoryActiveRootSpan);
550
-
551
320
  return router;
552
321
  };
553
322
  }
554
-
555
- /**
556
- * Creates a browser tracing integration that can be used with all React Router v6 compatible versions.
557
- */
558
- function createReactRouterV6CompatibleTracingIntegration(
559
- options,
560
- version,
561
- ) {
323
+ function createReactRouterV6CompatibleTracingIntegration(options, version) {
562
324
  const integration = browserTracingIntegration({ ...options, instrumentPageLoad: false, instrumentNavigation: false });
563
-
564
325
  const {
565
326
  useEffect,
566
327
  useLocation,
@@ -572,44 +333,36 @@ function createReactRouterV6CompatibleTracingIntegration(
572
333
  instrumentPageLoad = true,
573
334
  instrumentNavigation = true,
574
335
  lazyRouteTimeout,
575
- lazyRouteManifest,
336
+ lazyRouteManifest
576
337
  } = options;
577
-
578
338
  return {
579
339
  ...integration,
580
340
  setup(client) {
581
341
  integration.setup(client);
582
-
583
- const finalTimeout = options.finalTimeout ?? 30000;
584
- const defaultMaxWait = (options.idleTimeout ?? 1000) * 3;
342
+ const finalTimeout = options.finalTimeout ?? 3e4;
343
+ const defaultMaxWait = (options.idleTimeout ?? 1e3) * 3;
585
344
  const configuredMaxWait = lazyRouteTimeout ?? defaultMaxWait;
586
-
587
- // Cap Infinity at finalTimeout to prevent indefinite hangs
588
345
  if (configuredMaxWait === Infinity) {
589
346
  _lazyRouteTimeout = finalTimeout;
590
- DEBUG_BUILD &&
591
- debug.log(
592
- '[React Router] lazyRouteTimeout set to Infinity, capping at finalTimeout:',
593
- finalTimeout,
594
- 'ms to prevent indefinite hangs',
595
- );
347
+ DEBUG_BUILD && debug.log(
348
+ "[React Router] lazyRouteTimeout set to Infinity, capping at finalTimeout:",
349
+ finalTimeout,
350
+ "ms to prevent indefinite hangs"
351
+ );
596
352
  } else if (Number.isNaN(configuredMaxWait)) {
597
- DEBUG_BUILD &&
598
- debug.warn('[React Router] lazyRouteTimeout must be a number, falling back to default:', defaultMaxWait);
353
+ DEBUG_BUILD && debug.warn("[React Router] lazyRouteTimeout must be a number, falling back to default:", defaultMaxWait);
599
354
  _lazyRouteTimeout = defaultMaxWait;
600
355
  } else if (configuredMaxWait < 0) {
601
- DEBUG_BUILD &&
602
- debug.warn(
603
- '[React Router] lazyRouteTimeout must be non-negative or Infinity, got:',
604
- configuredMaxWait,
605
- 'falling back to:',
606
- defaultMaxWait,
607
- );
356
+ DEBUG_BUILD && debug.warn(
357
+ "[React Router] lazyRouteTimeout must be non-negative or Infinity, got:",
358
+ configuredMaxWait,
359
+ "falling back to:",
360
+ defaultMaxWait
361
+ );
608
362
  _lazyRouteTimeout = defaultMaxWait;
609
363
  } else {
610
364
  _lazyRouteTimeout = configuredMaxWait;
611
365
  }
612
-
613
366
  _useEffect = useEffect;
614
367
  _useLocation = useLocation;
615
368
  _useNavigationType = useNavigationType;
@@ -617,278 +370,188 @@ function createReactRouterV6CompatibleTracingIntegration(
617
370
  _createRoutesFromChildren = createRoutesFromChildren;
618
371
  _enableAsyncRouteHandlers = enableAsyncRouteHandlers;
619
372
  _lazyRouteManifest = lazyRouteManifest;
620
-
621
- // Initialize the router utils with the required dependencies
622
373
  initializeRouterUtils(matchRoutes, stripBasename || false);
623
374
  },
624
375
  afterAllSetup(client) {
625
376
  integration.afterAllSetup(client);
626
-
627
377
  const initPathName = WINDOW.location?.pathname;
628
378
  if (instrumentPageLoad && initPathName) {
629
379
  startBrowserTracingPageLoadSpan(client, {
630
380
  name: initPathName,
631
381
  attributes: {
632
- [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
633
- [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
634
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.pageload.react.reactrouter_v${version}`,
635
- },
382
+ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "url",
383
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "pageload",
384
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.pageload.react.reactrouter_v${version}`
385
+ }
636
386
  });
637
387
  }
638
-
639
388
  if (instrumentNavigation) {
640
389
  CLIENTS_WITH_INSTRUMENT_NAVIGATION.add(client);
641
390
  }
642
- },
391
+ }
643
392
  };
644
393
  }
645
-
646
394
  function createV6CompatibleWrapUseRoutes(origUseRoutes, version) {
647
395
  if (!_useEffect || !_useLocation || !_useNavigationType || !_matchRoutes) {
648
- DEBUG_BUILD &&
649
- debug.warn(
650
- 'reactRouterV6Instrumentation was unable to wrap `useRoutes` because of one or more missing parameters.',
651
- );
652
-
396
+ DEBUG_BUILD && debug.warn(
397
+ "reactRouterV6Instrumentation was unable to wrap `useRoutes` because of one or more missing parameters."
398
+ );
653
399
  return origUseRoutes;
654
400
  }
655
-
656
- const SentryRoutes
657
-
658
- = (props) => {
401
+ const SentryRoutes = (props) => {
659
402
  const isMountRenderPass = React.useRef(true);
660
403
  const { routes, locationArg } = props;
661
-
662
404
  const Routes = origUseRoutes(routes, locationArg);
663
-
664
405
  const location = _useLocation();
665
406
  const navigationType = _useNavigationType();
666
-
667
- // A value with stable identity to either pick `locationArg` if available or `location` if not
668
- const stableLocationParam =
669
- typeof locationArg === 'string' || locationArg?.pathname ? (locationArg ) : location;
670
-
407
+ const stableLocationParam = typeof locationArg === "string" || locationArg?.pathname ? locationArg : location;
671
408
  _useEffect(() => {
672
- const normalizedLocation =
673
- typeof stableLocationParam === 'string' ? { pathname: stableLocationParam } : stableLocationParam;
674
-
409
+ const normalizedLocation = typeof stableLocationParam === "string" ? { pathname: stableLocationParam } : stableLocationParam;
675
410
  if (isMountRenderPass.current) {
676
411
  addRoutesToAllRoutes(routes);
677
-
678
412
  updatePageloadTransaction({
679
413
  activeRootSpan: getActiveRootSpan(),
680
414
  location: normalizedLocation,
681
415
  routes,
682
- allRoutes: Array.from(allRoutes),
416
+ allRoutes: Array.from(allRoutes)
683
417
  });
684
418
  isMountRenderPass.current = false;
685
419
  } else {
686
- // Note: Component-based routes don't support lazy route tracking via lazyRouteTimeout
687
- // because React.lazy() loads happen at the component level, not the router level.
688
- // Use createBrowserRouter with patchRoutesOnNavigation for lazy route tracking.
689
420
  handleNavigation({
690
421
  location: normalizedLocation,
691
422
  routes,
692
423
  navigationType,
693
424
  version,
694
- allRoutes: Array.from(allRoutes),
425
+ allRoutes: Array.from(allRoutes)
695
426
  });
696
427
  }
697
428
  }, [navigationType, stableLocationParam]);
698
-
699
429
  return Routes;
700
430
  };
701
-
702
- // eslint-disable-next-line react/display-name
703
431
  return (routes, locationArg) => {
704
- return React.createElement(SentryRoutes, { routes: routes, locationArg: locationArg,} );
432
+ return /* @__PURE__ */ React.createElement(SentryRoutes, { routes, locationArg });
705
433
  };
706
434
  }
707
- function wrapPatchRoutesOnNavigation(
708
- opts,
709
- isMemoryRouter = false,
710
- capturedSpan,
711
- ) {
712
- if (!opts || !('patchRoutesOnNavigation' in opts) || typeof opts.patchRoutesOnNavigation !== 'function') {
435
+ function wrapPatchRoutesOnNavigation(opts, isMemoryRouter = false, capturedSpan) {
436
+ if (!opts || !("patchRoutesOnNavigation" in opts) || typeof opts.patchRoutesOnNavigation !== "function") {
713
437
  return opts || {};
714
438
  }
715
-
716
439
  const originalPatchRoutes = opts.patchRoutesOnNavigation;
717
440
  return {
718
441
  ...opts,
719
442
  patchRoutesOnNavigation: async (args) => {
720
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
721
- const targetPath = (args )?.path;
722
-
723
- // Use current active span if available, otherwise fall back to captured span (from router creation time).
724
- // This ensures navigation spans use their own span (not the stale pageload span), while still
725
- // supporting pageload spans that may have ended before patchRoutesOnNavigation is called.
443
+ const targetPath = args?.path;
726
444
  const activeRootSpan = getActiveRootSpan() ?? capturedSpan;
727
-
728
445
  if (!isMemoryRouter) {
729
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
730
- const originalPatch = (args )?.patch;
731
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
732
- const matches = (args )?.matches ;
446
+ const originalPatch = args?.patch;
447
+ const matches = args?.matches;
733
448
  if (originalPatch) {
734
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
735
- (args ).patch = (routeId, children) => {
449
+ args.patch = (routeId, children) => {
736
450
  addRoutesToAllRoutes(children);
737
-
738
- // Find the parent route from matches and attach children to it in allRoutes.
739
- // React Router's patch attaches children to its internal route copies, but we need
740
- // to update the route objects in our allRoutes Set for proper route matching.
741
451
  if (matches && matches.length > 0) {
742
452
  const leafMatch = matches[matches.length - 1];
743
453
  const leafRoute = leafMatch?.route;
744
454
  if (leafRoute) {
745
- // Find the matching route in allRoutes by id, reference, or path
746
- const matchingRoute = Array.from(allRoutes).find(route => {
747
- const idMatches = route.id !== undefined && route.id === routeId;
455
+ const matchingRoute = Array.from(allRoutes).find((route) => {
456
+ const idMatches = route.id !== void 0 && route.id === routeId;
748
457
  const referenceMatches = route === leafRoute;
749
- const pathMatches =
750
- route.path !== undefined && leafRoute.path !== undefined && route.path === leafRoute.path;
751
-
458
+ const pathMatches = route.path !== void 0 && leafRoute.path !== void 0 && route.path === leafRoute.path;
752
459
  return idMatches || referenceMatches || pathMatches;
753
460
  });
754
-
755
461
  if (matchingRoute) {
756
462
  addResolvedRoutesToParent(children, matchingRoute);
757
463
  }
758
464
  }
759
465
  }
760
-
761
- // Use the captured activeRootSpan instead of getActiveRootSpan() to avoid race conditions
762
- // where user navigates away during lazy route loading and we'd update the wrong span
763
- const spanJson = activeRootSpan ? spanToJSON(activeRootSpan) : undefined;
764
- // Only update if we have a valid targetPath (patchRoutesOnNavigation can be called without path),
765
- // the captured span exists, hasn't ended, and is a navigation span
766
- if (
767
- targetPath &&
768
- activeRootSpan &&
769
- spanJson &&
770
- !spanJson.timestamp && // Span hasn't ended yet
771
- spanJson.op === 'navigation'
772
- ) {
466
+ const spanJson = activeRootSpan ? spanToJSON(activeRootSpan) : void 0;
467
+ if (targetPath && activeRootSpan && spanJson && !spanJson.timestamp && // Span hasn't ended yet
468
+ spanJson.op === "navigation") {
773
469
  updateNavigationSpan(
774
470
  activeRootSpan,
775
- { pathname: targetPath, search: '', hash: '', state: null, key: 'default' },
471
+ { pathname: targetPath, search: "", hash: "", state: null, key: "default" },
776
472
  Array.from(allRoutes),
777
473
  true,
778
- _matchRoutes,
474
+ _matchRoutes
779
475
  );
780
476
  }
781
477
  return originalPatch(routeId, children);
782
478
  };
783
479
  }
784
480
  }
785
-
786
481
  const lazyLoadPromise = (async () => {
787
- // Set context so async handlers can access correct targetPath and span
788
482
  const contextToken = setNavigationContext(targetPath, activeRootSpan);
789
483
  let result;
790
484
  try {
791
485
  result = await originalPatchRoutes(args);
792
486
  } finally {
793
487
  clearNavigationContext(contextToken);
794
- // Resolve the deferred promise now that patchRoutesOnNavigation has completed.
795
- // This ensures patchedEnd has waited long enough for the lazy routes to load.
796
488
  if (activeRootSpan) {
797
489
  resolveDeferredLazyRoutePromise(activeRootSpan);
798
490
  }
799
491
  }
800
-
801
- // Use the captured activeRootSpan instead of getActiveRootSpan() to avoid race conditions
802
- // where user navigates away during lazy route loading and we'd update the wrong span
803
- const spanJson = activeRootSpan ? spanToJSON(activeRootSpan) : undefined;
804
- if (
805
- activeRootSpan &&
806
- spanJson &&
807
- !spanJson.timestamp && // Span hasn't ended yet
808
- spanJson.op === 'navigation'
809
- ) {
810
- // Use targetPath consistently - don't fall back to WINDOW.location which may have changed
811
- // if the user navigated away during async loading
492
+ const spanJson = activeRootSpan ? spanToJSON(activeRootSpan) : void 0;
493
+ if (activeRootSpan && spanJson && !spanJson.timestamp && // Span hasn't ended yet
494
+ spanJson.op === "navigation") {
812
495
  const pathname = targetPath;
813
-
814
496
  if (pathname) {
815
497
  updateNavigationSpan(
816
498
  activeRootSpan,
817
- { pathname, search: '', hash: '', state: null, key: 'default' },
499
+ { pathname, search: "", hash: "", state: null, key: "default" },
818
500
  Array.from(allRoutes),
819
501
  false,
820
- _matchRoutes,
502
+ _matchRoutes
821
503
  );
822
504
  }
823
505
  }
824
-
825
506
  return result;
826
507
  })();
827
-
828
508
  if (activeRootSpan) {
829
509
  trackLazyRouteLoad(activeRootSpan, lazyLoadPromise);
830
510
  }
831
-
832
511
  return lazyLoadPromise;
833
- },
512
+ }
834
513
  };
835
514
  }
836
-
837
- // eslint-disable-next-line complexity
838
- function handleNavigation(opts
839
-
840
- ) {
841
- const { location, routes, navigationType, version, matches, basename, allRoutes } = opts;
842
- const branches = Array.isArray(matches) ? matches : _matchRoutes(allRoutes || routes, location, basename);
843
-
515
+ function handleNavigation(opts) {
516
+ const { location, routes, navigationType, version, matches, basename, allRoutes: allRoutes2 } = opts;
517
+ const branches = Array.isArray(matches) ? matches : _matchRoutes(allRoutes2 || routes, location, basename);
844
518
  const client = getClient();
845
519
  if (!client || !CLIENTS_WITH_INSTRUMENT_NAVIGATION.has(client)) {
846
520
  return;
847
521
  }
848
-
849
522
  const activeRootSpan = getActiveRootSpan();
850
- if (activeRootSpan && spanToJSON(activeRootSpan).op === 'pageload' && navigationType === 'POP') {
523
+ if (activeRootSpan && spanToJSON(activeRootSpan).op === "pageload" && navigationType === "POP") {
851
524
  return;
852
525
  }
853
-
854
- if ((navigationType === 'PUSH' || navigationType === 'POP') && branches) {
526
+ if ((navigationType === "PUSH" || navigationType === "POP") && branches) {
855
527
  const [name, source] = resolveRouteNameAndSource(
856
528
  location,
857
- allRoutes || routes,
858
- allRoutes || routes,
859
- branches ,
529
+ allRoutes2 || routes,
530
+ allRoutes2 || routes,
531
+ branches,
860
532
  basename,
861
533
  _lazyRouteManifest,
862
- _enableAsyncRouteHandlers,
534
+ _enableAsyncRouteHandlers
863
535
  );
864
-
865
536
  const locationKey = computeLocationKey(location);
866
537
  const trackedNav = activeNavigationSpans.get(client);
867
-
868
- // Determine if this navigation should be skipped as a duplicate
869
- const trackedSpanHasEnded =
870
- trackedNav && !trackedNav.isPlaceholder ? !!spanToJSON(trackedNav.span).timestamp : false;
538
+ const trackedSpanHasEnded = trackedNav && !trackedNav.isPlaceholder ? !!spanToJSON(trackedNav.span).timestamp : false;
871
539
  const { skip, shouldUpdate } = shouldSkipNavigation(trackedNav, locationKey, name, trackedSpanHasEnded);
872
-
873
540
  if (skip) {
874
541
  if (shouldUpdate && trackedNav) {
875
542
  const oldName = trackedNav.routeName;
876
-
877
543
  if (trackedNav.isPlaceholder) {
878
- // Update placeholder's route name - the real span will be created with this name
879
544
  trackedNav.routeName = name;
880
- DEBUG_BUILD &&
881
- debug.log(
882
- `[Tracing] Updated placeholder navigation name from "${oldName}" to "${name}" (will apply to real span)`,
883
- );
545
+ DEBUG_BUILD && debug.log(
546
+ `[Tracing] Updated placeholder navigation name from "${oldName}" to "${name}" (will apply to real span)`
547
+ );
884
548
  } else {
885
- // Update existing real span from wildcard to parameterized route name
886
549
  trackedNav.span.updateName(name);
887
- trackedNav.span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source );
550
+ trackedNav.span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
888
551
  addNonEnumerableProperty(
889
- trackedNav.span ,
890
- '__sentry_navigation_name_set__',
891
- true,
552
+ trackedNav.span,
553
+ "__sentry_navigation_name_set__",
554
+ true
892
555
  );
893
556
  trackedNav.routeName = name;
894
557
  DEBUG_BUILD && debug.log(`[Tracing] Updated navigation span name from "${oldName}" to "${name}"`);
@@ -898,191 +561,124 @@ function handleNavigation(opts
898
561
  }
899
562
  return;
900
563
  }
901
-
902
- // Create new navigation span (first navigation or legitimate new navigation)
903
- // Reserve the spot in the map first to prevent race conditions
904
- // Mark as placeholder to prevent concurrent handleNavigation calls from creating duplicates
905
- const placeholderSpan = { end: () => {} } ;
564
+ const placeholderSpan = { end: () => {
565
+ } };
906
566
  const placeholderEntry = {
907
567
  span: placeholderSpan,
908
568
  routeName: name,
909
569
  pathname: location.pathname,
910
570
  locationKey,
911
- isPlaceholder: true ,
571
+ isPlaceholder: true
912
572
  };
913
573
  activeNavigationSpans.set(client, placeholderEntry);
914
-
915
574
  let navigationSpan;
916
575
  try {
917
576
  navigationSpan = startBrowserTracingNavigationSpan(client, {
918
- name: placeholderEntry.routeName, // Use placeholder's routeName in case it was updated
577
+ name: placeholderEntry.routeName,
578
+ // Use placeholder's routeName in case it was updated
919
579
  attributes: {
920
580
  [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
921
- [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
922
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter_v${version}`,
923
- },
581
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
582
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter_v${version}`
583
+ }
924
584
  });
925
585
  } catch (e) {
926
- // If span creation fails, remove the placeholder so we don't block future navigations
927
586
  activeNavigationSpans.delete(client);
928
587
  throw e;
929
588
  }
930
-
931
589
  if (navigationSpan) {
932
- // Update the map with the real span (isPlaceholder omitted, defaults to false)
933
590
  activeNavigationSpans.set(client, {
934
591
  span: navigationSpan,
935
- routeName: placeholderEntry.routeName, // Use the (potentially updated) placeholder routeName
592
+ routeName: placeholderEntry.routeName,
593
+ // Use the (potentially updated) placeholder routeName
936
594
  pathname: location.pathname,
937
- locationKey,
595
+ locationKey
938
596
  });
939
- patchSpanEnd(navigationSpan, location, routes, basename, 'navigation');
597
+ patchSpanEnd(navigationSpan, location, routes, basename, "navigation");
940
598
  } else {
941
- // If no span was created, remove the placeholder
942
599
  activeNavigationSpans.delete(client);
943
600
  }
944
601
  }
945
602
  }
946
-
947
- /* Only exported for testing purposes */
948
603
  function addRoutesToAllRoutes(routes) {
949
- routes.forEach(route => {
604
+ routes.forEach((route) => {
950
605
  const extractedChildRoutes = getChildRoutesRecursively(route);
951
-
952
- extractedChildRoutes.forEach(r => {
606
+ extractedChildRoutes.forEach((r) => {
953
607
  allRoutes.add(r);
954
608
  });
955
609
  });
956
610
  }
957
-
958
- function getChildRoutesRecursively(route, allRoutes = new Set()) {
959
- if (!allRoutes.has(route)) {
960
- allRoutes.add(route);
961
-
611
+ function getChildRoutesRecursively(route, allRoutes2 = /* @__PURE__ */ new Set()) {
612
+ if (!allRoutes2.has(route)) {
613
+ allRoutes2.add(route);
962
614
  if (route.children && !route.index) {
963
- route.children.forEach(child => {
964
- const childRoutes = getChildRoutesRecursively(child, allRoutes);
965
-
966
- childRoutes.forEach(r => {
967
- allRoutes.add(r);
615
+ route.children.forEach((child) => {
616
+ const childRoutes = getChildRoutesRecursively(child, allRoutes2);
617
+ childRoutes.forEach((r) => {
618
+ allRoutes2.add(r);
968
619
  });
969
620
  });
970
621
  }
971
622
  }
972
-
973
- return allRoutes;
623
+ return allRoutes2;
974
624
  }
975
-
976
625
  function updatePageloadTransaction({
977
626
  activeRootSpan,
978
627
  location,
979
628
  routes,
980
629
  matches,
981
630
  basename,
982
- allRoutes,
983
- }
984
-
985
- ) {
986
- const branches = Array.isArray(matches)
987
- ? matches
988
- : (_matchRoutes(allRoutes || routes, location, basename) );
989
-
631
+ allRoutes: allRoutes2
632
+ }) {
633
+ const branches = Array.isArray(matches) ? matches : _matchRoutes(allRoutes2 || routes, location, basename);
990
634
  if (branches) {
991
635
  const [name, source] = resolveRouteNameAndSource(
992
636
  location,
993
- allRoutes || routes,
994
- allRoutes || routes,
637
+ allRoutes2 || routes,
638
+ allRoutes2 || routes,
995
639
  branches,
996
640
  basename,
997
641
  _lazyRouteManifest,
998
- _enableAsyncRouteHandlers,
642
+ _enableAsyncRouteHandlers
999
643
  );
1000
-
1001
- getCurrentScope().setTransactionName(name || '/');
1002
-
644
+ getCurrentScope().setTransactionName(name || "/");
1003
645
  if (activeRootSpan) {
1004
646
  activeRootSpan.updateName(name);
1005
647
  activeRootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
1006
-
1007
- // Patch span.end() to ensure we update the name one last time before the span is sent
1008
- patchSpanEnd(activeRootSpan, location, routes, basename, 'pageload');
648
+ patchSpanEnd(activeRootSpan, location, routes, basename, "pageload");
1009
649
  }
1010
650
  } else if (activeRootSpan) {
1011
- // Even if branches is null (can happen when lazy routes haven't loaded yet),
1012
- // we still need to patch span.end() so that when lazy routes load and the span ends,
1013
- // we can update the transaction name correctly.
1014
- patchSpanEnd(activeRootSpan, location, routes, basename, 'pageload');
651
+ patchSpanEnd(activeRootSpan, location, routes, basename, "pageload");
1015
652
  }
1016
653
  }
1017
-
1018
- /**
1019
- * Determines if a span name should be updated during wildcard route resolution.
1020
- *
1021
- * Update conditions (in priority order):
1022
- * 1. No current name + allowNoCurrentName: true → always update (pageload spans)
1023
- * 2. Current name has wildcard + new is route without wildcard → upgrade (e.g., "/users/*" → "/users/:id")
1024
- * 3. Current source is not 'route' + new source is 'route' → upgrade (e.g., URL → parameterized route)
1025
- *
1026
- * @param currentName - The current span name (may be undefined)
1027
- * @param currentSource - The current span source ('route', 'url', or undefined)
1028
- * @param newName - The proposed new span name
1029
- * @param newSource - The proposed new span source
1030
- * @param allowNoCurrentName - If true, allow updates when there's no current name (for pageload spans)
1031
- * @returns true if the span name should be updated
1032
- */
1033
- function shouldUpdateWildcardSpanName(
1034
- currentName,
1035
- currentSource,
1036
- newName,
1037
- newSource,
1038
- allowNoCurrentName = false,
1039
- ) {
654
+ function shouldUpdateWildcardSpanName(currentName, currentSource, newName, newSource, allowNoCurrentName = false) {
1040
655
  if (!newName) {
1041
656
  return false;
1042
657
  }
1043
-
1044
658
  if (!currentName && allowNoCurrentName) {
1045
659
  return true;
1046
660
  }
1047
-
1048
661
  const hasWildcard = currentName && transactionNameHasWildcard(currentName);
1049
-
1050
- if (hasWildcard && newSource === 'route' && !transactionNameHasWildcard(newName)) {
662
+ if (hasWildcard && newSource === "route" && !transactionNameHasWildcard(newName)) {
1051
663
  return true;
1052
664
  }
1053
-
1054
- if (currentSource !== 'route' && newSource === 'route') {
665
+ if (currentSource !== "route" && newSource === "route") {
1055
666
  return true;
1056
667
  }
1057
-
1058
668
  return false;
1059
669
  }
1060
-
1061
- function tryUpdateSpanNameBeforeEnd(
1062
- span,
1063
- spanJson,
1064
- currentName,
1065
- location,
1066
- routes,
1067
- basename,
1068
- spanType,
1069
- allRoutes,
1070
- ) {
670
+ function tryUpdateSpanNameBeforeEnd(span, spanJson, currentName, location, routes, basename, spanType, allRoutes2) {
1071
671
  try {
1072
672
  const currentSource = spanJson.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
1073
-
1074
- if (currentSource === 'route' && currentName && !transactionNameHasWildcard(currentName)) {
673
+ if (currentSource === "route" && currentName && !transactionNameHasWildcard(currentName)) {
1075
674
  return;
1076
675
  }
1077
-
1078
- const currentAllRoutes = Array.from(allRoutes);
676
+ const currentAllRoutes = Array.from(allRoutes2);
1079
677
  const routesToUse = currentAllRoutes.length > 0 ? currentAllRoutes : routes;
1080
- const branches = _matchRoutes(routesToUse, location, basename) ;
1081
-
678
+ const branches = _matchRoutes(routesToUse, location, basename);
1082
679
  if (!branches) {
1083
680
  return;
1084
681
  }
1085
-
1086
682
  const [name, source] = resolveRouteNameAndSource(
1087
683
  location,
1088
684
  routesToUse,
@@ -1090,12 +686,10 @@ function tryUpdateSpanNameBeforeEnd(
1090
686
  branches,
1091
687
  basename,
1092
688
  _lazyRouteManifest,
1093
- _enableAsyncRouteHandlers,
689
+ _enableAsyncRouteHandlers
1094
690
  );
1095
-
1096
691
  const isImprovement = shouldUpdateWildcardSpanName(currentName, currentSource, name, source, true);
1097
- const spanNotEnded = spanType === 'pageload' || !spanJson.timestamp;
1098
-
692
+ const spanNotEnded = spanType === "pageload" || !spanJson.timestamp;
1099
693
  if (isImprovement && spanNotEnded) {
1100
694
  span.updateName(name);
1101
695
  span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
@@ -1104,68 +698,36 @@ function tryUpdateSpanNameBeforeEnd(
1104
698
  DEBUG_BUILD && debug.warn(`Error updating span details before ending: ${error}`);
1105
699
  }
1106
700
  }
1107
-
1108
- /**
1109
- * Patches the span.end() method to update the transaction name one last time before the span is sent.
1110
- * This handles cases where the span is cancelled early (e.g., document.hidden) before lazy routes have finished loading.
1111
- */
1112
- function patchSpanEnd(
1113
- span,
1114
- location,
1115
- routes,
1116
- basename,
1117
- spanType,
1118
- ) {
1119
- const patchedPropertyName = `__sentry_${spanType}_end_patched__` ;
1120
- const hasEndBeenPatched = (span )?.[patchedPropertyName];
1121
-
701
+ function patchSpanEnd(span, location, routes, basename, spanType) {
702
+ const patchedPropertyName = `__sentry_${spanType}_end_patched__`;
703
+ const hasEndBeenPatched = span?.[patchedPropertyName];
1122
704
  if (hasEndBeenPatched || !span.end) {
1123
705
  return;
1124
706
  }
1125
-
1126
- // Uses global allRoutes to access lazy-loaded routes added after this function was called.
1127
-
1128
707
  const originalEnd = span.end.bind(span);
1129
708
  let endCalled = false;
1130
-
1131
709
  span.end = function patchedEnd(...args) {
1132
710
  if (endCalled) {
1133
711
  return;
1134
712
  }
1135
713
  endCalled = true;
1136
-
1137
- // Capture timestamp immediately to avoid delay from async operations
1138
- // If no timestamp was provided, capture the current time now
1139
- const endTimestamp = args.length > 0 ? args[0] : Date.now() / 1000;
1140
-
714
+ const endTimestamp = args.length > 0 ? args[0] : Date.now() / 1e3;
1141
715
  const spanJson = spanToJSON(span);
1142
716
  const currentName = spanJson.description;
1143
717
  const currentSource = spanJson.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
1144
-
1145
- // Helper to clean up activeNavigationSpans after span ends
1146
718
  const cleanupNavigationSpan = () => {
1147
719
  const client = getClient();
1148
- if (client && spanType === 'navigation') {
720
+ if (client && spanType === "navigation") {
1149
721
  const trackedNav = activeNavigationSpans.get(client);
1150
722
  if (trackedNav && trackedNav.span === span) {
1151
723
  activeNavigationSpans.delete(client);
1152
724
  }
1153
725
  }
1154
726
  };
1155
-
1156
727
  const pendingPromises = pendingLazyRouteLoads.get(span);
1157
- const mayHaveLazyRoutes = (span ).__sentry_may_have_lazy_routes__;
1158
-
1159
- // Wait for lazy routes if:
1160
- // 1. (There are pending promises OR the span was marked as potentially having lazy routes) AND
1161
- // 2. Current name exists AND
1162
- // 3. Either the name has a wildcard OR the source is not 'route' (URL-based names)
1163
- const hasPendingOrMayHaveLazyRoutes = (pendingPromises && pendingPromises.size > 0) || mayHaveLazyRoutes;
1164
- const shouldWaitForLazyRoutes =
1165
- hasPendingOrMayHaveLazyRoutes &&
1166
- currentName &&
1167
- (transactionNameHasWildcard(currentName) || currentSource !== 'route');
1168
-
728
+ const mayHaveLazyRoutes = span.__sentry_may_have_lazy_routes__;
729
+ const hasPendingOrMayHaveLazyRoutes = pendingPromises && pendingPromises.size > 0 || mayHaveLazyRoutes;
730
+ const shouldWaitForLazyRoutes = hasPendingOrMayHaveLazyRoutes && currentName && (transactionNameHasWildcard(currentName) || currentSource !== "route");
1169
731
  if (shouldWaitForLazyRoutes) {
1170
732
  if (_lazyRouteTimeout === 0) {
1171
733
  tryUpdateSpanNameBeforeEnd(span, spanJson, currentName, location, routes, basename, spanType, allRoutes);
@@ -1173,107 +735,74 @@ function patchSpanEnd(
1173
735
  originalEnd(endTimestamp);
1174
736
  return;
1175
737
  }
1176
-
1177
- // If we have pending promises, wait for them. Otherwise, just wait for the timeout.
1178
- // This handles the case where we know lazy routes might load but patchRoutesOnNavigation
1179
- // hasn't been called yet.
1180
- const timeoutPromise = new Promise(r => setTimeout(r, _lazyRouteTimeout));
738
+ const timeoutPromise = new Promise((r) => setTimeout(r, _lazyRouteTimeout));
1181
739
  let waitPromise;
1182
-
1183
740
  if (pendingPromises && pendingPromises.size > 0) {
1184
- const allSettled = Promise.allSettled(pendingPromises).then(() => {});
741
+ const allSettled = Promise.allSettled(pendingPromises).then(() => {
742
+ });
1185
743
  waitPromise = _lazyRouteTimeout === Infinity ? allSettled : Promise.race([allSettled, timeoutPromise]);
1186
744
  } else {
1187
- // No pending promises yet, but we know lazy routes might load
1188
- // Wait for the timeout to give React Router time to call patchRoutesOnNavigation
1189
745
  waitPromise = timeoutPromise;
1190
746
  }
1191
-
1192
- waitPromise
1193
- .then(() => {
1194
- const updatedSpanJson = spanToJSON(span);
1195
- tryUpdateSpanNameBeforeEnd(
1196
- span,
1197
- updatedSpanJson,
1198
- updatedSpanJson.description,
1199
- location,
1200
- routes,
1201
- basename,
1202
- spanType,
1203
- allRoutes,
1204
- );
1205
- cleanupNavigationSpan();
1206
- originalEnd(endTimestamp);
1207
- })
1208
- .catch(() => {
1209
- cleanupNavigationSpan();
1210
- originalEnd(endTimestamp);
1211
- });
747
+ waitPromise.then(() => {
748
+ const updatedSpanJson = spanToJSON(span);
749
+ tryUpdateSpanNameBeforeEnd(
750
+ span,
751
+ updatedSpanJson,
752
+ updatedSpanJson.description,
753
+ location,
754
+ routes,
755
+ basename,
756
+ spanType,
757
+ allRoutes
758
+ );
759
+ cleanupNavigationSpan();
760
+ originalEnd(endTimestamp);
761
+ }).catch(() => {
762
+ cleanupNavigationSpan();
763
+ originalEnd(endTimestamp);
764
+ });
1212
765
  return;
1213
766
  }
1214
-
1215
767
  tryUpdateSpanNameBeforeEnd(span, spanJson, currentName, location, routes, basename, spanType, allRoutes);
1216
768
  cleanupNavigationSpan();
1217
769
  originalEnd(endTimestamp);
1218
770
  };
1219
-
1220
- addNonEnumerableProperty(span , patchedPropertyName, true);
771
+ addNonEnumerableProperty(span, patchedPropertyName, true);
1221
772
  }
1222
-
1223
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1224
- function createV6CompatibleWithSentryReactRouterRouting(
1225
- Routes,
1226
- version,
1227
- ) {
773
+ function createV6CompatibleWithSentryReactRouterRouting(Routes, version) {
1228
774
  if (!_useEffect || !_useLocation || !_useNavigationType || !_createRoutesFromChildren || !_matchRoutes) {
1229
- DEBUG_BUILD &&
1230
- debug.warn(`reactRouterV6Instrumentation was unable to wrap Routes because of one or more missing parameters.
775
+ DEBUG_BUILD && debug.warn(`reactRouterV6Instrumentation was unable to wrap Routes because of one or more missing parameters.
1231
776
  useEffect: ${_useEffect}. useLocation: ${_useLocation}. useNavigationType: ${_useNavigationType}.
1232
777
  createRoutesFromChildren: ${_createRoutesFromChildren}. matchRoutes: ${_matchRoutes}.`);
1233
-
1234
778
  return Routes;
1235
779
  }
1236
-
1237
780
  const SentryRoutes = (props) => {
1238
781
  const isMountRenderPass = React.useRef(true);
1239
-
1240
782
  const location = _useLocation();
1241
783
  const navigationType = _useNavigationType();
1242
-
1243
784
  _useEffect(
1244
785
  () => {
1245
- const routes = _createRoutesFromChildren(props.children) ;
1246
-
786
+ const routes = _createRoutesFromChildren(props.children);
1247
787
  if (isMountRenderPass.current) {
1248
788
  addRoutesToAllRoutes(routes);
1249
-
1250
789
  updatePageloadTransaction({
1251
790
  activeRootSpan: getActiveRootSpan(),
1252
791
  location,
1253
792
  routes,
1254
- allRoutes: Array.from(allRoutes),
793
+ allRoutes: Array.from(allRoutes)
1255
794
  });
1256
795
  isMountRenderPass.current = false;
1257
796
  } else {
1258
- // Note: Component-based routes don't support lazy route tracking via lazyRouteTimeout
1259
- // because React.lazy() loads happen at the component level, not the router level.
1260
- // Use createBrowserRouter with patchRoutesOnNavigation for lazy route tracking.
1261
797
  handleNavigation({ location, routes, navigationType, version, allRoutes: Array.from(allRoutes) });
1262
798
  }
1263
799
  },
1264
800
  // Re-run only on location/navigation changes, not children changes
1265
- [location, navigationType],
801
+ [location, navigationType]
1266
802
  );
1267
-
1268
- // @ts-expect-error Setting more specific React Component typing for `R` generic above
1269
- // will break advanced type inference done by react router params
1270
- return React.createElement(Routes, { ...props,} );
803
+ return /* @__PURE__ */ React.createElement(Routes, { ...props });
1271
804
  };
1272
-
1273
805
  hoistNonReactStatics(SentryRoutes, Routes);
1274
-
1275
- // @ts-expect-error Setting more specific React Component typing for `R` generic above
1276
- // will break advanced type inference done by react router params
1277
806
  return SentryRoutes;
1278
807
  }
1279
808