autotel-cloudflare 3.1.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.
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +7 -0
- package/dist/agents.js.map +1 -1
- package/package.json +8 -4
- package/src/actors/alarms.ts +0 -225
- package/src/actors/index.ts +0 -36
- package/src/actors/instrument-actor.test.ts +0 -179
- package/src/actors/instrument-actor.ts +0 -574
- package/src/actors/sockets.ts +0 -217
- package/src/actors/storage.ts +0 -263
- package/src/actors/traced-handler.ts +0 -300
- package/src/actors/types.ts +0 -98
- package/src/actors.ts +0 -50
- package/src/agents/agent.ts +0 -327
- package/src/agents/base.ts +0 -26
- package/src/agents/index.ts +0 -46
- package/src/agents/mcp.ts +0 -38
- package/src/agents/observability.ts +0 -145
- package/src/agents/otel-observability.test.ts +0 -269
- package/src/agents/otel-observability.ts +0 -557
- package/src/agents/types.ts +0 -61
- package/src/agents.ts +0 -87
- package/src/bindings/ai.test.ts +0 -170
- package/src/bindings/ai.ts +0 -73
- package/src/bindings/analytics-engine.test.ts +0 -175
- package/src/bindings/analytics-engine.ts +0 -78
- package/src/bindings/bindings-cache.test.ts +0 -80
- package/src/bindings/bindings-detection.test.ts +0 -235
- package/src/bindings/bindings-this-binding.test.ts +0 -294
- package/src/bindings/bindings.ts +0 -720
- package/src/bindings/browser-rendering.test.ts +0 -160
- package/src/bindings/browser-rendering.ts +0 -70
- package/src/bindings/common.ts +0 -84
- package/src/bindings/hyperdrive.test.ts +0 -176
- package/src/bindings/hyperdrive.ts +0 -74
- package/src/bindings/images.test.ts +0 -262
- package/src/bindings/images.ts +0 -182
- package/src/bindings/index.ts +0 -20
- package/src/bindings/queue-producer.test.ts +0 -224
- package/src/bindings/queue-producer.ts +0 -105
- package/src/bindings/rate-limiter.test.ts +0 -140
- package/src/bindings/rate-limiter.ts +0 -69
- package/src/bindings/vectorize.test.ts +0 -362
- package/src/bindings/vectorize.ts +0 -86
- package/src/bindings.ts +0 -6
- package/src/events.ts +0 -6
- package/src/execution-logger.test.ts +0 -127
- package/src/execution-logger.ts +0 -112
- package/src/global/cache.test.ts +0 -292
- package/src/global/cache.ts +0 -164
- package/src/global/fetch.test.ts +0 -399
- package/src/global/fetch.ts +0 -136
- package/src/global/index.ts +0 -7
- package/src/handlers/durable-objects.test.ts +0 -524
- package/src/handlers/durable-objects.ts +0 -250
- package/src/handlers/index.ts +0 -6
- package/src/handlers/workflows.test.ts +0 -411
- package/src/handlers/workflows.ts +0 -345
- package/src/handlers.ts +0 -6
- package/src/index.ts +0 -91
- package/src/logger.ts +0 -15
- package/src/native/native-tracing.test.ts +0 -54
- package/src/native/native-tracing.ts +0 -83
- package/src/native.ts +0 -12
- package/src/parse-error.ts +0 -1
- package/src/sampling.ts +0 -6
- package/src/testing.ts +0 -6
- package/src/wrappers/cf-attributes.test.ts +0 -334
- package/src/wrappers/define-worker-fetch.test.ts +0 -102
- package/src/wrappers/define-worker-fetch.ts +0 -71
- package/src/wrappers/index.ts +0 -13
- package/src/wrappers/instrument.integration.test.ts +0 -578
- package/src/wrappers/instrument.ts +0 -806
- package/src/wrappers/native-instrument.test.ts +0 -153
- package/src/wrappers/wrap-do.ts +0 -34
- 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
|
-
}
|
package/src/wrappers/index.ts
DELETED
|
@@ -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';
|