@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
@@ -2,6 +2,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const semanticConventions = require('@opentelemetry/semantic-conventions');
4
4
  const core = require('@sentry/core');
5
+ const debugBuild = require('../common/debug-build.js');
6
+ const serverGlobals = require('./serverGlobals.js');
7
+
8
+ // Track if we've already warned about duplicate instrumentation
9
+ let hasWarnedAboutDuplicateActionInstrumentation = false;
5
10
 
6
11
  /**
7
12
  * Wraps a React Router server action function with Sentry performance monitoring.
@@ -22,8 +27,23 @@ const core = require('@sentry/core');
22
27
  * );
23
28
  * ```
24
29
  */
25
- function wrapServerAction(options = {}, actionFn) {
30
+ function wrapServerAction(
31
+ options = {},
32
+ actionFn,
33
+ ) {
26
34
  return async function (args) {
35
+ // Skip instrumentation if instrumentation API is already handling it
36
+ if (serverGlobals.isInstrumentationApiUsed()) {
37
+ if (debugBuild.DEBUG_BUILD && !hasWarnedAboutDuplicateActionInstrumentation) {
38
+ hasWarnedAboutDuplicateActionInstrumentation = true;
39
+ core.debug.warn(
40
+ 'wrapServerAction is redundant when using the instrumentation API. ' +
41
+ 'The action is already instrumented automatically. You can safely remove wrapServerAction.',
42
+ );
43
+ }
44
+ return actionFn(args);
45
+ }
46
+
27
47
  const name = options.name || 'Executing Server Action';
28
48
  const active = core.getActiveSpan();
29
49
  if (active) {
@@ -1 +1 @@
1
- {"version":3,"file":"wrapServerAction.js","sources":["../../../src/server/wrapServerAction.ts"],"sourcesContent":["import { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport type { SpanAttributes } from '@sentry/core';\nimport {\n flushIfServerless,\n getActiveSpan,\n getRootSpan,\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 { ActionFunctionArgs } from 'react-router';\n\ntype SpanOptions = {\n name?: string;\n attributes?: SpanAttributes;\n};\n\n/**\n * Wraps a React Router server action function with Sentry performance monitoring.\n * @param options - Optional span configuration options including name, operation, description and attributes\n * @param actionFn - The server action function to wrap\n *\n * @example\n * ```ts\n * // Wrap an action function with custom span options\n * export const action = wrapServerAction(\n * {\n * name: 'Submit Form Data',\n * description: 'Processes form submission data',\n * },\n * async ({ request }) => {\n * // ... your action logic\n * }\n * );\n * ```\n */\nexport function wrapServerAction<T>(options: SpanOptions = {}, actionFn: (args: ActionFunctionArgs) => Promise<T>) {\n return async function (args: ActionFunctionArgs) {\n const name = options.name || 'Executing Server Action';\n const active = getActiveSpan();\n if (active) {\n const root = getRootSpan(active);\n const spanData = spanToJSON(root);\n if (spanData.origin === 'auto.http.otel.http') {\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET];\n\n if (target) {\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 updateSpanName(root, `${args.request.method} ${target}`);\n root.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.action',\n });\n }\n }\n }\n\n try {\n return await startSpan(\n {\n name,\n ...options,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.action',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.action',\n ...options.attributes,\n },\n },\n () => actionFn(args),\n );\n } finally {\n await flushIfServerless();\n }\n };\n}\n"],"names":["getActiveSpan","getRootSpan","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","flushIfServerless"],"mappings":";;;;;AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAI,OAAO,GAAgB,EAAE,EAAE,QAAQ,EAA4C;AACnH,EAAE,OAAO,gBAAgB,IAAI,EAAsB;AACnD,IAAI,MAAM,IAAA,GAAO,OAAO,CAAC,IAAA,IAAQ,yBAAyB;AAC1D,IAAI,MAAM,MAAA,GAASA,kBAAa,EAAE;AAClC,IAAI,IAAI,MAAM,EAAE;AAChB,MAAM,MAAM,IAAA,GAAOC,gBAAW,CAAC,MAAM,CAAC;AACtC,MAAM,MAAM,QAAA,GAAWC,eAAU,CAAC,IAAI,CAAC;AACvC,MAAM,IAAI,QAAQ,CAAC,MAAA,KAAW,qBAAqB,EAAE;AACrD;AACA,QAAQ,MAAM,SAAS,QAAQ,CAAC,IAAI,CAACC,wCAAoB,CAAC;;AAE1D,QAAQ,IAAI,MAAM,EAAE;AACpB;AACA;AACA,UAAUC,mBAAc,CAAC,IAAI,EAAE,CAAC,EAAA,IAAA,CAAA,OAAA,CAAA,MAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA,CAAA;AACA,UAAA,IAAA,CAAA,aAAA,CAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,KAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,+BAAA;AACA,WAAA,CAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;;AAEA,IAAA,IAAA;AACA,MAAA,OAAA,MAAAC,cAAA;AACA,QAAA;AACA,UAAA,IAAA;AACA,UAAA,GAAA,OAAA;AACA,UAAA,UAAA,EAAA;AACA,YAAA,CAAAD,qCAAA,GAAA,+BAAA;AACA,YAAA,CAAAE,iCAAA,GAAA,8BAAA;AACA,YAAA,GAAA,OAAA,CAAA,UAAA;AACA,WAAA;AACA,SAAA;AACA,QAAA,MAAA,QAAA,CAAA,IAAA,CAAA;AACA,OAAA;AACA,IAAA,CAAA,SAAA;AACA,MAAA,MAAAC,sBAAA,EAAA;AACA,IAAA;AACA,EAAA,CAAA;AACA;;;;"}
1
+ {"version":3,"file":"wrapServerAction.js","sources":["../../../src/server/wrapServerAction.ts"],"sourcesContent":["import { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport type { SpanAttributes } from '@sentry/core';\nimport {\n debug,\n flushIfServerless,\n getActiveSpan,\n getRootSpan,\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 { ActionFunctionArgs } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { isInstrumentationApiUsed } from './serverGlobals';\n\ntype SpanOptions = {\n name?: string;\n attributes?: SpanAttributes;\n};\n\n// Track if we've already warned about duplicate instrumentation\nlet hasWarnedAboutDuplicateActionInstrumentation = false;\n\n/**\n * Wraps a React Router server action function with Sentry performance monitoring.\n * @param options - Optional span configuration options including name, operation, description and attributes\n * @param actionFn - The server action function to wrap\n *\n * @example\n * ```ts\n * // Wrap an action function with custom span options\n * export const action = wrapServerAction(\n * {\n * name: 'Submit Form Data',\n * description: 'Processes form submission data',\n * },\n * async ({ request }) => {\n * // ... your action logic\n * }\n * );\n * ```\n */\nexport function wrapServerAction<T>(\n options: SpanOptions = {},\n actionFn: (args: ActionFunctionArgs) => Promise<T>,\n): (args: ActionFunctionArgs) => Promise<T> {\n return async function (args: ActionFunctionArgs): Promise<T> {\n // Skip instrumentation if instrumentation API is already handling it\n if (isInstrumentationApiUsed()) {\n if (DEBUG_BUILD && !hasWarnedAboutDuplicateActionInstrumentation) {\n hasWarnedAboutDuplicateActionInstrumentation = true;\n debug.warn(\n 'wrapServerAction is redundant when using the instrumentation API. ' +\n 'The action is already instrumented automatically. You can safely remove wrapServerAction.',\n );\n }\n return actionFn(args);\n }\n\n const name = options.name || 'Executing Server Action';\n const active = getActiveSpan();\n if (active) {\n const root = getRootSpan(active);\n const spanData = spanToJSON(root);\n if (spanData.origin === 'auto.http.otel.http') {\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET];\n\n if (target) {\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 updateSpanName(root, `${args.request.method} ${target}`);\n root.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.action',\n });\n }\n }\n }\n\n try {\n return await startSpan(\n {\n name,\n ...options,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.action',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.action',\n ...options.attributes,\n },\n },\n () => actionFn(args),\n );\n } finally {\n await flushIfServerless();\n }\n };\n}\n"],"names":["isInstrumentationApiUsed","DEBUG_BUILD","debug","getActiveSpan","getRootSpan","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","flushIfServerless"],"mappings":";;;;;;;AAuBA;AACA,IAAI,4CAAA,GAA+C,KAAK;;AAExD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB;AAChC,EAAE,OAAO,GAAgB,EAAE;AAC3B,EAAE,QAAQ;AACV,EAA4C;AAC5C,EAAE,OAAO,gBAAgB,IAAI,EAAkC;AAC/D;AACA,IAAI,IAAIA,sCAAwB,EAAE,EAAE;AACpC,MAAM,IAAIC,sBAAA,IAAe,CAAC,4CAA4C,EAAE;AACxE,QAAQ,4CAAA,GAA+C,IAAI;AAC3D,QAAQC,UAAK,CAAC,IAAI;AAClB,UAAU,oEAAA;AACV,YAAY,2FAA2F;AACvG,SAAS;AACT,MAAM;AACN,MAAM,OAAO,QAAQ,CAAC,IAAI,CAAC;AAC3B,IAAI;;AAEJ,IAAI,MAAM,IAAA,GAAO,OAAO,CAAC,IAAA,IAAQ,yBAAyB;AAC1D,IAAI,MAAM,MAAA,GAASC,kBAAa,EAAE;AAClC,IAAI,IAAI,MAAM,EAAE;AAChB,MAAM,MAAM,IAAA,GAAOC,gBAAW,CAAC,MAAM,CAAC;AACtC,MAAM,MAAM,QAAA,GAAWC,eAAU,CAAC,IAAI,CAAC;AACvC,MAAM,IAAI,QAAQ,CAAC,MAAA,KAAW,qBAAqB,EAAE;AACrD;AACA,QAAQ,MAAM,SAAS,QAAQ,CAAC,IAAI,CAACC,wCAAoB,CAAC;;AAE1D,QAAQ,IAAI,MAAM,EAAE;AACpB;AACA;AACA,UAAUC,mBAAc,CAAC,IAAI,EAAE,CAAC,EAAA,IAAA,CAAA,OAAA,CAAA,MAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA,CAAA;AACA,UAAA,IAAA,CAAA,aAAA,CAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,KAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,+BAAA;AACA,WAAA,CAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;;AAEA,IAAA,IAAA;AACA,MAAA,OAAA,MAAAC,cAAA;AACA,QAAA;AACA,UAAA,IAAA;AACA,UAAA,GAAA,OAAA;AACA,UAAA,UAAA,EAAA;AACA,YAAA,CAAAD,qCAAA,GAAA,+BAAA;AACA,YAAA,CAAAE,iCAAA,GAAA,8BAAA;AACA,YAAA,GAAA,OAAA,CAAA,UAAA;AACA,WAAA;AACA,SAAA;AACA,QAAA,MAAA,QAAA,CAAA,IAAA,CAAA;AACA,OAAA;AACA,IAAA,CAAA,SAAA;AACA,MAAA,MAAAC,sBAAA,EAAA;AACA,IAAA;AACA,EAAA,CAAA;AACA;;;;"}
@@ -2,6 +2,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const semanticConventions = require('@opentelemetry/semantic-conventions');
4
4
  const core = require('@sentry/core');
5
+ const debugBuild = require('../common/debug-build.js');
6
+ const serverGlobals = require('./serverGlobals.js');
7
+
8
+ // Track if we've already warned about duplicate instrumentation
9
+ let hasWarnedAboutDuplicateLoaderInstrumentation = false;
5
10
 
6
11
  /**
7
12
  * Wraps a React Router server loader function with Sentry performance monitoring.
@@ -22,8 +27,23 @@ const core = require('@sentry/core');
22
27
  * );
23
28
  * ```
24
29
  */
25
- function wrapServerLoader(options = {}, loaderFn) {
30
+ function wrapServerLoader(
31
+ options = {},
32
+ loaderFn,
33
+ ) {
26
34
  return async function (args) {
35
+ // Skip instrumentation if instrumentation API is already handling it
36
+ if (serverGlobals.isInstrumentationApiUsed()) {
37
+ if (debugBuild.DEBUG_BUILD && !hasWarnedAboutDuplicateLoaderInstrumentation) {
38
+ hasWarnedAboutDuplicateLoaderInstrumentation = true;
39
+ core.debug.warn(
40
+ 'wrapServerLoader is redundant when using the instrumentation API. ' +
41
+ 'The loader is already instrumented automatically. You can safely remove wrapServerLoader.',
42
+ );
43
+ }
44
+ return loaderFn(args);
45
+ }
46
+
27
47
  const name = options.name || 'Executing Server Loader';
28
48
  const active = core.getActiveSpan();
29
49
 
@@ -1 +1 @@
1
- {"version":3,"file":"wrapServerLoader.js","sources":["../../../src/server/wrapServerLoader.ts"],"sourcesContent":["import { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport type { SpanAttributes } from '@sentry/core';\nimport {\n flushIfServerless,\n getActiveSpan,\n getRootSpan,\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 { LoaderFunctionArgs } from 'react-router';\n\ntype SpanOptions = {\n name?: string;\n attributes?: SpanAttributes;\n};\n\n/**\n * Wraps a React Router server loader function with Sentry performance monitoring.\n * @param options - Optional span configuration options including name, operation, description and attributes\n * @param loaderFn - The server loader function to wrap\n *\n * @example\n * ```ts\n * // Wrap a loader function with custom span options\n * export const loader = wrapServerLoader(\n * {\n * name: 'Load Some Data',\n * description: 'Loads some data from the db',\n * },\n * async ({ params }) => {\n * // ... your loader logic\n * }\n * );\n * ```\n */\nexport function wrapServerLoader<T>(options: SpanOptions = {}, loaderFn: (args: LoaderFunctionArgs) => Promise<T>) {\n return async function (args: LoaderFunctionArgs) {\n const name = options.name || 'Executing Server Loader';\n const active = getActiveSpan();\n\n if (active) {\n const root = getRootSpan(active);\n const spanData = spanToJSON(root);\n if (spanData.origin === 'auto.http.otel.http') {\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET];\n\n if (target) {\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 updateSpanName(root, `${args.request.method} ${target}`);\n root.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.loader',\n });\n }\n }\n }\n try {\n return await startSpan(\n {\n name,\n ...options,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.loader',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.loader',\n ...options.attributes,\n },\n },\n () => loaderFn(args),\n );\n } finally {\n await flushIfServerless();\n }\n };\n}\n"],"names":["getActiveSpan","getRootSpan","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","flushIfServerless"],"mappings":";;;;;AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAI,OAAO,GAAgB,EAAE,EAAE,QAAQ,EAA4C;AACnH,EAAE,OAAO,gBAAgB,IAAI,EAAsB;AACnD,IAAI,MAAM,IAAA,GAAO,OAAO,CAAC,IAAA,IAAQ,yBAAyB;AAC1D,IAAI,MAAM,MAAA,GAASA,kBAAa,EAAE;;AAElC,IAAI,IAAI,MAAM,EAAE;AAChB,MAAM,MAAM,IAAA,GAAOC,gBAAW,CAAC,MAAM,CAAC;AACtC,MAAM,MAAM,QAAA,GAAWC,eAAU,CAAC,IAAI,CAAC;AACvC,MAAM,IAAI,QAAQ,CAAC,MAAA,KAAW,qBAAqB,EAAE;AACrD;AACA,QAAQ,MAAM,SAAS,QAAQ,CAAC,IAAI,CAACC,wCAAoB,CAAC;;AAE1D,QAAQ,IAAI,MAAM,EAAE;AACpB;AACA;AACA,UAAUC,mBAAc,CAAC,IAAI,EAAE,CAAC,EAAA,IAAA,CAAA,OAAA,CAAA,MAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA,CAAA;AACA,UAAA,IAAA,CAAA,aAAA,CAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,KAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,+BAAA;AACA,WAAA,CAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA,IAAA;AACA,MAAA,OAAA,MAAAC,cAAA;AACA,QAAA;AACA,UAAA,IAAA;AACA,UAAA,GAAA,OAAA;AACA,UAAA,UAAA,EAAA;AACA,YAAA,CAAAD,qCAAA,GAAA,+BAAA;AACA,YAAA,CAAAE,iCAAA,GAAA,8BAAA;AACA,YAAA,GAAA,OAAA,CAAA,UAAA;AACA,WAAA;AACA,SAAA;AACA,QAAA,MAAA,QAAA,CAAA,IAAA,CAAA;AACA,OAAA;AACA,IAAA,CAAA,SAAA;AACA,MAAA,MAAAC,sBAAA,EAAA;AACA,IAAA;AACA,EAAA,CAAA;AACA;;;;"}
1
+ {"version":3,"file":"wrapServerLoader.js","sources":["../../../src/server/wrapServerLoader.ts"],"sourcesContent":["import { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport type { SpanAttributes } from '@sentry/core';\nimport {\n debug,\n flushIfServerless,\n getActiveSpan,\n getRootSpan,\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 { LoaderFunctionArgs } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { isInstrumentationApiUsed } from './serverGlobals';\n\ntype SpanOptions = {\n name?: string;\n attributes?: SpanAttributes;\n};\n\n// Track if we've already warned about duplicate instrumentation\nlet hasWarnedAboutDuplicateLoaderInstrumentation = false;\n\n/**\n * Wraps a React Router server loader function with Sentry performance monitoring.\n * @param options - Optional span configuration options including name, operation, description and attributes\n * @param loaderFn - The server loader function to wrap\n *\n * @example\n * ```ts\n * // Wrap a loader function with custom span options\n * export const loader = wrapServerLoader(\n * {\n * name: 'Load Some Data',\n * description: 'Loads some data from the db',\n * },\n * async ({ params }) => {\n * // ... your loader logic\n * }\n * );\n * ```\n */\nexport function wrapServerLoader<T>(\n options: SpanOptions = {},\n loaderFn: (args: LoaderFunctionArgs) => Promise<T>,\n): (args: LoaderFunctionArgs) => Promise<T> {\n return async function (args: LoaderFunctionArgs): Promise<T> {\n // Skip instrumentation if instrumentation API is already handling it\n if (isInstrumentationApiUsed()) {\n if (DEBUG_BUILD && !hasWarnedAboutDuplicateLoaderInstrumentation) {\n hasWarnedAboutDuplicateLoaderInstrumentation = true;\n debug.warn(\n 'wrapServerLoader is redundant when using the instrumentation API. ' +\n 'The loader is already instrumented automatically. You can safely remove wrapServerLoader.',\n );\n }\n return loaderFn(args);\n }\n\n const name = options.name || 'Executing Server Loader';\n const active = getActiveSpan();\n\n if (active) {\n const root = getRootSpan(active);\n const spanData = spanToJSON(root);\n if (spanData.origin === 'auto.http.otel.http') {\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET];\n\n if (target) {\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 updateSpanName(root, `${args.request.method} ${target}`);\n root.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.loader',\n });\n }\n }\n }\n try {\n return await startSpan(\n {\n name,\n ...options,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react_router.loader',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.loader',\n ...options.attributes,\n },\n },\n () => loaderFn(args),\n );\n } finally {\n await flushIfServerless();\n }\n };\n}\n"],"names":["isInstrumentationApiUsed","DEBUG_BUILD","debug","getActiveSpan","getRootSpan","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","flushIfServerless"],"mappings":";;;;;;;AAuBA;AACA,IAAI,4CAAA,GAA+C,KAAK;;AAExD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB;AAChC,EAAE,OAAO,GAAgB,EAAE;AAC3B,EAAE,QAAQ;AACV,EAA4C;AAC5C,EAAE,OAAO,gBAAgB,IAAI,EAAkC;AAC/D;AACA,IAAI,IAAIA,sCAAwB,EAAE,EAAE;AACpC,MAAM,IAAIC,sBAAA,IAAe,CAAC,4CAA4C,EAAE;AACxE,QAAQ,4CAAA,GAA+C,IAAI;AAC3D,QAAQC,UAAK,CAAC,IAAI;AAClB,UAAU,oEAAA;AACV,YAAY,2FAA2F;AACvG,SAAS;AACT,MAAM;AACN,MAAM,OAAO,QAAQ,CAAC,IAAI,CAAC;AAC3B,IAAI;;AAEJ,IAAI,MAAM,IAAA,GAAO,OAAO,CAAC,IAAA,IAAQ,yBAAyB;AAC1D,IAAI,MAAM,MAAA,GAASC,kBAAa,EAAE;;AAElC,IAAI,IAAI,MAAM,EAAE;AAChB,MAAM,MAAM,IAAA,GAAOC,gBAAW,CAAC,MAAM,CAAC;AACtC,MAAM,MAAM,QAAA,GAAWC,eAAU,CAAC,IAAI,CAAC;AACvC,MAAM,IAAI,QAAQ,CAAC,MAAA,KAAW,qBAAqB,EAAE;AACrD;AACA,QAAQ,MAAM,SAAS,QAAQ,CAAC,IAAI,CAACC,wCAAoB,CAAC;;AAE1D,QAAQ,IAAI,MAAM,EAAE;AACpB;AACA;AACA,UAAUC,mBAAc,CAAC,IAAI,EAAE,CAAC,EAAA,IAAA,CAAA,OAAA,CAAA,MAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA,CAAA;AACA,UAAA,IAAA,CAAA,aAAA,CAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,KAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,+BAAA;AACA,WAAA,CAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA,IAAA;AACA,MAAA,OAAA,MAAAC,cAAA;AACA,QAAA;AACA,UAAA,IAAA;AACA,UAAA,GAAA,OAAA;AACA,UAAA,UAAA,EAAA;AACA,YAAA,CAAAD,qCAAA,GAAA,+BAAA;AACA,YAAA,CAAAE,iCAAA,GAAA,8BAAA;AACA,YAAA,GAAA,OAAA,CAAA,UAAA;AACA,WAAA;AACA,SAAA;AACA,QAAA,MAAA,QAAA,CAAA,IAAA,CAAA;AACA,OAAA;AACA,IAAA,CAAA,SAAA;AACA,MAAA,MAAAC,sBAAA,EAAA;AACA,IAAA;AACA,EAAA,CAAA;AACA;;;;"}
@@ -53,7 +53,7 @@ async function makeCustomSentryVitePlugins(options) {
53
53
  ...sentryVitePlugins.filter(plugin => {
54
54
  return [
55
55
  'sentry-telemetry-plugin',
56
- 'sentry-vite-release-injection-plugin',
56
+ 'sentry-vite-injection-plugin',
57
57
  ...(reactComponentAnnotation?.enabled || unstable_sentryVitePluginOptions?.reactComponentAnnotation?.enabled
58
58
  ? ['sentry-vite-component-name-annotate-plugin']
59
59
  : []),
@@ -1 +1 @@
1
- {"version":3,"file":"makeCustomSentryVitePlugins.js","sources":["../../../src/vite/makeCustomSentryVitePlugins.ts"],"sourcesContent":["import { sentryVitePlugin } from '@sentry/vite-plugin';\nimport { type Plugin } from 'vite';\nimport type { SentryReactRouterBuildOptions } from './types';\n\n/**\n * Create a custom subset of sentry's vite plugins\n */\nexport async function makeCustomSentryVitePlugins(options: SentryReactRouterBuildOptions): Promise<Plugin[]> {\n const {\n debug,\n unstable_sentryVitePluginOptions,\n bundleSizeOptimizations,\n authToken,\n org,\n project,\n telemetry,\n reactComponentAnnotation,\n release,\n } = options;\n\n const sentryVitePlugins = sentryVitePlugin({\n authToken: authToken ?? process.env.SENTRY_AUTH_TOKEN,\n bundleSizeOptimizations,\n debug: debug ?? false,\n org: org ?? process.env.SENTRY_ORG,\n project: project ?? process.env.SENTRY_PROJECT,\n telemetry: telemetry ?? true,\n _metaOptions: {\n telemetry: {\n metaFramework: 'react-router',\n },\n ...unstable_sentryVitePluginOptions?._metaOptions,\n },\n reactComponentAnnotation: {\n enabled: reactComponentAnnotation?.enabled ?? undefined,\n ignoredComponents: reactComponentAnnotation?.ignoredComponents ?? undefined,\n ...unstable_sentryVitePluginOptions?.reactComponentAnnotation,\n },\n release: {\n ...unstable_sentryVitePluginOptions?.release,\n ...release,\n },\n // will be handled in buildEnd hook\n sourcemaps: {\n disable: true,\n ...unstable_sentryVitePluginOptions?.sourcemaps,\n },\n ...unstable_sentryVitePluginOptions,\n }) as Plugin[];\n\n // only use a subset of the plugins as all upload and file deletion tasks will be handled in the buildEnd hook\n return [\n ...sentryVitePlugins.filter(plugin => {\n return [\n 'sentry-telemetry-plugin',\n 'sentry-vite-release-injection-plugin',\n ...(reactComponentAnnotation?.enabled || unstable_sentryVitePluginOptions?.reactComponentAnnotation?.enabled\n ? ['sentry-vite-component-name-annotate-plugin']\n : []),\n ].includes(plugin.name);\n }),\n ];\n}\n"],"names":["sentryVitePlugin"],"mappings":";;;;AAIA;AACA;AACA;AACO,eAAe,2BAA2B,CAAC,OAAO,EAAoD;AAC7G,EAAE,MAAM;AACR,IAAI,KAAK;AACT,IAAI,gCAAgC;AACpC,IAAI,uBAAuB;AAC3B,IAAI,SAAS;AACb,IAAI,GAAG;AACP,IAAI,OAAO;AACX,IAAI,SAAS;AACb,IAAI,wBAAwB;AAC5B,IAAI,OAAO;AACX,GAAE,GAAI,OAAO;;AAEb,EAAE,MAAM,iBAAA,GAAoBA,2BAAgB,CAAC;AAC7C,IAAI,SAAS,EAAE,SAAA,IAAa,OAAO,CAAC,GAAG,CAAC,iBAAiB;AACzD,IAAI,uBAAuB;AAC3B,IAAI,KAAK,EAAE,KAAA,IAAS,KAAK;AACzB,IAAI,GAAG,EAAE,GAAA,IAAO,OAAO,CAAC,GAAG,CAAC,UAAU;AACtC,IAAI,OAAO,EAAE,OAAA,IAAW,OAAO,CAAC,GAAG,CAAC,cAAc;AAClD,IAAI,SAAS,EAAE,SAAA,IAAa,IAAI;AAChC,IAAI,YAAY,EAAE;AAClB,MAAM,SAAS,EAAE;AACjB,QAAQ,aAAa,EAAE,cAAc;AACrC,OAAO;AACP,MAAM,GAAG,gCAAgC,EAAE,YAAY;AACvD,KAAK;AACL,IAAI,wBAAwB,EAAE;AAC9B,MAAM,OAAO,EAAE,wBAAwB,EAAE,OAAA,IAAW,SAAS;AAC7D,MAAM,iBAAiB,EAAE,wBAAwB,EAAE,iBAAA,IAAqB,SAAS;AACjF,MAAM,GAAG,gCAAgC,EAAE,wBAAwB;AACnE,KAAK;AACL,IAAI,OAAO,EAAE;AACb,MAAM,GAAG,gCAAgC,EAAE,OAAO;AAClD,MAAM,GAAG,OAAO;AAChB,KAAK;AACL;AACA,IAAI,UAAU,EAAE;AAChB,MAAM,OAAO,EAAE,IAAI;AACnB,MAAM,GAAG,gCAAgC,EAAE,UAAU;AACrD,KAAK;AACL,IAAI,GAAG,gCAAgC;AACvC,GAAG,CAAA;;AAEH;AACA,EAAE,OAAO;AACT,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,UAAU;AAC1C,MAAM,OAAO;AACb,QAAQ,yBAAyB;AACjC,QAAQ,sCAAsC;AAC9C,QAAQ,IAAI,wBAAwB,EAAE,WAAW,gCAAgC,EAAE,wBAAwB,EAAE;AAC7G,YAAY,CAAC,4CAA4C;AACzD,YAAY,EAAE,CAAC;AACf,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;AAC7B,IAAI,CAAC,CAAC;AACN,GAAG;AACH;;;;"}
1
+ {"version":3,"file":"makeCustomSentryVitePlugins.js","sources":["../../../src/vite/makeCustomSentryVitePlugins.ts"],"sourcesContent":["import { sentryVitePlugin } from '@sentry/vite-plugin';\nimport { type Plugin } from 'vite';\nimport type { SentryReactRouterBuildOptions } from './types';\n\n/**\n * Create a custom subset of sentry's vite plugins\n */\nexport async function makeCustomSentryVitePlugins(options: SentryReactRouterBuildOptions): Promise<Plugin[]> {\n const {\n debug,\n unstable_sentryVitePluginOptions,\n bundleSizeOptimizations,\n authToken,\n org,\n project,\n telemetry,\n reactComponentAnnotation,\n release,\n } = options;\n\n const sentryVitePlugins = sentryVitePlugin({\n authToken: authToken ?? process.env.SENTRY_AUTH_TOKEN,\n bundleSizeOptimizations,\n debug: debug ?? false,\n org: org ?? process.env.SENTRY_ORG,\n project: project ?? process.env.SENTRY_PROJECT,\n telemetry: telemetry ?? true,\n _metaOptions: {\n telemetry: {\n metaFramework: 'react-router',\n },\n ...unstable_sentryVitePluginOptions?._metaOptions,\n },\n reactComponentAnnotation: {\n enabled: reactComponentAnnotation?.enabled ?? undefined,\n ignoredComponents: reactComponentAnnotation?.ignoredComponents ?? undefined,\n ...unstable_sentryVitePluginOptions?.reactComponentAnnotation,\n },\n release: {\n ...unstable_sentryVitePluginOptions?.release,\n ...release,\n },\n // will be handled in buildEnd hook\n sourcemaps: {\n disable: true,\n ...unstable_sentryVitePluginOptions?.sourcemaps,\n },\n ...unstable_sentryVitePluginOptions,\n }) as Plugin[];\n\n // only use a subset of the plugins as all upload and file deletion tasks will be handled in the buildEnd hook\n return [\n ...sentryVitePlugins.filter(plugin => {\n return [\n 'sentry-telemetry-plugin',\n 'sentry-vite-injection-plugin',\n ...(reactComponentAnnotation?.enabled || unstable_sentryVitePluginOptions?.reactComponentAnnotation?.enabled\n ? ['sentry-vite-component-name-annotate-plugin']\n : []),\n ].includes(plugin.name);\n }),\n ];\n}\n"],"names":["sentryVitePlugin"],"mappings":";;;;AAIA;AACA;AACA;AACO,eAAe,2BAA2B,CAAC,OAAO,EAAoD;AAC7G,EAAE,MAAM;AACR,IAAI,KAAK;AACT,IAAI,gCAAgC;AACpC,IAAI,uBAAuB;AAC3B,IAAI,SAAS;AACb,IAAI,GAAG;AACP,IAAI,OAAO;AACX,IAAI,SAAS;AACb,IAAI,wBAAwB;AAC5B,IAAI,OAAO;AACX,GAAE,GAAI,OAAO;;AAEb,EAAE,MAAM,iBAAA,GAAoBA,2BAAgB,CAAC;AAC7C,IAAI,SAAS,EAAE,SAAA,IAAa,OAAO,CAAC,GAAG,CAAC,iBAAiB;AACzD,IAAI,uBAAuB;AAC3B,IAAI,KAAK,EAAE,KAAA,IAAS,KAAK;AACzB,IAAI,GAAG,EAAE,GAAA,IAAO,OAAO,CAAC,GAAG,CAAC,UAAU;AACtC,IAAI,OAAO,EAAE,OAAA,IAAW,OAAO,CAAC,GAAG,CAAC,cAAc;AAClD,IAAI,SAAS,EAAE,SAAA,IAAa,IAAI;AAChC,IAAI,YAAY,EAAE;AAClB,MAAM,SAAS,EAAE;AACjB,QAAQ,aAAa,EAAE,cAAc;AACrC,OAAO;AACP,MAAM,GAAG,gCAAgC,EAAE,YAAY;AACvD,KAAK;AACL,IAAI,wBAAwB,EAAE;AAC9B,MAAM,OAAO,EAAE,wBAAwB,EAAE,OAAA,IAAW,SAAS;AAC7D,MAAM,iBAAiB,EAAE,wBAAwB,EAAE,iBAAA,IAAqB,SAAS;AACjF,MAAM,GAAG,gCAAgC,EAAE,wBAAwB;AACnE,KAAK;AACL,IAAI,OAAO,EAAE;AACb,MAAM,GAAG,gCAAgC,EAAE,OAAO;AAClD,MAAM,GAAG,OAAO;AAChB,KAAK;AACL;AACA,IAAI,UAAU,EAAE;AAChB,MAAM,OAAO,EAAE,IAAI;AACnB,MAAM,GAAG,gCAAgC,EAAE,UAAU;AACrD,KAAK;AACL,IAAI,GAAG,gCAAgC;AACvC,GAAG,CAAA;;AAEH;AACA,EAAE,OAAO;AACT,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,UAAU;AAC1C,MAAM,OAAO;AACb,QAAQ,yBAAyB;AACjC,QAAQ,8BAA8B;AACtC,QAAQ,IAAI,wBAAwB,EAAE,WAAW,gCAAgC,EAAE,wBAAwB,EAAE;AAC7G,YAAY,CAAC,4CAA4C;AACzD,YAAY,EAAE,CAAC;AACf,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;AAC7B,IAAI,CAAC,CAAC;AACN,GAAG;AACH;;;;"}
@@ -0,0 +1,305 @@
1
+ import { startBrowserTracingNavigationSpan } from '@sentry/browser';
2
+ import { debug, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_OP, SPAN_STATUS_ERROR, GLOBAL_OBJ, getClient, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
3
+ import { DEBUG_BUILD } from '../common/debug-build.js';
4
+ import { captureInstrumentationError, getPathFromRequest, normalizeRoutePath, getPattern } from '../common/utils.js';
5
+
6
+ const WINDOW = GLOBAL_OBJ ;
7
+
8
+ // Tracks active numeric navigation span to prevent duplicate spans when popstate fires
9
+ let currentNumericNavigationSpan;
10
+
11
+ const SENTRY_CLIENT_INSTRUMENTATION_FLAG = '__sentryReactRouterClientInstrumentationUsed';
12
+ // Intentionally never reset - once set, instrumentation API handles all navigations for the session.
13
+ const SENTRY_NAVIGATE_HOOK_INVOKED_FLAG = '__sentryReactRouterNavigateHookInvoked';
14
+ const SENTRY_POPSTATE_LISTENER_ADDED_FLAG = '__sentryReactRouterPopstateListenerAdded';
15
+
16
+ const GLOBAL_WITH_FLAGS = GLOBAL_OBJ ;
17
+
18
+ /**
19
+ * Options for creating Sentry client instrumentation.
20
+ */
21
+
22
+ /**
23
+ * Creates a Sentry client instrumentation for React Router's instrumentation API.
24
+ * @experimental
25
+ */
26
+ function createSentryClientInstrumentation(
27
+ options = {},
28
+ ) {
29
+ const { captureErrors = true } = options;
30
+
31
+ DEBUG_BUILD && debug.log('React Router client instrumentation API created.');
32
+
33
+ return {
34
+ router(router) {
35
+ // Set the flag when React Router actually invokes our instrumentation.
36
+ // This ensures the flag is only set in Library Mode (where hooks run),
37
+ // not in Framework Mode (where hooks are never called).
38
+ // See: https://github.com/remix-run/react-router/discussions/13749
39
+ GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG] = true;
40
+ DEBUG_BUILD && debug.log('React Router client instrumentation API router hook registered.');
41
+
42
+ // Add popstate listener for browser back/forward navigation (persists for session, one listener only)
43
+ if (!GLOBAL_WITH_FLAGS[SENTRY_POPSTATE_LISTENER_ADDED_FLAG] && WINDOW.addEventListener) {
44
+ GLOBAL_WITH_FLAGS[SENTRY_POPSTATE_LISTENER_ADDED_FLAG] = true;
45
+
46
+ WINDOW.addEventListener('popstate', () => {
47
+ const client = getClient();
48
+ if (!client) {
49
+ currentNumericNavigationSpan = undefined;
50
+ return;
51
+ }
52
+
53
+ const pathname = WINDOW.location?.pathname || '/';
54
+
55
+ // If there's an active numeric navigation span, update it instead of creating a duplicate
56
+ if (currentNumericNavigationSpan) {
57
+ if (currentNumericNavigationSpan.isRecording()) {
58
+ currentNumericNavigationSpan.updateName(pathname);
59
+ }
60
+ currentNumericNavigationSpan = undefined;
61
+ return;
62
+ }
63
+
64
+ // Only create a new span for actual browser back/forward button clicks
65
+ startBrowserTracingNavigationSpan(client, {
66
+ name: pathname,
67
+ attributes: {
68
+ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
69
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
70
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',
71
+ 'navigation.type': 'browser.popstate',
72
+ },
73
+ });
74
+ });
75
+
76
+ DEBUG_BUILD && debug.log('React Router popstate listener registered for browser back/forward navigation.');
77
+ }
78
+
79
+ router.instrument({
80
+ async navigate(callNavigate, info) {
81
+ // navigate(0) triggers a page reload - skip span creation, but still capture errors
82
+ // (navigation can be rejected before reload, e.g., by a navigation guard)
83
+ if (info.to === 0) {
84
+ const result = await callNavigate();
85
+ if (result.status === 'error' && result.error instanceof Error) {
86
+ captureInstrumentationError(result, captureErrors, 'react_router.navigate', {
87
+ 'http.url': info.currentUrl,
88
+ });
89
+ }
90
+ return;
91
+ }
92
+
93
+ GLOBAL_WITH_FLAGS[SENTRY_NAVIGATE_HOOK_INVOKED_FLAG] = true;
94
+
95
+ // Handle numeric navigations (navigate(-1), navigate(1), etc.)
96
+ if (typeof info.to === 'number') {
97
+ const client = getClient();
98
+ let navigationSpan;
99
+
100
+ if (client) {
101
+ const navigationType = info.to < 0 ? 'router.back' : 'router.forward';
102
+ const currentPathname = WINDOW.location?.pathname || info.currentUrl;
103
+
104
+ navigationSpan = startBrowserTracingNavigationSpan(client, {
105
+ name: currentPathname,
106
+ attributes: {
107
+ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
108
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
109
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',
110
+ 'navigation.type': navigationType,
111
+ },
112
+ });
113
+
114
+ // Store ref so popstate listener can update it instead of creating a duplicate
115
+ currentNumericNavigationSpan = navigationSpan;
116
+ }
117
+
118
+ try {
119
+ const result = await callNavigate();
120
+
121
+ if (navigationSpan && WINDOW.location) {
122
+ navigationSpan.updateName(WINDOW.location.pathname);
123
+ }
124
+
125
+ if (result.status === 'error' && result.error instanceof Error) {
126
+ if (navigationSpan) {
127
+ navigationSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
128
+ }
129
+ captureInstrumentationError(result, captureErrors, 'react_router.navigate', {
130
+ 'http.url': WINDOW.location?.pathname || info.currentUrl,
131
+ });
132
+ }
133
+ } finally {
134
+ currentNumericNavigationSpan = undefined;
135
+ }
136
+ return;
137
+ }
138
+
139
+ // Handle string navigations (e.g., navigate('/about'))
140
+ const client = getClient();
141
+ const toPath = String(info.to);
142
+ let navigationSpan;
143
+
144
+ if (client) {
145
+ navigationSpan = startBrowserTracingNavigationSpan(client, {
146
+ name: toPath,
147
+ attributes: {
148
+ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
149
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
150
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',
151
+ 'navigation.type': 'router.navigate',
152
+ },
153
+ });
154
+ }
155
+
156
+ const result = await callNavigate();
157
+ if (result.status === 'error' && result.error instanceof Error) {
158
+ if (navigationSpan) {
159
+ navigationSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
160
+ }
161
+ captureInstrumentationError(result, captureErrors, 'react_router.navigate', {
162
+ 'http.url': toPath,
163
+ });
164
+ }
165
+ return;
166
+ },
167
+
168
+ async fetch(callFetch, info) {
169
+ await startSpan(
170
+ {
171
+ name: `Fetcher ${info.fetcherKey}`,
172
+ attributes: {
173
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.fetcher',
174
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
175
+ },
176
+ },
177
+ async span => {
178
+ const result = await callFetch();
179
+ if (result.status === 'error' && result.error instanceof Error) {
180
+ span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
181
+ captureInstrumentationError(result, captureErrors, 'react_router.fetcher', {
182
+ 'http.url': info.href,
183
+ });
184
+ }
185
+ },
186
+ );
187
+ },
188
+ });
189
+ },
190
+
191
+ route(route) {
192
+ route.instrument({
193
+ async loader(callLoader, info) {
194
+ const urlPath = getPathFromRequest(info.request);
195
+ const routePattern = normalizeRoutePath(getPattern(info)) || urlPath;
196
+
197
+ await startSpan(
198
+ {
199
+ name: routePattern,
200
+ attributes: {
201
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.client_loader',
202
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
203
+ },
204
+ },
205
+ async span => {
206
+ const result = await callLoader();
207
+ if (result.status === 'error' && result.error instanceof Error) {
208
+ span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
209
+ captureInstrumentationError(result, captureErrors, 'react_router.client_loader', {
210
+ 'http.url': urlPath,
211
+ });
212
+ }
213
+ },
214
+ );
215
+ },
216
+
217
+ async action(callAction, info) {
218
+ const urlPath = getPathFromRequest(info.request);
219
+ const routePattern = normalizeRoutePath(getPattern(info)) || urlPath;
220
+
221
+ await startSpan(
222
+ {
223
+ name: routePattern,
224
+ attributes: {
225
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.client_action',
226
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
227
+ },
228
+ },
229
+ async span => {
230
+ const result = await callAction();
231
+ if (result.status === 'error' && result.error instanceof Error) {
232
+ span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
233
+ captureInstrumentationError(result, captureErrors, 'react_router.client_action', {
234
+ 'http.url': urlPath,
235
+ });
236
+ }
237
+ },
238
+ );
239
+ },
240
+
241
+ async middleware(callMiddleware, info) {
242
+ const urlPath = getPathFromRequest(info.request);
243
+ const routePattern = normalizeRoutePath(getPattern(info)) || urlPath;
244
+
245
+ await startSpan(
246
+ {
247
+ name: routePattern,
248
+ attributes: {
249
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.client_middleware',
250
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
251
+ },
252
+ },
253
+ async span => {
254
+ const result = await callMiddleware();
255
+ if (result.status === 'error' && result.error instanceof Error) {
256
+ span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
257
+ captureInstrumentationError(result, captureErrors, 'react_router.client_middleware', {
258
+ 'http.url': urlPath,
259
+ });
260
+ }
261
+ },
262
+ );
263
+ },
264
+
265
+ async lazy(callLazy) {
266
+ await startSpan(
267
+ {
268
+ name: 'Lazy Route Load',
269
+ attributes: {
270
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.client_lazy',
271
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',
272
+ },
273
+ },
274
+ async span => {
275
+ const result = await callLazy();
276
+ if (result.status === 'error' && result.error instanceof Error) {
277
+ span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
278
+ captureInstrumentationError(result, captureErrors, 'react_router.client_lazy', {});
279
+ }
280
+ },
281
+ );
282
+ },
283
+ });
284
+ },
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Check if React Router's instrumentation API is being used on the client.
290
+ * @experimental
291
+ */
292
+ function isClientInstrumentationApiUsed() {
293
+ return !!GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG];
294
+ }
295
+
296
+ /**
297
+ * Check if React Router's instrumentation API's navigate hook was invoked.
298
+ * @experimental
299
+ */
300
+ function isNavigateHookInvoked() {
301
+ return !!GLOBAL_WITH_FLAGS[SENTRY_NAVIGATE_HOOK_INVOKED_FLAG];
302
+ }
303
+
304
+ export { createSentryClientInstrumentation, isClientInstrumentationApiUsed, isNavigateHookInvoked };
305
+ //# sourceMappingURL=createClientInstrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createClientInstrumentation.js","sources":["../../../src/client/createClientInstrumentation.ts"],"sourcesContent":["import { startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getClient,\n GLOBAL_OBJ,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n SPAN_STATUS_ERROR,\n startSpan,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport type { ClientInstrumentation, InstrumentableRoute, InstrumentableRouter } from '../common/types';\nimport { captureInstrumentationError, getPathFromRequest, getPattern, normalizeRoutePath } from '../common/utils';\n\nconst WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;\n\n// Tracks active numeric navigation span to prevent duplicate spans when popstate fires\nlet currentNumericNavigationSpan: Span | undefined;\n\nconst SENTRY_CLIENT_INSTRUMENTATION_FLAG = '__sentryReactRouterClientInstrumentationUsed';\n// Intentionally never reset - once set, instrumentation API handles all navigations for the session.\nconst SENTRY_NAVIGATE_HOOK_INVOKED_FLAG = '__sentryReactRouterNavigateHookInvoked';\nconst SENTRY_POPSTATE_LISTENER_ADDED_FLAG = '__sentryReactRouterPopstateListenerAdded';\n\ntype GlobalObjWithFlags = typeof GLOBAL_OBJ & {\n [SENTRY_CLIENT_INSTRUMENTATION_FLAG]?: boolean;\n [SENTRY_NAVIGATE_HOOK_INVOKED_FLAG]?: boolean;\n [SENTRY_POPSTATE_LISTENER_ADDED_FLAG]?: boolean;\n};\n\nconst GLOBAL_WITH_FLAGS = GLOBAL_OBJ as GlobalObjWithFlags;\n\n/**\n * Options for creating Sentry client instrumentation.\n */\nexport interface CreateSentryClientInstrumentationOptions {\n /**\n * Whether to capture errors from loaders/actions automatically.\n * Set to `false` to avoid duplicates if using custom error handlers.\n * @default true\n */\n captureErrors?: boolean;\n}\n\n/**\n * Creates a Sentry client instrumentation for React Router's instrumentation API.\n * @experimental\n */\nexport function createSentryClientInstrumentation(\n options: CreateSentryClientInstrumentationOptions = {},\n): ClientInstrumentation {\n const { captureErrors = true } = options;\n\n DEBUG_BUILD && debug.log('React Router client instrumentation API created.');\n\n return {\n router(router: InstrumentableRouter) {\n // Set the flag when React Router actually invokes our instrumentation.\n // This ensures the flag is only set in Library Mode (where hooks run),\n // not in Framework Mode (where hooks are never called).\n // See: https://github.com/remix-run/react-router/discussions/13749\n GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG] = true;\n DEBUG_BUILD && debug.log('React Router client instrumentation API router hook registered.');\n\n // Add popstate listener for browser back/forward navigation (persists for session, one listener only)\n if (!GLOBAL_WITH_FLAGS[SENTRY_POPSTATE_LISTENER_ADDED_FLAG] && WINDOW.addEventListener) {\n GLOBAL_WITH_FLAGS[SENTRY_POPSTATE_LISTENER_ADDED_FLAG] = true;\n\n WINDOW.addEventListener('popstate', () => {\n const client = getClient();\n if (!client) {\n currentNumericNavigationSpan = undefined;\n return;\n }\n\n const pathname = WINDOW.location?.pathname || '/';\n\n // If there's an active numeric navigation span, update it instead of creating a duplicate\n if (currentNumericNavigationSpan) {\n if (currentNumericNavigationSpan.isRecording()) {\n currentNumericNavigationSpan.updateName(pathname);\n }\n currentNumericNavigationSpan = undefined;\n return;\n }\n\n // Only create a new span for actual browser back/forward button clicks\n startBrowserTracingNavigationSpan(client, {\n name: pathname,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': 'browser.popstate',\n },\n });\n });\n\n DEBUG_BUILD && debug.log('React Router popstate listener registered for browser back/forward navigation.');\n }\n\n router.instrument({\n async navigate(callNavigate, info) {\n // navigate(0) triggers a page reload - skip span creation, but still capture errors\n // (navigation can be rejected before reload, e.g., by a navigation guard)\n if (info.to === 0) {\n const result = await callNavigate();\n if (result.status === 'error' && result.error instanceof Error) {\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n 'http.url': info.currentUrl,\n });\n }\n return;\n }\n\n GLOBAL_WITH_FLAGS[SENTRY_NAVIGATE_HOOK_INVOKED_FLAG] = true;\n\n // Handle numeric navigations (navigate(-1), navigate(1), etc.)\n if (typeof info.to === 'number') {\n const client = getClient();\n let navigationSpan;\n\n if (client) {\n const navigationType = info.to < 0 ? 'router.back' : 'router.forward';\n const currentPathname = WINDOW.location?.pathname || info.currentUrl;\n\n navigationSpan = startBrowserTracingNavigationSpan(client, {\n name: currentPathname,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': navigationType,\n },\n });\n\n // Store ref so popstate listener can update it instead of creating a duplicate\n currentNumericNavigationSpan = navigationSpan;\n }\n\n try {\n const result = await callNavigate();\n\n if (navigationSpan && WINDOW.location) {\n navigationSpan.updateName(WINDOW.location.pathname);\n }\n\n if (result.status === 'error' && result.error instanceof Error) {\n if (navigationSpan) {\n navigationSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n 'http.url': WINDOW.location?.pathname || info.currentUrl,\n });\n }\n } finally {\n currentNumericNavigationSpan = undefined;\n }\n return;\n }\n\n // Handle string navigations (e.g., navigate('/about'))\n const client = getClient();\n const toPath = String(info.to);\n let navigationSpan;\n\n if (client) {\n navigationSpan = startBrowserTracingNavigationSpan(client, {\n name: toPath,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router.instrumentation_api',\n 'navigation.type': 'router.navigate',\n },\n });\n }\n\n const result = await callNavigate();\n if (result.status === 'error' && result.error instanceof Error) {\n if (navigationSpan) {\n navigationSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n }\n captureInstrumentationError(result, captureErrors, 'react_router.navigate', {\n 'http.url': toPath,\n });\n }\n return;\n },\n\n async fetch(callFetch, info) {\n await startSpan(\n {\n name: `Fetcher ${info.fetcherKey}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.fetcher',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api',\n },\n },\n async span => {\n const result = await callFetch();\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.fetcher', {\n 'http.url': info.href,\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 routePattern = normalizeRoutePath(getPattern(info)) || urlPath;\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.client_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.client_loader', {\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async action(callAction, info) {\n const urlPath = getPathFromRequest(info.request);\n const routePattern = normalizeRoutePath(getPattern(info)) || urlPath;\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.client_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.client_action', {\n 'http.url': urlPath,\n });\n }\n },\n );\n },\n\n async middleware(callMiddleware, info) {\n const urlPath = getPathFromRequest(info.request);\n const routePattern = normalizeRoutePath(getPattern(info)) || urlPath;\n\n await startSpan(\n {\n name: routePattern,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react_router.client_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.client_middleware', {\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.client_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.client_lazy', {});\n }\n },\n );\n },\n });\n },\n };\n}\n\n/**\n * Check if React Router's instrumentation API is being used on the client.\n * @experimental\n */\nexport function isClientInstrumentationApiUsed(): boolean {\n return !!GLOBAL_WITH_FLAGS[SENTRY_CLIENT_INSTRUMENTATION_FLAG];\n}\n\n/**\n * Check if React Router's instrumentation API's navigate hook was invoked.\n * @experimental\n */\nexport function isNavigateHookInvoked(): boolean {\n return !!GLOBAL_WITH_FLAGS[SENTRY_NAVIGATE_HOOK_INVOKED_FLAG];\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { startBrowserTracingNavigationSpan } from '@sentry/browser';
2
- import { GLOBAL_OBJ, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, consoleSandbox, getActiveSpan, getRootSpan, getClient, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
2
+ import { GLOBAL_OBJ, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, debug, getActiveSpan, getRootSpan, getClient, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
3
3
  import { DEBUG_BUILD } from '../common/debug-build.js';
4
+ import { isClientInstrumentationApiUsed } from './createClientInstrumentation.js';
4
5
 
5
6
  const GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ
6
7
 
@@ -22,7 +23,6 @@ function instrumentHydratedRouter() {
22
23
 
23
24
  if (router) {
24
25
  // The first time we hit the router, we try to update the pageload transaction
25
- // todo: update pageload tx here
26
26
  const pageloadSpan = getActiveRootSpan();
27
27
 
28
28
  if (pageloadSpan) {
@@ -39,18 +39,18 @@ function instrumentHydratedRouter() {
39
39
  [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react_router',
40
40
  });
41
41
  }
42
+ }
42
43
 
43
- // Patching navigate for creating accurate navigation transactions
44
- if (typeof router.navigate === 'function') {
45
- const originalNav = router.navigate.bind(router);
46
- router.navigate = function sentryPatchedNavigate(...args) {
47
- maybeCreateNavigationTransaction(
48
- String(args[0]) || '<unknown route>', // will be updated anyway
49
- 'url', // this also will be updated once we have the parameterized route
50
- );
51
- return originalNav(...args);
52
- };
53
- }
44
+ // Patching navigate for creating accurate navigation transactions
45
+ if (typeof router.navigate === 'function') {
46
+ const originalNav = router.navigate.bind(router);
47
+ router.navigate = function sentryPatchedNavigate(...args) {
48
+ // Skip if instrumentation API is enabled (it handles navigation spans itself)
49
+ if (!isClientInstrumentationApiUsed()) {
50
+ maybeCreateNavigationTransaction(String(args[0]) || '<unknown route>', 'url');
51
+ }
52
+ return originalNav(...args);
53
+ };
54
54
  }
55
55
 
56
56
  // Subscribe to router state changes to update navigation transactions with parameterized routes
@@ -67,7 +67,8 @@ function instrumentHydratedRouter() {
67
67
  if (
68
68
  navigationSpanName &&
69
69
  newState.navigation.state === 'idle' && // navigation has completed
70
- normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName) // this event is for the currently active navigation
70
+ // this event is for the currently active navigation
71
+ normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName)
71
72
  ) {
72
73
  navigationSpan.updateName(parameterizedNavRoute);
73
74
  navigationSpan.setAttributes({
@@ -88,11 +89,7 @@ function instrumentHydratedRouter() {
88
89
  const interval = setInterval(() => {
89
90
  if (trySubscribe() || retryCount >= MAX_RETRIES) {
90
91
  if (retryCount >= MAX_RETRIES) {
91
- DEBUG_BUILD &&
92
- consoleSandbox(() => {
93
- // eslint-disable-next-line no-console
94
- console.warn('Unable to instrument React Router: router not found after hydration.');
95
- });
92
+ DEBUG_BUILD && debug.warn('Unable to instrument React Router: router not found after hydration.');
96
93
  }
97
94
  clearInterval(interval);
98
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hydratedRouter.js","sources":["../../../src/client/hydratedRouter.ts"],"sourcesContent":["import { startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n consoleSandbox,\n getActiveSpan,\n getClient,\n getRootSpan,\n GLOBAL_OBJ,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n} from '@sentry/core';\nimport type { DataRouter, RouterState } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\n\nconst GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __reactRouterDataRouter?: DataRouter;\n};\n\nconst MAX_RETRIES = 40; // 2 seconds at 50ms interval\n\n/**\n * Instruments the React Router Data Router for pageloads and navigation.\n *\n * This function waits for the router to be available after hydration, then:\n * 1. Updates the pageload transaction with parameterized route info\n * 2. Patches router.navigate() to create navigation transactions\n * 3. Subscribes to router state changes to update navigation transactions with parameterized routes\n */\nexport function instrumentHydratedRouter(): void {\n function trySubscribe(): boolean {\n const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;\n\n if (router) {\n // The first time we hit the router, we try to update the pageload transaction\n // todo: update pageload tx here\n const pageloadSpan = getActiveRootSpan();\n\n if (pageloadSpan) {\n const pageloadName = spanToJSON(pageloadSpan).description;\n const parameterizePageloadRoute = getParameterizedRoute(router.state);\n if (\n pageloadName &&\n // this event is for the currently active pageload\n normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName)\n ) {\n pageloadSpan.updateName(parameterizePageloadRoute);\n pageloadSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react_router',\n });\n }\n\n // Patching navigate for creating accurate navigation transactions\n if (typeof router.navigate === 'function') {\n const originalNav = router.navigate.bind(router);\n router.navigate = function sentryPatchedNavigate(...args) {\n maybeCreateNavigationTransaction(\n String(args[0]) || '<unknown route>', // will be updated anyway\n 'url', // this also will be updated once we have the parameterized route\n );\n return originalNav(...args);\n };\n }\n }\n\n // Subscribe to router state changes to update navigation transactions with parameterized routes\n router.subscribe(newState => {\n const navigationSpan = getActiveRootSpan();\n\n if (!navigationSpan) {\n return;\n }\n\n const navigationSpanName = spanToJSON(navigationSpan).description;\n const parameterizedNavRoute = getParameterizedRoute(newState);\n\n if (\n navigationSpanName &&\n newState.navigation.state === 'idle' && // navigation has completed\n normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName) // this event is for the currently active navigation\n ) {\n navigationSpan.updateName(parameterizedNavRoute);\n navigationSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router',\n });\n }\n });\n return true;\n }\n return false;\n }\n\n // Wait until the router is available (since the SDK loads before hydration)\n if (!trySubscribe()) {\n let retryCount = 0;\n // Retry until the router is available or max retries reached\n const interval = setInterval(() => {\n if (trySubscribe() || retryCount >= MAX_RETRIES) {\n if (retryCount >= MAX_RETRIES) {\n DEBUG_BUILD &&\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('Unable to instrument React Router: router not found after hydration.');\n });\n }\n clearInterval(interval);\n }\n retryCount++;\n }, 50);\n }\n}\n\nfunction maybeCreateNavigationTransaction(name: string, source: 'url' | 'route'): Span | undefined {\n const client = getClient();\n\n if (!client) {\n return undefined;\n }\n\n return startBrowserTracingNavigationSpan(client, {\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router',\n },\n });\n}\n\nfunction getActiveRootSpan(): Span | undefined {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n\n const rootSpan = getRootSpan(activeSpan);\n\n const op = spanToJSON(rootSpan).op;\n\n // Only use this root span if it is a pageload or navigation span\n return op === 'navigation' || op === 'pageload' ? rootSpan : undefined;\n}\n\nfunction getParameterizedRoute(routerState: RouterState): string {\n const lastMatch = routerState.matches[routerState.matches.length - 1];\n return normalizePathname(lastMatch?.route.path ?? routerState.location.pathname);\n}\n\nfunction normalizePathname(pathname: string): string {\n // Ensure it starts with a single slash\n let normalized = pathname.startsWith('/') ? pathname : `/${pathname}`;\n // Remove trailing slash unless it's the root\n if (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hydratedRouter.js","sources":["../../../src/client/hydratedRouter.ts"],"sourcesContent":["import { startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n debug,\n getActiveSpan,\n getClient,\n getRootSpan,\n GLOBAL_OBJ,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n} from '@sentry/core';\nimport type { DataRouter, RouterState } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { isClientInstrumentationApiUsed } from './createClientInstrumentation';\n\nconst GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __reactRouterDataRouter?: DataRouter;\n};\n\nconst MAX_RETRIES = 40; // 2 seconds at 50ms interval\n\n/**\n * Instruments the React Router Data Router for pageloads and navigation.\n *\n * This function waits for the router to be available after hydration, then:\n * 1. Updates the pageload transaction with parameterized route info\n * 2. Patches router.navigate() to create navigation transactions\n * 3. Subscribes to router state changes to update navigation transactions with parameterized routes\n */\nexport function instrumentHydratedRouter(): void {\n function trySubscribe(): boolean {\n const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;\n\n if (router) {\n // The first time we hit the router, we try to update the pageload transaction\n const pageloadSpan = getActiveRootSpan();\n\n if (pageloadSpan) {\n const pageloadName = spanToJSON(pageloadSpan).description;\n const parameterizePageloadRoute = getParameterizedRoute(router.state);\n if (\n pageloadName &&\n // this event is for the currently active pageload\n normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName)\n ) {\n pageloadSpan.updateName(parameterizePageloadRoute);\n pageloadSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react_router',\n });\n }\n }\n\n // Patching navigate for creating accurate navigation transactions\n if (typeof router.navigate === 'function') {\n const originalNav = router.navigate.bind(router);\n router.navigate = function sentryPatchedNavigate(...args) {\n // Skip if instrumentation API is enabled (it handles navigation spans itself)\n if (!isClientInstrumentationApiUsed()) {\n maybeCreateNavigationTransaction(String(args[0]) || '<unknown route>', 'url');\n }\n return originalNav(...args);\n };\n }\n\n // Subscribe to router state changes to update navigation transactions with parameterized routes\n router.subscribe(newState => {\n const navigationSpan = getActiveRootSpan();\n\n if (!navigationSpan) {\n return;\n }\n\n const navigationSpanName = spanToJSON(navigationSpan).description;\n const parameterizedNavRoute = getParameterizedRoute(newState);\n\n if (\n navigationSpanName &&\n newState.navigation.state === 'idle' && // navigation has completed\n // this event is for the currently active navigation\n normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName)\n ) {\n navigationSpan.updateName(parameterizedNavRoute);\n navigationSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router',\n });\n }\n });\n return true;\n }\n return false;\n }\n\n // Wait until the router is available (since the SDK loads before hydration)\n if (!trySubscribe()) {\n let retryCount = 0;\n // Retry until the router is available or max retries reached\n const interval = setInterval(() => {\n if (trySubscribe() || retryCount >= MAX_RETRIES) {\n if (retryCount >= MAX_RETRIES) {\n DEBUG_BUILD && debug.warn('Unable to instrument React Router: router not found after hydration.');\n }\n clearInterval(interval);\n }\n retryCount++;\n }, 50);\n }\n}\n\nfunction maybeCreateNavigationTransaction(name: string, source: 'url' | 'route'): Span | undefined {\n const client = getClient();\n\n if (!client) {\n return undefined;\n }\n\n return startBrowserTracingNavigationSpan(client, {\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react_router',\n },\n });\n}\n\nfunction getActiveRootSpan(): Span | undefined {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n\n const rootSpan = getRootSpan(activeSpan);\n\n const op = spanToJSON(rootSpan).op;\n\n // Only use this root span if it is a pageload or navigation span\n return op === 'navigation' || op === 'pageload' ? rootSpan : undefined;\n}\n\nfunction getParameterizedRoute(routerState: RouterState): string {\n const lastMatch = routerState.matches[routerState.matches.length - 1];\n return normalizePathname(lastMatch?.route.path ?? routerState.location.pathname);\n}\n\nfunction normalizePathname(pathname: string): string {\n // Ensure it starts with a single slash\n let normalized = pathname.startsWith('/') ? pathname : `/${pathname}`;\n // Remove trailing slash unless it's the root\n if (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n}\n"],"names":[],"mappings":"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"}