@boringnode/queue 0.0.1-alpha.4 → 0.2.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
@@ -1,5 +1,15 @@
1
1
  # @boringnode/queue
2
2
 
3
+ <div align="center">
4
+
5
+ [![typescript-image]][typescript-url]
6
+ [![gh-workflow-image]][gh-workflow-url]
7
+ [![npm-image]][npm-url]
8
+ [![npm-download-image]][npm-download-url]
9
+ [![license-image]][license-url]
10
+
11
+ </div>
12
+
3
13
  A simple and efficient queue system for Node.js applications. Built for simplicity and ease of use, `@boringnode/queue` allows you to dispatch background jobs and process them asynchronously with support for multiple queue adapters.
4
14
 
5
15
  ## Installation
@@ -19,6 +29,7 @@ npm install @boringnode/queue
19
29
  - **Priority Queues**: Process high-priority jobs first
20
30
  - **Retry with Backoff**: Automatic retries with exponential, linear, or fixed backoff strategies
21
31
  - **Job Timeout**: Automatically fail or retry jobs that exceed a time limit
32
+ - **Scheduled Jobs**: Cron-based or interval-based job scheduling with pause/resume support
22
33
 
23
34
  ## Quick Start
24
35
 
@@ -28,29 +39,27 @@ Create a job by extending the `Job` class:
28
39
 
29
40
  ```typescript
30
41
  import { Job } from '@boringnode/queue'
31
- import type { JobContext, JobOptions } from '@boringnode/queue/types'
42
+ import type { JobOptions } from '@boringnode/queue/types'
32
43
 
33
44
  interface SendEmailPayload {
34
45
  to: string
35
46
  }
36
47
 
37
48
  export default class SendEmailJob extends Job<SendEmailPayload> {
38
- static readonly jobName = 'SendEmailJob'
39
-
40
49
  static options: JobOptions = {
41
50
  queue: 'email',
42
51
  }
43
52
 
44
- constructor(payload: SendEmailPayload, context: JobContext) {
45
- super(payload, context)
46
- }
47
-
48
53
  async execute(): Promise<void> {
49
54
  console.log(`[Attempt ${this.context.attempt}] Sending email to: ${this.payload.to}`)
50
55
  }
51
56
  }
52
57
  ```
53
58
 
59
+ > **Note**: The job name defaults to the class name (`SendEmailJob`). You can override it with `name: 'CustomName'` in options if needed.
60
+ >
61
+ > **Warning**: If you minify your code in production, class names may be mangled. In that case, always specify `name` explicitly in your job options.
62
+
54
63
  ### 2. Configure the Queue Manager
55
64
 
56
65
  ```typescript
@@ -125,7 +134,7 @@ interface QueueManagerConfig {
125
134
  // Worker configuration
126
135
  worker: {
127
136
  concurrency: number
128
- pollingInterval: string
137
+ idleDelay: Duration
129
138
  }
130
139
 
131
140
  // Job discovery locations
@@ -243,8 +252,6 @@ Jobs with lower priority numbers are processed first:
243
252
 
244
253
  ```typescript
