@sentry/tanstackstart-react 10.63.0 → 10.65.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/server/middleware.js +1 -1
- package/build/cjs/server/middleware.js.map +1 -1
- package/build/cjs/vite/autoInstrumentMiddleware.js +3 -1
- package/build/cjs/vite/autoInstrumentMiddleware.js.map +1 -1
- package/build/cjs/vite/routePatterns.js +2 -1
- package/build/cjs/vite/routePatterns.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/server/middleware.js +2 -2
- package/build/esm/server/middleware.js.map +1 -1
- package/build/esm/vite/autoInstrumentMiddleware.js +3 -1
- package/build/esm/vite/autoInstrumentMiddleware.js.map +1 -1
- package/build/esm/vite/routePatterns.js +2 -1
- package/build/esm/vite/routePatterns.js.map +1 -1
- package/build/types/vite/autoInstrumentMiddleware.d.ts.map +1 -1
- package/build/types/vite/routePatterns.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -31,7 +31,7 @@ function wrapMiddlewareWithSentry(middleware, options) {
|
|
|
31
31
|
return node.startSpanManual(utils.getMiddlewareSpanOptions(options.name), async (span) => {
|
|
32
32
|
const nextState = { called: false };
|
|
33
33
|
const middlewareArgs = argsServer[0];
|
|
34
|
-
if (middlewareArgs && typeof middlewareArgs
|
|
34
|
+
if (core.isObjectLike(middlewareArgs) && typeof middlewareArgs.next === "function") {
|
|
35
35
|
middlewareArgs.next = getNextProxy(middlewareArgs.next, span, prevSpan, nextState);
|
|
36
36
|
}
|
|
37
37
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sources":["../../../src/server/middleware.ts"],"sourcesContent":["import { addNonEnumerableProperty } from '@sentry/core';\nimport type { Span } from '@sentry/node';\nimport { getActiveSpan, startSpanManual, withActiveSpan } from '@sentry/node';\nimport type { MiddlewareWrapperOptions, TanStackMiddlewareBase } from '../common/types';\nimport { getMiddlewareSpanOptions } from './utils';\n\nconst SENTRY_WRAPPED = '__SENTRY_WRAPPED__';\nexport const SENTRY_INTERNAL = '__SENTRY_INTERNAL__';\n\n/**\n * Creates a proxy for the next function that ends the current span and restores the parent span.\n * This ensures that subsequent middleware spans are children of the root span, not nested children.\n */\nfunction getNextProxy<T extends (...args: unknown[]) => unknown>(\n next: T,\n span: Span,\n prevSpan: Span | undefined,\n nextState: { called: boolean },\n): T {\n return new Proxy(next, {\n apply: (originalNext, thisArgNext, argsNext) => {\n nextState.called = true;\n span.end();\n\n if (prevSpan) {\n return withActiveSpan(prevSpan, () => {\n return Reflect.apply(originalNext, thisArgNext, argsNext);\n });\n }\n\n return Reflect.apply(originalNext, thisArgNext, argsNext);\n },\n });\n}\n\n/**\n * Wraps a TanStack Start middleware with Sentry instrumentation to create spans.\n */\nfunction wrapMiddlewareWithSentry<T extends TanStackMiddlewareBase>(\n middleware: T,\n options: MiddlewareWrapperOptions,\n): T {\n if (\n (middleware as TanStackMiddlewareBase & { [SENTRY_WRAPPED]?: boolean })[SENTRY_WRAPPED] ||\n (middleware as TanStackMiddlewareBase & { [SENTRY_INTERNAL]?: boolean })[SENTRY_INTERNAL]\n ) {\n // already instrumented or internal Sentry middleware\n return middleware;\n }\n\n // instrument server middleware\n if (middleware.options?.server) {\n middleware.options.server = new Proxy(middleware.options.server, {\n apply: (originalServer, thisArgServer, argsServer) => {\n const prevSpan = getActiveSpan();\n\n return startSpanManual(getMiddlewareSpanOptions(options.name), async (span: Span) => {\n const nextState = { called: false };\n\n // The server function receives { next, context, request } as first argument\n // Users call next() inside their middleware to move down the middleware chain. We proxy next() to end the span when it is called.\n const middlewareArgs = argsServer[0] as { next?: (...args: unknown[]) => unknown } | undefined;\n if (middlewareArgs && typeof middlewareArgs
|
|
1
|
+
{"version":3,"file":"middleware.js","sources":["../../../src/server/middleware.ts"],"sourcesContent":["import { isObjectLike, addNonEnumerableProperty } from '@sentry/core';\nimport type { Span } from '@sentry/node';\nimport { getActiveSpan, startSpanManual, withActiveSpan } from '@sentry/node';\nimport type { MiddlewareWrapperOptions, TanStackMiddlewareBase } from '../common/types';\nimport { getMiddlewareSpanOptions } from './utils';\n\nconst SENTRY_WRAPPED = '__SENTRY_WRAPPED__';\nexport const SENTRY_INTERNAL = '__SENTRY_INTERNAL__';\n\n/**\n * Creates a proxy for the next function that ends the current span and restores the parent span.\n * This ensures that subsequent middleware spans are children of the root span, not nested children.\n */\nfunction getNextProxy<T extends (...args: unknown[]) => unknown>(\n next: T,\n span: Span,\n prevSpan: Span | undefined,\n nextState: { called: boolean },\n): T {\n return new Proxy(next, {\n apply: (originalNext, thisArgNext, argsNext) => {\n nextState.called = true;\n span.end();\n\n if (prevSpan) {\n return withActiveSpan(prevSpan, () => {\n return Reflect.apply(originalNext, thisArgNext, argsNext);\n });\n }\n\n return Reflect.apply(originalNext, thisArgNext, argsNext);\n },\n });\n}\n\n/**\n * Wraps a TanStack Start middleware with Sentry instrumentation to create spans.\n */\nfunction wrapMiddlewareWithSentry<T extends TanStackMiddlewareBase>(\n middleware: T,\n options: MiddlewareWrapperOptions,\n): T {\n if (\n (middleware as TanStackMiddlewareBase & { [SENTRY_WRAPPED]?: boolean })[SENTRY_WRAPPED] ||\n (middleware as TanStackMiddlewareBase & { [SENTRY_INTERNAL]?: boolean })[SENTRY_INTERNAL]\n ) {\n // already instrumented or internal Sentry middleware\n return middleware;\n }\n\n // instrument server middleware\n if (middleware.options?.server) {\n middleware.options.server = new Proxy(middleware.options.server, {\n apply: (originalServer, thisArgServer, argsServer) => {\n const prevSpan = getActiveSpan();\n\n return startSpanManual(getMiddlewareSpanOptions(options.name), async (span: Span) => {\n const nextState = { called: false };\n\n // The server function receives { next, context, request } as first argument\n // Users call next() inside their middleware to move down the middleware chain. We proxy next() to end the span when it is called.\n const middlewareArgs = argsServer[0] as { next?: (...args: unknown[]) => unknown } | undefined;\n if (isObjectLike(middlewareArgs) && typeof middlewareArgs.next === 'function') {\n middlewareArgs.next = getNextProxy(middlewareArgs.next, span, prevSpan, nextState);\n }\n\n try {\n const result = await originalServer.apply(thisArgServer, argsServer);\n\n // End span here if next() wasn't called, else we already ended it in next()\n if (!nextState.called) {\n span.end();\n }\n\n return result;\n } catch (e) {\n span.end();\n throw e;\n }\n });\n },\n });\n\n // mark as instrumented\n addNonEnumerableProperty(middleware as unknown as Record<string, unknown>, SENTRY_WRAPPED, true);\n }\n\n return middleware;\n}\n\n/**\n * Wraps multiple TanStack Start middlewares with Sentry instrumentation.\n * Object keys are used as span names to avoid users having to specify this manually.\n *\n * @example\n * ```ts\n * import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\n *\n * const wrappedMiddlewares = wrapMiddlewaresWithSentry({\n * authMiddleware,\n * loggingMiddleware,\n * });\n *\n * createServerFn().middleware(wrappedMiddlewares)\n * ```\n *\n * @param middlewares - An object containing middlewares\n * @returns An array of wrapped middlewares\n */\nexport function wrapMiddlewaresWithSentry<T extends TanStackMiddlewareBase>(middlewares: Record<string, T>): T[] {\n return Object.entries(middlewares).map(([name, middleware]) => {\n return wrapMiddlewareWithSentry(middleware, { name });\n });\n}\n"],"names":["withActiveSpan","getActiveSpan","startSpanManual","getMiddlewareSpanOptions","isObjectLike","addNonEnumerableProperty"],"mappings":";;;;;;AAMA,MAAM,cAAA,GAAiB,oBAAA;AAChB,MAAM,eAAA,GAAkB;AAM/B,SAAS,YAAA,CACP,IAAA,EACA,IAAA,EACA,QAAA,EACA,SAAA,EACG;AACH,EAAA,OAAO,IAAI,MAAM,IAAA,EAAM;AAAA,IACrB,KAAA,EAAO,CAAC,YAAA,EAAc,WAAA,EAAa,QAAA,KAAa;AAC9C,MAAA,SAAA,CAAU,MAAA,GAAS,IAAA;AACnB,MAAA,IAAA,CAAK,GAAA,EAAI;AAET,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,OAAOA,mBAAA,CAAe,UAAU,MAAM;AACpC,UAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,YAAA,EAAc,WAAA,EAAa,QAAQ,CAAA;AAAA,QAC1D,CAAC,CAAA;AAAA,MACH;AAEA,MAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,YAAA,EAAc,WAAA,EAAa,QAAQ,CAAA;AAAA,IAC1D;AAAA,GACD,CAAA;AACH;AAKA,SAAS,wBAAA,CACP,YACA,OAAA,EACG;AACH,EAAA,IACG,UAAA,CAAuE,cAAc,CAAA,IACrF,UAAA,CAAwE,eAAe,CAAA,EACxF;AAEA,IAAA,OAAO,UAAA;AAAA,EACT;AAGA,EAAA,IAAI,UAAA,CAAW,SAAS,MAAA,EAAQ;AAC9B,IAAA,UAAA,CAAW,QAAQ,MAAA,GAAS,IAAI,KAAA,CAAM,UAAA,CAAW,QAAQ,MAAA,EAAQ;AAAA,MAC/D,KAAA,EAAO,CAAC,cAAA,EAAgB,aAAA,EAAe,UAAA,KAAe;AACpD,QAAA,MAAM,WAAWC,kBAAA,EAAc;AAE/B,QAAA,OAAOC,qBAAgBC,8BAAA,CAAyB,OAAA,CAAQ,IAAI,CAAA,EAAG,OAAO,IAAA,KAAe;AACnF,UAAA,MAAM,SAAA,GAAY,EAAE,MAAA,EAAQ,KAAA,EAAM;AAIlC,UAAA,MAAM,cAAA,GAAiB,WAAW,CAAC,CAAA;AACnC,UAAA,IAAIC,kBAAa,cAAc,CAAA,IAAK,OAAO,cAAA,CAAe,SAAS,UAAA,EAAY;AAC7E,YAAA,cAAA,CAAe,OAAO,YAAA,CAAa,cAAA,CAAe,IAAA,EAAM,IAAA,EAAM,UAAU,SAAS,CAAA;AAAA,UACnF;AAEA,UAAA,IAAI;AACF,YAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,KAAA,CAAM,eAAe,UAAU,CAAA;AAGnE,YAAA,IAAI,CAAC,UAAU,MAAA,EAAQ;AACrB,cAAA,IAAA,CAAK,GAAA,EAAI;AAAA,YACX;AAEA,YAAA,OAAO,MAAA;AAAA,UACT,SAAS,CAAA,EAAG;AACV,YAAA,IAAA,CAAK,GAAA,EAAI;AACT,YAAA,MAAM,CAAA;AAAA,UACR;AAAA,QACF,CAAC,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAGD,IAAAC,6BAAA,CAAyB,UAAA,EAAkD,gBAAgB,IAAI,CAAA;AAAA,EACjG;AAEA,EAAA,OAAO,UAAA;AACT;AAqBO,SAAS,0BAA4D,WAAA,EAAqC;AAC/G,EAAA,OAAO,MAAA,CAAO,QAAQ,WAAW,CAAA,CAAE,IAAI,CAAC,CAAC,IAAA,EAAM,UAAU,CAAA,KAAM;AAC7D,IAAA,OAAO,wBAAA,CAAyB,UAAA,EAAY,EAAE,IAAA,EAAM,CAAA;AAAA,EACtD,CAAC,CAAA;AACH;;;;;"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
+
const core = require('@sentry/core');
|
|
4
|
+
|
|
3
5
|
function wrapMiddlewareArrays(code, id, debug, regex) {
|
|
4
6
|
const skipped = [];
|
|
5
7
|
let didWrap = false;
|
|
@@ -92,7 +94,7 @@ function arrayToObjectShorthand(contents) {
|
|
|
92
94
|
if (!allIdentifiers || items.length === 0) {
|
|
93
95
|
return null;
|
|
94
96
|
}
|
|
95
|
-
const uniqueItems =
|
|
97
|
+
const uniqueItems = core.uniq(items);
|
|
96
98
|
return `{ ${uniqueItems.join(", ")} }`;
|
|
97
99
|
}
|
|
98
100
|
function addSentryImport(code) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoInstrumentMiddleware.js","sources":["../../../src/vite/autoInstrumentMiddleware.ts"],"sourcesContent":["import type { Plugin } from 'vite';\n\ntype AutoInstrumentMiddlewareOptions = {\n enabled?: boolean;\n debug?: boolean;\n};\n\ntype WrapResult = {\n code: string;\n didWrap: boolean;\n skipped: string[];\n};\n\ntype FileTransformState = {\n code: string;\n needsImport: boolean;\n skippedMiddlewares: string[];\n};\n\n/**\n * Core function that wraps middleware arrays matching the given regex.\n */\nfunction wrapMiddlewareArrays(code: string, id: string, debug: boolean, regex: RegExp): WrapResult {\n const skipped: string[] = [];\n let didWrap = false;\n\n const transformed = code.replace(regex, (match: string, key: string, contents: string) => {\n const objContents = arrayToObjectShorthand(contents);\n if (objContents) {\n didWrap = true;\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(`[Sentry] Auto-wrapping ${key} in ${id}`);\n }\n // Handle method call syntax like `.middleware([...])` vs object property syntax like `middleware: [...]`\n if (key.endsWith('(')) {\n return `${key}wrapMiddlewaresWithSentry(${objContents}))`;\n }\n return `${key}: wrapMiddlewaresWithSentry(${objContents})`;\n }\n // Track middlewares that couldn't be auto-wrapped\n // Skip if we matched whitespace only\n if (contents.trim()) {\n skipped.push(key);\n }\n return match;\n });\n\n return { code: transformed, didWrap, skipped };\n}\n\n/**\n * Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.\n */\nexport function wrapGlobalMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(requestMiddleware|functionMiddleware)\\s*:\\s*\\[([^\\]]*)\\]/g);\n}\n\n/**\n * Wraps route middleware arrays in createFileRoute() files.\n */\nexport function wrapRouteMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(middleware)\\s*:\\s*\\[([^\\]]*)\\]/g);\n}\n\n/**\n * Wraps middleware arrays in createServerFn().middleware([...]) calls.\n */\nexport function wrapServerFnMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(\\.middleware\\s*\\()\\s*\\[([^\\]]*)\\]\\s*\\)/g);\n}\n\n/**\n * Applies a wrap function to the current state and returns the updated state.\n */\nfunction applyWrap(\n state: FileTransformState,\n wrapFn: (code: string, id: string, debug: boolean) => WrapResult,\n id: string,\n debug: boolean,\n): FileTransformState {\n const result = wrapFn(state.code, id, debug);\n return {\n code: result.code,\n needsImport: state.needsImport || result.didWrap,\n skippedMiddlewares: [...state.skippedMiddlewares, ...result.skipped],\n };\n}\n\n/**\n * A Vite plugin that automatically instruments TanStack Start middlewares:\n * - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`\n * - `middleware` arrays in `createFileRoute()` route definitions\n */\nexport function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddlewareOptions = {}): Plugin {\n const { enabled = true, debug = false } = options;\n\n return {\n name: 'sentry-tanstack-middleware-auto-instrument',\n enforce: 'pre',\n\n transform(code, id) {\n if (!enabled) {\n return null;\n }\n\n // Skip if not a TS/JS file\n if (!/\\.(ts|tsx|js|jsx|mjs|mts)$/.test(id)) {\n return null;\n }\n\n // Detect file types that should be instrumented\n const isStartFile = id.includes('start') && code.includes('createStart(');\n const isRouteFile = code.includes('createFileRoute(') && /middleware\\s*:\\s*\\[/.test(code);\n const isServerFnFile = code.includes('createServerFn') && /\\.middleware\\s*\\(\\s*\\[/.test(code);\n\n if (!isStartFile && !isRouteFile && !isServerFnFile) {\n return null;\n }\n\n // Skip if the user already did some manual wrapping\n if (code.includes('wrapMiddlewaresWithSentry')) {\n return null;\n }\n\n let fileTransformState: FileTransformState = {\n code,\n needsImport: false,\n skippedMiddlewares: [],\n };\n\n // Wrap middlewares\n if (isStartFile) {\n fileTransformState = applyWrap(fileTransformState, wrapGlobalMiddleware, id, debug);\n }\n if (isRouteFile) {\n fileTransformState = applyWrap(fileTransformState, wrapRouteMiddleware, id, debug);\n }\n if (isServerFnFile) {\n fileTransformState = applyWrap(fileTransformState, wrapServerFnMiddleware, id, debug);\n }\n\n // Warn about middlewares that couldn't be auto-wrapped\n if (fileTransformState.skippedMiddlewares.length > 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] Could not auto-instrument ${fileTransformState.skippedMiddlewares.join(' and ')} in ${id}. ` +\n 'To instrument these middlewares, use wrapMiddlewaresWithSentry() manually. ',\n );\n }\n\n // We didn't wrap any middlewares, so we don't need to import the wrapMiddlewaresWithSentry function\n if (!fileTransformState.needsImport) {\n return null;\n }\n\n return { code: addSentryImport(fileTransformState.code), map: null };\n },\n };\n}\n\n/**\n * Convert array contents to object shorthand syntax.\n * e.g., \"foo, bar, baz\" → \"{ foo, bar, baz }\"\n *\n * Returns null if contents contain non-identifier expressions (function calls, etc.)\n * which cannot be converted to object shorthand.\n */\nexport function arrayToObjectShorthand(contents: string): string | null {\n const items = contents\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n\n // Only convert if all items are valid identifiers (no complex expressions)\n const allIdentifiers = items.every(item => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(item));\n if (!allIdentifiers || items.length === 0) {\n return null;\n }\n\n // Deduplicate to avoid invalid syntax like { foo, foo }\n const uniqueItems = [...new Set(items)];\n\n return `{ ${uniqueItems.join(', ')} }`;\n}\n\n/**\n * Adds the wrapMiddlewaresWithSentry import to the code.\n * Handles 'use client' and 'use server' directives by inserting the import after them.\n */\nexport function addSentryImport(code: string): string {\n const sentryImport = \"import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\\n\";\n\n // Don't add the import if it already exists\n if (code.includes(sentryImport.trimEnd())) {\n return code;\n }\n\n // Check for 'use server' or 'use client' directives, these need to be before any imports\n const directiveMatch = code.match(/^(['\"])use (client|server)\\1;?\\s*\\n?/);\n\n if (!directiveMatch) {\n return sentryImport + code;\n }\n\n const directive = directiveMatch[0];\n return directive + sentryImport + code.slice(directive.length);\n}\n"],"names":[],"mappings":";;AAsBA,SAAS,oBAAA,CAAqB,IAAA,EAAc,EAAA,EAAY,KAAA,EAAgB,KAAA,EAA2B;AACjG,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,OAAA,GAAU,KAAA;AAEd,EAAA,MAAM,cAAc,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAC,KAAA,EAAe,KAAa,QAAA,KAAqB;AACxF,IAAA,MAAM,WAAA,GAAc,uBAAuB,QAAQ,CAAA;AACnD,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,IAAI,KAAA,EAAO;AAET,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,uBAAA,EAA0B,GAAG,CAAA,IAAA,EAAO,EAAE,CAAA,CAAE,CAAA;AAAA,MACtD;AAEA,MAAA,IAAI,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,EAAG;AACrB,QAAA,OAAO,CAAA,EAAG,GAAG,CAAA,0BAAA,EAA6B,WAAW,CAAA,EAAA,CAAA;AAAA,MACvD;AACA,MAAA,OAAO,CAAA,EAAG,GAAG,CAAA,4BAAA,EAA+B,WAAW,CAAA,CAAA,CAAA;AAAA,IACzD;AAGA,IAAA,IAAI,QAAA,CAAS,MAAK,EAAG;AACnB,MAAA,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,IAClB;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAC,CAAA;AAED,EAAA,OAAO,EAAE,IAAA,EAAM,WAAA,EAAa,OAAA,EAAS,OAAA,EAAQ;AAC/C;AAKO,SAAS,oBAAA,CAAqB,IAAA,EAAc,EAAA,EAAY,KAAA,EAA4B;AACzF,EAAA,OAAO,oBAAA,CAAqB,IAAA,EAAM,EAAA,EAAI,KAAA,EAAO,4DAA4D,CAAA;AAC3G;AAKO,SAAS,mBAAA,CAAoB,IAAA,EAAc,EAAA,EAAY,KAAA,EAA4B;AACxF,EAAA,OAAO,oBAAA,CAAqB,IAAA,EAAM,EAAA,EAAI,KAAA,EAAO,kCAAkC,CAAA;AACjF;AAKO,SAAS,sBAAA,CAAuB,IAAA,EAAc,EAAA,EAAY,KAAA,EAA4B;AAC3F,EAAA,OAAO,oBAAA,CAAqB,IAAA,EAAM,EAAA,EAAI,KAAA,EAAO,0CAA0C,CAAA;AACzF;AAKA,SAAS,SAAA,CACP,KAAA,EACA,MAAA,EACA,EAAA,EACA,KAAA,EACoB;AACpB,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,KAAA,CAAM,IAAA,EAAM,IAAI,KAAK,CAAA;AAC3C,EAAA,OAAO;AAAA,IACL,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,WAAA,EAAa,KAAA,CAAM,WAAA,IAAe,MAAA,CAAO,OAAA;AAAA,IACzC,oBAAoB,CAAC,GAAG,MAAM,kBAAA,EAAoB,GAAG,OAAO,OAAO;AAAA,GACrE;AACF;AAOO,SAAS,kCAAA,CAAmC,OAAA,GAA2C,EAAC,EAAW;AACxG,EAAA,MAAM,EAAE,OAAA,GAAU,IAAA,EAAM,KAAA,GAAQ,OAAM,GAAI,OAAA;AAE1C,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,4CAAA;AAAA,IACN,OAAA,EAAS,KAAA;AAAA,IAET,SAAA,CAAU,MAAM,EAAA,EAAI;AAClB,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,IAAI,CAAC,4BAAA,CAA6B,IAAA,CAAK,EAAE,CAAA,EAAG;AAC1C,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,MAAM,cAAc,EAAA,CAAG,QAAA,CAAS,OAAO,CAAA,IAAK,IAAA,CAAK,SAAS,cAAc,CAAA;AACxE,MAAA,MAAM,cAAc,IAAA,CAAK,QAAA,CAAS,kBAAkB,CAAA,IAAK,qBAAA,CAAsB,KAAK,IAAI,CAAA;AACxF,MAAA,MAAM,iBAAiB,IAAA,CAAK,QAAA,CAAS,gBAAgB,CAAA,IAAK,wBAAA,CAAyB,KAAK,IAAI,CAAA;AAE5F,MAAA,IAAI,CAAC,WAAA,IAAe,CAAC,WAAA,IAAe,CAAC,cAAA,EAAgB;AACnD,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,2BAA2B,CAAA,EAAG;AAC9C,QAAA,OAAO,IAAA;AAAA,MACT;AAEA,MAAA,IAAI,kBAAA,GAAyC;AAAA,QAC3C,IAAA;AAAA,QACA,WAAA,EAAa,KAAA;AAAA,QACb,oBAAoB;AAAC,OACvB;AAGA,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,kBAAA,GAAqB,SAAA,CAAU,kBAAA,EAAoB,oBAAA,EAAsB,EAAA,EAAI,KAAK,CAAA;AAAA,MACpF;AACA,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,kBAAA,GAAqB,SAAA,CAAU,kBAAA,EAAoB,mBAAA,EAAqB,EAAA,EAAI,KAAK,CAAA;AAAA,MACnF;AACA,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,kBAAA,GAAqB,SAAA,CAAU,kBAAA,EAAoB,sBAAA,EAAwB,EAAA,EAAI,KAAK,CAAA;AAAA,MACtF;AAGA,MAAA,IAAI,kBAAA,CAAmB,kBAAA,CAAmB,MAAA,GAAS,CAAA,EAAG;AAEpD,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,sCAAsC,kBAAA,CAAmB,kBAAA,CAAmB,KAAK,OAAO,CAAC,OAAO,EAAE,CAAA,6EAAA;AAAA,SAEpG;AAAA,MACF;AAGA,MAAA,IAAI,CAAC,mBAAmB,WAAA,EAAa;AACnC,QAAA,OAAO,IAAA;AAAA,MACT;AAEA,MAAA,OAAO,EAAE,IAAA,EAAM,eAAA,CAAgB,mBAAmB,IAAI,CAAA,EAAG,KAAK,IAAA,EAAK;AAAA,IACrE;AAAA,GACF;AACF;AASO,SAAS,uBAAuB,QAAA,EAAiC;AACtE,EAAA,MAAM,KAAA,GAAQ,QAAA,CACX,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,IAAA,EAAM,CAAA,CACjB,MAAA,CAAO,OAAO,CAAA;AAGjB,EAAA,MAAM,iBAAiB,KAAA,CAAM,KAAA,CAAM,UAAQ,4BAAA,CAA6B,IAAA,CAAK,IAAI,CAAC,CAAA;AAClF,EAAA,IAAI,CAAC,cAAA,IAAkB,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AACzC,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,MAAM,cAAc,CAAC,GAAG,IAAI,GAAA,CAAI,KAAK,CAAC,CAAA;AAEtC,EAAA,OAAO,CAAA,EAAA,EAAK,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA,EAAA,CAAA;AACpC;AAMO,SAAS,gBAAgB,IAAA,EAAsB;AACpD,EAAA,MAAM,YAAA,GAAe,4EAAA;AAGrB,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,YAAA,CAAa,OAAA,EAAS,CAAA,EAAG;AACzC,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,KAAA,CAAM,sCAAsC,CAAA;AAExE,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,OAAO,YAAA,GAAe,IAAA;AAAA,EACxB;AAEA,EAAA,MAAM,SAAA,GAAY,eAAe,CAAC,CAAA;AAClC,EAAA,OAAO,SAAA,GAAY,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,UAAU,MAAM,CAAA;AAC/D;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"autoInstrumentMiddleware.js","sources":["../../../src/vite/autoInstrumentMiddleware.ts"],"sourcesContent":["import { uniq } from '@sentry/core';\nimport type { Plugin } from 'vite';\n\ntype AutoInstrumentMiddlewareOptions = {\n enabled?: boolean;\n debug?: boolean;\n};\n\ntype WrapResult = {\n code: string;\n didWrap: boolean;\n skipped: string[];\n};\n\ntype FileTransformState = {\n code: string;\n needsImport: boolean;\n skippedMiddlewares: string[];\n};\n\n/**\n * Core function that wraps middleware arrays matching the given regex.\n */\nfunction wrapMiddlewareArrays(code: string, id: string, debug: boolean, regex: RegExp): WrapResult {\n const skipped: string[] = [];\n let didWrap = false;\n\n const transformed = code.replace(regex, (match: string, key: string, contents: string) => {\n const objContents = arrayToObjectShorthand(contents);\n if (objContents) {\n didWrap = true;\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(`[Sentry] Auto-wrapping ${key} in ${id}`);\n }\n // Handle method call syntax like `.middleware([...])` vs object property syntax like `middleware: [...]`\n if (key.endsWith('(')) {\n return `${key}wrapMiddlewaresWithSentry(${objContents}))`;\n }\n return `${key}: wrapMiddlewaresWithSentry(${objContents})`;\n }\n // Track middlewares that couldn't be auto-wrapped\n // Skip if we matched whitespace only\n if (contents.trim()) {\n skipped.push(key);\n }\n return match;\n });\n\n return { code: transformed, didWrap, skipped };\n}\n\n/**\n * Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.\n */\nexport function wrapGlobalMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(requestMiddleware|functionMiddleware)\\s*:\\s*\\[([^\\]]*)\\]/g);\n}\n\n/**\n * Wraps route middleware arrays in createFileRoute() files.\n */\nexport function wrapRouteMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(middleware)\\s*:\\s*\\[([^\\]]*)\\]/g);\n}\n\n/**\n * Wraps middleware arrays in createServerFn().middleware([...]) calls.\n */\nexport function wrapServerFnMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(\\.middleware\\s*\\()\\s*\\[([^\\]]*)\\]\\s*\\)/g);\n}\n\n/**\n * Applies a wrap function to the current state and returns the updated state.\n */\nfunction applyWrap(\n state: FileTransformState,\n wrapFn: (code: string, id: string, debug: boolean) => WrapResult,\n id: string,\n debug: boolean,\n): FileTransformState {\n const result = wrapFn(state.code, id, debug);\n return {\n code: result.code,\n needsImport: state.needsImport || result.didWrap,\n skippedMiddlewares: [...state.skippedMiddlewares, ...result.skipped],\n };\n}\n\n/**\n * A Vite plugin that automatically instruments TanStack Start middlewares:\n * - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`\n * - `middleware` arrays in `createFileRoute()` route definitions\n */\nexport function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddlewareOptions = {}): Plugin {\n const { enabled = true, debug = false } = options;\n\n return {\n name: 'sentry-tanstack-middleware-auto-instrument',\n enforce: 'pre',\n\n transform(code, id) {\n if (!enabled) {\n return null;\n }\n\n // Skip if not a TS/JS file\n if (!/\\.(ts|tsx|js|jsx|mjs|mts)$/.test(id)) {\n return null;\n }\n\n // Detect file types that should be instrumented\n const isStartFile = id.includes('start') && code.includes('createStart(');\n const isRouteFile = code.includes('createFileRoute(') && /middleware\\s*:\\s*\\[/.test(code);\n const isServerFnFile = code.includes('createServerFn') && /\\.middleware\\s*\\(\\s*\\[/.test(code);\n\n if (!isStartFile && !isRouteFile && !isServerFnFile) {\n return null;\n }\n\n // Skip if the user already did some manual wrapping\n if (code.includes('wrapMiddlewaresWithSentry')) {\n return null;\n }\n\n let fileTransformState: FileTransformState = {\n code,\n needsImport: false,\n skippedMiddlewares: [],\n };\n\n // Wrap middlewares\n if (isStartFile) {\n fileTransformState = applyWrap(fileTransformState, wrapGlobalMiddleware, id, debug);\n }\n if (isRouteFile) {\n fileTransformState = applyWrap(fileTransformState, wrapRouteMiddleware, id, debug);\n }\n if (isServerFnFile) {\n fileTransformState = applyWrap(fileTransformState, wrapServerFnMiddleware, id, debug);\n }\n\n // Warn about middlewares that couldn't be auto-wrapped\n if (fileTransformState.skippedMiddlewares.length > 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] Could not auto-instrument ${fileTransformState.skippedMiddlewares.join(' and ')} in ${id}. ` +\n 'To instrument these middlewares, use wrapMiddlewaresWithSentry() manually. ',\n );\n }\n\n // We didn't wrap any middlewares, so we don't need to import the wrapMiddlewaresWithSentry function\n if (!fileTransformState.needsImport) {\n return null;\n }\n\n return { code: addSentryImport(fileTransformState.code), map: null };\n },\n };\n}\n\n/**\n * Convert array contents to object shorthand syntax.\n * e.g., \"foo, bar, baz\" → \"{ foo, bar, baz }\"\n *\n * Returns null if contents contain non-identifier expressions (function calls, etc.)\n * which cannot be converted to object shorthand.\n */\nexport function arrayToObjectShorthand(contents: string): string | null {\n const items = contents\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n\n // Only convert if all items are valid identifiers (no complex expressions)\n const allIdentifiers = items.every(item => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(item));\n if (!allIdentifiers || items.length === 0) {\n return null;\n }\n\n // Deduplicate to avoid invalid syntax like { foo, foo }\n const uniqueItems = uniq(items);\n\n return `{ ${uniqueItems.join(', ')} }`;\n}\n\n/**\n * Adds the wrapMiddlewaresWithSentry import to the code.\n * Handles 'use client' and 'use server' directives by inserting the import after them.\n */\nexport function addSentryImport(code: string): string {\n const sentryImport = \"import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\\n\";\n\n // Don't add the import if it already exists\n if (code.includes(sentryImport.trimEnd())) {\n return code;\n }\n\n // Check for 'use server' or 'use client' directives, these need to be before any imports\n const directiveMatch = code.match(/^(['\"])use (client|server)\\1;?\\s*\\n?/);\n\n if (!directiveMatch) {\n return sentryImport + code;\n }\n\n const directive = directiveMatch[0];\n return directive + sentryImport + code.slice(directive.length);\n}\n"],"names":["uniq"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
+
const core = require('@sentry/core');
|
|
5
6
|
|
|
6
7
|
function makeRoutePatternPlugin() {
|
|
7
8
|
let resolvedRoot = "";
|
|
@@ -43,7 +44,7 @@ function extractRoutePatterns(content) {
|
|
|
43
44
|
patterns.push(match[1]);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
|
-
return
|
|
47
|
+
return core.uniq(patterns).sort((a, b) => {
|
|
47
48
|
const aSegments = a.split("/");
|
|
48
49
|
const bSegments = b.split("/");
|
|
49
50
|
if (bSegments.length !== aSegments.length) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routePatterns.js","sources":["../../../src/vite/routePatterns.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { Plugin } from 'vite';\n\n/**\n * Extracts route patterns from TanStack Start's generated routeTree.gen.ts\n * and replaces `__SENTRY_ROUTE_PATTERNS__` references with the extracted patterns.\n *\n * The route tree file is read during `transform` rather than `config` because\n * TanStack Start generates it during the build.\n */\nexport function makeRoutePatternPlugin(): Plugin {\n let resolvedRoot = '';\n\n return {\n name: 'sentry-tanstackstart-route-patterns',\n enforce: 'post',\n\n configResolved(config) {\n resolvedRoot = config.root || process.cwd();\n },\n\n transform(code, _id) {\n // this is set in the `wrapFetchWithSentry` where the paths are getting replaced by their parametrized counterparts\n // so this extraction should only happen once during the build (for the `wrapFetchWithSentry` file)\n if (!code.includes('__SENTRY_ROUTE_PATTERNS__')) {\n return null;\n }\n\n // extract the patterns from the route tree file\n const routeTreePath = path.resolve(resolvedRoot, 'src/routeTree.gen.ts');\n let patterns: string[] = [];\n try {\n if (fs.existsSync(routeTreePath)) {\n patterns = extractRoutePatterns(fs.readFileSync(routeTreePath, 'utf-8'));\n }\n } catch {\n // skip\n }\n\n return {\n code: code.replace(/__SENTRY_ROUTE_PATTERNS__/g, JSON.stringify(patterns)),\n map: null,\n };\n },\n };\n}\n\n/**\n * Extracts full route path patterns from the content of routeTree.gen.ts.\n *\n * Parses the `fullPaths` type union which contains the resolved full paths\n * (e.g., `fullPaths: '/' | '/page-a' | '/users/$userId'`).\n * This is more reliable than `path:` properties which can be relative for nested routes.\n */\nexport function extractRoutePatterns(content: string): string[] {\n const fullPathsMatch = content.match(/fullPaths:\\s*([\\s\\S]*?)(?:\\n\\s*\\w|\\n\\})/);\n if (!fullPathsMatch) {\n return [];\n }\n\n const patterns: string[] = [];\n const pathRegex = /['\"]([^'\"]+)['\"]/g;\n let match;\n while ((match = pathRegex.exec(fullPathsMatch[1] || '')) !== null) {\n if (match[1]) {\n patterns.push(match[1]);\n }\n }\n\n return
|
|
1
|
+
{"version":3,"file":"routePatterns.js","sources":["../../../src/vite/routePatterns.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { uniq } from '@sentry/core';\nimport type { Plugin } from 'vite';\n\n/**\n * Extracts route patterns from TanStack Start's generated routeTree.gen.ts\n * and replaces `__SENTRY_ROUTE_PATTERNS__` references with the extracted patterns.\n *\n * The route tree file is read during `transform` rather than `config` because\n * TanStack Start generates it during the build.\n */\nexport function makeRoutePatternPlugin(): Plugin {\n let resolvedRoot = '';\n\n return {\n name: 'sentry-tanstackstart-route-patterns',\n enforce: 'post',\n\n configResolved(config) {\n resolvedRoot = config.root || process.cwd();\n },\n\n transform(code, _id) {\n // this is set in the `wrapFetchWithSentry` where the paths are getting replaced by their parametrized counterparts\n // so this extraction should only happen once during the build (for the `wrapFetchWithSentry` file)\n if (!code.includes('__SENTRY_ROUTE_PATTERNS__')) {\n return null;\n }\n\n // extract the patterns from the route tree file\n const routeTreePath = path.resolve(resolvedRoot, 'src/routeTree.gen.ts');\n let patterns: string[] = [];\n try {\n if (fs.existsSync(routeTreePath)) {\n patterns = extractRoutePatterns(fs.readFileSync(routeTreePath, 'utf-8'));\n }\n } catch {\n // skip\n }\n\n return {\n code: code.replace(/__SENTRY_ROUTE_PATTERNS__/g, JSON.stringify(patterns)),\n map: null,\n };\n },\n };\n}\n\n/**\n * Extracts full route path patterns from the content of routeTree.gen.ts.\n *\n * Parses the `fullPaths` type union which contains the resolved full paths\n * (e.g., `fullPaths: '/' | '/page-a' | '/users/$userId'`).\n * This is more reliable than `path:` properties which can be relative for nested routes.\n */\nexport function extractRoutePatterns(content: string): string[] {\n const fullPathsMatch = content.match(/fullPaths:\\s*([\\s\\S]*?)(?:\\n\\s*\\w|\\n\\})/);\n if (!fullPathsMatch) {\n return [];\n }\n\n const patterns: string[] = [];\n const pathRegex = /['\"]([^'\"]+)['\"]/g;\n let match;\n while ((match = pathRegex.exec(fullPathsMatch[1] || '')) !== null) {\n if (match[1]) {\n patterns.push(match[1]);\n }\n }\n\n return uniq(patterns).sort((a, b) => {\n const aSegments = a.split('/');\n const bSegments = b.split('/');\n if (bSegments.length !== aSegments.length) {\n return bSegments.length - aSegments.length;\n }\n const aDynamic = aSegments.filter(s => s.startsWith('$')).length;\n const bDynamic = bSegments.filter(s => s.startsWith('$')).length;\n return aDynamic - bDynamic;\n });\n}\n"],"names":["uniq"],"mappings":";;;;;;AAYO,SAAS,sBAAA,GAAiC;AAC/C,EAAA,IAAI,YAAA,GAAe,EAAA;AAEnB,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,qCAAA;AAAA,IACN,OAAA,EAAS,MAAA;AAAA,IAET,eAAe,MAAA,EAAQ;AACrB,MAAA,YAAA,GAAe,MAAA,CAAO,IAAA,IAAQ,OAAA,CAAQ,GAAA,EAAI;AAAA,IAC5C,CAAA;AAAA,IAEA,SAAA,CAAU,MAAM,GAAA,EAAK;AAGnB,MAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,2BAA2B,CAAA,EAAG;AAC/C,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,OAAA,CAAQ,YAAA,EAAc,sBAAsB,CAAA;AACvE,MAAA,IAAI,WAAqB,EAAC;AAC1B,MAAA,IAAI;AACF,QAAA,IAAI,EAAA,CAAG,UAAA,CAAW,aAAa,CAAA,EAAG;AAChC,UAAA,QAAA,GAAW,oBAAA,CAAqB,EAAA,CAAG,YAAA,CAAa,aAAA,EAAe,OAAO,CAAC,CAAA;AAAA,QACzE;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAER;AAEA,MAAA,OAAO;AAAA,QACL,MAAM,IAAA,CAAK,OAAA,CAAQ,8BAA8B,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,QACzE,GAAA,EAAK;AAAA,OACP;AAAA,IACF;AAAA,GACF;AACF;AASO,SAAS,qBAAqB,OAAA,EAA2B;AAC9D,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,KAAA,CAAM,yCAAyC,CAAA;AAC9E,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,MAAM,SAAA,GAAY,mBAAA;AAClB,EAAA,IAAI,KAAA;AACJ,EAAA,OAAA,CAAQ,KAAA,GAAQ,UAAU,IAAA,CAAK,cAAA,CAAe,CAAC,CAAA,IAAK,EAAE,OAAO,IAAA,EAAM;AACjE,IAAA,IAAI,KAAA,CAAM,CAAC,CAAA,EAAG;AACZ,MAAA,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,IACxB;AAAA,EACF;AAEA,EAAA,OAAOA,UAAK,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM;AACnC,IAAA,MAAM,SAAA,GAAY,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,MAAM,SAAA,GAAY,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,SAAA,CAAU,MAAA,KAAW,SAAA,CAAU,MAAA,EAAQ;AACzC,MAAA,OAAO,SAAA,CAAU,SAAS,SAAA,CAAU,MAAA;AAAA,IACtC;AACA,IAAA,MAAM,QAAA,GAAW,UAAU,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,UAAA,CAAW,GAAG,CAAC,CAAA,CAAE,MAAA;AAC1D,IAAA,MAAM,QAAA,GAAW,UAAU,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,UAAA,CAAW,GAAG,CAAC,CAAA,CAAE,MAAA;AAC1D,IAAA,OAAO,QAAA,GAAW,QAAA;AAAA,EACpB,CAAC,CAAA;AACH;;;;;"}
|
package/build/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"10.
|
|
1
|
+
{"type":"module","version":"10.65.0","sideEffects":false}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addNonEnumerableProperty } from '@sentry/core';
|
|
1
|
+
import { isObjectLike, addNonEnumerableProperty } from '@sentry/core';
|
|
2
2
|
import { getActiveSpan, startSpanManual, withActiveSpan } from '@sentry/node';
|
|
3
3
|
import { getMiddlewareSpanOptions } from './utils.js';
|
|
4
4
|
|
|
@@ -29,7 +29,7 @@ function wrapMiddlewareWithSentry(middleware, options) {
|
|
|
29
29
|
return startSpanManual(getMiddlewareSpanOptions(options.name), async (span) => {
|
|
30
30
|
const nextState = { called: false };
|
|
31
31
|
const middlewareArgs = argsServer[0];
|
|
32
|
-
if (middlewareArgs && typeof middlewareArgs
|
|
32
|
+
if (isObjectLike(middlewareArgs) && typeof middlewareArgs.next === "function") {
|
|
33
33
|
middlewareArgs.next = getNextProxy(middlewareArgs.next, span, prevSpan, nextState);
|
|
34
34
|
}
|
|
35
35
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sources":["../../../src/server/middleware.ts"],"sourcesContent":["import { addNonEnumerableProperty } from '@sentry/core';\nimport type { Span } from '@sentry/node';\nimport { getActiveSpan, startSpanManual, withActiveSpan } from '@sentry/node';\nimport type { MiddlewareWrapperOptions, TanStackMiddlewareBase } from '../common/types';\nimport { getMiddlewareSpanOptions } from './utils';\n\nconst SENTRY_WRAPPED = '__SENTRY_WRAPPED__';\nexport const SENTRY_INTERNAL = '__SENTRY_INTERNAL__';\n\n/**\n * Creates a proxy for the next function that ends the current span and restores the parent span.\n * This ensures that subsequent middleware spans are children of the root span, not nested children.\n */\nfunction getNextProxy<T extends (...args: unknown[]) => unknown>(\n next: T,\n span: Span,\n prevSpan: Span | undefined,\n nextState: { called: boolean },\n): T {\n return new Proxy(next, {\n apply: (originalNext, thisArgNext, argsNext) => {\n nextState.called = true;\n span.end();\n\n if (prevSpan) {\n return withActiveSpan(prevSpan, () => {\n return Reflect.apply(originalNext, thisArgNext, argsNext);\n });\n }\n\n return Reflect.apply(originalNext, thisArgNext, argsNext);\n },\n });\n}\n\n/**\n * Wraps a TanStack Start middleware with Sentry instrumentation to create spans.\n */\nfunction wrapMiddlewareWithSentry<T extends TanStackMiddlewareBase>(\n middleware: T,\n options: MiddlewareWrapperOptions,\n): T {\n if (\n (middleware as TanStackMiddlewareBase & { [SENTRY_WRAPPED]?: boolean })[SENTRY_WRAPPED] ||\n (middleware as TanStackMiddlewareBase & { [SENTRY_INTERNAL]?: boolean })[SENTRY_INTERNAL]\n ) {\n // already instrumented or internal Sentry middleware\n return middleware;\n }\n\n // instrument server middleware\n if (middleware.options?.server) {\n middleware.options.server = new Proxy(middleware.options.server, {\n apply: (originalServer, thisArgServer, argsServer) => {\n const prevSpan = getActiveSpan();\n\n return startSpanManual(getMiddlewareSpanOptions(options.name), async (span: Span) => {\n const nextState = { called: false };\n\n // The server function receives { next, context, request } as first argument\n // Users call next() inside their middleware to move down the middleware chain. We proxy next() to end the span when it is called.\n const middlewareArgs = argsServer[0] as { next?: (...args: unknown[]) => unknown } | undefined;\n if (middlewareArgs && typeof middlewareArgs
|
|
1
|
+
{"version":3,"file":"middleware.js","sources":["../../../src/server/middleware.ts"],"sourcesContent":["import { isObjectLike, addNonEnumerableProperty } from '@sentry/core';\nimport type { Span } from '@sentry/node';\nimport { getActiveSpan, startSpanManual, withActiveSpan } from '@sentry/node';\nimport type { MiddlewareWrapperOptions, TanStackMiddlewareBase } from '../common/types';\nimport { getMiddlewareSpanOptions } from './utils';\n\nconst SENTRY_WRAPPED = '__SENTRY_WRAPPED__';\nexport const SENTRY_INTERNAL = '__SENTRY_INTERNAL__';\n\n/**\n * Creates a proxy for the next function that ends the current span and restores the parent span.\n * This ensures that subsequent middleware spans are children of the root span, not nested children.\n */\nfunction getNextProxy<T extends (...args: unknown[]) => unknown>(\n next: T,\n span: Span,\n prevSpan: Span | undefined,\n nextState: { called: boolean },\n): T {\n return new Proxy(next, {\n apply: (originalNext, thisArgNext, argsNext) => {\n nextState.called = true;\n span.end();\n\n if (prevSpan) {\n return withActiveSpan(prevSpan, () => {\n return Reflect.apply(originalNext, thisArgNext, argsNext);\n });\n }\n\n return Reflect.apply(originalNext, thisArgNext, argsNext);\n },\n });\n}\n\n/**\n * Wraps a TanStack Start middleware with Sentry instrumentation to create spans.\n */\nfunction wrapMiddlewareWithSentry<T extends TanStackMiddlewareBase>(\n middleware: T,\n options: MiddlewareWrapperOptions,\n): T {\n if (\n (middleware as TanStackMiddlewareBase & { [SENTRY_WRAPPED]?: boolean })[SENTRY_WRAPPED] ||\n (middleware as TanStackMiddlewareBase & { [SENTRY_INTERNAL]?: boolean })[SENTRY_INTERNAL]\n ) {\n // already instrumented or internal Sentry middleware\n return middleware;\n }\n\n // instrument server middleware\n if (middleware.options?.server) {\n middleware.options.server = new Proxy(middleware.options.server, {\n apply: (originalServer, thisArgServer, argsServer) => {\n const prevSpan = getActiveSpan();\n\n return startSpanManual(getMiddlewareSpanOptions(options.name), async (span: Span) => {\n const nextState = { called: false };\n\n // The server function receives { next, context, request } as first argument\n // Users call next() inside their middleware to move down the middleware chain. We proxy next() to end the span when it is called.\n const middlewareArgs = argsServer[0] as { next?: (...args: unknown[]) => unknown } | undefined;\n if (isObjectLike(middlewareArgs) && typeof middlewareArgs.next === 'function') {\n middlewareArgs.next = getNextProxy(middlewareArgs.next, span, prevSpan, nextState);\n }\n\n try {\n const result = await originalServer.apply(thisArgServer, argsServer);\n\n // End span here if next() wasn't called, else we already ended it in next()\n if (!nextState.called) {\n span.end();\n }\n\n return result;\n } catch (e) {\n span.end();\n throw e;\n }\n });\n },\n });\n\n // mark as instrumented\n addNonEnumerableProperty(middleware as unknown as Record<string, unknown>, SENTRY_WRAPPED, true);\n }\n\n return middleware;\n}\n\n/**\n * Wraps multiple TanStack Start middlewares with Sentry instrumentation.\n * Object keys are used as span names to avoid users having to specify this manually.\n *\n * @example\n * ```ts\n * import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\n *\n * const wrappedMiddlewares = wrapMiddlewaresWithSentry({\n * authMiddleware,\n * loggingMiddleware,\n * });\n *\n * createServerFn().middleware(wrappedMiddlewares)\n * ```\n *\n * @param middlewares - An object containing middlewares\n * @returns An array of wrapped middlewares\n */\nexport function wrapMiddlewaresWithSentry<T extends TanStackMiddlewareBase>(middlewares: Record<string, T>): T[] {\n return Object.entries(middlewares).map(([name, middleware]) => {\n return wrapMiddlewareWithSentry(middleware, { name });\n });\n}\n"],"names":[],"mappings":";;;;AAMA,MAAM,cAAA,GAAiB,oBAAA;AAChB,MAAM,eAAA,GAAkB;AAM/B,SAAS,YAAA,CACP,IAAA,EACA,IAAA,EACA,QAAA,EACA,SAAA,EACG;AACH,EAAA,OAAO,IAAI,MAAM,IAAA,EAAM;AAAA,IACrB,KAAA,EAAO,CAAC,YAAA,EAAc,WAAA,EAAa,QAAA,KAAa;AAC9C,MAAA,SAAA,CAAU,MAAA,GAAS,IAAA;AACnB,MAAA,IAAA,CAAK,GAAA,EAAI;AAET,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,OAAO,cAAA,CAAe,UAAU,MAAM;AACpC,UAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,YAAA,EAAc,WAAA,EAAa,QAAQ,CAAA;AAAA,QAC1D,CAAC,CAAA;AAAA,MACH;AAEA,MAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,YAAA,EAAc,WAAA,EAAa,QAAQ,CAAA;AAAA,IAC1D;AAAA,GACD,CAAA;AACH;AAKA,SAAS,wBAAA,CACP,YACA,OAAA,EACG;AACH,EAAA,IACG,UAAA,CAAuE,cAAc,CAAA,IACrF,UAAA,CAAwE,eAAe,CAAA,EACxF;AAEA,IAAA,OAAO,UAAA;AAAA,EACT;AAGA,EAAA,IAAI,UAAA,CAAW,SAAS,MAAA,EAAQ;AAC9B,IAAA,UAAA,CAAW,QAAQ,MAAA,GAAS,IAAI,KAAA,CAAM,UAAA,CAAW,QAAQ,MAAA,EAAQ;AAAA,MAC/D,KAAA,EAAO,CAAC,cAAA,EAAgB,aAAA,EAAe,UAAA,KAAe;AACpD,QAAA,MAAM,WAAW,aAAA,EAAc;AAE/B,QAAA,OAAO,gBAAgB,wBAAA,CAAyB,OAAA,CAAQ,IAAI,CAAA,EAAG,OAAO,IAAA,KAAe;AACnF,UAAA,MAAM,SAAA,GAAY,EAAE,MAAA,EAAQ,KAAA,EAAM;AAIlC,UAAA,MAAM,cAAA,GAAiB,WAAW,CAAC,CAAA;AACnC,UAAA,IAAI,aAAa,cAAc,CAAA,IAAK,OAAO,cAAA,CAAe,SAAS,UAAA,EAAY;AAC7E,YAAA,cAAA,CAAe,OAAO,YAAA,CAAa,cAAA,CAAe,IAAA,EAAM,IAAA,EAAM,UAAU,SAAS,CAAA;AAAA,UACnF;AAEA,UAAA,IAAI;AACF,YAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,KAAA,CAAM,eAAe,UAAU,CAAA;AAGnE,YAAA,IAAI,CAAC,UAAU,MAAA,EAAQ;AACrB,cAAA,IAAA,CAAK,GAAA,EAAI;AAAA,YACX;AAEA,YAAA,OAAO,MAAA;AAAA,UACT,SAAS,CAAA,EAAG;AACV,YAAA,IAAA,CAAK,GAAA,EAAI;AACT,YAAA,MAAM,CAAA;AAAA,UACR;AAAA,QACF,CAAC,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAGD,IAAA,wBAAA,CAAyB,UAAA,EAAkD,gBAAgB,IAAI,CAAA;AAAA,EACjG;AAEA,EAAA,OAAO,UAAA;AACT;AAqBO,SAAS,0BAA4D,WAAA,EAAqC;AAC/G,EAAA,OAAO,MAAA,CAAO,QAAQ,WAAW,CAAA,CAAE,IAAI,CAAC,CAAC,IAAA,EAAM,UAAU,CAAA,KAAM;AAC7D,IAAA,OAAO,wBAAA,CAAyB,UAAA,EAAY,EAAE,IAAA,EAAM,CAAA;AAAA,EACtD,CAAC,CAAA;AACH;;;;"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { uniq } from '@sentry/core';
|
|
2
|
+
|
|
1
3
|
function wrapMiddlewareArrays(code, id, debug, regex) {
|
|
2
4
|
const skipped = [];
|
|
3
5
|
let didWrap = false;
|
|
@@ -90,7 +92,7 @@ function arrayToObjectShorthand(contents) {
|
|
|
90
92
|
if (!allIdentifiers || items.length === 0) {
|
|
91
93
|
return null;
|
|
92
94
|
}
|
|
93
|
-
const uniqueItems =
|
|
95
|
+
const uniqueItems = uniq(items);
|
|
94
96
|
return `{ ${uniqueItems.join(", ")} }`;
|
|
95
97
|
}
|
|
96
98
|
function addSentryImport(code) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoInstrumentMiddleware.js","sources":["../../../src/vite/autoInstrumentMiddleware.ts"],"sourcesContent":["import type { Plugin } from 'vite';\n\ntype AutoInstrumentMiddlewareOptions = {\n enabled?: boolean;\n debug?: boolean;\n};\n\ntype WrapResult = {\n code: string;\n didWrap: boolean;\n skipped: string[];\n};\n\ntype FileTransformState = {\n code: string;\n needsImport: boolean;\n skippedMiddlewares: string[];\n};\n\n/**\n * Core function that wraps middleware arrays matching the given regex.\n */\nfunction wrapMiddlewareArrays(code: string, id: string, debug: boolean, regex: RegExp): WrapResult {\n const skipped: string[] = [];\n let didWrap = false;\n\n const transformed = code.replace(regex, (match: string, key: string, contents: string) => {\n const objContents = arrayToObjectShorthand(contents);\n if (objContents) {\n didWrap = true;\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(`[Sentry] Auto-wrapping ${key} in ${id}`);\n }\n // Handle method call syntax like `.middleware([...])` vs object property syntax like `middleware: [...]`\n if (key.endsWith('(')) {\n return `${key}wrapMiddlewaresWithSentry(${objContents}))`;\n }\n return `${key}: wrapMiddlewaresWithSentry(${objContents})`;\n }\n // Track middlewares that couldn't be auto-wrapped\n // Skip if we matched whitespace only\n if (contents.trim()) {\n skipped.push(key);\n }\n return match;\n });\n\n return { code: transformed, didWrap, skipped };\n}\n\n/**\n * Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.\n */\nexport function wrapGlobalMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(requestMiddleware|functionMiddleware)\\s*:\\s*\\[([^\\]]*)\\]/g);\n}\n\n/**\n * Wraps route middleware arrays in createFileRoute() files.\n */\nexport function wrapRouteMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(middleware)\\s*:\\s*\\[([^\\]]*)\\]/g);\n}\n\n/**\n * Wraps middleware arrays in createServerFn().middleware([...]) calls.\n */\nexport function wrapServerFnMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(\\.middleware\\s*\\()\\s*\\[([^\\]]*)\\]\\s*\\)/g);\n}\n\n/**\n * Applies a wrap function to the current state and returns the updated state.\n */\nfunction applyWrap(\n state: FileTransformState,\n wrapFn: (code: string, id: string, debug: boolean) => WrapResult,\n id: string,\n debug: boolean,\n): FileTransformState {\n const result = wrapFn(state.code, id, debug);\n return {\n code: result.code,\n needsImport: state.needsImport || result.didWrap,\n skippedMiddlewares: [...state.skippedMiddlewares, ...result.skipped],\n };\n}\n\n/**\n * A Vite plugin that automatically instruments TanStack Start middlewares:\n * - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`\n * - `middleware` arrays in `createFileRoute()` route definitions\n */\nexport function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddlewareOptions = {}): Plugin {\n const { enabled = true, debug = false } = options;\n\n return {\n name: 'sentry-tanstack-middleware-auto-instrument',\n enforce: 'pre',\n\n transform(code, id) {\n if (!enabled) {\n return null;\n }\n\n // Skip if not a TS/JS file\n if (!/\\.(ts|tsx|js|jsx|mjs|mts)$/.test(id)) {\n return null;\n }\n\n // Detect file types that should be instrumented\n const isStartFile = id.includes('start') && code.includes('createStart(');\n const isRouteFile = code.includes('createFileRoute(') && /middleware\\s*:\\s*\\[/.test(code);\n const isServerFnFile = code.includes('createServerFn') && /\\.middleware\\s*\\(\\s*\\[/.test(code);\n\n if (!isStartFile && !isRouteFile && !isServerFnFile) {\n return null;\n }\n\n // Skip if the user already did some manual wrapping\n if (code.includes('wrapMiddlewaresWithSentry')) {\n return null;\n }\n\n let fileTransformState: FileTransformState = {\n code,\n needsImport: false,\n skippedMiddlewares: [],\n };\n\n // Wrap middlewares\n if (isStartFile) {\n fileTransformState = applyWrap(fileTransformState, wrapGlobalMiddleware, id, debug);\n }\n if (isRouteFile) {\n fileTransformState = applyWrap(fileTransformState, wrapRouteMiddleware, id, debug);\n }\n if (isServerFnFile) {\n fileTransformState = applyWrap(fileTransformState, wrapServerFnMiddleware, id, debug);\n }\n\n // Warn about middlewares that couldn't be auto-wrapped\n if (fileTransformState.skippedMiddlewares.length > 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] Could not auto-instrument ${fileTransformState.skippedMiddlewares.join(' and ')} in ${id}. ` +\n 'To instrument these middlewares, use wrapMiddlewaresWithSentry() manually. ',\n );\n }\n\n // We didn't wrap any middlewares, so we don't need to import the wrapMiddlewaresWithSentry function\n if (!fileTransformState.needsImport) {\n return null;\n }\n\n return { code: addSentryImport(fileTransformState.code), map: null };\n },\n };\n}\n\n/**\n * Convert array contents to object shorthand syntax.\n * e.g., \"foo, bar, baz\" → \"{ foo, bar, baz }\"\n *\n * Returns null if contents contain non-identifier expressions (function calls, etc.)\n * which cannot be converted to object shorthand.\n */\nexport function arrayToObjectShorthand(contents: string): string | null {\n const items = contents\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n\n // Only convert if all items are valid identifiers (no complex expressions)\n const allIdentifiers = items.every(item => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(item));\n if (!allIdentifiers || items.length === 0) {\n return null;\n }\n\n // Deduplicate to avoid invalid syntax like { foo, foo }\n const uniqueItems = [...new Set(items)];\n\n return `{ ${uniqueItems.join(', ')} }`;\n}\n\n/**\n * Adds the wrapMiddlewaresWithSentry import to the code.\n * Handles 'use client' and 'use server' directives by inserting the import after them.\n */\nexport function addSentryImport(code: string): string {\n const sentryImport = \"import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\\n\";\n\n // Don't add the import if it already exists\n if (code.includes(sentryImport.trimEnd())) {\n return code;\n }\n\n // Check for 'use server' or 'use client' directives, these need to be before any imports\n const directiveMatch = code.match(/^(['\"])use (client|server)\\1;?\\s*\\n?/);\n\n if (!directiveMatch) {\n return sentryImport + code;\n }\n\n const directive = directiveMatch[0];\n return directive + sentryImport + code.slice(directive.length);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"autoInstrumentMiddleware.js","sources":["../../../src/vite/autoInstrumentMiddleware.ts"],"sourcesContent":["import { uniq } from '@sentry/core';\nimport type { Plugin } from 'vite';\n\ntype AutoInstrumentMiddlewareOptions = {\n enabled?: boolean;\n debug?: boolean;\n};\n\ntype WrapResult = {\n code: string;\n didWrap: boolean;\n skipped: string[];\n};\n\ntype FileTransformState = {\n code: string;\n needsImport: boolean;\n skippedMiddlewares: string[];\n};\n\n/**\n * Core function that wraps middleware arrays matching the given regex.\n */\nfunction wrapMiddlewareArrays(code: string, id: string, debug: boolean, regex: RegExp): WrapResult {\n const skipped: string[] = [];\n let didWrap = false;\n\n const transformed = code.replace(regex, (match: string, key: string, contents: string) => {\n const objContents = arrayToObjectShorthand(contents);\n if (objContents) {\n didWrap = true;\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(`[Sentry] Auto-wrapping ${key} in ${id}`);\n }\n // Handle method call syntax like `.middleware([...])` vs object property syntax like `middleware: [...]`\n if (key.endsWith('(')) {\n return `${key}wrapMiddlewaresWithSentry(${objContents}))`;\n }\n return `${key}: wrapMiddlewaresWithSentry(${objContents})`;\n }\n // Track middlewares that couldn't be auto-wrapped\n // Skip if we matched whitespace only\n if (contents.trim()) {\n skipped.push(key);\n }\n return match;\n });\n\n return { code: transformed, didWrap, skipped };\n}\n\n/**\n * Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.\n */\nexport function wrapGlobalMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(requestMiddleware|functionMiddleware)\\s*:\\s*\\[([^\\]]*)\\]/g);\n}\n\n/**\n * Wraps route middleware arrays in createFileRoute() files.\n */\nexport function wrapRouteMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(middleware)\\s*:\\s*\\[([^\\]]*)\\]/g);\n}\n\n/**\n * Wraps middleware arrays in createServerFn().middleware([...]) calls.\n */\nexport function wrapServerFnMiddleware(code: string, id: string, debug: boolean): WrapResult {\n return wrapMiddlewareArrays(code, id, debug, /(\\.middleware\\s*\\()\\s*\\[([^\\]]*)\\]\\s*\\)/g);\n}\n\n/**\n * Applies a wrap function to the current state and returns the updated state.\n */\nfunction applyWrap(\n state: FileTransformState,\n wrapFn: (code: string, id: string, debug: boolean) => WrapResult,\n id: string,\n debug: boolean,\n): FileTransformState {\n const result = wrapFn(state.code, id, debug);\n return {\n code: result.code,\n needsImport: state.needsImport || result.didWrap,\n skippedMiddlewares: [...state.skippedMiddlewares, ...result.skipped],\n };\n}\n\n/**\n * A Vite plugin that automatically instruments TanStack Start middlewares:\n * - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`\n * - `middleware` arrays in `createFileRoute()` route definitions\n */\nexport function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddlewareOptions = {}): Plugin {\n const { enabled = true, debug = false } = options;\n\n return {\n name: 'sentry-tanstack-middleware-auto-instrument',\n enforce: 'pre',\n\n transform(code, id) {\n if (!enabled) {\n return null;\n }\n\n // Skip if not a TS/JS file\n if (!/\\.(ts|tsx|js|jsx|mjs|mts)$/.test(id)) {\n return null;\n }\n\n // Detect file types that should be instrumented\n const isStartFile = id.includes('start') && code.includes('createStart(');\n const isRouteFile = code.includes('createFileRoute(') && /middleware\\s*:\\s*\\[/.test(code);\n const isServerFnFile = code.includes('createServerFn') && /\\.middleware\\s*\\(\\s*\\[/.test(code);\n\n if (!isStartFile && !isRouteFile && !isServerFnFile) {\n return null;\n }\n\n // Skip if the user already did some manual wrapping\n if (code.includes('wrapMiddlewaresWithSentry')) {\n return null;\n }\n\n let fileTransformState: FileTransformState = {\n code,\n needsImport: false,\n skippedMiddlewares: [],\n };\n\n // Wrap middlewares\n if (isStartFile) {\n fileTransformState = applyWrap(fileTransformState, wrapGlobalMiddleware, id, debug);\n }\n if (isRouteFile) {\n fileTransformState = applyWrap(fileTransformState, wrapRouteMiddleware, id, debug);\n }\n if (isServerFnFile) {\n fileTransformState = applyWrap(fileTransformState, wrapServerFnMiddleware, id, debug);\n }\n\n // Warn about middlewares that couldn't be auto-wrapped\n if (fileTransformState.skippedMiddlewares.length > 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] Could not auto-instrument ${fileTransformState.skippedMiddlewares.join(' and ')} in ${id}. ` +\n 'To instrument these middlewares, use wrapMiddlewaresWithSentry() manually. ',\n );\n }\n\n // We didn't wrap any middlewares, so we don't need to import the wrapMiddlewaresWithSentry function\n if (!fileTransformState.needsImport) {\n return null;\n }\n\n return { code: addSentryImport(fileTransformState.code), map: null };\n },\n };\n}\n\n/**\n * Convert array contents to object shorthand syntax.\n * e.g., \"foo, bar, baz\" → \"{ foo, bar, baz }\"\n *\n * Returns null if contents contain non-identifier expressions (function calls, etc.)\n * which cannot be converted to object shorthand.\n */\nexport function arrayToObjectShorthand(contents: string): string | null {\n const items = contents\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n\n // Only convert if all items are valid identifiers (no complex expressions)\n const allIdentifiers = items.every(item => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(item));\n if (!allIdentifiers || items.length === 0) {\n return null;\n }\n\n // Deduplicate to avoid invalid syntax like { foo, foo }\n const uniqueItems = uniq(items);\n\n return `{ ${uniqueItems.join(', ')} }`;\n}\n\n/**\n * Adds the wrapMiddlewaresWithSentry import to the code.\n * Handles 'use client' and 'use server' directives by inserting the import after them.\n */\nexport function addSentryImport(code: string): string {\n const sentryImport = \"import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\\n\";\n\n // Don't add the import if it already exists\n if (code.includes(sentryImport.trimEnd())) {\n return code;\n }\n\n // Check for 'use server' or 'use client' directives, these need to be before any imports\n const directiveMatch = code.match(/^(['\"])use (client|server)\\1;?\\s*\\n?/);\n\n if (!directiveMatch) {\n return sentryImport + code;\n }\n\n const directive = directiveMatch[0];\n return directive + sentryImport + code.slice(directive.length);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
+
import { uniq } from '@sentry/core';
|
|
3
4
|
|
|
4
5
|
function makeRoutePatternPlugin() {
|
|
5
6
|
let resolvedRoot = "";
|
|
@@ -41,7 +42,7 @@ function extractRoutePatterns(content) {
|
|
|
41
42
|
patterns.push(match[1]);
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
return
|
|
45
|
+
return uniq(patterns).sort((a, b) => {
|
|
45
46
|
const aSegments = a.split("/");
|
|
46
47
|
const bSegments = b.split("/");
|
|
47
48
|
if (bSegments.length !== aSegments.length) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routePatterns.js","sources":["../../../src/vite/routePatterns.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { Plugin } from 'vite';\n\n/**\n * Extracts route patterns from TanStack Start's generated routeTree.gen.ts\n * and replaces `__SENTRY_ROUTE_PATTERNS__` references with the extracted patterns.\n *\n * The route tree file is read during `transform` rather than `config` because\n * TanStack Start generates it during the build.\n */\nexport function makeRoutePatternPlugin(): Plugin {\n let resolvedRoot = '';\n\n return {\n name: 'sentry-tanstackstart-route-patterns',\n enforce: 'post',\n\n configResolved(config) {\n resolvedRoot = config.root || process.cwd();\n },\n\n transform(code, _id) {\n // this is set in the `wrapFetchWithSentry` where the paths are getting replaced by their parametrized counterparts\n // so this extraction should only happen once during the build (for the `wrapFetchWithSentry` file)\n if (!code.includes('__SENTRY_ROUTE_PATTERNS__')) {\n return null;\n }\n\n // extract the patterns from the route tree file\n const routeTreePath = path.resolve(resolvedRoot, 'src/routeTree.gen.ts');\n let patterns: string[] = [];\n try {\n if (fs.existsSync(routeTreePath)) {\n patterns = extractRoutePatterns(fs.readFileSync(routeTreePath, 'utf-8'));\n }\n } catch {\n // skip\n }\n\n return {\n code: code.replace(/__SENTRY_ROUTE_PATTERNS__/g, JSON.stringify(patterns)),\n map: null,\n };\n },\n };\n}\n\n/**\n * Extracts full route path patterns from the content of routeTree.gen.ts.\n *\n * Parses the `fullPaths` type union which contains the resolved full paths\n * (e.g., `fullPaths: '/' | '/page-a' | '/users/$userId'`).\n * This is more reliable than `path:` properties which can be relative for nested routes.\n */\nexport function extractRoutePatterns(content: string): string[] {\n const fullPathsMatch = content.match(/fullPaths:\\s*([\\s\\S]*?)(?:\\n\\s*\\w|\\n\\})/);\n if (!fullPathsMatch) {\n return [];\n }\n\n const patterns: string[] = [];\n const pathRegex = /['\"]([^'\"]+)['\"]/g;\n let match;\n while ((match = pathRegex.exec(fullPathsMatch[1] || '')) !== null) {\n if (match[1]) {\n patterns.push(match[1]);\n }\n }\n\n return
|
|
1
|
+
{"version":3,"file":"routePatterns.js","sources":["../../../src/vite/routePatterns.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { uniq } from '@sentry/core';\nimport type { Plugin } from 'vite';\n\n/**\n * Extracts route patterns from TanStack Start's generated routeTree.gen.ts\n * and replaces `__SENTRY_ROUTE_PATTERNS__` references with the extracted patterns.\n *\n * The route tree file is read during `transform` rather than `config` because\n * TanStack Start generates it during the build.\n */\nexport function makeRoutePatternPlugin(): Plugin {\n let resolvedRoot = '';\n\n return {\n name: 'sentry-tanstackstart-route-patterns',\n enforce: 'post',\n\n configResolved(config) {\n resolvedRoot = config.root || process.cwd();\n },\n\n transform(code, _id) {\n // this is set in the `wrapFetchWithSentry` where the paths are getting replaced by their parametrized counterparts\n // so this extraction should only happen once during the build (for the `wrapFetchWithSentry` file)\n if (!code.includes('__SENTRY_ROUTE_PATTERNS__')) {\n return null;\n }\n\n // extract the patterns from the route tree file\n const routeTreePath = path.resolve(resolvedRoot, 'src/routeTree.gen.ts');\n let patterns: string[] = [];\n try {\n if (fs.existsSync(routeTreePath)) {\n patterns = extractRoutePatterns(fs.readFileSync(routeTreePath, 'utf-8'));\n }\n } catch {\n // skip\n }\n\n return {\n code: code.replace(/__SENTRY_ROUTE_PATTERNS__/g, JSON.stringify(patterns)),\n map: null,\n };\n },\n };\n}\n\n/**\n * Extracts full route path patterns from the content of routeTree.gen.ts.\n *\n * Parses the `fullPaths` type union which contains the resolved full paths\n * (e.g., `fullPaths: '/' | '/page-a' | '/users/$userId'`).\n * This is more reliable than `path:` properties which can be relative for nested routes.\n */\nexport function extractRoutePatterns(content: string): string[] {\n const fullPathsMatch = content.match(/fullPaths:\\s*([\\s\\S]*?)(?:\\n\\s*\\w|\\n\\})/);\n if (!fullPathsMatch) {\n return [];\n }\n\n const patterns: string[] = [];\n const pathRegex = /['\"]([^'\"]+)['\"]/g;\n let match;\n while ((match = pathRegex.exec(fullPathsMatch[1] || '')) !== null) {\n if (match[1]) {\n patterns.push(match[1]);\n }\n }\n\n return uniq(patterns).sort((a, b) => {\n const aSegments = a.split('/');\n const bSegments = b.split('/');\n if (bSegments.length !== aSegments.length) {\n return bSegments.length - aSegments.length;\n }\n const aDynamic = aSegments.filter(s => s.startsWith('$')).length;\n const bDynamic = bSegments.filter(s => s.startsWith('$')).length;\n return aDynamic - bDynamic;\n });\n}\n"],"names":[],"mappings":";;;;AAYO,SAAS,sBAAA,GAAiC;AAC/C,EAAA,IAAI,YAAA,GAAe,EAAA;AAEnB,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,qCAAA;AAAA,IACN,OAAA,EAAS,MAAA;AAAA,IAET,eAAe,MAAA,EAAQ;AACrB,MAAA,YAAA,GAAe,MAAA,CAAO,IAAA,IAAQ,OAAA,CAAQ,GAAA,EAAI;AAAA,IAC5C,CAAA;AAAA,IAEA,SAAA,CAAU,MAAM,GAAA,EAAK;AAGnB,MAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,2BAA2B,CAAA,EAAG;AAC/C,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,OAAA,CAAQ,YAAA,EAAc,sBAAsB,CAAA;AACvE,MAAA,IAAI,WAAqB,EAAC;AAC1B,MAAA,IAAI;AACF,QAAA,IAAI,EAAA,CAAG,UAAA,CAAW,aAAa,CAAA,EAAG;AAChC,UAAA,QAAA,GAAW,oBAAA,CAAqB,EAAA,CAAG,YAAA,CAAa,aAAA,EAAe,OAAO,CAAC,CAAA;AAAA,QACzE;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAER;AAEA,MAAA,OAAO;AAAA,QACL,MAAM,IAAA,CAAK,OAAA,CAAQ,8BAA8B,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,QACzE,GAAA,EAAK;AAAA,OACP;AAAA,IACF;AAAA,GACF;AACF;AASO,SAAS,qBAAqB,OAAA,EAA2B;AAC9D,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,KAAA,CAAM,yCAAyC,CAAA;AAC9E,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,MAAM,SAAA,GAAY,mBAAA;AAClB,EAAA,IAAI,KAAA;AACJ,EAAA,OAAA,CAAQ,KAAA,GAAQ,UAAU,IAAA,CAAK,cAAA,CAAe,CAAC,CAAA,IAAK,EAAE,OAAO,IAAA,EAAM;AACjE,IAAA,IAAI,KAAA,CAAM,CAAC,CAAA,EAAG;AACZ,MAAA,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,IACxB;AAAA,EACF;AAEA,EAAA,OAAO,KAAK,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM;AACnC,IAAA,MAAM,SAAA,GAAY,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,MAAM,SAAA,GAAY,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,SAAA,CAAU,MAAA,KAAW,SAAA,CAAU,MAAA,EAAQ;AACzC,MAAA,OAAO,SAAA,CAAU,SAAS,SAAA,CAAU,MAAA;AAAA,IACtC;AACA,IAAA,MAAM,QAAA,GAAW,UAAU,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,UAAA,CAAW,GAAG,CAAC,CAAA,CAAE,MAAA;AAC1D,IAAA,MAAM,QAAA,GAAW,UAAU,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,UAAA,CAAW,GAAG,CAAC,CAAA,CAAE,MAAA;AAC1D,IAAA,OAAO,QAAA,GAAW,QAAA;AAAA,EACpB,CAAC,CAAA;AACH;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoInstrumentMiddleware.d.ts","sourceRoot":"","sources":["../../../src/vite/autoInstrumentMiddleware.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"autoInstrumentMiddleware.d.ts","sourceRoot":"","sources":["../../../src/vite/autoInstrumentMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,KAAK,+BAA+B,GAAG;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAwCF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,UAAU,CAEzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,UAAU,CAExF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,UAAU,CAE3F;AAmBD;;;;GAIG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,GAAE,+BAAoC,GAAG,MAAM,CAiExG;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBtE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiBpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routePatterns.d.ts","sourceRoot":"","sources":["../../../src/vite/routePatterns.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"routePatterns.d.ts","sourceRoot":"","sources":["../../../src/vite/routePatterns.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAmC/C;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAyB9D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/tanstackstart-react",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.65.0",
|
|
4
4
|
"description": "Official Sentry SDK for TanStack Start React",
|
|
5
5
|
"repository": "git://github.com/getsentry/sentry-javascript.git",
|
|
6
6
|
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tanstackstart-react",
|
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@opentelemetry/api": "^1.9.1",
|
|
67
|
-
"@sentry/browser-utils": "10.
|
|
68
|
-
"@sentry/conventions": "^0.
|
|
69
|
-
"@sentry/core": "10.
|
|
70
|
-
"@sentry/node": "10.
|
|
71
|
-
"@sentry/react": "10.
|
|
67
|
+
"@sentry/browser-utils": "10.65.0",
|
|
68
|
+
"@sentry/conventions": "^0.15.1",
|
|
69
|
+
"@sentry/core": "10.65.0",
|
|
70
|
+
"@sentry/node": "10.65.0",
|
|
71
|
+
"@sentry/react": "10.65.0",
|
|
72
72
|
"@sentry/vite-plugin": "^5.3.0"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|