autotel-tanstack 1.13.35 → 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.
- package/package.json +4 -5
- package/src/auto.test.ts +0 -114
- package/src/auto.ts +0 -60
- package/src/browser/context.ts +0 -88
- package/src/browser/debug-headers.ts +0 -19
- package/src/browser/error-reporting.ts +0 -64
- package/src/browser/handlers.ts +0 -23
- package/src/browser/index.ts +0 -66
- package/src/browser/loaders.ts +0 -62
- package/src/browser/metrics.ts +0 -86
- package/src/browser/middleware.ts +0 -77
- package/src/browser/server-functions.ts +0 -31
- package/src/browser/testing.ts +0 -130
- package/src/browser/types.ts +0 -100
- package/src/context.test.ts +0 -90
- package/src/context.ts +0 -145
- package/src/debug-headers.ts +0 -109
- package/src/env.ts +0 -56
- package/src/error-reporting.ts +0 -204
- package/src/handlers.ts +0 -306
- package/src/index.ts +0 -97
- package/src/instrument.test.ts +0 -131
- package/src/instrument.ts +0 -97
- package/src/loaders.test.ts +0 -123
- package/src/loaders.ts +0 -356
- package/src/metrics.ts +0 -184
- package/src/middleware.test.ts +0 -198
- package/src/middleware.ts +0 -435
- package/src/route-filter.test.ts +0 -28
- package/src/route-filter.ts +0 -40
- package/src/server-functions.test.ts +0 -86
- package/src/server-functions.ts +0 -188
- package/src/testing.test.ts +0 -205
- package/src/testing.ts +0 -430
- package/src/types.test.ts +0 -46
- package/src/types.ts +0 -182
package/src/testing.ts
DELETED
|
@@ -1,430 +0,0 @@
|
|
|
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
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Serialized span shape returned by the test-spans HTTP endpoint.
|
|
280
|
-
* Mirrors the fields the Playwright side needs for assertions.
|
|
281
|
-
*
|
|
282
|
-
* Defined as a `type` (not `interface`) so it is assignable to
|
|
283
|
-
* `Record<string, unknown>` in TypeScript 6+ strict mode.
|
|
284
|
-
*/
|
|
285
|
-
export type SerializedSpan = {
|
|
286
|
-
name: string;
|
|
287
|
-
spanId: string;
|
|
288
|
-
traceId: string;
|
|
289
|
-
parentSpanId?: string;
|
|
290
|
-
attributes?: Record<string, unknown>;
|
|
291
|
-
status: { code: number; message?: string };
|
|
292
|
-
durationMs: number;
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
interface TestSpanExporter {
|
|
296
|
-
getFinishedSpans(): Array<{
|
|
297
|
-
name: string;
|
|
298
|
-
spanContext(): { spanId: string; traceId: string };
|
|
299
|
-
parentSpanContext?: { spanId: string };
|
|
300
|
-
attributes: Record<string, unknown>;
|
|
301
|
-
status: { code: number; message?: string };
|
|
302
|
-
duration: [number, number];
|
|
303
|
-
}>;
|
|
304
|
-
reset(): void;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function getExporter(): TestSpanExporter | undefined {
|
|
308
|
-
return (globalThis as Record<string, unknown>).__testSpanExporter as
|
|
309
|
-
| TestSpanExporter
|
|
310
|
-
| undefined;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function e2eGuard(): Response | null {
|
|
314
|
-
if (process.env.E2E !== '1') {
|
|
315
|
-
return Response.json(
|
|
316
|
-
{ error: 'test-spans endpoint only available in E2E mode' },
|
|
317
|
-
{ status: 404 },
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function exporterGuard(): Response | null {
|
|
324
|
-
if (!getExporter()) {
|
|
325
|
-
return Response.json(
|
|
326
|
-
{ error: 'in-memory span exporter not initialized' },
|
|
327
|
-
{ status: 500 },
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Accepts either a raw `Request` (legacy) or a TanStack Router context
|
|
335
|
-
* object containing `{ request: Request }` (Router 1.168+).
|
|
336
|
-
*/
|
|
337
|
-
type HandlerInput = Request | { request: Request };
|
|
338
|
-
|
|
339
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
340
|
-
type CreateFileRoute = (path: string) => (options: any) => any;
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Creates GET and DELETE handlers for a test-spans HTTP endpoint.
|
|
344
|
-
*
|
|
345
|
-
* Use in a TanStack Start route to expose in-memory spans for Playwright assertions.
|
|
346
|
-
* Only works when E2E=1 (set in webServer command).
|
|
347
|
-
*
|
|
348
|
-
* Handlers accept either a raw `Request` or a TanStack Router context
|
|
349
|
-
* object `{ request: Request }`, so they work with both Router < 1.168
|
|
350
|
-
* and Router >= 1.168.
|
|
351
|
-
*
|
|
352
|
-
* @example
|
|
353
|
-
* ```typescript
|
|
354
|
-
* // src/routes/api/test-spans.ts
|
|
355
|
-
* import { createFileRoute } from "@tanstack/react-router";
|
|
356
|
-
* import { createTestSpansHandlers } from 'autotel-tanstack/testing';
|
|
357
|
-
* const { GET, DELETE } = createTestSpansHandlers();
|
|
358
|
-
* export const Route = createFileRoute('/api/test-spans')({
|
|
359
|
-
* server: { handlers: { GET, DELETE } },
|
|
360
|
-
* });
|
|
361
|
-
* ```
|
|
362
|
-
*/
|
|
363
|
-
/**
|
|
364
|
-
* Creates a pre-built TanStack Start route for the test-spans endpoint.
|
|
365
|
-
*
|
|
366
|
-
* Reduces E2E boilerplate to three lines. The handlers accept both
|
|
367
|
-
* `Request` and `{ request: Request }` so they work with any Router version.
|
|
368
|
-
*
|
|
369
|
-
* @param createFileRoute - Pass `createFileRoute` from `@tanstack/react-router`
|
|
370
|
-
* @param path - Route path (default: `/api/test-spans`)
|
|
371
|
-
*
|
|
372
|
-
* @example
|
|
373
|
-
* ```typescript
|
|
374
|
-
* // src/routes/api/test-spans.ts
|
|
375
|
-
* import { createFileRoute } from "@tanstack/react-router";
|
|
376
|
-
* import { createTestSpansRoute } from "autotel-tanstack/testing";
|
|
377
|
-
*
|
|
378
|
-
* export const Route = createTestSpansRoute(createFileRoute);
|
|
379
|
-
* ```
|
|
380
|
-
*/
|
|
381
|
-
export function createTestSpansRoute(
|
|
382
|
-
createFileRoute: CreateFileRoute,
|
|
383
|
-
path = '/api/test-spans',
|
|
384
|
-
) {
|
|
385
|
-
const { GET, DELETE } = createTestSpansHandlers();
|
|
386
|
-
return createFileRoute(path)({
|
|
387
|
-
server: {
|
|
388
|
-
handlers: { GET, DELETE },
|
|
389
|
-
},
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
export function createTestSpansHandlers(): {
|
|
394
|
-
GET: (input: HandlerInput) => Response;
|
|
395
|
-
DELETE: (input: HandlerInput) => Response;
|
|
396
|
-
} {
|
|
397
|
-
return {
|
|
398
|
-
GET(_input: HandlerInput): Response {
|
|
399
|
-
const guard = e2eGuard() ?? exporterGuard();
|
|
400
|
-
if (guard) return guard;
|
|
401
|
-
|
|
402
|
-
const spans: SerializedSpan[] = getExporter()!
|
|
403
|
-
.getFinishedSpans()
|
|
404
|
-
.map((span) => {
|
|
405
|
-
const { spanId, traceId } = span.spanContext();
|
|
406
|
-
const serialized: SerializedSpan = {
|
|
407
|
-
name: span.name,
|
|
408
|
-
spanId,
|
|
409
|
-
traceId,
|
|
410
|
-
attributes: span.attributes,
|
|
411
|
-
status: span.status,
|
|
412
|
-
durationMs: span.duration[0] * 1000 + span.duration[1] / 1_000_000,
|
|
413
|
-
};
|
|
414
|
-
if (span.parentSpanContext?.spanId) {
|
|
415
|
-
serialized.parentSpanId = span.parentSpanContext.spanId;
|
|
416
|
-
}
|
|
417
|
-
return serialized;
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
return Response.json({ spans });
|
|
421
|
-
},
|
|
422
|
-
|
|
423
|
-
DELETE(_input: HandlerInput): Response {
|
|
424
|
-
const guard = e2eGuard() ?? exporterGuard();
|
|
425
|
-
if (guard) return guard;
|
|
426
|
-
getExporter()!.reset();
|
|
427
|
-
return Response.json({ ok: true });
|
|
428
|
-
},
|
|
429
|
-
};
|
|
430
|
-
}
|
package/src/types.test.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
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;
|