@coji/durably 0.10.0 → 0.11.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/README.md CHANGED
@@ -12,7 +12,7 @@ Step-oriented resumable batch execution for Node.js and browsers using SQLite.
12
12
  npm install @coji/durably kysely zod better-sqlite3
13
13
  ```
14
14
 
15
- See the [Getting Started Guide](https://coji.github.io/durably/guide/getting-started) for other SQLite backends (libsql, SQLocal for browsers).
15
+ See the [Quick Start](https://coji.github.io/durably/guide/quick-start) for other SQLite backends (libsql, SQLocal for browsers).
16
16
 
17
17
  ## Quick Start
18
18
 
@@ -30,7 +30,7 @@ const myJob = defineJob({
30
30
  },
31
31
  })
32
32
 
33
- const durably = createDurably({ dialect }).register({ myJob })
33
+ const durably = createDurably({ dialect, jobs: { myJob } })
34
34
 
35
35
  await durably.init() // migrate + start
36
36
  await durably.jobs.myJob.trigger({ id: '123' })
@@ -78,6 +78,14 @@ interface RunRetryEvent extends BaseEvent {
78
78
  jobName: string;
79
79
  labels: Record<string, string>;
80
80
  }
81
+ /**
82
+ * Progress data reported by step.progress()
83
+ */
84
+ interface ProgressData {
85
+ current: number;
86
+ total?: number;
87
+ message?: string;
88
+ }
81
89
  /**
82
90
  * Run progress event
83
91
  */
@@ -85,11 +93,7 @@ interface RunProgressEvent extends BaseEvent {
85
93
  type: 'run:progress';
86
94
  runId: string;
87
95
  jobName: string;
88
- progress: {
89
- current: number;
90
- total?: number;
91
- message?: string;
92
- };
96
+ progress: ProgressData;
93
97
  labels: Record<string, string>;
94
98
  }
95
99
  /**
@@ -136,17 +140,24 @@ interface StepCancelEvent extends BaseEvent {
136
140
  stepIndex: number;
137
141
  labels: Record<string, string>;
138
142
  }
143
+ /**
144
+ * Log data reported by step.log
145
+ */
146
+ interface LogData {
147
+ level: 'info' | 'warn' | 'error';
148
+ message: string;
149
+ data?: unknown;
150
+ stepName?: string | null;
151
+ }
139
152
  /**
140
153
  * Log write event
141
154
  */
142
- interface LogWriteEvent extends BaseEvent {
155
+ interface LogWriteEvent extends BaseEvent, LogData {
143
156
  type: 'log:write';
144
157
  runId: string;
145
158
  jobName: string;
146
159
  labels: Record<string, string>;
147
160
  stepName: string | null;
148
- level: 'info' | 'warn' | 'error';
149
- message: string;
150
161
  data: unknown;
151
162
  }
152
163
  /**
@@ -248,17 +259,17 @@ interface Database {
248
259
  /**
249
260
  * Run data for creating a new run
250
261
  */
251
- interface CreateRunInput {
262
+ interface CreateRunInput<TLabels extends Record<string, string> = Record<string, string>> {
252
263
  jobName: string;
253
264
  input: unknown;
254
265
  idempotencyKey?: string;
255
266
  concurrencyKey?: string;
256
- labels?: Record<string, string>;
267
+ labels?: TLabels;
257
268
  }
258
269
  /**
259
270
  * Run data returned from storage
260
271
  */
261
- interface Run {
272
+ interface Run<TLabels extends Record<string, string> = Record<string, string>> {
262
273
  id: string;
263
274
  jobName: string;
264
275
  input: unknown;
@@ -274,7 +285,7 @@ interface Run {
274
285
  } | null;
275
286
  output: unknown | null;
276
287
  error: string | null;
277
- labels: Record<string, string>;
288
+ labels: TLabels;
278
289
  heartbeatAt: string;
279
290
  startedAt: string | null;
280
291
  completedAt: string | null;
@@ -301,10 +312,14 @@ interface UpdateRunInput {
301
312
  /**
302
313
  * Run filter options
303
314
  */
304
- interface RunFilter$1 {
315
+ interface RunFilter<TLabels extends Record<string, string> = Record<string, string>> {
305
316
  status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
306
- jobName?: string;
307
- labels?: Record<string, string>;
317
+ /** Filter by job name(s). Pass a string for one, or an array for multiple (OR). */
318
+ jobName?: string | string[];
319
+ /** Filter by labels (all specified labels must match) */
320
+ labels?: {
321
+ [K in keyof TLabels]?: TLabels[K];
322
+ };
308
323
  /** Maximum number of runs to return */
309
324
  limit?: number;
310
325
  /** Number of runs to skip (for pagination) */
@@ -362,11 +377,11 @@ interface Log {
362
377
  * A client-safe subset of Run, excluding internal fields like
363
378
  * heartbeatAt, idempotencyKey, concurrencyKey, and updatedAt.
364
379
  */
365
- type ClientRun = Omit<Run, 'idempotencyKey' | 'concurrencyKey' | 'heartbeatAt' | 'updatedAt'>;
380
+ type ClientRun<TLabels extends Record<string, string> = Record<string, string>> = Omit<Run<TLabels>, 'idempotencyKey' | 'concurrencyKey' | 'heartbeatAt' | 'updatedAt'>;
366
381
  /**
367
382
  * Project a full Run to a ClientRun by stripping internal fields.
368
383
  */
369
- declare function toClientRun(run: Run): ClientRun;
384
+ declare function toClientRun<TLabels extends Record<string, string> = Record<string, string>>(run: Run<TLabels>): ClientRun<TLabels>;
370
385
  /**
371
386
  * Storage interface for database operations
372
387
  */
@@ -376,7 +391,7 @@ interface Storage {
376
391
  updateRun(runId: string, data: UpdateRunInput): Promise<void>;
377
392
  deleteRun(runId: string): Promise<void>;
378
393
  getRun<T extends Run = Run>(runId: string): Promise<T | null>;
379
- getRuns<T extends Run = Run>(filter?: RunFilter$1): Promise<T[]>;
394
+ getRuns<T extends Run = Run>(filter?: RunFilter): Promise<T[]>;
380
395
  claimNextPendingRun(excludeConcurrencyKeys: string[]): Promise<Run | null>;
381
396
  createStep(input: CreateStepInput): Promise<Step>;
382
397
  getSteps(runId: string): Promise<Step[]>;
@@ -411,39 +426,36 @@ interface StepContext {
411
426
  };
412
427
  }
413
428
  /**
414
- * Trigger options
429
+ * Trigger options for trigger() and batchTrigger()
415
430
  */
416
- interface TriggerOptions {
431
+ interface TriggerOptions<TLabels extends Record<string, string> = Record<string, string>> {
417
432
  idempotencyKey?: string;
418
433
  concurrencyKey?: string;
419
- labels?: Record<string, string>;
420
- /** Timeout in milliseconds for triggerAndWait() */
421
- timeout?: number;
434
+ labels?: TLabels;
422
435
  }
423
436
  /**
424
- * Run filter options
437
+ * Options for triggerAndWait() (extends TriggerOptions with wait-specific options)
425
438
  */
426
- interface RunFilter {
427
- status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
428
- jobName?: string;
429
- labels?: Record<string, string>;
430
- /** Maximum number of runs to return */
431
- limit?: number;
432
- /** Number of runs to skip (for pagination) */
433
- offset?: number;
439
+ interface TriggerAndWaitOptions<TLabels extends Record<string, string> = Record<string, string>> extends TriggerOptions<TLabels> {
440
+ /** Timeout in milliseconds */
441
+ timeout?: number;
442
+ /** Called when step.progress() is invoked during execution */
443
+ onProgress?: (progress: ProgressData) => void | Promise<void>;
444
+ /** Called when step.log is invoked during execution */
445
+ onLog?: (log: LogData) => void | Promise<void>;
434
446
  }
435
447
  /**
436
448
  * Typed run with output type
437
449
  */
438
- interface TypedRun<TOutput> extends Omit<Run, 'output'> {
450
+ interface TypedRun<TOutput, TLabels extends Record<string, string> = Record<string, string>> extends Omit<Run<TLabels>, 'output'> {
439
451
  output: TOutput | null;
440
452
  }
441
453
  /**
442
454
  * Batch trigger input - either just the input or input with options
443
455
  */
444
- type BatchTriggerInput<TInput> = TInput | {
456
+ type BatchTriggerInput<TInput, TLabels extends Record<string, string> = Record<string, string>> = TInput | {
445
457
  input: TInput;
446
- options?: TriggerOptions;
458
+ options?: TriggerOptions<TLabels>;
447
459
  };
448
460
  /**
449
461
  * Result of triggerAndWait
@@ -455,30 +467,30 @@ interface TriggerAndWaitResult<TOutput> {
455
467
  /**
456
468
  * Job handle returned by defineJob
457
469
  */
458
- interface JobHandle<TName extends string, TInput, TOutput> {
470
+ interface JobHandle<TName extends string, TInput, TOutput, TLabels extends Record<string, string> = Record<string, string>> {
459
471
  readonly name: TName;
460
472
  /**
461
473
  * Trigger a new run
462
474
  */
463
- trigger(input: TInput, options?: TriggerOptions): Promise<TypedRun<TOutput>>;
475
+ trigger(input: TInput, options?: TriggerOptions<TLabels>): Promise<TypedRun<TOutput, TLabels>>;
464
476
  /**
465
477
  * Trigger a new run and wait for completion
466
478
  * Returns the output directly, throws if the run fails
467
479
  */
468
- triggerAndWait(input: TInput, options?: TriggerOptions): Promise<TriggerAndWaitResult<TOutput>>;
480
+ triggerAndWait(input: TInput, options?: TriggerAndWaitOptions<TLabels>): Promise<TriggerAndWaitResult<TOutput>>;
469
481
  /**
470
482
  * Trigger multiple runs in a batch
471
483
  * All inputs are validated before any runs are created
472
484
  */
473
- batchTrigger(inputs: BatchTriggerInput<TInput>[]): Promise<TypedRun<TOutput>[]>;
485
+ batchTrigger(inputs: BatchTriggerInput<TInput, TLabels>[]): Promise<TypedRun<TOutput, TLabels>[]>;
474
486
  /**
475
487
  * Get a run by ID
476
488
  */
477
- getRun(id: string): Promise<TypedRun<TOutput> | null>;
489
+ getRun(id: string): Promise<TypedRun<TOutput, TLabels> | null>;
478
490
  /**
479
491
  * Get runs with optional filter
480
492
  */
481
- getRuns(filter?: Omit<RunFilter, 'jobName'>): Promise<TypedRun<TOutput>[]>;
493
+ getRuns(filter?: Omit<RunFilter<TLabels>, 'jobName'>): Promise<TypedRun<TOutput, TLabels>[]>;
482
494
  }
483
495
 
484
496
  /**
@@ -544,29 +556,46 @@ declare function defineJob<TName extends string, TInputSchema extends z.ZodType,
544
556
  /**
545
557
  * Options for creating a Durably instance
546
558
  */
547
- interface DurablyOptions {
559
+ interface DurablyOptions<TLabels extends Record<string, string> = Record<string, string>, TJobs extends Record<string, JobDefinition<string, any, any>> = Record<string, never>> {
548
560
  dialect: Dialect;
549
561
  pollingInterval?: number;
550
562
  heartbeatInterval?: number;
551
563
  staleThreshold?: number;
564
+ /**
565
+ * Zod schema for labels. When provided:
566
+ * - Labels are type-checked at compile time
567
+ * - Labels are validated at runtime on trigger()
568
+ */
569
+ labels?: z.ZodType<TLabels>;
570
+ /**
571
+ * Job definitions to register. Shorthand for calling .register() after creation.
572
+ * @example
573
+ * ```ts
574
+ * const durably = createDurably({
575
+ * dialect,
576
+ * jobs: { importCsv: importCsvJob, syncUsers: syncUsersJob },
577
+ * })
578
+ * ```
579
+ */
580
+ jobs?: TJobs;
552
581
  }
553
582
  /**
554
583
  * Plugin interface for extending Durably
555
584
  */
556
585
  interface DurablyPlugin {
557
586
  name: string;
558
- install(durably: Durably<any>): void;
587
+ install(durably: Durably<any, any>): void;
559
588
  }
560
589
  /**
561
590
  * Helper type to transform JobDefinition record to JobHandle record
562
591
  */
563
- type TransformToHandles<TJobs extends Record<string, JobDefinition<string, unknown, unknown>>> = {
564
- [K in keyof TJobs]: TJobs[K] extends JobDefinition<infer TName, infer TInput, infer TOutput> ? JobHandle<TName & string, TInput, TOutput> : never;
592
+ type TransformToHandles<TJobs extends Record<string, JobDefinition<string, unknown, unknown>>, TLabels extends Record<string, string> = Record<string, string>> = {
593
+ [K in keyof TJobs]: TJobs[K] extends JobDefinition<infer TName, infer TInput, infer TOutput> ? JobHandle<TName & string, TInput, TOutput, TLabels> : never;
565
594
  };
566
595
  /**
567
596
  * Durably instance with type-safe jobs
568
597
  */
569
- interface Durably<TJobs extends Record<string, JobHandle<string, unknown, unknown>> = Record<string, never>> {
598
+ interface Durably<TJobs extends Record<string, JobHandle<string, unknown, unknown, Record<string, string>>> = Record<string, never>, TLabels extends Record<string, string> = Record<string, string>> {
570
599
  /**
571
600
  * Registered job handles (type-safe)
572
601
  */
@@ -622,7 +651,7 @@ interface Durably<TJobs extends Record<string, JobHandle<string, unknown, unknow
622
651
  * // Usage: durably.jobs.importCsv.trigger({ rows: [...] })
623
652
  * ```
