duron 0.3.0-beta.9 → 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 +88 -23
  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 +140 -3
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/errors.js +152 -9
  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 +403 -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 +33 -29
  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 +84 -12
  71. package/src/index.ts +2 -0
  72. package/src/server.ts +39 -37
  73. package/src/step-manager.ts +246 -95
  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,8 +1,9 @@
1
1
  import { sql } from 'drizzle-orm'
2
2
  import {
3
+ bigint,
4
+ bigserial,
3
5
  boolean,
4
6
  check,
5
- doublePrecision,
6
7
  index,
7
8
  integer,
8
9
  jsonb,
@@ -25,6 +26,7 @@ export default function createSchema(schemaName: string) {
25
26
  id: uuid('id').primaryKey().defaultRandom(),
26
27
  action_name: text('action_name').notNull(),
27
28
  group_key: text('group_key').notNull(),
29
+ description: text('description'),
28
30
  status: text('status').$type<JobStatus>().notNull().default('created'),
29
31
  checksum: text('checksum').notNull(),
30
32
  input: jsonb('input').notNull().default({}),
@@ -35,7 +37,8 @@ export default function createSchema(schemaName: string) {
35
37
  started_at: timestamp('started_at', { withTimezone: true }),
36
38
  finished_at: timestamp('finished_at', { withTimezone: true }),
37
39
  client_id: text('client_id'),
38
- concurrency_limit: integer('concurrency_limit').notNull().default(10),
40
+ concurrency_limit: integer('concurrency_limit').notNull(),
41
+ concurrency_step_limit: integer('concurrency_step_limit').notNull(),
39
42
  created_at: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
40
43
  updated_at: timestamp('updated_at', { withTimezone: true })
41
44
  .notNull()
@@ -52,12 +55,14 @@ export default function createSchema(schemaName: string) {
52
55
  index('idx_jobs_action_name').on(table.action_name),
53
56
  index('idx_jobs_status').on(table.status),
54
57
  index('idx_jobs_group_key').on(table.group_key),
58
+ index('idx_jobs_description').on(table.description),
55
59
  index('idx_jobs_started_at').on(table.started_at),
56
60
  index('idx_jobs_finished_at').on(table.finished_at),
57
61
  index('idx_jobs_expires_at').on(table.expires_at),
58
62
  index('idx_jobs_client_id').on(table.client_id),
59
63
  index('idx_jobs_checksum').on(table.checksum),
60
64
  index('idx_jobs_concurrency_limit').on(table.concurrency_limit),
65
+ index('idx_jobs_concurrency_step_limit').on(table.concurrency_step_limit),
61
66
  // Composite indexes
62
67
  index('idx_jobs_action_status').on(table.action_name, table.status),
63
68
  index('idx_jobs_action_group').on(table.action_name, table.group_key),
@@ -129,35 +134,58 @@ export default function createSchema(schemaName: string) {
129
134
  ],
130
135
  )
131
136
 
132
- const metricsTable = schema.table(
133
- 'metrics',
137
+ /**
138
+ * OpenTelemetry spans table.
139
+ * Stores span data exported by PostgresSpanExporter.
140
+ *
141
+ * SpanKind values: 0=INTERNAL, 1=SERVER, 2=CLIENT, 3=PRODUCER, 4=CONSUMER
142
+ * StatusCode values: 0=UNSET, 1=OK, 2=ERROR
143
+ */
144
+ const spansTable = schema.table(
145
+ 'spans',
134
146
  {
135
- id: uuid('id').primaryKey().defaultRandom(),
136
- job_id: uuid('job_id')
137
- .notNull()
138
- .references(() => jobsTable.id, { onDelete: 'cascade' }),
147
+ id: bigserial('id', { mode: 'number' }).primaryKey(),
148
+ // OpenTelemetry span identifiers
149
+ trace_id: text('trace_id').notNull(), // 32-char hex
150
+ span_id: text('span_id').notNull(), // 16-char hex
151
+ parent_span_id: text('parent_span_id'), // 16-char hex, null for root spans
152
+ // Duron-specific references (extracted from span attributes)
153
+ job_id: uuid('job_id').references(() => jobsTable.id, { onDelete: 'cascade' }),
139
154
  step_id: uuid('step_id').references(() => jobStepsTable.id, { onDelete: 'cascade' }),
155
+ // Span metadata
140
156
  name: text('name').notNull(),
141
- value: doublePrecision('value').notNull(),
157
+ kind: integer('kind').notNull().default(0), // SpanKind enum
158
+ // Timing (stored as nanoseconds since epoch for precision)
159
+ start_time_unix_nano: bigint('start_time_unix_nano', { mode: 'bigint' }).notNull(),
160
+ end_time_unix_nano: bigint('end_time_unix_nano', { mode: 'bigint' }),
161
+ // Status
162
+ status_code: integer('status_code').notNull().default(0), // SpanStatusCode enum
163
+ status_message: text('status_message'),
164
+ // Span data
142
165
  attributes: jsonb('attributes').$type<Record<string, any>>().notNull().default({}),
143
- type: text('type').$type<'metric' | 'span_event' | 'span_attribute'>().notNull(),
144
- timestamp: timestamp('timestamp', { withTimezone: true }).notNull().defaultNow(),
145
- created_at: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
166
+ events: jsonb('events')
167
+ .$type<Array<{ name: string; timeUnixNano: string; attributes?: Record<string, any> }>>()
168
+ .notNull()
169
+ .default([]),
146
170
  },
147
171
  (table) => [
148
172
  // Single column indexes
149
- index('idx_metrics_job_id').on(table.job_id),
150
- index('idx_metrics_step_id').on(table.step_id),
151
- index('idx_metrics_name').on(table.name),
152
- index('idx_metrics_type').on(table.type),
153
- index('idx_metrics_timestamp').on(table.timestamp),
173
+ index('idx_spans_trace_id').on(table.trace_id),
174
+ index('idx_spans_span_id').on(table.span_id),
175
+ index('idx_spans_job_id').on(table.job_id),
176
+ index('idx_spans_step_id').on(table.step_id),
177
+ index('idx_spans_name').on(table.name),
178
+ index('idx_spans_kind').on(table.kind),
179
+ index('idx_spans_status_code').on(table.status_code),
154
180
  // Composite indexes
155
- index('idx_metrics_job_step').on(table.job_id, table.step_id),
156
- index('idx_metrics_job_name').on(table.job_id, table.name),
157
- index('idx_metrics_job_type').on(table.job_id, table.type),
158
- // GIN index for JSONB attributes filtering
159
- index('idx_metrics_attributes').using('gin', table.attributes),
160
- check('metrics_type_check', sql`${table.type} IN ('metric', 'span_event', 'span_attribute')`),
181
+ index('idx_spans_job_step').on(table.job_id, table.step_id),
182
+ index('idx_spans_trace_parent').on(table.trace_id, table.parent_span_id),
183
+ // GIN indexes for JSONB querying
184
+ index('idx_spans_attributes').using('gin', table.attributes),
185
+ index('idx_spans_events').using('gin', table.events),
186
+ // Constraints
187
+ check('spans_kind_check', sql`${table.kind} IN (0, 1, 2, 3, 4)`),
188
+ check('spans_status_code_check', sql`${table.status_code} IN (0, 1, 2)`),
161
189
  ],
162
190
  )
163
191
 
@@ -165,6 +193,6 @@ export default function createSchema(schemaName: string) {
165
193
  schema,
166
194
  jobsTable,
167
195
  jobStepsTable,
168
- metricsTable,
196
+ spansTable,
169
197
  }
170
198
  }
@@ -34,6 +34,7 @@ export const JobSchema = z.object({
34
34
  id: z.string(),
35
35
  actionName: z.string(),
36
36
  groupKey: z.string(),
37
+ description: z.string().nullable().default(null),
37
38
  input: z.any(),
38
39
  output: z.any().nullable(),
39
40
  error: z.any().nullable(),
@@ -45,7 +46,10 @@ export const JobSchema = z.object({
45
46
  createdAt: DateSchema,
46
47
  updatedAt: DateSchema,
47
48
  concurrencyLimit: z.coerce.number(),
49
+ concurrencyStepLimit: z.coerce.number(),
48
50
  clientId: z.string().nullable().optional(),
51
+ /** Duration in milliseconds (finishedAt - startedAt). Null if job hasn't finished. */
52
+ durationMs: z.coerce.number().nullable().default(null),
49
53
  })
50
54
 
51
55
  // ============================================================================
@@ -85,7 +89,16 @@ export const JobStepWithoutOutputSchema = JobStepSchema.omit({ output: true })
85
89
 
86
90
  export const SortOrderSchema = z.enum(['asc', 'desc'])
87
91
 
88
- export const JobSortFieldSchema = z.enum(['createdAt', 'startedAt', 'finishedAt', 'status', 'actionName', 'expiresAt'])
92
+ export const JobSortFieldSchema = z.enum([
93
+ 'createdAt',
94
+ 'startedAt',
95
+ 'finishedAt',
96
+ 'status',
97
+ 'actionName',
98
+ 'expiresAt',
99
+ 'duration',
100
+ 'description',
101
+ ])
89
102
 
90
103
  export const JobSortSchema = z.object({
91
104
  field: JobSortFieldSchema,
@@ -97,6 +110,7 @@ export const JobFiltersSchema = z.object({
97
110
  actionName: z.union([z.string(), z.array(z.string())]).optional(),
98
111
  groupKey: z.union([z.string(), z.array(z.string())]).optional(),
99
112
  clientId: z.union([z.string(), z.array(z.string())]).optional(),
113
+ description: z.string().optional(),
100
114
  createdAt: z.union([DateSchema, z.array(DateSchema).length(2)]).optional(),
101
115
  startedAt: z.union([DateSchema, z.array(DateSchema).length(2)]).optional(),
102
116
  finishedAt: z.union([DateSchema, z.array(DateSchema).length(2)]).optional(),
@@ -136,6 +150,10 @@ export const CreateJobOptionsSchema = z.object({
136
150
  timeoutMs: z.number(),
137
151
  /** The concurrency limit for this job's group */
138
152
  concurrencyLimit: z.number(),
153
+ /** The concurrency limit for steps within this job */
154
+ concurrencyStepLimit: z.number(),
155
+ /** Optional description for the job */
156
+ description: z.string().nullable().optional(),
139
157
  })
140
158
 
141
159
  export const RecoverJobsOptionsSchema = z.object({
@@ -295,59 +313,94 @@ export const JobStepStatusResultSchema = z.object({
295
313
  })
296
314
 
297
315
  // ============================================================================
298
- // Metrics Schemas
316
+ // Span Schemas (OpenTelemetry compatible)
299
317
  // ============================================================================
300
318
 
301
- export const MetricTypeSchema = z.enum(['metric', 'span_event', 'span_attribute'])
319
+ /**
320
+ * SpanKind values (OpenTelemetry standard):
321
+ * 0 = INTERNAL - Default, internal operation
322
+ * 1 = SERVER - Server-side handling of RPC/HTTP request
323
+ * 2 = CLIENT - Client-side of RPC/HTTP request
324
+ * 3 = PRODUCER - Producer of async message
325
+ * 4 = CONSUMER - Consumer of async message
326
+ */
327
+ export const SpanKindSchema = z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3), z.literal(4)])
328
+
329
+ /**
330
+ * SpanStatusCode values (OpenTelemetry standard):
331
+ * 0 = UNSET - Status not set
332
+ * 1 = OK - Operation completed successfully
333
+ * 2 = ERROR - Operation failed
334
+ */
335
+ export const SpanStatusCodeSchema = z.union([z.literal(0), z.literal(1), z.literal(2)])
336
+
337
+ export const SpanEventSchema = z.object({
338
+ name: z.string(),
339
+ timeUnixNano: z.string(),
340
+ attributes: z.record(z.string(), z.any()).optional(),
341
+ })
302
342
 
303
- export const MetricSchema = z.object({
304
- id: z.string(),
305
- jobId: z.string(),
343
+ export const SpanSchema = z.object({
344
+ id: z.number(),
345
+ traceId: z.string(),
346
+ spanId: z.string(),
347
+ parentSpanId: z.string().nullable(),
348
+ jobId: z.string().nullable(),
306
349
  stepId: z.string().nullable(),
307
350
  name: z.string(),
308
- value: z.number(),
351
+ kind: SpanKindSchema,
352
+ startTimeUnixNano: z.string().nullable(), // Stored as bigint but serialized as string for JSON
353
+ endTimeUnixNano: z.string().nullable(), // Stored as bigint but serialized as string for JSON
354
+ statusCode: SpanStatusCodeSchema,
355
+ statusMessage: z.string().nullable(),
309
356
  attributes: z.record(z.string(), z.any()),
310
- type: MetricTypeSchema,
311
- timestamp: DateSchema,
312
- createdAt: DateSchema,
357
+ events: z.array(SpanEventSchema),
313
358
  })
314
359
 
315
- export const MetricSortFieldSchema = z.enum(['name', 'value', 'timestamp', 'createdAt'])
360
+ export const SpanSortFieldSchema = z.enum(['name', 'startTimeUnixNano', 'endTimeUnixNano'])
316
361
 
317
- export const MetricSortSchema = z.object({
318
- field: MetricSortFieldSchema,
362
+ export const SpanSortSchema = z.object({
363
+ field: SpanSortFieldSchema,
319
364
  order: SortOrderSchema,
320
365
  })
321
366
 
322
- export const MetricFiltersSchema = z.object({
367
+ export const SpanFiltersSchema = z.object({
323
368
  name: z.union([z.string(), z.array(z.string())]).optional(),
324
- type: z.union([MetricTypeSchema, z.array(MetricTypeSchema)]).optional(),
369
+ kind: z.union([SpanKindSchema, z.array(SpanKindSchema)]).optional(),
370
+ statusCode: z.union([SpanStatusCodeSchema, z.array(SpanStatusCodeSchema)]).optional(),
371
+ traceId: z.string().optional(),
325
372
  attributesFilter: z.record(z.string(), z.any()).optional(),
326
- timestampRange: z.array(DateSchema).length(2).optional(),
327
373
  })
328
374
 
329
- export const InsertMetricOptionsSchema = z.object({
330
- jobId: z.string(),
331
- stepId: z.string().optional(),
375
+ export const InsertSpanOptionsSchema = z.object({
376
+ traceId: z.string(),
377
+ spanId: z.string(),
378
+ parentSpanId: z.string().nullable(),
379
+ jobId: z.string().nullable(),
380
+ stepId: z.string().nullable(),
332
381
  name: z.string(),
333
- value: z.number(),
382
+ kind: SpanKindSchema,
383
+ startTimeUnixNano: z.bigint(),
384
+ endTimeUnixNano: z.bigint().nullable(),
385
+ statusCode: SpanStatusCodeSchema,
386
+ statusMessage: z.string().nullable(),
334
387
  attributes: z.record(z.string(), z.any()).optional(),
335
- type: MetricTypeSchema,
388
+ events: z.array(SpanEventSchema).optional(),
336
389
  })
337
390
 
338
- export const GetMetricsOptionsSchema = z.object({
391
+ export const GetSpansOptionsSchema = z.object({
339
392
  jobId: z.string().optional(),
340
393
  stepId: z.string().optional(),
341
- filters: MetricFiltersSchema.optional(),
342
- sort: MetricSortSchema.optional(),
394
+ filters: SpanFiltersSchema.optional(),
395
+ sort: SpanSortSchema.optional(),
343
396
  })
344
397
 
345
- export const GetMetricsResultSchema = z.object({
346
- metrics: z.array(MetricSchema),
398
+ export const GetSpansResultSchema = z.object({
399
+ spans: z.array(SpanSchema),
347
400
  total: z.number().int().nonnegative(),
348
401
  })
349
402
 
350
- export const DeleteMetricsOptionsSchema = z.object({
403
+ export const DeleteSpansOptionsSchema = z.object({
351
404
  jobId: z.string(),
352
405
  })
353
406
 
@@ -386,12 +439,14 @@ export type DelayJobStepOptions = z.infer<typeof DelayJobStepOptionsSchema>
386
439
  export type CancelJobStepOptions = z.infer<typeof CancelJobStepOptionsSchema>
387
440
  export type CreateOrRecoverJobStepResult = z.infer<typeof CreateOrRecoverJobStepResultSchema>
388
441
  export type TimeTravelJobOptions = z.infer<typeof TimeTravelJobOptionsSchema>
389
- export type MetricType = z.infer<typeof MetricTypeSchema>
390
- export type Metric = z.infer<typeof MetricSchema>
391
- export type MetricSortField = z.infer<typeof MetricSortFieldSchema>
392
- export type MetricSort = z.infer<typeof MetricSortSchema>
393
- export type MetricFilters = z.infer<typeof MetricFiltersSchema>
394
- export type InsertMetricOptions = z.infer<typeof InsertMetricOptionsSchema>
395
- export type GetMetricsOptions = z.infer<typeof GetMetricsOptionsSchema>
396
- export type GetMetricsResult = z.infer<typeof GetMetricsResultSchema>
397
- export type DeleteMetricsOptions = z.infer<typeof DeleteMetricsOptionsSchema>
442
+ export type SpanKind = z.infer<typeof SpanKindSchema>
443
+ export type SpanStatusCode = z.infer<typeof SpanStatusCodeSchema>
444
+ export type SpanEvent = z.infer<typeof SpanEventSchema>
445
+ export type Span = z.infer<typeof SpanSchema>
446
+ export type SpanSortField = z.infer<typeof SpanSortFieldSchema>
447
+ export type SpanSort = z.infer<typeof SpanSortSchema>
448
+ export type SpanFilters = z.infer<typeof SpanFiltersSchema>
449
+ export type InsertSpanOptions = z.infer<typeof InsertSpanOptionsSchema>
450
+ export type GetSpansOptions = z.infer<typeof GetSpansOptionsSchema>
451
+ export type GetSpansResult = z.infer<typeof GetSpansResultSchema>
452
+ export type DeleteSpansOptions = z.infer<typeof DeleteSpansOptionsSchema>