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.
- package/dist/agents.d.ts +626 -123
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +260 -171
- 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/index.ts +0 -42
- package/src/agents/otel-observability.test.ts +0 -336
- package/src/agents/otel-observability.ts +0 -474
- package/src/agents/types.ts +0 -167
- package/src/agents.ts +0 -76
- 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,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Queue producer binding instrumentation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
trace,
|
|
7
|
-
SpanKind,
|
|
8
|
-
SpanStatusCode,
|
|
9
|
-
} from '@opentelemetry/api';
|
|
10
|
-
import type { WorkerTracer } from 'autotel-edge';
|
|
11
|
-
import { wrap, setAttr } from './common';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Instrument Queue producer binding
|
|
15
|
-
*/
|
|
16
|
-
export function instrumentQueueProducer<T extends Queue>(queue: T, queueName?: string): T {
|
|
17
|
-
const name = queueName || 'queue';
|
|
18
|
-
|
|
19
|
-
const handler: ProxyHandler<T> = {
|
|
20
|
-
get(target, prop) {
|
|
21
|
-
const value = Reflect.get(target, prop);
|
|
22
|
-
|
|
23
|
-
if (prop === 'send' && typeof value === 'function') {
|
|
24
|
-
return new Proxy(value, {
|
|
25
|
-
apply: (fnTarget, _thisArg, args) => {
|
|
26
|
-
const tracer = trace.getTracer('autotel-edge') as WorkerTracer;
|
|
27
|
-
|
|
28
|
-
return tracer.startActiveSpan(
|
|
29
|
-
`Queue ${name}: send`,
|
|
30
|
-
{
|
|
31
|
-
kind: SpanKind.PRODUCER,
|
|
32
|
-
attributes: {
|
|
33
|
-
'messaging.system': 'cloudflare-queues',
|
|
34
|
-
'messaging.operation.type': 'publish',
|
|
35
|
-
'messaging.operation': 'send',
|
|
36
|
-
'messaging.destination.name': name,
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
async (span) => {
|
|
40
|
-
try {
|
|
41
|
-
const result = await Reflect.apply(fnTarget, target, args);
|
|
42
|
-
setAttr(span, 'messaging.message.id', (result as any)?.messageId);
|
|
43
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
44
|
-
return result;
|
|
45
|
-
} catch (error) {
|
|
46
|
-
span.recordException(error as Error);
|
|
47
|
-
span.setStatus({
|
|
48
|
-
code: SpanStatusCode.ERROR,
|
|
49
|
-
message: error instanceof Error ? error.message : String(error),
|
|
50
|
-
});
|
|
51
|
-
throw error;
|
|
52
|
-
} finally {
|
|
53
|
-
span.end();
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
);
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (prop === 'sendBatch' && typeof value === 'function') {
|
|
62
|
-
return new Proxy(value, {
|
|
63
|
-
apply: (fnTarget, _thisArg, args) => {
|
|
64
|
-
const [messages] = args as [{ body: unknown }[]];
|
|
65
|
-
const tracer = trace.getTracer('autotel-edge') as WorkerTracer;
|
|
66
|
-
|
|
67
|
-
return tracer.startActiveSpan(
|
|
68
|
-
`Queue ${name}: sendBatch`,
|
|
69
|
-
{
|
|
70
|
-
kind: SpanKind.PRODUCER,
|
|
71
|
-
attributes: {
|
|
72
|
-
'messaging.system': 'cloudflare-queues',
|
|
73
|
-
'messaging.operation.type': 'publish',
|
|
74
|
-
'messaging.operation': 'sendBatch',
|
|
75
|
-
'messaging.destination.name': name,
|
|
76
|
-
'messaging.batch.message_count': Array.isArray(messages) ? messages.length : 0,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
async (span) => {
|
|
80
|
-
try {
|
|
81
|
-
const result = await Reflect.apply(fnTarget, target, args);
|
|
82
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
83
|
-
return result;
|
|
84
|
-
} catch (error) {
|
|
85
|
-
span.recordException(error as Error);
|
|
86
|
-
span.setStatus({
|
|
87
|
-
code: SpanStatusCode.ERROR,
|
|
88
|
-
message: error instanceof Error ? error.message : String(error),
|
|
89
|
-
});
|
|
90
|
-
throw error;
|
|
91
|
-
} finally {
|
|
92
|
-
span.end();
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
);
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return value;
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
return wrap(queue, handler);
|
|
105
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import { instrumentRateLimiter } from './rate-limiter';
|
|
3
|
-
import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
|
|
4
|
-
|
|
5
|
-
describe('Rate Limiter Instrumentation', () => {
|
|
6
|
-
let mockTracer: any;
|
|
7
|
-
let mockSpan: any;
|
|
8
|
-
let getTracerSpy: any;
|
|
9
|
-
let mockLimiter: 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
|
-
mockTracer = {
|
|
29
|
-
startActiveSpan: vi.fn((name, options, fn) => {
|
|
30
|
-
return fn(mockSpan);
|
|
31
|
-
}),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
getTracerSpy = vi.spyOn(trace, 'getTracer').mockReturnValue(mockTracer as any);
|
|
35
|
-
|
|
36
|
-
mockLimiter = {
|
|
37
|
-
limit: vi.fn(async () => ({ success: true })),
|
|
38
|
-
someOtherMethod: vi.fn(() => 'passthrough-value'),
|
|
39
|
-
};
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
getTracerSpy.mockRestore();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('limit()', () => {
|
|
47
|
-
it('should create span with correct attributes', async () => {
|
|
48
|
-
const instrumented = instrumentRateLimiter(mockLimiter, 'my-limiter');
|
|
49
|
-
|
|
50
|
-
await instrumented.limit({ key: 'user-123' });
|
|
51
|
-
|
|
52
|
-
expect(mockTracer.startActiveSpan).toHaveBeenCalledTimes(1);
|
|
53
|
-
|
|
54
|
-
const [spanName, spanOptions] = mockTracer.startActiveSpan.mock.calls[0];
|
|
55
|
-
expect(spanName).toBe('RateLimiter my-limiter: limit');
|
|
56
|
-
expect(spanOptions.kind).toBe(SpanKind.CLIENT);
|
|
57
|
-
expect(spanOptions.attributes['rate_limiter.system']).toBe('cloudflare-rate-limiter');
|
|
58
|
-
expect(spanOptions.attributes['rate_limiter.key']).toBe('user-123');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should record success from result', async () => {
|
|
62
|
-
const instrumented = instrumentRateLimiter(mockLimiter, 'my-limiter');
|
|
63
|
-
|
|
64
|
-
await instrumented.limit({ key: 'user-123' });
|
|
65
|
-
|
|
66
|
-
expect(mockSpan.setAttribute).toHaveBeenCalledWith('rate_limiter.success', true);
|
|
67
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
|
|
68
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should record success=false when rate limited', async () => {
|
|
72
|
-
mockLimiter.limit = vi.fn(async () => ({ success: false }));
|
|
73
|
-
|
|
74
|
-
const instrumented = instrumentRateLimiter(mockLimiter, 'my-limiter');
|
|
75
|
-
|
|
76
|
-
const result = await instrumented.limit({ key: 'user-456' });
|
|
77
|
-
|
|
78
|
-
expect(result.success).toBe(false);
|
|
79
|
-
expect(mockSpan.setAttribute).toHaveBeenCalledWith('rate_limiter.success', false);
|
|
80
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
|
|
81
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should handle errors by recording exception and rethrowing', async () => {
|
|
85
|
-
const error = new Error('Rate limiter unavailable');
|
|
86
|
-
mockLimiter.limit = vi.fn(async () => {
|
|
87
|
-
throw error;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const instrumented = instrumentRateLimiter(mockLimiter, 'my-limiter');
|
|
91
|
-
|
|
92
|
-
await expect(instrumented.limit({ key: 'user-789' })).rejects.toThrow('Rate limiter unavailable');
|
|
93
|
-
|
|
94
|
-
expect(mockSpan.recordException).toHaveBeenCalledWith(error);
|
|
95
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
96
|
-
code: SpanStatusCode.ERROR,
|
|
97
|
-
message: 'Rate limiter unavailable',
|
|
98
|
-
});
|
|
99
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should use default binding name when none provided', async () => {
|
|
103
|
-
const instrumented = instrumentRateLimiter(mockLimiter);
|
|
104
|
-
|
|
105
|
-
await instrumented.limit({ key: 'user-123' });
|
|
106
|
-
|
|
107
|
-
const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
|
|
108
|
-
expect(spanName).toBe('RateLimiter rate-limiter: limit');
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe('this-binding', () => {
|
|
113
|
-
it('should invoke limit() with original object as this, not the proxy', async () => {
|
|
114
|
-
let receivedThis: any;
|
|
115
|
-
const mockLim = {
|
|
116
|
-
limit: vi.fn(async function(this: any) {
|
|
117
|
-
// eslint-disable-next-line unicorn/no-this-assignment, @typescript-eslint/no-this-alias
|
|
118
|
-
receivedThis = this;
|
|
119
|
-
return { success: true };
|
|
120
|
-
}),
|
|
121
|
-
};
|
|
122
|
-
const instrumented = instrumentRateLimiter(mockLim, 'test');
|
|
123
|
-
await instrumented.limit({ key: 'user-123' });
|
|
124
|
-
expect(receivedThis).toBe(mockLim);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('Non-instrumented methods', () => {
|
|
129
|
-
it('should pass through non-instrumented methods unchanged', () => {
|
|
130
|
-
const instrumented = instrumentRateLimiter(mockLimiter, 'my-limiter');
|
|
131
|
-
|
|
132
|
-
const result = instrumented.someOtherMethod();
|
|
133
|
-
|
|
134
|
-
expect(result).toBe('passthrough-value');
|
|
135
|
-
expect(mockLimiter.someOtherMethod).toHaveBeenCalled();
|
|
136
|
-
// No span should be created for non-instrumented methods
|
|
137
|
-
expect(mockTracer.startActiveSpan).not.toHaveBeenCalled();
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rate Limiter binding instrumentation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
trace,
|
|
7
|
-
SpanKind,
|
|
8
|
-
SpanStatusCode,
|
|
9
|
-
} from '@opentelemetry/api';
|
|
10
|
-
import type { WorkerTracer } from 'autotel-edge';
|
|
11
|
-
import { wrap, setAttr } from './common';
|
|
12
|
-
|
|
13
|
-
interface RateLimiterLike {
|
|
14
|
-
limit(options: { key: string }): Promise<{ success: boolean }>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Instrument Rate Limiter binding (manual only — not auto-detected)
|
|
19
|
-
*/
|
|
20
|
-
export function instrumentRateLimiter<T extends RateLimiterLike>(limiter: T, bindingName?: string): T {
|
|
21
|
-
const name = bindingName || 'rate-limiter';
|
|
22
|
-
|
|
23
|
-
const handler: ProxyHandler<T> = {
|
|
24
|
-
get(target, prop) {
|
|
25
|
-
const value = Reflect.get(target, prop);
|
|
26
|
-
|
|
27
|
-
if (prop === 'limit' && typeof value === 'function') {
|
|
28
|
-
return new Proxy(value, {
|
|
29
|
-
apply: (fnTarget, _thisArg, args) => {
|
|
30
|
-
const [options] = args as [{ key: string }];
|
|
31
|
-
const tracer = trace.getTracer('autotel-edge') as WorkerTracer;
|
|
32
|
-
|
|
33
|
-
return tracer.startActiveSpan(
|
|
34
|
-
`RateLimiter ${name}: limit`,
|
|
35
|
-
{
|
|
36
|
-
kind: SpanKind.CLIENT,
|
|
37
|
-
attributes: {
|
|
38
|
-
'rate_limiter.system': 'cloudflare-rate-limiter',
|
|
39
|
-
'rate_limiter.key': options?.key,
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
async (span) => {
|
|
43
|
-
try {
|
|
44
|
-
const result = await Reflect.apply(fnTarget, target, args);
|
|
45
|
-
setAttr(span, 'rate_limiter.success', result?.success);
|
|
46
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
47
|
-
return result;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
span.recordException(error as Error);
|
|
50
|
-
span.setStatus({
|
|
51
|
-
code: SpanStatusCode.ERROR,
|
|
52
|
-
message: error instanceof Error ? error.message : String(error),
|
|
53
|
-
});
|
|
54
|
-
throw error;
|
|
55
|
-
} finally {
|
|
56
|
-
span.end();
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
);
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return value;
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
return wrap(limiter, handler);
|
|
69
|
-
}
|
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
|
|
3
|
-
import { instrumentVectorize } from './vectorize';
|
|
4
|
-
|
|
5
|
-
describe('Vectorize Binding Instrumentation', () => {
|
|
6
|
-
let mockTracer: any;
|
|
7
|
-
let mockSpan: any;
|
|
8
|
-
let getTracerSpy: any;
|
|
9
|
-
let mockVectorize: 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
|
-
mockTracer = {
|
|
29
|
-
startActiveSpan: vi.fn((name, options, fn) => {
|
|
30
|
-
return fn(mockSpan);
|
|
31
|
-
}),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
getTracerSpy = vi.spyOn(trace, 'getTracer').mockReturnValue(mockTracer as any);
|
|
35
|
-
|
|
36
|
-
mockVectorize = {
|
|
37
|
-
query: vi.fn(async () => ({
|
|
38
|
-
matches: [
|
|
39
|
-
{ id: 'vec-1', score: 0.95 },
|
|
40
|
-
{ id: 'vec-2', score: 0.87 },
|
|
41
|
-
{ id: 'vec-3', score: 0.72 },
|
|
42
|
-
],
|
|
43
|
-
count: 3,
|
|
44
|
-
})),
|
|
45
|
-
insert: vi.fn(async () => ({ mutationId: 'mut-1', count: 2 })),
|
|
46
|
-
upsert: vi.fn(async () => ({ mutationId: 'mut-2', count: 3 })),
|
|
47
|
-
deleteByIds: vi.fn(async () => ({ mutationId: 'mut-3', count: 1 })),
|
|
48
|
-
getByIds: vi.fn(async () => [{ id: 'vec-1', values: [0.1, 0.2] }]),
|
|
49
|
-
describe: vi.fn(async () => ({
|
|
50
|
-
dimensions: 128,
|
|
51
|
-
vectorCount: 1000,
|
|
52
|
-
processedUpTo: 'ts-123',
|
|
53
|
-
})),
|
|
54
|
-
// Non-instrumented method
|
|
55
|
-
toString: vi.fn(() => 'VectorizeIndex'),
|
|
56
|
-
} as unknown as VectorizeIndex;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('query()', () => {
|
|
60
|
-
it('should create span with correct attributes', async () => {
|
|
61
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
62
|
-
|
|
63
|
-
// The source reads topK from args[0], so we pass a query object as the first argument
|
|
64
|
-
await instrumented.query({ topK: 5 } as any, {} as any);
|
|
65
|
-
|
|
66
|
-
expect(mockTracer.startActiveSpan).toHaveBeenCalledWith(
|
|
67
|
-
'Vectorize my-index: query',
|
|
68
|
-
expect.objectContaining({
|
|
69
|
-
kind: SpanKind.CLIENT,
|
|
70
|
-
attributes: expect.objectContaining({
|
|
71
|
-
'db.system': 'cloudflare-vectorize',
|
|
72
|
-
'db.operation': 'query',
|
|
73
|
-
'db.collection.name': 'my-index',
|
|
74
|
-
'db.vectorize.top_k': 5,
|
|
75
|
-
}),
|
|
76
|
-
}),
|
|
77
|
-
expect.any(Function),
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should record matches_count from result', async () => {
|
|
82
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
83
|
-
|
|
84
|
-
await instrumented.query([0.1, 0.2, 0.3] as any, {} as any);
|
|
85
|
-
|
|
86
|
-
expect(mockSpan.setAttribute).toHaveBeenCalledWith('db.vectorize.matches_count', 3);
|
|
87
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
|
|
88
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should handle query without topK in first argument', async () => {
|
|
92
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
93
|
-
|
|
94
|
-
await instrumented.query([0.1, 0.2, 0.3] as any, {} as any);
|
|
95
|
-
|
|
96
|
-
const attributes = mockTracer.startActiveSpan.mock.calls[0][1].attributes;
|
|
97
|
-
expect(attributes['db.vectorize.top_k']).toBeUndefined();
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe('insert()', () => {
|
|
102
|
-
it('should set vectors_count from input array length', async () => {
|
|
103
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
104
|
-
|
|
105
|
-
const vectors = [
|
|
106
|
-
{ id: 'vec-1', values: [0.1, 0.2] },
|
|
107
|
-
{ id: 'vec-2', values: [0.3, 0.4] },
|
|
108
|
-
];
|
|
109
|
-
await instrumented.insert(vectors as any);
|
|
110
|
-
|
|
111
|
-
expect(mockTracer.startActiveSpan).toHaveBeenCalledWith(
|
|
112
|
-
'Vectorize my-index: insert',
|
|
113
|
-
expect.objectContaining({
|
|
114
|
-
kind: SpanKind.CLIENT,
|
|
115
|
-
attributes: expect.objectContaining({
|
|
116
|
-
'db.system': 'cloudflare-vectorize',
|
|
117
|
-
'db.operation': 'insert',
|
|
118
|
-
'db.collection.name': 'my-index',
|
|
119
|
-
'db.vectorize.vectors_count': 2,
|
|
120
|
-
}),
|
|
121
|
-
}),
|
|
122
|
-
expect.any(Function),
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
|
|
126
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe('upsert()', () => {
|
|
131
|
-
it('should set vectors_count from input array length', async () => {
|
|
132
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
133
|
-
|
|
134
|
-
const vectors = [
|
|
135
|
-
{ id: 'vec-1', values: [0.1, 0.2] },
|
|
136
|
-
{ id: 'vec-2', values: [0.3, 0.4] },
|
|
137
|
-
{ id: 'vec-3', values: [0.5, 0.6] },
|
|
138
|
-
];
|
|
139
|
-
await instrumented.upsert(vectors as any);
|
|
140
|
-
|
|
141
|
-
expect(mockTracer.startActiveSpan).toHaveBeenCalledWith(
|
|
142
|
-
'Vectorize my-index: upsert',
|
|
143
|
-
expect.objectContaining({
|
|
144
|
-
kind: SpanKind.CLIENT,
|
|
145
|
-
attributes: expect.objectContaining({
|
|
146
|
-
'db.system': 'cloudflare-vectorize',
|
|
147
|
-
'db.operation': 'upsert',
|
|
148
|
-
'db.collection.name': 'my-index',
|
|
149
|
-
'db.vectorize.vectors_count': 3,
|
|
150
|
-
}),
|
|
151
|
-
}),
|
|
152
|
-
expect.any(Function),
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
|
|
156
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('deleteByIds()', () => {
|
|
161
|
-
it('should create correct span', async () => {
|
|
162
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
163
|
-
|
|
164
|
-
await instrumented.deleteByIds(['vec-1', 'vec-2']);
|
|
165
|
-
|
|
166
|
-
expect(mockTracer.startActiveSpan).toHaveBeenCalledWith(
|
|
167
|
-
'Vectorize my-index: deleteByIds',
|
|
168
|
-
expect.objectContaining({
|
|
169
|
-
kind: SpanKind.CLIENT,
|
|
170
|
-
attributes: expect.objectContaining({
|
|
171
|
-
'db.system': 'cloudflare-vectorize',
|
|
172
|
-
'db.operation': 'deleteByIds',
|
|
173
|
-
'db.collection.name': 'my-index',
|
|
174
|
-
}),
|
|
175
|
-
}),
|
|
176
|
-
expect.any(Function),
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
|
|
180
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
describe('describe()', () => {
|
|
185
|
-
it('should create correct span', async () => {
|
|
186
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
187
|
-
|
|
188
|
-
await instrumented.describe();
|
|
189
|
-
|
|
190
|
-
expect(mockTracer.startActiveSpan).toHaveBeenCalledWith(
|
|
191
|
-
'Vectorize my-index: describe',
|
|
192
|
-
expect.objectContaining({
|
|
193
|
-
kind: SpanKind.CLIENT,
|
|
194
|
-
attributes: expect.objectContaining({
|
|
195
|
-
'db.system': 'cloudflare-vectorize',
|
|
196
|
-
'db.operation': 'describe',
|
|
197
|
-
'db.collection.name': 'my-index',
|
|
198
|
-
}),
|
|
199
|
-
}),
|
|
200
|
-
expect.any(Function),
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
|
|
204
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
describe('this-binding', () => {
|
|
209
|
-
it('should invoke methods with original object as this, not the proxy', async () => {
|
|
210
|
-
let receivedThis: any;
|
|
211
|
-
const mockVec = {
|
|
212
|
-
query: vi.fn(async function(this: any) {
|
|
213
|
-
// eslint-disable-next-line unicorn/no-this-assignment, @typescript-eslint/no-this-alias
|
|
214
|
-
receivedThis = this;
|
|
215
|
-
return { matches: [], count: 0 };
|
|
216
|
-
}),
|
|
217
|
-
insert: vi.fn(async () => ({ mutationId: 'mut-1', count: 0 })),
|
|
218
|
-
upsert: vi.fn(async () => ({ mutationId: 'mut-2', count: 0 })),
|
|
219
|
-
deleteByIds: vi.fn(async () => ({ mutationId: 'mut-3', count: 0 })),
|
|
220
|
-
getByIds: vi.fn(async () => []),
|
|
221
|
-
describe: vi.fn(async () => ({ dimensions: 128, vectorCount: 0, processedUpTo: '' })),
|
|
222
|
-
} as unknown as VectorizeIndex;
|
|
223
|
-
|
|
224
|
-
const instrumented = instrumentVectorize(mockVec, 'test');
|
|
225
|
-
await instrumented.query([0.1, 0.2] as any, {} as any);
|
|
226
|
-
expect(receivedThis).toBe(mockVec);
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
describe('Error handling', () => {
|
|
231
|
-
it('should record exception and set error status on query() failure', async () => {
|
|
232
|
-
mockVectorize.query = vi.fn(async () => {
|
|
233
|
-
throw new Error('Vectorize query failed');
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
237
|
-
|
|
238
|
-
await expect(instrumented.query([0.1, 0.2] as any, {} as any)).rejects.toThrow('Vectorize query failed');
|
|
239
|
-
|
|
240
|
-
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
241
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
242
|
-
code: SpanStatusCode.ERROR,
|
|
243
|
-
message: 'Vectorize query failed',
|
|
244
|
-
});
|
|
245
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('should record exception and set error status on insert() failure', async () => {
|
|
249
|
-
mockVectorize.insert = vi.fn(async () => {
|
|
250
|
-
throw new Error('Insert failed');
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
254
|
-
|
|
255
|
-
await expect(instrumented.insert([{ id: 'v1', values: [0.1] }] as any)).rejects.toThrow('Insert failed');
|
|
256
|
-
|
|
257
|
-
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
258
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
259
|
-
code: SpanStatusCode.ERROR,
|
|
260
|
-
message: 'Insert failed',
|
|
261
|
-
});
|
|
262
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('should record exception and set error status on upsert() failure', async () => {
|
|
266
|
-
mockVectorize.upsert = vi.fn(async () => {
|
|
267
|
-
throw new Error('Upsert failed');
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
271
|
-
|
|
272
|
-
await expect(instrumented.upsert([{ id: 'v1', values: [0.1] }] as any)).rejects.toThrow('Upsert failed');
|
|
273
|
-
|
|
274
|
-
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
275
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
276
|
-
code: SpanStatusCode.ERROR,
|
|
277
|
-
message: 'Upsert failed',
|
|
278
|
-
});
|
|
279
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it('should record exception and set error status on deleteByIds() failure', async () => {
|
|
283
|
-
mockVectorize.deleteByIds = vi.fn(async () => {
|
|
284
|
-
throw new Error('Delete failed');
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
288
|
-
|
|
289
|
-
await expect(instrumented.deleteByIds(['vec-1'])).rejects.toThrow('Delete failed');
|
|
290
|
-
|
|
291
|
-
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
292
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
293
|
-
code: SpanStatusCode.ERROR,
|
|
294
|
-
message: 'Delete failed',
|
|
295
|
-
});
|
|
296
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it('should record exception and set error status on describe() failure', async () => {
|
|
300
|
-
mockVectorize.describe = vi.fn(async () => {
|
|
301
|
-
throw new Error('Describe failed');
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
305
|
-
|
|
306
|
-
await expect(instrumented.describe()).rejects.toThrow('Describe failed');
|
|
307
|
-
|
|
308
|
-
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
309
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
310
|
-
code: SpanStatusCode.ERROR,
|
|
311
|
-
message: 'Describe failed',
|
|
312
|
-
});
|
|
313
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it('should handle non-Error exceptions', async () => {
|
|
317
|
-
mockVectorize.query = vi.fn(async () => {
|
|
318
|
-
throw 'string error';
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
322
|
-
|
|
323
|
-
await expect(instrumented.query([0.1] as any, {} as any)).rejects.toThrow('string error');
|
|
324
|
-
|
|
325
|
-
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
326
|
-
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
327
|
-
code: SpanStatusCode.ERROR,
|
|
328
|
-
message: 'string error',
|
|
329
|
-
});
|
|
330
|
-
expect(mockSpan.end).toHaveBeenCalled();
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
describe('Non-instrumented methods', () => {
|
|
335
|
-
it('should pass through non-traced methods without creating spans', () => {
|
|
336
|
-
const instrumented = instrumentVectorize(mockVectorize, 'my-index');
|
|
337
|
-
|
|
338
|
-
const result = instrumented.toString();
|
|
339
|
-
|
|
340
|
-
expect(result).toBe('VectorizeIndex');
|
|
341
|
-
expect(mockTracer.startActiveSpan).not.toHaveBeenCalled();
|
|
342
|
-
});
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
describe('Default index name', () => {
|
|
346
|
-
it('should use "vectorize" as default index name when none provided', async () => {
|
|
347
|
-
const instrumented = instrumentVectorize(mockVectorize);
|
|
348
|
-
|
|
349
|
-
await instrumented.query([0.1, 0.2] as any, {} as any);
|
|
350
|
-
|
|
351
|
-
expect(mockTracer.startActiveSpan).toHaveBeenCalledWith(
|
|
352
|
-
'Vectorize vectorize: query',
|
|
353
|
-
expect.objectContaining({
|
|
354
|
-
attributes: expect.objectContaining({
|
|
355
|
-
'db.collection.name': 'vectorize',
|
|
356
|
-
}),
|
|
357
|
-
}),
|
|
358
|
-
expect.any(Function),
|
|
359
|
-
);
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
});
|