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,170 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
3
- import { instrumentAI } from './ai';
4
-
5
- describe('AI Binding Instrumentation', () => {
6
- let mockTracer: any;
7
- let mockSpan: any;
8
- let getTracerSpy: any;
9
- let mockAI: 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
- mockAI = {
37
- run: vi.fn(async () => ({ response: 'Hello world' })),
38
- someOtherMethod: vi.fn(() => 'passthrough'),
39
- someProperty: 'test-value',
40
- };
41
- });
42
-
43
- afterEach(() => {
44
- getTracerSpy.mockRestore();
45
- });
46
-
47
- describe('instrumentAI()', () => {
48
- it('should wrap AI binding with proxy', () => {
49
- const instrumented = instrumentAI(mockAI);
50
-
51
- expect(instrumented).not.toBe(mockAI);
52
- expect(typeof instrumented.run).toBe('function');
53
- });
54
-
55
- it('should create span with correct name and attributes for run()', async () => {
56
- const instrumented = instrumentAI(mockAI, 'my-ai');
57
-
58
- await instrumented.run('@cf/meta/llama-2-7b-chat-int8', { prompt: 'Hello' });
59
-
60
- expect(mockTracer.startActiveSpan).toHaveBeenCalledTimes(1);
61
-
62
- const [spanName, options] = mockTracer.startActiveSpan.mock.calls[0];
63
- expect(spanName).toBe('AI my-ai: run @cf/meta/llama-2-7b-chat-int8');
64
- expect(options.kind).toBe(SpanKind.CLIENT);
65
- expect(options.attributes['gen_ai.provider.name']).toBe('cloudflare-workers-ai');
66
- expect(options.attributes['gen_ai.operation.name']).toBe('run');
67
- expect(options.attributes['gen_ai.request.model']).toBe('@cf/meta/llama-2-7b-chat-int8');
68
- });
69
-
70
- it('should use default binding name "ai" when no name is provided', async () => {
71
- const instrumented = instrumentAI(mockAI);
72
-
73
- await instrumented.run('@cf/meta/llama-2-7b-chat-int8', { prompt: 'Hello' });
74
-
75
- const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
76
- expect(spanName).toBe('AI ai: run @cf/meta/llama-2-7b-chat-int8');
77
- });
78
-
79
- it('should record token usage when result has usage.prompt_tokens and usage.completion_tokens', async () => {
80
- mockAI.run = vi.fn(async () => ({
81
- response: 'Hello world',
82
- usage: {
83
- prompt_tokens: 10,
84
- completion_tokens: 25,
85
- },
86
- }));
87
-
88
- const instrumented = instrumentAI(mockAI);
89
-
90
- await instrumented.run('@cf/meta/llama-2-7b-chat-int8', { prompt: 'Hello' });
91
-
92
- expect(mockSpan.setAttribute).toHaveBeenCalledWith('gen_ai.usage.input_tokens', 10);
93
- expect(mockSpan.setAttribute).toHaveBeenCalledWith('gen_ai.usage.output_tokens', 25);
94
- expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
95
- expect(mockSpan.end).toHaveBeenCalled();
96
- });
97
-
98
- it('should NOT set token attributes when usage is not present', async () => {
99
- mockAI.run = vi.fn(async () => ({
100
- response: 'Hello world',
101
- }));
102
-
103
- const instrumented = instrumentAI(mockAI);
104
-
105
- await instrumented.run('@cf/meta/llama-2-7b-chat-int8', { prompt: 'Hello' });
106
-
107
- expect(mockSpan.setAttribute).not.toHaveBeenCalledWith(
108
- 'gen_ai.usage.input_tokens',
109
- expect.anything(),
110
- );
111
- expect(mockSpan.setAttribute).not.toHaveBeenCalledWith(
112
- 'gen_ai.usage.output_tokens',
113
- expect.anything(),
114
- );
115
- expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
116
- expect(mockSpan.end).toHaveBeenCalled();
117
- });
118
-
119
- it('should handle errors correctly', async () => {
120
- const testError = new Error('AI model failed');
121
- mockAI.run = vi.fn(async () => {
122
- throw testError;
123
- });
124
-
125
- const instrumented = instrumentAI(mockAI);
126
-
127
- await expect(
128
- instrumented.run('@cf/meta/llama-2-7b-chat-int8', { prompt: 'Hello' }),
129
- ).rejects.toThrow('AI model failed');
130
-
131
- expect(mockSpan.recordException).toHaveBeenCalledWith(testError);
132
- expect(mockSpan.setStatus).toHaveBeenCalledWith({
133
- code: SpanStatusCode.ERROR,
134
- message: 'AI model failed',
135
- });
136
- expect(mockSpan.end).toHaveBeenCalled();
137
- });
138
-
139
- it('should invoke run() with original object as this, not the proxy', async () => {
140
- let receivedThis: any;
141
- const mockAIObj = {
142
- run: vi.fn(async function(this: any) {
143
- // eslint-disable-next-line unicorn/no-this-assignment, @typescript-eslint/no-this-alias
144
- receivedThis = this;
145
- return { response: 'Hello' };
146
- }),
147
- };
148
- const instrumented = instrumentAI(mockAIObj as any, 'test');
149
- await instrumented.run('@cf/meta/llama-2-7b-chat-int8', { prompt: 'Hello' });
150
- expect(receivedThis).toBe(mockAIObj);
151
- });
152
-
153
- it('should pass through non-instrumented methods unchanged', () => {
154
- const instrumented = instrumentAI(mockAI);
155
-
156
- const result = instrumented.someOtherMethod();
157
-
158
- expect(result).toBe('passthrough');
159
- expect(mockAI.someOtherMethod).toHaveBeenCalled();
160
- expect(mockTracer.startActiveSpan).not.toHaveBeenCalled();
161
- });
162
-
163
- it('should pass through non-instrumented properties unchanged', () => {
164
- const instrumented = instrumentAI(mockAI);
165
-
166
- expect(instrumented.someProperty).toBe('test-value');
167
- expect(mockTracer.startActiveSpan).not.toHaveBeenCalled();
168
- });
169
- });
170
- });
@@ -1,73 +0,0 @@
1
- /**
2
- * Workers AI 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 Workers AI binding
15
- */
16
- export function instrumentAI<T extends Ai>(ai: T, bindingName?: string): T {
17
- const name = bindingName || 'ai';
18
-
19
- const handler: ProxyHandler<T> = {
20
- get(target, prop) {
21
- const value = Reflect.get(target, prop);
22
-
23
- if (prop === 'run' && typeof value === 'function') {
24
- return new Proxy(value, {
25
- apply: (fnTarget, _thisArg, args) => {
26
- const [model] = args as [string, unknown, unknown];
27
- const tracer = trace.getTracer('autotel-edge') as WorkerTracer;
28
-
29
- return tracer.startActiveSpan(
30
- `AI ${name}: run ${model}`,
31
- {
32
- kind: SpanKind.CLIENT,
33
- attributes: {
34
- // Canonical GenAI provider attribute (replaces deprecated
35
- // `gen_ai.system`).
36
- 'gen_ai.provider.name': 'cloudflare-workers-ai',
37
- 'gen_ai.operation.name': 'run',
38
- 'gen_ai.request.model': model,
39
- },
40
- },
41
- async (span) => {
42
- try {
43
- const result = await Reflect.apply(fnTarget, target, args);
44
- if (result?.usage?.prompt_tokens !== undefined) {
45
- setAttr(span, 'gen_ai.usage.input_tokens', Number(result.usage.prompt_tokens));
46
- }
47
- if (result?.usage?.completion_tokens !== undefined) {
48
- setAttr(span, 'gen_ai.usage.output_tokens', Number(result.usage.completion_tokens));
49
- }
50
- span.setStatus({ code: SpanStatusCode.OK });
51
- return result;
52
- } catch (error) {
53
- span.recordException(error as Error);
54
- span.setStatus({
55
- code: SpanStatusCode.ERROR,
56
- message: error instanceof Error ? error.message : String(error),
57
- });
58
- throw error;
59
- } finally {
60
- span.end();
61
- }
62
- },
63
- );
64
- },
65
- });
66
- }
67
-
68
- return value;
69
- },
70
- };
71
-
72
- return wrap(ai, handler);
73
- }
@@ -1,175 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { instrumentAnalyticsEngine } from './analytics-engine';
3
- import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
4
-
5
- describe('Analytics Engine Instrumentation', () => {
6
- let mockTracer: any;
7
- let mockSpan: any;
8
- let getTracerSpy: any;
9
- let mockAE: 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
- mockAE = {
37
- writeDataPoint: vi.fn(),
38
- someOtherMethod: vi.fn(() => 'passthrough-value'),
39
- };
40
- });
41
-
42
- afterEach(() => {
43
- getTracerSpy.mockRestore();
44
- });
45
-
46
- describe('writeDataPoint()', () => {
47
- it('should create span with correct attributes', () => {
48
- const instrumented = instrumentAnalyticsEngine(mockAE, 'my-dataset');
49
-
50
- instrumented.writeDataPoint({
51
- indexes: ['idx1'],
52
- doubles: [1, 2],
53
- blobs: ['blob1'],
54
- });
55
-
56
- expect(mockTracer.startActiveSpan).toHaveBeenCalledTimes(1);
57
-
58
- const [spanName, spanOptions] = mockTracer.startActiveSpan.mock.calls[0];
59
- expect(spanName).toBe('AnalyticsEngine my-dataset: writeDataPoint');
60
- expect(spanOptions.kind).toBe(SpanKind.CLIENT);
61
- expect(spanOptions.attributes['analytics.system']).toBe('cloudflare-analytics-engine');
62
- expect(spanOptions.attributes['analytics.operation']).toBe('writeDataPoint');
63
- });
64
-
65
- it('should record indexes_count, doubles_count, blobs_count', () => {
66
- const instrumented = instrumentAnalyticsEngine(mockAE, 'my-dataset');
67
-
68
- instrumented.writeDataPoint({
69
- indexes: ['idx1', 'idx2'],
70
- doubles: [1, 2, 3],
71
- blobs: ['blob1'],
72
- });
73
-
74
- const spanOptions = mockTracer.startActiveSpan.mock.calls[0][1];
75
- expect(spanOptions.attributes['analytics.indexes_count']).toBe(2);
76
- expect(spanOptions.attributes['analytics.doubles_count']).toBe(3);
77
- expect(spanOptions.attributes['analytics.blobs_count']).toBe(1);
78
- });
79
-
80
- it('should handle a single index (non-array) by recording indexes_count as 1', () => {
81
- const instrumented = instrumentAnalyticsEngine(mockAE, 'my-dataset');
82
-
83
- instrumented.writeDataPoint({
84
- indexes: 'single-index' as any,
85
- });
86
-
87
- const spanOptions = mockTracer.startActiveSpan.mock.calls[0][1];
88
- expect(spanOptions.attributes['analytics.indexes_count']).toBe(1);
89
- });
90
-
91
- it('should work with no datapoint argument', () => {
92
- const instrumented = instrumentAnalyticsEngine(mockAE);
93
-
94
- instrumented.writeDataPoint(undefined as any);
95
-
96
- expect(mockTracer.startActiveSpan).toHaveBeenCalledTimes(1);
97
-
98
- const spanOptions = mockTracer.startActiveSpan.mock.calls[0][1];
99
- expect(spanOptions.attributes['analytics.system']).toBe('cloudflare-analytics-engine');
100
- expect(spanOptions.attributes['analytics.operation']).toBe('writeDataPoint');
101
- // No count attributes should be set when no datapoint
102
- expect(spanOptions.attributes['analytics.indexes_count']).toBeUndefined();
103
- expect(spanOptions.attributes['analytics.doubles_count']).toBeUndefined();
104
- expect(spanOptions.attributes['analytics.blobs_count']).toBeUndefined();
105
-
106
- expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
107
- expect(mockSpan.end).toHaveBeenCalled();
108
- });
109
-
110
- it('should handle errors by recording exception and rethrowing', () => {
111
- const error = new Error('writeDataPoint failed');
112
- mockAE.writeDataPoint = vi.fn(() => {
113
- throw error;
114
- });
115
-
116
- const instrumented = instrumentAnalyticsEngine(mockAE, 'my-dataset');
117
-
118
- expect(() => instrumented.writeDataPoint({ indexes: ['idx1'] })).toThrow('writeDataPoint failed');
119
-
120
- expect(mockSpan.recordException).toHaveBeenCalledWith(error);
121
- expect(mockSpan.setStatus).toHaveBeenCalledWith({
122
- code: SpanStatusCode.ERROR,
123
- message: 'writeDataPoint failed',
124
- });
125
- expect(mockSpan.end).toHaveBeenCalled();
126
- });
127
-
128
- it('should use default dataset name when none provided', () => {
129
- const instrumented = instrumentAnalyticsEngine(mockAE);
130
-
131
- instrumented.writeDataPoint({ indexes: ['idx1'] });
132
-
133
- const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
134
- expect(spanName).toBe('AnalyticsEngine analytics-engine: writeDataPoint');
135
- });
136
-
137
- it('should set OK status and end span on success', () => {
138
- const instrumented = instrumentAnalyticsEngine(mockAE, 'my-dataset');
139
-
140
- instrumented.writeDataPoint({ doubles: [42] });
141
-
142
- expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: SpanStatusCode.OK });
143
- expect(mockSpan.end).toHaveBeenCalled();
144
- expect(mockAE.writeDataPoint).toHaveBeenCalledWith({ doubles: [42] });
145
- });
146
- });
147
-
148
- describe('this-binding', () => {
149
- it('should invoke writeDataPoint() with original object as this, not the proxy', () => {
150
- let receivedThis: any;
151
- const mockAEObj = {
152
- writeDataPoint: vi.fn(function(this: any) {
153
- // eslint-disable-next-line unicorn/no-this-assignment, @typescript-eslint/no-this-alias
154
- receivedThis = this;
155
- }),
156
- };
157
- const instrumented = instrumentAnalyticsEngine(mockAEObj as any, 'test');
158
- instrumented.writeDataPoint({ indexes: ['idx1'] });
159
- expect(receivedThis).toBe(mockAEObj);
160
- });
161
- });
162
-
163
- describe('Non-instrumented methods', () => {
164
- it('should pass through non-instrumented methods unchanged', () => {
165
- const instrumented = instrumentAnalyticsEngine(mockAE, 'my-dataset');
166
-
167
- const result = instrumented.someOtherMethod();
168
-
169
- expect(result).toBe('passthrough-value');
170
- expect(mockAE.someOtherMethod).toHaveBeenCalled();
171
- // No span should be created for non-instrumented methods
172
- expect(mockTracer.startActiveSpan).not.toHaveBeenCalled();
173
- });
174
- });
175
- });
@@ -1,78 +0,0 @@
1
- /**
2
- * Analytics Engine 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 } from './common';
12
-
13
- /**
14
- * Instrument Analytics Engine binding
15
- */
16
- export function instrumentAnalyticsEngine<T extends AnalyticsEngineDataset>(ae: T, datasetName?: string): T {
17
- const name = datasetName || 'analytics-engine';
18
-
19
- const handler: ProxyHandler<T> = {
20
- get(target, prop) {
21
- const value = Reflect.get(target, prop);
22
-
23
- if (prop === 'writeDataPoint' && typeof value === 'function') {
24
- return new Proxy(value, {
25
- apply: (fnTarget, _thisArg, args) => {
26
- const [dataPoint] = args as [AnalyticsEngineDataPoint | undefined];
27
- const tracer = trace.getTracer('autotel-edge') as WorkerTracer;
28
-
29
- const attributes: Record<string, string | number> = {
30
- 'analytics.system': 'cloudflare-analytics-engine',
31
- 'analytics.operation': 'writeDataPoint',
32
- };
33
-
34
- if (dataPoint) {
35
- if (dataPoint.indexes) {
36
- attributes['analytics.indexes_count'] = Array.isArray(dataPoint.indexes) ? dataPoint.indexes.length : 1;
37
- }
38
- if (dataPoint.doubles) {
39
- attributes['analytics.doubles_count'] = dataPoint.doubles.length;
40
- }
41
- if (dataPoint.blobs) {
42
- attributes['analytics.blobs_count'] = dataPoint.blobs.length;
43
- }
44
- }
45
-
46
- return tracer.startActiveSpan(
47
- `AnalyticsEngine ${name}: writeDataPoint`,
48
- {
49
- kind: SpanKind.CLIENT,
50
- attributes,
51
- },
52
- (span) => {
53
- try {
54
- // writeDataPoint is synchronous/void
55
- Reflect.apply(fnTarget, target, args);
56
- span.setStatus({ code: SpanStatusCode.OK });
57
- } catch (error) {
58
- span.recordException(error as Error);
59
- span.setStatus({
60
- code: SpanStatusCode.ERROR,
61
- message: error instanceof Error ? error.message : String(error),
62
- });
63
- throw error;
64
- } finally {
65
- span.end();
66
- }
67
- },
68
- );
69
- },
70
- });
71
- }
72
-
73
- return value;
74
- },
75
- };
76
-
77
- return wrap(ae, handler);
78
- }
@@ -1,80 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { trace } from '@opentelemetry/api';
3
- import { instrumentBindings } from './bindings';
4
- import { isWrapped } from './common';
5
-
6
- describe('instrumentBindings() caching', () => {
7
- let mockTracer: any;
8
- let mockSpan: any;
9
- let getTracerSpy: 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
-
37
- afterEach(() => {
38
- getTracerSpy.mockRestore();
39
- });
40
-
41
- it('should return the same instrumented object for the same env reference', () => {
42
- const env = {
43
- MY_KV: { get: vi.fn(), put: vi.fn(), delete: vi.fn(), list: vi.fn() },
44
- };
45
-
46
- const first = instrumentBindings(env);
47
- const second = instrumentBindings(env);
48
-
49
- expect(first).toBe(second);
50
- });
51
-
52
- it('should return different instrumented objects for different env references', () => {
53
- const env1 = {
54
- MY_KV: { get: vi.fn(), put: vi.fn(), delete: vi.fn(), list: vi.fn() },
55
- };
56
- const env2 = {
57
- MY_KV: { get: vi.fn(), put: vi.fn(), delete: vi.fn(), list: vi.fn() },
58
- };
59
-
60
- const first = instrumentBindings(env1);
61
- const second = instrumentBindings(env2);
62
-
63
- expect(first).not.toBe(second);
64
- });
65
-
66
- it('should correctly instrument bindings even when returning from cache', () => {
67
- const env = {
68
- MY_KV: { get: vi.fn(), put: vi.fn(), delete: vi.fn(), list: vi.fn() },
69
- API_KEY: 'secret',
70
- };
71
-
72
- const first = instrumentBindings(env);
73
- const second = instrumentBindings(env);
74
-
75
- // Cached result should have instrumented bindings
76
- expect(isWrapped(second.MY_KV)).toBe(true);
77
- // Non-object values pass through
78
- expect(second.API_KEY).toBe('secret');
79
- });
80
- });