@sqlane/core 0.1.0 → 0.1.2

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.
@@ -0,0 +1,651 @@
1
+ export type Duration = string | number;
2
+ export type Jsonable<T> = T extends string | number | boolean | null ? T : T extends undefined | bigint | symbol | Function | Date | RegExp | Map<any, any> | Set<any> ? never : T extends readonly (infer U)[] ? readonly Jsonable<U>[] : T extends object ? {
3
+ [K in keyof T]: Jsonable<T[K]>;
4
+ } : never;
5
+ export type ExponentialBackoff = {
6
+ /**
7
+ * `delay × 2^(attempt − 1)`, at most `maxDelay`.
8
+ */
9
+ type: 'exponential';
10
+ delay: Duration;
11
+ maxDelay?: Duration;
12
+ /**
13
+ * 0–1: the delay is `delay × (1 − jitter × random())`. Default: 0.2.
14
+ */
15
+ jitter?: number;
16
+ };
17
+ export type FixedBackoff = {
18
+ type: 'fixed';
19
+ delay: Duration;
20
+ /**
21
+ * 0–1. Default: 0.2.
22
+ */
23
+ jitter?: number;
24
+ };
25
+ export type Backoff = ExponentialBackoff | FixedBackoff;
26
+ export type Retention = {
27
+ /**
28
+ * Default: `'1h'`.
29
+ */
30
+ completed?: Duration;
31
+ /**
32
+ * Default: `'30d'`.
33
+ */
34
+ failed?: Duration;
35
+ };
36
+ export type JobOptions = {
37
+ /**
38
+ * Matches `^[a-z][a-z0-9_.-]{0,127}$`.
39
+ */
40
+ name: string;
41
+ /**
42
+ * Default: 3.
43
+ */
44
+ maxAttempts?: number;
45
+ /**
46
+ * Default: 2.
47
+ */
48
+ maxStalls?: number;
49
+ backoff?: Backoff;
50
+ retention?: Retention;
51
+ /**
52
+ * No default. A job with mutex keys needs one.
53
+ */
54
+ timeout?: Duration;
55
+ /**
56
+ * Sync, from the payload only.
57
+ */
58
+ mutexKeys?: (payload: any) => readonly string[];
59
+ /**
60
+ * Added when a job fails for good.
61
+ */
62
+ failureJob?: string | JobDefinition;
63
+ };
64
+ export type JobDefinition = Readonly<JobOptions>;
65
+ export type EnqueueBaseOptions = {
66
+ /**
67
+ * 1–120 printable ASCII characters. Dedupe: a job with this id is added once while its row
68
+ * exists.
69
+ */
70
+ jobId?: string;
71
+ maxAttempts?: number;
72
+ maxStalls?: number;
73
+ retention?: Retention;
74
+ failureJob?: string | JobDefinition;
75
+ /**
76
+ * Replace the keys of the definition; `[]` clears them.
77
+ */
78
+ mutexKeys?: readonly string[];
79
+ };
80
+ export type EnqueueOptions = EnqueueBaseOptions & ({
81
+ delay?: Duration;
82
+ runAt?: never;
83
+ } | {
84
+ runAt: Date;
85
+ delay?: never;
86
+ });
87
+ export type EnqueueResult = {
88
+ /**
89
+ * `<job_name>:<jobId>`, or a new ULID without a `jobId`.
90
+ */
91
+ id: string;
92
+ /**
93
+ * False: a job with this `jobId` exists already, nothing was added.
94
+ */
95
+ created: boolean;
96
+ };
97
+ export type EnqueueItem<P> = {
98
+ payload: P;
99
+ options?: EnqueueOptions;
100
+ };
101
+ export type JobInfo = {
102
+ id: string;
103
+ name: string;
104
+ /**
105
+ * 1 for the first run.
106
+ */
107
+ attempt: number;
108
+ maxAttempts: number;
109
+ enqueuedAt: Date;
110
+ mutexKeys: readonly string[];
111
+ };
112
+ export type HandlerContext = {
113
+ /**
114
+ * As JSON gives it back.
115
+ */
116
+ payload: any;
117
+ job: JobInfo;
118
+ /**
119
+ * Aborted with a `JobTimeoutError`, a `LeaseLostError` or a `ShutdownError`.
120
+ */
121
+ signal: AbortSignal;
122
+ };
123
+ export type Handler = (context: HandlerContext) => unknown;
124
+ export type HandleOptions<L extends string> = {
125
+ timeout?: Duration;
126
+ } & ({
127
+ concurrency?: number;
128
+ lane?: never;
129
+ } | {
130
+ lane: L;
131
+ concurrency?: never;
132
+ });
133
+ export type LaneOptions = {
134
+ /**
135
+ * The handlers of the lane that run at the same time in one process.
136
+ */
137
+ concurrency: number;
138
+ };
139
+ export type JobDefaults = {
140
+ maxAttempts?: number;
141
+ maxStalls?: number;
142
+ backoff?: Backoff;
143
+ retention?: Retention;
144
+ timeout?: Duration;
145
+ };
146
+ export type Hint = {
147
+ publish: (channel: string, message: string) => Promise<void>;
148
+ /**
149
+ * Resolves to the unsubscribe function.
150
+ */
151
+ subscribe: (channel: string, onMessage: (message: string) => void) => Promise<() => Promise<void>>;
152
+ };
153
+ export type LogFn = (object: object, message?: string) => void;
154
+ export type Logger = {
155
+ debug: LogFn;
156
+ info: LogFn;
157
+ warn: LogFn;
158
+ error: LogFn;
159
+ };
160
+ export type JobEventBase = {
161
+ jobId: string;
162
+ jobName: string;
163
+ attempt: number;
164
+ };
165
+ export type SqlaneEvent = (JobEventBase & ({
166
+ type: 'claimed';
167
+ } | {
168
+ type: 'completed';
169
+ durationMs: number;
170
+ } | {
171
+ type: 'retried';
172
+ durationMs: number;
173
+ delayMs: number;
174
+ error: unknown;
175
+ } | {
176
+ type: 'failed';
177
+ durationMs: number;
178
+ error: unknown;
179
+ } | {
180
+ type: 'timed_out';
181
+ durationMs: number;
182
+ } | {
183
+ type: 'stalled';
184
+ } | {
185
+ type: 'released';
186
+ } | {
187
+ type: 'lease_lost';
188
+ } | {
189
+ type: 'failure_job_not_added';
190
+ failureJob: string;
191
+ error: unknown;
192
+ })) | {
193
+ type: 'error';
194
+ operation: string;
195
+ error: unknown;
196
+ };
197
+ export type Adapter<C> = {
198
+ dialect: 'mysql' | 'postgres';
199
+ /**
200
+ * Used by sqlane: wraps a connection given to `enqueue`.
201
+ */
202
+ wrap: (connection: C) => Executor;
203
+ /**
204
+ * Used by sqlane: the queue's own pool.
205
+ */
206
+ queue: Executor;
207
+ /**
208
+ * Used by sqlane: runs func on
209
+ * one connection of the queue's pool, then releases it.
210
+ */
211
+ withConnection: <T>(func: (executor: Executor) => Promise<T>) => Promise<T>;
212
+ /**
213
+ * Used by sqlane: runs func
214
+ * in a `READ COMMITTED` transaction on a connection of the queue's pool.
215
+ */
216
+ queueTransaction: <T>(func: (executor: Executor) => Promise<T>) => Promise<T>;
217
+ /**
218
+ * Used by sqlane:
219
+ * a pool → func in a transaction of its own; a connection or a client → func on it, in the caller's transaction.
220
+ */
221
+ inTransaction: <T>(connection: C, func: (executor: Executor) => Promise<T>) => Promise<T>;
222
+ /**
223
+ * PostgreSQL: hints by LISTEN/NOTIFY.
224
+ */
225
+ notify?: boolean;
226
+ /**
227
+ * Used by
228
+ * sqlane, with `notify`: LISTEN on a connection of its own; resolves to the unlisten function.
229
+ */
230
+ listen?: (channel: string, onMessage: (message: string) => void) => Promise<() => Promise<void>>;
231
+ };
232
+ export type Executor = {
233
+ query: (sql: string, params: readonly unknown[]) => Promise<readonly Record<string, unknown>[]>;
234
+ /**
235
+ * Runs an INSERT, UPDATE or DELETE:
236
+ * the number of rows it changed.
237
+ */
238
+ run: (sql: string, params: readonly unknown[]) => Promise<number>;
239
+ };
240
+ export type SqlaneOptions<C, L extends string> = {
241
+ adapter: Adapter<C>;
242
+ /**
243
+ * The definitions known by name.
244
+ */
245
+ jobs?: readonly JobDefinition[];
246
+ /**
247
+ * The handlers that run at the same time in this process. Default: 10.
248
+ */
249
+ concurrency?: number;
250
+ /**
251
+ * Limits shared by several job names.
252
+ */
253
+ lanes?: Record<L, LaneOptions>;
254
+ defaults?: JobDefaults;
255
+ /**
256
+ * Default: `'allow'`. `'reject'`: `enqueue` of a name that is not in
257
+ * `jobs` and not handled throws an `UnknownJobError`.
258
+ */
259
+ unknownJobs?: 'allow' | 'reject';
260
+ /**
261
+ * Default: null: polling only.
262
+ */
263
+ hint?: Hint | null;
264
+ /**
265
+ * Default: no logs.
266
+ */
267
+ logger?: Logger;
268
+ onEvent?: (event: SqlaneEvent) => void;
269
+ /**
270
+ * Default: `'sqlane_'`.
271
+ */
272
+ tablePrefix?: string;
273
+ /**
274
+ * Default: `'5s'`.
275
+ */
276
+ pollInterval?: Duration;
277
+ /**
278
+ * Default: 256 KiB.
279
+ */
280
+ maxPayloadBytes?: number;
281
+ /**
282
+ * Built-in deletion of the expired jobs. Default: true.
283
+ */
284
+ cleanup?: boolean;
285
+ };
286
+ export type DrainOptions = {
287
+ /**
288
+ * Default: `'10s'`.
289
+ */
290
+ timeout?: Duration;
291
+ /**
292
+ * Also runs the delayed jobs and the retries in backoff at once.
293
+ */
294
+ includeDelayed?: boolean;
295
+ };
296
+ export type DrainResult = {
297
+ completed: number;
298
+ /**
299
+ * Failed attempts that were retried.
300
+ */
301
+ retried: number;
302
+ /**
303
+ * Jobs that failed for good.
304
+ */
305
+ failed: number;
306
+ };
307
+ export type CloseOptions = {
308
+ /**
309
+ * From the call. Default: `'30s'`.
310
+ */
311
+ deadline?: Duration;
312
+ /**
313
+ * Default: `'release'`.
314
+ */
315
+ mode?: 'release' | 'abandon';
316
+ /**
317
+ * From the call: the latest time the process exits. Default: the deadline plus one
318
+ * lease.
319
+ */
320
+ exitsBy?: Duration;
321
+ };
322
+ export type MigrateOptions = {
323
+ /**
324
+ * Default: `'10m'`.
325
+ */
326
+ lockTimeout?: Duration;
327
+ };
328
+ export type JobStats = {
329
+ name: string;
330
+ pending: number;
331
+ due: number;
332
+ running: number;
333
+ /**
334
+ * Timed out, the handler has not settled.
335
+ */
336
+ stuck: number;
337
+ failed: number;
338
+ oldestDueAgeMs: number | null;
339
+ };
340
+ export type FailedJobPayload = {
341
+ job: {
342
+ id: string;
343
+ name: string;
344
+ payload: any;
345
+ attempt: number;
346
+ maxAttempts: number;
347
+ };
348
+ error: {
349
+ name: string;
350
+ message: string;
351
+ stack?: string;
352
+ };
353
+ };
354
+ export type Sqlane<C, L extends string> = {
355
+ /**
356
+ * Adds a job with the caller's connection: it must be in a transaction (or a
357
+ * pool: then sqlane opens its own transaction).
358
+ */
359
+ enqueue: <P>(connection: C, job: JobDefinition | string, payload: P & NoInfer<Jsonable<P>>, options?: EnqueueOptions) => Promise<EnqueueResult>;
360
+ /**
361
+ * The results are in the order of the items.
362
+ */
363
+ enqueueMany: <P>(connection: C, job: JobDefinition | string, items: readonly (EnqueueItem<P> & NoInfer<EnqueueItem<Jsonable<P>>>)[]) => Promise<EnqueueResult[]>;
364
+ handle: (job: JobDefinition | string, handler: Handler, options?: HandleOptions<L>) => void;
365
+ start: () => Promise<void>;
366
+ /**
367
+ * For tests: runs the due jobs of the handled
368
+ * names until none is left, without `start()`.
369
+ */
370
+ drain: (options?: DrainOptions) => Promise<DrainResult>;
371
+ close: (options?: CloseOptions) => Promise<void>;
372
+ /**
373
+ * Applies the missing migrations:
374
+ * their ids.
375
+ */
376
+ migrate: (options?: MigrateOptions) => Promise<{
377
+ applied: string[];
378
+ }>;
379
+ getStats: () => Promise<JobStats[]>;
380
+ /**
381
+ * Low level: probes this name now.
382
+ */
383
+ wake: (jobName: string) => void;
384
+ };
385
+ export type Migration = {
386
+ id: string;
387
+ statements: readonly string[];
388
+ };
389
+ /**
390
+ * A duration: milliseconds, or a string of parse-duration (`'500ms'`, `'5s'`, `'4m30s'`, `'1h'`, `'2 days'`).
391
+ * @typedef {string | number} Duration
392
+ */
393
+ /**
394
+ * The JSON form of T: T itself if it is JSON, else a type that T does not match. Optional fields and interfaces pass;
395
+ * a `Date`, a `Map`, a `Set`, a function, a `bigint` or a class instance with methods does not.
396
+ * @template T
397
+ * @typedef {T extends string | number | boolean | null ? T
398
+ * : T extends undefined | bigint | symbol | Function | Date | RegExp | Map<any, any> | Set<any> ? never
399
+ * : T extends readonly (infer U)[] ? readonly Jsonable<U>[]
400
+ * : T extends object ? { [K in keyof T]: Jsonable<T[K]> }
401
+ * : never} Jsonable
402
+ */
403
+ /**
404
+ * @typedef {object} ExponentialBackoff
405
+ * @property {'exponential'} type `delay × 2^(attempt − 1)`, at most `maxDelay`.
406
+ * @property {Duration} delay
407
+ * @property {Duration} [maxDelay]
408
+ * @property {number} [jitter] 0–1: the delay is `delay × (1 − jitter × random())`. Default: 0.2.
409
+ */
410
+ /**
411
+ * @typedef {object} FixedBackoff
412
+ * @property {'fixed'} type
413
+ * @property {Duration} delay
414
+ * @property {number} [jitter] 0–1. Default: 0.2.
415
+ */
416
+ /** @typedef {ExponentialBackoff | FixedBackoff} Backoff */
417
+ /**
418
+ * How long a finished job is kept. It is also the dedupe window of its `jobId`.
419
+ * @typedef {object} Retention
420
+ * @property {Duration} [completed] Default: `'1h'`.
421
+ * @property {Duration} [failed] Default: `'30d'`.
422
+ */
423
+ /**
424
+ * The options of a job name, shared by its producers and workers. A definition overrides the instance `defaults`.
425
+ * @typedef {object} JobOptions
426
+ * @property {string} name Matches `^[a-z][a-z0-9_.-]{0,127}$`.
427
+ * @property {number} [maxAttempts] Default: 3.
428
+ * @property {number} [maxStalls] Default: 2.
429
+ * @property {Backoff} [backoff]
430
+ * @property {Retention} [retention]
431
+ * @property {Duration} [timeout] No default. A job with mutex keys needs one.
432
+ * @property {(payload: any) => readonly string[]} [mutexKeys] Sync, from the payload only.
433
+ * @property {string | JobDefinition} [failureJob] Added when a job fails for good.
434
+ */
435
+ /**
436
+ * The result of `defineJob()`. Frozen.
437
+ * @typedef {Readonly<JobOptions>} JobDefinition
438
+ */
439
+ /**
440
+ * @typedef {object} EnqueueBaseOptions
441
+ * @property {string} [jobId] 1–120 printable ASCII characters. Dedupe: a job with this id is added once while its row
442
+ * exists.
443
+ * @property {number} [maxAttempts]
444
+ * @property {number} [maxStalls]
445
+ * @property {Retention} [retention]
446
+ * @property {string | JobDefinition} [failureJob]
447
+ * @property {readonly string[]} [mutexKeys] Replace the keys of the definition; `[]` clears them.
448
+ */
449
+ /**
450
+ * The options of an enqueue: `delay` or `runAt`, not both.
451
+ * @typedef {EnqueueBaseOptions & ({ delay?: Duration, runAt?: never } | { runAt: Date, delay?: never })}
452
+ * EnqueueOptions
453
+ */
454
+ /**
455
+ * @typedef {object} EnqueueResult
456
+ * @property {string} id `<job_name>:<jobId>`, or a new ULID without a `jobId`.
457
+ * @property {boolean} created False: a job with this `jobId` exists already, nothing was added.
458
+ */
459
+ /**
460
+ * @template P
461
+ * @typedef {object} EnqueueItem
462
+ * @property {P} payload
463
+ * @property {EnqueueOptions} [options]
464
+ */
465
+ /**
466
+ * @typedef {object} JobInfo
467
+ * @property {string} id
468
+ * @property {string} name
469
+ * @property {number} attempt 1 for the first run.
470
+ * @property {number} maxAttempts
471
+ * @property {Date} enqueuedAt
472
+ * @property {readonly string[]} mutexKeys
473
+ */
474
+ /**
475
+ * @typedef {object} HandlerContext
476
+ * @property {any} payload As JSON gives it back.
477
+ * @property {JobInfo} job
478
+ * @property {AbortSignal} signal Aborted with a `JobTimeoutError`, a `LeaseLostError` or a `ShutdownError`.
479
+ */
480
+ /**
481
+ * Runs a job. Its return value is ignored.
482
+ * @typedef {(context: HandlerContext) => unknown} Handler
483
+ */
484
+ /**
485
+ * A private lane (`concurrency`) or a shared lane of `lanes` (`lane`), not both.
486
+ * @template {string} L
487
+ * @typedef {{ timeout?: Duration } & (
488
+ * { concurrency?: number, lane?: never } | { lane: L, concurrency?: never }
489
+ * )} HandleOptions
490
+ */
491
+ /**
492
+ * @typedef {object} LaneOptions
493
+ * @property {number} concurrency The handlers of the lane that run at the same time in one process.
494
+ */
495
+ /**
496
+ * The instance defaults; a definition overrides them.
497
+ * @typedef {object} JobDefaults
498
+ * @property {number} [maxAttempts]
499
+ * @property {number} [maxStalls]
500
+ * @property {Backoff} [backoff]
501
+ * @property {Retention} [retention]
502
+ * @property {Duration} [timeout]
503
+ */
504
+ /**
505
+ * A pub/sub that wakes the other processes. Only an optimization: a lost message delays a job until the next poll.
506
+ * @typedef {object} Hint
507
+ * @property {(channel: string, message: string) => Promise<void>} publish
508
+ * @property {(channel: string, onMessage: (message: string) => void) => Promise<() => Promise<void>>} subscribe
509
+ * Resolves to the unsubscribe function.
510
+ */
511
+ /**
512
+ * @typedef {(object: object, message?: string) => void} LogFn
513
+ */
514
+ /**
515
+ * A pino-compatible logger.
516
+ * @typedef {object} Logger
517
+ * @property {LogFn} debug
518
+ * @property {LogFn} info
519
+ * @property {LogFn} warn
520
+ * @property {LogFn} error
521
+ */
522
+ /**
523
+ * @typedef {object} JobEventBase
524
+ * @property {string} jobId
525
+ * @property {string} jobName
526
+ * @property {number} attempt
527
+ */
528
+ /**
529
+ * An event of the job lifecycle, by `type`.
530
+ * @typedef {(JobEventBase & (
531
+ * | { type: 'claimed' }
532
+ * | { type: 'completed', durationMs: number }
533
+ * | { type: 'retried', durationMs: number, delayMs: number, error: unknown }
534
+ * | { type: 'failed', durationMs: number, error: unknown }
535
+ * | { type: 'timed_out', durationMs: number }
536
+ * | { type: 'stalled' }
537
+ * | { type: 'released' }
538
+ * | { type: 'lease_lost' }
539
+ * | { type: 'failure_job_not_added', failureJob: string, error: unknown }
540
+ * )) | { type: 'error', operation: string, error: unknown }} SqlaneEvent
541
+ */
542
+ /**
543
+ * The connections that `enqueue` accepts (C), and the queue's own pool. Made by `mysql2Adapter()` or `pgAdapter()`.
544
+ * @template C
545
+ * @typedef {object} Adapter
546
+ * @property {'mysql' | 'postgres'} dialect
547
+ * @property {(connection: C) => Executor} wrap Used by sqlane: wraps a connection given to `enqueue`.
548
+ * @property {Executor} queue Used by sqlane: the queue's own pool.
549
+ * @property {<T>(func: (executor: Executor) => Promise<T>) => Promise<T>} withConnection Used by sqlane: runs func on
550
+ * one connection of the queue's pool, then releases it.
551
+ * @property {<T>(func: (executor: Executor) => Promise<T>) => Promise<T>} queueTransaction Used by sqlane: runs func
552
+ * in a `READ COMMITTED` transaction on a connection of the queue's pool.
553
+ * @property {<T>(connection: C, func: (executor: Executor) => Promise<T>) => Promise<T>} inTransaction Used by sqlane:
554
+ * a pool → func in a transaction of its own; a connection or a client → func on it, in the caller's transaction.
555
+ * @property {boolean} [notify] PostgreSQL: hints by LISTEN/NOTIFY.
556
+ * @property {(channel: string, onMessage: (message: string) => void) => Promise<() => Promise<void>>} [listen] Used by
557
+ * sqlane, with `notify`: LISTEN on a connection of its own; resolves to the unlisten function.
558
+ */
559
+ /**
560
+ * Runs SQL. Used by sqlane, through an adapter.
561
+ * @typedef {object} Executor
562
+ * @property {(sql: string, params: readonly unknown[]) => Promise<readonly Record<string, unknown>[]>} query
563
+ * @property {(sql: string, params: readonly unknown[]) => Promise<number>} run Runs an INSERT, UPDATE or DELETE:
564
+ * the number of rows it changed.
565
+ */
566
+ /**
567
+ * @template C
568
+ * @template {string} L
569
+ * @typedef {object} SqlaneOptions
570
+ * @property {Adapter<C>} adapter
571
+ * @property {readonly JobDefinition[]} [jobs] The definitions known by name.
572
+ * @property {number} [concurrency] The handlers that run at the same time in this process. Default: 10.
573
+ * @property {Record<L, LaneOptions>} [lanes] Limits shared by several job names.
574
+ * @property {JobDefaults} [defaults]
575
+ * @property {'allow' | 'reject'} [unknownJobs] Default: `'allow'`. `'reject'`: `enqueue` of a name that is not in
576
+ * `jobs` and not handled throws an `UnknownJobError`.
577
+ * @property {Hint | null} [hint] Default: null: polling only.
578
+ * @property {Logger} [logger] Default: no logs.
579
+ * @property {(event: SqlaneEvent) => void} [onEvent]
580
+ * @property {string} [tablePrefix] Default: `'sqlane_'`.
581
+ * @property {Duration} [pollInterval] Default: `'5s'`.
582
+ * @property {number} [maxPayloadBytes] Default: 256 KiB.
583
+ * @property {boolean} [cleanup] Built-in deletion of the expired jobs. Default: true.
584
+ */
585
+ /**
586
+ * @typedef {object} DrainOptions
587
+ * @property {Duration} [timeout] Default: `'10s'`.
588
+ * @property {boolean} [includeDelayed] Also runs the delayed jobs and the retries in backoff at once.
589
+ */
590
+ /**
591
+ * @typedef {object} DrainResult
592
+ * @property {number} completed
593
+ * @property {number} retried Failed attempts that were retried.
594
+ * @property {number} failed Jobs that failed for good.
595
+ */
596
+ /**
597
+ * @typedef {object} CloseOptions
598
+ * @property {Duration} [deadline] From the call. Default: `'30s'`.
599
+ * @property {'release' | 'abandon'} [mode] Default: `'release'`.
600
+ * @property {Duration} [exitsBy] From the call: the latest time the process exits. Default: the deadline plus one
601
+ * lease.
602
+ */
603
+ /**
604
+ * @typedef {object} MigrateOptions
605
+ * @property {Duration} [lockTimeout] Default: `'10m'`.
606
+ */
607
+ /**
608
+ * @typedef {object} JobStats
609
+ * @property {string} name
610
+ * @property {number} pending
611
+ * @property {number} due
612
+ * @property {number} running
613
+ * @property {number} stuck Timed out, the handler has not settled.
614
+ * @property {number} failed
615
+ * @property {number | null} oldestDueAgeMs
616
+ */
617
+ /**
618
+ * The payload of a failure job.
619
+ * @typedef {object} FailedJobPayload
620
+ * @property {{ id: string, name: string, payload: any, attempt: number, maxAttempts: number }} job
621
+ * @property {{ name: string, message: string, stack?: string }} error
622
+ */
623
+ /**
624
+ * @template C The connections that `enqueue` accepts.
625
+ * @template {string} L The lane names.
626
+ * @typedef {object} Sqlane
627
+ * @property {<P>(
628
+ * connection: C, job: JobDefinition | string, payload: P & NoInfer<Jsonable<P>>, options?: EnqueueOptions,
629
+ * ) => Promise<EnqueueResult>} enqueue Adds a job with the caller's connection: it must be in a transaction (or a
630
+ * pool: then sqlane opens its own transaction).
631
+ * @property {<P>(
632
+ * connection: C,
633
+ * job: JobDefinition | string,
634
+ * items: readonly (EnqueueItem<P> & NoInfer<EnqueueItem<Jsonable<P>>>)[],
635
+ * ) => Promise<EnqueueResult[]>} enqueueMany The results are in the order of the items.
636
+ * @property {(job: JobDefinition | string, handler: Handler, options?: HandleOptions<L>) => void} handle
637
+ * @property {() => Promise<void>} start
638
+ * @property {(options?: DrainOptions) => Promise<DrainResult>} drain For tests: runs the due jobs of the handled
639
+ * names until none is left, without `start()`.
640
+ * @property {(options?: CloseOptions) => Promise<void>} close
641
+ * @property {(options?: MigrateOptions) => Promise<{ applied: string[] }>} migrate Applies the missing migrations:
642
+ * their ids.
643
+ * @property {() => Promise<JobStats[]>} getStats
644
+ * @property {(jobName: string) => void} wake Low level: probes this name now.
645
+ */
646
+ /**
647
+ * @typedef {object} Migration
648
+ * @property {string} id
649
+ * @property {readonly string[]} statements
650
+ */
651
+ export {};