@sentry/react-router 10.53.1 → 10.54.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/createClientInstrumentation.js +104 -175
- package/build/cjs/client/createClientInstrumentation.js.map +1 -1
- package/build/cjs/client/hydratedRouter.js +27 -74
- package/build/cjs/client/hydratedRouter.js.map +1 -1
- package/build/cjs/client/sdk.js +5 -16
- package/build/cjs/client/sdk.js.map +1 -1
- package/build/cjs/client/sentryOnError.js +4 -27
- package/build/cjs/client/sentryOnError.js.map +1 -1
- package/build/cjs/client/tracingIntegration.js +4 -18
- package/build/cjs/client/tracingIntegration.js.map +1 -1
- package/build/cjs/client/utils.js +5 -17
- package/build/cjs/client/utils.js.map +1 -1
- package/build/cjs/cloudflare/index.js +2 -15
- package/build/cjs/cloudflare/index.js.map +1 -1
- package/build/cjs/common/debug-build.js +0 -5
- package/build/cjs/common/debug-build.js.map +1 -1
- package/build/cjs/common/utils.js +8 -33
- package/build/cjs/common/utils.js.map +1 -1
- package/build/cjs/server/createSentryHandleError.js +4 -13
- package/build/cjs/server/createSentryHandleError.js.map +1 -1
- package/build/cjs/server/createSentryHandleRequest.js +14 -49
- package/build/cjs/server/createSentryHandleRequest.js.map +1 -1
- package/build/cjs/server/createServerInstrumentation.js +76 -114
- package/build/cjs/server/createServerInstrumentation.js.map +1 -1
- package/build/cjs/server/getMetaTagTransformer.js +2 -8
- package/build/cjs/server/getMetaTagTransformer.js.map +1 -1
- package/build/cjs/server/instrumentation/reactRouter.js +21 -47
- package/build/cjs/server/instrumentation/reactRouter.js.map +1 -1
- package/build/cjs/server/instrumentation/util.js +5 -41
- package/build/cjs/server/instrumentation/util.js.map +1 -1
- package/build/cjs/server/integration/lowQualityTransactionsFilterIntegration.js +5 -12
- package/build/cjs/server/integration/lowQualityTransactionsFilterIntegration.js.map +1 -1
- package/build/cjs/server/integration/reactRouterServer.js +10 -42
- package/build/cjs/server/integration/reactRouterServer.js.map +1 -1
- package/build/cjs/server/sdk.js +6 -20
- package/build/cjs/server/sdk.js.map +1 -1
- package/build/cjs/server/serverBuild.js +6 -31
- package/build/cjs/server/serverBuild.js.map +1 -1
- package/build/cjs/server/serverGlobals.js +6 -26
- package/build/cjs/server/serverGlobals.js.map +1 -1
- package/build/cjs/server/wrapSentryHandleRequest.js +13 -53
- package/build/cjs/server/wrapSentryHandleRequest.js.map +1 -1
- package/build/cjs/server/wrapServerAction.js +12 -44
- package/build/cjs/server/wrapServerAction.js.map +1 -1
- package/build/cjs/server/wrapServerLoader.js +12 -44
- package/build/cjs/server/wrapServerLoader.js.map +1 -1
- package/build/cjs/vite/buildEnd/handleOnBuildEnd.js +38 -72
- package/build/cjs/vite/buildEnd/handleOnBuildEnd.js.map +1 -1
- package/build/cjs/vite/makeConfigInjectorPlugin.js +4 -12
- package/build/cjs/vite/makeConfigInjectorPlugin.js.map +1 -1
- package/build/cjs/vite/makeCustomSentryVitePlugins.js +10 -15
- package/build/cjs/vite/makeCustomSentryVitePlugins.js.map +1 -1
- package/build/cjs/vite/makeEnableSourceMapsPlugin.js +19 -51
- package/build/cjs/vite/makeEnableSourceMapsPlugin.js.map +1 -1
- package/build/cjs/vite/makeServerBuildCapturePlugin.js +4 -15
- package/build/cjs/vite/makeServerBuildCapturePlugin.js.map +1 -1
- package/build/cjs/vite/plugin.js +3 -16
- package/build/cjs/vite/plugin.js.map +1 -1
- package/build/esm/client/createClientInstrumentation.js +104 -175
- package/build/esm/client/createClientInstrumentation.js.map +1 -1
- package/build/esm/client/hydratedRouter.js +27 -74
- package/build/esm/client/hydratedRouter.js.map +1 -1
- package/build/esm/client/sdk.js +5 -16
- package/build/esm/client/sdk.js.map +1 -1
- package/build/esm/client/sentryOnError.js +4 -27
- package/build/esm/client/sentryOnError.js.map +1 -1
- package/build/esm/client/tracingIntegration.js +4 -18
- package/build/esm/client/tracingIntegration.js.map +1 -1
- package/build/esm/client/utils.js +5 -17
- package/build/esm/client/utils.js.map +1 -1
- package/build/esm/cloudflare/index.js +2 -15
- package/build/esm/cloudflare/index.js.map +1 -1
- package/build/esm/common/debug-build.js +0 -5
- package/build/esm/common/debug-build.js.map +1 -1
- package/build/esm/common/utils.js +8 -33
- package/build/esm/common/utils.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/server/createSentryHandleError.js +4 -13
- package/build/esm/server/createSentryHandleError.js.map +1 -1
- package/build/esm/server/createSentryHandleRequest.js +14 -49
- package/build/esm/server/createSentryHandleRequest.js.map +1 -1
- package/build/esm/server/createServerInstrumentation.js +76 -114
- package/build/esm/server/createServerInstrumentation.js.map +1 -1
- package/build/esm/server/getMetaTagTransformer.js +2 -8
- package/build/esm/server/getMetaTagTransformer.js.map +1 -1
- package/build/esm/server/instrumentation/reactRouter.js +21 -47
- package/build/esm/server/instrumentation/reactRouter.js.map +1 -1
- package/build/esm/server/instrumentation/util.js +5 -41
- package/build/esm/server/instrumentation/util.js.map +1 -1
- package/build/esm/server/integration/lowQualityTransactionsFilterIntegration.js +5 -12
- package/build/esm/server/integration/lowQualityTransactionsFilterIntegration.js.map +1 -1
- package/build/esm/server/integration/reactRouterServer.js +10 -42
- package/build/esm/server/integration/reactRouterServer.js.map +1 -1
- package/build/esm/server/sdk.js +6 -20
- package/build/esm/server/sdk.js.map +1 -1
- package/build/esm/server/serverBuild.js +6 -31
- package/build/esm/server/serverBuild.js.map +1 -1
- package/build/esm/server/serverGlobals.js +6 -26
- package/build/esm/server/serverGlobals.js.map +1 -1
- package/build/esm/server/wrapSentryHandleRequest.js +13 -53
- package/build/esm/server/wrapSentryHandleRequest.js.map +1 -1
- package/build/esm/server/wrapServerAction.js +12 -44
- package/build/esm/server/wrapServerAction.js.map +1 -1
- package/build/esm/server/wrapServerLoader.js +12 -44
- package/build/esm/server/wrapServerLoader.js.map +1 -1
- package/build/esm/vite/buildEnd/handleOnBuildEnd.js +38 -72
- package/build/esm/vite/buildEnd/handleOnBuildEnd.js.map +1 -1
- package/build/esm/vite/makeConfigInjectorPlugin.js +4 -12
- package/build/esm/vite/makeConfigInjectorPlugin.js.map +1 -1
- package/build/esm/vite/makeCustomSentryVitePlugins.js +10 -15
- package/build/esm/vite/makeCustomSentryVitePlugins.js.map +1 -1
- package/build/esm/vite/makeEnableSourceMapsPlugin.js +19 -51
- package/build/esm/vite/makeEnableSourceMapsPlugin.js.map +1 -1
- package/build/esm/vite/makeServerBuildCapturePlugin.js +4 -15
- package/build/esm/vite/makeServerBuildCapturePlugin.js.map +1 -1
- package/build/esm/vite/plugin.js +3 -16
- package/build/esm/vite/plugin.js.map +1 -1
- package/build/types/client/hydratedRouter.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../src/common/utils.ts"],"sourcesContent":["import { captureException, debug } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { InstrumentationResult } from './types';\n\n/**\n * Extracts pathname from request URL.\n * Falls back to '<unknown>' with DEBUG warning if URL cannot be parsed.\n */\nexport function getPathFromRequest(request: { url: string }): string {\n try {\n return new URL(request.url).pathname;\n } catch {\n try {\n // Fallback: use a dummy base URL since we only care about the pathname\n return new URL(request.url, 'http://example.com').pathname;\n } catch (error) {\n DEBUG_BUILD && debug.warn('Failed to parse URL from request:', request.url, error);\n return '<unknown>';\n }\n }\n}\n\n/**\n * Extracts route pattern from instrumentation info.\n * Prefers `pattern` (planned for v8) over `unstable_pattern` (v7.x).\n */\nexport function getPattern(info: { pattern?: string; unstable_pattern?: string }): string | undefined {\n return info.pattern ?? info.unstable_pattern;\n}\n\n/**\n * Normalizes route path by ensuring it starts with a slash.\n * Returns undefined if the input is falsy.\n */\nexport function normalizeRoutePath(pattern?: string): string | undefined {\n if (!pattern) {\n return undefined;\n }\n return pattern.startsWith('/') ? pattern : `/${pattern}`;\n}\n\n/**\n * Captures an error from instrumentation result.\n * Caller must verify result contains an Error before calling.\n */\nexport function captureInstrumentationError(\n result: InstrumentationResult,\n captureErrors: boolean,\n mechanismType: string,\n data: Record<string, string | boolean>,\n): void {\n if (captureErrors) {\n captureException(result.error, {\n mechanism: {\n type: mechanismType,\n handled: false,\n data,\n },\n });\n }\n}\n"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../src/common/utils.ts"],"sourcesContent":["import { captureException, debug } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { InstrumentationResult } from './types';\n\n/**\n * Extracts pathname from request URL.\n * Falls back to '<unknown>' with DEBUG warning if URL cannot be parsed.\n */\nexport function getPathFromRequest(request: { url: string }): string {\n try {\n return new URL(request.url).pathname;\n } catch {\n try {\n // Fallback: use a dummy base URL since we only care about the pathname\n return new URL(request.url, 'http://example.com').pathname;\n } catch (error) {\n DEBUG_BUILD && debug.warn('Failed to parse URL from request:', request.url, error);\n return '<unknown>';\n }\n }\n}\n\n/**\n * Extracts route pattern from instrumentation info.\n * Prefers `pattern` (planned for v8) over `unstable_pattern` (v7.x).\n */\nexport function getPattern(info: { pattern?: string; unstable_pattern?: string }): string | undefined {\n return info.pattern ?? info.unstable_pattern;\n}\n\n/**\n * Normalizes route path by ensuring it starts with a slash.\n * Returns undefined if the input is falsy.\n */\nexport function normalizeRoutePath(pattern?: string): string | undefined {\n if (!pattern) {\n return undefined;\n }\n return pattern.startsWith('/') ? pattern : `/${pattern}`;\n}\n\n/**\n * Captures an error from instrumentation result.\n * Caller must verify result contains an Error before calling.\n */\nexport function captureInstrumentationError(\n result: InstrumentationResult,\n captureErrors: boolean,\n mechanismType: string,\n data: Record<string, string | boolean>,\n): void {\n if (captureErrors) {\n captureException(result.error, {\n mechanism: {\n type: mechanismType,\n handled: false,\n data,\n },\n });\n }\n}\n"],"names":[],"mappings":";;;AAQO,SAAS,mBAAmB,OAAA,EAAkC;AACnE,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,QAAA;AAAA,EAC9B,CAAA,CAAA,MAAQ;AACN,IAAA,IAAI;AAEF,MAAA,OAAO,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAA,EAAK,oBAAoB,CAAA,CAAE,QAAA;AAAA,IACpD,SAAS,KAAA,EAAO;AACd,MAAA,WAAA,IAAe,KAAA,CAAM,IAAA,CAAK,mCAAA,EAAqC,OAAA,CAAQ,KAAK,KAAK,CAAA;AACjF,MAAA,OAAO,WAAA;AAAA,IACT;AAAA,EACF;AACF;AAMO,SAAS,WAAW,IAAA,EAA2E;AACpG,EAAA,OAAO,IAAA,CAAK,WAAW,IAAA,CAAK,gBAAA;AAC9B;AAMO,SAAS,mBAAmB,OAAA,EAAsC;AACvE,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,QAAQ,UAAA,CAAW,GAAG,CAAA,GAAI,OAAA,GAAU,IAAI,OAAO,CAAA,CAAA;AACxD;AAMO,SAAS,2BAAA,CACd,MAAA,EACA,aAAA,EACA,aAAA,EACA,IAAA,EACM;AACN,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,gBAAA,CAAiB,OAAO,KAAA,EAAO;AAAA,MAC7B,SAAA,EAAW;AAAA,QACT,IAAA,EAAM,aAAA;AAAA,QACN,OAAA,EAAS,KAAA;AAAA,QACT;AAAA;AACF,KACD,CAAA;AAAA,EACH;AACF;;;;"}
|
package/build/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"10.
|
|
1
|
+
{"type":"module","version":"10.54.0"}
|
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
import { captureException, flushIfServerless } from '@sentry/core';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* A complete Sentry-instrumented handleError implementation that handles error reporting
|
|
5
|
-
*
|
|
6
|
-
* @returns A Sentry-instrumented handleError function
|
|
7
|
-
*/
|
|
8
3
|
function createSentryHandleError({ logErrors = false }) {
|
|
9
|
-
const handleError = async function
|
|
10
|
-
// React Router may abort some interrupted requests, don't report those
|
|
4
|
+
const handleError = async function handleError2(error, args) {
|
|
11
5
|
if (!args.request.signal.aborted) {
|
|
12
6
|
captureException(error, {
|
|
13
7
|
mechanism: {
|
|
14
|
-
type:
|
|
15
|
-
handled: false
|
|
16
|
-
}
|
|
8
|
+
type: "react-router",
|
|
9
|
+
handled: false
|
|
10
|
+
}
|
|
17
11
|
});
|
|
18
12
|
if (logErrors) {
|
|
19
|
-
// eslint-disable-next-line no-console
|
|
20
13
|
console.error(error);
|
|
21
14
|
}
|
|
22
15
|
try {
|
|
23
16
|
await flushIfServerless();
|
|
24
17
|
} catch {
|
|
25
|
-
// Ignore flush errors to ensure error handling completes gracefully
|
|
26
18
|
}
|
|
27
19
|
}
|
|
28
20
|
};
|
|
29
|
-
|
|
30
21
|
return handleError;
|
|
31
22
|
}
|
|
32
23
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSentryHandleError.js","sources":["../../../src/server/createSentryHandleError.ts"],"sourcesContent":["import { captureException, flushIfServerless } from '@sentry/core';\nimport type { HandleErrorFunction } from 'react-router';\n\nexport type SentryHandleErrorOptions = {\n logErrors?: boolean;\n};\n\n/**\n * A complete Sentry-instrumented handleError implementation that handles error reporting\n *\n * @returns A Sentry-instrumented handleError function\n */\nexport function createSentryHandleError({ logErrors = false }: SentryHandleErrorOptions): HandleErrorFunction {\n const handleError: HandleErrorFunction = async function handleError(error, args): Promise<void> {\n // React Router may abort some interrupted requests, don't report those\n if (!args.request.signal.aborted) {\n captureException(error, {\n mechanism: {\n type: 'react-router',\n handled: false,\n },\n });\n if (logErrors) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n try {\n await flushIfServerless();\n } catch {\n // Ignore flush errors to ensure error handling completes gracefully\n }\n }\n };\n\n return handleError;\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"createSentryHandleError.js","sources":["../../../src/server/createSentryHandleError.ts"],"sourcesContent":["import { captureException, flushIfServerless } from '@sentry/core';\nimport type { HandleErrorFunction } from 'react-router';\n\nexport type SentryHandleErrorOptions = {\n logErrors?: boolean;\n};\n\n/**\n * A complete Sentry-instrumented handleError implementation that handles error reporting\n *\n * @returns A Sentry-instrumented handleError function\n */\nexport function createSentryHandleError({ logErrors = false }: SentryHandleErrorOptions): HandleErrorFunction {\n const handleError: HandleErrorFunction = async function handleError(error, args): Promise<void> {\n // React Router may abort some interrupted requests, don't report those\n if (!args.request.signal.aborted) {\n captureException(error, {\n mechanism: {\n type: 'react-router',\n handled: false,\n },\n });\n if (logErrors) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n try {\n await flushIfServerless();\n } catch {\n // Ignore flush errors to ensure error handling completes gracefully\n }\n }\n };\n\n return handleError;\n}\n"],"names":["handleError"],"mappings":";;AAYO,SAAS,uBAAA,CAAwB,EAAE,SAAA,GAAY,KAAA,EAAM,EAAkD;AAC5G,EAAA,MAAM,WAAA,GAAmC,eAAeA,YAAAA,CAAY,KAAA,EAAO,IAAA,EAAqB;AAE9F,IAAA,IAAI,CAAC,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,OAAA,EAAS;AAChC,MAAA,gBAAA,CAAiB,KAAA,EAAO;AAAA,QACtB,SAAA,EAAW;AAAA,UACT,IAAA,EAAM,cAAA;AAAA,UACN,OAAA,EAAS;AAAA;AACX,OACD,CAAA;AACD,MAAA,IAAI,SAAA,EAAW;AAEb,QAAA,OAAA,CAAQ,MAAM,KAAK,CAAA;AAAA,MACrB;AACA,MAAA,IAAI;AACF,QAAA,MAAM,iBAAA,EAAkB;AAAA,MAC1B,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,WAAA;AACT;;;;"}
|
|
@@ -3,84 +3,49 @@ import { PassThrough } from 'stream';
|
|
|
3
3
|
import { getMetaTagTransformer } from './getMetaTagTransformer.js';
|
|
4
4
|
import { wrapSentryHandleRequest } from './wrapSentryHandleRequest.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
* A complete Sentry-instrumented handleRequest implementation that handles both
|
|
8
|
-
* route parametrization and trace meta tag injection.
|
|
9
|
-
*
|
|
10
|
-
* @param options Configuration options
|
|
11
|
-
* @returns A Sentry-instrumented handleRequest function
|
|
12
|
-
*/
|
|
13
|
-
function createSentryHandleRequest(
|
|
14
|
-
options,
|
|
15
|
-
) {
|
|
6
|
+
function createSentryHandleRequest(options) {
|
|
16
7
|
const {
|
|
17
|
-
streamTimeout =
|
|
8
|
+
streamTimeout = 1e4,
|
|
18
9
|
renderToPipeableStream,
|
|
19
10
|
ServerRouter,
|
|
20
11
|
createReadableStreamFromReadable,
|
|
21
|
-
botRegex = /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i
|
|
12
|
+
botRegex = /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i
|
|
22
13
|
} = options;
|
|
23
|
-
|
|
24
|
-
const handleRequest = function handleRequest(
|
|
25
|
-
request,
|
|
26
|
-
responseStatusCode,
|
|
27
|
-
responseHeaders,
|
|
28
|
-
routerContext,
|
|
29
|
-
_loadContext,
|
|
30
|
-
) {
|
|
14
|
+
const handleRequest = function handleRequest2(request, responseStatusCode, responseHeaders, routerContext, _loadContext) {
|
|
31
15
|
return new Promise((resolve, reject) => {
|
|
32
16
|
let shellRendered = false;
|
|
33
|
-
const userAgent = request.headers.get(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const readyOption = isBot || isSpaMode ? 'onAllReady' : 'onShellReady';
|
|
40
|
-
|
|
41
|
-
const { pipe, abort } = renderToPipeableStream(React.createElement(ServerRouter, { context: routerContext, url: request.url,} ), {
|
|
17
|
+
const userAgent = request.headers.get("user-agent");
|
|
18
|
+
const isBot = typeof userAgent === "string" && botRegex.test(userAgent);
|
|
19
|
+
const isSpaMode = !!routerContext.isSpaMode;
|
|
20
|
+
const readyOption = isBot || isSpaMode ? "onAllReady" : "onShellReady";
|
|
21
|
+
const { pipe, abort } = renderToPipeableStream(/* @__PURE__ */ React.createElement(ServerRouter, { context: routerContext, url: request.url }), {
|
|
42
22
|
[readyOption]() {
|
|
43
23
|
shellRendered = true;
|
|
44
24
|
const body = new PassThrough();
|
|
45
|
-
|
|
46
25
|
const stream = createReadableStreamFromReadable(body);
|
|
47
|
-
|
|
48
|
-
responseHeaders.set('Content-Type', 'text/html');
|
|
49
|
-
|
|
26
|
+
responseHeaders.set("Content-Type", "text/html");
|
|
50
27
|
resolve(
|
|
51
28
|
new Response(stream, {
|
|
52
29
|
headers: responseHeaders,
|
|
53
|
-
status: responseStatusCode
|
|
54
|
-
})
|
|
30
|
+
status: responseStatusCode
|
|
31
|
+
})
|
|
55
32
|
);
|
|
56
|
-
|
|
57
|
-
// this injects trace data to the HTML head
|
|
58
33
|
pipe(getMetaTagTransformer(body));
|
|
59
34
|
},
|
|
60
35
|
onShellError(error) {
|
|
61
36
|
reject(error);
|
|
62
37
|
},
|
|
63
38
|
onError(error) {
|
|
64
|
-
// eslint-disable-next-line no-param-reassign
|
|
65
39
|
responseStatusCode = 500;
|
|
66
|
-
// Log streaming rendering errors from inside the shell. Don't log
|
|
67
|
-
// errors encountered during initial shell rendering since they'll
|
|
68
|
-
// reject and get logged in handleDocumentRequest.
|
|
69
40
|
if (shellRendered) {
|
|
70
|
-
// eslint-disable-next-line no-console
|
|
71
41
|
console.error(error);
|
|
72
42
|
}
|
|
73
|
-
}
|
|
43
|
+
}
|
|
74
44
|
});
|
|
75
|
-
|
|
76
|
-
// Abort the rendering stream after the `streamTimeout`
|
|
77
45
|
setTimeout(abort, streamTimeout);
|
|
78
46
|
});
|
|
79
47
|
};
|
|
80
|
-
|
|
81
|
-
// Wrap the handle request function for request parametrization
|
|
82
|
-
return wrapSentryHandleRequest(handleRequest )
|
|
83
|
-
;
|
|
48
|
+
return wrapSentryHandleRequest(handleRequest);
|
|
84
49
|
}
|
|
85
50
|
|
|
86
51
|
export { createSentryHandleRequest };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSentryHandleRequest.js","sources":["../../../src/server/createSentryHandleRequest.tsx"],"sourcesContent":["import type { createReadableStreamFromReadable } from '@react-router/node';\nimport type { ReactNode } from 'react';\nimport React from 'react';\nimport type { AppLoadContext, EntryContext, RouterContextProvider, ServerRouter } from 'react-router';\nimport { PassThrough } from 'stream';\nimport { getMetaTagTransformer } from './getMetaTagTransformer';\nimport { wrapSentryHandleRequest } from './wrapSentryHandleRequest';\n\ntype RenderToPipeableStreamOptions = {\n [key: string]: unknown;\n onShellReady?: () => void;\n onAllReady?: () => void;\n onShellError?: (error: unknown) => void;\n onError?: (error: unknown) => void;\n};\n\ntype RenderToPipeableStreamResult = {\n pipe: (destination: NodeJS.WritableStream) => void;\n abort: () => void;\n};\n\ntype RenderToPipeableStreamFunction = (\n node: ReactNode,\n options: RenderToPipeableStreamOptions,\n) => RenderToPipeableStreamResult;\n\nexport interface SentryHandleRequestOptions {\n /**\n * Timeout in milliseconds after which the rendering stream will be aborted\n * @default 10000\n */\n streamTimeout?: number;\n\n /**\n * React's renderToPipeableStream function from 'react-dom/server'\n */\n renderToPipeableStream: RenderToPipeableStreamFunction;\n\n /**\n * The <ServerRouter /> component from '@react-router/server'\n */\n ServerRouter: typeof ServerRouter;\n\n /**\n * createReadableStreamFromReadable from '@react-router/node'\n */\n createReadableStreamFromReadable: typeof createReadableStreamFromReadable;\n\n /**\n * Regular expression to identify bot user agents\n * @default /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i\n */\n botRegex?: RegExp;\n}\n\ntype HandleRequestWithoutMiddleware = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n) => Promise<unknown>;\n\ntype HandleRequestWithMiddleware = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: RouterContextProvider,\n) => Promise<unknown>;\n\n/**\n * A complete Sentry-instrumented handleRequest implementation that handles both\n * route parametrization and trace meta tag injection.\n *\n * @param options Configuration options\n * @returns A Sentry-instrumented handleRequest function\n */\nexport function createSentryHandleRequest(\n options: SentryHandleRequestOptions,\n): HandleRequestWithoutMiddleware & HandleRequestWithMiddleware {\n const {\n streamTimeout = 10000,\n renderToPipeableStream,\n ServerRouter,\n createReadableStreamFromReadable,\n botRegex = /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i,\n } = options;\n\n const handleRequest = function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n _loadContext: AppLoadContext | RouterContextProvider,\n ): Promise<Response> {\n return new Promise((resolve, reject) => {\n let shellRendered = false;\n const userAgent = request.headers.get('user-agent');\n\n // Determine if we should use onAllReady or onShellReady\n const isBot = typeof userAgent === 'string' && botRegex.test(userAgent);\n const isSpaMode = !!(routerContext as { isSpaMode?: boolean }).isSpaMode;\n\n const readyOption = isBot || isSpaMode ? 'onAllReady' : 'onShellReady';\n\n const { pipe, abort } = renderToPipeableStream(<ServerRouter context={routerContext} url={request.url} />, {\n [readyOption]() {\n shellRendered = true;\n const body = new PassThrough();\n\n const stream = createReadableStreamFromReadable(body);\n\n responseHeaders.set('Content-Type', 'text/html');\n\n resolve(\n new Response(stream, {\n headers: responseHeaders,\n status: responseStatusCode,\n }),\n );\n\n // this injects trace data to the HTML head\n pipe(getMetaTagTransformer(body));\n },\n onShellError(error: unknown) {\n reject(error);\n },\n onError(error: unknown) {\n // eslint-disable-next-line no-param-reassign\n responseStatusCode = 500;\n // Log streaming rendering errors from inside the shell. Don't log\n // errors encountered during initial shell rendering since they'll\n // reject and get logged in handleDocumentRequest.\n if (shellRendered) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n },\n });\n\n // Abort the rendering stream after the `streamTimeout`\n setTimeout(abort, streamTimeout);\n });\n };\n\n // Wrap the handle request function for request parametrization\n return wrapSentryHandleRequest(handleRequest as HandleRequestWithoutMiddleware) as HandleRequestWithoutMiddleware &\n HandleRequestWithMiddleware;\n}\n"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"createSentryHandleRequest.js","sources":["../../../src/server/createSentryHandleRequest.tsx"],"sourcesContent":["import type { createReadableStreamFromReadable } from '@react-router/node';\nimport type { ReactNode } from 'react';\nimport React from 'react';\nimport type { AppLoadContext, EntryContext, RouterContextProvider, ServerRouter } from 'react-router';\nimport { PassThrough } from 'stream';\nimport { getMetaTagTransformer } from './getMetaTagTransformer';\nimport { wrapSentryHandleRequest } from './wrapSentryHandleRequest';\n\ntype RenderToPipeableStreamOptions = {\n [key: string]: unknown;\n onShellReady?: () => void;\n onAllReady?: () => void;\n onShellError?: (error: unknown) => void;\n onError?: (error: unknown) => void;\n};\n\ntype RenderToPipeableStreamResult = {\n pipe: (destination: NodeJS.WritableStream) => void;\n abort: () => void;\n};\n\ntype RenderToPipeableStreamFunction = (\n node: ReactNode,\n options: RenderToPipeableStreamOptions,\n) => RenderToPipeableStreamResult;\n\nexport interface SentryHandleRequestOptions {\n /**\n * Timeout in milliseconds after which the rendering stream will be aborted\n * @default 10000\n */\n streamTimeout?: number;\n\n /**\n * React's renderToPipeableStream function from 'react-dom/server'\n */\n renderToPipeableStream: RenderToPipeableStreamFunction;\n\n /**\n * The <ServerRouter /> component from '@react-router/server'\n */\n ServerRouter: typeof ServerRouter;\n\n /**\n * createReadableStreamFromReadable from '@react-router/node'\n */\n createReadableStreamFromReadable: typeof createReadableStreamFromReadable;\n\n /**\n * Regular expression to identify bot user agents\n * @default /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i\n */\n botRegex?: RegExp;\n}\n\ntype HandleRequestWithoutMiddleware = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: AppLoadContext,\n) => Promise<unknown>;\n\ntype HandleRequestWithMiddleware = (\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n loadContext: RouterContextProvider,\n) => Promise<unknown>;\n\n/**\n * A complete Sentry-instrumented handleRequest implementation that handles both\n * route parametrization and trace meta tag injection.\n *\n * @param options Configuration options\n * @returns A Sentry-instrumented handleRequest function\n */\nexport function createSentryHandleRequest(\n options: SentryHandleRequestOptions,\n): HandleRequestWithoutMiddleware & HandleRequestWithMiddleware {\n const {\n streamTimeout = 10000,\n renderToPipeableStream,\n ServerRouter,\n createReadableStreamFromReadable,\n botRegex = /bot|crawler|spider|googlebot|chrome-lighthouse|baidu|bing|google|yahoo|lighthouse/i,\n } = options;\n\n const handleRequest = function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n routerContext: EntryContext,\n _loadContext: AppLoadContext | RouterContextProvider,\n ): Promise<Response> {\n return new Promise((resolve, reject) => {\n let shellRendered = false;\n const userAgent = request.headers.get('user-agent');\n\n // Determine if we should use onAllReady or onShellReady\n const isBot = typeof userAgent === 'string' && botRegex.test(userAgent);\n const isSpaMode = !!(routerContext as { isSpaMode?: boolean }).isSpaMode;\n\n const readyOption = isBot || isSpaMode ? 'onAllReady' : 'onShellReady';\n\n const { pipe, abort } = renderToPipeableStream(<ServerRouter context={routerContext} url={request.url} />, {\n [readyOption]() {\n shellRendered = true;\n const body = new PassThrough();\n\n const stream = createReadableStreamFromReadable(body);\n\n responseHeaders.set('Content-Type', 'text/html');\n\n resolve(\n new Response(stream, {\n headers: responseHeaders,\n status: responseStatusCode,\n }),\n );\n\n // this injects trace data to the HTML head\n pipe(getMetaTagTransformer(body));\n },\n onShellError(error: unknown) {\n reject(error);\n },\n onError(error: unknown) {\n // eslint-disable-next-line no-param-reassign\n responseStatusCode = 500;\n // Log streaming rendering errors from inside the shell. Don't log\n // errors encountered during initial shell rendering since they'll\n // reject and get logged in handleDocumentRequest.\n if (shellRendered) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n },\n });\n\n // Abort the rendering stream after the `streamTimeout`\n setTimeout(abort, streamTimeout);\n });\n };\n\n // Wrap the handle request function for request parametrization\n return wrapSentryHandleRequest(handleRequest as HandleRequestWithoutMiddleware) as HandleRequestWithoutMiddleware &\n HandleRequestWithMiddleware;\n}\n"],"names":["handleRequest"],"mappings":";;;;;AA8EO,SAAS,0BACd,OAAA,EAC8D;AAC9D,EAAA,MAAM;AAAA,IACJ,aAAA,GAAgB,GAAA;AAAA,IAChB,sBAAA;AAAA,IACA,YAAA;AAAA,IACA,gCAAA;AAAA,IACA,QAAA,GAAW;AAAA,GACb,GAAI,OAAA;AAEJ,EAAA,MAAM,gBAAgB,SAASA,cAAAA,CAC7B,SACA,kBAAA,EACA,eAAA,EACA,eACA,YAAA,EACmB;AACnB,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,MAAA,IAAI,aAAA,GAAgB,KAAA;AACpB,MAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA;AAGlD,MAAA,MAAM,QAAQ,OAAO,SAAA,KAAc,QAAA,IAAY,QAAA,CAAS,KAAK,SAAS,CAAA;AACtE,MAAA,MAAM,SAAA,GAAY,CAAC,CAAE,aAAA,CAA0C,SAAA;AAE/D,MAAA,MAAM,WAAA,GAAc,KAAA,IAAS,SAAA,GAAY,YAAA,GAAe,cAAA;AAExD,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,sBAAA,iBAAuB,KAAA,CAAA,aAAA,CAAC,YAAA,EAAA,EAAa,OAAA,EAAS,aAAA,EAAe,GAAA,EAAK,OAAA,CAAQ,GAAA,EAAK,CAAA,EAAI;AAAA,QACzG,CAAC,WAAW,CAAA,GAAI;AACd,UAAA,aAAA,GAAgB,IAAA;AAChB,UAAA,MAAM,IAAA,GAAO,IAAI,WAAA,EAAY;AAE7B,UAAA,MAAM,MAAA,GAAS,iCAAiC,IAAI,CAAA;AAEpD,UAAA,eAAA,CAAgB,GAAA,CAAI,gBAAgB,WAAW,CAAA;AAE/C,UAAA,OAAA;AAAA,YACE,IAAI,SAAS,MAAA,EAAQ;AAAA,cACnB,OAAA,EAAS,eAAA;AAAA,cACT,MAAA,EAAQ;AAAA,aACT;AAAA,WACH;AAGA,UAAA,IAAA,CAAK,qBAAA,CAAsB,IAAI,CAAC,CAAA;AAAA,QAClC,CAAA;AAAA,QACA,aAAa,KAAA,EAAgB;AAC3B,UAAA,MAAA,CAAO,KAAK,CAAA;AAAA,QACd,CAAA;AAAA,QACA,QAAQ,KAAA,EAAgB;AAEtB,UAAA,kBAAA,GAAqB,GAAA;AAIrB,UAAA,IAAI,aAAA,EAAe;AAEjB,YAAA,OAAA,CAAQ,MAAM,KAAK,CAAA;AAAA,UACrB;AAAA,QACF;AAAA,OACD,CAAA;AAGD,MAAA,UAAA,CAAW,OAAO,aAAa,CAAA;AAAA,IACjC,CAAC,CAAA;AAAA,EACH,CAAA;AAGA,EAAA,OAAO,wBAAwB,aAA+C,CAAA;AAEhF;;;;"}
|
|
@@ -8,51 +8,35 @@ import { getMiddlewareName } from './serverBuild.js';
|
|
|
8
8
|
import { markInstrumentationApiUsed } from './serverGlobals.js';
|
|
9
9
|
export { isInstrumentationApiUsed } from './serverGlobals.js';
|
|
10
10
|
|
|
11
|
-
const MIDDLEWARE_COUNTER_KEY = createContextKey(
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Options for creating Sentry server instrumentation.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Creates a Sentry server instrumentation for React Router's instrumentation API.
|
|
19
|
-
* @experimental
|
|
20
|
-
*/
|
|
21
|
-
function createSentryServerInstrumentation(
|
|
22
|
-
options = {},
|
|
23
|
-
) {
|
|
11
|
+
const MIDDLEWARE_COUNTER_KEY = createContextKey("sentry_react_router_middleware_counter");
|
|
12
|
+
function createSentryServerInstrumentation(options = {}) {
|
|
24
13
|
const { captureErrors = true } = options;
|
|
25
|
-
|
|
26
14
|
markInstrumentationApiUsed();
|
|
27
|
-
DEBUG_BUILD && debug.log(
|
|
28
|
-
|
|
15
|
+
DEBUG_BUILD && debug.log("React Router server instrumentation API enabled.");
|
|
29
16
|
return {
|
|
30
17
|
handler(handler) {
|
|
31
18
|
handler.instrument({
|
|
32
19
|
async request(handleRequest, info) {
|
|
33
20
|
const pathname = getPathFromRequest(info.request);
|
|
34
21
|
const activeSpan = getActiveSpan();
|
|
35
|
-
const existingRootSpan = activeSpan ? getRootSpan(activeSpan) :
|
|
36
|
-
|
|
37
|
-
const counterStore = { counters: {} };
|
|
22
|
+
const existingRootSpan = activeSpan ? getRootSpan(activeSpan) : void 0;
|
|
23
|
+
const counterStore = { counters: {} };
|
|
38
24
|
const ctx = context.active().setValue(MIDDLEWARE_COUNTER_KEY, counterStore);
|
|
39
|
-
|
|
40
25
|
await context.with(ctx, async () => {
|
|
41
26
|
if (existingRootSpan) {
|
|
42
27
|
updateSpanName(existingRootSpan, `${info.request.method} ${pathname}`);
|
|
43
28
|
existingRootSpan.setAttributes({
|
|
44
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]:
|
|
45
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]:
|
|
46
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]:
|
|
29
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: "http.server",
|
|
30
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.react_router.instrumentation_api",
|
|
31
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "url"
|
|
47
32
|
});
|
|
48
|
-
|
|
49
33
|
try {
|
|
50
34
|
const result = await handleRequest();
|
|
51
|
-
if (result.status ===
|
|
52
|
-
existingRootSpan.setStatus({ code: SPAN_STATUS_ERROR, message:
|
|
53
|
-
captureInstrumentationError(result, captureErrors,
|
|
54
|
-
|
|
55
|
-
|
|
35
|
+
if (result.status === "error" && result.error instanceof Error) {
|
|
36
|
+
existingRootSpan.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" });
|
|
37
|
+
captureInstrumentationError(result, captureErrors, "react_router.request_handler", {
|
|
38
|
+
"http.method": info.request.method,
|
|
39
|
+
"http.url": pathname
|
|
56
40
|
});
|
|
57
41
|
}
|
|
58
42
|
} finally {
|
|
@@ -64,188 +48,166 @@ function createSentryServerInstrumentation(
|
|
|
64
48
|
name: `${info.request.method} ${pathname}`,
|
|
65
49
|
forceTransaction: true,
|
|
66
50
|
attributes: {
|
|
67
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]:
|
|
68
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]:
|
|
69
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
51
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: "http.server",
|
|
52
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.react_router.instrumentation_api",
|
|
53
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "url",
|
|
54
|
+
"http.request.method": info.request.method,
|
|
55
|
+
"url.path": pathname,
|
|
56
|
+
"url.full": info.request.url
|
|
57
|
+
}
|
|
74
58
|
},
|
|
75
|
-
async span => {
|
|
59
|
+
async (span) => {
|
|
76
60
|
try {
|
|
77
61
|
const result = await handleRequest();
|
|
78
|
-
if (result.status ===
|
|
79
|
-
span.setStatus({ code: SPAN_STATUS_ERROR, message:
|
|
80
|
-
captureInstrumentationError(result, captureErrors,
|
|
81
|
-
|
|
82
|
-
|
|
62
|
+
if (result.status === "error" && result.error instanceof Error) {
|
|
63
|
+
span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" });
|
|
64
|
+
captureInstrumentationError(result, captureErrors, "react_router.request_handler", {
|
|
65
|
+
"http.method": info.request.method,
|
|
66
|
+
"http.url": pathname
|
|
83
67
|
});
|
|
84
68
|
}
|
|
85
69
|
} finally {
|
|
86
70
|
await flushIfServerless();
|
|
87
71
|
}
|
|
88
|
-
}
|
|
72
|
+
}
|
|
89
73
|
);
|
|
90
74
|
}
|
|
91
75
|
});
|
|
92
|
-
}
|
|
76
|
+
}
|
|
93
77
|
});
|
|
94
78
|
},
|
|
95
|
-
|
|
96
79
|
route(route) {
|
|
97
80
|
const routeId = route.id;
|
|
98
|
-
|
|
99
81
|
route.instrument({
|
|
100
82
|
async loader(callLoader, info) {
|
|
101
83
|
const urlPath = getPathFromRequest(info.request);
|
|
102
84
|
const pattern = getPattern(info);
|
|
103
85
|
const routePattern = normalizeRoutePath(pattern) || urlPath;
|
|
104
86
|
updateRootSpanWithRoute(info.request.method, pattern, urlPath);
|
|
105
|
-
|
|
106
87
|
await startSpan(
|
|
107
88
|
{
|
|
108
89
|
name: routePattern,
|
|
109
90
|
attributes: {
|
|
110
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]:
|
|
111
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]:
|
|
112
|
-
}
|
|
91
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: "function.react_router.loader",
|
|
92
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api"
|
|
93
|
+
}
|
|
113
94
|
},
|
|
114
|
-
async span => {
|
|
95
|
+
async (span) => {
|
|
115
96
|
const result = await callLoader();
|
|
116
|
-
if (result.status ===
|
|
117
|
-
span.setStatus({ code: SPAN_STATUS_ERROR, message:
|
|
118
|
-
captureInstrumentationError(result, captureErrors,
|
|
119
|
-
|
|
120
|
-
|
|
97
|
+
if (result.status === "error" && result.error instanceof Error) {
|
|
98
|
+
span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" });
|
|
99
|
+
captureInstrumentationError(result, captureErrors, "react_router.loader", {
|
|
100
|
+
"http.method": info.request.method,
|
|
101
|
+
"http.url": urlPath
|
|
121
102
|
});
|
|
122
103
|
}
|
|
123
|
-
}
|
|
104
|
+
}
|
|
124
105
|
);
|
|
125
106
|
},
|
|
126
|
-
|
|
127
107
|
async action(callAction, info) {
|
|
128
108
|
const urlPath = getPathFromRequest(info.request);
|
|
129
109
|
const pattern = getPattern(info);
|
|
130
110
|
const routePattern = normalizeRoutePath(pattern) || urlPath;
|
|
131
111
|
updateRootSpanWithRoute(info.request.method, pattern, urlPath);
|
|
132
|
-
|
|
133
112
|
await startSpan(
|
|
134
113
|
{
|
|
135
114
|
name: routePattern,
|
|
136
115
|
attributes: {
|
|
137
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]:
|
|
138
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]:
|
|
139
|
-
}
|
|
116
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: "function.react_router.action",
|
|
117
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api"
|
|
118
|
+
}
|
|
140
119
|
},
|
|
141
|
-
async span => {
|
|
120
|
+
async (span) => {
|
|
142
121
|
const result = await callAction();
|
|
143
|
-
if (result.status ===
|
|
144
|
-
span.setStatus({ code: SPAN_STATUS_ERROR, message:
|
|
145
|
-
captureInstrumentationError(result, captureErrors,
|
|
146
|
-
|
|
147
|
-
|
|
122
|
+
if (result.status === "error" && result.error instanceof Error) {
|
|
123
|
+
span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" });
|
|
124
|
+
captureInstrumentationError(result, captureErrors, "react_router.action", {
|
|
125
|
+
"http.method": info.request.method,
|
|
126
|
+
"http.url": urlPath
|
|
148
127
|
});
|
|
149
128
|
}
|
|
150
|
-
}
|
|
129
|
+
}
|
|
151
130
|
);
|
|
152
131
|
},
|
|
153
|
-
|
|
154
132
|
async middleware(callMiddleware, info) {
|
|
155
133
|
const urlPath = getPathFromRequest(info.request);
|
|
156
134
|
const pattern = getPattern(info);
|
|
157
135
|
const routePattern = normalizeRoutePath(pattern) || urlPath;
|
|
158
|
-
|
|
159
136
|
updateRootSpanWithRoute(info.request.method, pattern, urlPath);
|
|
160
|
-
|
|
161
|
-
const counterStore = context.active().getValue(MIDDLEWARE_COUNTER_KEY)
|
|
162
|
-
|
|
163
|
-
;
|
|
137
|
+
const counterStore = context.active().getValue(MIDDLEWARE_COUNTER_KEY);
|
|
164
138
|
let middlewareIndex = 0;
|
|
165
139
|
if (counterStore) {
|
|
166
140
|
middlewareIndex = counterStore.counters[routeId] ?? 0;
|
|
167
141
|
counterStore.counters[routeId] = middlewareIndex + 1;
|
|
168
142
|
}
|
|
169
|
-
|
|
170
143
|
const middlewareName = getMiddlewareName(routeId, middlewareIndex);
|
|
171
|
-
|
|
172
144
|
await startSpan(
|
|
173
145
|
{
|
|
174
146
|
name: `middleware ${middlewareName || routeId}`,
|
|
175
147
|
attributes: {
|
|
176
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]:
|
|
177
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]:
|
|
178
|
-
|
|
148
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: "function.react_router.middleware",
|
|
149
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api",
|
|
150
|
+
"react_router.route.id": routeId,
|
|
179
151
|
[ATTR_HTTP_ROUTE]: routePattern,
|
|
180
|
-
...
|
|
181
|
-
|
|
182
|
-
}
|
|
152
|
+
...middlewareName && { "react_router.middleware.name": middlewareName },
|
|
153
|
+
"react_router.middleware.index": middlewareIndex
|
|
154
|
+
}
|
|
183
155
|
},
|
|
184
|
-
async span => {
|
|
156
|
+
async (span) => {
|
|
185
157
|
const result = await callMiddleware();
|
|
186
|
-
if (result.status ===
|
|
187
|
-
span.setStatus({ code: SPAN_STATUS_ERROR, message:
|
|
188
|
-
captureInstrumentationError(result, captureErrors,
|
|
189
|
-
|
|
190
|
-
|
|
158
|
+
if (result.status === "error" && result.error instanceof Error) {
|
|
159
|
+
span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" });
|
|
160
|
+
captureInstrumentationError(result, captureErrors, "react_router.middleware", {
|
|
161
|
+
"http.method": info.request.method,
|
|
162
|
+
"http.url": urlPath
|
|
191
163
|
});
|
|
192
164
|
}
|
|
193
|
-
}
|
|
165
|
+
}
|
|
194
166
|
);
|
|
195
167
|
},
|
|
196
|
-
|
|
197
168
|
async lazy(callLazy) {
|
|
198
169
|
await startSpan(
|
|
199
170
|
{
|
|
200
|
-
name:
|
|
171
|
+
name: "Lazy Route Load",
|
|
201
172
|
attributes: {
|
|
202
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]:
|
|
203
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]:
|
|
204
|
-
}
|
|
173
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: "function.react_router.lazy",
|
|
174
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.function.react_router.instrumentation_api"
|
|
175
|
+
}
|
|
205
176
|
},
|
|
206
|
-
async span => {
|
|
177
|
+
async (span) => {
|
|
207
178
|
const result = await callLazy();
|
|
208
|
-
if (result.status ===
|
|
209
|
-
span.setStatus({ code: SPAN_STATUS_ERROR, message:
|
|
210
|
-
captureInstrumentationError(result, captureErrors,
|
|
179
|
+
if (result.status === "error" && result.error instanceof Error) {
|
|
180
|
+
span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" });
|
|
181
|
+
captureInstrumentationError(result, captureErrors, "react_router.lazy", {});
|
|
211
182
|
}
|
|
212
|
-
}
|
|
183
|
+
}
|
|
213
184
|
);
|
|
214
|
-
}
|
|
185
|
+
}
|
|
215
186
|
});
|
|
216
|
-
}
|
|
187
|
+
}
|
|
217
188
|
};
|
|
218
189
|
}
|
|
219
|
-
|
|
220
190
|
function updateRootSpanWithRoute(method, pattern, urlPath) {
|
|
221
191
|
const activeSpan = getActiveSpan();
|
|
222
192
|
if (!activeSpan) return;
|
|
223
193
|
const rootSpan = getRootSpan(activeSpan);
|
|
224
194
|
if (!rootSpan) return;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (!urlPath || urlPath === '<unknown>') {
|
|
228
|
-
DEBUG_BUILD && debug.warn('Cannot update span with invalid URL path:', urlPath);
|
|
195
|
+
if (!urlPath || urlPath === "<unknown>") {
|
|
196
|
+
DEBUG_BUILD && debug.warn("Cannot update span with invalid URL path:", urlPath);
|
|
229
197
|
return;
|
|
230
198
|
}
|
|
231
|
-
|
|
232
199
|
const hasPattern = !!pattern;
|
|
233
200
|
const routeName = hasPattern ? normalizeRoutePath(pattern) || urlPath : urlPath;
|
|
234
|
-
|
|
235
201
|
const rpcMetadata = getRPCMetadata(context.active());
|
|
236
202
|
if (rpcMetadata?.type === RPCType.HTTP) {
|
|
237
203
|
rpcMetadata.route = routeName;
|
|
238
204
|
}
|
|
239
|
-
|
|
240
205
|
const transactionName = `${method} ${routeName}`;
|
|
241
206
|
updateSpanName(rootSpan, transactionName);
|
|
242
207
|
rootSpan.setAttributes({
|
|
243
208
|
[ATTR_HTTP_ROUTE]: routeName,
|
|
244
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: hasPattern ?
|
|
209
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: hasPattern ? "route" : "url"
|
|
245
210
|
});
|
|
246
|
-
|
|
247
|
-
// Also update the scope's transaction name so errors captured during this request
|
|
248
|
-
// have the correct transaction name (not the initial placeholder like "GET *")
|
|
249
211
|
getCurrentScope().setTransactionName(transactionName);
|
|
250
212
|
}
|
|
251
213
|
|