@boringnode/queue 0.5.0 → 0.5.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.
Files changed (37) hide show
  1. package/README.md +80 -6
  2. package/build/{chunk-WOUYSNK2.js → chunk-KI47AJ6U.js} +2 -2
  3. package/build/chunk-PZ5AY32C.js +10 -0
  4. package/build/chunk-PZ5AY32C.js.map +1 -0
  5. package/build/{chunk-ZZFSQY36.js → chunk-QEFYHCL7.js} +4 -6
  6. package/build/{chunk-ZZFSQY36.js.map → chunk-QEFYHCL7.js.map} +1 -1
  7. package/build/{chunk-OVYXMSSU.js → chunk-VRXHCWNK.js} +113 -31
  8. package/build/chunk-VRXHCWNK.js.map +1 -0
  9. package/build/chunk-WVLSICD4.js +20 -0
  10. package/build/chunk-WVLSICD4.js.map +1 -0
  11. package/build/index.d.ts +61 -10
  12. package/build/index.js +64 -51
  13. package/build/index.js.map +1 -1
  14. package/build/{index-B1XdqWpN.d.ts → job-Z5fBSzRX.d.ts} +160 -6
  15. package/build/src/contracts/adapter.d.ts +1 -1
  16. package/build/src/drivers/fake_adapter.d.ts +6 -1
  17. package/build/src/drivers/fake_adapter.js +4 -2
  18. package/build/src/drivers/knex_adapter.d.ts +1 -1
  19. package/build/src/drivers/knex_adapter.js +2 -1
  20. package/build/src/drivers/knex_adapter.js.map +1 -1
  21. package/build/src/drivers/redis_adapter.d.ts +1 -1
  22. package/build/src/drivers/redis_adapter.js +92 -93
  23. package/build/src/drivers/redis_adapter.js.map +1 -1
  24. package/build/src/drivers/sync_adapter.d.ts +1 -1
  25. package/build/src/drivers/sync_adapter.js +38 -18
  26. package/build/src/drivers/sync_adapter.js.map +1 -1
  27. package/build/src/otel.d.ts +63 -0
  28. package/build/src/otel.js +245 -0
  29. package/build/src/otel.js.map +1 -0
  30. package/build/src/types/index.d.ts +6 -1
  31. package/build/src/types/main.d.ts +1 -1
  32. package/build/src/types/tracing_channels.d.ts +34 -0
  33. package/build/src/types/tracing_channels.js +1 -0
  34. package/build/src/types/tracing_channels.js.map +1 -0
  35. package/package.json +36 -14
  36. package/build/chunk-OVYXMSSU.js.map +0 -1
  37. /package/build/{chunk-WOUYSNK2.js.map → chunk-KI47AJ6U.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/worker.ts","../src/job_pool.ts","../src/schedule.ts","../src/services/queue_schema.ts","../src/strategies/backoff_strategy.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { setTimeout } from 'node:timers/promises'\nimport debug from './debug.js'\nimport { parse } from './utils.js'\nimport { QueueManager } from './queue_manager.js'\nimport { JobPool } from './job_pool.js'\nimport { JobExecutionRuntime } from './job_runtime.js'\nimport type { Adapter, AcquiredJob } from './contracts/adapter.js'\nimport type { JobContext, JobOptions, QueueManagerConfig, WorkerCycle } from './types/main.js'\nimport { Locator } from './locator.js'\nimport { DEFAULT_PRIORITY } from './constants.js'\nimport type { Job } from './job.js'\nimport {\n DEFAULT_IDLE_DELAY,\n DEFAULT_STALLED_INTERVAL,\n DEFAULT_STALLED_THRESHOLD,\n DEFAULT_ERROR_RETRY_DELAY,\n} from './constants.js'\n\n/**\n * Job processing worker.\n *\n * The Worker continuously polls queues for jobs and executes them\n * with configurable concurrency. It handles:\n * - Concurrent job execution via JobPool\n * - Automatic retries with backoff strategies\n * - Stalled job detection and recovery\n * - Graceful shutdown on SIGINT/SIGTERM\n *\n * @example\n * ```typescript\n * import { Worker, redis } from '@boringnode/queue'\n *\n * const worker = new Worker({\n * default: 'redis',\n * adapters: { redis: redis() },\n * locations: ['./jobs/**\\/*.js'],\n * worker: {\n * concurrency: 5,\n * idleDelay: '1s',\n * },\n * })\n *\n * // Start processing jobs\n * await worker.start(['default', 'emails'])\n *\n * // Or for testing, process one cycle at a time\n * const cycle = await worker.processCycle(['default'])\n * ```\n */\nexport class Worker {\n readonly #id: string\n readonly #config: QueueManagerConfig\n readonly #idleDelay: number\n readonly #stalledInterval: number\n readonly #stalledThreshold: number\n readonly #maxStalledCount: number\n readonly #concurrency: number\n readonly #gracefulShutdown: boolean\n readonly #onShutdownSignal?: () => void | Promise<void>\n\n #adapter!: Adapter\n #running = false\n #initialized = false\n #generator?: AsyncGenerator<WorkerCycle, void, unknown>\n #pool?: JobPool\n #lastStalledCheck = 0\n #shutdownHandler?: () => Promise<void>\n\n /** Unique identifier for this worker instance */\n get id() {\n return this.#id\n }\n\n /**\n * Create a new worker instance.\n *\n * @param config - Queue configuration including adapter and worker settings\n */\n constructor(config: QueueManagerConfig) {\n this.#config = config\n this.#id = randomUUID()\n\n // Parse worker config once at construction\n this.#idleDelay = parse(config.worker?.idleDelay ?? DEFAULT_IDLE_DELAY)\n this.#stalledInterval = parse(config.worker?.stalledInterval ?? DEFAULT_STALLED_INTERVAL)\n this.#stalledThreshold = parse(config.worker?.stalledThreshold ?? DEFAULT_STALLED_THRESHOLD)\n this.#maxStalledCount = config.worker?.maxStalledCount ?? 1\n this.#concurrency = config.worker?.concurrency ?? 1\n this.#gracefulShutdown = config.worker?.gracefulShutdown ?? true\n this.#onShutdownSignal = config.worker?.onShutdownSignal\n\n debug('created worker with id %s and config %O', this.#id, config)\n }\n\n /**\n * Initialize the worker (called automatically by `start()`).\n *\n * Sets up the QueueManager and adapter connection.\n */\n async init() {\n if (this.#initialized) {\n return\n }\n\n debug('initializing worker %s', this.#id)\n\n await QueueManager.init(this.#config)\n\n this.#adapter = QueueManager.use()\n this.#adapter.setWorkerId(this.#id)\n\n this.#initialized = true\n\n debug('worker %s initialized', this.#id)\n }\n\n /**\n * Start processing jobs from the specified queues.\n *\n * This method blocks until the worker is stopped (via `stop()` or signal).\n * Jobs are processed concurrently up to the configured concurrency limit.\n *\n * @param queues - Queue names to process (default: ['default'])\n *\n * @example\n * ```typescript\n * // Process single queue\n * await worker.start()\n *\n * // Process multiple queues (priority order)\n * await worker.start(['high-priority', 'default', 'low-priority'])\n * ```\n */\n async start(queues: string[] = ['default']): Promise<void> {\n await this.init()\n\n if (this.#running) {\n debug('worker %s is already running', this.#id)\n return\n }\n\n this.#running = true\n\n debug('starting worker %s on queues: %O', this.#id, queues)\n\n this.#setupGracefulShutdown()\n\n for await (const cycle of this.process(queues)) {\n if (['started', 'completed'].includes(cycle.type)) {\n continue\n }\n\n if (['idle', 'error'].includes(cycle.type)) {\n // @ts-expect-error - we know suggestedDelay exists for these types\n const delay = parse(cycle.suggestedDelay)\n\n if (cycle.type === 'error') {\n debug('worker %s encountered an error: %O', this.#id, cycle.error)\n } else {\n debug('worker %s is idle, waiting for %dms', this.#id, delay)\n }\n\n await setTimeout(delay)\n }\n }\n }\n\n /**\n * Stop the worker gracefully.\n *\n * Waits for all running jobs to complete before stopping job consumption.\n * Adapter cleanup remains the responsibility of `QueueManager.destroy()`.\n * Called automatically on SIGINT/SIGTERM if gracefulShutdown is enabled.\n */\n async stop() {\n debug('stopping worker %s', this.#id)\n\n this.#running = false\n\n if (this.#pool) {\n debug('worker %s: waiting for %d running jobs to complete', this.#id, this.#pool.size)\n await this.#pool.drain()\n }\n\n this.#removeShutdownHandlers()\n }\n\n /**\n * Process a single cycle and return the result.\n *\n * Useful for testing or when you need fine-grained control.\n * Each cycle may start new jobs, complete a job, or return idle.\n *\n * @param queues - Queue names to process\n * @returns The cycle result, or null if the worker was stopped\n *\n * @example\n * ```typescript\n * const worker = new Worker(config)\n *\n * // Process cycles manually\n * let cycle = await worker.processCycle(['default'])\n * while (cycle) {\n * console.log('Cycle:', cycle.type)\n * cycle = await worker.processCycle(['default'])\n * }\n * ```\n */\n async processCycle(queues: string[]): Promise<WorkerCycle | null> {\n await this.init()\n\n this.#running = true\n\n if (!this.#generator) {\n this.#generator = this.process(queues)\n }\n\n const result = await this.#generator.next()\n\n if (result.done) {\n this.#generator = undefined\n return null\n }\n\n return result.value\n }\n\n /**\n * Generator that yields worker cycle events.\n *\n * Low-level API for processing jobs. Yields events for:\n * - `started`: A new job began execution\n * - `completed`: A job finished (success or failure)\n * - `idle`: No jobs available, suggest waiting\n * - `error`: An error occurred during processing\n *\n * @param queues - Queue names to process\n * @yields WorkerCycle events\n *\n * @example\n * ```typescript\n * for await (const cycle of worker.process(['default'])) {\n * switch (cycle.type) {\n * case 'started':\n * console.log(`Started job ${cycle.job.id}`)\n * break\n * case 'completed':\n * console.log(`Completed job ${cycle.job.id}`)\n * break\n * case 'idle':\n * await sleep(cycle.suggestedDelay)\n * break\n * }\n * }\n * ```\n */\n async *process(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n this.#pool = new JobPool()\n\n while (this.#running) {\n try {\n // Check for stalled jobs periodically\n await this.#checkStalledJobs(queues)\n\n // Dispatch any due scheduled jobs\n await this.#dispatchDueSchedules()\n\n yield* this.#fillPool(queues)\n\n if (this.#pool.isEmpty()) {\n yield { type: 'idle', suggestedDelay: this.#idleDelay }\n continue\n }\n\n const hasCapacity = this.#pool.hasCapacity(this.#concurrency)\n\n // If we have capacity, don't block indefinitely waiting for a completion;\n // wake up periodically to try to acquire newly enqueued jobs.\n const result = await Promise.race([\n this.#pool\n .waitForNextCompletion()\n .then((completed) => ({ kind: 'completed' as const, completed })),\n ...(hasCapacity\n ? [setTimeout(this.#idleDelay).then(() => ({ kind: 'tick' as const }))]\n : []),\n ])\n\n if (result.kind === 'tick') {\n // No completion yet, but we woke up to check the queue again\n continue\n }\n\n yield { type: 'completed', queue: result.completed.queue, job: result.completed.job }\n } catch (error) {\n yield {\n type: 'error',\n error: error as Error,\n suggestedDelay: parse(DEFAULT_ERROR_RETRY_DELAY),\n }\n }\n }\n }\n\n async *#fillPool(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n const slotsAvailable = this.#concurrency - this.#pool!.size\n\n if (slotsAvailable <= 0) return\n\n const popPromises = Array.from({ length: slotsAvailable }, () => this.#acquireNextJob(queues))\n\n const results = await Promise.all(popPromises)\n\n for (const result of results) {\n if (!result) continue\n\n const { job, queue } = result\n const promise = this.#execute(job, queue)\n this.#pool!.add(job, queue, promise)\n\n yield { type: 'started', queue, job }\n }\n }\n\n async #execute(job: AcquiredJob, queue: string): Promise<void> {\n const startTime = performance.now()\n\n debug('worker %s: executing job %s (%s)', this.#id, job.id, job.name)\n\n const { instance, options, context, payload } = await this.#initJob(job, queue)\n const configResolver = QueueManager.getConfigResolver()\n const retention = configResolver.resolveJobOptions(queue, options)\n const runtime = JobExecutionRuntime.from({\n jobName: job.name,\n options,\n retryConfig: configResolver.resolveRetryConfig(queue, options),\n defaultTimeout: configResolver.getWorkerTimeout(),\n })\n\n try {\n await runtime.execute(instance, payload, context)\n await this.#adapter.completeJob(job.id, queue, retention.removeOnComplete)\n\n const duration = (performance.now() - startTime).toFixed(2)\n debug('worker %s: successfully executed job %s in %dms', this.#id, job.id, duration)\n } catch (e) {\n const outcome = runtime.resolveFailure(e as Error, job.attempts)\n\n if (outcome.type === 'failed' && outcome.reason === 'timeout') {\n debug('worker %s: job %s timed out and failOnTimeout is set', this.#id, job.id)\n await this.#adapter.failJob(job.id, queue, outcome.storageError, retention.removeOnFail)\n await instance.failed?.(outcome.hookError)\n return\n }\n\n if (outcome.type === 'failed' && outcome.reason === 'no-retries') {\n debug('worker %s: job %s has no retries configured, marking as failed', this.#id, job.id)\n await this.#adapter.failJob(job.id, queue, outcome.storageError, retention.removeOnFail)\n await instance.failed?.(outcome.hookError)\n return\n }\n\n if (outcome.type === 'failed' && outcome.reason === 'max-attempts') {\n debug(\n 'worker %s: job %s has exceeded max retries (%d), marking as failed',\n this.#id,\n job.id,\n runtime.maxRetries\n )\n await this.#adapter.failJob(job.id, queue, outcome.storageError, retention.removeOnFail)\n await instance.failed?.(outcome.hookError)\n\n return\n }\n\n if (outcome.type === 'retry' && outcome.retryAt) {\n debug('worker %s: job %s will retry at %s', this.#id, job.id, outcome.retryAt.toISOString())\n await this.#adapter.retryJob(job.id, queue, outcome.retryAt)\n return\n }\n\n await this.#adapter.retryJob(job.id, queue)\n }\n }\n\n async #initJob(\n job: AcquiredJob,\n queue: string\n ): Promise<{\n instance: Job\n options: JobOptions\n context: JobContext\n payload: unknown\n }> {\n try {\n const JobClass = Locator.getOrThrow(job.name)\n\n const context: JobContext = {\n jobId: job.id,\n name: job.name,\n attempt: job.attempts + 1,\n queue,\n priority: job.priority ?? DEFAULT_PRIORITY,\n acquiredAt: new Date(job.acquiredAt),\n stalledCount: job.stalledCount ?? 0,\n }\n\n const jobFactory = QueueManager.getJobFactory()\n const instance = jobFactory ? await jobFactory(JobClass) : new JobClass()\n const options = JobClass.options || {}\n\n return { instance, options, context, payload: job.payload }\n } catch (error) {\n debug('worker %s: failed to initialize job %s (%s)', this.#id, job.id, job.name)\n const retention = QueueManager.getConfigResolver().resolveJobOptions(queue)\n await this.#adapter.failJob(job.id, queue, error as Error, retention.removeOnFail)\n throw error\n }\n }\n\n async #acquireNextJob(queues: string[]): Promise<{ job: AcquiredJob; queue: string } | null> {\n for (const queue of queues) {\n const job = await this.#adapter.popFrom(queue)\n\n if (!job) {\n continue\n }\n\n debug('worker %s: acquired job %s', this.#id, job.id)\n return { job, queue }\n }\n\n return null\n }\n\n async #checkStalledJobs(queues: string[]): Promise<void> {\n const now = Date.now()\n\n // Only check if enough time has passed since last check\n if (now - this.#lastStalledCheck < this.#stalledInterval) {\n return\n }\n\n this.#lastStalledCheck = now\n\n for (const queue of queues) {\n const recovered = await this.#adapter.recoverStalledJobs(\n queue,\n this.#stalledThreshold,\n this.#maxStalledCount\n )\n\n if (recovered > 0) {\n debug('worker %s: recovered %d stalled jobs from queue %s', this.#id, recovered, queue)\n }\n }\n }\n\n #setupGracefulShutdown() {\n if (!this.#gracefulShutdown) {\n return\n }\n\n this.#shutdownHandler = async () => {\n debug('received shutdown signal, stopping worker...')\n\n if (this.#onShutdownSignal) {\n await this.#onShutdownSignal()\n }\n\n await this.stop()\n }\n\n process.on('SIGINT', this.#shutdownHandler)\n process.on('SIGTERM', this.#shutdownHandler)\n }\n\n #removeShutdownHandlers() {\n if (this.#shutdownHandler) {\n process.off('SIGINT', this.#shutdownHandler)\n process.off('SIGTERM', this.#shutdownHandler)\n this.#shutdownHandler = undefined\n }\n }\n\n /**\n * Dispatch any due scheduled jobs.\n *\n * Claims due schedules from the adapter and dispatches the corresponding\n * jobs to their configured queues.\n */\n async #dispatchDueSchedules(): Promise<void> {\n // Keep claiming due schedules until there are none left\n while (true) {\n const schedule = await this.#adapter.claimDueSchedule()\n\n if (!schedule) {\n break\n }\n\n debug(\n 'worker %s: dispatching scheduled job %s (schedule: %s, runCount: %d)',\n this.#id,\n schedule.name,\n schedule.id,\n schedule.runCount + 1\n )\n\n // Get the job class to determine the target queue\n const JobClass = Locator.get(schedule.name)\n const queue = JobClass?.options?.queue ?? 'default'\n\n // Dispatch the job to the queue\n await this.#adapter.pushOn(queue, {\n id: randomUUID(),\n name: schedule.name,\n payload: schedule.payload,\n attempts: 0,\n priority: JobClass?.options?.priority,\n })\n }\n }\n}\n","import type { AcquiredJob } from './contracts/adapter.js'\n\n/**\n * Entry representing an active job in the pool.\n */\ninterface PoolEntry {\n /** Promise that resolves when the job completes */\n promise: Promise<void>\n /** The acquired job data */\n job: AcquiredJob\n /** The queue this job came from */\n queue: string\n}\n\n/**\n * Manages concurrent job execution with a fixed pool size.\n *\n * The pool tracks running jobs and returns the first one to complete,\n * allowing maximum throughput regardless of individual job duration:\n *\n * ```\n * Job A: ████████████████████░░░░░░░░░░ (slow - 10s)\n * Job B: ████ done (fast - 100ms) ← returns first\n * Job C: ████████████░░░░░░░░░░░░░░░░░░ (medium - 2s)\n * ↑\n * Slot freed, new job can start immediately\n * ```\n *\n * Key insight: slow jobs don't block the pool. As soon as any job\n * completes, its slot becomes available for new work.\n */\nexport class JobPool {\n #activeJobs = new Map<string, PoolEntry>()\n\n /** Number of currently running jobs */\n get size() {\n return this.#activeJobs.size\n }\n\n /**\n * Check if the pool has no running jobs.\n *\n * @returns True if no jobs are running\n */\n isEmpty() {\n return this.#activeJobs.size === 0\n }\n\n /**\n * Check if the pool can accept more jobs.\n *\n * @param concurrency - Maximum number of concurrent jobs\n * @returns True if there's room for more jobs\n */\n hasCapacity(concurrency: number) {\n return this.#activeJobs.size < concurrency\n }\n\n /**\n * Add a job to the pool.\n *\n * @param job - The acquired job data\n * @param queue - The queue the job came from\n * @param promise - Promise that resolves when the job completes\n */\n add(job: AcquiredJob, queue: string, promise: Promise<void>) {\n this.#activeJobs.set(job.id, { promise, job, queue })\n }\n\n /**\n * Wait for the next job to complete and return it.\n *\n * Uses `Promise.race()` internally, so the fastest job wins.\n * The completed job is removed from the pool.\n *\n * @returns The first job to complete (success or failure)\n */\n async waitForNextCompletion(): Promise<PoolEntry> {\n const completedJobId = await Promise.race(\n [...this.#activeJobs.entries()].map(async ([id, { promise }]) => {\n try {\n await promise\n } catch {\n // Errors are handled in Worker#execute\n }\n return id\n })\n )\n\n const completed = this.#activeJobs.get(completedJobId)!\n this.#activeJobs.delete(completedJobId)\n\n return completed\n }\n\n /**\n * Wait for all running jobs to complete.\n *\n * Used during graceful shutdown to ensure no jobs are abandoned.\n * Clears the pool after all jobs finish.\n */\n async drain(): Promise<void> {\n const promises = [...this.#activeJobs.values()].map(async ({ promise }) => {\n try {\n await promise\n } catch {\n // Errors are handled in Worker#execute\n }\n })\n\n await Promise.all(promises)\n this.#activeJobs.clear()\n }\n}\n","import { QueueManager } from './queue_manager.js'\nimport { JobDispatcher } from './job_dispatcher.js'\nimport type { ScheduleData, ScheduleListOptions, ScheduleStatus } from './types/main.js'\n\n/**\n * Represents a persisted job schedule.\n *\n * Use `Schedule.find()` or `Schedule.list()` to retrieve schedules,\n * then use instance methods to manage them.\n *\n * @example\n * ```typescript\n * const schedule = await Schedule.find('cleanup-daily')\n * if (schedule) {\n * await schedule.pause()\n * // Later...\n * await schedule.resume()\n * }\n *\n * // List all active schedules\n * const activeSchedules = await Schedule.list({ status: 'active' })\n * ```\n */\nexport class Schedule {\n readonly #data: ScheduleData\n\n constructor(data: ScheduleData) {\n this.#data = data\n }\n\n get id(): string {\n return this.#data.id\n }\n\n get name(): string {\n return this.#data.name\n }\n\n get payload(): unknown {\n return this.#data.payload\n }\n\n get cronExpression(): string | null {\n return this.#data.cronExpression\n }\n\n get everyMs(): number | null {\n return this.#data.everyMs\n }\n\n get timezone(): string {\n return this.#data.timezone\n }\n\n get from(): Date | null {\n return this.#data.from\n }\n\n get to(): Date | null {\n return this.#data.to\n }\n\n get limit(): number | null {\n return this.#data.limit\n }\n\n get runCount(): number {\n return this.#data.runCount\n }\n\n get nextRunAt(): Date | null {\n return this.#data.nextRunAt\n }\n\n get lastRunAt(): Date | null {\n return this.#data.lastRunAt\n }\n\n get status(): ScheduleStatus {\n return this.#data.status\n }\n\n get createdAt(): Date {\n return this.#data.createdAt\n }\n\n /**\n * Find a schedule by ID.\n *\n * @param id - The schedule ID\n * @returns The schedule instance, or null if not found\n */\n static async find(id: string): Promise<Schedule | null> {\n const adapter = QueueManager.use()\n const data = await adapter.getSchedule(id)\n\n if (!data) return null\n\n return new Schedule(data)\n }\n\n /**\n * List all schedules matching the given options.\n *\n * @param options - Optional filters for listing\n * @returns Array of schedule instances\n */\n static async list(options?: ScheduleListOptions): Promise<Schedule[]> {\n const adapter = QueueManager.use()\n const schedules = await adapter.listSchedules(options)\n\n return schedules.map((data) => new Schedule(data))\n }\n\n /**\n * Pause this schedule.\n * No jobs will be dispatched while paused.\n */\n async pause(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.updateSchedule(this.#data.id, { status: 'paused' })\n this.#data.status = 'paused'\n }\n\n /**\n * Resume this schedule.\n * Jobs will be dispatched according to the schedule.\n */\n async resume(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.updateSchedule(this.#data.id, { status: 'active' })\n this.#data.status = 'active'\n }\n\n /**\n * Delete this schedule permanently.\n */\n async delete(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.deleteSchedule(this.#data.id)\n }\n\n /**\n * Trigger immediate execution of this schedule's job.\n * Also updates runCount and lastRunAt.\n *\n * If the schedule has reached its limit, the job will not be dispatched.\n */\n async trigger(): Promise<void> {\n // Check if limit is reached\n if (this.#data.limit !== null && this.#data.runCount >= this.#data.limit) {\n return\n }\n\n const adapter = QueueManager.use()\n\n // Dispatch the job\n const dispatcher = new JobDispatcher(this.#data.name, this.#data.payload)\n await dispatcher.run()\n\n // Update run metadata\n const now = new Date()\n const newRunCount = this.#data.runCount + 1\n\n await adapter.updateSchedule(this.#data.id, {\n runCount: newRunCount,\n lastRunAt: now,\n })\n\n this.#data.runCount = newRunCount\n this.#data.lastRunAt = now\n }\n}\n","import type { Knex } from 'knex'\n\nexport class QueueSchemaService {\n #connection: Knex\n\n constructor(connection: Knex) {\n this.#connection = connection\n }\n\n /**\n * Creates the jobs table with the default schema.\n * The optional callback allows adding custom columns.\n */\n async createJobsTable(\n tableName: string = 'queue_jobs',\n extend?: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n await this.#connection.schema.createTable(tableName, (table) => {\n table.string('id', 255).notNullable()\n table.string('queue', 255).notNullable()\n table.enu('status', ['pending', 'active', 'delayed', 'completed', 'failed']).notNullable()\n table.text('data').notNullable()\n table.bigint('score').unsigned().nullable()\n table.string('worker_id', 255).nullable()\n table.bigint('acquired_at').unsigned().nullable()\n table.bigint('execute_at').unsigned().nullable()\n table.bigint('finished_at').unsigned().nullable()\n table.text('error').nullable()\n table.primary(['id', 'queue'])\n table.index(['queue', 'status', 'score'])\n table.index(['queue', 'status', 'execute_at'])\n table.index(['queue', 'status', 'finished_at'])\n\n extend?.(table)\n })\n }\n\n /**\n * Creates the schedules table with the default schema.\n * The optional callback allows adding custom columns.\n */\n async createSchedulesTable(\n tableName: string = 'queue_schedules',\n extend?: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n await this.#connection.schema.createTable(tableName, (table) => {\n table.string('id', 255).primary()\n table.string('status', 50).notNullable().defaultTo('active')\n table.string('name', 255).notNullable()\n table.text('payload').notNullable()\n table.string('cron_expression', 255).nullable()\n table.bigint('every_ms').unsigned().nullable()\n table.string('timezone', 100).notNullable().defaultTo('UTC')\n table.timestamp('from_date').nullable()\n table.timestamp('to_date').nullable()\n table.integer('run_limit').unsigned().nullable()\n table.integer('run_count').unsigned().notNullable().defaultTo(0)\n table.timestamp('next_run_at').nullable()\n table.timestamp('last_run_at').nullable()\n table\n .timestamp('created_at')\n .notNullable()\n .defaultTo(this.#connection.fn.now())\n table.index(['status', 'next_run_at'])\n\n extend?.(table)\n })\n }\n\n /**\n * Drops the jobs table if it exists.\n */\n async dropJobsTable(tableName: string = 'queue_jobs'): Promise<void> {\n await this.#connection.schema.dropTableIfExists(tableName)\n }\n\n /**\n * Drops the schedules table if it exists.\n */\n async dropSchedulesTable(tableName: string = 'queue_schedules'): Promise<void> {\n await this.#connection.schema.dropTableIfExists(tableName)\n }\n}\n","import type { BackoffConfig, Duration } from '../types/main.js'\nimport * as errors from '../exceptions.js'\nimport { parse } from '../utils.js'\nimport { RuntimeException } from '@poppinss/utils/exception'\nimport { assertUnreachable } from '@poppinss/utils/assert'\n\n/**\n * Calculates retry delays using configurable strategies.\n *\n * Supports three built-in strategies:\n * - `exponential`: Delay doubles each attempt (1s, 2s, 4s, 8s, ...)\n * - `linear`: Delay increases linearly (5s, 10s, 15s, 20s, ...)\n * - `fixed`: Same delay every time (10s, 10s, 10s, ...)\n *\n * All strategies support:\n * - `maxDelay`: Cap the maximum delay\n * - `jitter`: Add randomness to prevent thundering herd\n *\n * @example\n * ```typescript\n * const strategy = new BackoffStrategy({\n * strategy: 'exponential',\n * baseDelay: '1s',\n * maxDelay: '5m',\n * multiplier: 2,\n * jitter: true,\n * })\n *\n * strategy.calculateDelay(1) // ~1000ms\n * strategy.calculateDelay(2) // ~2000ms\n * strategy.calculateDelay(3) // ~4000ms\n * ```\n */\nexport class BackoffStrategy {\n readonly #config: BackoffConfig\n\n /**\n * Create a new backoff strategy.\n *\n * @param config - Backoff configuration\n * @throws {E_INVALID_BASE_DELAY} If baseDelay is not positive\n * @throws {E_INVALID_MAX_DELAY} If maxDelay is invalid\n * @throws {E_INVALID_MULTIPLIER} If multiplier is invalid\n */\n constructor(config: BackoffConfig) {\n this.#config = config\n this.#validateConfig()\n }\n\n /**\n * Calculate the delay for a given attempt number.\n *\n * @param attempt - The attempt number (1-based)\n * @returns Delay in milliseconds\n * @throws {RuntimeException} If attempt is less than 1\n *\n * @example\n * ```typescript\n * // Exponential: 1s, 2s, 4s, 8s, 16s, ...\n * strategy.calculateDelay(1) // 1000\n * strategy.calculateDelay(2) // 2000\n * strategy.calculateDelay(3) // 4000\n * ```\n */\n calculateDelay(attempt: number): number {\n if (attempt < 1) {\n throw new RuntimeException('Attempt number must be >= 1')\n }\n\n const baseDelayMs = parse(this.#config.baseDelay)\n const maxDelayMs = this.#config.maxDelay ? parse(this.#config.maxDelay) : Infinity\n const multiplier = this.#config.multiplier ?? 2\n\n let delay: number\n\n switch (this.#config.strategy) {\n case 'exponential':\n delay = baseDelayMs * Math.pow(multiplier, attempt - 1)\n break\n case 'linear':\n delay = baseDelayMs * attempt\n break\n case 'fixed':\n delay = baseDelayMs\n break\n default:\n assertUnreachable(this.#config.strategy)\n }\n\n // Apply max delay limit\n delay = Math.min(delay, maxDelayMs)\n\n if (this.#config.jitter) {\n delay = this.#applyJitter(delay)\n }\n\n return Math.floor(delay)\n }\n\n /**\n * Get the Date when the next retry should occur.\n *\n * @param attempt - The attempt number (1-based)\n * @returns Date for the next retry\n *\n * @example\n * ```typescript\n * const nextRetry = strategy.getNextRetryAt(3)\n * console.log(`Retry at: ${nextRetry.toISOString()}`)\n * ```\n */\n getNextRetryAt(attempt: number): Date {\n const delay = this.calculateDelay(attempt)\n return new Date(Date.now() + delay)\n }\n\n /**\n * Get a frozen copy of the configuration.\n *\n * @returns Readonly configuration object\n */\n getConfig(): Readonly<BackoffConfig> {\n return Object.freeze({ ...this.#config })\n }\n\n #validateConfig() {\n const baseDelayMs = parse(this.#config.baseDelay)\n\n if (baseDelayMs <= 0) {\n throw new errors.E_INVALID_BASE_DELAY([\n 'Base delay must be a positive integer greater than zero',\n ])\n }\n\n if (this.#config.maxDelay) {\n const maxDelayMs = parse(this.#config.maxDelay)\n\n if (maxDelayMs <= 0) {\n throw new errors.E_INVALID_MAX_DELAY([\n 'Max delay must be a positive integer greater than zero',\n ])\n }\n\n if (maxDelayMs <= baseDelayMs) {\n throw new errors.E_INVALID_MAX_DELAY(['Max delay should be greater than base delay'])\n }\n }\n\n if (this.#config.multiplier !== undefined) {\n if (this.#config.multiplier <= 0) {\n throw new errors.E_INVALID_MULTIPLIER([\n 'Multiplier must be a positive number greater than zero',\n ])\n }\n\n if (this.#config.strategy === 'exponential' && this.#config.multiplier < 1) {\n throw new errors.E_INVALID_MULTIPLIER(['Exponential strategy multiplier should be >= 1'])\n }\n }\n }\n\n #applyJitter(delay: number): number {\n const jitterRange = delay * 0.25\n const jitter = (Math.random() - 0.5) * 2 * jitterRange\n\n return Math.max(0, delay + jitter)\n }\n}\n\n/**\n * Create an exponential backoff strategy factory.\n *\n * Delay doubles with each attempt: 1s → 2s → 4s → 8s → ...\n *\n * Default config:\n * - baseDelay: 1s\n * - maxDelay: 5m\n * - multiplier: 2\n * - jitter: true\n *\n * @param config - Optional overrides for default config\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 5,\n * backoff: exponentialBackoff({ baseDelay: '500ms', maxDelay: '1m' }),\n * },\n * }\n * ```\n */\nexport function exponentialBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'exponential',\n baseDelay: '1s',\n maxDelay: '5m',\n multiplier: 2,\n jitter: true,\n ...config,\n })\n}\n\n/**\n * Create a linear backoff strategy factory.\n *\n * Delay increases linearly: 5s → 10s → 15s → 20s → ...\n *\n * Default config:\n * - baseDelay: 5s\n * - maxDelay: 2m\n *\n * @param config - Optional overrides for default config\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 3,\n * backoff: linearBackoff({ baseDelay: '10s' }),\n * },\n * }\n * ```\n */\nexport function linearBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'linear',\n baseDelay: '5s',\n maxDelay: '2m',\n ...config,\n })\n}\n\n/**\n * Create a fixed delay backoff strategy factory.\n *\n * Same delay every time: 10s → 10s → 10s → ...\n *\n * @param delay - The fixed delay (default: '10s')\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 3,\n * backoff: fixedBackoff('30s'),\n * },\n * }\n * ```\n */\nexport function fixedBackoff(delay: Duration = '10s') {\n return () =>\n new BackoffStrategy({\n strategy: 'fixed',\n baseDelay: delay,\n })\n}\n\n/**\n * Create a custom backoff strategy factory.\n *\n * Use this when you need full control over the configuration.\n *\n * @param config - Complete backoff configuration\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 5,\n * backoff: customBackoff({\n * strategy: 'exponential',\n * baseDelay: '100ms',\n * maxDelay: '30s',\n * multiplier: 3,\n * jitter: false,\n * }),\n * },\n * }\n * ```\n */\nexport function customBackoff(config: BackoffConfig) {\n return () => new BackoffStrategy(config)\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/worker.ts","../src/job_pool.ts","../src/schedule.ts","../src/services/queue_schema.ts","../src/strategies/backoff_strategy.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { setTimeout } from 'node:timers/promises'\nimport debug from './debug.js'\nimport { parse } from './utils.js'\nimport { QueueManager } from './queue_manager.js'\nimport { JobPool } from './job_pool.js'\nimport { JobExecutionRuntime } from './job_runtime.js'\nimport { dispatchChannel, executeChannel } from './tracing_channels.js'\nimport type { Adapter, AcquiredJob } from './contracts/adapter.js'\nimport type { JobContext, JobOptions, JobRetention, QueueManagerConfig, WorkerCycle } from './types/main.js'\nimport type { JobDispatchMessage, JobExecuteMessage } from './types/tracing_channels.js'\nimport { Locator } from './locator.js'\nimport { DEFAULT_PRIORITY } from './constants.js'\nimport type { Job } from './job.js'\nimport {\n DEFAULT_IDLE_DELAY,\n DEFAULT_STALLED_INTERVAL,\n DEFAULT_STALLED_THRESHOLD,\n DEFAULT_ERROR_RETRY_DELAY,\n} from './constants.js'\n\n/**\n * Job processing worker.\n *\n * The Worker continuously polls queues for jobs and executes them\n * with configurable concurrency. It handles:\n * - Concurrent job execution via JobPool\n * - Automatic retries with backoff strategies\n * - Stalled job detection and recovery\n * - Graceful shutdown on SIGINT/SIGTERM\n *\n * @example\n * ```typescript\n * import { Worker, redis } from '@boringnode/queue'\n *\n * const worker = new Worker({\n * default: 'redis',\n * adapters: { redis: redis() },\n * locations: ['./jobs/**\\/*.js'],\n * worker: {\n * concurrency: 5,\n * idleDelay: '1s',\n * },\n * })\n *\n * // Start processing jobs\n * await worker.start(['default', 'emails'])\n *\n * // Or for testing, process one cycle at a time\n * const cycle = await worker.processCycle(['default'])\n * ```\n */\nexport class Worker {\n readonly #id: string\n readonly #config: QueueManagerConfig\n readonly #idleDelay: number\n readonly #stalledInterval: number\n readonly #stalledThreshold: number\n readonly #maxStalledCount: number\n readonly #concurrency: number\n readonly #gracefulShutdown: boolean\n readonly #onShutdownSignal?: () => void | Promise<void>\n\n #adapter!: Adapter\n #wrapInternal: <T>(fn: () => Promise<T>) => Promise<T> = (fn) => fn()\n #running = false\n #initialized = false\n #generator?: AsyncGenerator<WorkerCycle, void, unknown>\n #pool?: JobPool\n #lastStalledCheck = 0\n #shutdownHandler?: () => Promise<void>\n\n /** Unique identifier for this worker instance */\n get id() {\n return this.#id\n }\n\n /**\n * Create a new worker instance.\n *\n * @param config - Queue configuration including adapter and worker settings\n */\n constructor(config: QueueManagerConfig) {\n this.#config = config\n this.#id = randomUUID()\n\n // Parse worker config once at construction\n this.#idleDelay = parse(config.worker?.idleDelay ?? DEFAULT_IDLE_DELAY)\n this.#stalledInterval = parse(config.worker?.stalledInterval ?? DEFAULT_STALLED_INTERVAL)\n this.#stalledThreshold = parse(config.worker?.stalledThreshold ?? DEFAULT_STALLED_THRESHOLD)\n this.#maxStalledCount = config.worker?.maxStalledCount ?? 1\n this.#concurrency = config.worker?.concurrency ?? 1\n this.#gracefulShutdown = config.worker?.gracefulShutdown ?? true\n this.#onShutdownSignal = config.worker?.onShutdownSignal\n\n debug('created worker with id %s and config %O', this.#id, config)\n }\n\n /**\n * Initialize the worker (called automatically by `start()`).\n *\n * Sets up the QueueManager and adapter connection.\n */\n async init() {\n if (this.#initialized) {\n return\n }\n\n debug('initializing worker %s', this.#id)\n\n await QueueManager.init(this.#config)\n\n this.#adapter = QueueManager.use()\n this.#adapter.setWorkerId(this.#id)\n this.#wrapInternal = QueueManager.getInternalOperationWrapper()\n\n this.#initialized = true\n\n debug('worker %s initialized', this.#id)\n }\n\n /**\n * Start processing jobs from the specified queues.\n *\n * This method blocks until the worker is stopped (via `stop()` or signal).\n * Jobs are processed concurrently up to the configured concurrency limit.\n *\n * @param queues - Queue names to process (default: ['default'])\n *\n * @example\n * ```typescript\n * // Process single queue\n * await worker.start()\n *\n * // Process multiple queues (priority order)\n * await worker.start(['high-priority', 'default', 'low-priority'])\n * ```\n */\n async start(queues: string[] = ['default']): Promise<void> {\n await this.init()\n\n if (this.#running) {\n debug('worker %s is already running', this.#id)\n return\n }\n\n this.#running = true\n\n debug('starting worker %s on queues: %O', this.#id, queues)\n\n this.#setupGracefulShutdown()\n\n for await (const cycle of this.process(queues)) {\n if (['started', 'completed'].includes(cycle.type)) {\n continue\n }\n\n if (['idle', 'error'].includes(cycle.type)) {\n // @ts-expect-error - we know suggestedDelay exists for these types\n const delay = parse(cycle.suggestedDelay)\n\n if (cycle.type === 'error') {\n debug('worker %s encountered an error: %O', this.#id, cycle.error)\n } else {\n debug('worker %s is idle, waiting for %dms', this.#id, delay)\n }\n\n await setTimeout(delay)\n }\n }\n }\n\n /**\n * Stop the worker gracefully.\n *\n * Waits for all running jobs to complete before stopping job consumption.\n * Adapter cleanup remains the responsibility of `QueueManager.destroy()`.\n * Called automatically on SIGINT/SIGTERM if gracefulShutdown is enabled.\n */\n async stop() {\n debug('stopping worker %s', this.#id)\n\n this.#running = false\n\n if (this.#pool) {\n debug('worker %s: waiting for %d running jobs to complete', this.#id, this.#pool.size)\n await this.#pool.drain()\n }\n\n this.#removeShutdownHandlers()\n }\n\n /**\n * Process a single cycle and return the result.\n *\n * Useful for testing or when you need fine-grained control.\n * Each cycle may start new jobs, complete a job, or return idle.\n *\n * @param queues - Queue names to process\n * @returns The cycle result, or null if the worker was stopped\n *\n * @example\n * ```typescript\n * const worker = new Worker(config)\n *\n * // Process cycles manually\n * let cycle = await worker.processCycle(['default'])\n * while (cycle) {\n * console.log('Cycle:', cycle.type)\n * cycle = await worker.processCycle(['default'])\n * }\n * ```\n */\n async processCycle(queues: string[]): Promise<WorkerCycle | null> {\n await this.init()\n\n this.#running = true\n\n if (!this.#generator) {\n this.#generator = this.process(queues)\n }\n\n const result = await this.#generator.next()\n\n if (result.done) {\n this.#generator = undefined\n return null\n }\n\n return result.value\n }\n\n /**\n * Generator that yields worker cycle events.\n *\n * Low-level API for processing jobs. Yields events for:\n * - `started`: A new job began execution\n * - `completed`: A job finished (success or failure)\n * - `idle`: No jobs available, suggest waiting\n * - `error`: An error occurred during processing\n *\n * @param queues - Queue names to process\n * @yields WorkerCycle events\n *\n * @example\n * ```typescript\n * for await (const cycle of worker.process(['default'])) {\n * switch (cycle.type) {\n * case 'started':\n * console.log(`Started job ${cycle.job.id}`)\n * break\n * case 'completed':\n * console.log(`Completed job ${cycle.job.id}`)\n * break\n * case 'idle':\n * await sleep(cycle.suggestedDelay)\n * break\n * }\n * }\n * ```\n */\n async *process(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n this.#pool = new JobPool()\n\n while (this.#running) {\n try {\n // Check for stalled jobs periodically\n await this.#checkStalledJobs(queues)\n\n // Dispatch any due scheduled jobs\n await this.#dispatchDueSchedules()\n\n yield* this.#fillPool(queues)\n\n if (this.#pool.isEmpty()) {\n yield { type: 'idle', suggestedDelay: this.#idleDelay }\n continue\n }\n\n const hasCapacity = this.#pool.hasCapacity(this.#concurrency)\n\n // If we have capacity, don't block indefinitely waiting for a completion;\n // wake up periodically to try to acquire newly enqueued jobs.\n const result = await Promise.race([\n this.#pool\n .waitForNextCompletion()\n .then((completed) => ({ kind: 'completed' as const, completed })),\n ...(hasCapacity\n ? [setTimeout(this.#idleDelay).then(() => ({ kind: 'tick' as const }))]\n : []),\n ])\n\n if (result.kind === 'tick') {\n // No completion yet, but we woke up to check the queue again\n continue\n }\n\n yield { type: 'completed', queue: result.completed.queue, job: result.completed.job }\n } catch (error) {\n yield {\n type: 'error',\n error: error as Error,\n suggestedDelay: parse(DEFAULT_ERROR_RETRY_DELAY),\n }\n }\n }\n }\n\n async *#fillPool(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n const slotsAvailable = this.#concurrency - this.#pool!.size\n\n if (slotsAvailable <= 0) return\n\n const popPromises = Array.from({ length: slotsAvailable }, () => this.#acquireNextJob(queues))\n\n const results = await Promise.all(popPromises)\n\n for (const result of results) {\n if (!result) continue\n\n const { job, queue } = result\n const promise = this.#execute(job, queue)\n this.#pool!.add(job, queue, promise)\n\n yield { type: 'started', queue, job }\n }\n }\n\n async #execute(job: AcquiredJob, queue: string): Promise<void> {\n const startTime = performance.now()\n\n debug('worker %s: executing job %s (%s)', this.#id, job.id, job.name)\n\n const { instance, options, context, payload } = await this.#initJob(job, queue)\n const configResolver = QueueManager.getConfigResolver()\n const retention = configResolver.resolveJobOptions(queue, options)\n const runtime = JobExecutionRuntime.from({\n jobName: job.name,\n options,\n retryConfig: configResolver.resolveRetryConfig(queue, options),\n defaultTimeout: configResolver.getWorkerTimeout(),\n })\n\n const executeMessage: JobExecuteMessage = { job, queue }\n\n const run = () => {\n return executeChannel.tracePromise(async () => {\n try {\n await runtime.execute(instance, payload, context)\n await this.#wrapInternal(() => this.#adapter.completeJob(job.id, queue, retention.removeOnComplete))\n executeMessage.status = 'completed'\n debug('worker %s: successfully executed job %s in %dms', this.#id, job.id, (performance.now() - startTime).toFixed(2))\n } catch (e) {\n await this.#handleExecutionFailure({ error: e as Error, job, queue, instance, runtime, retention, executeMessage })\n }\n\n executeMessage.duration = Number((performance.now() - startTime).toFixed(2))\n }, executeMessage)\n }\n\n const executionWrapper = QueueManager.getExecutionWrapper()\n await executionWrapper(run, job, queue)\n }\n\n async #handleExecutionFailure(options: {\n error: Error\n job: AcquiredJob\n queue: string\n instance: Job\n runtime: JobExecutionRuntime\n retention: { removeOnComplete?: JobRetention; removeOnFail?: JobRetention }\n executeMessage: JobExecuteMessage\n }) {\n const outcome = options.runtime.resolveFailure(options.error, options.job.attempts)\n options.executeMessage.error = options.error\n\n if (outcome.type === 'failed') {\n options.executeMessage.status = 'failed'\n await this.#wrapInternal(() =>\n this.#adapter.failJob(options.job.id, options.queue, outcome.storageError, options.retention.removeOnFail)\n )\n await options.instance.failed?.(outcome.hookError)\n return\n }\n\n if (outcome.type !== 'retry') return\n\n options.executeMessage.status = 'retrying'\n options.executeMessage.nextRetryAt = outcome.retryAt\n\n if (outcome.retryAt) {\n debug('worker %s: job %s will retry at %s', this.#id, options.job.id, outcome.retryAt.toISOString())\n await this.#wrapInternal(() => this.#adapter.retryJob(options.job.id, options.queue, outcome.retryAt))\n } else {\n await this.#wrapInternal(() => this.#adapter.retryJob(options.job.id, options.queue))\n }\n }\n\n async #initJob(\n job: AcquiredJob,\n queue: string\n ): Promise<{\n instance: Job\n options: JobOptions\n context: JobContext\n payload: unknown\n }> {\n try {\n const JobClass = Locator.getOrThrow(job.name)\n\n const context: JobContext = {\n jobId: job.id,\n name: job.name,\n attempt: job.attempts + 1,\n queue,\n priority: job.priority ?? DEFAULT_PRIORITY,\n acquiredAt: new Date(job.acquiredAt),\n stalledCount: job.stalledCount ?? 0,\n }\n\n const jobFactory = QueueManager.getJobFactory()\n const instance = jobFactory ? await jobFactory(JobClass) : new JobClass()\n const options = JobClass.options || {}\n\n return { instance, options, context, payload: job.payload }\n } catch (error) {\n debug('worker %s: failed to initialize job %s (%s)', this.#id, job.id, job.name)\n const retention = QueueManager.getConfigResolver().resolveJobOptions(queue)\n await this.#wrapInternal(() => this.#adapter.failJob(job.id, queue, error as Error, retention.removeOnFail))\n throw error\n }\n }\n\n async #acquireNextJob(queues: string[]): Promise<{ job: AcquiredJob; queue: string } | null> {\n for (const queue of queues) {\n const job = await this.#wrapInternal(() => this.#adapter.popFrom(queue))\n\n if (!job) {\n continue\n }\n\n debug('worker %s: acquired job %s', this.#id, job.id)\n return { job, queue }\n }\n\n return null\n }\n\n async #checkStalledJobs(queues: string[]): Promise<void> {\n const now = Date.now()\n\n // Only check if enough time has passed since last check\n if (now - this.#lastStalledCheck < this.#stalledInterval) {\n return\n }\n\n this.#lastStalledCheck = now\n\n for (const queue of queues) {\n const recovered = await this.#wrapInternal(() =>\n this.#adapter.recoverStalledJobs(queue, this.#stalledThreshold, this.#maxStalledCount)\n )\n\n if (recovered > 0) {\n debug('worker %s: recovered %d stalled jobs from queue %s', this.#id, recovered, queue)\n }\n }\n }\n\n #setupGracefulShutdown() {\n if (!this.#gracefulShutdown) {\n return\n }\n\n this.#shutdownHandler = async () => {\n debug('received shutdown signal, stopping worker...')\n\n if (this.#onShutdownSignal) {\n await this.#onShutdownSignal()\n }\n\n await this.stop()\n }\n\n process.on('SIGINT', this.#shutdownHandler)\n process.on('SIGTERM', this.#shutdownHandler)\n }\n\n #removeShutdownHandlers() {\n if (this.#shutdownHandler) {\n process.off('SIGINT', this.#shutdownHandler)\n process.off('SIGTERM', this.#shutdownHandler)\n this.#shutdownHandler = undefined\n }\n }\n\n /**\n * Dispatch any due scheduled jobs.\n *\n * Claims due schedules from the adapter and dispatches the corresponding\n * jobs to their configured queues.\n */\n async #dispatchDueSchedules(): Promise<void> {\n // Keep claiming due schedules until there are none left\n while (true) {\n const schedule = await this.#wrapInternal(() => this.#adapter.claimDueSchedule())\n\n if (!schedule) {\n break\n }\n\n debug(\n 'worker %s: dispatching scheduled job %s (schedule: %s, runCount: %d)',\n this.#id,\n schedule.name,\n schedule.id,\n schedule.runCount + 1\n )\n\n // Get the job class to determine the target queue\n const JobClass = Locator.get(schedule.name)\n const queue = JobClass?.options?.queue ?? 'default'\n\n const jobData = {\n id: randomUUID(),\n name: schedule.name,\n payload: schedule.payload,\n attempts: 0,\n priority: JobClass?.options?.priority,\n }\n\n const message: JobDispatchMessage = { jobs: [jobData], queue }\n await dispatchChannel.tracePromise(async () => {\n await this.#wrapInternal(() => this.#adapter.pushOn(queue, jobData))\n }, message)\n }\n }\n}\n","import type { AcquiredJob } from './contracts/adapter.js'\n\n/**\n * Entry representing an active job in the pool.\n */\ninterface PoolEntry {\n /** Promise that resolves when the job completes */\n promise: Promise<void>\n /** The acquired job data */\n job: AcquiredJob\n /** The queue this job came from */\n queue: string\n}\n\n/**\n * Manages concurrent job execution with a fixed pool size.\n *\n * The pool tracks running jobs and returns the first one to complete,\n * allowing maximum throughput regardless of individual job duration:\n *\n * ```\n * Job A: ████████████████████░░░░░░░░░░ (slow - 10s)\n * Job B: ████ done (fast - 100ms) ← returns first\n * Job C: ████████████░░░░░░░░░░░░░░░░░░ (medium - 2s)\n * ↑\n * Slot freed, new job can start immediately\n * ```\n *\n * Key insight: slow jobs don't block the pool. As soon as any job\n * completes, its slot becomes available for new work.\n */\nexport class JobPool {\n #activeJobs = new Map<string, PoolEntry>()\n\n /** Number of currently running jobs */\n get size() {\n return this.#activeJobs.size\n }\n\n /**\n * Check if the pool has no running jobs.\n *\n * @returns True if no jobs are running\n */\n isEmpty() {\n return this.#activeJobs.size === 0\n }\n\n /**\n * Check if the pool can accept more jobs.\n *\n * @param concurrency - Maximum number of concurrent jobs\n * @returns True if there's room for more jobs\n */\n hasCapacity(concurrency: number) {\n return this.#activeJobs.size < concurrency\n }\n\n /**\n * Add a job to the pool.\n *\n * @param job - The acquired job data\n * @param queue - The queue the job came from\n * @param promise - Promise that resolves when the job completes\n */\n add(job: AcquiredJob, queue: string, promise: Promise<void>) {\n this.#activeJobs.set(job.id, { promise, job, queue })\n }\n\n /**\n * Wait for the next job to complete and return it.\n *\n * Uses `Promise.race()` internally, so the fastest job wins.\n * The completed job is removed from the pool.\n *\n * @returns The first job to complete (success or failure)\n */\n async waitForNextCompletion(): Promise<PoolEntry> {\n const completedJobId = await Promise.race(\n [...this.#activeJobs.entries()].map(async ([id, { promise }]) => {\n try {\n await promise\n } catch {\n // Errors are handled in Worker#execute\n }\n return id\n })\n )\n\n const completed = this.#activeJobs.get(completedJobId)!\n this.#activeJobs.delete(completedJobId)\n\n return completed\n }\n\n /**\n * Wait for all running jobs to complete.\n *\n * Used during graceful shutdown to ensure no jobs are abandoned.\n * Clears the pool after all jobs finish.\n */\n async drain(): Promise<void> {\n const promises = [...this.#activeJobs.values()].map(async ({ promise }) => {\n try {\n await promise\n } catch {\n // Errors are handled in Worker#execute\n }\n })\n\n await Promise.all(promises)\n this.#activeJobs.clear()\n }\n}\n","import { QueueManager } from './queue_manager.js'\nimport { JobDispatcher } from './job_dispatcher.js'\nimport type { ScheduleData, ScheduleListOptions, ScheduleStatus } from './types/main.js'\n\n/**\n * Represents a persisted job schedule.\n *\n * Use `Schedule.find()` or `Schedule.list()` to retrieve schedules,\n * then use instance methods to manage them.\n *\n * @example\n * ```typescript\n * const schedule = await Schedule.find('cleanup-daily')\n * if (schedule) {\n * await schedule.pause()\n * // Later...\n * await schedule.resume()\n * }\n *\n * // List all active schedules\n * const activeSchedules = await Schedule.list({ status: 'active' })\n * ```\n */\nexport class Schedule {\n readonly #data: ScheduleData\n\n constructor(data: ScheduleData) {\n this.#data = data\n }\n\n get id(): string {\n return this.#data.id\n }\n\n get name(): string {\n return this.#data.name\n }\n\n get payload(): unknown {\n return this.#data.payload\n }\n\n get cronExpression(): string | null {\n return this.#data.cronExpression\n }\n\n get everyMs(): number | null {\n return this.#data.everyMs\n }\n\n get timezone(): string {\n return this.#data.timezone\n }\n\n get from(): Date | null {\n return this.#data.from\n }\n\n get to(): Date | null {\n return this.#data.to\n }\n\n get limit(): number | null {\n return this.#data.limit\n }\n\n get runCount(): number {\n return this.#data.runCount\n }\n\n get nextRunAt(): Date | null {\n return this.#data.nextRunAt\n }\n\n get lastRunAt(): Date | null {\n return this.#data.lastRunAt\n }\n\n get status(): ScheduleStatus {\n return this.#data.status\n }\n\n get createdAt(): Date {\n return this.#data.createdAt\n }\n\n /**\n * Find a schedule by ID.\n *\n * @param id - The schedule ID\n * @returns The schedule instance, or null if not found\n */\n static async find(id: string): Promise<Schedule | null> {\n const adapter = QueueManager.use()\n const data = await adapter.getSchedule(id)\n\n if (!data) return null\n\n return new Schedule(data)\n }\n\n /**\n * List all schedules matching the given options.\n *\n * @param options - Optional filters for listing\n * @returns Array of schedule instances\n */\n static async list(options?: ScheduleListOptions): Promise<Schedule[]> {\n const adapter = QueueManager.use()\n const schedules = await adapter.listSchedules(options)\n\n return schedules.map((data) => new Schedule(data))\n }\n\n /**\n * Pause this schedule.\n * No jobs will be dispatched while paused.\n */\n async pause(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.updateSchedule(this.#data.id, { status: 'paused' })\n this.#data.status = 'paused'\n }\n\n /**\n * Resume this schedule.\n * Jobs will be dispatched according to the schedule.\n */\n async resume(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.updateSchedule(this.#data.id, { status: 'active' })\n this.#data.status = 'active'\n }\n\n /**\n * Delete this schedule permanently.\n */\n async delete(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.deleteSchedule(this.#data.id)\n }\n\n /**\n * Trigger immediate execution of this schedule's job.\n * Also updates runCount and lastRunAt.\n *\n * If the schedule has reached its limit, the job will not be dispatched.\n *\n * @param payload - Optional custom payload for the job\n */\n async trigger(payload?: any): Promise<void> {\n // Check if limit is reached\n if (this.#data.limit !== null && this.#data.runCount >= this.#data.limit) {\n return\n }\n\n const adapter = QueueManager.use()\n\n // Dispatch the job\n const dispatcher = new JobDispatcher(this.#data.name, payload ?? this.#data.payload)\n await dispatcher.run()\n\n // Update run metadata\n const now = new Date()\n const newRunCount = this.#data.runCount + 1\n\n await adapter.updateSchedule(this.#data.id, {\n runCount: newRunCount,\n lastRunAt: now,\n })\n\n this.#data.runCount = newRunCount\n this.#data.lastRunAt = now\n }\n}\n","import type { Knex } from 'knex'\n\nexport class QueueSchemaService {\n #connection: Knex\n\n constructor(connection: Knex) {\n this.#connection = connection\n }\n\n /**\n * Creates the jobs table with the default schema.\n * The optional callback allows adding custom columns.\n */\n async createJobsTable(\n tableName: string = 'queue_jobs',\n extend?: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n await this.#connection.schema.createTable(tableName, (table) => {\n table.string('id', 255).notNullable()\n table.string('queue', 255).notNullable()\n table.enu('status', ['pending', 'active', 'delayed', 'completed', 'failed']).notNullable()\n table.text('data').notNullable()\n table.bigint('score').unsigned().nullable()\n table.string('worker_id', 255).nullable()\n table.bigint('acquired_at').unsigned().nullable()\n table.bigint('execute_at').unsigned().nullable()\n table.bigint('finished_at').unsigned().nullable()\n table.text('error').nullable()\n table.primary(['id', 'queue'])\n table.index(['queue', 'status', 'score'])\n table.index(['queue', 'status', 'execute_at'])\n table.index(['queue', 'status', 'finished_at'])\n\n extend?.(table)\n })\n }\n\n /**\n * Creates the schedules table with the default schema.\n * The optional callback allows adding custom columns.\n */\n async createSchedulesTable(\n tableName: string = 'queue_schedules',\n extend?: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n await this.#connection.schema.createTable(tableName, (table) => {\n table.string('id', 255).primary()\n table.string('status', 50).notNullable().defaultTo('active')\n table.string('name', 255).notNullable()\n table.text('payload').notNullable()\n table.string('cron_expression', 255).nullable()\n table.bigint('every_ms').unsigned().nullable()\n table.string('timezone', 100).notNullable().defaultTo('UTC')\n table.timestamp('from_date').nullable()\n table.timestamp('to_date').nullable()\n table.integer('run_limit').unsigned().nullable()\n table.integer('run_count').unsigned().notNullable().defaultTo(0)\n table.timestamp('next_run_at').nullable()\n table.timestamp('last_run_at').nullable()\n table\n .timestamp('created_at')\n .notNullable()\n .defaultTo(this.#connection.fn.now())\n table.index(['status', 'next_run_at'])\n\n extend?.(table)\n })\n }\n\n /**\n * Drops the jobs table if it exists.\n */\n async dropJobsTable(tableName: string = 'queue_jobs'): Promise<void> {\n await this.#connection.schema.dropTableIfExists(tableName)\n }\n\n /**\n * Drops the schedules table if it exists.\n */\n async dropSchedulesTable(tableName: string = 'queue_schedules'): Promise<void> {\n await this.#connection.schema.dropTableIfExists(tableName)\n }\n}\n","import type { BackoffConfig, Duration } from '../types/main.js'\nimport * as errors from '../exceptions.js'\nimport { parse } from '../utils.js'\nimport { RuntimeException } from '@poppinss/utils/exception'\nimport { assertUnreachable } from '@poppinss/utils/assert'\n\n/**\n * Calculates retry delays using configurable strategies.\n *\n * Supports three built-in strategies:\n * - `exponential`: Delay doubles each attempt (1s, 2s, 4s, 8s, ...)\n * - `linear`: Delay increases linearly (5s, 10s, 15s, 20s, ...)\n * - `fixed`: Same delay every time (10s, 10s, 10s, ...)\n *\n * All strategies support:\n * - `maxDelay`: Cap the maximum delay\n * - `jitter`: Add randomness to prevent thundering herd\n *\n * @example\n * ```typescript\n * const strategy = new BackoffStrategy({\n * strategy: 'exponential',\n * baseDelay: '1s',\n * maxDelay: '5m',\n * multiplier: 2,\n * jitter: true,\n * })\n *\n * strategy.calculateDelay(1) // ~1000ms\n * strategy.calculateDelay(2) // ~2000ms\n * strategy.calculateDelay(3) // ~4000ms\n * ```\n */\nexport class BackoffStrategy {\n readonly #config: BackoffConfig\n\n /**\n * Create a new backoff strategy.\n *\n * @param config - Backoff configuration\n * @throws {E_INVALID_BASE_DELAY} If baseDelay is not positive\n * @throws {E_INVALID_MAX_DELAY} If maxDelay is invalid\n * @throws {E_INVALID_MULTIPLIER} If multiplier is invalid\n */\n constructor(config: BackoffConfig) {\n this.#config = config\n this.#validateConfig()\n }\n\n /**\n * Calculate the delay for a given attempt number.\n *\n * @param attempt - The attempt number (1-based)\n * @returns Delay in milliseconds\n * @throws {RuntimeException} If attempt is less than 1\n *\n * @example\n * ```typescript\n * // Exponential: 1s, 2s, 4s, 8s, 16s, ...\n * strategy.calculateDelay(1) // 1000\n * strategy.calculateDelay(2) // 2000\n * strategy.calculateDelay(3) // 4000\n * ```\n */\n calculateDelay(attempt: number): number {\n if (attempt < 1) {\n throw new RuntimeException('Attempt number must be >= 1')\n }\n\n const baseDelayMs = parse(this.#config.baseDelay)\n const maxDelayMs = this.#config.maxDelay ? parse(this.#config.maxDelay) : Infinity\n const multiplier = this.#config.multiplier ?? 2\n\n let delay: number\n\n switch (this.#config.strategy) {\n case 'exponential':\n delay = baseDelayMs * Math.pow(multiplier, attempt - 1)\n break\n case 'linear':\n delay = baseDelayMs * attempt\n break\n case 'fixed':\n delay = baseDelayMs\n break\n default:\n assertUnreachable(this.#config.strategy)\n }\n\n // Apply max delay limit\n delay = Math.min(delay, maxDelayMs)\n\n if (this.#config.jitter) {\n delay = this.#applyJitter(delay)\n }\n\n return Math.floor(delay)\n }\n\n /**\n * Get the Date when the next retry should occur.\n *\n * @param attempt - The attempt number (1-based)\n * @returns Date for the next retry\n *\n * @example\n * ```typescript\n * const nextRetry = strategy.getNextRetryAt(3)\n * console.log(`Retry at: ${nextRetry.toISOString()}`)\n * ```\n */\n getNextRetryAt(attempt: number): Date {\n const delay = this.calculateDelay(attempt)\n return new Date(Date.now() + delay)\n }\n\n /**\n * Get a frozen copy of the configuration.\n *\n * @returns Readonly configuration object\n */\n getConfig(): Readonly<BackoffConfig> {\n return Object.freeze({ ...this.#config })\n }\n\n #validateConfig() {\n const baseDelayMs = parse(this.#config.baseDelay)\n\n if (baseDelayMs <= 0) {\n throw new errors.E_INVALID_BASE_DELAY([\n 'Base delay must be a positive integer greater than zero',\n ])\n }\n\n if (this.#config.maxDelay) {\n const maxDelayMs = parse(this.#config.maxDelay)\n\n if (maxDelayMs <= 0) {\n throw new errors.E_INVALID_MAX_DELAY([\n 'Max delay must be a positive integer greater than zero',\n ])\n }\n\n if (maxDelayMs <= baseDelayMs) {\n throw new errors.E_INVALID_MAX_DELAY(['Max delay should be greater than base delay'])\n }\n }\n\n if (this.#config.multiplier !== undefined) {\n if (this.#config.multiplier <= 0) {\n throw new errors.E_INVALID_MULTIPLIER([\n 'Multiplier must be a positive number greater than zero',\n ])\n }\n\n if (this.#config.strategy === 'exponential' && this.#config.multiplier < 1) {\n throw new errors.E_INVALID_MULTIPLIER(['Exponential strategy multiplier should be >= 1'])\n }\n }\n }\n\n #applyJitter(delay: number): number {\n const jitterRange = delay * 0.25\n const jitter = (Math.random() - 0.5) * 2 * jitterRange\n\n return Math.max(0, delay + jitter)\n }\n}\n\n/**\n * Create an exponential backoff strategy factory.\n *\n * Delay doubles with each attempt: 1s → 2s → 4s → 8s → ...\n *\n * Default config:\n * - baseDelay: 1s\n * - maxDelay: 5m\n * - multiplier: 2\n * - jitter: true\n *\n * @param config - Optional overrides for default config\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 5,\n * backoff: exponentialBackoff({ baseDelay: '500ms', maxDelay: '1m' }),\n * },\n * }\n * ```\n */\nexport function exponentialBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'exponential',\n baseDelay: '1s',\n maxDelay: '5m',\n multiplier: 2,\n jitter: true,\n ...config,\n })\n}\n\n/**\n * Create a linear backoff strategy factory.\n *\n * Delay increases linearly: 5s → 10s → 15s → 20s → ...\n *\n * Default config:\n * - baseDelay: 5s\n * - maxDelay: 2m\n *\n * @param config - Optional overrides for default config\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 3,\n * backoff: linearBackoff({ baseDelay: '10s' }),\n * },\n * }\n * ```\n */\nexport function linearBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'linear',\n baseDelay: '5s',\n maxDelay: '2m',\n ...config,\n })\n}\n\n/**\n * Create a fixed delay backoff strategy factory.\n *\n * Same delay every time: 10s → 10s → 10s → ...\n *\n * @param delay - The fixed delay (default: '10s')\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 3,\n * backoff: fixedBackoff('30s'),\n * },\n * }\n * ```\n */\nexport function fixedBackoff(delay: Duration = '10s') {\n return () =>\n new BackoffStrategy({\n strategy: 'fixed',\n baseDelay: delay,\n })\n}\n\n/**\n * Create a custom backoff strategy factory.\n *\n * Use this when you need full control over the configuration.\n *\n * @param config - Complete backoff configuration\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 5,\n * backoff: customBackoff({\n * strategy: 'exponential',\n * baseDelay: '100ms',\n * maxDelay: '30s',\n * multiplier: 3,\n * jitter: false,\n * }),\n * },\n * }\n * ```\n */\nexport function customBackoff(config: BackoffConfig) {\n return () => new BackoffStrategy(config)\n}\n"],"mappings":"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","names":[]}
@@ -206,7 +206,13 @@ type Duration = number | string;
206
206
  * - `{ age?, count? }`: Keep with pruning by age and/or count
207
207
  */
208
208
  type JobRetention = boolean | {
209
+ /**
210
+ * Keep jobs newer than this duration.
211
+ */
209
212
  age?: Duration;
213
+ /**
214
+ * Keep at most this many jobs.
215
+ */
210
216
  count?: number;
211
217
  };
212
218
  /**
@@ -265,7 +271,7 @@ interface JobData {
265
271
  /**
266
272
  * Job priority (lower = higher priority).
267
273
  *
268
- * @default 0
274
+ * @default 5
269
275
  */
270
276
  priority?: number;
271
277
  /**
@@ -291,6 +297,16 @@ interface JobData {
291
297
  * ```
292
298
  */
293
299
  groupId?: string;
300
+ /**
301
+ * Timestamp (ms) when the job was dispatched.
302
+ * Used to compute queue wait time in OTel instrumentation.
303
+ */
304
+ createdAt?: number;
305
+ /**
306
+ * Serialized trace context for distributed tracing.
307
+ * Injected by OTel plugin at dispatch time.
308
+ */
309
+ traceContext?: Record<string, string>;
294
310
  }
295
311
  /**
296
312
  * Record of a job's current state, including history for completed/failed jobs.
@@ -340,22 +356,28 @@ interface JobOptions {
340
356
  queue?: string;
341
357
  /**
342
358
  * Adapter name or factory to use for this job.
359
+ *
360
+ * Defaults to the queue manager's configured default adapter.
343
361
  */
344
362
  adapter?: string | (() => Adapter);
345
363
  /**
346
364
  * Maximum retry attempts before permanent failure.
347
365
  *
348
- * @default 3
366
+ * This is a convenience alias for `retry.maxRetries`.
367
+ *
368
+ * @default 0
349
369
  */
350
370
  maxRetries?: number;
351
371
  /**
352
372
  * Job priority (lower = higher priority).
353
373
  *
354
- * @default 0
374
+ * @default 5
355
375
  */
356
376
  priority?: number;
357
377
  /**
358
- * Retry configuration (backoff strategy, delays, etc.).
378
+ * Retry configuration for this job.
379
+ *
380
+ * Overrides queue-level and global retry settings.
359
381
  */
360
382
  retry?: RetryConfig;
361
383
  /**
@@ -367,10 +389,22 @@ interface JobOptions {
367
389
  /**
368
390
  * Whether to mark job as failed on timeout.
369
391
  *
370
- * @default true
392
+ * When disabled, timed out jobs follow the normal retry policy.
393
+ *
394
+ * @default false
371
395
  */
372
396
  failOnTimeout?: boolean;
397
+ /**
398
+ * Retention policy for completed jobs.
399
+ *
400
+ * By default, completed jobs are removed immediately.
401
+ */
373
402
  removeOnComplete?: JobRetention;
403
+ /**
404
+ * Retention policy for failed jobs.
405
+ *
406
+ * By default, failed jobs are removed immediately after the failure hooks run.
407
+ */
374
408
  removeOnFail?: JobRetention;
375
409
  }
