autotel-cloudflare 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/agents.d.ts +626 -123
  2. package/dist/agents.d.ts.map +1 -1
  3. package/dist/agents.js +260 -171
  4. package/dist/agents.js.map +1 -1
  5. package/package.json +8 -4
  6. package/src/actors/alarms.ts +0 -225
  7. package/src/actors/index.ts +0 -36
  8. package/src/actors/instrument-actor.test.ts +0 -179
  9. package/src/actors/instrument-actor.ts +0 -574
  10. package/src/actors/sockets.ts +0 -217
  11. package/src/actors/storage.ts +0 -263
  12. package/src/actors/traced-handler.ts +0 -300
  13. package/src/actors/types.ts +0 -98
  14. package/src/actors.ts +0 -50
  15. package/src/agents/index.ts +0 -42
  16. package/src/agents/otel-observability.test.ts +0 -336
  17. package/src/agents/otel-observability.ts +0 -474
  18. package/src/agents/types.ts +0 -167
  19. package/src/agents.ts +0 -76
  20. package/src/bindings/ai.test.ts +0 -170
  21. package/src/bindings/ai.ts +0 -73
  22. package/src/bindings/analytics-engine.test.ts +0 -175
  23. package/src/bindings/analytics-engine.ts +0 -78
  24. package/src/bindings/bindings-cache.test.ts +0 -80
  25. package/src/bindings/bindings-detection.test.ts +0 -235
  26. package/src/bindings/bindings-this-binding.test.ts +0 -294
  27. package/src/bindings/bindings.ts +0 -720
  28. package/src/bindings/browser-rendering.test.ts +0 -160
  29. package/src/bindings/browser-rendering.ts +0 -70
  30. package/src/bindings/common.ts +0 -84
  31. package/src/bindings/hyperdrive.test.ts +0 -176
  32. package/src/bindings/hyperdrive.ts +0 -74
  33. package/src/bindings/images.test.ts +0 -262
  34. package/src/bindings/images.ts +0 -182
  35. package/src/bindings/index.ts +0 -20
  36. package/src/bindings/queue-producer.test.ts +0 -224
  37. package/src/bindings/queue-producer.ts +0 -105
  38. package/src/bindings/rate-limiter.test.ts +0 -140
  39. package/src/bindings/rate-limiter.ts +0 -69
  40. package/src/bindings/vectorize.test.ts +0 -362
  41. package/src/bindings/vectorize.ts +0 -86
  42. package/src/bindings.ts +0 -6
  43. package/src/events.ts +0 -6
  44. package/src/execution-logger.test.ts +0 -127
  45. package/src/execution-logger.ts +0 -112
  46. package/src/global/cache.test.ts +0 -292
  47. package/src/global/cache.ts +0 -164
  48. package/src/global/fetch.test.ts +0 -399
  49. package/src/global/fetch.ts +0 -136
  50. package/src/global/index.ts +0 -7
  51. package/src/handlers/durable-objects.test.ts +0 -524
  52. package/src/handlers/durable-objects.ts +0 -250
  53. package/src/handlers/index.ts +0 -6
  54. package/src/handlers/workflows.test.ts +0 -411
  55. package/src/handlers/workflows.ts +0 -345
  56. package/src/handlers.ts +0 -6
  57. package/src/index.ts +0 -91
  58. package/src/logger.ts +0 -15
  59. package/src/native/native-tracing.test.ts +0 -54
  60. package/src/native/native-tracing.ts +0 -83
  61. package/src/native.ts +0 -12
  62. package/src/parse-error.ts +0 -1
  63. package/src/sampling.ts +0 -6
  64. package/src/testing.ts +0 -6
  65. package/src/wrappers/cf-attributes.test.ts +0 -334
  66. package/src/wrappers/define-worker-fetch.test.ts +0 -102
  67. package/src/wrappers/define-worker-fetch.ts +0 -71
  68. package/src/wrappers/index.ts +0 -13
  69. package/src/wrappers/instrument.integration.test.ts +0 -578
  70. package/src/wrappers/instrument.ts +0 -806
  71. package/src/wrappers/native-instrument.test.ts +0 -153
  72. package/src/wrappers/wrap-do.ts +0 -34
  73. package/src/wrappers/wrap-module.ts +0 -37
