duron 0.3.0-beta.8 → 0.3.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 (91) hide show
  1. package/dist/action-job.d.ts +33 -2
  2. package/dist/action-job.d.ts.map +1 -1
  3. package/dist/action-job.js +93 -26
  4. package/dist/action-manager.d.ts +44 -2
  5. package/dist/action-manager.d.ts.map +1 -1
  6. package/dist/action-manager.js +64 -3
  7. package/dist/action.d.ts +388 -7
  8. package/dist/action.d.ts.map +1 -1
  9. package/dist/action.js +44 -23
  10. package/dist/adapters/adapter.d.ts +365 -8
  11. package/dist/adapters/adapter.d.ts.map +1 -1
  12. package/dist/adapters/adapter.js +221 -15
  13. package/dist/adapters/postgres/base.d.ts +184 -6
  14. package/dist/adapters/postgres/base.d.ts.map +1 -1
  15. package/dist/adapters/postgres/base.js +436 -75
  16. package/dist/adapters/postgres/pglite.d.ts +37 -0
  17. package/dist/adapters/postgres/pglite.d.ts.map +1 -1
  18. package/dist/adapters/postgres/pglite.js +38 -0
  19. package/dist/adapters/postgres/postgres.d.ts +35 -0
  20. package/dist/adapters/postgres/postgres.d.ts.map +1 -1
  21. package/dist/adapters/postgres/postgres.js +42 -0
  22. package/dist/adapters/postgres/schema.d.ts +150 -37
  23. package/dist/adapters/postgres/schema.d.ts.map +1 -1
  24. package/dist/adapters/postgres/schema.default.d.ts +151 -38
  25. package/dist/adapters/postgres/schema.default.d.ts.map +1 -1
  26. package/dist/adapters/postgres/schema.default.js +2 -2
  27. package/dist/adapters/postgres/schema.js +60 -23
  28. package/dist/adapters/schemas.d.ts +124 -80
  29. package/dist/adapters/schemas.d.ts.map +1 -1
  30. package/dist/adapters/schemas.js +139 -26
  31. package/dist/client.d.ts +426 -22
  32. package/dist/client.d.ts.map +1 -1
  33. package/dist/client.js +370 -20
  34. package/dist/constants.js +6 -0
  35. package/dist/errors.d.ts +166 -9
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/errors.js +189 -19
  38. package/dist/index.d.ts +2 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/server.d.ts +99 -37
  41. package/dist/server.d.ts.map +1 -1
  42. package/dist/server.js +84 -25
  43. package/dist/step-manager.d.ts +111 -4
  44. package/dist/step-manager.d.ts.map +1 -1
  45. package/dist/step-manager.js +411 -75
  46. package/dist/telemetry/index.d.ts +1 -4
  47. package/dist/telemetry/index.d.ts.map +1 -1
  48. package/dist/telemetry/index.js +2 -4
  49. package/dist/telemetry/local-span-exporter.d.ts +56 -0
  50. package/dist/telemetry/local-span-exporter.d.ts.map +1 -0
  51. package/dist/telemetry/local-span-exporter.js +118 -0
  52. package/dist/utils/p-retry.d.ts +5 -0
  53. package/dist/utils/p-retry.d.ts.map +1 -1
  54. package/dist/utils/p-retry.js +8 -0
  55. package/dist/utils/wait-for-abort.d.ts +1 -0
  56. package/dist/utils/wait-for-abort.d.ts.map +1 -1
  57. package/dist/utils/wait-for-abort.js +1 -0
  58. package/migrations/postgres/{20260119153838_flimsy_thor_girl → 20260121160012_normal_bloodstrike}/migration.sql +32 -20
  59. package/migrations/postgres/{20260119153838_flimsy_thor_girl → 20260121160012_normal_bloodstrike}/snapshot.json +241 -66
  60. package/package.json +42 -26
  61. package/src/action-job.ts +43 -32
  62. package/src/action-manager.ts +5 -5
  63. package/src/action.ts +317 -149
  64. package/src/adapters/adapter.ts +54 -54
  65. package/src/adapters/postgres/base.ts +266 -86
  66. package/src/adapters/postgres/schema.default.ts +2 -2
  67. package/src/adapters/postgres/schema.ts +52 -24
  68. package/src/adapters/schemas.ts +91 -36
  69. package/src/client.ts +322 -68
  70. package/src/errors.ts +141 -30
  71. package/src/index.ts +2 -0
  72. package/src/server.ts +39 -37
  73. package/src/step-manager.ts +254 -91
  74. package/src/telemetry/index.ts +2 -20
  75. package/src/telemetry/local-span-exporter.ts +148 -0
  76. package/dist/telemetry/adapter.d.ts +0 -107
  77. package/dist/telemetry/adapter.d.ts.map +0 -1
  78. package/dist/telemetry/adapter.js +0 -134
  79. package/dist/telemetry/local.d.ts +0 -22
  80. package/dist/telemetry/local.d.ts.map +0 -1
  81. package/dist/telemetry/local.js +0 -243
  82. package/dist/telemetry/noop.d.ts +0 -17
  83. package/dist/telemetry/noop.d.ts.map +0 -1
  84. package/dist/telemetry/noop.js +0 -66
  85. package/dist/telemetry/opentelemetry.d.ts +0 -25
  86. package/dist/telemetry/opentelemetry.d.ts.map +0 -1
  87. package/dist/telemetry/opentelemetry.js +0 -312
  88. package/src/telemetry/adapter.ts +0 -642
  89. package/src/telemetry/local.ts +0 -429
  90. package/src/telemetry/noop.ts +0 -141
  91. package/src/telemetry/opentelemetry.ts +0 -453