624
653
  */
625
- register<TNewJobs extends Record<string, JobDefinition<string, any, any>>>(jobDefs: TNewJobs): Durably<TJobs & TransformToHandles<TNewJobs>>;
654
+ register<TNewJobs extends Record<string, JobDefinition<string, any, any>>>(jobDefs: TNewJobs): Durably<TJobs & TransformToHandles<TNewJobs, TLabels>, TLabels>;
626
655
  /**
627
656
  * Start the worker polling loop
628
657
  */
@@ -658,7 +687,7 @@ interface Durably<TJobs extends Record<string, JobHandle<string, unknown, unknow
658
687
  * const typedRun = await durably.getRun<MyRun>(runId)
659
688
  * ```
660
689
  */
661
- getRun<T extends Run = Run>(runId: string): Promise<T | null>;
690
+ getRun<T extends Run<TLabels> = Run<TLabels>>(runId: string): Promise<T | null>;
662
691
  /**
663
692
  * Get runs with optional filtering
664
693
  * @example
@@ -671,7 +700,7 @@ interface Durably<TJobs extends Record<string, JobHandle<string, unknown, unknow
671
700
  * const typedRuns = await durably.getRuns<MyRun>({ jobName: 'my-job' })
672
701
  * ```
673
702
  */
674
- getRuns<T extends Run = Run>(filter?: RunFilter$1): Promise<T[]>;
703
+ getRuns<T extends Run<TLabels> = Run<TLabels>>(filter?: RunFilter<TLabels>): Promise<T[]>;
675
704
  /**
676
705
  * Register a plugin
677
706
  */