376
410
  /**
@@ -438,23 +472,77 @@ type JobClass<T extends Job = Job> = (new (...args: unknown[]) => T) & {
438
472
  * ```
439
473
  */
440
474
  type JobFactory = (JobClass: JobClass) => Job | Promise<Job>;
475
+ /**
476
+ * Retry policy used by jobs, queues, or the queue manager.
477
+ */
441
478
  interface RetryConfig {
479
+ /**
480
+ * Number of retry attempts after the first failed execution.
481
+ *
482
+ * Set to `0` to disable retries.
483
+ *
484
+ * @default 0
485
+ */
442
486
  maxRetries?: number;
487
+ /**
488
+ * Factory that creates the backoff strategy used between retry attempts.
489
+ *
490
+ * If omitted, failed jobs are retried as soon as the adapter makes them
491
+ * available again.
492
+ */
443
493
  backoff?: () => BackoffStrategy$1;
444
494
  }
495
+ /**
496
+ * Built-in retry delay algorithms.
497
+ */
445
498
  type BackoffStrategy = 'exponential' | 'linear' | 'fixed';
499
+ /**
500
+ * Configuration for built-in and custom retry backoff strategies.
501
+ */
446
502
  interface BackoffConfig {
503
+ /**
504
+ * Strategy used to compute the delay before the next retry.
505
+ */
447
506
  strategy: BackoffStrategy;
507
+ /**
508
+ * Initial delay used by the strategy.
509
+ */
448
510
  baseDelay: Duration;
511
+ /**
512
+ * Upper bound for computed retry delays.
513
+ */
449
514
  maxDelay?: Duration;
515
+ /**
516
+ * Growth factor for exponential backoff.
517
+ */
450
518
  multiplier?: number;
519
+ /**
520
+ * Whether to randomize retry delays to avoid retry bursts.
521
+ */
451
522
  jitter?: boolean;
452
523
  }
