autotel-tanstack 1.13.34 → 1.13.36

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.
@@ -1,31 +0,0 @@
1
- /**
2
- * Browser stub for server-functions module
3
- *
4
- * In browser environments, these functions are no-ops that just return
5
- * the original functions without any tracing overhead.
6
- */
7
-
8
- import type { TraceServerFnConfig } from './types';
9
-
10
- /**
11
- * Browser stub: Returns the server function unchanged
12
- */
13
- export function traceServerFn<
14
- T extends (...args: unknown[]) => Promise<unknown>,
15
- >(serverFn: T, config?: TraceServerFnConfig): T {
16
- void config;
17
- return serverFn;
18
- }
19
-
20
- /**
21
- * Browser stub: Returns the createServerFn unchanged
22
- */
23
- export function createTracedServerFnFactory<
24
- TCreateServerFn extends (...args: unknown[]) => unknown,
25
- >(
26
- createServerFnOriginal: TCreateServerFn,
27
- defaultConfig?: Omit<TraceServerFnConfig, 'name'>,
28
- ): TCreateServerFn {
29
- void defaultConfig;
30
- return createServerFnOriginal;
31
- }
@@ -1,130 +0,0 @@
1
- /**
2
- * Browser stub for testing module
3
- *
4
- * Testing utilities are server-side only.
5
- * In browser, these return no-op implementations.
6
- */
7
-
8
- /**
9
- * Test span structure (stub)
10
- */
11
- export interface TestSpan {
12
- name: string;
13
- attributes: Record<string, unknown>;
14
- status: { code: number; message?: string };
15
- events: Array<{ name: string; attributes?: Record<string, unknown> }>;
16
- duration: number;
17
- }
18
-
19
- /**
20
- * Test collector structure (stub)
21
- */
22
- export interface TestCollector {
23
- getSpans(): TestSpan[];
24
- getSpansByName(name: string): TestSpan[];
25
- clear(): void;
26
- waitForSpans(count: number, timeout?: number): Promise<TestSpan[]>;
27
- }
28
-
29
- /**
30
- * Browser stub: Returns empty collector
31
- */
32
- export function createTestCollector(): TestCollector {
33
- return {
34
- getSpans: () => [],
35
- getSpansByName: () => [],
36
- clear: () => {},
37
- waitForSpans: async () => [],
38
- };
39
- }
40
-
41
- /**
42
- * Browser stub: No-op
43
- */
44
- export function assertSpanCreated(
45
- collector: TestCollector,
46
- name: string,
47
- ): void {
48
- void collector;
49
- void name;
50
- // No-op in browser
51
- }
52
-
53
- /**
54
- * Browser stub: No-op
55
- */
56
- export function assertSpanHasAttribute(
57
- collector: TestCollector,
58
- name: string,
59
- key: string,
60
- value?: unknown,
61
- ): void {
62
- void collector;
63
- void name;
64
- void key;
65
- void value;
66
- // No-op in browser
67
- }
68
-
69
- /**
70
- * Serialized span type (browser stub - mirrors server SerializedSpan).
71
- *
72
- * Defined as a `type` (not `interface`) so it is assignable to
73
- * `Record<string, unknown>` in TypeScript 6+ strict mode.
74
- */
75
- export type SerializedSpan = {
76
- name: string;
77
- spanId: string;
78
- traceId: string;
79
- parentSpanId?: string;
80
- attributes?: Record<string, unknown>;
81
- status: { code: number; message?: string };
82
- durationMs: number;
83
- };
84
-
85
- /**
86
- * Accepts either a raw `Request` (legacy) or a TanStack Router context
87
- * object containing `{ request: Request }` (Router 1.168+).
88
- */
89
- type HandlerInput = Request | { request: Request };
90
-
91
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
- type CreateFileRoute = (path: string) => (options: any) => any;
93
-
94
- /**
95
- * Browser stub: createTestSpansRoute is server-only.
96
- */
97
- export function createTestSpansRoute(
98
- createFileRoute: CreateFileRoute,
99
- path?: string,
100
- ): unknown {
101
- void createFileRoute;
102
- void path;
103
- throw new Error('createTestSpansRoute is server-only');
104
- }
105
-
106
- /**
107
- * Browser stub: test-spans handlers are server-only.
108
- * Returns no-op handlers that always return 404.
109
- */
110
- export function createTestSpansHandlers(): {
111
- GET: (input: HandlerInput) => Response;
112
- DELETE: (input: HandlerInput) => Response;
113
- } {
114
- return {
115
- GET(input: HandlerInput): Response {
116
- void input;
117
- return Response.json(
118
- { error: 'createTestSpansHandlers is server-only' },
119
- { status: 404 },
120
- );
121
- },
122
- DELETE(input: HandlerInput): Response {
123
- void input;
124
- return Response.json(
125
- { error: 'createTestSpansHandlers is server-only' },
126
- { status: 404 },
127
- );
128
- },
129
- };
130
- }
@@ -1,100 +0,0 @@
1
- /**
2
- * Browser stub for types module
3
- *
4
- * Provides type definitions without importing from @opentelemetry/api
5
- */
6
-
7
- /**
8
- * OpenTelemetry-compatible Attributes type (browser stub)
9
- */
10
- export type Attributes = Record<string, string | number | boolean | undefined>;
11
-
12
- /**
13
- * Configuration options for TanStack Start instrumentation
14
- */
15
- export interface TanStackInstrumentationConfig {
16
- service?: string;
17
- captureArgs?: boolean;
18
- captureResults?: boolean;
19
- captureErrors?: boolean;
20
- captureHeaders?: string[];
21
- excludePaths?: (string | RegExp)[];
22
- sampling?: 'always' | 'adaptive' | 'never';
23
- customAttributes?: (context: {
24
- type: 'request' | 'serverFn' | 'loader' | 'beforeLoad' | 'middleware';
25
- name: string;
26
- request?: Request;
27
- args?: unknown;
28
- result?: unknown;
29
- }) => Attributes;
30
- }
31
-
32
- /**
33
- * Configuration specific to tracing middleware
34
- */
35
- export interface TracingMiddlewareConfig extends TanStackInstrumentationConfig {
36
- type?: 'request' | 'function';
37
- }
38
-
39
- /**
40
- * Configuration for server function tracing
41
- */
42
- export interface TraceServerFnConfig {
43
- name?: string;
44
- captureArgs?: boolean;
45
- captureResults?: boolean;
46
- }
47
-
48
- /**
49
- * Configuration for loader tracing
50
- */
51
- export interface TraceLoaderConfig {
52
- name?: string;
53
- captureParams?: boolean;
54
- captureResult?: boolean;
55
- }
56
-
57
- /**
58
- * Configuration for handler wrapper
59
- */
60
- export interface WrapStartHandlerConfig extends TanStackInstrumentationConfig {
61
- endpoint?: string;
62
- headers?: Record<string, string>;
63
- }
64
-
65
- /**
66
- * Default configuration values
67
- */
68
- export const DEFAULT_CONFIG: Required<
69
- Omit<TanStackInstrumentationConfig, 'customAttributes' | 'service'>
70
- > = {
71
- captureArgs: true,
72
- captureResults: false,
73
- captureErrors: true,
74
- captureHeaders: ['x-request-id'],
75
- excludePaths: [],
76
- sampling: 'adaptive',
77
- };
78
-
79
- /**
80
- * Span attribute keys (stub - values are strings)
81
- */
82
- export const SPAN_ATTRIBUTES = {
83
- HTTP_REQUEST_METHOD: 'http.request.method',
84
- HTTP_RESPONSE_STATUS_CODE: 'http.response.status_code',
85
- URL_PATH: 'url.path',
86
- URL_QUERY: 'url.query',
87
- URL_FULL: 'url.full',
88
- RPC_SYSTEM: 'rpc.system',
89
- RPC_METHOD: 'rpc.method',
90
- TANSTACK_TYPE: 'tanstack.type',
91
- TANSTACK_SERVER_FN_NAME: 'tanstack.server_function.name',
92
- TANSTACK_SERVER_FN_METHOD: 'tanstack.server_function.method',
93
- TANSTACK_SERVER_FN_ARGS: 'tanstack.server_function.args',
94
- TANSTACK_SERVER_FN_RESULT: 'tanstack.server_function.result',
95
- TANSTACK_LOADER_ROUTE_ID: 'tanstack.loader.route_id',
96
- TANSTACK_LOADER_TYPE: 'tanstack.loader.type',
97
- TANSTACK_LOADER_PARAMS: 'tanstack.loader.params',
98
- TANSTACK_MIDDLEWARE_NAME: 'tanstack.middleware.name',
99
- TANSTACK_REQUEST_DURATION_MS: 'tanstack.request.duration_ms',
100
- } as const;
@@ -1,90 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- extractContextFromRequest,
4
- injectContextToHeaders,
5
- createTracedHeaders,
6
- } from './context';
7
-
8
- describe('context', () => {
9
- describe('extractContextFromRequest', () => {
10
- it('should return context from request with traceparent header', () => {
11
- const traceparent =
12
- '00-12345678901234567890123456789012-1234567890123456-01';
13
- const request = new Request('http://localhost/test', {
14
- headers: { traceparent },
15
- });
16
-
17
- const ctx = extractContextFromRequest(request);
18
- expect(ctx).toBeDefined();
19
- });
20
-
21
- it('should handle request without trace headers', () => {
22
- const request = new Request('http://localhost/test');
23
- const ctx = extractContextFromRequest(request);
24
- expect(ctx).toBeDefined(); // Should return ROOT_CONTEXT
25
- });
26
-
27
- it('should extract tracestate if present', () => {
28
- const request = new Request('http://localhost/test', {
29
- headers: {
30
- traceparent:
31
- '00-12345678901234567890123456789012-1234567890123456-01',
32
- tracestate: 'vendor=value',
33
- },
34
- });
35
-
36
- const ctx = extractContextFromRequest(request);
37
- expect(ctx).toBeDefined();
38
- });
39
-
40
- it('should extract baggage if present', () => {
41
- const request = new Request('http://localhost/test', {
42
- headers: {
43
- traceparent:
44
- '00-12345678901234567890123456789012-1234567890123456-01',
45
- baggage: 'userId=123,sessionId=abc',
46
- },
47
- });
48
-
49
- const ctx = extractContextFromRequest(request);
50
- expect(ctx).toBeDefined();
51
- });
52
- });
53
-
54
- describe('injectContextToHeaders', () => {
55
- it('should inject context into headers', () => {
56
- const headers = new Headers();
57
- const result = injectContextToHeaders(headers);
58
- expect(result).toBe(headers); // Returns same headers object
59
- });
60
-
61
- it('should work with existing headers', () => {
62
- const headers = new Headers({ 'Content-Type': 'application/json' });
63
- injectContextToHeaders(headers);
64
- expect(headers.get('Content-Type')).toBe('application/json');
65
- });
66
- });
67
-
68
- describe('createTracedHeaders', () => {
69
- it('should create headers with trace context', () => {
70
- const headers = createTracedHeaders();
71
- expect(headers).toBeInstanceOf(Headers);
72
- });
73
-
74
- it('should include existing headers', () => {
75
- const headers = createTracedHeaders({
76
- 'Content-Type': 'application/json',
77
- });
78
- expect(headers.get('Content-Type')).toBe('application/json');
79
- });
80
-
81
- it('should work with HeadersInit object', () => {
82
- const headers = createTracedHeaders({
83
- 'Content-Type': 'application/json',
84
- 'X-Custom': 'value',
85
- });
86
- expect(headers.get('Content-Type')).toBe('application/json');
87
- expect(headers.get('X-Custom')).toBe('value');
88
- });
89
- });
90
- });
package/src/context.ts DELETED
@@ -1,145 +0,0 @@
1
- import {
2
- context,
3
- propagation,
4
- type Context,
5
- ROOT_CONTEXT,
6
- } from '@opentelemetry/api';
7
-
8
- /**
9
- * Type representing values that can be used to initialize Headers
10
- * This is equivalent to the DOM's HeadersInit type but works in Node.js
11
- */
12
- export type HeadersInitType =
13
- | Headers
14
- | Record<string, string>
15
- | [string, string][];
16
-
17
- /**
18
- * Extract OpenTelemetry context from HTTP request headers
19
- *
20
- * This function extracts W3C Trace Context (traceparent, tracestate)
21
- * and Baggage from request headers to enable distributed tracing.
22
- *
23
- * @param request - The incoming HTTP request
24
- * @returns OpenTelemetry context with extracted trace information
25
- *
26
- * @example
27
- * ```typescript
28
- * const parentContext = extractContextFromRequest(request);
29
- * context.with(parentContext, async () => {
30
- * // Spans created here will be children of the extracted context
31
- * await trace('my-operation', async (ctx) => { ... });
32
- * });
33
- * ```
34
- */
35
- export function extractContextFromRequest(request: Request): Context {
36
- const carrier: Record<string, string> = {};
37
-
38
- // Extract W3C Trace Context headers
39
- const traceparent = request.headers.get('traceparent');
40
- const tracestate = request.headers.get('tracestate');
41
- const baggage = request.headers.get('baggage');
42
-
43
- if (traceparent) carrier.traceparent = traceparent;
44
- if (tracestate) carrier.tracestate = tracestate;
45
- if (baggage) carrier.baggage = baggage;
46
-
47
- // Return ROOT_CONTEXT if no trace headers present
48
- if (Object.keys(carrier).length === 0) {
49
- return ROOT_CONTEXT;
50
- }
51
-
52
- return propagation.extract(context.active(), carrier);
53
- }
54
-
55
- /**
56
- * Inject OpenTelemetry context into HTTP headers
57
- *
58
- * This function injects W3C Trace Context (traceparent, tracestate)
59
- * and Baggage into headers for outgoing requests.
60
- *
61
- * @param headers - Headers object to inject context into
62
- * @param ctx - Optional context to inject (defaults to active context)
63
- * @returns The headers object with injected trace context
64
- *
65
- * @example
66
- * ```typescript
67
- * const headers = new Headers();
68
- * injectContextToHeaders(headers);
69
- *
70
- * // Now use headers in outgoing fetch
71
- * await fetch('https://api.example.com', { headers });
72
- * ```
73
- */
74
- export function injectContextToHeaders(
75
- headers: Headers,
76
- ctx?: Context,
77
- ): Headers {
78
- const carrier: Record<string, string> = {};
79
- propagation.inject(ctx ?? context.active(), carrier);
80
-
81
- for (const [key, value] of Object.entries(carrier)) {
82
- headers.set(key, value);
83
- }
84
-
85
- return headers;
86
- }
87
-
88
- /**
89
- * Create a new Headers object with injected trace context
90
- *
91
- * Convenience function that creates a new Headers object
92
- * with the current trace context already injected.
93
- *
94
- * @param existingHeaders - Optional existing headers to include
95
- * @param ctx - Optional context to inject (defaults to active context)
96
- * @returns New Headers object with trace context
97
- *
98
- * @example
99
- * ```typescript
100
- * const headers = createTracedHeaders({ 'Content-Type': 'application/json' });
101
- * await fetch('https://api.example.com', {
102
- * method: 'POST',
103
- * headers,
104
- * body: JSON.stringify(data),
105
- * });
106
- * ```
107
- */
108
- export function createTracedHeaders(
109
- existingHeaders?: HeadersInitType,
110
- ctx?: Context,
111
- ): Headers {
112
- const headers = new Headers(existingHeaders);
113
- return injectContextToHeaders(headers, ctx);
114
- }
115
-
116
- /**
117
- * Run a function within a specific OpenTelemetry context
118
- *
119
- * This is a convenience wrapper around context.with() that
120
- * provides better TypeScript inference.
121
- *
122
- * @param parentContext - The context to run within
123
- * @param fn - The function to execute
124
- * @returns The result of the function
125
- *
126
- * @example
127
- * ```typescript
128
- * const parentContext = extractContextFromRequest(request);
129
- * const result = await runInContext(parentContext, async () => {
130
- * return await processRequest();
131
- * });
132
- * ```
133
- */
134
- export function runInContext<T>(parentContext: Context, fn: () => T): T {
135
- return context.with(parentContext, fn);
136
- }
137
-
138
- /**
139
- * Get the current active context
140
- *
141
- * @returns The current active OpenTelemetry context
142
- */
143
- export function getActiveContext(): Context {
144
- return context.active();
145
- }
@@ -1,109 +0,0 @@
1
- import { isServerSide } from './env';
2
- import type { MiddlewareHandler } from './middleware';
3
-
4
- /**
5
- * Configuration for debug headers middleware
6
- */
7
- export interface DebugHeadersConfig {
8
- /**
9
- * Whether to enable debug headers
10
- * @default process.env.NODE_ENV === 'development'
11
- */
12
- enabled?: boolean;
13
-
14
- /**
15
- * Custom headers to add
16
- */
17
- customHeaders?: Record<string, string | (() => string)>;
18
- }
19
-
20
- /**
21
- * Create middleware that adds debug headers to responses in development
22
- *
23
- * Adds helpful debug information to response headers:
24
- * - X-Debug-Timestamp: Request timestamp
25
- * - X-Debug-Node-Version: Node.js version
26
- * - X-Debug-Uptime: Process uptime in seconds
27
- * - X-Debug-Trace-Id: Current trace ID (if available)
28
- *
29
- * @param config - Configuration options
30
- * @returns Middleware handler
31
- *
32
- * @example
33
- * ```typescript
34
- * import { createStart } from '@tanstack/react-start';
35
- * import { debugHeadersMiddleware } from 'autotel-tanstack/debug-headers';
36
- *
37
- * export const startInstance = createStart(() => ({
38
- * requestMiddleware: [debugHeadersMiddleware()],
39
- * }));
40
- * ```
41
- */
42
- export function debugHeadersMiddleware(
43
- config: DebugHeadersConfig = {},
44
- ): MiddlewareHandler {
45
- // If we're in the browser, return a no-op middleware
46
- if (!isServerSide()) {
47
- return async function debugHeadersHandler(opts) {
48
- return opts.next();
49
- };
50
- }
51
-
52
- const enabled =
53
- config.enabled ??
54
- (typeof process !== 'undefined' && process.env.NODE_ENV === 'development');
55
-
56
- return async function debugHeadersHandler(opts) {
57
- const { next, request } = opts;
58
-
59
- if (!enabled || !request) {
60
- return next();
61
- }
62
-
63
- const result = await next();
64
-
65
- // Check if result is a Response
66
- if (!(result instanceof Response)) {
67
- return result;
68
- }
69
-
70
- const response = result;
71
-
72
- // Clone response to add headers (responses are immutable)
73
- const newHeaders = new Headers(response.headers);
74
-
75
- // Add standard debug headers
76
- newHeaders.set('X-Debug-Timestamp', new Date().toISOString());
77
- newHeaders.set('X-Debug-Node-Version', process.version);
78
- newHeaders.set('X-Debug-Uptime', Math.floor(process.uptime()).toString());
79
-
80
- // Add trace ID if available
81
- try {
82
- const { trace } = await import('@opentelemetry/api');
83
- const activeSpan = trace.getActiveSpan();
84
- if (activeSpan) {
85
- const spanContext = activeSpan.spanContext();
86
- if (spanContext.traceId) {
87
- newHeaders.set('X-Debug-Trace-Id', spanContext.traceId);
88
- }
89
- }
90
- } catch {
91
- // OpenTelemetry not available, skip trace ID
92
- }
93
-
94
- // Add custom headers
95
- if (config.customHeaders) {
96
- for (const [key, value] of Object.entries(config.customHeaders)) {
97
- const headerValue = typeof value === 'function' ? value() : value;
98
- newHeaders.set(`X-Debug-${key}`, headerValue);
99
- }
100
- }
101
-
102
- // Return new response with debug headers
103
- return new Response(response.body, {
104
- status: response.status,
105
- statusText: response.statusText,
106
- headers: newHeaders,
107
- });
108
- };
109
- }
package/src/env.ts DELETED
@@ -1,56 +0,0 @@
1
- /**
2
- * Environment detection utilities
3
- * Prevents server-only code from running in the browser
4
- */
5
-
6
- /**
7
- * Check if we're running in a browser environment
8
- * Uses typeof checks to avoid TypeScript DOM type requirements
9
- */
10
- export function isBrowser(): boolean {
11
- return (
12
- typeof globalThis !== 'undefined' &&
13
- // @ts-expect-error - window may not exist in Node.js, that's the point
14
- typeof globalThis.window !== 'undefined' &&
15
- // @ts-expect-error - document may not exist in Node.js, that's the point
16
- typeof globalThis.document !== 'undefined'
17
- );
18
- }
19
-
20
- /**
21
- * Check if we're running in a Node.js environment
22
- */
23
- export function isNode(): boolean {
24
- return (
25
- typeof process !== 'undefined' &&
26
- typeof process.versions !== 'undefined' &&
27
- typeof process.versions.node !== 'undefined'
28
- );
29
- }
30
-
31
- /**
32
- * Check if we're in a server-side context
33
- * In TanStack Start, middleware runs on the server, but router config
34
- * might be evaluated on both sides during SSR
35
- */
36
- export function isServerSide(): boolean {
37
- // In TanStack Start, if we're in a request handler context, we're on the server
38
- // Check for Node.js environment (server) and not browser
39
- return isNode() && !isBrowser();
40
- }
41
-
42
- /**
43
- * Safely check if a module is available (for optional dependencies)
44
- */
45
- export function isModuleAvailable(moduleName: string): boolean {
46
- try {
47
- // This will only work in Node.js, not browser
48
- if (typeof require !== 'undefined') {
49
- require.resolve(moduleName);
50
- return true;
51
- }
52
- return false;
53
- } catch {
54
- return false;
55
- }
56
- }