autotel-cloudflare 2.1.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/LICENSE +21 -0
- package/README.md +432 -0
- package/dist/actors.d.ts +248 -0
- package/dist/actors.js +1030 -0
- package/dist/actors.js.map +1 -0
- package/dist/agents.d.ts +219 -0
- package/dist/agents.js +276 -0
- package/dist/agents.js.map +1 -0
- package/dist/bindings.d.ts +40 -0
- package/dist/bindings.js +4 -0
- package/dist/bindings.js.map +1 -0
- package/dist/chunk-JDPN3HND.js +520 -0
- package/dist/chunk-JDPN3HND.js.map +1 -0
- package/dist/chunk-QXFYTHQF.js +298 -0
- package/dist/chunk-QXFYTHQF.js.map +1 -0
- package/dist/chunk-SKKRPS5K.js +50 -0
- package/dist/chunk-SKKRPS5K.js.map +1 -0
- package/dist/events.d.ts +1 -0
- package/dist/events.js +3 -0
- package/dist/events.js.map +1 -0
- package/dist/handlers.d.ts +121 -0
- package/dist/handlers.js +4 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +144 -0
- package/dist/index.js +576 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +3 -0
- package/dist/logger.js.map +1 -0
- package/dist/sampling.d.ts +4 -0
- package/dist/sampling.js +3 -0
- package/dist/sampling.js.map +1 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.js +3 -0
- package/dist/testing.js.map +1 -0
- package/package.json +107 -0
- package/src/actors/alarms.ts +225 -0
- package/src/actors/index.ts +36 -0
- package/src/actors/instrument-actor.test.ts +179 -0
- package/src/actors/instrument-actor.ts +574 -0
- package/src/actors/sockets.ts +217 -0
- package/src/actors/storage.ts +263 -0
- package/src/actors/traced-handler.ts +300 -0
- package/src/actors/types.ts +98 -0
- package/src/actors.ts +50 -0
- package/src/agents/index.ts +42 -0
- package/src/agents/otel-observability.test.ts +329 -0
- package/src/agents/otel-observability.ts +465 -0
- package/src/agents/types.ts +167 -0
- package/src/agents.ts +76 -0
- package/src/bindings/bindings.ts +621 -0
- package/src/bindings/common.ts +75 -0
- package/src/bindings/index.ts +12 -0
- package/src/bindings.ts +6 -0
- package/src/events.ts +6 -0
- package/src/global/cache.test.ts +292 -0
- package/src/global/cache.ts +164 -0
- package/src/global/fetch.test.ts +344 -0
- package/src/global/fetch.ts +134 -0
- package/src/global/index.ts +7 -0
- package/src/handlers/durable-objects.test.ts +524 -0
- package/src/handlers/durable-objects.ts +250 -0
- package/src/handlers/index.ts +6 -0
- package/src/handlers/workflows.ts +318 -0
- package/src/handlers.ts +6 -0
- package/src/index.ts +57 -0
- package/src/logger.ts +6 -0
- package/src/sampling.ts +6 -0
- package/src/testing.ts +6 -0
- package/src/wrappers/index.ts +8 -0
- package/src/wrappers/instrument.integration.test.ts +468 -0
- package/src/wrappers/instrument.ts +643 -0
- package/src/wrappers/wrap-do.ts +34 -0
- package/src/wrappers/wrap-module.ts +37 -0
package/src/bindings.ts
ADDED
package/src/events.ts
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { instrumentGlobalCache } from './cache';
|
|
3
|
+
import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
|
|
4
|
+
|
|
5
|
+
describe('Global Cache Instrumentation', () => {
|
|
6
|
+
let mockTracer: any;
|
|
7
|
+
let mockSpan: any;
|
|
8
|
+
let getTracerSpy: any;
|
|
9
|
+
let originalCaches: typeof globalThis.caches;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
// Save original caches
|
|
13
|
+
originalCaches = globalThis.caches;
|
|
14
|
+
|
|
15
|
+
mockSpan = {
|
|
16
|
+
spanContext: () => ({
|
|
17
|
+
traceId: 'test-trace-id',
|
|
18
|
+
spanId: 'test-span-id',
|
|
19
|
+
traceFlags: 1,
|
|
20
|
+
}),
|
|
21
|
+
setAttribute: vi.fn(),
|
|
22
|
+
setAttributes: vi.fn(),
|
|
23
|
+
setStatus: vi.fn(),
|
|
24
|
+
recordException: vi.fn(),
|
|
25
|
+
end: vi.fn(),
|
|
26
|
+
isRecording: () => true,
|
|
27
|
+
updateName: vi.fn(),
|
|
28
|
+
addEvent: vi.fn(),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
mockTracer = {
|
|
32
|
+
startActiveSpan: vi.fn((name, options, fn) => {
|
|
33
|
+
return fn(mockSpan);
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
getTracerSpy = vi.spyOn(trace, 'getTracer').mockReturnValue(mockTracer as any);
|
|
38
|
+
|
|
39
|
+
// Mock caches API
|
|
40
|
+
const mockCache = {
|
|
41
|
+
match: vi.fn(async (key) => {
|
|
42
|
+
// Simulate cache hit/miss
|
|
43
|
+
const url = key instanceof Request ? key.url : key;
|
|
44
|
+
if (typeof url === 'string' && url.includes('cached')) {
|
|
45
|
+
return new Response('cached data');
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}),
|
|
49
|
+
put: vi.fn(async () => {}),
|
|
50
|
+
delete: vi.fn(async () => true),
|
|
51
|
+
keys: vi.fn(async () => []),
|
|
52
|
+
add: vi.fn(async () => {}),
|
|
53
|
+
addAll: vi.fn(async () => {}),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
(globalThis as any).caches = {
|
|
57
|
+
default: mockCache,
|
|
58
|
+
open: vi.fn(async (name: string) => mockCache),
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
// Restore original caches
|
|
64
|
+
(globalThis as any).caches = originalCaches;
|
|
65
|
+
getTracerSpy.mockRestore();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('instrumentGlobalCache()', () => {
|
|
69
|
+
it('should wrap globalThis.caches', () => {
|
|
70
|
+
const originalCaches = globalThis.caches;
|
|
71
|
+
instrumentGlobalCache();
|
|
72
|
+
|
|
73
|
+
expect(globalThis.caches).not.toBe(originalCaches);
|
|
74
|
+
expect(globalThis.caches.default).toBeDefined();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should instrument caches.default', async () => {
|
|
78
|
+
instrumentGlobalCache();
|
|
79
|
+
|
|
80
|
+
const request = new Request('https://example.com/test');
|
|
81
|
+
await caches.default.match(request);
|
|
82
|
+
|
|
83
|
+
expect(mockTracer.startActiveSpan).toHaveBeenCalled();
|
|
84
|
+
|
|
85
|
+
const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
|
|
86
|
+
expect(spanName).toContain('Cache');
|
|
87
|
+
expect(spanName).toContain('default');
|
|
88
|
+
expect(spanName).toContain('match');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('cache.match() instrumentation', () => {
|
|
93
|
+
it('should create span for cache.match()', async () => {
|
|
94
|
+
instrumentGlobalCache();
|
|
95
|
+
|
|
96
|
+
const request = new Request('https://example.com/api/data');
|
|
97
|
+
await caches.default.match(request);
|
|
98
|
+
|
|
99
|
+
expect(mockTracer.startActiveSpan).toHaveBeenCalled();
|
|
100
|
+
|
|
101
|
+
const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
|
|
102
|
+
expect(spanName).toBe('Cache default.match');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should record cache hit (result found)', async () => {
|
|
106
|
+
instrumentGlobalCache();
|
|
107
|
+
|
|
108
|
+
const request = new Request('https://example.com/cached/data');
|
|
109
|
+
const result = await caches.default.match(request);
|
|
110
|
+
|
|
111
|
+
expect(result).toBeDefined();
|
|
112
|
+
expect(mockSpan.setAttribute).toHaveBeenCalledWith('cache.hit', true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should record cache miss (result not found)', async () => {
|
|
116
|
+
// Create a mock that explicitly returns undefined
|
|
117
|
+
const missCache = {
|
|
118
|
+
match: vi.fn(async () => undefined),
|
|
119
|
+
put: vi.fn(async () => {}),
|
|
120
|
+
delete: vi.fn(async () => true),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
(globalThis as any).caches = {
|
|
124
|
+
default: missCache,
|
|
125
|
+
open: vi.fn(),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
instrumentGlobalCache();
|
|
129
|
+
|
|
130
|
+
const request = new Request('https://example.com/uncached/data');
|
|
131
|
+
const result = await caches.default.match(request);
|
|
132
|
+
|
|
133
|
+
expect(result).toBeUndefined();
|
|
134
|
+
expect(mockSpan.setAttribute).toHaveBeenCalledWith('cache.hit', false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should sanitize URL in attributes', async () => {
|
|
138
|
+
instrumentGlobalCache();
|
|
139
|
+
|
|
140
|
+
const request = new Request('https://example.com/api/data?secret=123&token=abc');
|
|
141
|
+
await caches.default.match(request);
|
|
142
|
+
|
|
143
|
+
const options = mockTracer.startActiveSpan.mock.calls[0][1];
|
|
144
|
+
|
|
145
|
+
// URL should be sanitized (query params removed)
|
|
146
|
+
expect(options.attributes['cache.key']).toBe('https://example.com/api/data');
|
|
147
|
+
expect(options.attributes['cache.key']).not.toContain('secret');
|
|
148
|
+
expect(options.attributes['cache.key']).not.toContain('token');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should add cache operation attributes', async () => {
|
|
152
|
+
instrumentGlobalCache();
|
|
153
|
+
|
|
154
|
+
const request = new Request('https://example.com/test');
|
|
155
|
+
await caches.default.match(request);
|
|
156
|
+
|
|
157
|
+
const options = mockTracer.startActiveSpan.mock.calls[0][1];
|
|
158
|
+
expect(options.kind).toBe(SpanKind.CLIENT);
|
|
159
|
+
expect(options.attributes['cache.name']).toBe('default');
|
|
160
|
+
expect(options.attributes['cache.operation']).toBe('match');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('cache.put() instrumentation', () => {
|
|
165
|
+
it('should create span for cache.put()', async () => {
|
|
166
|
+
instrumentGlobalCache();
|
|
167
|
+
|
|
168
|
+
const request = new Request('https://example.com/test');
|
|
169
|
+
const response = new Response('data');
|
|
170
|
+
await caches.default.put(request, response);
|
|
171
|
+
|
|
172
|
+
expect(mockTracer.startActiveSpan).toHaveBeenCalled();
|
|
173
|
+
|
|
174
|
+
const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
|
|
175
|
+
expect(spanName).toBe('Cache default.put');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should add cache name and operation attributes', async () => {
|
|
179
|
+
instrumentGlobalCache();
|
|
180
|
+
|
|
181
|
+
const request = new Request('https://example.com/test');
|
|
182
|
+
const response = new Response('data');
|
|
183
|
+
await caches.default.put(request, response);
|
|
184
|
+
|
|
185
|
+
const options = mockTracer.startActiveSpan.mock.calls[0][1];
|
|
186
|
+
expect(options.attributes['cache.name']).toBe('default');
|
|
187
|
+
expect(options.attributes['cache.operation']).toBe('put');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should handle errors in cache.put()', async () => {
|
|
191
|
+
const errorCache = {
|
|
192
|
+
...globalThis.caches.default,
|
|
193
|
+
put: vi.fn(async () => {
|
|
194
|
+
throw new Error('Cache error');
|
|
195
|
+
}),
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
(globalThis as any).caches = {
|
|
199
|
+
default: errorCache,
|
|
200
|
+
open: vi.fn(),
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
instrumentGlobalCache();
|
|
204
|
+
|
|
205
|
+
const request = new Request('https://example.com/test');
|
|
206
|
+
const response = new Response('data');
|
|
207
|
+
|
|
208
|
+
await expect(caches.default.put(request, response)).rejects.toThrow('Cache error');
|
|
209
|
+
|
|
210
|
+
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
211
|
+
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
212
|
+
code: SpanStatusCode.ERROR,
|
|
213
|
+
message: 'Cache error',
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('cache.delete() instrumentation', () => {
|
|
219
|
+
it('should create span for cache.delete()', async () => {
|
|
220
|
+
instrumentGlobalCache();
|
|
221
|
+
|
|
222
|
+
const request = new Request('https://example.com/test');
|
|
223
|
+
await caches.default.delete(request);
|
|
224
|
+
|
|
225
|
+
expect(mockTracer.startActiveSpan).toHaveBeenCalled();
|
|
226
|
+
|
|
227
|
+
const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
|
|
228
|
+
expect(spanName).toBe('Cache default.delete');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should add cache operation attributes for delete', async () => {
|
|
232
|
+
instrumentGlobalCache();
|
|
233
|
+
|
|
234
|
+
const request = new Request('https://example.com/test');
|
|
235
|
+
await caches.default.delete(request);
|
|
236
|
+
|
|
237
|
+
const options = mockTracer.startActiveSpan.mock.calls[0][1];
|
|
238
|
+
expect(options.attributes['cache.name']).toBe('default');
|
|
239
|
+
expect(options.attributes['cache.operation']).toBe('delete');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('caches.open() instrumentation', () => {
|
|
244
|
+
it('should wrap caches.open() to instrument named caches', async () => {
|
|
245
|
+
instrumentGlobalCache();
|
|
246
|
+
|
|
247
|
+
const namedCache = await caches.open('my-cache');
|
|
248
|
+
|
|
249
|
+
// After instrumentation, caches.open is wrapped
|
|
250
|
+
// The key is that we can still call it and get a cache back
|
|
251
|
+
expect(namedCache).toBeDefined();
|
|
252
|
+
expect(typeof namedCache.match).toBe('function');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should instrument operations on named caches', async () => {
|
|
256
|
+
instrumentGlobalCache();
|
|
257
|
+
|
|
258
|
+
const namedCache = await caches.open('my-custom-cache');
|
|
259
|
+
const request = new Request('https://example.com/test');
|
|
260
|
+
await namedCache.match(request);
|
|
261
|
+
|
|
262
|
+
expect(mockTracer.startActiveSpan).toHaveBeenCalled();
|
|
263
|
+
|
|
264
|
+
const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
|
|
265
|
+
expect(spanName).toBe('Cache my-custom-cache.match');
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe('Edge cases', () => {
|
|
270
|
+
it('should handle string keys for match()', async () => {
|
|
271
|
+
instrumentGlobalCache();
|
|
272
|
+
|
|
273
|
+
// Some implementations allow string keys
|
|
274
|
+
await caches.default.match('https://example.com/test');
|
|
275
|
+
|
|
276
|
+
expect(mockTracer.startActiveSpan).toHaveBeenCalled();
|
|
277
|
+
|
|
278
|
+
const options = mockTracer.startActiveSpan.mock.calls[0][1];
|
|
279
|
+
expect(options.attributes['cache.key']).toBe('https://example.com/test');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should set OK status on successful operations', async () => {
|
|
283
|
+
instrumentGlobalCache();
|
|
284
|
+
|
|
285
|
+
const request = new Request('https://example.com/test');
|
|
286
|
+
await caches.default.match(request);
|
|
287
|
+
|
|
288
|
+
expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
|
|
289
|
+
expect(mockSpan.end).toHaveBeenCalled();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global Cache API instrumentation for Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* Automatically traces cache operations:
|
|
5
|
+
* - cache.match() - Read from cache
|
|
6
|
+
* - cache.put() - Write to cache
|
|
7
|
+
* - cache.delete() - Delete from cache
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
|
|
11
|
+
import { wrap } from '../bindings/common';
|
|
12
|
+
import { WorkerTracer } from 'autotel-edge';
|
|
13
|
+
|
|
14
|
+
type CacheOperation = 'match' | 'put' | 'delete';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sanitize URL for span attributes (remove query params that might contain sensitive data)
|
|
18
|
+
*/
|
|
19
|
+
function sanitizeURL(url: string): string {
|
|
20
|
+
const u = new URL(url);
|
|
21
|
+
return `${u.protocol}//${u.host}${u.pathname}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Instrument a cache method (match, put, delete)
|
|
26
|
+
*/
|
|
27
|
+
function instrumentCacheMethod<T extends Function>(
|
|
28
|
+
fn: T,
|
|
29
|
+
cacheName: string,
|
|
30
|
+
operation: CacheOperation,
|
|
31
|
+
): T {
|
|
32
|
+
const handler: ProxyHandler<T> = {
|
|
33
|
+
async apply(target, thisArg, argArray) {
|
|
34
|
+
const tracer = trace.getTracer('autotel-edge') as WorkerTracer;
|
|
35
|
+
|
|
36
|
+
// Extract URL from first argument (Request or string)
|
|
37
|
+
const firstArg = argArray[0];
|
|
38
|
+
const url =
|
|
39
|
+
firstArg instanceof Request
|
|
40
|
+
? firstArg.url
|
|
41
|
+
: typeof firstArg === 'string'
|
|
42
|
+
? firstArg
|
|
43
|
+
: undefined;
|
|
44
|
+
|
|
45
|
+
const spanName = `Cache ${cacheName}.${operation}`;
|
|
46
|
+
|
|
47
|
+
return tracer.startActiveSpan(
|
|
48
|
+
spanName,
|
|
49
|
+
{
|
|
50
|
+
kind: SpanKind.CLIENT,
|
|
51
|
+
attributes: {
|
|
52
|
+
'cache.name': cacheName,
|
|
53
|
+
'cache.operation': operation,
|
|
54
|
+
'cache.key': url ? sanitizeURL(url) : undefined,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
async (span) => {
|
|
58
|
+
try {
|
|
59
|
+
const result = await Reflect.apply(target, thisArg, argArray);
|
|
60
|
+
|
|
61
|
+
// For match operations, record whether it was a hit or miss
|
|
62
|
+
if (operation === 'match') {
|
|
63
|
+
span.setAttribute('cache.hit', !!result);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
67
|
+
return result;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
span.recordException(error as Error);
|
|
70
|
+
span.setStatus({
|
|
71
|
+
code: SpanStatusCode.ERROR,
|
|
72
|
+
message: error instanceof Error ? error.message : String(error),
|
|
73
|
+
});
|
|
74
|
+
throw error;
|
|
75
|
+
} finally {
|
|
76
|
+
span.end();
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return wrap(fn, handler);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Instrument a Cache instance
|
|
88
|
+
*/
|
|
89
|
+
function instrumentCache(cache: Cache, cacheName: string): Cache {
|
|
90
|
+
const handler: ProxyHandler<Cache> = {
|
|
91
|
+
get(target, prop) {
|
|
92
|
+
const value = Reflect.get(target, prop);
|
|
93
|
+
|
|
94
|
+
// Instrument the cache operation methods
|
|
95
|
+
if (
|
|
96
|
+
(prop === 'match' || prop === 'put' || prop === 'delete') &&
|
|
97
|
+
typeof value === 'function'
|
|
98
|
+
) {
|
|
99
|
+
return instrumentCacheMethod(
|
|
100
|
+
value.bind(target),
|
|
101
|
+
cacheName,
|
|
102
|
+
prop as CacheOperation,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Bind other methods to preserve `this` context
|
|
107
|
+
if (typeof value === 'function') {
|
|
108
|
+
return value.bind(target);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return value;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return wrap(cache, handler);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Instrument caches.open()
|
|
120
|
+
*/
|
|
121
|
+
function instrumentCachesOpen(
|
|
122
|
+
openFn: CacheStorage['open'],
|
|
123
|
+
): CacheStorage['open'] {
|
|
124
|
+
const handler: ProxyHandler<CacheStorage['open']> = {
|
|
125
|
+
async apply(target, thisArg, argArray) {
|
|
126
|
+
const cacheName = argArray[0];
|
|
127
|
+
const cache = await Reflect.apply(target, thisArg, argArray);
|
|
128
|
+
return instrumentCache(cache, cacheName);
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
return wrap(openFn, handler);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Instrument the global caches API
|
|
137
|
+
*
|
|
138
|
+
* This wraps globalThis.caches to automatically create spans for all cache operations.
|
|
139
|
+
*
|
|
140
|
+
* **Note:** This is called automatically when the library is initialized with
|
|
141
|
+
* `instrumentation.instrumentGlobalCache: true` (default).
|
|
142
|
+
*/
|
|
143
|
+
export function instrumentGlobalCache(): void {
|
|
144
|
+
const handler: ProxyHandler<typeof caches> = {
|
|
145
|
+
get(target, prop) {
|
|
146
|
+
if (prop === 'default') {
|
|
147
|
+
// Wrap the default cache
|
|
148
|
+
return instrumentCache(target.default, 'default');
|
|
149
|
+
} else if (prop === 'open') {
|
|
150
|
+
// Wrap the open method
|
|
151
|
+
const openFn = Reflect.get(target, prop);
|
|
152
|
+
if (typeof openFn === 'function') {
|
|
153
|
+
return instrumentCachesOpen(openFn.bind(target));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return Reflect.get(target, prop);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Replace global caches
|
|
162
|
+
// @ts-ignore - TypeScript doesn't like reassigning globalThis.caches
|
|
163
|
+
globalThis.caches = wrap(caches, handler);
|
|
164
|
+
}
|