@@ -680,7 +709,7 @@ interface Durably<TJobs extends Record<string, JobHandle<string, unknown, unknow
680
709
  * Get a registered job handle by name
681
710
  * Returns undefined if job is not registered
682
711
  */
683
- getJob<TName extends string = string>(name: TName): JobHandle<TName, Record<string, unknown>, unknown> | undefined;
712
+ getJob<TName extends string = string>(name: TName): JobHandle<TName, Record<string, unknown>, unknown, TLabels> | undefined;
684
713
  /**
685
714
  * Subscribe to events for a specific run
686
715
  * Returns a ReadableStream that can be used for SSE
@@ -690,11 +719,14 @@ interface Durably<TJobs extends Record<string, JobHandle<string, unknown, unknow
690
719
  /**
691
720
  * Create a Durably instance
692
721
  */
693
- declare function createDurably(options: DurablyOptions): Durably<Record<string, never>>;
722
+ declare function createDurably<TLabels extends Record<string, string> = Record<string, string>, TJobs extends Record<string, JobDefinition<string, any, any>> = Record<string, never>>(options: DurablyOptions<TLabels, TJobs> & {
723
+ jobs: TJobs;
724
+ }): Durably<TransformToHandles<TJobs, TLabels>, TLabels>;
725
+ declare function createDurably<TLabels extends Record<string, string> = Record<string, string>>(options: DurablyOptions<TLabels>): Durably<Record<string, never>, TLabels>;
694
726
 
