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/testing.js
CHANGED
|
@@ -1,169 +1,234 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { InMemorySpanExporter } from
|
|
3
|
-
import { SimpleSpanProcessor } from
|
|
4
|
-
import { init } from 'autotel';
|
|
1
|
+
import { init } from "autotel";
|
|
2
|
+
import { InMemorySpanExporter } from "autotel/exporters";
|
|
3
|
+
import { SimpleSpanProcessor } from "autotel/processors";
|
|
5
4
|
|
|
5
|
+
//#region src/testing.ts
|
|
6
|
+
/**
|
|
7
|
+
* Create a test harness for TanStack instrumentation testing
|
|
8
|
+
*
|
|
9
|
+
* This sets up autotel with an in-memory exporter for testing.
|
|
10
|
+
* Call this in your test setup to capture and assert on spans.
|
|
11
|
+
*
|
|
12
|
+
* @returns Test harness with assertion helpers
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { describe, it, beforeEach } from 'vitest';
|
|
17
|
+
* import { createTestHarness } from 'autotel-tanstack/testing';
|
|
18
|
+
*
|
|
19
|
+
* describe('MyServerFunction', () => {
|
|
20
|
+
* let harness: ReturnType<typeof createTestHarness>;
|
|
21
|
+
*
|
|
22
|
+
* beforeEach(() => {
|
|
23
|
+
* harness = createTestHarness();
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* afterEach(() => {
|
|
27
|
+
* harness.reset();
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* it('should trace the server function', async () => {
|
|
31
|
+
* await myServerFunction({ id: '123' });
|
|
32
|
+
*
|
|
33
|
+
* harness.assertServerFnTraced('myServerFunction');
|
|
34
|
+
* harness.assertSpanHasAttribute(
|
|
35
|
+
* /tanstack\.serverFn/,
|
|
36
|
+
* 'tanstack.server_function.name',
|
|
37
|
+
* 'myServerFunction'
|
|
38
|
+
* );
|
|
39
|
+
* });
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
6
43
|
function createTestHarness() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
assertSpanExists(`${method} ${path}`);
|
|
66
|
-
}
|
|
67
|
-
return {
|
|
68
|
-
exporter,
|
|
69
|
-
getSpans,
|
|
70
|
-
getSpansByName,
|
|
71
|
-
getSpansByType,
|
|
72
|
-
reset,
|
|
73
|
-
assertSpanExists,
|
|
74
|
-
assertSpanHasAttribute,
|
|
75
|
-
assertServerFnTraced,
|
|
76
|
-
assertLoaderTraced,
|
|
77
|
-
assertBeforeLoadTraced,
|
|
78
|
-
assertRequestTraced
|
|
79
|
-
};
|
|
44
|
+
const exporter = new InMemorySpanExporter();
|
|
45
|
+
init({
|
|
46
|
+
service: "test",
|
|
47
|
+
spanProcessors: [new SimpleSpanProcessor(exporter)]
|
|
48
|
+
});
|
|
49
|
+
function getSpans() {
|
|
50
|
+
return exporter.getFinishedSpans();
|
|
51
|
+
}
|
|
52
|
+
function getSpansByName(name) {
|
|
53
|
+
const spans = getSpans();
|
|
54
|
+
if (typeof name === "string") return spans.filter((s) => s.name === name);
|
|
55
|
+
return spans.filter((s) => name.test(s.name));
|
|
56
|
+
}
|
|
57
|
+
function getSpansByType(type) {
|
|
58
|
+
return getSpans().filter((s) => s.attributes["tanstack.type"] === type);
|
|
59
|
+
}
|
|
60
|
+
function reset() {
|
|
61
|
+
exporter.reset();
|
|
62
|
+
}
|
|
63
|
+
function assertSpanExists(name) {
|
|
64
|
+
if (getSpansByName(name).length === 0) {
|
|
65
|
+
const allSpanNames = getSpans().map((s) => s.name);
|
|
66
|
+
throw new Error(`Expected span "${name}" to exist. Found spans: ${JSON.stringify(allSpanNames)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function assertSpanHasAttribute(name, attr, value) {
|
|
70
|
+
const spans = getSpansByName(name);
|
|
71
|
+
if (spans.length === 0) throw new Error(`Span "${name}" not found`);
|
|
72
|
+
const span = spans[0];
|
|
73
|
+
const attrValue = span.attributes[attr];
|
|
74
|
+
if (attrValue === void 0) throw new Error(`Attribute "${attr}" not found on span "${span.name}". Available attributes: ${JSON.stringify(Object.keys(span.attributes))}`);
|
|
75
|
+
if (value !== void 0 && attrValue !== value) throw new Error(`Expected attribute "${attr}" to be "${value}", got "${attrValue}"`);
|
|
76
|
+
}
|
|
77
|
+
function assertServerFnTraced(name) {
|
|
78
|
+
assertSpanExists(`tanstack.serverFn.${name}`);
|
|
79
|
+
}
|
|
80
|
+
function assertLoaderTraced(routeId) {
|
|
81
|
+
assertSpanExists(`tanstack.loader.${routeId}`);
|
|
82
|
+
}
|
|
83
|
+
function assertBeforeLoadTraced(routeId) {
|
|
84
|
+
assertSpanExists(`tanstack.beforeLoad.${routeId}`);
|
|
85
|
+
}
|
|
86
|
+
function assertRequestTraced(method, path) {
|
|
87
|
+
assertSpanExists(`${method} ${path}`);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
exporter,
|
|
91
|
+
getSpans,
|
|
92
|
+
getSpansByName,
|
|
93
|
+
getSpansByType,
|
|
94
|
+
reset,
|
|
95
|
+
assertSpanExists,
|
|
96
|
+
assertSpanHasAttribute,
|
|
97
|
+
assertServerFnTraced,
|
|
98
|
+
assertLoaderTraced,
|
|
99
|
+
assertBeforeLoadTraced,
|
|
100
|
+
assertRequestTraced
|
|
101
|
+
};
|
|
80
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Mock request factory for testing
|
|
105
|
+
*
|
|
106
|
+
* Creates mock Request objects for testing middleware and handlers.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const request = createMockRequest('GET', '/api/users', {
|
|
111
|
+
* headers: { 'x-request-id': 'test-123' },
|
|
112
|
+
* });
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
81
115
|
function createMockRequest(method, path, options = {}) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
body: options.body
|
|
90
|
-
});
|
|
116
|
+
const headers = new Headers(options.headers);
|
|
117
|
+
if (options.traceparent) headers.set("traceparent", options.traceparent);
|
|
118
|
+
return new Request(`http://localhost${path}`, {
|
|
119
|
+
method,
|
|
120
|
+
headers,
|
|
121
|
+
body: options.body
|
|
122
|
+
});
|
|
91
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Generate a valid W3C traceparent header for testing
|
|
126
|
+
*
|
|
127
|
+
* @param traceId - Optional 32-char hex trace ID
|
|
128
|
+
* @param spanId - Optional 16-char hex span ID
|
|
129
|
+
* @returns Valid traceparent header string
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* const traceparent = generateTraceparent();
|
|
134
|
+
* const request = createMockRequest('GET', '/api/users', { traceparent });
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
92
137
|
function generateTraceparent(traceId, spanId) {
|
|
93
|
-
|
|
94
|
-
const trace = traceId || generateHex(32);
|
|
95
|
-
const span = spanId || generateHex(16);
|
|
96
|
-
const flags = "01";
|
|
97
|
-
return `${version}-${trace}-${span}-${flags}`;
|
|
138
|
+
return `00-${traceId || generateHex(32)}-${spanId || generateHex(16)}-01`;
|
|
98
139
|
}
|
|
99
140
|
function generateHex(length) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
return result;
|
|
141
|
+
const chars = "0123456789abcdef";
|
|
142
|
+
let result = "";
|
|
143
|
+
for (let i = 0; i < length; i++) result += chars[Math.floor(Math.random() * 16)];
|
|
144
|
+
return result;
|
|
106
145
|
}
|
|
107
146
|
function getExporter() {
|
|
108
|
-
|
|
147
|
+
return globalThis.__testSpanExporter;
|
|
109
148
|
}
|
|
110
149
|
function e2eGuard() {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
{ error: "test-spans endpoint only available in E2E mode" },
|
|
114
|
-
{ status: 404 }
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
return null;
|
|
150
|
+
if (process.env.E2E !== "1") return Response.json({ error: "test-spans endpoint only available in E2E mode" }, { status: 404 });
|
|
151
|
+
return null;
|
|
118
152
|
}
|
|
119
153
|
function exporterGuard() {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
{ error: "in-memory span exporter not initialized" },
|
|
123
|
-
{ status: 500 }
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
return null;
|
|
154
|
+
if (!getExporter()) return Response.json({ error: "in-memory span exporter not initialized" }, { status: 500 });
|
|
155
|
+
return null;
|
|
127
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Creates GET and DELETE handlers for a test-spans HTTP endpoint.
|
|
159
|
+
*
|
|
160
|
+
* Use in a TanStack Start route to expose in-memory spans for Playwright assertions.
|
|
161
|
+
* Only works when E2E=1 (set in webServer command).
|
|
162
|
+
*
|
|
163
|
+
* Handlers accept either a raw `Request` or a TanStack Router context
|
|
164
|
+
* object `{ request: Request }`, so they work with both Router < 1.168
|
|
165
|
+
* and Router >= 1.168.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* // src/routes/api/test-spans.ts
|
|
170
|
+
* import { createFileRoute } from "@tanstack/react-router";
|
|
171
|
+
* import { createTestSpansHandlers } from 'autotel-tanstack/testing';
|
|
172
|
+
* const { GET, DELETE } = createTestSpansHandlers();
|
|
173
|
+
* export const Route = createFileRoute('/api/test-spans')({
|
|
174
|
+
* server: { handlers: { GET, DELETE } },
|
|
175
|
+
* });
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
/**
|
|
179
|
+
* Creates a pre-built TanStack Start route for the test-spans endpoint.
|
|
180
|
+
*
|
|
181
|
+
* Reduces E2E boilerplate to three lines. The handlers accept both
|
|
182
|
+
* `Request` and `{ request: Request }` so they work with any Router version.
|
|
183
|
+
*
|
|
184
|
+
* @param createFileRoute - Pass `createFileRoute` from `@tanstack/react-router`
|
|
185
|
+
* @param path - Route path (default: `/api/test-spans`)
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* // src/routes/api/test-spans.ts
|
|
190
|
+
* import { createFileRoute } from "@tanstack/react-router";
|
|
191
|
+
* import { createTestSpansRoute } from "autotel-tanstack/testing";
|
|
192
|
+
*
|
|
193
|
+
* export const Route = createTestSpansRoute(createFileRoute);
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
128
196
|
function createTestSpansRoute(createFileRoute, path = "/api/test-spans") {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
});
|
|
197
|
+
const { GET, DELETE } = createTestSpansHandlers();
|
|
198
|
+
return createFileRoute(path)({ server: { handlers: {
|
|
199
|
+
GET,
|
|
200
|
+
DELETE
|
|
201
|
+
} } });
|
|
135
202
|
}
|
|
136
203
|
function createTestSpansHandlers() {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
};
|
|
204
|
+
return {
|
|
205
|
+
GET(_input) {
|
|
206
|
+
const guard = e2eGuard() ?? exporterGuard();
|
|
207
|
+
if (guard) return guard;
|
|
208
|
+
const spans = getExporter().getFinishedSpans().map((span) => {
|
|
209
|
+
const { spanId, traceId } = span.spanContext();
|
|
210
|
+
const serialized = {
|
|
211
|
+
name: span.name,
|
|
212
|
+
spanId,
|
|
213
|
+
traceId,
|
|
214
|
+
attributes: span.attributes,
|
|
215
|
+
status: span.status,
|
|
216
|
+
durationMs: span.duration[0] * 1e3 + span.duration[1] / 1e6
|
|
217
|
+
};
|
|
218
|
+
if (span.parentSpanContext?.spanId) serialized.parentSpanId = span.parentSpanContext.spanId;
|
|
219
|
+
return serialized;
|
|
220
|
+
});
|
|
221
|
+
return Response.json({ spans });
|
|
222
|
+
},
|
|
223
|
+
DELETE(_input) {
|
|
224
|
+
const guard = e2eGuard() ?? exporterGuard();
|
|
225
|
+
if (guard) return guard;
|
|
226
|
+
getExporter().reset();
|
|
227
|
+
return Response.json({ ok: true });
|
|
228
|
+
}
|
|
229
|
+
};
|
|
165
230
|
}
|
|
166
231
|
|
|
232
|
+
//#endregion
|
|
167
233
|
export { createMockRequest, createTestHarness, createTestSpansHandlers, createTestSpansRoute, generateTraceparent };
|
|
168
|
-
//# sourceMappingURL=testing.js.map
|
|
169
234
|
//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["import { type ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { InMemorySpanExporter } from 'autotel/exporters';\nimport { SimpleSpanProcessor } from 'autotel/processors';\nimport { init } from 'autotel';\n\n/**\n * Test harness for TanStack instrumentation testing\n *\n * Provides utilities for testing TanStack Start applications\n * with autotel-tanstack instrumentation.\n */\nexport interface TestHarness {\n /**\n * The in-memory span exporter\n */\n exporter: {\n getFinishedSpans(): ReadableSpan[];\n reset(): void;\n };\n\n /**\n * Get all finished spans\n */\n getSpans(): ReadableSpan[];\n\n /**\n * Get spans by name (exact match or regex)\n */\n getSpansByName(name: string | RegExp): ReadableSpan[];\n\n /**\n * Get spans by TanStack type\n */\n getSpansByType(\n type: 'request' | 'serverFn' | 'loader' | 'beforeLoad',\n ): ReadableSpan[];\n\n /**\n * Reset collected spans\n */\n reset(): void;\n\n /**\n * Assert a span exists\n */\n assertSpanExists(name: string | RegExp): void;\n\n /**\n * Assert a span has a specific attribute\n */\n assertSpanHasAttribute(\n name: string | RegExp,\n attr: string,\n value?: unknown,\n ): void;\n\n /**\n * Assert a server function was traced\n */\n assertServerFnTraced(name: string): void;\n\n /**\n * Assert a loader was traced\n */\n assertLoaderTraced(routeId: string): void;\n\n /**\n * Assert a beforeLoad was traced\n */\n assertBeforeLoadTraced(routeId: string): void;\n\n /**\n * Assert an HTTP request was traced\n */\n assertRequestTraced(method: string, path: string): void;\n}\n\n/**\n * Create a test harness for TanStack instrumentation testing\n *\n * This sets up autotel with an in-memory exporter for testing.\n * Call this in your test setup to capture and assert on spans.\n *\n * @returns Test harness with assertion helpers\n *\n * @example\n * ```typescript\n * import { describe, it, beforeEach } from 'vitest';\n * import { createTestHarness } from 'autotel-tanstack/testing';\n *\n * describe('MyServerFunction', () => {\n * let harness: ReturnType<typeof createTestHarness>;\n *\n * beforeEach(() => {\n * harness = createTestHarness();\n * });\n *\n * afterEach(() => {\n * harness.reset();\n * });\n *\n * it('should trace the server function', async () => {\n * await myServerFunction({ id: '123' });\n *\n * harness.assertServerFnTraced('myServerFunction');\n * harness.assertSpanHasAttribute(\n * /tanstack\\.serverFn/,\n * 'tanstack.server_function.name',\n * 'myServerFunction'\n * );\n * });\n * });\n * ```\n */\nexport function createTestHarness(): TestHarness {\n const exporter = new InMemorySpanExporter();\n\n init({\n service: 'test',\n spanProcessors: [new SimpleSpanProcessor(exporter)],\n });\n\n function getSpans(): ReadableSpan[] {\n return exporter.getFinishedSpans() as ReadableSpan[];\n }\n\n function getSpansByName(name: string | RegExp): ReadableSpan[] {\n const spans = getSpans();\n if (typeof name === 'string') {\n return spans.filter((s) => s.name === name);\n }\n return spans.filter((s) => name.test(s.name));\n }\n\n function getSpansByType(\n type: 'request' | 'serverFn' | 'loader' | 'beforeLoad',\n ): ReadableSpan[] {\n return getSpans().filter((s) => s.attributes['tanstack.type'] === type);\n }\n\n function reset(): void {\n exporter.reset();\n }\n\n function assertSpanExists(name: string | RegExp): void {\n const spans = getSpansByName(name);\n if (spans.length === 0) {\n const allSpanNames = getSpans().map((s) => s.name);\n throw new Error(\n `Expected span \"${name}\" to exist. Found spans: ${JSON.stringify(allSpanNames)}`,\n );\n }\n }\n\n function assertSpanHasAttribute(\n name: string | RegExp,\n attr: string,\n value?: unknown,\n ): void {\n const spans = getSpansByName(name);\n if (spans.length === 0) {\n throw new Error(`Span \"${name}\" not found`);\n }\n\n const span = spans[0];\n const attrValue = span.attributes[attr];\n\n if (attrValue === undefined) {\n throw new Error(\n `Attribute \"${attr}\" not found on span \"${span.name}\". ` +\n `Available attributes: ${JSON.stringify(Object.keys(span.attributes))}`,\n );\n }\n\n if (value !== undefined && attrValue !== value) {\n throw new Error(\n `Expected attribute \"${attr}\" to be \"${value}\", got \"${attrValue}\"`,\n );\n }\n }\n\n function assertServerFnTraced(name: string): void {\n assertSpanExists(`tanstack.serverFn.${name}`);\n }\n\n function assertLoaderTraced(routeId: string): void {\n assertSpanExists(`tanstack.loader.${routeId}`);\n }\n\n function assertBeforeLoadTraced(routeId: string): void {\n assertSpanExists(`tanstack.beforeLoad.${routeId}`);\n }\n\n function assertRequestTraced(method: string, path: string): void {\n assertSpanExists(`${method} ${path}`);\n }\n\n return {\n exporter,\n getSpans,\n getSpansByName,\n getSpansByType,\n reset,\n assertSpanExists,\n assertSpanHasAttribute,\n assertServerFnTraced,\n assertLoaderTraced,\n assertBeforeLoadTraced,\n assertRequestTraced,\n };\n}\n\n/**\n * Mock request factory for testing\n *\n * Creates mock Request objects for testing middleware and handlers.\n *\n * @example\n * ```typescript\n * const request = createMockRequest('GET', '/api/users', {\n * headers: { 'x-request-id': 'test-123' },\n * });\n * ```\n */\nexport function createMockRequest(\n method: string,\n path: string,\n options: {\n headers?: Record<string, string>;\n body?: string;\n traceparent?: string;\n } = {},\n): Request {\n const headers = new Headers(options.headers);\n\n if (options.traceparent) {\n headers.set('traceparent', options.traceparent);\n }\n\n return new Request(`http://localhost${path}`, {\n method,\n headers,\n body: options.body,\n });\n}\n\n/**\n * Generate a valid W3C traceparent header for testing\n *\n * @param traceId - Optional 32-char hex trace ID\n * @param spanId - Optional 16-char hex span ID\n * @returns Valid traceparent header string\n *\n * @example\n * ```typescript\n * const traceparent = generateTraceparent();\n * const request = createMockRequest('GET', '/api/users', { traceparent });\n * ```\n */\nexport function generateTraceparent(traceId?: string, spanId?: string): string {\n const version = '00';\n const trace = traceId || generateHex(32);\n const span = spanId || generateHex(16);\n const flags = '01'; // Sampled\n\n return `${version}-${trace}-${span}-${flags}`;\n}\n\nfunction generateHex(length: number): string {\n const chars = '0123456789abcdef';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars[Math.floor(Math.random() * 16)];\n }\n return result;\n}\n\n/**\n * Serialized span shape returned by the test-spans HTTP endpoint.\n * Mirrors the fields the Playwright side needs for assertions.\n *\n * Defined as a `type` (not `interface`) so it is assignable to\n * `Record<string, unknown>` in TypeScript 6+ strict mode.\n */\nexport type SerializedSpan = {\n name: string;\n spanId: string;\n traceId: string;\n parentSpanId?: string;\n attributes?: Record<string, unknown>;\n status: { code: number; message?: string };\n durationMs: number;\n};\n\ninterface TestSpanExporter {\n getFinishedSpans(): Array<{\n name: string;\n spanContext(): { spanId: string; traceId: string };\n parentSpanContext?: { spanId: string };\n attributes: Record<string, unknown>;\n status: { code: number; message?: string };\n duration: [number, number];\n }>;\n reset(): void;\n}\n\nfunction getExporter(): TestSpanExporter | undefined {\n return (globalThis as Record<string, unknown>).__testSpanExporter as\n | TestSpanExporter\n | undefined;\n}\n\nfunction e2eGuard(): Response | null {\n if (process.env.E2E !== '1') {\n return Response.json(\n { error: 'test-spans endpoint only available in E2E mode' },\n { status: 404 },\n );\n }\n return null;\n}\n\nfunction exporterGuard(): Response | null {\n if (!getExporter()) {\n return Response.json(\n { error: 'in-memory span exporter not initialized' },\n { status: 500 },\n );\n }\n return null;\n}\n\n/**\n * Accepts either a raw `Request` (legacy) or a TanStack Router context\n * object containing `{ request: Request }` (Router 1.168+).\n */\ntype HandlerInput = Request | { request: Request };\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype CreateFileRoute = (path: string) => (options: any) => any;\n\n/**\n * Creates GET and DELETE handlers for a test-spans HTTP endpoint.\n *\n * Use in a TanStack Start route to expose in-memory spans for Playwright assertions.\n * Only works when E2E=1 (set in webServer command).\n *\n * Handlers accept either a raw `Request` or a TanStack Router context\n * object `{ request: Request }`, so they work with both Router < 1.168\n * and Router >= 1.168.\n *\n * @example\n * ```typescript\n * // src/routes/api/test-spans.ts\n * import { createFileRoute } from \"@tanstack/react-router\";\n * import { createTestSpansHandlers } from 'autotel-tanstack/testing';\n * const { GET, DELETE } = createTestSpansHandlers();\n * export const Route = createFileRoute('/api/test-spans')({\n * server: { handlers: { GET, DELETE } },\n * });\n * ```\n */\n/**\n * Creates a pre-built TanStack Start route for the test-spans endpoint.\n *\n * Reduces E2E boilerplate to three lines. The handlers accept both\n * `Request` and `{ request: Request }` so they work with any Router version.\n *\n * @param createFileRoute - Pass `createFileRoute` from `@tanstack/react-router`\n * @param path - Route path (default: `/api/test-spans`)\n *\n * @example\n * ```typescript\n * // src/routes/api/test-spans.ts\n * import { createFileRoute } from \"@tanstack/react-router\";\n * import { createTestSpansRoute } from \"autotel-tanstack/testing\";\n *\n * export const Route = createTestSpansRoute(createFileRoute);\n * ```\n */\nexport function createTestSpansRoute(\n createFileRoute: CreateFileRoute,\n path = '/api/test-spans',\n) {\n const { GET, DELETE } = createTestSpansHandlers();\n return createFileRoute(path)({\n server: {\n handlers: { GET, DELETE },\n },\n });\n}\n\nexport function createTestSpansHandlers(): {\n GET: (input: HandlerInput) => Response;\n DELETE: (input: HandlerInput) => Response;\n} {\n return {\n GET(_input: HandlerInput): Response {\n const guard = e2eGuard() ?? exporterGuard();\n if (guard) return guard;\n\n const spans: SerializedSpan[] = getExporter()!\n .getFinishedSpans()\n .map((span) => {\n const { spanId, traceId } = span.spanContext();\n const serialized: SerializedSpan = {\n name: span.name,\n spanId,\n traceId,\n attributes: span.attributes,\n status: span.status,\n durationMs: span.duration[0] * 1000 + span.duration[1] / 1_000_000,\n };\n if (span.parentSpanContext?.spanId) {\n serialized.parentSpanId = span.parentSpanContext.spanId;\n }\n return serialized;\n });\n\n return Response.json({ spans });\n },\n\n DELETE(_input: HandlerInput): Response {\n const guard = e2eGuard() ?? exporterGuard();\n if (guard) return guard;\n getExporter()!.reset();\n return Response.json({ ok: true });\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["import { type ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { InMemorySpanExporter } from 'autotel/exporters';\nimport { SimpleSpanProcessor } from 'autotel/processors';\nimport { init } from 'autotel';\n\n/**\n * Test harness for TanStack instrumentation testing\n *\n * Provides utilities for testing TanStack Start applications\n * with autotel-tanstack instrumentation.\n */\nexport interface TestHarness {\n /**\n * The in-memory span exporter\n */\n exporter: {\n getFinishedSpans(): ReadableSpan[];\n reset(): void;\n };\n\n /**\n * Get all finished spans\n */\n getSpans(): ReadableSpan[];\n\n /**\n * Get spans by name (exact match or regex)\n */\n getSpansByName(name: string | RegExp): ReadableSpan[];\n\n /**\n * Get spans by TanStack type\n */\n getSpansByType(\n type: 'request' | 'serverFn' | 'loader' | 'beforeLoad',\n ): ReadableSpan[];\n\n /**\n * Reset collected spans\n */\n reset(): void;\n\n /**\n * Assert a span exists\n */\n assertSpanExists(name: string | RegExp): void;\n\n /**\n * Assert a span has a specific attribute\n */\n assertSpanHasAttribute(\n name: string | RegExp,\n attr: string,\n value?: unknown,\n ): void;\n\n /**\n * Assert a server function was traced\n */\n assertServerFnTraced(name: string): void;\n\n /**\n * Assert a loader was traced\n */\n assertLoaderTraced(routeId: string): void;\n\n /**\n * Assert a beforeLoad was traced\n */\n assertBeforeLoadTraced(routeId: string): void;\n\n /**\n * Assert an HTTP request was traced\n */\n assertRequestTraced(method: string, path: string): void;\n}\n\n/**\n * Create a test harness for TanStack instrumentation testing\n *\n * This sets up autotel with an in-memory exporter for testing.\n * Call this in your test setup to capture and assert on spans.\n *\n * @returns Test harness with assertion helpers\n *\n * @example\n * ```typescript\n * import { describe, it, beforeEach } from 'vitest';\n * import { createTestHarness } from 'autotel-tanstack/testing';\n *\n * describe('MyServerFunction', () => {\n * let harness: ReturnType<typeof createTestHarness>;\n *\n * beforeEach(() => {\n * harness = createTestHarness();\n * });\n *\n * afterEach(() => {\n * harness.reset();\n * });\n *\n * it('should trace the server function', async () => {\n * await myServerFunction({ id: '123' });\n *\n * harness.assertServerFnTraced('myServerFunction');\n * harness.assertSpanHasAttribute(\n * /tanstack\\.serverFn/,\n * 'tanstack.server_function.name',\n * 'myServerFunction'\n * );\n * });\n * });\n * ```\n */\nexport function createTestHarness(): TestHarness {\n const exporter = new InMemorySpanExporter();\n\n init({\n service: 'test',\n spanProcessors: [new SimpleSpanProcessor(exporter)],\n });\n\n function getSpans(): ReadableSpan[] {\n return exporter.getFinishedSpans() as ReadableSpan[];\n }\n\n function getSpansByName(name: string | RegExp): ReadableSpan[] {\n const spans = getSpans();\n if (typeof name === 'string') {\n return spans.filter((s) => s.name === name);\n }\n return spans.filter((s) => name.test(s.name));\n }\n\n function getSpansByType(\n type: 'request' | 'serverFn' | 'loader' | 'beforeLoad',\n ): ReadableSpan[] {\n return getSpans().filter((s) => s.attributes['tanstack.type'] === type);\n }\n\n function reset(): void {\n exporter.reset();\n }\n\n function assertSpanExists(name: string | RegExp): void {\n const spans = getSpansByName(name);\n if (spans.length === 0) {\n const allSpanNames = getSpans().map((s) => s.name);\n throw new Error(\n `Expected span \"${name}\" to exist. Found spans: ${JSON.stringify(allSpanNames)}`,\n );\n }\n }\n\n function assertSpanHasAttribute(\n name: string | RegExp,\n attr: string,\n value?: unknown,\n ): void {\n const spans = getSpansByName(name);\n if (spans.length === 0) {\n throw new Error(`Span \"${name}\" not found`);\n }\n\n const span = spans[0];\n const attrValue = span.attributes[attr];\n\n if (attrValue === undefined) {\n throw new Error(\n `Attribute \"${attr}\" not found on span \"${span.name}\". ` +\n `Available attributes: ${JSON.stringify(Object.keys(span.attributes))}`,\n );\n }\n\n if (value !== undefined && attrValue !== value) {\n throw new Error(\n `Expected attribute \"${attr}\" to be \"${value}\", got \"${attrValue}\"`,\n );\n }\n }\n\n function assertServerFnTraced(name: string): void {\n assertSpanExists(`tanstack.serverFn.${name}`);\n }\n\n function assertLoaderTraced(routeId: string): void {\n assertSpanExists(`tanstack.loader.${routeId}`);\n }\n\n function assertBeforeLoadTraced(routeId: string): void {\n assertSpanExists(`tanstack.beforeLoad.${routeId}`);\n }\n\n function assertRequestTraced(method: string, path: string): void {\n assertSpanExists(`${method} ${path}`);\n }\n\n return {\n exporter,\n getSpans,\n getSpansByName,\n getSpansByType,\n reset,\n assertSpanExists,\n assertSpanHasAttribute,\n assertServerFnTraced,\n assertLoaderTraced,\n assertBeforeLoadTraced,\n assertRequestTraced,\n };\n}\n\n/**\n * Mock request factory for testing\n *\n * Creates mock Request objects for testing middleware and handlers.\n *\n * @example\n * ```typescript\n * const request = createMockRequest('GET', '/api/users', {\n * headers: { 'x-request-id': 'test-123' },\n * });\n * ```\n */\nexport function createMockRequest(\n method: string,\n path: string,\n options: {\n headers?: Record<string, string>;\n body?: string;\n traceparent?: string;\n } = {},\n): Request {\n const headers = new Headers(options.headers);\n\n if (options.traceparent) {\n headers.set('traceparent', options.traceparent);\n }\n\n return new Request(`http://localhost${path}`, {\n method,\n headers,\n body: options.body,\n });\n}\n\n/**\n * Generate a valid W3C traceparent header for testing\n *\n * @param traceId - Optional 32-char hex trace ID\n * @param spanId - Optional 16-char hex span ID\n * @returns Valid traceparent header string\n *\n * @example\n * ```typescript\n * const traceparent = generateTraceparent();\n * const request = createMockRequest('GET', '/api/users', { traceparent });\n * ```\n */\nexport function generateTraceparent(traceId?: string, spanId?: string): string {\n const version = '00';\n const trace = traceId || generateHex(32);\n const span = spanId || generateHex(16);\n const flags = '01'; // Sampled\n\n return `${version}-${trace}-${span}-${flags}`;\n}\n\nfunction generateHex(length: number): string {\n const chars = '0123456789abcdef';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars[Math.floor(Math.random() * 16)];\n }\n return result;\n}\n\n/**\n * Serialized span shape returned by the test-spans HTTP endpoint.\n * Mirrors the fields the Playwright side needs for assertions.\n *\n * Defined as a `type` (not `interface`) so it is assignable to\n * `Record<string, unknown>` in TypeScript 6+ strict mode.\n */\nexport type SerializedSpan = {\n name: string;\n spanId: string;\n traceId: string;\n parentSpanId?: string;\n attributes?: Record<string, unknown>;\n status: { code: number; message?: string };\n durationMs: number;\n};\n\ninterface TestSpanExporter {\n getFinishedSpans(): Array<{\n name: string;\n spanContext(): { spanId: string; traceId: string };\n parentSpanContext?: { spanId: string };\n attributes: Record<string, unknown>;\n status: { code: number; message?: string };\n duration: [number, number];\n }>;\n reset(): void;\n}\n\nfunction getExporter(): TestSpanExporter | undefined {\n return (globalThis as Record<string, unknown>).__testSpanExporter as\n | TestSpanExporter\n | undefined;\n}\n\nfunction e2eGuard(): Response | null {\n if (process.env.E2E !== '1') {\n return Response.json(\n { error: 'test-spans endpoint only available in E2E mode' },\n { status: 404 },\n );\n }\n return null;\n}\n\nfunction exporterGuard(): Response | null {\n if (!getExporter()) {\n return Response.json(\n { error: 'in-memory span exporter not initialized' },\n { status: 500 },\n );\n }\n return null;\n}\n\n/**\n * Accepts either a raw `Request` (legacy) or a TanStack Router context\n * object containing `{ request: Request }` (Router 1.168+).\n */\ntype HandlerInput = Request | { request: Request };\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype CreateFileRoute = (path: string) => (options: any) => any;\n\n/**\n * Creates GET and DELETE handlers for a test-spans HTTP endpoint.\n *\n * Use in a TanStack Start route to expose in-memory spans for Playwright assertions.\n * Only works when E2E=1 (set in webServer command).\n *\n * Handlers accept either a raw `Request` or a TanStack Router context\n * object `{ request: Request }`, so they work with both Router < 1.168\n * and Router >= 1.168.\n *\n * @example\n * ```typescript\n * // src/routes/api/test-spans.ts\n * import { createFileRoute } from \"@tanstack/react-router\";\n * import { createTestSpansHandlers } from 'autotel-tanstack/testing';\n * const { GET, DELETE } = createTestSpansHandlers();\n * export const Route = createFileRoute('/api/test-spans')({\n * server: { handlers: { GET, DELETE } },\n * });\n * ```\n */\n/**\n * Creates a pre-built TanStack Start route for the test-spans endpoint.\n *\n * Reduces E2E boilerplate to three lines. The handlers accept both\n * `Request` and `{ request: Request }` so they work with any Router version.\n *\n * @param createFileRoute - Pass `createFileRoute` from `@tanstack/react-router`\n * @param path - Route path (default: `/api/test-spans`)\n *\n * @example\n * ```typescript\n * // src/routes/api/test-spans.ts\n * import { createFileRoute } from \"@tanstack/react-router\";\n * import { createTestSpansRoute } from \"autotel-tanstack/testing\";\n *\n * export const Route = createTestSpansRoute(createFileRoute);\n * ```\n */\nexport function createTestSpansRoute(\n createFileRoute: CreateFileRoute,\n path = '/api/test-spans',\n) {\n const { GET, DELETE } = createTestSpansHandlers();\n return createFileRoute(path)({\n server: {\n handlers: { GET, DELETE },\n },\n });\n}\n\nexport function createTestSpansHandlers(): {\n GET: (input: HandlerInput) => Response;\n DELETE: (input: HandlerInput) => Response;\n} {\n return {\n GET(_input: HandlerInput): Response {\n const guard = e2eGuard() ?? exporterGuard();\n if (guard) return guard;\n\n const spans: SerializedSpan[] = getExporter()!\n .getFinishedSpans()\n .map((span) => {\n const { spanId, traceId } = span.spanContext();\n const serialized: SerializedSpan = {\n name: span.name,\n spanId,\n traceId,\n attributes: span.attributes,\n status: span.status,\n durationMs: span.duration[0] * 1000 + span.duration[1] / 1_000_000,\n };\n if (span.parentSpanContext?.spanId) {\n serialized.parentSpanId = span.parentSpanContext.spanId;\n }\n return serialized;\n });\n\n return Response.json({ spans });\n },\n\n DELETE(_input: HandlerInput): Response {\n const guard = e2eGuard() ?? exporterGuard();\n if (guard) return guard;\n getExporter()!.reset();\n return Response.json({ ok: true });\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//#region src/browser/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Browser stub for types module
|
|
4
|
+
*
|
|
5
|
+
* Provides type definitions without importing from @opentelemetry/api
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* OpenTelemetry-compatible Attributes type (browser stub)
|
|
9
|
+
*/
|
|
10
|
+
type Attributes = Record<string, string | number | boolean | undefined>;
|
|
11
|
+
/**
|
|
12
|
+
* Configuration options for TanStack Start instrumentation
|
|
13
|
+
*/
|
|
14
|
+
interface TanStackInstrumentationConfig {
|
|
15
|
+
service?: string;
|
|
16
|
+
captureArgs?: boolean;
|
|
17
|
+
captureResults?: boolean;
|
|
18
|
+
captureErrors?: boolean;
|
|
19
|
+
captureHeaders?: string[];
|
|
20
|
+
excludePaths?: (string | RegExp)[];
|
|
21
|
+
sampling?: 'always' | 'adaptive' | 'never';
|
|
22
|
+
customAttributes?: (context: {
|
|
23
|
+
type: 'request' | 'serverFn' | 'loader' | 'beforeLoad' | 'middleware';
|
|
24
|
+
name: string;
|
|
25
|
+
request?: Request;
|
|
26
|
+
args?: unknown;
|
|
27
|
+
result?: unknown;
|
|
28
|
+
}) => Attributes;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Configuration specific to tracing middleware
|
|
32
|
+
*/
|
|
33
|
+
interface TracingMiddlewareConfig extends TanStackInstrumentationConfig {
|
|
34
|
+
type?: 'request' | 'function';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Configuration for server function tracing
|
|
38
|
+
*/
|
|
39
|
+
interface TraceServerFnConfig {
|
|
40
|
+
name?: string;
|
|
41
|
+
captureArgs?: boolean;
|
|
42
|
+
captureResults?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Configuration for loader tracing
|
|
46
|
+
*/
|
|
47
|
+
interface TraceLoaderConfig {
|
|
48
|
+
name?: string;
|
|
49
|
+
captureParams?: boolean;
|
|
50
|
+
captureResult?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Configuration for handler wrapper
|
|
54
|
+
*/
|
|
55
|
+
interface WrapStartHandlerConfig extends TanStackInstrumentationConfig {
|
|
56
|
+
endpoint?: string;
|
|
57
|
+
headers?: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Default configuration values
|
|
61
|
+
*/
|
|
62
|
+
declare const DEFAULT_CONFIG: Required<Omit<TanStackInstrumentationConfig, 'customAttributes' | 'service'>>;
|
|
63
|
+
/**
|
|
64
|
+
* Span attribute keys (stub - values are strings)
|
|
65
|
+
*/
|
|
66
|
+
declare const SPAN_ATTRIBUTES: {
|
|
67
|
+
readonly HTTP_REQUEST_METHOD: "http.request.method";
|
|
68
|
+
readonly HTTP_RESPONSE_STATUS_CODE: "http.response.status_code";
|
|
69
|
+
readonly URL_PATH: "url.path";
|
|
70
|
+
readonly URL_QUERY: "url.query";
|
|
71
|
+
readonly URL_FULL: "url.full";
|
|
72
|
+
readonly RPC_SYSTEM: "rpc.system";
|
|
73
|
+
readonly RPC_METHOD: "rpc.method";
|
|
74
|
+
readonly TANSTACK_TYPE: "tanstack.type";
|
|
75
|
+
readonly TANSTACK_SERVER_FN_NAME: "tanstack.server_function.name";
|
|
76
|
+
readonly TANSTACK_SERVER_FN_METHOD: "tanstack.server_function.method";
|
|
77
|
+
readonly TANSTACK_SERVER_FN_ARGS: "tanstack.server_function.args";
|
|
78
|
+
readonly TANSTACK_SERVER_FN_RESULT: "tanstack.server_function.result";
|
|
79
|
+
readonly TANSTACK_LOADER_ROUTE_ID: "tanstack.loader.route_id";
|
|
80
|
+
readonly TANSTACK_LOADER_TYPE: "tanstack.loader.type";
|
|
81
|
+
readonly TANSTACK_LOADER_PARAMS: "tanstack.loader.params";
|
|
82
|
+
readonly TANSTACK_MIDDLEWARE_NAME: "tanstack.middleware.name";
|
|
83
|
+
readonly TANSTACK_REQUEST_DURATION_MS: "tanstack.request.duration_ms";
|
|
84
|
+
};
|
|
85
|
+
//#endregion
|
|
86
|
+
export { TraceLoaderConfig as a, WrapStartHandlerConfig as c, TanStackInstrumentationConfig as i, DEFAULT_CONFIG as n, TraceServerFnConfig as o, SPAN_ATTRIBUTES as r, TracingMiddlewareConfig as s, Attributes as t };
|
|
87
|
+
//# sourceMappingURL=types-BJ7FyVoX.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BJ7FyVoX.d.ts","names":[],"sources":["../src/browser/types.ts"],"mappings":";;AASA;;;;AAA+B;AAK/B;;AAL+B,KAAnB,UAAA,GAAa,MAAM;;;;UAKd,6BAAA;EACf,OAAA;EACA,WAAA;EACA,cAAA;EACA,aAAA;EACA,cAAA;EACA,YAAA,aAAyB,MAAA;EACzB,QAAA;EACA,gBAAA,IAAoB,OAAA;IAClB,IAAA;IACA,IAAA;IACA,OAAA,GAAU,OAAA;IACV,IAAA;IACA,MAAA;EAAA,MACI,UAAA;AAAA;;;;UAMS,uBAAA,SAAgC,6BAA6B;EAC5E,IAAI;AAAA;;;;UAMW,mBAAA;EACf,IAAA;EACA,WAAA;EACA,cAAA;AAAA;;;;UAMe,iBAAA;EACf,IAAA;EACA,aAAA;EACA,aAAA;AAAA;;;;UAMe,sBAAA,SAA+B,6BAA6B;EAC3E,QAAA;EACA,OAAA,GAAU,MAAA;AAAA;;;;cAMC,cAAA,EAAgB,QAAA,CAC3B,IAAA,CAAK,6BAAA;;;;cAaM,eAAA;EAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/types.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default configuration values
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
captureArgs: true,
|
|
7
|
+
captureResults: false,
|
|
8
|
+
captureErrors: true,
|
|
9
|
+
captureHeaders: ["x-request-id"],
|
|
10
|
+
excludePaths: [],
|
|
11
|
+
sampling: "adaptive"
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Span attribute keys following OpenTelemetry semantic conventions
|
|
15
|
+
*/
|
|
16
|
+
const SPAN_ATTRIBUTES = {
|
|
17
|
+
HTTP_REQUEST_METHOD: "http.request.method",
|
|
18
|
+
HTTP_RESPONSE_STATUS_CODE: "http.response.status_code",
|
|
19
|
+
URL_PATH: "url.path",
|
|
20
|
+
URL_QUERY: "url.query",
|
|
21
|
+
URL_FULL: "url.full",
|
|
22
|
+
RPC_SYSTEM: "rpc.system",
|
|
23
|
+
RPC_METHOD: "rpc.method",
|
|
24
|
+
TANSTACK_TYPE: "tanstack.type",
|
|
25
|
+
TANSTACK_SERVER_FN_NAME: "tanstack.server_function.name",
|
|
26
|
+
TANSTACK_SERVER_FN_METHOD: "tanstack.server_function.method",
|
|
27
|
+
TANSTACK_SERVER_FN_ARGS: "tanstack.server_function.args",
|
|
28
|
+
TANSTACK_SERVER_FN_RESULT: "tanstack.server_function.result",
|
|
29
|
+
TANSTACK_LOADER_ROUTE_ID: "tanstack.loader.route_id",
|
|
30
|
+
TANSTACK_LOADER_TYPE: "tanstack.loader.type",
|
|
31
|
+
TANSTACK_LOADER_PARAMS: "tanstack.loader.params",
|
|
32
|
+
TANSTACK_MIDDLEWARE_NAME: "tanstack.middleware.name",
|
|
33
|
+
TANSTACK_REQUEST_DURATION_MS: "tanstack.request.duration_ms"
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { SPAN_ATTRIBUTES as n, DEFAULT_CONFIG as t };
|
|
38
|
+
//# sourceMappingURL=types-BrccP0yX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BrccP0yX.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type { Attributes } from '@opentelemetry/api';\n\n/**\n * Configuration options for TanStack Start instrumentation\n */\nexport interface TanStackInstrumentationConfig {\n /**\n * Service name for spans\n * @default process.env.OTEL_SERVICE_NAME || 'tanstack-start'\n */\n service?: string;\n\n /**\n * Whether to capture function arguments as span attributes\n * Warning: May contain PII, review before enabling in production\n * @default true\n */\n captureArgs?: boolean;\n\n /**\n * Whether to capture function results as span attributes\n * Warning: May contain PII, disable in production\n * @default false\n */\n captureResults?: boolean;\n\n /**\n * Whether to capture errors and record exceptions\n * @default true\n */\n captureErrors?: boolean;\n\n /**\n * HTTP headers to capture as span attributes\n * @default ['x-request-id']\n */\n captureHeaders?: string[];\n\n /**\n * URL paths to exclude from tracing (glob patterns)\n * @default []\n */\n excludePaths?: (string | RegExp)[];\n\n /**\n * Sampling strategy\n * - 'always': Sample all requests (100%)\n * - 'adaptive': Use autotel's adaptive sampling (errors + slow = 100%, baseline 10%)\n * - 'never': Disable sampling (for testing/debugging)\n * @default 'adaptive'\n */\n sampling?: 'always' | 'adaptive' | 'never';\n\n /**\n * Custom function to extract additional span attributes\n */\n customAttributes?: (context: {\n type: 'request' | 'serverFn' | 'loader' | 'beforeLoad' | 'middleware';\n name: string;\n request?: Request;\n args?: unknown;\n result?: unknown;\n }) => Attributes;\n}\n\n/**\n * Configuration specific to tracing middleware\n */\nexport interface TracingMiddlewareConfig extends TanStackInstrumentationConfig {\n /**\n * Type of middleware\n * - 'request': For global request middleware (routes, SSR)\n * - 'function': For server function middleware\n * @default 'request'\n */\n type?: 'request' | 'function';\n}\n\n/**\n * Configuration for server function tracing\n */\nexport interface TraceServerFnConfig {\n /**\n * Explicit name for the span\n * If not provided, will attempt to infer from function name\n */\n name?: string;\n\n /**\n * Whether to capture function arguments\n * @default true\n */\n captureArgs?: boolean;\n\n /**\n * Whether to capture function results\n * @default false\n */\n captureResults?: boolean;\n}\n\n/**\n * Configuration for loader tracing\n */\nexport interface TraceLoaderConfig {\n /**\n * Explicit name for the span\n * If not provided, will use route ID\n */\n name?: string;\n\n /**\n * Whether to capture route params\n * @default true\n */\n captureParams?: boolean;\n\n /**\n * Whether to capture loader result\n * @default false\n */\n captureResult?: boolean;\n}\n\n/**\n * Configuration for handler wrapper\n */\nexport interface WrapStartHandlerConfig extends TanStackInstrumentationConfig {\n /**\n * OTLP endpoint URL\n * @default process.env.OTEL_EXPORTER_OTLP_ENDPOINT\n */\n endpoint?: string;\n\n /**\n * OTLP headers (e.g., for authentication)\n * @default parsed from process.env.OTEL_EXPORTER_OTLP_HEADERS\n */\n headers?: Record<string, string>;\n}\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG: Required<\n Omit<TanStackInstrumentationConfig, 'customAttributes' | 'service'>\n> = {\n captureArgs: true,\n captureResults: false,\n captureErrors: true,\n captureHeaders: ['x-request-id'],\n excludePaths: [],\n sampling: 'adaptive',\n};\n\n/**\n * Span attribute keys following OpenTelemetry semantic conventions\n */\nexport const SPAN_ATTRIBUTES = {\n // HTTP semantic conventions\n HTTP_REQUEST_METHOD: 'http.request.method',\n HTTP_RESPONSE_STATUS_CODE: 'http.response.status_code',\n URL_PATH: 'url.path',\n URL_QUERY: 'url.query',\n URL_FULL: 'url.full',\n\n // RPC semantic conventions (for server functions)\n RPC_SYSTEM: 'rpc.system',\n RPC_METHOD: 'rpc.method',\n\n // TanStack-specific attributes\n TANSTACK_TYPE: 'tanstack.type',\n TANSTACK_SERVER_FN_NAME: 'tanstack.server_function.name',\n TANSTACK_SERVER_FN_METHOD: 'tanstack.server_function.method',\n TANSTACK_SERVER_FN_ARGS: 'tanstack.server_function.args',\n TANSTACK_SERVER_FN_RESULT: 'tanstack.server_function.result',\n TANSTACK_LOADER_ROUTE_ID: 'tanstack.loader.route_id',\n TANSTACK_LOADER_TYPE: 'tanstack.loader.type',\n TANSTACK_LOADER_PARAMS: 'tanstack.loader.params',\n TANSTACK_MIDDLEWARE_NAME: 'tanstack.middleware.name',\n TANSTACK_REQUEST_DURATION_MS: 'tanstack.request.duration_ms',\n} as const;\n"],"mappings":";;;;AAgJA,MAAa,iBAET;CACF,aAAa;CACb,gBAAgB;CAChB,eAAe;CACf,gBAAgB,CAAC,cAAc;CAC/B,cAAc,CAAC;CACf,UAAU;AACZ;;;;AAKA,MAAa,kBAAkB;CAE7B,qBAAqB;CACrB,2BAA2B;CAC3B,UAAU;CACV,WAAW;CACX,UAAU;CAGV,YAAY;CACZ,YAAY;CAGZ,eAAe;CACf,yBAAyB;CACzB,2BAA2B;CAC3B,yBAAyB;CACzB,2BAA2B;CAC3B,0BAA0B;CAC1B,sBAAsB;CACtB,wBAAwB;CACxB,0BAA0B;CAC1B,8BAA8B;AAChC"}
|