UNPKG

@stacksjs/queue

Version:
199 lines (168 loc) 4.54 kB
import type { QueueOption } from '@stacksjs/types'; declare const queueDriver: 'database'; declare interface JobConfig { handle?: (payload?: any) => Promise<void> action?: string | (() => Promise<void>) } declare interface Dispatchable { dispatch: () => Promise<void> dispatchNow: () => Promise<void> delay: (seconds: number) => this afterResponse: () => this chain: (jobs: Dispatchable[]) => this onQueue: (queue: string) => this } export declare function runJob(name: string, options: QueueOption): Promise<void>; export declare class Queue implements Dispatchable { protected options: QueueOption = {} constructor( protected name: string, protected payload?: any, ) { } async dispatch(): Promise<void> { const queueName = this.options.queue || 'default' const jobPayload = this.createJobPayload(queueName) if (this.isQueuedDriver()) { await this.storeQueuedJob(jobPayload) return } if (this.options.afterResponse) { this.deferAfterResponse() return } if (this.options.delay) { this.deferWithDelay() return } await this.runJobImmediately(jobPayload) } private isQueuedDriver(): boolean { return ['database', 'redis'].includes(queueDriver) } private createJobPayload(queueName: string): QueueOption { return { queue: queueName, payload: this.payload, context: this.options.context, maxTries: this.options.maxTries, timeout: this.options.timeout, backoff: this.options.backoff, delay: this.options.delay, } } private async storeQueuedJob(jobPayload: any): Promise<void> { await storeJob(this.name, jobPayload) } private deferAfterResponse(): void { process.on('beforeExit', async () => { await this.dispatchNow() }) } private deferWithDelay(): void { setTimeout(async () => { await this.dispatchNow() }, this.options.delay || 0) } private async runJobImmediately(jobPayload: any): Promise<void> { try { await runJob(this.name, jobPayload) await this.runChainedJobs() } catch (error) { log.error(`Failed to dispatch job ${this.name}:`, error) throw error } } private async runChainedJobs(): Promise<void> { if (!this.options.chainedJobs?.length) return for (const job of this.options.chainedJobs) { await job.dispatch() } } async dispatchNow(): Promise<void> { try { await runJob(this.name, { payload: this.payload, context: this.options.context, immediate: true, }) if (this.options.chainedJobs?.length) { for (const job of this.options.chainedJobs) { await job.dispatchNow() } } } catch (error) { log.error(`Failed to execute job ${this.name}:`, error) throw error } } delay(seconds: number): this { this.options.delay = seconds return this } onQueue(queue: string): this { this.options.queue = queue return this } chain(jobs: Dispatchable[]): this { this.options.chainedJobs = jobs return this } afterResponse(): this { this.options.afterResponse = true return this } tries(count: number): this { this.options.maxTries = count return this } timeout(seconds: number): this { this.options.timeout = seconds return this } backoff(attempts: number[]): this { this.options.backoff = attempts return this } withContext(context: any): this { this.options.context = context return this } } export declare class JobFactory { static make(name: string, payload?: any): Queue { return new Queue(name, payload) } static dispatch(name: string, payload?: any): Queue { const job = new Queue(name, payload) job.dispatch() return job } static async dispatchNow(name: string, payload?: any): Promise<void> { await new Queue(name, payload).dispatchNow() } static later(delay: number, name: string, payload?: any): Queue { return new Queue(name, payload).delay(delay) } static chain(jobs: Queue[]): Queue { const firstJob = jobs[0] return firstJob.chain(jobs.slice(1)) } } export declare function job(name: string, payload?: any): Queue; export declare function processOrder(payload, context): void; export default { async handle() { } }; export default { action: 'SendWelcomeEmail' }; export default { action: async () => { } }; export default async function(payload, context) { };