autotel-tanstack 1.4.0 → 1.4.2

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/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # autotel-tanstack
2
2
 
3
- OpenTelemetry instrumentation for TanStack Start applications. Automatic tracing for server functions, middleware, and route loaders.
3
+ OpenTelemetry instrumentation for TanStack Start applications. Automatic tracing for server functions, middleware, and route loaders using TanStack's native patterns.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Zero-Config Option** - Just import `autotel-tanstack/auto` and you're done
8
- - **Framework-Aligned API** - Uses TanStack's middleware patterns
7
+ - **TanStack-Native** - Uses `createMiddleware().server()` builder pattern
8
+ - **Zero Boilerplate** - Global middleware traces all server functions automatically
9
9
  - **Full Coverage** - Server functions, loaders, beforeLoad, HTTP requests
10
10
  - **Tree-Shakeable** - Only bundle what you use
11
11
  - **Type-Safe** - Full TypeScript support
@@ -17,91 +17,120 @@ OpenTelemetry instrumentation for TanStack Start applications. Automatic tracing
17
17
  npm install autotel-tanstack autotel
18
18
  # or
19
19
  pnpm add autotel-tanstack autotel
20
- # or
21
- yarn add autotel-tanstack autotel
22
20
  ```
23
21
 
24
22
  ## Quick Start
25
23
 
26
- ### Option 1: Zero-Config (Recommended)
24
+ ### TanStack-Native Setup (Recommended)
25
+
26
+ Configure global middleware in `start.ts` using TanStack's native patterns:
27
27
 
28
28
  ```typescript
29
- // app/start.ts (React) or app/start.ts (Solid)
30
- import 'autotel-tanstack/auto';
31
- import { createStart } from '@tanstack/react-start';
29
+ // src/start.ts
30
+ import { createStart, createMiddleware } from '@tanstack/react-start';
31
+ import { createTracingServerHandler } from 'autotel-tanstack/middleware';
32
+ import './instrumentation'; // Initialize autotel
33
+
34
+ // Global request tracing middleware
35
+ const requestTracingMiddleware = createMiddleware().server(
36
+ createTracingServerHandler({
37
+ captureHeaders: ['x-request-id', 'user-agent'],
38
+ excludePaths: ['/health', '/metrics'],
39
+ }),
40
+ );
32
41
 
33
- // Set env vars: OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT
34
- export const startInstance = createStart(() => ({}));
35
- ```
42
+ // Global server function tracing middleware
43
+ const functionTracingMiddleware = createMiddleware({ type: 'function' }).server(
44
+ createTracingServerHandler({
45
+ type: 'function',
46
+ captureArgs: true,
47
+ }),
48
+ );
36
49
 
37
- ### Option 2: Middleware-Based
50
+ export const startInstance = createStart(() => ({
51
+ requestMiddleware: [requestTracingMiddleware],
52
+ functionMiddleware: [functionTracingMiddleware],
53
+ }));
54
+ ```
38
55
 
39
56
  ```typescript
40
- // app/start.ts
41
- import { createStart } from '@tanstack/react-start';
42
- import { tracingMiddleware } from 'autotel-tanstack/middleware';
57
+ // src/instrumentation.ts
43
58
  import { init } from 'autotel';
44
59
 
45
- // Initialize autotel
46
60
  init({
47
61
  service: 'my-app',
48
- endpoint: 'https://api.honeycomb.io',
49
- headers: { 'x-honeycomb-team': process.env.HONEYCOMB_API_KEY },
62
+ endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
50
63
  });
51
-
52
- export const startInstance = createStart(() => ({
53
- requestMiddleware: [tracingMiddleware()],
54
- }));
55
64
  ```
56
65
 
57
- ### Option 3: Handler Wrapper (Full Control)
66
+ That's it! All server functions and requests are now automatically traced.
67
+
68
+ ### Zero-Config Alternative
69
+
70
+ For quick setup using environment variables:
58
71
 
59
72
  ```typescript
60
- // server.ts
61
- import {
62
- createStartHandler,
63
- defaultStreamHandler,
64
- } from '@tanstack/react-start/server';
65
- import { wrapStartHandler } from 'autotel-tanstack/handlers';
73
+ // src/start.ts
74
+ import 'autotel-tanstack/auto';
75
+ import { createStart } from '@tanstack/react-start';
66
76
 
67
- export default wrapStartHandler({
68
- service: 'my-app',
69
- endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
70
- headers: { 'x-honeycomb-team': process.env.HONEYCOMB_API_KEY },
71
- })(createStartHandler(defaultStreamHandler));
77
+ // Set env vars: OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT
78
+ export const startInstance = createStart(() => ({}));
72
79
  ```
73
80
 
74
81
  ## Usage
75
82
 
76
- ### Tracing Server Functions
83
+ ### Server Functions (Auto-Traced)
84
+
85
+ With global `functionMiddleware` configured in `start.ts`, server functions are automatically traced:
77
86
 
78
87
  ```typescript
79
88
  import { createServerFn } from '@tanstack/react-start';
80
- import { functionTracingMiddleware } from 'autotel-tanstack/middleware';
81
89
 
82
- // Using middleware (recommended)
83
- export const getUser = createServerFn({ method: 'GET' })
84
- .middleware([functionTracingMiddleware()])
85
- .handler(async ({ data: id }) => {
90
+ // Automatically traced - no middleware needed per-function!
91
+ export const getUser = createServerFn({ method: 'GET' }).handler(
92
+ async ({ data: id }) => {
86
93
  return await db.users.findUnique({ where: { id } });
94
+ },
95
+ );
96
+
97
+ export const createUser = createServerFn({ method: 'POST' })
98
+ .inputValidator((d: UserInput) => d)
99
+ .handler(async ({ data }) => {
100
+ return await db.users.create({ data });
87
101
  });
102
+ ```
88
103
 
89
- // Or using explicit wrapper
90
- import { traceServerFn } from 'autotel-tanstack/server-functions';
104
+ ### Per-Function Middleware (When Needed)
91
105
 
