@sentry/react-router 9.15.0 → 9.16.1

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 (42) hide show
  1. package/build/cjs/client/hydratedRouter.js +140 -0
  2. package/build/cjs/client/hydratedRouter.js.map +1 -0
  3. package/build/cjs/client/sdk.js +19 -5
  4. package/build/cjs/client/sdk.js.map +1 -1
  5. package/build/cjs/client/tracingIntegration.js +27 -0
  6. package/build/cjs/client/tracingIntegration.js.map +1 -0
  7. package/build/cjs/index.client.js +2 -0
  8. package/build/cjs/index.client.js.map +1 -1
  9. package/build/cjs/index.server.js +2 -0
  10. package/build/cjs/index.server.js.map +1 -1
  11. package/build/cjs/vite/makeConfigInjectorPlugin.js +25 -0
  12. package/build/cjs/vite/makeConfigInjectorPlugin.js.map +1 -0
  13. package/build/cjs/vite/plugin.js +3 -0
  14. package/build/cjs/vite/plugin.js.map +1 -1
  15. package/build/esm/client/hydratedRouter.js +138 -0
  16. package/build/esm/client/hydratedRouter.js.map +1 -0
  17. package/build/esm/client/sdk.js +20 -6
  18. package/build/esm/client/sdk.js.map +1 -1
  19. package/build/esm/client/tracingIntegration.js +25 -0
  20. package/build/esm/client/tracingIntegration.js.map +1 -0
  21. package/build/esm/index.client.js +1 -0
  22. package/build/esm/index.client.js.map +1 -1
  23. package/build/esm/index.server.js +1 -0
  24. package/build/esm/index.server.js.map +1 -1
  25. package/build/esm/package.json +1 -1
  26. package/build/esm/vite/makeConfigInjectorPlugin.js +23 -0
  27. package/build/esm/vite/makeConfigInjectorPlugin.js.map +1 -0
  28. package/build/esm/vite/plugin.js +3 -0
  29. package/build/esm/vite/plugin.js.map +1 -1
  30. package/build/types/client/hydratedRouter.d.ts +10 -0
  31. package/build/types/client/hydratedRouter.d.ts.map +1 -0
  32. package/build/types/client/index.d.ts +1 -0
  33. package/build/types/client/index.d.ts.map +1 -1
  34. package/build/types/client/sdk.d.ts.map +1 -1
  35. package/build/types/client/tracingIntegration.d.ts +7 -0
  36. package/build/types/client/tracingIntegration.d.ts.map +1 -0
  37. package/build/types/vite/index.d.ts +1 -0
  38. package/build/types/vite/index.d.ts.map +1 -1
  39. package/build/types/vite/makeConfigInjectorPlugin.d.ts +12 -0
  40. package/build/types/vite/makeConfigInjectorPlugin.d.ts.map +1 -0
  41. package/build/types/vite/plugin.d.ts.map +1 -1
  42. package/package.json +4 -4
