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.
Files changed (150) hide show
  1. package/dist/auto.d.ts +8 -35
  2. package/dist/auto.d.ts.map +1 -0
  3. package/dist/auto.js +41 -22
  4. package/dist/auto.js.map +1 -1
  5. package/dist/browser/context.d.ts +50 -0
  6. package/dist/browser/context.d.ts.map +1 -0
  7. package/dist/browser/context.js +54 -2
  8. package/dist/browser/context.js.map +1 -1
  9. package/dist/browser/debug-headers.d.ts +10 -0
  10. package/dist/browser/debug-headers.d.ts.map +1 -0
  11. package/dist/browser/debug-headers.js +12 -2
  12. package/dist/browser/debug-headers.js.map +1 -1
  13. package/dist/browser/error-reporting.d.ts +39 -0
  14. package/dist/browser/error-reporting.d.ts.map +1 -0
  15. package/dist/browser/error-reporting.js +35 -2
  16. package/dist/browser/error-reporting.js.map +1 -1
  17. package/dist/browser/handlers.d.ts +14 -0
  18. package/dist/browser/handlers.d.ts.map +1 -0
  19. package/dist/browser/handlers.js +10 -2
  20. package/dist/browser/handlers.js.map +1 -1
  21. package/dist/browser/index.d.ts +11 -0
  22. package/dist/browser/index.js +12 -12
  23. package/dist/browser/loaders.d.ts +31 -0
  24. package/dist/browser/loaders.d.ts.map +1 -0
  25. package/dist/browser/loaders.js +29 -2
  26. package/dist/browser/loaders.js.map +1 -1
  27. package/dist/browser/metrics.d.ts +56 -0
  28. package/dist/browser/metrics.d.ts.map +1 -0
  29. package/dist/browser/metrics.js +48 -2
  30. package/dist/browser/metrics.js.map +1 -1
  31. package/dist/browser/middleware.d.ts +42 -0
  32. package/dist/browser/middleware.d.ts.map +1 -0
  33. package/dist/browser/middleware.js +36 -2
  34. package/dist/browser/middleware.js.map +1 -1
  35. package/dist/browser/server-functions.d.ts +14 -0
  36. package/dist/browser/server-functions.d.ts.map +1 -0
  37. package/dist/browser/server-functions.js +16 -2
  38. package/dist/browser/server-functions.js.map +1 -1
  39. package/dist/browser/testing.d.ts +85 -0
  40. package/dist/browser/testing.d.ts.map +1 -0
  41. package/dist/browser/testing.js +43 -2
  42. package/dist/browser/testing.js.map +1 -1
  43. package/dist/browser/types.d.ts +2 -0
  44. package/dist/browser/types.js +37 -2
  45. package/dist/browser/types.js.map +1 -1
  46. package/dist/context.d.ts +5 -3
  47. package/dist/context.d.ts.map +1 -0
  48. package/dist/context.js +112 -3
  49. package/dist/context.js.map +1 -1
  50. package/dist/debug-headers.d.ts +14 -14
  51. package/dist/debug-headers.d.ts.map +1 -0
  52. package/dist/debug-headers.js +62 -4
  53. package/dist/debug-headers.js.map +1 -1
  54. package/dist/env-BpFWNnpL.js +30 -0
  55. package/dist/env-BpFWNnpL.js.map +1 -0
  56. package/dist/error-reporting.d.ts +37 -37
  57. package/dist/error-reporting.d.ts.map +1 -0
  58. package/dist/error-reporting.js +154 -3
  59. package/dist/error-reporting.js.map +1 -1
  60. package/dist/handlers.d.ts +5 -4
  61. package/dist/handlers.d.ts.map +1 -0
  62. package/dist/handlers.js +192 -6
  63. package/dist/handlers.js.map +1 -1
  64. package/dist/index.d.ts +15 -16
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +14 -16
  67. package/dist/instrument-DS7YCE1R.d.ts +10 -0
  68. package/dist/instrument-DS7YCE1R.d.ts.map +1 -0
  69. package/dist/instrument-DdLlMfRi.js +80 -0
  70. package/dist/instrument-DdLlMfRi.js.map +1 -0
  71. package/dist/loaders-DrVVY25K.d.ts +2402 -0
  72. package/dist/loaders-DrVVY25K.d.ts.map +1 -0
  73. package/dist/loaders.d.ts +2 -116
  74. package/dist/loaders.js +234 -5
  75. package/dist/loaders.js.map +1 -1
  76. package/dist/metrics.d.ts +39 -39
  77. package/dist/metrics.d.ts.map +1 -0
  78. package/dist/metrics.js +144 -3
  79. package/dist/metrics.js.map +1 -1
  80. package/dist/middleware.d.ts +16 -15
  81. package/dist/middleware.d.ts.map +1 -0
  82. package/dist/middleware.js +290 -7
  83. package/dist/middleware.js.map +1 -1
  84. package/dist/route-filter-dLg-j3jR.js +33 -0
  85. package/dist/route-filter-dLg-j3jR.js.map +1 -0
  86. package/dist/server-functions.d.ts +4 -3
  87. package/dist/server-functions.d.ts.map +1 -0
  88. package/dist/server-functions.js +133 -5
  89. package/dist/server-functions.js.map +1 -1
  90. package/dist/testing.d.ts +164 -65
  91. package/dist/testing.d.ts.map +1 -0
  92. package/dist/testing.js +212 -147
  93. package/dist/testing.js.map +1 -1
  94. package/dist/types-BJ7FyVoX.d.ts +87 -0
  95. package/dist/types-BJ7FyVoX.d.ts.map +1 -0
  96. package/dist/types-BrccP0yX.js +38 -0
  97. package/dist/types-BrccP0yX.js.map +1 -0
  98. package/dist/types-pQgmQa4j.d.ts +154 -0
  99. package/dist/types-pQgmQa4j.d.ts.map +1 -0
  100. package/package.json +7 -7
  101. package/dist/browser/index.js.map +0 -1
  102. package/dist/chunk-7OXOAS64.js +0 -41
  103. package/dist/chunk-7OXOAS64.js.map +0 -1
  104. package/dist/chunk-A7WMQ2BC.js +0 -25
  105. package/dist/chunk-A7WMQ2BC.js.map +0 -1
  106. package/dist/chunk-CCME55EK.js +0 -28
  107. package/dist/chunk-CCME55EK.js.map +0 -1
  108. package/dist/chunk-CSFIPJC2.js +0 -11
  109. package/dist/chunk-CSFIPJC2.js.map +0 -1
  110. package/dist/chunk-DTZCOB4W.js +0 -32
  111. package/dist/chunk-DTZCOB4W.js.map +0 -1
  112. package/dist/chunk-EFSKEYDJ.js +0 -20
  113. package/dist/chunk-EFSKEYDJ.js.map +0 -1
  114. package/dist/chunk-EGRHWZRV.js +0 -3
  115. package/dist/chunk-EGRHWZRV.js.map +0 -1
  116. package/dist/chunk-ESU66L3L.js +0 -92
  117. package/dist/chunk-ESU66L3L.js.map +0 -1
  118. package/dist/chunk-EUYFVNYE.js +0 -16
  119. package/dist/chunk-EUYFVNYE.js.map +0 -1
  120. package/dist/chunk-FFQ4FJKE.js +0 -185
  121. package/dist/chunk-FFQ4FJKE.js.map +0 -1
  122. package/dist/chunk-G526TOMY.js +0 -96
  123. package/dist/chunk-G526TOMY.js.map +0 -1
  124. package/dist/chunk-I4LX3LOG.js +0 -35
  125. package/dist/chunk-I4LX3LOG.js.map +0 -1
  126. package/dist/chunk-JXO7H6KO.js +0 -10
  127. package/dist/chunk-JXO7H6KO.js.map +0 -1
  128. package/dist/chunk-KPXGFKPU.js +0 -193
  129. package/dist/chunk-KPXGFKPU.js.map +0 -1
  130. package/dist/chunk-LRA2UVVS.js +0 -210
  131. package/dist/chunk-LRA2UVVS.js.map +0 -1
  132. package/dist/chunk-MFYOV2SF.js +0 -32
  133. package/dist/chunk-MFYOV2SF.js.map +0 -1
  134. package/dist/chunk-MNP65ZX7.js +0 -21
  135. package/dist/chunk-MNP65ZX7.js.map +0 -1
  136. package/dist/chunk-NTY64BKS.js +0 -38
  137. package/dist/chunk-NTY64BKS.js.map +0 -1
  138. package/dist/chunk-UMEJU65Q.js +0 -34
  139. package/dist/chunk-UMEJU65Q.js.map +0 -1
  140. package/dist/chunk-UTPW3QRT.js +0 -52
  141. package/dist/chunk-UTPW3QRT.js.map +0 -1
  142. package/dist/chunk-V3RO5N2M.js +0 -8
  143. package/dist/chunk-V3RO5N2M.js.map +0 -1
  144. package/dist/chunk-XXBHZR3M.js +0 -99
  145. package/dist/chunk-XXBHZR3M.js.map +0 -1
  146. package/dist/chunk-YQYYPJCK.js +0 -37
  147. package/dist/chunk-YQYYPJCK.js.map +0 -1
  148. package/dist/index.js.map +0 -1
  149. package/dist/instrument-DRR7VL63.d.ts +0 -46
  150. package/dist/types-m5OjZJ-4.d.ts +0 -152