695
727
  /**
696
728
  * Plugin that persists log events to the database
697
729
  */
698
730
  declare function withLogPersistence(): DurablyPlugin;
699
731
 
700
- export { type StepsTable as A, createDurably as B, type ClientRun as C, type Durably as D, type ErrorHandler as E, defineJob as F, toClientRun as G, withLogPersistence as H, type JobDefinition as J, type Log as L, type Run as R, type SchemaVersionsTable as S, type TriggerAndWaitResult as T, type WorkerErrorEvent as W, type Database as a, type DurablyEvent as b, type DurablyOptions as c, type DurablyPlugin as d, type EventType as e, type JobHandle as f, type JobInput as g, type JobOutput as h, type LogWriteEvent as i, type LogsTable as j, type RunCancelEvent as k, type RunCompleteEvent as l, type RunDeleteEvent as m, type RunFailEvent as n, type RunFilter$1 as o, type RunProgressEvent as p, type RunRetryEvent as q, type RunStartEvent as r, type RunTriggerEvent as s, type RunsTable as t, type Step as u, type StepCancelEvent as v, type StepCompleteEvent as w, type StepContext as x, type StepFailEvent as y, type StepStartEvent as z };
732
+ export { type StepStartEvent as A, type BatchTriggerInput as B, type ClientRun as C, type Durably as D, type ErrorHandler as E, type StepsTable as F, type TriggerAndWaitResult as G, type TriggerOptions as H, createDurably as I, type JobDefinition as J, defineJob as K, type Log as L, toClientRun as M, withLogPersistence as N, type ProgressData as P, type Run as R, type SchemaVersionsTable as S, type TriggerAndWaitOptions as T, type WorkerErrorEvent as W, type RunFilter as a, type Database as b, type DurablyEvent as c, type DurablyOptions as d, type DurablyPlugin as e, type EventType as f, type JobHandle as g, type JobInput as h, type JobOutput as i, type LogData as j, type LogWriteEvent as k, type LogsTable as l, type RunCancelEvent as m, type RunCompleteEvent as n, type RunDeleteEvent as o, type RunFailEvent as p, type RunProgressEvent as q, type RunRetryEvent as r, type RunStartEvent as s, type RunTriggerEvent as t, type RunsTable as u, type Step as v, type StepCancelEvent as w, type StepCompleteEvent as x, type StepContext as y, type StepFailEvent as z };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { D as Durably } from './index-BjlCb0gP.js';
2
- export { C as ClientRun, a as Database, b as DurablyEvent, c as DurablyOptions, d as DurablyPlugin, E as ErrorHandler, e as EventType, J as JobDefinition, f as JobHandle, g as JobInput, h as JobOutput, L as Log, i as LogWriteEvent, j as LogsTable, R as Run, k as RunCancelEvent, l as RunCompleteEvent, m as RunDeleteEvent, n as RunFailEvent, o as RunFilter, p as RunProgressEvent, q as RunRetryEvent, r as RunStartEvent, s as RunTriggerEvent, t as RunsTable, S as SchemaVersionsTable, u as Step, v as StepCancelEvent, w as StepCompleteEvent, x as StepContext, y as StepFailEvent, z as StepStartEvent, A as StepsTable, T as TriggerAndWaitResult, W as WorkerErrorEvent, B as createDurably, F as defineJob, G as toClientRun, H as withLogPersistence } from './index-BjlCb0gP.js';
1
+ import { R as Run, a as RunFilter, D as Durably } from './index-fppJjkF-.js';
2
+ export { B as BatchTriggerInput, C as ClientRun, b as Database, c as DurablyEvent, d as DurablyOptions, e as DurablyPlugin, E as ErrorHandler, f as EventType, J as JobDefinition, g as JobHandle, h as JobInput, i as JobOutput, L as Log, j as LogData, k as LogWriteEvent, l as LogsTable, P as ProgressData, m as RunCancelEvent, n as RunCompleteEvent, o as RunDeleteEvent, p as RunFailEvent, q as RunProgressEvent, r as RunRetryEvent, s as RunStartEvent, t as RunTriggerEvent, u as RunsTable, S as SchemaVersionsTable, v as Step, w as StepCancelEvent, x as StepCompleteEvent, y as StepContext, z as StepFailEvent, A as StepStartEvent, F as StepsTable, T as TriggerAndWaitOptions, G as TriggerAndWaitResult, H as TriggerOptions, W as WorkerErrorEvent, I as createDurably, K as defineJob, M as toClientRun, N as withLogPersistence } from './index-fppJjkF-.js';
3
3
  import 'kysely';
