@boringnode/queue 0.0.1-alpha.3 → 0.1.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.
@@ -0,0 +1,1013 @@
1
+ /**
2
+ * Calculates retry delays using configurable strategies.
3
+ *
4
+ * Supports three built-in strategies:
5
+ * - `exponential`: Delay doubles each attempt (1s, 2s, 4s, 8s, ...)
6
+ * - `linear`: Delay increases linearly (5s, 10s, 15s, 20s, ...)
7
+ * - `fixed`: Same delay every time (10s, 10s, 10s, ...)
8
+ *
9
+ * All strategies support:
10
+ * - `maxDelay`: Cap the maximum delay
11
+ * - `jitter`: Add randomness to prevent thundering herd
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const strategy = new BackoffStrategy({
16
+ * strategy: 'exponential',
17
+ * baseDelay: '1s',
18
+ * maxDelay: '5m',
19
+ * multiplier: 2,
20
+ * jitter: true,
21
+ * })
22
+ *
23
+ * strategy.calculateDelay(1) // ~1000ms
24
+ * strategy.calculateDelay(2) // ~2000ms
25
+ * strategy.calculateDelay(3) // ~4000ms
26
+ * ```
27
+ */
28
+ declare class BackoffStrategy$1 {
29
+ #private;
30
+ /**
31
+ * Create a new backoff strategy.
32
+ *
33
+ * @param config - Backoff configuration
34
+ * @throws {E_INVALID_BASE_DELAY} If baseDelay is not positive
35
+ * @throws {E_INVALID_MAX_DELAY} If maxDelay is invalid
36
+ * @throws {E_INVALID_MULTIPLIER} If multiplier is invalid
37
+ */
38
+ constructor(config: BackoffConfig);
39
+ /**
40
+ * Calculate the delay for a given attempt number.
41
+ *
42
+ * @param attempt - The attempt number (1-based)
43
+ * @returns Delay in milliseconds
44
+ * @throws {RuntimeException} If attempt is less than 1
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * // Exponential: 1s, 2s, 4s, 8s, 16s, ...
49
+ * strategy.calculateDelay(1) // 1000
50
+ * strategy.calculateDelay(2) // 2000
51
+ * strategy.calculateDelay(3) // 4000
52
+ * ```
53
+ */
54
+ calculateDelay(attempt: number): number;
55
+ /**
56
+ * Get the Date when the next retry should occur.
57
+ *
58
+ * @param attempt - The attempt number (1-based)
59
+ * @returns Date for the next retry
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const nextRetry = strategy.getNextRetryAt(3)
64
+ * console.log(`Retry at: ${nextRetry.toISOString()}`)
65
+ * ```
66
+ */
67
+ getNextRetryAt(attempt: number): Date;
68
+ /**
69
+ * Get a frozen copy of the configuration.
70
+ *
71
+ * @returns Readonly configuration object
72
+ */
73
+ getConfig(): Readonly<BackoffConfig>;
74
+ }
75
+ /**
76
+ * Create an exponential backoff strategy factory.
77
+ *
78
+ * Delay doubles with each attempt: 1s → 2s → 4s → 8s → ...
79
+ *
80
+ * Default config:
81
+ * - baseDelay: 1s
82
+ * - maxDelay: 5m
83
+ * - multiplier: 2
84
+ * - jitter: true
85
+ *
86
+ * @param config - Optional overrides for default config
87
+ * @returns Factory function for creating BackoffStrategy instances
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const config = {
92
+ * retry: {
93
+ * maxRetries: 5,
94
+ * backoff: exponentialBackoff({ baseDelay: '500ms', maxDelay: '1m' }),
95
+ * },
96
+ * }
97
+ * ```
98
+ */
99
+ declare function exponentialBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>): () => BackoffStrategy$1;
100
+ /**
101
+ * Create a linear backoff strategy factory.
102
+ *
103
+ * Delay increases linearly: 5s → 10s → 15s → 20s → ...
104
+ *
105
+ * Default config:
106
+ * - baseDelay: 5s
107
+ * - maxDelay: 2m
108
+ *
109
+ * @param config - Optional overrides for default config
110
+ * @returns Factory function for creating BackoffStrategy instances
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * const config = {
115
+ * retry: {
116
+ * maxRetries: 3,
117
+ * backoff: linearBackoff({ baseDelay: '10s' }),
118
+ * },
119
+ * }
120
+ * ```
121
+ */
122
+ declare function linearBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>): () => BackoffStrategy$1;
123
+ /**
124
+ * Create a fixed delay backoff strategy factory.
125
+ *
126
+ * Same delay every time: 10s → 10s → 10s → ...
127
+ *
128
+ * @param delay - The fixed delay (default: '10s')
129
+ * @returns Factory function for creating BackoffStrategy instances
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const config = {
134
+ * retry: {
135
+ * maxRetries: 3,
136
+ * backoff: fixedBackoff('30s'),
137
+ * },
138
+ * }
139
+ * ```
140
+ */
141
+ declare function fixedBackoff(delay?: Duration): () => BackoffStrategy$1;
142
+ /**
143
+ * Create a custom backoff strategy factory.
144
+ *
145
+ * Use this when you need full control over the configuration.
146
+ *
147
+ * @param config - Complete backoff configuration
148
+ * @returns Factory function for creating BackoffStrategy instances
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * const config = {
153
+ * retry: {
154
+ * maxRetries: 5,
155
+ * backoff: customBackoff({
156
+ * strategy: 'exponential',
157
+ * baseDelay: '100ms',
158
+ * maxDelay: '30s',
159
+ * multiplier: 3,
160
+ * jitter: false,
161
+ * }),
162
+ * },
163
+ * }
164
+ * ```
165
+ */
166
+ declare function customBackoff(config: BackoffConfig): () => BackoffStrategy$1;
167
+
168
+ interface LogObject {
169
+ [key: string]: unknown;
170
+ }
171
+ interface ErrorObject extends LogObject {
172
+ err?: Error;
173
+ }
174
+ interface Logger {
175
+ trace(msg: string): void;
176
+ trace(obj: LogObject, msg: string): void;
177
+ debug(msg: string): void;
178
+ debug(obj: LogObject, msg: string): void;
179
+ info(msg: string): void;
180
+ info(obj: LogObject, msg: string): void;
181
+ warn(msg: string): void;
182
+ warn(obj: LogObject, msg: string): void;
183
+ error(msg: string): void;
184
+ error(obj: ErrorObject, msg: string): void;
185
+ child(obj: LogObject): Logger;
186
+ }
187
+
188
+ type Duration = number | string;
189
+ /**
190
+ * Result returned when dispatching a job.
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * const { jobId } = await SendEmailJob.dispatch(payload)
195
+ * console.log(`Dispatched job: ${jobId}`)
196
+ * ```
197
+ */
198
+ interface DispatchResult {
199
+ /** Unique identifier for this specific job instance */
200
+ jobId: string;
201
+ }
202
+ interface JobData {
203
+ id: string;
204
+ name: string;
205
+ payload: any;
206
+ attempts: number;
207
+ priority?: number;
208
+ nextRetryAt?: Date;
209
+ stalledCount?: number;
210
+ }
211
+ interface JobOptions {
212
+ queue?: string;
213
+ adapter?: string | (() => Adapter);
214
+ maxRetries?: number;
215
+ priority?: number;
216
+ retry?: RetryConfig;
217
+ timeout?: Duration;
218
+ failOnTimeout?: boolean;
219
+ }
220
+ /**
221
+ * Context information available to a job during execution.
222
+ *
223
+ * Provides metadata about the current job execution, including
224
+ * retry information, queue details, and timing.
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * class MyJob extends Job<Payload> {
229
+ * async execute() {
230
+ * console.log(`Attempt ${this.context.attempt} of job ${this.context.jobId}`)
231
+ * console.log(`Running on queue: ${this.context.queue}`)
232
+ * }
233
+ * }
234
+ * ```
235
+ */
236
+ interface JobContext {
237
+ /** Unique identifier for this job */
238
+ jobId: string;
239
+ /** Job class name */
240
+ name: string;
241
+ /** Current attempt number (1-based: first attempt = 1) */
242
+ attempt: number;
243
+ /** Queue name this job is being processed from */
244
+ queue: string;
245
+ /** Job priority (lower number = higher priority) */
246
+ priority: number;
247
+ /** When this job was acquired by the worker for processing */
248
+ acquiredAt: Date;
249
+ /** Number of times this job has been recovered from stalled state */
250
+ stalledCount: number;
251
+ }
252
+ type JobClass<T extends Job = Job> = (new (payload: any, context: JobContext) => T) & {
253
+ options?: JobOptions;
254
+ };
255
+ /**
256
+ * Factory function for custom job instantiation.
257
+ *
258
+ * Use this to integrate with IoC containers for dependency injection.
259
+ * The factory receives the job class, payload, and context, and must return
260
+ * a job instance (or a Promise that resolves to one).
261
+ *
262
+ * @param JobClass - The job class to instantiate
263
+ * @param payload - The payload data for the job
264
+ * @param context - The job execution context (jobId, attempt, queue, etc.)
265
+ * @returns The job instance, or a Promise resolving to the instance
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * // With AdonisJS IoC container
270
+ * const worker = new Worker({
271
+ * worker: {
272
+ * jobFactory: async (JobClass, payload, context) => {
273
+ * return app.container.make(JobClass, [payload, context])
274
+ * }
275
+ * }
276
+ * })
277
+ * ```
278
+ */
279
+ type JobFactory = (JobClass: JobClass, payload: any, context: JobContext) => Job | Promise<Job>;
280
+ interface RetryConfig {
281
+ maxRetries?: number;
282
+ backoff?: () => BackoffStrategy$1;
283
+ }
284
+ type BackoffStrategy = 'exponential' | 'linear' | 'fixed';
285
+ interface BackoffConfig {
286
+ strategy: BackoffStrategy;
287
+ baseDelay: Duration;
288
+ maxDelay?: Duration;
289
+ multiplier?: number;
290
+ jitter?: boolean;
291
+ }
292
+ interface QueueConfig {
293
+ adapter?: string;
294
+ retry?: any;
295
+ }
296
+ interface WorkerConfig {
297
+ /**
298
+ * Maximum number of jobs to process concurrently.
299
+ * @default 1
300
+ */
301
+ concurrency?: number;
302
+ /**
303
+ * Delay between queue polls when idle (no jobs available).
304
+ * @default '2s'
305
+ */
306
+ idleDelay?: Duration;
307
+ /**
308
+ * Maximum duration a job can run before being timed out.
309
+ * Can be overridden per job via JobOptions.timeout.
310
+ * @default undefined (no timeout)
311
+ */
312
+ timeout?: Duration;
313
+ /**
314
+ * Duration after which an active job is considered stalled.
315
+ * A stalled job is one that was acquired but the worker stopped
316
+ * responding (e.g., due to a crash).
317
+ * @default '30s'
318
+ */
319
+ stalledThreshold?: Duration;
320
+ /**
321
+ * How often to check for stalled jobs.
322
+ * @default '30s'
323
+ */
324
+ stalledInterval?: Duration;
325
+ /**
326
+ * Maximum number of times a job can be recovered from stalled state
327
+ * before being marked as failed permanently.
328
+ * @default 1
329
+ */
330
+ maxStalledCount?: number;
331
+ /**
332
+ * Whether to automatically stop the worker on SIGINT/SIGTERM signals.
333
+ * When enabled, the worker will wait for running jobs to complete
334
+ * before stopping.
335
+ * @default true
336
+ */
337
+ gracefulShutdown?: boolean;
338
+ /**
339
+ * Callback invoked when a shutdown signal is received.
340
+ * Called before the worker starts stopping.
341
+ */
342
+ onShutdownSignal?: () => void | Promise<void>;
343
+ }
344
+ type WorkerCycle = {
345
+ type: 'started';
346
+ queue: string;
347
+ job: any;
348
+ } | {
349
+ type: 'completed';
350
+ queue: string;
351
+ job: any;
352
+ } | {
353
+ type: 'idle';
354
+ suggestedDelay: Duration;
355
+ } | {
356
+ type: 'error';
357
+ error: Error;
358
+ suggestedDelay: Duration;
359
+ };
360
+ type AdapterFactory<T extends Adapter = Adapter> = () => T;
361
+ /**
362
+ * Status of a schedule.
363
+ */
364
+ type ScheduleStatus = 'active' | 'paused';
365
+ /**
366
+ * Configuration for creating a schedule.
367
+ * Used by ScheduleBuilder to collect schedule options before creation.
368
+ */
369
+ interface ScheduleConfig {
370
+ /** Optional ID for the schedule (UUID if not set). Used for upsert. */
371
+ id?: string;
372
+ /** Job class name */
373
+ jobName: string;
374
+ /** Job payload */
375
+ payload: any;
376
+ /** Cron expression (mutually exclusive with everyMs) */
377
+ cronExpression?: string;
378
+ /** Interval in milliseconds (mutually exclusive with cronExpression) */
379
+ everyMs?: number;
380
+ /** IANA timezone for cron evaluation */
381
+ timezone: string;
382
+ /** Start boundary - no jobs dispatched before this */
383
+ from?: Date;
384
+ /** End boundary - no jobs dispatched after this */
385
+ to?: Date;
386
+ /** Maximum number of runs (null = unlimited) */
387
+ limit?: number;
388
+ }
389
+ /**
390
+ * Persisted schedule data.
391
+ * Represents a schedule stored in the adapter.
392
+ */
393
+ interface ScheduleData {
394
+ /** Unique identifier */
395
+ id: string;
396
+ /** Job class name */
397
+ jobName: string;
398
+ /** Job payload */
399
+ payload: any;
400
+ /** Cron expression (null if using interval) */
401
+ cronExpression: string | null;
402
+ /** Interval in milliseconds (null if using cron) */
403
+ everyMs: number | null;
404
+ /** IANA timezone */
405
+ timezone: string;
406
+ /** Start boundary - no jobs dispatched before this */
407
+ from: Date | null;
408
+ /** End boundary - no jobs dispatched after this */
409
+ to: Date | null;
410
+ /** Maximum number of runs */
411
+ limit: number | null;
412
+ /** Number of times this schedule has run */
413
+ runCount: number;
414
+ /** Next scheduled run time */
415
+ nextRunAt: Date | null;
416
+ /** Last run time */
417
+ lastRunAt: Date | null;
418
+ /** Current status */
419
+ status: ScheduleStatus;
420
+ /** When the schedule was created */
421
+ createdAt: Date;
422
+ }
423
+ /**
424
+ * Result returned when creating a schedule.
425
+ */
426
+ interface ScheduleResult {
427
+ /** Unique identifier for the schedule */
428
+ scheduleId: string;
429
+ }
430
+ /**
431
+ * Options for listing schedules.
432
+ */
433
+ interface ScheduleListOptions {
434
+ /** Filter by status */
435
+ status?: ScheduleStatus;
436
+ }
437
+ interface QueueManagerConfig {
438
+ default: string;
439
+ adapters: Record<string, AdapterFactory>;
440
+ retry?: RetryConfig;
441
+ queues?: Record<string, QueueConfig>;
442
+ worker?: WorkerConfig;
443
+ locations?: string[];
444
+ logger?: Logger;
445
+ /**
446
+ * Custom factory function for job instantiation.
447
+ *
448
+ * Use this to integrate with IoC containers for dependency injection.
449
+ * When provided, this factory is called instead of `new JobClass(payload, context)`.
450
+ *
451
+ * @example
452
+ * ```typescript
453
+ * await QueueManager.init({
454
+ * default: 'redis',
455
+ * adapters: { redis: redis() },
456
+ * jobFactory: async (JobClass, payload, context) => {
457
+ * return app.container.make(JobClass, [payload, context])
458
+ * }
459
+ * })
460
+ * ```
461
+ */
462
+ jobFactory?: JobFactory;
463
+ }
464
+
465
+ /**
466
+ * A job that has been acquired by a worker for processing.
467
+ * Extends JobData with the timestamp when the job was acquired.
468
+ */
469
+ interface AcquiredJob extends JobData {
470
+ /** Timestamp (in ms) when the job was acquired by the worker */
471
+ acquiredAt: number;
472
+ }
473
+ /**
474
+ * Adapter interface for queue storage backends.
475
+ *
476
+ * Implementations handle job persistence, atomic operations, and
477
+ * concurrency control. Built-in adapters: Redis, Knex (PostgreSQL/SQLite).
478
+ *
479
+ * @example
480
+ * ```typescript
481
+ * import { redis } from '@boringnode/queue'
482
+ *
483
+ * const config = {
484
+ * default: 'redis',
485
+ * adapters: {
486
+ * redis: redis({ host: 'localhost', port: 6379 })
487
+ * }
488
+ * }
489
+ * ```
490
+ */
491
+ interface Adapter {
492
+ /**
493
+ * Set the worker ID for this adapter instance.
494
+ * Required before calling pop methods when consuming jobs.
495
+ *
496
+ * @param workerId - Unique identifier for the worker
497
+ */
498
+ setWorkerId(workerId: string): void;
499
+ /**
500
+ * Pop the next available job from the default queue.
501
+ * Atomically moves the job from pending to active state.
502
+ *
503
+ * @returns The acquired job, or null if queue is empty
504
+ */
505
+ pop(): Promise<AcquiredJob | null>;
506
+ /**
507
+ * Pop the next available job from a specific queue.
508
+ * Atomically moves the job from pending to active state.
509
+ *
510
+ * @param queue - The queue name to pop from
511
+ * @returns The acquired job, or null if queue is empty
512
+ */
513
+ popFrom(queue: string): Promise<AcquiredJob | null>;
514
+ /**
515
+ * Recover stalled jobs that have been active for too long.
516
+ * A stalled job is one where the worker stopped responding (e.g., crash).
517
+ *
518
+ * Jobs within maxStalledCount are moved back to pending.
519
+ * Jobs exceeding maxStalledCount are failed permanently.
520
+ *
521
+ * @param queue - The queue to check for stalled jobs
522
+ * @param stalledThreshold - Duration in ms after which a job is considered stalled
523
+ * @param maxStalledCount - Maximum times a job can be recovered before failing
524
+ * @returns Number of jobs that were recovered (not including permanently failed ones)
525
+ */
526
+ recoverStalledJobs(queue: string, stalledThreshold: number, maxStalledCount: number): Promise<number>;
527
+ /**
528
+ * Mark a job as completed and remove it from the queue.
529
+ *
530
+ * @param jobId - The job ID to complete
531
+ * @param queue - The queue the job belongs to
532
+ */
533
+ completeJob(jobId: string, queue: string): Promise<void>;
534
+ /**
535
+ * Mark a job as failed permanently and remove it from the queue.
536
+ *
537
+ * @param jobId - The job ID to fail
538
+ * @param queue - The queue the job belongs to
539
+ * @param error - Optional error that caused the failure
540
+ */
541
+ failJob(jobId: string, queue: string, error?: Error): Promise<void>;
542
+ /**
543
+ * Retry a job by moving it back to pending with incremented attempts.
544
+ *
545
+ * @param jobId - The job ID to retry
546
+ * @param queue - The queue the job belongs to
547
+ * @param retryAt - Optional future date to delay the retry
548
+ */
549
+ retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void>;
550
+ /**
551
+ * Push a job to the default queue for immediate processing.
552
+ *
553
+ * @param jobData - The job data to push
554
+ */
555
+ push(jobData: JobData): Promise<void>;
556
+ /**
557
+ * Push a job to a specific queue for immediate processing.
558
+ *
559
+ * @param queue - The queue name to push to
560
+ * @param jobData - The job data to push
561
+ */
562
+ pushOn(queue: string, jobData: JobData): Promise<void>;
563
+ /**
564
+ * Push a job to the default queue with a delay.
565
+ *
566
+ * @param jobData - The job data to push
567
+ * @param delay - Delay in milliseconds before the job becomes available
568
+ */
569
+ pushLater(jobData: JobData, delay: number): Promise<void>;
570
+ /**
571
+ * Push a job to a specific queue with a delay.
572
+ *
573
+ * @param queue - The queue name to push to
574
+ * @param jobData - The job data to push
575
+ * @param delay - Delay in milliseconds before the job becomes available
576
+ */
577
+ pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
578
+ /**
579
+ * Get the number of pending jobs in the default queue.
580
+ *
581
+ * @returns The number of pending jobs
582
+ */
583
+ size(): Promise<number>;
584
+ /**
585
+ * Get the number of pending jobs in a specific queue.
586
+ *
587
+ * @param queue - The queue name to check
588
+ * @returns The number of pending jobs
589
+ */
590
+ sizeOf(queue: string): Promise<number>;
591
+ /**
592
+ * Clean up resources (close connections, etc.).
593
+ * Called when the worker stops or the adapter is no longer needed.
594
+ */
595
+ destroy(): Promise<void>;
596
+ /**
597
+ * Create or update a schedule.
598
+ *
599
+ * If a schedule with the given id exists, it will be updated (upsert).
600
+ * Otherwise, a new schedule is created.
601
+ *
602
+ * @param config - The schedule configuration
603
+ * @returns The schedule ID
604
+ */
605
+ createSchedule(config: ScheduleConfig): Promise<string>;
606
+ /**
607
+ * Get a schedule by ID.
608
+ *
609
+ * @param id - The schedule ID
610
+ * @returns The schedule data, or null if not found
611
+ */
612
+ getSchedule(id: string): Promise<ScheduleData | null>;
613
+ /**
614
+ * List all schedules matching the given options.
615
+ *
616
+ * @param options - Optional filters for listing
617
+ * @returns Array of schedule data
618
+ */
619
+ listSchedules(options?: ScheduleListOptions): Promise<ScheduleData[]>;
620
+ /**
621
+ * Update a schedule's status or run metadata.
622
+ *
623
+ * @param id - The schedule ID
624
+ * @param updates - The fields to update
625
+ */
626
+ updateSchedule(id: string, updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>): Promise<void>;
627
+ /**
628
+ * Delete a schedule permanently.
629
+ *
630
+ * @param id - The schedule ID to delete
631
+ */
632
+ deleteSchedule(id: string): Promise<void>;
633
+ /**
634
+ * Atomically claim a due schedule for execution.
635
+ *
636
+ * This method:
637
+ * 1. Finds ONE schedule where nextRunAt <= now AND status = 'active'
638
+ * 2. Calculates and updates its nextRunAt to the next occurrence
639
+ * 3. Increments runCount and sets lastRunAt
640
+ * 4. Returns the schedule data for job dispatching
641
+ *
642
+ * The atomic nature prevents multiple workers from claiming the same schedule.
643
+ *
644
+ * @returns The claimed schedule, or null if no schedules are due
645
+ */
646
+ claimDueSchedule(): Promise<ScheduleData | null>;
647
+ }
648
+
649
+ /**
650
+ * Fluent builder for dispatching jobs to the queue.
651
+ *
652
+ * Provides a chainable API for configuring job options before dispatch.
653
+ * Usually created via `Job.dispatch()` rather than directly.
654
+ *
655
+ * ```
656
+ * Job.dispatch(payload)
657
+ * .toQueue('emails') // optional: target queue
658
+ * .priority(1) // optional: 1-10, lower = higher priority
659
+ * .in('5m') // optional: delay before processing
660
+ * .with('redis') // optional: specific adapter
661
+ * .run() // dispatch the job
662
+ * ```
663
+ *
664
+ * @typeParam T - The payload type for this job
665
+ *
666
+ * @example
667
+ * ```typescript
668
+ * // Simple dispatch (auto-runs via thenable)
669
+ * await SendEmailJob.dispatch({ to: 'user@example.com', subject: 'Hello' })
670
+ *
671
+ * // With options
672
+ * const jobId = await SendEmailJob.dispatch({ to: 'user@example.com' })
673
+ * .toQueue('high-priority')
674
+ * .priority(1)
675
+ * .run()
676
+ *
677
+ * // Delayed job
678
+ * await ReminderJob.dispatch({ userId: 123 }).in('24h')
679
+ * ```
680
+ */
681
+ declare class JobDispatcher<T> {
682
+ #private;
683
+ /**
684
+ * Create a new job dispatcher.
685
+ *
686
+ * @param name - The job class name (used to locate the class at runtime)
687
+ * @param payload - The data to pass to the job
688
+ */
689
+ constructor(name: string, payload: T);
690
+ /**
691
+ * Set the target queue for this job.
692
+ *
693
+ * @param queue - Queue name (default: 'default')
694
+ * @returns This dispatcher for chaining
695
+ *
696
+ * @example
697
+ * ```typescript
698
+ * await SendEmailJob.dispatch(payload).toQueue('emails')
699
+ * ```
700
+ */
701
+ toQueue(queue: string): this;
702
+ /**
703
+ * Delay the job execution.
704
+ *
705
+ * The job will be stored in a delayed state and moved to pending
706
+ * after the delay expires.
707
+ *
708
+ * @param delay - Delay as milliseconds or duration string ('5s', '1h', '7d')
709
+ * @returns This dispatcher for chaining
710
+ *
711
+ * @example
712
+ * ```typescript
713
+ * // Send reminder in 24 hours
714
+ * await ReminderJob.dispatch(payload).in('24h')
715
+ *
716
+ * // Process in 5 minutes
717
+ * await CleanupJob.dispatch(payload).in('5m')
718
+ * ```
719
+ */
720
+ in(delay: Duration): this;
721
+ /**
722
+ * Set the job priority.
723
+ *
724
+ * Lower numbers = higher priority. Jobs with lower priority values
725
+ * are processed before jobs with higher values.
726
+ *
727
+ * @param priority - Priority level (1-10, default: 5)
728
+ * @returns This dispatcher for chaining
729
+ *
730
+ * @example
731
+ * ```typescript
732
+ * // High priority job
733
+ * await UrgentJob.dispatch(payload).priority(1)
734
+ *
735
+ * // Low priority job
736
+ * await BackgroundJob.dispatch(payload).priority(10)
737
+ * ```
738
+ */
739
+ priority(priority: number): this;
740
+ /**
741
+ * Use a specific adapter for this job.
742
+ *
743
+ * @param adapter - Adapter name or factory function
744
+ * @returns This dispatcher for chaining
745
+ *
746
+ * @example
747
+ * ```typescript
748
+ * // Use named adapter
749
+ * await Job.dispatch(payload).with('redis')
750
+ *
751
+ * // Use custom adapter instance
752
+ * await Job.dispatch(payload).with(() => new CustomAdapter())
753
+ * ```
754
+ */
755
+ with(adapter: string | (() => Adapter)): this;
756
+ /**
757
+ * Dispatch the job to the queue.
758
+ *
759
+ * @returns A DispatchResult containing the jobId
760
+ *
761
+ * @example
762
+ * ```typescript
763
+ * const { jobId } = await SendEmailJob.dispatch(payload).run()
764
+ * console.log(`Dispatched job: ${jobId}`)
765
+ * ```
766
+ */
767
+ run(): Promise<DispatchResult>;
768
+ /**
769
+ * Thenable implementation for auto-dispatch when awaited.
770
+ *
771
+ * Allows `await Job.dispatch(payload)` without explicit `.run()`.
772
+ *
773
+ * @param onFulfilled - Success callback
774
+ * @param onRejected - Error callback
775
+ * @returns Promise resolving to the DispatchResult
776
+ */
777
+ then(onFulfilled?: (value: DispatchResult) => any, onRejected?: (reason: any) => any): Promise<any>;
778
+ }
779
+
780
+ /**
781
+ * Fluent builder for creating job schedules.
782
+ *
783
+ * @example
784
+ * ```typescript
785
+ * // Create with cron
786
+ * const { scheduleId } = await new ScheduleBuilder('CleanupJob', { days: 30 })
787
+ * .id('cleanup-daily')
788
+ * .cron('0 0 * * *')
789
+ * .timezone('Europe/Paris')
790
+ * .run()
791
+ *
792
+ * // Create with interval
793
+ * const { scheduleId } = await new ScheduleBuilder('SyncJob', {})
794
+ * .every('5m')
795
+ * .run()
796
+ * ```
797
+ */
798
+ declare class ScheduleBuilder implements PromiseLike<ScheduleResult> {
799
+ #private;
800
+ constructor(jobName: string, payload: any);
801
+ /**
802
+ * Set a custom schedule ID.
803
+ * If not specified, defaults to the job name.
804
+ * If a schedule with this ID exists, it will be updated (upsert).
805
+ */
806
+ id(scheduleId: string): this;
807
+ /**
808
+ * Set a cron expression for the schedule.
809
+ * Mutually exclusive with `every()`.
810
+ */
811
+ cron(expression: string): this;
812
+ /**
813
+ * Set a repeating interval for the schedule.
814
+ * Mutually exclusive with `cron()`.
815
+ */
816
+ every(interval: Duration): this;
817
+ /**
818
+ * Set the timezone for cron evaluation.
819
+ * @default 'UTC'
820
+ */
821
+ timezone(tz: string): this;
822
+ /**
823
+ * Set the start boundary for the schedule.
824
+ * No jobs will be dispatched before this date.
825
+ */
826
+ from(date: Date): this;
827
+ /**
828
+ * Set the end boundary for the schedule.
829
+ * No jobs will be dispatched after this date.
830
+ */
831
+ to(date: Date): this;
832
+ /**
833
+ * Set both start and end boundaries for the schedule.
834
+ * Shorthand for `.from(start).to(end)`.
835
+ */
836
+ between(from: Date, to: Date): this;
837
+ /**
838
+ * Set the maximum number of runs for this schedule.
839
+ */
840
+ limit(maxRuns: number): this;
841
+ /**
842
+ * Create the schedule and return the schedule ID.
843
+ */
844
+ run(): Promise<ScheduleResult>;
845
+ /**
846
+ * Implement PromiseLike to allow `await builder.every('5m')` syntax.
847
+ */
848
+ then<TResult1 = ScheduleResult, TResult2 = never>(onfulfilled?: ((value: ScheduleResult) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
849
+ }
850
+
851
+ /**
852
+ * Abstract base class for all queue jobs.
853
+ *
854
+ * Extend this class to create your own jobs. Each job must implement
855
+ * the `execute()` method which contains the job's business logic.
856
+ *
857
+ * @typeParam Payload - The type of data this job receives
858
+ *
859
+ * @example
860
+ * ```typescript
861
+ * import { Job } from '@boringnode/queue'
862
+ * import type { JobContext } from '@boringnode/queue'
863
+ *
864
+ * interface SendEmailPayload {
865
+ * to: string
866
+ * subject: string
867
+ * body: string
868
+ * }
869
+ *
870
+ * export default class SendEmailJob extends Job<SendEmailPayload> {
871
+ * static options = {
872
+ * queue: 'emails',
873
+ * maxRetries: 3,
874
+ * }
875
+ *
876
+ * constructor(payload: SendEmailPayload, context: JobContext) {
877
+ * super(payload, context)
878
+ * }
879
+ *
880
+ * async execute() {
881
+ * console.log(`Attempt ${this.context.attempt} for job ${this.context.jobId}`)
882
+ * await sendEmail(this.payload.to, this.payload.subject, this.payload.body)
883
+ * }
884
+ *
885
+ * async failed(error: Error) {
886
+ * console.error(`Failed to send email to ${this.payload.to}:`, error)
887
+ * }
888
+ * }
889
+ * ```
890
+ */
891
+ declare abstract class Job<Payload = any> {
892
+ #private;
893
+ /** Static options for this job class (queue, retries, timeout, etc.) */
894
+ static options: JobOptions;
895
+ /** The payload data passed to this job instance */
896
+ get payload(): Payload;
897
+ /**
898
+ * Context information for the current job execution.
899
+ *
900
+ * Provides metadata such as job ID, current attempt number,
901
+ * queue name, priority, and timing information.
902
+ *
903
+ * @example
904
+ * ```typescript
905
+ * async execute() {
906
+ * if (this.context.attempt > 1) {
907
+ * console.log(`Retry attempt ${this.context.attempt}`)
908
+ * }
909
+ * console.log(`Processing job ${this.context.jobId} on queue ${this.context.queue}`)
910
+ * }
911
+ * ```
912
+ */
913
+ get context(): JobContext;
914
+ /**
915
+ * Create a new job instance.
916
+ *
917
+ * @param payload - The data to be processed by this job
918
+ * @param context - The job execution context (provided by the worker)
919
+ */
920
+ constructor(payload: Payload, context: JobContext);
921
+ /**
922
+ * Dispatch this job to the queue.
923
+ *
924
+ * Returns a JobDispatcher for fluent configuration before dispatching.
925
+ * The job is not actually dispatched until `.run()` is called or the
926
+ * dispatcher is awaited.
927
+ *
928
+ * @param payload - The data to pass to the job
929
+ * @returns A JobDispatcher for fluent configuration
930
+ *
931
+ * @example
932
+ * ```typescript
933
+ * // Simple dispatch
934
+ * await SendEmailJob.dispatch({ to: 'user@example.com', subject: 'Hello' })
935
+ *
936
+ * // With options
937
+ * await SendEmailJob.dispatch({ to: 'user@example.com' })
938
+ * .toQueue('high-priority')
939
+ * .priority(1)
940
+ * .in('5m')
941
+ * .run()
942
+ * ```
943
+ */
944
+ static dispatch<T extends Job>(this: new (payload: any, context: JobContext) => T, payload: T extends Job<infer P> ? P : never): JobDispatcher<T extends Job<infer P> ? P : never>;
945
+ /**
946
+ * Create a schedule for this job.
947
+ *
948
+ * Returns a ScheduleBuilder for fluent configuration before creating the schedule.
949
+ * The schedule is not actually created until `.run()` is called or the
950
+ * builder is awaited.
951
+ *
952
+ * @param payload - The data to pass to the job on each run
953
+ * @returns A ScheduleBuilder for fluent configuration
954
+ *
955
+ * @example
956
+ * ```typescript
957
+ * // Cron schedule
958
+ * await CleanupJob.schedule({ days: 30 })
959
+ * .id('cleanup-daily')
960
+ * .cron('0 0 * * *')
961
+ * .timezone('Europe/Paris')
962
+ * .run()
963
+ *
964
+ * // Interval schedule
965
+ * await SyncJob.schedule({ source: 'api' })
966
+ * .every('5m')
967
+ * .run()
968
+ * ```
969
+ */
970
+ static schedule<T extends Job>(this: new (payload: any, context: JobContext) => T, payload: T extends Job<infer P> ? P : never): ScheduleBuilder;
971
+ /**
972
+ * Execute the job's business logic.
973
+ *
974
+ * This method is called by the worker when processing the job.
975
+ * Implement your job's logic here.
976
+ *
977
+ * @param signal - Optional AbortSignal for timeout handling.
978
+ * Check `signal.aborted` for long-running operations.
979
+ * @throws Any error thrown will trigger retry logic (if configured)
980
+ *
981
+ * @example
982
+ * ```typescript
983
+ * async execute(signal?: AbortSignal) {
984
+ * for (const item of this.payload.items) {
985
+ * if (signal?.aborted) {
986
+ * throw new Error('Job timed out')
987
+ * }
988
+ * await processItem(item)
989
+ * }
990
+ * }
991
+ * ```
992
+ */
993
+ abstract execute(signal?: AbortSignal): Promise<void>;
994
+ /**
995
+ * Called when the job has permanently failed (after all retries exhausted).
996
+ *
997
+ * Use this hook for cleanup, logging, or notifications.
998
+ * This is optional - implement only if you need failure handling.
999
+ *
1000
+ * @param error - The error that caused the final failure
1001
+ *
1002
+ * @example
1003
+ * ```typescript
1004
+ * async failed(error: Error) {
1005
+ * await notifyAdmin(`Job failed: ${error.message}`)
1006
+ * await cleanup(this.payload)
1007
+ * }
1008
+ * ```
1009
+ */
1010
+ failed?(error: Error): Promise<void>;
1011
+ }
1012
+
1013
+ export { type Adapter as A, type BackoffStrategy as B, type Duration as D, type JobData as J, type Logger as L, type QueueManagerConfig as Q, type RetryConfig as R, type ScheduleConfig as S, type WorkerCycle as W, type AcquiredJob as a, type ScheduleData as b, type ScheduleListOptions as c, type JobFactory as d, Job as e, type JobClass as f, type ScheduleStatus as g, ScheduleBuilder as h, customBackoff as i, exponentialBackoff as j, fixedBackoff as k, linearBackoff as l, type DispatchResult as m, type JobOptions as n, type JobContext as o, type BackoffConfig as p, type QueueConfig as q, type WorkerConfig as r, type AdapterFactory as s, type ScheduleResult as t };