@boringnode/queue 0.3.2 → 0.3.4

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.
@@ -290,7 +290,7 @@ var QueueManagerSingleton = class {
290
290
  return instance;
291
291
  } catch (error) {
292
292
  const message = error instanceof Error ? error.message : String(error);
293
- throw new E_ADAPTER_INIT_ERROR([adapter, message]);
293
+ throw new E_ADAPTER_INIT_ERROR([adapter, message], { cause: error });
294
294
  }
295
295
  }
296
296
  /**
@@ -864,7 +864,12 @@ var ScheduleBuilder = class {
864
864
  try {
865
865
  CronExpressionParser.parse(this.#cronExpression, { tz: this.#timezone });
866
866
  } catch (error) {
867
- throw new E_INVALID_CRON_EXPRESSION([this.#cronExpression, error.message]);
867
+ throw new E_INVALID_CRON_EXPRESSION(
868
+ [this.#cronExpression, error.message],
869
+ {
870
+ cause: error
871
+ }
872
+ );
868
873
  }
869
874
  }
870
875
  const config = {
@@ -1547,4 +1552,4 @@ export {
1547
1552
  ScheduleBuilder,
1548
1553
  Job
1549
1554
  };
1550
- //# sourceMappingURL=chunk-RO6VVBGK.js.map
1555
+ //# sourceMappingURL=chunk-H6WOFLPJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/drivers/fake_adapter.ts","../src/debug.ts","../src/job_dispatcher.ts","../src/locator.ts","../src/logger.ts","../src/queue_manager.ts","../src/job_batch_dispatcher.ts","../src/schedule_builder.ts","../src/job.ts"],"sourcesContent":["import assert from 'node:assert/strict'\nimport { randomUUID } from 'node:crypto'\nimport { isDeepStrictEqual } from 'node:util'\nimport { CronExpressionParser } from 'cron-parser'\nimport type { Adapter, AcquiredJob } from '../contracts/adapter.js'\nimport type {\n JobData,\n JobClass,\n JobRecord,\n JobRetention,\n ScheduleConfig,\n ScheduleData,\n ScheduleListOptions,\n} from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\nimport { parse } from '../utils.js'\nimport { Job } from '../job.js'\n\ninterface ActiveJob {\n job: JobData\n acquiredAt: number\n queue: string\n}\n\ninterface DelayedJob {\n job: JobData\n executeAt: number\n delay: number\n}\n\nexport interface FakeJobRecord {\n queue: string\n job: JobData\n delay?: number\n pushedAt: number\n}\n\nexport type FakeJobMatcher = string | JobClass | ((job: JobData) => boolean)\nexport type FakePayloadMatcher =\n | ((payload: any) => boolean)\n | object\n | string\n | number\n | boolean\n | null\n | undefined\nexport type FakeDelayMatcher = number | ((delay: number | undefined) => boolean)\n\nexport interface FakeJobQuery {\n queue?: string\n payload?: FakePayloadMatcher\n delay?: FakeDelayMatcher\n}\n\n/**\n * Create a fake adapter factory.\n */\nexport function fake() {\n return () => new FakeAdapter()\n}\n\n/**\n * In-memory adapter designed for tests with assertion helpers.\n */\nexport class FakeAdapter implements Adapter {\n #queues = new Map<string, JobData[]>()\n #activeJobs = new Map<string, ActiveJob>()\n #delayedJobs = new Map<string, Map<string, DelayedJob>>()\n #completedJobs = new Map<string, JobRecord[]>()\n #failedJobs = new Map<string, JobRecord[]>()\n #pendingTimeouts = new Set<NodeJS.Timeout>()\n #schedules = new Map<string, ScheduleData>()\n #pushedJobs: FakeJobRecord[] = []\n\n setWorkerId(_workerId: string): void {}\n\n getPushedJobs(): FakeJobRecord[] {\n return [...this.#pushedJobs]\n }\n\n getPushedJobsOn(queue: string): FakeJobRecord[] {\n return this.#pushedJobs.filter((record) => record.queue === queue)\n }\n\n findPushed(matcher: FakeJobMatcher, query?: FakeJobQuery): FakeJobRecord | undefined {\n return this.#pushedJobs.find((record) => this.#matchesRecord(record, matcher, query))\n }\n\n clearPushedJobs(): void {\n this.#pushedJobs = []\n }\n\n clear(): void {\n for (const timeout of this.#pendingTimeouts) {\n clearTimeout(timeout)\n }\n\n this.#pendingTimeouts.clear()\n this.#queues.clear()\n this.#activeJobs.clear()\n this.#delayedJobs.clear()\n this.#completedJobs.clear()\n this.#failedJobs.clear()\n this.#schedules.clear()\n this.#pushedJobs = []\n }\n\n assertPushed(matcher: FakeJobMatcher, query?: FakeJobQuery): void {\n const record = this.findPushed(matcher, query)\n assert.ok(record, this.#formatFailure('Expected job to be pushed', matcher, query))\n }\n\n assertNotPushed(matcher: FakeJobMatcher, query?: FakeJobQuery): void {\n const record = this.findPushed(matcher, query)\n assert.ok(!record, this.#formatFailure('Expected job to not be pushed', matcher, query))\n }\n\n assertPushedCount(count: number, options?: { queue?: string }): void {\n const actual = options?.queue\n ? this.#pushedJobs.filter((record) => record.queue === options.queue).length\n : this.#pushedJobs.length\n\n const suffix = options?.queue ? ` on \"${options.queue}\"` : ''\n assert.equal(actual, count, `Expected ${count} pushed job(s)${suffix}, got ${actual}`)\n }\n\n assertNothingPushed(): void {\n assert.equal(\n this.#pushedJobs.length,\n 0,\n `Expected no jobs to be pushed, got ${this.#pushedJobs.length}`\n )\n }\n\n async size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n async sizeOf(queue: string): Promise<number> {\n const jobs = this.#queues.get(queue) || []\n\n return jobs.length\n }\n\n async push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<void> {\n this.#recordPush(queue, jobData)\n this.#enqueue(queue, jobData)\n }\n\n async pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n this.#recordPush(queue, jobData, delay)\n this.#schedulePush(queue, jobData, delay)\n\n return Promise.resolve()\n }\n\n async pushMany(jobs: JobData[]): Promise<void> {\n return this.pushManyOn('default', jobs)\n }\n\n async pushManyOn(queue: string, jobs: JobData[]): Promise<void> {\n for (const job of jobs) {\n await this.pushOn(queue, job)\n }\n }\n\n async pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<AcquiredJob | null> {\n const jobs = this.#queues.get(queue)\n\n if (!jobs || jobs.length === 0) {\n return null\n }\n\n // Find job with highest priority (lowest priority number)\n let bestIndex = 0\n let bestPriority = jobs[0].priority ?? DEFAULT_PRIORITY\n\n for (let i = 1; i < jobs.length; i++) {\n const priority = jobs[i].priority ?? DEFAULT_PRIORITY\n if (priority < bestPriority) {\n bestPriority = priority\n bestIndex = i\n }\n }\n\n const [job] = jobs.splice(bestIndex, 1)\n if (!job) {\n return null\n }\n\n const acquiredAt = Date.now()\n this.#activeJobs.set(job.id, { job, acquiredAt, queue })\n\n return { ...job, acquiredAt }\n }\n\n async completeJob(jobId: string, queue: string, removeOnComplete?: JobRetention): Promise<void> {\n const active = this.#activeJobs.get(jobId)\n if (!active) return\n\n this.#activeJobs.delete(jobId)\n\n if (removeOnComplete === undefined || removeOnComplete === true) {\n return\n }\n\n this.#storeHistory(queue, 'completed', active.job, removeOnComplete)\n }\n\n async failJob(\n jobId: string,\n queue: string,\n error?: Error,\n removeOnFail?: JobRetention\n ): Promise<void> {\n const active = this.#activeJobs.get(jobId)\n if (!active) return\n\n this.#activeJobs.delete(jobId)\n\n if (removeOnFail === undefined || removeOnFail === true) {\n return\n }\n\n this.#storeHistory(queue, 'failed', active.job, removeOnFail, error)\n }\n\n async retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void> {\n const active = this.#activeJobs.get(jobId)\n if (!active) return\n\n this.#activeJobs.delete(jobId)\n\n const updatedJob = {\n ...active.job,\n attempts: (active.job.attempts || 0) + 1,\n }\n\n if (retryAt) {\n const delay = retryAt.getTime() - Date.now()\n\n if (delay > 0) {\n this.#schedulePush(queue, updatedJob, delay)\n return\n }\n }\n\n this.#enqueue(queue, updatedJob)\n }\n\n async recoverStalledJobs(\n queue: string,\n stalledThreshold: number,\n maxStalledCount: number\n ): Promise<number> {\n const now = Date.now()\n let recovered = 0\n\n for (const [jobId, active] of this.#activeJobs.entries()) {\n if (active.queue !== queue) {\n continue\n }\n\n const isStalled = now - active.acquiredAt > stalledThreshold\n\n if (!isStalled) {\n continue\n }\n\n const currentStalledCount = active.job.stalledCount ?? 0\n\n // Check if job has exceeded max stalled count\n if (currentStalledCount >= maxStalledCount) {\n // Fail permanently - just remove from active\n this.#activeJobs.delete(jobId)\n continue\n }\n\n // Recover the job - put back in queue with incremented stalledCount\n this.#activeJobs.delete(jobId)\n\n const updatedJob = {\n ...active.job,\n stalledCount: currentStalledCount + 1,\n }\n\n this.#enqueue(active.queue, updatedJob)\n recovered++\n }\n\n return recovered\n }\n\n async getJob(jobId: string, queue: string): Promise<JobRecord | null> {\n const active = this.#activeJobs.get(jobId)\n if (active && active.queue === queue) {\n return { status: 'active', data: active.job }\n }\n\n const pendingJobs = this.#queues.get(queue)\n const pending = pendingJobs?.find((job) => job.id === jobId)\n if (pending) {\n return { status: 'pending', data: pending }\n }\n\n const delayed = this.#delayedJobs.get(queue)?.get(jobId)\n if (delayed) {\n return { status: 'delayed', data: delayed.job }\n }\n\n const completed = this.#findHistory(this.#completedJobs, queue, jobId)\n if (completed) {\n return completed\n }\n\n const failed = this.#findHistory(this.#failedJobs, queue, jobId)\n if (failed) {\n return failed\n }\n\n return null\n }\n\n destroy(): Promise<void> {\n for (const timeout of this.#pendingTimeouts) {\n clearTimeout(timeout)\n }\n\n this.#pendingTimeouts.clear()\n\n return Promise.resolve()\n }\n\n async createSchedule(config: ScheduleConfig): Promise<string> {\n const id = config.id ?? randomUUID()\n const now = new Date()\n\n const schedule: ScheduleData = {\n id,\n name: config.name,\n payload: config.payload,\n cronExpression: config.cronExpression ?? null,\n everyMs: config.everyMs ?? null,\n timezone: config.timezone,\n from: config.from ?? null,\n to: config.to ?? null,\n limit: config.limit ?? null,\n runCount: 0,\n nextRunAt: null, // Will be calculated by the caller\n lastRunAt: null,\n status: 'active',\n createdAt: now,\n }\n\n this.#schedules.set(id, schedule)\n return id\n }\n\n async getSchedule(id: string): Promise<ScheduleData | null> {\n return this.#schedules.get(id) ?? null\n }\n\n async listSchedules(options?: ScheduleListOptions): Promise<ScheduleData[]> {\n const schedules = Array.from(this.#schedules.values())\n\n if (options?.status) {\n return schedules.filter((s) => s.status === options.status)\n }\n\n return schedules\n }\n\n async updateSchedule(\n id: string,\n updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>\n ): Promise<void> {\n const schedule = this.#schedules.get(id)\n if (!schedule) return\n\n if (updates.status !== undefined) schedule.status = updates.status\n if (updates.nextRunAt !== undefined) schedule.nextRunAt = updates.nextRunAt\n if (updates.lastRunAt !== undefined) schedule.lastRunAt = updates.lastRunAt\n if (updates.runCount !== undefined) schedule.runCount = updates.runCount\n }\n\n async deleteSchedule(id: string): Promise<void> {\n this.#schedules.delete(id)\n }\n\n async claimDueSchedule(): Promise<ScheduleData | null> {\n const now = new Date()\n\n // Find first due schedule\n const schedule = Array.from(this.#schedules.values()).find((s) => {\n if (s.status !== 'active') return false\n if (s.nextRunAt === null || s.nextRunAt > now) return false\n if (s.limit !== null && s.runCount >= s.limit) return false\n if (s.to !== null && now > s.to) return false\n return true\n })\n\n if (!schedule) return null\n\n // Calculate next run\n let nextRunAt: Date | null = null\n if (schedule.everyMs) {\n nextRunAt = new Date(now.getTime() + schedule.everyMs)\n } else if (schedule.cronExpression) {\n const cron = CronExpressionParser.parse(schedule.cronExpression, {\n currentDate: now,\n tz: schedule.timezone || 'UTC',\n })\n nextRunAt = cron.next().toDate()\n }\n\n // Check if limit will be reached after this run\n const newRunCount = schedule.runCount + 1\n if (schedule.limit !== null && newRunCount >= schedule.limit) {\n nextRunAt = null // No more runs\n }\n\n // Check if end date will be passed\n if (nextRunAt && schedule.to !== null && nextRunAt > schedule.to) {\n nextRunAt = null // Past end date\n }\n\n // Clone schedule data before updating (return old state)\n const claimedSchedule: ScheduleData = { ...schedule }\n\n // Update schedule atomically\n schedule.nextRunAt = nextRunAt\n schedule.lastRunAt = now\n schedule.runCount = newRunCount\n\n return claimedSchedule\n }\n\n #recordPush(queue: string, jobData: JobData, delay?: number) {\n this.#pushedJobs.push({\n queue,\n job: jobData,\n delay,\n pushedAt: Date.now(),\n })\n }\n\n #enqueue(queue: string, jobData: JobData) {\n if (!this.#queues.has(queue)) {\n this.#queues.set(queue, [])\n }\n\n this.#queues.get(queue)!.push(jobData)\n }\n\n #schedulePush(queue: string, jobData: JobData, delay: number) {\n if (!this.#delayedJobs.has(queue)) {\n this.#delayedJobs.set(queue, new Map())\n }\n\n const executeAt = Date.now() + delay\n this.#delayedJobs.get(queue)!.set(jobData.id, { job: jobData, executeAt, delay })\n\n const timeout = setTimeout(() => {\n this.#pendingTimeouts.delete(timeout)\n this.#delayedJobs.get(queue)?.delete(jobData.id)\n this.#enqueue(queue, jobData)\n }, delay)\n\n this.#pendingTimeouts.add(timeout)\n }\n\n #storeHistory(\n queue: string,\n status: 'completed' | 'failed',\n job: JobData,\n retention: JobRetention,\n error?: Error\n ) {\n const record: JobRecord = {\n status,\n data: job,\n finishedAt: Date.now(),\n error: error?.message,\n }\n\n const store = status === 'completed' ? this.#completedJobs : this.#failedJobs\n\n if (!store.has(queue)) {\n store.set(queue, [])\n }\n\n const records = store.get(queue)!\n records.push(record)\n\n if (retention && retention !== true) {\n this.#applyRetention(records, retention)\n }\n }\n\n #applyRetention(records: JobRecord[], retention: JobRetention) {\n if (retention === false || retention === true) {\n return\n }\n\n if (retention.age !== undefined) {\n const maxAgeMs = parse(retention.age)\n if (maxAgeMs > 0) {\n const cutoff = Date.now() - maxAgeMs\n const filtered = records.filter((record) => (record.finishedAt ?? 0) >= cutoff)\n records.splice(0, records.length, ...filtered)\n }\n }\n\n if (retention.count !== undefined && retention.count > 0 && records.length > retention.count) {\n records.splice(0, records.length - retention.count)\n }\n }\n\n #findHistory(store: Map<string, JobRecord[]>, queue: string, jobId: string): JobRecord | null {\n const records = store.get(queue)\n if (!records) return null\n\n return records.find((record) => record.data.id === jobId) ?? null\n }\n\n #matchesRecord(record: FakeJobRecord, matcher: FakeJobMatcher, query?: FakeJobQuery): boolean {\n if (query?.queue && record.queue !== query.queue) {\n return false\n }\n\n const matchesJob =\n typeof matcher === 'string'\n ? record.job.name === matcher\n : this.#isJobClass(matcher)\n ? record.job.name === this.#getJobClassName(matcher)\n : matcher(record.job)\n\n if (!matchesJob) {\n return false\n }\n\n if (query?.payload !== undefined) {\n const payloadMatcher = query.payload\n const matchesPayload =\n typeof payloadMatcher === 'function'\n ? payloadMatcher(record.job.payload)\n : isDeepStrictEqual(record.job.payload, payloadMatcher)\n\n if (!matchesPayload) {\n return false\n }\n }\n\n if (query?.delay !== undefined) {\n const delayMatcher = query.delay\n const matchesDelay =\n typeof delayMatcher === 'function'\n ? delayMatcher(record.delay)\n : record.delay === delayMatcher\n\n if (!matchesDelay) {\n return false\n }\n }\n\n return true\n }\n\n #formatFailure(prefix: string, matcher: FakeJobMatcher, query?: FakeJobQuery): string {\n const parts = [prefix]\n\n const matcherName = this.#getMatcherName(matcher)\n if (matcherName) {\n parts.push(`for \"${matcherName}\"`)\n }\n\n if (query?.queue) {\n parts.push(`on \"${query.queue}\"`)\n }\n\n if (query?.payload !== undefined) {\n parts.push('with matching payload')\n }\n\n if (query?.delay !== undefined) {\n parts.push('with matching delay')\n }\n\n const suffix = this.#pushedJobs.length\n ? `Pushed jobs: ${this.#pushedJobs.map((record) => record.job.name).join(', ')}`\n : 'Pushed jobs: none'\n\n return `${parts.join(' ')}. ${suffix}.`\n }\n\n #getMatcherName(matcher: FakeJobMatcher): string | undefined {\n if (typeof matcher === 'string') {\n return matcher\n }\n\n if (this.#isJobClass(matcher)) {\n return this.#getJobClassName(matcher)\n }\n\n return undefined\n }\n\n #isJobClass(matcher: FakeJobMatcher): matcher is JobClass {\n return typeof matcher === 'function' && matcher.prototype instanceof Job\n }\n\n #getJobClassName(JobClass: JobClass): string {\n return JobClass.options?.name || JobClass.name\n }\n}\n","import { debuglog } from 'node:util'\n\nexport default debuglog('boringnode:queue')\n","import debug from './debug.js'\nimport { randomUUID } from 'node:crypto'\nimport { QueueManager } from './queue_manager.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type { DispatchResult, Duration } from './types/main.js'\nimport { parse } from './utils.js'\n\n/**\n * Fluent builder for dispatching jobs to the queue.\n *\n * Provides a chainable API for configuring job options before dispatch.\n * Usually created via `Job.dispatch()` rather than directly.\n *\n * ```\n * Job.dispatch(payload)\n * .toQueue('emails') // optional: target queue\n * .priority(1) // optional: 1-10, lower = higher priority\n * .in('5m') // optional: delay before processing\n * .with('redis') // optional: specific adapter\n * .run() // dispatch the job\n * ```\n *\n * @typeParam T - The payload type for this job\n *\n * @example\n * ```typescript\n * // Simple dispatch (auto-runs via thenable)\n * await SendEmailJob.dispatch({ to: 'user@example.com', subject: 'Hello' })\n *\n * // With options\n * const jobId = await SendEmailJob.dispatch({ to: 'user@example.com' })\n * .toQueue('high-priority')\n * .priority(1)\n * .run()\n *\n * // Delayed job\n * await ReminderJob.dispatch({ userId: 123 }).in('24h')\n * ```\n */\nexport class JobDispatcher<T> {\n readonly #name: string\n readonly #payload: T\n #queue: string = 'default'\n #adapter?: string | (() => Adapter)\n #delay?: Duration\n #priority?: number\n #groupId?: string\n\n /**\n * Create a new job dispatcher.\n *\n * @param name - The job class name (used to locate the class at runtime)\n * @param payload - The data to pass to the job\n */\n constructor(name: string, payload: T) {\n this.#name = name\n this.#payload = payload\n }\n\n /**\n * Set the target queue for this job.\n *\n * @param queue - Queue name (default: 'default')\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await SendEmailJob.dispatch(payload).toQueue('emails')\n * ```\n */\n toQueue(queue: string): this {\n this.#queue = queue\n\n return this\n }\n\n /**\n * Delay the job execution.\n *\n * The job will be stored in a delayed state and moved to pending\n * after the delay expires.\n *\n * @param delay - Delay as milliseconds or duration string ('5s', '1h', '7d')\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * // Send reminder in 24 hours\n * await ReminderJob.dispatch(payload).in('24h')\n *\n * // Process in 5 minutes\n * await CleanupJob.dispatch(payload).in('5m')\n * ```\n */\n in(delay: Duration): this {\n this.#delay = delay\n\n return this\n }\n\n /**\n * Set the job priority.\n *\n * Lower numbers = higher priority. Jobs with lower priority values\n * are processed before jobs with higher values.\n *\n * @param priority - Priority level (1-10, default: 5)\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * // High priority job\n * await UrgentJob.dispatch(payload).priority(1)\n *\n * // Low priority job\n * await BackgroundJob.dispatch(payload).priority(10)\n * ```\n */\n priority(priority: number): this {\n this.#priority = priority\n\n return this\n }\n\n /**\n * Assign this job to a group.\n *\n * Jobs with the same groupId can be filtered and displayed together\n * in monitoring UIs. Useful for batch operations like newsletters\n * or bulk exports.\n *\n * @param groupId - Group identifier\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * // Group newsletter jobs together\n * await SendEmailJob.dispatch({ to: 'user@example.com' })\n * .group('newsletter-jan-2025')\n * .run()\n * ```\n */\n group(groupId: string): this {\n this.#groupId = groupId\n\n return this\n }\n\n /**\n * Use a specific adapter for this job.\n *\n * @param adapter - Adapter name or factory function\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * // Use named adapter\n * await Job.dispatch(payload).with('redis')\n *\n * // Use custom adapter instance\n * await Job.dispatch(payload).with(() => new CustomAdapter())\n * ```\n */\n with(adapter: string | (() => Adapter)) {\n this.#adapter = adapter\n\n return this\n }\n\n /**\n * Dispatch the job to the queue.\n *\n * @returns A DispatchResult containing the jobId\n *\n * @example\n * ```typescript\n * const { jobId } = await SendEmailJob.dispatch(payload).run()\n * console.log(`Dispatched job: ${jobId}`)\n * ```\n */\n async run(): Promise<DispatchResult> {\n const id = randomUUID()\n\n debug('dispatching job %s with id %s using payload %s', this.#name, id, this.#payload)\n\n const adapter = this.#getAdapterInstance()\n\n const payload = {\n id,\n name: this.#name,\n payload: this.#payload,\n attempts: 0,\n priority: this.#priority,\n groupId: this.#groupId,\n }\n\n if (this.#delay) {\n const parsedDelay = parse(this.#delay)\n\n await adapter.pushLaterOn(this.#queue, payload, parsedDelay)\n } else {\n await adapter.pushOn(this.#queue, payload)\n }\n\n return {\n jobId: id,\n }\n }\n\n /**\n * Thenable implementation for auto-dispatch when awaited.\n *\n * Allows `await Job.dispatch(payload)` without explicit `.run()`.\n *\n * @param onFulfilled - Success callback\n * @param onRejected - Error callback\n * @returns Promise resolving to the DispatchResult\n */\n then(\n onFulfilled?: (value: DispatchResult) => any,\n onRejected?: (reason: any) => any\n ): Promise<any> {\n return this.run().then(onFulfilled, onRejected)\n }\n\n #getAdapterInstance(): Adapter {\n if (!this.#adapter) {\n return QueueManager.use()\n }\n\n if (typeof this.#adapter === 'string') {\n return QueueManager.use(this.#adapter)\n }\n\n return this.#adapter()\n }\n}\n","import { Job } from './job.js'\nimport * as errors from './exceptions.js'\nimport type { JobClass } from './types/main.js'\nimport debug from './debug.js'\nimport { glob } from 'node:fs/promises'\nimport { resolve } from 'node:path'\n\n/**\n * Job class registry.\n *\n * The Locator maintains a mapping of job names to their classes,\n * allowing the Worker to instantiate jobs by name when processing.\n *\n * Jobs are typically registered automatically via `QueueManager.init()`\n * using the `locations` config option, but can also be registered manually.\n *\n * @example\n * ```typescript\n * import { Locator } from '@boringnode/queue'\n * import SendEmailJob from './jobs/send_email_job.js'\n *\n * // Manual registration\n * Locator.register('SendEmailJob', SendEmailJob)\n *\n * // Auto-registration via glob (used by QueueManager.init)\n * await Locator.registerFromGlob(['./jobs/**\\/*.js'])\n *\n * // Retrieve a job class\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * ```\n */\nclass LocatorSingleton {\n #registry = new Map<string, JobClass>()\n\n /**\n * Register a job class with a given name.\n *\n * @param name - The job name (usually the class name)\n * @param JobClass - The job class constructor\n *\n * @example\n * ```typescript\n * Locator.register('SendEmailJob', SendEmailJob)\n * ```\n */\n register<T extends Job>(name: string, JobClass: JobClass<T>) {\n debug('registering job: %s', name)\n\n this.#registry.set(name, JobClass)\n }\n\n /**\n * Auto-register job classes from files matching glob patterns.\n *\n * Each file should have a default export that is a Job class.\n * The class name is used as the registration name.\n *\n * @param patterns - Glob patterns to match job files\n * @returns Number of jobs successfully registered\n *\n * @example\n * ```typescript\n * const count = await Locator.registerFromGlob([\n * './jobs/**\\/*.js',\n * './app/jobs/**\\/*.ts'\n * ])\n * console.log(`Registered ${count} jobs`)\n * ```\n */\n async registerFromGlob(patterns: string[]): Promise<number> {\n let registered = 0\n\n for (const pattern of patterns) {\n debug('registering jobs from glob pattern: %s', pattern)\n for await (const file of glob(pattern)) {\n debug('found job file: %s', file)\n\n try {\n const absolutePath = resolve(file)\n const module = await import(`file://${absolutePath}`)\n const JobClass = module.default as JobClass\n\n if (JobClass && typeof JobClass === 'function') {\n const jobName = JobClass.options?.name || JobClass.name\n this.register(jobName, JobClass)\n registered++\n }\n } catch (error) {\n console.warn(`Failed to load job from ${file}:`, error)\n }\n }\n }\n\n return registered\n }\n\n /**\n * Get a job class by name.\n *\n * @param name - The job name to look up\n * @returns The job class, or undefined if not found\n *\n * @example\n * ```typescript\n * const JobClass = Locator.get('SendEmailJob')\n * if (JobClass) {\n * const instance = new JobClass(payload)\n * }\n * ```\n */\n get<T extends Job = Job>(name: string): JobClass<T> | undefined {\n return this.#registry.get(name) as JobClass<T> | undefined\n }\n\n /**\n * Get a job class by name, throwing if not found.\n *\n * @param name - The job name to look up\n * @returns The job class\n * @throws {E_JOB_NOT_FOUND} If the job is not registered\n *\n * @example\n * ```typescript\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * const instance = new JobClass(payload)\n * ```\n */\n getOrThrow<T extends Job = Job>(name: string): JobClass<T> {\n const JobClass = this.get<T>(name)\n\n if (!JobClass) {\n throw new errors.E_JOB_NOT_FOUND([name])\n }\n\n return JobClass\n }\n\n /**\n * Remove all registered jobs.\n *\n * Primarily useful for testing.\n */\n clear(): void {\n this.#registry.clear()\n }\n}\n\n/** Global job class registry singleton */\nexport const Locator = new LocatorSingleton()\n","export interface LogObject {\n [key: string]: unknown\n}\n\nexport interface ErrorObject extends LogObject {\n err?: Error\n}\n\nexport interface Logger {\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n\n child(obj: LogObject): Logger\n}\n\n/**\n * A simple logger that writes to console.\n */\nclass ConsoleLogger implements Logger {\n #prefix: string\n\n constructor(prefix: string = 'queue') {\n this.#prefix = prefix\n }\n\n #format(level: string, msgOrObj: string | LogObject, msg?: string): [string, LogObject?] {\n const prefix = `[${this.#prefix}] ${level}:`\n\n if (typeof msgOrObj === 'object') {\n return [`${prefix} ${msg}`, msgOrObj]\n }\n\n return [`${prefix} ${msgOrObj}`]\n }\n\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n trace(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('TRACE', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n debug(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('DEBUG', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n info(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('INFO', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n warn(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('WARN', msgOrObj, msg)\n\n if (obj) {\n return console.warn(message, obj)\n }\n\n console.warn(message)\n }\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n error(msgOrObj: string | ErrorObject, msg?: string): void {\n const [message, obj] = this.#format('ERROR', msgOrObj, msg)\n\n if (obj) {\n return console.error(message, obj)\n }\n\n console.error(message)\n }\n\n child(obj: LogObject): Logger {\n const childPrefix = obj.pkg ? String(obj.pkg) : this.#prefix\n return new ConsoleLogger(childPrefix)\n }\n}\n\nexport const consoleLogger = new ConsoleLogger()\n","import * as errors from './exceptions.js'\nimport debug from './debug.js'\nimport { Locator } from './locator.js'\nimport { consoleLogger, type Logger } from './logger.js'\nimport { FakeAdapter } from './drivers/fake_adapter.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type {\n AdapterFactory,\n JobFactory,\n JobOptions,\n QueueConfig,\n QueueManagerConfig,\n RetryConfig,\n} from './types/main.js'\n\ntype QueueManagerFakeState = {\n defaultAdapter: string\n adapters: Record<string, AdapterFactory>\n adapterInstances: Map<string, Adapter>\n globalRetryConfig?: RetryConfig\n globalJobOptions?: JobOptions\n queueConfigs: Map<string, QueueConfig>\n logger: Logger\n jobFactory?: JobFactory\n fakeAdapter: FakeAdapter\n}\n\n/**\n * Central configuration and adapter management for the queue system.\n *\n * The QueueManager is responsible for:\n * - Initializing adapters and job registration\n * - Providing adapter instances to workers and dispatchers\n * - Managing retry configuration across global, queue, and job levels\n *\n * @example\n * ```typescript\n * import { QueueManager, redis } from '@boringnode/queue'\n *\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis({ host: 'localhost' }),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * retry: {\n * maxRetries: 3,\n * backoff: exponentialBackoff(),\n * },\n * })\n *\n * // Get the default adapter\n * const adapter = QueueManager.use()\n *\n * // Clean up when done\n * await QueueManager.destroy()\n * ```\n */\nclass QueueManagerSingleton {\n #initialized = false\n #defaultAdapter!: string\n #adapters: Record<string, AdapterFactory> = {}\n #adapterInstances: Map<string, Adapter> = new Map()\n #globalRetryConfig?: RetryConfig\n #globalJobOptions?: JobOptions\n #queueConfigs: Map<string, QueueConfig> = new Map()\n #logger: Logger = consoleLogger\n #jobFactory?: JobFactory\n #fakeState?: QueueManagerFakeState\n\n /**\n * Initialize the queue system with the given configuration.\n *\n * This must be called before using the queue system. It:\n * - Validates the configuration\n * - Registers adapters\n * - Auto-discovers and registers job classes from `locations`\n *\n * @param config - The queue configuration\n * @returns This instance for chaining\n * @throws {E_CONFIGURATION_ERROR} If the configuration is invalid\n *\n * @example\n * ```typescript\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis(),\n * postgres: knex(pgConfig),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * })\n * ```\n */\n async init(config: QueueManagerConfig) {\n debug('initializing queue manager with config: %O', config)\n\n this.#validateConfig(config)\n\n this.#adapterInstances.clear()\n\n this.#defaultAdapter = config.default\n this.#adapters = config.adapters\n this.#globalRetryConfig = config.retry\n this.#globalJobOptions = config.defaultJobOptions\n this.#logger = config.logger ?? consoleLogger\n this.#jobFactory = config.jobFactory\n\n if (config.queues) {\n for (const [queue, queueConfig] of Object.entries(config.queues)) {\n this.#queueConfigs.set(queue, queueConfig as QueueConfig)\n }\n }\n\n if (config.locations && config.locations.length > 0) {\n const registered = await Locator.registerFromGlob(config.locations)\n\n if (registered === 0) {\n this.#logger.warn(\n `No jobs found for locations: ${config.locations.join(', ')}. ` +\n 'Verify your glob patterns match your job files.'\n )\n }\n }\n\n this.#initialized = true\n\n return this\n }\n\n /**\n * Get an adapter instance by name.\n *\n * Adapter instances are cached and reused. If no name is provided,\n * the default adapter is returned.\n *\n * @param adapter - Adapter name (optional, defaults to the default adapter)\n * @returns The adapter instance\n * @throws {E_QUEUE_NOT_INITIALIZED} If `init()` hasn't been called\n * @throws {E_CONFIGURATION_ERROR} If the adapter is not registered\n * @throws {E_ADAPTER_INIT_ERROR} If the adapter factory throws\n *\n * @example\n * ```typescript\n * // Get default adapter\n * const adapter = QueueManager.use()\n *\n * // Get specific adapter\n * const redisAdapter = QueueManager.use('redis')\n * ```\n */\n use(adapter?: string): Adapter {\n if (!this.#initialized) {\n throw new errors.E_QUEUE_NOT_INITIALIZED()\n }\n\n if (!adapter) {\n adapter = this.#defaultAdapter\n }\n\n // Return cached instance if exists\n const cached = this.#adapterInstances.get(adapter)\n if (cached) {\n return cached\n }\n\n const adapterFactory = this.#adapters[adapter]\n\n if (!adapterFactory) {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${adapter}\" is not registered`])\n }\n\n debug('using adapter \"%s\"', adapter)\n\n try {\n const instance = adapterFactory()\n this.#adapterInstances.set(adapter, instance)\n return instance\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new errors.E_ADAPTER_INIT_ERROR([adapter, message], { cause: error })\n }\n }\n\n /**\n * Replace all adapters with a fake adapter for testing.\n *\n * The fake adapter records pushed jobs and exposes assertion helpers.\n * Call `restore()` to return to the previous configuration.\n *\n * @returns The fake adapter instance for assertions\n * @throws {E_QUEUE_NOT_INITIALIZED} If `init()` hasn't been called\n *\n * @example\n * ```typescript\n * const fake = QueueManager.fake()\n *\n * await SendEmailJob.dispatch({ to: 'user@example.com' })\n *\n * fake.assertPushed(SendEmailJob)\n * QueueManager.restore()\n * ```\n */\n fake(): FakeAdapter {\n if (!this.#initialized) {\n throw new errors.E_QUEUE_NOT_INITIALIZED()\n }\n\n if (this.#fakeState) {\n return this.#fakeState.fakeAdapter\n }\n\n const fakeAdapter = new FakeAdapter()\n\n this.#fakeState = {\n defaultAdapter: this.#defaultAdapter,\n adapters: this.#adapters,\n adapterInstances: this.#adapterInstances,\n globalRetryConfig: this.#globalRetryConfig,\n globalJobOptions: this.#globalJobOptions,\n queueConfigs: this.#queueConfigs,\n logger: this.#logger,\n jobFactory: this.#jobFactory,\n fakeAdapter,\n }\n\n const fakeFactory = () => fakeAdapter\n const nextAdapters: Record<string, AdapterFactory> = {}\n\n for (const name of Object.keys(this.#fakeState.adapters)) {\n nextAdapters[name] = fakeFactory\n }\n\n this.#adapters = nextAdapters\n this.#adapterInstances = new Map()\n\n return fakeAdapter\n }\n\n /**\n * Restore adapters after calling `fake()`.\n */\n restore(): void {\n if (!this.#fakeState) {\n return\n }\n\n void this.#fakeState.fakeAdapter.destroy()\n\n for (const adapter of this.#adapterInstances.values()) {\n void adapter.destroy()\n }\n\n const state = this.#fakeState\n this.#fakeState = undefined\n\n this.#defaultAdapter = state.defaultAdapter\n this.#adapters = state.adapters\n this.#adapterInstances = state.adapterInstances\n this.#globalRetryConfig = state.globalRetryConfig\n this.#globalJobOptions = state.globalJobOptions\n this.#queueConfigs = state.queueConfigs\n this.#logger = state.logger\n this.#jobFactory = state.jobFactory\n }\n\n /**\n * Get the merged retry configuration for a job.\n *\n * Configuration is merged with priority: job > queue > global.\n * This allows specific jobs or queues to override global defaults.\n *\n * @param queue - The queue name\n * @param jobRetryConfig - Optional job-level retry config\n * @returns The merged retry configuration\n *\n * @example\n * ```typescript\n * // Global: maxRetries=3, Queue: maxRetries=5, Job: maxRetries=1\n * // Result: maxRetries=1 (job wins)\n * const config = QueueManager.getMergedRetryConfig('emails', { maxRetries: 1 })\n * ```\n */\n getMergedRetryConfig(queue: string, jobRetryConfig?: RetryConfig): RetryConfig {\n const queueConfig = this.#queueConfigs.get(queue)\n const queueRetryConfig = queueConfig?.retry || {}\n\n let maxRetries =\n jobRetryConfig?.maxRetries ??\n queueRetryConfig.maxRetries ??\n this.#globalRetryConfig?.maxRetries ??\n 0\n\n let backoff =\n jobRetryConfig?.backoff || queueRetryConfig.backoff || this.#globalRetryConfig?.backoff\n\n return { maxRetries, backoff }\n }\n\n /**\n * Get the configured job factory for custom instantiation.\n *\n * @returns The job factory function, or undefined if not configured\n */\n getJobFactory(): JobFactory | undefined {\n return this.#jobFactory\n }\n\n /**\n * Get the merged job options for a job (priority: job > queue > global).\n */\n getMergedJobOptions(queue: string, jobOptions?: JobOptions): JobOptions {\n const queueConfig = this.#queueConfigs.get(queue)\n const queueJobOptions = queueConfig?.defaultJobOptions\n\n return {\n removeOnComplete:\n jobOptions?.removeOnComplete ??\n queueJobOptions?.removeOnComplete ??\n this.#globalJobOptions?.removeOnComplete,\n removeOnFail:\n jobOptions?.removeOnFail ??\n queueJobOptions?.removeOnFail ??\n this.#globalJobOptions?.removeOnFail,\n }\n }\n\n #validateConfig(config: QueueManagerConfig): void {\n if (!config.adapters || Object.keys(config.adapters).length === 0) {\n throw new errors.E_CONFIGURATION_ERROR(['At least one adapter must be configured'])\n }\n\n if (!config.default) {\n throw new errors.E_CONFIGURATION_ERROR(['Default adapter must be specified'])\n }\n\n if (!config.adapters[config.default]) {\n throw new errors.E_CONFIGURATION_ERROR([\n `Default adapter \"${config.default}\" not found in adapters configuration`,\n ])\n }\n\n for (const [name, factory] of Object.entries(config.adapters)) {\n if (typeof factory !== 'function') {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${name}\" must be a factory function`])\n }\n }\n }\n\n /**\n * Clean up all adapter instances and reset state.\n *\n * Call this when shutting down the application or when\n * you need to reinitialize with a new configuration.\n *\n * @example\n * ```typescript\n * // On application shutdown\n * await QueueManager.destroy()\n * ```\n */\n async destroy() {\n for (const [name, adapter] of this.#adapterInstances) {\n debug('destroying adapter \"%s\"', name)\n await adapter.destroy()\n }\n\n if (this.#fakeState) {\n await this.#fakeState.fakeAdapter.destroy()\n\n for (const [name, adapter] of this.#fakeState.adapterInstances) {\n debug('destroying adapter \"%s\"', name)\n await adapter.destroy()\n }\n }\n\n this.#adapterInstances.clear()\n this.#initialized = false\n this.#fakeState = undefined\n }\n}\n\n/** Global queue manager singleton */\nexport const QueueManager = new QueueManagerSingleton()\n","import debug from './debug.js'\nimport { randomUUID } from 'node:crypto'\nimport { QueueManager } from './queue_manager.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type { DispatchManyResult } from './types/main.js'\n\n/**\n * Fluent builder for dispatching multiple jobs to the queue in a single batch.\n *\n * Provides a chainable API for configuring job options before dispatch.\n * Usually created via `Job.dispatchMany()` rather than directly.\n *\n * ```\n * Job.dispatchMany(payloads)\n * .toQueue('emails') // optional: target queue\n * .priority(1) // optional: 1-10, lower = higher priority\n * .group('batch-123') // optional: group all jobs together\n * .with('redis') // optional: specific adapter\n * .run() // dispatch all jobs\n * ```\n *\n * @typeParam T - The payload type for these jobs\n *\n * @example\n * ```typescript\n * // Batch dispatch for newsletter\n * const { jobIds } = await SendEmailJob.dispatchMany([\n * { to: 'user1@example.com', subject: 'Newsletter' },\n * { to: 'user2@example.com', subject: 'Newsletter' },\n * ])\n * .group('newsletter-jan-2025')\n * .toQueue('emails')\n * .run()\n *\n * console.log(`Dispatched ${jobIds.length} jobs`)\n * ```\n */\nexport class JobBatchDispatcher<T> {\n readonly #name: string\n readonly #payloads: T[]\n #queue: string = 'default'\n #adapter?: string | (() => Adapter)\n #priority?: number\n #groupId?: string\n\n /**\n * Create a new batch job dispatcher.\n *\n * @param name - The job class name (used to locate the class at runtime)\n * @param payloads - Array of data to pass to each job\n */\n constructor(name: string, payloads: T[]) {\n this.#name = name\n this.#payloads = payloads\n }\n\n /**\n * Set the target queue for all jobs.\n *\n * @param queue - Queue name (default: 'default')\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await SendEmailJob.dispatchMany(payloads).toQueue('emails')\n * ```\n */\n toQueue(queue: string): this {\n this.#queue = queue\n\n return this\n }\n\n /**\n * Set the priority for all jobs.\n *\n * Lower numbers = higher priority. Jobs with lower priority values\n * are processed before jobs with higher values.\n *\n * @param priority - Priority level (1-10, default: 5)\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await UrgentJob.dispatchMany(payloads).priority(1)\n * ```\n */\n priority(priority: number): this {\n this.#priority = priority\n\n return this\n }\n\n /**\n * Assign all jobs to a group.\n *\n * Jobs with the same groupId can be filtered and displayed together\n * in monitoring UIs. Useful for batch operations like newsletters\n * or bulk exports.\n *\n * @param groupId - Group identifier\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await SendEmailJob.dispatchMany(recipients)\n * .group('newsletter-jan-2025')\n * .run()\n * ```\n */\n group(groupId: string): this {\n this.#groupId = groupId\n\n return this\n }\n\n /**\n * Use a specific adapter for these jobs.\n *\n * @param adapter - Adapter name or factory function\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await Job.dispatchMany(payloads).with('redis')\n * ```\n */\n with(adapter: string | (() => Adapter)) {\n this.#adapter = adapter\n\n return this\n }\n\n /**\n * Dispatch all jobs to the queue.\n *\n * @returns A DispatchManyResult containing all jobIds\n *\n * @example\n * ```typescript\n * const { jobIds } = await SendEmailJob.dispatchMany(payloads).run()\n * console.log(`Dispatched ${jobIds.length} jobs`)\n * ```\n */\n async run(): Promise<DispatchManyResult> {\n debug('dispatching %d jobs of type %s', this.#payloads.length, this.#name)\n\n const adapter = this.#getAdapterInstance()\n\n const jobs = this.#payloads.map((payload) => ({\n id: randomUUID(),\n name: this.#name,\n payload,\n attempts: 0,\n priority: this.#priority,\n groupId: this.#groupId,\n }))\n\n await adapter.pushManyOn(this.#queue, jobs)\n\n return {\n jobIds: jobs.map((job) => job.id),\n }\n }\n\n /**\n * Thenable implementation for auto-dispatch when awaited.\n *\n * Allows `await Job.dispatchMany(payloads)` without explicit `.run()`.\n *\n * @param onFulfilled - Success callback\n * @param onRejected - Error callback\n * @returns Promise resolving to the DispatchManyResult\n */\n then(\n onFulfilled?: (value: DispatchManyResult) => any,\n onRejected?: (reason: any) => any\n ): Promise<any> {\n return this.run().then(onFulfilled, onRejected)\n }\n\n #getAdapterInstance(): Adapter {\n if (!this.#adapter) {\n return QueueManager.use()\n }\n\n if (typeof this.#adapter === 'string') {\n return QueueManager.use(this.#adapter)\n }\n\n return this.#adapter()\n }\n}\n","import type { Duration, ScheduleConfig, ScheduleResult } from './types/main.js'\nimport { QueueManager } from './queue_manager.js'\nimport { parse } from './utils.js'\nimport { CronExpressionParser } from 'cron-parser'\nimport * as errors from './exceptions.js'\n\n/**\n * Fluent builder for creating job schedules.\n *\n * @example\n * ```typescript\n * // Create with cron\n * const { scheduleId } = await new ScheduleBuilder('CleanupJob', { days: 30 })\n * .id('cleanup-daily')\n * .cron('0 0 * * *')\n * .timezone('Europe/Paris')\n * .run()\n *\n * // Create with interval\n * const { scheduleId } = await new ScheduleBuilder('SyncJob', {})\n * .every('5m')\n * .run()\n * ```\n */\nexport class ScheduleBuilder implements PromiseLike<ScheduleResult> {\n #name: string\n #payload: any\n #id?: string\n #cronExpression?: string\n #everyMs?: number\n #timezone: string = 'UTC'\n #from?: Date\n #to?: Date\n #limit?: number\n\n constructor(name: string, payload: any) {\n this.#name = name\n this.#payload = payload\n }\n\n /**\n * Set a custom schedule ID.\n * If not specified, defaults to the job name.\n * If a schedule with this ID exists, it will be updated (upsert).\n */\n id(scheduleId: string): this {\n this.#id = scheduleId\n return this\n }\n\n /**\n * Set a cron expression for the schedule.\n * Mutually exclusive with `every()`.\n */\n cron(expression: string): this {\n this.#cronExpression = expression\n return this\n }\n\n /**\n * Set a repeating interval for the schedule.\n * Mutually exclusive with `cron()`.\n */\n every(interval: Duration): this {\n this.#everyMs = parse(interval)\n return this\n }\n\n /**\n * Set the timezone for cron evaluation.\n * @default 'UTC'\n */\n timezone(tz: string): this {\n this.#timezone = tz\n return this\n }\n\n /**\n * Set the start boundary for the schedule.\n * No jobs will be dispatched before this date.\n */\n from(date: Date): this {\n this.#from = date\n return this\n }\n\n /**\n * Set the end boundary for the schedule.\n * No jobs will be dispatched after this date.\n */\n to(date: Date): this {\n this.#to = date\n return this\n }\n\n /**\n * Set both start and end boundaries for the schedule.\n * Shorthand for `.from(start).to(end)`.\n */\n between(from: Date, to: Date): this {\n return this.from(from).to(to)\n }\n\n /**\n * Set the maximum number of runs for this schedule.\n */\n limit(maxRuns: number): this {\n this.#limit = maxRuns\n return this\n }\n\n /**\n * Create the schedule and return the schedule ID.\n */\n async run(): Promise<ScheduleResult> {\n // Validation\n if (!this.#cronExpression && !this.#everyMs) {\n throw new errors.E_INVALID_SCHEDULE_CONFIG([\n 'Schedule must have either a cron expression or an interval',\n ])\n }\n\n if (this.#cronExpression && this.#everyMs) {\n throw new errors.E_INVALID_SCHEDULE_CONFIG([\n 'Schedule cannot have both a cron expression and an interval',\n ])\n }\n\n // Validate cron expression\n if (this.#cronExpression) {\n try {\n CronExpressionParser.parse(this.#cronExpression, { tz: this.#timezone })\n } catch (error) {\n throw new errors.E_INVALID_CRON_EXPRESSION(\n [this.#cronExpression, (error as Error).message],\n {\n cause: error,\n }\n )\n }\n }\n\n const config: ScheduleConfig = {\n id: this.#id ?? this.#name,\n name: this.#name,\n payload: this.#payload,\n cronExpression: this.#cronExpression,\n everyMs: this.#everyMs,\n timezone: this.#timezone,\n from: this.#from,\n to: this.#to,\n limit: this.#limit,\n }\n\n const adapter = QueueManager.use()\n const scheduleId = await adapter.createSchedule(config)\n\n // Calculate and set nextRunAt\n const nextRunAt = this.#calculateNextRunAt()\n await adapter.updateSchedule(scheduleId, { nextRunAt })\n\n return { scheduleId }\n }\n\n /**\n * Calculate the next run time based on cron or interval.\n */\n #calculateNextRunAt(): Date {\n const now = new Date()\n let nextRun: Date\n\n if (this.#cronExpression) {\n const cron = CronExpressionParser.parse(this.#cronExpression, {\n currentDate: now,\n tz: this.#timezone,\n })\n nextRun = cron.next().toDate()\n } else {\n // Interval-based: next run is now + interval\n nextRun = new Date(now.getTime() + this.#everyMs!)\n }\n\n // Respect from boundary\n if (this.#from && nextRun < this.#from) {\n if (this.#cronExpression) {\n // Recalculate from the start boundary\n const cron = CronExpressionParser.parse(this.#cronExpression, {\n currentDate: this.#from,\n tz: this.#timezone,\n })\n nextRun = cron.next().toDate()\n } else {\n nextRun = this.#from\n }\n }\n\n return nextRun\n }\n\n /**\n * Implement PromiseLike to allow `await builder.every('5m')` syntax.\n */\n then<TResult1 = ScheduleResult, TResult2 = never>(\n onfulfilled?: ((value: ScheduleResult) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.run().then(onfulfilled, onrejected)\n }\n}\n","import { JobDispatcher } from './job_dispatcher.js'\nimport { JobBatchDispatcher } from './job_batch_dispatcher.js'\nimport { ScheduleBuilder } from './schedule_builder.js'\nimport type { JobContext, JobOptions } from './types/main.js'\n\n/**\n * Abstract base class for all queue jobs.\n *\n * Extend this class to create your own jobs. Each job must implement\n * the `execute()` method which contains the job's business logic.\n *\n * The constructor is reserved for dependency injection. Payload and context\n * are provided separately via the `$hydrate()` method (called by the worker).\n *\n * @typeParam Payload - The type of data this job receives\n *\n * @example\n * ```typescript\n * import { Job } from '@boringnode/queue'\n *\n * interface SendEmailPayload {\n * to: string\n * subject: string\n * body: string\n * }\n *\n * export default class SendEmailJob extends Job<SendEmailPayload> {\n * static options = {\n * queue: 'emails',\n * maxRetries: 3,\n * }\n *\n * // Constructor is for dependency injection only\n * constructor(private mailer: MailerService) {\n * super()\n * }\n *\n * async execute() {\n * console.log(`Attempt ${this.context.attempt} for job ${this.context.jobId}`)\n * await this.mailer.send(this.payload.to, this.payload.subject, this.payload.body)\n * }\n *\n * async failed(error: Error) {\n * console.error(`Failed to send email to ${this.payload.to}:`, error)\n * }\n * }\n * ```\n */\nexport abstract class Job<Payload = any> {\n #payload!: Payload\n #context!: JobContext\n #signal?: AbortSignal\n\n /**\n * Static options for this job class.\n *\n * Override this property in subclasses to configure job behavior\n * such as queue name, retry policy, timeout, and more.\n *\n * @example\n * ```typescript\n * class SendEmailJob extends Job<SendEmailPayload> {\n * static options = {\n * queue: 'emails',\n * maxRetries: 3,\n * timeout: '30s',\n * }\n * }\n * ```\n */\n static options: JobOptions = {}\n\n /**\n * The payload data passed to this job instance.\n *\n * Contains the data provided when the job was dispatched.\n * Available after the job has been hydrated by the worker.\n *\n * @example\n * ```typescript\n * async execute() {\n * const { to, subject, body } = this.payload\n * await sendEmail(to, subject, body)\n * }\n * ```\n */\n get payload(): Payload {\n return this.#payload\n }\n\n /**\n * Context information for the current job execution.\n *\n * Provides metadata such as job ID, current attempt number,\n * queue name, priority, and timing information.\n *\n * @example\n * ```typescript\n * async execute() {\n * if (this.context.attempt > 1) {\n * console.log(`Retry attempt ${this.context.attempt}`)\n * }\n * console.log(`Processing job ${this.context.jobId} on queue ${this.context.queue}`)\n * }\n * ```\n */\n get context(): JobContext {\n return this.#context\n }\n\n /**\n * The abort signal for timeout handling.\n *\n * Check `signal.aborted` in long-running operations to handle timeouts gracefully.\n *\n * @example\n * ```typescript\n * async execute() {\n * for (const item of this.payload.items) {\n * if (this.signal?.aborted) {\n * throw new Error('Job timed out')\n * }\n * await processItem(item)\n * }\n * }\n * ```\n */\n get signal(): AbortSignal | undefined {\n return this.#signal\n }\n\n /**\n * Hydrate the job with payload, context, and optional abort signal.\n *\n * This method is called by the worker after instantiation to provide\n * the job's runtime data. It should not be called directly by user code.\n *\n * @param payload - The data to be processed by this job\n * @param context - The job execution context\n * @param signal - Optional abort signal for timeout handling\n *\n * @internal\n */\n $hydrate(payload: Payload, context: JobContext, signal?: AbortSignal): void {\n this.#payload = payload\n this.#context = Object.freeze(context)\n this.#signal = signal\n }\n\n /**\n * Dispatch this job to the queue.\n *\n * Returns a JobDispatcher for fluent configuration before dispatching.\n * The job is not actually dispatched until `.run()` is called or the\n * dispatcher is awaited.\n *\n * @param payload - The data to pass to the job\n * @returns A JobDispatcher for fluent configuration\n *\n * @example\n * ```typescript\n * // Simple dispatch\n * await SendEmailJob.dispatch({ to: 'user@example.com', subject: 'Hello' })\n *\n * // With options\n * await SendEmailJob.dispatch({ to: 'user@example.com' })\n * .toQueue('high-priority')\n * .priority(1)\n * .in('5m')\n * .run()\n * ```\n */\n static dispatch<T extends Job>(\n this: new (...args: any[]) => T,\n payload: T extends Job<infer P> ? P : never\n ): JobDispatcher<T extends Job<infer P> ? P : never> {\n const options = (this as any).options || {}\n const jobName = options.name || this.name\n\n const dispatcher = new JobDispatcher<T extends Job<infer P> ? P : never>(jobName, payload)\n\n if (options.queue) {\n dispatcher.toQueue(options.queue)\n }\n\n if (options.adapter) {\n dispatcher.with(options.adapter)\n }\n\n if (options.priority !== undefined) {\n dispatcher.priority(options.priority)\n }\n\n return dispatcher\n }\n\n /**\n * Dispatch multiple jobs to the queue in a single batch.\n *\n * Returns a JobBatchDispatcher for fluent configuration before dispatching.\n * The jobs are not actually dispatched until `.run()` is called or the\n * dispatcher is awaited.\n *\n * This is more efficient than calling `dispatch()` multiple times as it\n * uses batched operations (e.g., Redis pipeline, SQL batch insert).\n *\n * @param payloads - Array of data to pass to each job\n * @returns A JobBatchDispatcher for fluent configuration\n *\n * @example\n * ```typescript\n * // Batch dispatch for newsletter\n * const { jobIds } = await SendEmailJob.dispatchMany([\n * { to: 'user1@example.com', subject: 'Newsletter' },\n * { to: 'user2@example.com', subject: 'Newsletter' },\n * ])\n * .group('newsletter-jan-2025')\n * .toQueue('emails')\n * .run()\n *\n * console.log(`Dispatched ${jobIds.length} jobs`)\n * ```\n */\n static dispatchMany<T extends Job>(\n this: new (...args: any[]) => T,\n payloads: (T extends Job<infer P> ? P : never)[]\n ): JobBatchDispatcher<T extends Job<infer P> ? P : never> {\n const options = (this as any).options || {}\n const jobName = options.name || this.name\n\n const dispatcher = new JobBatchDispatcher<T extends Job<infer P> ? P : never>(jobName, payloads)\n\n if (options.queue) {\n dispatcher.toQueue(options.queue)\n }\n\n if (options.adapter) {\n dispatcher.with(options.adapter)\n }\n\n if (options.priority !== undefined) {\n dispatcher.priority(options.priority)\n }\n\n return dispatcher\n }\n\n /**\n * Create a schedule for this job.\n *\n * Returns a ScheduleBuilder for fluent configuration before creating the schedule.\n * The schedule is not actually created until `.run()` is called or the\n * builder is awaited.\n *\n * @param payload - The data to pass to the job on each run\n * @returns A ScheduleBuilder for fluent configuration\n *\n * @example\n * ```typescript\n * // Cron schedule\n * await CleanupJob.schedule({ days: 30 })\n * .id('cleanup-daily')\n * .cron('0 0 * * *')\n * .timezone('Europe/Paris')\n * .run()\n *\n * // Interval schedule\n * await SyncJob.schedule({ source: 'api' })\n * .every('5m')\n * .run()\n * ```\n */\n static schedule<T extends Job>(\n this: new (...args: any[]) => T,\n payload: T extends Job<infer P> ? P : never\n ): ScheduleBuilder {\n const options = (this as any).options || {}\n const jobName = options.name || this.name\n\n return new ScheduleBuilder(jobName, payload)\n }\n\n /**\n * Execute the job's business logic.\n *\n * This method is called by the worker when processing the job.\n * Implement your job's logic here.\n *\n * For timeout handling, use `this.signal` which is available after hydration.\n *\n * @throws Any error thrown will trigger retry logic (if configured)\n *\n * @example\n * ```typescript\n * async execute() {\n * for (const item of this.payload.items) {\n * if (this.signal?.aborted) {\n * throw new Error('Job timed out')\n * }\n * await processItem(item)\n * }\n * }\n * ```\n */\n abstract execute(): Promise<void>\n\n /**\n * Called when the job has permanently failed (after all retries exhausted).\n *\n * Use this hook for cleanup, logging, or notifications.\n * This is optional - implement only if you need failure handling.\n *\n * @param error - The error that caused the final failure\n *\n * @example\n * ```typescript\n * async failed(error: Error) {\n * await notifyAdmin(`Job failed: ${error.message}`)\n * await cleanup(this.payload)\n * }\n * ```\n */\n failed?(error: Error): Promise<void>\n}\n"],"mappings":"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","names":["randomUUID","CronExpressionParser","randomUUID","randomUUID","randomUUID","CronExpressionParser"]}
package/build/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  QueueManager,
7
7
  ScheduleBuilder,
8
8
  debug_default
9
- } from "./chunk-RO6VVBGK.js";
9
+ } from "./chunk-H6WOFLPJ.js";
10
10
  import {
11
11
  DEFAULT_ERROR_RETRY_DELAY,
12
12
  DEFAULT_IDLE_DELAY,
@@ -288,8 +288,15 @@ var Worker = class {
288
288
  yield { type: "idle", suggestedDelay: this.#idleDelay };
289
289
  continue;
290
290
  }
291
- const completed = await this.#pool.waitForNextCompletion();
292
- yield { type: "completed", queue: completed.queue, job: completed.job };
291
+ const hasCapacity = this.#pool.hasCapacity(this.#concurrency);
292
+ const result = await Promise.race([
293
+ this.#pool.waitForNextCompletion().then((completed) => ({ kind: "completed", completed })),
294
+ ...hasCapacity ? [setTimeout(this.#idleDelay).then(() => ({ kind: "tick" }))] : []
295
+ ]);
296
+ if (result.kind === "tick") {
297
+ continue;
298
+ }
299
+ yield { type: "completed", queue: result.completed.queue, job: result.completed.job };
293
300
  } catch (error) {
294
301
  yield {
295
302
  type: "error",
@@ -345,7 +352,7 @@ var Worker = class {
345
352
  mergedConfig.maxRetries
346
353
  );
347
354
  await this.#adapter.failJob(job.id, queue, e, retention.removeOnFail);
348
- const exception = new E_JOB_MAX_ATTEMPTS_REACHED([job.name]);
355
+ const exception = new E_JOB_MAX_ATTEMPTS_REACHED([job.name], { cause: e });
349
356
  await instance.failed?.(exception);
350
357
  return;
351
358
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/worker.ts","../src/job_pool.ts","../src/schedule.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 completed = await this.#pool.waitForNextCompletion()\n yield { type: 'completed', queue: completed.queue, job: 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])\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: any\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: any,\n context: JobContext,\n timeout?: number\n ): Promise<void> {\n if (!timeout) {\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 = new Promise<never>((_, reject) => {\n signal.addEventListener('abort', () => {\n reject(new errors.E_JOB_TIMEOUT([instance.constructor.name, timeout]))\n })\n })\n\n await Promise.race([instance.execute(), abortPromise])\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(): any {\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 { BackoffConfig, Duration } from '../types/main.js'\nimport * as errors from '../exceptions.js'\nimport { parse } from '../utils.js'\nimport { RuntimeException } from '@poppinss/utils'\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/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: any\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: any,\n context: JobContext,\n timeout?: number\n ): Promise<void> {\n if (!timeout) {\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 = new Promise<never>((_, reject) => {\n signal.addEventListener('abort', () => {\n reject(new errors.E_JOB_TIMEOUT([instance.constructor.name, timeout]))\n })\n })\n\n await Promise.race([instance.execute(), abortPromise])\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(): any {\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 { BackoffConfig, Duration } from '../types/main.js'\nimport * as errors from '../exceptions.js'\nimport { parse } from '../utils.js'\nimport { RuntimeException } from '@poppinss/utils'\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,7 +1,7 @@
1
1
  import {
2
2
  FakeAdapter,
3
3
  fake
4
- } from "../../chunk-RO6VVBGK.js";
4
+ } from "../../chunk-H6WOFLPJ.js";
5
5
  import "../../chunk-3BIR4IQD.js";
6
6
  export {
7
7
  FakeAdapter,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Locator,
3
3
  QueueManager
4
- } from "../../chunk-RO6VVBGK.js";
4
+ } from "../../chunk-H6WOFLPJ.js";
5
5
  import {
6
6
  DEFAULT_PRIORITY
7
7
  } from "../../chunk-3BIR4IQD.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@boringnode/queue",
3
3
  "description": "A simple and efficient queue system for Node.js applications",
4
- "version": "0.3.2",
4
+ "version": "0.3.4",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
7
7
  "files": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/drivers/fake_adapter.ts","../src/debug.ts","../src/job_dispatcher.ts","../src/locator.ts","../src/logger.ts","../src/queue_manager.ts","../src/job_batch_dispatcher.ts","../src/schedule_builder.ts","../src/job.ts"],"sourcesContent":["import assert from 'node:assert/strict'\nimport { randomUUID } from 'node:crypto'\nimport { isDeepStrictEqual } from 'node:util'\nimport { CronExpressionParser } from 'cron-parser'\nimport type { Adapter, AcquiredJob } from '../contracts/adapter.js'\nimport type {\n JobData,\n JobClass,\n JobRecord,\n JobRetention,\n ScheduleConfig,\n ScheduleData,\n ScheduleListOptions,\n} from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\nimport { parse } from '../utils.js'\nimport { Job } from '../job.js'\n\ninterface ActiveJob {\n job: JobData\n acquiredAt: number\n queue: string\n}\n\ninterface DelayedJob {\n job: JobData\n executeAt: number\n delay: number\n}\n\nexport interface FakeJobRecord {\n queue: string\n job: JobData\n delay?: number\n pushedAt: number\n}\n\nexport type FakeJobMatcher = string | JobClass | ((job: JobData) => boolean)\nexport type FakePayloadMatcher =\n | ((payload: any) => boolean)\n | object\n | string\n | number\n | boolean\n | null\n | undefined\nexport type FakeDelayMatcher = number | ((delay: number | undefined) => boolean)\n\nexport interface FakeJobQuery {\n queue?: string\n payload?: FakePayloadMatcher\n delay?: FakeDelayMatcher\n}\n\n/**\n * Create a fake adapter factory.\n */\nexport function fake() {\n return () => new FakeAdapter()\n}\n\n/**\n * In-memory adapter designed for tests with assertion helpers.\n */\nexport class FakeAdapter implements Adapter {\n #queues = new Map<string, JobData[]>()\n #activeJobs = new Map<string, ActiveJob>()\n #delayedJobs = new Map<string, Map<string, DelayedJob>>()\n #completedJobs = new Map<string, JobRecord[]>()\n #failedJobs = new Map<string, JobRecord[]>()\n #pendingTimeouts = new Set<NodeJS.Timeout>()\n #schedules = new Map<string, ScheduleData>()\n #pushedJobs: FakeJobRecord[] = []\n\n setWorkerId(_workerId: string): void {}\n\n getPushedJobs(): FakeJobRecord[] {\n return [...this.#pushedJobs]\n }\n\n getPushedJobsOn(queue: string): FakeJobRecord[] {\n return this.#pushedJobs.filter((record) => record.queue === queue)\n }\n\n findPushed(matcher: FakeJobMatcher, query?: FakeJobQuery): FakeJobRecord | undefined {\n return this.#pushedJobs.find((record) => this.#matchesRecord(record, matcher, query))\n }\n\n clearPushedJobs(): void {\n this.#pushedJobs = []\n }\n\n clear(): void {\n for (const timeout of this.#pendingTimeouts) {\n clearTimeout(timeout)\n }\n\n this.#pendingTimeouts.clear()\n this.#queues.clear()\n this.#activeJobs.clear()\n this.#delayedJobs.clear()\n this.#completedJobs.clear()\n this.#failedJobs.clear()\n this.#schedules.clear()\n this.#pushedJobs = []\n }\n\n assertPushed(matcher: FakeJobMatcher, query?: FakeJobQuery): void {\n const record = this.findPushed(matcher, query)\n assert.ok(record, this.#formatFailure('Expected job to be pushed', matcher, query))\n }\n\n assertNotPushed(matcher: FakeJobMatcher, query?: FakeJobQuery): void {\n const record = this.findPushed(matcher, query)\n assert.ok(!record, this.#formatFailure('Expected job to not be pushed', matcher, query))\n }\n\n assertPushedCount(count: number, options?: { queue?: string }): void {\n const actual = options?.queue\n ? this.#pushedJobs.filter((record) => record.queue === options.queue).length\n : this.#pushedJobs.length\n\n const suffix = options?.queue ? ` on \"${options.queue}\"` : ''\n assert.equal(actual, count, `Expected ${count} pushed job(s)${suffix}, got ${actual}`)\n }\n\n assertNothingPushed(): void {\n assert.equal(\n this.#pushedJobs.length,\n 0,\n `Expected no jobs to be pushed, got ${this.#pushedJobs.length}`\n )\n }\n\n async size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n async sizeOf(queue: string): Promise<number> {\n const jobs = this.#queues.get(queue) || []\n\n return jobs.length\n }\n\n async push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<void> {\n this.#recordPush(queue, jobData)\n this.#enqueue(queue, jobData)\n }\n\n async pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n this.#recordPush(queue, jobData, delay)\n this.#schedulePush(queue, jobData, delay)\n\n return Promise.resolve()\n }\n\n async pushMany(jobs: JobData[]): Promise<void> {\n return this.pushManyOn('default', jobs)\n }\n\n async pushManyOn(queue: string, jobs: JobData[]): Promise<void> {\n for (const job of jobs) {\n await this.pushOn(queue, job)\n }\n }\n\n async pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<AcquiredJob | null> {\n const jobs = this.#queues.get(queue)\n\n if (!jobs || jobs.length === 0) {\n return null\n }\n\n // Find job with highest priority (lowest priority number)\n let bestIndex = 0\n let bestPriority = jobs[0].priority ?? DEFAULT_PRIORITY\n\n for (let i = 1; i < jobs.length; i++) {\n const priority = jobs[i].priority ?? DEFAULT_PRIORITY\n if (priority < bestPriority) {\n bestPriority = priority\n bestIndex = i\n }\n }\n\n const [job] = jobs.splice(bestIndex, 1)\n if (!job) {\n return null\n }\n\n const acquiredAt = Date.now()\n this.#activeJobs.set(job.id, { job, acquiredAt, queue })\n\n return { ...job, acquiredAt }\n }\n\n async completeJob(jobId: string, queue: string, removeOnComplete?: JobRetention): Promise<void> {\n const active = this.#activeJobs.get(jobId)\n if (!active) return\n\n this.#activeJobs.delete(jobId)\n\n if (removeOnComplete === undefined || removeOnComplete === true) {\n return\n }\n\n this.#storeHistory(queue, 'completed', active.job, removeOnComplete)\n }\n\n async failJob(\n jobId: string,\n queue: string,\n error?: Error,\n removeOnFail?: JobRetention\n ): Promise<void> {\n const active = this.#activeJobs.get(jobId)\n if (!active) return\n\n this.#activeJobs.delete(jobId)\n\n if (removeOnFail === undefined || removeOnFail === true) {\n return\n }\n\n this.#storeHistory(queue, 'failed', active.job, removeOnFail, error)\n }\n\n async retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void> {\n const active = this.#activeJobs.get(jobId)\n if (!active) return\n\n this.#activeJobs.delete(jobId)\n\n const updatedJob = {\n ...active.job,\n attempts: (active.job.attempts || 0) + 1,\n }\n\n if (retryAt) {\n const delay = retryAt.getTime() - Date.now()\n\n if (delay > 0) {\n this.#schedulePush(queue, updatedJob, delay)\n return\n }\n }\n\n this.#enqueue(queue, updatedJob)\n }\n\n async recoverStalledJobs(\n queue: string,\n stalledThreshold: number,\n maxStalledCount: number\n ): Promise<number> {\n const now = Date.now()\n let recovered = 0\n\n for (const [jobId, active] of this.#activeJobs.entries()) {\n if (active.queue !== queue) {\n continue\n }\n\n const isStalled = now - active.acquiredAt > stalledThreshold\n\n if (!isStalled) {\n continue\n }\n\n const currentStalledCount = active.job.stalledCount ?? 0\n\n // Check if job has exceeded max stalled count\n if (currentStalledCount >= maxStalledCount) {\n // Fail permanently - just remove from active\n this.#activeJobs.delete(jobId)\n continue\n }\n\n // Recover the job - put back in queue with incremented stalledCount\n this.#activeJobs.delete(jobId)\n\n const updatedJob = {\n ...active.job,\n stalledCount: currentStalledCount + 1,\n }\n\n this.#enqueue(active.queue, updatedJob)\n recovered++\n }\n\n return recovered\n }\n\n async getJob(jobId: string, queue: string): Promise<JobRecord | null> {\n const active = this.#activeJobs.get(jobId)\n if (active && active.queue === queue) {\n return { status: 'active', data: active.job }\n }\n\n const pendingJobs = this.#queues.get(queue)\n const pending = pendingJobs?.find((job) => job.id === jobId)\n if (pending) {\n return { status: 'pending', data: pending }\n }\n\n const delayed = this.#delayedJobs.get(queue)?.get(jobId)\n if (delayed) {\n return { status: 'delayed', data: delayed.job }\n }\n\n const completed = this.#findHistory(this.#completedJobs, queue, jobId)\n if (completed) {\n return completed\n }\n\n const failed = this.#findHistory(this.#failedJobs, queue, jobId)\n if (failed) {\n return failed\n }\n\n return null\n }\n\n destroy(): Promise<void> {\n for (const timeout of this.#pendingTimeouts) {\n clearTimeout(timeout)\n }\n\n this.#pendingTimeouts.clear()\n\n return Promise.resolve()\n }\n\n async createSchedule(config: ScheduleConfig): Promise<string> {\n const id = config.id ?? randomUUID()\n const now = new Date()\n\n const schedule: ScheduleData = {\n id,\n name: config.name,\n payload: config.payload,\n cronExpression: config.cronExpression ?? null,\n everyMs: config.everyMs ?? null,\n timezone: config.timezone,\n from: config.from ?? null,\n to: config.to ?? null,\n limit: config.limit ?? null,\n runCount: 0,\n nextRunAt: null, // Will be calculated by the caller\n lastRunAt: null,\n status: 'active',\n createdAt: now,\n }\n\n this.#schedules.set(id, schedule)\n return id\n }\n\n async getSchedule(id: string): Promise<ScheduleData | null> {\n return this.#schedules.get(id) ?? null\n }\n\n async listSchedules(options?: ScheduleListOptions): Promise<ScheduleData[]> {\n const schedules = Array.from(this.#schedules.values())\n\n if (options?.status) {\n return schedules.filter((s) => s.status === options.status)\n }\n\n return schedules\n }\n\n async updateSchedule(\n id: string,\n updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>\n ): Promise<void> {\n const schedule = this.#schedules.get(id)\n if (!schedule) return\n\n if (updates.status !== undefined) schedule.status = updates.status\n if (updates.nextRunAt !== undefined) schedule.nextRunAt = updates.nextRunAt\n if (updates.lastRunAt !== undefined) schedule.lastRunAt = updates.lastRunAt\n if (updates.runCount !== undefined) schedule.runCount = updates.runCount\n }\n\n async deleteSchedule(id: string): Promise<void> {\n this.#schedules.delete(id)\n }\n\n async claimDueSchedule(): Promise<ScheduleData | null> {\n const now = new Date()\n\n // Find first due schedule\n const schedule = Array.from(this.#schedules.values()).find((s) => {\n if (s.status !== 'active') return false\n if (s.nextRunAt === null || s.nextRunAt > now) return false\n if (s.limit !== null && s.runCount >= s.limit) return false\n if (s.to !== null && now > s.to) return false\n return true\n })\n\n if (!schedule) return null\n\n // Calculate next run\n let nextRunAt: Date | null = null\n if (schedule.everyMs) {\n nextRunAt = new Date(now.getTime() + schedule.everyMs)\n } else if (schedule.cronExpression) {\n const cron = CronExpressionParser.parse(schedule.cronExpression, {\n currentDate: now,\n tz: schedule.timezone || 'UTC',\n })\n nextRunAt = cron.next().toDate()\n }\n\n // Check if limit will be reached after this run\n const newRunCount = schedule.runCount + 1\n if (schedule.limit !== null && newRunCount >= schedule.limit) {\n nextRunAt = null // No more runs\n }\n\n // Check if end date will be passed\n if (nextRunAt && schedule.to !== null && nextRunAt > schedule.to) {\n nextRunAt = null // Past end date\n }\n\n // Clone schedule data before updating (return old state)\n const claimedSchedule: ScheduleData = { ...schedule }\n\n // Update schedule atomically\n schedule.nextRunAt = nextRunAt\n schedule.lastRunAt = now\n schedule.runCount = newRunCount\n\n return claimedSchedule\n }\n\n #recordPush(queue: string, jobData: JobData, delay?: number) {\n this.#pushedJobs.push({\n queue,\n job: jobData,\n delay,\n pushedAt: Date.now(),\n })\n }\n\n #enqueue(queue: string, jobData: JobData) {\n if (!this.#queues.has(queue)) {\n this.#queues.set(queue, [])\n }\n\n this.#queues.get(queue)!.push(jobData)\n }\n\n #schedulePush(queue: string, jobData: JobData, delay: number) {\n if (!this.#delayedJobs.has(queue)) {\n this.#delayedJobs.set(queue, new Map())\n }\n\n const executeAt = Date.now() + delay\n this.#delayedJobs.get(queue)!.set(jobData.id, { job: jobData, executeAt, delay })\n\n const timeout = setTimeout(() => {\n this.#pendingTimeouts.delete(timeout)\n this.#delayedJobs.get(queue)?.delete(jobData.id)\n this.#enqueue(queue, jobData)\n }, delay)\n\n this.#pendingTimeouts.add(timeout)\n }\n\n #storeHistory(\n queue: string,\n status: 'completed' | 'failed',\n job: JobData,\n retention: JobRetention,\n error?: Error\n ) {\n const record: JobRecord = {\n status,\n data: job,\n finishedAt: Date.now(),\n error: error?.message,\n }\n\n const store = status === 'completed' ? this.#completedJobs : this.#failedJobs\n\n if (!store.has(queue)) {\n store.set(queue, [])\n }\n\n const records = store.get(queue)!\n records.push(record)\n\n if (retention && retention !== true) {\n this.#applyRetention(records, retention)\n }\n }\n\n #applyRetention(records: JobRecord[], retention: JobRetention) {\n if (retention === false || retention === true) {\n return\n }\n\n if (retention.age !== undefined) {\n const maxAgeMs = parse(retention.age)\n if (maxAgeMs > 0) {\n const cutoff = Date.now() - maxAgeMs\n const filtered = records.filter((record) => (record.finishedAt ?? 0) >= cutoff)\n records.splice(0, records.length, ...filtered)\n }\n }\n\n if (retention.count !== undefined && retention.count > 0 && records.length > retention.count) {\n records.splice(0, records.length - retention.count)\n }\n }\n\n #findHistory(store: Map<string, JobRecord[]>, queue: string, jobId: string): JobRecord | null {\n const records = store.get(queue)\n if (!records) return null\n\n return records.find((record) => record.data.id === jobId) ?? null\n }\n\n #matchesRecord(record: FakeJobRecord, matcher: FakeJobMatcher, query?: FakeJobQuery): boolean {\n if (query?.queue && record.queue !== query.queue) {\n return false\n }\n\n const matchesJob =\n typeof matcher === 'string'\n ? record.job.name === matcher\n : this.#isJobClass(matcher)\n ? record.job.name === this.#getJobClassName(matcher)\n : matcher(record.job)\n\n if (!matchesJob) {\n return false\n }\n\n if (query?.payload !== undefined) {\n const payloadMatcher = query.payload\n const matchesPayload =\n typeof payloadMatcher === 'function'\n ? payloadMatcher(record.job.payload)\n : isDeepStrictEqual(record.job.payload, payloadMatcher)\n\n if (!matchesPayload) {\n return false\n }\n }\n\n if (query?.delay !== undefined) {\n const delayMatcher = query.delay\n const matchesDelay =\n typeof delayMatcher === 'function'\n ? delayMatcher(record.delay)\n : record.delay === delayMatcher\n\n if (!matchesDelay) {\n return false\n }\n }\n\n return true\n }\n\n #formatFailure(prefix: string, matcher: FakeJobMatcher, query?: FakeJobQuery): string {\n const parts = [prefix]\n\n const matcherName = this.#getMatcherName(matcher)\n if (matcherName) {\n parts.push(`for \"${matcherName}\"`)\n }\n\n if (query?.queue) {\n parts.push(`on \"${query.queue}\"`)\n }\n\n if (query?.payload !== undefined) {\n parts.push('with matching payload')\n }\n\n if (query?.delay !== undefined) {\n parts.push('with matching delay')\n }\n\n const suffix = this.#pushedJobs.length\n ? `Pushed jobs: ${this.#pushedJobs.map((record) => record.job.name).join(', ')}`\n : 'Pushed jobs: none'\n\n return `${parts.join(' ')}. ${suffix}.`\n }\n\n #getMatcherName(matcher: FakeJobMatcher): string | undefined {\n if (typeof matcher === 'string') {\n return matcher\n }\n\n if (this.#isJobClass(matcher)) {\n return this.#getJobClassName(matcher)\n }\n\n return undefined\n }\n\n #isJobClass(matcher: FakeJobMatcher): matcher is JobClass {\n return typeof matcher === 'function' && matcher.prototype instanceof Job\n }\n\n #getJobClassName(JobClass: JobClass): string {\n return JobClass.options?.name || JobClass.name\n }\n}\n","import { debuglog } from 'node:util'\n\nexport default debuglog('boringnode:queue')\n","import debug from './debug.js'\nimport { randomUUID } from 'node:crypto'\nimport { QueueManager } from './queue_manager.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type { DispatchResult, Duration } from './types/main.js'\nimport { parse } from './utils.js'\n\n/**\n * Fluent builder for dispatching jobs to the queue.\n *\n * Provides a chainable API for configuring job options before dispatch.\n * Usually created via `Job.dispatch()` rather than directly.\n *\n * ```\n * Job.dispatch(payload)\n * .toQueue('emails') // optional: target queue\n * .priority(1) // optional: 1-10, lower = higher priority\n * .in('5m') // optional: delay before processing\n * .with('redis') // optional: specific adapter\n * .run() // dispatch the job\n * ```\n *\n * @typeParam T - The payload type for this job\n *\n * @example\n * ```typescript\n * // Simple dispatch (auto-runs via thenable)\n * await SendEmailJob.dispatch({ to: 'user@example.com', subject: 'Hello' })\n *\n * // With options\n * const jobId = await SendEmailJob.dispatch({ to: 'user@example.com' })\n * .toQueue('high-priority')\n * .priority(1)\n * .run()\n *\n * // Delayed job\n * await ReminderJob.dispatch({ userId: 123 }).in('24h')\n * ```\n */\nexport class JobDispatcher<T> {\n readonly #name: string\n readonly #payload: T\n #queue: string = 'default'\n #adapter?: string | (() => Adapter)\n #delay?: Duration\n #priority?: number\n #groupId?: string\n\n /**\n * Create a new job dispatcher.\n *\n * @param name - The job class name (used to locate the class at runtime)\n * @param payload - The data to pass to the job\n */\n constructor(name: string, payload: T) {\n this.#name = name\n this.#payload = payload\n }\n\n /**\n * Set the target queue for this job.\n *\n * @param queue - Queue name (default: 'default')\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await SendEmailJob.dispatch(payload).toQueue('emails')\n * ```\n */\n toQueue(queue: string): this {\n this.#queue = queue\n\n return this\n }\n\n /**\n * Delay the job execution.\n *\n * The job will be stored in a delayed state and moved to pending\n * after the delay expires.\n *\n * @param delay - Delay as milliseconds or duration string ('5s', '1h', '7d')\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * // Send reminder in 24 hours\n * await ReminderJob.dispatch(payload).in('24h')\n *\n * // Process in 5 minutes\n * await CleanupJob.dispatch(payload).in('5m')\n * ```\n */\n in(delay: Duration): this {\n this.#delay = delay\n\n return this\n }\n\n /**\n * Set the job priority.\n *\n * Lower numbers = higher priority. Jobs with lower priority values\n * are processed before jobs with higher values.\n *\n * @param priority - Priority level (1-10, default: 5)\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * // High priority job\n * await UrgentJob.dispatch(payload).priority(1)\n *\n * // Low priority job\n * await BackgroundJob.dispatch(payload).priority(10)\n * ```\n */\n priority(priority: number): this {\n this.#priority = priority\n\n return this\n }\n\n /**\n * Assign this job to a group.\n *\n * Jobs with the same groupId can be filtered and displayed together\n * in monitoring UIs. Useful for batch operations like newsletters\n * or bulk exports.\n *\n * @param groupId - Group identifier\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * // Group newsletter jobs together\n * await SendEmailJob.dispatch({ to: 'user@example.com' })\n * .group('newsletter-jan-2025')\n * .run()\n * ```\n */\n group(groupId: string): this {\n this.#groupId = groupId\n\n return this\n }\n\n /**\n * Use a specific adapter for this job.\n *\n * @param adapter - Adapter name or factory function\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * // Use named adapter\n * await Job.dispatch(payload).with('redis')\n *\n * // Use custom adapter instance\n * await Job.dispatch(payload).with(() => new CustomAdapter())\n * ```\n */\n with(adapter: string | (() => Adapter)) {\n this.#adapter = adapter\n\n return this\n }\n\n /**\n * Dispatch the job to the queue.\n *\n * @returns A DispatchResult containing the jobId\n *\n * @example\n * ```typescript\n * const { jobId } = await SendEmailJob.dispatch(payload).run()\n * console.log(`Dispatched job: ${jobId}`)\n * ```\n */\n async run(): Promise<DispatchResult> {\n const id = randomUUID()\n\n debug('dispatching job %s with id %s using payload %s', this.#name, id, this.#payload)\n\n const adapter = this.#getAdapterInstance()\n\n const payload = {\n id,\n name: this.#name,\n payload: this.#payload,\n attempts: 0,\n priority: this.#priority,\n groupId: this.#groupId,\n }\n\n if (this.#delay) {\n const parsedDelay = parse(this.#delay)\n\n await adapter.pushLaterOn(this.#queue, payload, parsedDelay)\n } else {\n await adapter.pushOn(this.#queue, payload)\n }\n\n return {\n jobId: id,\n }\n }\n\n /**\n * Thenable implementation for auto-dispatch when awaited.\n *\n * Allows `await Job.dispatch(payload)` without explicit `.run()`.\n *\n * @param onFulfilled - Success callback\n * @param onRejected - Error callback\n * @returns Promise resolving to the DispatchResult\n */\n then(\n onFulfilled?: (value: DispatchResult) => any,\n onRejected?: (reason: any) => any\n ): Promise<any> {\n return this.run().then(onFulfilled, onRejected)\n }\n\n #getAdapterInstance(): Adapter {\n if (!this.#adapter) {\n return QueueManager.use()\n }\n\n if (typeof this.#adapter === 'string') {\n return QueueManager.use(this.#adapter)\n }\n\n return this.#adapter()\n }\n}\n","import { Job } from './job.js'\nimport * as errors from './exceptions.js'\nimport type { JobClass } from './types/main.js'\nimport debug from './debug.js'\nimport { glob } from 'node:fs/promises'\nimport { resolve } from 'node:path'\n\n/**\n * Job class registry.\n *\n * The Locator maintains a mapping of job names to their classes,\n * allowing the Worker to instantiate jobs by name when processing.\n *\n * Jobs are typically registered automatically via `QueueManager.init()`\n * using the `locations` config option, but can also be registered manually.\n *\n * @example\n * ```typescript\n * import { Locator } from '@boringnode/queue'\n * import SendEmailJob from './jobs/send_email_job.js'\n *\n * // Manual registration\n * Locator.register('SendEmailJob', SendEmailJob)\n *\n * // Auto-registration via glob (used by QueueManager.init)\n * await Locator.registerFromGlob(['./jobs/**\\/*.js'])\n *\n * // Retrieve a job class\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * ```\n */\nclass LocatorSingleton {\n #registry = new Map<string, JobClass>()\n\n /**\n * Register a job class with a given name.\n *\n * @param name - The job name (usually the class name)\n * @param JobClass - The job class constructor\n *\n * @example\n * ```typescript\n * Locator.register('SendEmailJob', SendEmailJob)\n * ```\n */\n register<T extends Job>(name: string, JobClass: JobClass<T>) {\n debug('registering job: %s', name)\n\n this.#registry.set(name, JobClass)\n }\n\n /**\n * Auto-register job classes from files matching glob patterns.\n *\n * Each file should have a default export that is a Job class.\n * The class name is used as the registration name.\n *\n * @param patterns - Glob patterns to match job files\n * @returns Number of jobs successfully registered\n *\n * @example\n * ```typescript\n * const count = await Locator.registerFromGlob([\n * './jobs/**\\/*.js',\n * './app/jobs/**\\/*.ts'\n * ])\n * console.log(`Registered ${count} jobs`)\n * ```\n */\n async registerFromGlob(patterns: string[]): Promise<number> {\n let registered = 0\n\n for (const pattern of patterns) {\n debug('registering jobs from glob pattern: %s', pattern)\n for await (const file of glob(pattern)) {\n debug('found job file: %s', file)\n\n try {\n const absolutePath = resolve(file)\n const module = await import(`file://${absolutePath}`)\n const JobClass = module.default as JobClass\n\n if (JobClass && typeof JobClass === 'function') {\n const jobName = JobClass.options?.name || JobClass.name\n this.register(jobName, JobClass)\n registered++\n }\n } catch (error) {\n console.warn(`Failed to load job from ${file}:`, error)\n }\n }\n }\n\n return registered\n }\n\n /**\n * Get a job class by name.\n *\n * @param name - The job name to look up\n * @returns The job class, or undefined if not found\n *\n * @example\n * ```typescript\n * const JobClass = Locator.get('SendEmailJob')\n * if (JobClass) {\n * const instance = new JobClass(payload)\n * }\n * ```\n */\n get<T extends Job = Job>(name: string): JobClass<T> | undefined {\n return this.#registry.get(name) as JobClass<T> | undefined\n }\n\n /**\n * Get a job class by name, throwing if not found.\n *\n * @param name - The job name to look up\n * @returns The job class\n * @throws {E_JOB_NOT_FOUND} If the job is not registered\n *\n * @example\n * ```typescript\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * const instance = new JobClass(payload)\n * ```\n */\n getOrThrow<T extends Job = Job>(name: string): JobClass<T> {\n const JobClass = this.get<T>(name)\n\n if (!JobClass) {\n throw new errors.E_JOB_NOT_FOUND([name])\n }\n\n return JobClass\n }\n\n /**\n * Remove all registered jobs.\n *\n * Primarily useful for testing.\n */\n clear(): void {\n this.#registry.clear()\n }\n}\n\n/** Global job class registry singleton */\nexport const Locator = new LocatorSingleton()\n","export interface LogObject {\n [key: string]: unknown\n}\n\nexport interface ErrorObject extends LogObject {\n err?: Error\n}\n\nexport interface Logger {\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n\n child(obj: LogObject): Logger\n}\n\n/**\n * A simple logger that writes to console.\n */\nclass ConsoleLogger implements Logger {\n #prefix: string\n\n constructor(prefix: string = 'queue') {\n this.#prefix = prefix\n }\n\n #format(level: string, msgOrObj: string | LogObject, msg?: string): [string, LogObject?] {\n const prefix = `[${this.#prefix}] ${level}:`\n\n if (typeof msgOrObj === 'object') {\n return [`${prefix} ${msg}`, msgOrObj]\n }\n\n return [`${prefix} ${msgOrObj}`]\n }\n\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n trace(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('TRACE', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n debug(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('DEBUG', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n info(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('INFO', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n warn(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('WARN', msgOrObj, msg)\n\n if (obj) {\n return console.warn(message, obj)\n }\n\n console.warn(message)\n }\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n error(msgOrObj: string | ErrorObject, msg?: string): void {\n const [message, obj] = this.#format('ERROR', msgOrObj, msg)\n\n if (obj) {\n return console.error(message, obj)\n }\n\n console.error(message)\n }\n\n child(obj: LogObject): Logger {\n const childPrefix = obj.pkg ? String(obj.pkg) : this.#prefix\n return new ConsoleLogger(childPrefix)\n }\n}\n\nexport const consoleLogger = new ConsoleLogger()\n","import * as errors from './exceptions.js'\nimport debug from './debug.js'\nimport { Locator } from './locator.js'\nimport { consoleLogger, type Logger } from './logger.js'\nimport { FakeAdapter } from './drivers/fake_adapter.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type {\n AdapterFactory,\n JobFactory,\n JobOptions,\n QueueConfig,\n QueueManagerConfig,\n RetryConfig,\n} from './types/main.js'\n\ntype QueueManagerFakeState = {\n defaultAdapter: string\n adapters: Record<string, AdapterFactory>\n adapterInstances: Map<string, Adapter>\n globalRetryConfig?: RetryConfig\n globalJobOptions?: JobOptions\n queueConfigs: Map<string, QueueConfig>\n logger: Logger\n jobFactory?: JobFactory\n fakeAdapter: FakeAdapter\n}\n\n/**\n * Central configuration and adapter management for the queue system.\n *\n * The QueueManager is responsible for:\n * - Initializing adapters and job registration\n * - Providing adapter instances to workers and dispatchers\n * - Managing retry configuration across global, queue, and job levels\n *\n * @example\n * ```typescript\n * import { QueueManager, redis } from '@boringnode/queue'\n *\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis({ host: 'localhost' }),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * retry: {\n * maxRetries: 3,\n * backoff: exponentialBackoff(),\n * },\n * })\n *\n * // Get the default adapter\n * const adapter = QueueManager.use()\n *\n * // Clean up when done\n * await QueueManager.destroy()\n * ```\n */\nclass QueueManagerSingleton {\n #initialized = false\n #defaultAdapter!: string\n #adapters: Record<string, AdapterFactory> = {}\n #adapterInstances: Map<string, Adapter> = new Map()\n #globalRetryConfig?: RetryConfig\n #globalJobOptions?: JobOptions\n #queueConfigs: Map<string, QueueConfig> = new Map()\n #logger: Logger = consoleLogger\n #jobFactory?: JobFactory\n #fakeState?: QueueManagerFakeState\n\n /**\n * Initialize the queue system with the given configuration.\n *\n * This must be called before using the queue system. It:\n * - Validates the configuration\n * - Registers adapters\n * - Auto-discovers and registers job classes from `locations`\n *\n * @param config - The queue configuration\n * @returns This instance for chaining\n * @throws {E_CONFIGURATION_ERROR} If the configuration is invalid\n *\n * @example\n * ```typescript\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis(),\n * postgres: knex(pgConfig),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * })\n * ```\n */\n async init(config: QueueManagerConfig) {\n debug('initializing queue manager with config: %O', config)\n\n this.#validateConfig(config)\n\n this.#adapterInstances.clear()\n\n this.#defaultAdapter = config.default\n this.#adapters = config.adapters\n this.#globalRetryConfig = config.retry\n this.#globalJobOptions = config.defaultJobOptions\n this.#logger = config.logger ?? consoleLogger\n this.#jobFactory = config.jobFactory\n\n if (config.queues) {\n for (const [queue, queueConfig] of Object.entries(config.queues)) {\n this.#queueConfigs.set(queue, queueConfig as QueueConfig)\n }\n }\n\n if (config.locations && config.locations.length > 0) {\n const registered = await Locator.registerFromGlob(config.locations)\n\n if (registered === 0) {\n this.#logger.warn(\n `No jobs found for locations: ${config.locations.join(', ')}. ` +\n 'Verify your glob patterns match your job files.'\n )\n }\n }\n\n this.#initialized = true\n\n return this\n }\n\n /**\n * Get an adapter instance by name.\n *\n * Adapter instances are cached and reused. If no name is provided,\n * the default adapter is returned.\n *\n * @param adapter - Adapter name (optional, defaults to the default adapter)\n * @returns The adapter instance\n * @throws {E_QUEUE_NOT_INITIALIZED} If `init()` hasn't been called\n * @throws {E_CONFIGURATION_ERROR} If the adapter is not registered\n * @throws {E_ADAPTER_INIT_ERROR} If the adapter factory throws\n *\n * @example\n * ```typescript\n * // Get default adapter\n * const adapter = QueueManager.use()\n *\n * // Get specific adapter\n * const redisAdapter = QueueManager.use('redis')\n * ```\n */\n use(adapter?: string): Adapter {\n if (!this.#initialized) {\n throw new errors.E_QUEUE_NOT_INITIALIZED()\n }\n\n if (!adapter) {\n adapter = this.#defaultAdapter\n }\n\n // Return cached instance if exists\n const cached = this.#adapterInstances.get(adapter)\n if (cached) {\n return cached\n }\n\n const adapterFactory = this.#adapters[adapter]\n\n if (!adapterFactory) {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${adapter}\" is not registered`])\n }\n\n debug('using adapter \"%s\"', adapter)\n\n try {\n const instance = adapterFactory()\n this.#adapterInstances.set(adapter, instance)\n return instance\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new errors.E_ADAPTER_INIT_ERROR([adapter, message])\n }\n }\n\n /**\n * Replace all adapters with a fake adapter for testing.\n *\n * The fake adapter records pushed jobs and exposes assertion helpers.\n * Call `restore()` to return to the previous configuration.\n *\n * @returns The fake adapter instance for assertions\n * @throws {E_QUEUE_NOT_INITIALIZED} If `init()` hasn't been called\n *\n * @example\n * ```typescript\n * const fake = QueueManager.fake()\n *\n * await SendEmailJob.dispatch({ to: 'user@example.com' })\n *\n * fake.assertPushed(SendEmailJob)\n * QueueManager.restore()\n * ```\n */\n fake(): FakeAdapter {\n if (!this.#initialized) {\n throw new errors.E_QUEUE_NOT_INITIALIZED()\n }\n\n if (this.#fakeState) {\n return this.#fakeState.fakeAdapter\n }\n\n const fakeAdapter = new FakeAdapter()\n\n this.#fakeState = {\n defaultAdapter: this.#defaultAdapter,\n adapters: this.#adapters,\n adapterInstances: this.#adapterInstances,\n globalRetryConfig: this.#globalRetryConfig,\n globalJobOptions: this.#globalJobOptions,\n queueConfigs: this.#queueConfigs,\n logger: this.#logger,\n jobFactory: this.#jobFactory,\n fakeAdapter,\n }\n\n const fakeFactory = () => fakeAdapter\n const nextAdapters: Record<string, AdapterFactory> = {}\n\n for (const name of Object.keys(this.#fakeState.adapters)) {\n nextAdapters[name] = fakeFactory\n }\n\n this.#adapters = nextAdapters\n this.#adapterInstances = new Map()\n\n return fakeAdapter\n }\n\n /**\n * Restore adapters after calling `fake()`.\n */\n restore(): void {\n if (!this.#fakeState) {\n return\n }\n\n void this.#fakeState.fakeAdapter.destroy()\n\n for (const adapter of this.#adapterInstances.values()) {\n void adapter.destroy()\n }\n\n const state = this.#fakeState\n this.#fakeState = undefined\n\n this.#defaultAdapter = state.defaultAdapter\n this.#adapters = state.adapters\n this.#adapterInstances = state.adapterInstances\n this.#globalRetryConfig = state.globalRetryConfig\n this.#globalJobOptions = state.globalJobOptions\n this.#queueConfigs = state.queueConfigs\n this.#logger = state.logger\n this.#jobFactory = state.jobFactory\n }\n\n /**\n * Get the merged retry configuration for a job.\n *\n * Configuration is merged with priority: job > queue > global.\n * This allows specific jobs or queues to override global defaults.\n *\n * @param queue - The queue name\n * @param jobRetryConfig - Optional job-level retry config\n * @returns The merged retry configuration\n *\n * @example\n * ```typescript\n * // Global: maxRetries=3, Queue: maxRetries=5, Job: maxRetries=1\n * // Result: maxRetries=1 (job wins)\n * const config = QueueManager.getMergedRetryConfig('emails', { maxRetries: 1 })\n * ```\n */\n getMergedRetryConfig(queue: string, jobRetryConfig?: RetryConfig): RetryConfig {\n const queueConfig = this.#queueConfigs.get(queue)\n const queueRetryConfig = queueConfig?.retry || {}\n\n let maxRetries =\n jobRetryConfig?.maxRetries ??\n queueRetryConfig.maxRetries ??\n this.#globalRetryConfig?.maxRetries ??\n 0\n\n let backoff =\n jobRetryConfig?.backoff || queueRetryConfig.backoff || this.#globalRetryConfig?.backoff\n\n return { maxRetries, backoff }\n }\n\n /**\n * Get the configured job factory for custom instantiation.\n *\n * @returns The job factory function, or undefined if not configured\n */\n getJobFactory(): JobFactory | undefined {\n return this.#jobFactory\n }\n\n /**\n * Get the merged job options for a job (priority: job > queue > global).\n */\n getMergedJobOptions(queue: string, jobOptions?: JobOptions): JobOptions {\n const queueConfig = this.#queueConfigs.get(queue)\n const queueJobOptions = queueConfig?.defaultJobOptions\n\n return {\n removeOnComplete:\n jobOptions?.removeOnComplete ??\n queueJobOptions?.removeOnComplete ??\n this.#globalJobOptions?.removeOnComplete,\n removeOnFail:\n jobOptions?.removeOnFail ??\n queueJobOptions?.removeOnFail ??\n this.#globalJobOptions?.removeOnFail,\n }\n }\n\n #validateConfig(config: QueueManagerConfig): void {\n if (!config.adapters || Object.keys(config.adapters).length === 0) {\n throw new errors.E_CONFIGURATION_ERROR(['At least one adapter must be configured'])\n }\n\n if (!config.default) {\n throw new errors.E_CONFIGURATION_ERROR(['Default adapter must be specified'])\n }\n\n if (!config.adapters[config.default]) {\n throw new errors.E_CONFIGURATION_ERROR([\n `Default adapter \"${config.default}\" not found in adapters configuration`,\n ])\n }\n\n for (const [name, factory] of Object.entries(config.adapters)) {\n if (typeof factory !== 'function') {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${name}\" must be a factory function`])\n }\n }\n }\n\n /**\n * Clean up all adapter instances and reset state.\n *\n * Call this when shutting down the application or when\n * you need to reinitialize with a new configuration.\n *\n * @example\n * ```typescript\n * // On application shutdown\n * await QueueManager.destroy()\n * ```\n */\n async destroy() {\n for (const [name, adapter] of this.#adapterInstances) {\n debug('destroying adapter \"%s\"', name)\n await adapter.destroy()\n }\n\n if (this.#fakeState) {\n await this.#fakeState.fakeAdapter.destroy()\n\n for (const [name, adapter] of this.#fakeState.adapterInstances) {\n debug('destroying adapter \"%s\"', name)\n await adapter.destroy()\n }\n }\n\n this.#adapterInstances.clear()\n this.#initialized = false\n this.#fakeState = undefined\n }\n}\n\n/** Global queue manager singleton */\nexport const QueueManager = new QueueManagerSingleton()\n","import debug from './debug.js'\nimport { randomUUID } from 'node:crypto'\nimport { QueueManager } from './queue_manager.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type { DispatchManyResult } from './types/main.js'\n\n/**\n * Fluent builder for dispatching multiple jobs to the queue in a single batch.\n *\n * Provides a chainable API for configuring job options before dispatch.\n * Usually created via `Job.dispatchMany()` rather than directly.\n *\n * ```\n * Job.dispatchMany(payloads)\n * .toQueue('emails') // optional: target queue\n * .priority(1) // optional: 1-10, lower = higher priority\n * .group('batch-123') // optional: group all jobs together\n * .with('redis') // optional: specific adapter\n * .run() // dispatch all jobs\n * ```\n *\n * @typeParam T - The payload type for these jobs\n *\n * @example\n * ```typescript\n * // Batch dispatch for newsletter\n * const { jobIds } = await SendEmailJob.dispatchMany([\n * { to: 'user1@example.com', subject: 'Newsletter' },\n * { to: 'user2@example.com', subject: 'Newsletter' },\n * ])\n * .group('newsletter-jan-2025')\n * .toQueue('emails')\n * .run()\n *\n * console.log(`Dispatched ${jobIds.length} jobs`)\n * ```\n */\nexport class JobBatchDispatcher<T> {\n readonly #name: string\n readonly #payloads: T[]\n #queue: string = 'default'\n #adapter?: string | (() => Adapter)\n #priority?: number\n #groupId?: string\n\n /**\n * Create a new batch job dispatcher.\n *\n * @param name - The job class name (used to locate the class at runtime)\n * @param payloads - Array of data to pass to each job\n */\n constructor(name: string, payloads: T[]) {\n this.#name = name\n this.#payloads = payloads\n }\n\n /**\n * Set the target queue for all jobs.\n *\n * @param queue - Queue name (default: 'default')\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await SendEmailJob.dispatchMany(payloads).toQueue('emails')\n * ```\n */\n toQueue(queue: string): this {\n this.#queue = queue\n\n return this\n }\n\n /**\n * Set the priority for all jobs.\n *\n * Lower numbers = higher priority. Jobs with lower priority values\n * are processed before jobs with higher values.\n *\n * @param priority - Priority level (1-10, default: 5)\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await UrgentJob.dispatchMany(payloads).priority(1)\n * ```\n */\n priority(priority: number): this {\n this.#priority = priority\n\n return this\n }\n\n /**\n * Assign all jobs to a group.\n *\n * Jobs with the same groupId can be filtered and displayed together\n * in monitoring UIs. Useful for batch operations like newsletters\n * or bulk exports.\n *\n * @param groupId - Group identifier\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await SendEmailJob.dispatchMany(recipients)\n * .group('newsletter-jan-2025')\n * .run()\n * ```\n */\n group(groupId: string): this {\n this.#groupId = groupId\n\n return this\n }\n\n /**\n * Use a specific adapter for these jobs.\n *\n * @param adapter - Adapter name or factory function\n * @returns This dispatcher for chaining\n *\n * @example\n * ```typescript\n * await Job.dispatchMany(payloads).with('redis')\n * ```\n */\n with(adapter: string | (() => Adapter)) {\n this.#adapter = adapter\n\n return this\n }\n\n /**\n * Dispatch all jobs to the queue.\n *\n * @returns A DispatchManyResult containing all jobIds\n *\n * @example\n * ```typescript\n * const { jobIds } = await SendEmailJob.dispatchMany(payloads).run()\n * console.log(`Dispatched ${jobIds.length} jobs`)\n * ```\n */\n async run(): Promise<DispatchManyResult> {\n debug('dispatching %d jobs of type %s', this.#payloads.length, this.#name)\n\n const adapter = this.#getAdapterInstance()\n\n const jobs = this.#payloads.map((payload) => ({\n id: randomUUID(),\n name: this.#name,\n payload,\n attempts: 0,\n priority: this.#priority,\n groupId: this.#groupId,\n }))\n\n await adapter.pushManyOn(this.#queue, jobs)\n\n return {\n jobIds: jobs.map((job) => job.id),\n }\n }\n\n /**\n * Thenable implementation for auto-dispatch when awaited.\n *\n * Allows `await Job.dispatchMany(payloads)` without explicit `.run()`.\n *\n * @param onFulfilled - Success callback\n * @param onRejected - Error callback\n * @returns Promise resolving to the DispatchManyResult\n */\n then(\n onFulfilled?: (value: DispatchManyResult) => any,\n onRejected?: (reason: any) => any\n ): Promise<any> {\n return this.run().then(onFulfilled, onRejected)\n }\n\n #getAdapterInstance(): Adapter {\n if (!this.#adapter) {\n return QueueManager.use()\n }\n\n if (typeof this.#adapter === 'string') {\n return QueueManager.use(this.#adapter)\n }\n\n return this.#adapter()\n }\n}\n","import type { Duration, ScheduleConfig, ScheduleResult } from './types/main.js'\nimport { QueueManager } from './queue_manager.js'\nimport { parse } from './utils.js'\nimport { CronExpressionParser } from 'cron-parser'\nimport * as errors from './exceptions.js'\n\n/**\n * Fluent builder for creating job schedules.\n *\n * @example\n * ```typescript\n * // Create with cron\n * const { scheduleId } = await new ScheduleBuilder('CleanupJob', { days: 30 })\n * .id('cleanup-daily')\n * .cron('0 0 * * *')\n * .timezone('Europe/Paris')\n * .run()\n *\n * // Create with interval\n * const { scheduleId } = await new ScheduleBuilder('SyncJob', {})\n * .every('5m')\n * .run()\n * ```\n */\nexport class ScheduleBuilder implements PromiseLike<ScheduleResult> {\n #name: string\n #payload: any\n #id?: string\n #cronExpression?: string\n #everyMs?: number\n #timezone: string = 'UTC'\n #from?: Date\n #to?: Date\n #limit?: number\n\n constructor(name: string, payload: any) {\n this.#name = name\n this.#payload = payload\n }\n\n /**\n * Set a custom schedule ID.\n * If not specified, defaults to the job name.\n * If a schedule with this ID exists, it will be updated (upsert).\n */\n id(scheduleId: string): this {\n this.#id = scheduleId\n return this\n }\n\n /**\n * Set a cron expression for the schedule.\n * Mutually exclusive with `every()`.\n */\n cron(expression: string): this {\n this.#cronExpression = expression\n return this\n }\n\n /**\n * Set a repeating interval for the schedule.\n * Mutually exclusive with `cron()`.\n */\n every(interval: Duration): this {\n this.#everyMs = parse(interval)\n return this\n }\n\n /**\n * Set the timezone for cron evaluation.\n * @default 'UTC'\n */\n timezone(tz: string): this {\n this.#timezone = tz\n return this\n }\n\n /**\n * Set the start boundary for the schedule.\n * No jobs will be dispatched before this date.\n */\n from(date: Date): this {\n this.#from = date\n return this\n }\n\n /**\n * Set the end boundary for the schedule.\n * No jobs will be dispatched after this date.\n */\n to(date: Date): this {\n this.#to = date\n return this\n }\n\n /**\n * Set both start and end boundaries for the schedule.\n * Shorthand for `.from(start).to(end)`.\n */\n between(from: Date, to: Date): this {\n return this.from(from).to(to)\n }\n\n /**\n * Set the maximum number of runs for this schedule.\n */\n limit(maxRuns: number): this {\n this.#limit = maxRuns\n return this\n }\n\n /**\n * Create the schedule and return the schedule ID.\n */\n async run(): Promise<ScheduleResult> {\n // Validation\n if (!this.#cronExpression && !this.#everyMs) {\n throw new errors.E_INVALID_SCHEDULE_CONFIG([\n 'Schedule must have either a cron expression or an interval',\n ])\n }\n\n if (this.#cronExpression && this.#everyMs) {\n throw new errors.E_INVALID_SCHEDULE_CONFIG([\n 'Schedule cannot have both a cron expression and an interval',\n ])\n }\n\n // Validate cron expression\n if (this.#cronExpression) {\n try {\n CronExpressionParser.parse(this.#cronExpression, { tz: this.#timezone })\n } catch (error) {\n throw new errors.E_INVALID_CRON_EXPRESSION([this.#cronExpression, (error as Error).message])\n }\n }\n\n const config: ScheduleConfig = {\n id: this.#id ?? this.#name,\n name: this.#name,\n payload: this.#payload,\n cronExpression: this.#cronExpression,\n everyMs: this.#everyMs,\n timezone: this.#timezone,\n from: this.#from,\n to: this.#to,\n limit: this.#limit,\n }\n\n const adapter = QueueManager.use()\n const scheduleId = await adapter.createSchedule(config)\n\n // Calculate and set nextRunAt\n const nextRunAt = this.#calculateNextRunAt()\n await adapter.updateSchedule(scheduleId, { nextRunAt })\n\n return { scheduleId }\n }\n\n /**\n * Calculate the next run time based on cron or interval.\n */\n #calculateNextRunAt(): Date {\n const now = new Date()\n let nextRun: Date\n\n if (this.#cronExpression) {\n const cron = CronExpressionParser.parse(this.#cronExpression, {\n currentDate: now,\n tz: this.#timezone,\n })\n nextRun = cron.next().toDate()\n } else {\n // Interval-based: next run is now + interval\n nextRun = new Date(now.getTime() + this.#everyMs!)\n }\n\n // Respect from boundary\n if (this.#from && nextRun < this.#from) {\n if (this.#cronExpression) {\n // Recalculate from the start boundary\n const cron = CronExpressionParser.parse(this.#cronExpression, {\n currentDate: this.#from,\n tz: this.#timezone,\n })\n nextRun = cron.next().toDate()\n } else {\n nextRun = this.#from\n }\n }\n\n return nextRun\n }\n\n /**\n * Implement PromiseLike to allow `await builder.every('5m')` syntax.\n */\n then<TResult1 = ScheduleResult, TResult2 = never>(\n onfulfilled?: ((value: ScheduleResult) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.run().then(onfulfilled, onrejected)\n }\n}\n","import { JobDispatcher } from './job_dispatcher.js'\nimport { JobBatchDispatcher } from './job_batch_dispatcher.js'\nimport { ScheduleBuilder } from './schedule_builder.js'\nimport type { JobContext, JobOptions } from './types/main.js'\n\n/**\n * Abstract base class for all queue jobs.\n *\n * Extend this class to create your own jobs. Each job must implement\n * the `execute()` method which contains the job's business logic.\n *\n * The constructor is reserved for dependency injection. Payload and context\n * are provided separately via the `$hydrate()` method (called by the worker).\n *\n * @typeParam Payload - The type of data this job receives\n *\n * @example\n * ```typescript\n * import { Job } from '@boringnode/queue'\n *\n * interface SendEmailPayload {\n * to: string\n * subject: string\n * body: string\n * }\n *\n * export default class SendEmailJob extends Job<SendEmailPayload> {\n * static options = {\n * queue: 'emails',\n * maxRetries: 3,\n * }\n *\n * // Constructor is for dependency injection only\n * constructor(private mailer: MailerService) {\n * super()\n * }\n *\n * async execute() {\n * console.log(`Attempt ${this.context.attempt} for job ${this.context.jobId}`)\n * await this.mailer.send(this.payload.to, this.payload.subject, this.payload.body)\n * }\n *\n * async failed(error: Error) {\n * console.error(`Failed to send email to ${this.payload.to}:`, error)\n * }\n * }\n * ```\n */\nexport abstract class Job<Payload = any> {\n #payload!: Payload\n #context!: JobContext\n #signal?: AbortSignal\n\n /**\n * Static options for this job class.\n *\n * Override this property in subclasses to configure job behavior\n * such as queue name, retry policy, timeout, and more.\n *\n * @example\n * ```typescript\n * class SendEmailJob extends Job<SendEmailPayload> {\n * static options = {\n * queue: 'emails',\n * maxRetries: 3,\n * timeout: '30s',\n * }\n * }\n * ```\n */\n static options: JobOptions = {}\n\n /**\n * The payload data passed to this job instance.\n *\n * Contains the data provided when the job was dispatched.\n * Available after the job has been hydrated by the worker.\n *\n * @example\n * ```typescript\n * async execute() {\n * const { to, subject, body } = this.payload\n * await sendEmail(to, subject, body)\n * }\n * ```\n */\n get payload(): Payload {\n return this.#payload\n }\n\n /**\n * Context information for the current job execution.\n *\n * Provides metadata such as job ID, current attempt number,\n * queue name, priority, and timing information.\n *\n * @example\n * ```typescript\n * async execute() {\n * if (this.context.attempt > 1) {\n * console.log(`Retry attempt ${this.context.attempt}`)\n * }\n * console.log(`Processing job ${this.context.jobId} on queue ${this.context.queue}`)\n * }\n * ```\n */\n get context(): JobContext {\n return this.#context\n }\n\n /**\n * The abort signal for timeout handling.\n *\n * Check `signal.aborted` in long-running operations to handle timeouts gracefully.\n *\n * @example\n * ```typescript\n * async execute() {\n * for (const item of this.payload.items) {\n * if (this.signal?.aborted) {\n * throw new Error('Job timed out')\n * }\n * await processItem(item)\n * }\n * }\n * ```\n */\n get signal(): AbortSignal | undefined {\n return this.#signal\n }\n\n /**\n * Hydrate the job with payload, context, and optional abort signal.\n *\n * This method is called by the worker after instantiation to provide\n * the job's runtime data. It should not be called directly by user code.\n *\n * @param payload - The data to be processed by this job\n * @param context - The job execution context\n * @param signal - Optional abort signal for timeout handling\n *\n * @internal\n */\n $hydrate(payload: Payload, context: JobContext, signal?: AbortSignal): void {\n this.#payload = payload\n this.#context = Object.freeze(context)\n this.#signal = signal\n }\n\n /**\n * Dispatch this job to the queue.\n *\n * Returns a JobDispatcher for fluent configuration before dispatching.\n * The job is not actually dispatched until `.run()` is called or the\n * dispatcher is awaited.\n *\n * @param payload - The data to pass to the job\n * @returns A JobDispatcher for fluent configuration\n *\n * @example\n * ```typescript\n * // Simple dispatch\n * await SendEmailJob.dispatch({ to: 'user@example.com', subject: 'Hello' })\n *\n * // With options\n * await SendEmailJob.dispatch({ to: 'user@example.com' })\n * .toQueue('high-priority')\n * .priority(1)\n * .in('5m')\n * .run()\n * ```\n */\n static dispatch<T extends Job>(\n this: new (...args: any[]) => T,\n payload: T extends Job<infer P> ? P : never\n ): JobDispatcher<T extends Job<infer P> ? P : never> {\n const options = (this as any).options || {}\n const jobName = options.name || this.name\n\n const dispatcher = new JobDispatcher<T extends Job<infer P> ? P : never>(jobName, payload)\n\n if (options.queue) {\n dispatcher.toQueue(options.queue)\n }\n\n if (options.adapter) {\n dispatcher.with(options.adapter)\n }\n\n if (options.priority !== undefined) {\n dispatcher.priority(options.priority)\n }\n\n return dispatcher\n }\n\n /**\n * Dispatch multiple jobs to the queue in a single batch.\n *\n * Returns a JobBatchDispatcher for fluent configuration before dispatching.\n * The jobs are not actually dispatched until `.run()` is called or the\n * dispatcher is awaited.\n *\n * This is more efficient than calling `dispatch()` multiple times as it\n * uses batched operations (e.g., Redis pipeline, SQL batch insert).\n *\n * @param payloads - Array of data to pass to each job\n * @returns A JobBatchDispatcher for fluent configuration\n *\n * @example\n * ```typescript\n * // Batch dispatch for newsletter\n * const { jobIds } = await SendEmailJob.dispatchMany([\n * { to: 'user1@example.com', subject: 'Newsletter' },\n * { to: 'user2@example.com', subject: 'Newsletter' },\n * ])\n * .group('newsletter-jan-2025')\n * .toQueue('emails')\n * .run()\n *\n * console.log(`Dispatched ${jobIds.length} jobs`)\n * ```\n */\n static dispatchMany<T extends Job>(\n this: new (...args: any[]) => T,\n payloads: (T extends Job<infer P> ? P : never)[]\n ): JobBatchDispatcher<T extends Job<infer P> ? P : never> {\n const options = (this as any).options || {}\n const jobName = options.name || this.name\n\n const dispatcher = new JobBatchDispatcher<T extends Job<infer P> ? P : never>(jobName, payloads)\n\n if (options.queue) {\n dispatcher.toQueue(options.queue)\n }\n\n if (options.adapter) {\n dispatcher.with(options.adapter)\n }\n\n if (options.priority !== undefined) {\n dispatcher.priority(options.priority)\n }\n\n return dispatcher\n }\n\n /**\n * Create a schedule for this job.\n *\n * Returns a ScheduleBuilder for fluent configuration before creating the schedule.\n * The schedule is not actually created until `.run()` is called or the\n * builder is awaited.\n *\n * @param payload - The data to pass to the job on each run\n * @returns A ScheduleBuilder for fluent configuration\n *\n * @example\n * ```typescript\n * // Cron schedule\n * await CleanupJob.schedule({ days: 30 })\n * .id('cleanup-daily')\n * .cron('0 0 * * *')\n * .timezone('Europe/Paris')\n * .run()\n *\n * // Interval schedule\n * await SyncJob.schedule({ source: 'api' })\n * .every('5m')\n * .run()\n * ```\n */\n static schedule<T extends Job>(\n this: new (...args: any[]) => T,\n payload: T extends Job<infer P> ? P : never\n ): ScheduleBuilder {\n const options = (this as any).options || {}\n const jobName = options.name || this.name\n\n return new ScheduleBuilder(jobName, payload)\n }\n\n /**\n * Execute the job's business logic.\n *\n * This method is called by the worker when processing the job.\n * Implement your job's logic here.\n *\n * For timeout handling, use `this.signal` which is available after hydration.\n *\n * @throws Any error thrown will trigger retry logic (if configured)\n *\n * @example\n * ```typescript\n * async execute() {\n * for (const item of this.payload.items) {\n * if (this.signal?.aborted) {\n * throw new Error('Job timed out')\n * }\n * await processItem(item)\n * }\n * }\n * ```\n */\n abstract execute(): Promise<void>\n\n /**\n * Called when the job has permanently failed (after all retries exhausted).\n *\n * Use this hook for cleanup, logging, or notifications.\n * This is optional - implement only if you need failure handling.\n *\n * @param error - The error that caused the final failure\n *\n * @example\n * ```typescript\n * async failed(error: Error) {\n * await notifyAdmin(`Job failed: ${error.message}`)\n * await cleanup(this.payload)\n * }\n * ```\n */\n failed?(error: Error): Promise<void>\n}\n"],"mappings":";;;;;;;;;;;;AAAA,OAAO,YAAY;AACnB,SAAS,cAAAA,mBAAkB;AAC3B,SAAS,yBAAyB;AAClC,SAAS,wBAAAC,6BAA4B;;;ACHrC,SAAS,gBAAgB;AAEzB,IAAO,gBAAQ,SAAS,kBAAkB;;;ACD1C,SAAS,kBAAkB;;;ACG3B,SAAS,YAAY;AACrB,SAAS,eAAe;AA0BxB,IAAM,mBAAN,MAAuB;AAAA,EACrB,YAAY,oBAAI,IAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAatC,SAAwB,MAAc,UAAuB;AAC3D,kBAAM,uBAAuB,IAAI;AAEjC,SAAK,UAAU,IAAI,MAAM,QAAQ;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,iBAAiB,UAAqC;AAC1D,QAAI,aAAa;AAEjB,eAAW,WAAW,UAAU;AAC9B,oBAAM,0CAA0C,OAAO;AACvD,uBAAiB,QAAQ,KAAK,OAAO,GAAG;AACtC,sBAAM,sBAAsB,IAAI;AAEhC,YAAI;AACF,gBAAM,eAAe,QAAQ,IAAI;AACjC,gBAAM,SAAS,MAAM,OAAO,UAAU,YAAY;AAClD,gBAAM,WAAW,OAAO;AAExB,cAAI,YAAY,OAAO,aAAa,YAAY;AAC9C,kBAAM,UAAU,SAAS,SAAS,QAAQ,SAAS;AACnD,iBAAK,SAAS,SAAS,QAAQ;AAC/B;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,kBAAQ,KAAK,2BAA2B,IAAI,KAAK,KAAK;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,IAAyB,MAAuC;AAC9D,WAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,WAAgC,MAA2B;AACzD,UAAM,WAAW,KAAK,IAAO,IAAI;AAEjC,QAAI,CAAC,UAAU;AACb,YAAM,IAAW,gBAAgB,CAAC,IAAI,CAAC;AAAA,IACzC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAc;AACZ,SAAK,UAAU,MAAM;AAAA,EACvB;AACF;AAGO,IAAM,UAAU,IAAI,iBAAiB;;;ACtH5C,IAAM,gBAAN,MAAM,eAAgC;AAAA,EACpC;AAAA,EAEA,YAAY,SAAiB,SAAS;AACpC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAQ,OAAe,UAA8B,KAAoC;AACvF,UAAM,SAAS,IAAI,KAAK,OAAO,KAAK,KAAK;AAEzC,QAAI,OAAO,aAAa,UAAU;AAChC,aAAO,CAAC,GAAG,MAAM,IAAI,GAAG,IAAI,QAAQ;AAAA,IACtC;AAEA,WAAO,CAAC,GAAG,MAAM,IAAI,QAAQ,EAAE;AAAA,EACjC;AAAA,EAIA,MAAM,UAA8B,KAAoB;AACtD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,SAAS,UAAU,GAAG;AAE1D,QAAI,KAAK;AACP,aAAO,QAAQ,IAAI,SAAS,GAAG;AAAA,IACjC;AAEA,YAAQ,IAAI,OAAO;AAAA,EACrB;AAAA,EAIA,MAAM,UAA8B,KAAoB;AACtD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,SAAS,UAAU,GAAG;AAE1D,QAAI,KAAK;AACP,aAAO,QAAQ,IAAI,SAAS,GAAG;AAAA,IACjC;AAEA,YAAQ,IAAI,OAAO;AAAA,EACrB;AAAA,EAIA,KAAK,UAA8B,KAAoB;AACrD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,QAAQ,UAAU,GAAG;AAEzD,QAAI,KAAK;AACP,aAAO,QAAQ,IAAI,SAAS,GAAG;AAAA,IACjC;AAEA,YAAQ,IAAI,OAAO;AAAA,EACrB;AAAA,EAIA,KAAK,UAA8B,KAAoB;AACrD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,QAAQ,UAAU,GAAG;AAEzD,QAAI,KAAK;AACP,aAAO,QAAQ,KAAK,SAAS,GAAG;AAAA,IAClC;AAEA,YAAQ,KAAK,OAAO;AAAA,EACtB;AAAA,EAIA,MAAM,UAAgC,KAAoB;AACxD,UAAM,CAAC,SAAS,GAAG,IAAI,KAAK,QAAQ,SAAS,UAAU,GAAG;AAE1D,QAAI,KAAK;AACP,aAAO,QAAQ,MAAM,SAAS,GAAG;AAAA,IACnC;AAEA,YAAQ,MAAM,OAAO;AAAA,EACvB;AAAA,EAEA,MAAM,KAAwB;AAC5B,UAAM,cAAc,IAAI,MAAM,OAAO,IAAI,GAAG,IAAI,KAAK;AACrD,WAAO,IAAI,eAAc,WAAW;AAAA,EACtC;AACF;AAEO,IAAM,gBAAgB,IAAI,cAAc;;;ACvD/C,IAAM,wBAAN,MAA4B;AAAA,EAC1B,eAAe;AAAA,EACf;AAAA,EACA,YAA4C,CAAC;AAAA,EAC7C,oBAA0C,oBAAI,IAAI;AAAA,EAClD;AAAA,EACA;AAAA,EACA,gBAA0C,oBAAI,IAAI;AAAA,EAClD,UAAkB;AAAA,EAClB;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,MAAM,KAAK,QAA4B;AACrC,kBAAM,8CAA8C,MAAM;AAE1D,SAAK,gBAAgB,MAAM;AAE3B,SAAK,kBAAkB,MAAM;AAE7B,SAAK,kBAAkB,OAAO;AAC9B,SAAK,YAAY,OAAO;AACxB,SAAK,qBAAqB,OAAO;AACjC,SAAK,oBAAoB,OAAO;AAChC,SAAK,UAAU,OAAO,UAAU;AAChC,SAAK,cAAc,OAAO;AAE1B,QAAI,OAAO,QAAQ;AACjB,iBAAW,CAAC,OAAO,WAAW,KAAK,OAAO,QAAQ,OAAO,MAAM,GAAG;AAChE,aAAK,cAAc,IAAI,OAAO,WAA0B;AAAA,MAC1D;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,OAAO,UAAU,SAAS,GAAG;AACnD,YAAM,aAAa,MAAM,QAAQ,iBAAiB,OAAO,SAAS;AAElE,UAAI,eAAe,GAAG;AACpB,aAAK,QAAQ;AAAA,UACX,gCAAgC,OAAO,UAAU,KAAK,IAAI,CAAC;AAAA,QAE7D;AAAA,MACF;AAAA,IACF;AAEA,SAAK,eAAe;AAEpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,IAAI,SAA2B;AAC7B,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAW,wBAAwB;AAAA,IAC3C;AAEA,QAAI,CAAC,SAAS;AACZ,gBAAU,KAAK;AAAA,IACjB;AAGA,UAAM,SAAS,KAAK,kBAAkB,IAAI,OAAO;AACjD,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,UAAM,iBAAiB,KAAK,UAAU,OAAO;AAE7C,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAW,sBAAsB,CAAC,YAAY,OAAO,qBAAqB,CAAC;AAAA,IACnF;AAEA,kBAAM,sBAAsB,OAAO;AAEnC,QAAI;AACF,YAAM,WAAW,eAAe;AAChC,WAAK,kBAAkB,IAAI,SAAS,QAAQ;AAC5C,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAM,IAAW,qBAAqB,CAAC,SAAS,OAAO,CAAC;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,OAAoB;AAClB,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAW,wBAAwB;AAAA,IAC3C;AAEA,QAAI,KAAK,YAAY;AACnB,aAAO,KAAK,WAAW;AAAA,IACzB;AAEA,UAAM,cAAc,IAAI,YAAY;AAEpC,SAAK,aAAa;AAAA,MAChB,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,MACf,kBAAkB,KAAK;AAAA,MACvB,mBAAmB,KAAK;AAAA,MACxB,kBAAkB,KAAK;AAAA,MACvB,cAAc,KAAK;AAAA,MACnB,QAAQ,KAAK;AAAA,MACb,YAAY,KAAK;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,cAAc,MAAM;AAC1B,UAAM,eAA+C,CAAC;AAEtD,eAAW,QAAQ,OAAO,KAAK,KAAK,WAAW,QAAQ,GAAG;AACxD,mBAAa,IAAI,IAAI;AAAA,IACvB;AAEA,SAAK,YAAY;AACjB,SAAK,oBAAoB,oBAAI,IAAI;AAEjC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,QAAI,CAAC,KAAK,YAAY;AACpB;AAAA,IACF;AAEA,SAAK,KAAK,WAAW,YAAY,QAAQ;AAEzC,eAAW,WAAW,KAAK,kBAAkB,OAAO,GAAG;AACrD,WAAK,QAAQ,QAAQ;AAAA,IACvB;AAEA,UAAM,QAAQ,KAAK;AACnB,SAAK,aAAa;AAElB,SAAK,kBAAkB,MAAM;AAC7B,SAAK,YAAY,MAAM;AACvB,SAAK,oBAAoB,MAAM;AAC/B,SAAK,qBAAqB,MAAM;AAChC,SAAK,oBAAoB,MAAM;AAC/B,SAAK,gBAAgB,MAAM;AAC3B,SAAK,UAAU,MAAM;AACrB,SAAK,cAAc,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,qBAAqB,OAAe,gBAA2C;AAC7E,UAAM,cAAc,KAAK,cAAc,IAAI,KAAK;AAChD,UAAM,mBAAmB,aAAa,SAAS,CAAC;AAEhD,QAAI,aACF,gBAAgB,cAChB,iBAAiB,cACjB,KAAK,oBAAoB,cACzB;AAEF,QAAI,UACF,gBAAgB,WAAW,iBAAiB,WAAW,KAAK,oBAAoB;AAElF,WAAO,EAAE,YAAY,QAAQ;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAwC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,OAAe,YAAqC;AACtE,UAAM,cAAc,KAAK,cAAc,IAAI,KAAK;AAChD,UAAM,kBAAkB,aAAa;AAErC,WAAO;AAAA,MACL,kBACE,YAAY,oBACZ,iBAAiB,oBACjB,KAAK,mBAAmB;AAAA,MAC1B,cACE,YAAY,gBACZ,iBAAiB,gBACjB,KAAK,mBAAmB;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,gBAAgB,QAAkC;AAChD,QAAI,CAAC,OAAO,YAAY,OAAO,KAAK,OAAO,QAAQ,EAAE,WAAW,GAAG;AACjE,YAAM,IAAW,sBAAsB,CAAC,yCAAyC,CAAC;AAAA,IACpF;AAEA,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAW,sBAAsB,CAAC,mCAAmC,CAAC;AAAA,IAC9E;AAEA,QAAI,CAAC,OAAO,SAAS,OAAO,OAAO,GAAG;AACpC,YAAM,IAAW,sBAAsB;AAAA,QACrC,oBAAoB,OAAO,OAAO;AAAA,MACpC,CAAC;AAAA,IACH;AAEA,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AAC7D,UAAI,OAAO,YAAY,YAAY;AACjC,cAAM,IAAW,sBAAsB,CAAC,YAAY,IAAI,8BAA8B,CAAC;AAAA,MACzF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,UAAU;AACd,eAAW,CAAC,MAAM,OAAO,KAAK,KAAK,mBAAmB;AACpD,oBAAM,2BAA2B,IAAI;AACrC,YAAM,QAAQ,QAAQ;AAAA,IACxB;AAEA,QAAI,KAAK,YAAY;AACnB,YAAM,KAAK,WAAW,YAAY,QAAQ;AAE1C,iBAAW,CAAC,MAAM,OAAO,KAAK,KAAK,WAAW,kBAAkB;AAC9D,sBAAM,2BAA2B,IAAI;AACrC,cAAM,QAAQ,QAAQ;AAAA,MACxB;AAAA,IACF;AAEA,SAAK,kBAAkB,MAAM;AAC7B,SAAK,eAAe;AACpB,SAAK,aAAa;AAAA,EACpB;AACF;AAGO,IAAM,eAAe,IAAI,sBAAsB;;;AHxV/C,IAAM,gBAAN,MAAuB;AAAA,EACnB;AAAA,EACA;AAAA,EACT,SAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,MAAc,SAAY;AACpC,SAAK,QAAQ;AACb,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,QAAQ,OAAqB;AAC3B,SAAK,SAAS;AAEd,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,GAAG,OAAuB;AACxB,SAAK,SAAS;AAEd,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,SAAS,UAAwB;AAC/B,SAAK,YAAY;AAEjB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,SAAuB;AAC3B,SAAK,WAAW;AAEhB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,KAAK,SAAmC;AACtC,SAAK,WAAW;AAEhB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,MAA+B;AACnC,UAAM,KAAK,WAAW;AAEtB,kBAAM,kDAAkD,KAAK,OAAO,IAAI,KAAK,QAAQ;AAErF,UAAM,UAAU,KAAK,oBAAoB;AAEzC,UAAM,UAAU;AAAA,MACd;AAAA,MACA,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,UAAU;AAAA,MACV,UAAU,KAAK;AAAA,MACf,SAAS,KAAK;AAAA,IAChB;AAEA,QAAI,KAAK,QAAQ;AACf,YAAM,cAAc,MAAM,KAAK,MAAM;AAErC,YAAM,QAAQ,YAAY,KAAK,QAAQ,SAAS,WAAW;AAAA,IAC7D,OAAO;AACL,YAAM,QAAQ,OAAO,KAAK,QAAQ,OAAO;AAAA,IAC3C;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,KACE,aACA,YACc;AACd,WAAO,KAAK,IAAI,EAAE,KAAK,aAAa,UAAU;AAAA,EAChD;AAAA,EAEA,sBAA+B;AAC7B,QAAI,CAAC,KAAK,UAAU;AAClB,aAAO,aAAa,IAAI;AAAA,IAC1B;AAEA,QAAI,OAAO,KAAK,aAAa,UAAU;AACrC,aAAO,aAAa,IAAI,KAAK,QAAQ;AAAA,IACvC;AAEA,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;;;AI3OA,SAAS,cAAAC,mBAAkB;AAoCpB,IAAM,qBAAN,MAA4B;AAAA,EACxB;AAAA,EACA;AAAA,EACT,SAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,MAAc,UAAe;AACvC,SAAK,QAAQ;AACb,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,QAAQ,OAAqB;AAC3B,SAAK,SAAS;AAEd,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,SAAS,UAAwB;AAC/B,SAAK,YAAY;AAEjB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,SAAuB;AAC3B,SAAK,WAAW;AAEhB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,KAAK,SAAmC;AACtC,SAAK,WAAW;AAEhB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,MAAmC;AACvC,kBAAM,kCAAkC,KAAK,UAAU,QAAQ,KAAK,KAAK;AAEzE,UAAM,UAAU,KAAK,oBAAoB;AAEzC,UAAM,OAAO,KAAK,UAAU,IAAI,CAAC,aAAa;AAAA,MAC5C,IAAIC,YAAW;AAAA,MACf,MAAM,KAAK;AAAA,MACX;AAAA,MACA,UAAU;AAAA,MACV,UAAU,KAAK;AAAA,MACf,SAAS,KAAK;AAAA,IAChB,EAAE;AAEF,UAAM,QAAQ,WAAW,KAAK,QAAQ,IAAI;AAE1C,WAAO;AAAA,MACL,QAAQ,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,KACE,aACA,YACc;AACd,WAAO,KAAK,IAAI,EAAE,KAAK,aAAa,UAAU;AAAA,EAChD;AAAA,EAEA,sBAA+B;AAC7B,QAAI,CAAC,KAAK,UAAU;AAClB,aAAO,aAAa,IAAI;AAAA,IAC1B;AAEA,QAAI,OAAO,KAAK,aAAa,UAAU;AACrC,aAAO,aAAa,IAAI,KAAK,QAAQ;AAAA,IACvC;AAEA,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;;;AC7LA,SAAS,4BAA4B;AAqB9B,IAAM,kBAAN,MAA6D;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAoB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,MAAc,SAAc;AACtC,SAAK,QAAQ;AACb,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,GAAG,YAA0B;AAC3B,SAAK,MAAM;AACX,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,YAA0B;AAC7B,SAAK,kBAAkB;AACvB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAA0B;AAC9B,SAAK,WAAW,MAAM,QAAQ;AAC9B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,IAAkB;AACzB,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,MAAkB;AACrB,SAAK,QAAQ;AACb,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,GAAG,MAAkB;AACnB,SAAK,MAAM;AACX,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,MAAY,IAAgB;AAClC,WAAO,KAAK,KAAK,IAAI,EAAE,GAAG,EAAE;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAuB;AAC3B,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAA+B;AAEnC,QAAI,CAAC,KAAK,mBAAmB,CAAC,KAAK,UAAU;AAC3C,YAAM,IAAW,0BAA0B;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,KAAK,mBAAmB,KAAK,UAAU;AACzC,YAAM,IAAW,0BAA0B;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,IACH;AAGA,QAAI,KAAK,iBAAiB;AACxB,UAAI;AACF,6BAAqB,MAAM,KAAK,iBAAiB,EAAE,IAAI,KAAK,UAAU,CAAC;AAAA,MACzE,SAAS,OAAO;AACd,cAAM,IAAW,0BAA0B,CAAC,KAAK,iBAAkB,MAAgB,OAAO,CAAC;AAAA,MAC7F;AAAA,IACF;AAEA,UAAM,SAAyB;AAAA,MAC7B,IAAI,KAAK,OAAO,KAAK;AAAA,MACrB,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,gBAAgB,KAAK;AAAA,MACrB,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,MACX,IAAI,KAAK;AAAA,MACT,OAAO,KAAK;AAAA,IACd;AAEA,UAAM,UAAU,aAAa,IAAI;AACjC,UAAM,aAAa,MAAM,QAAQ,eAAe,MAAM;AAGtD,UAAM,YAAY,KAAK,oBAAoB;AAC3C,UAAM,QAAQ,eAAe,YAAY,EAAE,UAAU,CAAC;AAEtD,WAAO,EAAE,WAAW;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,sBAA4B;AAC1B,UAAM,MAAM,oBAAI,KAAK;AACrB,QAAI;AAEJ,QAAI,KAAK,iBAAiB;AACxB,YAAM,OAAO,qBAAqB,MAAM,KAAK,iBAAiB;AAAA,QAC5D,aAAa;AAAA,QACb,IAAI,KAAK;AAAA,MACX,CAAC;AACD,gBAAU,KAAK,KAAK,EAAE,OAAO;AAAA,IAC/B,OAAO;AAEL,gBAAU,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,QAAS;AAAA,IACnD;AAGA,QAAI,KAAK,SAAS,UAAU,KAAK,OAAO;AACtC,UAAI,KAAK,iBAAiB;AAExB,cAAM,OAAO,qBAAqB,MAAM,KAAK,iBAAiB;AAAA,UAC5D,aAAa,KAAK;AAAA,UAClB,IAAI,KAAK;AAAA,QACX,CAAC;AACD,kBAAU,KAAK,KAAK,EAAE,OAAO;AAAA,MAC/B,OAAO;AACL,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,KACE,aACA,YAC8B;AAC9B,WAAO,KAAK,IAAI,EAAE,KAAK,aAAa,UAAU;AAAA,EAChD;AACF;;;AC3JO,IAAe,MAAf,MAAkC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,OAAO,UAAsB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgB9B,IAAI,UAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,IAAI,UAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,IAAI,SAAkC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,SAAS,SAAkB,SAAqB,QAA4B;AAC1E,SAAK,WAAW;AAChB,SAAK,WAAW,OAAO,OAAO,OAAO;AACrC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,OAAO,SAEL,SACmD;AACnD,UAAM,UAAW,KAAa,WAAW,CAAC;AAC1C,UAAM,UAAU,QAAQ,QAAQ,KAAK;AAErC,UAAM,aAAa,IAAI,cAAkD,SAAS,OAAO;AAEzF,QAAI,QAAQ,OAAO;AACjB,iBAAW,QAAQ,QAAQ,KAAK;AAAA,IAClC;AAEA,QAAI,QAAQ,SAAS;AACnB,iBAAW,KAAK,QAAQ,OAAO;AAAA,IACjC;AAEA,QAAI,QAAQ,aAAa,QAAW;AAClC,iBAAW,SAAS,QAAQ,QAAQ;AAAA,IACtC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BA,OAAO,aAEL,UACwD;AACxD,UAAM,UAAW,KAAa,WAAW,CAAC;AAC1C,UAAM,UAAU,QAAQ,QAAQ,KAAK;AAErC,UAAM,aAAa,IAAI,mBAAuD,SAAS,QAAQ;AAE/F,QAAI,QAAQ,OAAO;AACjB,iBAAW,QAAQ,QAAQ,KAAK;AAAA,IAClC;AAEA,QAAI,QAAQ,SAAS;AACnB,iBAAW,KAAK,QAAQ,OAAO;AAAA,IACjC;AAEA,QAAI,QAAQ,aAAa,QAAW;AAClC,iBAAW,SAAS,QAAQ,QAAQ;AAAA,IACtC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2BA,OAAO,SAEL,SACiB;AACjB,UAAM,UAAW,KAAa,WAAW,CAAC;AAC1C,UAAM,UAAU,QAAQ,QAAQ,KAAK;AAErC,WAAO,IAAI,gBAAgB,SAAS,OAAO;AAAA,EAC7C;AA2CF;;;AR1QO,SAAS,OAAO;AACrB,SAAO,MAAM,IAAI,YAAY;AAC/B;AAKO,IAAM,cAAN,MAAqC;AAAA,EAC1C,UAAU,oBAAI,IAAuB;AAAA,EACrC,cAAc,oBAAI,IAAuB;AAAA,EACzC,eAAe,oBAAI,IAAqC;AAAA,EACxD,iBAAiB,oBAAI,IAAyB;AAAA,EAC9C,cAAc,oBAAI,IAAyB;AAAA,EAC3C,mBAAmB,oBAAI,IAAoB;AAAA,EAC3C,aAAa,oBAAI,IAA0B;AAAA,EAC3C,cAA+B,CAAC;AAAA,EAEhC,YAAY,WAAyB;AAAA,EAAC;AAAA,EAEtC,gBAAiC;AAC/B,WAAO,CAAC,GAAG,KAAK,WAAW;AAAA,EAC7B;AAAA,EAEA,gBAAgB,OAAgC;AAC9C,WAAO,KAAK,YAAY,OAAO,CAAC,WAAW,OAAO,UAAU,KAAK;AAAA,EACnE;AAAA,EAEA,WAAW,SAAyB,OAAiD;AACnF,WAAO,KAAK,YAAY,KAAK,CAAC,WAAW,KAAK,eAAe,QAAQ,SAAS,KAAK,CAAC;AAAA,EACtF;AAAA,EAEA,kBAAwB;AACtB,SAAK,cAAc,CAAC;AAAA,EACtB;AAAA,EAEA,QAAc;AACZ,eAAW,WAAW,KAAK,kBAAkB;AAC3C,mBAAa,OAAO;AAAA,IACtB;AAEA,SAAK,iBAAiB,MAAM;AAC5B,SAAK,QAAQ,MAAM;AACnB,SAAK,YAAY,MAAM;AACvB,SAAK,aAAa,MAAM;AACxB,SAAK,eAAe,MAAM;AAC1B,SAAK,YAAY,MAAM;AACvB,SAAK,WAAW,MAAM;AACtB,SAAK,cAAc,CAAC;AAAA,EACtB;AAAA,EAEA,aAAa,SAAyB,OAA4B;AAChE,UAAM,SAAS,KAAK,WAAW,SAAS,KAAK;AAC7C,WAAO,GAAG,QAAQ,KAAK,eAAe,6BAA6B,SAAS,KAAK,CAAC;AAAA,EACpF;AAAA,EAEA,gBAAgB,SAAyB,OAA4B;AACnE,UAAM,SAAS,KAAK,WAAW,SAAS,KAAK;AAC7C,WAAO,GAAG,CAAC,QAAQ,KAAK,eAAe,iCAAiC,SAAS,KAAK,CAAC;AAAA,EACzF;AAAA,EAEA,kBAAkB,OAAe,SAAoC;AACnE,UAAM,SAAS,SAAS,QACpB,KAAK,YAAY,OAAO,CAAC,WAAW,OAAO,UAAU,QAAQ,KAAK,EAAE,SACpE,KAAK,YAAY;AAErB,UAAM,SAAS,SAAS,QAAQ,QAAQ,QAAQ,KAAK,MAAM;AAC3D,WAAO,MAAM,QAAQ,OAAO,YAAY,KAAK,iBAAiB,MAAM,SAAS,MAAM,EAAE;AAAA,EACvF;AAAA,EAEA,sBAA4B;AAC1B,WAAO;AAAA,MACL,KAAK,YAAY;AAAA,MACjB;AAAA,MACA,sCAAsC,KAAK,YAAY,MAAM;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,MAAM,OAAwB;AAC5B,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,MAAM,OAAO,OAAgC;AAC3C,UAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,KAAK,CAAC;AAEzC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,KAAK,SAAiC;AAC1C,WAAO,KAAK,OAAO,WAAW,OAAO;AAAA,EACvC;AAAA,EAEA,MAAM,OAAO,OAAe,SAAiC;AAC3D,SAAK,YAAY,OAAO,OAAO;AAC/B,SAAK,SAAS,OAAO,OAAO;AAAA,EAC9B;AAAA,EAEA,MAAM,UAAU,SAAkB,OAA8B;AAC9D,WAAO,KAAK,YAAY,WAAW,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,YAAY,OAAe,SAAkB,OAA8B;AACzE,SAAK,YAAY,OAAO,SAAS,KAAK;AACtC,SAAK,cAAc,OAAO,SAAS,KAAK;AAExC,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,SAAS,MAAgC;AAC7C,WAAO,KAAK,WAAW,WAAW,IAAI;AAAA,EACxC;AAAA,EAEA,MAAM,WAAW,OAAe,MAAgC;AAC9D,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,OAAO,OAAO,GAAG;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,MAAM,MAAmC;AACvC,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EAEA,MAAM,QAAQ,OAA4C;AACxD,UAAM,OAAO,KAAK,QAAQ,IAAI,KAAK;AAEnC,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,aAAO;AAAA,IACT;AAGA,QAAI,YAAY;AAChB,QAAI,eAAe,KAAK,CAAC,EAAE,YAAY;AAEvC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,WAAW,KAAK,CAAC,EAAE,YAAY;AACrC,UAAI,WAAW,cAAc;AAC3B,uBAAe;AACf,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,UAAM,CAAC,GAAG,IAAI,KAAK,OAAO,WAAW,CAAC;AACtC,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,KAAK,IAAI;AAC5B,SAAK,YAAY,IAAI,IAAI,IAAI,EAAE,KAAK,YAAY,MAAM,CAAC;AAEvD,WAAO,EAAE,GAAG,KAAK,WAAW;AAAA,EAC9B;AAAA,EAEA,MAAM,YAAY,OAAe,OAAe,kBAAgD;AAC9F,UAAM,SAAS,KAAK,YAAY,IAAI,KAAK;AACzC,QAAI,CAAC,OAAQ;AAEb,SAAK,YAAY,OAAO,KAAK;AAE7B,QAAI,qBAAqB,UAAa,qBAAqB,MAAM;AAC/D;AAAA,IACF;AAEA,SAAK,cAAc,OAAO,aAAa,OAAO,KAAK,gBAAgB;AAAA,EACrE;AAAA,EAEA,MAAM,QACJ,OACA,OACA,OACA,cACe;AACf,UAAM,SAAS,KAAK,YAAY,IAAI,KAAK;AACzC,QAAI,CAAC,OAAQ;AAEb,SAAK,YAAY,OAAO,KAAK;AAE7B,QAAI,iBAAiB,UAAa,iBAAiB,MAAM;AACvD;AAAA,IACF;AAEA,SAAK,cAAc,OAAO,UAAU,OAAO,KAAK,cAAc,KAAK;AAAA,EACrE;AAAA,EAEA,MAAM,SAAS,OAAe,OAAe,SAA+B;AAC1E,UAAM,SAAS,KAAK,YAAY,IAAI,KAAK;AACzC,QAAI,CAAC,OAAQ;AAEb,SAAK,YAAY,OAAO,KAAK;AAE7B,UAAM,aAAa;AAAA,MACjB,GAAG,OAAO;AAAA,MACV,WAAW,OAAO,IAAI,YAAY,KAAK;AAAA,IACzC;AAEA,QAAI,SAAS;AACX,YAAM,QAAQ,QAAQ,QAAQ,IAAI,KAAK,IAAI;AAE3C,UAAI,QAAQ,GAAG;AACb,aAAK,cAAc,OAAO,YAAY,KAAK;AAC3C;AAAA,MACF;AAAA,IACF;AAEA,SAAK,SAAS,OAAO,UAAU;AAAA,EACjC;AAAA,EAEA,MAAM,mBACJ,OACA,kBACA,iBACiB;AACjB,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,YAAY;AAEhB,eAAW,CAAC,OAAO,MAAM,KAAK,KAAK,YAAY,QAAQ,GAAG;AACxD,UAAI,OAAO,UAAU,OAAO;AAC1B;AAAA,MACF;AAEA,YAAM,YAAY,MAAM,OAAO,aAAa;AAE5C,UAAI,CAAC,WAAW;AACd;AAAA,MACF;AAEA,YAAM,sBAAsB,OAAO,IAAI,gBAAgB;AAGvD,UAAI,uBAAuB,iBAAiB;AAE1C,aAAK,YAAY,OAAO,KAAK;AAC7B;AAAA,MACF;AAGA,WAAK,YAAY,OAAO,KAAK;AAE7B,YAAM,aAAa;AAAA,QACjB,GAAG,OAAO;AAAA,QACV,cAAc,sBAAsB;AAAA,MACtC;AAEA,WAAK,SAAS,OAAO,OAAO,UAAU;AACtC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,OAAe,OAA0C;AACpE,UAAM,SAAS,KAAK,YAAY,IAAI,KAAK;AACzC,QAAI,UAAU,OAAO,UAAU,OAAO;AACpC,aAAO,EAAE,QAAQ,UAAU,MAAM,OAAO,IAAI;AAAA,IAC9C;AAEA,UAAM,cAAc,KAAK,QAAQ,IAAI,KAAK;AAC1C,UAAM,UAAU,aAAa,KAAK,CAAC,QAAQ,IAAI,OAAO,KAAK;AAC3D,QAAI,SAAS;AACX,aAAO,EAAE,QAAQ,WAAW,MAAM,QAAQ;AAAA,IAC5C;AAEA,UAAM,UAAU,KAAK,aAAa,IAAI,KAAK,GAAG,IAAI,KAAK;AACvD,QAAI,SAAS;AACX,aAAO,EAAE,QAAQ,WAAW,MAAM,QAAQ,IAAI;AAAA,IAChD;AAEA,UAAM,YAAY,KAAK,aAAa,KAAK,gBAAgB,OAAO,KAAK;AACrE,QAAI,WAAW;AACb,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,KAAK,aAAa,KAAK,aAAa,OAAO,KAAK;AAC/D,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,UAAyB;AACvB,eAAW,WAAW,KAAK,kBAAkB;AAC3C,mBAAa,OAAO;AAAA,IACtB;AAEA,SAAK,iBAAiB,MAAM;AAE5B,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,eAAe,QAAyC;AAC5D,UAAM,KAAK,OAAO,MAAMC,YAAW;AACnC,UAAM,MAAM,oBAAI,KAAK;AAErB,UAAM,WAAyB;AAAA,MAC7B;AAAA,MACA,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,SAAS,OAAO,WAAW;AAAA,MAC3B,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO,QAAQ;AAAA,MACrB,IAAI,OAAO,MAAM;AAAA,MACjB,OAAO,OAAO,SAAS;AAAA,MACvB,UAAU;AAAA,MACV,WAAW;AAAA;AAAA,MACX,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,WAAW;AAAA,IACb;AAEA,SAAK,WAAW,IAAI,IAAI,QAAQ;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,IAA0C;AAC1D,WAAO,KAAK,WAAW,IAAI,EAAE,KAAK;AAAA,EACpC;AAAA,EAEA,MAAM,cAAc,SAAwD;AAC1E,UAAM,YAAY,MAAM,KAAK,KAAK,WAAW,OAAO,CAAC;AAErD,QAAI,SAAS,QAAQ;AACnB,aAAO,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,MAAM;AAAA,IAC5D;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,eACJ,IACA,SACe;AACf,UAAM,WAAW,KAAK,WAAW,IAAI,EAAE;AACvC,QAAI,CAAC,SAAU;AAEf,QAAI,QAAQ,WAAW,OAAW,UAAS,SAAS,QAAQ;AAC5D,QAAI,QAAQ,cAAc,OAAW,UAAS,YAAY,QAAQ;AAClE,QAAI,QAAQ,cAAc,OAAW,UAAS,YAAY,QAAQ;AAClE,QAAI,QAAQ,aAAa,OAAW,UAAS,WAAW,QAAQ;AAAA,EAClE;AAAA,EAEA,MAAM,eAAe,IAA2B;AAC9C,SAAK,WAAW,OAAO,EAAE;AAAA,EAC3B;AAAA,EAEA,MAAM,mBAAiD;AACrD,UAAM,MAAM,oBAAI,KAAK;AAGrB,UAAM,WAAW,MAAM,KAAK,KAAK,WAAW,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM;AAChE,UAAI,EAAE,WAAW,SAAU,QAAO;AAClC,UAAI,EAAE,cAAc,QAAQ,EAAE,YAAY,IAAK,QAAO;AACtD,UAAI,EAAE,UAAU,QAAQ,EAAE,YAAY,EAAE,MAAO,QAAO;AACtD,UAAI,EAAE,OAAO,QAAQ,MAAM,EAAE,GAAI,QAAO;AACxC,aAAO;AAAA,IACT,CAAC;AAED,QAAI,CAAC,SAAU,QAAO;AAGtB,QAAI,YAAyB;AAC7B,QAAI,SAAS,SAAS;AACpB,kBAAY,IAAI,KAAK,IAAI,QAAQ,IAAI,SAAS,OAAO;AAAA,IACvD,WAAW,SAAS,gBAAgB;AAClC,YAAM,OAAOC,sBAAqB,MAAM,SAAS,gBAAgB;AAAA,QAC/D,aAAa;AAAA,QACb,IAAI,SAAS,YAAY;AAAA,MAC3B,CAAC;AACD,kBAAY,KAAK,KAAK,EAAE,OAAO;AAAA,IACjC;AAGA,UAAM,cAAc,SAAS,WAAW;AACxC,QAAI,SAAS,UAAU,QAAQ,eAAe,SAAS,OAAO;AAC5D,kBAAY;AAAA,IACd;AAGA,QAAI,aAAa,SAAS,OAAO,QAAQ,YAAY,SAAS,IAAI;AAChE,kBAAY;AAAA,IACd;AAGA,UAAM,kBAAgC,EAAE,GAAG,SAAS;AAGpD,aAAS,YAAY;AACrB,aAAS,YAAY;AACrB,aAAS,WAAW;AAEpB,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,OAAe,SAAkB,OAAgB;AAC3D,SAAK,YAAY,KAAK;AAAA,MACpB;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,UAAU,KAAK,IAAI;AAAA,IACrB,CAAC;AAAA,EACH;AAAA,EAEA,SAAS,OAAe,SAAkB;AACxC,QAAI,CAAC,KAAK,QAAQ,IAAI,KAAK,GAAG;AAC5B,WAAK,QAAQ,IAAI,OAAO,CAAC,CAAC;AAAA,IAC5B;AAEA,SAAK,QAAQ,IAAI,KAAK,EAAG,KAAK,OAAO;AAAA,EACvC;AAAA,EAEA,cAAc,OAAe,SAAkB,OAAe;AAC5D,QAAI,CAAC,KAAK,aAAa,IAAI,KAAK,GAAG;AACjC,WAAK,aAAa,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACxC;AAEA,UAAM,YAAY,KAAK,IAAI,IAAI;AAC/B,SAAK,aAAa,IAAI,KAAK,EAAG,IAAI,QAAQ,IAAI,EAAE,KAAK,SAAS,WAAW,MAAM,CAAC;AAEhF,UAAM,UAAU,WAAW,MAAM;AAC/B,WAAK,iBAAiB,OAAO,OAAO;AACpC,WAAK,aAAa,IAAI,KAAK,GAAG,OAAO,QAAQ,EAAE;AAC/C,WAAK,SAAS,OAAO,OAAO;AAAA,IAC9B,GAAG,KAAK;AAER,SAAK,iBAAiB,IAAI,OAAO;AAAA,EACnC;AAAA,EAEA,cACE,OACA,QACA,KACA,WACA,OACA;AACA,UAAM,SAAoB;AAAA,MACxB;AAAA,MACA,MAAM;AAAA,MACN,YAAY,KAAK,IAAI;AAAA,MACrB,OAAO,OAAO;AAAA,IAChB;AAEA,UAAM,QAAQ,WAAW,cAAc,KAAK,iBAAiB,KAAK;AAElE,QAAI,CAAC,MAAM,IAAI,KAAK,GAAG;AACrB,YAAM,IAAI,OAAO,CAAC,CAAC;AAAA,IACrB;AAEA,UAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,YAAQ,KAAK,MAAM;AAEnB,QAAI,aAAa,cAAc,MAAM;AACnC,WAAK,gBAAgB,SAAS,SAAS;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,gBAAgB,SAAsB,WAAyB;AAC7D,QAAI,cAAc,SAAS,cAAc,MAAM;AAC7C;AAAA,IACF;AAEA,QAAI,UAAU,QAAQ,QAAW;AAC/B,YAAM,WAAW,MAAM,UAAU,GAAG;AACpC,UAAI,WAAW,GAAG;AAChB,cAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,cAAM,WAAW,QAAQ,OAAO,CAAC,YAAY,OAAO,cAAc,MAAM,MAAM;AAC9E,gBAAQ,OAAO,GAAG,QAAQ,QAAQ,GAAG,QAAQ;AAAA,MAC/C;AAAA,IACF;AAEA,QAAI,UAAU,UAAU,UAAa,UAAU,QAAQ,KAAK,QAAQ,SAAS,UAAU,OAAO;AAC5F,cAAQ,OAAO,GAAG,QAAQ,SAAS,UAAU,KAAK;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,aAAa,OAAiC,OAAe,OAAiC;AAC5F,UAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,QAAI,CAAC,QAAS,QAAO;AAErB,WAAO,QAAQ,KAAK,CAAC,WAAW,OAAO,KAAK,OAAO,KAAK,KAAK;AAAA,EAC/D;AAAA,EAEA,eAAe,QAAuB,SAAyB,OAA+B;AAC5F,QAAI,OAAO,SAAS,OAAO,UAAU,MAAM,OAAO;AAChD,aAAO;AAAA,IACT;AAEA,UAAM,aACJ,OAAO,YAAY,WACf,OAAO,IAAI,SAAS,UACpB,KAAK,YAAY,OAAO,IACtB,OAAO,IAAI,SAAS,KAAK,iBAAiB,OAAO,IACjD,QAAQ,OAAO,GAAG;AAE1B,QAAI,CAAC,YAAY;AACf,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,YAAY,QAAW;AAChC,YAAM,iBAAiB,MAAM;AAC7B,YAAM,iBACJ,OAAO,mBAAmB,aACtB,eAAe,OAAO,IAAI,OAAO,IACjC,kBAAkB,OAAO,IAAI,SAAS,cAAc;AAE1D,UAAI,CAAC,gBAAgB;AACnB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,QAAW;AAC9B,YAAM,eAAe,MAAM;AAC3B,YAAM,eACJ,OAAO,iBAAiB,aACpB,aAAa,OAAO,KAAK,IACzB,OAAO,UAAU;AAEvB,UAAI,CAAC,cAAc;AACjB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,QAAgB,SAAyB,OAA8B;AACpF,UAAM,QAAQ,CAAC,MAAM;AAErB,UAAM,cAAc,KAAK,gBAAgB,OAAO;AAChD,QAAI,aAAa;AACf,YAAM,KAAK,QAAQ,WAAW,GAAG;AAAA,IACnC;AAEA,QAAI,OAAO,OAAO;AAChB,YAAM,KAAK,OAAO,MAAM,KAAK,GAAG;AAAA,IAClC;AAEA,QAAI,OAAO,YAAY,QAAW;AAChC,YAAM,KAAK,uBAAuB;AAAA,IACpC;AAEA,QAAI,OAAO,UAAU,QAAW;AAC9B,YAAM,KAAK,qBAAqB;AAAA,IAClC;AAEA,UAAM,SAAS,KAAK,YAAY,SAC5B,gBAAgB,KAAK,YAAY,IAAI,CAAC,WAAW,OAAO,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC,KAC5E;AAEJ,WAAO,GAAG,MAAM,KAAK,GAAG,CAAC,KAAK,MAAM;AAAA,EACtC;AAAA,EAEA,gBAAgB,SAA6C;AAC3D,QAAI,OAAO,YAAY,UAAU;AAC/B,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,YAAY,OAAO,GAAG;AAC7B,aAAO,KAAK,iBAAiB,OAAO;AAAA,IACtC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,SAA8C;AACxD,WAAO,OAAO,YAAY,cAAc,QAAQ,qBAAqB;AAAA,EACvE;AAAA,EAEA,iBAAiB,UAA4B;AAC3C,WAAO,SAAS,SAAS,QAAQ,SAAS;AAAA,EAC5C;AACF;","names":["randomUUID","CronExpressionParser","randomUUID","randomUUID","randomUUID","CronExpressionParser"]}