autotel-tanstack 1.13.10 → 1.13.14

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/dist/auto.js CHANGED
@@ -1,7 +1,8 @@
1
- export { functionTracingMiddleware, tracingMiddleware } from './chunk-NP4OJO7T.js';
2
- export { traceServerFn } from './chunk-5JJXFTG3.js';
3
- export { traceBeforeLoad, traceLoader } from './chunk-5RYSHDCO.js';
1
+ export { functionTracingMiddleware, tracingMiddleware } from './chunk-LRA2UVVS.js';
2
+ export { traceServerFn } from './chunk-ESU66L3L.js';
3
+ export { traceBeforeLoad, traceLoader } from './chunk-KPXGFKPU.js';
4
4
  import './chunk-EUYFVNYE.js';
5
+ import './chunk-CCME55EK.js';
5
6
  import './chunk-I4LX3LOG.js';
6
7
  import './chunk-NTY64BKS.js';
7
8
  import './chunk-EGRHWZRV.js';
package/dist/auto.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auto.ts"],"names":[],"mappings":";;;;;;;;;;;AA8BA,IAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,iBAAA,IAAqB,gBAAA;AAGjD,IAAM,QAAA,GAAW,QAAQ,GAAA,CAAI,2BAAA;AAG7B,IAAI,OAAA;AACJ,IAAI,OAAA,CAAQ,IAAI,0BAAA,EAA4B;AAC1C,EAAA,OAAA,GAAU,EAAC;AACX,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,0BAAA,CAA2B,MAAM,GAAG,CAAA;AAC9D,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,CAAC,GAAA,EAAK,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACnC,IAAA,IAAI,OAAO,KAAA,EAAO;AAChB,MAAA,OAAA,CAAQ,GAAA,CAAI,IAAA,EAAM,CAAA,GAAI,MAAM,IAAA,EAAK;AAAA,IACnC;AAAA,EACF;AACF;AAIA,SAAS,YAAA,GAAmC;AAC1C,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,CAAI,aAAA;AACxB,EAAA,IAAI,GAAA,KAAQ,UAAU,OAAO,QAAA;AAC7B,EAAA,IAAI,GAAA,KAAQ,MAAA,IAAU,GAAA,KAAQ,GAAA,EAAK,OAAO,IAAA;AAC1C,EAAA,IAAI,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,GAAA,EAAK,OAAO,KAAA;AAC3C,EAAA,IAAI,CAAC,QAAA,IAAY,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,eAAe,OAAO,QAAA;AAChE,EAAA,OAAO,KAAA;AACT;AAMA,IAAI,OAAA,CAAQ,GAAA,CAAI,GAAA,KAAQ,GAAA,EAAK;AAC3B,EAAA,MAAM,WAAA,GAAc,IAAI,oBAAA,EAAqB;AAC7C,EAAA,MAAM,YAAA,GAAe,IAAI,mBAAA,CAAoB,WAAW,CAAA;AACxD,EAAC,WAAuC,kBAAA,GAAqB,WAAA;AAE7D,EAAA,IAAA,CAAK;AAAA,IACH,OAAA;AAAA,IACA,cAAA,EAAgB,CAAC,YAAY;AAAA,GAC9B,CAAA;AACH,CAAA,MAAO;AAEL,EAAA,IAAA,CAAK;AAAA,IACH,OAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAO,YAAA;AAAa,GACrB,CAAA;AACH;AAGA,IAAI,QAAQ,GAAA,CAAI,QAAA,KAAa,aAAA,IAAiB,OAAA,CAAQ,IAAI,aAAA,EAAe;AACvE,EAAA,OAAA,CAAQ,IAAI,2CAAA,EAA6C;AAAA,IACvD,OAAA;AAAA,IACA,UACE,OAAA,CAAQ,GAAA,CAAI,GAAA,KAAQ,GAAA,GAChB,qBACA,QAAA,IAAY,kBAAA;AAAA,IAClB,UAAA,EAAY,CAAC,CAAC;AAAA,GACf,CAAA;AACH;AAUO,SAAS,2BAAA,GAAuC;AACrD,EAAA,OAAO,IAAA;AACT;AAKO,SAAS,cAAA,GAAyB;AACvC,EAAA,OAAO,OAAA;AACT;AAKO,SAAS,WAAA,GAAkC;AAChD,EAAA,OAAO,QAAA;AACT","file":"auto.js","sourcesContent":["/**\n * Zero-config auto-instrumentation for TanStack Start\n *\n * Import this module to automatically instrument TanStack Start applications\n * with OpenTelemetry tracing. Configuration is read from environment variables.\n *\n * Environment Variables:\n * - OTEL_SERVICE_NAME: Service name (default: 'tanstack-start')\n * - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector URL\n * - OTEL_EXPORTER_OTLP_HEADERS: Authentication headers (key=value,key=value)\n * - AUTOTEL_DEBUG: Set to 'true' or 'pretty' to log spans to the server console\n *\n * @example\n * ```typescript\n * // app/start.ts\n * import 'autotel-tanstack/auto';\n * import { createStart } from '@tanstack/react-start';\n *\n * // Tracing is automatically configured!\n * export const startInstance = createStart(() => ({}));\n * ```\n *\n * @module\n */\n\nimport { init } from 'autotel';\nimport { InMemorySpanExporter } from 'autotel/exporters';\nimport { SimpleSpanProcessor } from 'autotel/processors';\n\n// Parse service name\nconst service = process.env.OTEL_SERVICE_NAME || 'tanstack-start';\n\n// Parse endpoint\nconst endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n\n// Parse headers\nlet headers: Record<string, string> | undefined;\nif (process.env.OTEL_EXPORTER_OTLP_HEADERS) {\n headers = {};\n const pairs = process.env.OTEL_EXPORTER_OTLP_HEADERS.split(',');\n for (const pair of pairs) {\n const [key, value] = pair.split('=');\n if (key && value) {\n headers[key.trim()] = value.trim();\n }\n }\n}\n\n// Debug: span output to server console. AUTOTEL_DEBUG=pretty | true, or default\n// to pretty in dev when no OTLP endpoint is set so you see spans immediately.\nfunction resolveDebug(): boolean | 'pretty' {\n const env = process.env.AUTOTEL_DEBUG;\n if (env === 'pretty') return 'pretty';\n if (env === 'true' || env === '1') return true;\n if (env === 'false' || env === '0') return false;\n if (!endpoint && process.env.NODE_ENV === 'development') return 'pretty';\n return false;\n}\n\n// E2E mode: use InMemorySpanExporter so tests can capture and assert on spans.\n// When E2E=1, skip the normal OTLP path and use in-memory storage instead.\n// Note: combined E2E + OTLP (two processors) is handled at integration level\n// because constructing an OTLP processor requires deps not available here.\nif (process.env.E2E === '1') {\n const e2eExporter = new InMemorySpanExporter();\n const e2eProcessor = new SimpleSpanProcessor(e2eExporter);\n (globalThis as Record<string, unknown>).__testSpanExporter = e2eExporter;\n\n init({\n service,\n spanProcessors: [e2eProcessor],\n });\n} else {\n // Initialize autotel (production path — unchanged)\n init({\n service,\n endpoint,\n headers,\n debug: resolveDebug(),\n });\n}\n\n// Log initialization (only in development)\nif (process.env.NODE_ENV === 'development' || process.env.AUTOTEL_DEBUG) {\n console.log('[autotel-tanstack] Auto-initialized with:', {\n service,\n endpoint:\n process.env.E2E === '1'\n ? '(E2E: in-memory)'\n : endpoint || '(not configured)',\n hasHeaders: !!headers,\n });\n}\n\n// Re-export middleware for convenience\nexport { tracingMiddleware, functionTracingMiddleware } from './middleware';\nexport { traceServerFn } from './server-functions';\nexport { traceLoader, traceBeforeLoad } from './loaders';\n\n/**\n * Check if auto-instrumentation is active\n */\nexport function isAutoInstrumentationActive(): boolean {\n return true;\n}\n\n/**\n * Get the configured service name\n */\nexport function getServiceName(): string {\n return service;\n}\n\n/**\n * Get the configured endpoint\n */\nexport function getEndpoint(): string | undefined {\n return endpoint;\n}\n"]}
1
+ {"version":3,"sources":["../src/auto.ts"],"names":[],"mappings":";;;;;;;;;;;;AA8BA,IAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,iBAAA,IAAqB,gBAAA;AAGjD,IAAM,QAAA,GAAW,QAAQ,GAAA,CAAI,2BAAA;AAG7B,IAAI,OAAA;AACJ,IAAI,OAAA,CAAQ,IAAI,0BAAA,EAA4B;AAC1C,EAAA,OAAA,GAAU,EAAC;AACX,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,0BAAA,CAA2B,MAAM,GAAG,CAAA;AAC9D,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,CAAC,GAAA,EAAK,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACnC,IAAA,IAAI,OAAO,KAAA,EAAO;AAChB,MAAA,OAAA,CAAQ,GAAA,CAAI,IAAA,EAAM,CAAA,GAAI,MAAM,IAAA,EAAK;AAAA,IACnC;AAAA,EACF;AACF;AAIA,SAAS,YAAA,GAAmC;AAC1C,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,CAAI,aAAA;AACxB,EAAA,IAAI,GAAA,KAAQ,UAAU,OAAO,QAAA;AAC7B,EAAA,IAAI,GAAA,KAAQ,MAAA,IAAU,GAAA,KAAQ,GAAA,EAAK,OAAO,IAAA;AAC1C,EAAA,IAAI,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,GAAA,EAAK,OAAO,KAAA;AAC3C,EAAA,IAAI,CAAC,QAAA,IAAY,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,eAAe,OAAO,QAAA;AAChE,EAAA,OAAO,KAAA;AACT;AAMA,IAAI,OAAA,CAAQ,GAAA,CAAI,GAAA,KAAQ,GAAA,EAAK;AAC3B,EAAA,MAAM,WAAA,GAAc,IAAI,oBAAA,EAAqB;AAC7C,EAAA,MAAM,YAAA,GAAe,IAAI,mBAAA,CAAoB,WAAW,CAAA;AACxD,EAAC,WAAuC,kBAAA,GAAqB,WAAA;AAE7D,EAAA,IAAA,CAAK;AAAA,IACH,OAAA;AAAA,IACA,cAAA,EAAgB,CAAC,YAAY;AAAA,GAC9B,CAAA;AACH,CAAA,MAAO;AAEL,EAAA,IAAA,CAAK;AAAA,IACH,OAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAO,YAAA;AAAa,GACrB,CAAA;AACH;AAGA,IAAI,QAAQ,GAAA,CAAI,QAAA,KAAa,aAAA,IAAiB,OAAA,CAAQ,IAAI,aAAA,EAAe;AACvE,EAAA,OAAA,CAAQ,IAAI,2CAAA,EAA6C;AAAA,IACvD,OAAA;AAAA,IACA,UACE,OAAA,CAAQ,GAAA,CAAI,GAAA,KAAQ,GAAA,GAChB,qBACA,QAAA,IAAY,kBAAA;AAAA,IAClB,UAAA,EAAY,CAAC,CAAC;AAAA,GACf,CAAA;AACH;AAUO,SAAS,2BAAA,GAAuC;AACrD,EAAA,OAAO,IAAA;AACT;AAKO,SAAS,cAAA,GAAyB;AACvC,EAAA,OAAO,OAAA;AACT;AAKO,SAAS,WAAA,GAAkC;AAChD,EAAA,OAAO,QAAA;AACT","file":"auto.js","sourcesContent":["/**\n * Zero-config auto-instrumentation for TanStack Start\n *\n * Import this module to automatically instrument TanStack Start applications\n * with OpenTelemetry tracing. Configuration is read from environment variables.\n *\n * Environment Variables:\n * - OTEL_SERVICE_NAME: Service name (default: 'tanstack-start')\n * - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector URL\n * - OTEL_EXPORTER_OTLP_HEADERS: Authentication headers (key=value,key=value)\n * - AUTOTEL_DEBUG: Set to 'true' or 'pretty' to log spans to the server console\n *\n * @example\n * ```typescript\n * // app/start.ts\n * import 'autotel-tanstack/auto';\n * import { createStart } from '@tanstack/react-start';\n *\n * // Tracing is automatically configured!\n * export const startInstance = createStart(() => ({}));\n * ```\n *\n * @module\n */\n\nimport { init } from 'autotel';\nimport { InMemorySpanExporter } from 'autotel/exporters';\nimport { SimpleSpanProcessor } from 'autotel/processors';\n\n// Parse service name\nconst service = process.env.OTEL_SERVICE_NAME || 'tanstack-start';\n\n// Parse endpoint\nconst endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n\n// Parse headers\nlet headers: Record<string, string> | undefined;\nif (process.env.OTEL_EXPORTER_OTLP_HEADERS) {\n headers = {};\n const pairs = process.env.OTEL_EXPORTER_OTLP_HEADERS.split(',');\n for (const pair of pairs) {\n const [key, value] = pair.split('=');\n if (key && value) {\n headers[key.trim()] = value.trim();\n }\n }\n}\n\n// Debug: span output to server console. AUTOTEL_DEBUG=pretty | true, or default\n// to pretty in dev when no OTLP endpoint is set so you see spans immediately.\nfunction resolveDebug(): boolean | 'pretty' {\n const env = process.env.AUTOTEL_DEBUG;\n if (env === 'pretty') return 'pretty';\n if (env === 'true' || env === '1') return true;\n if (env === 'false' || env === '0') return false;\n if (!endpoint && process.env.NODE_ENV === 'development') return 'pretty';\n return false;\n}\n\n// E2E mode: use InMemorySpanExporter so tests can capture and assert on spans.\n// When E2E=1, skip the normal OTLP path and use in-memory storage instead.\n// Note: combined E2E + OTLP (two processors) is handled at integration level\n// because constructing an OTLP processor requires deps not available here.\nif (process.env.E2E === '1') {\n const e2eExporter = new InMemorySpanExporter();\n const e2eProcessor = new SimpleSpanProcessor(e2eExporter);\n (globalThis as Record<string, unknown>).__testSpanExporter = e2eExporter;\n\n init({\n service,\n spanProcessors: [e2eProcessor],\n });\n} else {\n // Initialize autotel (production path — unchanged)\n init({\n service,\n endpoint,\n headers,\n debug: resolveDebug(),\n });\n}\n\n// Log initialization (only in development)\nif (process.env.NODE_ENV === 'development' || process.env.AUTOTEL_DEBUG) {\n console.log('[autotel-tanstack] Auto-initialized with:', {\n service,\n endpoint:\n process.env.E2E === '1'\n ? '(E2E: in-memory)'\n : endpoint || '(not configured)',\n hasHeaders: !!headers,\n });\n}\n\n// Re-export middleware for convenience\nexport { tracingMiddleware, functionTracingMiddleware } from './middleware';\nexport { traceServerFn } from './server-functions';\nexport { traceLoader, traceBeforeLoad } from './loaders';\n\n/**\n * Check if auto-instrumentation is active\n */\nexport function isAutoInstrumentationActive(): boolean {\n return true;\n}\n\n/**\n * Get the configured service name\n */\nexport function getServiceName(): string {\n return service;\n}\n\n/**\n * Get the configured endpoint\n */\nexport function getEndpoint(): string | undefined {\n return endpoint;\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { shouldInstrumentPath } from 'autotel-edge';
2
+
3
+ // src/route-filter.ts
4
+ function isExcludedPath(pathname, excludePaths) {
5
+ for (const pattern of excludePaths) {
6
+ if (pattern instanceof RegExp) {
7
+ if (pattern.test(pathname)) return true;
8
+ continue;
9
+ }
10
+ if (pattern.includes("*") || pattern.includes("?")) {
11
+ if (!shouldInstrumentPath(pathname, {
12
+ include: void 0,
13
+ exclude: [pattern]
14
+ })) {
15
+ return true;
16
+ }
17
+ continue;
18
+ }
19
+ if (pathname === pattern || pathname.startsWith(pattern)) {
20
+ return true;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+
26
+ export { isExcludedPath };
27
+ //# sourceMappingURL=chunk-CCME55EK.js.map
28
+ //# sourceMappingURL=chunk-CCME55EK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/route-filter.ts"],"names":[],"mappings":";;;AAWO,SAAS,cAAA,CACd,UACA,YAAA,EACS;AACT,EAAA,KAAA,MAAW,WAAW,YAAA,EAAc;AAClC,IAAA,IAAI,mBAAmB,MAAA,EAAQ;AAC7B,MAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,QAAQ,CAAA,EAAG,OAAO,IAAA;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,QAAQ,QAAA,CAAS,GAAG,KAAK,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AAClD,MAAA,IACE,CAAC,qBAAqB,QAAA,EAAU;AAAA,QAC9B,OAAA,EAAS,MAAA;AAAA,QACT,OAAA,EAAS,CAAC,OAAO;AAAA,OAClB,CAAA,EACD;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,QAAA,KAAa,OAAA,IAAW,QAAA,CAAS,UAAA,CAAW,OAAO,CAAA,EAAG;AACxD,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT","file":"chunk-CCME55EK.js","sourcesContent":["import { shouldInstrumentPath } from 'autotel-edge';\n\n/**\n * TanStack historically supported:\n * - glob strings (`/api/internal/*`)\n * - regex values\n * - plain-string prefix matching (`/health` matches `/healthz`)\n *\n * This helper keeps those semantics while delegating glob matching to\n * autotel-edge's shared middleware toolkit.\n */\nexport function isExcludedPath(\n pathname: string,\n excludePaths: Array<string | RegExp>,\n): boolean {\n for (const pattern of excludePaths) {\n if (pattern instanceof RegExp) {\n if (pattern.test(pathname)) return true;\n continue;\n }\n\n if (pattern.includes('*') || pattern.includes('?')) {\n if (\n !shouldInstrumentPath(pathname, {\n include: undefined,\n exclude: [pattern],\n })\n ) {\n return true;\n }\n continue;\n }\n\n if (pathname === pattern || pathname.startsWith(pattern)) {\n return true;\n }\n }\n\n return false;\n}\n"]}
@@ -53,11 +53,11 @@ function traceServerFn(serverFn, config = {}) {
53
53
  ctx.setStatus({ code: SpanStatusCode.OK });
54
54
  return result;
55
55
  } catch (error) {
56
- ctx.recordException(error);
57
- ctx.setStatus({
58
- code: SpanStatusCode.ERROR,
59
- message: error.message
60
- });
56
+ if ("recordError" in ctx && typeof ctx.recordError === "function") {
57
+ ctx.recordError(error);
58
+ } else if ("recordException" in ctx && typeof ctx.recordException === "function") {
59
+ ctx.recordException(error);
60
+ }
61
61
  throw error;
62
62
  }
63
63
  });
@@ -88,5 +88,5 @@ function createTracedServerFnFactory(createServerFnOriginal, defaultConfig = {})
88
88
  }
89
89
 
90
90
  export { createTracedServerFnFactory, traceServerFn };
91
- //# sourceMappingURL=chunk-5JJXFTG3.js.map
92
- //# sourceMappingURL=chunk-5JJXFTG3.js.map
91
+ //# sourceMappingURL=chunk-ESU66L3L.js.map
92
+ //# sourceMappingURL=chunk-ESU66L3L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server-functions.ts"],"names":[],"mappings":"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","file":"chunk-ESU66L3L.js","sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { trace, type TraceContext } from 'autotel';\nimport { isServerSide } from './env';\nimport { type TraceServerFnConfig, SPAN_ATTRIBUTES } from './types';\n\n/**\n * Wrap a TanStack server function with OpenTelemetry tracing\n *\n * This function wraps a server function to automatically create spans\n * for each invocation. It captures function name, arguments (optionally),\n * results (optionally), and errors.\n *\n * @param serverFn - The server function to wrap\n * @param config - Configuration options\n * @returns Wrapped server function with tracing\n *\n * @example\n * ```typescript\n * import { createServerFn } from '@tanstack/react-start';\n * import { traceServerFn } from 'autotel-tanstack/server-functions';\n *\n * const getUserBase = createServerFn({ method: 'GET' })\n * .handler(async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * });\n *\n * export const getUser = traceServerFn(getUserBase, { name: 'getUser' });\n * ```\n *\n * @example\n * ```typescript\n * // With argument and result capture (careful with PII!)\n * export const createUser = traceServerFn(\n * createServerFn({ method: 'POST' })\n * .handler(async ({ data }) => {\n * return await db.users.create({ data });\n * }),\n * {\n * name: 'createUser',\n * captureArgs: true,\n * captureResults: false, // Don't capture for PII reasons\n * }\n * );\n * ```\n */\nexport function traceServerFn<T extends (...args: any[]) => any>(\n serverFn: T,\n config: TraceServerFnConfig = {},\n): T {\n const fnName = config.name || serverFn.name || 'serverFn';\n const captureArgs = config.captureArgs ?? true;\n const captureResults = config.captureResults ?? false;\n\n return new Proxy(serverFn, {\n apply(target, thisArg, argArray) {\n // If we're in the browser, just call the function without tracing\n // Server functions should never run in the browser, but this prevents\n // autotel (which uses Node.js APIs) from being executed if it somehow does\n if (!isServerSide()) {\n return target.apply(thisArg, argArray);\n }\n\n return trace(`tanstack.serverFn.${fnName}`, async (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.RPC_SYSTEM]: 'tanstack-start',\n [SPAN_ATTRIBUTES.RPC_METHOD]: fnName,\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'serverFn',\n [SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_NAME]: fnName,\n });\n\n // Capture arguments if configured\n if (captureArgs && argArray.length > 0) {\n const args = argArray[0];\n if (args !== undefined) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS,\n JSON.stringify(args),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS,\n '[non-serializable]',\n );\n }\n }\n }\n\n try {\n const result = await Reflect.apply(target, thisArg, argArray);\n\n // Capture result if configured\n if (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 ('recordError' in ctx && typeof ctx.recordError === 'function') {\n ctx.recordError(error);\n } else if (\n 'recordException' in ctx &&\n typeof ctx.recordException === 'function'\n ) {\n ctx.recordException(error);\n }\n throw error;\n }\n });\n },\n\n get(target, prop, receiver) {\n return Reflect.get(target, prop, receiver);\n },\n }) as T;\n}\n\n/**\n * Create a traced version of createServerFn\n *\n * This higher-order function wraps TanStack's createServerFn to automatically\n * add tracing to all created server functions.\n *\n * @param createServerFnOriginal - The original createServerFn from TanStack\n * @param defaultConfig - Default configuration for all server functions\n * @returns Wrapped createServerFn that produces traced server functions\n *\n * @example\n * ```typescript\n * import { createServerFn as originalCreateServerFn } from '@tanstack/react-start';\n * import { createTracedServerFnFactory } from 'autotel-tanstack/server-functions';\n *\n * export const createServerFn = createTracedServerFnFactory(originalCreateServerFn);\n *\n * // Now all server functions created with createServerFn are automatically traced\n * export const getUser = createServerFn({ method: 'GET' })\n * .handler(async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * });\n * ```\n */\nexport function createTracedServerFnFactory<\n TCreateServerFn extends (...args: any[]) => any,\n>(\n createServerFnOriginal: TCreateServerFn,\n defaultConfig: Omit<TraceServerFnConfig, 'name'> = {},\n): TCreateServerFn {\n return new Proxy(createServerFnOriginal, {\n apply(target, thisArg, argArray) {\n const result = Reflect.apply(target, thisArg, argArray);\n\n // If the result has a .handler method, wrap it\n if (\n result &&\n typeof result === 'object' &&\n 'handler' in result &&\n typeof result.handler === 'function'\n ) {\n const originalHandler = result.handler.bind(result);\n\n result.handler = function tracedHandler(handlerFn: unknown) {\n const wrappedHandler = originalHandler(handlerFn as never);\n\n // Try to infer function name from the handler\n const fnName = (handlerFn as { name?: string })?.name || 'serverFn';\n\n return traceServerFn(wrappedHandler, {\n ...defaultConfig,\n name: fnName,\n });\n };\n }\n\n return result;\n },\n }) as TCreateServerFn;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { isExcludedPath } from './chunk-CCME55EK.js';
1
2
  import { DEFAULT_CONFIG, SPAN_ATTRIBUTES } from './chunk-I4LX3LOG.js';
2
3
  import { extractContextFromRequest } from './chunk-NTY64BKS.js';
3
4
  import { context, SpanStatusCode } from '@opentelemetry/api';
@@ -26,19 +27,7 @@ function wrapStartHandler(config = {}) {
26
27
  return function wrapHandler(handler) {
27
28
  return async function tracedHandler(request, opts) {
28
29
  const url = new URL(request.url);
29
- const shouldExclude = mergedConfig.excludePaths.some((pattern) => {
30
- if (typeof pattern === "string") {
31
- if (pattern.includes("*")) {
32
- const regex = new RegExp(
33
- "^" + pattern.replaceAll("*", ".*").replaceAll("?", ".") + "$"
34
- );
35
- return regex.test(url.pathname);
36
- }
37
- return url.pathname === pattern || url.pathname.startsWith(pattern);
38
- }
39
- return pattern.test(url.pathname);
40
- });
41
- if (shouldExclude) {
30
+ if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) {
42
31
  return handler(request, opts);
43
32
  }
44
33
  const parentContext = extractContextFromRequest(request);
@@ -103,11 +92,7 @@ function wrapStartHandler(config = {}) {
103
92
  duration
104
93
  );
105
94
  if (mergedConfig.captureErrors) {
106
- ctx.recordException(error);
107
- ctx.setStatus({
108
- code: SpanStatusCode.ERROR,
109
- message: error.message
110
- });
95
+ ctx.recordError(error);
111
96
  }
112
97
  throw error;
113
98
  }
@@ -121,19 +106,7 @@ function createTracedHandler(config = {}) {
121
106
  return function wrapHandler(handler) {
122
107
  return async function tracedHandler(request, opts) {
123
108
  const url = new URL(request.url);
124
- const shouldExclude = mergedConfig.excludePaths.some((pattern) => {
125
- if (typeof pattern === "string") {
126
- if (pattern.includes("*")) {
127
- const regex = new RegExp(
128
- "^" + pattern.replaceAll("*", ".*").replaceAll("?", ".") + "$"
129
- );
130
- return regex.test(url.pathname);
131
- }
132
- return url.pathname === pattern || url.pathname.startsWith(pattern);
133
- }
134
- return pattern.test(url.pathname);
135
- });
136
- if (shouldExclude) {
109
+ if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) {
137
110
  return handler(request, opts);
138
111
  }
139
112
  const parentContext = extractContextFromRequest(request);
@@ -197,11 +170,7 @@ function createTracedHandler(config = {}) {
197
170
  duration
198
171
  );
199
172
  if (mergedConfig.captureErrors) {
200
- ctx.recordException(error);
201
- ctx.setStatus({
202
- code: SpanStatusCode.ERROR,
203
- message: error.message
204
- });
173
+ ctx.recordError(error);
205
174
  }
206
175
  throw error;
207
176
  }
@@ -212,5 +181,5 @@ function createTracedHandler(config = {}) {
212
181
  }
213
182
 
214
183
  export { createTracedHandler, wrapStartHandler };
215
- //# sourceMappingURL=chunk-Z5D2V4DU.js.map
216
- //# sourceMappingURL=chunk-Z5D2V4DU.js.map
184
+ //# sourceMappingURL=chunk-FFQ4FJKE.js.map
185
+ //# sourceMappingURL=chunk-FFQ4FJKE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers.ts"],"names":[],"mappings":"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","file":"chunk-FFQ4FJKE.js","sourcesContent":["import { context, SpanStatusCode } from '@opentelemetry/api';\nimport { trace, init, type TraceContext } from 'autotel';\nimport { extractContextFromRequest } from './context';\nimport { isExcludedPath } from './route-filter';\nimport {\n type WrapStartHandlerConfig,\n DEFAULT_CONFIG,\n SPAN_ATTRIBUTES,\n} from './types';\n\n/**\n * Request handler type (compatible with TanStack Start handlers)\n */\ntype RequestHandler = (\n request: Request,\n opts?: { context?: Record<string, unknown> },\n) => Promise<Response> | Response;\n\n/**\n * Wrap a TanStack Start handler with OpenTelemetry tracing\n *\n * This function wraps the entire request handler to automatically create\n * spans for all incoming requests. It initializes OpenTelemetry and\n * provides comprehensive request tracing.\n *\n * @param config - Configuration options including OTLP endpoint and headers\n * @returns Function that wraps a request handler\n *\n * @example\n * ```typescript\n * // server.ts\n * import { createStartHandler, defaultStreamHandler } from '@tanstack/react-start/server';\n * import { wrapStartHandler } from 'autotel-tanstack/handlers';\n *\n * export default wrapStartHandler({\n * service: 'my-app',\n * endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,\n * headers: { 'x-honeycomb-team': process.env.HONEYCOMB_API_KEY },\n * })(createStartHandler(defaultStreamHandler));\n * ```\n *\n * @example\n * ```typescript\n * // With env var configuration (recommended for production)\n * // Set OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS\n * export default wrapStartHandler()(createStartHandler(defaultStreamHandler));\n * ```\n */\nexport function wrapStartHandler(\n config: WrapStartHandlerConfig = {},\n): (handler: RequestHandler) => RequestHandler {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // Initialize autotel with provided configuration\n const service =\n config.service || process.env.OTEL_SERVICE_NAME || 'tanstack-start';\n const endpoint = config.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n\n // Parse headers from env if not provided\n let headers = config.headers;\n if (!headers && process.env.OTEL_EXPORTER_OTLP_HEADERS) {\n headers = {};\n const pairs = process.env.OTEL_EXPORTER_OTLP_HEADERS.split(',');\n for (const pair of pairs) {\n const [key, value] = pair.split('=');\n if (key && value) {\n headers[key.trim()] = value.trim();\n }\n }\n }\n\n // Initialize OpenTelemetry\n init({\n service,\n endpoint,\n headers,\n });\n\n return function wrapHandler(handler: RequestHandler): RequestHandler {\n return async function tracedHandler(\n request: Request,\n opts?: { context?: Record<string, unknown> },\n ): Promise<Response> {\n const url = new URL(request.url);\n\n // Check if path should be excluded\n if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) {\n return handler(request, opts);\n }\n\n // Extract parent context from request headers\n const parentContext = extractContextFromRequest(request);\n\n // Run within parent context\n return context.with(parentContext, async () => {\n const spanName = `${request.method} ${url.pathname}`;\n\n return trace(spanName, async (ctx: TraceContext) => {\n // Set HTTP semantic attributes\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.HTTP_REQUEST_METHOD]: request.method,\n [SPAN_ATTRIBUTES.URL_PATH]: url.pathname,\n [SPAN_ATTRIBUTES.URL_FULL]: request.url,\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'request',\n });\n\n if (url.search) {\n ctx.setAttribute(SPAN_ATTRIBUTES.URL_QUERY, url.search);\n }\n\n // Capture configured headers\n if (mergedConfig.captureHeaders) {\n for (const header of mergedConfig.captureHeaders) {\n const value = request.headers.get(header);\n if (value) {\n ctx.setAttribute(\n `http.request.header.${header.toLowerCase()}`,\n value,\n );\n }\n }\n }\n\n // Add custom attributes\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 response = await handler(request, opts);\n const duration = Date.now() - startTime;\n\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS,\n duration,\n );\n ctx.setAttribute(\n SPAN_ATTRIBUTES.HTTP_RESPONSE_STATUS_CODE,\n response.status,\n );\n\n // Set status based on HTTP status code\n if (response.status >= 400) {\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: `HTTP ${response.status}`,\n });\n } else {\n ctx.setStatus({ code: SpanStatusCode.OK });\n }\n\n return response;\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.recordError(error);\n }\n\n throw error;\n }\n });\n });\n };\n };\n}\n\n/**\n * Create a traced handler without auto-initialization\n *\n * Use this when you want to initialize autotel separately\n * (e.g., with more advanced configuration).\n *\n * @param config - Configuration options (excluding endpoint/headers)\n * @returns Function that wraps a request handler\n *\n * @example\n * ```typescript\n * import { init } from 'autotel';\n * import { createTracedHandler } from 'autotel-tanstack/handlers';\n *\n * // Initialize autotel with custom configuration\n * init({\n * service: 'my-app',\n * endpoint: 'https://api.honeycomb.io',\n * instrumentations: [/* custom instrumentations *\\/],\n * });\n *\n * // Wrap handler without re-initializing\n * export default createTracedHandler({\n * captureHeaders: ['x-request-id'],\n * })(createStartHandler(defaultStreamHandler));\n * ```\n */\nexport function createTracedHandler(\n config: Omit<WrapStartHandlerConfig, 'endpoint' | 'headers' | 'service'> = {},\n): (handler: RequestHandler) => RequestHandler {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n return function wrapHandler(handler: RequestHandler): RequestHandler {\n return async function tracedHandler(\n request: Request,\n opts?: { context?: Record<string, unknown> },\n ): Promise<Response> {\n const url = new URL(request.url);\n\n // Check if path should be excluded\n if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) {\n return handler(request, opts);\n }\n\n const parentContext = extractContextFromRequest(request);\n\n return context.with(parentContext, async () => {\n const spanName = `${request.method} ${url.pathname}`;\n\n return trace(spanName, async (ctx: TraceContext) => {\n ctx.setAttributes({\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 ctx.setAttribute(SPAN_ATTRIBUTES.URL_QUERY, url.search);\n }\n\n if (mergedConfig.captureHeaders) {\n for (const header of mergedConfig.captureHeaders) {\n const value = request.headers.get(header);\n if (value) {\n ctx.setAttribute(\n `http.request.header.${header.toLowerCase()}`,\n value,\n );\n }\n }\n }\n\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 response = await handler(request, opts);\n const duration = Date.now() - startTime;\n\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS,\n duration,\n );\n ctx.setAttribute(\n SPAN_ATTRIBUTES.HTTP_RESPONSE_STATUS_CODE,\n response.status,\n );\n\n if (response.status >= 400) {\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: `HTTP ${response.status}`,\n });\n } else {\n ctx.setStatus({ code: SpanStatusCode.OK });\n }\n\n return response;\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.recordError(error);\n }\n\n throw error;\n }\n });\n });\n };\n };\n}\n"]}
@@ -79,11 +79,11 @@ function traceLoader(loaderFn, config = {}) {
79
79
  ctx.setStatus({ code: SpanStatusCode.OK });
80
80
  return asyncResult;
81
81
  } catch (error) {
82
- ctx.recordException(error);
83
- ctx.setStatus({
84
- code: SpanStatusCode.ERROR,
85
- message: error.message
86
- });
82
+ if ("recordError" in ctx && typeof ctx.recordError === "function") {
83
+ ctx.recordError(error);
84
+ } else if ("recordException" in ctx && typeof ctx.recordException === "function") {
85
+ ctx.recordException(error);
86
+ }
87
87
  throw error;
88
88
  }
89
89
  });
@@ -153,11 +153,11 @@ function traceBeforeLoad(beforeLoadFn, config = {}) {
153
153
  ctx.setAttribute("tanstack.beforeLoad.redirect", true);
154
154
  ctx.setStatus({ code: SpanStatusCode.OK });
155
155
  } else {
156
- ctx.recordException(error);
157
- ctx.setStatus({
158
- code: SpanStatusCode.ERROR,
159
- message: error.message
160
- });
156
+ if ("recordError" in ctx && typeof ctx.recordError === "function") {
157
+ ctx.recordError(error);
158
+ } else if ("recordException" in ctx && typeof ctx.recordException === "function") {
159
+ ctx.recordException(error);
160
+ }
161
161
  }
162
162
  throw error;
163
163
  }
@@ -189,5 +189,5 @@ function createTracedRoute(routeId, config = {}) {
189
189
  }
190
190
 
191
191
  export { createTracedRoute, traceBeforeLoad, traceLoader };
192
- //# sourceMappingURL=chunk-5RYSHDCO.js.map
193
- //# sourceMappingURL=chunk-5RYSHDCO.js.map
192
+ //# sourceMappingURL=chunk-KPXGFKPU.js.map
193
+ //# sourceMappingURL=chunk-KPXGFKPU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/loaders.ts"],"names":[],"mappings":"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","file":"chunk-KPXGFKPU.js","sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { trace, type TraceContext } from 'autotel';\nimport { isServerSide } from './env';\nimport { type TraceLoaderConfig, SPAN_ATTRIBUTES } from './types';\n\n// Re-export types from @tanstack/react-router for consumers who need them\nexport type { LoaderFnContext } from '@tanstack/react-router';\n\n/**\n * Internal type for extracting route info from TanStack context.\n * This is a minimal interface used only for instrumentation - the actual\n * TanStack types flow through the generic parameter.\n */\ninterface TanStackContextInternal {\n route?: { id?: string };\n params?: Record<string, string>;\n}\n\n/**\n * Wrap a TanStack route loader with OpenTelemetry tracing\n *\n * This function wraps a loader function to automatically create spans\n * for each invocation. It captures route ID, params (optionally),\n * and errors.\n *\n * The generic type TLoaderFn preserves the full TanStack Router type inference,\n * including typed params, context, and return types.\n *\n * @param loaderFn - The loader function to wrap\n * @param config - Configuration options\n * @returns Wrapped loader function with tracing (preserves original types)\n *\n * @example\n * ```typescript\n * import { createFileRoute } from '@tanstack/react-router';\n * import { traceLoader } from 'autotel-tanstack/loaders';\n *\n * export const Route = createFileRoute('/users/$userId')({\n * // Types are fully preserved - params.userId is typed as string\n * loader: traceLoader(async ({ params }) => {\n * return await db.users.findUnique({ where: { id: params.userId } });\n * }),\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Sync loaders are also supported\n * export const Route = createFileRoute('/static')({\n * loader: traceLoader(({ context }) => ({\n * message: `Welcome, ${context.userId}!`,\n * })),\n * });\n * ```\n */\nexport function traceLoader<TLoaderFn extends (ctx: any) => any>(\n loaderFn: TLoaderFn,\n config: TraceLoaderConfig = {},\n): TLoaderFn {\n const captureParams = config.captureParams ?? true;\n const captureResult = config.captureResult ?? false;\n\n const wrapped = (context: TanStackContextInternal) => {\n // If we're in the browser, just call the loader without tracing\n // This prevents autotel (which uses Node.js APIs) from being executed in the browser\n if (!isServerSide()) {\n return loaderFn(context);\n }\n\n const routeId = context?.route?.id || 'unknown';\n const spanName = config.name || `tanstack.loader.${routeId}`;\n\n // Handle both sync and async loaders\n const result = loaderFn(context);\n const isPromise = result instanceof Promise;\n\n if (!isPromise) {\n // Sync loader - wrap in trace synchronously\n return trace(spanName, (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'loader',\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID]: routeId,\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_TYPE]: 'loader',\n });\n\n if (captureParams && context?.params) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n JSON.stringify(context.params),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n '[non-serializable]',\n );\n }\n }\n\n if (captureResult && result !== undefined) {\n try {\n ctx.setAttribute('tanstack.loader.result', JSON.stringify(result));\n } catch {\n ctx.setAttribute('tanstack.loader.result', '[non-serializable]');\n }\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return result;\n });\n }\n\n // Async loader\n return trace(spanName, async (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'loader',\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID]: routeId,\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_TYPE]: 'loader',\n });\n\n if (captureParams && context?.params) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n JSON.stringify(context.params),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n '[non-serializable]',\n );\n }\n }\n\n try {\n const asyncResult = await result;\n\n if (captureResult && asyncResult !== undefined) {\n try {\n ctx.setAttribute(\n 'tanstack.loader.result',\n JSON.stringify(asyncResult),\n );\n } catch {\n ctx.setAttribute('tanstack.loader.result', '[non-serializable]');\n }\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return asyncResult;\n } catch (error) {\n if ('recordError' in ctx && typeof ctx.recordError === 'function') {\n ctx.recordError(error);\n } else if (\n 'recordException' in ctx &&\n typeof ctx.recordException === 'function'\n ) {\n ctx.recordException(error);\n }\n throw error;\n }\n });\n };\n\n return wrapped as TLoaderFn;\n}\n\n/**\n * Wrap a TanStack route beforeLoad function with OpenTelemetry tracing\n *\n * This function wraps a beforeLoad function to automatically create spans.\n * beforeLoad runs before the route component renders and is typically\n * used for auth checks, redirects, or data prefetching.\n *\n * The generic type TBeforeLoadFn preserves the full TanStack Router type inference,\n * including typed params, context, search, and return types.\n *\n * @param beforeLoadFn - The beforeLoad function to wrap\n * @param config - Configuration options\n * @returns Wrapped beforeLoad function with tracing (preserves original types)\n *\n * @example\n * ```typescript\n * import { createFileRoute, redirect } from '@tanstack/react-router';\n * import { traceBeforeLoad } from 'autotel-tanstack/loaders';\n *\n * export const Route = createFileRoute('/dashboard')({\n * // Types are fully preserved - context, params, search are all typed\n * beforeLoad: traceBeforeLoad(async ({ context, params }) => {\n * if (!context.auth.isAuthenticated) {\n * throw redirect({ to: '/login' });\n * }\n * return { userId: params.userId }; // Return type flows to loader context\n * }),\n * loader: ({ context }) => {\n * // context.userId is typed from beforeLoad return\n * return { user: context.userId };\n * },\n * });\n * ```\n */\nexport function traceBeforeLoad<TBeforeLoadFn extends (opts: any) => any>(\n beforeLoadFn: TBeforeLoadFn,\n config: TraceLoaderConfig = {},\n): TBeforeLoadFn {\n const captureParams = config.captureParams ?? true;\n\n const wrapped = (input: TanStackContextInternal) => {\n // Skip tracing in browser\n if (!isServerSide()) {\n return beforeLoadFn(input);\n }\n\n const routeId = input?.route?.id || 'unknown';\n const spanName = config.name || `tanstack.beforeLoad.${routeId}`;\n\n // Handle both sync and async beforeLoad\n const result = beforeLoadFn(input);\n const isPromise = result instanceof Promise;\n\n if (!isPromise) {\n // Sync beforeLoad\n return trace(spanName, (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'beforeLoad',\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID]: routeId,\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_TYPE]: 'beforeLoad',\n });\n\n if (captureParams && input?.params) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n JSON.stringify(input.params),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n '[non-serializable]',\n );\n }\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return result;\n });\n }\n\n // Async beforeLoad\n return trace(spanName, async (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'beforeLoad',\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID]: routeId,\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_TYPE]: 'beforeLoad',\n });\n\n if (captureParams && input?.params) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n JSON.stringify(input.params),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n '[non-serializable]',\n );\n }\n }\n\n try {\n const asyncResult = await result;\n ctx.setStatus({ code: SpanStatusCode.OK });\n return asyncResult;\n } catch (error) {\n // Check if this is a redirect or notFound (expected control flow)\n const errorName = (error as Error).name;\n if (errorName === 'RedirectError' || errorName === 'NotFoundError') {\n // Mark as OK since these are expected control flow\n ctx.setAttribute('tanstack.beforeLoad.redirect', true);\n ctx.setStatus({ code: SpanStatusCode.OK });\n } else {\n if ('recordError' in ctx && typeof ctx.recordError === 'function') {\n ctx.recordError(error);\n } else if (\n 'recordException' in ctx &&\n typeof ctx.recordException === 'function'\n ) {\n ctx.recordException(error);\n }\n }\n throw error;\n }\n });\n };\n\n return wrapped as TBeforeLoadFn;\n}\n\n/**\n * Create a traced route configuration helper\n *\n * This higher-order function helps create route configurations\n * with automatic tracing for both loader and beforeLoad.\n *\n * @param routeId - The route identifier\n * @param config - Tracing configuration\n * @returns Object with traced loader and beforeLoad wrappers\n *\n * @example\n * ```typescript\n * import { createFileRoute } from '@tanstack/react-router';\n * import { createTracedRoute } from 'autotel-tanstack/loaders';\n *\n * const traced = createTracedRoute('/users/$userId');\n *\n * export const Route = createFileRoute('/users/$userId')({\n * beforeLoad: traced.beforeLoad(async ({ context }) => {\n * // Auth check\n * }),\n * loader: traced.loader(async ({ params }) => {\n * return await getUser(params.userId);\n * }),\n * });\n * ```\n */\nexport function createTracedRoute(\n routeId: string,\n config: Omit<TraceLoaderConfig, 'name'> = {},\n) {\n return {\n /**\n * Wrap a loader function with tracing\n */\n loader<TLoaderFn extends (ctx: any) => any>(\n loaderFn: TLoaderFn,\n ): TLoaderFn {\n return traceLoader(loaderFn, {\n ...config,\n name: `tanstack.loader.${routeId}`,\n });\n },\n\n /**\n * Wrap a beforeLoad function with tracing\n */\n beforeLoad<TBeforeLoadFn extends (opts: any) => any>(\n beforeLoadFn: TBeforeLoadFn,\n ): TBeforeLoadFn {\n return traceBeforeLoad(beforeLoadFn, {\n ...config,\n name: `tanstack.beforeLoad.${routeId}`,\n });\n },\n };\n}\n"]}
@@ -1,26 +1,10 @@
1
1
  import { isServerSide } from './chunk-EUYFVNYE.js';
2
+ import { isExcludedPath } from './chunk-CCME55EK.js';
2
3
  import { DEFAULT_CONFIG, SPAN_ATTRIBUTES } from './chunk-I4LX3LOG.js';
3
4
  import { extractContextFromRequest } from './chunk-NTY64BKS.js';
4
5
  import { SpanStatusCode, context } from '@opentelemetry/api';
5
6
  import { trace } from 'autotel';
6
7
 
7
- function shouldExcludePath(pathname, excludePaths) {
8
- for (const pattern of excludePaths) {
9
- if (typeof pattern === "string") {
10
- if (pattern.includes("*")) {
11
- const regex = new RegExp(
12
- "^" + pattern.replaceAll("*", ".*").replaceAll("?", ".") + "$"
13
- );
14
- if (regex.test(pathname)) return true;
15
- } else {
16
- if (pathname === pattern || pathname.startsWith(pattern)) return true;
17
- }
18
- } else {
19
- if (pattern.test(pathname)) return true;
20
- }
21
- }
22
- return false;
23
- }
24
8
  function buildRequestAttributes(request, config) {
25
9
  const url = new URL(request.url);
26
10
  const attrs = {
@@ -109,11 +93,11 @@ function createTracingMiddleware(config) {
109
93
  return result;
110
94
  } catch (error) {
111
95
  if (mergedConfig.captureErrors) {
112
- ctx.recordException(error);
113
- ctx.setStatus({
114
- code: SpanStatusCode.ERROR,
115
- message: error.message
116
- });
96
+ if ("recordError" in ctx && typeof ctx.recordError === "function") {
97
+ ctx.recordError(error);
98
+ } else if ("recordException" in ctx && typeof ctx.recordException === "function") {
99
+ ctx.recordException(error);
100
+ }
117
101
  try {
118
102
  const { reportError } = await import('./error-reporting.js');
119
103
  reportError(error, {
@@ -132,7 +116,7 @@ function createTracingMiddleware(config) {
132
116
  return next();
133
117
  }
134
118
  const url = new URL(request.url);
135
- if (shouldExcludePath(url.pathname, mergedConfig.excludePaths)) {
119
+ if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) {
136
120
  return next();
137
121
  }
138
122
  const parentContext = extractContextFromRequest(request);
@@ -179,11 +163,11 @@ function createTracingMiddleware(config) {
179
163
  duration
180
164
  );
181
165
  if (mergedConfig.captureErrors) {
182
- ctx.recordException(error);
183
- ctx.setStatus({
184
- code: SpanStatusCode.ERROR,
185
- message: error.message
186
- });
166
+ if ("recordError" in ctx && typeof ctx.recordError === "function") {
167
+ ctx.recordError(error);
168
+ } else if ("recordException" in ctx && typeof ctx.recordException === "function") {
169
+ ctx.recordException(error);
170
+ }
187
171
  try {
188
172
  const { reportError } = await import('./error-reporting.js');
189
173
  reportError(error, {
@@ -222,5 +206,5 @@ function createTracingServerHandler(config) {
222
206
  }
223
207
 
224
208
  export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware };
225
- //# sourceMappingURL=chunk-NP4OJO7T.js.map
226
- //# sourceMappingURL=chunk-NP4OJO7T.js.map
209
+ //# sourceMappingURL=chunk-LRA2UVVS.js.map
210
+ //# sourceMappingURL=chunk-LRA2UVVS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts"],"names":["tracingMiddleware"],"mappings":"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","file":"chunk-LRA2UVVS.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 { isExcludedPath } from './route-filter';\nimport {\n type TracingMiddlewareConfig,\n DEFAULT_CONFIG,\n SPAN_ATTRIBUTES,\n} from './types';\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 if ('recordError' in ctx && typeof ctx.recordError === 'function') {\n ctx.recordError(error);\n } else if (\n 'recordException' in ctx &&\n typeof ctx.recordException === 'function'\n ) {\n ctx.recordException(error);\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 (isExcludedPath(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 if ('recordError' in ctx && typeof ctx.recordError === 'function') {\n ctx.recordError(error);\n } else if (\n 'recordException' in ctx &&\n typeof ctx.recordException === 'function'\n ) {\n ctx.recordException(error);\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: any) => any {\n const handler = createTracingMiddleware<TContext>(config);\n\n // Adapt TanStack's signature to our handler\n return async (opts: any) => {\n return handler(opts);\n };\n}\n"]}
package/dist/handlers.js CHANGED
@@ -1,4 +1,5 @@
1
- export { createTracedHandler, wrapStartHandler } from './chunk-Z5D2V4DU.js';
1
+ export { createTracedHandler, wrapStartHandler } from './chunk-FFQ4FJKE.js';
2
+ import './chunk-CCME55EK.js';
2
3
  import './chunk-I4LX3LOG.js';
3
4
  import './chunk-NTY64BKS.js';
4
5
  import './chunk-EGRHWZRV.js';
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  export { debugHeadersMiddleware } from './chunk-UTPW3QRT.js';
2
2
  export { createMetricsHandler, metricsCollector, recordTiming } from './chunk-G526TOMY.js';
3
3
  export { createErrorReportingHandler, errorStore, reportError, withErrorReporting } from './chunk-XXBHZR3M.js';
4
- export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from './chunk-NP4OJO7T.js';
5
- export { createTracedServerFnFactory, traceServerFn } from './chunk-5JJXFTG3.js';
6
- export { createTracedRoute, traceBeforeLoad, traceLoader } from './chunk-5RYSHDCO.js';
4
+ export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from './chunk-LRA2UVVS.js';
5
+ export { createTracedServerFnFactory, traceServerFn } from './chunk-ESU66L3L.js';
6
+ export { createTracedRoute, traceBeforeLoad, traceLoader } from './chunk-KPXGFKPU.js';
7
7
  export { isBrowser, isNode, isServerSide } from './chunk-EUYFVNYE.js';
8
- export { createTracedHandler, wrapStartHandler } from './chunk-Z5D2V4DU.js';
8
+ export { createTracedHandler, wrapStartHandler } from './chunk-FFQ4FJKE.js';
9
+ import './chunk-CCME55EK.js';
9
10
  export { DEFAULT_CONFIG, SPAN_ATTRIBUTES } from './chunk-I4LX3LOG.js';
10
11
  export { createTracedHeaders, extractContextFromRequest, getActiveContext, injectContextToHeaders, runInContext } from './chunk-NTY64BKS.js';
11
12
  import './chunk-EGRHWZRV.js';
package/dist/loaders.js CHANGED
@@ -1,4 +1,4 @@
1
- export { createTracedRoute, traceBeforeLoad, traceLoader } from './chunk-5RYSHDCO.js';
1
+ export { createTracedRoute, traceBeforeLoad, traceLoader } from './chunk-KPXGFKPU.js';
2
2
  import './chunk-EUYFVNYE.js';
3
3
  import './chunk-I4LX3LOG.js';
4
4
  import './chunk-EGRHWZRV.js';
@@ -1,5 +1,6 @@
1
- export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from './chunk-NP4OJO7T.js';
1
+ export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware } from './chunk-LRA2UVVS.js';
2
2
  import './chunk-EUYFVNYE.js';
3
+ import './chunk-CCME55EK.js';
3
4
  import './chunk-I4LX3LOG.js';
4
5
  import './chunk-NTY64BKS.js';
5
6
  import './chunk-EGRHWZRV.js';
@@ -1,4 +1,4 @@
1
- export { createTracedServerFnFactory, traceServerFn } from './chunk-5JJXFTG3.js';
1
+ export { createTracedServerFnFactory, traceServerFn } from './chunk-ESU66L3L.js';
2
2
  import './chunk-EUYFVNYE.js';
3
3
  import './chunk-I4LX3LOG.js';
4
4
  import './chunk-EGRHWZRV.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel-tanstack",
3
- "version": "1.13.10",
3
+ "version": "1.13.14",
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",
@@ -98,9 +98,7 @@
98
98
  "dist",
99
99
  "src",
100
100
  "README.md",
101
- "skills",
102
- "!skills/_artifacts",
103
- "bin"
101
+ "skills"
104
102
  ],
105
103
  "keywords": [
106
104
  "opentelemetry",
@@ -122,13 +120,13 @@
122
120
  "license": "MIT",
123
121
  "dependencies": {
124
122
  "@opentelemetry/api": "^1.9.1",
125
- "@tanstack/intent": "^0.0.36",
126
- "autotel": "2.26.3",
127
- "autotel-adapters": "0.2.9"
123
+ "autotel": "3.0.3",
124
+ "autotel-adapters": "0.2.13",
125
+ "autotel-edge": "3.16.8"
128
126
  },
129
127
  "peerDependencies": {
130
- "@tanstack/react-start": "^1.167.49",
131
- "@tanstack/solid-start": "^1.167.47"
128
+ "@tanstack/react-start": "^1.167.59",
129
+ "@tanstack/solid-start": "^1.167.56"
132
130
  },
133
131
  "peerDependenciesMeta": {
134
132
  "@tanstack/react-start": {
@@ -139,8 +137,8 @@
139
137
  }
140
138
  },
141
139
  "devDependencies": {
142
- "@opentelemetry/sdk-trace-base": "^2.7.0",
143
- "@tanstack/react-router": "^1.168.24",
140
+ "@opentelemetry/sdk-trace-base": "^2.7.1",
141
+ "@tanstack/react-router": "^1.169.1",
144
142
  "@types/node": "^25.6.0",
145
143
  "rimraf": "^6.1.3",
146
144
  "tsup": "^8.5.1",
@@ -157,14 +155,11 @@
157
155
  "url": "https://github.com/jagreehal/autotel/issues"
158
156
  },
159
157
  "homepage": "https://github.com/jagreehal/autotel/tree/main/packages/autotel-tanstack#readme",
160
- "bin": {
161
- "intent": "./bin/intent.js"
162
- },
163
158
  "scripts": {
164
159
  "build": "tsup",
165
160
  "dev": "tsup --watch",
166
- "lint": "npx eslint src/**/*.ts",
167
- "lint:fix": "npx eslint src/**/*.ts --fix",
161
+ "lint": "eslint src/**/*.ts",
162
+ "lint:fix": "eslint src/**/*.ts --fix",
168
163
  "format": "prettier --write .",
169
164
  "format:check": "prettier --check src/**/*.ts",
170
165
  "type-check": "tsc --noEmit",
@@ -2,11 +2,6 @@
2
2
  name: autotel-tanstack
3
3
  description: >
4
4
  OpenTelemetry for TanStack Start. Trace server functions, route loaders, middleware, and request handlers. Supports zero-config, middleware-based, and explicit wrapper patterns.
5
- type: integration
6
- library: autotel-tanstack
7
- library_version: '1.12.0'
8
- sources:
9
- - jagreehal/autotel:packages/autotel-tanstack/CLAUDE.md
10
5
  ---
11
6
 
12
7
  # autotel-tanstack
package/src/handlers.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { context, SpanStatusCode } from '@opentelemetry/api';
2
2
  import { trace, init, type TraceContext } from 'autotel';
3
3
  import { extractContextFromRequest } from './context';
4
+ import { isExcludedPath } from './route-filter';
4
5
  import {
5
6
  type WrapStartHandlerConfig,
6
7
  DEFAULT_CONFIG,
@@ -83,20 +84,7 @@ export function wrapStartHandler(
83
84
  const url = new URL(request.url);
84
85
 
85
86
  // Check if path should be excluded
86
- const shouldExclude = mergedConfig.excludePaths.some((pattern) => {
87
- if (typeof pattern === 'string') {
88
- if (pattern.includes('*')) {
89
- const regex = new RegExp(
90
- '^' + pattern.replaceAll('*', '.*').replaceAll('?', '.') + '$',
91
- );
92
- return regex.test(url.pathname);
93
- }
94
- return url.pathname === pattern || url.pathname.startsWith(pattern);
95
- }
96
- return pattern.test(url.pathname);
97
- });
98
-
99
- if (shouldExclude) {
87
+ if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) {
100
88
  return handler(request, opts);
101
89
  }
102
90
 
@@ -179,11 +167,7 @@ export function wrapStartHandler(
179
167
  );
180
168
 
181
169
  if (mergedConfig.captureErrors) {
182
- ctx.recordException(error as Error);
183
- ctx.setStatus({
184
- code: SpanStatusCode.ERROR,
185
- message: (error as Error).message,
186
- });
170
+ ctx.recordError(error);
187
171
  }
188
172
 
189
173
  throw error;
@@ -234,20 +218,7 @@ export function createTracedHandler(
234
218
  const url = new URL(request.url);
235
219
 
236
220
  // Check if path should be excluded
237
- const shouldExclude = mergedConfig.excludePaths.some((pattern) => {
238
- if (typeof pattern === 'string') {
239
- if (pattern.includes('*')) {
240
- const regex = new RegExp(
241
- '^' + pattern.replaceAll('*', '.*').replaceAll('?', '.') + '$',
242
- );
243
- return regex.test(url.pathname);
244
- }
245
- return url.pathname === pattern || url.pathname.startsWith(pattern);
246
- }
247
- return pattern.test(url.pathname);
248
- });
249
-
250
- if (shouldExclude) {
221
+ if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) {
251
222
  return handler(request, opts);
252
223
  }
253
224
 
@@ -323,11 +294,7 @@ export function createTracedHandler(
323
294
  );
324
295
 
325
296
  if (mergedConfig.captureErrors) {
326
- ctx.recordException(error as Error);
327
- ctx.setStatus({
328
- code: SpanStatusCode.ERROR,
329
- message: (error as Error).message,
330
- });
297
+ ctx.recordError(error);
331
298
  }
332
299
 
333
300
  throw error;
package/src/loaders.ts CHANGED
@@ -149,11 +149,14 @@ export function traceLoader<TLoaderFn extends (ctx: any) => any>(
149
149
  ctx.setStatus({ code: SpanStatusCode.OK });
150
150
  return asyncResult;
151
151
  } catch (error) {
152
- ctx.recordException(error as Error);
153
- ctx.setStatus({
154
- code: SpanStatusCode.ERROR,
155
- message: (error as Error).message,
156
- });
152
+ if ('recordError' in ctx && typeof ctx.recordError === 'function') {
153
+ ctx.recordError(error);
154
+ } else if (
155
+ 'recordException' in ctx &&
156
+ typeof ctx.recordException === 'function'
157
+ ) {
158
+ ctx.recordException(error);
159
+ }
157
160
  throw error;
158
161
  }
159
162
  });
@@ -277,11 +280,14 @@ export function traceBeforeLoad<TBeforeLoadFn extends (opts: any) => any>(
277
280
  ctx.setAttribute('tanstack.beforeLoad.redirect', true);
278
281
  ctx.setStatus({ code: SpanStatusCode.OK });
279
282
  } else {
280
- ctx.recordException(error as Error);
281
- ctx.setStatus({
282
- code: SpanStatusCode.ERROR,
283
- message: (error as Error).message,
284
- });
283
+ if ('recordError' in ctx && typeof ctx.recordError === 'function') {
284
+ ctx.recordError(error);
285
+ } else if (
286
+ 'recordException' in ctx &&
287
+ typeof ctx.recordException === 'function'
288
+ ) {
289
+ ctx.recordException(error);
290
+ }
285
291
  }
286
292
  throw error;
287
293
  }
package/src/middleware.ts CHANGED
@@ -2,37 +2,13 @@ import { context, SpanStatusCode, type Attributes } from '@opentelemetry/api';
2
2
  import { trace, type TraceContext } from 'autotel';
3
3
  import { extractContextFromRequest } from './context';
4
4
  import { isServerSide } from './env';
5
+ import { isExcludedPath } from './route-filter';
5
6
  import {
6
7
  type TracingMiddlewareConfig,
7
8
  DEFAULT_CONFIG,
8
9
  SPAN_ATTRIBUTES,
9
10
  } from './types';
10
11
 
11
- /**
12
- * Check if a path should be excluded from tracing
13
- */
14
- function shouldExcludePath(
15
- pathname: string,
16
- excludePaths: (string | RegExp)[],
17
- ): boolean {
18
- for (const pattern of excludePaths) {
19
- if (typeof pattern === 'string') {
20
- // Simple glob matching
21
- if (pattern.includes('*')) {
22
- const regex = new RegExp(
23
- '^' + pattern.replaceAll('*', '.*').replaceAll('?', '.') + '$',
24
- );
25
- if (regex.test(pathname)) return true;
26
- } else {
27
- if (pathname === pattern || pathname.startsWith(pattern)) return true;
28
- }
29
- } else {
30
- if (pattern.test(pathname)) return true;
31
- }
32
- }
33
- return false;
34
- }
35
-
36
12
  /**
37
13
  * Build span attributes for HTTP requests
38
14
  */
@@ -219,11 +195,14 @@ export function createTracingMiddleware<TContext = unknown>(
219
195
  return result;
220
196
  } catch (error) {
221
197
  if (mergedConfig.captureErrors) {
222
- ctx.recordException(error as Error);
223
- ctx.setStatus({
224
- code: SpanStatusCode.ERROR,
225
- message: (error as Error).message,
226
- });
198
+ if ('recordError' in ctx && typeof ctx.recordError === 'function') {
199
+ ctx.recordError(error);
200
+ } else if (
201
+ 'recordException' in ctx &&
202
+ typeof ctx.recordException === 'function'
203
+ ) {
204
+ ctx.recordException(error);
205
+ }
227
206
 
228
207
  // Report error to error store
229
208
  try {
@@ -251,7 +230,7 @@ export function createTracingMiddleware<TContext = unknown>(
251
230
  const url = new URL(request.url);
252
231
 
253
232
  // Check if path should be excluded
254
- if (shouldExcludePath(url.pathname, mergedConfig.excludePaths)) {
233
+ if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) {
255
234
  return next();
256
235
  }
257
236
 
@@ -315,11 +294,14 @@ export function createTracingMiddleware<TContext = unknown>(
315
294
  );
316
295
 
317
296
  if (mergedConfig.captureErrors) {
318
- ctx.recordException(error as Error);
319
- ctx.setStatus({
320
- code: SpanStatusCode.ERROR,
321
- message: (error as Error).message,
322
- });
297
+ if ('recordError' in ctx && typeof ctx.recordError === 'function') {
298
+ ctx.recordError(error);
299
+ } else if (
300
+ 'recordException' in ctx &&
301
+ typeof ctx.recordException === 'function'
302
+ ) {
303
+ ctx.recordException(error);
304
+ }
323
305
 
324
306
  // Report error to error store
325
307
  try {
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { isExcludedPath } from './route-filter';
3
+
4
+ describe('route-filter', () => {
5
+ it('matches plain string paths as prefix for backwards compatibility', () => {
6
+ expect(isExcludedPath('/health', ['/health'])).toBe(true);
7
+ expect(isExcludedPath('/healthz', ['/health'])).toBe(true);
8
+ expect(isExcludedPath('/api/users', ['/health'])).toBe(false);
9
+ });
10
+
11
+ it('matches glob patterns through shared autotel-edge matcher', () => {
12
+ expect(isExcludedPath('/api/internal/debug', ['/api/internal/*'])).toBe(
13
+ true,
14
+ );
15
+ expect(isExcludedPath('/api/public/debug', ['/api/internal/*'])).toBe(
16
+ false,
17
+ );
18
+ });
19
+
20
+ it('matches regex patterns', () => {
21
+ expect(isExcludedPath('/api/v2/health', [/^\/api\/v\d+\/health$/])).toBe(
22
+ true,
23
+ );
24
+ expect(isExcludedPath('/api/v2/users', [/^\/api\/v\d+\/health$/])).toBe(
25
+ false,
26
+ );
27
+ });
28
+ });
@@ -0,0 +1,40 @@
1
+ import { shouldInstrumentPath } from 'autotel-edge';
2
+
3
+ /**
4
+ * TanStack historically supported:
5
+ * - glob strings (`/api/internal/*`)
6
+ * - regex values
7
+ * - plain-string prefix matching (`/health` matches `/healthz`)
8
+ *
9
+ * This helper keeps those semantics while delegating glob matching to
10
+ * autotel-edge's shared middleware toolkit.
11
+ */
12
+ export function isExcludedPath(
13
+ pathname: string,
14
+ excludePaths: Array<string | RegExp>,
15
+ ): boolean {
16
+ for (const pattern of excludePaths) {
17
+ if (pattern instanceof RegExp) {
18
+ if (pattern.test(pathname)) return true;
19
+ continue;
20
+ }
21
+
22
+ if (pattern.includes('*') || pattern.includes('?')) {
23
+ if (
24
+ !shouldInstrumentPath(pathname, {
25
+ include: undefined,
26
+ exclude: [pattern],
27
+ })
28
+ ) {
29
+ return true;
30
+ }
31
+ continue;
32
+ }
33
+
34
+ if (pathname === pattern || pathname.startsWith(pattern)) {
35
+ return true;
36
+ }
37
+ }
38
+
39
+ return false;
40
+ }
@@ -107,11 +107,14 @@ export function traceServerFn<T extends (...args: any[]) => any>(
107
107
  ctx.setStatus({ code: SpanStatusCode.OK });
108
108
  return result;
109
109
  } catch (error) {
110
- ctx.recordException(error as Error);
111
- ctx.setStatus({
112
- code: SpanStatusCode.ERROR,
113
- message: (error as Error).message,
114
- });
110
+ if ('recordError' in ctx && typeof ctx.recordError === 'function') {
111
+ ctx.recordError(error);
112
+ } else if (
113
+ 'recordException' in ctx &&
114
+ typeof ctx.recordException === 'function'
115
+ ) {
116
+ ctx.recordException(error);
117
+ }
115
118
  throw error;
116
119
  }
117
120
  });
package/bin/intent.js DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- // Auto-generated by @tanstack/intent setup
3
- // Exposes the intent end-user CLI for consumers of this library.
4
- // Commit this file, then add to your package.json:
5
- // "bin": { "intent": "./bin/intent.js" }
6
- await import('@tanstack/intent/intent-library');
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server-functions.ts"],"names":[],"mappings":"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","file":"chunk-5JJXFTG3.js","sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { trace, type TraceContext } from 'autotel';\nimport { isServerSide } from './env';\nimport { type TraceServerFnConfig, SPAN_ATTRIBUTES } from './types';\n\n/**\n * Wrap a TanStack server function with OpenTelemetry tracing\n *\n * This function wraps a server function to automatically create spans\n * for each invocation. It captures function name, arguments (optionally),\n * results (optionally), and errors.\n *\n * @param serverFn - The server function to wrap\n * @param config - Configuration options\n * @returns Wrapped server function with tracing\n *\n * @example\n * ```typescript\n * import { createServerFn } from '@tanstack/react-start';\n * import { traceServerFn } from 'autotel-tanstack/server-functions';\n *\n * const getUserBase = createServerFn({ method: 'GET' })\n * .handler(async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * });\n *\n * export const getUser = traceServerFn(getUserBase, { name: 'getUser' });\n * ```\n *\n * @example\n * ```typescript\n * // With argument and result capture (careful with PII!)\n * export const createUser = traceServerFn(\n * createServerFn({ method: 'POST' })\n * .handler(async ({ data }) => {\n * return await db.users.create({ data });\n * }),\n * {\n * name: 'createUser',\n * captureArgs: true,\n * captureResults: false, // Don't capture for PII reasons\n * }\n * );\n * ```\n */\nexport function traceServerFn<T extends (...args: any[]) => any>(\n serverFn: T,\n config: TraceServerFnConfig = {},\n): T {\n const fnName = config.name || serverFn.name || 'serverFn';\n const captureArgs = config.captureArgs ?? true;\n const captureResults = config.captureResults ?? false;\n\n return new Proxy(serverFn, {\n apply(target, thisArg, argArray) {\n // If we're in the browser, just call the function without tracing\n // Server functions should never run in the browser, but this prevents\n // autotel (which uses Node.js APIs) from being executed if it somehow does\n if (!isServerSide()) {\n return target.apply(thisArg, argArray);\n }\n\n return trace(`tanstack.serverFn.${fnName}`, async (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.RPC_SYSTEM]: 'tanstack-start',\n [SPAN_ATTRIBUTES.RPC_METHOD]: fnName,\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'serverFn',\n [SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_NAME]: fnName,\n });\n\n // Capture arguments if configured\n if (captureArgs && argArray.length > 0) {\n const args = argArray[0];\n if (args !== undefined) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS,\n JSON.stringify(args),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS,\n '[non-serializable]',\n );\n }\n }\n }\n\n try {\n const result = await Reflect.apply(target, thisArg, argArray);\n\n // Capture result if configured\n if (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 ctx.recordException(error as Error);\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: (error as Error).message,\n });\n throw error;\n }\n });\n },\n\n get(target, prop, receiver) {\n return Reflect.get(target, prop, receiver);\n },\n }) as T;\n}\n\n/**\n * Create a traced version of createServerFn\n *\n * This higher-order function wraps TanStack's createServerFn to automatically\n * add tracing to all created server functions.\n *\n * @param createServerFnOriginal - The original createServerFn from TanStack\n * @param defaultConfig - Default configuration for all server functions\n * @returns Wrapped createServerFn that produces traced server functions\n *\n * @example\n * ```typescript\n * import { createServerFn as originalCreateServerFn } from '@tanstack/react-start';\n * import { createTracedServerFnFactory } from 'autotel-tanstack/server-functions';\n *\n * export const createServerFn = createTracedServerFnFactory(originalCreateServerFn);\n *\n * // Now all server functions created with createServerFn are automatically traced\n * export const getUser = createServerFn({ method: 'GET' })\n * .handler(async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * });\n * ```\n */\nexport function createTracedServerFnFactory<\n TCreateServerFn extends (...args: any[]) => any,\n>(\n createServerFnOriginal: TCreateServerFn,\n defaultConfig: Omit<TraceServerFnConfig, 'name'> = {},\n): TCreateServerFn {\n return new Proxy(createServerFnOriginal, {\n apply(target, thisArg, argArray) {\n const result = Reflect.apply(target, thisArg, argArray);\n\n // If the result has a .handler method, wrap it\n if (\n result &&\n typeof result === 'object' &&\n 'handler' in result &&\n typeof result.handler === 'function'\n ) {\n const originalHandler = result.handler.bind(result);\n\n result.handler = function tracedHandler(handlerFn: unknown) {\n const wrappedHandler = originalHandler(handlerFn as never);\n\n // Try to infer function name from the handler\n const fnName = (handlerFn as { name?: string })?.name || 'serverFn';\n\n return traceServerFn(wrappedHandler, {\n ...defaultConfig,\n name: fnName,\n });\n };\n }\n\n return result;\n },\n }) as TCreateServerFn;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/loaders.ts"],"names":[],"mappings":"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","file":"chunk-5RYSHDCO.js","sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { trace, type TraceContext } from 'autotel';\nimport { isServerSide } from './env';\nimport { type TraceLoaderConfig, SPAN_ATTRIBUTES } from './types';\n\n// Re-export types from @tanstack/react-router for consumers who need them\nexport type { LoaderFnContext } from '@tanstack/react-router';\n\n/**\n * Internal type for extracting route info from TanStack context.\n * This is a minimal interface used only for instrumentation - the actual\n * TanStack types flow through the generic parameter.\n */\ninterface TanStackContextInternal {\n route?: { id?: string };\n params?: Record<string, string>;\n}\n\n/**\n * Wrap a TanStack route loader with OpenTelemetry tracing\n *\n * This function wraps a loader function to automatically create spans\n * for each invocation. It captures route ID, params (optionally),\n * and errors.\n *\n * The generic type TLoaderFn preserves the full TanStack Router type inference,\n * including typed params, context, and return types.\n *\n * @param loaderFn - The loader function to wrap\n * @param config - Configuration options\n * @returns Wrapped loader function with tracing (preserves original types)\n *\n * @example\n * ```typescript\n * import { createFileRoute } from '@tanstack/react-router';\n * import { traceLoader } from 'autotel-tanstack/loaders';\n *\n * export const Route = createFileRoute('/users/$userId')({\n * // Types are fully preserved - params.userId is typed as string\n * loader: traceLoader(async ({ params }) => {\n * return await db.users.findUnique({ where: { id: params.userId } });\n * }),\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Sync loaders are also supported\n * export const Route = createFileRoute('/static')({\n * loader: traceLoader(({ context }) => ({\n * message: `Welcome, ${context.userId}!`,\n * })),\n * });\n * ```\n */\nexport function traceLoader<TLoaderFn extends (ctx: any) => any>(\n loaderFn: TLoaderFn,\n config: TraceLoaderConfig = {},\n): TLoaderFn {\n const captureParams = config.captureParams ?? true;\n const captureResult = config.captureResult ?? false;\n\n const wrapped = (context: TanStackContextInternal) => {\n // If we're in the browser, just call the loader without tracing\n // This prevents autotel (which uses Node.js APIs) from being executed in the browser\n if (!isServerSide()) {\n return loaderFn(context);\n }\n\n const routeId = context?.route?.id || 'unknown';\n const spanName = config.name || `tanstack.loader.${routeId}`;\n\n // Handle both sync and async loaders\n const result = loaderFn(context);\n const isPromise = result instanceof Promise;\n\n if (!isPromise) {\n // Sync loader - wrap in trace synchronously\n return trace(spanName, (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'loader',\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID]: routeId,\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_TYPE]: 'loader',\n });\n\n if (captureParams && context?.params) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n JSON.stringify(context.params),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n '[non-serializable]',\n );\n }\n }\n\n if (captureResult && result !== undefined) {\n try {\n ctx.setAttribute('tanstack.loader.result', JSON.stringify(result));\n } catch {\n ctx.setAttribute('tanstack.loader.result', '[non-serializable]');\n }\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return result;\n });\n }\n\n // Async loader\n return trace(spanName, async (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'loader',\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID]: routeId,\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_TYPE]: 'loader',\n });\n\n if (captureParams && context?.params) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n JSON.stringify(context.params),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n '[non-serializable]',\n );\n }\n }\n\n try {\n const asyncResult = await result;\n\n if (captureResult && asyncResult !== undefined) {\n try {\n ctx.setAttribute(\n 'tanstack.loader.result',\n JSON.stringify(asyncResult),\n );\n } catch {\n ctx.setAttribute('tanstack.loader.result', '[non-serializable]');\n }\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return asyncResult;\n } catch (error) {\n ctx.recordException(error as Error);\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: (error as Error).message,\n });\n throw error;\n }\n });\n };\n\n return wrapped as TLoaderFn;\n}\n\n/**\n * Wrap a TanStack route beforeLoad function with OpenTelemetry tracing\n *\n * This function wraps a beforeLoad function to automatically create spans.\n * beforeLoad runs before the route component renders and is typically\n * used for auth checks, redirects, or data prefetching.\n *\n * The generic type TBeforeLoadFn preserves the full TanStack Router type inference,\n * including typed params, context, search, and return types.\n *\n * @param beforeLoadFn - The beforeLoad function to wrap\n * @param config - Configuration options\n * @returns Wrapped beforeLoad function with tracing (preserves original types)\n *\n * @example\n * ```typescript\n * import { createFileRoute, redirect } from '@tanstack/react-router';\n * import { traceBeforeLoad } from 'autotel-tanstack/loaders';\n *\n * export const Route = createFileRoute('/dashboard')({\n * // Types are fully preserved - context, params, search are all typed\n * beforeLoad: traceBeforeLoad(async ({ context, params }) => {\n * if (!context.auth.isAuthenticated) {\n * throw redirect({ to: '/login' });\n * }\n * return { userId: params.userId }; // Return type flows to loader context\n * }),\n * loader: ({ context }) => {\n * // context.userId is typed from beforeLoad return\n * return { user: context.userId };\n * },\n * });\n * ```\n */\nexport function traceBeforeLoad<TBeforeLoadFn extends (opts: any) => any>(\n beforeLoadFn: TBeforeLoadFn,\n config: TraceLoaderConfig = {},\n): TBeforeLoadFn {\n const captureParams = config.captureParams ?? true;\n\n const wrapped = (input: TanStackContextInternal) => {\n // Skip tracing in browser\n if (!isServerSide()) {\n return beforeLoadFn(input);\n }\n\n const routeId = input?.route?.id || 'unknown';\n const spanName = config.name || `tanstack.beforeLoad.${routeId}`;\n\n // Handle both sync and async beforeLoad\n const result = beforeLoadFn(input);\n const isPromise = result instanceof Promise;\n\n if (!isPromise) {\n // Sync beforeLoad\n return trace(spanName, (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'beforeLoad',\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID]: routeId,\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_TYPE]: 'beforeLoad',\n });\n\n if (captureParams && input?.params) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n JSON.stringify(input.params),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n '[non-serializable]',\n );\n }\n }\n\n ctx.setStatus({ code: SpanStatusCode.OK });\n return result;\n });\n }\n\n // Async beforeLoad\n return trace(spanName, async (ctx: TraceContext) => {\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'beforeLoad',\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID]: routeId,\n [SPAN_ATTRIBUTES.TANSTACK_LOADER_TYPE]: 'beforeLoad',\n });\n\n if (captureParams && input?.params) {\n try {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n JSON.stringify(input.params),\n );\n } catch {\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_LOADER_PARAMS,\n '[non-serializable]',\n );\n }\n }\n\n try {\n const asyncResult = await result;\n ctx.setStatus({ code: SpanStatusCode.OK });\n return asyncResult;\n } catch (error) {\n // Check if this is a redirect or notFound (expected control flow)\n const errorName = (error as Error).name;\n if (errorName === 'RedirectError' || errorName === 'NotFoundError') {\n // Mark as OK since these are expected control flow\n ctx.setAttribute('tanstack.beforeLoad.redirect', true);\n ctx.setStatus({ code: SpanStatusCode.OK });\n } else {\n ctx.recordException(error as Error);\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: (error as Error).message,\n });\n }\n throw error;\n }\n });\n };\n\n return wrapped as TBeforeLoadFn;\n}\n\n/**\n * Create a traced route configuration helper\n *\n * This higher-order function helps create route configurations\n * with automatic tracing for both loader and beforeLoad.\n *\n * @param routeId - The route identifier\n * @param config - Tracing configuration\n * @returns Object with traced loader and beforeLoad wrappers\n *\n * @example\n * ```typescript\n * import { createFileRoute } from '@tanstack/react-router';\n * import { createTracedRoute } from 'autotel-tanstack/loaders';\n *\n * const traced = createTracedRoute('/users/$userId');\n *\n * export const Route = createFileRoute('/users/$userId')({\n * beforeLoad: traced.beforeLoad(async ({ context }) => {\n * // Auth check\n * }),\n * loader: traced.loader(async ({ params }) => {\n * return await getUser(params.userId);\n * }),\n * });\n * ```\n */\nexport function createTracedRoute(\n routeId: string,\n config: Omit<TraceLoaderConfig, 'name'> = {},\n) {\n return {\n /**\n * Wrap a loader function with tracing\n */\n loader<TLoaderFn extends (ctx: any) => any>(\n loaderFn: TLoaderFn,\n ): TLoaderFn {\n return traceLoader(loaderFn, {\n ...config,\n name: `tanstack.loader.${routeId}`,\n });\n },\n\n /**\n * Wrap a beforeLoad function with tracing\n */\n beforeLoad<TBeforeLoadFn extends (opts: any) => any>(\n beforeLoadFn: TBeforeLoadFn,\n ): TBeforeLoadFn {\n return traceBeforeLoad(beforeLoadFn, {\n ...config,\n name: `tanstack.beforeLoad.${routeId}`,\n });\n },\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/middleware.ts"],"names":["tracingMiddleware"],"mappings":"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","file":"chunk-NP4OJO7T.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: any) => any {\n const handler = createTracingMiddleware<TContext>(config);\n\n // Adapt TanStack's signature to our handler\n return async (opts: any) => {\n return handler(opts);\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/handlers.ts"],"names":[],"mappings":"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","file":"chunk-Z5D2V4DU.js","sourcesContent":["import { context, SpanStatusCode } from '@opentelemetry/api';\nimport { trace, init, type TraceContext } from 'autotel';\nimport { extractContextFromRequest } from './context';\nimport {\n type WrapStartHandlerConfig,\n DEFAULT_CONFIG,\n SPAN_ATTRIBUTES,\n} from './types';\n\n/**\n * Request handler type (compatible with TanStack Start handlers)\n */\ntype RequestHandler = (\n request: Request,\n opts?: { context?: Record<string, unknown> },\n) => Promise<Response> | Response;\n\n/**\n * Wrap a TanStack Start handler with OpenTelemetry tracing\n *\n * This function wraps the entire request handler to automatically create\n * spans for all incoming requests. It initializes OpenTelemetry and\n * provides comprehensive request tracing.\n *\n * @param config - Configuration options including OTLP endpoint and headers\n * @returns Function that wraps a request handler\n *\n * @example\n * ```typescript\n * // server.ts\n * import { createStartHandler, defaultStreamHandler } from '@tanstack/react-start/server';\n * import { wrapStartHandler } from 'autotel-tanstack/handlers';\n *\n * export default wrapStartHandler({\n * service: 'my-app',\n * endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,\n * headers: { 'x-honeycomb-team': process.env.HONEYCOMB_API_KEY },\n * })(createStartHandler(defaultStreamHandler));\n * ```\n *\n * @example\n * ```typescript\n * // With env var configuration (recommended for production)\n * // Set OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS\n * export default wrapStartHandler()(createStartHandler(defaultStreamHandler));\n * ```\n */\nexport function wrapStartHandler(\n config: WrapStartHandlerConfig = {},\n): (handler: RequestHandler) => RequestHandler {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // Initialize autotel with provided configuration\n const service =\n config.service || process.env.OTEL_SERVICE_NAME || 'tanstack-start';\n const endpoint = config.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n\n // Parse headers from env if not provided\n let headers = config.headers;\n if (!headers && process.env.OTEL_EXPORTER_OTLP_HEADERS) {\n headers = {};\n const pairs = process.env.OTEL_EXPORTER_OTLP_HEADERS.split(',');\n for (const pair of pairs) {\n const [key, value] = pair.split('=');\n if (key && value) {\n headers[key.trim()] = value.trim();\n }\n }\n }\n\n // Initialize OpenTelemetry\n init({\n service,\n endpoint,\n headers,\n });\n\n return function wrapHandler(handler: RequestHandler): RequestHandler {\n return async function tracedHandler(\n request: Request,\n opts?: { context?: Record<string, unknown> },\n ): Promise<Response> {\n const url = new URL(request.url);\n\n // Check if path should be excluded\n const shouldExclude = mergedConfig.excludePaths.some((pattern) => {\n if (typeof pattern === 'string') {\n if (pattern.includes('*')) {\n const regex = new RegExp(\n '^' + pattern.replaceAll('*', '.*').replaceAll('?', '.') + '$',\n );\n return regex.test(url.pathname);\n }\n return url.pathname === pattern || url.pathname.startsWith(pattern);\n }\n return pattern.test(url.pathname);\n });\n\n if (shouldExclude) {\n return handler(request, opts);\n }\n\n // Extract parent context from request headers\n const parentContext = extractContextFromRequest(request);\n\n // Run within parent context\n return context.with(parentContext, async () => {\n const spanName = `${request.method} ${url.pathname}`;\n\n return trace(spanName, async (ctx: TraceContext) => {\n // Set HTTP semantic attributes\n ctx.setAttributes({\n [SPAN_ATTRIBUTES.HTTP_REQUEST_METHOD]: request.method,\n [SPAN_ATTRIBUTES.URL_PATH]: url.pathname,\n [SPAN_ATTRIBUTES.URL_FULL]: request.url,\n [SPAN_ATTRIBUTES.TANSTACK_TYPE]: 'request',\n });\n\n if (url.search) {\n ctx.setAttribute(SPAN_ATTRIBUTES.URL_QUERY, url.search);\n }\n\n // Capture configured headers\n if (mergedConfig.captureHeaders) {\n for (const header of mergedConfig.captureHeaders) {\n const value = request.headers.get(header);\n if (value) {\n ctx.setAttribute(\n `http.request.header.${header.toLowerCase()}`,\n value,\n );\n }\n }\n }\n\n // Add custom attributes\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 response = await handler(request, opts);\n const duration = Date.now() - startTime;\n\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS,\n duration,\n );\n ctx.setAttribute(\n SPAN_ATTRIBUTES.HTTP_RESPONSE_STATUS_CODE,\n response.status,\n );\n\n // Set status based on HTTP status code\n if (response.status >= 400) {\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: `HTTP ${response.status}`,\n });\n } else {\n ctx.setStatus({ code: SpanStatusCode.OK });\n }\n\n return response;\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\n throw error;\n }\n });\n });\n };\n };\n}\n\n/**\n * Create a traced handler without auto-initialization\n *\n * Use this when you want to initialize autotel separately\n * (e.g., with more advanced configuration).\n *\n * @param config - Configuration options (excluding endpoint/headers)\n * @returns Function that wraps a request handler\n *\n * @example\n * ```typescript\n * import { init } from 'autotel';\n * import { createTracedHandler } from 'autotel-tanstack/handlers';\n *\n * // Initialize autotel with custom configuration\n * init({\n * service: 'my-app',\n * endpoint: 'https://api.honeycomb.io',\n * instrumentations: [/* custom instrumentations *\\/],\n * });\n *\n * // Wrap handler without re-initializing\n * export default createTracedHandler({\n * captureHeaders: ['x-request-id'],\n * })(createStartHandler(defaultStreamHandler));\n * ```\n */\nexport function createTracedHandler(\n config: Omit<WrapStartHandlerConfig, 'endpoint' | 'headers' | 'service'> = {},\n): (handler: RequestHandler) => RequestHandler {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n return function wrapHandler(handler: RequestHandler): RequestHandler {\n return async function tracedHandler(\n request: Request,\n opts?: { context?: Record<string, unknown> },\n ): Promise<Response> {\n const url = new URL(request.url);\n\n // Check if path should be excluded\n const shouldExclude = mergedConfig.excludePaths.some((pattern) => {\n if (typeof pattern === 'string') {\n if (pattern.includes('*')) {\n const regex = new RegExp(\n '^' + pattern.replaceAll('*', '.*').replaceAll('?', '.') + '$',\n );\n return regex.test(url.pathname);\n }\n return url.pathname === pattern || url.pathname.startsWith(pattern);\n }\n return pattern.test(url.pathname);\n });\n\n if (shouldExclude) {\n return handler(request, opts);\n }\n\n const parentContext = extractContextFromRequest(request);\n\n return context.with(parentContext, async () => {\n const spanName = `${request.method} ${url.pathname}`;\n\n return trace(spanName, async (ctx: TraceContext) => {\n ctx.setAttributes({\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 ctx.setAttribute(SPAN_ATTRIBUTES.URL_QUERY, url.search);\n }\n\n if (mergedConfig.captureHeaders) {\n for (const header of mergedConfig.captureHeaders) {\n const value = request.headers.get(header);\n if (value) {\n ctx.setAttribute(\n `http.request.header.${header.toLowerCase()}`,\n value,\n );\n }\n }\n }\n\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 response = await handler(request, opts);\n const duration = Date.now() - startTime;\n\n ctx.setAttribute(\n SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS,\n duration,\n );\n ctx.setAttribute(\n SPAN_ATTRIBUTES.HTTP_RESPONSE_STATUS_CODE,\n response.status,\n );\n\n if (response.status >= 400) {\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: `HTTP ${response.status}`,\n });\n } else {\n ctx.setStatus({ code: SpanStatusCode.OK });\n }\n\n return response;\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\n throw error;\n }\n });\n });\n };\n };\n}\n"]}