@sentry/react-router 9.15.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.
- package/build/cjs/client/hydratedRouter.js +140 -0
- package/build/cjs/client/hydratedRouter.js.map +1 -0
- package/build/cjs/client/sdk.js +19 -5
- package/build/cjs/client/sdk.js.map +1 -1
- package/build/cjs/client/tracingIntegration.js +27 -0
- package/build/cjs/client/tracingIntegration.js.map +1 -0
- package/build/cjs/index.client.js +2 -0
- package/build/cjs/index.client.js.map +1 -1
- package/build/cjs/index.server.js +2 -0
- package/build/cjs/index.server.js.map +1 -1
- package/build/cjs/vite/makeConfigInjectorPlugin.js +25 -0
- package/build/cjs/vite/makeConfigInjectorPlugin.js.map +1 -0
- package/build/cjs/vite/plugin.js +3 -0
- package/build/cjs/vite/plugin.js.map +1 -1
- package/build/esm/client/hydratedRouter.js +138 -0
- package/build/esm/client/hydratedRouter.js.map +1 -0
- package/build/esm/client/sdk.js +20 -6
- package/build/esm/client/sdk.js.map +1 -1
- package/build/esm/client/tracingIntegration.js +25 -0
- package/build/esm/client/tracingIntegration.js.map +1 -0
- package/build/esm/index.client.js +1 -0
- package/build/esm/index.client.js.map +1 -1
- package/build/esm/index.server.js +1 -0
- package/build/esm/index.server.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/vite/makeConfigInjectorPlugin.js +23 -0
- package/build/esm/vite/makeConfigInjectorPlugin.js.map +1 -0
- package/build/esm/vite/plugin.js +3 -0
- package/build/esm/vite/plugin.js.map +1 -1
- package/build/types/client/hydratedRouter.d.ts +10 -0
- package/build/types/client/hydratedRouter.d.ts.map +1 -0
- package/build/types/client/index.d.ts +1 -0
- package/build/types/client/index.d.ts.map +1 -1
- package/build/types/client/sdk.d.ts.map +1 -1
- package/build/types/client/tracingIntegration.d.ts +7 -0
- package/build/types/client/tracingIntegration.d.ts.map +1 -0
- package/build/types/vite/index.d.ts +1 -0
- package/build/types/vite/index.d.ts.map +1 -1
- package/build/types/vite/makeConfigInjectorPlugin.d.ts +12 -0
- package/build/types/vite/makeConfigInjectorPlugin.d.ts.map +1 -0
- package/build/types/vite/plugin.d.ts.map +1 -1
- 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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"}
|
package/build/cjs/client/sdk.js
CHANGED
|
@@ -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
|
-
|
|
11
|
-
|
|
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(
|
|
28
|
+
core.applySdkMetadata(options, 'react-router', ['react-router', 'browser']);
|
|
15
29
|
|
|
16
|
-
const client = browser.init(
|
|
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
|
|
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;;;;"}
|
package/build/cjs/vite/plugin.js
CHANGED
|
@@ -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":"
|
|
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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"}
|
package/build/esm/client/sdk.js
CHANGED
|
@@ -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
|
-
|
|
9
|
-
|
|
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(
|
|
26
|
+
applySdkMetadata(options, 'react-router', ['react-router', 'browser']);
|
|
13
27
|
|
|
14
|
-
const client = init$1(
|
|
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
|
|
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 +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":";;;;;;"}
|
package/build/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"9.
|
|
1
|
+
{"type":"module","version":"9.16.0"}
|
|
@@ -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;;;;"}
|
package/build/esm/vite/plugin.js
CHANGED
|
@@ -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":"
|
|
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 +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;
|
|
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;
|
|
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.
|
|
3
|
+
"version": "9.16.0",
|
|
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.
|
|
40
|
+
"@sentry/browser": "9.16.0",
|
|
41
41
|
"@sentry/cli": "^2.43.0",
|
|
42
|
-
"@sentry/core": "9.
|
|
43
|
-
"@sentry/node": "9.
|
|
42
|
+
"@sentry/core": "9.16.0",
|
|
43
|
+
"@sentry/node": "9.16.0",
|
|
44
44
|
"@sentry/vite-plugin": "^3.2.4",
|
|
45
45
|
"glob": "11.0.1"
|
|
46
46
|
},
|