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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +432 -0
  3. package/dist/actors.d.ts +248 -0
  4. package/dist/actors.js +1030 -0
  5. package/dist/actors.js.map +1 -0
  6. package/dist/agents.d.ts +219 -0
  7. package/dist/agents.js +276 -0
  8. package/dist/agents.js.map +1 -0
  9. package/dist/bindings.d.ts +40 -0
  10. package/dist/bindings.js +4 -0
  11. package/dist/bindings.js.map +1 -0
  12. package/dist/chunk-JDPN3HND.js +520 -0
  13. package/dist/chunk-JDPN3HND.js.map +1 -0
  14. package/dist/chunk-QXFYTHQF.js +298 -0
  15. package/dist/chunk-QXFYTHQF.js.map +1 -0
  16. package/dist/chunk-SKKRPS5K.js +50 -0
  17. package/dist/chunk-SKKRPS5K.js.map +1 -0
  18. package/dist/events.d.ts +1 -0
  19. package/dist/events.js +3 -0
  20. package/dist/events.js.map +1 -0
  21. package/dist/handlers.d.ts +121 -0
  22. package/dist/handlers.js +4 -0
  23. package/dist/handlers.js.map +1 -0
  24. package/dist/index.d.ts +144 -0
  25. package/dist/index.js +576 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/logger.d.ts +1 -0
  28. package/dist/logger.js +3 -0
  29. package/dist/logger.js.map +1 -0
  30. package/dist/sampling.d.ts +4 -0
  31. package/dist/sampling.js +3 -0
  32. package/dist/sampling.js.map +1 -0
  33. package/dist/testing.d.ts +1 -0
  34. package/dist/testing.js +3 -0
  35. package/dist/testing.js.map +1 -0
  36. package/package.json +107 -0
  37. package/src/actors/alarms.ts +225 -0
  38. package/src/actors/index.ts +36 -0
  39. package/src/actors/instrument-actor.test.ts +179 -0
  40. package/src/actors/instrument-actor.ts +574 -0
  41. package/src/actors/sockets.ts +217 -0
  42. package/src/actors/storage.ts +263 -0
  43. package/src/actors/traced-handler.ts +300 -0
  44. package/src/actors/types.ts +98 -0
  45. package/src/actors.ts +50 -0
  46. package/src/agents/index.ts +42 -0
  47. package/src/agents/otel-observability.test.ts +329 -0
  48. package/src/agents/otel-observability.ts +465 -0
  49. package/src/agents/types.ts +167 -0
  50. package/src/agents.ts +76 -0
  51. package/src/bindings/bindings.ts +621 -0
  52. package/src/bindings/common.ts +75 -0
  53. package/src/bindings/index.ts +12 -0
  54. package/src/bindings.ts +6 -0
  55. package/src/events.ts +6 -0
  56. package/src/global/cache.test.ts +292 -0
  57. package/src/global/cache.ts +164 -0
  58. package/src/global/fetch.test.ts +344 -0
  59. package/src/global/fetch.ts +134 -0
  60. package/src/global/index.ts +7 -0
  61. package/src/handlers/durable-objects.test.ts +524 -0
  62. package/src/handlers/durable-objects.ts +250 -0
  63. package/src/handlers/index.ts +6 -0
  64. package/src/handlers/workflows.ts +318 -0
  65. package/src/handlers.ts +6 -0
  66. package/src/index.ts +57 -0
  67. package/src/logger.ts +6 -0
  68. package/src/sampling.ts +6 -0
  69. package/src/testing.ts +6 -0
  70. package/src/wrappers/index.ts +8 -0
  71. package/src/wrappers/instrument.integration.test.ts +468 -0
  72. package/src/wrappers/instrument.ts +643 -0
  73. package/src/wrappers/wrap-do.ts +34 -0
  74. package/src/wrappers/wrap-module.ts +37 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Actor sockets instrumentation
