duron 0.2.1 → 0.3.0-beta.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 (77) hide show
  1. package/dist/action-job.d.ts +2 -0
  2. package/dist/action-job.d.ts.map +1 -1
  3. package/dist/action-job.js +20 -1
  4. package/dist/action-manager.d.ts +2 -0
  5. package/dist/action-manager.d.ts.map +1 -1
  6. package/dist/action-manager.js +3 -0
  7. package/dist/action.d.ts +7 -0
  8. package/dist/action.d.ts.map +1 -1
  9. package/dist/action.js +1 -0
  10. package/dist/adapters/adapter.d.ts +10 -2
  11. package/dist/adapters/adapter.d.ts.map +1 -1
  12. package/dist/adapters/adapter.js +59 -1
  13. package/dist/adapters/postgres/base.d.ts +9 -4
  14. package/dist/adapters/postgres/base.d.ts.map +1 -1
  15. package/dist/adapters/postgres/base.js +269 -19
  16. package/dist/adapters/postgres/schema.d.ts +249 -105
  17. package/dist/adapters/postgres/schema.d.ts.map +1 -1
  18. package/dist/adapters/postgres/schema.default.d.ts +249 -106
  19. package/dist/adapters/postgres/schema.default.d.ts.map +1 -1
  20. package/dist/adapters/postgres/schema.default.js +2 -2
  21. package/dist/adapters/postgres/schema.js +29 -1
  22. package/dist/adapters/schemas.d.ts +140 -7
  23. package/dist/adapters/schemas.d.ts.map +1 -1
  24. package/dist/adapters/schemas.js +52 -4
  25. package/dist/client.d.ts +8 -1
  26. package/dist/client.d.ts.map +1 -1
  27. package/dist/client.js +29 -1
  28. package/dist/errors.d.ts +6 -0
  29. package/dist/errors.d.ts.map +1 -1
  30. package/dist/errors.js +16 -1
  31. package/dist/index.d.ts +3 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3 -1
  34. package/dist/server.d.ts +220 -16
  35. package/dist/server.d.ts.map +1 -1
  36. package/dist/server.js +123 -8
  37. package/dist/step-manager.d.ts +8 -2
  38. package/dist/step-manager.d.ts.map +1 -1
  39. package/dist/step-manager.js +138 -15
  40. package/dist/telemetry/adapter.d.ts +85 -0
  41. package/dist/telemetry/adapter.d.ts.map +1 -0
  42. package/dist/telemetry/adapter.js +128 -0
  43. package/dist/telemetry/index.d.ts +5 -0
  44. package/dist/telemetry/index.d.ts.map +1 -0
  45. package/dist/telemetry/index.js +4 -0
  46. package/dist/telemetry/local.d.ts +21 -0
  47. package/dist/telemetry/local.d.ts.map +1 -0
  48. package/dist/telemetry/local.js +180 -0
  49. package/dist/telemetry/noop.d.ts +16 -0
  50. package/dist/telemetry/noop.d.ts.map +1 -0
  51. package/dist/telemetry/noop.js +39 -0
  52. package/dist/telemetry/opentelemetry.d.ts +24 -0
  53. package/dist/telemetry/opentelemetry.d.ts.map +1 -0
  54. package/dist/telemetry/opentelemetry.js +202 -0
  55. package/migrations/postgres/20260117231749_clumsy_penance/migration.sql +3 -0
  56. package/migrations/postgres/20260117231749_clumsy_penance/snapshot.json +988 -0
  57. package/migrations/postgres/20260118202533_wealthy_mysterio/migration.sql +24 -0
  58. package/migrations/postgres/20260118202533_wealthy_mysterio/snapshot.json +1362 -0
  59. package/package.json +6 -4
  60. package/src/action-job.ts +35 -0
  61. package/src/action-manager.ts +5 -0
  62. package/src/action.ts +56 -0
  63. package/src/adapters/adapter.ts +151 -0
  64. package/src/adapters/postgres/base.ts +342 -23
  65. package/src/adapters/postgres/schema.default.ts +2 -2
  66. package/src/adapters/postgres/schema.ts +49 -1
  67. package/src/adapters/schemas.ts +81 -5
  68. package/src/client.ts +80 -2
  69. package/src/errors.ts +45 -1
  70. package/src/index.ts +3 -1
  71. package/src/server.ts +163 -8
  72. package/src/step-manager.ts +232 -13
  73. package/src/telemetry/adapter.ts +468 -0
  74. package/src/telemetry/index.ts +17 -0
  75. package/src/telemetry/local.ts +336 -0
  76. package/src/telemetry/noop.ts +95 -0
  77. package/src/telemetry/opentelemetry.ts +310 -0