@@ -0,0 +1,140 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const browser = require('@sentry/browser');
4
+ const core = require('@sentry/core');
5
+ const debugBuild = require('../common/debug-build.js');
6
+
7
+ const GLOBAL_OBJ_WITH_DATA_ROUTER = core.GLOBAL_OBJ
8
+
9
+ ;
10
+
11
+ const MAX_RETRIES = 40; // 2 seconds at 50ms interval
12
+
13
+ /**
14
+ * Instruments the React Router Data Router for pageloads and navigation.
15
+ *
16
+ * This function waits for the router to be available after hydration, then:
17
+ * 1. Updates the pageload transaction with parameterized route info
18
+ * 2. Patches router.navigate() to create navigation transactions
19
+ * 3. Subscribes to router state changes to update navigation transactions with parameterized routes
20
+ */
21
+ function instrumentHydratedRouter() {
22
+ function trySubscribe() {
23
+ const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;
24
+
25
+ if (router) {
26
+ // The first time we hit the router, we try to update the pageload transaction
27
+ // todo: update pageload tx here
28
+ const pageloadSpan = getActiveRootSpan();
29
+ const pageloadName = pageloadSpan ? core.spanToJSON(pageloadSpan).description : undefined;
30
+ const parameterizePageloadRoute = getParameterizedRoute(router.state);
31
+ if (
32
+ pageloadName &&
33
+ normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName) && // this event is for the currently active pageload
34
+ normalizePathname(parameterizePageloadRoute) !== normalizePathname(pageloadName) // route is not parameterized yet
35
+ ) {
36
+ pageloadSpan?.updateName(parameterizePageloadRoute);
37
+ pageloadSpan?.setAttribute(core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
38
+ }
39
+
40
+ // Patching navigate for creating accurate navigation transactions
41
+ if (typeof router.navigate === 'function') {
42
+ const originalNav = router.navigate.bind(router);
43
+ router.navigate = function sentryPatchedNavigate(...args) {
44
+ maybeCreateNavigationTransaction(
45
+ String(args[0]) || '<unknown route>', // will be updated anyway
46
+ 'url', // this also will be updated once we have the parameterized route
47
+ );
48
+ return originalNav(...args);
49
+ };
50
+ }
51
+
52
+ // Subscribe to router state changes to update navigation transactions with parameterized routes
53
+ router.subscribe(newState => {
54
+ const navigationSpan = getActiveRootSpan();
55
+ const navigationSpanName = navigationSpan ? core.spanToJSON(navigationSpan).description : undefined;
56
+ const parameterizedNavRoute = getParameterizedRoute(newState);
57
+
58
+ if (
59
+ navigationSpanName && // we have an active pageload tx
60
+ newState.navigation.state === 'idle' && // navigation has completed
61
+ normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName) && // this event is for the currently active navigation
62
+ normalizePathname(parameterizedNavRoute) !== normalizePathname(navigationSpanName) // route is not parameterized yet
63
+ ) {
64
+ navigationSpan?.updateName(parameterizedNavRoute);
65
+ navigationSpan?.setAttribute(core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
66
+ }
67
+ });
68
+ return true;
69
+ }
70
+ return false;
71
+ }
72
+
73
+ // Wait until the router is available (since the SDK loads before hydration)
74
+ if (!trySubscribe()) {
75
+ let retryCount = 0;
76
+ // Retry until the router is available or max retries reached
77
+ const interval = setInterval(() => {
78
+ if (trySubscribe() || retryCount >= MAX_RETRIES) {
79
+ if (retryCount >= MAX_RETRIES) {
80
+ debugBuild.DEBUG_BUILD &&
81
+ core.consoleSandbox(() => {
82
+ // eslint-disable-next-line no-console
83
+ console.warn('Unable to instrument React Router: router not found after hydration.');
84
+ });
85
+ }
86
+ clearInterval(interval);
87
+ }
88
+ retryCount++;
89
+ }, 50);
90
+ }
91
+ }
92
+
93
+ function maybeCreateNavigationTransaction(name, source) {
94
+ const client = core.getClient();
95
+
96
+ if (!client) {
97
+ return undefined;
98
+ }
99
+
100
+ return browser.startBrowserTracingNavigationSpan(client, {
101
+ name,
102
+ attributes: {
103
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
104
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
105
+ [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react-router',
106
+ },
107
+ });
108
+ }
109
+
110
+ function getActiveRootSpan() {
111
+ const activeSpan = core.getActiveSpan();
112
+ if (!activeSpan) {
113
+ return undefined;
114
+ }
115
+
116
+ const rootSpan = core.getRootSpan(activeSpan);
117
+
118
+ const op = core.spanToJSON(rootSpan).op;
119
+
120
+ // Only use this root span if it is a pageload or navigation span
121
+ return op === 'navigation' || op === 'pageload' ? rootSpan : undefined;
122
+ }
123
+
124
+ function getParameterizedRoute(routerState) {
125
+ const lastMatch = routerState.matches[routerState.matches.length - 1];
126
+ return normalizePathname(lastMatch?.route.path ?? routerState.location.pathname);
127
+ }
128
+
129
+ function normalizePathname(pathname) {
130
+ // Ensure it starts with a single slash
131
+ let normalized = pathname.startsWith('/') ? pathname : `/${pathname}`;
132
+ // Remove trailing slash unless it's the root
133
+ if (normalized.length > 1 && normalized.endsWith('/')) {
134
+ normalized = normalized.slice(0, -1);
135
+ }
136
+ return normalized;
137
+ }
138
+
139
+ exports.instrumentHydratedRouter = instrumentHydratedRouter;
140
+ //# sourceMappingURL=hydratedRouter.js.map
@@ -0,0 +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 const pageloadName = pageloadSpan ? spanToJSON(pageloadSpan).description : undefined;\n const parameterizePageloadRoute = getParameterizedRoute(router.state);\n if (\n pageloadName &&\n normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName) && // this event is for the currently active pageload\n normalizePathname(parameterizePageloadRoute) !== normalizePathname(pageloadName) // route is not parameterized yet\n ) {\n pageloadSpan?.updateName(parameterizePageloadRoute);\n pageloadSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');\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 // Subscribe to router state changes to update navigation transactions with parameterized routes\n router.subscribe(newState => {\n const navigationSpan = getActiveRootSpan();\n const navigationSpanName = navigationSpan ? spanToJSON(navigationSpan).description : undefined;\n const parameterizedNavRoute = getParameterizedRoute(newState);\n\n if (\n navigationSpanName && // we have an active pageload tx\n newState.navigation.state === 'idle' && // navigation has completed\n normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName) && // this event is for the currently active navigation\n normalizePathname(parameterizedNavRoute) !== normalizePathname(navigationSpanName) // route is not parameterized yet\n ) {\n navigationSpan?.updateName(parameterizedNavRoute);\n navigationSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');\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":["GLOBAL_OBJ","spanToJSON","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","DEBUG_BUILD","consoleSandbox","getClient","startBrowserTracingNavigationSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","getActiveSpan","getRootSpan"],"mappings":"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"}
@@ -3,17 +3,31 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3
3
  const browser = require('@sentry/browser');
4
4
  const core = require('@sentry/core');
5
5
 
6
+ const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';
7
+
6
8
  /**
7
9
  * Initializes the client side of the React Router SDK.
8
10
  */
9
11
  function init(options) {
10
- const opts = {
11
- ...options,
12
- };
12
+ // If BrowserTracing integration was passed to options, emit a warning
13
+ if (options.integrations && Array.isArray(options.integrations)) {
14
+ const hasBrowserTracing = options.integrations.some(
15
+ integration => integration.name === BROWSER_TRACING_INTEGRATION_ID,
16
+ );
17
+
18
+ if (hasBrowserTracing) {
19
+ core.consoleSandbox(() => {
20
+ // eslint-disable-next-line no-console
21
+ console.warn(
22
+ 'browserTracingIntegration is not fully compatible with @sentry/react-router. Please use reactRouterTracingIntegration instead.',
23
+ );
24
+ });
25
+ }
26
+ }
13
27
 
14
- core.applySdkMetadata(opts, 'react-router', ['react-router', 'browser']);
28
+ core.applySdkMetadata(options, 'react-router', ['react-router', 'browser']);
15
29
 
16
- const client = browser.init(opts);
30
+ const client = browser.init(options);
17
31
 
18
32
  core.setTag('runtime', 'browser');
19
33
 
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.js","sources":["../../../src/client/sdk.ts"],"sourcesContent":["import type { BrowserOptions } from '@sentry/browser';\nimport { init as browserInit } from '@sentry/browser';\nimport type { Client } from '@sentry/core';\nimport { applySdkMetadata, setTag } from '@sentry/core';\n\n/**\n * Initializes the client side of the React Router SDK.\n */\nexport function init(options: BrowserOptions): Client | undefined {\n const opts = {\n ...options,\n };\n\n applySdkMetadata(opts, 'react-router', ['react-router', 'browser']);\n\n const client = browserInit(opts);\n\n setTag('runtime', 'browser');\n\n return client;\n}\n"],"names":["applySdkMetadata","browserInit","setTag"],"mappings":";;;;;AAKA;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,EAAsC;AAClE,EAAE,MAAM,OAAO;AACf,IAAI,GAAG,OAAO;AACd,GAAG;;AAEH,EAAEA,qBAAgB,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;;AAErE,EAAE,MAAM,MAAO,GAAEC,YAAW,CAAC,IAAI,CAAC;;AAElC,EAAEC,WAAM,CAAC,SAAS,EAAE,SAAS,CAAC;;AAE9B,EAAE,OAAO,MAAM;AACf;;;;"}
1
+ {"version":3,"file":"sdk.js","sources":["../../../src/client/sdk.ts"],"sourcesContent":["import type { BrowserOptions } from '@sentry/browser';\nimport { init as browserInit } from '@sentry/browser';\nimport type { Client } from '@sentry/core';\nimport { applySdkMetadata, consoleSandbox, setTag } from '@sentry/core';\n\nconst BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';\n\n/**\n * Initializes the client side of the React Router SDK.\n */\nexport function init(options: BrowserOptions): Client | undefined {\n // If BrowserTracing integration was passed to options, emit a warning\n if (options.integrations && Array.isArray(options.integrations)) {\n const hasBrowserTracing = options.integrations.some(\n integration => integration.name === BROWSER_TRACING_INTEGRATION_ID,\n );\n\n if (hasBrowserTracing) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n 'browserTracingIntegration is not fully compatible with @sentry/react-router. Please use reactRouterTracingIntegration instead.',\n );\n });\n }\n }\n\n applySdkMetadata(options, 'react-router', ['react-router', 'browser']);\n\n const client = browserInit(options);\n\n setTag('runtime', 'browser');\n\n return client;\n}\n"],"names":["consoleSandbox","applySdkMetadata","browserInit","setTag"],"mappings":";;;;;AAKA,MAAM,8BAAA,GAAiC,gBAAgB;;AAEvD;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,EAAsC;AAClE;AACA,EAAE,IAAI,OAAO,CAAC,gBAAgB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE;AACnE,IAAI,MAAM,iBAAkB,GAAE,OAAO,CAAC,YAAY,CAAC,IAAI;AACvD,MAAM,eAAe,WAAW,CAAC,IAAA,KAAS,8BAA8B;AACxE,KAAK;;AAEL,IAAI,IAAI,iBAAiB,EAAE;AAC3B,MAAMA,mBAAc,CAAC,MAAM;AAC3B;AACA,QAAQ,OAAO,CAAC,IAAI;AACpB,UAAU,gIAAgI;AAC1I,SAAS;AACT,OAAO,CAAC;AACR;AACA;;AAEA,EAAEC,qBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;;AAExE,EAAE,MAAM,MAAO,GAAEC,YAAW,CAAC,OAAO,CAAC;;AAErC,EAAEC,WAAM,CAAC,SAAS,EAAE,SAAS,CAAC;;AAE9B,EAAE,OAAO,MAAM;AACf;;;;"}
@@ -0,0 +1,27 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const browser = require('@sentry/browser');
4
+ const hydratedRouter = require('./hydratedRouter.js');
5
+
6
+ /**
7
+ * Browser tracing integration for React Router (Framework) applications.
8
+ * This integration will create navigation spans and enhance transactions names with parameterized routes.
9
+ */
10
+ function reactRouterTracingIntegration() {
11
+ const browserTracingIntegrationInstance = browser.browserTracingIntegration({
12
+ // Navigation transactions are started within the hydrated router instrumentation
13
+ instrumentNavigation: false,
14
+ });
15
+
16
+ return {
17
+ ...browserTracingIntegrationInstance,
18
+ name: 'ReactRouterTracingIntegration',
19
+ afterAllSetup(client) {
20
+ browserTracingIntegrationInstance.afterAllSetup(client);
21
+ hydratedRouter.instrumentHydratedRouter();
22
+ },
23
+ };
24
+ }
25
+
26
+ exports.reactRouterTracingIntegration = reactRouterTracingIntegration;
27
+ //# sourceMappingURL=tracingIntegration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracingIntegration.js","sources":["../../../src/client/tracingIntegration.ts"],"sourcesContent":["import { browserTracingIntegration as originalBrowserTracingIntegration } from '@sentry/browser';\nimport type { Integration } from '@sentry/core';\nimport { instrumentHydratedRouter } from './hydratedRouter';\n\n/**\n * Browser tracing integration for React Router (Framework) applications.\n * This integration will create navigation spans and enhance transactions names with parameterized routes.\n */\nexport function reactRouterTracingIntegration(): Integration {\n const browserTracingIntegrationInstance = originalBrowserTracingIntegration({\n // Navigation transactions are started within the hydrated router instrumentation\n instrumentNavigation: false,\n });\n\n return {\n ...browserTracingIntegrationInstance,\n name: 'ReactRouterTracingIntegration',\n afterAllSetup(client) {\n browserTracingIntegrationInstance.afterAllSetup(client);\n instrumentHydratedRouter();\n },\n };\n}\n"],"names":["originalBrowserTracingIntegration","instrumentHydratedRouter"],"mappings":";;;;;AAIA;AACA;AACA;AACA;AACO,SAAS,6BAA6B,GAAgB;AAC7D,EAAE,MAAM,iCAAA,GAAoCA,iCAAiC,CAAC;AAC9E;AACA,IAAI,oBAAoB,EAAE,KAAK;AAC/B,GAAG,CAAC;;AAEJ,EAAE,OAAO;AACT,IAAI,GAAG,iCAAiC;AACxC,IAAI,IAAI,EAAE,+BAA+B;AACzC,IAAI,aAAa,CAAC,MAAM,EAAE;AAC1B,MAAM,iCAAiC,CAAC,aAAa,CAAC,MAAM,CAAC;AAC7D,MAAMC,uCAAwB,EAAE;AAChC,KAAK;AACL,GAAG;AACH;;;;"}
@@ -2,10 +2,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const browser = require('@sentry/browser');
4
4
  const sdk = require('./client/sdk.js');
5
+ const tracingIntegration = require('./client/tracingIntegration.js');
5
6
 
6
7
 
7
8
 
8
9
  exports.init = sdk.init;
10
+ exports.reactRouterTracingIntegration = tracingIntegration.reactRouterTracingIntegration;
9
11
  Object.prototype.hasOwnProperty.call(browser, '__proto__') &&
10
12
  !Object.prototype.hasOwnProperty.call(exports, '__proto__') &&
11
13
  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":";;;;;;;;;;;;;;;;;;;"}
@@ -6,6 +6,7 @@ const wrapSentryHandleRequest = require('./server/wrapSentryHandleRequest.js');
6
6
  const createSentryHandleRequest = require('./server/createSentryHandleRequest.js');
7
7
  const plugin = require('./vite/plugin.js');
8
8
  const handleOnBuildEnd = require('./vite/buildEnd/handleOnBuildEnd.js');
9
+ const makeConfigInjectorPlugin = require('./vite/makeConfigInjectorPlugin.js');
9
10
 
10
11
 
11
12
 
@@ -16,6 +17,7 @@ exports.wrapSentryHandleRequest = wrapSentryHandleRequest.wrapSentryHandleReques
16
17
  exports.createSentryHandleRequest = createSentryHandleRequest.createSentryHandleRequest;
17
18
  exports.sentryReactRouter = plugin.sentryReactRouter;
18
19
  exports.sentryOnBuildEnd = handleOnBuildEnd.sentryOnBuildEnd;
20
+ exports.makeConfigInjectorPlugin = makeConfigInjectorPlugin.makeConfigInjectorPlugin;
19
21
  Object.prototype.hasOwnProperty.call(node, '__proto__') &&
20
22
  !Object.prototype.hasOwnProperty.call(exports, '__proto__') &&
21
23
  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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,25 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ /**
4
+ * Creates a Vite plugin that injects the Sentry options into the global Vite config.
5
+ * This ensures the sentryConfig is available to other components that need access to it,
6
+ * like the buildEnd hook.
7
+ *
8
+ * @param options - Configuration options for the Sentry Vite plugin
9
+ * @returns A Vite plugin that injects sentryConfig into the global config
10
+ */
11
+ function makeConfigInjectorPlugin(options) {
12
+ return {
13
+ name: 'sentry-react-router-config-injector',
14
+ enforce: 'pre',
15
+ config(config) {
16
+ return {
17
+ ...config,
18
+ sentryConfig: options,
19
+ };
20
+ },
21
+ };
22
+ }
23
+
24
+ exports.makeConfigInjectorPlugin = makeConfigInjectorPlugin;
25
+ //# sourceMappingURL=makeConfigInjectorPlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeConfigInjectorPlugin.js","sources":["../../../src/vite/makeConfigInjectorPlugin.ts"],"sourcesContent":["import { type Plugin } from 'vite';\nimport type { SentryReactRouterBuildOptions } from './types';\n\n/**\n * Creates a Vite plugin that injects the Sentry options into the global Vite config.\n * This ensures the sentryConfig is available to other components that need access to it,\n * like the buildEnd hook.\n *\n * @param options - Configuration options for the Sentry Vite plugin\n * @returns A Vite plugin that injects sentryConfig into the global config\n */\nexport function makeConfigInjectorPlugin(options: SentryReactRouterBuildOptions): Plugin {\n return {\n name: 'sentry-react-router-config-injector',\n enforce: 'pre',\n config(config) {\n return {\n ...config,\n sentryConfig: options,\n };\n },\n };\n}\n"],"names":[],"mappings":";;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,wBAAwB,CAAC,OAAO,EAAyC;AACzF,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,qCAAqC;AAC/C,IAAI,OAAO,EAAE,KAAK;AAClB,IAAI,MAAM,CAAC,MAAM,EAAE;AACnB,MAAM,OAAO;AACb,QAAQ,GAAG,MAAM;AACjB,QAAQ,YAAY,EAAE,OAAO;AAC7B,OAAO;AACP,KAAK;AACL,GAAG;AACH;;;;"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
+ const makeConfigInjectorPlugin = require('./makeConfigInjectorPlugin.js');
3
4
  const makeCustomSentryVitePlugins = require('./makeCustomSentryVitePlugins.js');
4
5
  const makeEnableSourceMapsPlugin = require('./makeEnableSourceMapsPlugin.js');
5
6
 
@@ -16,6 +17,8 @@ async function sentryReactRouter(
16
17
  ) {
17
18
  const plugins = [];
18
19
 
20
+ plugins.push(makeConfigInjectorPlugin.makeConfigInjectorPlugin(options));
21
+
19
22
  if (process.env.NODE_ENV !== 'development' && config.command === 'build' && config.mode !== 'development') {
20
23
  plugins.push(makeEnableSourceMapsPlugin.makeEnableSourceMapsPlugin(options));
21
24
  plugins.push(...(await makeCustomSentryVitePlugins.makeCustomSentryVitePlugins(options)));
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../../src/vite/plugin.ts"],"sourcesContent":["import type { ConfigEnv } from 'vite';\nimport { type Plugin } from 'vite';\nimport { makeCustomSentryVitePlugins } from './makeCustomSentryVitePlugins';\nimport { makeEnableSourceMapsPlugin } from './makeEnableSourceMapsPlugin';\nimport type { SentryReactRouterBuildOptions } from './types';\n\n/**\n * A Vite plugin for Sentry that handles source map uploads and bundle size optimizations.\n *\n * @param options - Configuration options for the Sentry Vite plugin\n * @param viteConfig - The Vite user config object\n * @returns An array of Vite plugins\n */\nexport async function sentryReactRouter(\n options: SentryReactRouterBuildOptions = {},\n config: ConfigEnv,\n): Promise<Plugin[]> {\n const plugins: Plugin[] = [];\n\n if (process.env.NODE_ENV !== 'development' && config.command === 'build' && config.mode !== 'development') {\n plugins.push(makeEnableSourceMapsPlugin(options));\n plugins.push(...(await makeCustomSentryVitePlugins(options)));\n }\n\n return plugins;\n}\n"],"names":["makeEnableSourceMapsPlugin","makeCustomSentryVitePlugins"],"mappings":";;;;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,iBAAiB;AACvC,EAAE,OAAO,GAAkC,EAAE;AAC7C,EAAE,MAAM;AACR,EAAqB;AACrB,EAAE,MAAM,OAAO,GAAa,EAAE;;AAE9B,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAA,KAAa,aAAc,IAAG,MAAM,CAAC,OAAA,KAAY,OAAQ,IAAG,MAAM,CAAC,IAAA,KAAS,aAAa,EAAE;AAC7G,IAAI,OAAO,CAAC,IAAI,CAACA,qDAA0B,CAAC,OAAO,CAAC,CAAC;AACrD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,MAAMC,uDAA2B,CAAC,OAAO,CAAC,CAAC,CAAC;AACjE;;AAEA,EAAE,OAAO,OAAO;AAChB;;;;"}
1
+ {"version":3,"file":"plugin.js","sources":["../../../src/vite/plugin.ts"],"sourcesContent":["import type { ConfigEnv } from 'vite';\nimport { type Plugin } from 'vite';\nimport { makeConfigInjectorPlugin } from './makeConfigInjectorPlugin';\nimport { makeCustomSentryVitePlugins } from './makeCustomSentryVitePlugins';\nimport { makeEnableSourceMapsPlugin } from './makeEnableSourceMapsPlugin';\nimport type { SentryReactRouterBuildOptions } from './types';\n\n/**\n * A Vite plugin for Sentry that handles source map uploads and bundle size optimizations.\n *\n * @param options - Configuration options for the Sentry Vite plugin\n * @param viteConfig - The Vite user config object\n * @returns An array of Vite plugins\n */\nexport async function sentryReactRouter(\n options: SentryReactRouterBuildOptions = {},\n config: ConfigEnv,\n): Promise<Plugin[]> {\n const plugins: Plugin[] = [];\n\n plugins.push(makeConfigInjectorPlugin(options));\n\n if (process.env.NODE_ENV !== 'development' && config.command === 'build' && config.mode !== 'development') {\n plugins.push(makeEnableSourceMapsPlugin(options));\n plugins.push(...(await makeCustomSentryVitePlugins(options)));\n }\n\n return plugins;\n}\n"],"names":["makeConfigInjectorPlugin","makeEnableSourceMapsPlugin","makeCustomSentryVitePlugins"],"mappings":";;;;;;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,iBAAiB;AACvC,EAAE,OAAO,GAAkC,EAAE;AAC7C,EAAE,MAAM;AACR,EAAqB;AACrB,EAAE,MAAM,OAAO,GAAa,EAAE;;AAE9B,EAAE,OAAO,CAAC,IAAI,CAACA,iDAAwB,CAAC,OAAO,CAAC,CAAC;;AAEjD,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAA,KAAa,aAAc,IAAG,MAAM,CAAC,OAAA,KAAY,OAAQ,IAAG,MAAM,CAAC,IAAA,KAAS,aAAa,EAAE;AAC7G,IAAI,OAAO,CAAC,IAAI,CAACC,qDAA0B,CAAC,OAAO,CAAC,CAAC;AACrD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,MAAMC,uDAA2B,CAAC,OAAO,CAAC,CAAC,CAAC;AACjE;;AAEA,EAAE,OAAO,OAAO;AAChB;;;;"}
@@ -0,0 +1,138 @@
1
+ import { startBrowserTracingNavigationSpan } from '@sentry/browser';
2
+ import { GLOBAL_OBJ, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, consoleSandbox, getActiveSpan, getRootSpan, getClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
3
+ import { DEBUG_BUILD } from '../common/debug-build.js';
4
+
5
+ const GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ
6
+
7
+ ;
8
+
9
+ const MAX_RETRIES = 40; // 2 seconds at 50ms interval
10
+
11
+ /**
12
+ * Instruments the React Router Data Router for pageloads and navigation.
13
+ *
14
+ * This function waits for the router to be available after hydration, then:
15
+ * 1. Updates the pageload transaction with parameterized route info
16
+ * 2. Patches router.navigate() to create navigation transactions
17
+ * 3. Subscribes to router state changes to update navigation transactions with parameterized routes
18
+ */
19
+ function instrumentHydratedRouter() {
20
+ function trySubscribe() {
21
+ const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;
22
+
23
+ if (router) {
24
+ // The first time we hit the router, we try to update the pageload transaction
25
+ // todo: update pageload tx here
26
+ const pageloadSpan = getActiveRootSpan();
27
+ const pageloadName = pageloadSpan ? spanToJSON(pageloadSpan).description : undefined;
28
+ const parameterizePageloadRoute = getParameterizedRoute(router.state);
29
+ if (
30
+ pageloadName &&
31
+ normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName) && // this event is for the currently active pageload
32
+ normalizePathname(parameterizePageloadRoute) !== normalizePathname(pageloadName) // route is not parameterized yet
33
+ ) {
34
+ pageloadSpan?.updateName(parameterizePageloadRoute);
35
+ pageloadSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
36
+ }
37
+
38
+ // Patching navigate for creating accurate navigation transactions
39
+ if (typeof router.navigate === 'function') {
40
+ const originalNav = router.navigate.bind(router);
41
+ router.navigate = function sentryPatchedNavigate(...args) {
42
+ maybeCreateNavigationTransaction(
43
+ String(args[0]) || '<unknown route>', // will be updated anyway
44
+ 'url', // this also will be updated once we have the parameterized route
45
+ );
46
+ return originalNav(...args);
47
+ };
48
+ }
49
+
50
+ // Subscribe to router state changes to update navigation transactions with parameterized routes
51
+ router.subscribe(newState => {
52
+ const navigationSpan = getActiveRootSpan();
53
+ const navigationSpanName = navigationSpan ? spanToJSON(navigationSpan).description : undefined;
54
+ const parameterizedNavRoute = getParameterizedRoute(newState);
55
+
56
+ if (
57
+ navigationSpanName && // we have an active pageload tx
58
+ newState.navigation.state === 'idle' && // navigation has completed
59
+ normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName) && // this event is for the currently active navigation
60
+ normalizePathname(parameterizedNavRoute) !== normalizePathname(navigationSpanName) // route is not parameterized yet
61
+ ) {
62
+ navigationSpan?.updateName(parameterizedNavRoute);
63
+ navigationSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
64
+ }
65
+ });
66
+ return true;
67
+ }
68
+ return false;
69
+ }
70
+
71
+ // Wait until the router is available (since the SDK loads before hydration)
72
+ if (!trySubscribe()) {
73
+ let retryCount = 0;
74
+ // Retry until the router is available or max retries reached
75
+ const interval = setInterval(() => {
76
+ if (trySubscribe() || retryCount >= MAX_RETRIES) {
77
+ if (retryCount >= MAX_RETRIES) {
78
+ DEBUG_BUILD &&
79
+ consoleSandbox(() => {
80
+ // eslint-disable-next-line no-console
81
+ console.warn('Unable to instrument React Router: router not found after hydration.');
82
+ });
83
+ }
84
+ clearInterval(interval);
85
+ }
86
+ retryCount++;
87
+ }, 50);
88
+ }
89
+ }
90
+
91
+ function maybeCreateNavigationTransaction(name, source) {
92
+ const client = getClient();
93
+
94
+ if (!client) {
95
+ return undefined;
96
+ }
97
+
98
+ return startBrowserTracingNavigationSpan(client, {
99
+ name,
100
+ attributes: {
101
+ [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
102
+ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
103
+ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react-router',
104
+ },
105
+ });
106
+ }
107
+
108
+ function getActiveRootSpan() {
109
+ const activeSpan = getActiveSpan();
110
+ if (!activeSpan) {
111
+ return undefined;
112
+ }
113
+
114
+ const rootSpan = getRootSpan(activeSpan);
115
+
116
+ const op = spanToJSON(rootSpan).op;
117
+
118
+ // Only use this root span if it is a pageload or navigation span
119
+ return op === 'navigation' || op === 'pageload' ? rootSpan : undefined;
120
+ }
121
+
122
+ function getParameterizedRoute(routerState) {
123
+ const lastMatch = routerState.matches[routerState.matches.length - 1];
124
+ return normalizePathname(lastMatch?.route.path ?? routerState.location.pathname);
125
+ }
126
+
127
+ function normalizePathname(pathname) {
128
+ // Ensure it starts with a single slash
129
+ let normalized = pathname.startsWith('/') ? pathname : `/${pathname}`;
130
+ // Remove trailing slash unless it's the root
131
+ if (normalized.length > 1 && normalized.endsWith('/')) {
132
+ normalized = normalized.slice(0, -1);
133
+ }
134
+ return normalized;
135
+ }
136
+
137
+ export { instrumentHydratedRouter };
138
+ //# sourceMappingURL=hydratedRouter.js.map
@@ -0,0 +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 const pageloadName = pageloadSpan ? spanToJSON(pageloadSpan).description : undefined;\n const parameterizePageloadRoute = getParameterizedRoute(router.state);\n if (\n pageloadName &&\n normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName) && // this event is for the currently active pageload\n normalizePathname(parameterizePageloadRoute) !== normalizePathname(pageloadName) // route is not parameterized yet\n ) {\n pageloadSpan?.updateName(parameterizePageloadRoute);\n pageloadSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');\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 // Subscribe to router state changes to update navigation transactions with parameterized routes\n router.subscribe(newState => {\n const navigationSpan = getActiveRootSpan();\n const navigationSpanName = navigationSpan ? spanToJSON(navigationSpan).description : undefined;\n const parameterizedNavRoute = getParameterizedRoute(newState);\n\n if (\n navigationSpanName && // we have an active pageload tx\n newState.navigation.state === 'idle' && // navigation has completed\n normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName) && // this event is for the currently active navigation\n normalizePathname(parameterizedNavRoute) !== normalizePathname(navigationSpanName) // route is not parameterized yet\n ) {\n navigationSpan?.updateName(parameterizedNavRoute);\n navigationSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');\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,17 +1,31 @@
1
1
  import { init as init$1 } from '@sentry/browser';
2
- import { applySdkMetadata, setTag } from '@sentry/core';
2
+ import { consoleSandbox, applySdkMetadata, setTag } from '@sentry/core';
3
+
4
+ const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';
3
5
 
4
6
  /**
5
7
  * Initializes the client side of the React Router SDK.
6
8
  */
7
9
  function init(options) {
8
- const opts = {
9
- ...options,
10
- };
10
+ // If BrowserTracing integration was passed to options, emit a warning
11
+ if (options.integrations && Array.isArray(options.integrations)) {
12
+ const hasBrowserTracing = options.integrations.some(
13
+ integration => integration.name === BROWSER_TRACING_INTEGRATION_ID,
14
+ );
15
+
16
+ if (hasBrowserTracing) {
17
+ consoleSandbox(() => {
18
+ // eslint-disable-next-line no-console
19
+ console.warn(
20
+ 'browserTracingIntegration is not fully compatible with @sentry/react-router. Please use reactRouterTracingIntegration instead.',
21
+ );
22
+ });
23
+ }
24
+ }
11
25
 
12
- applySdkMetadata(opts, 'react-router', ['react-router', 'browser']);
26
+ applySdkMetadata(options, 'react-router', ['react-router', 'browser']);
13
27
 
14
- const client = init$1(opts);
28
+ const client = init$1(options);
15
29
 
16
30
  setTag('runtime', 'browser');
17
31
 
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.js","sources":["../../../src/client/sdk.ts"],"sourcesContent":["import type { BrowserOptions } from '@sentry/browser';\nimport { init as browserInit } from '@sentry/browser';\nimport type { Client } from '@sentry/core';\nimport { applySdkMetadata, setTag } from '@sentry/core';\n\n/**\n * Initializes the client side of the React Router SDK.\n */\nexport function init(options: BrowserOptions): Client | undefined {\n const opts = {\n ...options,\n };\n\n applySdkMetadata(opts, 'react-router', ['react-router', 'browser']);\n\n const client = browserInit(opts);\n\n setTag('runtime', 'browser');\n\n return client;\n}\n"],"names":["browserInit"],"mappings":";;;AAKA;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,EAAsC;AAClE,EAAE,MAAM,OAAO;AACf,IAAI,GAAG,OAAO;AACd,GAAG;;AAEH,EAAE,gBAAgB,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;;AAErE,EAAE,MAAM,MAAO,GAAEA,MAAW,CAAC,IAAI,CAAC;;AAElC,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;;AAE9B,EAAE,OAAO,MAAM;AACf;;;;"}
1
+ {"version":3,"file":"sdk.js","sources":["../../../src/client/sdk.ts"],"sourcesContent":["import type { BrowserOptions } from '@sentry/browser';\nimport { init as browserInit } from '@sentry/browser';\nimport type { Client } from '@sentry/core';\nimport { applySdkMetadata, consoleSandbox, setTag } from '@sentry/core';\n\nconst BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';\n\n/**\n * Initializes the client side of the React Router SDK.\n */\nexport function init(options: BrowserOptions): Client | undefined {\n // If BrowserTracing integration was passed to options, emit a warning\n if (options.integrations && Array.isArray(options.integrations)) {\n const hasBrowserTracing = options.integrations.some(\n integration => integration.name === BROWSER_TRACING_INTEGRATION_ID,\n );\n\n if (hasBrowserTracing) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n 'browserTracingIntegration is not fully compatible with @sentry/react-router. Please use reactRouterTracingIntegration instead.',\n );\n });\n }\n }\n\n applySdkMetadata(options, 'react-router', ['react-router', 'browser']);\n\n const client = browserInit(options);\n\n setTag('runtime', 'browser');\n\n return client;\n}\n"],"names":["browserInit"],"mappings":";;;AAKA,MAAM,8BAAA,GAAiC,gBAAgB;;AAEvD;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,EAAsC;AAClE;AACA,EAAE,IAAI,OAAO,CAAC,gBAAgB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE;AACnE,IAAI,MAAM,iBAAkB,GAAE,OAAO,CAAC,YAAY,CAAC,IAAI;AACvD,MAAM,eAAe,WAAW,CAAC,IAAA,KAAS,8BAA8B;AACxE,KAAK;;AAEL,IAAI,IAAI,iBAAiB,EAAE;AAC3B,MAAM,cAAc,CAAC,MAAM;AAC3B;AACA,QAAQ,OAAO,CAAC,IAAI;AACpB,UAAU,gIAAgI;AAC1I,SAAS;AACT,OAAO,CAAC;AACR;AACA;;AAEA,EAAE,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;;AAExE,EAAE,MAAM,MAAO,GAAEA,MAAW,CAAC,OAAO,CAAC;;AAErC,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;;AAE9B,EAAE,OAAO,MAAM;AACf;;;;"}
@@ -0,0 +1,25 @@
1
+ import { browserTracingIntegration } from '@sentry/browser';
2
+ import { instrumentHydratedRouter } from './hydratedRouter.js';
3
+
4
+ /**
5
+ * Browser tracing integration for React Router (Framework) applications.
6
+ * This integration will create navigation spans and enhance transactions names with parameterized routes.
7
+ */
8
+ function reactRouterTracingIntegration() {
9
+ const browserTracingIntegrationInstance = browserTracingIntegration({
10
+ // Navigation transactions are started within the hydrated router instrumentation
11
+ instrumentNavigation: false,
12
+ });
13
+
14
+ return {
15
+ ...browserTracingIntegrationInstance,
16
+ name: 'ReactRouterTracingIntegration',
17
+ afterAllSetup(client) {
18
+ browserTracingIntegrationInstance.afterAllSetup(client);
19
+ instrumentHydratedRouter();
20
+ },
21
+ };
22
+ }
23
+
24
+ export { reactRouterTracingIntegration };
25
+ //# sourceMappingURL=tracingIntegration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracingIntegration.js","sources":["../../../src/client/tracingIntegration.ts"],"sourcesContent":["import { browserTracingIntegration as originalBrowserTracingIntegration } from '@sentry/browser';\nimport type { Integration } from '@sentry/core';\nimport { instrumentHydratedRouter } from './hydratedRouter';\n\n/**\n * Browser tracing integration for React Router (Framework) applications.\n * This integration will create navigation spans and enhance transactions names with parameterized routes.\n */\nexport function reactRouterTracingIntegration(): Integration {\n const browserTracingIntegrationInstance = originalBrowserTracingIntegration({\n // Navigation transactions are started within the hydrated router instrumentation\n instrumentNavigation: false,\n });\n\n return {\n ...browserTracingIntegrationInstance,\n name: 'ReactRouterTracingIntegration',\n afterAllSetup(client) {\n browserTracingIntegrationInstance.afterAllSetup(client);\n instrumentHydratedRouter();\n },\n };\n}\n"],"names":["originalBrowserTracingIntegration"],"mappings":";;;AAIA;AACA;AACA;AACA;AACO,SAAS,6BAA6B,GAAgB;AAC7D,EAAE,MAAM,iCAAA,GAAoCA,yBAAiC,CAAC;AAC9E;AACA,IAAI,oBAAoB,EAAE,KAAK;AAC/B,GAAG,CAAC;;AAEJ,EAAE,OAAO;AACT,IAAI,GAAG,iCAAiC;AACxC,IAAI,IAAI,EAAE,+BAA+B;AACzC,IAAI,aAAa,CAAC,MAAM,EAAE;AAC1B,MAAM,iCAAiC,CAAC,aAAa,CAAC,MAAM,CAAC;AAC7D,MAAM,wBAAwB,EAAE;AAChC,KAAK;AACL,GAAG;AACH;;;;"}
@@ -1,3 +1,4 @@
1
1
  export * from '@sentry/browser';
2
2
  export { init } from './client/sdk.js';
3
+ export { reactRouterTracingIntegration } from './client/tracingIntegration.js';
3
4
  //# sourceMappingURL=index.client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.client.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"index.client.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -4,4 +4,5 @@ export { getMetaTagTransformer, sentryHandleRequest, wrapSentryHandleRequest } f
4
4
  export { createSentryHandleRequest } from './server/createSentryHandleRequest.js';
5
5
  export { sentryReactRouter } from './vite/plugin.js';
6
6
  export { sentryOnBuildEnd } from './vite/buildEnd/handleOnBuildEnd.js';
7
+ export { makeConfigInjectorPlugin } from './vite/makeConfigInjectorPlugin.js';
7
8
  //# sourceMappingURL=index.server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.server.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
1
+ {"version":3,"file":"index.server.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
@@ -1 +1 @@
1
- {"type":"module","version":"9.15.0"}
1
+ {"type":"module","version":"9.16.1"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Creates a Vite plugin that injects the Sentry options into the global Vite config.
3
+ * This ensures the sentryConfig is available to other components that need access to it,
4
+ * like the buildEnd hook.
5
+ *
6
+ * @param options - Configuration options for the Sentry Vite plugin
7
+ * @returns A Vite plugin that injects sentryConfig into the global config
8
+ */
9
+ function makeConfigInjectorPlugin(options) {
10
+ return {
11
+ name: 'sentry-react-router-config-injector',
12
+ enforce: 'pre',
13
+ config(config) {
14
+ return {
15
+ ...config,
16
+ sentryConfig: options,
17
+ };
18
+ },
19
+ };
20
+ }
21
+
22
+ export { makeConfigInjectorPlugin };
23
+ //# sourceMappingURL=makeConfigInjectorPlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeConfigInjectorPlugin.js","sources":["../../../src/vite/makeConfigInjectorPlugin.ts"],"sourcesContent":["import { type Plugin } from 'vite';\nimport type { SentryReactRouterBuildOptions } from './types';\n\n/**\n * Creates a Vite plugin that injects the Sentry options into the global Vite config.\n * This ensures the sentryConfig is available to other components that need access to it,\n * like the buildEnd hook.\n *\n * @param options - Configuration options for the Sentry Vite plugin\n * @returns A Vite plugin that injects sentryConfig into the global config\n */\nexport function makeConfigInjectorPlugin(options: SentryReactRouterBuildOptions): Plugin {\n return {\n name: 'sentry-react-router-config-injector',\n enforce: 'pre',\n config(config) {\n return {\n ...config,\n sentryConfig: options,\n };\n },\n };\n}\n"],"names":[],"mappings":"AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,wBAAwB,CAAC,OAAO,EAAyC;AACzF,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,qCAAqC;AAC/C,IAAI,OAAO,EAAE,KAAK;AAClB,IAAI,MAAM,CAAC,MAAM,EAAE;AACnB,MAAM,OAAO;AACb,QAAQ,GAAG,MAAM;AACjB,QAAQ,YAAY,EAAE,OAAO;AAC7B,OAAO;AACP,KAAK;AACL,GAAG;AACH;;;;"}
@@ -1,3 +1,4 @@
1
+ import { makeConfigInjectorPlugin } from './makeConfigInjectorPlugin.js';
1
2
  import { makeCustomSentryVitePlugins } from './makeCustomSentryVitePlugins.js';
2
3
  import { makeEnableSourceMapsPlugin } from './makeEnableSourceMapsPlugin.js';
3
4
 
@@ -14,6 +15,8 @@ async function sentryReactRouter(
14
15
  ) {
15
16
  const plugins = [];
16
17
 
18
+ plugins.push(makeConfigInjectorPlugin(options));
19
+
17
20
  if (process.env.NODE_ENV !== 'development' && config.command === 'build' && config.mode !== 'development') {
18
21
  plugins.push(makeEnableSourceMapsPlugin(options));
19
22
  plugins.push(...(await makeCustomSentryVitePlugins(options)));
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../../src/vite/plugin.ts"],"sourcesContent":["import type { ConfigEnv } from 'vite';\nimport { type Plugin } from 'vite';\nimport { makeCustomSentryVitePlugins } from './makeCustomSentryVitePlugins';\nimport { makeEnableSourceMapsPlugin } from './makeEnableSourceMapsPlugin';\nimport type { SentryReactRouterBuildOptions } from './types';\n\n/**\n * A Vite plugin for Sentry that handles source map uploads and bundle size optimizations.\n *\n * @param options - Configuration options for the Sentry Vite plugin\n * @param viteConfig - The Vite user config object\n * @returns An array of Vite plugins\n */\nexport async function sentryReactRouter(\n options: SentryReactRouterBuildOptions = {},\n config: ConfigEnv,\n): Promise<Plugin[]> {\n const plugins: Plugin[] = [];\n\n if (process.env.NODE_ENV !== 'development' && config.command === 'build' && config.mode !== 'development') {\n plugins.push(makeEnableSourceMapsPlugin(options));\n plugins.push(...(await makeCustomSentryVitePlugins(options)));\n }\n\n return plugins;\n}\n"],"names":[],"mappings":";;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,iBAAiB;AACvC,EAAE,OAAO,GAAkC,EAAE;AAC7C,EAAE,MAAM;AACR,EAAqB;AACrB,EAAE,MAAM,OAAO,GAAa,EAAE;;AAE9B,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAA,KAAa,aAAc,IAAG,MAAM,CAAC,OAAA,KAAY,OAAQ,IAAG,MAAM,CAAC,IAAA,KAAS,aAAa,EAAE;AAC7G,IAAI,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAC;AACrD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,MAAM,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAC;AACjE;;AAEA,EAAE,OAAO,OAAO;AAChB;;;;"}
1
+ {"version":3,"file":"plugin.js","sources":["../../../src/vite/plugin.ts"],"sourcesContent":["import type { ConfigEnv } from 'vite';\nimport { type Plugin } from 'vite';\nimport { makeConfigInjectorPlugin } from './makeConfigInjectorPlugin';\nimport { makeCustomSentryVitePlugins } from './makeCustomSentryVitePlugins';\nimport { makeEnableSourceMapsPlugin } from './makeEnableSourceMapsPlugin';\nimport type { SentryReactRouterBuildOptions } from './types';\n\n/**\n * A Vite plugin for Sentry that handles source map uploads and bundle size optimizations.\n *\n * @param options - Configuration options for the Sentry Vite plugin\n * @param viteConfig - The Vite user config object\n * @returns An array of Vite plugins\n */\nexport async function sentryReactRouter(\n options: SentryReactRouterBuildOptions = {},\n config: ConfigEnv,\n): Promise<Plugin[]> {\n const plugins: Plugin[] = [];\n\n plugins.push(makeConfigInjectorPlugin(options));\n\n if (process.env.NODE_ENV !== 'development' && config.command === 'build' && config.mode !== 'development') {\n plugins.push(makeEnableSourceMapsPlugin(options));\n plugins.push(...(await makeCustomSentryVitePlugins(options)));\n }\n\n return plugins;\n}\n"],"names":[],"mappings":";;;;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,iBAAiB;AACvC,EAAE,OAAO,GAAkC,EAAE;AAC7C,EAAE,MAAM;AACR,EAAqB;AACrB,EAAE,MAAM,OAAO,GAAa,EAAE;;AAE9B,EAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC;;AAEjD,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAA,KAAa,aAAc,IAAG,MAAM,CAAC,OAAA,KAAY,OAAQ,IAAG,MAAM,CAAC,IAAA,KAAS,aAAa,EAAE;AAC7G,IAAI,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAC;AACrD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,MAAM,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAC;AACjE;;AAEA,EAAE,OAAO,OAAO;AAChB;;;;"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Instruments the React Router Data Router for pageloads and navigation.
3
+ *
4
+ * This function waits for the router to be available after hydration, then:
5
+ * 1. Updates the pageload transaction with parameterized route info
6
+ * 2. Patches router.navigate() to create navigation transactions
7
+ * 3. Subscribes to router state changes to update navigation transactions with parameterized routes
8
+ */
9
+ export declare function instrumentHydratedRouter(): void;
10
+ //# sourceMappingURL=hydratedRouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydratedRouter.d.ts","sourceRoot":"","sources":["../../../src/client/hydratedRouter.ts"],"names":[],"mappings":"AAsBA;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAsE/C"}
@@ -1,3 +1,4 @@
1
1
  export * from '@sentry/browser';
2
2
  export { init } from './sdk';
3
+ export { reactRouterTracingIntegration } from './tracingIntegration';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAEhC,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAEhC,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAC7B,OAAO,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/client/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAYhE"}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/client/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAK3C;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAwBhE"}
@@ -0,0 +1,7 @@
1
+ import type { Integration } from '@sentry/core';
2
+ /**
3
+ * Browser tracing integration for React Router (Framework) applications.
4
+ * This integration will create navigation spans and enhance transactions names with parameterized routes.
5
+ */
6
+ export declare function reactRouterTracingIntegration(): Integration;
7
+ //# sourceMappingURL=tracingIntegration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracingIntegration.d.ts","sourceRoot":"","sources":["../../../src/client/tracingIntegration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,WAAW,CAc3D"}
@@ -1,4 +1,5 @@
1
1
  export { sentryReactRouter } from './plugin';
2
2
  export { sentryOnBuildEnd } from './buildEnd/handleOnBuildEnd';
3
3
  export type { SentryReactRouterBuildOptions } from './types';
4
+ export { makeConfigInjectorPlugin } from './makeConfigInjectorPlugin';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,YAAY,EAAE,6BAA6B,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,YAAY,EAAE,6BAA6B,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type Plugin } from 'vite';
2
+ import type { SentryReactRouterBuildOptions } from './types';
3
+ /**
4
+ * Creates a Vite plugin that injects the Sentry options into the global Vite config.
5
+ * This ensures the sentryConfig is available to other components that need access to it,
6
+ * like the buildEnd hook.
7
+ *
8
+ * @param options - Configuration options for the Sentry Vite plugin
9
+ * @returns A Vite plugin that injects sentryConfig into the global config
10
+ */
11
+ export declare function makeConfigInjectorPlugin(options: SentryReactRouterBuildOptions): Plugin;
12
+ //# sourceMappingURL=makeConfigInjectorPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeConfigInjectorPlugin.d.ts","sourceRoot":"","sources":["../../../src/vite/makeConfigInjectorPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,SAAS,CAAC;AAE7D;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,6BAA6B,GAAG,MAAM,CAWvF"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/vite/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,SAAS,CAAC;AAE7D;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,2CAAoC,EAC3C,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CASnB"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/vite/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,SAAS,CAAC;AAE7D;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,2CAAoC,EAC3C,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAWnB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/react-router",
3
- "version": "9.15.0",
3
+ "version": "9.16.1",
4
4
  "description": "Official Sentry SDK for React Router (Framework)",
5
5
  "repository": "git://github.com/getsentry/sentry-javascript.git",
6
6
  "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react-router",
@@ -37,10 +37,10 @@
37
37
  "@opentelemetry/api": "^1.9.0",
38
38
  "@opentelemetry/core": "^1.30.1",
39
39
  "@opentelemetry/semantic-conventions": "^1.30.0",
40
- "@sentry/browser": "9.15.0",
40
+ "@sentry/browser": "9.16.1",
41
41
  "@sentry/cli": "^2.43.0",
42
- "@sentry/core": "9.15.0",
43
- "@sentry/node": "9.15.0",
42
+ "@sentry/core": "9.16.1",
43
+ "@sentry/node": "9.16.1",
44
44
  "@sentry/vite-plugin": "^3.2.4",
45
45
  "glob": "11.0.1"
46
46
  },