@@ -1,334 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { instrument } from './instrument';
3
- import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
4
-
5
- describe('CF Attributes extraction via instrument()', () => {
6
- let mockTracer: any;
7
- let mockSpan: any;
8
- let getTracerSpy: any;
9
- let capturedSpanOptions: any;
10
-
11
- beforeEach(() => {
12
- mockSpan = {
13
- spanContext: () => ({
14
- traceId: 'test-trace-id',
15
- spanId: 'test-span-id',
16
- traceFlags: 1,
17
- }),
18
- setAttribute: vi.fn(),
19
- setAttributes: vi.fn(),
20
- setStatus: vi.fn(),
21
- recordException: vi.fn(),
22
- end: vi.fn(),
23
- isRecording: () => true,
24
- updateName: vi.fn(),
25
- addEvent: vi.fn(),
26
- };
27
-
28
- capturedSpanOptions = null;
29
-
30
- mockTracer = {
31
- startActiveSpan: vi.fn((...args: any[]) => {
32
- // startActiveSpan can be called with (name, options, context, fn) or (name, options, fn)
33
- // The fetch instrumentation calls it with 4 args: (name, options, parentContext, fn)
34
- const fn = args.at(-1);
35
- if (args.length >= 2) {
36
- capturedSpanOptions = args[1];
37
- }
38
- return fn(mockSpan);
39
- }),
40
- setHeadSampler: vi.fn(),
41
- forceFlush: vi.fn(async () => {}),
42
- };
43
-
44
- getTracerSpy = vi.spyOn(trace, 'getTracer').mockReturnValue(mockTracer as any);
45
- });
46
-
47
- afterEach(() => {
48
- getTracerSpy.mockRestore();
49
- });
50
-
51
- function createMockCtx() {
52
- return {
53
- waitUntil: vi.fn(),
54
- passThroughOnException: vi.fn(),
55
- } as any;
56
- }
57
-
58
- it('should extract CF attributes when request has .cf object', async () => {
59
- const handler = {
60
- async fetch(request: Request) {
61
- return new Response('OK', { status: 200 });
62
- },
63
- };
64
-
65
- const instrumented = instrument(handler, {
66
- service: { name: 'test-worker' },
67
- });
68
-
69
- const request = new Request('http://example.com/test');
70
- // Attach .cf properties to the request (Cloudflare Workers runtime does this)
71
- Object.defineProperty(request, 'cf', {
72
- value: {
73
- colo: 'SJC',
74
- country: 'US',
75
- city: 'San Jose',
76
- region: 'California',
77
- continent: 'NA',
78
- timezone: 'America/Los_Angeles',
79
- latitude: '37.3382',
80
- longitude: '-121.8863',
81
- asn: 13_335,
82
- asOrganization: 'Cloudflare Inc',
83
- httpProtocol: 'HTTP/2',
84
- tlsVersion: 'TLSv1.3',
85
- clientTcpRtt: 5,
86
- },
87
- writable: false,
88
- enumerable: true,
89
- });
90
-
91
- const env = {} as any;
92
- const ctx = createMockCtx();
93
-
94
- await instrumented.fetch!(request, env, ctx);
95
-
96
- expect(capturedSpanOptions).toBeDefined();
97
- const attrs = capturedSpanOptions.attributes;
98
-
99
- expect(attrs['cloudflare.colo']).toBe('SJC');
100
- expect(attrs['cloudflare.country']).toBe('US');
101
- expect(attrs['cloudflare.city']).toBe('San Jose');
102
- expect(attrs['cloudflare.region']).toBe('California');
103
- expect(attrs['cloudflare.continent']).toBe('NA');
104
- expect(attrs['cloudflare.timezone']).toBe('America/Los_Angeles');
105
- expect(attrs['cloudflare.latitude']).toBe('37.3382');
106
- expect(attrs['cloudflare.longitude']).toBe('-121.8863');
107
- expect(attrs['cloudflare.asn']).toBe(13_335);
108
- expect(attrs['cloudflare.as_organization']).toBe('Cloudflare Inc');
109
- expect(attrs['cloudflare.http_protocol']).toBe('HTTP/2');
110
- expect(attrs['cloudflare.tls_version']).toBe('TLSv1.3');
111
- expect(attrs['cloudflare.client_tcp_rtt']).toBe(5);
112
- });
113
-
114
- it('should extract CF ray_id from cf-ray header', async () => {
115
- const handler = {
116
- async fetch(request: Request) {
117
- return new Response('OK', { status: 200 });
118
- },
119
- };
120
-
121
- const instrumented = instrument(handler, {
122
- service: { name: 'test-worker' },
123
- });
124
-
125
- const request = new Request('http://example.com/test', {
126
- headers: {
127
- 'cf-ray': '8a1b2c3d4e5f6-SJC',
128
- },
129
- });
130
- Object.defineProperty(request, 'cf', {
131
- value: { colo: 'SJC' },
132
- writable: false,
133
- enumerable: true,
134
- });
135
-
136
- const env = {} as any;
137
- const ctx = createMockCtx();
138
-
139
- await instrumented.fetch!(request, env, ctx);
140
-
141
- expect(capturedSpanOptions).toBeDefined();
142
- const attrs = capturedSpanOptions.attributes;
143
- expect(attrs['cloudflare.ray_id']).toBe('8a1b2c3d4e5f6-SJC');
144
- expect(attrs['cloudflare.colo']).toBe('SJC');
145
- });
146
-
147
- it('should not include CF attributes when request.cf is undefined', async () => {
148
- const handler = {
149
- async fetch(request: Request) {
150
- return new Response('OK', { status: 200 });
151
- },
152
- };
153
-
154
- const instrumented = instrument(handler, {
155
- service: { name: 'test-worker' },
156
- });
157
-
158
- // Standard request without .cf (local dev / Miniflare scenario)
159
- const request = new Request('http://example.com/test');
160
- const env = {} as any;
161
- const ctx = createMockCtx();
162
-
163
- await instrumented.fetch!(request, env, ctx);
164
-
165
- expect(capturedSpanOptions).toBeDefined();
166
- const attrs = capturedSpanOptions.attributes;
167
-
168
- // Standard HTTP attributes should still be present
169
- expect(attrs['http.request.method']).toBe('GET');
170
- expect(attrs['url.full']).toBe('http://example.com/test');
171
-
172
- // No cloudflare.* attributes should be present
173
- const cfKeys = Object.keys(attrs).filter(k => k.startsWith('cloudflare.'));
174
- expect(cfKeys).toHaveLength(0);
175
- });
176
-
177
- it('should correctly map all CF fields to cloudflare.* attribute names', async () => {
178
- const handler = {
179
- async fetch(request: Request) {
180
- return new Response('OK', { status: 200 });
181
- },
182
- };
183
-
184
- const instrumented = instrument(handler, {
185
- service: { name: 'test-worker' },
186
- });
187
-
188
- const request = new Request('http://example.com/test', {
189
- headers: { 'cf-ray': 'abc123-LAX' },
190
- });
191
- Object.defineProperty(request, 'cf', {
192
- value: {
193
- colo: 'LAX',
194
- country: 'US',
195
- city: 'Los Angeles',
196
- region: 'California',
197
- continent: 'NA',
198
- timezone: 'America/Los_Angeles',
199
- latitude: '34.0522',
200
- longitude: '-118.2437',
201
- asn: 13_335,
202
- asOrganization: 'Cloudflare Inc',
203
- httpProtocol: 'HTTP/3',
204
- tlsVersion: 'TLSv1.3',
205
- clientTcpRtt: 10,
206
- },
207
- writable: false,
208
- enumerable: true,
209
- });
210
-
211
- const env = {} as any;
212
- const ctx = createMockCtx();
213
-
214
- await instrumented.fetch!(request, env, ctx);
215
-
216
- const attrs = capturedSpanOptions.attributes;
217
-
218
- // Verify the exact mapping from CF property names to attribute names
219
- const expectedMappings: Record<string, [string, any]> = {
220
- 'cloudflare.colo': ['colo', 'LAX'],
221
- 'cloudflare.ray_id': ['cf-ray header', 'abc123-LAX'],
222
- 'cloudflare.country': ['country', 'US'],
223
- 'cloudflare.city': ['city', 'Los Angeles'],
224
- 'cloudflare.region': ['region', 'California'],
225
- 'cloudflare.continent': ['continent', 'NA'],
226
- 'cloudflare.timezone': ['timezone', 'America/Los_Angeles'],
227
- 'cloudflare.latitude': ['latitude', '34.0522'],
228
- 'cloudflare.longitude': ['longitude', '-118.2437'],
229
- 'cloudflare.asn': ['asn', 13_335],
230
- 'cloudflare.as_organization': ['asOrganization', 'Cloudflare Inc'],
231
- 'cloudflare.http_protocol': ['httpProtocol', 'HTTP/3'],
232
- 'cloudflare.tls_version': ['tlsVersion', 'TLSv1.3'],
233
- 'cloudflare.client_tcp_rtt': ['clientTcpRtt', 10],
234
- };
235
-
236
- for (const [attrKey, [, expectedValue]] of Object.entries(expectedMappings)) {
237
- expect(attrs[attrKey]).toBe(expectedValue);
238
- }
239
- });
240
-
241
- it('preserves valid falsy numeric CF attributes (0 values)', async () => {
242
- const handler = {
243
- async fetch(_request: Request) {
244
- return new Response('OK', { status: 200 });
245
- },
246
- };
247
-
248
- const instrumented = instrument(handler, {
249
- service: { name: 'test-worker' },
250
- });
251
-
252
- const request = new Request('http://example.com/test');
253
- Object.defineProperty(request, 'cf', {
254
- value: {
255
- latitude: 0,
256
- longitude: 0,
257
- asn: 0,
258
- clientTcpRtt: 0,
259
- },
260
- writable: false,
261
- enumerable: true,
262
- });
263
-
264
- const env = {} as any;
265
- const ctx = createMockCtx();
266
-
267
- await instrumented.fetch!(request, env, ctx);
268
-
269
- const attrs = capturedSpanOptions.attributes;
270
- expect(attrs['cloudflare.latitude']).toBe(0);
271
- expect(attrs['cloudflare.longitude']).toBe(0);
272
- expect(attrs['cloudflare.asn']).toBe(0);
273
- expect(attrs['cloudflare.client_tcp_rtt']).toBe(0);
274
- });
275
-
276
- it('should skip fetch instrumentation when route does not match include patterns', async () => {
277
- const handler = {
278
- async fetch(_request: Request) {
279
- return new Response('OK', { status: 200 });
280
- },
281
- };
282
-
283
- const instrumented = instrument(handler, {
284
- service: { name: 'test-worker' },
285
- handlers: {
286
- fetch: {
287
- include: ['/api/**'],
288
- exclude: ['/api/internal/**'],
289
- },
290
- },
291
- });
292
-
293
- const request = new Request('http://example.com/health');
294
- const env = {} as any;
295
- const ctx = createMockCtx();
296
-
297
- const response = await instrumented.fetch!(request, env, ctx);
298
-
299
- expect(response.status).toBe(200);
300
- expect(mockTracer.startActiveSpan).not.toHaveBeenCalled();
301
- expect(capturedSpanOptions).toBeNull();
302
- });
303
-
304
- it('should apply per-route service mapping to fetch span attributes', async () => {
305
- const handler = {
306
- async fetch(_request: Request) {
307
- return new Response('OK', { status: 200 });
308
- },
309
- };
310
-
311
- const instrumented = instrument(handler, {
312
- service: { name: 'default-worker' },
313
- handlers: {
314
- fetch: {
315
- routes: {
316
- '/api/auth/**': { service: 'auth-service' },
317
- '/api/**': { service: 'api-service' },
318
- },
319
- },
320
- },
321
- });
322
-
323
- const request = new Request('http://example.com/api/auth/login');
324
- const env = {} as any;
325
- const ctx = createMockCtx();
326
-
327
- await instrumented.fetch!(request, env, ctx);
328
-
329
- expect(capturedSpanOptions).toBeDefined();
330
- const attrs = capturedSpanOptions.attributes;
331
- expect(attrs['service.name']).toBe('auth-service');
332
- expect(attrs['autotel.route.service']).toBe('auth-service');
333
- });
334
- });
@@ -1,102 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { defineWorkerFetch } from './define-worker-fetch';
3
-
4
- describe('defineWorkerFetch', () => {
5
- interface Env {
6
- SERVICE_NAME?: string;
7
- }
8
-
9
- it('returns an object with a fetch method', () => {
10
- const worker = defineWorkerFetch<Env>(
11
- { service: { name: 'test-worker' } },
12
- async () => new Response('ok'),
13
- );
14
-
15
- expect(worker).toBeDefined();
16
- expect(typeof worker.fetch).toBe('function');
17
- });
18
-
19
- it('invokes the user handler and returns its response', async () => {
20
- let called = false;
21
-
22
- const worker = defineWorkerFetch<Env>(
23
- { service: { name: 'test-worker' } },
24
- async (request, _env, _ctx, _log) => {
25
- called = true;
26
- return new Response('hello', { status: 201 });
27
- },
28
- );
29
-
30
- const request = new Request('http://example.com/route');
31
- const env = {} as Env;
32
- const ctx = {
33
- waitUntil: vi.fn(),
34
- passThroughOnException: vi.fn(),
35
- } as any;
36
-
37
- const response = await worker.fetch(request, env, ctx);
38
-
39
- expect(called).toBe(true);
40
- expect(response.status).toBe(201);
41
- expect(await response.text()).toBe('hello');
42
- });
43
-
44
- it('passes a logger as the fourth argument', async () => {
45
- let receivedLog: unknown;
46
-
47
- const worker = defineWorkerFetch<Env>(
48
- { service: { name: 'test-worker' } },
49
- async (_request, _env, _ctx, log) => {
50
- receivedLog = log;
51
- return new Response('ok');
52
- },
53
- );
54
-
55
- const request = new Request('http://example.com/');
56
- const ctx = {
57
- waitUntil: vi.fn(),
58
- passThroughOnException: vi.fn(),
59
- } as any;
60
-
61
- await worker.fetch(request, {} as Env, ctx);
62
-
63
- expect(receivedLog).toBeDefined();
64
- expect(typeof (receivedLog as { set?: unknown }).set).toBe('function');
65
- expect(typeof (receivedLog as { emitNow?: unknown }).emitNow).toBe('function');
66
- });
67
-
68
- it('calls waitUntil so async exports flush before response returns', async () => {
69
- const worker = defineWorkerFetch<Env>(
70
- { service: { name: 'test-worker' } },
71
- async () => new Response('ok'),
72
- );
73
-
74
- const waitUntilSpy = vi.fn();
75
- const ctx = {
76
- waitUntil: waitUntilSpy,
77
- passThroughOnException: vi.fn(),
78
- } as any;
79
-
80
- await worker.fetch(new Request('http://example.com/'), {} as Env, ctx);
81
-
82
- expect(waitUntilSpy).toHaveBeenCalled();
83
- });
84
-
85
- it('propagates handler errors', async () => {
86
- const worker = defineWorkerFetch<Env>(
87
- { service: { name: 'test-worker' } },
88
- async () => {
89
- throw new Error('boom');
90
- },
91
- );
92
-
93
- const ctx = {
94
- waitUntil: vi.fn(),
95
- passThroughOnException: vi.fn(),
96
- } as any;
97
-
98
- await expect(
99
- worker.fetch(new Request('http://example.com/'), {} as Env, ctx),
100
- ).rejects.toThrow('boom');
101
- });
102
- });
@@ -1,71 +0,0 @@
1
- /**
2
- * Ergonomic wrapper for Cloudflare Workers `fetch` handlers.
3
- *
4
- * `defineWorkerFetch` instruments the handler the same way `instrument()` does
5
- * (so span exports already flush via `ctx.waitUntil`), and additionally injects
6
- * a request-scoped `ExecutionLogger` as the fourth argument — pre-populated
7
- * with method, path, cf-ray and `request.cf` context.
8
- *
9
- * Use this instead of `wrapModule` / `instrument` when your handler wants the
10
- * logger handed in directly rather than reaching for `getRequestLogger()`.
11
- *
12
- * @example
13
- * ```ts
14
- * import { defineWorkerFetch } from 'autotel-cloudflare'
15
- *
16
- * export default defineWorkerFetch(
17
- * { service: { name: 'my-worker' } },
18
- * async (request, env, ctx, log) => {
19
- * log.set({ route: '/health' })
20
- * log.emitNow({ status: 200 })
21
- * return new Response('ok')
22
- * },
23
- * )
24
- * ```
25
- */
26
- import type { ConfigurationOption } from 'autotel-edge';
27
- import { instrument } from './instrument';
28
- import { createWorkersLogger, type WorkersLoggerOptions } from '../execution-logger';
29
- import type { ExecutionLogger } from '../execution-logger';
30
-
31
- export type DefineWorkerFetchOptions = WorkersLoggerOptions;
32
-
33
- export type WorkerFetchHandler<E> = (
34
- request: Request,
35
- env: E,
36
- ctx: ExecutionContext,
37
- log: ExecutionLogger,
38
- ) => Response | Promise<Response>;
39
-
40
- /**
41
- * Wrap a Workers fetch handler so:
42
- * - the handler is instrumented (spans, propagation, waitUntil export flush)
43
- * - the handler receives a request-scoped logger as its fourth argument
44
- */
45
- export function defineWorkerFetch<E = unknown>(
46
- config: ConfigurationOption,
47
- handler: WorkerFetchHandler<E>,
48
- loggerOptions: DefineWorkerFetchOptions = {},
49
- ): { fetch: (request: Request, env: E, ctx: ExecutionContext) => Promise<Response> } {
50
- const wrapped = instrument(
51
- {
52
- fetch(request: Request, env: E, ctx: ExecutionContext) {
53
- const log = createWorkersLogger(request, loggerOptions);
54
- return handler(request, env, ctx, log);
55
- },
56
- },
57
- config,
58
- );
59
-
60
- return {
61
- fetch(request, env, ctx) {
62
- return Promise.resolve(
63
- (wrapped.fetch as unknown as (
64
- req: Request,
65
- env: E,
66
- ctx: ExecutionContext,
67
- ) => Response | Promise<Response>)(request, env, ctx),
68
- );
69
- },
70
- };
71
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Wrapper APIs for Cloudflare Workers
3
- * Provides multiple API styles for maximum flexibility
4
- */
5
-
6
- export { instrument } from './instrument';
7
- export { wrapModule } from './wrap-module';
8
- export { wrapDurableObject } from './wrap-do';
9
- export {
10
- defineWorkerFetch,
11
- type DefineWorkerFetchOptions,
12
- type WorkerFetchHandler,
13
- } from './define-worker-fetch';