@@ -0,0 +1,468 @@
1
+ import type { Logger } from 'pino'
2
+
3
+ import type { Adapter } from '../adapters/adapter.js'
4
+
5
+ // ============================================================================
6
+ // Types
7
+ // ============================================================================
8
+
9
+ /**
10
+ * Interface representing the minimal Duron client required by telemetry adapters.
11
+ * This avoids circular dependencies by using a minimal interface.
12
+ */
13
+ export interface TelemetryClient {
14
+ /**
15
+ * The database adapter instance.
16
+ */
17
+ database: Adapter
18
+ }
19
+
20
+ /**
21
+ * Span represents a trace span for job or step execution.
22
+ */
23
+ export interface Span {
24
+ /**
25
+ * Unique identifier for this span.
26
+ */
27
+ id: string
28
+
29
+ /**
30
+ * The job ID this span belongs to.
31
+ */
32
+ jobId: string
33
+
34
+ /**
35
+ * The step ID this span belongs to (null for job spans).
36
+ */
37
+ stepId: string | null
38
+
39
+ /**
40
+ * Parent span ID for nested spans.
41
+ */
42
+ parentSpanId: string | null
43
+ }
44
+
45
+ /**
46
+ * Options for starting a job span.
47
+ */
48
+ export interface StartJobSpanOptions {
49
+ jobId: string
50
+ actionName: string
51
+ groupKey: string
52
+ input?: any
53
+ }
54
+
55
+ /**
56
+ * Options for starting a step span.
57
+ */
58
+ export interface StartStepSpanOptions {
59
+ jobId: string
60
+ stepId: string
61
+ stepName: string
62
+ parentSpan?: Span
63
+ parentStepId: string | null
64
+ }
65
+
66
+ /**
67
+ * Options for ending a span.
68
+ */
69
+ export interface EndSpanOptions {
70
+ status: 'ok' | 'error' | 'cancelled'
71
+ error?: any
72
+ }
73
+
74
+ /**
75
+ * Options for starting a database span.
76
+ */
77
+ export interface StartDatabaseSpanOptions {
78
+ operation: string
79
+ query?: string
80
+ }
81
+
82
+ /**
83
+ * Options for recording a metric.
84
+ */
85
+ export interface RecordMetricOptions {
86
+ jobId: string
87
+ stepId?: string
88
+ name: string
89
+ value: number
90
+ attributes?: Record<string, any>
91
+ }
92
+
93
+ /**
94
+ * Options for adding a span event.
95
+ */
96
+ export interface AddSpanEventOptions {
97
+ span: Span
98
+ name: string
99
+ attributes?: Record<string, any>
100
+ }
101
+
102
+ /**
103
+ * Options for adding a span attribute.
104
+ */
105
+ export interface AddSpanAttributeOptions {
106
+ span: Span
107
+ key: string
108
+ value: string | number | boolean
109
+ }
110
+
111
+ /**
112
+ * Observe context provided to action and step handlers.
113
+ */
114
+ export interface ObserveContext {
115
+ /**
116
+ * Record a custom metric.
117
+ *
118
+ * @param name - The metric name (e.g., 'ai.tokens.total', 'processing.duration_ms')
119
+ * @param value - The metric value
120
+ * @param attributes - Optional attributes for the metric
121
+ */
122
+ recordMetric(name: string, value: number, attributes?: Record<string, any>): void
123
+
124
+ /**
125
+ * Add an attribute to the current span.
126
+ *
127
+ * @param key - The attribute key
128
+ * @param value - The attribute value
129
+ */
130
+ addSpanAttribute(key: string, value: string | number | boolean): void
131
+
132
+ /**
133
+ * Add an event to the current span.
134
+ *
135
+ * @param name - The event name
136
+ * @param attributes - Optional event attributes
137
+ */
138
+ addSpanEvent(name: string, attributes?: Record<string, any>): void
139
+ }
140
+
141
+ // ============================================================================
142
+ // Abstract Telemetry Adapter
143
+ // ============================================================================
144
+
145
+ /**
146
+ * Abstract base class for telemetry adapters.
147
+ * All telemetry adapters must extend this class and implement its abstract methods.
148
+ */
149
+ export abstract class TelemetryAdapter {
150
+ #logger: Logger | null = null
151
+ #client: TelemetryClient | null = null
152
+ #started: boolean = false
153
+ #stopped: boolean = false
154
+ #starting: Promise<boolean> | null = null
155
+ #stopping: Promise<boolean> | null = null
156
+
157
+ // ============================================================================
158
+ // Lifecycle Methods
159
+ // ============================================================================
160
+
161
+ /**
162
+ * Start the telemetry adapter.
163
+ * Performs any necessary initialization.
164
+ *
165
+ * @returns Promise resolving to `true` if started successfully, `false` otherwise
166
+ */
167
+ async start(): Promise<boolean> {
168
+ try {
169
+ if (this.#stopping || this.#stopped) {
170
+ return false
171
+ }
172
+
173
+ if (this.#started) {
174
+ return true
175
+ }
176
+
177
+ if (this.#starting) {
178
+ return this.#starting
179
+ }
180
+
181
+ this.#starting = (async () => {
182
+ await this._start()
183
+ this.#started = true
184
+ this.#starting = null
185
+ return true
186
+ })()
187
+
188
+ return this.#starting
189
+ } catch (error) {
190
+ this.#logger?.error(error, 'Error in TelemetryAdapter.start()')
191
+ throw error
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Stop the telemetry adapter.
197
+ * Performs cleanup.
198
+ *
199
+ * @returns Promise resolving to `true` if stopped successfully, `false` otherwise
200
+ */
201
+ async stop(): Promise<boolean> {
202
+ try {
203
+ if (this.#stopped) {
204
+ return true
205
+ }
206
+
207
+ if (this.#stopping) {
208
+ return this.#stopping
209
+ }
210
+
211
+ this.#stopping = (async () => {
212
+ await this._stop()
213
+ this.#stopped = true
214
+ this.#stopping = null
215
+ return true
216
+ })()
217
+
218
+ return this.#stopping
219
+ } catch (error) {
220
+ this.#logger?.error(error, 'Error in TelemetryAdapter.stop()')
221
+ throw error
222
+ }
223
+ }
224
+
225
+ // ============================================================================
226
+ // Configuration Methods
227
+ // ============================================================================
228
+
229
+ /**
230
+ * Set the logger instance for this adapter.
231
+ *
232
+ * @param logger - The logger instance to use for logging
233
+ */
234
+ setLogger(logger: Logger): void {
235
+ this.#logger = logger
236
+ }
237
+
238
+ /**
239
+ * Get the logger instance for this adapter.
240
+ *
241
+ * @returns The logger instance, or `null` if not set
242
+ */
243
+ get logger(): Logger | null {
244
+ return this.#logger
245
+ }
246
+
247
+ /**
248
+ * Set the Duron client instance for this adapter.
249
+ * This is called automatically by the Duron client during initialization.
250
+ *
251
+ * @param client - The Duron client instance
252
+ */
253
+ setClient(client: TelemetryClient): void {
254
+ this.#client = client
255
+ }
256
+
257
+ /**
258
+ * Get the Duron client instance.
259
+ * Available to subclasses for accessing the database adapter.
260
+ *
261
+ * @returns The Duron client instance, or `null` if not set
262
+ */
263
+ protected get client(): TelemetryClient | null {
264
+ return this.#client
265
+ }
266
+
267
+ // ============================================================================
268
+ // Span Methods
269
+ // ============================================================================
270
+
271
+ /**
272
+ * Start a span for job execution.
273
+ *
274
+ * @param options - Options for the job span
275
+ * @returns The created span
276
+ */
277
+ async startJobSpan(options: StartJobSpanOptions): Promise<Span> {
278
+ await this.start()
279
+ return this._startJobSpan(options)
280
+ }
281
+
282
+ /**
283
+ * End a job span.
284
+ *
285
+ * @param span - The span to end
286
+ * @param options - End options including status and error
287
+ */
288
+ async endJobSpan(span: Span, options: EndSpanOptions): Promise<void> {
289
+ await this.start()
290
+ return this._endJobSpan(span, options)
291
+ }
292
+
293
+ /**
294
+ * Start a span for step execution.
295
+ *
296
+ * @param options - Options for the step span
297
+ * @returns The created span
298
+ */
299
+ async startStepSpan(options: StartStepSpanOptions): Promise<Span> {
300
+ await this.start()
301
+ return this._startStepSpan(options)
302
+ }
303
+
304
+ /**
305
+ * End a step span.
306
+ *
307
+ * @param span - The span to end
308
+ * @param options - End options including status and error
309
+ */
310
+ async endStepSpan(span: Span, options: EndSpanOptions): Promise<void> {
311
+ await this.start()
312
+ return this._endStepSpan(span, options)
313
+ }
314
+
315
+ /**
316
+ * Start a span for database operation (optional tracing).
317
+ *
318
+ * @param options - Options for the database span
319
+ * @returns The created span, or null if database tracing is disabled
320
+ */
321
+ async startDatabaseSpan(options: StartDatabaseSpanOptions): Promise<Span | null> {
322
+ await this.start()
323
+ return this._startDatabaseSpan(options)
324
+ }
325
+
326
+ /**
327
+ * End a database span.
328
+ *
329
+ * @param span - The span to end
330
+ * @param options - End options including status and error
331
+ */
332
+ async endDatabaseSpan(span: Span | null, options: EndSpanOptions): Promise<void> {
333
+ if (!span) return
334
+ await this.start()
335
+ return this._endDatabaseSpan(span, options)
336
+ }
337
+
338
+ // ============================================================================
339
+ // Metrics Methods
340
+ // ============================================================================
341
+
342
+ /**
343
+ * Record a metric.
344
+ *
345
+ * @param options - Options for recording the metric
346
+ */
347
+ async recordMetric(options: RecordMetricOptions): Promise<void> {
348
+ await this.start()
349
+ return this._recordMetric(options)
350
+ }
351
+
352
+ /**
353
+ * Add an event to a span.
354
+ *
355
+ * @param options - Options for the span event
356
+ */
357
+ async addSpanEvent(options: AddSpanEventOptions): Promise<void> {
358
+ await this.start()
359
+ return this._addSpanEvent(options)
360
+ }
361
+
362
+ /**
363
+ * Add an attribute to a span.
364
+ *
365
+ * @param options - Options for the span attribute
366
+ */
367
+ async addSpanAttribute(options: AddSpanAttributeOptions): Promise<void> {
368
+ await this.start()
369
+ return this._addSpanAttribute(options)
370
+ }
371
+
372
+ // ============================================================================
373
+ // Context Methods
374
+ // ============================================================================
375
+
376
+ /**
377
+ * Create an observe context for action/step handlers.
378
+ *
379
+ * @param jobId - The job ID
380
+ * @param stepId - The step ID (optional)
381
+ * @param span - The current span
382
+ * @returns ObserveContext for use in handlers
383
+ */
384
+ createObserveContext(jobId: string, stepId: string | null, span: Span): ObserveContext {
385
+ return {
386
+ recordMetric: (name: string, value: number, attributes?: Record<string, any>) => {
387
+ this.recordMetric({
388
+ jobId,
389
+ stepId: stepId ?? undefined,
390
+ name,
391
+ value,
392
+ attributes,
393
+ }).catch((err) => {
394
+ this.#logger?.error(err, 'Error recording metric')
395
+ })
396
+ },
397
+ addSpanAttribute: (key: string, value: string | number | boolean) => {
398
+ this.addSpanAttribute({ span, key, value }).catch((err) => {
399
+ this.#logger?.error(err, 'Error adding span attribute')
400
+ })
401
+ },
402
+ addSpanEvent: (name: string, attributes?: Record<string, any>) => {
403
+ this.addSpanEvent({ span, name, attributes }).catch((err) => {
404
+ this.#logger?.error(err, 'Error adding span event')
405
+ })
406
+ },
407
+ }
408
+ }
409
+
410
+ // ============================================================================
411
+ // Protected Abstract Methods (to be implemented by adapters)
412
+ // ============================================================================
413
+
414
+ /**
415
+ * Start the adapter.
416
+ */
417
+ protected abstract _start(): Promise<void>
418
+
419
+ /**
420
+ * Stop the adapter.
421
+ */
422
+ protected abstract _stop(): Promise<void>
423
+
424
+ /**
425
+ * Internal method to start a job span.
426
+ */
427
+ protected abstract _startJobSpan(options: StartJobSpanOptions): Promise<Span>
428
+
429
+ /**
430
+ * Internal method to end a job span.
431
+ */
432
+ protected abstract _endJobSpan(span: Span, options: EndSpanOptions): Promise<void>
433
+
434
+ /**
435
+ * Internal method to start a step span.
436
+ */
437
+ protected abstract _startStepSpan(options: StartStepSpanOptions): Promise<Span>
438
+
439
+ /**
440
+ * Internal method to end a step span.
441
+ */
442
+ protected abstract _endStepSpan(span: Span, options: EndSpanOptions): Promise<void>
443
+
444
+ /**
445
+ * Internal method to start a database span.
446
+ */
447
+ protected abstract _startDatabaseSpan(options: StartDatabaseSpanOptions): Promise<Span | null>
448
+
449
+ /**
450
+ * Internal method to end a database span.
451
+ */
452
+ protected abstract _endDatabaseSpan(span: Span, options: EndSpanOptions): Promise<void>
453
+
454
+ /**
455
+ * Internal method to record a metric.
456
+ */
457
+ protected abstract _recordMetric(options: RecordMetricOptions): Promise<void>
458
+
459
+ /**
460
+ * Internal method to add a span event.
461
+ */
462
+ protected abstract _addSpanEvent(options: AddSpanEventOptions): Promise<void>
463
+
464
+ /**
465
+ * Internal method to add a span attribute.
466
+ */
467
+ protected abstract _addSpanAttribute(options: AddSpanAttributeOptions): Promise<void>
468
+ }
@@ -0,0 +1,17 @@
1
+ // Re-export telemetry adapters and types
2
+
3
+ export {
4
+ type AddSpanAttributeOptions,
5
+ type AddSpanEventOptions,
6
+ type EndSpanOptions,
7
+ type ObserveContext,
8
+ type RecordMetricOptions,
9
+ type Span,
10
+ type StartDatabaseSpanOptions,
11
+ type StartJobSpanOptions,
12
+ type StartStepSpanOptions,
13
+ TelemetryAdapter,
14
+ } from './adapter.js'
15
+ export { LocalTelemetryAdapter, type LocalTelemetryAdapterOptions, localTelemetryAdapter } from './local.js'
16
+ export { NoopTelemetryAdapter, noopTelemetryAdapter } from './noop.js'
17
+ export { OpenTelemetryAdapter, type OpenTelemetryAdapterOptions, openTelemetryAdapter } from './opentelemetry.js'