diff --git a/packages/react-router/src/server/createSentryHandleRequest.tsx b/packages/react-router/src/server/createSentryHandleRequest.tsx index eaf13eb16779..75080b827165 100644 --- a/packages/react-router/src/server/createSentryHandleRequest.tsx +++ b/packages/react-router/src/server/createSentryHandleRequest.tsx @@ -53,6 +53,22 @@ export interface SentryHandleRequestOptions { botRegex?: RegExp; } +type HandleRequestWithoutMiddleware = ( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + routerContext: EntryContext, + loadContext: AppLoadContext, +) => Promise; + +type HandleRequestWithMiddleware = ( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + routerContext: EntryContext, + loadContext: RouterContextProvider, +) => Promise; + /** * A complete Sentry-instrumented handleRequest implementation that handles both * route parametrization and trace meta tag injection. @@ -62,13 +78,7 @@ export interface SentryHandleRequestOptions { */ export function createSentryHandleRequest( options: SentryHandleRequestOptions, -): ( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - routerContext: EntryContext, - loadContext: AppLoadContext | RouterContextProvider, -) => Promise { +): HandleRequestWithoutMiddleware & HandleRequestWithMiddleware { const { streamTimeout = 10000, renderToPipeableStream, @@ -135,5 +145,6 @@ export function createSentryHandleRequest( }; // Wrap the handle request function for request parametrization - return wrapSentryHandleRequest(handleRequest); + return wrapSentryHandleRequest(handleRequest as HandleRequestWithoutMiddleware) as HandleRequestWithoutMiddleware & + HandleRequestWithMiddleware; } diff --git a/packages/react-router/src/server/wrapSentryHandleRequest.ts b/packages/react-router/src/server/wrapSentryHandleRequest.ts index 5651ad208a9d..308b5c07a32b 100644 --- a/packages/react-router/src/server/wrapSentryHandleRequest.ts +++ b/packages/react-router/src/server/wrapSentryHandleRequest.ts @@ -10,12 +10,20 @@ import { } from '@sentry/core'; import type { AppLoadContext, EntryContext, RouterContextProvider } from 'react-router'; -type OriginalHandleRequest = ( +type OriginalHandleRequestWithoutMiddleware = ( request: Request, responseStatusCode: number, responseHeaders: Headers, routerContext: EntryContext, - loadContext: AppLoadContext | RouterContextProvider, + loadContext: AppLoadContext, +) => Promise; + +type OriginalHandleRequestWithMiddleware = ( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + routerContext: EntryContext, + loadContext: RouterContextProvider, ) => Promise; /** @@ -24,7 +32,27 @@ type OriginalHandleRequest = ( * @param originalHandle - The original handleRequest function to wrap * @returns A wrapped version of the handle request function with Sentry instrumentation */ -export function wrapSentryHandleRequest(originalHandle: OriginalHandleRequest): OriginalHandleRequest { +export function wrapSentryHandleRequest( + originalHandle: OriginalHandleRequestWithoutMiddleware, +): OriginalHandleRequestWithoutMiddleware; +/** + * Wraps the original handleRequest function to add Sentry instrumentation. + * + * @param originalHandle - The original handleRequest function to wrap + * @returns A wrapped version of the handle request function with Sentry instrumentation + */ +export function wrapSentryHandleRequest( + originalHandle: OriginalHandleRequestWithMiddleware, +): OriginalHandleRequestWithMiddleware; +/** + * Wraps the original handleRequest function to add Sentry instrumentation. + * + * @param originalHandle - The original handleRequest function to wrap + * @returns A wrapped version of the handle request function with Sentry instrumentation + */ +export function wrapSentryHandleRequest( + originalHandle: OriginalHandleRequestWithoutMiddleware | OriginalHandleRequestWithMiddleware, +): OriginalHandleRequestWithoutMiddleware | OriginalHandleRequestWithMiddleware { return async function sentryInstrumentedHandleRequest( request: Request, responseStatusCode: number, @@ -57,10 +85,39 @@ export function wrapSentryHandleRequest(originalHandle: OriginalHandleRequest): } try { - return await originalHandle(request, responseStatusCode, responseHeaders, routerContext, loadContext); + // Type guard to call the correct overload based on loadContext type + if (isRouterContextProvider(loadContext)) { + // loadContext is RouterContextProvider + return await (originalHandle as OriginalHandleRequestWithMiddleware)( + request, + responseStatusCode, + responseHeaders, + routerContext, + loadContext, + ); + } else { + // loadContext is AppLoadContext + return await (originalHandle as OriginalHandleRequestWithoutMiddleware)( + request, + responseStatusCode, + responseHeaders, + routerContext, + loadContext, + ); + } } finally { await flushIfServerless(); } + + /** + * Helper type guard to determine if the context is a RouterContextProvider. + * + * @param ctx - The context to check + * @returns True if the context is a RouterContextProvider + */ + function isRouterContextProvider(ctx: AppLoadContext | RouterContextProvider): ctx is RouterContextProvider { + return typeof (ctx as RouterContextProvider)?.get === 'function'; + } }; }