package/dist/testing.js CHANGED
@@ -1,169 +1,234 @@
1
- import './chunk-EGRHWZRV.js';
2
- import { InMemorySpanExporter } from 'autotel/exporters';
3
- import { SimpleSpanProcessor } from 'autotel/processors';
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
- const exporter = new InMemorySpanExporter();
8
- init({
9
- service: "test",
10
- spanProcessors: [new SimpleSpanProcessor(exporter)]
11
- });
12
- function getSpans() {
13
- return exporter.getFinishedSpans();
14
- }
15
- function getSpansByName(name) {
16
- const spans = getSpans();
17
- if (typeof name === "string") {
18
- return spans.filter((s) => s.name === name);
19
- }
20
- return spans.filter((s) => name.test(s.name));
21
- }
22
- function getSpansByType(type) {
23
- return getSpans().filter((s) => s.attributes["tanstack.type"] === type);
24
- }
25
- function reset() {
26
- exporter.reset();
27
- }
28
- function assertSpanExists(name) {
29
- const spans = getSpansByName(name);
30
- if (spans.length === 0) {
31
- const allSpanNames = getSpans().map((s) => s.name);
32
- throw new Error(
33
- `Expected span "${name}" to exist. Found spans: ${JSON.stringify(allSpanNames)}`
34
- );
35
- }
36
- }
37
- function assertSpanHasAttribute(name, attr, value) {
38
- const spans = getSpansByName(name);
39
- if (spans.length === 0) {
40
- throw new Error(`Span "${name}" not found`);
41
- }
42
- const span = spans[0];
43
- const attrValue = span.attributes[attr];
44
- if (attrValue === void 0) {
45
- throw new Error(
46
- `Attribute "${attr}" not found on span "${span.name}". Available attributes: ${JSON.stringify(Object.keys(span.attributes))}`
47
- );
48
- }
49
- if (value !== void 0 && attrValue !== value) {
50
- throw new Error(
51
- `Expected attribute "${attr}" to be "${value}", got "${attrValue}"`
52
- );
53
- }
54
- }
55
- function assertServerFnTraced(name) {
56
- assertSpanExists(`tanstack.serverFn.${name}`);
57
- }
58
- function assertLoaderTraced(routeId) {
59
- assertSpanExists(`tanstack.loader.${routeId}`);
60
- }
61
- function assertBeforeLoadTraced(routeId) {
62
- assertSpanExists(`tanstack.beforeLoad.${routeId}`);
63
- }
64
- function assertRequestTraced(method, path) {
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
- const headers = new Headers(options.headers);
83
- if (options.traceparent) {
84
- headers.set("traceparent", options.traceparent);
85
- }
86
- return new Request(`http://localhost${path}`, {
87
- method,
88
- headers,
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
- const version = "00";
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
- const chars = "0123456789abcdef";
101
- let result = "";
102
- for (let i = 0; i < length; i++) {
103
- result += chars[Math.floor(Math.random() * 16)];
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
- return globalThis.__testSpanExporter;
147
+ return globalThis.__testSpanExporter;
109
148
  }
110
149
  function e2eGuard() {
111
- if (process.env.E2E !== "1") {
112
- return Response.json(
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
- if (!getExporter()) {
121
- return Response.json(
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
- const { GET, DELETE } = createTestSpansHandlers();
130
- return createFileRoute(path)({
131
- server: {
132
- handlers: { GET, DELETE }
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
- return {
138
- GET(_input) {
139
- const guard = e2eGuard() ?? exporterGuard();
140
- if (guard) return guard;
141
- const spans = getExporter().getFinishedSpans().map((span) => {
142
- const { spanId, traceId } = span.spanContext();
143
- const serialized = {
144
- name: span.name,
145
- spanId,
146
- traceId,
147
- attributes: span.attributes,
148
- status: span.status,
149
- durationMs: span.duration[0] * 1e3 + span.duration[1] / 1e6
150
- };
151
- if (span.parentSpanContext?.spanId) {
152
- serialized.parentSpanId = span.parentSpanContext.spanId;
153
- }
154
- return serialized;
155
- });
156
- return Response.json({ spans });
157
- },
158
- DELETE(_input) {
159
- const guard = e2eGuard() ?? exporterGuard();
160
- if (guard) return guard;
161
- getExporter().reset();
162
- return Response.json({ ok: true });
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
@@ -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"}