245
254
  export default class UrgentJob extends Job<Payload> {
246
- static readonly jobName = 'UrgentJob'
247
-
248
255
  static options: JobOptions = {
249
256
  priority: 1, // Processed before default priority (5)
250
257
  }
@@ -263,8 +270,6 @@ Configure automatic retries with backoff strategies:
263
270
  import { exponentialBackoff, linearBackoff, fixedBackoff } from '@boringnode/queue'
264
271
 
265
272
  export default class ReliableJob extends Job<Payload> {
266
- static readonly jobName = 'ReliableJob'
267
-
268
273
  static options: JobOptions = {
269
274
  maxRetries: 5,
270
275
  retry: {
@@ -296,8 +301,6 @@ Set a maximum execution time for jobs:
296
301
 
297
302
  ```typescript
298
303
  export default class LimitedJob extends Job<Payload> {
299
- static readonly jobName = 'LimitedJob'
300
-
301
304
  static options: JobOptions = {
302
305
  timeout: '30s', // Maximum execution time
303
306
  failOnTimeout: false, // Retry on timeout (default)
@@ -319,19 +322,42 @@ const config = {
319
322
  }
320
323
  ```
321
324
 
325
+ ### Handling Timeout Gracefully
326
+
327
+ Jobs have access to an abort signal via `this.signal` to handle timeouts gracefully:
328
+
329
+ ```typescript
330
+ export default class LongRunningJob extends Job<Payload> {
331
+ static options: JobOptions = {
332
+ timeout: '30s',
333
+ }
334
+
335
+ async execute(): Promise<void> {
336
+ for (const item of this.payload.items) {
337
+ // Check if the job has been aborted
338
+ if (this.signal?.aborted) {
339
+ throw new Error('Job timed out')
340
+ }
341
+
342
+ await this.processItem(item)
343
+ }
344
+ }
345
+
346
+ private async processItem(item: any): Promise<void> {
347
+ // Pass the signal to fetch or other async operations
348
+ await fetch(item.url, { signal: this.signal })
349
+ }
350
+ }
351
+ ```
352
+
322
353
  ## Job Context
323
354
 
324
355
  Every job has access to execution context via `this.context`. This provides metadata about the current job execution:
325
356
 
326
357
  ```typescript
327
358
  import { Job } from '@boringnode/queue'
328
- import type { JobContext } from '@boringnode/queue'
329
359
 
330
360
  export default class MyJob extends Job<Payload> {
331
- constructor(payload: Payload, context: JobContext) {
332
- super(payload, context)
333
- }
334
-
335
361
  async execute(): Promise<void> {
336
362
  console.log(`Job ID: ${this.context.jobId}`)
337
363
  console.log(`Attempt: ${this.context.attempt}`) // 1, 2, 3...
@@ -349,7 +375,7 @@ export default class MyJob extends Job<Payload> {
349
375
  ### Context Properties
350
376
 
351
377
  | Property | Type | Description |
352
- | -------------- | ------ | ----------------------------------------------- |
378
+ |----------------|--------|-------------------------------------------------|
353
379
  | `jobId` | string | Unique identifier for this job |
354
380
  | `name` | string | Job class name |
355
381
  | `attempt` | number | Current attempt number (1-based) |
@@ -360,7 +386,7 @@ export default class MyJob extends Job<Payload> {
360
386
 
361
387
  ## Dependency Injection
362
388
 
363
- Use the `jobFactory` option to integrate with IoC containers for dependency injection. This allows your jobs to receive injected services in their constructor.
389
+ Use the `jobFactory` option to integrate with IoC containers for dependency injection. The constructor is reserved for injecting dependencies - payload and context are provided separately by the worker.
364
390
 
365
391
  ```typescript
366
392
  import { QueueManager } from '@boringnode/queue'
@@ -368,9 +394,9 @@ import { QueueManager } from '@boringnode/queue'
368
394
  await QueueManager.init({
369
395
  default: 'redis',
370
396
  adapters: { redis: redis(connection) },
371
- jobFactory: async (JobClass, payload, context) => {
397
+ jobFactory: async (JobClass) => {
372
398
  // Use your IoC container to instantiate jobs
373
- return app.container.make(JobClass, [payload, context])
399
+ return app.container.make(JobClass)
374
400
  },
375
401
  })
376
402
  ```
@@ -379,7 +405,6 @@ Example with injected dependencies:
379
405
 
380
406
  ```typescript
381
407
  import { Job } from '@boringnode/queue'
382
- import type { JobContext } from '@boringnode/queue'
383
408
 
384
409
  interface SendEmailPayload {
385
410
  to: string
@@ -387,15 +412,11 @@ interface SendEmailPayload {
387
412
  }
388
413
 
389
414
  export default class SendEmailJob extends Job<SendEmailPayload> {
390
- static readonly jobName = 'SendEmailJob'
391
-
392
415
  constructor(
393
- payload: SendEmailPayload,
394
- context: JobContext,
395
416
  private mailer: MailerService, // Injected by IoC container
396
417
  private logger: Logger // Injected by IoC container
397
418
  ) {
398
- super(payload, context)
419
+ super()
399
420
  }
400
421
 
401
422
  async execute(): Promise<void> {
@@ -405,7 +426,98 @@ export default class SendEmailJob extends Job<SendEmailPayload> {
405
426
  }
406
427
  ```
407
428
 
408
- Without a `jobFactory`, jobs are instantiated with `new JobClass(payload, context)`.
429
+ Without a `jobFactory`, jobs are instantiated with `new JobClass()`.
430
+
431
+ ## Scheduled Jobs
432
+
433
+ Schedule jobs to run on a recurring basis using cron expressions or fixed intervals. Schedules are persisted and survive worker restarts.
434
+
435
+ ### Creating a Schedule
436
+
437
+ ```typescript
438
+ import { Schedule } from '@boringnode/queue'
439
+
440
+ // Run every 10 seconds (uses job name as schedule ID by default)
441
+ const { scheduleId } = await MetricsJob.schedule({ endpoint: '/api/health' }).every('10s').run()
442
+
443
+ // Run on a cron schedule with custom ID
444
+ await CleanupJob.schedule({ days: 30 })
445
+ .id('daily-cleanup') // Custom ID (optional, defaults to job name)
446
+ .cron('0 * * * *') // Every hour at minute 0
447
+ .timezone('Europe/Paris') // Optional timezone (default: UTC)
448
+ .run()
449
+
450
+ // Schedule with constraints
451
+ await ReportJob.schedule({ type: 'weekly' })
452
+ .id('weekly-report')
453
+ .cron('0 9 * * MON') // Every Monday at 9am
454
+ .from(new Date('2024-01-01')) // Start date
455
+ .to(new Date('2024-12-31')) // End date
456
+ .limit(52) // Maximum 52 runs
457
+ .run()
458
+ ```
459
+
460
+ ### Managing Schedules
461
+
462
+ ```typescript
463
+ import { Schedule } from '@boringnode/queue'
464
+
465
+ // Find a schedule by ID
466
+ const schedule = await Schedule.find('health-check')
467
+
468
+ if (schedule) {
469
+ console.log(`Status: ${schedule.status}`) // 'active' or 'paused'
470
+ console.log(`Run count: ${schedule.runCount}`)
471
+ console.log(`Next run: ${schedule.nextRunAt}`)
472
+ console.log(`Last run: ${schedule.lastRunAt}`)
473
+
474
+ // Pause the schedule
475
+ await schedule.pause()
476
+
477
+ // Resume the schedule
478
+ await schedule.resume()
479
+
480
+ // Trigger an immediate run (outside of the normal schedule)
481
+ await schedule.trigger()
482
+
483
+ // Delete the schedule
484
+ await schedule.delete()
485
+ }
486
+ ```
487
+
488
+ ### Listing Schedules
489
+
490
+ ```typescript
491
+ import { Schedule } from '@boringnode/queue'
492
+
493
+ // List all schedules
494
+ const all = await Schedule.list()
495
+
496
+ // Filter by status
497
+ const active = await Schedule.list({ status: 'active' })
498
+ const paused = await Schedule.list({ status: 'paused' })
499
+ ```
500
+
501
+ ### Schedule Options
502
+
503
+ | Method | Description |
504
+ |----------------------|-------------------------------------------------|
505
+ | `.id(string)` | Unique identifier (defaults to job name) |
506
+ | `.every(duration)` | Run at fixed intervals ('5s', '1m', '1h', '1d') |
507
+ | `.cron(expression)` | Run on a cron schedule |
508
+ | `.timezone(tz)` | Timezone for cron expressions (default: 'UTC') |
509
+ | `.from(date)` | Don't run before this date |
510
+ | `.to(date)` | Don't run after this date |
511
+ | `.between(from, to)` | Shorthand for `.from().to()` |
512
+ | `.limit(n)` | Maximum number of runs |
513
+
514
+ ### How Scheduling Works
515
+
516
+ - Schedules are **persisted** in the database (via the adapter)
517
+ - The **Worker** polls for due schedules and dispatches jobs automatically
518
+ - Each schedule run creates a **new job** with a unique ID
519
+ - Multiple workers can run concurrently - only one will claim each due schedule
520
+ - Failed jobs do **not** affect the schedule (the next run will still occur)
409
521
 
410
522
  ## Job Discovery
411
523
 
@@ -420,10 +532,11 @@ const config = {
420
532
  Jobs must:
421
533
 
422
534
  - Extend the `Job` class
423
- - Have a static `jobName` property
424
535
  - Implement the `execute` method
425
536
  - Be exported as default
426
537
 
538
+ The job name is automatically derived from the class name, or can be explicitly set via `static options = { name: 'CustomName' }`.
539
+
427
540
  ## Logging
428
541
 
429
542
  You can pass a logger to the queue manager for debugging or monitoring. The logger must be compatible with the [pino](https://github.com/pinojs/pino) interface.
@@ -449,7 +562,7 @@ By default, a simple console logger is used that only outputs warnings and error
449
562
  Performance comparison with BullMQ using realistic jobs (5ms simulated work per job):
450
563
 
451
564
  | Jobs | Concurrency | @boringnode/queue | BullMQ | Diff |
452
- | ---- | ----------- | ----------------- | ------ | ----------- |
565
+ |------|-------------|-------------------|--------|-------------|
453
566
  | 100 | 1 | 562ms | 596ms | 5.7% faster |
454
567
  | 100 | 5 | 116ms | 117ms | ~same |
455
568
  | 100 | 10 | 62ms | 62ms | ~same |
@@ -475,3 +588,14 @@ npm run benchmark
475
588
  # Custom job duration
476
589
  npm run benchmark -- --duration=10
477
590
  ```
591
+
592
+ [gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/boringnode/queue/checks.yml?branch=main&style=for-the-badge
593
+ [gh-workflow-url]: https://github.com/boringnode/queue/actions/workflows/checks.yml
594
+ [npm-image]: https://img.shields.io/npm/v/@boringnode/queue.svg?style=for-the-badge&logo=npm
595
+ [npm-url]: https://www.npmjs.com/package/@boringnode/queue
596
+ [npm-download-image]: https://img.shields.io/npm/dm/@boringnode/queue?style=for-the-badge
597
+ [npm-download-url]: https://www.npmjs.com/package/@boringnode/queue
598
+ [typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
599
+ [typescript-url]: https://www.typescriptlang.org
600
+ [license-image]: https://img.shields.io/npm/l/@boringnode/queue?color=blueviolet&style=for-the-badge
601
+ [license-url]: LICENSE.md
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  E_INVALID_DURATION_EXPRESSION,
3
3
  PRIORITY_SCORE_MULTIPLIER
4
- } from "./chunk-HMGNQSSG.js";
4
+ } from "./chunk-SMOKFZ46.js";
5
5
 
6
6
  // src/utils.ts
7
7
  import { parse as parseDuration } from "@lukeed/ms";
@@ -23,4 +23,4 @@ export {
23
23
  parse,
24
24
  calculateScore
25
25
  };
26
- //# sourceMappingURL=chunk-5PDDRF5O.js.map
26
+ //# sourceMappingURL=chunk-NPQKBCCY.js.map
@@ -3,7 +3,7 @@ import {
3
3
  E_CONFIGURATION_ERROR,
4
4
  E_JOB_NOT_FOUND,
5
5
  E_QUEUE_NOT_INITIALIZED
6
- } from "./chunk-HMGNQSSG.js";
6
+ } from "./chunk-SMOKFZ46.js";
7
7
 
8
8
  // src/debug.ts
9
9
  import { debuglog } from "util";
@@ -57,8 +57,9 @@ var LocatorSingleton = class {
57
57
  const absolutePath = resolve(file);
58
58
  const module = await import(`file://${absolutePath}`);
59
59
  const JobClass = module.default;
60
- if (JobClass && typeof JobClass === "function" && JobClass.name) {
61
- this.register(JobClass.name, JobClass);
60
+ if (JobClass && typeof JobClass === "function") {
61
+ const jobName = JobClass.options?.name || JobClass.name;
62
+ this.register(jobName, JobClass);
62
63
  registered++;
63
64
  }
64
65
  } catch (error) {
@@ -354,4 +355,4 @@ export {
354
355
  Locator,
355
356
  QueueManager
356
357
  };
357
- //# sourceMappingURL=chunk-CD45GT6E.js.map
358
+ //# sourceMappingURL=chunk-RIXMQXYJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/debug.ts","../src/locator.ts","../src/logger.ts","../src/queue_manager.ts"],"sourcesContent":["import { debuglog } from 'node:util'\n\nexport default debuglog('boringnode:queue')\n","import { Job } from './job.js'\nimport * as errors from './exceptions.js'\nimport type { JobClass } from './types/main.js'\nimport debug from './debug.js'\nimport { glob } from 'node:fs/promises'\nimport { resolve } from 'node:path'\n\n/**\n * Job class registry.\n *\n * The Locator maintains a mapping of job names to their classes,\n * allowing the Worker to instantiate jobs by name when processing.\n *\n * Jobs are typically registered automatically via `QueueManager.init()`\n * using the `locations` config option, but can also be registered manually.\n *\n * @example\n * ```typescript\n * import { Locator } from '@boringnode/queue'\n * import SendEmailJob from './jobs/send_email_job.js'\n *\n * // Manual registration\n * Locator.register('SendEmailJob', SendEmailJob)\n *\n * // Auto-registration via glob (used by QueueManager.init)\n * await Locator.registerFromGlob(['./jobs/**\\/*.js'])\n *\n * // Retrieve a job class\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * ```\n */\nclass LocatorSingleton {\n #registry = new Map<string, JobClass>()\n\n /**\n * Register a job class with a given name.\n *\n * @param name - The job name (usually the class name)\n * @param JobClass - The job class constructor\n *\n * @example\n * ```typescript\n * Locator.register('SendEmailJob', SendEmailJob)\n * ```\n */\n register<T extends Job>(name: string, JobClass: JobClass<T>) {\n debug('registering job: %s', name)\n\n this.#registry.set(name, JobClass)\n }\n\n /**\n * Auto-register job classes from files matching glob patterns.\n *\n * Each file should have a default export that is a Job class.\n * The class name is used as the registration name.\n *\n * @param patterns - Glob patterns to match job files\n * @returns Number of jobs successfully registered\n *\n * @example\n * ```typescript\n * const count = await Locator.registerFromGlob([\n * './jobs/**\\/*.js',\n * './app/jobs/**\\/*.ts'\n * ])\n * console.log(`Registered ${count} jobs`)\n * ```\n */\n async registerFromGlob(patterns: string[]): Promise<number> {\n let registered = 0\n\n for (const pattern of patterns) {\n debug('registering jobs from glob pattern: %s', pattern)\n for await (const file of glob(pattern)) {\n debug('found job file: %s', file)\n\n try {\n const absolutePath = resolve(file)\n const module = await import(`file://${absolutePath}`)\n const JobClass = module.default as JobClass\n\n if (JobClass && typeof JobClass === 'function') {\n const jobName = JobClass.options?.name || JobClass.name\n this.register(jobName, JobClass)\n registered++\n }\n } catch (error) {\n console.warn(`Failed to load job from ${file}:`, error)\n }\n }\n }\n\n return registered\n }\n\n /**\n * Get a job class by name.\n *\n * @param name - The job name to look up\n * @returns The job class, or undefined if not found\n *\n * @example\n * ```typescript\n * const JobClass = Locator.get('SendEmailJob')\n * if (JobClass) {\n * const instance = new JobClass(payload)\n * }\n * ```\n */\n get<T extends Job = Job>(name: string): JobClass<T> | undefined {\n return this.#registry.get(name) as JobClass<T> | undefined\n }\n\n /**\n * Get a job class by name, throwing if not found.\n *\n * @param name - The job name to look up\n * @returns The job class\n * @throws {E_JOB_NOT_FOUND} If the job is not registered\n *\n * @example\n * ```typescript\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * const instance = new JobClass(payload)\n * ```\n */\n getOrThrow<T extends Job = Job>(name: string): JobClass<T> {\n const JobClass = this.get<T>(name)\n\n if (!JobClass) {\n throw new errors.E_JOB_NOT_FOUND([name])\n }\n\n return JobClass\n }\n\n /**\n * Remove all registered jobs.\n *\n * Primarily useful for testing.\n */\n clear(): void {\n this.#registry.clear()\n }\n}\n\n/** Global job class registry singleton */\nexport const Locator = new LocatorSingleton()\n","export interface LogObject {\n [key: string]: unknown\n}\n\nexport interface ErrorObject extends LogObject {\n err?: Error\n}\n\nexport interface Logger {\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n\n child(obj: LogObject): Logger\n}\n\n/**\n * A simple logger that writes to console.\n */\nclass ConsoleLogger implements Logger {\n #prefix: string\n\n constructor(prefix: string = 'queue') {\n this.#prefix = prefix\n }\n\n #format(level: string, msgOrObj: string | LogObject, msg?: string): [string, LogObject?] {\n const prefix = `[${this.#prefix}] ${level}:`\n\n if (typeof msgOrObj === 'object') {\n return [`${prefix} ${msg}`, msgOrObj]\n }\n\n return [`${prefix} ${msgOrObj}`]\n }\n\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n trace(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('TRACE', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n debug(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('DEBUG', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n info(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('INFO', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n warn(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('WARN', msgOrObj, msg)\n\n if (obj) {\n return console.warn(message, obj)\n }\n\n console.warn(message)\n }\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n error(msgOrObj: string | ErrorObject, msg?: string): void {\n const [message, obj] = this.#format('ERROR', msgOrObj, msg)\n\n if (obj) {\n return console.error(message, obj)\n }\n\n console.error(message)\n }\n\n child(obj: LogObject): Logger {\n const childPrefix = obj.pkg ? String(obj.pkg) : this.#prefix\n return new ConsoleLogger(childPrefix)\n }\n}\n\nexport const consoleLogger = new ConsoleLogger()\n","import * as errors from './exceptions.js'\nimport debug from './debug.js'\nimport { Locator } from './locator.js'\nimport { consoleLogger, type Logger } from './logger.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type {\n AdapterFactory,\n JobFactory,\n QueueConfig,\n QueueManagerConfig,\n RetryConfig,\n} from './types/main.js'\n\n/**\n * Central configuration and adapter management for the queue system.\n *\n * The QueueManager is responsible for:\n * - Initializing adapters and job registration\n * - Providing adapter instances to workers and dispatchers\n * - Managing retry configuration across global, queue, and job levels\n *\n * @example\n * ```typescript\n * import { QueueManager, redis } from '@boringnode/queue'\n *\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis({ host: 'localhost' }),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * retry: {\n * maxRetries: 3,\n * backoff: exponentialBackoff(),\n * },\n * })\n *\n * // Get the default adapter\n * const adapter = QueueManager.use()\n *\n * // Clean up when done\n * await QueueManager.destroy()\n * ```\n */\nclass QueueManagerSingleton {\n #initialized = false\n #defaultAdapter!: string\n #adapters: Record<string, AdapterFactory> = {}\n #adapterInstances: Map<string, Adapter> = new Map()\n #globalRetryConfig?: RetryConfig\n #queueConfigs: Map<string, QueueConfig> = new Map()\n #logger: Logger = consoleLogger\n #jobFactory?: JobFactory\n\n /**\n * Initialize the queue system with the given configuration.\n *\n * This must be called before using the queue system. It:\n * - Validates the configuration\n * - Registers adapters\n * - Auto-discovers and registers job classes from `locations`\n *\n * @param config - The queue configuration\n * @returns This instance for chaining\n * @throws {E_CONFIGURATION_ERROR} If the configuration is invalid\n *\n * @example\n * ```typescript\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis(),\n * postgres: knex(pgConfig),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * })\n * ```\n */\n async init(config: QueueManagerConfig) {\n debug('initializing queue manager with config: %O', config)\n\n this.#validateConfig(config)\n\n this.#adapterInstances.clear()\n\n this.#defaultAdapter = config.default\n this.#adapters = config.adapters\n this.#globalRetryConfig = config.retry\n this.#logger = config.logger ?? consoleLogger\n this.#jobFactory = config.jobFactory\n\n if (config.queues) {\n for (const [queue, queueConfig] of Object.entries(config.queues)) {\n this.#queueConfigs.set(queue, queueConfig as QueueConfig)\n }\n }\n\n if (config.locations && config.locations.length > 0) {\n const registered = await Locator.registerFromGlob(config.locations)\n\n if (registered === 0) {\n this.#logger.warn(\n `No jobs found for locations: ${config.locations.join(', ')}. ` +\n 'Verify your glob patterns match your job files.'\n )\n }\n }\n\n this.#initialized = true\n\n return this\n }\n\n /**\n * Get an adapter instance by name.\n *\n * Adapter instances are cached and reused. If no name is provided,\n * the default adapter is returned.\n *\n * @param adapter - Adapter name (optional, defaults to the default adapter)\n * @returns The adapter instance\n * @throws {E_QUEUE_NOT_INITIALIZED} If `init()` hasn't been called\n * @throws {E_CONFIGURATION_ERROR} If the adapter is not registered\n * @throws {E_ADAPTER_INIT_ERROR} If the adapter factory throws\n *\n * @example\n * ```typescript\n * // Get default adapter\n * const adapter = QueueManager.use()\n *\n * // Get specific adapter\n * const redisAdapter = QueueManager.use('redis')\n * ```\n */\n use(adapter?: string): Adapter {\n if (!this.#initialized) {\n throw new errors.E_QUEUE_NOT_INITIALIZED()\n }\n\n if (!adapter) {\n adapter = this.#defaultAdapter\n }\n\n // Return cached instance if exists\n const cached = this.#adapterInstances.get(adapter)\n if (cached) {\n return cached\n }\n\n const adapterFactory = this.#adapters[adapter]\n\n if (!adapterFactory) {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${adapter}\" is not registered`])\n }\n\n debug('using adapter \"%s\"', adapter)\n\n try {\n const instance = adapterFactory()\n this.#adapterInstances.set(adapter, instance)\n return instance\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new errors.E_ADAPTER_INIT_ERROR([adapter, message])\n }\n }\n\n /**\n * Get the merged retry configuration for a job.\n *\n * Configuration is merged with priority: job > queue > global.\n * This allows specific jobs or queues to override global defaults.\n *\n * @param queue - The queue name\n * @param jobRetryConfig - Optional job-level retry config\n * @returns The merged retry configuration\n *\n * @example\n * ```typescript\n * // Global: maxRetries=3, Queue: maxRetries=5, Job: maxRetries=1\n * // Result: maxRetries=1 (job wins)\n * const config = QueueManager.getMergedRetryConfig('emails', { maxRetries: 1 })\n * ```\n */\n getMergedRetryConfig(queue: string, jobRetryConfig?: RetryConfig): RetryConfig {\n const queueConfig = this.#queueConfigs.get(queue)\n const queueRetryConfig = queueConfig?.retry || {}\n\n let maxRetries =\n jobRetryConfig?.maxRetries ||\n queueRetryConfig.maxRetries ||\n this.#globalRetryConfig?.maxRetries ||\n 0\n\n let backoff =\n jobRetryConfig?.backoff || queueRetryConfig.backoff || this.#globalRetryConfig?.backoff\n\n return { maxRetries, backoff }\n }\n\n /**\n * Get the configured job factory for custom instantiation.\n *\n * @returns The job factory function, or undefined if not configured\n */\n getJobFactory(): JobFactory | undefined {\n return this.#jobFactory\n }\n\n #validateConfig(config: QueueManagerConfig): void {\n if (!config.adapters || Object.keys(config.adapters).length === 0) {\n throw new errors.E_CONFIGURATION_ERROR(['At least one adapter must be configured'])\n }\n\n if (!config.default) {\n throw new errors.E_CONFIGURATION_ERROR(['Default adapter must be specified'])\n }\n\n if (!config.adapters[config.default]) {\n throw new errors.E_CONFIGURATION_ERROR([\n `Default adapter \"${config.default}\" not found in adapters configuration`,\n ])\n }\n\n for (const [name, factory] of Object.entries(config.adapters)) {\n if (typeof factory !== 'function') {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${name}\" must be a factory function`])\n }\n }\n }\n\n /**\n * Clean up all adapter instances and reset state.\n *\n * Call this when shutting down the application or when\n * you need to reinitialize with a new configuration.\n *\n * @example\n * ```typescript\n * // On application shutdown\n * await QueueManager.destroy()\n * ```\n */\n async destroy() {\n for (const [name, adapter] of this.#adapterInstances) {\n debug('destroying adapter \"%s\"', name)\n await adapter.destroy()\n }\n this.#adapterInstances.clear()\n this.#initialized = false\n }\n}\n\n/** Global queue manager singleton */\nexport const QueueManager = new QueueManagerSingleton()\n"],"mappings":";;;;;;;;AAAA,SAAS,gBAAgB;AAEzB,IAAO,gBAAQ,SAAS,kBAAkB;;;ACE1C,SAAS,YAAY;AACrB,SAAS,eAAe;AA0BxB,IAAM,mBAAN,MAAuB;AAAA,EACrB,YAAY,oBAAI,IAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAatC,SAAwB,MAAc,UAAuB;AAC3D,kBAAM,uBAAuB,IAAI;AAEjC,SAAK,UAAU,IAAI,MAAM,QAAQ;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,iBAAiB,UAAqC;AAC1D,QAAI,aAAa;AAEjB,eAAW,WAAW,UAAU;AAC9B,oBAAM,0CAA0C,OAAO;AACvD,uBAAiB,QAAQ,KAAK,OAAO,GAAG;AACtC,sBAAM,sBAAsB,IAAI;AAEhC,YAAI;AACF,gBAAM,eAAe,QAAQ,IAAI;AACjC,gBAAM,SAAS,MAAM,OAAO,UAAU,YAAY;AAClD,gBAAM,WAAW,OAAO;AAExB,cAAI,YAAY,OAAO,aAAa,YAAY;AAC9C,kBAAM,UAAU,SAAS,SAAS,QAAQ,SAAS;AACnD,iBAAK,SAAS,SAAS,QAAQ;AAC/B;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,kBAAQ,KAAK,2BAA2B,IAAI,KAAK,KAAK;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,IAAyB,MAAuC;AAC9D,WAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,WAAgC,MAA2B;AACzD,UAAM,WAAW,KAAK,IAAO,IAAI;AAEjC,QAAI,CAAC,UAAU;AACb,YAAM,IAAW,gBAAgB,CAAC,IAAI,CAAC;AAAA,IACzC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAc;AACZ,SAAK,UAAU,MAAM;AAAA,EACvB;AACF;AAGO,IAAM,UAAU,IAAI,iBAAiB;;;ACtH5C,IAAM,gBAAN,MAAM,eAAgC;AAAA,EACpC;AAAA,EAEA,YAAY,SAAiB,SAAS;AACpC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAQ,OAAe,UAA8B,KAAoC;AACvF,UAAM,SAAS,IAAI,KAAK,OAAO,KAAK,KAAK;AAEzC,QAAI,OAAO,aAAa,UAAU;AAChC,aAAO,CAAC,GAAG,MAAM,IAAI,GAAG,IAAI,QAAQ;AAAA,IACtC;AAEA,WAAO,CAAC,GAAG,MAAM,IAAI,QAAQ,EAAE;AAAA,EACjC;AAAA,EAIA,MAAM,UAA8B,KAAoB;AACtD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,SAAS,UAAU,GAAG;AAE1D,QAAI,KAAK;AACP,aAAO,QAAQ,IAAI,SAAS,GAAG;AAAA,IACjC;AAEA,YAAQ,IAAI,OAAO;AAAA,EACrB;AAAA,EAIA,MAAM,UAA8B,KAAoB;AACtD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,SAAS,UAAU,GAAG;AAE1D,QAAI,KAAK;AACP,aAAO,QAAQ,IAAI,SAAS,GAAG;AAAA,IACjC;AAEA,YAAQ,IAAI,OAAO;AAAA,EACrB;AAAA,EAIA,KAAK,UAA8B,KAAoB;AACrD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,QAAQ,UAAU,GAAG;AAEzD,QAAI,KAAK;AACP,aAAO,QAAQ,IAAI,SAAS,GAAG;AAAA,IACjC;AAEA,YAAQ,IAAI,OAAO;AAAA,EACrB;AAAA,EAIA,KAAK,UAA8B,KAAoB;AACrD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,QAAQ,UAAU,GAAG;AAEzD,QAAI,KAAK;AACP,aAAO,QAAQ,KAAK,SAAS,GAAG;AAAA,IAClC;AAEA,YAAQ,KAAK,OAAO;AAAA,EACtB;AAAA,EAIA,MAAM,UAAgC,KAAoB;AACxD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,SAAS,UAAU,GAAG;AAE1D,QAAI,KAAK;AACP,aAAO,QAAQ,MAAM,SAAS,GAAG;AAAA,IACnC;AAEA,YAAQ,MAAM,OAAO;AAAA,EACvB;AAAA,EAEA,MAAM,KAAwB;AAC5B,UAAM,cAAc,IAAI,MAAM,OAAO,IAAI,GAAG,IAAI,KAAK;AACrD,WAAO,IAAI,eAAc,WAAW;AAAA,EACtC;AACF;AAEO,IAAM,gBAAgB,IAAI,cAAc;;;ACrE/C,IAAM,wBAAN,MAA4B;AAAA,EAC1B,eAAe;AAAA,EACf;AAAA,EACA,YAA4C,CAAC;AAAA,EAC7C,oBAA0C,oBAAI,IAAI;AAAA,EAClD;AAAA,EACA,gBAA0C,oBAAI,IAAI;AAAA,EAClD,UAAkB;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,MAAM,KAAK,QAA4B;AACrC,kBAAM,8CAA8C,MAAM;AAE1D,SAAK,gBAAgB,MAAM;AAE3B,SAAK,kBAAkB,MAAM;AAE7B,SAAK,kBAAkB,OAAO;AAC9B,SAAK,YAAY,OAAO;AACxB,SAAK,qBAAqB,OAAO;AACjC,SAAK,UAAU,OAAO,UAAU;AAChC,SAAK,cAAc,OAAO;AAE1B,QAAI,OAAO,QAAQ;AACjB,iBAAW,CAAC,OAAO,WAAW,KAAK,OAAO,QAAQ,OAAO,MAAM,GAAG;AAChE,aAAK,cAAc,IAAI,OAAO,WAA0B;AAAA,MAC1D;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,OAAO,UAAU,SAAS,GAAG;AACnD,YAAM,aAAa,MAAM,QAAQ,iBAAiB,OAAO,SAAS;AAElE,UAAI,eAAe,GAAG;AACpB,aAAK,QAAQ;AAAA,UACX,gCAAgC,OAAO,UAAU,KAAK,IAAI,CAAC;AAAA,QAE7D;AAAA,MACF;AAAA,IACF;AAEA,SAAK,eAAe;AAEpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,IAAI,SAA2B;AAC7B,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAW,wBAAwB;AAAA,IAC3C;AAEA,QAAI,CAAC,SAAS;AACZ,gBAAU,KAAK;AAAA,IACjB;AAGA,UAAM,SAAS,KAAK,kBAAkB,IAAI,OAAO;AACjD,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,UAAM,iBAAiB,KAAK,UAAU,OAAO;AAE7C,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAW,sBAAsB,CAAC,YAAY,OAAO,qBAAqB,CAAC;AAAA,IACnF;AAEA,kBAAM,sBAAsB,OAAO;AAEnC,QAAI;AACF,YAAM,WAAW,eAAe;AAChC,WAAK,kBAAkB,IAAI,SAAS,QAAQ;AAC5C,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAM,IAAW,qBAAqB,CAAC,SAAS,OAAO,CAAC;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,qBAAqB,OAAe,gBAA2C;AAC7E,UAAM,cAAc,KAAK,cAAc,IAAI,KAAK;AAChD,UAAM,mBAAmB,aAAa,SAAS,CAAC;AAEhD,QAAI,aACF,gBAAgB,cAChB,iBAAiB,cACjB,KAAK,oBAAoB,cACzB;AAEF,QAAI,UACF,gBAAgB,WAAW,iBAAiB,WAAW,KAAK,oBAAoB;AAElF,WAAO,EAAE,YAAY,QAAQ;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAwC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,gBAAgB,QAAkC;AAChD,QAAI,CAAC,OAAO,YAAY,OAAO,KAAK,OAAO,QAAQ,EAAE,WAAW,GAAG;AACjE,YAAM,IAAW,sBAAsB,CAAC,yCAAyC,CAAC;AAAA,IACpF;AAEA,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAW,sBAAsB,CAAC,mCAAmC,CAAC;AAAA,IAC9E;AAEA,QAAI,CAAC,OAAO,SAAS,OAAO,OAAO,GAAG;AACpC,YAAM,IAAW,sBAAsB;AAAA,QACrC,oBAAoB,OAAO,OAAO;AAAA,MACpC,CAAC;AAAA,IACH;AAEA,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AAC7D,UAAI,OAAO,YAAY,YAAY;AACjC,cAAM,IAAW,sBAAsB,CAAC,YAAY,IAAI,8BAA8B,CAAC;AAAA,MACzF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,UAAU;AACd,eAAW,CAAC,MAAM,OAAO,KAAK,KAAK,mBAAmB;AACpD,oBAAM,2BAA2B,IAAI;AACrC,YAAM,QAAQ,QAAQ;AAAA,IACxB;AACA,SAAK,kBAAkB,MAAM;AAC7B,SAAK,eAAe;AAAA,EACtB;AACF;AAGO,IAAM,eAAe,IAAI,sBAAsB;","names":[]}
@@ -10,9 +10,11 @@ __export(exceptions_exports, {
10
10
  E_ADAPTER_INIT_ERROR: () => E_ADAPTER_INIT_ERROR,
11
11
  E_CONFIGURATION_ERROR: () => E_CONFIGURATION_ERROR,
12
12
  E_INVALID_BASE_DELAY: () => E_INVALID_BASE_DELAY,
13
+ E_INVALID_CRON_EXPRESSION: () => E_INVALID_CRON_EXPRESSION,
13
14
  E_INVALID_DURATION_EXPRESSION: () => E_INVALID_DURATION_EXPRESSION,
14
15
  E_INVALID_MAX_DELAY: () => E_INVALID_MAX_DELAY,
15
16
  E_INVALID_MULTIPLIER: () => E_INVALID_MULTIPLIER,
17
+ E_INVALID_SCHEDULE_CONFIG: () => E_INVALID_SCHEDULE_CONFIG,
16
18
  E_JOB_MAX_ATTEMPTS_REACHED: () => E_JOB_MAX_ATTEMPTS_REACHED,
17
19
  E_JOB_NOT_FOUND: () => E_JOB_NOT_FOUND,
18
20
  E_JOB_TIMEOUT: () => E_JOB_TIMEOUT,
@@ -72,6 +74,16 @@ var E_NO_JOBS_FOUND = createError(
72
74
  "E_NO_JOBS_FOUND",
73
75
  500
74
76
  );
77
+ var E_INVALID_CRON_EXPRESSION = createError(
78
+ 'Invalid cron expression "%s": %s',
79
+ "E_INVALID_CRON_EXPRESSION",
80
+ 500
81
+ );
82
+ var E_INVALID_SCHEDULE_CONFIG = createError(
83
+ "Invalid schedule configuration: %s",
84
+ "E_INVALID_SCHEDULE_CONFIG",
85
+ 500
86
+ );
75
87
 
76
88
  // src/constants.ts
77
89
  var DEFAULT_PRIORITY = 5;
@@ -92,6 +104,8 @@ export {
92
104
  E_JOB_TIMEOUT,
93
105
  E_QUEUE_NOT_INITIALIZED,
94
106
  E_ADAPTER_INIT_ERROR,
107
+ E_INVALID_CRON_EXPRESSION,
108
+ E_INVALID_SCHEDULE_CONFIG,
95
109
  exceptions_exports,
96
110
  DEFAULT_PRIORITY,
97
111
  PRIORITY_SCORE_MULTIPLIER,
@@ -100,4 +114,4 @@ export {
100
114
  DEFAULT_STALLED_THRESHOLD,
101
115
  DEFAULT_ERROR_RETRY_DELAY
102
116
  };
103
- //# sourceMappingURL=chunk-HMGNQSSG.js.map
117
+ //# sourceMappingURL=chunk-SMOKFZ46.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/exceptions.ts","../src/constants.ts"],"sourcesContent":["import { createError } from '@poppinss/utils'\n\nexport const E_INVALID_DURATION_EXPRESSION = createError(\n 'Invalid duration expression: \"%s\"',\n 'E_INVALID_DURATION_EXPRESSION',\n 500\n)\n\nexport const E_INVALID_BASE_DELAY = createError<[reason: string]>(\n 'Invalid base delay. Reason: %s',\n 'E_INVALID_BASE_DELAY',\n 500\n)\n\nexport const E_INVALID_MAX_DELAY = createError<[reason: string]>(\n 'Invalid max delay. Reason: %s',\n 'E_INVALID_MAX_DELAY',\n 500\n)\n\nexport const E_INVALID_MULTIPLIER = createError<[reason: string]>(\n 'Invalid multiplier. Reason: %s',\n 'E_INVALID_MULTIPLIER',\n 500\n)\n\nexport const E_CONFIGURATION_ERROR = createError<[reason: string]>(\n 'Configuration error. Reason: %s',\n 'E_CONFIGURATION_ERROR',\n 500\n)\n\nexport const E_JOB_NOT_FOUND = createError<[jobName: string]>(\n 'Requested job \"%s\" is not registered',\n 'E_JOB_NOT_FOUND'\n)\n\nexport const E_JOB_MAX_ATTEMPTS_REACHED = createError<[jobName: string]>(\n 'The job \"%s\" has reached the maximum number of retry attempts',\n 'E_JOB_MAX_ATTEMPTS_REACHED'\n)\n\nexport const E_JOB_TIMEOUT = createError<[jobName: string, timeout: number]>(\n 'The job \"%s\" has exceeded the timeout of %dms',\n 'E_JOB_TIMEOUT'\n)\n\nexport const E_QUEUE_NOT_INITIALIZED = createError(\n 'QueueManager is not initialized. Call QueueManager.init() before using it.',\n 'E_QUEUE_NOT_INITIALIZED',\n 500\n)\n\nexport const E_ADAPTER_INIT_ERROR = createError<[adapterName: string, originalMessage: string]>(\n 'Failed to initialize adapter \"%s\". Reason: %s',\n 'E_ADAPTER_INIT_ERROR',\n 500\n)\n\nexport const E_NO_JOBS_FOUND = createError<[patterns: string]>(\n 'No jobs found for the specified locations: %s. Verify your glob patterns match your job files.',\n 'E_NO_JOBS_FOUND',\n 500\n)\n\nexport const E_INVALID_CRON_EXPRESSION = createError<[expression: string, reason: string]>(\n 'Invalid cron expression \"%s\": %s',\n 'E_INVALID_CRON_EXPRESSION',\n 500\n)\n\nexport const E_INVALID_SCHEDULE_CONFIG = createError<[reason: string]>(\n 'Invalid schedule configuration: %s',\n 'E_INVALID_SCHEDULE_CONFIG',\n 500\n)\n","/**\n * Default job priority (1-10 scale, lower = higher priority)\n */\nexport const DEFAULT_PRIORITY = 5\n\n/**\n * Multiplier used in score calculation: priority * multiplier + timestamp\n *\n * This ensures higher priority jobs are processed first,\n * while preserving FIFO order within the same priority.\n * The value (1e13) leaves room for ~300 years of millisecond timestamps.\n */\nexport const PRIORITY_SCORE_MULTIPLIER = 1e13\n\n/**\n * Default delay when the worker is idle (no jobs in queue)\n */\nexport const DEFAULT_IDLE_DELAY = '2s'\n\n/**\n * Default interval between stalled job checks\n */\nexport const DEFAULT_STALLED_INTERVAL = '30s'\n\n/**\n * Default threshold after which a job is considered stalled\n */\nexport const DEFAULT_STALLED_THRESHOLD = '30s'\n\n/**\n * Default delay before retrying after an error\n */\nexport const DEFAULT_ERROR_RETRY_DELAY = '5s'\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,mBAAmB;AAErB,IAAM,gCAAgC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AACF;AAEO,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AACF;AAEO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AACF;AAEO,IAAM,0BAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,4BAA4B;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,4BAA4B;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AACF;;;ACxEO,IAAM,mBAAmB;AASzB,IAAM,4BAA4B;AAKlC,IAAM,qBAAqB;AAK3B,IAAM,2BAA2B;AAKjC,IAAM,4BAA4B;AAKlC,IAAM,4BAA4B;","names":[]}