524
+ /**
525
+ * Runtime configuration for a named queue.
526
+ */
453
527
  interface QueueConfig {
528
+ /**
529
+ * Adapter name used by jobs dispatched to this queue.
530
+ *
531
+ * Falls back to the queue manager's default adapter.
532
+ */
454
533
  adapter?: string;
534
+ /**
535
+ * Retry policy applied to jobs in this queue unless overridden by job options.
536
+ */
455
537
  retry?: RetryConfig;
538
+ /**
539
+ * Default job options applied to jobs in this queue unless overridden by the job.
540
+ */
456
541
  defaultJobOptions?: JobOptions;
457
542
  }
543
+ /**
544
+ * Runtime options for workers that poll queues and execute jobs.
545
+ */
458
546
  interface WorkerConfig {
459
547
  /**
460
548
  * Maximum number of jobs to process concurrently.
@@ -503,22 +591,32 @@ interface WorkerConfig {
503
591
  */
504
592
  onShutdownSignal?: () => void | Promise<void>;
505
593
  }
594
+ /**
595
+ * Event yielded by the low-level worker processing generator.
596
+ */
506
597
  type WorkerCycle = {
598
+ /** A job was acquired and execution started. */
507
599
  type: 'started';
508
600
  queue: string;
509
601
  job: JobData;
510
602
  } | {
603
+ /** A running job finished, either successfully or after failure handling. */
511
604
  type: 'completed';
512
605
  queue: string;
513
606
  job: JobData;
514
607
  } | {
608
+ /** No work was available. Consumers should wait before polling again. */
515
609
  type: 'idle';
516
610
  suggestedDelay: Duration;
517
611
  } | {
612
+ /** An unexpected worker loop error occurred. */
518
613
  type: 'error';
519
614
  error: Error;
520
615
  suggestedDelay: Duration;
521
616
  };
