@boringnode/queue 0.4.1 → 0.5.1

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 +90 -0
  2. package/build/chunk-KI47AJ6U.js +128 -0
  3. package/build/chunk-KI47AJ6U.js.map +1 -0
  4. package/build/chunk-PZ5AY32C.js +10 -0
  5. package/build/chunk-PZ5AY32C.js.map +1 -0
  6. package/build/{chunk-ZZFSQY36.js → chunk-QEFYHCL7.js} +4 -6
  7. package/build/{chunk-ZZFSQY36.js.map → chunk-QEFYHCL7.js.map} +1 -1
  8. package/build/{chunk-6EBS7CW4.js → chunk-VHN3XZDC.js} +180 -65
  9. package/build/chunk-VHN3XZDC.js.map +1 -0
  10. package/build/chunk-WVLSICD4.js +20 -0
  11. package/build/chunk-WVLSICD4.js.map +1 -0
  12. package/build/index.d.ts +81 -25
  13. package/build/index.js +79 -113
  14. package/build/index.js.map +1 -1
  15. package/build/{index-BAMFA6FI.d.ts → job-DImdhRFO.d.ts} +19 -4
  16. package/build/src/contracts/adapter.d.ts +1 -1
  17. package/build/src/drivers/fake_adapter.d.ts +1 -1
  18. package/build/src/drivers/fake_adapter.js +4 -2
  19. package/build/src/drivers/knex_adapter.d.ts +1 -1
  20. package/build/src/drivers/knex_adapter.js +2 -1
  21. package/build/src/drivers/knex_adapter.js.map +1 -1
  22. package/build/src/drivers/redis_adapter.d.ts +1 -1
  23. package/build/src/drivers/redis_adapter.js +2 -1
  24. package/build/src/drivers/redis_adapter.js.map +1 -1
  25. package/build/src/drivers/sync_adapter.d.ts +1 -1
  26. package/build/src/drivers/sync_adapter.js +74 -19
  27. package/build/src/drivers/sync_adapter.js.map +1 -1
  28. package/build/src/otel.d.ts +63 -0
  29. package/build/src/otel.js +242 -0
  30. package/build/src/otel.js.map +1 -0
  31. package/build/src/types/index.d.ts +6 -1
  32. package/build/src/types/main.d.ts +1 -1
  33. package/build/src/types/tracing_channels.d.ts +34 -0
  34. package/build/src/types/tracing_channels.js +1 -0
  35. package/build/src/types/tracing_channels.js.map +1 -0
  36. package/package.json +35 -12
  37. package/build/chunk-6EBS7CW4.js.map +0 -1
package/build/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ import {
2
+ JobExecutionRuntime
3
+ } from "./chunk-KI47AJ6U.js";
1
4
  import {
2
5
  Job,
3
6
  JobBatchDispatcher,
@@ -6,7 +9,12 @@ import {
6
9
  QueueManager,
7
10
  ScheduleBuilder,
8
11
  debug_default
9
- } from "./chunk-6EBS7CW4.js";
12
+ } from "./chunk-VHN3XZDC.js";
13
+ import {
14
+ dispatchChannel,
15
+ executeChannel,
16
+ tracing_channels_exports
17
+ } from "./chunk-WVLSICD4.js";
10
18
  import {
11
19
  DEFAULT_ERROR_RETRY_DELAY,
12
20
  DEFAULT_IDLE_DELAY,
@@ -16,11 +24,10 @@ import {
16
24
  E_INVALID_BASE_DELAY,
17
25
  E_INVALID_MAX_DELAY,
18
26
  E_INVALID_MULTIPLIER,
19
- E_JOB_MAX_ATTEMPTS_REACHED,
20
- E_JOB_TIMEOUT,
21
27
  exceptions_exports,
22
28
  parse
23
- } from "./chunk-ZZFSQY36.js";
29
+ } from "./chunk-QEFYHCL7.js";
30
+ import "./chunk-PZ5AY32C.js";
24
31
 
25
32
  // src/worker.ts
26
33
  import { randomUUID } from "crypto";