3
+ *
4
+ * Traces operations on actor.sockets
5
+ */
6
+
7
+ import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
8
+ import type { WorkerTracer } from 'autotel-edge';
9
+ import { wrap } from '../bindings/common';
10
+ import type { ActorLike } from './types';
11
+
12
+ /**
13
+ * Get the tracer instance
14
+ */
15
+ function getTracer(): WorkerTracer {
16
+ return trace.getTracer('autotel-cloudflare-actors') as WorkerTracer;
17
+ }
18
+
19
+ /**
20
+ * Instrument Actor sockets for tracing
21
+ *
22
+ * Captures:
23
+ * - acceptWebSocket: Accept an incoming WebSocket connection
24
+ * - broadcast: Send message to all connected sockets
25
+ * - send: Send message to a specific socket
26
+ */
27
+ export function instrumentActorSockets(
28
+ sockets: unknown,
29
+ actorInstance: ActorLike,
30
+ actorClass: object,
31
+ ): unknown {
32
+ if (!sockets || typeof sockets !== 'object') {
33
+ return sockets;
34
+ }
35
+
36
+ const actorClassName = (actorClass as { name?: string }).name || 'Actor';
37
+ const actorName = actorInstance.name || actorClassName;
38
+
39
+ const socketsHandler: ProxyHandler<object> = {
40
+ get(target, prop) {
41
+ const value = Reflect.get(target, prop);
42
+
43
+ // Instrument acceptWebSocket method
44
+ if (prop === 'acceptWebSocket' && typeof value === 'function') {
45
+ return function instrumentedAcceptWebSocket(
46
+ this: unknown,
47
+ request: Request,
48
+ ): unknown {
49
+ const tracer = getTracer();
50
+ const spanName = `Actor ${actorName}: sockets.acceptWebSocket`;
51
+
52
+ return tracer.startActiveSpan(
53
+ spanName,
54
+ {
55
+ kind: SpanKind.SERVER,
56
+ attributes: {
57
+ 'actor.name': actorName,
58
+ 'actor.class': actorClassName,
59
+ 'websocket.operation': 'accept',
60
+ 'url.full': request.url,
61
+ },
62
+ },
63
+ (span) => {
64
+ try {
65
+ const result = value.call(target, request);
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
+ // Instrument broadcast method
84
+ if (prop === 'broadcast' && typeof value === 'function') {
85
+ return function instrumentedBroadcast(this: unknown, message: unknown): void {
86
+ const tracer = getTracer();
87
+ const spanName = `Actor ${actorName}: sockets.broadcast`;
88
+
89
+ tracer.startActiveSpan(
90
+ spanName,
91
+ {
92
+ kind: SpanKind.PRODUCER,
93
+ attributes: {
94
+ 'actor.name': actorName,
95
+ 'actor.class': actorClassName,
96
+ 'websocket.operation': 'broadcast',
97
+ 'websocket.message.type': typeof message,
98
+ 'websocket.message.size':
99
+ typeof message === 'string'
100
+ ? message.length
101
+ : message instanceof ArrayBuffer
102
+ ? message.byteLength
103
+ : 0,
104
+ },
105
+ },
106
+ (span) => {
107
+ try {
108
+ value.call(target, message);
109
+ span.setStatus({ code: SpanStatusCode.OK });
110
+ } catch (error) {
111
+ span.recordException(error as Error);
112
+ span.setStatus({
113
+ code: SpanStatusCode.ERROR,
114
+ message: error instanceof Error ? error.message : String(error),
115
+ });
116
+ throw error;
117
+ } finally {
118
+ span.end();
119
+ }
120
+ },
121
+ );
122
+ };
123
+ }
124
+
125
+ // Instrument send method
126
+ if (prop === 'send' && typeof value === 'function') {
127
+ return function instrumentedSend(this: unknown, ws: WebSocket, message: unknown): void {
128
+ const tracer = getTracer();
129
+ const spanName = `Actor ${actorName}: sockets.send`;
130
+
131
+ tracer.startActiveSpan(
132
+ spanName,
133
+ {
134
+ kind: SpanKind.PRODUCER,
135
+ attributes: {
136
+ 'actor.name': actorName,
137
+ 'actor.class': actorClassName,
138
+ 'websocket.operation': 'send',
139
+ 'websocket.message.type': typeof message,
140
+ 'websocket.message.size':
141
+ typeof message === 'string'
142
+ ? message.length
143
+ : message instanceof ArrayBuffer
144
+ ? message.byteLength
145
+ : 0,
146
+ },
147
+ },
148
+ (span) => {
149
+ try {
150
+ value.call(target, ws, message);
151
+ span.setStatus({ code: SpanStatusCode.OK });
152
+ } catch (error) {
153
+ span.recordException(error as Error);
154
+ span.setStatus({
155
+ code: SpanStatusCode.ERROR,
156
+ message: error instanceof Error ? error.message : String(error),
157
+ });
158
+ throw error;
159
+ } finally {
160
+ span.end();
161
+ }
162
+ },
163
+ );
164
+ };
165
+ }
166
+
167
+ // Instrument getConnections method (if exists)
168
+ if (prop === 'getConnections' && typeof value === 'function') {
169
+ return function instrumentedGetConnections(this: unknown): unknown {
170
+ const tracer = getTracer();
171
+ const spanName = `Actor ${actorName}: sockets.getConnections`;
172
+
173
+ return tracer.startActiveSpan(
174
+ spanName,
175
+ {
176
+ kind: SpanKind.CLIENT,
177
+ attributes: {
178
+ 'actor.name': actorName,
179
+ 'actor.class': actorClassName,
180
+ 'websocket.operation': 'getConnections',
181
+ },
182
+ },
183
+ (span) => {
184
+ try {
185
+ const result = value.call(target);
186
+ // Try to capture connection count if result is array-like
187
+ if (Array.isArray(result)) {
188
+ span.setAttribute('websocket.connections.count', result.length);
189
+ }
190
+ span.setStatus({ code: SpanStatusCode.OK });
191
+ return result;
192
+ } catch (error) {
193
+ span.recordException(error as Error);
194
+ span.setStatus({
195
+ code: SpanStatusCode.ERROR,
196
+ message: error instanceof Error ? error.message : String(error),
197
+ });
198
+ throw error;
199
+ } finally {
200
+ span.end();
201
+ }
202
+ },
203
+ );
204
+ };
205
+ }
206
+
207
+ // Bind other methods to the target
208
+ if (typeof value === 'function') {
209
+ return value.bind(target);
210
+ }
211
+
212
+ return value;
213
+ },
214
+ };
215
+
216
+ return wrap(sockets, socketsHandler);
217
+ }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Actor storage instrumentation
3
+ *
4
+ * Traces operations on actor.storage including SQL queries
5
+ */
6
+
7
+ import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
8
+ import type { WorkerTracer } from 'autotel-edge';
9
+ import { wrap } from '../bindings/common';
10
+ import type { ActorLike } from './types';
11
+
12
+ /**
13
+ * Get the tracer instance
14
+ */
15
+ function getTracer(): WorkerTracer {
16
+ return trace.getTracer('autotel-cloudflare-actors') as WorkerTracer;
17
+ }
18
+
19
+ /**
20
+ * Instrument Actor storage for tracing
21
+ *
22
+ * Captures:
23
+ * - SQL query operations
24
+ * - Key-value operations (if available)
25
+ */
26
+ export function instrumentActorStorage(
27
+ storage: unknown,
28
+ actorInstance: ActorLike,
29
+ actorClass: object,
30
+ ): unknown {
31
+ if (!storage || typeof storage !== 'object') {
32
+ return storage;
33
+ }
34
+
35
+ const actorClassName = (actorClass as { name?: string }).name || 'Actor';
36
+ const actorName = actorInstance.name || actorClassName;
37
+
38
+ const storageHandler: ProxyHandler<object> = {
39
+ get(target, prop) {
40
+ const value = Reflect.get(target, prop);
41
+
42
+ // Instrument SQL query method if it exists
43
+ // The Actors Storage class has an exec method for SQL
44
+ if (prop === 'exec' && typeof value === 'function') {
45
+ return function instrumentedExec(
46
+ this: unknown,
47
+ query: string,
48
+ ...params: unknown[]
49
+ ): unknown {
50
+ const tracer = getTracer();
51
+ const spanName = `Actor ${actorName}: storage.exec`;
52
+
53
+ return tracer.startActiveSpan(
54
+ spanName,
55
+ {
56
+ kind: SpanKind.CLIENT,
57
+ attributes: {
58
+ 'actor.name': actorName,
59
+ 'actor.class': actorClassName,
60
+ 'db.system': 'sqlite',
61
+ 'db.operation': 'exec',
62
+ 'db.statement': query,
63
+ 'db.statement.params_count': params.length,
64
+ },
65
+ },
66
+ (span) => {
67
+ try {
68
+ const result = value.call(target, query, ...params);
69
+ span.setStatus({ code: SpanStatusCode.OK });
70
+ return result;
71
+ } catch (error) {
72
+ span.recordException(error as Error);
73
+ span.setStatus({
74
+ code: SpanStatusCode.ERROR,
75
+ message: error instanceof Error ? error.message : String(error),
76
+ });
77
+ throw error;
78
+ } finally {
79
+ span.end();
80
+ }
81
+ },
82
+ );
83
+ };
84
+ }
85
+
86
+ // Instrument query method (alternative name for exec)
87
+ if (prop === 'query' && typeof value === 'function') {
88
+ return function instrumentedQuery(
89
+ this: unknown,
90
+ query: string,
91
+ ...params: unknown[]
92
+ ): unknown {
93
+ const tracer = getTracer();
94
+ const spanName = `Actor ${actorName}: storage.query`;
95
+
96
+ return tracer.startActiveSpan(
97
+ spanName,
98
+ {
99
+ kind: SpanKind.CLIENT,
100
+ attributes: {
101
+ 'actor.name': actorName,
102
+ 'actor.class': actorClassName,
103
+ 'db.system': 'sqlite',
104
+ 'db.operation': 'query',
105
+ 'db.statement': query,
106
+ 'db.statement.params_count': params.length,
107
+ },
108
+ },
109
+ (span) => {
110
+ try {
111
+ const result = value.call(target, query, ...params);
112
+ span.setStatus({ code: SpanStatusCode.OK });
113
+ return result;
114
+ } catch (error) {
115
+ span.recordException(error as Error);
116
+ span.setStatus({
117
+ code: SpanStatusCode.ERROR,
118
+ message: error instanceof Error ? error.message : String(error),
119
+ });
120
+ throw error;
121
+ } finally {
122
+ span.end();
123
+ }
124
+ },
125
+ );
126
+ };
127
+ }
128
+
129
+ // Instrument get method
130
+ if (prop === 'get' && typeof value === 'function') {
131
+ return async function instrumentedGet(this: unknown, key: string): Promise<unknown> {
132
+ const tracer = getTracer();
133
+ const spanName = `Actor ${actorName}: storage.get`;
134
+
135
+ return tracer.startActiveSpan(
136
+ spanName,
137
+ {
138
+ kind: SpanKind.CLIENT,
139
+ attributes: {
140
+ 'actor.name': actorName,
141
+ 'actor.class': actorClassName,
142
+ 'db.system': 'durable_object_storage',
143
+ 'db.operation': 'get',
144
+ 'db.key': key,
145
+ },
146
+ },
147
+ async (span) => {
148
+ try {
149
+ const result = await value.call(target, key);
150
+ span.setAttributes({
151
+ 'db.result.found': result !== null && result !== undefined,
152
+ });
153
+ span.setStatus({ code: SpanStatusCode.OK });
154
+ return result;
155
+ } catch (error) {
156
+ span.recordException(error as Error);
157
+ span.setStatus({
158
+ code: SpanStatusCode.ERROR,
159
+ message: error instanceof Error ? error.message : String(error),
160
+ });
161
+ throw error;
162
+ } finally {
163
+ span.end();
164
+ }
165
+ },
166
+ );
167
+ };
168
+ }
169
+
170
+ // Instrument put method
171
+ if (prop === 'put' && typeof value === 'function') {
172
+ return async function instrumentedPut(
173
+ this: unknown,
174
+ key: string,
175
+ val: unknown,
176
+ ): Promise<void> {
177
+ const tracer = getTracer();
178
+ const spanName = `Actor ${actorName}: storage.put`;
179
+
180
+ return tracer.startActiveSpan(
181
+ spanName,
182
+ {
183
+ kind: SpanKind.CLIENT,
184
+ attributes: {
185
+ 'actor.name': actorName,
186
+ 'actor.class': actorClassName,
187
+ 'db.system': 'durable_object_storage',
188
+ 'db.operation': 'put',
189
+ 'db.key': key,
190
+ 'db.value_type': typeof val,
191
+ },
192
+ },
193
+ async (span) => {
194
+ try {
195
+ await value.call(target, key, val);
196
+ span.setStatus({ code: SpanStatusCode.OK });
197
+ } catch (error) {
198
+ span.recordException(error as Error);
199
+ span.setStatus({
200
+ code: SpanStatusCode.ERROR,
201
+ message: error instanceof Error ? error.message : String(error),
202
+ });
203
+ throw error;
204
+ } finally {
205
+ span.end();
206
+ }
207
+ },
208
+ );
209
+ };
210
+ }
211
+
212
+ // Instrument delete method
213
+ if (prop === 'delete' && typeof value === 'function') {
214
+ return async function instrumentedDelete(this: unknown, key: string): Promise<boolean> {
215
+ const tracer = getTracer();
216
+ const spanName = `Actor ${actorName}: storage.delete`;
217
+
218
+ return tracer.startActiveSpan(
219
+ spanName,
220
+ {
221
+ kind: SpanKind.CLIENT,
222
+ attributes: {
223
+ 'actor.name': actorName,
224
+ 'actor.class': actorClassName,
225
+ 'db.system': 'durable_object_storage',
226
+ 'db.operation': 'delete',
227
+ 'db.key': key,
228
+ },
229
+ },
230
+ async (span) => {
231
+ try {
232
+ const result = await value.call(target, key);
233
+ span.setAttributes({
234
+ 'db.result.deleted': result,
235
+ });
236
+ span.setStatus({ code: SpanStatusCode.OK });
237
+ return result;
238
+ } catch (error) {
239
+ span.recordException(error as Error);
240
+ span.setStatus({
241
+ code: SpanStatusCode.ERROR,
242
+ message: error instanceof Error ? error.message : String(error),
243
+ });
244
+ throw error;
245
+ } finally {
246
+ span.end();
247
+ }
248
+ },
249
+ );
250
+ };
251
+ }
252
+
253
+ // Bind other methods to the target
254
+ if (typeof value === 'function') {
255
+ return value.bind(target);
256
+ }
257
+
258
+ return value;
259
+ },
260
+ };
261
+
262
+ return wrap(storage, storageHandler);
263
+ }