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