@sentry/react-router 10.36.0 → 10.38.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 (89) hide show
  1. package/build/cjs/client/createClientInstrumentation.js +309 -0
  2. package/build/cjs/client/createClientInstrumentation.js.map +1 -0
  3. package/build/cjs/client/hydratedRouter.js +15 -18
  4. package/build/cjs/client/hydratedRouter.js.map +1 -1
  5. package/build/cjs/client/tracingIntegration.js +25 -2
  6. package/build/cjs/client/tracingIntegration.js.map +1 -1
  7. package/build/cjs/cloudflare/index.js +4 -0
  8. package/build/cjs/cloudflare/index.js.map +1 -1
  9. package/build/cjs/common/utils.js +68 -0
  10. package/build/cjs/common/utils.js.map +1 -0
  11. package/build/cjs/index.client.js +4 -0
  12. package/build/cjs/index.client.js.map +1 -1
  13. package/build/cjs/index.server.js +4 -0
  14. package/build/cjs/index.server.js.map +1 -1
  15. package/build/cjs/server/createSentryHandleError.js +1 -4
  16. package/build/cjs/server/createSentryHandleError.js.map +1 -1
  17. package/build/cjs/server/createServerInstrumentation.js +231 -0
  18. package/build/cjs/server/createServerInstrumentation.js.map +1 -0
  19. package/build/cjs/server/instrumentation/reactRouter.js +8 -0
  20. package/build/cjs/server/instrumentation/reactRouter.js.map +1 -1
  21. package/build/cjs/server/integration/reactRouterServer.js +16 -6
  22. package/build/cjs/server/integration/reactRouterServer.js.map +1 -1
  23. package/build/cjs/server/serverGlobals.js +25 -0
  24. package/build/cjs/server/serverGlobals.js.map +1 -0
  25. package/build/cjs/server/wrapSentryHandleRequest.js +22 -7
  26. package/build/cjs/server/wrapSentryHandleRequest.js.map +1 -1
  27. package/build/cjs/server/wrapServerAction.js +21 -1
  28. package/build/cjs/server/wrapServerAction.js.map +1 -1
  29. package/build/cjs/server/wrapServerLoader.js +21 -1
  30. package/build/cjs/server/wrapServerLoader.js.map +1 -1
  31. package/build/cjs/vite/makeCustomSentryVitePlugins.js +1 -1
  32. package/build/cjs/vite/makeCustomSentryVitePlugins.js.map +1 -1
  33. package/build/esm/client/createClientInstrumentation.js +305 -0
  34. package/build/esm/client/createClientInstrumentation.js.map +1 -0
  35. package/build/esm/client/hydratedRouter.js +16 -19
  36. package/build/esm/client/hydratedRouter.js.map +1 -1
  37. package/build/esm/client/tracingIntegration.js +25 -2
  38. package/build/esm/client/tracingIntegration.js.map +1 -1
  39. package/build/esm/cloudflare/index.js +1 -0
  40. package/build/esm/cloudflare/index.js.map +1 -1
  41. package/build/esm/common/utils.js +63 -0
  42. package/build/esm/common/utils.js.map +1 -0
  43. package/build/esm/index.client.js +1 -0
  44. package/build/esm/index.client.js.map +1 -1
  45. package/build/esm/index.server.js +2 -0
  46. package/build/esm/index.server.js.map +1 -1
  47. package/build/esm/package.json +1 -1
  48. package/build/esm/server/createSentryHandleError.js +1 -4
  49. package/build/esm/server/createSentryHandleError.js.map +1 -1
  50. package/build/esm/server/createServerInstrumentation.js +229 -0
  51. package/build/esm/server/createServerInstrumentation.js.map +1 -0
  52. package/build/esm/server/instrumentation/reactRouter.js +9 -1
  53. package/build/esm/server/instrumentation/reactRouter.js.map +1 -1
  54. package/build/esm/server/integration/reactRouterServer.js +16 -6
  55. package/build/esm/server/integration/reactRouterServer.js.map +1 -1
  56. package/build/esm/server/serverGlobals.js +22 -0
  57. package/build/esm/server/serverGlobals.js.map +1 -0
  58. package/build/esm/server/wrapSentryHandleRequest.js +23 -8
  59. package/build/esm/server/wrapSentryHandleRequest.js.map +1 -1
  60. package/build/esm/server/wrapServerAction.js +22 -2
  61. package/build/esm/server/wrapServerAction.js.map +1 -1
  62. package/build/esm/server/wrapServerLoader.js +22 -2
  63. package/build/esm/server/wrapServerLoader.js.map +1 -1
  64. package/build/esm/vite/makeCustomSentryVitePlugins.js +1 -1
  65. package/build/esm/vite/makeCustomSentryVitePlugins.js.map +1 -1
  66. package/build/types/client/createClientInstrumentation.d.ts +28 -0
  67. package/build/types/client/createClientInstrumentation.d.ts.map +1 -0
  68. package/build/types/client/hydratedRouter.d.ts.map +1 -1
  69. package/build/types/client/index.d.ts +2 -1
  70. package/build/types/client/index.d.ts.map +1 -1
  71. package/build/types/client/tracingIntegration.d.ts +32 -2
  72. package/build/types/client/tracingIntegration.d.ts.map +1 -1
  73. package/build/types/common/types.d.ts +88 -0
  74. package/build/types/common/types.d.ts.map +1 -0
  75. package/build/types/common/utils.d.ts +27 -0
  76. package/build/types/common/utils.d.ts.map +1 -0
  77. package/build/types/server/createSentryHandleError.d.ts.map +1 -1
  78. package/build/types/server/createServerInstrumentation.d.ts +18 -0
  79. package/build/types/server/createServerInstrumentation.d.ts.map +1 -0
  80. package/build/types/server/index.d.ts +1 -0
  81. package/build/types/server/index.d.ts.map +1 -1
  82. package/build/types/server/instrumentation/reactRouter.d.ts.map +1 -1
  83. package/build/types/server/integration/reactRouterServer.d.ts.map +1 -1
  84. package/build/types/server/serverGlobals.d.ts +11 -0
  85. package/build/types/server/serverGlobals.d.ts.map +1 -0
  86. package/build/types/server/wrapSentryHandleRequest.d.ts.map +1 -1
  87. package/build/types/server/wrapServerAction.d.ts.map +1 -1
  88. package/build/types/server/wrapServerLoader.d.ts.map +1 -1
  89. package/package.json +12 -12
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["../../../src/common/utils.ts"],"sourcesContent":["import { captureException, debug } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { InstrumentationResult } from './types';\n\n/**\n * Extracts pathname from request URL.\n * Falls back to '<unknown>' with DEBUG warning if URL cannot be parsed.\n */\nexport function getPathFromRequest(request: { url: string }): string {\n try {\n return new URL(request.url).pathname;\n } catch {\n try {\n // Fallback: use a dummy base URL since we only care about the pathname\n return new URL(request.url, 'http://example.com').pathname;\n } catch (error) {\n DEBUG_BUILD && debug.warn('Failed to parse URL from request:', request.url, error);\n return '<unknown>';\n }\n }\n}\n\n/**\n * Extracts route pattern from instrumentation info.\n * Prefers `pattern` (planned for v8) over `unstable_pattern` (v7.x).\n */\nexport function getPattern(info: { pattern?: string; unstable_pattern?: string }): string | undefined {\n return info.pattern ?? info.unstable_pattern;\n}\n\n/**\n * Normalizes route path by ensuring it starts with a slash.\n * Returns undefined if the input is falsy.\n */\nexport function normalizeRoutePath(pattern?: string): string | undefined {\n if (!pattern) {\n return undefined;\n }\n return pattern.startsWith('/') ? pattern : `/${pattern}`;\n}\n\n/**\n * Captures an error from instrumentation result.\n * Caller must verify result contains an Error before calling.\n */\nexport function captureInstrumentationError(\n result: InstrumentationResult,\n captureErrors: boolean,\n mechanismType: string,\n data: Record<string, string | boolean>,\n): void {\n if (captureErrors) {\n captureException(result.error, {\n mechanism: {\n type: mechanismType,\n handled: false,\n data,\n },\n });\n }\n}\n"],"names":["DEBUG_BUILD","debug","captureException"],"mappings":";;;;;AAIA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,OAAO,EAA2B;AACrE,EAAE,IAAI;AACN,IAAI,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ;AACxC,EAAE,EAAE,MAAM;AACV,IAAI,IAAI;AACR;AACA,MAAM,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC,QAAQ;AAChE,IAAI,CAAA,CAAE,OAAO,KAAK,EAAE;AACpB,MAAMA,sBAAA,IAAeC,UAAK,CAAC,IAAI,CAAC,mCAAmC,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC;AACxF,MAAM,OAAO,WAAW;AACxB,IAAI;AACJ,EAAE;AACF;;AAEA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,IAAI,EAAuE;AACtG,EAAE,OAAO,IAAI,CAAC,WAAW,IAAI,CAAC,gBAAgB;AAC9C;;AAEA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,OAAO,EAA+B;AACzE,EAAE,IAAI,CAAC,OAAO,EAAE;AAChB,IAAI,OAAO,SAAS;AACpB,EAAE;AACF,EAAE,OAAO,OAAO,CAAC,UAAU,CAAC,GAAG,CAAA,GAAI,OAAA,GAAU,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;AACA;;AAEA;AACA;AACA;AACA;AACA,SAAA,2BAAA;AACA,EAAA,MAAA;AACA,EAAA,aAAA;AACA,EAAA,aAAA;AACA,EAAA,IAAA;AACA,EAAA;AACA,EAAA,IAAA,aAAA,EAAA;AACA,IAAAC,qBAAA,CAAA,MAAA,CAAA,KAAA,EAAA;AACA,MAAA,SAAA,EAAA;AACA,QAAA,IAAA,EAAA,aAAA;AACA,QAAA,OAAA,EAAA,KAAA;AACA,QAAA,IAAA;AACA,OAAA;AACA,KAAA,CAAA;AACA,EAAA;AACA;;;;;;;"}
@@ -4,6 +4,7 @@ const browser = require('@sentry/browser');
4
4
  const sdk = require('./client/sdk.js');
