@coji/durably 0.6.0 → 0.7.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.
package/dist/index.d.ts CHANGED
@@ -1,633 +1,7 @@
1
- import { Dialect, Kysely } from 'kysely';
2
- import { z } from 'zod';
3
-
4
- /**
5
- * Base event interface
6
- */
7
- interface BaseEvent {
8
- type: string;
9
- timestamp: string;
10
- sequence: number;
11
- }
12
- /**
13
- * Run trigger event (emitted when a job is triggered, before worker picks it up)
14
- */
15
- interface RunTriggerEvent extends BaseEvent {
16
- type: 'run:trigger';
17
- runId: string;
18
- jobName: string;
19
- payload: unknown;
20
- }
21
- /**
22
- * Run start event
23
- */
24
- interface RunStartEvent extends BaseEvent {
25
- type: 'run:start';
26
- runId: string;
27
- jobName: string;
28
- payload: unknown;
29
- }
30
- /**
31
- * Run complete event
32
- */
33
- interface RunCompleteEvent extends BaseEvent {
34
- type: 'run:complete';
35
- runId: string;
36
- jobName: string;
37
- output: unknown;
38
- duration: number;
39
- }
40
- /**
41
- * Run fail event
42
- */
43
- interface RunFailEvent extends BaseEvent {
44
- type: 'run:fail';
45
- runId: string;
46
- jobName: string;
47
- error: string;
48
- failedStepName: string;
49
- }
50
- /**
51
- * Run cancel event
52
- */
53
- interface RunCancelEvent extends BaseEvent {
54
- type: 'run:cancel';
55
- runId: string;
56
- jobName: string;
57
- }
58
- /**
59
- * Run retry event (emitted when a failed run is retried)
60
- */
61
- interface RunRetryEvent extends BaseEvent {
62
- type: 'run:retry';
63
- runId: string;
64
- jobName: string;
65
- }
66
- /**
67
- * Run progress event
68
- */
69
- interface RunProgressEvent extends BaseEvent {
70
- type: 'run:progress';
71
- runId: string;
72
- jobName: string;
73
- progress: {
74
- current: number;
75
- total?: number;
76
- message?: string;
77
- };
78
- }
79
- /**
80
- * Step start event
81
- */
82
- interface StepStartEvent extends BaseEvent {
83
- type: 'step:start';
84
- runId: string;
85
- jobName: string;
86
- stepName: string;
87
- stepIndex: number;
88
- }
89
- /**
90
- * Step complete event
91
- */
92
- interface StepCompleteEvent extends BaseEvent {
93
- type: 'step:complete';
94
- runId: string;
95
- jobName: string;
96
- stepName: string;
97
- stepIndex: number;
98
- output: unknown;
99
- duration: number;
100
- }
101
- /**
102
- * Step fail event
103
- */
104
- interface StepFailEvent extends BaseEvent {
105
- type: 'step:fail';
106
- runId: string;
107
- jobName: string;
108
- stepName: string;
109
- stepIndex: number;
110
- error: string;
111
- }
112
- /**
113
- * Log write event
114
- */
115
- interface LogWriteEvent extends BaseEvent {
116
- type: 'log:write';
117
- runId: string;
118
- stepName: string | null;
119
- level: 'info' | 'warn' | 'error';
120
- message: string;
121
- data: unknown;
122
- }
123
- /**
124
- * Worker error event (internal errors like heartbeat failures)
125
- */
126
- interface WorkerErrorEvent extends BaseEvent {
127
- type: 'worker:error';
128
- error: string;
129
- context: string;
130
- runId?: string;
131
- }
132
- /**
133
- * All event types as discriminated union
134
- */
135
- type DurablyEvent = RunTriggerEvent | RunStartEvent | RunCompleteEvent | RunFailEvent | RunCancelEvent | RunRetryEvent | RunProgressEvent | StepStartEvent | StepCompleteEvent | StepFailEvent | LogWriteEvent | WorkerErrorEvent;
136
- /**
137
- * Event types for type-safe event names
138
- */
139
- type EventType = DurablyEvent['type'];
140
- /**
141
- * Extract event by type
142
- */
143
- type EventByType<T extends EventType> = Extract<DurablyEvent, {
144
- type: T;
145
- }>;
146
- /**
147
- * Event input (without auto-generated fields)
148
- */
149
- type EventInput<T extends EventType> = Omit<EventByType<T>, 'timestamp' | 'sequence'>;
150
- /**
151
- * All possible event inputs as a union (properly distributed)
152
- */
153
- type AnyEventInput = EventInput<'run:trigger'> | EventInput<'run:start'> | EventInput<'run:complete'> | EventInput<'run:fail'> | EventInput<'run:cancel'> | EventInput<'run:retry'> | EventInput<'run:progress'> | EventInput<'step:start'> | EventInput<'step:complete'> | EventInput<'step:fail'> | EventInput<'log:write'> | EventInput<'worker:error'>;
154
- /**
155
- * Event listener function
156
- */
157
- type EventListener<T extends EventType> = (event: EventByType<T>) => void;
158
- /**
159
- * Unsubscribe function returned by on()
160
- */
161
- type Unsubscribe = () => void;
162
- /**
163
- * Error handler function for listener exceptions
164
- */
165
- type ErrorHandler = (error: Error, event: DurablyEvent) => void;
166
-
167
- /**
168
- * Database schema types for Durably
169
- */
170
- interface RunsTable {
171
- id: string;
172
- job_name: string;
173
- payload: string;
174
- status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
175
- idempotency_key: string | null;
176
- concurrency_key: string | null;
177
- current_step_index: number;
178
- progress: string | null;
179
- output: string | null;
180
- error: string | null;
181
- heartbeat_at: string;
182
- created_at: string;
183
- updated_at: string;
184
- }
185
- interface StepsTable {
186
- id: string;
187
- run_id: string;
188
- name: string;
189
- index: number;
190
- status: 'completed' | 'failed';
191
- output: string | null;
192
- error: string | null;
193
- started_at: string;
194
- completed_at: string | null;
195
- }
196
- interface LogsTable {
197
- id: string;
198
- run_id: string;
199
- step_name: string | null;
200
- level: 'info' | 'warn' | 'error';
201
- message: string;
202
- data: string | null;
203
- created_at: string;
204
- }
205
- interface SchemaVersionsTable {
206
- version: number;
207
- applied_at: string;
208
- }
209
- interface Database {
210
- durably_runs: RunsTable;
211
- durably_steps: StepsTable;
212
- durably_logs: LogsTable;
213
- durably_schema_versions: SchemaVersionsTable;
214
- }
215
-
216
- /**
217
- * Run data for creating a new run
218
- */
219
- interface CreateRunInput {
220
- jobName: string;
221
- payload: unknown;
222
- idempotencyKey?: string;
223
- concurrencyKey?: string;
224
- }
225
- /**
226
- * Run data returned from storage
227
- */
228
- interface Run {
229
- id: string;
230
- jobName: string;
231
- payload: unknown;
232
- status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
233
- idempotencyKey: string | null;
234
- concurrencyKey: string | null;
235
- currentStepIndex: number;
236
- stepCount: number;
237
- progress: {
238
- current: number;
239
- total?: number;
240
- message?: string;
241
- } | null;
242
- output: unknown | null;
243
- error: string | null;
244
- heartbeatAt: string;
245
- createdAt: string;
246
- updatedAt: string;
247
- }
248
- /**
249
- * Run update data
250
- */
251
- interface UpdateRunInput {
252
- status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
253
- currentStepIndex?: number;
254
- progress?: {
255
- current: number;
256
- total?: number;
257
- message?: string;
258
- } | null;
259
- output?: unknown;
260
- error?: string | null;
261
- heartbeatAt?: string;
262
- }
263
- /**
264
- * Run filter options
265
- */
266
- interface RunFilter$1 {
267
- status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
268
- jobName?: string;
269
- /** Maximum number of runs to return */
270
- limit?: number;
271
- /** Number of runs to skip (for pagination) */
272
- offset?: number;
273
- }
274
- /**
275
- * Step data for creating a new step
276
- */
277
- interface CreateStepInput {
278
- runId: string;
279
- name: string;
280
- index: number;
281
- status: 'completed' | 'failed';
282
- output?: unknown;
283
- error?: string;
284
- startedAt: string;
285
- }
286
- /**
287
- * Step data returned from storage
288
- */
289
- interface Step {
290
- id: string;
291
- runId: string;
292
- name: string;
293
- index: number;
294
- status: 'completed' | 'failed';
295
- output: unknown | null;
296
- error: string | null;
297
- startedAt: string;
298
- completedAt: string | null;
299
- }
300
- /**
301
- * Log data for creating a new log
302
- */
303
- interface CreateLogInput {
304
- runId: string;
305
- stepName: string | null;
306
- level: 'info' | 'warn' | 'error';
307
- message: string;
308
- data?: unknown;
309
- }
310
- /**
311
- * Log data returned from storage
312
- */
313
- interface Log {
314
- id: string;
315
- runId: string;
316
- stepName: string | null;
317
- level: 'info' | 'warn' | 'error';
318
- message: string;
319
- data: unknown | null;
320
- createdAt: string;
321
- }
322
- /**
323
- * Storage interface for database operations
324
- */
325
- interface Storage {
326
- createRun(input: CreateRunInput): Promise<Run>;
327
- batchCreateRuns(inputs: CreateRunInput[]): Promise<Run[]>;
328
- updateRun(runId: string, data: UpdateRunInput): Promise<void>;
329
- deleteRun(runId: string): Promise<void>;
330
- getRun(runId: string): Promise<Run | null>;
331
- getRuns(filter?: RunFilter$1): Promise<Run[]>;
332
- getNextPendingRun(excludeConcurrencyKeys: string[]): Promise<Run | null>;
333
- createStep(input: CreateStepInput): Promise<Step>;
334
- getSteps(runId: string): Promise<Step[]>;
335
- getCompletedStep(runId: string, name: string): Promise<Step | null>;
336
- createLog(input: CreateLogInput): Promise<Log>;
337
- getLogs(runId: string): Promise<Log[]>;
338
- }
339
-
340
- /**
341
- * Step context passed to the job function
342
- */
343
- interface StepContext {
344
- /**
345
- * The ID of the current run
346
- */
347
- readonly runId: string;
348
- /**
349
- * Execute a step with automatic persistence and replay
350
- */
351
- run<T>(name: string, fn: () => T | Promise<T>): Promise<T>;
352
- /**
353
- * Report progress for the current run
354
- */
355
- progress(current: number, total?: number, message?: string): void;
356
- /**
357
- * Log a message
358
- */
359
- log: {
360
- info(message: string, data?: unknown): void;
361
- warn(message: string, data?: unknown): void;
362
- error(message: string, data?: unknown): void;
363
- };
364
- }
365
- /**
366
- * Trigger options
367
- */
368
- interface TriggerOptions {
369
- idempotencyKey?: string;
370
- concurrencyKey?: string;
371
- /** Timeout in milliseconds for triggerAndWait() */
372
- timeout?: number;
373
- }
374
- /**
375
- * Run filter options
376
- */
377
- interface RunFilter {
378
- status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
379
- jobName?: string;
380
- /** Maximum number of runs to return */
381
- limit?: number;
382
- /** Number of runs to skip (for pagination) */
383
- offset?: number;
384
- }
385
- /**
386
- * Typed run with output type
387
- */
388
- interface TypedRun<TOutput> extends Omit<Run, 'output'> {
389
- output: TOutput | null;
390
- }
391
- /**
392
- * Batch trigger input - either just the input or input with options
393
- */
394
- type BatchTriggerInput<TInput> = TInput | {
395
- input: TInput;
396
- options?: TriggerOptions;
397
- };
398
- /**
399
- * Result of triggerAndWait
400
- */
401
- interface TriggerAndWaitResult<TOutput> {
402
- id: string;
403
- output: TOutput;
404
- }
405
- /**
406
- * Job handle returned by defineJob
407
- */
408
- interface JobHandle<TName extends string, TInput, TOutput> {
409
- readonly name: TName;
410
- /**
411
- * Trigger a new run
412
- */
413
- trigger(input: TInput, options?: TriggerOptions): Promise<TypedRun<TOutput>>;
414
- /**
415
- * Trigger a new run and wait for completion
416
- * Returns the output directly, throws if the run fails
417
- */
418
- triggerAndWait(input: TInput, options?: TriggerOptions): Promise<TriggerAndWaitResult<TOutput>>;
419
- /**
420
- * Trigger multiple runs in a batch
421
- * All inputs are validated before any runs are created
422
- */
423
- batchTrigger(inputs: BatchTriggerInput<TInput>[]): Promise<TypedRun<TOutput>[]>;
424
- /**
425
- * Get a run by ID
426
- */
427
- getRun(id: string): Promise<TypedRun<TOutput> | null>;
428
- /**
429
- * Get runs with optional filter
430
- */
431
- getRuns(filter?: Omit<RunFilter, 'jobName'>): Promise<TypedRun<TOutput>[]>;
432
- }
433
-
434
- /**
435
- * Job run function type
436
- */
437
- type JobRunFunction<TInput, TOutput> = (step: StepContext, payload: TInput) => Promise<TOutput>;
438
- /**
439
- * Job definition - a standalone description of a job
440
- * This is the result of calling defineJob() and can be passed to durably.register()
441
- */
442
- interface JobDefinition<TName extends string, TInput, TOutput> {
443
- readonly name: TName;
444
- readonly input: z.ZodType<TInput>;
445
- readonly output: z.ZodType<TOutput> | undefined;
446
- readonly run: JobRunFunction<TInput, TOutput>;
447
- }
448
- /**
449
- * Extract input type from a JobDefinition
450
- * @example
451
- * ```ts
452
- * type Input = JobInput<typeof myJob> // { userId: string }
453
- * ```
454
- */
455
- type JobInput<T> = T extends JobDefinition<string, infer TInput, unknown> ? TInput : never;
456
- /**
457
- * Extract output type from a JobDefinition
458
- * @example
459
- * ```ts
460
- * type Output = JobOutput<typeof myJob> // { count: number }
461
- * ```
462
- */
463
- type JobOutput<T> = T extends JobDefinition<string, unknown, infer TOutput> ? TOutput : never;
464
- /**
465
- * Configuration for defining a job
466
- */
467
- interface DefineJobConfig<TName extends string, TInputSchema extends z.ZodType, TOutputSchema extends z.ZodType | undefined> {
468
- name: TName;
469
- input: TInputSchema;
470
- output?: TOutputSchema;
471
- run: JobRunFunction<z.infer<TInputSchema>, TOutputSchema extends z.ZodType ? z.infer<TOutputSchema> : void>;
472
- }
473
- /**
474
- * Define a job - creates a JobDefinition that can be registered with durably.register()
475
- *
476
- * @example
477
- * ```ts
478
- * import { defineJob } from '@coji/durably'
479
- * import { z } from 'zod'
480
- *
481
- * export const syncUsers = defineJob({
482
- * name: 'sync-users',
483
- * input: z.object({ orgId: z.string() }),
484
- * output: z.object({ syncedCount: z.number() }),
485
- * run: async (step, payload) => {
486
- * const users = await step.run('fetch-users', () => fetchUsers(payload.orgId))
487
- * return { syncedCount: users.length }
488
- * },
489
- * })
490
- * ```
491
- */
492
- declare function defineJob<TName extends string, TInputSchema extends z.ZodType, TOutputSchema extends z.ZodType | undefined = undefined>(config: DefineJobConfig<TName, TInputSchema, TOutputSchema>): JobDefinition<TName, z.infer<TInputSchema>, TOutputSchema extends z.ZodType ? z.infer<TOutputSchema> : void>;
493
-
494
- /**
495
- * Options for creating a Durably instance
496
- */
497
- interface DurablyOptions {
498
- dialect: Dialect;
499
- pollingInterval?: number;
500
- heartbeatInterval?: number;
501
- staleThreshold?: number;
502
- }
503
- /**
504
- * Plugin interface for extending Durably
505
- */
506
- interface DurablyPlugin {
507
- name: string;
508
- install(durably: Durably<any>): void;
509
- }
510
- /**
511
- * Helper type to transform JobDefinition record to JobHandle record
512
- */
513
- type TransformToHandles<TJobs extends Record<string, JobDefinition<string, unknown, unknown>>> = {
514
- [K in keyof TJobs]: TJobs[K] extends JobDefinition<infer TName, infer TInput, infer TOutput> ? JobHandle<TName & string, TInput, TOutput> : never;
515
- };
516
- /**
517
- * Durably instance with type-safe jobs
518
- */
519
- interface Durably<TJobs extends Record<string, JobHandle<string, unknown, unknown>> = Record<string, never>> {
520
- /**
521
- * Registered job handles (type-safe)
522
- */
523
- readonly jobs: TJobs;
524
- /**
525
- * Initialize Durably: run migrations and start the worker
526
- * This is the recommended way to start Durably.
527
- * Equivalent to calling migrate() then start().
528
- * @example
529
- * ```ts
530
- * const durably = createDurably({ dialect }).register({ ... })
531
- * await durably.init()
532
- * ```
533
- */
534
- init(): Promise<void>;
535
- /**
536
- * Run database migrations
537
- * This is idempotent and safe to call multiple times
538
- */
539
- migrate(): Promise<void>;
540
- /**
541
- * Get the underlying Kysely database instance
542
- * Useful for testing and advanced use cases
543
- */
544
- readonly db: Kysely<Database>;
545
- /**
546
- * Storage layer for database operations
547
- */
548
- readonly storage: Storage;
549
- /**
550
- * Register an event listener
551
- * @returns Unsubscribe function
552
- */
553
- on<T extends EventType>(type: T, listener: EventListener<T>): Unsubscribe;
554
- /**
555
- * Emit an event (auto-assigns timestamp and sequence)
556
- */
557
- emit(event: AnyEventInput): void;
558
- /**
559
- * Register an error handler for listener exceptions
560
- */
561
- onError(handler: ErrorHandler): void;
562
- /**
563
- * Register job definitions and return a new Durably instance with type-safe jobs
564
- * @example
565
- * ```ts
566
- * const durably = createDurably({ dialect })
567
- * .register({
568
- * importCsv: importCsvJob,
569
- * syncUsers: syncUsersJob,
570
- * })
571
- * await durably.migrate()
572
- * // Usage: durably.jobs.importCsv.trigger({ rows: [...] })
573
- * ```
574
- */
575
- register<TNewJobs extends Record<string, JobDefinition<string, any, any>>>(jobDefs: TNewJobs): Durably<TJobs & TransformToHandles<TNewJobs>>;
576
- /**
577
- * Start the worker polling loop
578
- */
579
- start(): void;
580
- /**
581
- * Stop the worker after current run completes
582
- */
583
- stop(): Promise<void>;
584
- /**
585
- * Retry a failed run by resetting it to pending
586
- * @throws Error if run is not in failed status
587
- */
588
- retry(runId: string): Promise<void>;
589
- /**
590
- * Cancel a pending or running run
591
- * @throws Error if run is already completed, failed, or cancelled
592
- */
593
- cancel(runId: string): Promise<void>;
594
- /**
595
- * Delete a completed, failed, or cancelled run and its associated steps and logs
596
- * @throws Error if run is pending or running, or does not exist
597
- */
598
- deleteRun(runId: string): Promise<void>;
599
- /**
600
- * Get a run by ID (returns unknown output type)
601
- */
602
- getRun(runId: string): Promise<Run | null>;
603
- /**
604
- * Get runs with optional filtering
605
- */
606
- getRuns(filter?: RunFilter$1): Promise<Run[]>;
607
- /**
608
- * Register a plugin
609
- */
610
- use(plugin: DurablyPlugin): void;
611
- /**
612
- * Get a registered job handle by name
613
- * Returns undefined if job is not registered
614
- */
615
- getJob<TName extends string = string>(name: TName): JobHandle<TName, Record<string, unknown>, unknown> | undefined;
616
- /**
617
- * Subscribe to events for a specific run
618
- * Returns a ReadableStream that can be used for SSE
619
- */
620
- subscribe(runId: string): ReadableStream<DurablyEvent>;
621
- }
622
- /**
623
- * Create a Durably instance
624
- */
625
- declare function createDurably(options: DurablyOptions): Durably<Record<string, never>>;
626
-
627
- /**
628
- * Plugin that persists log events to the database
629
- */
630
- declare function withLogPersistence(): DurablyPlugin;
1
+ import { D as Durably } from './index-4aPZWn8r.js';
2
+ export { p as Database, g as DurablyEvent, a as DurablyOptions, b as DurablyPlugin, E as ErrorHandler, h as EventType, J as JobDefinition, n as JobHandle, e as JobInput, f as JobOutput, u as Log, L as LogWriteEvent, q as LogsTable, v as Run, R as RunCompleteEvent, i as RunFailEvent, x as RunFilter, j as RunProgressEvent, k as RunStartEvent, r as RunsTable, s as SchemaVersionsTable, y as Step, S as StepCompleteEvent, o as StepContext, l as StepFailEvent, m as StepStartEvent, t as StepsTable, T as TriggerAndWaitResult, W as WorkerErrorEvent, c as createDurably, d as defineJob, w as withLogPersistence } from './index-4aPZWn8r.js';
3
+ import 'kysely';
4
+ import 'zod';
631
5
 
632
6
  /**
633
7
  * Error thrown when a run is cancelled during execution.
@@ -765,4 +139,4 @@ interface CreateDurablyHandlerOptions {
765
139
  */
766
140
  declare function createDurablyHandler(durably: Durably, options?: CreateDurablyHandlerOptions): DurablyHandler;
767
141
 
768
- export { CancelledError, type Database, type Durably, type DurablyEvent, type DurablyHandler, type DurablyOptions, type DurablyPlugin, type ErrorHandler, type EventType, type JobDefinition, type JobHandle, type JobInput, type JobOutput, type Log, type LogWriteEvent, type LogsTable, type Run, type RunCompleteEvent, type RunFailEvent, type RunFilter$1 as RunFilter, type RunProgressEvent, type RunStartEvent, type RunsTable, type SchemaVersionsTable, type Step, type StepCompleteEvent, type StepContext, type StepFailEvent, type StepStartEvent, type StepsTable, type TriggerAndWaitResult, type TriggerRequest, type TriggerResponse, type WorkerErrorEvent, createDurably, createDurablyHandler, defineJob, withLogPersistence };
142
+ export { CancelledError, Durably, type DurablyHandler, type TriggerRequest, type TriggerResponse, createDurablyHandler };