autotel-tanstack 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/dist/auto.d.ts +44 -0
  4. package/dist/auto.js +47 -0
  5. package/dist/auto.js.map +1 -0
  6. package/dist/browser/context.d.ts +48 -0
  7. package/dist/browser/context.js +3 -0
  8. package/dist/browser/context.js.map +1 -0
  9. package/dist/browser/debug-headers.d.ts +16 -0
  10. package/dist/browser/debug-headers.js +3 -0
  11. package/dist/browser/debug-headers.js.map +1 -0
  12. package/dist/browser/error-reporting.d.ts +37 -0
  13. package/dist/browser/error-reporting.js +3 -0
  14. package/dist/browser/error-reporting.js.map +1 -0
  15. package/dist/browser/handlers.d.ts +19 -0
  16. package/dist/browser/handlers.js +3 -0
  17. package/dist/browser/handlers.js.map +1 -0
  18. package/dist/browser/index.d.ts +10 -0
  19. package/dist/browser/index.js +12 -0
  20. package/dist/browser/index.js.map +1 -0
  21. package/dist/browser/loaders.d.ts +36 -0
  22. package/dist/browser/loaders.js +3 -0
  23. package/dist/browser/loaders.js.map +1 -0
  24. package/dist/browser/metrics.d.ts +54 -0
  25. package/dist/browser/metrics.js +3 -0
  26. package/dist/browser/metrics.js.map +1 -0
  27. package/dist/browser/middleware.d.ts +39 -0
  28. package/dist/browser/middleware.js +3 -0
  29. package/dist/browser/middleware.js.map +1 -0
  30. package/dist/browser/server-functions.d.ts +19 -0
  31. package/dist/browser/server-functions.js +3 -0
  32. package/dist/browser/server-functions.js.map +1 -0
  33. package/dist/browser/testing.d.ts +45 -0
  34. package/dist/browser/testing.js +3 -0
  35. package/dist/browser/testing.js.map +1 -0
  36. package/dist/browser/types.d.ts +85 -0
  37. package/dist/browser/types.js +3 -0
  38. package/dist/browser/types.js.map +1 -0
  39. package/dist/chunk-4C7T5ZIM.js +20 -0
  40. package/dist/chunk-4C7T5ZIM.js.map +1 -0
  41. package/dist/chunk-CSFIPJC2.js +11 -0
  42. package/dist/chunk-CSFIPJC2.js.map +1 -0
  43. package/dist/chunk-DTZCOB4W.js +32 -0
  44. package/dist/chunk-DTZCOB4W.js.map +1 -0
  45. package/dist/chunk-EGRHWZRV.js +3 -0
  46. package/dist/chunk-EGRHWZRV.js.map +1 -0
  47. package/dist/chunk-EUYFVNYE.js +16 -0
  48. package/dist/chunk-EUYFVNYE.js.map +1 -0
  49. package/dist/chunk-HIQYW2HB.js +20 -0
  50. package/dist/chunk-HIQYW2HB.js.map +1 -0
  51. package/dist/chunk-HKM7LMO6.js +129 -0
  52. package/dist/chunk-HKM7LMO6.js.map +1 -0
  53. package/dist/chunk-I4LX3LOG.js +35 -0
  54. package/dist/chunk-I4LX3LOG.js.map +1 -0
  55. package/dist/chunk-JSI6QG7M.js +96 -0
  56. package/dist/chunk-JSI6QG7M.js.map +1 -0
  57. package/dist/chunk-JXO7H6KO.js +10 -0
  58. package/dist/chunk-JXO7H6KO.js.map +1 -0
  59. package/dist/chunk-MFYOV2SF.js +32 -0
  60. package/dist/chunk-MFYOV2SF.js.map +1 -0
  61. package/dist/chunk-MNP65ZX7.js +21 -0
  62. package/dist/chunk-MNP65ZX7.js.map +1 -0
  63. package/dist/chunk-NTY64BKS.js +38 -0
  64. package/dist/chunk-NTY64BKS.js.map +1 -0
  65. package/dist/chunk-OLBHLVLE.js +220 -0
  66. package/dist/chunk-OLBHLVLE.js.map +1 -0
  67. package/dist/chunk-TNOQTZ3N.js +92 -0
  68. package/dist/chunk-TNOQTZ3N.js.map +1 -0
  69. package/dist/chunk-UMEJU65Q.js +34 -0
  70. package/dist/chunk-UMEJU65Q.js.map +1 -0
  71. package/dist/chunk-UTPW3QRT.js +52 -0
  72. package/dist/chunk-UTPW3QRT.js.map +1 -0
  73. package/dist/chunk-V3RO5N2M.js +8 -0
  74. package/dist/chunk-V3RO5N2M.js.map +1 -0
  75. package/dist/chunk-XXBHZR3M.js +99 -0
  76. package/dist/chunk-XXBHZR3M.js.map +1 -0
  77. package/dist/chunk-Z3MJ3GZ6.js +18 -0
  78. package/dist/chunk-Z3MJ3GZ6.js.map +1 -0
  79. package/dist/chunk-Z5D2V4DU.js +216 -0
  80. package/dist/chunk-Z5D2V4DU.js.map +1 -0
  81. package/dist/context.d.ts +94 -0
  82. package/dist/context.js +4 -0
  83. package/dist/context.js.map +1 -0
  84. package/dist/debug-headers.d.ts +43 -0
  85. package/dist/debug-headers.js +5 -0
  86. package/dist/debug-headers.js.map +1 -0
  87. package/dist/error-reporting.d.ts +118 -0
  88. package/dist/error-reporting.js +4 -0
  89. package/dist/error-reporting.js.map +1 -0
  90. package/dist/handlers.d.ts +70 -0
  91. package/dist/handlers.js +6 -0
  92. package/dist/handlers.js.map +1 -0
  93. package/dist/index.d.ts +34 -0
  94. package/dist/index.js +14 -0
  95. package/dist/index.js.map +1 -0
  96. package/dist/loaders.d.ts +124 -0
  97. package/dist/loaders.js +6 -0
  98. package/dist/loaders.js.map +1 -0
  99. package/dist/metrics.d.ts +113 -0
  100. package/dist/metrics.js +4 -0
  101. package/dist/metrics.js.map +1 -0
  102. package/dist/middleware.d.ts +104 -0
  103. package/dist/middleware.js +7 -0
  104. package/dist/middleware.js.map +1 -0
  105. package/dist/server-functions.d.ts +71 -0
  106. package/dist/server-functions.js +6 -0
  107. package/dist/server-functions.js.map +1 -0
  108. package/dist/testing.d.ts +128 -0
  109. package/dist/testing.js +110 -0
  110. package/dist/testing.js.map +1 -0
  111. package/dist/types-C37KSxMN.d.ts +152 -0
  112. package/package.json +166 -0
  113. package/src/auto.ts +86 -0
  114. package/src/browser/context.ts +88 -0
  115. package/src/browser/debug-headers.ts +19 -0
  116. package/src/browser/error-reporting.ts +63 -0
  117. package/src/browser/handlers.ts +23 -0
  118. package/src/browser/index.ts +65 -0
  119. package/src/browser/loaders.ts +62 -0
  120. package/src/browser/metrics.ts +86 -0
  121. package/src/browser/middleware.ts +61 -0
  122. package/src/browser/server-functions.ts +31 -0
  123. package/src/browser/testing.ts +67 -0
  124. package/src/browser/types.ts +100 -0
  125. package/src/context.test.ts +90 -0
  126. package/src/context.ts +145 -0
  127. package/src/debug-headers.ts +109 -0
  128. package/src/env.ts +56 -0
  129. package/src/error-reporting.ts +204 -0
  130. package/src/handlers.ts +339 -0
  131. package/src/index.ts +92 -0
  132. package/src/loaders.test.ts +123 -0
  133. package/src/loaders.ts +267 -0
  134. package/src/metrics.ts +183 -0
  135. package/src/middleware.test.ts +191 -0
  136. package/src/middleware.ts +400 -0
  137. package/src/server-functions.test.ts +86 -0
  138. package/src/server-functions.ts +184 -0
  139. package/src/testing.test.ts +72 -0
  140. package/src/testing.ts +276 -0
  141. package/src/types.test.ts +46 -0
  142. package/src/types.ts +182 -0
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createMockRequest, generateTraceparent } from './testing';
3
+
4
+ describe('testing utilities', () => {
5
+ describe('createMockRequest', () => {
6
+ it('should create a GET request', () => {
7
+ const request = createMockRequest('GET', '/api/users');
8
+
9
+ expect(request.method).toBe('GET');
10
+ expect(request.url).toBe('http://localhost/api/users');
11
+ });
12
+
13
+ it('should create a POST request', () => {
14
+ const request = createMockRequest('POST', '/api/users', {
15
+ body: JSON.stringify({ name: 'Test' }),
16
+ });
17
+
18
+ expect(request.method).toBe('POST');
19
+ });
20
+
21
+ it('should include custom headers', () => {
22
+ const request = createMockRequest('GET', '/api/users', {
23
+ headers: { 'x-request-id': 'test-123' },
24
+ });
25
+
26
+ expect(request.headers.get('x-request-id')).toBe('test-123');
27
+ });
28
+
29
+ it('should include traceparent header', () => {
30
+ const traceparent =
31
+ '00-12345678901234567890123456789012-1234567890123456-01';
32
+ const request = createMockRequest('GET', '/api/users', { traceparent });
33
+
34
+ expect(request.headers.get('traceparent')).toBe(traceparent);
35
+ });
36
+ });
37
+
38
+ describe('generateTraceparent', () => {
39
+ it('should generate valid traceparent format', () => {
40
+ const traceparent = generateTraceparent();
41
+
42
+ // Format: version-traceId-spanId-flags
43
+ const parts = traceparent.split('-');
44
+ expect(parts).toHaveLength(4);
45
+ expect(parts[0]).toBe('00'); // version
46
+ expect(parts[1]).toHaveLength(32); // trace ID
47
+ expect(parts[2]).toHaveLength(16); // span ID
48
+ expect(parts[3]).toBe('01'); // sampled flag
49
+ });
50
+
51
+ it('should use provided trace ID', () => {
52
+ const traceId = '12345678901234567890123456789012';
53
+ const traceparent = generateTraceparent(traceId);
54
+
55
+ expect(traceparent).toContain(traceId);
56
+ });
57
+
58
+ it('should use provided span ID', () => {
59
+ const spanId = '1234567890123456';
60
+ const traceparent = generateTraceparent(undefined, spanId);
61
+
62
+ expect(traceparent).toContain(spanId);
63
+ });
64
+
65
+ it('should generate different values each time', () => {
66
+ const tp1 = generateTraceparent();
67
+ const tp2 = generateTraceparent();
68
+
69
+ expect(tp1).not.toBe(tp2);
70
+ });
71
+ });
72
+ });
package/src/testing.ts ADDED
@@ -0,0 +1,276 @@
1
+ import { type ReadableSpan } from '@opentelemetry/sdk-trace-base';
2
+ import { InMemorySpanExporter } from 'autotel/exporters';
3
+ import { SimpleSpanProcessor } from 'autotel/processors';
4
+ import { init } from 'autotel';
5
+
6
+ /**
7
+ * Test harness for TanStack instrumentation testing
8
+ *
9
+ * Provides utilities for testing TanStack Start applications
10
+ * with autotel-tanstack instrumentation.
11
+ */
12
+ export interface TestHarness {
13
+ /**
14
+ * The in-memory span exporter
15
+ */
16
+ exporter: {
17
+ getFinishedSpans(): ReadableSpan[];
18
+ reset(): void;
19
+ };
20
+
21
+ /**
22
+ * Get all finished spans
23
+ */
24
+ getSpans(): ReadableSpan[];
25
+
26
+ /**
27
+ * Get spans by name (exact match or regex)
28
+ */
29
+ getSpansByName(name: string | RegExp): ReadableSpan[];
30
+
31
+ /**
32
+ * Get spans by TanStack type
33
+ */
34
+ getSpansByType(
35
+ type: 'request' | 'serverFn' | 'loader' | 'beforeLoad',
36
+ ): ReadableSpan[];
37
+
38
+ /**
39
+ * Reset collected spans
40
+ */
41
+ reset(): void;
42
+
43
+ /**
44
+ * Assert a span exists
45
+ */
46
+ assertSpanExists(name: string | RegExp): void;
47
+
48
+ /**
49
+ * Assert a span has a specific attribute
50
+ */
51
+ assertSpanHasAttribute(
52
+ name: string | RegExp,
53
+ attr: string,
54
+ value?: unknown,
55
+ ): void;
56
+
57
+ /**
58
+ * Assert a server function was traced
59
+ */
60
+ assertServerFnTraced(name: string): void;
61
+
62
+ /**
63
+ * Assert a loader was traced
64
+ */
65
+ assertLoaderTraced(routeId: string): void;
66
+
67
+ /**
68
+ * Assert a beforeLoad was traced
69
+ */
70
+ assertBeforeLoadTraced(routeId: string): void;
71
+
72
+ /**
73
+ * Assert an HTTP request was traced
74
+ */
75
+ assertRequestTraced(method: string, path: string): void;
76
+ }
77
+
78
+ /**
79
+ * Create a test harness for TanStack instrumentation testing
80
+ *
81
+ * This sets up autotel with an in-memory exporter for testing.
82
+ * Call this in your test setup to capture and assert on spans.
83
+ *
84
+ * @returns Test harness with assertion helpers
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * import { describe, it, beforeEach } from 'vitest';
89
+ * import { createTestHarness } from 'autotel-tanstack/testing';
90
+ *
91
+ * describe('MyServerFunction', () => {
92
+ * let harness: ReturnType<typeof createTestHarness>;
93
+ *
94
+ * beforeEach(() => {
95
+ * harness = createTestHarness();
96
+ * });
97
+ *
98
+ * afterEach(() => {
99
+ * harness.reset();
100
+ * });
101
+ *
102
+ * it('should trace the server function', async () => {
103
+ * await myServerFunction({ id: '123' });
104
+ *
105
+ * harness.assertServerFnTraced('myServerFunction');
106
+ * harness.assertSpanHasAttribute(
107
+ * /tanstack\.serverFn/,
108
+ * 'tanstack.server_function.name',
109
+ * 'myServerFunction'
110
+ * );
111
+ * });
112
+ * });
113
+ * ```
114
+ */
115
+ export function createTestHarness(): TestHarness {
116
+ const exporter = new InMemorySpanExporter();
117
+
118
+ init({
119
+ service: 'test',
120
+ spanProcessors: [new SimpleSpanProcessor(exporter)],
121
+ });
122
+
123
+ function getSpans(): ReadableSpan[] {
124
+ return exporter.getFinishedSpans() as ReadableSpan[];
125
+ }
126
+
127
+ function getSpansByName(name: string | RegExp): ReadableSpan[] {
128
+ const spans = getSpans();
129
+ if (typeof name === 'string') {
130
+ return spans.filter((s) => s.name === name);
131
+ }
132
+ return spans.filter((s) => name.test(s.name));
133
+ }
134
+
135
+ function getSpansByType(
136
+ type: 'request' | 'serverFn' | 'loader' | 'beforeLoad',
137
+ ): ReadableSpan[] {
138
+ return getSpans().filter((s) => s.attributes['tanstack.type'] === type);
139
+ }
140
+
141
+ function reset(): void {
142
+ exporter.reset();
143
+ }
144
+
145
+ function assertSpanExists(name: string | RegExp): void {
146
+ const spans = getSpansByName(name);
147
+ if (spans.length === 0) {
148
+ const allSpanNames = getSpans().map((s) => s.name);
149
+ throw new Error(
150
+ `Expected span "${name}" to exist. Found spans: ${JSON.stringify(allSpanNames)}`,
151
+ );
152
+ }
153
+ }
154
+
155
+ function assertSpanHasAttribute(
156
+ name: string | RegExp,
157
+ attr: string,
158
+ value?: unknown,
159
+ ): void {
160
+ const spans = getSpansByName(name);
161
+ if (spans.length === 0) {
162
+ throw new Error(`Span "${name}" not found`);
163
+ }
164
+
165
+ const span = spans[0];
166
+ const attrValue = span.attributes[attr];
167
+
168
+ if (attrValue === undefined) {
169
+ throw new Error(
170
+ `Attribute "${attr}" not found on span "${span.name}". ` +
171
+ `Available attributes: ${JSON.stringify(Object.keys(span.attributes))}`,
172
+ );
173
+ }
174
+
175
+ if (value !== undefined && attrValue !== value) {
176
+ throw new Error(
177
+ `Expected attribute "${attr}" to be "${value}", got "${attrValue}"`,
178
+ );
179
+ }
180
+ }
181
+
182
+ function assertServerFnTraced(name: string): void {
183
+ assertSpanExists(`tanstack.serverFn.${name}`);
184
+ }
185
+
186
+ function assertLoaderTraced(routeId: string): void {
187
+ assertSpanExists(`tanstack.loader.${routeId}`);
188
+ }
189
+
190
+ function assertBeforeLoadTraced(routeId: string): void {
191
+ assertSpanExists(`tanstack.beforeLoad.${routeId}`);
192
+ }
193
+
194
+ function assertRequestTraced(method: string, path: string): void {
195
+ assertSpanExists(`${method} ${path}`);
196
+ }
197
+
198
+ return {
199
+ exporter,
200
+ getSpans,
201
+ getSpansByName,
202
+ getSpansByType,
203
+ reset,
204
+ assertSpanExists,
205
+ assertSpanHasAttribute,
206
+ assertServerFnTraced,
207
+ assertLoaderTraced,
208
+ assertBeforeLoadTraced,
209
+ assertRequestTraced,
210
+ };
211
+ }
212
+
213
+ /**
214
+ * Mock request factory for testing
215
+ *
216
+ * Creates mock Request objects for testing middleware and handlers.
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * const request = createMockRequest('GET', '/api/users', {
221
+ * headers: { 'x-request-id': 'test-123' },
222
+ * });
223
+ * ```
224
+ */
225
+ export function createMockRequest(
226
+ method: string,
227
+ path: string,
228
+ options: {
229
+ headers?: Record<string, string>;
230
+ body?: string;
231
+ traceparent?: string;
232
+ } = {},
233
+ ): Request {
234
+ const headers = new Headers(options.headers);
235
+
236
+ if (options.traceparent) {
237
+ headers.set('traceparent', options.traceparent);
238
+ }
239
+
240
+ return new Request(`http://localhost${path}`, {
241
+ method,
242
+ headers,
243
+ body: options.body,
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Generate a valid W3C traceparent header for testing
249
+ *
250
+ * @param traceId - Optional 32-char hex trace ID
251
+ * @param spanId - Optional 16-char hex span ID
252
+ * @returns Valid traceparent header string
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * const traceparent = generateTraceparent();
257
+ * const request = createMockRequest('GET', '/api/users', { traceparent });
258
+ * ```
259
+ */
260
+ export function generateTraceparent(traceId?: string, spanId?: string): string {
261
+ const version = '00';
262
+ const trace = traceId || generateHex(32);
263
+ const span = spanId || generateHex(16);
264
+ const flags = '01'; // Sampled
265
+
266
+ return `${version}-${trace}-${span}-${flags}`;
267
+ }
268
+
269
+ function generateHex(length: number): string {
270
+ const chars = '0123456789abcdef';
271
+ let result = '';
272
+ for (let i = 0; i < length; i++) {
273
+ result += chars[Math.floor(Math.random() * 16)];
274
+ }
275
+ return result;
276
+ }
@@ -0,0 +1,46 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { DEFAULT_CONFIG, SPAN_ATTRIBUTES } from './types';
3
+
4
+ describe('types', () => {
5
+ describe('DEFAULT_CONFIG', () => {
6
+ it('should have sensible defaults', () => {
7
+ expect(DEFAULT_CONFIG.captureArgs).toBe(true);
8
+ expect(DEFAULT_CONFIG.captureResults).toBe(false);
9
+ expect(DEFAULT_CONFIG.captureErrors).toBe(true);
10
+ expect(DEFAULT_CONFIG.sampling).toBe('adaptive');
11
+ });
12
+
13
+ it('should capture x-request-id by default', () => {
14
+ expect(DEFAULT_CONFIG.captureHeaders).toContain('x-request-id');
15
+ });
16
+
17
+ it('should not exclude any paths by default', () => {
18
+ expect(DEFAULT_CONFIG.excludePaths).toEqual([]);
19
+ });
20
+ });
21
+
22
+ describe('SPAN_ATTRIBUTES', () => {
23
+ it('should have HTTP semantic convention attributes', () => {
24
+ expect(SPAN_ATTRIBUTES.HTTP_REQUEST_METHOD).toBe('http.request.method');
25
+ expect(SPAN_ATTRIBUTES.HTTP_RESPONSE_STATUS_CODE).toBe(
26
+ 'http.response.status_code',
27
+ );
28
+ expect(SPAN_ATTRIBUTES.URL_PATH).toBe('url.path');
29
+ });
30
+
31
+ it('should have RPC semantic convention attributes', () => {
32
+ expect(SPAN_ATTRIBUTES.RPC_SYSTEM).toBe('rpc.system');
33
+ expect(SPAN_ATTRIBUTES.RPC_METHOD).toBe('rpc.method');
34
+ });
35
+
36
+ it('should have TanStack-specific attributes', () => {
37
+ expect(SPAN_ATTRIBUTES.TANSTACK_TYPE).toBe('tanstack.type');
38
+ expect(SPAN_ATTRIBUTES.TANSTACK_SERVER_FN_NAME).toBe(
39
+ 'tanstack.server_function.name',
40
+ );
41
+ expect(SPAN_ATTRIBUTES.TANSTACK_LOADER_ROUTE_ID).toBe(
42
+ 'tanstack.loader.route_id',
43
+ );
44
+ });
45
+ });
46
+ });
package/src/types.ts ADDED
@@ -0,0 +1,182 @@
1
+ import type { Attributes } from '@opentelemetry/api';
2
+
3
+ /**
4
+ * Configuration options for TanStack Start instrumentation
5
+ */
6
+ export interface TanStackInstrumentationConfig {
7
+ /**
8
+ * Service name for spans
9
+ * @default process.env.OTEL_SERVICE_NAME || 'tanstack-start'
10
+ */
11
+ service?: string;
12
+
13
+ /**
14
+ * Whether to capture function arguments as span attributes
15
+ * Warning: May contain PII, review before enabling in production
16
+ * @default true
17
+ */
18
+ captureArgs?: boolean;
19
+
20
+ /**
21
+ * Whether to capture function results as span attributes
22
+ * Warning: May contain PII, disable in production
23
+ * @default false
24
+ */
25
+ captureResults?: boolean;
26
+
27
+ /**
28
+ * Whether to capture errors and record exceptions
29
+ * @default true
30
+ */
31
+ captureErrors?: boolean;
32
+
33
+ /**
34
+ * HTTP headers to capture as span attributes
35
+ * @default ['x-request-id']
36
+ */
37
+ captureHeaders?: string[];
38
+
39
+ /**
40
+ * URL paths to exclude from tracing (glob patterns)
41
+ * @default []
42
+ */
43
+ excludePaths?: (string | RegExp)[];
44
+
45
+ /**
46
+ * Sampling strategy
47
+ * - 'always': Sample all requests (100%)
48
+ * - 'adaptive': Use autotel's adaptive sampling (errors + slow = 100%, baseline 10%)
49
+ * - 'never': Disable sampling (for testing/debugging)
50
+ * @default 'adaptive'
51
+ */
52
+ sampling?: 'always' | 'adaptive' | 'never';
53
+
54
+ /**
55
+ * Custom function to extract additional span attributes
56
+ */
57
+ customAttributes?: (context: {
58
+ type: 'request' | 'serverFn' | 'loader' | 'beforeLoad' | 'middleware';
59
+ name: string;
60
+ request?: Request;
61
+ args?: unknown;
62
+ result?: unknown;
63
+ }) => Attributes;
64
+ }
65
+
66
+ /**
67
+ * Configuration specific to tracing middleware
68
+ */
69
+ export interface TracingMiddlewareConfig extends TanStackInstrumentationConfig {
70
+ /**
71
+ * Type of middleware
72
+ * - 'request': For global request middleware (routes, SSR)
73
+ * - 'function': For server function middleware
74
+ * @default 'request'
75
+ */
76
+ type?: 'request' | 'function';
77
+ }
78
+
79
+ /**
80
+ * Configuration for server function tracing
81
+ */
82
+ export interface TraceServerFnConfig {
83
+ /**
84
+ * Explicit name for the span
85
+ * If not provided, will attempt to infer from function name
86
+ */
87
+ name?: string;
88
+
89
+ /**
90
+ * Whether to capture function arguments
91
+ * @default true
92
+ */
93
+ captureArgs?: boolean;
94
+
95
+ /**
96
+ * Whether to capture function results
97
+ * @default false
98
+ */
99
+ captureResults?: boolean;
100
+ }
101
+
102
+ /**
103
+ * Configuration for loader tracing
104
+ */
105
+ export interface TraceLoaderConfig {
106
+ /**
107
+ * Explicit name for the span
108
+ * If not provided, will use route ID
109
+ */
110
+ name?: string;
111
+
112
+ /**
113
+ * Whether to capture route params
114
+ * @default true
115
+ */
116
+ captureParams?: boolean;
117
+
118
+ /**
119
+ * Whether to capture loader result
120
+ * @default false
121
+ */
122
+ captureResult?: boolean;
123
+ }
124
+
125
+ /**
126
+ * Configuration for handler wrapper
127
+ */
128
+ export interface WrapStartHandlerConfig extends TanStackInstrumentationConfig {
129
+ /**
130
+ * OTLP endpoint URL
131
+ * @default process.env.OTEL_EXPORTER_OTLP_ENDPOINT
132
+ */
133
+ endpoint?: string;
134
+
135
+ /**
136
+ * OTLP headers (e.g., for authentication)
137
+ * @default parsed from process.env.OTEL_EXPORTER_OTLP_HEADERS
138
+ */
139
+ headers?: Record<string, string>;
140
+ }
141
+
142
+ /**
143
+ * Default configuration values
144
+ */
145
+ export const DEFAULT_CONFIG: Required<
146
+ Omit<TanStackInstrumentationConfig, 'customAttributes' | 'service'>
147
+ > = {
148
+ captureArgs: true,
149
+ captureResults: false,
150
+ captureErrors: true,
151
+ captureHeaders: ['x-request-id'],
152
+ excludePaths: [],
153
+ sampling: 'adaptive',
154
+ };
155
+
156
+ /**
157
+ * Span attribute keys following OpenTelemetry semantic conventions
158
+ */
159
+ export const SPAN_ATTRIBUTES = {
160
+ // HTTP semantic conventions
161
+ HTTP_REQUEST_METHOD: 'http.request.method',
162
+ HTTP_RESPONSE_STATUS_CODE: 'http.response.status_code',
163
+ URL_PATH: 'url.path',
164
+ URL_QUERY: 'url.query',
165
+ URL_FULL: 'url.full',
166
+
167
+ // RPC semantic conventions (for server functions)
168
+ RPC_SYSTEM: 'rpc.system',
169
+ RPC_METHOD: 'rpc.method',
170
+
171
+ // TanStack-specific attributes
172
+ TANSTACK_TYPE: 'tanstack.type',
173
+ TANSTACK_SERVER_FN_NAME: 'tanstack.server_function.name',
174
+ TANSTACK_SERVER_FN_METHOD: 'tanstack.server_function.method',
175
+ TANSTACK_SERVER_FN_ARGS: 'tanstack.server_function.args',
176
+ TANSTACK_SERVER_FN_RESULT: 'tanstack.server_function.result',
177
+ TANSTACK_LOADER_ROUTE_ID: 'tanstack.loader.route_id',
178
+ TANSTACK_LOADER_TYPE: 'tanstack.loader.type',
179
+ TANSTACK_LOADER_PARAMS: 'tanstack.loader.params',
180
+ TANSTACK_MIDDLEWARE_NAME: 'tanstack.middleware.name',
181
+ TANSTACK_REQUEST_DURATION_MS: 'tanstack.request.duration_ms',
182
+ } as const;