617
+ /**
618
+ * Factory used to lazily create adapter instances.
619
+ */
522
620
  type AdapterFactory<T extends Adapter = Adapter> = () => T;
523
621
  /**
524
622
  * Status of a schedule.
@@ -597,13 +695,59 @@ interface ScheduleListOptions {
597
695
  status?: ScheduleStatus;
598
696
  }
599
697
  interface QueueManagerConfig {
698
+ /**
699
+ * Name of the adapter used when a job does not select one explicitly.
700
+ *
701
+ * Must match one of the keys from `adapters`.
702
+ */
600
703
  default: string;
704
+ /**
705
+ * Available queue adapters keyed by name.
706
+ *
707
+ * Adapters are lazy-instantiated the first time they are used.
708
+ */
601
709
  adapters: Record<string, AdapterFactory>;
710
+ /**
711
+ * Global retry configuration applied to all jobs unless overridden by
712
+ * queue-level or job-level options.
713
+ */
602
714
  retry?: RetryConfig;
715
+ /**
716
+ * Global job options applied to all jobs unless overridden by queue-level
717
+ * or job-level options.
718
+ */
603
719
  defaultJobOptions?: JobOptions;
720
+ /**
721
+ * Per-queue configuration keyed by queue name.
722
+ *
723
+ * Use this to select adapters or defaults for specific queues.
724
+ */
604
725
  queues?: Record<string, QueueConfig>;