92
- const getUserBase = createServerFn({ method: 'GET' }).handler(
93
- async ({ data: id }) => {
94
- return await db.users.findUnique({ where: { id } });
95
- },
106
+ For function-specific middleware, use TanStack's `.middleware()` chaining:
107
+
108
+ ```typescript
109
+ import { createServerFn, createMiddleware } from '@tanstack/react-start';
110
+ import { createTracingServerHandler } from 'autotel-tanstack/middleware';
111
+
112
+ // Custom middleware for this function only
113
+ const customTracing = createMiddleware({ type: 'function' }).server(
114
+ createTracingServerHandler({
115
+ type: 'function',
116
+ captureArgs: true,
117
+ captureResults: true, // Capture results for this specific function
118
+ }),
96
119
  );
97
120
 
98
- export const getUser = traceServerFn(getUserBase, { name: 'getUser' });
121
+ export const sensitiveOperation = createServerFn({ method: 'POST' })
122
+ .middleware([customTracing])
123
+ .handler(async ({ data }) => {
124
+ // ...
125
+ });
99
126
  ```
100
127
 
101
- ### Tracing Route Loaders
128
+ ### Route Loaders
129
+
130
+ Use `traceLoader` and `traceBeforeLoad` for route-level tracing:
102
131
 
103
132
  ```typescript
104
- import { createFileRoute } from '@tanstack/react-router';
133
+ import { createFileRoute, redirect } from '@tanstack/react-router';
105
134
  import { traceLoader, traceBeforeLoad } from 'autotel-tanstack/loaders';
106
135
 
107
136
  export const Route = createFileRoute('/users/$userId')({
@@ -116,32 +145,14 @@ export const Route = createFileRoute('/users/$userId')({
116
145
  });
117
146
  ```
118
147
 
119
- ### Using createTracedRoute Helper
120
-
121
- ```typescript
122
- import { createFileRoute } from '@tanstack/react-router';
123
- import { createTracedRoute } from 'autotel-tanstack/loaders';
124
-
125
- const traced = createTracedRoute('/users/$userId');
126
-
127
- export const Route = createFileRoute('/users/$userId')({
128
- beforeLoad: traced.beforeLoad(async ({ context }) => {
129
- // Auth check
130
- }),
131
- loader: traced.loader(async ({ params }) => {
132
- return await getUser(params.userId);
133
- }),
134
- });
135
- ```
136
-
137
148
  ## Configuration
138
149
 
139
- ### Middleware Configuration
150
+ ### createTracingServerHandler Options
140
151
 
141
152
  ```typescript
142
- import { createTracingMiddleware } from 'autotel-tanstack/middleware';
153
+ import { createTracingServerHandler } from 'autotel-tanstack/middleware';
143
154
 
144
- const middleware = createTracingMiddleware({
155
+ const handler = createTracingServerHandler({
145
156
  // Type: 'request' for global middleware, 'function' for server functions
146
157
  type: 'request',
147
158
 
@@ -167,27 +178,6 @@ const middleware = createTracingMiddleware({
167
178
  });
168
179
  ```
169
180
 
170
- ### Handler Configuration
171
-
172
- ```typescript
173
- import { wrapStartHandler } from 'autotel-tanstack/handlers';
174
-
175
- const handler = wrapStartHandler({
176
- // Service name (default: OTEL_SERVICE_NAME or 'tanstack-start')
177
- service: 'my-app',
178
-
179
- // OTLP endpoint (default: OTEL_EXPORTER_OTLP_ENDPOINT)
180
- endpoint: 'https://api.honeycomb.io',
181
-
182
- // OTLP headers (default: parsed from OTEL_EXPORTER_OTLP_HEADERS)
183
- headers: { 'x-honeycomb-team': 'YOUR_API_KEY' },
184
-
185
- // All middleware config options also available
186
- captureHeaders: ['x-request-id'],
187
- excludePaths: ['/health'],
188
- });
189
- ```
190
-
191
181
  ## Environment Variables
192
182
 
193
183
  | Variable | Description | Example |
@@ -221,51 +211,6 @@ const handler = wrapStartHandler({
221
211
  - `tanstack.loader.type` - "loader" or "beforeLoad"
222
212
  - `tanstack.loader.params` - Route params (if enabled)
223
213
 
224
- ## Testing
225
-
226
- ```typescript
227
- import { describe, it, beforeEach, afterEach } from 'vitest';
228
- import { createTestHarness } from 'autotel-tanstack/testing';
229
-
230
- describe('MyServerFunction', () => {
231
- let harness: ReturnType<typeof createTestHarness>;
232
-
233
- beforeEach(() => {
234
- harness = createTestHarness();
235
- });
236
-
237
- afterEach(() => {
238
- harness.reset();
239
- });
240
-
241
- it('should trace the server function', async () => {
242
- await myServerFunction({ id: '123' });
243
-
244
- harness.assertServerFnTraced('myServerFunction');
245
- harness.assertSpanHasAttribute(
246
- /tanstack\.serverFn/,
247
- 'tanstack.server_function.name',
248
- 'myServerFunction',
249
- );
250
- });
251
- });
252
- ```
253
-
254
- ### Mock Utilities
255
-
256
- ```typescript
257
- import {
258
- createMockRequest,
259
- generateTraceparent,
260
- } from 'autotel-tanstack/testing';
261
-
262
- // Create mock request
263
- const request = createMockRequest('GET', '/api/users', {
264
- headers: { 'x-request-id': 'test-123' },
265
- traceparent: generateTraceparent(),
266
- });
267
- ```
268
-
269
214
  ## Context Propagation
270
215
 
271
216
  For distributed tracing across services:
@@ -284,6 +229,25 @@ await fetch('https://api.example.com', { headers, method: 'POST', body });
284
229
  const parentContext = extractContextFromRequest(request);
285
230
  ```
286
231
 
232
+ ## Testing
233
+
234
+ ```typescript
235
+ import { describe, it, expect } from 'vitest';
236
+ import { createTestCollector } from 'autotel-tanstack/testing';
237
+
238
+ describe('MyServerFunction', () => {
239
+ it('should trace the server function', async () => {
240
+ const collector = createTestCollector();
241
+
242
+ await myServerFunction({ id: '123' });
243
+
244
+ const spans = collector.getSpans();
245
+ expect(spans).toHaveLength(1);
246
+ expect(spans[0].name).toContain('myServerFunction');
247
+ });
248
+ });
249
+ ```
250
+
287
251
  ## Supported Frameworks
288
252
 
289
253
  - **@tanstack/react-start** ^1.139.14
package/dist/auto.js CHANGED
@@ -1,4 +1,4 @@
1
- export { functionTracingMiddleware, tracingMiddleware } from './chunk-OLBHLVLE.js';
1
+ export { functionTracingMiddleware, tracingMiddleware } from './chunk-ETD6SGPH.js';
2
2
  export { traceServerFn } from './chunk-TNOQTZ3N.js';
3
3
  export { traceBeforeLoad, traceLoader } from './chunk-HKM7LMO6.js';
4
4
  import './chunk-EUYFVNYE.js';
@@ -1,7 +1,7 @@
1
1
  export { createMetricsHandler, getMetrics, metricsCollector, recordError, recordTiming, resetMetrics } from '../chunk-UMEJU65Q.js';
2
2
  export { clearErrors, createErrorReportingHandler, getRecentErrors, reportError, withErrorReporting } from '../chunk-HIQYW2HB.js';
3
3
  export { DEFAULT_CONFIG, SPAN_ATTRIBUTES } from '../chunk-MFYOV2SF.js';
4
- export { createTracingMiddleware, functionTracingMiddleware, tracingMiddleware } from '../chunk-4C7T5ZIM.js';
4
+ export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from '../chunk-A7WMQ2BC.js';
5
5
  export { createTracedServerFnFactory, traceServerFn } from '../chunk-CSFIPJC2.js';
6
6
  export { createTracedRoute, traceBeforeLoad, traceLoader } from '../chunk-MNP65ZX7.js';
7
7
  export { createTracedHeaders, extractContextFromRequest, getActiveContext, getCurrentSpanId, getCurrentTraceId, getTraceParent, getTraceState, injectContextToHeaders, runInContext } from '../chunk-DTZCOB4W.js';
@@ -1,3 +1,3 @@
1
- export { createTracingMiddleware, functionTracingMiddleware, tracingMiddleware } from '../chunk-4C7T5ZIM.js';
1
+ export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from '../chunk-A7WMQ2BC.js';
2
2
  //# sourceMappingURL=middleware.js.map
3
3
  //# sourceMappingURL=middleware.js.map
@@ -14,7 +14,12 @@ function functionTracingMiddleware(config) {
14
14
  return opts.next();
15
15
  };
16
16
  }
17
+ function createTracingServerHandler(config) {
18
+ return async function noopHandler(opts) {
19
+ return opts.next();
20
+ };
21
+ }
17
22
 
18
- export { createTracingMiddleware, functionTracingMiddleware, tracingMiddleware };
19
- //# sourceMappingURL=chunk-4C7T5ZIM.js.map
20
- //# sourceMappingURL=chunk-4C7T5ZIM.js.map
23
+ export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware };
24
+ //# sourceMappingURL=chunk-A7WMQ2BC.js.map
25
+ //# sourceMappingURL=chunk-A7WMQ2BC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/browser/middleware.ts"],"names":[],"mappings":";AA6BO,SAAS,wBACd,MAAA,EAC6B;AAE7B,EAAA,OAAO,eAAe,eAAe,IAAA,EAAM;AACzC,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB,CAAA;AACF;AAKO,SAAS,kBACd,MAAA,EAC6B;AAE7B,EAAA,OAAO,eAAe,eAAe,IAAA,EAAM;AACzC,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB,CAAA;AACF;AAKO,SAAS,0BACd,MAAA,EAC6B;AAE7B,EAAA,OAAO,eAAe,eAAe,IAAA,EAAM;AACzC,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB,CAAA;AACF;AAKO,SAAS,2BACd,MAAA,EAKsB;AAEtB,EAAA,OAAO,eAAe,YAAY,IAAA,EAAM;AACtC,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB,CAAA;AACF","file":"chunk-A7WMQ2BC.js","sourcesContent":["/**\n * Browser stub for middleware module\n *\n * In browser environments, these functions return pass-through middleware\n * that just calls next() without any tracing overhead.\n */\n\nimport type { TracingMiddlewareConfig } from './types';\n\n/**\n * Generic middleware handler type\n */\nexport interface MiddlewareHandler<TContext = unknown> {\n (opts: {\n next: (ctx?: Partial<TContext>) => Promise<TContext>;\n context: TContext;\n request?: Request;\n pathname?: string;\n data?: unknown;\n method?: string;\n filename?: string;\n functionId?: string;\n signal?: AbortSignal;\n }): Promise<TContext>;\n}\n\n/**\n * Browser stub: Returns pass-through middleware\n */\nexport function createTracingMiddleware<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): MiddlewareHandler<TContext> {\n void config;\n return async function noopMiddleware(opts) {\n return opts.next();\n };\n}\n\n/**\n * Browser stub: Returns pass-through middleware\n */\nexport function tracingMiddleware<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): MiddlewareHandler<TContext> {\n void config;\n return async function noopMiddleware(opts) {\n return opts.next();\n };\n}\n\n/**\n * Browser stub: Returns pass-through middleware\n */\nexport function functionTracingMiddleware<TContext = unknown>(\n config?: Omit<TracingMiddlewareConfig, 'type'>,\n): MiddlewareHandler<TContext> {\n void config;\n return async function noopMiddleware(opts) {\n return opts.next();\n };\n}\n\n/**\n * Browser stub: Returns pass-through handler for createMiddleware().server()\n */\nexport function createTracingServerHandler<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): (opts: {\n next: (ctx?: Partial<TContext>) => Promise<TContext>;\n context: TContext;\n request?: Request;\n}) => Promise<TContext> {\n void config;\n return async function noopHandler(opts) {\n return opts.next();\n };\n}\n"]}
@@ -214,7 +214,13 @@ function functionTracingMiddleware(config) {
214
214
  type: "function"
215
215
  });
216
216
  }
217
+ function createTracingServerHandler(config) {
218
+ const handler = createTracingMiddleware(config);
219
+ return async (opts) => {
220
+ return handler(opts);
221
+ };
222
+ }
217
223
 
218
- export { createTracingMiddleware, functionTracingMiddleware, tracingMiddleware };
219
- //# sourceMappingURL=chunk-OLBHLVLE.js.map
220
- //# sourceMappingURL=chunk-OLBHLVLE.js.map
224
+ export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware };
225
+ //# sourceMappingURL=chunk-ETD6SGPH.js.map
226
+ //# sourceMappingURL=chunk-ETD6SGPH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts"],"names":["tracingMiddleware"],"mappings":"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","file":"chunk-ETD6SGPH.js","sourcesContent":["import { context, SpanStatusCode, type Attributes } from '@opentelemetry/api';\nimport { trace, type TraceContext } from 'autotel';\nimport { extractContextFromRequest } from './context';\nimport { isServerSide } from './env';\nimport {\n type TracingMiddlewareConfig,\n DEFAULT_CONFIG,\n SPAN_ATTRIBUTES,\n} from './types';\n\n/**\n * Check if a path should be excluded from tracing\n */\nfunction shouldExcludePath(\n pathname: string,\n excludePaths: (string | RegExp)[],\n): boolean {\n for (const pattern of excludePaths) {\n if (typeof pattern === 'string') {\n // Simple glob matching\n if (pattern.includes('*')) {\n const regex = new RegExp(\n '^' + pattern.replaceAll('*', '.*').replaceAll('?', '.') + '$',\n );\n if (regex.test(pathname)) return true;\n } else {\n if (pathname === pattern || pathname.startsWith(pattern)) return true;\n }\n } else {\n if (pattern.test(pathname)) return true;\n }\n }\n return false;\n}\n\n/**\n * Build span attributes for HTTP requests\n */\nfunction buildRequestAttributes(\n request: Request,\n config: Required<\n Omit<TracingMiddlewareConfig, 'customAttributes' | 'service' | 'type'>\n >,\n): Attributes {\n const url = new URL(request.url);\n const attrs: Attributes = {\n [SPAN_ATTRIBUTES.HTTP_REQUEST_METHOD]: request.method,\n [SPAN_ATTRIBUTES.URL_PATH]: url.pathname,\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'request',\n };\n\n if (url.search) {\n attrs[SPAN_ATTRIBUTES.URL_QUERY] = url.search;\n }\n\n // Capture configured headers\n if (config.captureHeaders) {\n for (const header of config.captureHeaders) {\n const value = request.headers.get(header);\n if (value) {\n attrs[`http.request.header.${header.toLowerCase()}`] = value;\n }\n }\n }\n\n return attrs;\n}\n\n/**\n * Build span attributes for server functions\n */\nfunction buildServerFnAttributes(\n functionName: string,\n method: string,\n args: unknown,\n config: Required<\n Omit<TracingMiddlewareConfig, 'customAttributes' | 'service' | 'type'>\n >,\n): Attributes {\n const attrs: Attributes = {\n [SPAN_ATTRIBUTES.RPC_SYSTEM]: 'tanstack-start',\n [SPAN_ATTRIBUTES.RPC_METHOD]: functionName,\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'serverFn',\n [SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_NAME]: functionName,\n [SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_METHOD]: method,\n };\n\n if (config.captureArgs && args !== undefined) {\n try {\n attrs[SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS] = JSON.stringify(args);\n } catch {\n attrs[SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS] = '[non-serializable]';\n }\n }\n\n return attrs;\n}\n\n/**\n * Generic middleware handler type (compatible with TanStack's middleware pattern)\n *\n * This type represents the shape of TanStack middleware handlers.\n * We use a generic type to avoid direct dependency on TanStack packages.\n */\nexport interface MiddlewareHandler<TContext = unknown> {\n (opts: {\n next: (ctx?: Partial<TContext>) => Promise<TContext>;\n context: TContext;\n request?: Request;\n pathname?: string;\n data?: unknown;\n method?: string;\n filename?: string;\n functionId?: string;\n signal?: AbortSignal;\n }): Promise<TContext>;\n}\n\n/**\n * Create a TanStack-compatible tracing middleware\n *\n * This creates middleware that automatically traces all requests/server functions\n * with OpenTelemetry spans. Use with TanStack Start's middleware system.\n *\n * @param config - Configuration options\n * @returns Middleware handler compatible with TanStack Start\n *\n * @example\n * ```typescript\n * // Global request middleware in app/start.ts\n * import { createStart } from '@tanstack/react-start';\n * import { createTracingMiddleware } from 'autotel-tanstack/middleware';\n *\n * export const startInstance = createStart(() => ({\n * requestMiddleware: [\n * createTracingMiddleware({\n * captureHeaders: ['x-request-id', 'user-agent'],\n * excludePaths: ['/health', '/metrics'],\n * }),\n * ],\n * }));\n * ```\n *\n * @example\n * ```typescript\n * // Server function middleware\n * import { createServerFn } from '@tanstack/react-start';\n * import { createTracingMiddleware } from 'autotel-tanstack/middleware';\n *\n * export const getUser = createServerFn({ method: 'GET' })\n * .middleware([createTracingMiddleware({ type: 'function' })])\n * .handler(async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * });\n * ```\n */\nexport function createTracingMiddleware<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): MiddlewareHandler<TContext> {\n const mergedConfig = {\n ...DEFAULT_CONFIG,\n ...config,\n type: config?.type ?? 'request',\n };\n\n return async function tracingMiddleware(opts) {\n // If we're in the browser, return a no-op middleware\n // This prevents autotel (which uses Node.js APIs) from being bundled/executed in the browser\n if (!isServerSide()) {\n return opts.next();\n }\n const { next, request, pathname, data, functionId } = opts;\n\n // For function middleware\n if (mergedConfig.type === 'function') {\n const fnName = functionId || 'unknown';\n const method = (opts as { method?: string }).method || 'POST';\n\n return trace(`tanstack.serverFn.${fnName}`, async (ctx: TraceContext) => {\n const attrs = buildServerFnAttributes(\n fnName,\n method,\n data,\n mergedConfig,\n );\n ctx.setAttributes(attrs as Record<string, string | number | boolean>);\n\n // Add custom attributes if provided\n if (config?.customAttributes) {\n const customAttrs = config.customAttributes({\n type: 'serverFn',\n name: fnName,\n args: data,\n });\n ctx.setAttributes(\n customAttrs as Record<string, string | number | boolean>,\n );\n }\n\n try {\n const result = await next();\n\n // Capture result if configured\n if (mergedConfig.captureResults && result !== undefined) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_RESULT,\n JSON.stringify(result),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_RESULT,\n '[non-serializable]',\n );\n }\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n if (mergedConfig.captureErrors) {\n ctx.recordException(error as Error);\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: (error as Error).message,\n });\n\n // Report error to error store\n try {\n const { reportError } = await import('./error-reporting');\n reportError(error as Error, {\n type: 'serverFn',\n name: fnName,\n method,\n });\n } catch {\n // Error reporting not available, skip\n }\n }\n throw error;\n }\n }) as Promise<TContext>;\n }\n\n // For request middleware\n if (!request) {\n // No request available, just pass through\n return next();\n }\n\n const url = new URL(request.url);\n\n // Check if path should be excluded\n if (shouldExcludePath(url.pathname, mergedConfig.excludePaths)) {\n return next();\n }\n\n // Extract parent context from request headers\n const parentContext = extractContextFromRequest(request);\n\n // Run within parent context for distributed tracing\n return context.with(parentContext, async () => {\n const spanName = `${request.method} ${pathname || url.pathname}`;\n\n return trace(spanName, async (ctx: TraceContext) => {\n const attrs = buildRequestAttributes(request, mergedConfig);\n ctx.setAttributes(attrs as Record<string, string | number | boolean>);\n\n // Add custom attributes if provided\n if (config?.customAttributes) {\n const customAttrs = config.customAttributes({\n type: 'request',\n name: spanName,\n request,\n });\n ctx.setAttributes(\n customAttrs as Record<string, string | number | boolean>,\n );\n }\n\n const startTime = Date.now();\n\n try {\n const result = await next();\n\n const duration = Date.now() - startTime;\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS,\n duration,\n );\n\n // Record timing in metrics collector\n try {\n const { metricsCollector } = await import('./metrics');\n metricsCollector.recordTiming(spanName, duration);\n } catch {\n // Metrics not available, skip\n }\n\n // Try to get response status from result if it's a Response\n if (result && typeof result === 'object' && 'status' in result) {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.HTTP_RESPONSE_STATUS_CODE,\n (result as { status: number }).status,\n );\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n const duration = Date.now() - startTime;\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS,\n duration,\n );\n\n if (mergedConfig.captureErrors) {\n ctx.recordException(error as Error);\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: (error as Error).message,\n });\n\n // Report error to error store\n try {\n const { reportError } = await import('./error-reporting');\n reportError(error as Error, {\n type: 'request',\n method: request.method,\n pathname: url.pathname,\n });\n } catch {\n // Error reporting not available, skip\n }\n }\n throw error;\n }\n }) as Promise<TContext>;\n });\n };\n}\n\n/**\n * Pre-configured tracing middleware with sensible defaults\n *\n * Convenience export for quick setup. Uses adaptive sampling,\n * captures x-request-id header, and excludes common health check paths.\n *\n * @param config - Optional configuration overrides\n * @returns Middleware handler\n *\n * @example\n * ```typescript\n * import { createStart } from '@tanstack/react-start';\n * import { tracingMiddleware } from 'autotel-tanstack/middleware';\n *\n * export const startInstance = createStart(() => ({\n * requestMiddleware: [tracingMiddleware()],\n * }));\n * ```\n */\nexport function tracingMiddleware<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): MiddlewareHandler<TContext> {\n return createTracingMiddleware({\n sampling: 'adaptive',\n captureHeaders: ['x-request-id', 'user-agent'],\n excludePaths: ['/health', '/healthz', '/ready', '/metrics', '/_ping'],\n ...config,\n });\n}\n\n/**\n * Create function-specific tracing middleware\n *\n * Convenience wrapper for server function middleware.\n *\n * @param config - Optional configuration\n * @returns Middleware handler for server functions\n *\n * @example\n * ```typescript\n * import { createServerFn } from '@tanstack/react-start';\n * import { functionTracingMiddleware } from 'autotel-tanstack/middleware';\n *\n * export const getUser = createServerFn({ method: 'GET' })\n * .middleware([functionTracingMiddleware()])\n * .handler(async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * });\n * ```\n */\nexport function functionTracingMiddleware<TContext = unknown>(\n config?: Omit<TracingMiddlewareConfig, 'type'>,\n): MiddlewareHandler<TContext> {\n return createTracingMiddleware({\n ...config,\n type: 'function',\n });\n}\n\n/**\n * Create a tracing handler for use with TanStack's native createMiddleware()\n *\n * This provides the raw tracing logic that you can pass to createMiddleware().server().\n * Use this when you want full control over the middleware builder pattern.\n *\n * The handler accepts TanStack's middleware signature `{ next, context, request }`\n * and internally adapts it to our more flexible MiddlewareHandler interface.\n *\n * @param config - Configuration options\n * @returns Server handler function compatible with createMiddleware().server()\n *\n * @example\n * ```typescript\n * import { createStart, createMiddleware } from '@tanstack/react-start';\n * import { createTracingServerHandler } from 'autotel-tanstack/middleware';\n *\n * // TanStack-native middleware creation\n * const requestTracingMiddleware = createMiddleware().server(\n * createTracingServerHandler({ captureHeaders: ['x-request-id'] })\n * );\n *\n * export const start = createStart(() => ({\n * requestMiddleware: [requestTracingMiddleware],\n * }));\n * ```\n *\n * @example\n * ```typescript\n * // For server functions - use createMiddleware({ type: 'function' })\n * import { createStart, createMiddleware } from '@tanstack/react-start';\n * import { createTracingServerHandler } from 'autotel-tanstack/middleware';\n *\n * const functionTracingMiddleware = createMiddleware({ type: 'function' }).server(\n * createTracingServerHandler({ type: 'function', captureArgs: true })\n * );\n *\n * export const start = createStart(() => ({\n * functionMiddleware: [functionTracingMiddleware],\n * }));\n * ```\n */\nexport function createTracingServerHandler<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): (opts: {\n next: (ctx?: Partial<TContext>) => Promise<TContext>;\n context: TContext;\n request?: Request;\n}) => Promise<TContext> {\n const handler = createTracingMiddleware<TContext>(config);\n\n // Adapt TanStack's signature to our handler\n return async (opts) => {\n return handler(opts);\n };\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { D as DEFAULT_CONFIG, S as SPAN_ATTRIBUTES, T as TanStackInstrumentationConfig, c as TraceLoaderConfig, b as TraceServerFnConfig, a as TracingMiddlewareConfig, W as WrapStartHandlerConfig } from './types-C37KSxMN.js';
2
- export { MiddlewareHandler, createTracingMiddleware, functionTracingMiddleware, tracingMiddleware } from './middleware.js';
2
+ export { MiddlewareHandler, createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from './middleware.js';
3
3
  export { createTracedServerFnFactory, traceServerFn } from './server-functions.js';
4
4
  export { createTracedRoute, traceBeforeLoad, traceLoader } from './loaders.js';
5
5
  export { createTracedHandler, wrapStartHandler } from './handlers.js';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { debugHeadersMiddleware } from './chunk-UTPW3QRT.js';
2
2
  export { createMetricsHandler, metricsCollector, recordTiming } from './chunk-JSI6QG7M.js';
3
3
  export { createErrorReportingHandler, errorStore, reportError, withErrorReporting } from './chunk-XXBHZR3M.js';
4
- export { createTracingMiddleware, functionTracingMiddleware, tracingMiddleware } from './chunk-OLBHLVLE.js';
4
+ export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from './chunk-ETD6SGPH.js';
5
5
  export { createTracedServerFnFactory, traceServerFn } from './chunk-TNOQTZ3N.js';
6
6
  export { createTracedRoute, traceBeforeLoad, traceLoader } from './chunk-HKM7LMO6.js';
7
7
  export { isBrowser, isNode, isServerSide } from './chunk-EUYFVNYE.js';
@@ -100,5 +100,52 @@ declare function tracingMiddleware<TContext = unknown>(config?: TracingMiddlewar
100
100
  * ```
101
101
  */
102
102
  declare function functionTracingMiddleware<TContext = unknown>(config?: Omit<TracingMiddlewareConfig, 'type'>): MiddlewareHandler<TContext>;
103
+ /**
104
+ * Create a tracing handler for use with TanStack's native createMiddleware()
105
+ *
106
+ * This provides the raw tracing logic that you can pass to createMiddleware().server().
107
+ * Use this when you want full control over the middleware builder pattern.
108
+ *
109
+ * The handler accepts TanStack's middleware signature `{ next, context, request }`
110
+ * and internally adapts it to our more flexible MiddlewareHandler interface.
111
+ *
112
+ * @param config - Configuration options
113
+ * @returns Server handler function compatible with createMiddleware().server()
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * import { createStart, createMiddleware } from '@tanstack/react-start';
118
+ * import { createTracingServerHandler } from 'autotel-tanstack/middleware';
119
+ *
120
+ * // TanStack-native middleware creation
121
+ * const requestTracingMiddleware = createMiddleware().server(
122
+ * createTracingServerHandler({ captureHeaders: ['x-request-id'] })
123
+ * );
124
+ *
125
+ * export const start = createStart(() => ({
126
+ * requestMiddleware: [requestTracingMiddleware],
127
+ * }));
128
+ * ```
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * // For server functions - use createMiddleware({ type: 'function' })
133
+ * import { createStart, createMiddleware } from '@tanstack/react-start';
134
+ * import { createTracingServerHandler } from 'autotel-tanstack/middleware';
135
+ *
136
+ * const functionTracingMiddleware = createMiddleware({ type: 'function' }).server(
137
+ * createTracingServerHandler({ type: 'function', captureArgs: true })
138
+ * );
139
+ *
140
+ * export const start = createStart(() => ({
141
+ * functionMiddleware: [functionTracingMiddleware],
142
+ * }));
143
+ * ```
144
+ */
145
+ declare function createTracingServerHandler<TContext = unknown>(config?: TracingMiddlewareConfig): (opts: {
146
+ next: (ctx?: Partial<TContext>) => Promise<TContext>;
147
+ context: TContext;
148
+ request?: Request;
149
+ }) => Promise<TContext>;
103
150
 
104
- export { type MiddlewareHandler, createTracingMiddleware, functionTracingMiddleware, tracingMiddleware };
151
+ export { type MiddlewareHandler, createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware };
@@ -1,4 +1,4 @@
1
- export { createTracingMiddleware, functionTracingMiddleware, tracingMiddleware } from './chunk-OLBHLVLE.js';
1
+ export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from './chunk-ETD6SGPH.js';
2
2
  import './chunk-EUYFVNYE.js';
3
3
  import './chunk-I4LX3LOG.js';
4
4
  import './chunk-NTY64BKS.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel-tanstack",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "OpenTelemetry instrumentation for TanStack Start - automatic tracing for server functions, middleware, and route loaders",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -117,7 +117,7 @@
117
117
  "license": "MIT",
118
118
  "dependencies": {
119
119
  "@opentelemetry/api": "^1.9.0",
120
- "autotel": "2.10.0"
120
+ "autotel": "2.11.0"
121
121
  },
122
122
  "peerDependencies": {
123
123
  "@tanstack/react-start": "^1.139.14",
@@ -23,6 +23,7 @@ export {
23
23
  createTracingMiddleware,
24
24
  tracingMiddleware,
25
25
  functionTracingMiddleware,
26
+ createTracingServerHandler,
26
27
  type MiddlewareHandler,
27
28
  } from './middleware';
28
29
  export {
@@ -59,3 +59,19 @@ export function functionTracingMiddleware<TContext = unknown>(
59
59
  return opts.next();
60
60
  };
61
61
  }
62
+
63
+ /**
64
+ * Browser stub: Returns pass-through handler for createMiddleware().server()
65
+ */
66
+ export function createTracingServerHandler<TContext = unknown>(
67
+ config?: TracingMiddlewareConfig,
68
+ ): (opts: {
69
+ next: (ctx?: Partial<TContext>) => Promise<TContext>;
70
+ context: TContext;
71
+ request?: Request;
72
+ }) => Promise<TContext> {
73
+ void config;
74
+ return async function noopHandler(opts) {
75
+ return opts.next();
76
+ };
77
+ }
package/src/index.ts CHANGED
@@ -39,6 +39,7 @@ export {
39
39
  createTracingMiddleware,
40
40
  tracingMiddleware,
41
41
  functionTracingMiddleware,
42
+ createTracingServerHandler,
42
43
  type MiddlewareHandler,
43
44
  } from './middleware';
44
45
 
package/src/middleware.ts CHANGED
@@ -398,3 +398,60 @@ export function functionTracingMiddleware<TContext = unknown>(
398
398
  type: 'function',
399
399
  });
400
400
  }
401
+
402
+ /**
403
+ * Create a tracing handler for use with TanStack's native createMiddleware()
404
+ *
405
+ * This provides the raw tracing logic that you can pass to createMiddleware().server().
406
+ * Use this when you want full control over the middleware builder pattern.
407
+ *
408
+ * The handler accepts TanStack's middleware signature `{ next, context, request }`
409
+ * and internally adapts it to our more flexible MiddlewareHandler interface.
410
+ *
411
+ * @param config - Configuration options
412
+ * @returns Server handler function compatible with createMiddleware().server()
413
+ *
414
+ * @example
415
+ * ```typescript
416
+ * import { createStart, createMiddleware } from '@tanstack/react-start';
417
+ * import { createTracingServerHandler } from 'autotel-tanstack/middleware';
418
+ *
419
+ * // TanStack-native middleware creation
420
+ * const requestTracingMiddleware = createMiddleware().server(
421
+ * createTracingServerHandler({ captureHeaders: ['x-request-id'] })
422
+ * );
423
+ *
424
+ * export const start = createStart(() => ({
425
+ * requestMiddleware: [requestTracingMiddleware],
426
+ * }));
427
+ * ```
428
+ *
429
+ * @example
430
+ * ```typescript
431
+ * // For server functions - use createMiddleware({ type: 'function' })
432
+ * import { createStart, createMiddleware } from '@tanstack/react-start';
433
+ * import { createTracingServerHandler } from 'autotel-tanstack/middleware';
434
+ *
435
+ * const functionTracingMiddleware = createMiddleware({ type: 'function' }).server(
436
+ * createTracingServerHandler({ type: 'function', captureArgs: true })
437
+ * );
438
+ *
439
+ * export const start = createStart(() => ({
440
+ * functionMiddleware: [functionTracingMiddleware],
441
+ * }));
442
+ * ```
443
+ */
444
+ export function createTracingServerHandler<TContext = unknown>(
445
+ config?: TracingMiddlewareConfig,
446
+ ): (opts: {
447
+ next: (ctx?: Partial<TContext>) => Promise<TContext>;
448
+ context: TContext;
449
+ request?: Request;
450
+ }) => Promise<TContext> {
451
+ const handler = createTracingMiddleware<TContext>(config);
452
+
453
+ // Adapt TanStack's signature to our handler
454
+ return async (opts) => {
455
+ return handler(opts);
456
+ };
457
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/browser/middleware.ts"],"names":[],"mappings":";AA6BO,SAAS,wBACd,MAAA,EAC6B;AAE7B,EAAA,OAAO,eAAe,eAAe,IAAA,EAAM;AACzC,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB,CAAA;AACF;AAKO,SAAS,kBACd,MAAA,EAC6B;AAE7B,EAAA,OAAO,eAAe,eAAe,IAAA,EAAM;AACzC,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB,CAAA;AACF;AAKO,SAAS,0BACd,MAAA,EAC6B;AAE7B,EAAA,OAAO,eAAe,eAAe,IAAA,EAAM;AACzC,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB,CAAA;AACF","file":"chunk-4C7T5ZIM.js","sourcesContent":["/**\n * Browser stub for middleware module\n *\n * In browser environments, these functions return pass-through middleware\n * that just calls next() without any tracing overhead.\n */\n\nimport type { TracingMiddlewareConfig } from './types';\n\n/**\n * Generic middleware handler type\n */\nexport interface MiddlewareHandler<TContext = unknown> {\n (opts: {\n next: (ctx?: Partial<TContext>) => Promise<TContext>;\n context: TContext;\n request?: Request;\n pathname?: string;\n data?: unknown;\n method?: string;\n filename?: string;\n functionId?: string;\n signal?: AbortSignal;\n }): Promise<TContext>;\n}\n\n/**\n * Browser stub: Returns pass-through middleware\n */\nexport function createTracingMiddleware<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): MiddlewareHandler<TContext> {\n void config;\n return async function noopMiddleware(opts) {\n return opts.next();\n };\n}\n\n/**\n * Browser stub: Returns pass-through middleware\n */\nexport function tracingMiddleware<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): MiddlewareHandler<TContext> {\n void config;\n return async function noopMiddleware(opts) {\n return opts.next();\n };\n}\n\n/**\n * Browser stub: Returns pass-through middleware\n */\nexport function functionTracingMiddleware<TContext = unknown>(\n config?: Omit<TracingMiddlewareConfig, 'type'>,\n): MiddlewareHandler<TContext> {\n void config;\n return async function noopMiddleware(opts) {\n return opts.next();\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/middleware.ts"],"names":["tracingMiddleware"],"mappings":";;;;;;AAaA,SAAS,iBAAA,CACP,UACA,YAAA,EACS;AACT,EAAA,KAAA,MAAW,WAAW,YAAA,EAAc;AAClC,IAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAE/B,MAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AACzB,QAAA,MAAM,QAAQ,IAAI,MAAA;AAAA,UAChB,GAAA,GAAM,QAAQ,UAAA,CAAW,GAAA,EAAK,IAAI,CAAA,CAAE,UAAA,CAAW,GAAA,EAAK,GAAG,CAAA,GAAI;AAAA,SAC7D;AACA,QAAA,IAAI,KAAA,CAAM,IAAA,CAAK,QAAQ,CAAA,EAAG,OAAO,IAAA;AAAA,MACnC,CAAA,MAAO;AACL,QAAA,IAAI,aAAa,OAAA,IAAW,QAAA,CAAS,UAAA,CAAW,OAAO,GAAG,OAAO,IAAA;AAAA,MACnE;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,QAAQ,CAAA,EAAG,OAAO,IAAA;AAAA,IACrC;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAKA,SAAS,sBAAA,CACP,SACA,MAAA,EAGY;AACZ,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,EAAA,MAAM,KAAA,GAAoB;AAAA,IACxB,CAAC,eAAA,CAAgB,mBAAmB,GAAG,OAAA,CAAQ,MAAA;AAAA,IAC/C,CAAC,eAAA,CAAgB,QAAQ,GAAG,GAAA,CAAI,QAAA;AAAA,IAChC,CAAC,eAAA,CAAgB,aAAa,GAAG;AAAA,GACnC;AAEA,EAAA,IAAI,IAAI,MAAA,EAAQ;AACd,IAAA,KAAA,CAAM,eAAA,CAAgB,SAAS,CAAA,GAAI,GAAA,CAAI,MAAA;AAAA,EACzC;AAGA,EAAA,IAAI,OAAO,cAAA,EAAgB;AACzB,IAAA,KAAA,MAAW,MAAA,IAAU,OAAO,cAAA,EAAgB;AAC1C,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACxC,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,KAAA,CAAM,CAAA,oBAAA,EAAuB,MAAA,CAAO,WAAA,EAAa,EAAE,CAAA,GAAI,KAAA;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAKA,SAAS,uBAAA,CACP,YAAA,EACA,MAAA,EACA,IAAA,EACA,MAAA,EAGY;AACZ,EAAA,MAAM,KAAA,GAAoB;AAAA,IACxB,CAAC,eAAA,CAAgB,UAAU,GAAG,gBAAA;AAAA,IAC9B,CAAC,eAAA,CAAgB,UAAU,GAAG,YAAA;AAAA,IAC9B,CAAC,eAAA,CAAgB,aAAa,GAAG,UAAA;AAAA,IACjC,CAAC,eAAA,CAAgB,uBAAuB,GAAG,YAAA;AAAA,IAC3C,CAAC,eAAA,CAAgB,yBAAyB,GAAG;AAAA,GAC/C;AAEA,EAAA,IAAI,MAAA,CAAO,WAAA,IAAe,IAAA,KAAS,MAAA,EAAW;AAC5C,IAAA,IAAI;AACF,MAAA,KAAA,CAAM,eAAA,CAAgB,uBAAuB,CAAA,GAAI,IAAA,CAAK,UAAU,IAAI,CAAA;AAAA,IACtE,CAAA,CAAA,MAAQ;AACN,MAAA,KAAA,CAAM,eAAA,CAAgB,uBAAuB,CAAA,GAAI,oBAAA;AAAA,IACnD;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AA4DO,SAAS,wBACd,MAAA,EAC6B;AAC7B,EAAA,MAAM,YAAA,GAAe;AAAA,IACnB,GAAG,cAAA;AAAA,IACH,GAAG,MAAA;AAAA,IACH,IAAA,EAAM,QAAQ,IAAA,IAAQ;AAAA,GACxB;AAEA,EAAA,OAAO,eAAeA,mBAAkB,IAAA,EAAM;AAG5C,IAAA,IAAI,CAAC,cAAa,EAAG;AACnB,MAAA,OAAO,KAAK,IAAA,EAAK;AAAA,IACnB;AACA,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAS,QAAA,EAAU,IAAA,EAAM,YAAW,GAAI,IAAA;AAGtD,IAAA,IAAI,YAAA,CAAa,SAAS,UAAA,EAAY;AACpC,MAAA,MAAM,SAAS,UAAA,IAAc,SAAA;AAC7B,MAAA,MAAM,MAAA,GAAU,KAA6B,MAAA,IAAU,MAAA;AAEvD,MAAA,OAAO,KAAA,CAAM,CAAA,kBAAA,EAAqB,MAAM,CAAA,CAAA,EAAI,OAAO,GAAA,KAAsB;AACvE,QAAA,MAAM,KAAA,GAAQ,uBAAA;AAAA,UACZ,MAAA;AAAA,UACA,MAAA;AAAA,UACA,IAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,GAAA,CAAI,cAAc,KAAkD,CAAA;AAGpE,QAAA,IAAI,QAAQ,gBAAA,EAAkB;AAC5B,UAAA,MAAM,WAAA,GAAc,OAAO,gBAAA,CAAiB;AAAA,YAC1C,IAAA,EAAM,UAAA;AAAA,YACN,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM;AAAA,WACP,CAAA;AACD,UAAA,GAAA,CAAI,aAAA;AAAA,YACF;AAAA,WACF;AAAA,QACF;AAEA,QAAA,IAAI;AACF,UAAA,MAAM,MAAA,GAAS,MAAM,IAAA,EAAK;AAG1B,UAAA,IAAI,YAAA,CAAa,cAAA,IAAkB,MAAA,KAAW,KAAA,CAAA,EAAW;AACvD,YAAA,IAAI;AACF,cAAA,GAAA,CAAI,YAAA;AAAA,gBACF,eAAA,CAAgB,yBAAA;AAAA,gBAChB,IAAA,CAAK,UAAU,MAAM;AAAA,eACvB;AAAA,YACF,CAAA,CAAA,MAAQ;AACN,cAAA,GAAA,CAAI,YAAA;AAAA,gBACF,eAAA,CAAgB,yBAAA;AAAA,gBAChB;AAAA,eACF;AAAA,YACF;AAAA,UACF;AAEA,UAAA,GAAA,CAAI,SAAA,CAAU,EAAE,IAAA,EAAM,cAAA,CAAe,IAAI,CAAA;AACzC,UAAA,OAAO,MAAA;AAAA,QACT,SAAS,KAAA,EAAO;AACd,UAAA,IAAI,aAAa,aAAA,EAAe;AAC9B,YAAA,GAAA,CAAI,gBAAgB,KAAc,CAAA;AAClC,YAAA,GAAA,CAAI,SAAA,CAAU;AAAA,cACZ,MAAM,cAAA,CAAe,KAAA;AAAA,cACrB,SAAU,KAAA,CAAgB;AAAA,aAC3B,CAAA;AAGD,YAAA,IAAI;AACF,cAAA,MAAM,EAAE,WAAA,EAAY,GAAI,MAAM,OAAO,sBAAmB,CAAA;AACxD,cAAA,WAAA,CAAY,KAAA,EAAgB;AAAA,gBAC1B,IAAA,EAAM,UAAA;AAAA,gBACN,IAAA,EAAM,MAAA;AAAA,gBACN;AAAA,eACD,CAAA;AAAA,YACH,CAAA,CAAA,MAAQ;AAAA,YAER;AAAA,UACF;AACA,UAAA,MAAM,KAAA;AAAA,QACR;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAGA,IAAA,IAAI,CAAC,OAAA,EAAS;AAEZ,MAAA,OAAO,IAAA,EAAK;AAAA,IACd;AAEA,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAG/B,IAAA,IAAI,iBAAA,CAAkB,GAAA,CAAI,QAAA,EAAU,YAAA,CAAa,YAAY,CAAA,EAAG;AAC9D,MAAA,OAAO,IAAA,EAAK;AAAA,IACd;AAGA,IAAA,MAAM,aAAA,GAAgB,0BAA0B,OAAO,CAAA;AAGvD,IAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,aAAA,EAAe,YAAY;AAC7C,MAAA,MAAM,WAAW,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,EAAI,QAAA,IAAY,IAAI,QAAQ,CAAA,CAAA;AAE9D,MAAA,OAAO,KAAA,CAAM,QAAA,EAAU,OAAO,GAAA,KAAsB;AAClD,QAAA,MAAM,KAAA,GAAQ,sBAAA,CAAuB,OAAA,EAAS,YAAY,CAAA;AAC1D,QAAA,GAAA,CAAI,cAAc,KAAkD,CAAA;AAGpE,QAAA,IAAI,QAAQ,gBAAA,EAAkB;AAC5B,UAAA,MAAM,WAAA,GAAc,OAAO,gBAAA,CAAiB;AAAA,YAC1C,IAAA,EAAM,SAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN;AAAA,WACD,CAAA;AACD,UAAA,GAAA,CAAI,aAAA;AAAA,YACF;AAAA,WACF;AAAA,QACF;AAEA,QAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,QAAA,IAAI;AACF,UAAA,MAAM,MAAA,GAAS,MAAM,IAAA,EAAK;AAE1B,UAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAC9B,UAAA,GAAA,CAAI,YAAA;AAAA,YACF,eAAA,CAAgB,4BAAA;AAAA,YAChB;AAAA,WACF;AAGA,UAAA,IAAI;AACF,YAAA,MAAM,EAAE,gBAAA,EAAiB,GAAI,MAAM,OAAO,cAAW,CAAA;AACrD,YAAA,gBAAA,CAAiB,YAAA,CAAa,UAAU,QAAQ,CAAA;AAAA,UAClD,CAAA,CAAA,MAAQ;AAAA,UAER;AAGA,UAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,YAAY,MAAA,EAAQ;AAC9D,YAAA,GAAA,CAAI,YAAA;AAAA,cACF,eAAA,CAAgB,yBAAA;AAAA,cACf,MAAA,CAA8B;AAAA,aACjC;AAAA,UACF;AAEA,UAAA,GAAA,CAAI,SAAA,CAAU,EAAE,IAAA,EAAM,cAAA,CAAe,IAAI,CAAA;AACzC,UAAA,OAAO,MAAA;AAAA,QACT,SAAS,KAAA,EAAO;AACd,UAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAC9B,UAAA,GAAA,CAAI,YAAA;AAAA,YACF,eAAA,CAAgB,4BAAA;AAAA,YAChB;AAAA,WACF;AAEA,UAAA,IAAI,aAAa,aAAA,EAAe;AAC9B,YAAA,GAAA,CAAI,gBAAgB,KAAc,CAAA;AAClC,YAAA,GAAA,CAAI,SAAA,CAAU;AAAA,cACZ,MAAM,cAAA,CAAe,KAAA;AAAA,cACrB,SAAU,KAAA,CAAgB;AAAA,aAC3B,CAAA;AAGD,YAAA,IAAI;AACF,cAAA,MAAM,EAAE,WAAA,EAAY,GAAI,MAAM,OAAO,sBAAmB,CAAA;AACxD,cAAA,WAAA,CAAY,KAAA,EAAgB;AAAA,gBAC1B,IAAA,EAAM,SAAA;AAAA,gBACN,QAAQ,OAAA,CAAQ,MAAA;AAAA,gBAChB,UAAU,GAAA,CAAI;AAAA,eACf,CAAA;AAAA,YACH,CAAA,CAAA,MAAQ;AAAA,YAER;AAAA,UACF;AACA,UAAA,MAAM,KAAA;AAAA,QACR;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH,CAAA;AACF;AAqBO,SAAS,kBACd,MAAA,EAC6B;AAC7B,EAAA,OAAO,uBAAA,CAAwB;AAAA,IAC7B,QAAA,EAAU,UAAA;AAAA,IACV,cAAA,EAAgB,CAAC,cAAA,EAAgB,YAAY,CAAA;AAAA,IAC7C,cAAc,CAAC,SAAA,EAAW,UAAA,EAAY,QAAA,EAAU,YAAY,QAAQ,CAAA;AAAA,IACpE,GAAG;AAAA,GACJ,CAAA;AACH;AAsBO,SAAS,0BACd,MAAA,EAC6B;AAC7B,EAAA,OAAO,uBAAA,CAAwB;AAAA,IAC7B,GAAG,MAAA;AAAA,IACH,IAAA,EAAM;AAAA,GACP,CAAA;AACH","file":"chunk-OLBHLVLE.js","sourcesContent":["import { context, SpanStatusCode, type Attributes } from '@opentelemetry/api';\nimport { trace, type TraceContext } from 'autotel';\nimport { extractContextFromRequest } from './context';\nimport { isServerSide } from './env';\nimport {\n type TracingMiddlewareConfig,\n DEFAULT_CONFIG,\n SPAN_ATTRIBUTES,\n} from './types';\n\n/**\n * Check if a path should be excluded from tracing\n */\nfunction shouldExcludePath(\n pathname: string,\n excludePaths: (string | RegExp)[],\n): boolean {\n for (const pattern of excludePaths) {\n if (typeof pattern === 'string') {\n // Simple glob matching\n if (pattern.includes('*')) {\n const regex = new RegExp(\n '^' + pattern.replaceAll('*', '.*').replaceAll('?', '.') + '$',\n );\n if (regex.test(pathname)) return true;\n } else {\n if (pathname === pattern || pathname.startsWith(pattern)) return true;\n }\n } else {\n if (pattern.test(pathname)) return true;\n }\n }\n return false;\n}\n\n/**\n * Build span attributes for HTTP requests\n */\nfunction buildRequestAttributes(\n request: Request,\n config: Required<\n Omit<TracingMiddlewareConfig, 'customAttributes' | 'service' | 'type'>\n >,\n): Attributes {\n const url = new URL(request.url);\n const attrs: Attributes = {\n [SPAN_ATTRIBUTES.HTTP_REQUEST_METHOD]: request.method,\n [SPAN_ATTRIBUTES.URL_PATH]: url.pathname,\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'request',\n };\n\n if (url.search) {\n attrs[SPAN_ATTRIBUTES.URL_QUERY] = url.search;\n }\n\n // Capture configured headers\n if (config.captureHeaders) {\n for (const header of config.captureHeaders) {\n const value = request.headers.get(header);\n if (value) {\n attrs[`http.request.header.${header.toLowerCase()}`] = value;\n }\n }\n }\n\n return attrs;\n}\n\n/**\n * Build span attributes for server functions\n */\nfunction buildServerFnAttributes(\n functionName: string,\n method: string,\n args: unknown,\n config: Required<\n Omit<TracingMiddlewareConfig, 'customAttributes' | 'service' | 'type'>\n >,\n): Attributes {\n const attrs: Attributes = {\n [SPAN_ATTRIBUTES.RPC_SYSTEM]: 'tanstack-start',\n [SPAN_ATTRIBUTES.RPC_METHOD]: functionName,\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'serverFn',\n [SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_NAME]: functionName,\n [SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_METHOD]: method,\n };\n\n if (config.captureArgs && args !== undefined) {\n try {\n attrs[SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS] = JSON.stringify(args);\n } catch {\n attrs[SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS] = '[non-serializable]';\n }\n }\n\n return attrs;\n}\n\n/**\n * Generic middleware handler type (compatible with TanStack's middleware pattern)\n *\n * This type represents the shape of TanStack middleware handlers.\n * We use a generic type to avoid direct dependency on TanStack packages.\n */\nexport interface MiddlewareHandler<TContext = unknown> {\n (opts: {\n next: (ctx?: Partial<TContext>) => Promise<TContext>;\n context: TContext;\n request?: Request;\n pathname?: string;\n data?: unknown;\n method?: string;\n filename?: string;\n functionId?: string;\n signal?: AbortSignal;\n }): Promise<TContext>;\n}\n\n/**\n * Create a TanStack-compatible tracing middleware\n *\n * This creates middleware that automatically traces all requests/server functions\n * with OpenTelemetry spans. Use with TanStack Start's middleware system.\n *\n * @param config - Configuration options\n * @returns Middleware handler compatible with TanStack Start\n *\n * @example\n * ```typescript\n * // Global request middleware in app/start.ts\n * import { createStart } from '@tanstack/react-start';\n * import { createTracingMiddleware } from 'autotel-tanstack/middleware';\n *\n * export const startInstance = createStart(() => ({\n * requestMiddleware: [\n * createTracingMiddleware({\n * captureHeaders: ['x-request-id', 'user-agent'],\n * excludePaths: ['/health', '/metrics'],\n * }),\n * ],\n * }));\n * ```\n *\n * @example\n * ```typescript\n * // Server function middleware\n * import { createServerFn } from '@tanstack/react-start';\n * import { createTracingMiddleware } from 'autotel-tanstack/middleware';\n *\n * export const getUser = createServerFn({ method: 'GET' })\n * .middleware([createTracingMiddleware({ type: 'function' })])\n * .handler(async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * });\n * ```\n */\nexport function createTracingMiddleware<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): MiddlewareHandler<TContext> {\n const mergedConfig = {\n ...DEFAULT_CONFIG,\n ...config,\n type: config?.type ?? 'request',\n };\n\n return async function tracingMiddleware(opts) {\n // If we're in the browser, return a no-op middleware\n // This prevents autotel (which uses Node.js APIs) from being bundled/executed in the browser\n if (!isServerSide()) {\n return opts.next();\n }\n const { next, request, pathname, data, functionId } = opts;\n\n // For function middleware\n if (mergedConfig.type === 'function') {\n const fnName = functionId || 'unknown';\n const method = (opts as { method?: string }).method || 'POST';\n\n return trace(`tanstack.serverFn.${fnName}`, async (ctx: TraceContext) => {\n const attrs = buildServerFnAttributes(\n fnName,\n method,\n data,\n mergedConfig,\n );\n ctx.setAttributes(attrs as Record<string, string | number | boolean>);\n\n // Add custom attributes if provided\n if (config?.customAttributes) {\n const customAttrs = config.customAttributes({\n type: 'serverFn',\n name: fnName,\n args: data,\n });\n ctx.setAttributes(\n customAttrs as Record<string, string | number | boolean>,\n );\n }\n\n try {\n const result = await next();\n\n // Capture result if configured\n if (mergedConfig.captureResults && result !== undefined) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_RESULT,\n JSON.stringify(result),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_RESULT,\n '[non-serializable]',\n );\n }\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n if (mergedConfig.captureErrors) {\n ctx.recordException(error as Error);\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: (error as Error).message,\n });\n\n // Report error to error store\n try {\n const { reportError } = await import('./error-reporting');\n reportError(error as Error, {\n type: 'serverFn',\n name: fnName,\n method,\n });\n } catch {\n // Error reporting not available, skip\n }\n }\n throw error;\n }\n }) as Promise<TContext>;\n }\n\n // For request middleware\n if (!request) {\n // No request available, just pass through\n return next();\n }\n\n const url = new URL(request.url);\n\n // Check if path should be excluded\n if (shouldExcludePath(url.pathname, mergedConfig.excludePaths)) {\n return next();\n }\n\n // Extract parent context from request headers\n const parentContext = extractContextFromRequest(request);\n\n // Run within parent context for distributed tracing\n return context.with(parentContext, async () => {\n const spanName = `${request.method} ${pathname || url.pathname}`;\n\n return trace(spanName, async (ctx: TraceContext) => {\n const attrs = buildRequestAttributes(request, mergedConfig);\n ctx.setAttributes(attrs as Record<string, string | number | boolean>);\n\n // Add custom attributes if provided\n if (config?.customAttributes) {\n const customAttrs = config.customAttributes({\n type: 'request',\n name: spanName,\n request,\n });\n ctx.setAttributes(\n customAttrs as Record<string, string | number | boolean>,\n );\n }\n\n const startTime = Date.now();\n\n try {\n const result = await next();\n\n const duration = Date.now() - startTime;\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS,\n duration,\n );\n\n // Record timing in metrics collector\n try {\n const { metricsCollector } = await import('./metrics');\n metricsCollector.recordTiming(spanName, duration);\n } catch {\n // Metrics not available, skip\n }\n\n // Try to get response status from result if it's a Response\n if (result && typeof result === 'object' && 'status' in result) {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.HTTP_RESPONSE_STATUS_CODE,\n (result as { status: number }).status,\n );\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n const duration = Date.now() - startTime;\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS,\n duration,\n );\n\n if (mergedConfig.captureErrors) {\n ctx.recordException(error as Error);\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: (error as Error).message,\n });\n\n // Report error to error store\n try {\n const { reportError } = await import('./error-reporting');\n reportError(error as Error, {\n type: 'request',\n method: request.method,\n pathname: url.pathname,\n });\n } catch {\n // Error reporting not available, skip\n }\n }\n throw error;\n }\n }) as Promise<TContext>;\n });\n };\n}\n\n/**\n * Pre-configured tracing middleware with sensible defaults\n *\n * Convenience export for quick setup. Uses adaptive sampling,\n * captures x-request-id header, and excludes common health check paths.\n *\n * @param config - Optional configuration overrides\n * @returns Middleware handler\n *\n * @example\n * ```typescript\n * import { createStart } from '@tanstack/react-start';\n * import { tracingMiddleware } from 'autotel-tanstack/middleware';\n *\n * export const startInstance = createStart(() => ({\n * requestMiddleware: [tracingMiddleware()],\n * }));\n * ```\n */\nexport function tracingMiddleware<TContext = unknown>(\n config?: TracingMiddlewareConfig,\n): MiddlewareHandler<TContext> {\n return createTracingMiddleware({\n sampling: 'adaptive',\n captureHeaders: ['x-request-id', 'user-agent'],\n excludePaths: ['/health', '/healthz', '/ready', '/metrics', '/_ping'],\n ...config,\n });\n}\n\n/**\n * Create function-specific tracing middleware\n *\n * Convenience wrapper for server function middleware.\n *\n * @param config - Optional configuration\n * @returns Middleware handler for server functions\n *\n * @example\n * ```typescript\n * import { createServerFn } from '@tanstack/react-start';\n * import { functionTracingMiddleware } from 'autotel-tanstack/middleware';\n *\n * export const getUser = createServerFn({ method: 'GET' })\n * .middleware([functionTracingMiddleware()])\n * .handler(async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * });\n * ```\n */\nexport function functionTracingMiddleware<TContext = unknown>(\n config?: Omit<TracingMiddlewareConfig, 'type'>,\n): MiddlewareHandler<TContext> {\n return createTracingMiddleware({\n ...config,\n type: 'function',\n });\n}\n"]}