@sentry/react-router 9.13.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.
@@ -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 sentryHandleRequest = require('./server/sentryHandleRequest.js');
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.sentryHandleRequest = sentryHandleRequest.sentryHandleRequest;
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 sentryHandleRequest(originalHandle) {
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
- //# sourceMappingURL=sentryHandleRequest.js.map
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;;;;;;"}
@@ -1,6 +1,7 @@
1
1
  export * from '@sentry/node';
2
2
  export { init } from './server/sdk.js';
3
- export { sentryHandleRequest } from './server/sentryHandleRequest.js';
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":";;;;;"}
@@ -1 +1 @@
1
- {"type":"module","version":"9.13.0"}
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 sentryHandleRequest(originalHandle) {
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
- export { sentryHandleRequest };
46
- //# sourceMappingURL=sentryHandleRequest.js.map
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;;;;"}
@@ -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 './sentryHandleRequest';
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;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/react-router",
3
- "version": "9.13.0",
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.13.0",
40
+ "@sentry/browser": "9.14.0",
41
41
  "@sentry/cli": "^2.43.0",
42
- "@sentry/core": "9.13.0",
43
- "@sentry/node": "9.13.0",
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"}