@sentry/react-router 9.14.0 → 9.16.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 (62) 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/server/createSentryHandleRequest.js +1 -1
  12. package/build/cjs/server/createSentryHandleRequest.js.map +1 -1
  13. package/build/cjs/server/wrapSentryHandleRequest.js.map +1 -1
  14. package/build/cjs/vite/buildEnd/handleOnBuildEnd.js +10 -1
  15. package/build/cjs/vite/buildEnd/handleOnBuildEnd.js.map +1 -1
  16. package/build/cjs/vite/makeConfigInjectorPlugin.js +25 -0
  17. package/build/cjs/vite/makeConfigInjectorPlugin.js.map +1 -0
  18. package/build/cjs/vite/makeCustomSentryVitePlugins.js +16 -2
  19. package/build/cjs/vite/makeCustomSentryVitePlugins.js.map +1 -1
  20. package/build/cjs/vite/plugin.js +3 -0
  21. package/build/cjs/vite/plugin.js.map +1 -1
  22. package/build/esm/client/hydratedRouter.js +138 -0
  23. package/build/esm/client/hydratedRouter.js.map +1 -0
  24. package/build/esm/client/sdk.js +20 -6
  25. package/build/esm/client/sdk.js.map +1 -1
  26. package/build/esm/client/tracingIntegration.js +25 -0
  27. package/build/esm/client/tracingIntegration.js.map +1 -0
  28. package/build/esm/index.client.js +1 -0
  29. package/build/esm/index.client.js.map +1 -1
  30. package/build/esm/index.server.js +1 -0
  31. package/build/esm/index.server.js.map +1 -1
  32. package/build/esm/package.json +1 -1
  33. package/build/esm/server/createSentryHandleRequest.js +1 -1
  34. package/build/esm/server/createSentryHandleRequest.js.map +1 -1
  35. package/build/esm/server/wrapSentryHandleRequest.js.map +1 -1
  36. package/build/esm/vite/buildEnd/handleOnBuildEnd.js +10 -1
  37. package/build/esm/vite/buildEnd/handleOnBuildEnd.js.map +1 -1
  38. package/build/esm/vite/makeConfigInjectorPlugin.js +23 -0
  39. package/build/esm/vite/makeConfigInjectorPlugin.js.map +1 -0
  40. package/build/esm/vite/makeCustomSentryVitePlugins.js +16 -2
  41. package/build/esm/vite/makeCustomSentryVitePlugins.js.map +1 -1
  42. package/build/esm/vite/plugin.js +3 -0
  43. package/build/esm/vite/plugin.js.map +1 -1
  44. package/build/types/client/hydratedRouter.d.ts +10 -0
  45. package/build/types/client/hydratedRouter.d.ts.map +1 -0
  46. package/build/types/client/index.d.ts +1 -0
  47. package/build/types/client/index.d.ts.map +1 -1
  48. package/build/types/client/sdk.d.ts.map +1 -1
  49. package/build/types/client/tracingIntegration.d.ts +7 -0
  50. package/build/types/client/tracingIntegration.d.ts.map +1 -0
  51. package/build/types/index.types.d.ts +3 -3
  52. package/build/types/index.types.d.ts.map +1 -1
  53. package/build/types/server/createSentryHandleRequest.d.ts +2 -2
  54. package/build/types/server/createSentryHandleRequest.d.ts.map +1 -1
  55. package/build/types/vite/buildEnd/handleOnBuildEnd.d.ts.map +1 -1
  56. package/build/types/vite/index.d.ts +1 -0
  57. package/build/types/vite/index.d.ts.map +1 -1
  58. package/build/types/vite/makeConfigInjectorPlugin.d.ts +12 -0
  59. package/build/types/vite/makeConfigInjectorPlugin.d.ts.map +1 -0
  60. package/build/types/vite/makeCustomSentryVitePlugins.d.ts.map +1 -1
  61. package/build/types/vite/plugin.d.ts.map +1 -1
  62. package/package.json +4 -4
@@ -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.14.0"}
1
+ {"type":"module","version":"9.16.0"}
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { wrapSentryHandleRequest, getMetaTagTransformer } from './wrapSentryHandleRequest.js';
3
2
  import { PassThrough } from 'stream';
3
+ import { wrapSentryHandleRequest, getMetaTagTransformer } from './wrapSentryHandleRequest.js';
4
4
 