726
+ /**
727
+ * Worker runtime options used by `Worker` instances.
728
+ */
605
729
  worker?: WorkerConfig;
730
+ /**
731
+ * Glob patterns used to discover and register job classes.
732
+ *
733
+ * These locations are used by `init()` when `autoLoadJobs` is enabled,
734
+ * and by `QueueManager.loadJobs()` when called without arguments.
735
+ */
606
736
  locations?: string[];
737
+ /**
738
+ * Whether `init()` should immediately register jobs from configured locations.
739
+ *
740
+ * Framework integrations may disable this to defer job loading until a
741
+ * command lifecycle is ready, then call `QueueManager.loadJobs()`.
742
+ *
743
+ * @default true
744
+ */
745
+ autoLoadJobs?: boolean;
746
+ /**
747
+ * Logger used by the queue runtime.
748
+ *
749
+ * Defaults to the console logger.
750
+ */
607
751
  logger?: Logger;
608
752
  /**
609
753
  * Custom factory function for job instantiation.
@@ -625,6 +769,16 @@ interface QueueManagerConfig {
625
769
  * ```
626
770
  */
627
771
  jobFactory?: JobFactory;
772
+ /**
773
+ * Wraps internal adapter operations (Redis, Knex calls) to suppress
774
+ * or customize instrumentation. Used by OTel to suppress child spans.
775
+ */
776
+ internalOperationWrapper?: <T>(fn: () => Promise<T>) => Promise<T>;
777
+ /**
778
+ * Wraps job execution to inject tracing context or custom behavior.
779
+ * Called around `runtime.execute()` for each job attempt.
780
+ */
781
+ executionWrapper?: <T>(fn: () => Promise<T>, job: AcquiredJob, queue: string) => Promise<T>;
628
782
  }