5
5
  const tracingIntegration = require('./client/tracingIntegration.js');
6
6
  const react = require('@sentry/react');
7
+ const createClientInstrumentation = require('./client/createClientInstrumentation.js');
7
8
 
8
9
 
9
10
 
@@ -16,6 +17,9 @@ exports.reactErrorHandler = react.reactErrorHandler;
16
17
  exports.useProfiler = react.useProfiler;
17
18
  exports.withErrorBoundary = react.withErrorBoundary;
18
19
  exports.withProfiler = react.withProfiler;
20
+ exports.createSentryClientInstrumentation = createClientInstrumentation.createSentryClientInstrumentation;
21
+ exports.isClientInstrumentationApiUsed = createClientInstrumentation.isClientInstrumentationApiUsed;
22
+ exports.isNavigateHookInvoked = createClientInstrumentation.isNavigateHookInvoked;
19
23
  Object.prototype.hasOwnProperty.call(browser, '__proto__') &&
20
24
  !Object.prototype.hasOwnProperty.call(exports, '__proto__') &&
21
25
  Object.defineProperty(exports, '__proto__', {
@@ -1 +1 @@
1
- {"version":3,"file":"index.client.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.client.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,9 +8,11 @@ const wrapServerAction = require('./server/wrapServerAction.js');
8
8
  const wrapServerLoader = require('./server/wrapServerLoader.js');
9
9
  const createSentryHandleError = require('./server/createSentryHandleError.js');
10
10
  const getMetaTagTransformer = require('./server/getMetaTagTransformer.js');
11
+ const createServerInstrumentation = require('./server/createServerInstrumentation.js');
11
12
  const plugin = require('./vite/plugin.js');
12
13
  const handleOnBuildEnd = require('./vite/buildEnd/handleOnBuildEnd.js');
13
14
  const makeConfigInjectorPlugin = require('./vite/makeConfigInjectorPlugin.js');
15
+ const serverGlobals = require('./server/serverGlobals.js');
14
16
 
15
17
 
16
18
 
@@ -22,9 +24,11 @@ exports.wrapServerAction = wrapServerAction.wrapServerAction;
22
24
  exports.wrapServerLoader = wrapServerLoader.wrapServerLoader;
23
25
  exports.createSentryHandleError = createSentryHandleError.createSentryHandleError;
24
26
  exports.getMetaTagTransformer = getMetaTagTransformer.getMetaTagTransformer;
27
+ exports.createSentryServerInstrumentation = createServerInstrumentation.createSentryServerInstrumentation;
25
28
  exports.sentryReactRouter = plugin.sentryReactRouter;
26
29
  exports.sentryOnBuildEnd = handleOnBuildEnd.sentryOnBuildEnd;
27
30
  exports.makeConfigInjectorPlugin = makeConfigInjectorPlugin.makeConfigInjectorPlugin;
31
+ exports.isInstrumentationApiUsed = serverGlobals.isInstrumentationApiUsed;
28
32
  Object.prototype.hasOwnProperty.call(node, '__proto__') &&
29
33
  !Object.prototype.hasOwnProperty.call(exports, '__proto__') &&
30
34
  Object.defineProperty(exports, '__proto__', {
@@ -1 +1 @@
1
- {"version":3,"file":"index.server.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.server.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,10 +8,7 @@ const core = require('@sentry/core');
8
8
  * @returns A Sentry-instrumented handleError function
9
9
  */
10
10
  function createSentryHandleError({ logErrors = false }) {
11
- const handleError = async function handleError(
12
- error,
13
- args,
14
- ) {
11
+ const handleError = async function handleError(error, args) {
15
12
  // React Router may abort some interrupted requests, don't report those
16
13
  if (!args.request.signal.aborted) {
17
14
  core.captureException(error, {
@@ -1 +1 @@
1
- {"version":3,"file":"createSentryHandleError.js","sources":["../../../src/server/createSentryHandleError.ts"],"sourcesContent":["import { captureException, flushIfServerless } from '@sentry/core';\nimport type { ActionFunctionArgs, HandleErrorFunction, LoaderFunctionArgs } from 'react-router';\n\nexport type SentryHandleErrorOptions = {\n logErrors?: boolean;\n};\n\n/**\n * A complete Sentry-instrumented handleError implementation that handles error reporting\n *\n * @returns A Sentry-instrumented handleError function\n */\nexport function createSentryHandleError({ logErrors = false }: SentryHandleErrorOptions): HandleErrorFunction {\n const handleError = async function handleError(\n error: unknown,\n args: LoaderFunctionArgs | ActionFunctionArgs,\n ): Promise<void> {\n // React Router may abort some interrupted requests, don't report those\n if (!args.request.signal.aborted) {\n captureException(error, {\n mechanism: {\n type: 'react-router',\n handled: false,\n },\n });\n if (logErrors) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n try {\n await flushIfServerless();\n } catch {\n // Ignore flush errors to ensure error handling completes gracefully\n }\n }\n };\n\n return handleError;\n}\n"],"names":["captureException","flushIfServerless"],"mappings":";;;;AAOA;AACA;AACA;AACA;AACA;AACO,SAAS,uBAAuB,CAAC,EAAE,YAAY,KAAA,EAAO,EAAiD;AAC9G,EAAE,MAAM,WAAA,GAAc,eAAe,WAAW;AAChD,IAAI,KAAK;AACT,IAAI,IAAI;AACR,IAAmB;AACnB;AACA,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE;AACtC,MAAMA,qBAAgB,CAAC,KAAK,EAAE;AAC9B,QAAQ,SAAS,EAAE;AACnB,UAAU,IAAI,EAAE,cAAc;AAC9B,UAAU,OAAO,EAAE,KAAK;AACxB,SAAS;AACT,OAAO,CAAC;AACR,MAAM,IAAI,SAAS,EAAE;AACrB;AACA,QAAQ,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AAC5B,MAAM;AACN,MAAM,IAAI;AACV,QAAQ,MAAMC,sBAAiB,EAAE;AACjC,MAAM,EAAE,MAAM;AACd;AACA,MAAM;AACN,IAAI;AACJ,EAAE,CAAC;;AAEH,EAAE,OAAO,WAAW;AACpB;;;;"}
1
+ {"version":3,"file":"createSentryHandleError.js","sources":["../../../src/server/createSentryHandleError.ts"],"sourcesContent":["import { captureException, flushIfServerless } from '@sentry/core';\nimport type { HandleErrorFunction } from 'react-router';\n\nexport type SentryHandleErrorOptions = {\n logErrors?: boolean;\n};\n\n/**\n * A complete Sentry-instrumented handleError implementation that handles error reporting\n *\n * @returns A Sentry-instrumented handleError function\n */\nexport function createSentryHandleError({ logErrors = false }: SentryHandleErrorOptions): HandleErrorFunction {\n const handleError: HandleErrorFunction = async function handleError(error, args): Promise<void> {\n // React Router may abort some interrupted requests, don't report those\n if (!args.request.signal.aborted) {\n captureException(error, {\n mechanism: {\n type: 'react-router',\n handled: false,\n },\n });\n if (logErrors) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n try {\n await flushIfServerless();\n } catch {\n // Ignore flush errors to ensure error handling completes gracefully\n }\n }\n };\n\n return handleError;\n}\n"],"names":["captureException","flushIfServerless"],"mappings":";;;;AAOA;AACA;AACA;AACA;AACA;AACO,SAAS,uBAAuB,CAAC,EAAE,YAAY,KAAA,EAAO,EAAiD;AAC9G,EAAE,MAAM,WAAW,GAAwB,eAAe,WAAW,CAAC,KAAK,EAAE,IAAI,EAAiB;AAClG;AACA,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE;AACtC,MAAMA,qBAAgB,CAAC,KAAK,EAAE;AAC9B,QAAQ,SAAS,EAAE;AACnB,UAAU,IAAI,EAAE,cAAc;AAC9B,UAAU,OAAO,EAAE,KAAK;AACxB,SAAS;AACT,OAAO,CAAC;AACR,MAAM,IAAI,SAAS,EAAE;AACrB;AACA,QAAQ,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AAC5B,MAAM;AACN,MAAM,IAAI;AACV,QAAQ,MAAMC,sBAAiB,EAAE;AACjC,MAAM,EAAE,MAAM;AACd;AACA,MAAM;AACN,IAAI;AACJ,EAAE,CAAC;;AAEH,EAAE,OAAO,WAAW;AACpB;;;;"}
@@ -0,0 +1,231 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const api = require('@opentelemetry/api');
4
+ const core$1 = require('@opentelemetry/core');
5
+ const semanticConventions = require('@opentelemetry/semantic-conventions');
6
+ const core = require('@sentry/core');
7
+ const debugBuild = require('../common/debug-build.js');
8
+ const utils = require('../common/utils.js');
9
+ const serverGlobals = require('./serverGlobals.js');
10
+
11
+ /**
12
+ * Options for creating Sentry server instrumentation.
13
+ */
14
+
15
+ /**
16
+ * Creates a Sentry server instrumentation for React Router's instrumentation API.
17
+ * @experimental
18
+ */
19
+ function createSentryServerInstrumentation(
20
+ options = {},
21
+ ) {
22
+ const { captureErrors = true } = options;
23
+
24
+ serverGlobals.markInstrumentationApiUsed();
25
+ debugBuild.DEBUG_BUILD && core.debug.log('React Router server instrumentation API enabled.');
26
+
27
+ return {
28
+ handler(handler) {
29
+ handler.instrument({
30
+ async request(handleRequest, info) {
31
+ const pathname = utils.getPathFromRequest(info.request);
32
+ const activeSpan = core.getActiveSpan();
33
+ const existingRootSpan = activeSpan ? core.getRootSpan(activeSpan) : undefined;
34
+
35
+ if (existingRootSpan) {
36
+ core.updateSpanName(existingRootSpan, `${info.request.method} ${pathname}`);
37
+ existingRootSpan.setAttributes({
38
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',
39
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.instrumentation_api',
40
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
41
+ });
42
+
43
+ try {
44
+ const result = await handleRequest();
45
+ if (result.status === 'error' && result.error instanceof Error) {
46
+ existingRootSpan.setStatus({ code: core.SPAN_STATUS_ERROR, message: 'internal_error' });
47
+ utils.captureInstrumentationError(result, captureErrors, 'react_router.request_handler', {
48
+ 'http.method': info.request.method,
49
+ 'http.url': pathname,
50
+ });
51
+ }
52
+ } finally {
53
+ await core.flushIfServerless();
54
+ }
55
+ } else {
56
+ await core.startSpan(
57
+ {
58
+ name: `${info.request.method} ${pathname}`,
59
+ forceTransaction: true,
60
+ attributes: {
61
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',
62
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.instrumentation_api',
63
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
64
+ 'http.request.method': info.request.method,
65
+ 'url.path': pathname,
66
+ 'url.full': info.request.url,
67
+ },
68
+ },
69
+ async span => {
70
+ try {
71
+ const result = await handleRequest();
72
+ if (result.status === 'error' && result.error instanceof Error) {
73
+ span.setStatus({ code: core.SPAN_STATUS_ERROR, message: 'internal_error' });
74
+ utils.captureInstrumentationError(result, captureErrors, 'react_router.request_handler', {
75
+ 'http.method': info.request.method,
76
+ 'http.url': pathname,
77
+ });
78
+ }
79
+ } finally {
80
+ await core.flushIfServerless();
81
+ }
82
+ },
83
+ );
84
+ }
85
+ },
86
+ });
87
+ },
88
+
89
+ route(route) {
90
+ route.instrument({
91
+ async loader(callLoader, info) {
92
+ const urlPath = utils.getPathFromRequest(info.request);
93
+ const pattern = utils.getPattern(info);
94
+ const routePattern = utils.normalizeRoutePath(pattern) || urlPath;
95
+ updateRootSpanWithRoute(info.request.method, pattern, urlPath);
96
+
97
+ await core.startSpan(
98
+ {
99
+ name: routePattern,
100
+ attributes: {
101
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.loader',
102
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
103
+ },
104
+ },
105
+ async span => {
106
+ const result = await callLoader();
107
+ if (result.status === 'error' && result.error instanceof Error) {
108
+ span.setStatus({ code: core.SPAN_STATUS_ERROR, message: 'internal_error' });
109
+ utils.captureInstrumentationError(result, captureErrors, 'react_router.loader', {
110
+ 'http.method': info.request.method,
111
+ 'http.url': urlPath,
112
+ });
113
+ }
114
+ },
115
+ );
116
+ },
117
+
118
+ async action(callAction, info) {
119
+ const urlPath = utils.getPathFromRequest(info.request);
120
+ const pattern = utils.getPattern(info);
121
+ const routePattern = utils.normalizeRoutePath(pattern) || urlPath;
122
+ updateRootSpanWithRoute(info.request.method, pattern, urlPath);
123
+
124
+ await core.startSpan(
125
+ {
126
+ name: routePattern,
127
+ attributes: {
128
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.action',
129
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
130
+ },
131
+ },
132
+ async span => {
133
+ const result = await callAction();
134
+ if (result.status === 'error' && result.error instanceof Error) {
135
+ span.setStatus({ code: core.SPAN_STATUS_ERROR, message: 'internal_error' });
136
+ utils.captureInstrumentationError(result, captureErrors, 'react_router.action', {
137
+ 'http.method': info.request.method,
138
+ 'http.url': urlPath,
139
+ });
140
+ }
141
+ },
142
+ );
143
+ },
144
+
145
+ async middleware(callMiddleware, info) {
146
+ const urlPath = utils.getPathFromRequest(info.request);
147
+ const pattern = utils.getPattern(info);
148
+ const routePattern = utils.normalizeRoutePath(pattern) || urlPath;
149
+
150
+ // Update root span with parameterized route (same as loader/action)
151
+ updateRootSpanWithRoute(info.request.method, pattern, urlPath);
152
+
153
+ await core.startSpan(
154
+ {
155
+ name: routePattern,
156
+ attributes: {
157
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.middleware',
158
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
159
+ },
160
+ },
161
+ async span => {
162
+ const result = await callMiddleware();
163
+ if (result.status === 'error' && result.error instanceof Error) {
164
+ span.setStatus({ code: core.SPAN_STATUS_ERROR, message: 'internal_error' });
165
+ utils.captureInstrumentationError(result, captureErrors, 'react_router.middleware', {
166
+ 'http.method': info.request.method,
167
+ 'http.url': urlPath,
168
+ });
169
+ }
170
+ },
171
+ );
172
+ },
173
+
174
+ async lazy(callLazy) {
175
+ await core.startSpan(
176
+ {
177
+ name: 'Lazy Route Load',
178
+ attributes: {
179
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.lazy',
180
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
181
+ },
182
+ },
183
+ async span => {
184
+ const result = await callLazy();
185
+ if (result.status === 'error' && result.error instanceof Error) {
186
+ span.setStatus({ code: core.SPAN_STATUS_ERROR, message: 'internal_error' });
187
+ utils.captureInstrumentationError(result, captureErrors, 'react_router.lazy', {});
188
+ }
189
+ },
190
+ );
191
+ },
192
+ });
193
+ },
194
+ };
195
+ }
196
+
197
+ function updateRootSpanWithRoute(method, pattern, urlPath) {
198
+ const activeSpan = core.getActiveSpan();
199
+ if (!activeSpan) return;
200
+ const rootSpan = core.getRootSpan(activeSpan);
201
+ if (!rootSpan) return;
202
+
203
+ // Skip update if URL path is invalid (failed to parse)
204
+ if (!urlPath || urlPath === '<unknown>') {
205
+ debugBuild.DEBUG_BUILD && core.debug.warn('Cannot update span with invalid URL path:', urlPath);
206
+ return;
207
+ }
208
+
209
+ const hasPattern = !!pattern;
210
+ const routeName = hasPattern ? utils.normalizeRoutePath(pattern) || urlPath : urlPath;
211
+
212
+ const rpcMetadata = core$1.getRPCMetadata(api.context.active());
213
+ if (rpcMetadata?.type === core$1.RPCType.HTTP) {
214
+ rpcMetadata.route = routeName;
215
+ }
216
+
217
+ const transactionName = `${method} ${routeName}`;
218
+ core.updateSpanName(rootSpan, transactionName);
219
+ rootSpan.setAttributes({
220
+ [semanticConventions.ATTR_HTTP_ROUTE]: routeName,
221
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: hasPattern ? 'route' : 'url',
222
+ });
223
+
224
+ // Also update the scope's transaction name so errors captured during this request
225
+ // have the correct transaction name (not the initial placeholder like "GET *")
226
+ core.getCurrentScope().setTransactionName(transactionName);
227
+ }
228
+
229
+ exports.isInstrumentationApiUsed = serverGlobals.isInstrumentationApiUsed;
230
+ exports.createSentryServerInstrumentation = createSentryServerInstrumentation;
231
+ //# sourceMappingURL=createServerInstrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createServerInstrumentation.js","sources":["../../../src/server/createServerInstrumentation.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { getRPCMetadata, RPCType } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport {\n debug,\n flushIfServerless,\n getActiveSpan,\n getCurrentScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n SPAN_STATUS_ERROR,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport type { InstrumentableRequestHandler, InstrumentableRoute, ServerInstrumentation } from '../common/types';\nimport { captureInstrumentationError, getPathFromRequest, getPattern, normalizeRoutePath } from '../common/utils';\nimport { markInstrumentationApiUsed } from './serverGlobals';\n\n// Re-export for backward compatibility and external use\nexport { isInstrumentationApiUsed } from './serverGlobals';\n\n/**\n * Options for creating Sentry server instrumentation.\n */\nexport interface CreateSentryServerInstrumentationOptions {\n /**\n * Whether to capture errors from loaders/actions automatically.\n * @default true\n */\n captureErrors?: boolean;\n}\n\n/**\n * Creates a Sentry server instrumentation for React Router's instrumentation API.\n * @experimental\n */\nexport function createSentryServerInstrumentation(\n options: CreateSentryServerInstrumentationOptions = {},\n): ServerInstrumentation {\n const { captureErrors = true } = options;\n\n markInstrumentationApiUsed();\n DEBUG_BUILD && debug.log('React Router server instrumentation API enabled.');\n\n return {\n handler(handler: InstrumentableRequestHandler) {\n handler.instrument({\n async request(handleRequest, info) {\n const pathname = getPathFromRequest(info.request);\n const activeSpan = getActiveSpan();\n const existingRootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;\n\n if (existingRootSpan) {\n updateSpanName(existingRootSpan, `${info.request.method} ${pathname}`);\n existingRootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.instrumentation_api',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n });\n\n try {\n const result = await handleRequest();\n if (result.status === 'error' && result.error instanceof Error) {\n existingRootSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.request_handler', {\n 'http.method': info.request.method,\n 'http.url': pathname,\n });\n }\n } finally {\n await flushIfServerless();\n }\n } else {\n await startSpan(\n {\n name: `${info.request.method} ${pathname}`,\n forceTransaction: true,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.instrumentation_api',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n 'http.request.method': info.request.method,\n 'url.path': pathname,\n 'url.full': info.request.url,\n },\n },\n async span => {\n try {\n const result = await handleRequest();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.request_handler', {\n 'http.method': info.request.method,\n 'http.url': pathname,\n });\n }\n } finally {\n await flushIfServerless();\n }\n },\n );\n }\n },\n });\n },\n\n route(route: InstrumentableRoute) {\n route.instrument({\n async loader(callLoader, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.loader',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLoader();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.loader', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async action(callAction, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.action',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callAction();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.action', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async middleware(callMiddleware, info) {\n const urlPath = getPathFromRequest(info.request);\n const pattern = getPattern(info);\n const routePattern = normalizeRoutePath(pattern) || urlPath;\n\n // Update root span with parameterized route (same as loader/action)\n updateRootSpanWithRoute(info.request.method, pattern, urlPath);\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.middleware',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callMiddleware();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.middleware', {\n 'http.method': info.request.method,\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async lazy(callLazy) {\n await startSpan(\n {\n name: 'Lazy Route Load',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.lazy',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callLazy();\n if (result.status === 'error' && result.error instanceof Error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureInstrumentationError(result, captureErrors, 'react_router.lazy', {});\n }\n },\n );\n },\n });\n },\n };\n}\n\nfunction updateRootSpanWithRoute(method: string, pattern: string | undefined, urlPath: string): void {\n const activeSpan = getActiveSpan();\n if (!activeSpan) return;\n const rootSpan = getRootSpan(activeSpan);\n if (!rootSpan) return;\n\n // Skip update if URL path is invalid (failed to parse)\n if (!urlPath || urlPath === '<unknown>') {\n DEBUG_BUILD && debug.warn('Cannot update span with invalid URL path:', urlPath);\n return;\n }\n\n const hasPattern = !!pattern;\n const routeName = hasPattern ? normalizeRoutePath(pattern) || urlPath : urlPath;\n\n const rpcMetadata = getRPCMetadata(context.active());\n if (rpcMetadata?.type === RPCType.HTTP) {\n rpcMetadata.route = routeName;\n }\n\n const transactionName = `${method} ${routeName}`;\n updateSpanName(rootSpan, transactionName);\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: hasPattern ? 'route' : 'url',\n });\n\n // Also update the scope's transaction name so errors captured during this request\n // have the correct transaction name (not the initial placeholder like \"GET *\")\n getCurrentScope().setTransactionName(transactionName);\n}\n"],"names":["markInstrumentationApiUsed","DEBUG_BUILD","debug","getPathFromRequest","getActiveSpan","getRootSpan","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SPAN_STATUS_ERROR","captureInstrumentationError","flushIfServerless","startSpan","getPattern","normalizeRoutePath","getRPCMetadata","context","RPCType","ATTR_HTTP_ROUTE","getCurrentScope"],"mappings":"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"}
@@ -4,6 +4,7 @@ const instrumentation = require('@opentelemetry/instrumentation');
4
4
  const semanticConventions = require('@opentelemetry/semantic-conventions');
5
5
  const core = require('@sentry/core');
6
6
  const debugBuild = require('../../common/debug-build.js');
7
+ const serverGlobals = require('../serverGlobals.js');
7
8
  const util = require('./util.js');
8
9
 
9
10
  const supportedVersions = ['>=7.0.0'];
@@ -63,6 +64,13 @@ class ReactRouterInstrumentation extends instrumentation.InstrumentationBase {
63
64
  return originalRequestHandler(request, initialContext);
64
65
  }
65
66
 
67
+ // Skip OTEL instrumentation if instrumentation API is being used
68
+ // as it handles loader/action spans itself
69
+ if (serverGlobals.isInstrumentationApiUsed()) {
70
+ debugBuild.DEBUG_BUILD && core.debug.log('Skipping OTEL loader/action instrumentation - using instrumentation API');
71
+ return originalRequestHandler(request, initialContext);
72
+ }
73
+
66
74
  const activeSpan = core.getActiveSpan();
67
75
  const rootSpan = activeSpan && core.getRootSpan(activeSpan);
68
76
 
@@ -1 +1 @@
1
- {"version":3,"file":"reactRouter.js","sources":["../../../../src/server/instrumentation/reactRouter.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport {\n debug,\n getActiveSpan,\n getRootSpan,\n SDK_VERSION,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport type * as reactRouter from 'react-router';\nimport { DEBUG_BUILD } from '../../common/debug-build';\nimport { getOpName, getSpanName, isDataRequest } from './util';\n\ntype ReactRouterModuleExports = typeof reactRouter;\n\nconst supportedVersions = ['>=7.0.0'];\nconst COMPONENT = 'react-router';\n\n/**\n * Instrumentation for React Router's server request handler.\n * This patches the requestHandler function to add Sentry performance monitoring for data loaders.\n */\nexport class ReactRouterInstrumentation extends InstrumentationBase<InstrumentationConfig> {\n public constructor(config: InstrumentationConfig = {}) {\n super('ReactRouterInstrumentation', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the React Router server modules to be patched.\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition {\n const reactRouterServerModule = new InstrumentationNodeModuleDefinition(\n COMPONENT,\n supportedVersions,\n (moduleExports: ReactRouterModuleExports) => {\n return this._createPatchedModuleProxy(moduleExports);\n },\n (_moduleExports: unknown) => {\n // nothing to unwrap here\n return _moduleExports;\n },\n );\n\n return reactRouterServerModule;\n }\n\n /**\n * Creates a proxy around the React Router module exports that patches the createRequestHandler function.\n * This allows us to wrap the request handler to add performance monitoring for data loaders and actions.\n */\n private _createPatchedModuleProxy(moduleExports: ReactRouterModuleExports): ReactRouterModuleExports {\n return new Proxy(moduleExports, {\n get(target, prop, receiver) {\n if (prop === 'createRequestHandler') {\n const original = target[prop];\n return function sentryWrappedCreateRequestHandler(this: unknown, ...args: unknown[]) {\n const originalRequestHandler = original.apply(this, args);\n\n return async function sentryWrappedRequestHandler(request: Request, initialContext?: unknown) {\n let url: URL;\n try {\n url = new URL(request.url);\n } catch {\n return originalRequestHandler(request, initialContext);\n }\n\n // We currently just want to trace loaders and actions\n if (!isDataRequest(url.pathname)) {\n return originalRequestHandler(request, initialContext);\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n\n if (!rootSpan) {\n DEBUG_BUILD && debug.log('No active root span found, skipping tracing for data request');\n return originalRequestHandler(request, initialContext);\n }\n\n // We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route\n // So we force this to be a more sensible name here\n // TODO: try to set derived parameterized route from build here (args[0])\n const spanData = spanToJSON(rootSpan);\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET] || url.pathname;\n updateSpanName(rootSpan, `${request.method} ${target}`);\n rootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.server',\n });\n\n return startSpan(\n {\n name: getSpanName(url.pathname, request.method),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method),\n },\n },\n () => {\n return originalRequestHandler(request, initialContext);\n },\n );\n };\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","InstrumentationNodeModuleDefinition","isDataRequest","getActiveSpan","getRootSpan","DEBUG_BUILD","debug","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","getSpanName","SEMANTIC_ATTRIBUTE_SENTRY_OP","getOpName"],"mappings":"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"}
1
+ {"version":3,"file":"reactRouter.js","sources":["../../../../src/server/instrumentation/reactRouter.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport {\n debug,\n getActiveSpan,\n getRootSpan,\n SDK_VERSION,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport type * as reactRouter from 'react-router';\nimport { DEBUG_BUILD } from '../../common/debug-build';\nimport { isInstrumentationApiUsed } from '../serverGlobals';\nimport { getOpName, getSpanName, isDataRequest } from './util';\n\ntype ReactRouterModuleExports = typeof reactRouter;\n\nconst supportedVersions = ['>=7.0.0'];\nconst COMPONENT = 'react-router';\n\n/**\n * Instrumentation for React Router's server request handler.\n * This patches the requestHandler function to add Sentry performance monitoring for data loaders.\n */\nexport class ReactRouterInstrumentation extends InstrumentationBase<InstrumentationConfig> {\n public constructor(config: InstrumentationConfig = {}) {\n super('ReactRouterInstrumentation', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the React Router server modules to be patched.\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition {\n const reactRouterServerModule = new InstrumentationNodeModuleDefinition(\n COMPONENT,\n supportedVersions,\n (moduleExports: ReactRouterModuleExports) => {\n return this._createPatchedModuleProxy(moduleExports);\n },\n (_moduleExports: unknown) => {\n // nothing to unwrap here\n return _moduleExports;\n },\n );\n\n return reactRouterServerModule;\n }\n\n /**\n * Creates a proxy around the React Router module exports that patches the createRequestHandler function.\n * This allows us to wrap the request handler to add performance monitoring for data loaders and actions.\n */\n private _createPatchedModuleProxy(moduleExports: ReactRouterModuleExports): ReactRouterModuleExports {\n return new Proxy(moduleExports, {\n get(target, prop, receiver) {\n if (prop === 'createRequestHandler') {\n const original = target[prop];\n return function sentryWrappedCreateRequestHandler(this: unknown, ...args: unknown[]) {\n const originalRequestHandler = original.apply(this, args);\n\n return async function sentryWrappedRequestHandler(request: Request, initialContext?: unknown) {\n let url: URL;\n try {\n url = new URL(request.url);\n } catch {\n return originalRequestHandler(request, initialContext);\n }\n\n // We currently just want to trace loaders and actions\n if (!isDataRequest(url.pathname)) {\n return originalRequestHandler(request, initialContext);\n }\n\n // Skip OTEL instrumentation if instrumentation API is being used\n // as it handles loader/action spans itself\n if (isInstrumentationApiUsed()) {\n DEBUG_BUILD && debug.log('Skipping OTEL loader/action instrumentation - using instrumentation API');\n return originalRequestHandler(request, initialContext);\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n\n if (!rootSpan) {\n DEBUG_BUILD && debug.log('No active root span found, skipping tracing for data request');\n return originalRequestHandler(request, initialContext);\n }\n\n // We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route\n // So we force this to be a more sensible name here\n // TODO: try to set derived parameterized route from build here (args[0])\n const spanData = spanToJSON(rootSpan);\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET] || url.pathname;\n updateSpanName(rootSpan, `${request.method} ${target}`);\n rootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.server',\n });\n\n return startSpan(\n {\n name: getSpanName(url.pathname, request.method),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method),\n },\n },\n () => {\n return originalRequestHandler(request, initialContext);\n },\n );\n };\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","InstrumentationNodeModuleDefinition","isDataRequest","isInstrumentationApiUsed","DEBUG_BUILD","debug","getActiveSpan","getRootSpan","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","getSpanName","SEMANTIC_ATTRIBUTE_SENTRY_OP","getOpName"],"mappings":"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"}
@@ -4,6 +4,7 @@ const semanticConventions = require('@opentelemetry/semantic-conventions');
4
4
  const core = require('@sentry/core');
5
5
  const node = require('@sentry/node');
6
6
  const reactRouter = require('../instrumentation/reactRouter.js');
7
+ const serverGlobals = require('../serverGlobals.js');
7
8
 
8
9
  const INTEGRATION_NAME = 'ReactRouterServer';
9
10
 
@@ -25,6 +26,11 @@ const reactRouterServerIntegration = core.defineIntegration(() => {
25
26
  return {
26
27
  name: INTEGRATION_NAME,
27
28
  setupOnce() {
29
+ // Skip OTEL patching if the instrumentation API is in use
30
+ if (serverGlobals.isInstrumentationApiUsed()) {
31
+ return;
32
+ }
33
+
28
34
  if (
29
35
  (node.NODE_VERSION.major === 20 && node.NODE_VERSION.minor < 19) || // https://nodejs.org/en/blog/release/v20.19.0
30
36
  (node.NODE_VERSION.major === 22 && node.NODE_VERSION.minor < 12) // https://nodejs.org/en/blog/release/v22.12.0
@@ -38,13 +44,17 @@ const reactRouterServerIntegration = core.defineIntegration(() => {
38
44
  if (
39
45
  event.type === 'transaction' &&
40
46
  event.contexts?.trace?.data &&
41
- event.contexts.trace.data[semanticConventions.ATTR_HTTP_ROUTE] === '*' &&
42
- // This means the name has been adjusted before, but the http.route remains, so we need to remove it
43
- event.transaction !== 'GET *' &&
44
- event.transaction !== 'POST *'
47
+ event.contexts.trace.data[semanticConventions.ATTR_HTTP_ROUTE] === '*'
45
48
  ) {
46
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
47
- delete event.contexts.trace.data[semanticConventions.ATTR_HTTP_ROUTE];
49
+ const origin = event.contexts.trace.origin;
50
+ const isInstrumentationApiOrigin = origin?.includes('instrumentation_api');
51
+
52
+ // For instrumentation_api, always clean up bogus `*` route since we set better names
53
+ // For legacy, only clean up if the name has been adjusted (not METHOD *)
54
+ if (isInstrumentationApiOrigin || !event.transaction?.endsWith(' *')) {
55
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
56
+ delete event.contexts.trace.data[semanticConventions.ATTR_HTTP_ROUTE];
57
+ }
48
58
  }
49
59
 
50
60
  return event;
@@ -1 +1 @@
1
- {"version":3,"file":"reactRouterServer.js","sources":["../../../../src/server/integration/reactRouterServer.ts"],"sourcesContent":["import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce, NODE_VERSION } from '@sentry/node';\nimport { ReactRouterInstrumentation } from '../instrumentation/reactRouter';\n\nconst INTEGRATION_NAME = 'ReactRouterServer';\n\nconst instrumentReactRouter = generateInstrumentOnce(INTEGRATION_NAME, () => {\n return new ReactRouterInstrumentation();\n});\n\nexport const instrumentReactRouterServer = Object.assign(\n (): void => {\n instrumentReactRouter();\n },\n { id: INTEGRATION_NAME },\n);\n\n/**\n * Integration capturing tracing data for React Router server functions.\n */\nexport const reactRouterServerIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n if (\n (NODE_VERSION.major === 20 && NODE_VERSION.minor < 19) || // https://nodejs.org/en/blog/release/v20.19.0\n (NODE_VERSION.major === 22 && NODE_VERSION.minor < 12) // https://nodejs.org/en/blog/release/v22.12.0\n ) {\n instrumentReactRouterServer();\n }\n },\n processEvent(event) {\n // Express generates bogus `*` routes for data loaders, which we want to remove here\n // we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point\n if (\n event.type === 'transaction' &&\n event.contexts?.trace?.data &&\n event.contexts.trace.data[ATTR_HTTP_ROUTE] === '*' &&\n // This means the name has been adjusted before, but the http.route remains, so we need to remove it\n event.transaction !== 'GET *' &&\n event.transaction !== 'POST *'\n ) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete event.contexts.trace.data[ATTR_HTTP_ROUTE];\n }\n\n return event;\n },\n };\n});\n"],"names":["generateInstrumentOnce","ReactRouterInstrumentation","defineIntegration","NODE_VERSION","ATTR_HTTP_ROUTE"],"mappings":";;;;;;;AAKA,MAAM,gBAAA,GAAmB,mBAAmB;;AAE5C,MAAM,qBAAA,GAAwBA,2BAAsB,CAAC,gBAAgB,EAAE,MAAM;AAC7E,EAAE,OAAO,IAAIC,sCAA0B,EAAE;AACzC,CAAC,CAAC;;AAEK,MAAM,2BAAA,GAA8B,MAAM,CAAC,MAAM;AACxD,EAAE,MAAY;AACd,IAAI,qBAAqB,EAAE;AAC3B,EAAE,CAAC;AACH,EAAE,EAAE,EAAE,EAAE,gBAAA,EAAkB;AAC1B;;AAEA;AACA;AACA;MACa,4BAAA,GAA+BC,sBAAiB,CAAC,MAAM;AACpE,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM;AACN,QAAQ,CAACC,iBAAY,CAAC,KAAA,KAAU,EAAA,IAAMA,iBAAY,CAAC,KAAA,GAAQ,EAAE;AAC7D,SAASA,iBAAY,CAAC,KAAA,KAAU,EAAA,IAAMA,iBAAY,CAAC,KAAA,GAAQ,EAAE,CAAA;AAC7D,QAAQ;AACR,QAAQ,2BAA2B,EAAE;AACrC,MAAM;AACN,IAAI,CAAC;AACL,IAAI,YAAY,CAAC,KAAK,EAAE;AACxB;AACA;AACA,MAAM;AACN,QAAQ,KAAK,CAAC,IAAA,KAAS,aAAA;AACvB,QAAQ,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAA;AAC/B,QAAQ,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAACC,mCAAe,CAAA,KAAM,GAAA;AACvD;AACA,QAAQ,KAAK,CAAC,WAAA,KAAgB,OAAA;AAC9B,QAAQ,KAAK,CAAC,WAAA,KAAgB;AAC9B,QAAQ;AACR;AACA,QAAQ,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAACA,mCAAe,CAAC;AACzD,MAAM;;AAEN,MAAM,OAAO,KAAK;AAClB,IAAI,CAAC;AACL,GAAG;AACH,CAAC;;;;;"}
1
+ {"version":3,"file":"reactRouterServer.js","sources":["../../../../src/server/integration/reactRouterServer.ts"],"sourcesContent":["import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce, NODE_VERSION } from '@sentry/node';\nimport { ReactRouterInstrumentation } from '../instrumentation/reactRouter';\nimport { isInstrumentationApiUsed } from '../serverGlobals';\n\nconst INTEGRATION_NAME = 'ReactRouterServer';\n\nconst instrumentReactRouter = generateInstrumentOnce(INTEGRATION_NAME, () => {\n return new ReactRouterInstrumentation();\n});\n\nexport const instrumentReactRouterServer = Object.assign(\n (): void => {\n instrumentReactRouter();\n },\n { id: INTEGRATION_NAME },\n);\n\n/**\n * Integration capturing tracing data for React Router server functions.\n */\nexport const reactRouterServerIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n // Skip OTEL patching if the instrumentation API is in use\n if (isInstrumentationApiUsed()) {\n return;\n }\n\n if (\n (NODE_VERSION.major === 20 && NODE_VERSION.minor < 19) || // https://nodejs.org/en/blog/release/v20.19.0\n (NODE_VERSION.major === 22 && NODE_VERSION.minor < 12) // https://nodejs.org/en/blog/release/v22.12.0\n ) {\n instrumentReactRouterServer();\n }\n },\n processEvent(event) {\n // Express generates bogus `*` routes for data loaders, which we want to remove here\n // we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point\n if (\n event.type === 'transaction' &&\n event.contexts?.trace?.data &&\n event.contexts.trace.data[ATTR_HTTP_ROUTE] === '*'\n ) {\n const origin = event.contexts.trace.origin;\n const isInstrumentationApiOrigin = origin?.includes('instrumentation_api');\n\n // For instrumentation_api, always clean up bogus `*` route since we set better names\n // For legacy, only clean up if the name has been adjusted (not METHOD *)\n if (isInstrumentationApiOrigin || !event.transaction?.endsWith(' *')) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete event.contexts.trace.data[ATTR_HTTP_ROUTE];\n }\n }\n\n return event;\n },\n };\n});\n"],"names":["generateInstrumentOnce","ReactRouterInstrumentation","defineIntegration","isInstrumentationApiUsed","NODE_VERSION","ATTR_HTTP_ROUTE"],"mappings":";;;;;;;;AAMA,MAAM,gBAAA,GAAmB,mBAAmB;;AAE5C,MAAM,qBAAA,GAAwBA,2BAAsB,CAAC,gBAAgB,EAAE,MAAM;AAC7E,EAAE,OAAO,IAAIC,sCAA0B,EAAE;AACzC,CAAC,CAAC;;AAEK,MAAM,2BAAA,GAA8B,MAAM,CAAC,MAAM;AACxD,EAAE,MAAY;AACd,IAAI,qBAAqB,EAAE;AAC3B,EAAE,CAAC;AACH,EAAE,EAAE,EAAE,EAAE,gBAAA,EAAkB;AAC1B;;AAEA;AACA;AACA;MACa,4BAAA,GAA+BC,sBAAiB,CAAC,MAAM;AACpE,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB;AACA,MAAM,IAAIC,sCAAwB,EAAE,EAAE;AACtC,QAAQ;AACR,MAAM;;AAEN,MAAM;AACN,QAAQ,CAACC,iBAAY,CAAC,KAAA,KAAU,EAAA,IAAMA,iBAAY,CAAC,KAAA,GAAQ,EAAE;AAC7D,SAASA,iBAAY,CAAC,KAAA,KAAU,EAAA,IAAMA,iBAAY,CAAC,KAAA,GAAQ,EAAE,CAAA;AAC7D,QAAQ;AACR,QAAQ,2BAA2B,EAAE;AACrC,MAAM;AACN,IAAI,CAAC;AACL,IAAI,YAAY,CAAC,KAAK,EAAE;AACxB;AACA;AACA,MAAM;AACN,QAAQ,KAAK,CAAC,IAAA,KAAS,aAAA;AACvB,QAAQ,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAA;AAC/B,QAAQ,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAACC,mCAAe,CAAA,KAAM;AACvD,QAAQ;AACR,QAAQ,MAAM,SAAS,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM;AAClD,QAAQ,MAAM,6BAA6B,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC;;AAElF;AACA;AACA,QAAQ,IAAI,0BAAA,IAA8B,CAAC,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC9E;AACA,UAAU,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAACA,mCAAe,CAAC;AAC3D,QAAQ;AACR,MAAM;;AAEN,MAAM,OAAO,KAAK;AAClB,IAAI,CAAC;AACL,GAAG;AACH,CAAC;;;;;"}
@@ -0,0 +1,25 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const core = require('@sentry/core');
4
+
5
+ const SENTRY_SERVER_INSTRUMENTATION_FLAG = '__sentryReactRouterServerInstrumentationUsed';
6
+
7
+ /**
8
+ * Mark that the React Router instrumentation API is being used on the server.
9
+ * @internal
10
+ */
11
+ function markInstrumentationApiUsed() {
12
+ (core.GLOBAL_OBJ )[SENTRY_SERVER_INSTRUMENTATION_FLAG] = true;
13
+ }
14
+
15
+ /**
16
+ * Check if React Router's instrumentation API is being used on the server.
17
+ * @experimental
18
+ */
19
+ function isInstrumentationApiUsed() {
20
+ return !!(core.GLOBAL_OBJ )[SENTRY_SERVER_INSTRUMENTATION_FLAG];
21
+ }
22
+
23
+ exports.isInstrumentationApiUsed = isInstrumentationApiUsed;
24
+ exports.markInstrumentationApiUsed = markInstrumentationApiUsed;
25
+ //# sourceMappingURL=serverGlobals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverGlobals.js","sources":["../../../src/server/serverGlobals.ts"],"sourcesContent":["import { GLOBAL_OBJ } from '@sentry/core';\n\nconst SENTRY_SERVER_INSTRUMENTATION_FLAG = '__sentryReactRouterServerInstrumentationUsed';\n\ntype GlobalObjWithFlag = typeof GLOBAL_OBJ & {\n [SENTRY_SERVER_INSTRUMENTATION_FLAG]?: boolean;\n};\n\n/**\n * Mark that the React Router instrumentation API is being used on the server.\n * @internal\n */\nexport function markInstrumentationApiUsed(): void {\n (GLOBAL_OBJ as GlobalObjWithFlag)[SENTRY_SERVER_INSTRUMENTATION_FLAG] = true;\n}\n\n/**\n * Check if React Router's instrumentation API is being used on the server.\n * @experimental\n */\nexport function isInstrumentationApiUsed(): boolean {\n return !!(GLOBAL_OBJ as GlobalObjWithFlag)[SENTRY_SERVER_INSTRUMENTATION_FLAG];\n}\n"],"names":["GLOBAL_OBJ"],"mappings":";;;;AAEA,MAAM,kCAAA,GAAqC,8CAA8C;;AAMzF;AACA;AACA;AACA;AACO,SAAS,0BAA0B,GAAS;AACnD,EAAE,CAACA,eAAA,GAAiC,kCAAkC,CAAA,GAAI,IAAI;AAC9E;;AAEA;AACA;AACA;AACA;AACO,SAAS,wBAAwB,GAAY;AACpD,EAAE,OAAO,CAAC,CAAC,CAACA,kBAAiC,kCAAkC,CAAC;AAChF;;;;;"}
@@ -4,6 +4,7 @@ const api = require('@opentelemetry/api');
4
4
  const core$1 = require('@opentelemetry/core');
5
5
  const semanticConventions = require('@opentelemetry/semantic-conventions');
6
6
  const core = require('@sentry/core');
7
+ const serverGlobals = require('./serverGlobals.js');
7
8
 
8
9
  /**
9
10
  * Wraps the original handleRequest function to add Sentry instrumentation.
@@ -28,7 +29,8 @@ function wrapSentryHandleRequest(
28
29
  const rootSpan = activeSpan ? core.getRootSpan(activeSpan) : undefined;
29
30
 
30
31
  if (parameterizedPath && rootSpan) {
31
- const routeName = `/${parameterizedPath}`;
32
+ // Normalize route name - avoid "//" for root routes
33
+ const routeName = parameterizedPath.startsWith('/') ? parameterizedPath : `/${parameterizedPath}`;
32
34
 
33
35
  // The express instrumentation writes on the rpcMetadata and that ends up stomping on the `http.route` attribute.
34
36
  const rpcMetadata = core$1.getRPCMetadata(api.context.active());
@@ -37,12 +39,25 @@ function wrapSentryHandleRequest(
37
39
  rpcMetadata.route = routeName;
38
40
  }
39
41
 
40
- // The span exporter picks up the `http.route` (ATTR_HTTP_ROUTE) attribute to set the transaction name
41
- rootSpan.setAttributes({
42
- [semanticConventions.ATTR_HTTP_ROUTE]: routeName,
43
- [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
44
- [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.request_handler',
45
- });
42
+ const transactionName = `${request.method} ${routeName}`;
43
+
44
+ core.updateSpanName(rootSpan, transactionName);
45
+ core.getCurrentScope().setTransactionName(transactionName);
46
+
47
+ // Set route attributes - acts as fallback for lazy-only routes when using instrumentation API
48
+ // Don't override origin when instrumentation API is used (preserve instrumentation_api origin)
49
+ if (serverGlobals.isInstrumentationApiUsed()) {
50
+ rootSpan.setAttributes({
51
+ [semanticConventions.ATTR_HTTP_ROUTE]: routeName,
52
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
53
+ });
54
+ } else {
55
+ rootSpan.setAttributes({
56
+ [semanticConventions.ATTR_HTTP_ROUTE]: routeName,
57
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
58
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.request_handler',
59
+ });
60
+ }
46
61
  }
47
62
 
48
63
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"wrapSentryHandleRequest.js","sources":["../../../src/server/wrapSentryHandleRequest.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { getRPCMetadata, RPCType } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport {\n flushIfServerless,\n getActiveSpan,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n} from '@sentry/core';\nimport type { AppLoadContext, EntryContext, RouterContextProvider } from 'react-router';\n\ntype OriginalHandleRequestWithoutMiddleware = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n) => Promise<unknown>;\n\ntype OriginalHandleRequestWithMiddleware = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: RouterContextProvider,\n) => Promise<unknown>;\n\n/**\n * Wraps the original handleRequest function to add Sentry instrumentation.\n *\n * @param originalHandle - The original handleRequest function to wrap\n * @returns A wrapped version of the handle request function with Sentry instrumentation\n */\nexport function wrapSentryHandleRequest(\n originalHandle: OriginalHandleRequestWithoutMiddleware,\n): OriginalHandleRequestWithoutMiddleware;\n/**\n * Wraps the original handleRequest function to add Sentry instrumentation.\n *\n * @param originalHandle - The original handleRequest function to wrap\n * @returns A wrapped version of the handle request function with Sentry instrumentation\n */\nexport function wrapSentryHandleRequest(\n originalHandle: OriginalHandleRequestWithMiddleware,\n): OriginalHandleRequestWithMiddleware;\n/**\n * Wraps the original handleRequest function to add Sentry instrumentation.\n *\n * @param originalHandle - The original handleRequest function to wrap\n * @returns A wrapped version of the handle request function with Sentry instrumentation\n */\nexport function wrapSentryHandleRequest(\n originalHandle: OriginalHandleRequestWithoutMiddleware | OriginalHandleRequestWithMiddleware,\n): OriginalHandleRequestWithoutMiddleware | OriginalHandleRequestWithMiddleware {\n return async function sentryInstrumentedHandleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext | RouterContextProvider,\n ) {\n const parameterizedPath =\n routerContext?.staticHandlerContext?.matches?.[routerContext.staticHandlerContext.matches.length - 1]?.route.path;\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;\n\n if (parameterizedPath && rootSpan) {\n const routeName = `/${parameterizedPath}`;\n\n // The express instrumentation writes on the rpcMetadata and that ends up stomping on the `http.route` attribute.\n const rpcMetadata = getRPCMetadata(context.active());\n\n if (rpcMetadata?.type === RPCType.HTTP) {\n rpcMetadata.route = routeName;\n }\n\n // The span exporter picks up the `http.route` (ATTR_HTTP_ROUTE) attribute to set the transaction name\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.request_handler',\n });\n }\n\n try {\n // Type guard to call the correct overload based on loadContext type\n if (isRouterContextProvider(loadContext)) {\n // loadContext is RouterContextProvider\n return await (originalHandle as OriginalHandleRequestWithMiddleware)(\n request,\n responseStatusCode,\n responseHeaders,\n routerContext,\n loadContext,\n );\n } else {\n // loadContext is AppLoadContext\n return await (originalHandle as OriginalHandleRequestWithoutMiddleware)(\n request,\n responseStatusCode,\n responseHeaders,\n routerContext,\n loadContext,\n );\n }\n } finally {\n await flushIfServerless();\n }\n\n /**\n * Helper type guard to determine if the context is a RouterContextProvider.\n *\n * @param ctx - The context to check\n * @returns True if the context is a RouterContextProvider\n */\n function isRouterContextProvider(ctx: AppLoadContext | RouterContextProvider): ctx is RouterContextProvider {\n return typeof (ctx as RouterContextProvider)?.get === 'function';\n }\n };\n}\n\n// todo(v11): remove this\n/** @deprecated Use `wrapSentryHandleRequest` instead. */\nexport const sentryHandleRequest = wrapSentryHandleRequest;\n"],"names":["getActiveSpan","getRootSpan","getRPCMetadata","context","RPCType","ATTR_HTTP_ROUTE","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","flushIfServerless"],"mappings":";;;;;;;AA8CA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,uBAAuB;AACvC,EAAE,cAAc;AAChB,EAAgF;AAChF,EAAE,OAAO,eAAe,+BAA+B;AACvD,IAAI,OAAO;AACX,IAAI,kBAAkB;AACtB,IAAI,eAAe;AACnB,IAAI,aAAa;AACjB,IAAI,WAAW;AACf,IAAI;AACJ,IAAI,MAAM,iBAAA;AACV,MAAM,aAAa,EAAE,oBAAoB,EAAE,OAAO,GAAG,aAAa,CAAC,oBAAoB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI;;AAEvH,IAAI,MAAM,UAAA,GAAaA,kBAAa,EAAE;AACtC,IAAI,MAAM,QAAA,GAAW,UAAA,GAAaC,gBAAW,CAAC,UAAU,CAAA,GAAI,SAAS;;AAErE,IAAI,IAAI,iBAAA,IAAqB,QAAQ,EAAE;AACvC,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAA;;AAEA;AACA,MAAA,MAAA,WAAA,GAAAC,qBAAA,CAAAC,WAAA,CAAA,MAAA,EAAA,CAAA;;AAEA,MAAA,IAAA,WAAA,EAAA,IAAA,KAAAC,cAAA,CAAA,IAAA,EAAA;AACA,QAAA,WAAA,CAAA,KAAA,GAAA,SAAA;AACA,MAAA;;AAEA;AACA,MAAA,QAAA,CAAA,aAAA,CAAA;AACA,QAAA,CAAAC,mCAAA,GAAA,SAAA;AACA,QAAA,CAAAC,qCAAA,GAAA,OAAA;AACA,QAAA,CAAAC,qCAAA,GAAA,wCAAA;AACA,OAAA,CAAA;AACA,IAAA;;AAEA,IAAA,IAAA;AACA;AACA,MAAA,IAAA,uBAAA,CAAA,WAAA,CAAA,EAAA;AACA;AACA,QAAA,OAAA,MAAA,CAAA,cAAA;AACA,UAAA,OAAA;AACA,UAAA,kBAAA;AACA,UAAA,eAAA;AACA,UAAA,aAAA;AACA,UAAA,WAAA;AACA,SAAA;AACA,MAAA,CAAA,MAAA;AACA;AACA,QAAA,OAAA,MAAA,CAAA,cAAA;AACA,UAAA,OAAA;AACA,UAAA,kBAAA;AACA,UAAA,eAAA;AACA,UAAA,aAAA;AACA,UAAA,WAAA;AACA,SAAA;AACA,MAAA;AACA,IAAA,CAAA,SAAA;AACA,MAAA,MAAAC,sBAAA,EAAA;AACA,IAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA,SAAA,uBAAA,CAAA,GAAA,EAAA;AACA,MAAA,OAAA,OAAA,CAAA,GAAA,IAAA,GAAA,KAAA,UAAA;AACA,IAAA;AACA,EAAA,CAAA;AACA;;AAEA;AACA;AACA,MAAA,mBAAA,GAAA;;;;;"}
1
+ {"version":3,"file":"wrapSentryHandleRequest.js","sources":["../../../src/server/wrapSentryHandleRequest.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { getRPCMetadata, RPCType } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport {\n flushIfServerless,\n getActiveSpan,\n getCurrentScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n updateSpanName,\n} from '@sentry/core';\nimport type { AppLoadContext, EntryContext, RouterContextProvider } from 'react-router';\nimport { isInstrumentationApiUsed } from './serverGlobals';\n\ntype OriginalHandleRequestWithoutMiddleware = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n) => Promise<unknown>;\n\ntype OriginalHandleRequestWithMiddleware = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: RouterContextProvider,\n) => Promise<unknown>;\n\n/**\n * Wraps the original handleRequest function to add Sentry instrumentation.\n *\n * @param originalHandle - The original handleRequest function to wrap\n * @returns A wrapped version of the handle request function with Sentry instrumentation\n */\nexport function wrapSentryHandleRequest(\n originalHandle: OriginalHandleRequestWithoutMiddleware,\n): OriginalHandleRequestWithoutMiddleware;\n/**\n * Wraps the original handleRequest function to add Sentry instrumentation.\n *\n * @param originalHandle - The original handleRequest function to wrap\n * @returns A wrapped version of the handle request function with Sentry instrumentation\n */\nexport function wrapSentryHandleRequest(\n originalHandle: OriginalHandleRequestWithMiddleware,\n): OriginalHandleRequestWithMiddleware;\n/**\n * Wraps the original handleRequest function to add Sentry instrumentation.\n *\n * @param originalHandle - The original handleRequest function to wrap\n * @returns A wrapped version of the handle request function with Sentry instrumentation\n */\nexport function wrapSentryHandleRequest(\n originalHandle: OriginalHandleRequestWithoutMiddleware | OriginalHandleRequestWithMiddleware,\n): OriginalHandleRequestWithoutMiddleware | OriginalHandleRequestWithMiddleware {\n return async function sentryInstrumentedHandleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext | RouterContextProvider,\n ) {\n const parameterizedPath =\n routerContext?.staticHandlerContext?.matches?.[routerContext.staticHandlerContext.matches.length - 1]?.route.path;\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;\n\n if (parameterizedPath && rootSpan) {\n // Normalize route name - avoid \"//\" for root routes\n const routeName = parameterizedPath.startsWith('/') ? parameterizedPath : `/${parameterizedPath}`;\n\n // The express instrumentation writes on the rpcMetadata and that ends up stomping on the `http.route` attribute.\n const rpcMetadata = getRPCMetadata(context.active());\n\n if (rpcMetadata?.type === RPCType.HTTP) {\n rpcMetadata.route = routeName;\n }\n\n const transactionName = `${request.method} ${routeName}`;\n\n updateSpanName(rootSpan, transactionName);\n getCurrentScope().setTransactionName(transactionName);\n\n // Set route attributes - acts as fallback for lazy-only routes when using instrumentation API\n // Don't override origin when instrumentation API is used (preserve instrumentation_api origin)\n if (isInstrumentationApiUsed()) {\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n });\n } else {\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.request_handler',\n });\n }\n }\n\n try {\n // Type guard to call the correct overload based on loadContext type\n if (isRouterContextProvider(loadContext)) {\n // loadContext is RouterContextProvider\n return await (originalHandle as OriginalHandleRequestWithMiddleware)(\n request,\n responseStatusCode,\n responseHeaders,\n routerContext,\n loadContext,\n );\n } else {\n // loadContext is AppLoadContext\n return await (originalHandle as OriginalHandleRequestWithoutMiddleware)(\n request,\n responseStatusCode,\n responseHeaders,\n routerContext,\n loadContext,\n );\n }\n } finally {\n await flushIfServerless();\n }\n\n /**\n * Helper type guard to determine if the context is a RouterContextProvider.\n *\n * @param ctx - The context to check\n * @returns True if the context is a RouterContextProvider\n */\n function isRouterContextProvider(ctx: AppLoadContext | RouterContextProvider): ctx is RouterContextProvider {\n return typeof (ctx as RouterContextProvider)?.get === 'function';\n }\n };\n}\n\n// todo(v11): remove this\n/** @deprecated Use `wrapSentryHandleRequest` instead. */\nexport const sentryHandleRequest = wrapSentryHandleRequest;\n"],"names":["getActiveSpan","getRootSpan","getRPCMetadata","context","RPCType","updateSpanName","getCurrentScope","isInstrumentationApiUsed","ATTR_HTTP_ROUTE","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","flushIfServerless"],"mappings":";;;;;;;;AAiDA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,uBAAuB;AACvC,EAAE,cAAc;AAChB,EAAgF;AAChF,EAAE,OAAO,eAAe,+BAA+B;AACvD,IAAI,OAAO;AACX,IAAI,kBAAkB;AACtB,IAAI,eAAe;AACnB,IAAI,aAAa;AACjB,IAAI,WAAW;AACf,IAAI;AACJ,IAAI,MAAM,iBAAA;AACV,MAAM,aAAa,EAAE,oBAAoB,EAAE,OAAO,GAAG,aAAa,CAAC,oBAAoB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI;;AAEvH,IAAI,MAAM,UAAA,GAAaA,kBAAa,EAAE;AACtC,IAAI,MAAM,QAAA,GAAW,UAAA,GAAaC,gBAAW,CAAC,UAAU,CAAA,GAAI,SAAS;;AAErE,IAAI,IAAI,iBAAA,IAAqB,QAAQ,EAAE;AACvC;AACA,MAAM,MAAM,SAAA,GAAY,iBAAiB,CAAC,UAAU,CAAC,GAAG,CAAA,GAAI,oBAAoB,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAA;;AAEA;AACA,MAAA,MAAA,WAAA,GAAAC,qBAAA,CAAAC,WAAA,CAAA,MAAA,EAAA,CAAA;;AAEA,MAAA,IAAA,WAAA,EAAA,IAAA,KAAAC,cAAA,CAAA,IAAA,EAAA;AACA,QAAA,WAAA,CAAA,KAAA,GAAA,SAAA;AACA,MAAA;;AAEA,MAAA,MAAA,eAAA,GAAA,CAAA,EAAA,OAAA,CAAA,MAAA,CAAA,CAAA,EAAA,SAAA,CAAA,CAAA;;AAEA,MAAAC,mBAAA,CAAA,QAAA,EAAA,eAAA,CAAA;AACA,MAAAC,oBAAA,EAAA,CAAA,kBAAA,CAAA,eAAA,CAAA;;AAEA;AACA;AACA,MAAA,IAAAC,sCAAA,EAAA,EAAA;AACA,QAAA,QAAA,CAAA,aAAA,CAAA;AACA,UAAA,CAAAC,mCAAA,GAAA,SAAA;AACA,UAAA,CAAAC,qCAAA,GAAA,OAAA;AACA,SAAA,CAAA;AACA,MAAA,CAAA,MAAA;AACA,QAAA,QAAA,CAAA,aAAA,CAAA;AACA,UAAA,CAAAD,mCAAA,GAAA,SAAA;AACA,UAAA,CAAAC,qCAAA,GAAA,OAAA;AACA,UAAA,CAAAC,qCAAA,GAAA,wCAAA;AACA,SAAA,CAAA;AACA,MAAA;AACA,IAAA;;AAEA,IAAA,IAAA;AACA;AACA,MAAA,IAAA,uBAAA,CAAA,WAAA,CAAA,EAAA;AACA;AACA,QAAA,OAAA,MAAA,CAAA,cAAA;AACA,UAAA,OAAA;AACA,UAAA,kBAAA;AACA,UAAA,eAAA;AACA,UAAA,aAAA;AACA,UAAA,WAAA;AACA,SAAA;AACA,MAAA,CAAA,MAAA;AACA;AACA,QAAA,OAAA,MAAA,CAAA,cAAA;AACA,UAAA,OAAA;AACA,UAAA,kBAAA;AACA,UAAA,eAAA;AACA,UAAA,aAAA;AACA,UAAA,WAAA;AACA,SAAA;AACA,MAAA;AACA,IAAA,CAAA,SAAA;AACA,MAAA,MAAAC,sBAAA,EAAA;AACA,IAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA,SAAA,uBAAA,CAAA,GAAA,EAAA;AACA,MAAA,OAAA,OAAA,CAAA,GAAA,IAAA,GAAA,KAAA,UAAA;AACA,IAAA;AACA,EAAA,CAAA;AACA;;AAEA;AACA;AACA,MAAA,mBAAA,GAAA;;;;;"}