@sentry/tanstackstart-react 10.37.0 → 10.39.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/index.client.js +1 -1
- package/build/cjs/index.server.js +1 -1
- package/build/cjs/server/utils.js +1 -1
- package/build/cjs/server/utils.js.map +1 -1
- package/build/cjs/vite/autoInstrumentMiddleware.js +126 -45
- package/build/cjs/vite/autoInstrumentMiddleware.js.map +1 -1
- package/build/esm/index.client.js +1 -1
- package/build/esm/index.server.js +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/server/utils.js +1 -1
- package/build/esm/server/utils.js.map +1 -1
- package/build/esm/vite/autoInstrumentMiddleware.js +123 -46
- package/build/esm/vite/autoInstrumentMiddleware.js.map +1 -1
- package/build/types/vite/autoInstrumentMiddleware.d.ts +25 -2
- package/build/types/vite/autoInstrumentMiddleware.d.ts.map +1 -1
- package/build/types-ts3.8/vite/autoInstrumentMiddleware.d.ts +25 -2
- package/package.json +6 -6
|
@@ -2,10 +2,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
|
|
3
3
|
const index = require('./server/index.js');
|
|
4
4
|
const sentryTanstackStart = require('./vite/sentryTanstackStart.js');
|
|
5
|
+
const node = require('@sentry/node');
|
|
5
6
|
const sdk = require('./server/sdk.js');
|
|
6
7
|
const wrapFetchWithSentry = require('./server/wrapFetchWithSentry.js');
|
|
7
8
|
const middleware = require('./server/middleware.js');
|
|
8
|
-
const node = require('@sentry/node');
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
@@ -23,7 +23,7 @@ function getMiddlewareSpanOptions(name) {
|
|
|
23
23
|
op: 'middleware.tanstackstart',
|
|
24
24
|
name,
|
|
25
25
|
attributes: {
|
|
26
|
-
[node.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: '
|
|
26
|
+
[node.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.middleware.tanstackstart',
|
|
27
27
|
[node.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'middleware.tanstackstart',
|
|
28
28
|
},
|
|
29
29
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../src/server/utils.ts"],"sourcesContent":["import type { StartSpanOptions } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/node';\n\n/**\n * Extracts the SHA-256 hash from a server function pathname.\n * Server function pathnames are structured as `/_serverFn/<hash>`.\n * This function matches the pattern and returns the hash if found.\n *\n * @param pathname - the pathname of the server function\n * @returns the sha256 of the server function\n */\nexport function extractServerFunctionSha256(pathname: string): string {\n const serverFnMatch = pathname.match(/\\/_serverFn\\/([a-f0-9]{64})/i);\n return serverFnMatch?.[1] ?? 'unknown';\n}\n\n/**\n * Returns span options for TanStack Start middleware spans.\n */\nexport function getMiddlewareSpanOptions(name: string): StartSpanOptions {\n return {\n op: 'middleware.tanstackstart',\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: '
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../src/server/utils.ts"],"sourcesContent":["import type { StartSpanOptions } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/node';\n\n/**\n * Extracts the SHA-256 hash from a server function pathname.\n * Server function pathnames are structured as `/_serverFn/<hash>`.\n * This function matches the pattern and returns the hash if found.\n *\n * @param pathname - the pathname of the server function\n * @returns the sha256 of the server function\n */\nexport function extractServerFunctionSha256(pathname: string): string {\n const serverFnMatch = pathname.match(/\\/_serverFn\\/([a-f0-9]{64})/i);\n return serverFnMatch?.[1] ?? 'unknown';\n}\n\n/**\n * Returns span options for TanStack Start middleware spans.\n */\nexport function getMiddlewareSpanOptions(name: string): StartSpanOptions {\n return {\n op: 'middleware.tanstackstart',\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.middleware.tanstackstart',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'middleware.tanstackstart',\n },\n };\n}\n"],"names":["SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP"],"mappings":";;;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,2BAA2B,CAAC,QAAQ,EAAkB;AACtE,EAAE,MAAM,gBAAgB,QAAQ,CAAC,KAAK,CAAC,8BAA8B,CAAC;AACtE,EAAE,OAAO,aAAa,GAAG,CAAC,CAAA,IAAK,SAAS;AACxC;;AAEA;AACA;AACA;AACO,SAAS,wBAAwB,CAAC,IAAI,EAA4B;AACzE,EAAE,OAAO;AACT,IAAI,EAAE,EAAE,0BAA0B;AAClC,IAAI,IAAI;AACR,IAAI,UAAU,EAAE;AAChB,MAAM,CAACA,qCAAgC,GAAG,+BAA+B;AACzE,MAAM,CAACC,iCAA4B,GAAG,0BAA0B;AAChE,KAAK;AACL,GAAG;AACH;;;;;"}
|
|
@@ -1,8 +1,79 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
|
|
4
|
+
* Core function that wraps middleware arrays matching the given regex.
|
|
5
|
+
*/
|
|
6
|
+
function wrapMiddlewareArrays(code, id, debug, regex) {
|
|
7
|
+
const skipped = [];
|
|
8
|
+
let didWrap = false;
|
|
9
|
+
|
|
10
|
+
const transformed = code.replace(regex, (match, key, contents) => {
|
|
11
|
+
const objContents = arrayToObjectShorthand(contents);
|
|
12
|
+
if (objContents) {
|
|
13
|
+
didWrap = true;
|
|
14
|
+
if (debug) {
|
|
15
|
+
// eslint-disable-next-line no-console
|
|
16
|
+
console.log(`[Sentry] Auto-wrapping ${key} in ${id}`);
|
|
17
|
+
}
|
|
18
|
+
// Handle method call syntax like `.middleware([...])` vs object property syntax like `middleware: [...]`
|
|
19
|
+
if (key.endsWith('(')) {
|
|
20
|
+
return `${key}wrapMiddlewaresWithSentry(${objContents}))`;
|
|
21
|
+
}
|
|
22
|
+
return `${key}: wrapMiddlewaresWithSentry(${objContents})`;
|
|
23
|
+
}
|
|
24
|
+
// Track middlewares that couldn't be auto-wrapped
|
|
25
|
+
// Skip if we matched whitespace only
|
|
26
|
+
if (contents.trim()) {
|
|
27
|
+
skipped.push(key);
|
|
28
|
+
}
|
|
29
|
+
return match;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return { code: transformed, didWrap, skipped };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.
|
|
37
|
+
*/
|
|
38
|
+
function wrapGlobalMiddleware(code, id, debug) {
|
|
39
|
+
return wrapMiddlewareArrays(code, id, debug, /(requestMiddleware|functionMiddleware)\s*:\s*\[([^\]]*)\]/g);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Wraps route middleware arrays in createFileRoute() files.
|
|
44
|
+
*/
|
|
45
|
+
function wrapRouteMiddleware(code, id, debug) {
|
|
46
|
+
return wrapMiddlewareArrays(code, id, debug, /(middleware)\s*:\s*\[([^\]]*)\]/g);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Wraps middleware arrays in createServerFn().middleware([...]) calls.
|
|
51
|
+
*/
|
|
52
|
+
function wrapServerFnMiddleware(code, id, debug) {
|
|
53
|
+
return wrapMiddlewareArrays(code, id, debug, /(\.middleware\s*\()\s*\[([^\]]*)\]\s*\)/g);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Applies a wrap function to the current state and returns the updated state.
|
|
58
|
+
*/
|
|
59
|
+
function applyWrap(
|
|
60
|
+
state,
|
|
61
|
+
wrapFn,
|
|
62
|
+
id,
|
|
63
|
+
debug,
|
|
64
|
+
) {
|
|
65
|
+
const result = wrapFn(state.code, id, debug);
|
|
66
|
+
return {
|
|
67
|
+
code: result.code,
|
|
68
|
+
needsImport: state.needsImport || result.didWrap,
|
|
69
|
+
skippedMiddlewares: [...state.skippedMiddlewares, ...result.skipped],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A Vite plugin that automatically instruments TanStack Start middlewares:
|
|
75
|
+
* - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`
|
|
76
|
+
* - `middleware` arrays in `createFileRoute()` route definitions
|
|
6
77
|
*/
|
|
7
78
|
function makeAutoInstrumentMiddlewarePlugin(options = {}) {
|
|
8
79
|
const { enabled = true, debug = false } = options;
|
|
@@ -21,9 +92,12 @@ function makeAutoInstrumentMiddlewarePlugin(options = {}) {
|
|
|
21
92
|
return null;
|
|
22
93
|
}
|
|
23
94
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
95
|
+
// Detect file types that should be instrumented
|
|
96
|
+
const isStartFile = id.includes('start') && code.includes('createStart(');
|
|
97
|
+
const isRouteFile = code.includes('createFileRoute(') && /middleware\s*:\s*\[/.test(code);
|
|
98
|
+
const isServerFnFile = code.includes('createServerFn') && /\.middleware\s*\(\s*\[/.test(code);
|
|
99
|
+
|
|
100
|
+
if (!isStartFile && !isRouteFile && !isServerFnFile) {
|
|
27
101
|
return null;
|
|
28
102
|
}
|
|
29
103
|
|
|
@@ -32,58 +106,38 @@ function makeAutoInstrumentMiddlewarePlugin(options = {}) {
|
|
|
32
106
|
return null;
|
|
33
107
|
}
|
|
34
108
|
|
|
35
|
-
let
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Track middlewares that couldn't be auto-wrapped
|
|
52
|
-
// Skip if we matched whitespace only
|
|
53
|
-
if (contents.trim()) {
|
|
54
|
-
skippedMiddlewares.push(key);
|
|
55
|
-
}
|
|
56
|
-
return match;
|
|
57
|
-
},
|
|
58
|
-
);
|
|
109
|
+
let fileTransformState = {
|
|
110
|
+
code,
|
|
111
|
+
needsImport: false,
|
|
112
|
+
skippedMiddlewares: [],
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Wrap middlewares
|
|
116
|
+
if (isStartFile) {
|
|
117
|
+
fileTransformState = applyWrap(fileTransformState, wrapGlobalMiddleware, id, debug);
|
|
118
|
+
}
|
|
119
|
+
if (isRouteFile) {
|
|
120
|
+
fileTransformState = applyWrap(fileTransformState, wrapRouteMiddleware, id, debug);
|
|
121
|
+
}
|
|
122
|
+
if (isServerFnFile) {
|
|
123
|
+
fileTransformState = applyWrap(fileTransformState, wrapServerFnMiddleware, id, debug);
|
|
124
|
+
}
|
|
59
125
|
|
|
60
126
|
// Warn about middlewares that couldn't be auto-wrapped
|
|
61
|
-
if (skippedMiddlewares.length > 0) {
|
|
127
|
+
if (fileTransformState.skippedMiddlewares.length > 0) {
|
|
62
128
|
// eslint-disable-next-line no-console
|
|
63
129
|
console.warn(
|
|
64
|
-
`[Sentry] Could not auto-instrument ${skippedMiddlewares.join(' and ')} in ${id}. ` +
|
|
130
|
+
`[Sentry] Could not auto-instrument ${fileTransformState.skippedMiddlewares.join(' and ')} in ${id}. ` +
|
|
65
131
|
'To instrument these middlewares, use wrapMiddlewaresWithSentry() manually. ',
|
|
66
132
|
);
|
|
67
133
|
}
|
|
68
134
|
|
|
69
135
|
// We didn't wrap any middlewares, so we don't need to import the wrapMiddlewaresWithSentry function
|
|
70
|
-
if (!needsImport) {
|
|
136
|
+
if (!fileTransformState.needsImport) {
|
|
71
137
|
return null;
|
|
72
138
|
}
|
|
73
139
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// Check for 'use server' or 'use client' directives, these need to be before any imports
|
|
77
|
-
const directiveMatch = transformed.match(/^(['"])use (client|server)\1;?\s*\n?/);
|
|
78
|
-
if (directiveMatch) {
|
|
79
|
-
// Insert import after the directive
|
|
80
|
-
const directive = directiveMatch[0];
|
|
81
|
-
transformed = directive + sentryImport + transformed.slice(directive.length);
|
|
82
|
-
} else {
|
|
83
|
-
transformed = sentryImport + transformed;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return { code: transformed, map: null };
|
|
140
|
+
return { code: addSentryImport(fileTransformState.code), map: null };
|
|
87
141
|
},
|
|
88
142
|
};
|
|
89
143
|
}
|
|
@@ -113,6 +167,33 @@ function arrayToObjectShorthand(contents) {
|
|
|
113
167
|
return `{ ${uniqueItems.join(', ')} }`;
|
|
114
168
|
}
|
|
115
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Adds the wrapMiddlewaresWithSentry import to the code.
|
|
172
|
+
* Handles 'use client' and 'use server' directives by inserting the import after them.
|
|
173
|
+
*/
|
|
174
|
+
function addSentryImport(code) {
|
|
175
|
+
const sentryImport = "import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\n";
|
|
176
|
+
|
|
177
|
+
// Don't add the import if it already exists
|
|
178
|
+
if (code.includes(sentryImport.trimEnd())) {
|
|
179
|
+
return code;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check for 'use server' or 'use client' directives, these need to be before any imports
|
|
183
|
+
const directiveMatch = code.match(/^(['"])use (client|server)\1;?\s*\n?/);
|
|
184
|
+
|
|
185
|
+
if (!directiveMatch) {
|
|
186
|
+
return sentryImport + code;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const directive = directiveMatch[0];
|
|
190
|
+
return directive + sentryImport + code.slice(directive.length);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
exports.addSentryImport = addSentryImport;
|
|
116
194
|
exports.arrayToObjectShorthand = arrayToObjectShorthand;
|
|
117
195
|
exports.makeAutoInstrumentMiddlewarePlugin = makeAutoInstrumentMiddlewarePlugin;
|
|
196
|
+
exports.wrapGlobalMiddleware = wrapGlobalMiddleware;
|
|
197
|
+
exports.wrapRouteMiddleware = wrapRouteMiddleware;
|
|
198
|
+
exports.wrapServerFnMiddleware = wrapServerFnMiddleware;
|
|
118
199
|
//# sourceMappingURL=autoInstrumentMiddleware.js.map
|
|
@@ -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\n/**\n * A Vite plugin that automatically instruments TanStack Start middlewares\n * by wrapping `requestMiddleware` and `functionMiddleware` arrays in `createStart()`.\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 // Only wrap requestMiddleware and functionMiddleware in createStart()\n // createStart() should always be in a file named start.ts\n if (!id.includes('start') || !code.includes('createStart(')) {\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 transformed = code;\n let needsImport = false;\n const skippedMiddlewares: string[] = [];\n\n transformed = transformed.replace(\n /(requestMiddleware|functionMiddleware)\\s*:\\s*\\[([^\\]]*)\\]/g,\n (match: string, key: string, contents: string) => {\n const objContents = arrayToObjectShorthand(contents);\n if (objContents) {\n needsImport = true;\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(`[Sentry] Auto-wrapping ${key} in ${id}`);\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 skippedMiddlewares.push(key);\n }\n return match;\n },\n );\n\n // Warn about middlewares that couldn't be auto-wrapped\n if (skippedMiddlewares.length > 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] Could not auto-instrument ${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 (!needsImport) {\n return null;\n }\n\n const sentryImport = \"import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\\n\";\n\n // Check for 'use server' or 'use client' directives, these need to be before any imports\n const directiveMatch = transformed.match(/^(['\"])use (client|server)\\1;?\\s*\\n?/);\n if (directiveMatch) {\n // Insert import after the directive\n const directive = directiveMatch[0];\n transformed = directive + sentryImport + transformed.slice(directive.length);\n } else {\n transformed = sentryImport + transformed;\n }\n\n return { code: transformed, 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"],"names":[],"mappings":"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"}
|
|
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,7 +1,7 @@
|
|
|
1
1
|
export { ErrorBoundary, withErrorBoundary } from './server/index.js';
|
|
2
2
|
export { sentryTanstackStart } from './vite/sentryTanstackStart.js';
|
|
3
|
+
export * from '@sentry/node';
|
|
3
4
|
export { init } from './server/sdk.js';
|
|
4
5
|
export { wrapFetchWithSentry } from './server/wrapFetchWithSentry.js';
|
|
5
6
|
export { wrapMiddlewaresWithSentry } from './server/middleware.js';
|
|
6
|
-
export * from '@sentry/node';
|
|
7
7
|
//# sourceMappingURL=index.server.js.map
|
package/build/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"10.
|
|
1
|
+
{"type":"module","version":"10.39.0-alpha.0","sideEffects":false}
|
|
@@ -21,7 +21,7 @@ function getMiddlewareSpanOptions(name) {
|
|
|
21
21
|
op: 'middleware.tanstackstart',
|
|
22
22
|
name,
|
|
23
23
|
attributes: {
|
|
24
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: '
|
|
24
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.middleware.tanstackstart',
|
|
25
25
|
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'middleware.tanstackstart',
|
|
26
26
|
},
|
|
27
27
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../src/server/utils.ts"],"sourcesContent":["import type { StartSpanOptions } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/node';\n\n/**\n * Extracts the SHA-256 hash from a server function pathname.\n * Server function pathnames are structured as `/_serverFn/<hash>`.\n * This function matches the pattern and returns the hash if found.\n *\n * @param pathname - the pathname of the server function\n * @returns the sha256 of the server function\n */\nexport function extractServerFunctionSha256(pathname: string): string {\n const serverFnMatch = pathname.match(/\\/_serverFn\\/([a-f0-9]{64})/i);\n return serverFnMatch?.[1] ?? 'unknown';\n}\n\n/**\n * Returns span options for TanStack Start middleware spans.\n */\nexport function getMiddlewareSpanOptions(name: string): StartSpanOptions {\n return {\n op: 'middleware.tanstackstart',\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: '
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../src/server/utils.ts"],"sourcesContent":["import type { StartSpanOptions } from '@sentry/core';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/node';\n\n/**\n * Extracts the SHA-256 hash from a server function pathname.\n * Server function pathnames are structured as `/_serverFn/<hash>`.\n * This function matches the pattern and returns the hash if found.\n *\n * @param pathname - the pathname of the server function\n * @returns the sha256 of the server function\n */\nexport function extractServerFunctionSha256(pathname: string): string {\n const serverFnMatch = pathname.match(/\\/_serverFn\\/([a-f0-9]{64})/i);\n return serverFnMatch?.[1] ?? 'unknown';\n}\n\n/**\n * Returns span options for TanStack Start middleware spans.\n */\nexport function getMiddlewareSpanOptions(name: string): StartSpanOptions {\n return {\n op: 'middleware.tanstackstart',\n name,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.middleware.tanstackstart',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'middleware.tanstackstart',\n },\n };\n}\n"],"names":[],"mappings":";;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,2BAA2B,CAAC,QAAQ,EAAkB;AACtE,EAAE,MAAM,gBAAgB,QAAQ,CAAC,KAAK,CAAC,8BAA8B,CAAC;AACtE,EAAE,OAAO,aAAa,GAAG,CAAC,CAAA,IAAK,SAAS;AACxC;;AAEA;AACA;AACA;AACO,SAAS,wBAAwB,CAAC,IAAI,EAA4B;AACzE,EAAE,OAAO;AACT,IAAI,EAAE,EAAE,0BAA0B;AAClC,IAAI,IAAI;AACR,IAAI,UAAU,EAAE;AAChB,MAAM,CAAC,gCAAgC,GAAG,+BAA+B;AACzE,MAAM,CAAC,4BAA4B,GAAG,0BAA0B;AAChE,KAAK;AACL,GAAG;AACH;;;;"}
|
|
@@ -1,6 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
2
|
+
* Core function that wraps middleware arrays matching the given regex.
|
|
3
|
+
*/
|
|
4
|
+
function wrapMiddlewareArrays(code, id, debug, regex) {
|
|
5
|
+
const skipped = [];
|
|
6
|
+
let didWrap = false;
|
|
7
|
+
|
|
8
|
+
const transformed = code.replace(regex, (match, key, contents) => {
|
|
9
|
+
const objContents = arrayToObjectShorthand(contents);
|
|
10
|
+
if (objContents) {
|
|
11
|
+
didWrap = true;
|
|
12
|
+
if (debug) {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.log(`[Sentry] Auto-wrapping ${key} in ${id}`);
|
|
15
|
+
}
|
|
16
|
+
// Handle method call syntax like `.middleware([...])` vs object property syntax like `middleware: [...]`
|
|
17
|
+
if (key.endsWith('(')) {
|
|
18
|
+
return `${key}wrapMiddlewaresWithSentry(${objContents}))`;
|
|
19
|
+
}
|
|
20
|
+
return `${key}: wrapMiddlewaresWithSentry(${objContents})`;
|
|
21
|
+
}
|
|
22
|
+
// Track middlewares that couldn't be auto-wrapped
|
|
23
|
+
// Skip if we matched whitespace only
|
|
24
|
+
if (contents.trim()) {
|
|
25
|
+
skipped.push(key);
|
|
26
|
+
}
|
|
27
|
+
return match;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return { code: transformed, didWrap, skipped };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.
|
|
35
|
+
*/
|
|
36
|
+
function wrapGlobalMiddleware(code, id, debug) {
|
|
37
|
+
return wrapMiddlewareArrays(code, id, debug, /(requestMiddleware|functionMiddleware)\s*:\s*\[([^\]]*)\]/g);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Wraps route middleware arrays in createFileRoute() files.
|
|
42
|
+
*/
|
|
43
|
+
function wrapRouteMiddleware(code, id, debug) {
|
|
44
|
+
return wrapMiddlewareArrays(code, id, debug, /(middleware)\s*:\s*\[([^\]]*)\]/g);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Wraps middleware arrays in createServerFn().middleware([...]) calls.
|
|
49
|
+
*/
|
|
50
|
+
function wrapServerFnMiddleware(code, id, debug) {
|
|
51
|
+
return wrapMiddlewareArrays(code, id, debug, /(\.middleware\s*\()\s*\[([^\]]*)\]\s*\)/g);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Applies a wrap function to the current state and returns the updated state.
|
|
56
|
+
*/
|
|
57
|
+
function applyWrap(
|
|
58
|
+
state,
|
|
59
|
+
wrapFn,
|
|
60
|
+
id,
|
|
61
|
+
debug,
|
|
62
|
+
) {
|
|
63
|
+
const result = wrapFn(state.code, id, debug);
|
|
64
|
+
return {
|
|
65
|
+
code: result.code,
|
|
66
|
+
needsImport: state.needsImport || result.didWrap,
|
|
67
|
+
skippedMiddlewares: [...state.skippedMiddlewares, ...result.skipped],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A Vite plugin that automatically instruments TanStack Start middlewares:
|
|
73
|
+
* - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`
|
|
74
|
+
* - `middleware` arrays in `createFileRoute()` route definitions
|
|
4
75
|
*/
|
|
5
76
|
function makeAutoInstrumentMiddlewarePlugin(options = {}) {
|
|
6
77
|
const { enabled = true, debug = false } = options;
|
|
@@ -19,9 +90,12 @@ function makeAutoInstrumentMiddlewarePlugin(options = {}) {
|
|
|
19
90
|
return null;
|
|
20
91
|
}
|
|
21
92
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
93
|
+
// Detect file types that should be instrumented
|
|
94
|
+
const isStartFile = id.includes('start') && code.includes('createStart(');
|
|
95
|
+
const isRouteFile = code.includes('createFileRoute(') && /middleware\s*:\s*\[/.test(code);
|
|
96
|
+
const isServerFnFile = code.includes('createServerFn') && /\.middleware\s*\(\s*\[/.test(code);
|
|
97
|
+
|
|
98
|
+
if (!isStartFile && !isRouteFile && !isServerFnFile) {
|
|
25
99
|
return null;
|
|
26
100
|
}
|
|
27
101
|
|
|
@@ -30,58 +104,38 @@ function makeAutoInstrumentMiddlewarePlugin(options = {}) {
|
|
|
30
104
|
return null;
|
|
31
105
|
}
|
|
32
106
|
|
|
33
|
-
let
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// Track middlewares that couldn't be auto-wrapped
|
|
50
|
-
// Skip if we matched whitespace only
|
|
51
|
-
if (contents.trim()) {
|
|
52
|
-
skippedMiddlewares.push(key);
|
|
53
|
-
}
|
|
54
|
-
return match;
|
|
55
|
-
},
|
|
56
|
-
);
|
|
107
|
+
let fileTransformState = {
|
|
108
|
+
code,
|
|
109
|
+
needsImport: false,
|
|
110
|
+
skippedMiddlewares: [],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Wrap middlewares
|
|
114
|
+
if (isStartFile) {
|
|
115
|
+
fileTransformState = applyWrap(fileTransformState, wrapGlobalMiddleware, id, debug);
|
|
116
|
+
}
|
|
117
|
+
if (isRouteFile) {
|
|
118
|
+
fileTransformState = applyWrap(fileTransformState, wrapRouteMiddleware, id, debug);
|
|
119
|
+
}
|
|
120
|
+
if (isServerFnFile) {
|
|
121
|
+
fileTransformState = applyWrap(fileTransformState, wrapServerFnMiddleware, id, debug);
|
|
122
|
+
}
|
|
57
123
|
|
|
58
124
|
// Warn about middlewares that couldn't be auto-wrapped
|
|
59
|
-
if (skippedMiddlewares.length > 0) {
|
|
125
|
+
if (fileTransformState.skippedMiddlewares.length > 0) {
|
|
60
126
|
// eslint-disable-next-line no-console
|
|
61
127
|
console.warn(
|
|
62
|
-
`[Sentry] Could not auto-instrument ${skippedMiddlewares.join(' and ')} in ${id}. ` +
|
|
128
|
+
`[Sentry] Could not auto-instrument ${fileTransformState.skippedMiddlewares.join(' and ')} in ${id}. ` +
|
|
63
129
|
'To instrument these middlewares, use wrapMiddlewaresWithSentry() manually. ',
|
|
64
130
|
);
|
|
65
131
|
}
|
|
66
132
|
|
|
67
133
|
// We didn't wrap any middlewares, so we don't need to import the wrapMiddlewaresWithSentry function
|
|
68
|
-
if (!needsImport) {
|
|
134
|
+
if (!fileTransformState.needsImport) {
|
|
69
135
|
return null;
|
|
70
136
|
}
|
|
71
137
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Check for 'use server' or 'use client' directives, these need to be before any imports
|
|
75
|
-
const directiveMatch = transformed.match(/^(['"])use (client|server)\1;?\s*\n?/);
|
|
76
|
-
if (directiveMatch) {
|
|
77
|
-
// Insert import after the directive
|
|
78
|
-
const directive = directiveMatch[0];
|
|
79
|
-
transformed = directive + sentryImport + transformed.slice(directive.length);
|
|
80
|
-
} else {
|
|
81
|
-
transformed = sentryImport + transformed;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return { code: transformed, map: null };
|
|
138
|
+
return { code: addSentryImport(fileTransformState.code), map: null };
|
|
85
139
|
},
|
|
86
140
|
};
|
|
87
141
|
}
|
|
@@ -111,5 +165,28 @@ function arrayToObjectShorthand(contents) {
|
|
|
111
165
|
return `{ ${uniqueItems.join(', ')} }`;
|
|
112
166
|
}
|
|
113
167
|
|
|
114
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Adds the wrapMiddlewaresWithSentry import to the code.
|
|
170
|
+
* Handles 'use client' and 'use server' directives by inserting the import after them.
|
|
171
|
+
*/
|
|
172
|
+
function addSentryImport(code) {
|
|
173
|
+
const sentryImport = "import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\n";
|
|
174
|
+
|
|
175
|
+
// Don't add the import if it already exists
|
|
176
|
+
if (code.includes(sentryImport.trimEnd())) {
|
|
177
|
+
return code;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check for 'use server' or 'use client' directives, these need to be before any imports
|
|
181
|
+
const directiveMatch = code.match(/^(['"])use (client|server)\1;?\s*\n?/);
|
|
182
|
+
|
|
183
|
+
if (!directiveMatch) {
|
|
184
|
+
return sentryImport + code;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const directive = directiveMatch[0];
|
|
188
|
+
return directive + sentryImport + code.slice(directive.length);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export { addSentryImport, arrayToObjectShorthand, makeAutoInstrumentMiddlewarePlugin, wrapGlobalMiddleware, wrapRouteMiddleware, wrapServerFnMiddleware };
|
|
115
192
|
//# sourceMappingURL=autoInstrumentMiddleware.js.map
|
|
@@ -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\n/**\n * A Vite plugin that automatically instruments TanStack Start middlewares\n * by wrapping `requestMiddleware` and `functionMiddleware` arrays in `createStart()`.\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 // Only wrap requestMiddleware and functionMiddleware in createStart()\n // createStart() should always be in a file named start.ts\n if (!id.includes('start') || !code.includes('createStart(')) {\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 transformed = code;\n let needsImport = false;\n const skippedMiddlewares: string[] = [];\n\n transformed = transformed.replace(\n /(requestMiddleware|functionMiddleware)\\s*:\\s*\\[([^\\]]*)\\]/g,\n (match: string, key: string, contents: string) => {\n const objContents = arrayToObjectShorthand(contents);\n if (objContents) {\n needsImport = true;\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(`[Sentry] Auto-wrapping ${key} in ${id}`);\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 skippedMiddlewares.push(key);\n }\n return match;\n },\n );\n\n // Warn about middlewares that couldn't be auto-wrapped\n if (skippedMiddlewares.length > 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Sentry] Could not auto-instrument ${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 (!needsImport) {\n return null;\n }\n\n const sentryImport = \"import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\\n\";\n\n // Check for 'use server' or 'use client' directives, these need to be before any imports\n const directiveMatch = transformed.match(/^(['\"])use (client|server)\\1;?\\s*\\n?/);\n if (directiveMatch) {\n // Insert import after the directive\n const directive = directiveMatch[0];\n transformed = directive + sentryImport + transformed.slice(directive.length);\n } else {\n transformed = sentryImport + transformed;\n }\n\n return { code: transformed, 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"],"names":[],"mappings":"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"}
|
|
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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"}
|
|
@@ -3,9 +3,27 @@ type AutoInstrumentMiddlewareOptions = {
|
|
|
3
3
|
enabled?: boolean;
|
|
4
4
|
debug?: boolean;
|
|
5
5
|
};
|
|
6
|
+
type WrapResult = {
|
|
7
|
+
code: string;
|
|
8
|
+
didWrap: boolean;
|
|
9
|
+
skipped: string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.
|
|
13
|
+
*/
|
|
14
|
+
export declare function wrapGlobalMiddleware(code: string, id: string, debug: boolean): WrapResult;
|
|
15
|
+
/**
|
|
16
|
+
* Wraps route middleware arrays in createFileRoute() files.
|
|
17
|
+
*/
|
|
18
|
+
export declare function wrapRouteMiddleware(code: string, id: string, debug: boolean): WrapResult;
|
|
6
19
|
/**
|
|
7
|
-
*
|
|
8
|
-
|
|
20
|
+
* Wraps middleware arrays in createServerFn().middleware([...]) calls.
|
|
21
|
+
*/
|
|
22
|
+
export declare function wrapServerFnMiddleware(code: string, id: string, debug: boolean): WrapResult;
|
|
23
|
+
/**
|
|
24
|
+
* A Vite plugin that automatically instruments TanStack Start middlewares:
|
|
25
|
+
* - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`
|
|
26
|
+
* - `middleware` arrays in `createFileRoute()` route definitions
|
|
9
27
|
*/
|
|
10
28
|
export declare function makeAutoInstrumentMiddlewarePlugin(options?: AutoInstrumentMiddlewareOptions): Plugin;
|
|
11
29
|
/**
|
|
@@ -16,5 +34,10 @@ export declare function makeAutoInstrumentMiddlewarePlugin(options?: AutoInstrum
|
|
|
16
34
|
* which cannot be converted to object shorthand.
|
|
17
35
|
*/
|
|
18
36
|
export declare function arrayToObjectShorthand(contents: string): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Adds the wrapMiddlewaresWithSentry import to the code.
|
|
39
|
+
* Handles 'use client' and 'use server' directives by inserting the import after them.
|
|
40
|
+
*/
|
|
41
|
+
export declare function addSentryImport(code: string): string;
|
|
19
42
|
export {};
|
|
20
43
|
//# sourceMappingURL=autoInstrumentMiddleware.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoInstrumentMiddleware.d.ts","sourceRoot":"","sources":["../../../src/vite/autoInstrumentMiddleware.ts"],"names":[],"mappings":"AAAA,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
|
|
1
|
+
{"version":3,"file":"autoInstrumentMiddleware.d.ts","sourceRoot":"","sources":["../../../src/vite/autoInstrumentMiddleware.ts"],"names":[],"mappings":"AAAA,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"}
|
|
@@ -3,9 +3,27 @@ type AutoInstrumentMiddlewareOptions = {
|
|
|
3
3
|
enabled?: boolean;
|
|
4
4
|
debug?: boolean;
|
|
5
5
|
};
|
|
6
|
+
type WrapResult = {
|
|
7
|
+
code: string;
|
|
8
|
+
didWrap: boolean;
|
|
9
|
+
skipped: string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.
|
|
13
|
+
*/
|
|
14
|
+
export declare function wrapGlobalMiddleware(code: string, id: string, debug: boolean): WrapResult;
|
|
15
|
+
/**
|
|
16
|
+
* Wraps route middleware arrays in createFileRoute() files.
|
|
17
|
+
*/
|
|
18
|
+
export declare function wrapRouteMiddleware(code: string, id: string, debug: boolean): WrapResult;
|
|
6
19
|
/**
|
|
7
|
-
*
|
|
8
|
-
|
|
20
|
+
* Wraps middleware arrays in createServerFn().middleware([...]) calls.
|
|
21
|
+
*/
|
|
22
|
+
export declare function wrapServerFnMiddleware(code: string, id: string, debug: boolean): WrapResult;
|
|
23
|
+
/**
|
|
24
|
+
* A Vite plugin that automatically instruments TanStack Start middlewares:
|
|
25
|
+
* - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`
|
|
26
|
+
* - `middleware` arrays in `createFileRoute()` route definitions
|
|
9
27
|
*/
|
|
10
28
|
export declare function makeAutoInstrumentMiddlewarePlugin(options?: AutoInstrumentMiddlewareOptions): Plugin;
|
|
11
29
|
/**
|
|
@@ -16,5 +34,10 @@ export declare function makeAutoInstrumentMiddlewarePlugin(options?: AutoInstrum
|
|
|
16
34
|
* which cannot be converted to object shorthand.
|
|
17
35
|
*/
|
|
18
36
|
export declare function arrayToObjectShorthand(contents: string): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Adds the wrapMiddlewaresWithSentry import to the code.
|
|
39
|
+
* Handles 'use client' and 'use server' directives by inserting the import after them.
|
|
40
|
+
*/
|
|
41
|
+
export declare function addSentryImport(code: string): string;
|
|
19
42
|
export {};
|
|
20
43
|
//# sourceMappingURL=autoInstrumentMiddleware.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/tanstackstart-react",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.39.0-alpha.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",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@opentelemetry/api": "^1.9.0",
|
|
54
54
|
"@opentelemetry/semantic-conventions": "^1.37.0",
|
|
55
|
-
"@sentry-internal/browser-utils": "10.
|
|
56
|
-
"@sentry/core": "10.
|
|
57
|
-
"@sentry/node": "10.
|
|
58
|
-
"@sentry/react": "10.
|
|
59
|
-
"@sentry/vite-plugin": "^4.
|
|
55
|
+
"@sentry-internal/browser-utils": "10.39.0-alpha.0",
|
|
56
|
+
"@sentry/core": "10.39.0-alpha.0",
|
|
57
|
+
"@sentry/node": "10.39.0-alpha.0",
|
|
58
|
+
"@sentry/react": "10.39.0-alpha.0",
|
|
59
|
+
"@sentry/vite-plugin": "^4.8.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"vite": "^5.4.11"
|