5
5
  /**
6
6
  * A complete Sentry-instrumented handleRequest implementation that handles both
@@ -1 +1 @@
1
- {"version":3,"file":"createSentryHandleRequest.js","sources":["../../../src/server/createSentryHandleRequest.tsx"],"sourcesContent":["import React from 'react';\nimport type { AppLoadContext, EntryContext, ServerRouter } from 'react-router';\nimport type { ReactNode } from 'react';\nimport { getMetaTagTransformer, wrapSentryHandleRequest } from './wrapSentryHandleRequest';\nimport type { createReadableStreamFromReadable } from '@react-router/node';\nimport { PassThrough } from 'stream';\n\ntype RenderToPipeableStreamOptions = {\n [key: string]: unknown;\n onShellReady?: () => void;\n onAllReady?: () => void;\n onShellError?: (error: unknown) => void;\n onError?: (error: unknown) => void;\n};\n\ntype RenderToPipeableStreamResult = {\n pipe: (destination: NodeJS.WritableStream) => void;\n abort: () => void;\n};\n\ntype RenderToPipeableStreamFunction = (\n node: ReactNode,\n options: RenderToPipeableStreamOptions,\n) => RenderToPipeableStreamResult;\n\nexport interface SentryHandleRequestOptions {\n /**\n * Timeout in milliseconds after which the rendering stream will be aborted\n * @default 10000\n */\n streamTimeout?: number;\n\n /**\n * React's renderToPipeableStream function from 'react-dom/server'\n */\n renderToPipeableStream: RenderToPipeableStreamFunction;\n\n /**\n * The <ServerRouter /> component from '@react-router/server'\n */\n ServerRouter: typeof ServerRouter;\n\n /**\n * createReadableStreamFromReadable from '@react-router/node'\n */\n createReadableStreamFromReadable: typeof createReadableStreamFromReadable;\n\n /**\n * Regular expression to identify bot user agents\n * @default /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i\n */\n botRegex?: RegExp;\n}\n\n/**\n * A complete Sentry-instrumented handleRequest implementation that handles both\n * route parametrization and trace meta tag injection.\n *\n * @param options Configuration options\n * @returns A Sentry-instrumented handleRequest function\n */\nexport function createSentryHandleRequest(\n options: SentryHandleRequestOptions,\n): (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n) => Promise<unknown> {\n const {\n streamTimeout = 10000,\n renderToPipeableStream,\n ServerRouter,\n createReadableStreamFromReadable,\n botRegex = /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i,\n } = options;\n\n const handleRequest = function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n _loadContext: AppLoadContext,\n ): Promise<Response> {\n return new Promise((resolve, reject) => {\n let shellRendered = false;\n const userAgent = request.headers.get('user-agent');\n\n // Determine if we should use onAllReady or onShellReady\n const isBot = typeof userAgent === 'string' && botRegex.test(userAgent);\n const isSpaMode = !!(routerContext as { isSpaMode?: boolean }).isSpaMode;\n\n const readyOption = isBot || isSpaMode ? 'onAllReady' : 'onShellReady';\n\n const { pipe, abort } = renderToPipeableStream(<ServerRouter context={routerContext} url={request.url} />, {\n [readyOption]() {\n shellRendered = true;\n const body = new PassThrough();\n\n const stream = createReadableStreamFromReadable(body);\n\n responseHeaders.set('Content-Type', 'text/html');\n\n resolve(\n new Response(stream, {\n headers: responseHeaders,\n status: responseStatusCode,\n }),\n );\n\n // this injects trace data to the HTML head\n pipe(getMetaTagTransformer(body));\n },\n onShellError(error: unknown) {\n reject(error);\n },\n onError(error: unknown) {\n // eslint-disable-next-line no-param-reassign\n responseStatusCode = 500;\n // Log streaming rendering errors from inside the shell. Don't log\n // errors encountered during initial shell rendering since they'll\n // reject and get logged in handleDocumentRequest.\n if (shellRendered) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n },\n });\n\n // Abort the rendering stream after the `streamTimeout`\n setTimeout(abort, streamTimeout);\n });\n };\n\n // Wrap the handle request function for request parametrization\n return wrapSentryHandleRequest(handleRequest);\n}\n"],"names":[],"mappings":";;;;AAsDA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,yBAAyB;AACzC,EAAE,OAAO;AACT;;AAMA,CAAsB;AACtB,EAAE,MAAM;AACR,IAAI,aAAA,GAAgB,KAAK;AACzB,IAAI,sBAAsB;AAC1B,IAAI,YAAY;AAChB,IAAI,gCAAgC;AACpC,IAAI,QAAA,GAAW,oFAAoF;AACnG,GAAE,GAAI,OAAO;;AAEb,EAAE,MAAM,aAAA,GAAgB,SAAS,aAAa;AAC9C,IAAI,OAAO;AACX,IAAI,kBAAkB;AACtB,IAAI,eAAe;AACnB,IAAI,aAAa;AACjB,IAAI,YAAY;AAChB,IAAuB;AACvB,IAAI,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK;AAC5C,MAAM,IAAI,aAAc,GAAE,KAAK;AAC/B,MAAM,MAAM,SAAU,GAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;;AAEzD;AACA,MAAM,MAAM,KAAA,GAAQ,OAAO,SAAU,KAAI,QAAS,IAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;AAC7E,MAAM,MAAM,YAAY,CAAC,CAAC,CAAC,aAAA,GAA0C,SAAS;;AAE9E,MAAM,MAAM,cAAc,KAAA,IAAS,SAAU,GAAE,YAAa,GAAE,cAAc;;AAE5E,MAAM,MAAM,EAAE,IAAI,EAAE,OAAQ,GAAE,sBAAsB,CAAC,KAAC,CAAA,aAAA,CAAA,YAAA,EAAA,EAAa,OAAO,EAAC,aAAc,EAAE,GAAG,EAAC,OAAQ,CAAC,GAAG,EAAE,EAAE,EAAE;AACjH,QAAQ,CAAC,WAAW,CAAC,GAAG;AACxB,UAAU,aAAA,GAAgB,IAAI;AAC9B,UAAU,MAAM,IAAK,GAAE,IAAI,WAAW,EAAE;;AAExC,UAAU,MAAM,MAAO,GAAE,gCAAgC,CAAC,IAAI,CAAC;;AAE/D,UAAU,eAAe,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC;;AAE1D,UAAU,OAAO;AACjB,YAAY,IAAI,QAAQ,CAAC,MAAM,EAAE;AACjC,cAAc,OAAO,EAAE,eAAe;AACtC,cAAc,MAAM,EAAE,kBAAkB;AACxC,aAAa,CAAC;AACd,WAAW;;AAEX;AACA,UAAU,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;AAC3C,SAAS;AACT,QAAQ,YAAY,CAAC,KAAK,EAAW;AACrC,UAAU,MAAM,CAAC,KAAK,CAAC;AACvB,SAAS;AACT,QAAQ,OAAO,CAAC,KAAK,EAAW;AAChC;AACA,UAAU,kBAAA,GAAqB,GAAG;AAClC;AACA;AACA;AACA,UAAU,IAAI,aAAa,EAAE;AAC7B;AACA,YAAY,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AAChC;AACA,SAAS;AACT,OAAO,CAAC;;AAER;AACA,MAAM,UAAU,CAAC,KAAK,EAAE,aAAa,CAAC;AACtC,KAAK,CAAC;AACN,GAAG;;AAEH;AACA,EAAE,OAAO,uBAAuB,CAAC,aAAa,CAAC;AAC/C;;;;"}
1
+ {"version":3,"file":"createSentryHandleRequest.js","sources":["../../../src/server/createSentryHandleRequest.tsx"],"sourcesContent":["import type { createReadableStreamFromReadable } from '@react-router/node';\nimport type { ReactNode } from 'react';\nimport React from 'react';\nimport type { AppLoadContext, EntryContext, ServerRouter } from 'react-router';\nimport { PassThrough } from 'stream';\nimport { getMetaTagTransformer, wrapSentryHandleRequest } from './wrapSentryHandleRequest';\n\ntype RenderToPipeableStreamOptions = {\n [key: string]: unknown;\n onShellReady?: () => void;\n onAllReady?: () => void;\n onShellError?: (error: unknown) => void;\n onError?: (error: unknown) => void;\n};\n\ntype RenderToPipeableStreamResult = {\n pipe: (destination: NodeJS.WritableStream) => void;\n abort: () => void;\n};\n\ntype RenderToPipeableStreamFunction = (\n node: ReactNode,\n options: RenderToPipeableStreamOptions,\n) => RenderToPipeableStreamResult;\n\nexport interface SentryHandleRequestOptions {\n /**\n * Timeout in milliseconds after which the rendering stream will be aborted\n * @default 10000\n */\n streamTimeout?: number;\n\n /**\n * React's renderToPipeableStream function from 'react-dom/server'\n */\n renderToPipeableStream: RenderToPipeableStreamFunction;\n\n /**\n * The <ServerRouter /> component from '@react-router/server'\n */\n ServerRouter: typeof ServerRouter;\n\n /**\n * createReadableStreamFromReadable from '@react-router/node'\n */\n createReadableStreamFromReadable: typeof createReadableStreamFromReadable;\n\n /**\n * Regular expression to identify bot user agents\n * @default /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i\n */\n botRegex?: RegExp;\n}\n\n/**\n * A complete Sentry-instrumented handleRequest implementation that handles both\n * route parametrization and trace meta tag injection.\n *\n * @param options Configuration options\n * @returns A Sentry-instrumented handleRequest function\n */\nexport function createSentryHandleRequest(\n options: SentryHandleRequestOptions,\n): (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n) => Promise<unknown> {\n const {\n streamTimeout = 10000,\n renderToPipeableStream,\n ServerRouter,\n createReadableStreamFromReadable,\n botRegex = /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i,\n } = options;\n\n const handleRequest = function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n _loadContext: AppLoadContext,\n ): Promise<Response> {\n return new Promise((resolve, reject) => {\n let shellRendered = false;\n const userAgent = request.headers.get('user-agent');\n\n // Determine if we should use onAllReady or onShellReady\n const isBot = typeof userAgent === 'string' && botRegex.test(userAgent);\n const isSpaMode = !!(routerContext as { isSpaMode?: boolean }).isSpaMode;\n\n const readyOption = isBot || isSpaMode ? 'onAllReady' : 'onShellReady';\n\n const { pipe, abort } = renderToPipeableStream(<ServerRouter context={routerContext} url={request.url} />, {\n [readyOption]() {\n shellRendered = true;\n const body = new PassThrough();\n\n const stream = createReadableStreamFromReadable(body);\n\n responseHeaders.set('Content-Type', 'text/html');\n\n resolve(\n new Response(stream, {\n headers: responseHeaders,\n status: responseStatusCode,\n }),\n );\n\n // this injects trace data to the HTML head\n pipe(getMetaTagTransformer(body));\n },\n onShellError(error: unknown) {\n reject(error);\n },\n onError(error: unknown) {\n // eslint-disable-next-line no-param-reassign\n responseStatusCode = 500;\n // Log streaming rendering errors from inside the shell. Don't log\n // errors encountered during initial shell rendering since they'll\n // reject and get logged in handleDocumentRequest.\n if (shellRendered) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n },\n });\n\n // Abort the rendering stream after the `streamTimeout`\n setTimeout(abort, streamTimeout);\n });\n };\n\n // Wrap the handle request function for request parametrization\n return wrapSentryHandleRequest(handleRequest);\n}\n"],"names":[],"mappings":";;;;AAsDA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,yBAAyB;AACzC,EAAE,OAAO;AACT;;AAMA,CAAsB;AACtB,EAAE,MAAM;AACR,IAAI,aAAA,GAAgB,KAAK;AACzB,IAAI,sBAAsB;AAC1B,IAAI,YAAY;AAChB,IAAI,gCAAgC;AACpC,IAAI,QAAA,GAAW,oFAAoF;AACnG,GAAE,GAAI,OAAO;;AAEb,EAAE,MAAM,aAAA,GAAgB,SAAS,aAAa;AAC9C,IAAI,OAAO;AACX,IAAI,kBAAkB;AACtB,IAAI,eAAe;AACnB,IAAI,aAAa;AACjB,IAAI,YAAY;AAChB,IAAuB;AACvB,IAAI,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK;AAC5C,MAAM,IAAI,aAAc,GAAE,KAAK;AAC/B,MAAM,MAAM,SAAU,GAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;;AAEzD;AACA,MAAM,MAAM,KAAA,GAAQ,OAAO,SAAU,KAAI,QAAS,IAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;AAC7E,MAAM,MAAM,YAAY,CAAC,CAAC,CAAC,aAAA,GAA0C,SAAS;;AAE9E,MAAM,MAAM,cAAc,KAAA,IAAS,SAAU,GAAE,YAAa,GAAE,cAAc;;AAE5E,MAAM,MAAM,EAAE,IAAI,EAAE,OAAQ,GAAE,sBAAsB,CAAC,KAAC,CAAA,aAAA,CAAA,YAAA,EAAA,EAAa,OAAO,EAAC,aAAc,EAAE,GAAG,EAAC,OAAQ,CAAC,GAAG,EAAE,EAAE,EAAE;AACjH,QAAQ,CAAC,WAAW,CAAC,GAAG;AACxB,UAAU,aAAA,GAAgB,IAAI;AAC9B,UAAU,MAAM,IAAK,GAAE,IAAI,WAAW,EAAE;;AAExC,UAAU,MAAM,MAAO,GAAE,gCAAgC,CAAC,IAAI,CAAC;;AAE/D,UAAU,eAAe,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC;;AAE1D,UAAU,OAAO;AACjB,YAAY,IAAI,QAAQ,CAAC,MAAM,EAAE;AACjC,cAAc,OAAO,EAAE,eAAe;AACtC,cAAc,MAAM,EAAE,kBAAkB;AACxC,aAAa,CAAC;AACd,WAAW;;AAEX;AACA,UAAU,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;AAC3C,SAAS;AACT,QAAQ,YAAY,CAAC,KAAK,EAAW;AACrC,UAAU,MAAM,CAAC,KAAK,CAAC;AACvB,SAAS;AACT,QAAQ,OAAO,CAAC,KAAK,EAAW;AAChC;AACA,UAAU,kBAAA,GAAqB,GAAG;AAClC;AACA;AACA;AACA,UAAU,IAAI,aAAa,EAAE;AAC7B;AACA,YAAY,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AAChC;AACA,SAAS;AACT,OAAO,CAAC;;AAER;AACA,MAAM,UAAU,CAAC,KAAK,EAAE,aAAa,CAAC;AACtC,KAAK,CAAC;AACN,GAAG;;AAEH;AACA,EAAE,OAAO,uBAAuB,CAAC,aAAa,CAAC;AAC/C;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrapSentryHandleRequest.js","sources":["../../../src/server/wrapSentryHandleRequest.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { RPCType, getRPCMetadata } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getActiveSpan, getRootSpan, getTraceMetaTags } from '@sentry/core';\nimport type { AppLoadContext, EntryContext } from 'react-router';\nimport type { PassThrough } from 'stream';\nimport { Transform } from 'stream';\n\ntype OriginalHandleRequest = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n) => Promise<unknown>;\n\n/**\n * Wraps the original handleRequest function to add Sentry instrumentation.\n *\n * @param originalHandle - The original handleRequest function to wrap\n * @returns A wrapped version of the handle request function with Sentry instrumentation\n */\nexport function wrapSentryHandleRequest(originalHandle: OriginalHandleRequest): OriginalHandleRequest {\n return async function sentryInstrumentedHandleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n ) {\n const parameterizedPath =\n routerContext?.staticHandlerContext?.matches?.[routerContext.staticHandlerContext.matches.length - 1]?.route.path;\n if (parameterizedPath) {\n const activeSpan = getActiveSpan();\n if (activeSpan) {\n const rootSpan = getRootSpan(activeSpan);\n const routeName = `/${parameterizedPath}`;\n\n // The express instrumentation writes on the rpcMetadata and that ends up stomping on the `http.route` attribute.\n const rpcMetadata = getRPCMetadata(context.active());\n if (rpcMetadata?.type === RPCType.HTTP) {\n rpcMetadata.route = routeName;\n }\n\n // The span exporter picks up the `http.route` (ATTR_HTTP_ROUTE) attribute to set the transaction name\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n });\n }\n }\n\n return originalHandle(request, responseStatusCode, responseHeaders, routerContext, loadContext);\n };\n}\n\n/** @deprecated Use `wrapSentryHandleRequest` instead. */\nexport const sentryHandleRequest = wrapSentryHandleRequest;\n\n/**\n * Injects Sentry trace meta tags into the HTML response by piping through a transform stream.\n * This enables distributed tracing by adding trace context to the HTML document head.\n *\n * @param body - PassThrough stream containing the HTML response body to modify\n */\nexport function getMetaTagTransformer(body: PassThrough): Transform {\n const headClosingTag = '</head>';\n const htmlMetaTagTransformer = new Transform({\n transform(chunk, _encoding, callback) {\n const html = Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);\n if (html.includes(headClosingTag)) {\n const modifiedHtml = html.replace(headClosingTag, `${getTraceMetaTags()}${headClosingTag}`);\n callback(null, modifiedHtml);\n return;\n }\n callback(null, chunk);\n },\n });\n htmlMetaTagTransformer.pipe(body);\n return htmlMetaTagTransformer;\n}\n"],"names":[],"mappings":";;;;;;AAgBA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,uBAAuB,CAAC,cAAc,EAAgD;AACtG,EAAE,OAAO,eAAe,+BAA+B;AACvD,IAAI,OAAO;AACX,IAAI,kBAAkB;AACtB,IAAI,eAAe;AACnB,IAAI,aAAa;AACjB,IAAI,WAAW;AACf,IAAI;AACJ,IAAI,MAAM,iBAAkB;AAC5B,MAAM,aAAa,EAAE,oBAAoB,EAAE,OAAO,GAAG,aAAa,CAAC,oBAAoB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI;AACvH,IAAI,IAAI,iBAAiB,EAAE;AAC3B,MAAM,MAAM,UAAA,GAAa,aAAa,EAAE;AACxC,MAAM,IAAI,UAAU,EAAE;AACtB,QAAQ,MAAM,QAAS,GAAE,WAAW,CAAC,UAAU,CAAC;AAChD,QAAQ,MAAM,YAAY,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAA;;AAEA;AACA,QAAA,MAAA,WAAA,GAAA,cAAA,CAAA,OAAA,CAAA,MAAA,EAAA,CAAA;AACA,QAAA,IAAA,WAAA,EAAA,IAAA,KAAA,OAAA,CAAA,IAAA,EAAA;AACA,UAAA,WAAA,CAAA,KAAA,GAAA,SAAA;AACA;;AAEA;AACA,QAAA,QAAA,CAAA,aAAA,CAAA;AACA,UAAA,CAAA,eAAA,GAAA,SAAA;AACA,UAAA,CAAA,gCAAA,GAAA,OAAA;AACA,SAAA,CAAA;AACA;AACA;;AAEA,IAAA,OAAA,cAAA,CAAA,OAAA,EAAA,kBAAA,EAAA,eAAA,EAAA,aAAA,EAAA,WAAA,CAAA;AACA,GAAA;AACA;;AAEA;AACA,MAAA,mBAAA,GAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAA,qBAAA,CAAA,IAAA,EAAA;AACA,EAAA,MAAA,cAAA,GAAA,SAAA;AACA,EAAA,MAAA,sBAAA,GAAA,IAAA,SAAA,CAAA;AACA,IAAA,SAAA,CAAA,KAAA,EAAA,SAAA,EAAA,QAAA,EAAA;AACA,MAAA,MAAA,IAAA,GAAA,MAAA,CAAA,QAAA,CAAA,KAAA,CAAA,GAAA,KAAA,CAAA,QAAA,EAAA,GAAA,MAAA,CAAA,KAAA,CAAA;AACA,MAAA,IAAA,IAAA,CAAA,QAAA,CAAA,cAAA,CAAA,EAAA;AACA,QAAA,MAAA,YAAA,GAAA,IAAA,CAAA,OAAA,CAAA,cAAA,EAAA,CAAA,EAAA,gBAAA,EAAA,CAAA,EAAA,cAAA,CAAA,CAAA,CAAA;AACA,QAAA,QAAA,CAAA,IAAA,EAAA,YAAA,CAAA;AACA,QAAA;AACA;AACA,MAAA,QAAA,CAAA,IAAA,EAAA,KAAA,CAAA;AACA,KAAA;AACA,GAAA,CAAA;AACA,EAAA,sBAAA,CAAA,IAAA,CAAA,IAAA,CAAA;AACA,EAAA,OAAA,sBAAA;AACA;;;;"}
1
+ {"version":3,"file":"wrapSentryHandleRequest.js","sources":["../../../src/server/wrapSentryHandleRequest.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { getRPCMetadata, RPCType } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport { getActiveSpan, getRootSpan, getTraceMetaTags, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';\nimport type { AppLoadContext, EntryContext } from 'react-router';\nimport type { PassThrough } from 'stream';\nimport { Transform } from 'stream';\n\ntype OriginalHandleRequest = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n) => Promise<unknown>;\n\n/**\n * Wraps the original handleRequest function to add Sentry instrumentation.\n *\n * @param originalHandle - The original handleRequest function to wrap\n * @returns A wrapped version of the handle request function with Sentry instrumentation\n */\nexport function wrapSentryHandleRequest(originalHandle: OriginalHandleRequest): OriginalHandleRequest {\n return async function sentryInstrumentedHandleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n ) {\n const parameterizedPath =\n routerContext?.staticHandlerContext?.matches?.[routerContext.staticHandlerContext.matches.length - 1]?.route.path;\n if (parameterizedPath) {\n const activeSpan = getActiveSpan();\n if (activeSpan) {\n const rootSpan = getRootSpan(activeSpan);\n const routeName = `/${parameterizedPath}`;\n\n // The express instrumentation writes on the rpcMetadata and that ends up stomping on the `http.route` attribute.\n const rpcMetadata = getRPCMetadata(context.active());\n if (rpcMetadata?.type === RPCType.HTTP) {\n rpcMetadata.route = routeName;\n }\n\n // The span exporter picks up the `http.route` (ATTR_HTTP_ROUTE) attribute to set the transaction name\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n });\n }\n }\n\n return originalHandle(request, responseStatusCode, responseHeaders, routerContext, loadContext);\n };\n}\n\n/** @deprecated Use `wrapSentryHandleRequest` instead. */\nexport const sentryHandleRequest = wrapSentryHandleRequest;\n\n/**\n * Injects Sentry trace meta tags into the HTML response by piping through a transform stream.\n * This enables distributed tracing by adding trace context to the HTML document head.\n *\n * @param body - PassThrough stream containing the HTML response body to modify\n */\nexport function getMetaTagTransformer(body: PassThrough): Transform {\n const headClosingTag = '</head>';\n const htmlMetaTagTransformer = new Transform({\n transform(chunk, _encoding, callback) {\n const html = Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);\n if (html.includes(headClosingTag)) {\n const modifiedHtml = html.replace(headClosingTag, `${getTraceMetaTags()}${headClosingTag}`);\n callback(null, modifiedHtml);\n return;\n }\n callback(null, chunk);\n },\n });\n htmlMetaTagTransformer.pipe(body);\n return htmlMetaTagTransformer;\n}\n"],"names":[],"mappings":";;;;;;AAgBA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,uBAAuB,CAAC,cAAc,EAAgD;AACtG,EAAE,OAAO,eAAe,+BAA+B;AACvD,IAAI,OAAO;AACX,IAAI,kBAAkB;AACtB,IAAI,eAAe;AACnB,IAAI,aAAa;AACjB,IAAI,WAAW;AACf,IAAI;AACJ,IAAI,MAAM,iBAAkB;AAC5B,MAAM,aAAa,EAAE,oBAAoB,EAAE,OAAO,GAAG,aAAa,CAAC,oBAAoB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI;AACvH,IAAI,IAAI,iBAAiB,EAAE;AAC3B,MAAM,MAAM,UAAA,GAAa,aAAa,EAAE;AACxC,MAAM,IAAI,UAAU,EAAE;AACtB,QAAQ,MAAM,QAAS,GAAE,WAAW,CAAC,UAAU,CAAC;AAChD,QAAQ,MAAM,YAAY,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAA;;AAEA;AACA,QAAA,MAAA,WAAA,GAAA,cAAA,CAAA,OAAA,CAAA,MAAA,EAAA,CAAA;AACA,QAAA,IAAA,WAAA,EAAA,IAAA,KAAA,OAAA,CAAA,IAAA,EAAA;AACA,UAAA,WAAA,CAAA,KAAA,GAAA,SAAA;AACA;;AAEA;AACA,QAAA,QAAA,CAAA,aAAA,CAAA;AACA,UAAA,CAAA,eAAA,GAAA,SAAA;AACA,UAAA,CAAA,gCAAA,GAAA,OAAA;AACA,SAAA,CAAA;AACA;AACA;;AAEA,IAAA,OAAA,cAAA,CAAA,OAAA,EAAA,kBAAA,EAAA,eAAA,EAAA,aAAA,EAAA,WAAA,CAAA;AACA,GAAA;AACA;;AAEA;AACA,MAAA,mBAAA,GAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAA,qBAAA,CAAA,IAAA,EAAA;AACA,EAAA,MAAA,cAAA,GAAA,SAAA;AACA,EAAA,MAAA,sBAAA,GAAA,IAAA,SAAA,CAAA;AACA,IAAA,SAAA,CAAA,KAAA,EAAA,SAAA,EAAA,QAAA,EAAA;AACA,MAAA,MAAA,IAAA,GAAA,MAAA,CAAA,QAAA,CAAA,KAAA,CAAA,GAAA,KAAA,CAAA,QAAA,EAAA,GAAA,MAAA,CAAA,KAAA,CAAA;AACA,MAAA,IAAA,IAAA,CAAA,QAAA,CAAA,cAAA,CAAA,EAAA;AACA,QAAA,MAAA,YAAA,GAAA,IAAA,CAAA,OAAA,CAAA,cAAA,EAAA,CAAA,EAAA,gBAAA,EAAA,CAAA,EAAA,cAAA,CAAA,CAAA,CAAA;AACA,QAAA,QAAA,CAAA,IAAA,EAAA,YAAA,CAAA;AACA,QAAA;AACA;AACA,MAAA,QAAA,CAAA,IAAA,EAAA,KAAA,CAAA;AACA,KAAA;AACA,GAAA,CAAA;AACA,EAAA,sBAAA,CAAA,IAAA,CAAA,IAAA,CAAA;AACA,EAAA,OAAA,sBAAA;AACA;;;;"}
@@ -17,6 +17,8 @@ function getSentryConfig(viteConfig) {
17
17
  * and optionally deletes the source map files after upload.
18
18
  */
19
19
  const sentryOnBuildEnd = async ({ reactRouterConfig, viteConfig }) => {
20
+ const sentryConfig = getSentryConfig(viteConfig);
21
+
20
22
  const {
21
23
  authToken,
22
24
  org,
@@ -25,7 +27,14 @@ const sentryOnBuildEnd = async ({ reactRouterConfig, viteConfig }) => {
25
27
  sourceMapsUploadOptions = { enabled: true },
26
28
  debug = false,
27
29
  unstable_sentryVitePluginOptions,
28
- } = getSentryConfig(viteConfig);
30
+ } = {
31
+ ...sentryConfig.unstable_sentryVitePluginOptions,
32
+ ...sentryConfig,
33
+ release: {
34
+ ...sentryConfig.unstable_sentryVitePluginOptions?.release,
35
+ ...sentryConfig.release,
36
+ },
37
+ };
29
38
 
30
39
  const cliInstance = new SentryCli(null, {
31
40
  authToken,
@@ -1 +1 @@
1
- {"version":3,"file":"handleOnBuildEnd.js","sources":["../../../../src/vite/buildEnd/handleOnBuildEnd.ts"],"sourcesContent":["import { rm } from 'node:fs/promises';\nimport type { Config } from '@react-router/dev/config';\nimport SentryCli from '@sentry/cli';\nimport { glob } from 'glob';\nimport type { SentryReactRouterBuildOptions } from '../types';\n\ntype BuildEndHook = NonNullable<Config['buildEnd']>;\n\nfunction getSentryConfig(viteConfig: unknown): SentryReactRouterBuildOptions {\n if (!viteConfig || typeof viteConfig !== 'object' || !('sentryConfig' in viteConfig)) {\n // eslint-disable-next-line no-console\n console.error('[Sentry] sentryConfig not found - it needs to be passed to vite.config.ts');\n }\n\n return (viteConfig as { sentryConfig: SentryReactRouterBuildOptions }).sentryConfig;\n}\n\n/**\n * A build end hook that handles Sentry release creation and source map uploads.\n * It creates a new Sentry release if configured, uploads source maps to Sentry,\n * and optionally deletes the source map files after upload.\n */\nexport const sentryOnBuildEnd: BuildEndHook = async ({ reactRouterConfig, viteConfig }) => {\n const {\n authToken,\n org,\n project,\n release,\n sourceMapsUploadOptions = { enabled: true },\n debug = false,\n unstable_sentryVitePluginOptions,\n } = getSentryConfig(viteConfig);\n\n const cliInstance = new SentryCli(null, {\n authToken,\n org,\n project,\n ...unstable_sentryVitePluginOptions,\n });\n // check if release should be created\n if (release?.name) {\n try {\n await cliInstance.releases.new(release.name);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[Sentry] Could not create release', error);\n }\n }\n\n if (sourceMapsUploadOptions?.enabled ?? (true && viteConfig.build.sourcemap !== false)) {\n // inject debugIds\n try {\n await cliInstance.execute(['sourcemaps', 'inject', reactRouterConfig.buildDirectory], debug);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[Sentry] Could not inject debug ids', error);\n }\n\n // upload sourcemaps\n try {\n await cliInstance.releases.uploadSourceMaps(release?.name || 'undefined', {\n include: [\n {\n paths: [reactRouterConfig.buildDirectory],\n },\n ],\n });\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[Sentry] Could not upload sourcemaps', error);\n }\n }\n // delete sourcemaps after upload\n let updatedFilesToDeleteAfterUpload = sourceMapsUploadOptions?.filesToDeleteAfterUpload;\n // set a default value no option was set\n if (typeof sourceMapsUploadOptions?.filesToDeleteAfterUpload === 'undefined') {\n updatedFilesToDeleteAfterUpload = [`${reactRouterConfig.buildDirectory}/**/*.map`];\n if (debug) {\n // eslint-disable-next-line no-console\n console.info(\n `[Sentry] Automatically setting \\`sourceMapsUploadOptions.filesToDeleteAfterUpload: ${JSON.stringify(\n updatedFilesToDeleteAfterUpload,\n )}\\` to delete generated source maps after they were uploaded to Sentry.`,\n );\n }\n }\n if (updatedFilesToDeleteAfterUpload) {\n try {\n const filePathsToDelete = await glob(updatedFilesToDeleteAfterUpload, {\n absolute: true,\n nodir: true,\n });\n if (debug) {\n filePathsToDelete.forEach(filePathToDelete => {\n // eslint-disable-next-line no-console\n console.info(`Deleting asset after upload: ${filePathToDelete}`);\n });\n }\n await Promise.all(\n filePathsToDelete.map(filePathToDelete =>\n rm(filePathToDelete, { force: true }).catch((e: unknown) => {\n if (debug) {\n // This is allowed to fail - we just don't do anything\n // eslint-disable-next-line no-console\n console.debug(`An error occurred while attempting to delete asset: ${filePathToDelete}`, e);\n }\n }),\n ),\n );\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Error deleting files after sourcemap upload:', error);\n }\n }\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handleOnBuildEnd.js","sources":["../../../../src/vite/buildEnd/handleOnBuildEnd.ts"],"sourcesContent":["import { rm } from 'node:fs/promises';\nimport type { Config } from '@react-router/dev/config';\nimport SentryCli from '@sentry/cli';\nimport { glob } from 'glob';\nimport type { SentryReactRouterBuildOptions } from '../types';\n\ntype BuildEndHook = NonNullable<Config['buildEnd']>;\n\nfunction getSentryConfig(viteConfig: unknown): SentryReactRouterBuildOptions {\n if (!viteConfig || typeof viteConfig !== 'object' || !('sentryConfig' in viteConfig)) {\n // eslint-disable-next-line no-console\n console.error('[Sentry] sentryConfig not found - it needs to be passed to vite.config.ts');\n }\n\n return (viteConfig as { sentryConfig: SentryReactRouterBuildOptions }).sentryConfig;\n}\n\n/**\n * A build end hook that handles Sentry release creation and source map uploads.\n * It creates a new Sentry release if configured, uploads source maps to Sentry,\n * and optionally deletes the source map files after upload.\n */\nexport const sentryOnBuildEnd: BuildEndHook = async ({ reactRouterConfig, viteConfig }) => {\n const sentryConfig = getSentryConfig(viteConfig);\n\n const {\n authToken,\n org,\n project,\n release,\n sourceMapsUploadOptions = { enabled: true },\n debug = false,\n unstable_sentryVitePluginOptions,\n }: SentryReactRouterBuildOptions = {\n ...sentryConfig.unstable_sentryVitePluginOptions,\n ...sentryConfig,\n release: {\n ...sentryConfig.unstable_sentryVitePluginOptions?.release,\n ...sentryConfig.release,\n },\n };\n\n const cliInstance = new SentryCli(null, {\n authToken,\n org,\n project,\n ...unstable_sentryVitePluginOptions,\n });\n // check if release should be created\n if (release?.name) {\n try {\n await cliInstance.releases.new(release.name);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[Sentry] Could not create release', error);\n }\n }\n\n if (sourceMapsUploadOptions?.enabled ?? (true && viteConfig.build.sourcemap !== false)) {\n // inject debugIds\n try {\n await cliInstance.execute(['sourcemaps', 'inject', reactRouterConfig.buildDirectory], debug);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[Sentry] Could not inject debug ids', error);\n }\n\n // upload sourcemaps\n try {\n await cliInstance.releases.uploadSourceMaps(release?.name || 'undefined', {\n include: [\n {\n paths: [reactRouterConfig.buildDirectory],\n },\n ],\n });\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[Sentry] Could not upload sourcemaps', error);\n }\n }\n // delete sourcemaps after upload\n let updatedFilesToDeleteAfterUpload = sourceMapsUploadOptions?.filesToDeleteAfterUpload;\n // set a default value no option was set\n if (typeof sourceMapsUploadOptions?.filesToDeleteAfterUpload === 'undefined') {\n updatedFilesToDeleteAfterUpload = [`${reactRouterConfig.buildDirectory}/**/*.map`];\n if (debug) {\n // eslint-disable-next-line no-console\n console.info(\n `[Sentry] Automatically setting \\`sourceMapsUploadOptions.filesToDeleteAfterUpload: ${JSON.stringify(\n updatedFilesToDeleteAfterUpload,\n )}\\` to delete generated source maps after they were uploaded to Sentry.`,\n );\n }\n }\n if (updatedFilesToDeleteAfterUpload) {\n try {\n const filePathsToDelete = await glob(updatedFilesToDeleteAfterUpload, {\n absolute: true,\n nodir: true,\n });\n if (debug) {\n filePathsToDelete.forEach(filePathToDelete => {\n // eslint-disable-next-line no-console\n console.info(`Deleting asset after upload: ${filePathToDelete}`);\n });\n }\n await Promise.all(\n filePathsToDelete.map(filePathToDelete =>\n rm(filePathToDelete, { force: true }).catch((e: unknown) => {\n if (debug) {\n // This is allowed to fail - we just don't do anything\n // eslint-disable-next-line no-console\n console.debug(`An error occurred while attempting to delete asset: ${filePathToDelete}`, e);\n }\n }),\n ),\n );\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Error deleting files after sourcemap upload:', error);\n }\n }\n};\n"],"names":[],"mappings":"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"}
@@ -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;;;;"}
@@ -4,8 +4,16 @@ import { sentryVitePlugin } from '@sentry/vite-plugin';
4
4
  * Create a custom subset of sentry's vite plugins
5
5
  */
6
6
  async function makeCustomSentryVitePlugins(options) {
7
- const { debug, unstable_sentryVitePluginOptions, bundleSizeOptimizations, authToken, org, project, telemetry } =
8
- options;
7
+ const {
8
+ debug,
9
+ unstable_sentryVitePluginOptions,
10
+ bundleSizeOptimizations,
11
+ authToken,
12
+ org,
13
+ project,
14
+ telemetry,
15
+ release,
16
+ } = options;
9
17
 
10
18
  const sentryVitePlugins = sentryVitePlugin({
11
19
  authToken: authToken ?? process.env.SENTRY_AUTH_TOKEN,
@@ -18,10 +26,16 @@ async function makeCustomSentryVitePlugins(options) {
18
26
  telemetry: {
19
27
  metaFramework: 'react-router',
20
28
  },
29
+ ...unstable_sentryVitePluginOptions?._metaOptions,
30
+ },
31
+ release: {
32
+ ...unstable_sentryVitePluginOptions?.release,
33
+ ...release,
21
34
  },
22
35
  // will be handled in buildEnd hook
23
36
  sourcemaps: {
24
37
  disable: true,
38
+ ...unstable_sentryVitePluginOptions?.sourcemaps,
25
39
  },
26
40
  ...unstable_sentryVitePluginOptions,
27
41
  }) ;
@@ -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 { debug, unstable_sentryVitePluginOptions, bundleSizeOptimizations, authToken, org, project, telemetry } =\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 },\n // will be handled in buildEnd hook\n sourcemaps: {\n disable: true,\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 ['sentry-telemetry-plugin', 'sentry-vite-release-injection-plugin'].includes(plugin.name);\n }),\n ];\n}\n"],"names":[],"mappings":";;AAIA;AACA;AACA;AACO,eAAe,2BAA2B,CAAC,OAAO,EAAoD;AAC7G,EAAE,MAAM,EAAE,KAAK,EAAE,gCAAgC,EAAE,uBAAuB,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,WAAY;AACjH,IAAI,OAAO;;AAEX,EAAE,MAAM,iBAAA,GAAoB,gBAAgB,CAAC;AAC7C,IAAI,SAAS,EAAE,SAAU,IAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB;AACzD,IAAI,uBAAuB;AAC3B,IAAI,KAAK,EAAE,KAAM,IAAG,KAAK;AACzB,IAAI,GAAG,EAAE,GAAI,IAAG,OAAO,CAAC,GAAG,CAAC,UAAU;AACtC,IAAI,OAAO,EAAE,OAAQ,IAAG,OAAO,CAAC,GAAG,CAAC,cAAc;AAClD,IAAI,SAAS,EAAE,SAAU,IAAG,IAAI;AAChC,IAAI,YAAY,EAAE;AAClB,MAAM,SAAS,EAAE;AACjB,QAAQ,aAAa,EAAE,cAAc;AACrC,OAAO;AACP,KAAK;AACL;AACA,IAAI,UAAU,EAAE;AAChB,MAAM,OAAO,EAAE,IAAI;AACnB,KAAK;AACL,IAAI,GAAG,gCAAgC;AACvC,GAAG,CAAE;;AAEL;AACA,EAAE,OAAO;AACT,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,UAAU;AAC1C,MAAM,OAAO,CAAC,yBAAyB,EAAE,sCAAsC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;AACtG,KAAK,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 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 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 ['sentry-telemetry-plugin', 'sentry-vite-release-injection-plugin'].includes(plugin.name);\n }),\n ];\n}\n"],"names":[],"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,OAAO;AACX,GAAE,GAAI,OAAO;;AAEb,EAAE,MAAM,iBAAA,GAAoB,gBAAgB,CAAC;AAC7C,IAAI,SAAS,EAAE,SAAU,IAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB;AACzD,IAAI,uBAAuB;AAC3B,IAAI,KAAK,EAAE,KAAM,IAAG,KAAK;AACzB,IAAI,GAAG,EAAE,GAAI,IAAG,OAAO,CAAC,GAAG,CAAC,UAAU;AACtC,IAAI,OAAO,EAAE,OAAQ,IAAG,OAAO,CAAC,GAAG,CAAC,cAAc;AAClD,IAAI,SAAS,EAAE,SAAU,IAAG,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,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,CAAE;;AAEL;AACA,EAAE,OAAO;AACT,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,UAAU;AAC1C,MAAM,OAAO,CAAC,yBAAyB,EAAE,sCAAsC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;AACtG,KAAK,CAAC;AACN,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,9 +1,9 @@
1
- export * from './client';
2
- export * from './server';
3
- export * from './vite';
4
1
  import type { Integration, Options, StackParser } from '@sentry/core';
5
2
  import type * as clientSdk from './client';
6
3
  import type * as serverSdk from './server';
4
+ export * from './client';
5
+ export * from './server';
6
+ export * from './vite';
7
7
  /** Initializes Sentry React Router SDK */
8
8
  export declare function init(options: Options | clientSdk.BrowserOptions | serverSdk.NodeOptions): void;
9
9
  export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration;
@@ -1 +1 @@
1
- {"version":3,"file":"index.types.d.ts","sourceRoot":"","sources":["../../src/index.types.ts"],"names":[],"mappings":"AAEA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AAEvB,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,KAAK,SAAS,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,KAAK,SAAS,MAAM,UAAU,CAAC;AAE3C,0CAA0C;AAC1C,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,cAAc,GAAG,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;AAExG,MAAM,CAAC,OAAO,CAAC,MAAM,uBAAuB,EAAE,OAAO,SAAS,CAAC,uBAAuB,CAAC;AACvF,MAAM,CAAC,OAAO,CAAC,MAAM,uBAAuB,EAAE,OAAO,SAAS,CAAC,uBAAuB,CAAC;AACvF,MAAM,CAAC,OAAO,CAAC,MAAM,kBAAkB,EAAE,WAAW,CAAC;AACrD,MAAM,CAAC,OAAO,CAAC,MAAM,sBAAsB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,WAAW,EAAE,CAAC;AAEjF,MAAM,CAAC,OAAO,CAAC,MAAM,MAAM,EAAE,OAAO,SAAS,CAAC,MAAM,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC"}
1
+ {"version":3,"file":"index.types.d.ts","sourceRoot":"","sources":["../../src/index.types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,KAAK,SAAS,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,KAAK,SAAS,MAAM,UAAU,CAAC;AAG3C,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AAEvB,0CAA0C;AAC1C,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,cAAc,GAAG,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;AAExG,MAAM,CAAC,OAAO,CAAC,MAAM,uBAAuB,EAAE,OAAO,SAAS,CAAC,uBAAuB,CAAC;AACvF,MAAM,CAAC,OAAO,CAAC,MAAM,uBAAuB,EAAE,OAAO,SAAS,CAAC,uBAAuB,CAAC;AACvF,MAAM,CAAC,OAAO,CAAC,MAAM,kBAAkB,EAAE,WAAW,CAAC;AACrD,MAAM,CAAC,OAAO,CAAC,MAAM,sBAAsB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,WAAW,EAAE,CAAC;AAEjF,MAAM,CAAC,OAAO,CAAC,MAAM,MAAM,EAAE,OAAO,SAAS,CAAC,MAAM,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC"}
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
- import type { AppLoadContext, EntryContext, ServerRouter } from 'react-router';
3
- import type { ReactNode } from 'react';
4
2
  import type { createReadableStreamFromReadable } from '@react-router/node';
3
+ import type { ReactNode } from 'react';
4
+ import type { AppLoadContext, EntryContext, ServerRouter } from 'react-router';
5
5
  type RenderToPipeableStreamOptions = {
6
6
  [key: string]: unknown;
7
7
  onShellReady?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"createSentryHandleRequest.d.ts","sourceRoot":"","sources":["../../../src/server/createSentryHandleRequest.tsx"],"names":[],"mappings":";AACA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,oBAAoB,CAAC;AAG3E,KAAK,6BAA6B,GAAG;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAClC,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,cAAc,KAAK,IAAI,CAAC;IACnD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,KAAK,8BAA8B,GAAG,CACpC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,6BAA6B,KACnC,4BAA4B,CAAC;AAElC,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,sBAAsB,EAAE,8BAA8B,CAAC;IAEvD;;OAEG;IACH,YAAY,EAAE,OAAO,YAAY,CAAC;IAElC;;OAEG;IACH,gCAAgC,EAAE,OAAO,gCAAgC,CAAC;IAE1E;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,CACD,OAAO,EAAE,OAAO,EAChB,kBAAkB,EAAE,MAAM,EAC1B,eAAe,EAAE,OAAO,EACxB,aAAa,EAAE,YAAY,EAC3B,WAAW,EAAE,cAAc,KACxB,OAAO,CAAC,OAAO,CAAC,CAoEpB"}
1
+ {"version":3,"file":"createSentryHandleRequest.d.ts","sourceRoot":"","sources":["../../../src/server/createSentryHandleRequest.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAI/E,KAAK,6BAA6B,GAAG;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAClC,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,cAAc,KAAK,IAAI,CAAC;IACnD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,KAAK,8BAA8B,GAAG,CACpC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,6BAA6B,KACnC,4BAA4B,CAAC;AAElC,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,sBAAsB,EAAE,8BAA8B,CAAC;IAEvD;;OAEG;IACH,YAAY,EAAE,OAAO,YAAY,CAAC;IAElC;;OAEG;IACH,gCAAgC,EAAE,OAAO,gCAAgC,CAAC;IAE1E;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,CACD,OAAO,EAAE,OAAO,EAChB,kBAAkB,EAAE,MAAM,EAC1B,eAAe,EAAE,OAAO,EACxB,aAAa,EAAE,YAAY,EAC3B,WAAW,EAAE,cAAc,KACxB,OAAO,CAAC,OAAO,CAAC,CAoEpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"handleOnBuildEnd.d.ts","sourceRoot":"","sources":["../../../../src/vite/buildEnd/handleOnBuildEnd.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAKvD,KAAK,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAWpD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,YA4F9B,CAAC"}
1
+ {"version":3,"file":"handleOnBuildEnd.d.ts","sourceRoot":"","sources":["../../../../src/vite/buildEnd/handleOnBuildEnd.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAKvD,KAAK,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAWpD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAqG9B,CAAC"}
@@ -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