629
783
 
630
784
  /**
@@ -1436,4 +1590,4 @@ declare abstract class Job<Payload = any> {
1436
1590
  failed?(error: Error): Promise<void>;
1437
1591
  }
1438
1592
 
1439
- 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 JobClass as a, type AcquiredJob as b, type JobRetention as c, type JobRecord as d, type ScheduleData as e, type ScheduleListOptions as f, type JobOptions as g, type QueueConfig as h, type JobFactory as i, Job as j, type ScheduleStatus as k, ScheduleBuilder as l, JobBatchDispatcher as m, customBackoff as n, linearBackoff as o, exponentialBackoff as p, fixedBackoff as q, type JobStatus as r, type DispatchResult as s, type DispatchManyResult as t, type JobContext as u, type BackoffConfig as v, type WorkerConfig as w, type AdapterFactory as x, type ScheduleResult as y };
1593
+ export { type Adapter as A, type BackoffConfig 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 JobClass as a, type AcquiredJob as b, type JobRetention as c, type JobRecord as d, type ScheduleData as e, type ScheduleListOptions as f, type JobOptions as g, type QueueConfig as h, type JobFactory as i, Job as j, type ScheduleStatus as k, ScheduleBuilder as l, JobBatchDispatcher as m, customBackoff as n, linearBackoff as o, exponentialBackoff as p, fixedBackoff as q, type AdapterFactory as r, type BackoffStrategy as s, type DispatchManyResult as t, type DispatchResult as u, type JobContext as v, type JobStatus as w, type ScheduleResult as x, type WorkerConfig as y };
@@ -1 +1 @@
1
- export { b as AcquiredJob, A as Adapter } from '../../index-B1XdqWpN.js';
1
+ export { b as AcquiredJob, A as Adapter } from '../../job-Z5fBSzRX.js';
@@ -1,4 +1,4 @@
1
- import { A as Adapter, J as JobData, a as JobClass, b as AcquiredJob, c as JobRetention, d as JobRecord, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../index-B1XdqWpN.js';
1
+ import { A as Adapter, J as JobData, a as JobClass, b as AcquiredJob, c as JobRetention, d as JobRecord, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-Z5fBSzRX.js';
2
2
 
3
3
  interface FakeJobRecord {
4
4
  queue: string;
@@ -23,6 +23,11 @@ declare function fake(): () => FakeAdapter;
23
23
  */