4
4
  import 'zod';
5
5
 
@@ -12,15 +12,23 @@ declare class CancelledError extends Error {
12
12
  constructor(runId: string);
13
13
  }
14
14
 
15
+ /**
16
+ * Run operation types for onRunAccess
17
+ */
18
+ type RunOperation = 'read' | 'subscribe' | 'steps' | 'retry' | 'cancel' | 'delete';
19
+ /**
20
+ * Subscription filter — only fields that SSE subscriptions actually support.
21
+ */
22
+ type RunsSubscribeFilter<TLabels extends Record<string, string> = Record<string, string>> = Pick<RunFilter<TLabels>, 'jobName' | 'labels'>;
15
23
  /**
16
24
  * Request body for triggering a job
17
25
  */
18
- interface TriggerRequest {
26
+ interface TriggerRequest<TLabels extends Record<string, string> = Record<string, string>> {
19
27
  jobName: string;
20
- input: Record<string, unknown>;
28
+ input: unknown;
21
29
  idempotencyKey?: string;
22
30
  concurrencyKey?: string;
23
- labels?: Record<string, string>;
31
+ labels?: TLabels;
24
32
  }
25
33
  /**
26
34
  * Response for trigger endpoint
@@ -28,127 +36,69 @@ interface TriggerRequest {
28
36
  interface TriggerResponse {
29
37
  runId: string;
30
38
  }
39
+ /**
40
+ * Auth middleware configuration.
41
+ * When `auth` is set, `authenticate` is required.
42
+ * TContext is inferred from authenticate's return type.
43
+ * TLabels is inferred from the Durably instance.
44
+ */
45
+ interface AuthConfig<TContext, TLabels extends Record<string, string> = Record<string, string>> {
46
+ /** Authenticate every request. Return context or throw Response to reject. */
47
+ authenticate: (request: Request) => Promise<TContext> | TContext;
48
+ /** Guard before trigger. Called after body validation and job resolution. */
49
+ onTrigger?: (ctx: TContext, trigger: TriggerRequest<TLabels>) => Promise<void> | void;
50
+ /** Guard before run-level operations. Run is pre-fetched. */
51
+ onRunAccess?: (ctx: TContext, run: Run<TLabels>, info: {
52
+ operation: RunOperation;
53
+ }) => Promise<void> | void;
54
+ /** Scope runs list queries (GET /runs). */
55
+ scopeRuns?: (ctx: TContext, filter: RunFilter<TLabels>) => RunFilter<TLabels> | Promise<RunFilter<TLabels>>;
56
+ /** Scope runs subscribe stream (GET /runs/subscribe). Falls back to scopeRuns if not set. */
57
+ scopeRunsSubscribe?: (ctx: TContext, filter: RunsSubscribeFilter<TLabels>) => RunsSubscribeFilter<TLabels> | Promise<RunsSubscribeFilter<TLabels>>;
58
+ }
31
59
  /**
32
60
  * Handler interface for HTTP endpoints
33
61
  */
34
62
  interface DurablyHandler {
35
63
  /**
36
- * Handle all Durably HTTP requests with automatic routing
64
+ * Handle all Durably HTTP requests with automatic routing + auth
37
65
  *
38
66
  * Routes:
39
67
  * - GET {basePath}/subscribe?runId=xxx - SSE stream
40
68
  * - GET {basePath}/runs - List runs
69
+ * - GET {basePath}/runs/subscribe - SSE stream of run updates
41
70
  * - GET {basePath}/run?runId=xxx - Get single run
71
+ * - GET {basePath}/steps?runId=xxx - Get steps
42
72
  * - POST {basePath}/trigger - Trigger a job
43
73
  * - POST {basePath}/retry?runId=xxx - Retry a failed run
44
74
  * - POST {basePath}/cancel?runId=xxx - Cancel a run
45
- *
46
- * @param request - The incoming HTTP request
47
- * @param basePath - The base path to strip from the URL (e.g., '/api/durably')
48
- * @returns Response or null if route not matched
49
- *
50
- * @example
51
- * ```ts
52
- * // React Router / Remix
53
- * export async function loader({ request }) {
54
- * return durablyHandler.handle(request, '/api/durably')
55
- * }
56
- * export async function action({ request }) {
57
- * return durablyHandler.handle(request, '/api/durably')
58
- * }
59
- * ```
75
+ * - DELETE {basePath}/run?runId=xxx - Delete a run
60
76
  */
61
77
  handle(request: Request, basePath: string): Promise<Response>;
62
- /**
63
- * Handle job trigger request
64
- * Expects POST with JSON body: { jobName, input, idempotencyKey?, concurrencyKey? }
65
- * Returns JSON: { runId }
66
- */
67
- trigger(request: Request): Promise<Response>;
68
- /**
69
- * Handle subscription request
70
- * Expects GET with query param: runId
71
- * Returns SSE stream of events
72
- */
73
- subscribe(request: Request): Response;
74
- /**
75
- * Handle runs list request
76
- * Expects GET with optional query params: jobName, status, limit, offset
77
- * Returns JSON array of runs
78
- */
79
- runs(request: Request): Promise<Response>;
80
- /**
81
- * Handle single run request
82
- * Expects GET with query param: runId
83
- * Returns JSON run object or 404
84
- */
85
- run(request: Request): Promise<Response>;
86
- /**
87
- * Handle retry request
88
- * Expects POST with query param: runId
89
- * Returns JSON: { success: true }
90
- */
91
- retry(request: Request): Promise<Response>;
92
- /**
93
- * Handle cancel request
94
- * Expects POST with query param: runId
95
- * Returns JSON: { success: true }
96
- */
97
- cancel(request: Request): Promise<Response>;
98
- /**
99
- * Handle delete request
100
- * Expects DELETE with query param: runId
101
- * Returns JSON: { success: true }
102
- */
103
- delete(request: Request): Promise<Response>;
104
- /**
105
- * Handle steps request
106
- * Expects GET with query param: runId
107
- * Returns JSON array of steps
108
- */
109
- steps(request: Request): Promise<Response>;
110
- /**
111
- * Handle runs subscription request
112
- * Expects GET with optional query param: jobName
113
- * Returns SSE stream of run update notifications
114
- */
115
- runsSubscribe(request: Request): Response;
116
78
  }
117
79
  /**
118
80
  * Options for createDurablyHandler
119
81
  */
120
- interface CreateDurablyHandlerOptions {
82
+ interface CreateDurablyHandlerOptions<TContext = undefined, TLabels extends Record<string, string> = Record<string, string>> {
121
83
  /**
122
- * Called before handling each request.
84
+ * Called before handling each request (after authentication).
123
85
  * Use this to initialize Durably (migrate, start worker, etc.)
124
- *
125
- * @example
126
- * ```ts
127
- * const durablyHandler = createDurablyHandler(durably, {
128
- * onRequest: async () => {
129
- * await durably.migrate()
130
- * durably.start()
131
- * }
132
- * })
133
- * ```
134
86
  */
135
87
  onRequest?: () => Promise<void> | void;
136
88
  /**
137
89
  * Throttle interval in milliseconds for SSE progress events.
138
- * When set, `run:progress` events are throttled per run so the client
139
- * receives at most one progress update per throttle window.
140
- * The first and last progress events are always delivered.
141
- * Non-progress events are never throttled.
142
- *
143
- * Set to 0 to disable throttling.
144
90
  * @default 100
145
91
  */
146
92
  sseThrottleMs?: number;
93
+ /**
94
+ * Auth middleware. When set, authenticate is required and auth applies to ALL endpoints.
95
+ */
96
+ auth?: AuthConfig<TContext, TLabels>;
147
97
  }
148
98
  /**
149
99
  * Create HTTP handlers for Durably
150
100
  * Uses Web Standard Request/Response for framework-agnostic usage
151
101
  */
152
- declare function createDurablyHandler(durably: Durably, options?: CreateDurablyHandlerOptions): DurablyHandler;
102
+ declare function createDurablyHandler<TContext = undefined, TLabels extends Record<string, string> = Record<string, string>>(durably: Durably<any, TLabels>, options?: CreateDurablyHandlerOptions<TContext, TLabels>): DurablyHandler;
153
103
 
154
- export { CancelledError, type CreateDurablyHandlerOptions, Durably, type DurablyHandler, type TriggerRequest, type TriggerResponse, createDurablyHandler };
104
+ export { type AuthConfig, CancelledError, type CreateDurablyHandlerOptions, Durably, type DurablyHandler, Run, RunFilter, type RunOperation, type RunsSubscribeFilter, type TriggerRequest, type TriggerResponse, createDurablyHandler };