autotel-tanstack 1.13.30 → 1.13.32
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.d.ts +8 -35
- package/dist/auto.d.ts.map +1 -0
- package/dist/auto.js +41 -22
- package/dist/auto.js.map +1 -1
- package/dist/browser/context.d.ts +50 -0
- package/dist/browser/context.d.ts.map +1 -0
- package/dist/browser/context.js +54 -2
- package/dist/browser/context.js.map +1 -1
- package/dist/browser/debug-headers.d.ts +10 -0
- package/dist/browser/debug-headers.d.ts.map +1 -0
- package/dist/browser/debug-headers.js +12 -2
- package/dist/browser/debug-headers.js.map +1 -1
- package/dist/browser/error-reporting.d.ts +39 -0
- package/dist/browser/error-reporting.d.ts.map +1 -0
- package/dist/browser/error-reporting.js +35 -2
- package/dist/browser/error-reporting.js.map +1 -1
- package/dist/browser/handlers.d.ts +14 -0
- package/dist/browser/handlers.d.ts.map +1 -0
- package/dist/browser/handlers.js +10 -2
- package/dist/browser/handlers.js.map +1 -1
- package/dist/browser/index.d.ts +11 -0
- package/dist/browser/index.js +12 -12
- package/dist/browser/loaders.d.ts +31 -0
- package/dist/browser/loaders.d.ts.map +1 -0
- package/dist/browser/loaders.js +29 -2
- package/dist/browser/loaders.js.map +1 -1
- package/dist/browser/metrics.d.ts +56 -0
- package/dist/browser/metrics.d.ts.map +1 -0
- package/dist/browser/metrics.js +48 -2
- package/dist/browser/metrics.js.map +1 -1
- package/dist/browser/middleware.d.ts +42 -0
- package/dist/browser/middleware.d.ts.map +1 -0
- package/dist/browser/middleware.js +36 -2
- package/dist/browser/middleware.js.map +1 -1
- package/dist/browser/server-functions.d.ts +14 -0
- package/dist/browser/server-functions.d.ts.map +1 -0
- package/dist/browser/server-functions.js +16 -2
- package/dist/browser/server-functions.js.map +1 -1
- package/dist/browser/testing.d.ts +85 -0
- package/dist/browser/testing.d.ts.map +1 -0
- package/dist/browser/testing.js +43 -2
- package/dist/browser/testing.js.map +1 -1
- package/dist/browser/types.d.ts +2 -0
- package/dist/browser/types.js +37 -2
- package/dist/browser/types.js.map +1 -1
- package/dist/context.d.ts +5 -3
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +112 -3
- package/dist/context.js.map +1 -1
- package/dist/debug-headers.d.ts +14 -14
- package/dist/debug-headers.d.ts.map +1 -0
- package/dist/debug-headers.js +62 -4
- package/dist/debug-headers.js.map +1 -1
- package/dist/env-BpFWNnpL.js +30 -0
- package/dist/env-BpFWNnpL.js.map +1 -0
- package/dist/error-reporting.d.ts +37 -37
- package/dist/error-reporting.d.ts.map +1 -0
- package/dist/error-reporting.js +154 -3
- package/dist/error-reporting.js.map +1 -1
- package/dist/handlers.d.ts +5 -4
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +192 -6
- package/dist/handlers.js.map +1 -1
- package/dist/index.d.ts +15 -16
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -16
- package/dist/instrument-DS7YCE1R.d.ts +10 -0
- package/dist/instrument-DS7YCE1R.d.ts.map +1 -0
- package/dist/instrument-DdLlMfRi.js +80 -0
- package/dist/instrument-DdLlMfRi.js.map +1 -0
- package/dist/loaders-DrVVY25K.d.ts +2402 -0
- package/dist/loaders-DrVVY25K.d.ts.map +1 -0
- package/dist/loaders.d.ts +2 -116
- package/dist/loaders.js +234 -5
- package/dist/loaders.js.map +1 -1
- package/dist/metrics.d.ts +39 -39
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +144 -3
- package/dist/metrics.js.map +1 -1
- package/dist/middleware.d.ts +16 -15
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +290 -7
- package/dist/middleware.js.map +1 -1
- package/dist/route-filter-dLg-j3jR.js +33 -0
- package/dist/route-filter-dLg-j3jR.js.map +1 -0
- package/dist/server-functions.d.ts +4 -3
- package/dist/server-functions.d.ts.map +1 -0
- package/dist/server-functions.js +133 -5
- package/dist/server-functions.js.map +1 -1
- package/dist/testing.d.ts +164 -65
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +212 -147
- package/dist/testing.js.map +1 -1
- package/dist/types-BJ7FyVoX.d.ts +87 -0
- package/dist/types-BJ7FyVoX.d.ts.map +1 -0
- package/dist/types-BrccP0yX.js +38 -0
- package/dist/types-BrccP0yX.js.map +1 -0
- package/dist/types-pQgmQa4j.d.ts +154 -0
- package/dist/types-pQgmQa4j.d.ts.map +1 -0
- package/package.json +7 -7
- package/dist/browser/index.js.map +0 -1
- package/dist/chunk-7OXOAS64.js +0 -41
- package/dist/chunk-7OXOAS64.js.map +0 -1
- package/dist/chunk-A7WMQ2BC.js +0 -25
- package/dist/chunk-A7WMQ2BC.js.map +0 -1
- package/dist/chunk-CCME55EK.js +0 -28
- package/dist/chunk-CCME55EK.js.map +0 -1
- package/dist/chunk-CSFIPJC2.js +0 -11
- package/dist/chunk-CSFIPJC2.js.map +0 -1
- package/dist/chunk-DTZCOB4W.js +0 -32
- package/dist/chunk-DTZCOB4W.js.map +0 -1
- package/dist/chunk-EFSKEYDJ.js +0 -20
- package/dist/chunk-EFSKEYDJ.js.map +0 -1
- package/dist/chunk-EGRHWZRV.js +0 -3
- package/dist/chunk-EGRHWZRV.js.map +0 -1
- package/dist/chunk-ESU66L3L.js +0 -92
- package/dist/chunk-ESU66L3L.js.map +0 -1
- package/dist/chunk-EUYFVNYE.js +0 -16
- package/dist/chunk-EUYFVNYE.js.map +0 -1
- package/dist/chunk-FFQ4FJKE.js +0 -185
- package/dist/chunk-FFQ4FJKE.js.map +0 -1
- package/dist/chunk-G526TOMY.js +0 -96
- package/dist/chunk-G526TOMY.js.map +0 -1
- package/dist/chunk-I4LX3LOG.js +0 -35
- package/dist/chunk-I4LX3LOG.js.map +0 -1
- package/dist/chunk-JXO7H6KO.js +0 -10
- package/dist/chunk-JXO7H6KO.js.map +0 -1
- package/dist/chunk-KPXGFKPU.js +0 -193
- package/dist/chunk-KPXGFKPU.js.map +0 -1
- package/dist/chunk-LRA2UVVS.js +0 -210
- package/dist/chunk-LRA2UVVS.js.map +0 -1
- package/dist/chunk-MFYOV2SF.js +0 -32
- package/dist/chunk-MFYOV2SF.js.map +0 -1
- package/dist/chunk-MNP65ZX7.js +0 -21
- package/dist/chunk-MNP65ZX7.js.map +0 -1
- package/dist/chunk-NTY64BKS.js +0 -38
- package/dist/chunk-NTY64BKS.js.map +0 -1
- package/dist/chunk-UMEJU65Q.js +0 -34
- package/dist/chunk-UMEJU65Q.js.map +0 -1
- package/dist/chunk-UTPW3QRT.js +0 -52
- package/dist/chunk-UTPW3QRT.js.map +0 -1
- package/dist/chunk-V3RO5N2M.js +0 -8
- package/dist/chunk-V3RO5N2M.js.map +0 -1
- package/dist/chunk-XXBHZR3M.js +0 -99
- package/dist/chunk-XXBHZR3M.js.map +0 -1
- package/dist/chunk-YQYYPJCK.js +0 -37
- package/dist/chunk-YQYYPJCK.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/instrument-DRR7VL63.d.ts +0 -46
- package/dist/types-m5OjZJ-4.d.ts +0 -152
package/dist/loaders.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"loaders.js"}
|
|
1
|
+
{"version":3,"file":"loaders.js","names":[],"sources":["../src/loaders.ts"],"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"],"mappings":"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"}
|
package/dist/metrics.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/metrics.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Performance metrics collection for TanStack Start
|
|
5
4
|
*
|
|
@@ -10,12 +9,12 @@ import * as _tanstack_react_start from '@tanstack/react-start';
|
|
|
10
9
|
* Performance timing data
|
|
11
10
|
*/
|
|
12
11
|
interface TimingStats {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
count: number;
|
|
13
|
+
avg: number;
|
|
14
|
+
p50: number;
|
|
15
|
+
p95: number;
|
|
16
|
+
min: number;
|
|
17
|
+
max: number;
|
|
19
18
|
}
|
|
20
19
|
/**
|
|
21
20
|
* Metrics collector for performance data
|
|
@@ -36,28 +35,28 @@ interface TimingStats {
|
|
|
36
35
|
* ```
|
|
37
36
|
*/
|
|
38
37
|
declare class MetricsCollector {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
38
|
+
private metrics;
|
|
39
|
+
private readonly maxSamples;
|
|
40
|
+
/**
|
|
41
|
+
* Record a timing measurement
|
|
42
|
+
*/
|
|
43
|
+
recordTiming(name: string, duration: number): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get statistics for a metric
|
|
46
|
+
*/
|
|
47
|
+
getStats(name: string): TimingStats | null;
|
|
48
|
+
/**
|
|
49
|
+
* Get all collected metrics
|
|
50
|
+
*/
|
|
51
|
+
getAllStats(): Record<string, TimingStats>;
|
|
52
|
+
/**
|
|
53
|
+
* Reset all metrics
|
|
54
|
+
*/
|
|
55
|
+
reset(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Reset a specific metric
|
|
58
|
+
*/
|
|
59
|
+
resetMetric(name: string): void;
|
|
61
60
|
}
|
|
62
61
|
/**
|
|
63
62
|
* Global metrics collector instance
|
|
@@ -85,13 +84,13 @@ declare const metricsCollector: MetricsCollector;
|
|
|
85
84
|
* });
|
|
86
85
|
* ```
|
|
87
86
|
*/
|
|
88
|
-
declare function createMetricsHandler(): () => Promise<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
declare function createMetricsHandler(): () => Promise<import("@tanstack/react-start").JsonResponse<{
|
|
88
|
+
system: {
|
|
89
|
+
uptime: number;
|
|
90
|
+
memory: NodeJS.MemoryUsage;
|
|
91
|
+
timestamp: string;
|
|
92
|
+
};
|
|
93
|
+
application: Record<string, TimingStats>;
|
|
95
94
|
}>>;
|
|
96
95
|
/**
|
|
97
96
|
* Auto-record timing from a function execution
|
|
@@ -109,5 +108,6 @@ declare function createMetricsHandler(): () => Promise<_tanstack_react_start.Jso
|
|
|
109
108
|
* ```
|
|
110
109
|
*/
|
|
111
110
|
declare function recordTiming<T extends (...args: any[]) => any>(metricName: string, fn: T): T;
|
|
112
|
-
|
|
113
|
-
export {
|
|
111
|
+
//#endregion
|
|
112
|
+
export { TimingStats, createMetricsHandler, metricsCollector, recordTiming };
|
|
113
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","names":[],"sources":["../src/metrics.ts"],"mappings":";;AAUA;;;;;;;;UAAiB,WAAA;EACf,KAAA;EACA,GAAA;EACA,GAAA;EACA,GAAA;EACA,GAAA;EACA,GAAA;AAAA;;;;;;;;;;;;;;;;;;;cAqBI,gBAAA;EAAA,QACI,OAAA;EAAA,iBACS,UAAA;EAyEmC;;;EApEpD,YAAA,CAAa,IAAA,UAAc,QAAA;EA4Fb;;;EA3Ed,QAAA,CAAS,IAAA,WAAe,WAAA;;;;EAsBxB,WAAA,IAAe,MAAA,SAAe,WAAA;EAqDI;;;EAvClC,KAAA;;;;EAOA,WAAA,CAAY,IAAA;AAAA;;;;cAQD,gBAAA,EAAgB,gBAAyB;;AAsDtD;;;;;;;;;;;AAGI;;;;;;;;;;iBAjCY,oBAAA,UAAoB,OAAA,iCAAA,YAAA;;;;;;;;;;;;;;;;;;;;;;;iBA8BpB,YAAA,eAA2B,IAAA,iBACzC,UAAA,UACA,EAAA,EAAI,CAAA,GACH,CAAC"}
|
package/dist/metrics.js
CHANGED
|
@@ -1,4 +1,145 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
//#region src/metrics.ts
|
|
2
|
+
/**
|
|
3
|
+
* Metrics collector for performance data
|
|
4
|
+
*
|
|
5
|
+
* Collects timing metrics and provides statistical analysis.
|
|
6
|
+
* Thread-safe for concurrent access.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { metricsCollector } from 'autotel-tanstack/metrics';
|
|
11
|
+
*
|
|
12
|
+
* // Record a timing
|
|
13
|
+
* metricsCollector.recordTiming('serverFn.getUser', 150);
|
|
14
|
+
*
|
|
15
|
+
* // Get stats
|
|
16
|
+
* const stats = metricsCollector.getStats('serverFn.getUser');
|
|
17
|
+
* console.log(`Average: ${stats.avg}ms, P95: ${stats.p95}ms`);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
var MetricsCollector = class {
|
|
21
|
+
metrics = /* @__PURE__ */ new Map();
|
|
22
|
+
maxSamples = 1e3;
|
|
23
|
+
/**
|
|
24
|
+
* Record a timing measurement
|
|
25
|
+
*/
|
|
26
|
+
recordTiming(name, duration) {
|
|
27
|
+
if (!this.metrics.has(name)) this.metrics.set(name, []);
|
|
28
|
+
const timings = this.metrics.get(name);
|
|
29
|
+
timings.push(duration);
|
|
30
|
+
if (timings.length > this.maxSamples) timings.shift();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get statistics for a metric
|
|
34
|
+
*/
|
|
35
|
+
getStats(name) {
|
|
36
|
+
const timings = this.metrics.get(name);
|
|
37
|
+
if (!timings || timings.length === 0) return null;
|
|
38
|
+
const sorted = [...timings].toSorted((a, b) => a - b);
|
|
39
|
+
const sum = timings.reduce((a, b) => a + b, 0);
|
|
40
|
+
return {
|
|
41
|
+
count: timings.length,
|
|
42
|
+
avg: sum / timings.length,
|
|
43
|
+
p50: sorted.at(Math.floor(sorted.length * .5)) ?? 0,
|
|
44
|
+
p95: sorted.at(Math.floor(sorted.length * .95)) ?? 0,
|
|
45
|
+
min: sorted[0] ?? 0,
|
|
46
|
+
max: sorted.at(-1) ?? 0
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get all collected metrics
|
|
51
|
+
*/
|
|
52
|
+
getAllStats() {
|
|
53
|
+
const stats = {};
|
|
54
|
+
for (const [name] of this.metrics) {
|
|
55
|
+
const stat = this.getStats(name);
|
|
56
|
+
if (stat) stats[name] = stat;
|
|
57
|
+
}
|
|
58
|
+
return stats;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Reset all metrics
|
|
62
|
+
*/
|
|
63
|
+
reset() {
|
|
64
|
+
this.metrics.clear();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Reset a specific metric
|
|
68
|
+
*/
|
|
69
|
+
resetMetric(name) {
|
|
70
|
+
this.metrics.delete(name);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Global metrics collector instance
|
|
75
|
+
*/
|
|
76
|
+
const metricsCollector = new MetricsCollector();
|
|
77
|
+
/**
|
|
78
|
+
* Helper to create a metrics endpoint handler
|
|
79
|
+
*
|
|
80
|
+
* Returns a handler that exposes metrics in JSON format.
|
|
81
|
+
* Use this to create a `/metrics` endpoint.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* // routes/metrics.ts
|
|
86
|
+
* import { createFileRoute } from '@tanstack/react-router';
|
|
87
|
+
* import { json } from '@tanstack/react-start';
|
|
88
|
+
* import { createMetricsHandler } from 'autotel-tanstack/metrics';
|
|
89
|
+
*
|
|
90
|
+
* export const Route = createFileRoute('/metrics')({
|
|
91
|
+
* server: {
|
|
92
|
+
* handlers: {
|
|
93
|
+
* GET: createMetricsHandler(),
|
|
94
|
+
* },
|
|
95
|
+
* },
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
function createMetricsHandler() {
|
|
100
|
+
return async () => {
|
|
101
|
+
const { json } = await import("@tanstack/react-start");
|
|
102
|
+
return json({
|
|
103
|
+
system: {
|
|
104
|
+
uptime: process.uptime(),
|
|
105
|
+
memory: process.memoryUsage(),
|
|
106
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
107
|
+
},
|
|
108
|
+
application: metricsCollector.getAllStats()
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Auto-record timing from a function execution
|
|
114
|
+
*
|
|
115
|
+
* Wraps a function to automatically record its execution time.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* import { recordTiming } from 'autotel-tanstack/metrics';
|
|
120
|
+
*
|
|
121
|
+
* const getUser = createServerFn({ method: 'GET' })
|
|
122
|
+
* .handler(recordTiming('serverFn.getUser', async ({ data: id }) => {
|
|
123
|
+
* return await db.users.findUnique({ where: { id } });
|
|
124
|
+
* }));
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
function recordTiming(metricName, fn) {
|
|
128
|
+
return (async (...args) => {
|
|
129
|
+
const startTime = Date.now();
|
|
130
|
+
try {
|
|
131
|
+
const result = await fn(...args);
|
|
132
|
+
const duration = Date.now() - startTime;
|
|
133
|
+
metricsCollector.recordTiming(metricName, duration);
|
|
134
|
+
return result;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
const duration = Date.now() - startTime;
|
|
137
|
+
metricsCollector.recordTiming(`${metricName}.error`, duration);
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
export { createMetricsHandler, metricsCollector, recordTiming };
|
|
4
145
|
//# sourceMappingURL=metrics.js.map
|
package/dist/metrics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"file":"metrics.js","names":[],"sources":["../src/metrics.ts"],"sourcesContent":["/**\n * Performance metrics collection for TanStack Start\n *\n * Provides utilities to collect and expose performance metrics\n * following the patterns from TanStack Start observability guide.\n */\n\n/**\n * Performance timing data\n */\nexport interface TimingStats {\n count: number;\n avg: number;\n p50: number;\n p95: number;\n min: number;\n max: number;\n}\n\n/**\n * Metrics collector for performance data\n *\n * Collects timing metrics and provides statistical analysis.\n * Thread-safe for concurrent access.\n *\n * @example\n * ```typescript\n * import { metricsCollector } from 'autotel-tanstack/metrics';\n *\n * // Record a timing\n * metricsCollector.recordTiming('serverFn.getUser', 150);\n *\n * // Get stats\n * const stats = metricsCollector.getStats('serverFn.getUser');\n * console.log(`Average: ${stats.avg}ms, P95: ${stats.p95}ms`);\n * ```\n */\nclass MetricsCollector {\n private metrics = new Map<string, number[]>();\n private readonly maxSamples = 1000; // Limit memory usage\n\n /**\n * Record a timing measurement\n */\n recordTiming(name: string, duration: number): void {\n if (!this.metrics.has(name)) {\n this.metrics.set(name, []);\n }\n\n const timings = this.metrics.get(name)!;\n timings.push(duration);\n\n // Limit samples to prevent memory issues\n if (timings.length > this.maxSamples) {\n timings.shift(); // Remove oldest\n }\n }\n\n /**\n * Get statistics for a metric\n */\n getStats(name: string): TimingStats | null {\n const timings = this.metrics.get(name);\n if (!timings || timings.length === 0) {\n return null;\n }\n\n const sorted = [...timings].toSorted((a, b) => a - b);\n const sum = timings.reduce((a, b) => a + b, 0);\n\n return {\n count: timings.length,\n avg: sum / timings.length,\n p50: sorted.at(Math.floor(sorted.length * 0.5)) ?? 0,\n p95: sorted.at(Math.floor(sorted.length * 0.95)) ?? 0,\n min: sorted[0] ?? 0,\n max: sorted.at(-1) ?? 0,\n };\n }\n\n /**\n * Get all collected metrics\n */\n getAllStats(): Record<string, TimingStats> {\n const stats: Record<string, TimingStats> = {};\n for (const [name] of this.metrics) {\n const stat = this.getStats(name);\n if (stat) {\n stats[name] = stat;\n }\n }\n return stats;\n }\n\n /**\n * Reset all metrics\n */\n reset(): void {\n this.metrics.clear();\n }\n\n /**\n * Reset a specific metric\n */\n resetMetric(name: string): void {\n this.metrics.delete(name);\n }\n}\n\n/**\n * Global metrics collector instance\n */\nexport const metricsCollector = new MetricsCollector();\n\n/**\n * Helper to create a metrics endpoint handler\n *\n * Returns a handler that exposes metrics in JSON format.\n * Use this to create a `/metrics` endpoint.\n *\n * @example\n * ```typescript\n * // routes/metrics.ts\n * import { createFileRoute } from '@tanstack/react-router';\n * import { json } from '@tanstack/react-start';\n * import { createMetricsHandler } from 'autotel-tanstack/metrics';\n *\n * export const Route = createFileRoute('/metrics')({\n * server: {\n * handlers: {\n * GET: createMetricsHandler(),\n * },\n * },\n * });\n * ```\n */\nexport function createMetricsHandler() {\n return async () => {\n const { json } = await import('@tanstack/react-start');\n\n return json({\n system: {\n uptime: process.uptime(),\n memory: process.memoryUsage(),\n timestamp: new Date().toISOString(),\n },\n application: metricsCollector.getAllStats(),\n });\n };\n}\n\n/**\n * Auto-record timing from a function execution\n *\n * Wraps a function to automatically record its execution time.\n *\n * @example\n * ```typescript\n * import { recordTiming } from 'autotel-tanstack/metrics';\n *\n * const getUser = createServerFn({ method: 'GET' })\n * .handler(recordTiming('serverFn.getUser', async ({ data: id }) => {\n * return await db.users.findUnique({ where: { id } });\n * }));\n * ```\n */\nexport function recordTiming<T extends (...args: any[]) => any>(\n metricName: string,\n fn: T,\n): T {\n return (async (...args: Parameters<T>) => {\n const startTime = Date.now();\n try {\n const result = await fn(...args);\n const duration = Date.now() - startTime;\n metricsCollector.recordTiming(metricName, duration);\n return result as ReturnType<T>;\n } catch (error) {\n const duration = Date.now() - startTime;\n metricsCollector.recordTiming(`${metricName}.error`, duration);\n throw error;\n }\n }) as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,IAAM,mBAAN,MAAuB;CACrB,AAAQ,0BAAU,IAAI,IAAsB;CAC5C,AAAiB,aAAa;;;;CAK9B,aAAa,MAAc,UAAwB;EACjD,IAAI,CAAC,KAAK,QAAQ,IAAI,IAAI,GACxB,KAAK,QAAQ,IAAI,MAAM,CAAC,CAAC;EAG3B,MAAM,UAAU,KAAK,QAAQ,IAAI,IAAI;EACrC,QAAQ,KAAK,QAAQ;EAGrB,IAAI,QAAQ,SAAS,KAAK,YACxB,QAAQ,MAAM;CAElB;;;;CAKA,SAAS,MAAkC;EACzC,MAAM,UAAU,KAAK,QAAQ,IAAI,IAAI;EACrC,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO;EAGT,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC;EACpD,MAAM,MAAM,QAAQ,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC;EAE7C,OAAO;GACL,OAAO,QAAQ;GACf,KAAK,MAAM,QAAQ;GACnB,KAAK,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,EAAG,CAAC,KAAK;GACnD,KAAK,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,GAAI,CAAC,KAAK;GACpD,KAAK,OAAO,MAAM;GAClB,KAAK,OAAO,GAAG,EAAE,KAAK;EACxB;CACF;;;;CAKA,cAA2C;EACzC,MAAM,QAAqC,CAAC;EAC5C,KAAK,MAAM,CAAC,SAAS,KAAK,SAAS;GACjC,MAAM,OAAO,KAAK,SAAS,IAAI;GAC/B,IAAI,MACF,MAAM,QAAQ;EAElB;EACA,OAAO;CACT;;;;CAKA,QAAc;EACZ,KAAK,QAAQ,MAAM;CACrB;;;;CAKA,YAAY,MAAoB;EAC9B,KAAK,QAAQ,OAAO,IAAI;CAC1B;AACF;;;;AAKA,MAAa,mBAAmB,IAAI,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;AAwBrD,SAAgB,uBAAuB;CACrC,OAAO,YAAY;EACjB,MAAM,EAAE,SAAS,MAAM,OAAO;EAE9B,OAAO,KAAK;GACV,QAAQ;IACN,QAAQ,QAAQ,OAAO;IACvB,QAAQ,QAAQ,YAAY;IAC5B,4BAAW,IAAI,KAAK,EAAC,CAAC,YAAY;GACpC;GACA,aAAa,iBAAiB,YAAY;EAC5C,CAAC;CACH;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,aACd,YACA,IACG;CACH,QAAQ,OAAO,GAAG,SAAwB;EACxC,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI;GACF,MAAM,SAAS,MAAM,GAAG,GAAG,IAAI;GAC/B,MAAM,WAAW,KAAK,IAAI,IAAI;GAC9B,iBAAiB,aAAa,YAAY,QAAQ;GAClD,OAAO;EACT,SAAS,OAAO;GACd,MAAM,WAAW,KAAK,IAAI,IAAI;GAC9B,iBAAiB,aAAa,GAAG,WAAW,SAAS,QAAQ;GAC7D,MAAM;EACR;CACF;AACF"}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import '@opentelemetry/api';
|
|
1
|
+
import { o as TracingMiddlewareConfig } from "./types-pQgmQa4j.js";
|
|
3
2
|
|
|
3
|
+
//#region src/middleware.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Generic middleware handler type (compatible with TanStack's middleware pattern)
|
|
6
6
|
*
|
|
@@ -8,17 +8,17 @@ import '@opentelemetry/api';
|
|
|
8
8
|
* We use a generic type to avoid direct dependency on TanStack packages.
|
|
9
9
|
*/
|
|
10
10
|
interface MiddlewareHandler<TContext = unknown> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
(opts: {
|
|
12
|
+
next: (ctx?: Partial<TContext>) => Promise<TContext>;
|
|
13
|
+
context: TContext;
|
|
14
|
+
request?: Request;
|
|
15
|
+
pathname?: string;
|
|
16
|
+
data?: unknown;
|
|
17
|
+
method?: string;
|
|
18
|
+
filename?: string;
|
|
19
|
+
functionId?: string;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
}): Promise<TContext>;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Create a TanStack-compatible tracing middleware
|
|
@@ -143,5 +143,6 @@ declare function functionTracingMiddleware<TContext = unknown>(config?: Omit<Tra
|
|
|
143
143
|
* ```
|
|
144
144
|
*/
|
|
145
145
|
declare function createTracingServerHandler<TContext = unknown>(config?: TracingMiddlewareConfig): (opts: any) => any;
|
|
146
|
-
|
|
147
|
-
export {
|
|
146
|
+
//#endregion
|
|
147
|
+
export { MiddlewareHandler, createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware };
|
|
148
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","names":[],"sources":["../src/middleware.ts"],"mappings":";;;;;AAgFA;;;;UAAiB,iBAAA;EAAA,CACd,IAAA;IACC,IAAA,GAAO,GAAA,GAAM,OAAA,CAAQ,QAAA,MAAc,OAAA,CAAQ,QAAA;IAC3C,OAAA,EAAS,QAAA;IACT,OAAA,GAAU,OAAA;IACV,QAAA;IACA,IAAA;IACA,MAAA;IACA,QAAA;IACA,UAAA;IACA,MAAA,GAAS,WAAA;EAAA,IACP,OAAA,CAAQ,QAAA;AAAA;;;;;;;;;;;;;;;;;;;;AAAQ;AAyCtB;;;;;;;;;;;;;;AAE6B;AAiN7B;;;iBAnNgB,uBAAA,qBACd,MAAA,GAAS,uBAAA,GACR,iBAAA,CAAkB,QAAA;;;;;;;;;;;AAmNQ;AA6B7B;;;;;;;;iBA/BgB,iBAAA,qBACd,MAAA,GAAS,uBAAA,GACR,iBAAA,CAAkB,QAAA;;;;;;;;AA+BQ;AAiD7B;;;;;;;;;AAEa;;;iBArDG,yBAAA,qBACd,MAAA,GAAS,IAAA,CAAK,uBAAA,YACb,iBAAA,CAAkB,QAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiDL,0BAAA,qBACd,MAAA,GAAS,uBAAuB,IAC9B,IAAA"}
|
package/dist/middleware.js
CHANGED
|
@@ -1,8 +1,291 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
1
|
+
import { n as SPAN_ATTRIBUTES, t as DEFAULT_CONFIG } from "./types-BrccP0yX.js";
|
|
2
|
+
import { extractContextFromRequest } from "./context.js";
|
|
3
|
+
import { r as isServerSide } from "./env-BpFWNnpL.js";
|
|
4
|
+
import { t as isExcludedPath } from "./route-filter-dLg-j3jR.js";
|
|
5
|
+
import { SpanStatusCode, context } from "@opentelemetry/api";
|
|
6
|
+
import { trace } from "autotel";
|
|
7
|
+
|
|
8
|
+
//#region src/middleware.ts
|
|
9
|
+
/**
|
|
10
|
+
* Build span attributes for HTTP requests
|
|
11
|
+
*/
|
|
12
|
+
function buildRequestAttributes(request, config) {
|
|
13
|
+
const url = new URL(request.url);
|
|
14
|
+
const attrs = {
|
|
15
|
+
[SPAN_ATTRIBUTES.HTTP_REQUEST_METHOD]: request.method,
|
|
16
|
+
[SPAN_ATTRIBUTES.URL_PATH]: url.pathname,
|
|
17
|
+
[SPAN_ATTRIBUTES.TANSTACK_TYPE]: "request"
|
|
18
|
+
};
|
|
19
|
+
if (url.search) attrs[SPAN_ATTRIBUTES.URL_QUERY] = url.search;
|
|
20
|
+
if (config.captureHeaders) for (const header of config.captureHeaders) {
|
|
21
|
+
const value = request.headers.get(header);
|
|
22
|
+
if (value) attrs[`http.request.header.${header.toLowerCase()}`] = value;
|
|
23
|
+
}
|
|
24
|
+
return attrs;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build span attributes for server functions
|
|
28
|
+
*/
|
|
29
|
+
function buildServerFnAttributes(functionName, method, args, config) {
|
|
30
|
+
const attrs = {
|
|
31
|
+
[SPAN_ATTRIBUTES.RPC_SYSTEM]: "tanstack-start",
|
|
32
|
+
[SPAN_ATTRIBUTES.RPC_METHOD]: functionName,
|
|
33
|
+
[SPAN_ATTRIBUTES.TANSTACK_TYPE]: "serverFn",
|
|
34
|
+
[SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_NAME]: functionName,
|
|
35
|
+
[SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_METHOD]: method
|
|
36
|
+
};
|
|
37
|
+
if (config.captureArgs && args !== void 0) try {
|
|
38
|
+
attrs[SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS] = JSON.stringify(args);
|
|
39
|
+
} catch {
|
|
40
|
+
attrs[SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_ARGS] = "[non-serializable]";
|
|
41
|
+
}
|
|
42
|
+
return attrs;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a TanStack-compatible tracing middleware
|
|
46
|
+
*
|
|
47
|
+
* This creates middleware that automatically traces all requests/server functions
|
|
48
|
+
* with OpenTelemetry spans. Use with TanStack Start's middleware system.
|
|
49
|
+
*
|
|
50
|
+
* @param config - Configuration options
|
|
51
|
+
* @returns Middleware handler compatible with TanStack Start
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* // Global request middleware in app/start.ts
|
|
56
|
+
* import { createStart } from '@tanstack/react-start';
|
|
57
|
+
* import { createTracingMiddleware } from 'autotel-tanstack/middleware';
|
|
58
|
+
*
|
|
59
|
+
* export const startInstance = createStart(() => ({
|
|
60
|
+
* requestMiddleware: [
|
|
61
|
+
* createTracingMiddleware({
|
|
62
|
+
* captureHeaders: ['x-request-id', 'user-agent'],
|
|
63
|
+
* excludePaths: ['/health', '/metrics'],
|
|
64
|
+
* }),
|
|
65
|
+
* ],
|
|
66
|
+
* }));
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* // Server function middleware
|
|
72
|
+
* import { createServerFn } from '@tanstack/react-start';
|
|
73
|
+
* import { createTracingMiddleware } from 'autotel-tanstack/middleware';
|
|
74
|
+
*
|
|
75
|
+
* export const getUser = createServerFn({ method: 'GET' })
|
|
76
|
+
* .middleware([createTracingMiddleware({ type: 'function' })])
|
|
77
|
+
* .handler(async ({ data: id }) => {
|
|
78
|
+
* return await db.users.findUnique({ where: { id } });
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
function createTracingMiddleware(config) {
|
|
83
|
+
const mergedConfig = {
|
|
84
|
+
...DEFAULT_CONFIG,
|
|
85
|
+
...config,
|
|
86
|
+
type: config?.type ?? "request"
|
|
87
|
+
};
|
|
88
|
+
return async function tracingMiddleware(opts) {
|
|
89
|
+
if (!isServerSide()) return opts.next();
|
|
90
|
+
const { next, request, pathname, data, functionId } = opts;
|
|
91
|
+
if (mergedConfig.type === "function") {
|
|
92
|
+
const fnName = functionId || "unknown";
|
|
93
|
+
const method = opts.method || "POST";
|
|
94
|
+
return trace(`tanstack.serverFn.${fnName}`, async (ctx) => {
|
|
95
|
+
const attrs = buildServerFnAttributes(fnName, method, data, mergedConfig);
|
|
96
|
+
ctx.setAttributes(attrs);
|
|
97
|
+
if (config?.customAttributes) {
|
|
98
|
+
const customAttrs = config.customAttributes({
|
|
99
|
+
type: "serverFn",
|
|
100
|
+
name: fnName,
|
|
101
|
+
args: data
|
|
102
|
+
});
|
|
103
|
+
ctx.setAttributes(customAttrs);
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const result = await next();
|
|
107
|
+
if (mergedConfig.captureResults && result !== void 0) try {
|
|
108
|
+
ctx.setAttribute(SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_RESULT, JSON.stringify(result));
|
|
109
|
+
} catch {
|
|
110
|
+
ctx.setAttribute(SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_RESULT, "[non-serializable]");
|
|
111
|
+
}
|
|
112
|
+
ctx.setStatus({ code: SpanStatusCode.OK });
|
|
113
|
+
return result;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (mergedConfig.captureErrors) {
|
|
116
|
+
if ("recordError" in ctx && typeof ctx.recordError === "function") ctx.recordError(error);
|
|
117
|
+
else if ("recordException" in ctx && typeof ctx.recordException === "function") ctx.recordException(error);
|
|
118
|
+
try {
|
|
119
|
+
const { reportError } = await import("./error-reporting.js");
|
|
120
|
+
reportError(error, {
|
|
121
|
+
type: "serverFn",
|
|
122
|
+
name: fnName,
|
|
123
|
+
method
|
|
124
|
+
});
|
|
125
|
+
} catch {}
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (!request) return next();
|
|
132
|
+
const url = new URL(request.url);
|
|
133
|
+
if (isExcludedPath(url.pathname, mergedConfig.excludePaths)) return next();
|
|
134
|
+
const parentContext = extractContextFromRequest(request);
|
|
135
|
+
return context.with(parentContext, async () => {
|
|
136
|
+
const spanName = `${request.method} ${pathname || url.pathname}`;
|
|
137
|
+
return trace(spanName, async (ctx) => {
|
|
138
|
+
const attrs = buildRequestAttributes(request, mergedConfig);
|
|
139
|
+
ctx.setAttributes(attrs);
|
|
140
|
+
if (config?.customAttributes) {
|
|
141
|
+
const customAttrs = config.customAttributes({
|
|
142
|
+
type: "request",
|
|
143
|
+
name: spanName,
|
|
144
|
+
request
|
|
145
|
+
});
|
|
146
|
+
ctx.setAttributes(customAttrs);
|
|
147
|
+
}
|
|
148
|
+
const startTime = Date.now();
|
|
149
|
+
try {
|
|
150
|
+
const result = await next();
|
|
151
|
+
const duration = Date.now() - startTime;
|
|
152
|
+
ctx.setAttribute(SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS, duration);
|
|
153
|
+
try {
|
|
154
|
+
const { metricsCollector } = await import("./metrics.js");
|
|
155
|
+
metricsCollector.recordTiming(spanName, duration);
|
|
156
|
+
} catch {}
|
|
157
|
+
if (result && typeof result === "object" && "status" in result) ctx.setAttribute(SPAN_ATTRIBUTES.HTTP_RESPONSE_STATUS_CODE, result.status);
|
|
158
|
+
ctx.setStatus({ code: SpanStatusCode.OK });
|
|
159
|
+
return result;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
const duration = Date.now() - startTime;
|
|
162
|
+
ctx.setAttribute(SPAN_ATTRIBUTES.TANSTACK_REQUEST_DURATION_MS, duration);
|
|
163
|
+
if (mergedConfig.captureErrors) {
|
|
164
|
+
if ("recordError" in ctx && typeof ctx.recordError === "function") ctx.recordError(error);
|
|
165
|
+
else if ("recordException" in ctx && typeof ctx.recordException === "function") ctx.recordException(error);
|
|
166
|
+
try {
|
|
167
|
+
const { reportError } = await import("./error-reporting.js");
|
|
168
|
+
reportError(error, {
|
|
169
|
+
type: "request",
|
|
170
|
+
method: request.method,
|
|
171
|
+
pathname: url.pathname
|
|
172
|
+
});
|
|
173
|
+
} catch {}
|
|
174
|
+
}
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Pre-configured tracing middleware with sensible defaults
|
|
183
|
+
*
|
|
184
|
+
* Convenience export for quick setup. Uses adaptive sampling,
|
|
185
|
+
* captures x-request-id header, and excludes common health check paths.
|
|
186
|
+
*
|
|
187
|
+
* @param config - Optional configuration overrides
|
|
188
|
+
* @returns Middleware handler
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* import { createStart } from '@tanstack/react-start';
|
|
193
|
+
* import { tracingMiddleware } from 'autotel-tanstack/middleware';
|
|
194
|
+
*
|
|
195
|
+
* export const startInstance = createStart(() => ({
|
|
196
|
+
* requestMiddleware: [tracingMiddleware()],
|
|
197
|
+
* }));
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
function tracingMiddleware(config) {
|
|
201
|
+
return createTracingMiddleware({
|
|
202
|
+
sampling: "adaptive",
|
|
203
|
+
captureHeaders: ["x-request-id", "user-agent"],
|
|
204
|
+
excludePaths: [
|
|
205
|
+
"/health",
|
|
206
|
+
"/healthz",
|
|
207
|
+
"/ready",
|
|
208
|
+
"/metrics",
|
|
209
|
+
"/_ping"
|
|
210
|
+
],
|
|
211
|
+
...config
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Create function-specific tracing middleware
|
|
216
|
+
*
|
|
217
|
+
* Convenience wrapper for server function middleware.
|
|
218
|
+
*
|
|
219
|
+
* @param config - Optional configuration
|
|
220
|
+
* @returns Middleware handler for server functions
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* import { createServerFn } from '@tanstack/react-start';
|
|
225
|
+
* import { functionTracingMiddleware } from 'autotel-tanstack/middleware';
|
|
226
|
+
*
|
|
227
|
+
* export const getUser = createServerFn({ method: 'GET' })
|
|
228
|
+
* .middleware([functionTracingMiddleware()])
|
|
229
|
+
* .handler(async ({ data: id }) => {
|
|
230
|
+
* return await db.users.findUnique({ where: { id } });
|
|
231
|
+
* });
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
function functionTracingMiddleware(config) {
|
|
235
|
+
return createTracingMiddleware({
|
|
236
|
+
...config,
|
|
237
|
+
type: "function"
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Create a tracing handler for use with TanStack's native createMiddleware()
|
|
242
|
+
*
|
|
243
|
+
* This provides the raw tracing logic that you can pass to createMiddleware().server().
|
|
244
|
+
* Use this when you want full control over the middleware builder pattern.
|
|
245
|
+
*
|
|
246
|
+
* The handler accepts TanStack's middleware signature `{ next, context, request }`
|
|
247
|
+
* and internally adapts it to our more flexible MiddlewareHandler interface.
|
|
248
|
+
*
|
|
249
|
+
* @param config - Configuration options
|
|
250
|
+
* @returns Server handler function compatible with createMiddleware().server()
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* import { createStart, createMiddleware } from '@tanstack/react-start';
|
|
255
|
+
* import { createTracingServerHandler } from 'autotel-tanstack/middleware';
|
|
256
|
+
*
|
|
257
|
+
* // TanStack-native middleware creation
|
|
258
|
+
* const requestTracingMiddleware = createMiddleware().server(
|
|
259
|
+
* createTracingServerHandler({ captureHeaders: ['x-request-id'] })
|
|
260
|
+
* );
|
|
261
|
+
*
|
|
262
|
+
* export const start = createStart(() => ({
|
|
263
|
+
* requestMiddleware: [requestTracingMiddleware],
|
|
264
|
+
* }));
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* // For server functions - use createMiddleware({ type: 'function' })
|
|
270
|
+
* import { createStart, createMiddleware } from '@tanstack/react-start';
|
|
271
|
+
* import { createTracingServerHandler } from 'autotel-tanstack/middleware';
|
|
272
|
+
*
|
|
273
|
+
* const functionTracingMiddleware = createMiddleware({ type: 'function' }).server(
|
|
274
|
+
* createTracingServerHandler({ type: 'function', captureArgs: true })
|
|
275
|
+
* );
|
|
276
|
+
*
|
|
277
|
+
* export const start = createStart(() => ({
|
|
278
|
+
* functionMiddleware: [functionTracingMiddleware],
|
|
279
|
+
* }));
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
function createTracingServerHandler(config) {
|
|
283
|
+
const handler = createTracingMiddleware(config);
|
|
284
|
+
return async (opts) => {
|
|
285
|
+
return handler(opts);
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
//#endregion
|
|
290
|
+
export { createTracingMiddleware, createTracingServerHandler, functionTracingMiddleware, tracingMiddleware };
|
|
8
291
|
//# sourceMappingURL=middleware.js.map
|