@lara-node/queue 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1782 -0
- package/dist/index.d.cts +604 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +604 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1744 -0
- package/dist/index.js.map +1 -0
- package/dist/queue.config.cjs +41 -0
- package/dist/queue.config.js +38 -0
- package/dist/queue.config.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["parser"],"sources":["../src/Job.ts","../src/Drivers/SyncDriver.ts","../src/Queue/QueueJob.ts","../src/Queue/FailedJob.ts","../src/Drivers/DatabaseDriver.ts","../src/Drivers/RedisDriver.ts","../src/Queue.ts","../src/Worker.ts","../src/Scheduler.ts","../src/QueueServiceProvider.ts"],"sourcesContent":["import crypto from \"crypto\";\nimport { SerializedJob, JobOptions } from \"./types.js\";\nimport queueConfig from \"./queue.config.js\";\n\n/*\n|--------------------------------------------------------------------------\n| Payload Encryption Helpers\n|--------------------------------------------------------------------------\n| AES-256-GCM encryption for job payloads when shouldBeEncrypted = true.\n| Uses APP_KEY from environment, same pattern as auth.ts.\n*/\n\nlet _encryptionKey: Buffer | undefined;\nfunction getEncryptionKey(): Buffer {\n if (_encryptionKey) return _encryptionKey;\n const raw = process.env.APP_KEY;\n if (!raw) throw new Error(\"APP_KEY is not set — cannot encrypt job payload.\");\n const stripped = raw.replace(/^base64:/, \"\");\n const decoded = Buffer.from(stripped, \"base64\");\n _encryptionKey =\n decoded.length === 32 ? decoded : crypto.createHash(\"sha256\").update(decoded).digest();\n return _encryptionKey;\n}\n\nexport function encryptPayload(data: string): string {\n const key = getEncryptionKey();\n const iv = crypto.randomBytes(12);\n const cipher = crypto.createCipheriv(\"aes-256-gcm\", key, iv);\n let encrypted = cipher.update(data, \"utf8\", \"base64\");\n encrypted += cipher.final(\"base64\");\n const tag = cipher.getAuthTag();\n return [iv.toString(\"base64\"), tag.toString(\"base64\"), encrypted].join(\":\");\n}\n\nexport function decryptPayload(data: string): string {\n const key = getEncryptionKey();\n const [ivB64, tagB64, encrypted] = data.split(\":\");\n const iv = Buffer.from(ivB64, \"base64\");\n const tag = Buffer.from(tagB64, \"base64\");\n const decipher = crypto.createDecipheriv(\"aes-256-gcm\", key, iv);\n decipher.setAuthTag(tag);\n let decrypted = decipher.update(encrypted, \"base64\", \"utf8\");\n decrypted += decipher.final(\"utf8\");\n return decrypted;\n}\n\n/*\n|--------------------------------------------------------------------------\n| Job Base Class\n|--------------------------------------------------------------------------\n|\n| This is the base class for all queueable jobs. It provides the\n| serialization, deserialization, and dispatch functionality.\n|\n*/\n\n// Global job registry\nconst jobRegistry: Map<string, new () => Job> = new Map();\n\nexport function registerJob(name: string, jobClass: new () => Job): void {\n jobRegistry.set(name, jobClass);\n}\n\nexport function getJobClass(name: string): (new () => Job) | undefined {\n return jobRegistry.get(name);\n}\n\nexport function getRegisteredJobs(): Map<string, new () => Job> {\n return jobRegistry;\n}\n\n// Decorator to auto-register jobs\nexport function Queueable(name?: string) {\n return function <T extends new (...args: any[]) => Job>(constructor: T) {\n const jobName = name || constructor.name;\n registerJob(jobName, constructor as unknown as new () => Job);\n return constructor;\n };\n}\n\nexport abstract class Job {\n /*\n |--------------------------------------------------------------------------\n | Job Configuration\n |--------------------------------------------------------------------------\n */\n\n /**\n * The name of the queue the job should be sent to.\n */\n public queue: string = \"default\";\n\n /**\n * The name of the connection the job should be sent to.\n */\n public connection: string = queueConfig.default;\n\n /**\n * The number of times the job may be attempted.\n */\n public tries: number = queueConfig.defaults.tries;\n\n /**\n * The maximum number of unhandled exceptions to allow before failing.\n */\n public maxExceptions: number = queueConfig.defaults.maxExceptions;\n\n /**\n * The number of seconds the job can run before timing out.\n */\n public timeout: number = queueConfig.defaults.timeout;\n\n /**\n * The number of seconds to wait before retrying the job.\n */\n public backoff: number | number[] = queueConfig.defaults.backoff;\n\n /**\n * Indicate if the job should be encrypted.\n */\n public shouldBeEncrypted: boolean = false;\n\n /**\n * The unique ID of the job (for unique jobs).\n */\n public uniqueId?: string;\n\n /**\n * The number of seconds the unique lock should be held.\n */\n public uniqueFor?: number;\n\n /**\n * Number of seconds to delay the job.\n */\n public delay: number = 0;\n\n /**\n * Job UUID.\n */\n private _uuid: string = \"\";\n\n /**\n * Current attempt number.\n */\n private _attempts: number = 0;\n\n /*\n |--------------------------------------------------------------------------\n | Abstract Methods\n |--------------------------------------------------------------------------\n */\n\n /**\n * Execute the job.\n */\n abstract handle(): Promise<void>;\n\n /*\n |--------------------------------------------------------------------------\n | Job Lifecycle Hooks\n |--------------------------------------------------------------------------\n */\n\n /**\n * Handle a job failure.\n */\n failed(exception: Error): void {\n // Override in subclass to handle failures\n }\n\n /**\n * Determine the time at which the job should timeout.\n */\n retryUntil(): Date | null {\n return null;\n }\n\n /**\n * Get the middleware the job should pass through.\n */\n middleware(): any[] {\n return [];\n }\n\n /**\n * Get the tags that should be assigned to the job.\n */\n tags(): string[] {\n return [];\n }\n\n /*\n |--------------------------------------------------------------------------\n | Getters\n |--------------------------------------------------------------------------\n */\n\n get uuid(): string {\n return this._uuid;\n }\n\n get attempts(): number {\n return this._attempts;\n }\n\n /*\n |--------------------------------------------------------------------------\n | Serialization\n |--------------------------------------------------------------------------\n */\n\n /**\n * Get the display name for the queued job.\n */\n displayName(): string {\n return this.constructor.name;\n }\n\n /**\n * Prepare the job for serialization.\n */\n protected getSerializableProperties(): Record<string, any> {\n const props: Record<string, any> = {};\n\n // Get all own properties except private ones\n for (const key of Object.keys(this)) {\n if (!key.startsWith(\"_\")) {\n props[key] = (this as any)[key];\n }\n }\n\n return props;\n }\n\n /**\n * Serialize the job to a format that can be stored.\n */\n serialize(): SerializedJob {\n const uuid = this._uuid || crypto.randomUUID();\n this._uuid = uuid;\n\n const retryUntilDate = this.retryUntil();\n const rawData = JSON.stringify(this.getSerializableProperties());\n\n return {\n id: crypto.randomUUID(),\n uuid,\n displayName: this.displayName(),\n job: this.constructor.name,\n data: this.shouldBeEncrypted ? encryptPayload(rawData) : rawData,\n encrypted: this.shouldBeEncrypted,\n queue: this.queue,\n attempts: this._attempts,\n maxTries: this.tries,\n maxExceptions: this.maxExceptions,\n exceptionCount: 0,\n timeout: this.timeout,\n backoff: this.backoff,\n retryUntil: retryUntilDate ? retryUntilDate.getTime() : null,\n createdAt: Date.now(),\n availableAt: Date.now() + this.delay * 1000,\n reservedAt: null,\n };\n }\n\n /**\n * Restore the job from serialized data.\n */\n static deserialize<T extends Job>(serialized: SerializedJob): T | null {\n const JobClass = getJobClass(serialized.job);\n\n if (!JobClass) {\n console.error(\n `Job class \"${serialized.job}\" not found in registry. Make sure to use @Queueable decorator.`,\n );\n return null;\n }\n\n const instance = new JobClass() as T;\n\n let rawData = serialized.data;\n if (serialized.encrypted) {\n try {\n rawData = decryptPayload(rawData);\n } catch (e) {\n console.error(`[Job] Failed to decrypt payload for ${serialized.job}:`, e);\n return null;\n }\n }\n\n const data = JSON.parse(rawData);\n\n for (const [key, value] of Object.entries(data)) {\n (instance as any)[key] = value;\n }\n\n (instance as any)._uuid = serialized.uuid;\n (instance as any)._attempts = serialized.attempts;\n\n return instance;\n }\n\n /*\n |--------------------------------------------------------------------------\n | Dispatching\n |--------------------------------------------------------------------------\n */\n\n /**\n * Dispatch the job with the given arguments.\n */\n static dispatch<T extends Job>(this: new () => T, ...args: any[]): PendingDispatch<T> {\n const job = new this();\n return new PendingDispatch(job);\n }\n\n /**\n * Dispatch the job synchronously.\n */\n static dispatchSync<T extends Job>(this: new () => T): Promise<void> {\n const job = new this();\n return job.handle();\n }\n\n /**\n * Dispatch the job after the response is sent.\n */\n static dispatchAfterResponse<T extends Job>(this: new () => T): PendingDispatch<T> {\n const job = new this();\n return new PendingDispatch(job).afterResponse();\n }\n\n /*\n |--------------------------------------------------------------------------\n | Fluent Configuration Methods\n |--------------------------------------------------------------------------\n */\n\n onQueue(queue: string): this {\n this.queue = queue;\n return this;\n }\n\n onConnection(connection: string): this {\n this.connection = connection;\n return this;\n }\n\n withDelay(seconds: number): this {\n this.delay = seconds;\n return this;\n }\n\n withTries(tries: number): this {\n this.tries = tries;\n return this;\n }\n\n withTimeout(seconds: number): this {\n this.timeout = seconds;\n return this;\n }\n\n withBackoff(backoff: number | number[]): this {\n this.backoff = backoff;\n return this;\n }\n}\n\n/*\n|--------------------------------------------------------------------------\n| Pending Dispatch\n|--------------------------------------------------------------------------\n|\n| This class represents a job that is pending dispatch and allows\n| for fluent configuration before actually dispatching.\n|\n*/\n\nexport class PendingDispatch<T extends Job> {\n private _afterResponse: boolean = false;\n\n constructor(private job: T) {}\n\n onQueue(queue: string): this {\n this.job.onQueue(queue);\n return this;\n }\n\n onConnection(connection: string): this {\n this.job.onConnection(connection);\n return this;\n }\n\n delay(seconds: number): this {\n this.job.withDelay(seconds);\n return this;\n }\n\n tries(n: number): this {\n this.job.withTries(n);\n return this;\n }\n\n timeout(seconds: number): this {\n this.job.withTimeout(seconds);\n return this;\n }\n\n backoff(b: number | number[]): this {\n this.job.withBackoff(b);\n return this;\n }\n\n maxExceptions(n: number): this {\n this.job.maxExceptions = n;\n return this;\n }\n\n afterResponse(): this {\n this._afterResponse = true;\n return this;\n }\n\n async dispatch(): Promise<string> {\n const { Queue } = await import(\"./Queue.js\");\n\n if (this._afterResponse) {\n setImmediate(async () => {\n await Queue.push(this.job);\n });\n return this.job.uuid;\n }\n\n return Queue.push(this.job);\n }\n}\n\n/*\n|--------------------------------------------------------------------------\n| Helper Function\n|--------------------------------------------------------------------------\n*/\n\nexport function dispatch<T extends Job>(job: T): PendingDispatch<T> {\n return new PendingDispatch(job);\n}\n","import { QueueDriverInterface, SerializedJob } from \"../types.js\";\nimport queueConfig from \"../queue.config.js\";\n\n/*\n|--------------------------------------------------------------------------\n| Sync Queue Driver\n|--------------------------------------------------------------------------\n|\n| Executes jobs synchronously — useful for local development or testing.\n| Respects maxTries and backoff; retries happen inline without real delays.\n|\n*/\n\nexport class SyncDriver implements QueueDriverInterface {\n private jobs: Map<string, SerializedJob[]> = new Map();\n\n async size(queue: string = \"default\"): Promise<number> {\n return this.jobs.get(queue)?.length ?? 0;\n }\n\n async push(job: SerializedJob, queue: string = \"default\"): Promise<string> {\n if (!this.jobs.has(queue)) this.jobs.set(queue, []);\n\n const { Job: JobBase } = await import(\"../Job.js\");\n\n const maxTries = job.maxTries || queueConfig.defaults.tries;\n const maxExceptions = job.maxExceptions ?? queueConfig.defaults.maxExceptions;\n\n let lastError: Error | null = null;\n\n while (job.attempts < maxTries && (job.exceptionCount ?? 0) < maxExceptions) {\n job.attempts += 1;\n job.exceptionCount = job.exceptionCount ?? 0;\n\n const instance = JobBase.deserialize(job);\n if (!instance) break;\n\n try {\n await instance.handle();\n // Success — stop retrying\n return job.id;\n } catch (error) {\n lastError = error as Error;\n job.exceptionCount += 1;\n\n const shouldRetry = job.attempts < maxTries && job.exceptionCount < maxExceptions;\n\n if (!shouldRetry) break;\n\n // Sync driver: no real delay between retries\n console.warn(\n `[Sync] Job ${job.displayName} failed (attempt ${job.attempts}/${maxTries}), retrying...`,\n );\n }\n }\n\n // Permanent failure\n if (lastError) {\n console.error(\n `[Sync] Job ${job.displayName} failed permanently after ${job.attempts} attempt(s):`,\n lastError.message,\n );\n const instance = JobBase.deserialize(job);\n if (instance) {\n try {\n instance.failed(lastError);\n } catch {\n // swallow errors from failed()\n }\n }\n throw lastError;\n }\n\n return job.id;\n }\n\n async later(delay: number, job: SerializedJob, queue: string = \"default\"): Promise<string> {\n // Fire-and-forget — does not block the caller\n setTimeout(() => {\n this.push(job, queue).catch((err) => {\n console.error(`[Sync] Delayed job ${job.displayName} failed:`, err);\n });\n }, delay * 1000);\n return job.id;\n }\n\n async pop(queue: string = \"default\"): Promise<SerializedJob | null> {\n const jobs = this.jobs.get(queue);\n if (!jobs || jobs.length === 0) return null;\n return jobs.shift() ?? null;\n }\n\n async delete(job: SerializedJob, queue: string = \"default\"): Promise<void> {\n const jobs = this.jobs.get(queue);\n if (jobs) {\n const index = jobs.findIndex((j) => j.id === job.id);\n if (index > -1) jobs.splice(index, 1);\n }\n }\n\n async release(job: SerializedJob, delay: number, queue: string = \"default\"): Promise<void> {\n job.availableAt = Date.now() + delay * 1000;\n job.reservedAt = null;\n if (!this.jobs.has(queue)) this.jobs.set(queue, []);\n this.jobs.get(queue)!.push(job);\n }\n\n async clear(queue: string = \"default\"): Promise<number> {\n const count = this.jobs.get(queue)?.length ?? 0;\n this.jobs.set(queue, []);\n return count;\n }\n\n async getJobs(queue: string = \"default\"): Promise<SerializedJob[]> {\n return this.jobs.get(queue) ?? [];\n }\n}\n","import { Model } from \"@lara-node/db\";\n\n/*\n|--------------------------------------------------------------------------\n| Queue Job Model\n|--------------------------------------------------------------------------\n|\n| This model represents a job in the queue.\n|\n*/\n\nexport class QueueJob extends Model {\n static table = \"jobs\";\n static primaryKey = \"id\";\n static timestamps = false;\n\n static fillable = [\n \"uuid\",\n \"queue\",\n \"payload\",\n \"attempts\",\n \"reserved_at\",\n \"available_at\",\n \"created_at\",\n ];\n\n static casts = {\n attempts: \"int\",\n reserved_at: \"int\",\n available_at: \"int\",\n created_at: \"int\",\n } as any;\n\n /*\n |--------------------------------------------------------------------------\n | Scopes\n |--------------------------------------------------------------------------\n */\n\n /**\n * Scope to get jobs for a specific queue.\n */\n static scopeForQueue(query: any, queue: string) {\n return query.where(\"queue\", queue);\n }\n\n /**\n * Scope to get available jobs (not reserved or reservation expired).\n */\n static scopeAvailable(query: any, now: number, retryAfter: number) {\n const expiredReservation = now - retryAfter;\n return query\n .where(function (q: any) {\n q.whereNull(\"reserved_at\").orWhere(\"reserved_at\", \"<\", expiredReservation);\n })\n .where(\"available_at\", \"<=\", now);\n }\n\n /**\n * Scope to get the next available job.\n */\n static scopeNextAvailable(query: any, queue: string, now: number, retryAfter: number) {\n return query.forQueue(queue).available(now, retryAfter).orderBy(\"id\", \"asc\");\n }\n\n /*\n |--------------------------------------------------------------------------\n | Helpers\n |--------------------------------------------------------------------------\n */\n\n /**\n * Get the parsed payload.\n */\n getParsedPayload(): any {\n try {\n return JSON.parse(this.payload);\n } catch {\n return null;\n }\n }\n\n /**\n * Mark the job as reserved.\n */\n async reserve(timestamp: number): Promise<void> {\n this.reserved_at = timestamp;\n this.attempts = (this.attempts || 0) + 1;\n await this.save();\n }\n\n /**\n * Release the job back to the queue.\n */\n async release(availableAt: number, payload?: string): Promise<void> {\n this.reserved_at = null;\n this.available_at = availableAt;\n if (payload) {\n this.payload = payload;\n }\n await this.save();\n }\n}\n\nexport default QueueJob;\n","import { Model } from \"@lara-node/db\";\n\n/*\n|--------------------------------------------------------------------------\n| Failed Job Model\n|--------------------------------------------------------------------------\n|\n| This model represents a failed job in the queue.\n|\n*/\n\nexport class FailedJob extends Model {\n static table = \"failed_jobs\";\n static primaryKey = \"id\";\n static timestamps = false;\n\n static fillable = [\"uuid\", \"connection\", \"queue\", \"payload\", \"exception\", \"failed_at\"];\n\n static casts = {\n id: \"int\",\n failed_at: \"datetime\",\n } as any;\n\n /*\n |--------------------------------------------------------------------------\n | Scopes\n |--------------------------------------------------------------------------\n */\n\n /**\n * Scope to find by UUID.\n */\n static scopeByUuid(query: any, uuid: string) {\n return query.where(\"uuid\", uuid);\n }\n\n /**\n * Scope to get jobs for a specific queue.\n */\n static scopeForQueue(query: any, queue: string) {\n return query.where(\"queue\", queue);\n }\n\n /**\n * Scope to get jobs for a specific connection.\n */\n static scopeForConnection(query: any, connection: string) {\n return query.where(\"connection\", connection);\n }\n\n /*\n |--------------------------------------------------------------------------\n | Helpers\n |--------------------------------------------------------------------------\n */\n\n /**\n * Get the parsed payload.\n */\n getParsedPayload(): any {\n try {\n return JSON.parse(this.payload);\n } catch {\n return null;\n }\n }\n\n /**\n * Get a summary of the exception.\n */\n getExceptionSummary(maxLength: number = 100): string {\n if (!this.exception) return \"\";\n const firstLine = this.exception.split(\"\\n\")[0];\n return firstLine.length > maxLength ? firstLine.substring(0, maxLength) + \"...\" : firstLine;\n }\n}\n\nexport default FailedJob;\n","import { QueueDriverInterface, FailedJobsInterface, SerializedJob } from \"../types.js\";\nimport queueConfig from \"../queue.config.js\";\nimport QueueJob from \"../Queue/QueueJob.js\";\nimport FailedJob from \"../Queue/FailedJob.js\";\n\n/*\n|--------------------------------------------------------------------------\n| Database Queue Driver\n|--------------------------------------------------------------------------\n|\n| Stores jobs in a database table. Every state transition uses an explicit\n| query-based UPDATE so attempts, reserved_at, available_at, and payload\n| are always written — no reliance on ORM dirty-tracking.\n|\n*/\n\nexport class DatabaseDriver implements QueueDriverInterface, FailedJobsInterface {\n private defaultQueue: string;\n private retryAfter: number;\n\n constructor(config?: { table?: string; queue?: string; retry_after?: number }) {\n const dbConfig = queueConfig.connections.database;\n this.defaultQueue = config?.queue || dbConfig.queue || \"default\";\n this.retryAfter = config?.retry_after || dbConfig.retry_after || 90;\n }\n\n async size(queue: string = this.defaultQueue): Promise<number> {\n return await QueueJob.query().where(\"queue\", queue).count();\n }\n\n async push(job: SerializedJob, queue: string = this.defaultQueue): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n const availableAt = Math.floor(job.availableAt / 1000);\n\n await QueueJob.create({\n uuid: job.uuid,\n queue: queue,\n payload: JSON.stringify(job),\n attempts: 0,\n available_at: availableAt,\n created_at: now,\n });\n\n return job.id;\n }\n\n async later(\n delay: number,\n job: SerializedJob,\n queue: string = this.defaultQueue,\n ): Promise<string> {\n job.availableAt = Date.now() + delay * 1000;\n return this.push(job, queue);\n }\n\n async pop(queue: string = this.defaultQueue): Promise<SerializedJob | null> {\n const now = Math.floor(Date.now() / 1000);\n const expiredReservation = now - this.retryAfter;\n\n // Fetch next available job (not reserved, or reservation expired)\n const queueJob = await QueueJob.query()\n .where(\"queue\", queue)\n .where(function (q: any) {\n q.whereNull(\"reserved_at\").orWhere(\"reserved_at\", \"<\", expiredReservation);\n })\n .where(\"available_at\", \"<=\", now)\n .orderBy(\"id\", \"asc\")\n .first();\n\n if (!queueJob) return null;\n\n // Compute the new attempt count before writing it\n const newAttempts = (queueJob.attempts || 0) + 1;\n\n // Explicit UPDATE — never relies on ORM dirty-tracking for attempts\n await QueueJob.query().where(\"id\", queueJob.id).update({\n reserved_at: now,\n attempts: newAttempts,\n });\n\n const job: SerializedJob = JSON.parse(queueJob.payload);\n job.reservedAt = now * 1000;\n job.attempts = newAttempts;\n\n // Carry the DB row ID privately so release/delete can target it by PK\n (job as any).__dbRowId = queueJob.id;\n\n return job;\n }\n\n async delete(job: SerializedJob, _queue: string = this.defaultQueue): Promise<void> {\n const rowId = (job as any).__dbRowId;\n\n if (rowId != null) {\n await QueueJob.query().where(\"id\", rowId).delete();\n } else {\n await QueueJob.query().where(\"uuid\", job.uuid).delete();\n }\n }\n\n async release(\n job: SerializedJob,\n delay: number,\n _queue: string = this.defaultQueue,\n ): Promise<void> {\n // availableAt stored as Unix seconds in the DB column\n const availableAt = Math.floor((Date.now() + delay * 1000) / 1000);\n const rowId = (job as any).__dbRowId;\n\n // Update the SerializedJob object before serialising so the stored payload\n // reflects the latest state (attempts, exceptionCount, availableAt).\n job.availableAt = availableAt * 1000;\n job.reservedAt = null;\n\n // Strip the private routing key from the payload before writing to DB\n const { __dbRowId: _strip, ...cleanJob } = job as any;\n const payload = JSON.stringify(cleanJob);\n\n // Explicit UPDATE — always writes reserved_at, available_at, attempts, payload\n if (rowId != null) {\n await QueueJob.query().where(\"id\", rowId).update({\n reserved_at: null,\n available_at: availableAt,\n attempts: job.attempts,\n payload: payload,\n });\n } else {\n await QueueJob.query().where(\"uuid\", job.uuid).update({\n reserved_at: null,\n available_at: availableAt,\n attempts: job.attempts,\n payload: payload,\n });\n }\n }\n\n async clear(queue: string = this.defaultQueue): Promise<number> {\n const count = await this.size(queue);\n await QueueJob.query().where(\"queue\", queue).delete();\n return count;\n }\n\n async getJobs(queue: string = this.defaultQueue): Promise<SerializedJob[]> {\n const jobs = await QueueJob.query().where(\"queue\", queue).orderBy(\"id\", \"asc\").get();\n return jobs.map((job: QueueJob) => JSON.parse(job.payload));\n }\n\n /*\n |--------------------------------------------------------------------------\n | Failed Jobs Management\n |--------------------------------------------------------------------------\n */\n\n async logFailed(\n connection: string,\n queue: string,\n job: SerializedJob,\n exception: Error,\n ): Promise<void> {\n // Strip routing metadata from the persisted payload\n const { __dbRowId: _strip, ...cleanJob } = job as any;\n await FailedJob.create({\n uuid: job.uuid,\n connection: connection,\n queue: queue,\n payload: JSON.stringify(cleanJob),\n exception: exception.stack || exception.message,\n });\n }\n\n async getFailedJobs(): Promise<any[]> {\n const jobs = await FailedJob.query().orderBy(\"failed_at\", \"desc\").get();\n return jobs.map((job: FailedJob) => job.toJSON());\n }\n\n async retryFailed(uuid: string): Promise<boolean> {\n const failedJob = await FailedJob.query().where(\"uuid\", uuid).first();\n if (!failedJob) return false;\n\n const job: SerializedJob = JSON.parse(failedJob.payload);\n\n // Reset all retry counters\n job.attempts = 0;\n job.exceptionCount = 0;\n job.reservedAt = null;\n job.availableAt = Date.now();\n\n await this.push(job, failedJob.queue);\n await this.forgetFailed(uuid);\n\n return true;\n }\n\n async forgetFailed(uuid: string): Promise<boolean> {\n const deleted = await FailedJob.query().where(\"uuid\", uuid).delete();\n return deleted > 0;\n }\n\n async flushFailed(): Promise<number> {\n const count = await FailedJob.query().count();\n await FailedJob.query().delete();\n return count;\n }\n}\n","import { createClient, RedisClientType } from \"redis\";\nimport { QueueDriverInterface, FailedJobsInterface, SerializedJob } from \"../types.js\";\nimport queueConfig from \"../queue.config.js\";\n\n/*\n|--------------------------------------------------------------------------\n| Redis Queue Driver\n|--------------------------------------------------------------------------\n|\n| Uses Redis data structures for high-performance job processing:\n| - Main queue: List (RPUSH / LPOP)\n| - Delayed jobs: ZSet scored by availableAt (ms), member = uuid\n| - Reserved: Hash keyed by uuid → full payload JSON\n| - Delayed body: Hash keyed by uuid → full payload JSON\n| - Failed jobs: Hash keyed by uuid → failed job JSON\n|\n| UUID-keyed reserved / delayed body hashes eliminate the fragile\n| \"payload must exactly match\" issue that plagued the old ZSet approach.\n|\n*/\n\nexport class RedisDriver implements QueueDriverInterface, FailedJobsInterface {\n private client: RedisClientType | null = null;\n private defaultQueue: string;\n private retryAfter: number;\n private prefix: string;\n private initialized: boolean = false;\n\n constructor(config?: { queue?: string; retry_after?: number; prefix?: string }) {\n const redisConfig = queueConfig.connections.redis;\n this.defaultQueue = config?.queue || redisConfig.queue || \"default\";\n this.retryAfter = config?.retry_after || redisConfig.retry_after || 90;\n const appName = process.env.APP_NAME || \"app\";\n this.prefix = config?.prefix || process.env.REDIS_PREFIX || `${appName}_queue`;\n }\n\n // Key helpers\n private key(queue: string, suffix: string = \"\"): string {\n const base = `${this.prefix}:${queue}`;\n return suffix ? `${base}:${suffix}` : base;\n }\n\n async init(): Promise<void> {\n if (this.initialized && this.client) return;\n\n try {\n const redisUrl =\n process.env.REDIS_URL ||\n `redis://${process.env.REDIS_HOST || \"localhost\"}:${process.env.REDIS_PORT || 6379}`;\n\n this.client = createClient({\n url: redisUrl,\n password: process.env.REDIS_PASSWORD || undefined,\n });\n\n this.client.on(\"error\", (err) => {\n console.error(\"[RedisDriver] Connection error:\", err);\n });\n\n await this.client.connect();\n this.initialized = true;\n } catch (error) {\n console.error(\"[RedisDriver] Failed to connect:\", error);\n throw error;\n }\n }\n\n private async ensureConnected(): Promise<RedisClientType> {\n if (!this.client || !this.initialized) await this.init();\n return this.client!;\n }\n\n async size(queue: string = this.defaultQueue): Promise<number> {\n const client = await this.ensureConnected();\n const pending = await client.lLen(this.key(queue));\n const delayed = await client.zCard(this.key(queue, \"delayed:score\"));\n const reserved = await client.hLen(this.key(queue, \"reserved\"));\n return pending + delayed + reserved;\n }\n\n async push(job: SerializedJob, queue: string = this.defaultQueue): Promise<string> {\n const client = await this.ensureConnected();\n\n if (job.availableAt > Date.now()) {\n // Delayed: store payload in hash, add UUID to sorted set scored by availableAt\n await client.hSet(this.key(queue, \"delayed:body\"), job.uuid, JSON.stringify(job));\n await client.zAdd(this.key(queue, \"delayed:score\"), {\n score: job.availableAt,\n value: job.uuid,\n });\n } else {\n await client.rPush(this.key(queue), JSON.stringify(job));\n }\n\n return job.id;\n }\n\n async later(\n delay: number,\n job: SerializedJob,\n queue: string = this.defaultQueue,\n ): Promise<string> {\n job.availableAt = Date.now() + delay * 1000;\n return this.push(job, queue);\n }\n\n async pop(queue: string = this.defaultQueue): Promise<SerializedJob | null> {\n const client = await this.ensureConnected();\n\n await this.migrateDelayedJobs(queue);\n await this.migrateExpiredReserved(queue);\n\n const payload = await client.lPop(this.key(queue));\n if (!payload) return null;\n\n const job: SerializedJob = JSON.parse(payload);\n job.attempts += 1;\n job.reservedAt = Date.now();\n\n // Store reserved payload in hash keyed by UUID — no fragile ZSet serialization\n await client.hSet(this.key(queue, \"reserved\"), job.uuid, JSON.stringify(job));\n\n return job;\n }\n\n private async migrateDelayedJobs(queue: string): Promise<void> {\n const client = await this.ensureConnected();\n const now = Date.now();\n\n const uuids = await client.zRangeByScore(\n this.key(queue, \"delayed:score\"),\n \"-inf\",\n now.toString(),\n );\n\n for (const uuid of uuids) {\n const body = await client.hGet(this.key(queue, \"delayed:body\"), uuid);\n if (body) {\n await client.rPush(this.key(queue), body);\n await client.hDel(this.key(queue, \"delayed:body\"), uuid);\n }\n await client.zRem(this.key(queue, \"delayed:score\"), uuid);\n }\n }\n\n private async migrateExpiredReserved(queue: string): Promise<void> {\n const client = await this.ensureConnected();\n const now = Date.now();\n const cutoff = now - this.retryAfter * 1000;\n\n const entries = await client.hGetAll(this.key(queue, \"reserved\"));\n\n for (const [uuid, body] of Object.entries(entries)) {\n const job: SerializedJob = JSON.parse(body);\n if (job.reservedAt != null && job.reservedAt < cutoff) {\n // Put back in the main queue with attempts already incremented from pop()\n await client.rPush(this.key(queue), body);\n await client.hDel(this.key(queue, \"reserved\"), uuid);\n }\n }\n }\n\n async delete(job: SerializedJob, queue: string = this.defaultQueue): Promise<void> {\n const client = await this.ensureConnected();\n await client.hDel(this.key(queue, \"reserved\"), job.uuid);\n }\n\n async release(\n job: SerializedJob,\n delay: number,\n queue: string = this.defaultQueue,\n ): Promise<void> {\n const client = await this.ensureConnected();\n\n // Remove from reserved hash\n await client.hDel(this.key(queue, \"reserved\"), job.uuid);\n\n // Update job state\n job.reservedAt = null;\n job.availableAt = Date.now() + delay * 1000;\n\n const newPayload = JSON.stringify(job);\n\n if (delay > 0) {\n await client.hSet(this.key(queue, \"delayed:body\"), job.uuid, newPayload);\n await client.zAdd(this.key(queue, \"delayed:score\"), {\n score: job.availableAt,\n value: job.uuid,\n });\n } else {\n await client.rPush(this.key(queue), newPayload);\n }\n }\n\n async clear(queue: string = this.defaultQueue): Promise<number> {\n const client = await this.ensureConnected();\n const size = await this.size(queue);\n\n await client.del(this.key(queue));\n await client.del(this.key(queue, \"delayed:score\"));\n await client.del(this.key(queue, \"delayed:body\"));\n await client.del(this.key(queue, \"reserved\"));\n\n return size;\n }\n\n async getJobs(queue: string = this.defaultQueue): Promise<SerializedJob[]> {\n const client = await this.ensureConnected();\n\n const pending = await client.lRange(this.key(queue), 0, -1);\n const delayedBodies = Object.values(await client.hGetAll(this.key(queue, \"delayed:body\")));\n const reservedBodies = Object.values(await client.hGetAll(this.key(queue, \"reserved\")));\n\n return [...pending, ...delayedBodies, ...reservedBodies].map((p) => JSON.parse(p));\n }\n\n /*\n |--------------------------------------------------------------------------\n | Failed Jobs Management\n |--------------------------------------------------------------------------\n */\n\n async logFailed(\n connection: string,\n queue: string,\n job: SerializedJob,\n exception: Error,\n ): Promise<void> {\n const client = await this.ensureConnected();\n\n const failedJob = {\n uuid: job.uuid,\n connection,\n queue,\n payload: job,\n exception: exception.stack || exception.message,\n failed_at: new Date().toISOString(),\n };\n\n await client.hSet(`${this.prefix}:failed_jobs`, job.uuid, JSON.stringify(failedJob));\n }\n\n async getFailedJobs(): Promise<any[]> {\n const client = await this.ensureConnected();\n const jobs = await client.hGetAll(`${this.prefix}:failed_jobs`);\n return Object.values(jobs).map((j) => JSON.parse(j));\n }\n\n async retryFailed(uuid: string): Promise<boolean> {\n const client = await this.ensureConnected();\n\n const data = await client.hGet(`${this.prefix}:failed_jobs`, uuid);\n if (!data) return false;\n\n const failedJob = JSON.parse(data);\n const job: SerializedJob = failedJob.payload;\n\n job.attempts = 0;\n job.exceptionCount = 0;\n job.reservedAt = null;\n job.availableAt = Date.now();\n\n await this.push(job, failedJob.queue);\n await this.forgetFailed(uuid);\n\n return true;\n }\n\n async forgetFailed(uuid: string): Promise<boolean> {\n const client = await this.ensureConnected();\n const result = await client.hDel(`${this.prefix}:failed_jobs`, uuid);\n return result > 0;\n }\n\n async flushFailed(): Promise<number> {\n const client = await this.ensureConnected();\n const count = await client.hLen(`${this.prefix}:failed_jobs`);\n await client.del(`${this.prefix}:failed_jobs`);\n return count;\n }\n\n async disconnect(): Promise<void> {\n if (this.client) {\n await this.client.quit();\n this.client = null;\n this.initialized = false;\n }\n }\n}\n","import { QueueDriverInterface, FailedJobsInterface, SerializedJob } from \"./types.js\";\nimport { SyncDriver, DatabaseDriver, RedisDriver } from \"./Drivers/index.js\";\nimport { Job } from \"./Job.js\";\nimport queueConfig from \"./queue.config.js\";\nimport { Cache } from \"@lara-node/cache\";\n\n/*\n|--------------------------------------------------------------------------\n| Queue Manager\n|--------------------------------------------------------------------------\n|\n| This class manages queue connections and provides a unified interface\n| for dispatching jobs to different queue backends.\n|\n*/\n\nclass QueueManager {\n private connections: Map<string, QueueDriverInterface> = new Map();\n private defaultConnection: string;\n\n constructor() {\n this.defaultConnection = queueConfig.default;\n }\n\n /*\n |--------------------------------------------------------------------------\n | Connection Management\n |--------------------------------------------------------------------------\n */\n\n /**\n * Get a queue connection instance.\n * All drivers are cached — database queries are stateless per-call so sharing\n * the driver instance is safe and avoids repeated object allocation on every poll.\n */\n connection(name?: string): QueueDriverInterface {\n const connectionName = name || this.defaultConnection;\n\n if (!this.connections.has(connectionName)) {\n const config = queueConfig.connections[connectionName];\n if (!config) {\n throw new Error(`Queue connection [${connectionName}] is not defined.`);\n }\n this.connections.set(connectionName, this.resolve(connectionName));\n }\n\n return this.connections.get(connectionName)!;\n }\n\n /**\n * Resolve a queue connection instance.\n */\n private resolve(name: string): QueueDriverInterface {\n const config = queueConfig.connections[name];\n\n if (!config) {\n throw new Error(`Queue connection [${name}] is not defined.`);\n }\n\n switch (config.driver) {\n case \"sync\":\n return new SyncDriver();\n case \"database\":\n return new DatabaseDriver({\n table: config.table,\n queue: config.queue,\n retry_after: config.retry_after,\n });\n case \"redis\":\n return new RedisDriver({\n queue: config.queue,\n retry_after: config.retry_after,\n });\n default:\n throw new Error(`Queue driver [${config.driver}] is not supported.`);\n }\n }\n\n /**\n * Get the default connection name.\n */\n getDefaultDriver(): string {\n return this.defaultConnection;\n }\n\n /**\n * Set the default connection name.\n */\n setDefaultDriver(name: string): void {\n this.defaultConnection = name;\n }\n\n /*\n |--------------------------------------------------------------------------\n | Queue Operations\n |--------------------------------------------------------------------------\n */\n\n /**\n * Push a new job onto the queue.\n * Respects uniqueId/uniqueFor — silently deduplicates if a lock already exists.\n */\n async push(job: Job, queue?: string): Promise<string> {\n // Unique job enforcement — single cache read instead of has() + get()\n if (job.uniqueId) {\n const lockKey = `queue:unique:${job.uniqueId}`;\n const existingUuid = await Cache.get(lockKey).catch(() => null);\n if (existingUuid != null) {\n return (existingUuid as string) ?? job.uniqueId;\n }\n const ttl = job.uniqueFor && job.uniqueFor > 0 ? job.uniqueFor : null;\n await Cache.set(lockKey, job.uuid || \"pending\", ttl).catch(() => {});\n }\n\n const serialized = job.serialize();\n const connection = this.connection(job.connection);\n const queueName = queue || job.queue;\n\n if (job.delay > 0) {\n return connection.later(job.delay, serialized, queueName);\n }\n\n return connection.push(serialized, queueName);\n }\n\n /**\n * Release the unique lock for a job after it completes or permanently fails.\n */\n async releaseUniqueLock(job: SerializedJob): Promise<void> {\n // uniqueId is stored in the serialized data — access via a type-safe cast\n const uniqueId = (job as any).uniqueId as string | undefined;\n if (uniqueId) {\n await Cache.del(`queue:unique:${uniqueId}`).catch(() => {});\n }\n }\n\n /**\n * Push a new job onto the queue after a delay.\n */\n async later(delay: number, job: Job, queue?: string): Promise<string> {\n job.withDelay(delay);\n return this.push(job, queue);\n }\n\n /**\n * Push a raw payload onto the queue.\n */\n async pushRaw(payload: SerializedJob, queue?: string, connection?: string): Promise<string> {\n return this.connection(connection).push(payload, queue);\n }\n\n /**\n * Push multiple jobs onto the queue.\n */\n async bulk(jobs: Job[], queue?: string): Promise<string[]> {\n return Promise.all(jobs.map((job) => this.push(job, queue)));\n }\n\n /**\n * Pop the next job from the queue.\n */\n async pop(queue?: string, connection?: string): Promise<SerializedJob | null> {\n return this.connection(connection).pop(queue);\n }\n\n /**\n * Get the size of the queue.\n */\n async size(queue?: string, connection?: string): Promise<number> {\n return this.connection(connection).size(queue);\n }\n\n /**\n * Clear all jobs from the queue.\n */\n async clear(queue?: string, connection?: string): Promise<number> {\n return this.connection(connection).clear(queue);\n }\n\n /**\n * Get all jobs from a queue.\n */\n async getJobs(queue?: string, connection?: string): Promise<SerializedJob[]> {\n return this.connection(connection).getJobs(queue);\n }\n\n /*\n |--------------------------------------------------------------------------\n | Failed Jobs\n |--------------------------------------------------------------------------\n */\n\n private asFailedDriver(driver: QueueDriverInterface): FailedJobsInterface | null {\n return \"logFailed\" in driver ? (driver as unknown as FailedJobsInterface) : null;\n }\n\n async logFailed(\n connectionName: string,\n queue: string,\n job: SerializedJob,\n exception: Error,\n ): Promise<void> {\n const failed = this.asFailedDriver(this.connection(connectionName));\n if (failed) await failed.logFailed(connectionName, queue, job, exception);\n }\n\n async getFailedJobs(connection?: string): Promise<any[]> {\n const failed = this.asFailedDriver(this.connection(connection));\n return failed ? failed.getFailedJobs() : [];\n }\n\n async retryFailed(uuid: string, connection?: string): Promise<boolean> {\n const failed = this.asFailedDriver(this.connection(connection));\n return failed ? failed.retryFailed(uuid) : false;\n }\n\n async forgetFailed(uuid: string, connection?: string): Promise<boolean> {\n const failed = this.asFailedDriver(this.connection(connection));\n return failed ? failed.forgetFailed(uuid) : false;\n }\n\n async flushFailed(connection?: string): Promise<number> {\n const failed = this.asFailedDriver(this.connection(connection));\n return failed ? failed.flushFailed() : 0;\n }\n}\n\n// Export a singleton instance\nexport const Queue = new QueueManager();\n\n// Also export the class for testing\nexport { QueueManager };\n","import { Queue } from \"./Queue.js\";\nimport { Job } from \"./Job.js\";\nimport { SerializedJob, WorkerOptions } from \"./types.js\";\nimport queueConfig from \"./queue.config.js\";\nimport { Cache } from \"@lara-node/cache\";\nimport { EventEmitter } from \"events\";\n\n/*\n|--------------------------------------------------------------------------\n| Queue Worker\n|--------------------------------------------------------------------------\n|\n| Processes jobs from the queue. Supports daemon mode and single-run mode.\n| Handles retries, backoff, maxExceptions, retryUntil, maintenance mode,\n| and graceful restart via cache signal.\n|\n*/\n\n// Guard so signal handlers are only registered once across all Worker instances\nlet signalsRegistered = false;\n\nexport class Worker extends EventEmitter {\n private running: boolean = false;\n private paused: boolean = false;\n private shouldQuit: boolean = false;\n private jobsProcessed: number = 0;\n private startTime: number = 0;\n private currentJob: SerializedJob | null = null;\n // Throttle expensive per-tick cache checks to every N idle cycles\n private idleTicks: number = 0;\n private static readonly IDLE_CHECK_INTERVAL = 5;\n\n private connectionName: string;\n private queues: string[];\n private options: Required<Omit<WorkerOptions, \"workerId\">>;\n /** Horizon worker ID — enables Cache-based control signals from the dashboard. */\n private readonly workerId?: string;\n\n private readonly restartKey: string;\n private readonly horizonCtrlPrefix: string;\n\n constructor(\n connectionName?: string,\n queues: string | string[] = \"default\",\n options: WorkerOptions = {},\n ) {\n super();\n\n this.connectionName = connectionName || queueConfig.default;\n this.queues = Array.isArray(queues) ? queues : [queues];\n this.restartKey = `${process.env.APP_NAME || \"app\"}:queue:restart`;\n this.horizonCtrlPrefix = `${process.env.APP_NAME || \"app\"}:horizon:ctrl`;\n this.workerId = options.workerId;\n\n this.options = {\n connection: this.connectionName,\n queue: queues,\n delay: options.delay ?? 0,\n memory: options.memory ?? 128,\n timeout: options.timeout ?? queueConfig.defaults.timeout,\n sleep: options.sleep ?? 3,\n maxTries: options.maxTries ?? queueConfig.defaults.tries,\n maxJobs: options.maxJobs ?? 0,\n maxTime: options.maxTime ?? 0,\n force: options.force ?? false,\n stopWhenEmpty: options.stopWhenEmpty ?? false,\n rest: options.rest ?? 0,\n verbose: options.verbose ?? false,\n };\n }\n\n /*\n |--------------------------------------------------------------------------\n | Worker Lifecycle\n |--------------------------------------------------------------------------\n */\n\n async daemon(): Promise<void> {\n if (this.running) return;\n\n this.running = true;\n this.shouldQuit = false;\n this.startTime = Date.now();\n this.jobsProcessed = 0;\n\n console.log(\n `[Worker] Starting on connection [${this.connectionName}] processing queues: ${this.queues.join(\", \")}`,\n );\n this.emit(\"worker:start\", { connection: this.connectionName, queues: this.queues });\n\n this.registerSignalHandlers();\n\n while (this.running && !this.shouldQuit) {\n if (this.paused) {\n await this.sleep(this.options.sleep * 1000);\n continue;\n }\n\n // Cheap synchronous guards — run every tick\n if (this.options.maxJobs > 0 && this.jobsProcessed >= this.options.maxJobs) {\n console.log(`[Worker] Max jobs (${this.options.maxJobs}) reached, stopping...`);\n this.stop();\n break;\n }\n\n if (this.options.maxTime > 0) {\n const elapsed = (Date.now() - this.startTime) / 1000;\n if (elapsed >= this.options.maxTime) {\n console.log(`[Worker] Max time (${this.options.maxTime}s) reached, stopping...`);\n this.stop();\n break;\n }\n }\n\n // Expensive async checks (cache/memory) batched every IDLE_CHECK_INTERVAL idle ticks\n // or always after a job runs (idleTicks reset to 0 after processing).\n if (this.idleTicks % Worker.IDLE_CHECK_INTERVAL === 0) {\n if (this.memoryExceeded()) {\n console.log(\"[Worker] Memory limit exceeded, stopping...\");\n this.stop();\n break;\n }\n\n // Batch all cache lookups in parallel\n const [inMaintenance, restart, horizonSignal] = await Promise.all([\n this.options.force ? Promise.resolve(false) : this.isInMaintenanceMode(),\n this.shouldRestart(),\n this.checkHorizonSignal(),\n ]);\n\n // Apply Horizon dashboard control signal if present\n if (horizonSignal === \"stop\") {\n console.log(\"[Worker] Horizon stop signal received, stopping...\");\n this.stop();\n break;\n }\n if (horizonSignal === \"pause\" && !this.paused) {\n console.log(\"[Worker] Horizon pause signal received.\");\n this.pause();\n }\n if (horizonSignal === \"resume\" && this.paused) {\n console.log(\"[Worker] Horizon resume signal received.\");\n this.resume();\n }\n\n if (restart) {\n console.log(\"[Worker] Restart signal detected, stopping...\");\n this.stop();\n break;\n }\n\n if (inMaintenance) {\n if (this.options.verbose)\n console.log(\"[Worker] Application in maintenance mode, sleeping...\");\n this.idleTicks++;\n await this.sleep(this.options.sleep * 1000);\n continue;\n }\n }\n\n const job = await this.getNextJob();\n\n if (job) {\n this.idleTicks = 0;\n await this.process(job);\n this.jobsProcessed++;\n\n if (this.options.rest > 0) {\n await this.sleep(this.options.rest * 1000);\n }\n } else {\n if (this.options.stopWhenEmpty) {\n console.log(\"[Worker] Queue is empty, stopping...\");\n this.stop();\n break;\n }\n this.idleTicks++;\n await this.sleep(this.options.sleep * 1000);\n }\n }\n\n this.emit(\"worker:stop\", {\n connection: this.connectionName,\n jobsProcessed: this.jobsProcessed,\n runtime: (Date.now() - this.startTime) / 1000,\n });\n\n console.log(`[Worker] Stopped. Processed ${this.jobsProcessed} jobs.`);\n }\n\n async runNextJob(): Promise<boolean> {\n const job = await this.getNextJob();\n if (!job) return false;\n await this.process(job);\n return true;\n }\n\n stop(): void {\n this.shouldQuit = true;\n this.running = false;\n }\n\n pause(): void {\n this.paused = true;\n this.emit(\"worker:pause\");\n }\n\n resume(): void {\n this.paused = false;\n this.emit(\"worker:resume\");\n }\n\n /*\n |--------------------------------------------------------------------------\n | Job Processing\n |--------------------------------------------------------------------------\n */\n\n private async getNextJob(): Promise<SerializedJob | null> {\n for (const queue of this.queues) {\n try {\n if (this.options.verbose) {\n console.log(\n `[Worker] Polling queue [${queue}] on connection [${this.connectionName}]...`,\n );\n }\n const job = await Queue.pop(queue, this.connectionName);\n if (job) return job;\n } catch (error) {\n console.error(`[Worker] Error popping job from queue [${queue}]:`, error);\n }\n }\n if (this.options.verbose) {\n console.log(`[Worker] No jobs found, sleeping for ${this.options.sleep}s...`);\n }\n return null;\n }\n\n private async process(serializedJob: SerializedJob): Promise<void> {\n this.currentJob = serializedJob;\n\n this.emit(\"job:processing\", { connectionName: this.connectionName, job: serializedJob });\n console.log(`[Worker] Processing job: ${serializedJob.displayName} (${serializedJob.uuid})`);\n\n try {\n const job = Job.deserialize(serializedJob);\n\n if (!job) {\n throw new Error(`Failed to deserialize job: ${serializedJob.job}`);\n }\n\n const timeoutMs = (serializedJob.timeout || this.options.timeout) * 1000;\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => reject(new Error(\"Job timed out\")), timeoutMs);\n });\n\n try {\n await Promise.race([job.handle(), timeoutPromise]);\n } finally {\n clearTimeout(timeoutHandle);\n }\n\n await this.handleSuccess(serializedJob);\n } catch (error) {\n await this.handleFailure(serializedJob, error as Error);\n } finally {\n this.currentJob = null;\n }\n }\n\n private async handleSuccess(job: SerializedJob): Promise<void> {\n const driver = Queue.connection(this.connectionName);\n await driver.delete(job, job.queue);\n\n // Release unique lock so another job with same uniqueId can be dispatched\n await Queue.releaseUniqueLock(job);\n\n this.emit(\"job:processed\", { connectionName: this.connectionName, job });\n console.log(`[Worker] Job completed: ${job.displayName} (${job.uuid})`);\n }\n\n private async handleFailure(job: SerializedJob, error: Error): Promise<void> {\n console.error(`[Worker] Job failed: ${job.displayName} (${job.uuid}) — ${error.message}`);\n\n this.emit(\"job:exception\", { connectionName: this.connectionName, job, exception: error });\n\n // Increment exception count and persist it into the in-memory job object\n // so it is included in the payload when the job is released back to the queue.\n job.exceptionCount = (job.exceptionCount ?? 0) + 1;\n\n const maxTries = job.maxTries || this.options.maxTries;\n const maxExceptions = job.maxExceptions ?? queueConfig.defaults.maxExceptions;\n const retryUntilExpired = job.retryUntil != null && Date.now() > job.retryUntil;\n\n const shouldFailPermanently =\n retryUntilExpired || job.attempts >= maxTries || job.exceptionCount >= maxExceptions;\n\n const driver = Queue.connection(this.connectionName);\n\n if (!shouldFailPermanently) {\n const delay = this.calculateBackoff(job);\n\n console.log(\n `[Worker] Releasing job for retry — attempt ${job.attempts}/${maxTries}, ` +\n `exceptions ${job.exceptionCount}/${maxExceptions}, delay ${delay}s`,\n );\n\n await driver.release(job, delay, job.queue);\n } else {\n const reason = retryUntilExpired\n ? \"retryUntil deadline passed\"\n : job.exceptionCount >= maxExceptions\n ? `maxExceptions (${maxExceptions}) reached`\n : `maxTries (${maxTries}) reached`;\n\n console.log(`[Worker] Job failed permanently after ${job.attempts} attempt(s): ${reason}`);\n\n await driver.delete(job, job.queue);\n await Queue.logFailed(this.connectionName, job.queue, job, error);\n\n // Release unique lock on permanent failure too\n await Queue.releaseUniqueLock(job);\n\n const jobInstance = Job.deserialize(job);\n if (jobInstance) {\n try {\n jobInstance.failed(error);\n } catch (e) {\n console.error(\"[Worker] Error in job.failed():\", e);\n }\n }\n\n this.emit(\"job:failed\", { connectionName: this.connectionName, job, exception: error });\n }\n }\n\n private calculateBackoff(job: SerializedJob): number {\n const backoff = job.backoff || queueConfig.defaults.backoff;\n\n if (typeof backoff === \"number\") return backoff;\n\n if (Array.isArray(backoff)) {\n const index = Math.min(job.attempts - 1, backoff.length - 1);\n return backoff[Math.max(0, index)];\n }\n\n return 0;\n }\n\n /*\n |--------------------------------------------------------------------------\n | Utilities\n |--------------------------------------------------------------------------\n */\n\n private memoryExceeded(): boolean {\n const used = process.memoryUsage().heapUsed / 1024 / 1024;\n return used >= this.options.memory;\n }\n\n private async isInMaintenanceMode(): Promise<boolean> {\n try {\n return await Cache.has(`${process.env.APP_NAME || \"app\"}:maintenance`);\n } catch {\n return false;\n }\n }\n\n private async shouldRestart(): Promise<boolean> {\n try {\n const restartTs = await Cache.get(this.restartKey);\n return restartTs != null && Number(restartTs) > this.startTime;\n } catch {\n return false;\n }\n }\n\n /** Read and immediately clear a Horizon dashboard control signal for this worker. */\n private async checkHorizonSignal(): Promise<\"pause\" | \"resume\" | \"stop\" | null> {\n if (!this.workerId) return null;\n const key = `${this.horizonCtrlPrefix}:${this.workerId}`;\n try {\n const sig = await Cache.get(key);\n if (sig) await Cache.del(key);\n return (sig as \"pause\" | \"resume\" | \"stop\" | null) ?? null;\n } catch {\n return null;\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n private registerSignalHandlers(): void {\n if (signalsRegistered) return;\n signalsRegistered = true;\n\n const signals: NodeJS.Signals[] = [\"SIGINT\", \"SIGTERM\", \"SIGQUIT\"];\n for (const signal of signals) {\n process.on(signal, () => {\n console.log(`\\n[Worker] Received ${signal}, stopping gracefully...`);\n this.stop();\n });\n }\n }\n\n /*\n |--------------------------------------------------------------------------\n | Status\n |--------------------------------------------------------------------------\n */\n\n isRunning(): boolean {\n return this.running;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n getJobsProcessed(): number {\n return this.jobsProcessed;\n }\n\n getCurrentJob(): SerializedJob | null {\n return this.currentJob;\n }\n\n getRuntime(): number {\n return this.startTime > 0 ? (Date.now() - this.startTime) / 1000 : 0;\n }\n\n getStatus(): {\n running: boolean;\n paused: boolean;\n jobsProcessed: number;\n runtime: number;\n currentJob: SerializedJob | null;\n memory: number;\n } {\n return {\n running: this.running,\n paused: this.paused,\n jobsProcessed: this.jobsProcessed,\n runtime: this.getRuntime(),\n currentJob: this.currentJob,\n memory: process.memoryUsage().heapUsed / 1024 / 1024,\n };\n }\n}\n","import { EventEmitter } from \"events\";\nimport { CronExpressionParser as parser } from \"cron-parser\";\nimport { Cache } from \"@lara-node/cache\";\n\n/*\n|--------------------------------------------------------------------------\n| Distributed Lock Helpers\n|--------------------------------------------------------------------------\n*/\n\nconst SCHEDULER_LOCK_PREFIX = `${process.env.APP_NAME || \"app\"}:scheduler:lock`;\n\nasync function acquireLock(key: string, ttlSeconds: number): Promise<boolean> {\n const lockKey = `${SCHEDULER_LOCK_PREFIX}:${key}`;\n const existing = await Cache.get(lockKey);\n if (existing) return false;\n await Cache.set(lockKey, Date.now(), ttlSeconds);\n return true;\n}\n\nasync function releaseLock(key: string): Promise<void> {\n await Cache.del(`${SCHEDULER_LOCK_PREFIX}:${key}`);\n}\n\n/*\n|--------------------------------------------------------------------------\n| ScheduledTask Interface\n|--------------------------------------------------------------------------\n*/\n\nexport interface ScheduledTask {\n name: string;\n callback: () => Promise<void> | void;\n expression: string;\n timezone?: string;\n description?: string;\n withoutOverlapping: boolean;\n onOneServer: boolean;\n evenInMaintenanceMode: boolean;\n runInBackground: boolean;\n conditions: Array<() => boolean | Promise<boolean>>;\n skipConditions: Array<() => boolean | Promise<boolean>>;\n betweenStart?: string; // \"HH:MM\"\n betweenEnd?: string; // \"HH:MM\"\n onSuccessHook?: (task: ScheduledTask) => void;\n onFailureHook?: (task: ScheduledTask, error: Error) => void;\n lastRun?: Date;\n nextRun?: Date;\n isRunning: boolean;\n}\n\nexport function createObservableTask(task: ScheduledTask): ScheduledTask {\n // Update nextRun once at registration — no Proxy needed since expression\n // changes only happen during builder chaining, not at runtime.\n try {\n const interval = parser.parse(task.expression, { tz: task.timezone || \"UTC\" });\n task.nextRun = interval.next().toDate();\n } catch {\n // invalid expression — leave nextRun as-is\n }\n return task;\n}\n\n/*\n|--------------------------------------------------------------------------\n| Schedule Manager\n|--------------------------------------------------------------------------\n*/\n\nexport class Schedule {\n private tasks: ScheduledTask[] = [];\n private running: boolean = false;\n private events: EventEmitter = new EventEmitter();\n // Cache split cron parts keyed by expression string — avoids re-splitting on every tick\n private readonly cronPartsCache = new Map<string, string[]>();\n\n /*\n |--------------------------------------------------------------------------\n | Task Registration\n |--------------------------------------------------------------------------\n */\n\n call(callback: () => Promise<void> | void): ScheduledTaskBuilder {\n const task = createObservableTask(this.defaultTask(`closure-${Date.now()}`, callback));\n this.tasks.push(task);\n return new ScheduledTaskBuilder(task);\n }\n\n command(command: string, args: string[] = []): ScheduledTaskBuilder {\n const callback = async () => {\n const { exec } = await import(\"child_process\");\n const { promisify } = await import(\"util\");\n const execAsync = promisify(exec);\n const fullCommand = `npm run artisan -- ${command} ${args.join(\" \")}`;\n console.log(`[Scheduler] Running command: ${fullCommand}`);\n const { stdout, stderr } = await execAsync(fullCommand);\n if (stdout) console.log(stdout);\n if (stderr) console.error(stderr);\n };\n const task = createObservableTask(\n this.defaultTask(`command:${command}`, callback, `Artisan command: ${command}`),\n );\n this.tasks.push(task);\n return new ScheduledTaskBuilder(task);\n }\n\n exec(command: string): ScheduledTaskBuilder {\n const callback = async () => {\n const { exec } = await import(\"child_process\");\n const { promisify } = await import(\"util\");\n const execAsync = promisify(exec);\n console.log(`[Scheduler] Executing: ${command}`);\n const { stdout, stderr } = await execAsync(command);\n if (stdout) console.log(stdout);\n if (stderr) console.error(stderr);\n };\n const task = createObservableTask(\n this.defaultTask(`exec:${command.slice(0, 50)}`, callback, `Shell command: ${command}`),\n );\n this.tasks.push(task);\n return new ScheduledTaskBuilder(task);\n }\n\n job<T extends { dispatch: () => { dispatch: () => Promise<string> } }>(\n JobClass: T,\n queue?: string,\n ): ScheduledTaskBuilder {\n const callback = async () => {\n const pending = JobClass.dispatch();\n if (queue) (pending as any).onQueue(queue);\n await pending.dispatch();\n };\n const task = createObservableTask(\n this.defaultTask(\n `job:${(JobClass as any).name || \"anonymous\"}`,\n callback,\n `Dispatch job: ${(JobClass as any).name}`,\n ),\n );\n this.tasks.push(task);\n return new ScheduledTaskBuilder(task);\n }\n\n private defaultTask(\n name: string,\n callback: () => Promise<void> | void,\n description?: string,\n ): ScheduledTask {\n return {\n name,\n callback,\n expression: \"* * * * *\",\n description,\n withoutOverlapping: false,\n onOneServer: false,\n evenInMaintenanceMode: false,\n runInBackground: false,\n conditions: [],\n skipConditions: [],\n isRunning: false,\n };\n }\n\n /*\n |--------------------------------------------------------------------------\n | Execution\n |--------------------------------------------------------------------------\n */\n\n updateNextTaskRun(task: ScheduledTask): void {\n const interval = parser.parse(task.expression, { tz: task.timezone || \"UTC\" });\n task.nextRun = interval.next().toDate();\n }\n\n getTasks(): ScheduledTask[] {\n return this.tasks;\n }\n\n getDueTasks(): ScheduledTask[] {\n const now = new Date();\n return this.tasks.filter((task) => this.isDue(task, now));\n }\n\n private isDue(task: ScheduledTask, now: Date = new Date()): boolean {\n if (!this.matchesCronExpression(task.expression, now)) return false;\n\n // between() filter\n if (task.betweenStart && task.betweenEnd) {\n const [sh, sm] = task.betweenStart.split(\":\").map(Number);\n const [eh, em] = task.betweenEnd.split(\":\").map(Number);\n const current = now.getHours() * 60 + now.getMinutes();\n const start = sh * 60 + sm;\n const end = eh * 60 + em;\n if (current < start || current > end) return false;\n }\n\n return true;\n }\n\n private matchesCronExpression(expression: string, date: Date): boolean {\n let parts = this.cronPartsCache.get(expression);\n if (!parts) {\n parts = expression.split(\" \");\n if (parts.length !== 5) {\n console.warn(`[Scheduler] Invalid cron expression: ${expression}`);\n return false;\n }\n this.cronPartsCache.set(expression, parts);\n }\n const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;\n return (\n this.matchCronPart(minute, date.getMinutes()) &&\n this.matchCronPart(hour, date.getHours()) &&\n this.matchCronPart(dayOfMonth, date.getDate()) &&\n this.matchCronPart(month, date.getMonth() + 1) &&\n this.matchCronPart(dayOfWeek, date.getDay())\n );\n }\n\n private matchCronPart(pattern: string, value: number): boolean {\n if (pattern === \"*\") return true;\n if (pattern.startsWith(\"*/\")) {\n const interval = parseInt(pattern.slice(2), 10);\n return value % interval === 0;\n }\n if (pattern.includes(\"-\")) {\n const [start, end] = pattern.split(\"-\").map(Number);\n return value >= start && value <= end;\n }\n if (pattern.includes(\",\")) {\n return pattern.split(\",\").map(Number).includes(value);\n }\n return parseInt(pattern, 10) === value;\n }\n\n async runDueTasks(): Promise<void> {\n const maintenanceKey = `${process.env.APP_NAME || \"app\"}:maintenance`;\n const inMaintenance = await Cache.has(maintenanceKey).catch(() => false);\n\n const dueTasks = this.getDueTasks();\n console.log(`[Scheduler] Found ${dueTasks.length} due task(s)`);\n\n for (const task of dueTasks) {\n // Maintenance mode gate\n if (inMaintenance && !task.evenInMaintenanceMode) {\n console.log(`[Scheduler] Skipping task in maintenance mode: ${task.name}`);\n continue;\n }\n\n // Evaluate when() conditions — all must pass\n if (task.conditions.length > 0) {\n const results = await Promise.all(task.conditions.map((fn) => fn()));\n if (!results.every(Boolean)) {\n if (this.options.verbose)\n console.log(`[Scheduler] Skipping task (when() failed): ${task.name}`);\n continue;\n }\n }\n\n // Evaluate skip() conditions — any true → skip\n if (task.skipConditions.length > 0) {\n const results = await Promise.all(task.skipConditions.map((fn) => fn()));\n if (results.some(Boolean)) {\n if (this.options.verbose)\n console.log(`[Scheduler] Skipping task (skip() matched): ${task.name}`);\n continue;\n }\n }\n\n // In-process overlapping guard\n if (task.withoutOverlapping && task.isRunning) {\n console.log(`[Scheduler] Skipping overlapping task (in-memory): ${task.name}`);\n continue;\n }\n\n // Distributed overlapping lock\n if (task.withoutOverlapping) {\n const lockAcquired = await acquireLock(`overlap:${task.name}`, 300).catch(() => false);\n if (!lockAcquired) {\n console.log(`[Scheduler] Skipping overlapping task (distributed lock): ${task.name}`);\n continue;\n }\n }\n\n // Single-server lock (one cron tick ≈ 60s + 5s buffer)\n if (task.onOneServer) {\n const lockAcquired = await acquireLock(\n `once:${task.name}:${Math.floor(Date.now() / 60000)}`,\n 65,\n ).catch(() => false);\n if (!lockAcquired) {\n console.log(\n `[Scheduler] Skipping task (onOneServer, another server has it): ${task.name}`,\n );\n continue;\n }\n }\n\n await this.runTask(task);\n }\n }\n\n private options = { verbose: false };\n\n private async runTask(task: ScheduledTask): Promise<void> {\n task.isRunning = true;\n task.lastRun = new Date();\n this.updateNextTaskRun(task);\n\n console.log(`[Scheduler] Running task: ${task.name}`);\n this.events.emit(\"task:start\", task);\n\n const finalize = async (error?: Error) => {\n task.isRunning = false;\n if (task.withoutOverlapping) {\n await releaseLock(`overlap:${task.name}`).catch(() => {});\n }\n if (error) {\n task.onFailureHook?.(task, error);\n } else {\n task.onSuccessHook?.(task);\n }\n };\n\n try {\n if (task.runInBackground) {\n setImmediate(async () => {\n try {\n await task.callback();\n this.events.emit(\"task:success\", task);\n await finalize();\n } catch (error) {\n console.error(`[Scheduler] Task failed: ${task.name}`, error);\n this.events.emit(\"task:failed\", task, error);\n await finalize(error as Error);\n }\n });\n // For background tasks, don't hold isRunning — the setImmediate owns it\n } else {\n await task.callback();\n this.events.emit(\"task:success\", task);\n await finalize();\n }\n } catch (error) {\n console.error(`[Scheduler] Task failed: ${task.name}`, error);\n this.events.emit(\"task:failed\", task, error);\n await finalize(error as Error);\n }\n }\n\n async start(): Promise<void> {\n if (this.running) {\n console.log(\"[Scheduler] Already running\");\n return;\n }\n\n this.running = true;\n console.log(\"[Scheduler] Starting scheduler daemon...\");\n\n await this.runDueTasks();\n\n while (this.running) {\n const now = new Date();\n const msUntilNextMinute = (60 - now.getSeconds()) * 1000 - now.getMilliseconds();\n await this.sleep(msUntilNextMinute);\n if (this.running) await this.runDueTasks();\n }\n }\n\n stop(): void {\n this.running = false;\n console.log(\"[Scheduler] Stopping...\");\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n on(\n event: \"task:start\" | \"task:success\" | \"task:failed\",\n listener: (task: ScheduledTask, error?: any) => void,\n ): void {\n this.events.on(event, listener);\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/*\n|--------------------------------------------------------------------------\n| Scheduled Task Builder\n|--------------------------------------------------------------------------\n*/\n\nexport class ScheduledTaskBuilder {\n constructor(private task: ScheduledTask) {}\n\n /*\n |--------------------------------------------------------------------------\n | Frequency — sub-hourly\n |--------------------------------------------------------------------------\n */\n\n everyMinute(): this {\n return this.cron(\"* * * * *\");\n }\n everyTwoMinutes(): this {\n return this.cron(\"*/2 * * * *\");\n }\n everyThreeMinutes(): this {\n return this.cron(\"*/3 * * * *\");\n }\n everyFiveMinutes(): this {\n return this.cron(\"*/5 * * * *\");\n }\n everyTenMinutes(): this {\n return this.cron(\"*/10 * * * *\");\n }\n everyFifteenMinutes(): this {\n return this.cron(\"*/15 * * * *\");\n }\n everyTwentyMinutes(): this {\n return this.cron(\"*/20 * * * *\");\n }\n everyThirtyMinutes(): this {\n return this.cron(\"*/30 * * * *\");\n }\n everyFortyFiveMinutes(): this {\n return this.cron(\"*/45 * * * *\");\n }\n\n /** Run every N minutes (arbitrary interval). */\n everyNMinutes(n: number): this {\n return this.cron(`*/${n} * * * *`);\n }\n\n /*\n |--------------------------------------------------------------------------\n | Frequency — hourly / daily / weekly / monthly / yearly\n |--------------------------------------------------------------------------\n */\n\n hourly(): this {\n return this.cron(\"0 * * * *\");\n }\n hourlyAt(minute: number): this {\n return this.cron(`${minute} * * * *`);\n }\n everyTwoHours(): this {\n return this.cron(\"0 */2 * * *\");\n }\n everyFourHours(): this {\n return this.cron(\"0 */4 * * *\");\n }\n everySixHours(): this {\n return this.cron(\"0 */6 * * *\");\n }\n\n /** Run every N hours (arbitrary interval). */\n everyNHours(n: number): this {\n return this.cron(`0 */${n} * * *`);\n }\n\n daily(): this {\n return this.cron(\"0 0 * * *\");\n }\n\n dailyAt(time: string): this {\n const [hour, minute] = time.split(\":\").map(Number);\n return this.cron(`${minute ?? 0} ${hour} * * *`);\n }\n\n twiceDaily(firstHour: number = 1, secondHour: number = 13): this {\n return this.cron(`0 ${firstHour},${secondHour} * * *`);\n }\n\n twiceDailyAt(\n firstHour: number,\n firstMinute: number,\n secondHour: number,\n secondMinute: number,\n ): this {\n return this.cron(`${firstMinute} ${firstHour},${secondHour} * * *`);\n }\n\n weekly(): this {\n return this.cron(\"0 0 * * 0\");\n }\n\n weeklyOn(dayOfWeek: number, time: string = \"0:0\"): this {\n const [hour, minute] = time.split(\":\").map(Number);\n return this.cron(`${minute ?? 0} ${hour} * * ${dayOfWeek}`);\n }\n\n monthly(): this {\n return this.cron(\"0 0 1 * *\");\n }\n\n monthlyOn(day: number, time: string = \"0:0\"): this {\n const [hour, minute] = time.split(\":\").map(Number);\n return this.cron(`${minute ?? 0} ${hour} ${day} * *`);\n }\n\n /**\n * Run on the last day of the month at midnight.\n * Uses day 28 as a universally-safe proxy (real last-day check runs at runtime).\n */\n lastDayOfMonth(time: string = \"0:0\"): this {\n const [hour, minute] = time.split(\":\").map(Number);\n // Schedule on day 28 and let a when() condition handle the true last-day check\n this.cron(`${minute ?? 0} ${hour} 28-31 * *`);\n this.task.conditions.push(() => {\n const now = new Date();\n const tomorrow = new Date(now);\n tomorrow.setDate(now.getDate() + 1);\n return tomorrow.getDate() === 1; // tomorrow is the 1st ⇒ today is the last day\n });\n return this;\n }\n\n quarterly(): this {\n return this.cron(\"0 0 1 1,4,7,10 *\");\n }\n yearly(): this {\n return this.cron(\"0 0 1 1 *\");\n }\n yearlyOn(month: number, day: number = 1, time: string = \"0:0\"): this {\n const [hour, minute] = time.split(\":\").map(Number);\n return this.cron(`${minute ?? 0} ${hour} ${day} ${month} *`);\n }\n\n /*\n |--------------------------------------------------------------------------\n | Frequency — day-of-week shortcuts\n |--------------------------------------------------------------------------\n */\n\n weekdays(): this {\n const parts = this.task.expression.split(\" \");\n parts[4] = \"1-5\";\n return this.cron(parts.join(\" \"));\n }\n\n weekends(): this {\n const parts = this.task.expression.split(\" \");\n parts[4] = \"0,6\";\n return this.cron(parts.join(\" \"));\n }\n\n sundays(): this {\n return this._setDow(\"0\");\n }\n mondays(): this {\n return this._setDow(\"1\");\n }\n tuesdays(): this {\n return this._setDow(\"2\");\n }\n wednesdays(): this {\n return this._setDow(\"3\");\n }\n thursdays(): this {\n return this._setDow(\"4\");\n }\n fridays(): this {\n return this._setDow(\"5\");\n }\n saturdays(): this {\n return this._setDow(\"6\");\n }\n\n private _setDow(dow: string): this {\n const parts = this.task.expression.split(\" \");\n parts[4] = dow;\n return this.cron(parts.join(\" \"));\n }\n\n /*\n |--------------------------------------------------------------------------\n | Time window filter\n |--------------------------------------------------------------------------\n */\n\n /**\n * Only run the task if current time is between start and end (inclusive).\n * @param start \"HH:MM\"\n * @param end \"HH:MM\"\n */\n between(start: string, end: string): this {\n this.task.betweenStart = start;\n this.task.betweenEnd = end;\n return this;\n }\n\n /*\n |--------------------------------------------------------------------------\n | Conditions\n |--------------------------------------------------------------------------\n */\n\n /** Only run when all provided callbacks return true. */\n when(callback: () => boolean | Promise<boolean>): this {\n this.task.conditions.push(callback);\n return this;\n }\n\n /** Skip the task when callback returns true. */\n skip(callback: () => boolean | Promise<boolean>): this {\n this.task.skipConditions.push(callback);\n return this;\n }\n\n /*\n |--------------------------------------------------------------------------\n | Configuration\n |--------------------------------------------------------------------------\n */\n\n cron(expression: string): this {\n this.task.expression = expression;\n try {\n const interval = parser.parse(expression, { tz: this.task.timezone || \"UTC\" });\n this.task.nextRun = interval.next().toDate();\n } catch {\n // invalid expression — leave nextRun as-is\n }\n return this;\n }\n\n name(name: string): this {\n this.task.name = name;\n return this;\n }\n\n description(description: string): this {\n this.task.description = description;\n return this;\n }\n\n timezone(timezone: string): this {\n this.task.timezone = timezone;\n try {\n const interval = parser.parse(this.task.expression, { tz: timezone });\n this.task.nextRun = interval.next().toDate();\n } catch {\n // invalid expression — leave nextRun as-is\n }\n return this;\n }\n\n withoutOverlapping(): this {\n this.task.withoutOverlapping = true;\n return this;\n }\n\n onOneServer(): this {\n this.task.onOneServer = true;\n return this;\n }\n\n evenInMaintenanceMode(): this {\n this.task.evenInMaintenanceMode = true;\n return this;\n }\n\n runInBackground(): this {\n this.task.runInBackground = true;\n return this;\n }\n\n /*\n |--------------------------------------------------------------------------\n | Output / Lifecycle Hooks\n |--------------------------------------------------------------------------\n */\n\n onSuccess(callback: (task: ScheduledTask) => void): this {\n this.task.onSuccessHook = callback;\n return this;\n }\n\n onFailure(callback: (task: ScheduledTask, error: Error) => void): this {\n this.task.onFailureHook = callback;\n return this;\n }\n}\n\n// Singleton\nexport const scheduler = new Schedule();","import { ServiceProvider } from \"@lara-node/core\";\nimport { Queue, QueueManager, scheduler, Schedule } from \"./index.js\";\n\n/**\n * Framework QueueServiceProvider.\n *\n * Override `schedule()` in your app's Console Kernel (or your own QueueServiceProvider)\n * to register cron tasks.\n *\n * @example\n * // app/Providers/QueueServiceProvider.ts\n * import { QueueServiceProvider as BaseProvider } from '@lara-node/queue';\n *\n * export class QueueServiceProvider extends BaseProvider {\n * protected schedule(): void {\n * this.scheduler.command('invoice:mark-overdue').dailyAt('00:00');\n * }\n * }\n */\nexport class QueueServiceProvider extends ServiceProvider {\n protected scheduler = scheduler;\n\n register(): void {\n this.container.singleton(QueueManager, () => Queue);\n this.container.alias(QueueManager, \"queue\");\n this.container.singleton(Schedule, () => scheduler);\n this.container.alias(Schedule, \"schedule\");\n }\n\n boot(): void {\n this.schedule();\n }\n\n /** Override to define scheduled tasks. */\n protected schedule(): void {}\n\n provides(): string[] {\n return [\"queue\", \"schedule\", QueueManager.name, Schedule.name];\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/queue.config.ts
|
|
2
|
+
const queueConfig = {
|
|
3
|
+
default: process.env.QUEUE_CONNECTION ?? "sync",
|
|
4
|
+
connections: {
|
|
5
|
+
sync: { driver: "sync" },
|
|
6
|
+
database: {
|
|
7
|
+
driver: "database",
|
|
8
|
+
table: "jobs",
|
|
9
|
+
queue: "default",
|
|
10
|
+
retry_after: 90
|
|
11
|
+
},
|
|
12
|
+
redis: {
|
|
13
|
+
driver: "redis",
|
|
14
|
+
connection: process.env.REDIS_QUEUE_CONNECTION ?? "default",
|
|
15
|
+
queue: process.env.REDIS_QUEUE ?? "default",
|
|
16
|
+
retry_after: 90
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
failed: {
|
|
20
|
+
driver: "database",
|
|
21
|
+
table: "failed_jobs"
|
|
22
|
+
},
|
|
23
|
+
defaults: {
|
|
24
|
+
tries: 3,
|
|
25
|
+
timeout: 60,
|
|
26
|
+
backoff: [
|
|
27
|
+
1,
|
|
28
|
+
5,
|
|
29
|
+
10
|
|
30
|
+
],
|
|
31
|
+
maxExceptions: 1
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
queueConfig.default;
|
|
35
|
+
//#endregion
|
|
36
|
+
Object.defineProperty(exports, "queueConfig", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function() {
|
|
39
|
+
return queueConfig;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/queue.config.ts
|
|
2
|
+
const queueConfig = {
|
|
3
|
+
default: process.env.QUEUE_CONNECTION ?? "sync",
|
|
4
|
+
connections: {
|
|
5
|
+
sync: { driver: "sync" },
|
|
6
|
+
database: {
|
|
7
|
+
driver: "database",
|
|
8
|
+
table: "jobs",
|
|
9
|
+
queue: "default",
|
|
10
|
+
retry_after: 90
|
|
11
|
+
},
|
|
12
|
+
redis: {
|
|
13
|
+
driver: "redis",
|
|
14
|
+
connection: process.env.REDIS_QUEUE_CONNECTION ?? "default",
|
|
15
|
+
queue: process.env.REDIS_QUEUE ?? "default",
|
|
16
|
+
retry_after: 90
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
failed: {
|
|
20
|
+
driver: "database",
|
|
21
|
+
table: "failed_jobs"
|
|
22
|
+
},
|
|
23
|
+
defaults: {
|
|
24
|
+
tries: 3,
|
|
25
|
+
timeout: 60,
|
|
26
|
+
backoff: [
|
|
27
|
+
1,
|
|
28
|
+
5,
|
|
29
|
+
10
|
|
30
|
+
],
|
|
31
|
+
maxExceptions: 1
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
queueConfig.default;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { queueConfig as t };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=queue.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.config.js","names":[],"sources":["../src/queue.config.ts"],"sourcesContent":["export interface QueueConnectionConfig {\n driver: \"sync\" | \"database\" | \"redis\";\n table?: string;\n queue?: string;\n retry_after?: number;\n connection?: string;\n}\n\nexport interface QueueConfig {\n default: string;\n connections: Record<string, QueueConnectionConfig>;\n failed: { driver: \"database\"; table: string };\n defaults: { tries: number; timeout: number; backoff: number | number[]; maxExceptions: number };\n}\n\nconst queueConfig: QueueConfig = {\n default: process.env.QUEUE_CONNECTION ?? \"sync\",\n connections: {\n sync: { driver: \"sync\" },\n database: { driver: \"database\", table: \"jobs\", queue: \"default\", retry_after: 90 },\n redis: {\n driver: \"redis\",\n connection: process.env.REDIS_QUEUE_CONNECTION ?? \"default\",\n queue: process.env.REDIS_QUEUE ?? \"default\",\n retry_after: 90,\n },\n },\n failed: { driver: \"database\", table: \"failed_jobs\" },\n defaults: { tries: 3, timeout: 60, backoff: [1, 5, 10], maxExceptions: 1 },\n};\n\nexport const QUEUE_CONNECTION = queueConfig.default;\nexport default queueConfig;\n"],"mappings":";AAeA,MAAM,cAA2B;CAC/B,SAAS,QAAQ,IAAI,oBAAoB;CACzC,aAAa;EACX,MAAM,EAAE,QAAQ,OAAO;EACvB,UAAU;GAAE,QAAQ;GAAY,OAAO;GAAQ,OAAO;GAAW,aAAa;EAAG;EACjF,OAAO;GACL,QAAQ;GACR,YAAY,QAAQ,IAAI,0BAA0B;GAClD,OAAO,QAAQ,IAAI,eAAe;GAClC,aAAa;EACf;CACF;CACA,QAAQ;EAAE,QAAQ;EAAY,OAAO;CAAc;CACnD,UAAU;EAAE,OAAO;EAAG,SAAS;EAAI,SAAS;GAAC;GAAG;GAAG;EAAE;EAAG,eAAe;CAAE;AAC3E;AAEgC,YAAY"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lara-node/queue",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lara-Node job queue, worker, and scheduler",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsdown",
|
|
25
|
+
"dev": "tsdown --watch",
|
|
26
|
+
"clean": "rimraf dist",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "vitest run"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@lara-node/cache": "workspace:*",
|
|
32
|
+
"@lara-node/core": "workspace:*",
|
|
33
|
+
"@lara-node/db": "workspace:*",
|
|
34
|
+
"cron-parser": "^5.5.0",
|
|
35
|
+
"redis": "^5.10.0",
|
|
36
|
+
"reflect-metadata": "^0.2.2"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^24.12.2",
|
|
40
|
+
"rimraf": "^6.0.1",
|
|
41
|
+
"tsdown": "^0.12.9",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"vitest": "^3.2.3"
|
|
44
|
+
}
|
|
45
|
+
}
|