@@ -1,453 +0,0 @@
1
- import type { Span as OTelSpan, Tracer as OTelTracer, TracerProvider } from '@opentelemetry/api'
2
-
3
- import {
4
- type AddSpanAttributeOptions,
5
- type AddSpanEventOptions,
6
- type EndSpanOptions,
7
- type RecordMetricOptions,
8
- type Span,
9
- type StartDatabaseSpanOptions,
10
- type StartJobSpanOptions,
11
- type StartSpanOptions,
12
- type StartStepSpanOptions,
13
- TelemetryAdapter,
14
- type Tracer,
15
- type TracerSpan,
16
- } from './adapter.js'
17
-
18
- // ============================================================================
19
- // Types
20
- // ============================================================================
21
-
22
- export interface OpenTelemetryAdapterOptions {
23
- /**
24
- * Service name for telemetry.
25
- * Used as the tracer name.
26
- */
27
- serviceName?: string
28
-
29
- /**
30
- * Optional TracerProvider to use.
31
- * If not provided, uses the global tracer provider.
32
- */
33
- tracerProvider?: TracerProvider
34
-
35
- /**
36
- * Whether to trace database queries.
37
- * @default false
38
- */
39
- traceDatabaseQueries?: boolean
40
- }
41
-
42
- interface ExtendedSpan extends Span {
43
- otelSpan: OTelSpan
44
- }
45
-
46
- // ============================================================================
47
- // OpenTelemetry Adapter
48
- // ============================================================================
49
-
50
- /**
51
- * OpenTelemetry telemetry adapter.
52
- * Exports traces to external systems like Jaeger, OTLP, etc.
53
- */
54
- export class OpenTelemetryAdapter extends TelemetryAdapter {
55
- #serviceName: string
56
- #tracerProvider: TracerProvider | null
57
- #traceDatabaseQueries: boolean
58
- #tracer: OTelTracer | null = null
59
- #spanMap = new Map<string, OTelSpan>()
60
- #tracerCache = new Map<string, Tracer>()
61
-
62
- constructor(options: OpenTelemetryAdapterOptions = {}) {
63
- super()
64
- this.#serviceName = options.serviceName ?? 'duron'
65
- this.#tracerProvider = options.tracerProvider ?? null
66
- this.#traceDatabaseQueries = options.traceDatabaseQueries ?? false
67
- }
68
-
69
- // ============================================================================
70
- // Lifecycle Methods
71
- // ============================================================================
72
-
73
- protected async _start(): Promise<void> {
74
- // Dynamically import OpenTelemetry API to make it optional
75
- const api = await import('@opentelemetry/api')
76
-
77
- // Get tracer from provider or global
78
- if (this.#tracerProvider) {
79
- this.#tracer = this.#tracerProvider.getTracer(this.#serviceName)
80
- } else {
81
- this.#tracer = api.trace.getTracer(this.#serviceName)
82
- }
83
- }
84
-
85
- protected async _stop(): Promise<void> {
86
- this.#spanMap.clear()
87
- this.#tracer = null
88
- }
89
-
90
- // ============================================================================
91
- // Span Methods
92
- // ============================================================================
93
-
94
- protected async _startJobSpan(options: StartJobSpanOptions): Promise<Span> {
95
- if (!this.#tracer) {
96
- throw new Error('OpenTelemetry tracer not initialized')
97
- }
98
-
99
- const api = await import('@opentelemetry/api')
100
-
101
- const otelSpan = this.#tracer.startSpan(`job:${options.actionName}`, {
102
- kind: api.SpanKind.INTERNAL,
103
- attributes: {
104
- 'duron.job.id': options.jobId,
105
- 'duron.job.action_name': options.actionName,
106
- 'duron.job.group_key': options.groupKey,
107
- },
108
- })
109
-
110
- const spanId = `job:${options.jobId}`
111
- this.#spanMap.set(spanId, otelSpan)
112
-
113
- const span: ExtendedSpan = {
114
- id: spanId,
115
- jobId: options.jobId,
116
- stepId: null,
117
- parentSpanId: null,
118
- otelSpan,
119
- }
120
-
121
- return span
122
- }
123
-
124
- protected async _endJobSpan(span: Span, options: EndSpanOptions): Promise<void> {
125
- const api = await import('@opentelemetry/api')
126
- const extSpan = span as ExtendedSpan
127
- const otelSpan = extSpan.otelSpan
128
-
129
- if (options.status === 'error') {
130
- otelSpan.setStatus({
131
- code: api.SpanStatusCode.ERROR,
132
- message: options.error?.message ?? 'Unknown error',
133
- })
134
- if (options.error) {
135
- otelSpan.recordException(options.error)
136
- }
137
- } else if (options.status === 'cancelled') {
138
- otelSpan.setStatus({
139
- code: api.SpanStatusCode.OK,
140
- message: 'Cancelled',
141
- })
142
- otelSpan.setAttribute('duron.job.cancelled', true)
143
- } else {
144
- otelSpan.setStatus({ code: api.SpanStatusCode.OK })
145
- }
146
-
147
- otelSpan.end()
148
- this.#spanMap.delete(span.id)
149
- }
150
-
151
- protected async _startStepSpan(options: StartStepSpanOptions): Promise<Span> {
152
- if (!this.#tracer) {
153
- throw new Error('OpenTelemetry tracer not initialized')
154
- }
155
-
156
- const api = await import('@opentelemetry/api')
157
-
158
- // Get parent span context
159
- let parentContext = api.context.active()
160
- if (options.parentSpan) {
161
- const parentExtSpan = options.parentSpan as ExtendedSpan
162
- if (parentExtSpan.otelSpan) {
163
- parentContext = api.trace.setSpan(api.context.active(), parentExtSpan.otelSpan)
164
- }
165
- }
166
-
167
- const otelSpan = this.#tracer.startSpan(
168
- `step:${options.stepName}`,
169
- {
170
- kind: api.SpanKind.INTERNAL,
171
- attributes: {
172
- 'duron.job.id': options.jobId,
173
- 'duron.step.id': options.stepId,
174
- 'duron.step.name': options.stepName,
175
- 'duron.step.parent_step_id': options.parentStepId ?? undefined,
176
- },
177
- },
178
- parentContext,
179
- )
180
-
181
- const spanId = `step:${options.stepId}`
182
- this.#spanMap.set(spanId, otelSpan)
183
-
184
- const span: ExtendedSpan = {
185
- id: spanId,
186
- jobId: options.jobId,
187
- stepId: options.stepId,
188
- parentSpanId: options.parentSpan?.id ?? null,
189
- otelSpan,
190
- }
191
-
192
- return span
193
- }
194
-
195
- protected async _endStepSpan(span: Span, options: EndSpanOptions): Promise<void> {
196
- const api = await import('@opentelemetry/api')
197
- const extSpan = span as ExtendedSpan
198
- const otelSpan = extSpan.otelSpan
199
-
200
- if (options.status === 'error') {
201
- otelSpan.setStatus({
202
- code: api.SpanStatusCode.ERROR,
203
- message: options.error?.message ?? 'Unknown error',
204
- })
205
- if (options.error) {
206
- otelSpan.recordException(options.error)
207
- }
208
- } else if (options.status === 'cancelled') {
209
- otelSpan.setStatus({
210
- code: api.SpanStatusCode.OK,
211
- message: 'Cancelled',
212
- })
213
- otelSpan.setAttribute('duron.step.cancelled', true)
214
- } else {
215
- otelSpan.setStatus({ code: api.SpanStatusCode.OK })
216
- }
217
-
218
- otelSpan.end()
219
- this.#spanMap.delete(span.id)
220
- }
221
-
222
- protected async _startDatabaseSpan(options: StartDatabaseSpanOptions): Promise<Span | null> {
223
- if (!this.#traceDatabaseQueries || !this.#tracer) {
224
- return null
225
- }
226
-
227
- const api = await import('@opentelemetry/api')
228
-
229
- const otelSpan = this.#tracer.startSpan(`db:${options.operation}`, {
230
- kind: api.SpanKind.CLIENT,
231
- attributes: {
232
- 'db.system': 'postgresql',
233
- 'db.operation': options.operation,
234
- 'db.statement': options.query,
235
- },
236
- })
237
-
238
- const spanId = `db:${globalThis.crypto.randomUUID()}`
239
- this.#spanMap.set(spanId, otelSpan)
240
-
241
- const span: ExtendedSpan = {
242
- id: spanId,
243
- jobId: '',
244
- stepId: null,
245
- parentSpanId: null,
246
- otelSpan,
247
- }
248
-
249
- return span
250
- }
251
-
252
- protected async _endDatabaseSpan(span: Span, options: EndSpanOptions): Promise<void> {
253
- const api = await import('@opentelemetry/api')
254
- const extSpan = span as ExtendedSpan
255
- const otelSpan = extSpan.otelSpan
256
-
257
- if (options.status === 'error') {
258
- otelSpan.setStatus({
259
- code: api.SpanStatusCode.ERROR,
260
- message: options.error?.message ?? 'Unknown error',
261
- })
262
- if (options.error) {
263
- otelSpan.recordException(options.error)
264
- }
265
- } else {
266
- otelSpan.setStatus({ code: api.SpanStatusCode.OK })
267
- }
268
-
269
- otelSpan.end()
270
- this.#spanMap.delete(span.id)
271
- }
272
-
273
- // ============================================================================
274
- // Metrics Methods
275
- // ============================================================================
276
-
277
- protected async _recordMetric(options: RecordMetricOptions): Promise<void> {
278
- // OpenTelemetry metrics would require MeterProvider
279
- // For now, we record as span events on the current active span
280
- const api = await import('@opentelemetry/api')
281
- const activeSpan = api.trace.getActiveSpan()
282
-
283
- if (activeSpan) {
284
- activeSpan.addEvent('metric', {
285
- 'metric.name': options.name,
286
- 'metric.value': options.value,
287
- ...options.attributes,
288
- })
289
- }
290
- }
291
-
292
- protected async _addSpanEvent(options: AddSpanEventOptions): Promise<void> {
293
- const extSpan = options.span as ExtendedSpan
294
- if (extSpan.otelSpan) {
295
- extSpan.otelSpan.addEvent(options.name, options.attributes)
296
- }
297
- }
298
-
299
- protected async _addSpanAttribute(options: AddSpanAttributeOptions): Promise<void> {
300
- const extSpan = options.span as ExtendedSpan
301
- if (extSpan.otelSpan) {
302
- extSpan.otelSpan.setAttribute(options.key, options.value)
303
- }
304
- }
305
-
306
- // ============================================================================
307
- // Tracer Methods
308
- // ============================================================================
309
-
310
- protected _getTracer(name: string): Tracer {
311
- // Return cached tracer if available
312
- const cached = this.#tracerCache.get(name)
313
- if (cached) {
314
- return cached
315
- }
316
-
317
- const adapter = this
318
-
319
- const tracer: Tracer = {
320
- name,
321
-
322
- startSpan(spanName: string, options?: StartSpanOptions): TracerSpan {
323
- // We need to dynamically get the OpenTelemetry API
324
- // Since _getTracer is synchronous, we need to handle this carefully
325
- let otelSpan: OTelSpan | null = null
326
- let api: typeof import('@opentelemetry/api') | null = null
327
- let ended = false
328
-
329
- // Initialize the span asynchronously but return synchronously
330
- const initPromise = (async () => {
331
- api = await import('@opentelemetry/api')
332
-
333
- // Get the tracer
334
- let otelTracer: OTelTracer
335
- if (adapter.#tracerProvider) {
336
- otelTracer = adapter.#tracerProvider.getTracer(name)
337
- } else {
338
- otelTracer = api.trace.getTracer(name)
339
- }
340
-
341
- // Map kind
342
- let spanKind = api.SpanKind.INTERNAL
343
- if (options?.kind === 'client') spanKind = api.SpanKind.CLIENT
344
- else if (options?.kind === 'server') spanKind = api.SpanKind.SERVER
345
- else if (options?.kind === 'producer') spanKind = api.SpanKind.PRODUCER
346
- else if (options?.kind === 'consumer') spanKind = api.SpanKind.CONSUMER
347
-
348
- // Get parent context
349
- let parentContext = api.context.active()
350
- if (options?.parentSpan) {
351
- const parentOtelSpan = (options.parentSpan as any)._otelSpan as OTelSpan | undefined
352
- if (parentOtelSpan) {
353
- parentContext = api.trace.setSpan(api.context.active(), parentOtelSpan)
354
- }
355
- }
356
-
357
- otelSpan = otelTracer.startSpan(
358
- spanName,
359
- {
360
- kind: spanKind,
361
- attributes: options?.attributes,
362
- },
363
- parentContext,
364
- )
365
- })()
366
-
367
- const tracerSpan: TracerSpan & { _otelSpan?: OTelSpan } = {
368
- setAttribute(key: string, value: string | number | boolean): void {
369
- initPromise.then(() => {
370
- if (otelSpan && !ended) {
371
- otelSpan.setAttribute(key, value)
372
- }
373
- })
374
- },
375
-
376
- setAttributes(attributes: Record<string, string | number | boolean>): void {
377
- initPromise.then(() => {
378
- if (otelSpan && !ended) {
379
- otelSpan.setAttributes(attributes)
380
- }
381
- })
382
- },
383
-
384
- addEvent(eventName: string, attributes?: Record<string, string | number | boolean>): void {
385
- initPromise.then(() => {
386
- if (otelSpan && !ended) {
387
- otelSpan.addEvent(eventName, attributes)
388
- }
389
- })
390
- },
391
-
392
- recordException(error: Error): void {
393
- initPromise.then(() => {
394
- if (otelSpan && !ended) {
395
- otelSpan.recordException(error)
396
- }
397
- })
398
- },
399
-
400
- setStatusOk(): void {
401
- initPromise.then(() => {
402
- if (otelSpan && api && !ended) {
403
- otelSpan.setStatus({ code: api.SpanStatusCode.OK })
404
- }
405
- })
406
- },
407
-
408
- setStatusError(message?: string): void {
409
- initPromise.then(() => {
410
- if (otelSpan && api && !ended) {
411
- otelSpan.setStatus({ code: api.SpanStatusCode.ERROR, message })
412
- }
413
- })
414
- },
415
-
416
- end(): void {
417
- initPromise.then(() => {
418
- if (otelSpan && !ended) {
419
- ended = true
420
- otelSpan.end()
421
- }
422
- })
423
- },
424
-
425
- isRecording(): boolean {
426
- return otelSpan?.isRecording() ?? false
427
- },
428
- }
429
-
430
- // Store reference for parent context propagation
431
- initPromise.then(() => {
432
- if (otelSpan) {
433
- ;(tracerSpan as any)._otelSpan = otelSpan
434
- }
435
- })
436
-
437
- return tracerSpan
438
- },
439
- }
440
-
441
- this.#tracerCache.set(name, tracer)
442
- return tracer
443
- }
444
- }
445
-
446
- /**
447
- * Create an OpenTelemetry telemetry adapter.
448
- * Exports traces to external systems like Jaeger, OTLP, etc.
449
- *
450
- * @param options - Configuration options
451
- * @returns OpenTelemetryAdapter instance
452
- */
453
- export const openTelemetryAdapter = (options?: OpenTelemetryAdapterOptions) => new OpenTelemetryAdapter(options)