@@ -22,8 +22,7 @@ import {
2222 startSpan ,
2323 withIsolationScope ,
2424} from '@sentry/node' ;
25- import type { APIContext , MiddlewareResponseHandler } from 'astro' ;
26- import type { ResolvedRouteWithCasedPattern } from '../integration/types' ;
25+ import type { APIContext , MiddlewareResponseHandler , RoutePart } from 'astro' ;
2726
2827type MiddlewareOptions = {
2928 /**
@@ -96,9 +95,6 @@ async function instrumentRequest(
9695 addNonEnumerableProperty ( locals , '__sentry_wrapped__' , true ) ;
9796 }
9897
99- const storedBuildTimeRoutes = ( globalThis as unknown as { __sentryRouteInfo ?: ResolvedRouteWithCasedPattern [ ] } )
100- ?. __sentryRouteInfo ;
101-
10298 const isDynamicPageRequest = checkIsDynamicPageRequest ( ctx ) ;
10399
104100 const request = ctx . request ;
@@ -135,10 +131,21 @@ async function instrumentRequest(
135131 // `routePattern` is available after Astro 5
136132 const contextWithRoutePattern = ctx as Parameters < MiddlewareResponseHandler > [ 0 ] & { routePattern ?: string } ;
137133 const rawRoutePattern = contextWithRoutePattern . routePattern ;
138- const foundRoute = storedBuildTimeRoutes ?. find ( route => route . pattern === rawRoutePattern ) ;
134+
135+ // @ts -expect-error Implicit any on Symbol.for (This is available in Astro 5)
136+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
137+ const routesFromManifest = ctx ?. [ Symbol . for ( 'context.routes' ) ] ?. manifest ?. routes ;
138+
139+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
140+ const matchedRouteSegmentsFromManifest = routesFromManifest ?. find (
141+ ( route : { routeData ?: { route ?: string } } ) => route ?. routeData ?. route === rawRoutePattern ,
142+ ) ?. routeData ?. segments ;
139143
140144 const parametrizedRoute =
141- foundRoute ?. patternCaseSensitive || interpolateRouteFromUrlAndParams ( ctx . url . pathname , ctx . params ) ;
145+ // Astro v5 - Joining the segments to get the correct casing of the parametrized route
146+ ( matchedRouteSegmentsFromManifest && joinRouteSegments ( matchedRouteSegmentsFromManifest ) ) ||
147+ // Fallback (Astro v4 and earlier)
148+ interpolateRouteFromUrlAndParams ( ctx . url . pathname , ctx . params ) ;
142149
143150 const source = parametrizedRoute ? 'route' : 'url' ;
144151 // storing res in a variable instead of directly returning is necessary to
@@ -365,3 +372,18 @@ function checkIsDynamicPageRequest(context: Parameters<MiddlewareResponseHandler
365372 return false ;
366373 }
367374}
375+
376+ /**
377+ * Join Astro route segments into a case-sensitive single path string.
378+ *
379+ * Astro lowercases the parametrized route. Joining segments manually is recommended to get the correct casing of the routes.
380+ * Recommendation in comment: https://github.com/withastro/astro/issues/13885#issuecomment-2934203029
381+ * Function Reference: https://github.com/joanrieu/astro-typed-links/blob/b3dc12c6fe8d672a2bc2ae2ccc57c8071bbd09fa/package/src/integration.ts#L16
382+ */
383+ function joinRouteSegments ( segments : RoutePart [ ] [ ] ) : string {
384+ const parthArray = segments . map ( segment =>
385+ segment . map ( routePart => ( routePart . dynamic ? `[${ routePart . content } ]` : routePart . content ) ) . join ( '' ) ,
386+ ) ;
387+
388+ return `/${ parthArray . join ( '/' ) } ` ;
389+ }
0 commit comments