24
24
  declare class FakeAdapter implements Adapter {
25
25
  #private;
26
+ /**
27
+ * Set the function to call when the fake is disposed
28
+ */
29
+ onDispose(fn: () => void): this;
30
+ [Symbol.dispose](): void;
26
31
  setWorkerId(_workerId: string): void;
27
32
  getPushedJobs(): FakeJobRecord[];
28
33
  getPushedJobsOn(queue: string): FakeJobRecord[];
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  FakeAdapter,
3
3
  fake
4
- } from "../../chunk-OVYXMSSU.js";
5
- import "../../chunk-ZZFSQY36.js";
4
+ } from "../../chunk-VRXHCWNK.js";
5
+ import "../../chunk-WVLSICD4.js";
6
+ import "../../chunk-QEFYHCL7.js";
7
+ import "../../chunk-PZ5AY32C.js";
6
8
  export {
7
9
  FakeAdapter,
8
10
  fake
@@ -1,5 +1,5 @@
1
1
  import { Knex } from 'knex';
2
- import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../index-B1XdqWpN.js';
2
+ import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-Z5fBSzRX.js';
3
3
 
4
4
  interface KnexAdapterOptions {
5
5
  connection: Knex;
@@ -2,7 +2,8 @@ import {
2
2
  DEFAULT_PRIORITY,
3
3
  calculateScore,
4
4
  resolveRetention
5
- } from "../../chunk-ZZFSQY36.js";
5
+ } from "../../chunk-QEFYHCL7.js";
6
+ import "../../chunk-PZ5AY32C.js";
6
7
 
7
8
  // src/drivers/knex_adapter.ts
8
9
  import { randomUUID } from "crypto";