@sentry/react-router 9.38.0 → 9.39.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 +39 -26
- package/build/cjs/client/hydratedRouter.js.map +1 -1
- package/build/cjs/server/instrumentation/reactRouter.js +9 -4
- package/build/cjs/server/instrumentation/reactRouter.js.map +1 -1
- package/build/cjs/server/instrumentation/util.js +0 -3
- package/build/cjs/server/instrumentation/util.js.map +1 -1
- package/build/cjs/server/integration/reactRouterServer.js +24 -1
- package/build/cjs/server/integration/reactRouterServer.js.map +1 -1
- package/build/cjs/server/sdk.js +5 -37
- package/build/cjs/server/sdk.js.map +1 -1
- package/build/cjs/server/wrapSentryHandleRequest.js +15 -16
- package/build/cjs/server/wrapSentryHandleRequest.js.map +1 -1
- package/build/cjs/server/wrapServerAction.js +12 -7
- package/build/cjs/server/wrapServerAction.js.map +1 -1
- package/build/cjs/server/wrapServerLoader.js +12 -7
- package/build/cjs/server/wrapServerLoader.js.map +1 -1
- package/build/esm/client/hydratedRouter.js +40 -27
- package/build/esm/client/hydratedRouter.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/server/instrumentation/reactRouter.js +11 -6
- package/build/esm/server/instrumentation/reactRouter.js.map +1 -1
- package/build/esm/server/instrumentation/util.js +1 -3
- package/build/esm/server/instrumentation/util.js.map +1 -1
- package/build/esm/server/integration/reactRouterServer.js +25 -2
- package/build/esm/server/integration/reactRouterServer.js.map +1 -1
- package/build/esm/server/sdk.js +7 -39
- package/build/esm/server/sdk.js.map +1 -1
- package/build/esm/server/wrapSentryHandleRequest.js +16 -17
- package/build/esm/server/wrapSentryHandleRequest.js.map +1 -1
- package/build/esm/server/wrapServerAction.js +13 -8
- package/build/esm/server/wrapServerAction.js.map +1 -1
- package/build/esm/server/wrapServerLoader.js +13 -8
- package/build/esm/server/wrapServerLoader.js.map +1 -1
- package/build/types/client/hydratedRouter.d.ts.map +1 -1
- package/build/types/server/instrumentation/reactRouter.d.ts.map +1 -1
- package/build/types/server/integration/reactRouterServer.d.ts.map +1 -1
- package/build/types/server/sdk.d.ts.map +1 -1
- package/build/types/server/wrapSentryHandleRequest.d.ts.map +1 -1
- package/build/types/server/wrapServerAction.d.ts.map +1 -1
- package/build/types/server/wrapServerLoader.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -26,43 +26,56 @@ function instrumentHydratedRouter() {
|
|
|
26
26
|
// The first time we hit the router, we try to update the pageload transaction
|
|
27
27
|
// todo: update pageload tx here
|
|
28
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
29
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
30
|
+
if (pageloadSpan) {
|
|
31
|
+
const pageloadName = core.spanToJSON(pageloadSpan).description;
|
|
32
|
+
const parameterizePageloadRoute = getParameterizedRoute(router.state);
|
|
33
|
+
if (
|
|
34
|
+
pageloadName &&
|
|
35
|
+
// this event is for the currently active pageload
|
|
36
|
+
normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName)
|
|
37
|
+
) {
|
|
38
|
+
pageloadSpan.updateName(parameterizePageloadRoute);
|
|
39
|
+
pageloadSpan.setAttributes({
|
|
40
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
|
|
41
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react-router',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Patching navigate for creating accurate navigation transactions
|
|
46
|
+
if (typeof router.navigate === 'function') {
|
|
47
|
+
const originalNav = router.navigate.bind(router);
|
|
48
|
+
router.navigate = function sentryPatchedNavigate(...args) {
|
|
49
|
+
maybeCreateNavigationTransaction(
|
|
50
|
+
String(args[0]) || '<unknown route>', // will be updated anyway
|
|
51
|
+
'url', // this also will be updated once we have the parameterized route
|
|
52
|
+
);
|
|
53
|
+
return originalNav(...args);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
// Subscribe to router state changes to update navigation transactions with parameterized routes
|
|
53
59
|
router.subscribe(newState => {
|
|
54
60
|
const navigationSpan = getActiveRootSpan();
|
|
55
|
-
|
|
61
|
+
|
|
62
|
+
if (!navigationSpan) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const navigationSpanName = core.spanToJSON(navigationSpan).description;
|
|
56
67
|
const parameterizedNavRoute = getParameterizedRoute(newState);
|
|
57
68
|
|
|
58
69
|
if (
|
|
59
|
-
navigationSpanName &&
|
|
70
|
+
navigationSpanName &&
|
|
60
71
|
newState.navigation.state === 'idle' && // navigation has completed
|
|
61
|
-
normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName)
|
|
62
|
-
normalizePathname(parameterizedNavRoute) !== normalizePathname(navigationSpanName) // route is not parameterized yet
|
|
72
|
+
normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName) // this event is for the currently active navigation
|
|
63
73
|
) {
|
|
64
|
-
navigationSpan
|
|
65
|
-
navigationSpan
|
|
74
|
+
navigationSpan.updateName(parameterizedNavRoute);
|
|
75
|
+
navigationSpan.setAttributes({
|
|
76
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
|
|
77
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react-router',
|
|
78
|
+
});
|
|
66
79
|
}
|
|
67
80
|
});
|
|
68
81
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydratedRouter.js","sources":["../../../src/client/hydratedRouter.ts"],"sourcesContent":["import { startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n consoleSandbox,\n getActiveSpan,\n getClient,\n getRootSpan,\n GLOBAL_OBJ,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n} from '@sentry/core';\nimport type { DataRouter, RouterState } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\n\nconst GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __reactRouterDataRouter?: DataRouter;\n};\n\nconst MAX_RETRIES = 40; // 2 seconds at 50ms interval\n\n/**\n * Instruments the React Router Data Router for pageloads and navigation.\n *\n * This function waits for the router to be available after hydration, then:\n * 1. Updates the pageload transaction with parameterized route info\n * 2. Patches router.navigate() to create navigation transactions\n * 3. Subscribes to router state changes to update navigation transactions with parameterized routes\n */\nexport function instrumentHydratedRouter(): void {\n function trySubscribe(): boolean {\n const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;\n\n if (router) {\n // The first time we hit the router, we try to update the pageload transaction\n // todo: update pageload tx here\n const pageloadSpan = getActiveRootSpan();\n const pageloadName =
|
|
1
|
+
{"version":3,"file":"hydratedRouter.js","sources":["../../../src/client/hydratedRouter.ts"],"sourcesContent":["import { startBrowserTracingNavigationSpan } from '@sentry/browser';\nimport type { Span } from '@sentry/core';\nimport {\n consoleSandbox,\n getActiveSpan,\n getClient,\n getRootSpan,\n GLOBAL_OBJ,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n} from '@sentry/core';\nimport type { DataRouter, RouterState } from 'react-router';\nimport { DEBUG_BUILD } from '../common/debug-build';\n\nconst GLOBAL_OBJ_WITH_DATA_ROUTER = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __reactRouterDataRouter?: DataRouter;\n};\n\nconst MAX_RETRIES = 40; // 2 seconds at 50ms interval\n\n/**\n * Instruments the React Router Data Router for pageloads and navigation.\n *\n * This function waits for the router to be available after hydration, then:\n * 1. Updates the pageload transaction with parameterized route info\n * 2. Patches router.navigate() to create navigation transactions\n * 3. Subscribes to router state changes to update navigation transactions with parameterized routes\n */\nexport function instrumentHydratedRouter(): void {\n function trySubscribe(): boolean {\n const router = GLOBAL_OBJ_WITH_DATA_ROUTER.__reactRouterDataRouter;\n\n if (router) {\n // The first time we hit the router, we try to update the pageload transaction\n // todo: update pageload tx here\n const pageloadSpan = getActiveRootSpan();\n\n if (pageloadSpan) {\n const pageloadName = spanToJSON(pageloadSpan).description;\n const parameterizePageloadRoute = getParameterizedRoute(router.state);\n if (\n pageloadName &&\n // this event is for the currently active pageload\n normalizePathname(router.state.location.pathname) === normalizePathname(pageloadName)\n ) {\n pageloadSpan.updateName(parameterizePageloadRoute);\n pageloadSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react-router',\n });\n }\n\n // Patching navigate for creating accurate navigation transactions\n if (typeof router.navigate === 'function') {\n const originalNav = router.navigate.bind(router);\n router.navigate = function sentryPatchedNavigate(...args) {\n maybeCreateNavigationTransaction(\n String(args[0]) || '<unknown route>', // will be updated anyway\n 'url', // this also will be updated once we have the parameterized route\n );\n return originalNav(...args);\n };\n }\n }\n\n // Subscribe to router state changes to update navigation transactions with parameterized routes\n router.subscribe(newState => {\n const navigationSpan = getActiveRootSpan();\n\n if (!navigationSpan) {\n return;\n }\n\n const navigationSpanName = spanToJSON(navigationSpan).description;\n const parameterizedNavRoute = getParameterizedRoute(newState);\n\n if (\n navigationSpanName &&\n newState.navigation.state === 'idle' && // navigation has completed\n normalizePathname(newState.location.pathname) === normalizePathname(navigationSpanName) // this event is for the currently active navigation\n ) {\n navigationSpan.updateName(parameterizedNavRoute);\n navigationSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react-router',\n });\n }\n });\n return true;\n }\n return false;\n }\n\n // Wait until the router is available (since the SDK loads before hydration)\n if (!trySubscribe()) {\n let retryCount = 0;\n // Retry until the router is available or max retries reached\n const interval = setInterval(() => {\n if (trySubscribe() || retryCount >= MAX_RETRIES) {\n if (retryCount >= MAX_RETRIES) {\n DEBUG_BUILD &&\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn('Unable to instrument React Router: router not found after hydration.');\n });\n }\n clearInterval(interval);\n }\n retryCount++;\n }, 50);\n }\n}\n\nfunction maybeCreateNavigationTransaction(name: string, source: 'url' | 'route'): Span | undefined {\n const client = getClient();\n\n if (!client) {\n return undefined;\n }\n\n return startBrowserTracingNavigationSpan(client, {\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react-router',\n },\n });\n}\n\nfunction getActiveRootSpan(): Span | undefined {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n return undefined;\n }\n\n const rootSpan = getRootSpan(activeSpan);\n\n const op = spanToJSON(rootSpan).op;\n\n // Only use this root span if it is a pageload or navigation span\n return op === 'navigation' || op === 'pageload' ? rootSpan : undefined;\n}\n\nfunction getParameterizedRoute(routerState: RouterState): string {\n const lastMatch = routerState.matches[routerState.matches.length - 1];\n return normalizePathname(lastMatch?.route.path ?? routerState.location.pathname);\n}\n\nfunction normalizePathname(pathname: string): string {\n // Ensure it starts with a single slash\n let normalized = pathname.startsWith('/') ? pathname : `/${pathname}`;\n // Remove trailing slash unless it's the root\n if (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n return normalized;\n}\n"],"names":["GLOBAL_OBJ","spanToJSON","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","DEBUG_BUILD","consoleSandbox","getClient","startBrowserTracingNavigationSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","getActiveSpan","getRootSpan"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const instrumentation = require('@opentelemetry/instrumentation');
|
|
4
|
+
const semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
4
5
|
const core = require('@sentry/core');
|
|
5
6
|
const debugBuild = require('../../common/debug-build.js');
|
|
6
7
|
const util = require('./util.js');
|
|
@@ -70,19 +71,23 @@ class ReactRouterInstrumentation extends instrumentation.InstrumentationBase {
|
|
|
70
71
|
return originalRequestHandler(request, initialContext);
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
//
|
|
74
|
-
//
|
|
74
|
+
// We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route
|
|
75
|
+
// So we force this to be a more sensible name here
|
|
75
76
|
// TODO: try to set derived parameterized route from build here (args[0])
|
|
77
|
+
const spanData = core.spanToJSON(rootSpan);
|
|
78
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
79
|
+
const target = spanData.data[semanticConventions.SEMATTRS_HTTP_TARGET] || url.pathname;
|
|
80
|
+
core.updateSpanName(rootSpan, `${request.method} ${target}`);
|
|
76
81
|
rootSpan.setAttributes({
|
|
77
82
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
|
|
78
|
-
[
|
|
83
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.server',
|
|
79
84
|
});
|
|
80
85
|
|
|
81
86
|
return core.startSpan(
|
|
82
87
|
{
|
|
83
88
|
name: util.getSpanName(url.pathname, request.method),
|
|
84
89
|
attributes: {
|
|
85
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router',
|
|
90
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.server',
|
|
86
91
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: util.getOpName(url.pathname, request.method),
|
|
87
92
|
},
|
|
88
93
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactRouter.js","sources":["../../../../src/server/instrumentation/reactRouter.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport {\n getActiveSpan,\n getRootSpan,\n logger,\n SDK_VERSION,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n} from '@sentry/core';\nimport type * as reactRouter from 'react-router';\nimport { DEBUG_BUILD } from '../../common/debug-build';\nimport { getOpName, getSpanName, isDataRequest
|
|
1
|
+
{"version":3,"file":"reactRouter.js","sources":["../../../../src/server/instrumentation/reactRouter.ts"],"sourcesContent":["import type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';\nimport { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport {\n getActiveSpan,\n getRootSpan,\n logger,\n SDK_VERSION,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport type * as reactRouter from 'react-router';\nimport { DEBUG_BUILD } from '../../common/debug-build';\nimport { getOpName, getSpanName, isDataRequest } from './util';\n\ntype ReactRouterModuleExports = typeof reactRouter;\n\nconst supportedVersions = ['>=7.0.0'];\nconst COMPONENT = 'react-router';\n\n/**\n * Instrumentation for React Router's server request handler.\n * This patches the requestHandler function to add Sentry performance monitoring for data loaders.\n */\nexport class ReactRouterInstrumentation extends InstrumentationBase<InstrumentationConfig> {\n public constructor(config: InstrumentationConfig = {}) {\n super('ReactRouterInstrumentation', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the React Router server modules to be patched.\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition {\n const reactRouterServerModule = new InstrumentationNodeModuleDefinition(\n COMPONENT,\n supportedVersions,\n (moduleExports: ReactRouterModuleExports) => {\n return this._createPatchedModuleProxy(moduleExports);\n },\n (_moduleExports: unknown) => {\n // nothing to unwrap here\n return _moduleExports;\n },\n );\n\n return reactRouterServerModule;\n }\n\n /**\n * Creates a proxy around the React Router module exports that patches the createRequestHandler function.\n * This allows us to wrap the request handler to add performance monitoring for data loaders and actions.\n */\n private _createPatchedModuleProxy(moduleExports: ReactRouterModuleExports): ReactRouterModuleExports {\n return new Proxy(moduleExports, {\n get(target, prop, receiver) {\n if (prop === 'createRequestHandler') {\n const original = target[prop];\n return function sentryWrappedCreateRequestHandler(this: unknown, ...args: unknown[]) {\n const originalRequestHandler = original.apply(this, args);\n\n return async function sentryWrappedRequestHandler(request: Request, initialContext?: unknown) {\n let url: URL;\n try {\n url = new URL(request.url);\n } catch (error) {\n return originalRequestHandler(request, initialContext);\n }\n\n // We currently just want to trace loaders and actions\n if (!isDataRequest(url.pathname)) {\n return originalRequestHandler(request, initialContext);\n }\n\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan && getRootSpan(activeSpan);\n\n if (!rootSpan) {\n DEBUG_BUILD && logger.debug('No active root span found, skipping tracing for data request');\n return originalRequestHandler(request, initialContext);\n }\n\n // We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route\n // So we force this to be a more sensible name here\n // TODO: try to set derived parameterized route from build here (args[0])\n const spanData = spanToJSON(rootSpan);\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET] || url.pathname;\n updateSpanName(rootSpan, `${request.method} ${target}`);\n rootSpan.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.server',\n });\n\n return startSpan(\n {\n name: getSpanName(url.pathname, request.method),\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.server',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: getOpName(url.pathname, request.method),\n },\n },\n () => {\n return originalRequestHandler(request, initialContext);\n },\n );\n };\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","InstrumentationNodeModuleDefinition","isDataRequest","getActiveSpan","getRootSpan","DEBUG_BUILD","logger","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","getSpanName","SEMANTIC_ATTRIBUTE_SENTRY_OP","getOpName"],"mappings":"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"}
|
|
@@ -52,9 +52,6 @@ function isDataRequest(pathname) {
|
|
|
52
52
|
return pathname.endsWith('.data');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
const SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE = 'sentry.overwrite-route';
|
|
56
|
-
|
|
57
|
-
exports.SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE = SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE;
|
|
58
55
|
exports.getOpName = getOpName;
|
|
59
56
|
exports.getSpanName = getSpanName;
|
|
60
57
|
exports.isActionRequest = isActionRequest;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sources":["../../../../src/server/instrumentation/util.ts"],"sourcesContent":["/**\n * Gets the op name for a request based on whether it's a loader or action request.\n * @param pathName The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function getOpName(pathName: string, requestMethod: string): string {\n return isLoaderRequest(pathName, requestMethod)\n ? 'function.react-router.loader'\n : isActionRequest(pathName, requestMethod)\n ? 'function.react-router.action'\n : 'function.react-router';\n}\n\n/**\n * Gets the span name for a request based on whether it's a loader or action request.\n * @param pathName The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function getSpanName(pathName: string, requestMethod: string): string {\n return isLoaderRequest(pathName, requestMethod)\n ? 'Executing Server Loader'\n : isActionRequest(pathName, requestMethod)\n ? 'Executing Server Action'\n : 'Unknown Data Request';\n}\n\n/**\n * Checks if the request is a server loader request\n * @param pathname The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function isLoaderRequest(pathname: string, requestMethod: string): boolean {\n return isDataRequest(pathname) && requestMethod === 'GET';\n}\n\n/**\n * Checks if the request is a server action request\n * @param pathname The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function isActionRequest(pathname: string, requestMethod: string): boolean {\n return isDataRequest(pathname) && requestMethod === 'POST';\n}\n\n/**\n * Checks if the request is a react-router data request\n * @param pathname The URL pathname to check\n */\nexport function isDataRequest(pathname: string): boolean {\n return pathname.endsWith('.data');\n}\n\nexport const SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE = 'sentry.overwrite-route';\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACO,SAAS,SAAS,CAAC,QAAQ,EAAU,aAAa,EAAkB;AAC3E,EAAE,OAAO,eAAe,CAAC,QAAQ,EAAE,aAAa;AAChD,MAAM;AACN,MAAM,eAAe,CAAC,QAAQ,EAAE,aAAa;AAC7C,QAAQ;AACR,QAAQ,uBAAuB;AAC/B;;AAEA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,QAAQ,EAAU,aAAa,EAAkB;AAC7E,EAAE,OAAO,eAAe,CAAC,QAAQ,EAAE,aAAa;AAChD,MAAM;AACN,MAAM,eAAe,CAAC,QAAQ,EAAE,aAAa;AAC7C,QAAQ;AACR,QAAQ,sBAAsB;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,QAAQ,EAAU,aAAa,EAAmB;AAClF,EAAE,OAAO,aAAa,CAAC,QAAQ,KAAK,aAAA,KAAkB,KAAK;AAC3D;;AAEA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,QAAQ,EAAU,aAAa,EAAmB;AAClF,EAAE,OAAO,aAAa,CAAC,QAAQ,KAAK,aAAA,KAAkB,MAAM;AAC5D;;AAEA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,QAAQ,EAAmB;AACzD,EAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;AACnC
|
|
1
|
+
{"version":3,"file":"util.js","sources":["../../../../src/server/instrumentation/util.ts"],"sourcesContent":["/**\n * Gets the op name for a request based on whether it's a loader or action request.\n * @param pathName The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function getOpName(pathName: string, requestMethod: string): string {\n return isLoaderRequest(pathName, requestMethod)\n ? 'function.react-router.loader'\n : isActionRequest(pathName, requestMethod)\n ? 'function.react-router.action'\n : 'function.react-router';\n}\n\n/**\n * Gets the span name for a request based on whether it's a loader or action request.\n * @param pathName The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function getSpanName(pathName: string, requestMethod: string): string {\n return isLoaderRequest(pathName, requestMethod)\n ? 'Executing Server Loader'\n : isActionRequest(pathName, requestMethod)\n ? 'Executing Server Action'\n : 'Unknown Data Request';\n}\n\n/**\n * Checks if the request is a server loader request\n * @param pathname The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function isLoaderRequest(pathname: string, requestMethod: string): boolean {\n return isDataRequest(pathname) && requestMethod === 'GET';\n}\n\n/**\n * Checks if the request is a server action request\n * @param pathname The URL pathname to check\n * @param requestMethod The HTTP request method\n */\nexport function isActionRequest(pathname: string, requestMethod: string): boolean {\n return isDataRequest(pathname) && requestMethod === 'POST';\n}\n\n/**\n * Checks if the request is a react-router data request\n * @param pathname The URL pathname to check\n */\nexport function isDataRequest(pathname: string): boolean {\n return pathname.endsWith('.data');\n}\n\nexport const SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE = 'sentry.overwrite-route';\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACO,SAAS,SAAS,CAAC,QAAQ,EAAU,aAAa,EAAkB;AAC3E,EAAE,OAAO,eAAe,CAAC,QAAQ,EAAE,aAAa;AAChD,MAAM;AACN,MAAM,eAAe,CAAC,QAAQ,EAAE,aAAa;AAC7C,QAAQ;AACR,QAAQ,uBAAuB;AAC/B;;AAEA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,QAAQ,EAAU,aAAa,EAAkB;AAC7E,EAAE,OAAO,eAAe,CAAC,QAAQ,EAAE,aAAa;AAChD,MAAM;AACN,MAAM,eAAe,CAAC,QAAQ,EAAE,aAAa;AAC7C,QAAQ;AACR,QAAQ,sBAAsB;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,QAAQ,EAAU,aAAa,EAAmB;AAClF,EAAE,OAAO,aAAa,CAAC,QAAQ,KAAK,aAAA,KAAkB,KAAK;AAC3D;;AAEA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,QAAQ,EAAU,aAAa,EAAmB;AAClF,EAAE,OAAO,aAAa,CAAC,QAAQ,KAAK,aAAA,KAAkB,MAAM;AAC5D;;AAEA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,QAAQ,EAAmB;AACzD,EAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;AACnC;;;;;;;;"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
+
const semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
3
4
|
const core = require('@sentry/core');
|
|
4
5
|
const node = require('@sentry/node');
|
|
5
6
|
const reactRouter = require('../instrumentation/reactRouter.js');
|
|
@@ -24,7 +25,29 @@ const reactRouterServerIntegration = core.defineIntegration(() => {
|
|
|
24
25
|
return {
|
|
25
26
|
name: INTEGRATION_NAME,
|
|
26
27
|
setupOnce() {
|
|
27
|
-
|
|
28
|
+
if (
|
|
29
|
+
(node.NODE_VERSION.major === 20 && node.NODE_VERSION.minor < 19) || // https://nodejs.org/en/blog/release/v20.19.0
|
|
30
|
+
(node.NODE_VERSION.major === 22 && node.NODE_VERSION.minor < 12) // https://nodejs.org/en/blog/release/v22.12.0
|
|
31
|
+
) {
|
|
32
|
+
instrumentReactRouterServer();
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
processEvent(event) {
|
|
36
|
+
// Express generates bogus `*` routes for data loaders, which we want to remove here
|
|
37
|
+
// we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point
|
|
38
|
+
if (
|
|
39
|
+
event.type === 'transaction' &&
|
|
40
|
+
event.contexts?.trace?.data &&
|
|
41
|
+
event.contexts.trace.data[semanticConventions.ATTR_HTTP_ROUTE] === '*' &&
|
|
42
|
+
// This means the name has been adjusted before, but the http.route remains, so we need to remove it
|
|
43
|
+
event.transaction !== 'GET *' &&
|
|
44
|
+
event.transaction !== 'POST *'
|
|
45
|
+
) {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
47
|
+
delete event.contexts.trace.data[semanticConventions.ATTR_HTTP_ROUTE];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return event;
|
|
28
51
|
},
|
|
29
52
|
};
|
|
30
53
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactRouterServer.js","sources":["../../../../src/server/integration/reactRouterServer.ts"],"sourcesContent":["import { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node';\nimport { ReactRouterInstrumentation } from '../instrumentation/reactRouter';\n\nconst INTEGRATION_NAME = 'ReactRouterServer';\n\nconst instrumentReactRouter = generateInstrumentOnce('React-Router-Server', () => {\n return new ReactRouterInstrumentation();\n});\n\nexport const instrumentReactRouterServer = Object.assign(\n (): void => {\n instrumentReactRouter();\n },\n { id: INTEGRATION_NAME },\n);\n\n/**\n * Integration capturing tracing data for React Router server functions.\n */\nexport const reactRouterServerIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentReactRouterServer();\n },\n };\n});\n"],"names":["generateInstrumentOnce","ReactRouterInstrumentation","defineIntegration"],"mappings":"
|
|
1
|
+
{"version":3,"file":"reactRouterServer.js","sources":["../../../../src/server/integration/reactRouterServer.ts"],"sourcesContent":["import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport { defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce, NODE_VERSION } from '@sentry/node';\nimport { ReactRouterInstrumentation } from '../instrumentation/reactRouter';\n\nconst INTEGRATION_NAME = 'ReactRouterServer';\n\nconst instrumentReactRouter = generateInstrumentOnce('React-Router-Server', () => {\n return new ReactRouterInstrumentation();\n});\n\nexport const instrumentReactRouterServer = Object.assign(\n (): void => {\n instrumentReactRouter();\n },\n { id: INTEGRATION_NAME },\n);\n\n/**\n * Integration capturing tracing data for React Router server functions.\n */\nexport const reactRouterServerIntegration = defineIntegration(() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n if (\n (NODE_VERSION.major === 20 && NODE_VERSION.minor < 19) || // https://nodejs.org/en/blog/release/v20.19.0\n (NODE_VERSION.major === 22 && NODE_VERSION.minor < 12) // https://nodejs.org/en/blog/release/v22.12.0\n ) {\n instrumentReactRouterServer();\n }\n },\n processEvent(event) {\n // Express generates bogus `*` routes for data loaders, which we want to remove here\n // we cannot do this earlier because some OTEL instrumentation adds this at some unexpected point\n if (\n event.type === 'transaction' &&\n event.contexts?.trace?.data &&\n event.contexts.trace.data[ATTR_HTTP_ROUTE] === '*' &&\n // This means the name has been adjusted before, but the http.route remains, so we need to remove it\n event.transaction !== 'GET *' &&\n event.transaction !== 'POST *'\n ) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete event.contexts.trace.data[ATTR_HTTP_ROUTE];\n }\n\n return event;\n },\n };\n});\n"],"names":["generateInstrumentOnce","ReactRouterInstrumentation","defineIntegration","NODE_VERSION","ATTR_HTTP_ROUTE"],"mappings":";;;;;;;AAKA,MAAM,gBAAA,GAAmB,mBAAmB;;AAE5C,MAAM,qBAAA,GAAwBA,2BAAsB,CAAC,qBAAqB,EAAE,MAAM;AAClF,EAAE,OAAO,IAAIC,sCAA0B,EAAE;AACzC,CAAC,CAAC;;AAEK,MAAM,2BAAA,GAA8B,MAAM,CAAC,MAAM;AACxD,EAAE,MAAY;AACd,IAAI,qBAAqB,EAAE;AAC3B,GAAG;AACH,EAAE,EAAE,EAAE,EAAE,gBAAA,EAAkB;AAC1B;;AAEA;AACA;AACA;MACa,4BAAA,GAA+BC,sBAAiB,CAAC,MAAM;AACpE,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM;AACN,QAAQ,CAACC,iBAAY,CAAC,KAAA,KAAU,EAAA,IAAMA,iBAAY,CAAC,KAAA,GAAQ,EAAE;AAC7D,SAASA,iBAAY,CAAC,KAAA,KAAU,EAAA,IAAMA,iBAAY,CAAC,KAAA,GAAQ,EAAE,CAAA;AAC7D,QAAQ;AACR,QAAQ,2BAA2B,EAAE;AACrC;AACA,KAAK;AACL,IAAI,YAAY,CAAC,KAAK,EAAE;AACxB;AACA;AACA,MAAM;AACN,QAAQ,KAAK,CAAC,IAAA,KAAS,aAAA;AACvB,QAAQ,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAA;AAC/B,QAAQ,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAACC,mCAAe,CAAA,KAAM,GAAA;AACvD;AACA,QAAQ,KAAK,CAAC,WAAA,KAAgB,OAAA;AAC9B,QAAQ,KAAK,CAAC,WAAA,KAAgB;AAC9B,QAAQ;AACR;AACA,QAAQ,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAACA,mCAAe,CAAC;AACzD;;AAEA,MAAM,OAAO,KAAK;AAClB,KAAK;AACL,GAAG;AACH,CAAC;;;;;"}
|
package/build/cjs/server/sdk.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
4
3
|
const core = require('@sentry/core');
|
|
5
4
|
const node = require('@sentry/node');
|
|
6
5
|
const debugBuild = require('../common/debug-build.js');
|
|
7
|
-
const util = require('./instrumentation/util.js');
|
|
8
6
|
const lowQualityTransactionsFilterIntegration = require('./integration/lowQualityTransactionsFilterIntegration.js');
|
|
9
7
|
const reactRouterServer = require('./integration/reactRouterServer.js');
|
|
10
8
|
|
|
@@ -13,16 +11,11 @@ const reactRouterServer = require('./integration/reactRouterServer.js');
|
|
|
13
11
|
* @param options The options for the SDK.
|
|
14
12
|
*/
|
|
15
13
|
function getDefaultReactRouterServerIntegrations(options) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
(
|
|
20
|
-
|
|
21
|
-
) {
|
|
22
|
-
integrations.push(reactRouterServer.reactRouterServerIntegration());
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return integrations;
|
|
14
|
+
return [
|
|
15
|
+
...node.getDefaultIntegrations(options),
|
|
16
|
+
lowQualityTransactionsFilterIntegration.lowQualityTransactionsFilterIntegration(options),
|
|
17
|
+
reactRouterServer.reactRouterServerIntegration(),
|
|
18
|
+
];
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
/**
|
|
@@ -42,31 +35,6 @@ function init(options) {
|
|
|
42
35
|
|
|
43
36
|
core.setTag('runtime', 'node');
|
|
44
37
|
|
|
45
|
-
// Overwrite the transaction name for instrumented data loaders because the trace data gets overwritten at a later point.
|
|
46
|
-
// We only update the tx in case SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE got set in our instrumentation before.
|
|
47
|
-
core.getGlobalScope().addEventProcessor(
|
|
48
|
-
Object.assign(
|
|
49
|
-
(event => {
|
|
50
|
-
const overwrite = event.contexts?.trace?.data?.[util.SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE];
|
|
51
|
-
if (
|
|
52
|
-
event.type === 'transaction' &&
|
|
53
|
-
(event.transaction === 'GET *' || event.transaction === 'POST *') &&
|
|
54
|
-
event.contexts?.trace?.data?.[semanticConventions.ATTR_HTTP_ROUTE] === '*' &&
|
|
55
|
-
overwrite
|
|
56
|
-
) {
|
|
57
|
-
event.transaction = overwrite;
|
|
58
|
-
event.contexts.trace.data[semanticConventions.ATTR_HTTP_ROUTE] = 'url';
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// always yeet this attribute into the void, as this should not reach the server
|
|
62
|
-
delete event.contexts?.trace?.data?.[util.SEMANTIC_ATTRIBUTE_SENTRY_OVERWRITE];
|
|
63
|
-
|
|
64
|
-
return event;
|
|
65
|
-
}) ,
|
|
66
|
-
{ id: 'ReactRouterTransactionEnhancer' },
|
|
67
|
-
),
|
|
68
|
-
);
|
|
69
|
-
|
|
70
38
|
debugBuild.DEBUG_BUILD && core.logger.log('SDK successfully initialized');
|
|
71
39
|
|
|
72
40
|
return client;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.js","sources":["../../../src/server/sdk.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"sdk.js","sources":["../../../src/server/sdk.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport { applySdkMetadata, logger, setTag } from '@sentry/core';\nimport type { NodeClient, NodeOptions } from '@sentry/node';\nimport { getDefaultIntegrations as getNodeDefaultIntegrations, init as initNodeSdk } from '@sentry/node';\nimport { DEBUG_BUILD } from '../common/debug-build';\nimport { lowQualityTransactionsFilterIntegration } from './integration/lowQualityTransactionsFilterIntegration';\nimport { reactRouterServerIntegration } from './integration/reactRouterServer';\n\n/**\n * Returns the default integrations for the React Router SDK.\n * @param options The options for the SDK.\n */\nexport function getDefaultReactRouterServerIntegrations(options: NodeOptions): Integration[] {\n return [\n ...getNodeDefaultIntegrations(options),\n lowQualityTransactionsFilterIntegration(options),\n reactRouterServerIntegration(),\n ];\n}\n\n/**\n * Initializes the server side of the React Router SDK\n */\nexport function init(options: NodeOptions): NodeClient | undefined {\n const opts: NodeOptions = {\n ...options,\n defaultIntegrations: getDefaultReactRouterServerIntegrations(options),\n };\n\n DEBUG_BUILD && logger.log('Initializing SDK...');\n\n applySdkMetadata(opts, 'react-router', ['react-router', 'node']);\n\n const client = initNodeSdk(opts);\n\n setTag('runtime', 'node');\n\n DEBUG_BUILD && logger.log('SDK successfully initialized');\n\n return client;\n}\n"],"names":["getNodeDefaultIntegrations","lowQualityTransactionsFilterIntegration","reactRouterServerIntegration","DEBUG_BUILD","logger","applySdkMetadata","initNodeSdk","setTag"],"mappings":";;;;;;;;AAQA;AACA;AACA;AACA;AACO,SAAS,uCAAuC,CAAC,OAAO,EAA8B;AAC7F,EAAE,OAAO;AACT,IAAI,GAAGA,2BAA0B,CAAC,OAAO,CAAC;AAC1C,IAAIC,+EAAuC,CAAC,OAAO,CAAC;AACpD,IAAIC,8CAA4B,EAAE;AAClC,GAAG;AACH;;AAEA;AACA;AACA;AACO,SAAS,IAAI,CAAC,OAAO,EAAuC;AACnE,EAAE,MAAM,IAAI,GAAgB;AAC5B,IAAI,GAAG,OAAO;AACd,IAAI,mBAAmB,EAAE,uCAAuC,CAAC,OAAO,CAAC;AACzE,GAAG;;AAEH,EAAEC,0BAAeC,WAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC;;AAElD,EAAEC,qBAAgB,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;;AAElE,EAAE,MAAM,MAAA,GAASC,SAAW,CAAC,IAAI,CAAC;;AAElC,EAAEC,WAAM,CAAC,SAAS,EAAE,MAAM,CAAC;;AAE3B,EAAEJ,0BAAeC,WAAM,CAAC,GAAG,CAAC,8BAA8B,CAAC;;AAE3D,EAAE,OAAO,MAAM;AACf;;;;;"}
|
|
@@ -23,26 +23,25 @@ function wrapSentryHandleRequest(originalHandle) {
|
|
|
23
23
|
const parameterizedPath =
|
|
24
24
|
routerContext?.staticHandlerContext?.matches?.[routerContext.staticHandlerContext.matches.length - 1]?.route.path;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (activeSpan) {
|
|
29
|
-
const rootSpan = core.getRootSpan(activeSpan);
|
|
30
|
-
const routeName = `/${parameterizedPath}`;
|
|
26
|
+
const activeSpan = core.getActiveSpan();
|
|
27
|
+
const rootSpan = activeSpan ? core.getRootSpan(activeSpan) : undefined;
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
if (parameterizedPath && rootSpan) {
|
|
30
|
+
const routeName = `/${parameterizedPath}`;
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
32
|
+
// The express instrumentation writes on the rpcMetadata and that ends up stomping on the `http.route` attribute.
|
|
33
|
+
const rpcMetadata = core$1.getRPCMetadata(api.context.active());
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
[semanticConventions.ATTR_HTTP_ROUTE]: routeName,
|
|
42
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
|
|
43
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]: `${request.method} ${routeName}`,
|
|
44
|
-
});
|
|
35
|
+
if (rpcMetadata?.type === core$1.RPCType.HTTP) {
|
|
36
|
+
rpcMetadata.route = routeName;
|
|
45
37
|
}
|
|
38
|
+
|
|
39
|
+
// The span exporter picks up the `http.route` (ATTR_HTTP_ROUTE) attribute to set the transaction name
|
|
40
|
+
rootSpan.setAttributes({
|
|
41
|
+
[semanticConventions.ATTR_HTTP_ROUTE]: routeName,
|
|
42
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
|
|
43
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.request-handler',
|
|
44
|
+
});
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
return originalHandle(request, responseStatusCode, responseHeaders, routerContext, loadContext);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapSentryHandleRequest.js","sources":["../../../src/server/wrapSentryHandleRequest.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { getRPCMetadata, RPCType } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport {\n getActiveSpan,\n getRootSpan,\n getTraceMetaTags,\n
|
|
1
|
+
{"version":3,"file":"wrapSentryHandleRequest.js","sources":["../../../src/server/wrapSentryHandleRequest.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { getRPCMetadata, RPCType } from '@opentelemetry/core';\nimport { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';\nimport {\n getActiveSpan,\n getRootSpan,\n getTraceMetaTags,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n} 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\n const activeSpan = getActiveSpan();\n const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;\n\n if (parameterizedPath && rootSpan) {\n const routeName = `/${parameterizedPath}`;\n\n // The express instrumentation writes on the rpcMetadata and that ends up stomping on the `http.route` attribute.\n const rpcMetadata = getRPCMetadata(context.active());\n\n if (rpcMetadata?.type === RPCType.HTTP) {\n rpcMetadata.route = routeName;\n }\n\n // The span exporter picks up the `http.route` (ATTR_HTTP_ROUTE) attribute to set the transaction name\n rootSpan.setAttributes({\n [ATTR_HTTP_ROUTE]: routeName,\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.request-handler',\n });\n }\n\n 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":["getActiveSpan","getRootSpan","getRPCMetadata","context","RPCType","ATTR_HTTP_ROUTE","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","Transform","getTraceMetaTags"],"mappings":";;;;;;;;AAsBA;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,iBAAA;AACV,MAAM,aAAa,EAAE,oBAAoB,EAAE,OAAO,GAAG,aAAa,CAAC,oBAAoB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI;;AAEvH,IAAI,MAAM,UAAA,GAAaA,kBAAa,EAAE;AACtC,IAAI,MAAM,QAAA,GAAW,UAAA,GAAaC,gBAAW,CAAC,UAAU,CAAA,GAAI,SAAS;;AAErE,IAAI,IAAI,iBAAA,IAAqB,QAAQ,EAAE;AACvC,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAA;;AAEA;AACA,MAAA,MAAA,WAAA,GAAAC,qBAAA,CAAAC,WAAA,CAAA,MAAA,EAAA,CAAA;;AAEA,MAAA,IAAA,WAAA,EAAA,IAAA,KAAAC,cAAA,CAAA,IAAA,EAAA;AACA,QAAA,WAAA,CAAA,KAAA,GAAA,SAAA;AACA;;AAEA;AACA,MAAA,QAAA,CAAA,aAAA,CAAA;AACA,QAAA,CAAAC,mCAAA,GAAA,SAAA;AACA,QAAA,CAAAC,qCAAA,GAAA,OAAA;AACA,QAAA,CAAAC,qCAAA,GAAA,wCAAA;AACA,OAAA,CAAA;AACA;;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,IAAAC,gBAAA,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,EAAAC,qBAAA,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,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
+
const semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
3
4
|
const core = require('@sentry/core');
|
|
4
|
-
const util = require('./instrumentation/util.js');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Wraps a React Router server action function with Sentry performance monitoring.
|
|
@@ -28,13 +28,18 @@ function wrapServerAction(options = {}, actionFn) {
|
|
|
28
28
|
const active = core.getActiveSpan();
|
|
29
29
|
if (active) {
|
|
30
30
|
const root = core.getRootSpan(active);
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const spanData = core.spanToJSON(root);
|
|
32
|
+
if (spanData.origin === 'auto.http.otel.http') {
|
|
33
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
34
|
+
const target = spanData.data[semanticConventions.SEMATTRS_HTTP_TARGET];
|
|
35
|
+
|
|
36
|
+
if (target) {
|
|
37
|
+
// We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route
|
|
38
|
+
// So we force this to be a more sensible name here
|
|
39
|
+
core.updateSpanName(root, `${args.request.method} ${target}`);
|
|
35
40
|
root.setAttributes({
|
|
36
41
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
|
|
37
|
-
[
|
|
42
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.action',
|
|
38
43
|
});
|
|
39
44
|
}
|
|
40
45
|
}
|
|
@@ -45,7 +50,7 @@ function wrapServerAction(options = {}, actionFn) {
|
|
|
45
50
|
name,
|
|
46
51
|
...options,
|
|
47
52
|
attributes: {
|
|
48
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router',
|
|
53
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.action',
|
|
49
54
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react-router.action',
|
|
50
55
|
...options.attributes,
|
|
51
56
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapServerAction.js","sources":["../../../src/server/wrapServerAction.ts"],"sourcesContent":["import type { SpanAttributes } from '@sentry/core';\nimport {\n getActiveSpan,\n getRootSpan,\n
|
|
1
|
+
{"version":3,"file":"wrapServerAction.js","sources":["../../../src/server/wrapServerAction.ts"],"sourcesContent":["import { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport type { SpanAttributes } from '@sentry/core';\nimport {\n getActiveSpan,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport type { ActionFunctionArgs } from 'react-router';\n\ntype SpanOptions = {\n name?: string;\n attributes?: SpanAttributes;\n};\n\n/**\n * Wraps a React Router server action function with Sentry performance monitoring.\n * @param options - Optional span configuration options including name, operation, description and attributes\n * @param actionFn - The server action function to wrap\n *\n * @example\n * ```ts\n * // Wrap an action function with custom span options\n * export const action = wrapServerAction(\n * {\n * name: 'Submit Form Data',\n * description: 'Processes form submission data',\n * },\n * async ({ request }) => {\n * // ... your action logic\n * }\n * );\n * ```\n */\nexport function wrapServerAction<T>(options: SpanOptions = {}, actionFn: (args: ActionFunctionArgs) => Promise<T>) {\n return async function (args: ActionFunctionArgs) {\n const name = options.name || 'Executing Server Action';\n const active = getActiveSpan();\n if (active) {\n const root = getRootSpan(active);\n const spanData = spanToJSON(root);\n if (spanData.origin === 'auto.http.otel.http') {\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET];\n\n if (target) {\n // We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route\n // So we force this to be a more sensible name here\n updateSpanName(root, `${args.request.method} ${target}`);\n root.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.action',\n });\n }\n }\n }\n\n return startSpan(\n {\n name,\n ...options,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.action',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react-router.action',\n ...options.attributes,\n },\n },\n () => actionFn(args),\n );\n };\n}\n"],"names":["getActiveSpan","getRootSpan","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP"],"mappings":";;;;;AAmBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAI,OAAO,GAAgB,EAAE,EAAE,QAAQ,EAA4C;AACnH,EAAE,OAAO,gBAAgB,IAAI,EAAsB;AACnD,IAAI,MAAM,IAAA,GAAO,OAAO,CAAC,IAAA,IAAQ,yBAAyB;AAC1D,IAAI,MAAM,MAAA,GAASA,kBAAa,EAAE;AAClC,IAAI,IAAI,MAAM,EAAE;AAChB,MAAM,MAAM,IAAA,GAAOC,gBAAW,CAAC,MAAM,CAAC;AACtC,MAAM,MAAM,QAAA,GAAWC,eAAU,CAAC,IAAI,CAAC;AACvC,MAAM,IAAI,QAAQ,CAAC,MAAA,KAAW,qBAAqB,EAAE;AACrD;AACA,QAAQ,MAAM,SAAS,QAAQ,CAAC,IAAI,CAACC,wCAAoB,CAAC;;AAE1D,QAAQ,IAAI,MAAM,EAAE;AACpB;AACA;AACA,UAAUC,mBAAc,CAAC,IAAI,EAAE,CAAC,EAAA,IAAA,CAAA,OAAA,CAAA,MAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA,CAAA;AACA,UAAA,IAAA,CAAA,aAAA,CAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,KAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,+BAAA;AACA,WAAA,CAAA;AACA;AACA;AACA;;AAEA,IAAA,OAAAC,cAAA;AACA,MAAA;AACA,QAAA,IAAA;AACA,QAAA,GAAA,OAAA;AACA,QAAA,UAAA,EAAA;AACA,UAAA,CAAAD,qCAAA,GAAA,+BAAA;AACA,UAAA,CAAAE,iCAAA,GAAA,8BAAA;AACA,UAAA,GAAA,OAAA,CAAA,UAAA;AACA,SAAA;AACA,OAAA;AACA,MAAA,MAAA,QAAA,CAAA,IAAA,CAAA;AACA,KAAA;AACA,GAAA;AACA;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
+
const semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
3
4
|
const core = require('@sentry/core');
|
|
4
|
-
const util = require('./instrumentation/util.js');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Wraps a React Router server loader function with Sentry performance monitoring.
|
|
@@ -26,16 +26,21 @@ function wrapServerLoader(options = {}, loaderFn) {
|
|
|
26
26
|
return async function (args) {
|
|
27
27
|
const name = options.name || 'Executing Server Loader';
|
|
28
28
|
const active = core.getActiveSpan();
|
|
29
|
+
|
|
29
30
|
if (active) {
|
|
30
31
|
const root = core.getRootSpan(active);
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
32
|
+
const spanData = core.spanToJSON(root);
|
|
33
|
+
if (spanData.origin === 'auto.http.otel.http') {
|
|
34
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
35
|
+
const target = spanData.data[semanticConventions.SEMATTRS_HTTP_TARGET];
|
|
34
36
|
|
|
35
|
-
if (
|
|
37
|
+
if (target) {
|
|
38
|
+
// We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route
|
|
39
|
+
// So we force this to be a more sensible name here
|
|
40
|
+
core.updateSpanName(root, `${args.request.method} ${target}`);
|
|
36
41
|
root.setAttributes({
|
|
37
42
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
|
|
38
|
-
[
|
|
43
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.loader',
|
|
39
44
|
});
|
|
40
45
|
}
|
|
41
46
|
}
|
|
@@ -45,7 +50,7 @@ function wrapServerLoader(options = {}, loaderFn) {
|
|
|
45
50
|
name,
|
|
46
51
|
...options,
|
|
47
52
|
attributes: {
|
|
48
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router',
|
|
53
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.loader',
|
|
49
54
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react-router.loader',
|
|
50
55
|
...options.attributes,
|
|
51
56
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapServerLoader.js","sources":["../../../src/server/wrapServerLoader.ts"],"sourcesContent":["import type { SpanAttributes } from '@sentry/core';\nimport {\n getActiveSpan,\n getRootSpan,\n
|
|
1
|
+
{"version":3,"file":"wrapServerLoader.js","sources":["../../../src/server/wrapServerLoader.ts"],"sourcesContent":["import { SEMATTRS_HTTP_TARGET } from '@opentelemetry/semantic-conventions';\nimport type { SpanAttributes } from '@sentry/core';\nimport {\n getActiveSpan,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n startSpan,\n updateSpanName,\n} from '@sentry/core';\nimport type { LoaderFunctionArgs } from 'react-router';\n\ntype SpanOptions = {\n name?: string;\n attributes?: SpanAttributes;\n};\n\n/**\n * Wraps a React Router server loader function with Sentry performance monitoring.\n * @param options - Optional span configuration options including name, operation, description and attributes\n * @param loaderFn - The server loader function to wrap\n *\n * @example\n * ```ts\n * // Wrap a loader function with custom span options\n * export const loader = wrapServerLoader(\n * {\n * name: 'Load Some Data',\n * description: 'Loads some data from the db',\n * },\n * async ({ params }) => {\n * // ... your loader logic\n * }\n * );\n * ```\n */\nexport function wrapServerLoader<T>(options: SpanOptions = {}, loaderFn: (args: LoaderFunctionArgs) => Promise<T>) {\n return async function (args: LoaderFunctionArgs) {\n const name = options.name || 'Executing Server Loader';\n const active = getActiveSpan();\n\n if (active) {\n const root = getRootSpan(active);\n const spanData = spanToJSON(root);\n if (spanData.origin === 'auto.http.otel.http') {\n // eslint-disable-next-line deprecation/deprecation\n const target = spanData.data[SEMATTRS_HTTP_TARGET];\n\n if (target) {\n // We cannot rely on the regular span name inferral here, as the express instrumentation sets `*` as the route\n // So we force this to be a more sensible name here\n updateSpanName(root, `${args.request.method} ${target}`);\n root.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.loader',\n });\n }\n }\n }\n return startSpan(\n {\n name,\n ...options,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.react-router.loader',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.react-router.loader',\n ...options.attributes,\n },\n },\n () => loaderFn(args),\n );\n };\n}\n"],"names":["getActiveSpan","getRootSpan","spanToJSON","SEMATTRS_HTTP_TARGET","updateSpanName","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP"],"mappings":";;;;;AAmBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAI,OAAO,GAAgB,EAAE,EAAE,QAAQ,EAA4C;AACnH,EAAE,OAAO,gBAAgB,IAAI,EAAsB;AACnD,IAAI,MAAM,IAAA,GAAO,OAAO,CAAC,IAAA,IAAQ,yBAAyB;AAC1D,IAAI,MAAM,MAAA,GAASA,kBAAa,EAAE;;AAElC,IAAI,IAAI,MAAM,EAAE;AAChB,MAAM,MAAM,IAAA,GAAOC,gBAAW,CAAC,MAAM,CAAC;AACtC,MAAM,MAAM,QAAA,GAAWC,eAAU,CAAC,IAAI,CAAC;AACvC,MAAM,IAAI,QAAQ,CAAC,MAAA,KAAW,qBAAqB,EAAE;AACrD;AACA,QAAQ,MAAM,SAAS,QAAQ,CAAC,IAAI,CAACC,wCAAoB,CAAC;;AAE1D,QAAQ,IAAI,MAAM,EAAE;AACpB;AACA;AACA,UAAUC,mBAAc,CAAC,IAAI,EAAE,CAAC,EAAA,IAAA,CAAA,OAAA,CAAA,MAAA,CAAA,CAAA,EAAA,MAAA,CAAA,CAAA,CAAA;AACA,UAAA,IAAA,CAAA,aAAA,CAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,KAAA;AACA,YAAA,CAAAC,qCAAA,GAAA,+BAAA;AACA,WAAA,CAAA;AACA;AACA;AACA;AACA,IAAA,OAAAC,cAAA;AACA,MAAA;AACA,QAAA,IAAA;AACA,QAAA,GAAA,OAAA;AACA,QAAA,UAAA,EAAA;AACA,UAAA,CAAAD,qCAAA,GAAA,+BAAA;AACA,UAAA,CAAAE,iCAAA,GAAA,8BAAA;AACA,UAAA,GAAA,OAAA,CAAA,UAAA;AACA,SAAA;AACA,OAAA;AACA,MAAA,MAAA,QAAA,CAAA,IAAA,CAAA;AACA,KAAA;AACA,GAAA;AACA;;;;"}
|