@@ -112,6 +119,7 @@ var Worker = class {
112
119
  #gracefulShutdown;
113
120
  #onShutdownSignal;
114
121
  #adapter;
122
+ #wrapInternal = (fn) => fn();
115
123
  #running = false;
116
124
  #initialized = false;
117
125
  #generator;
@@ -152,6 +160,7 @@ var Worker = class {
152
160
  await QueueManager.init(this.#config);
153
161
  this.#adapter = QueueManager.use();
154
162
  this.#adapter.setWorkerId(this.#id);
163
+ this.#wrapInternal = QueueManager.getInternalOperationWrapper();
155
164
  this.#initialized = true;
156
165
  debug_default("worker %s initialized", this.#id);
157
166
  }
@@ -199,7 +208,8 @@ var Worker = class {
199
208
  /**
200
209
  * Stop the worker gracefully.
201
210
  *
202
- * Waits for all running jobs to complete before shutting down.
211
+ * Waits for all running jobs to complete before stopping job consumption.
212
+ * Adapter cleanup remains the responsibility of `QueueManager.destroy()`.
203
213
  * Called automatically on SIGINT/SIGTERM if gracefulShutdown is enabled.
204
214
  */
205
215
  async stop() {
@@ -209,9 +219,6 @@ var Worker = class {
209
219
  debug_default("worker %s: waiting for %d running jobs to complete", this.#id, this.#pool.size);
210
220
  await this.#pool.drain();
211
221
  }
212
- if (this.#adapter) {
213
- await this.#adapter.destroy();
214
- }
215
222
  this.#removeShutdownHandlers();
216
223
  }
217
224
  /**
@@ -322,48 +329,51 @@ var Worker = class {
322
329
  async #execute(job, queue) {
323
330
  const startTime = performance.now();
324
331
  debug_default("worker %s: executing job %s (%s)", this.#id, job.id, job.name);
325
- const { instance, options, timeout, context, payload } = await this.#initJob(job, queue);
326
- const retention = QueueManager.getMergedJobOptions(queue, options);
327
- try {
328
- await this.#executeWithTimeout(instance, payload, context, timeout);
329
- await this.#adapter.completeJob(job.id, queue, retention.removeOnComplete);
330
- const duration = (performance.now() - startTime).toFixed(2);
331
- debug_default("worker %s: successfully executed job %s in %dms", this.#id, job.id, duration);
332
- } catch (e) {
333
- const isTimeout = e instanceof E_JOB_TIMEOUT;
334
- if (isTimeout && options.failOnTimeout) {
335
- debug_default("worker %s: job %s timed out and failOnTimeout is set", this.#id, job.id);
336
- await this.#adapter.failJob(job.id, queue, e, retention.removeOnFail);
337
- await instance.failed?.(e);
338
- return;
339
- }
340
- const mergedConfig = QueueManager.getMergedRetryConfig(queue, options.retry);
341
- if (typeof mergedConfig.maxRetries === "undefined" || mergedConfig.maxRetries <= 0) {
342
- debug_default("worker %s: job %s has no retries configured, marking as failed", this.#id, job.id);
343
- await this.#adapter.failJob(job.id, queue, e, retention.removeOnFail);
344
- await instance.failed?.(e);
345
- return;
346
- }
347
- if (job.attempts >= mergedConfig.maxRetries) {
348
- debug_default(
349
- "worker %s: job %s has exceeded max retries (%d), marking as failed",
350
- this.#id,
351
- job.id,
352
- mergedConfig.maxRetries
353
- );
354
- await this.#adapter.failJob(job.id, queue, e, retention.removeOnFail);
355
- const exception = new E_JOB_MAX_ATTEMPTS_REACHED([job.name], { cause: e });
356
- await instance.failed?.(exception);
357
- return;
358
- }
359
- if (mergedConfig.backoff) {
360
- const strategy = mergedConfig.backoff();
361
- const nextRetryAt = strategy.getNextRetryAt(job.attempts + 1);
362
- debug_default("worker %s: job %s will retry at %s", this.#id, job.id, nextRetryAt.toISOString());
363
- await this.#adapter.retryJob(job.id, queue, nextRetryAt);
364
- return;
365
- }
366
- await this.#adapter.retryJob(job.id, queue);
332
+ const { instance, options, context, payload } = await this.#initJob(job, queue);
333
+ const configResolver = QueueManager.getConfigResolver();
334
+ const retention = configResolver.resolveJobOptions(queue, options);
335
+ const runtime = JobExecutionRuntime.from({
336
+ jobName: job.name,
337
+ options,
338
+ retryConfig: configResolver.resolveRetryConfig(queue, options),
339
+ defaultTimeout: configResolver.getWorkerTimeout()
340
+ });
341
+ const executeMessage = { job, queue };
342
+ const run = () => {
343
+ return executeChannel.tracePromise(async () => {
344
+ try {
345
+ await runtime.execute(instance, payload, context);
346
+ await this.#wrapInternal(() => this.#adapter.completeJob(job.id, queue, retention.removeOnComplete));
347
+ executeMessage.status = "completed";
348
+ debug_default("worker %s: successfully executed job %s in %dms", this.#id, job.id, (performance.now() - startTime).toFixed(2));
349
+ } catch (e) {
350
+ await this.#handleExecutionFailure({ error: e, job, queue, instance, runtime, retention, executeMessage });
351
+ }
352
+ executeMessage.duration = Number((performance.now() - startTime).toFixed(2));
353
+ }, executeMessage);
354
+ };
355
+ const executionWrapper = QueueManager.getExecutionWrapper();
356
+ await executionWrapper(run, job, queue);
357
+ }
358
+ async #handleExecutionFailure(options) {
359
+ const outcome = options.runtime.resolveFailure(options.error, options.job.attempts);
360
+ options.executeMessage.error = options.error;
361
+ if (outcome.type === "failed") {
362
+ options.executeMessage.status = "failed";
363
+ await this.#wrapInternal(
364
+ () => this.#adapter.failJob(options.job.id, options.queue, outcome.storageError, options.retention.removeOnFail)
365
+ );
366
+ await options.instance.failed?.(outcome.hookError);
367
+ return;
368
+ }
369
+ if (outcome.type !== "retry") return;
370
+ options.executeMessage.status = "retrying";
371
+ options.executeMessage.nextRetryAt = outcome.retryAt;
372
+ if (outcome.retryAt) {
373
+ debug_default("worker %s: job %s will retry at %s", this.#id, options.job.id, outcome.retryAt.toISOString());
374
+ await this.#wrapInternal(() => this.#adapter.retryJob(options.job.id, options.queue, outcome.retryAt));
375
+ } else {
376
+ await this.#wrapInternal(() => this.#adapter.retryJob(options.job.id, options.queue));
367
377
  }
368
378
  }
369
379
  async #initJob(job, queue) {
@@ -381,66 +391,17 @@ var Worker = class {
381
391
  const jobFactory = QueueManager.getJobFactory();
382
392
  const instance = jobFactory ? await jobFactory(JobClass) : new JobClass();
383
393
  const options = JobClass.options || {};
384
- const timeout = this.#getJobTimeout(options);
385
- return { instance, options, timeout, context, payload: job.payload };
394
+ return { instance, options, context, payload: job.payload };
386
395
  } catch (error) {
387
396
  debug_default("worker %s: failed to initialize job %s (%s)", this.#id, job.id, job.name);
388
- const retention = QueueManager.getMergedJobOptions(queue);
389
- await this.#adapter.failJob(job.id, queue, error, retention.removeOnFail);
397
+ const retention = QueueManager.getConfigResolver().resolveJobOptions(queue);
398
+ await this.#wrapInternal(() => this.#adapter.failJob(job.id, queue, error, retention.removeOnFail));
390
399
  throw error;
391
400
  }
392
401
  }
393
- #getJobTimeout(options) {
394
- if (options.timeout !== void 0) {
395
- return parse(options.timeout);
396
- }
397
- if (this.#config.worker?.timeout !== void 0) {
398
- return parse(this.#config.worker.timeout);
399
- }
400
- return void 0;
401
- }
402
- async #executeWithTimeout(instance, payload, context, timeout) {
403
- if (timeout === void 0) {
404
- instance.$hydrate(payload, context);
405
- return instance.execute();
406
- }
407
- const signal = AbortSignal.timeout(timeout);
408
- instance.$hydrate(payload, context, signal);
409
- const { abortPromise, cleanupAbortListener } = this.#createTimeoutAbortRace(
410
- signal,
411
- instance.constructor.name,
412
- timeout
413
- );
414
- try {
415
- await Promise.race([instance.execute(), abortPromise]);
416
- } finally {
417
- cleanupAbortListener();
418
- }
419
- }
420
- #createTimeoutAbortRace(signal, jobName, timeout) {
421
- let abortHandler;
422
- const abortPromise = new Promise((_, reject) => {
423
- abortHandler = () => {
424
- reject(new E_JOB_TIMEOUT([jobName, timeout]));
425
- };
426
- if (signal.aborted) {
427
- abortHandler();
428
- return;
429
- }
430
- signal.addEventListener("abort", abortHandler, { once: true });
431
- });
432
- return {
433
- abortPromise,
434
- cleanupAbortListener: () => {
435
- if (abortHandler) {
436
- signal.removeEventListener("abort", abortHandler);
437
- }
438
- }
439
- };
440
- }
441
402
  async #acquireNextJob(queues) {
442
403
  for (const queue of queues) {
443
- const job = await this.#adapter.popFrom(queue);
404
+ const job = await this.#wrapInternal(() => this.#adapter.popFrom(queue));
444
405
  if (!job) {
445
406
  continue;
446
407
  }
@@ -456,10 +417,8 @@ var Worker = class {
456
417
  }
457
418
  this.#lastStalledCheck = now;
458
419
  for (const queue of queues) {
459
- const recovered = await this.#adapter.recoverStalledJobs(
460
- queue,
461
- this.#stalledThreshold,
462
- this.#maxStalledCount
420
+ const recovered = await this.#wrapInternal(
421
+ () => this.#adapter.recoverStalledJobs(queue, this.#stalledThreshold, this.#maxStalledCount)
463
422
  );
464
423
  if (recovered > 0) {
465
424
  debug_default("worker %s: recovered %d stalled jobs from queue %s", this.#id, recovered, queue);
@@ -495,7 +454,7 @@ var Worker = class {
495
454
  */
496
455
  async #dispatchDueSchedules() {
497
456
  while (true) {
498
- const schedule = await this.#adapter.claimDueSchedule();
457
+ const schedule = await this.#wrapInternal(() => this.#adapter.claimDueSchedule());
499
458
  if (!schedule) {
500
459
  break;
501
460
  }
@@ -508,13 +467,17 @@ var Worker = class {
508
467
  );
509
468
  const JobClass = Locator.get(schedule.name);
510
469
  const queue = JobClass?.options?.queue ?? "default";
511
- await this.#adapter.pushOn(queue, {
470
+ const jobData = {
512
471
  id: randomUUID(),
513
472
  name: schedule.name,
514
473
  payload: schedule.payload,
515
474
  attempts: 0,
516
475
  priority: JobClass?.options?.priority
517
- });
476
+ };
477
+ const message = { jobs: [jobData], queue };
478
+ await dispatchChannel.tracePromise(async () => {
479
+ await this.#wrapInternal(() => this.#adapter.pushOn(queue, jobData));
480
+ }, message);
518
481
  }
519
482
  }
520
483
  };
@@ -620,13 +583,15 @@ var Schedule = class _Schedule {
620
583
  * Also updates runCount and lastRunAt.
621
584
  *
622
585
  * If the schedule has reached its limit, the job will not be dispatched.
586
+ *
587
+ * @param payload - Optional custom payload for the job
623
588
  */
624
- async trigger() {
589
+ async trigger(payload) {
625
590
  if (this.#data.limit !== null && this.#data.runCount >= this.#data.limit) {
626
591
  return;
627
592
  }
628
593
  const adapter = QueueManager.use();
629
- const dispatcher = new JobDispatcher(this.#data.name, this.#data.payload);
594
+ const dispatcher = new JobDispatcher(this.#data.name, payload ?? this.#data.payload);
630
595
  await dispatcher.run();
631
596
  const now = /* @__PURE__ */ new Date();
632
597
  const newRunCount = this.#data.runCount + 1;
@@ -864,6 +829,7 @@ export {
864
829
  exceptions_exports as errors,
865
830
  exponentialBackoff,
866
831
  fixedBackoff,
867
- linearBackoff
832
+ linearBackoff,
833
+ tracing_channels_exports as tracingChannels
868
834
  };
869
835
  //# sourceMappingURL=index.js.map
@@ -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 * as errors from './exceptions.js'\nimport { QueueManager } from './queue_manager.js'\nimport { JobPool } from './job_pool.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 shutting down.\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 if (this.#adapter) {\n await this.#adapter.destroy()\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, timeout, context, payload } = await this.#initJob(job, queue)\n const retention = QueueManager.getMergedJobOptions(queue, options)\n\n try {\n await this.#executeWithTimeout(instance, payload, context, timeout)\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 isTimeout = e instanceof errors.E_JOB_TIMEOUT\n\n if (isTimeout && options.failOnTimeout) {\n debug('worker %s: job %s timed out and failOnTimeout is set', this.#id, job.id)\n await this.#adapter.failJob(job.id, queue, e as Error, retention.removeOnFail)\n await instance.failed?.(e as Error)\n return\n }\n\n const mergedConfig = QueueManager.getMergedRetryConfig(queue, options.retry)\n\n if (typeof mergedConfig.maxRetries === 'undefined' || mergedConfig.maxRetries <= 0) {\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, e as Error, retention.removeOnFail)\n await instance.failed?.(e as Error)\n return\n }\n\n if (job.attempts >= mergedConfig.maxRetries!) {\n debug(\n 'worker %s: job %s has exceeded max retries (%d), marking as failed',\n this.#id,\n job.id,\n mergedConfig.maxRetries\n )\n await this.#adapter.failJob(job.id, queue, e as Error, retention.removeOnFail)\n const exception = new errors.E_JOB_MAX_ATTEMPTS_REACHED([job.name], { cause: e })\n await instance.failed?.(exception)\n\n return\n }\n\n if (mergedConfig.backoff) {\n const strategy = mergedConfig.backoff()\n const nextRetryAt = strategy.getNextRetryAt(job.attempts + 1)\n\n debug('worker %s: job %s will retry at %s', this.#id, job.id, nextRetryAt.toISOString())\n\n await this.#adapter.retryJob(job.id, queue, nextRetryAt)\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 timeout: number | undefined\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 const timeout = this.#getJobTimeout(options)\n\n return { instance, options, timeout, 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.getMergedJobOptions(queue)\n await this.#adapter.failJob(job.id, queue, error as Error, retention.removeOnFail)\n throw error\n }\n }\n\n #getJobTimeout(options: JobOptions): number | undefined {\n if (options.timeout !== undefined) {\n return parse(options.timeout)\n }\n\n if (this.#config.worker?.timeout !== undefined) {\n return parse(this.#config.worker.timeout)\n }\n\n return undefined\n }\n\n async #executeWithTimeout(\n instance: Job,\n payload: unknown,\n context: JobContext,\n timeout?: number\n ): Promise<void> {\n if (timeout === undefined) {\n instance.$hydrate(payload, context)\n return instance.execute()\n }\n\n const signal = AbortSignal.timeout(timeout)\n instance.$hydrate(payload, context, signal)\n\n const { abortPromise, cleanupAbortListener } = this.#createTimeoutAbortRace(\n signal,\n instance.constructor.name,\n timeout\n )\n\n try {\n await Promise.race([instance.execute(), abortPromise])\n } finally {\n cleanupAbortListener()\n }\n }\n\n #createTimeoutAbortRace(signal: AbortSignal, jobName: string, timeout: number) {\n let abortHandler: (() => void) | undefined\n\n const abortPromise = new Promise<never>((_, reject) => {\n abortHandler = () => {\n reject(new errors.E_JOB_TIMEOUT([jobName, timeout]))\n }\n\n if (signal.aborted) {\n abortHandler()\n return\n }\n\n signal.addEventListener('abort', abortHandler, { once: true })\n })\n\n return {\n abortPromise,\n cleanupAbortListener: () => {\n if (abortHandler) {\n signal.removeEventListener('abort', abortHandler)\n }\n },\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":[]}
@@ -291,6 +291,11 @@ interface JobData {
291
291
  * ```
292
292
  */
293
293
  groupId?: string;
294
+ /**
295
+ * Serialized trace context for distributed tracing.
296
+ * Injected by OTel plugin at dispatch time.
297
+ */
298
+ traceContext?: Record<string, string>;
294
299
  }
295
300
  /**
296
301
  * Record of a job's current state, including history for completed/failed jobs.
@@ -625,6 +630,16 @@ interface QueueManagerConfig {
625
630
  * ```
626
631
  */
627
632
  jobFactory?: JobFactory;
633
+ /**
634
+ * Wraps internal adapter operations (Redis, Knex calls) to suppress
635
+ * or customize instrumentation. Used by OTel to suppress child spans.
636
+ */
637
+ internalOperationWrapper?: <T>(fn: () => Promise<T>) => Promise<T>;
638
+ /**
639
+ * Wraps job execution to inject tracing context or custom behavior.
640
+ * Called around `runtime.execute()` for each job attempt.
641
+ */
642
+ executionWrapper?: <T>(fn: () => Promise<T>, job: AcquiredJob, queue: string) => Promise<T>;
628
643
  }
629
644
 
630
645
  /**
@@ -1339,7 +1354,7 @@ declare abstract class Job<Payload = any> {
1339
1354
  * .run()
1340
1355
  * ```
1341
1356
  */
1342
- static dispatch<T extends Job>(this: new (...args: unknown[]) => T, payload: T extends Job<infer P> ? P : never): JobDispatcher<T extends Job<infer P> ? P : never>;
1357
+ static dispatch<T extends Job>(this: abstract new (...args: any[]) => T, payload: T extends Job<infer P> ? P : never): JobDispatcher<T extends Job<infer P> ? P : never>;
1343
1358
  /**
1344
1359
  * Dispatch multiple jobs to the queue in a single batch.
1345
1360
  *
@@ -1367,7 +1382,7 @@ declare abstract class Job<Payload = any> {
1367
1382
  * console.log(`Dispatched ${jobIds.length} jobs`)
1368
1383
  * ```
1369
1384
  */
1370
- static dispatchMany<T extends Job>(this: new (...args: unknown[]) => T, payloads: (T extends Job<infer P> ? P : never)[]): JobBatchDispatcher<T extends Job<infer P> ? P : never>;
1385
+ static dispatchMany<T extends Job>(this: abstract new (...args: any[]) => T, payloads: (T extends Job<infer P> ? P : never)[]): JobBatchDispatcher<T extends Job<infer P> ? P : never>;
1371
1386
  /**
1372
1387
  * Create a schedule for this job.
1373
1388
  *
@@ -1393,7 +1408,7 @@ declare abstract class Job<Payload = any> {
1393
1408
  * .run()
1394
1409
  * ```
1395
1410
  */
1396
- static schedule<T extends Job>(this: new (...args: unknown[]) => T, payload: T extends Job<infer P> ? P : never): ScheduleBuilder<T extends Job<infer P> ? P : never>;
1411
+ static schedule<T extends Job>(this: abstract new (...args: any[]) => T, payload: T extends Job<infer P> ? P : never): ScheduleBuilder<T extends Job<infer P> ? P : never>;
1397
1412
  /**
1398
1413
  * Execute the job's business logic.
1399
1414
  *
@@ -1436,4 +1451,4 @@ declare abstract class Job<Payload = any> {
1436
1451
  failed?(error: Error): Promise<void>;
1437
1452
  }
1438
1453
 
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 JobFactory as g, type JobOptions as h, Job as i, type ScheduleStatus as j, ScheduleBuilder as k, JobBatchDispatcher as l, customBackoff as m, linearBackoff as n, exponentialBackoff as o, fixedBackoff as p, type JobStatus as q, type DispatchResult as r, type DispatchManyResult as s, type JobContext as t, type BackoffConfig as u, type QueueConfig as v, type WorkerConfig as w, type AdapterFactory as x, type ScheduleResult as y };
1454
+ 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-BAMFA6FI.js';
1
+ export { b as AcquiredJob, A as Adapter } from '../../job-DImdhRFO.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-BAMFA6FI.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-DImdhRFO.js';
2
2
 
3
3
  interface FakeJobRecord {
4
4
  queue: string;
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  FakeAdapter,
3
3
  fake
4
- } from "../../chunk-6EBS7CW4.js";
5
- import "../../chunk-ZZFSQY36.js";
4
+ } from "../../chunk-VHN3XZDC.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