@libeilong/mq 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -17,6 +17,8 @@ interface HandleOptions {
17
17
  interface BaseTaskOptions {
18
18
  name?: string;
19
19
  concurrency?: number;
20
+ strategyPollInterval?: number;
21
+ autoStart?: boolean;
20
22
  queueOptions?: {
21
23
  keepCompleted?: number;
22
24
  keepFailed?: number;
@@ -39,10 +41,15 @@ declare abstract class BaseTask<D = any> {
39
41
  get name(): string;
40
42
  constructor(connection: IORedis, opts?: Partial<BaseTaskOptions>);
41
43
  private setupEventListeners;
44
+ run(): Promise<void>;
42
45
  abstract handle(job: Job$1<D>, opts: HandleOptions): Promise<any>;
43
46
  onFailed(job: Job$1<D>, error: Error): Promise<void>;
44
47
  onFinalFailed(job: Job$1<D>, error: Error): Promise<void>;
45
48
  onCompleted(job: Job$1<D>): Promise<void>;
49
+ setGroupConfig(groupId: string, config: {
50
+ priority?: number;
51
+ concurrency?: number;
52
+ }): Promise<void>;
46
53
  start(opts: ({
47
54
  groupId: string;
48
55
  data: D;
package/dist/index.d.ts CHANGED
@@ -17,6 +17,8 @@ interface HandleOptions {
17
17
  interface BaseTaskOptions {
18
18
  name?: string;
19
19
  concurrency?: number;
20
+ strategyPollInterval?: number;
21
+ autoStart?: boolean;
20
22
  queueOptions?: {
21
23
  keepCompleted?: number;
22
24
  keepFailed?: number;
@@ -39,10 +41,15 @@ declare abstract class BaseTask<D = any> {
39
41
  get name(): string;
40
42
  constructor(connection: IORedis, opts?: Partial<BaseTaskOptions>);
41
43
  private setupEventListeners;
44
+ run(): Promise<void>;
42
45
  abstract handle(job: Job$1<D>, opts: HandleOptions): Promise<any>;
43
46
  onFailed(job: Job$1<D>, error: Error): Promise<void>;
44
47
  onFinalFailed(job: Job$1<D>, error: Error): Promise<void>;
45
48
  onCompleted(job: Job$1<D>): Promise<void>;
49
+ setGroupConfig(groupId: string, config: {
50
+ priority?: number;
51
+ concurrency?: number;
52
+ }): Promise<void>;
46
53
  start(opts: ({
47
54
  groupId: string;
48
55
  data: D;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`groupmq-plus`);c=s(c);var l=class extends Error{constructor(e,t){super(e),this.name=`TaskRetryLaterError`,this.customDelay=t}},u=class{get name(){return this.opts?.name||this.constructor.name}constructor(e,t){this.connection=e;let n=t??{},r={keepCompleted:0,maxAttempts:3,jobTimeoutMs:3e4};this.opts={concurrency:1,debug:!1,...n,queueOptions:{...r,...n.queueOptions}},this.queue=new c.Queue({redis:e,namespace:this.name,jobTimeoutMs:this.opts.queueOptions?.jobTimeoutMs,maxAttempts:this.opts.queueOptions?.maxAttempts,keepCompleted:this.opts.queueOptions?.keepCompleted,keepFailed:this.opts.queueOptions?.keepFailed,orderingDelayMs:this.opts.queueOptions?.orderingDelayMs}),this.worker=new c.Worker({queue:this.queue,name:this.name,concurrency:this.opts.concurrency,strategy:this.opts.strategy,handler:async e=>this.handle(e,{token:e.id,retryLater:t=>{let n=e.attemptsMade,r=e.opts.attempts,i={};i=t instanceof Error?{error:t,message:t.message}:typeof t==`string`?{message:t}:t||{};let{error:a,delay:o}=i,s=i.message||(a instanceof Error?a.message:void 0)||`retry later`,c=a instanceof Error?a:void 0;if(n+1>=r)throw c||Error(s);let u=new l(s,o);throw c&&(u.stack=c.stack,u.cause=c),u},abort:e=>{throw new c.UnrecoverableError(e||`failed`)}}),backoff:(e,t)=>t instanceof l&&typeof t.customDelay==`number`?t.customDelay:1e3*2**(e-1)}),this.setupEventListeners()}setupEventListeners(){this.worker.on(`completed`,e=>{this.opts.debug&&console.log(`[COMPLETED] ${this.name}-${e.id} (Group: ${e.groupId})`),this.onCompleted(e)}),this.worker.on(`failed`,async e=>{let t=e.attemptsMade>=e.opts.attempts,n=Error(e.failedReason||`task failed`);e.stacktrace&&(n.stack=e.stacktrace),this.opts.debug&&(t?console.error(`[FINAL FAILED] ${this.name}-${e.id}: ${n.message}`):console.log(`[RETRY] ${this.name}-${e.id}: ${n.message}`)),await this.onFailed(e,n),t&&await this.onFinalFailed(e,n)}),this.worker.on(`error`,e=>{this.opts.debug&&console.error(`[WORKER ERROR] ${this.name}`,e)}),this.worker.run()}async onFailed(e,t){}async onFinalFailed(e,t){}async onCompleted(e){}async start(e){let t;if(`children`in e){if(e.children.length===0)throw Error(`Flow must include at least one child job`);t=await this.queue.addFlow({parent:{...e,groupId:e.groupId,data:e.data},children:e.children.map(e=>({...e,groupId:e.groupId,data:e.data}))})}else t=await this.queue.add({groupId:e.groupId,data:e.data,jobId:e.jobId,delay:e.delay});return t.finished=async()=>t.waitUntilFinished(),t}async close(){await this.worker.close()}};exports.BaseTask=u,Object.defineProperty(exports,`Job`,{enumerable:!0,get:function(){return c.Job}}),Object.defineProperty(exports,`Queue`,{enumerable:!0,get:function(){return c.Queue}}),exports.TaskRetryLaterError=l,Object.defineProperty(exports,`UnrecoverableError`,{enumerable:!0,get:function(){return c.UnrecoverableError}}),Object.defineProperty(exports,`Worker`,{enumerable:!0,get:function(){return c.Worker}});
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`groupmq-plus`);c=s(c);var l=class extends Error{constructor(e,t){super(e),this.name=`TaskRetryLaterError`,this.customDelay=t}},u=class{get name(){return this.opts?.name||this.constructor.name}constructor(e,t){this.connection=e;let n=t??{},r={keepCompleted:0,maxAttempts:3,jobTimeoutMs:3e4};this.opts={concurrency:1,debug:!1,autoStart:!0,...n,queueOptions:{...r,...n.queueOptions}},this.queue=new c.Queue({redis:e,namespace:this.name,jobTimeoutMs:this.opts.queueOptions?.jobTimeoutMs,maxAttempts:this.opts.queueOptions?.maxAttempts,keepCompleted:this.opts.queueOptions?.keepCompleted,keepFailed:this.opts.queueOptions?.keepFailed,orderingDelayMs:this.opts.queueOptions?.orderingDelayMs}),this.worker=new c.Worker({queue:this.queue,name:this.name,concurrency:this.opts.concurrency,strategy:this.opts.strategy,strategyPollInterval:this.opts.strategyPollInterval,autoStart:this.opts.autoStart,handler:async e=>this.handle(e,{token:e.id,retryLater:t=>{let n=e.attemptsMade,r=e.opts.attempts,i={};i=t instanceof Error?{error:t,message:t.message}:typeof t==`string`?{message:t}:t||{};let{error:a,delay:o}=i,s=i.message||(a instanceof Error?a.message:void 0)||`retry later`,c=a instanceof Error?a:void 0;if(n+1>=r)throw c||Error(s);let u=new l(s,o);throw c&&(u.stack=c.stack,u.cause=c),u},abort:e=>{throw new c.UnrecoverableError(e||`failed`)}}),backoff:(e,t)=>t instanceof l&&typeof t.customDelay==`number`?t.customDelay:1e3*2**(e-1)}),this.setupEventListeners()}setupEventListeners(){this.worker.on(`completed`,e=>{this.opts.debug&&console.log(`[COMPLETED] ${this.name}-${e.id} (Group: ${e.groupId})`),this.onCompleted(e)}),this.worker.on(`failed`,async e=>{let t=e.attemptsMade>=e.opts.attempts,n=Error(e.failedReason||`task failed`);e.stacktrace&&(n.stack=e.stacktrace),this.opts.debug&&(t?console.error(`[FINAL FAILED] ${this.name}-${e.id}: ${n.message}`):console.log(`[RETRY] ${this.name}-${e.id}: ${n.message}`)),await this.onFailed(e,n),t&&await this.onFinalFailed(e,n)}),this.worker.on(`error`,e=>{this.opts.debug&&console.error(`[WORKER ERROR] ${this.name}`,e)})}async run(){return this.worker.run()}async onFailed(e,t){}async onFinalFailed(e,t){}async onCompleted(e){}setGroupConfig(e,t){return this.queue.setGroupConfig(e,t)}async start(e){let t;if(`children`in e){if(e.children.length===0)throw Error(`Flow must include at least one child job`);t=await this.queue.addFlow({parent:{...e,groupId:e.groupId,data:e.data},children:e.children.map(e=>({...e,groupId:e.groupId,data:e.data}))})}else t=await this.queue.add({groupId:e.groupId,data:e.data,jobId:e.jobId,delay:e.delay});return t.finished=async()=>t.waitUntilFinished(),t}async close(){await this.worker.close()}};exports.BaseTask=u,Object.defineProperty(exports,`Job`,{enumerable:!0,get:function(){return c.Job}}),Object.defineProperty(exports,`Queue`,{enumerable:!0,get:function(){return c.Queue}}),exports.TaskRetryLaterError=l,Object.defineProperty(exports,`UnrecoverableError`,{enumerable:!0,get:function(){return c.UnrecoverableError}}),Object.defineProperty(exports,`Worker`,{enumerable:!0,get:function(){return c.Worker}});
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["connection: IORedis","Queue","Worker","options: RetryOptions","UnrecoverableError","job: Job<D>"],"sources":["../src/BaseTask.ts"],"sourcesContent":["import { FlowJob, Queue, Worker, Job, UnrecoverableError, AddOptions, WorkerOptions } from 'groupmq-plus'\nimport IORedis from 'ioredis'\n\nexport interface RetryOptions {\n error?: Error | unknown\n message?: string\n delay?: number\n}\n\nexport interface HandleOptions {\n token: string\n retryLater(options?: RetryOptions): void\n retryLater(errorOrMessage?: Error | string): void\n retryLater(arg?: RetryOptions | Error | string): void\n abort(message?: string): void\n}\n\nexport interface BaseTaskOptions {\n name?: string\n concurrency?: number\n queueOptions?: {\n keepCompleted?: number\n keepFailed?: number\n maxAttempts?: number\n jobTimeoutMs?: number\n orderingDelayMs?: number\n }\n debug?: boolean\n strategy?: WorkerOptions<any>['strategy']\n}\n\nexport class TaskRetryLaterError extends Error {\n public customDelay?: number\n\n constructor(message?: string, delay?: number) {\n super(message)\n this.name = 'TaskRetryLaterError'\n this.customDelay = delay\n }\n}\n\nexport abstract class BaseTask<D = any> {\n public queue: Queue<D>\n public worker: Worker<D>\n protected opts: BaseTaskOptions\n\n get name() {\n return this.opts?.name || this.constructor.name\n }\n\n constructor(protected connection: IORedis, opts?: Partial<BaseTaskOptions>) {\n const normalizedOpts = opts ?? {}\n const defaultQueueOptions = {\n keepCompleted: 0,\n maxAttempts: 3,\n jobTimeoutMs: 30000,\n }\n this.opts = {\n concurrency: 1,\n debug: false,\n ...normalizedOpts,\n queueOptions: {\n ...defaultQueueOptions,\n ...normalizedOpts.queueOptions,\n },\n }\n\n this.queue = new Queue<D>({\n redis: connection,\n namespace: this.name,\n jobTimeoutMs: this.opts.queueOptions?.jobTimeoutMs,\n maxAttempts: this.opts.queueOptions?.maxAttempts,\n keepCompleted: this.opts.queueOptions?.keepCompleted,\n keepFailed: this.opts.queueOptions?.keepFailed,\n orderingDelayMs: this.opts.queueOptions?.orderingDelayMs,\n })\n\n this.worker = new Worker<D>({\n queue: this.queue,\n name: this.name,\n concurrency: this.opts.concurrency,\n strategy: this.opts.strategy,\n handler: async (job) => {\n return this.handle(job, {\n token: job.id,\n retryLater: (arg: RetryOptions | Error | string) => {\n const currentAttempts = job.attemptsMade\n const maxAttempts = job.opts.attempts\n\n let options: RetryOptions = {}\n if (arg instanceof Error) {\n options = { error: arg, message: arg.message }\n } else if (typeof arg === 'string') {\n options = { message: arg }\n } else {\n options = arg || {}\n }\n\n const { error, delay } = options\n const message = options.message || (error instanceof Error ? error.message : undefined) || 'retry later'\n const errorObj = error instanceof Error ? error : undefined\n\n if (currentAttempts + 1 >= maxAttempts) {\n if (errorObj) throw errorObj\n throw new Error(message)\n }\n\n const retryErr = new TaskRetryLaterError(message, delay)\n if (errorObj) {\n retryErr.stack = errorObj.stack\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - preserve cause on older runtimes\n retryErr.cause = errorObj\n }\n throw retryErr\n },\n abort: (message?: string) => {\n throw new UnrecoverableError(message || 'failed')\n },\n })\n },\n backoff: (attempt: number, error: unknown) => {\n if (error instanceof TaskRetryLaterError && typeof error.customDelay === 'number') {\n return error.customDelay\n }\n return 1000 * Math.pow(2, attempt - 1)\n },\n })\n\n this.setupEventListeners()\n }\n\n private setupEventListeners() {\n this.worker.on('completed', (job) => {\n if (this.opts.debug) {\n console.log(`[COMPLETED] ${this.name}-${job.id} (Group: ${job.groupId})`)\n }\n this.onCompleted(job)\n })\n\n this.worker.on('failed', async (job) => {\n const isFinalFailure = job.attemptsMade >= job.opts.attempts\n const error = new Error(job.failedReason || 'task failed')\n if (job.stacktrace) {\n error.stack = job.stacktrace\n }\n\n if (this.opts.debug) {\n if (!isFinalFailure) {\n console.log(`[RETRY] ${this.name}-${job.id}: ${error.message}`)\n } else {\n console.error(`[FINAL FAILED] ${this.name}-${job.id}: ${error.message}`)\n }\n }\n\n await this.onFailed(job, error)\n\n if (isFinalFailure) {\n await this.onFinalFailed(job, error)\n }\n })\n\n this.worker.on('error', (err) => {\n if (this.opts.debug) {\n console.error(`[WORKER ERROR] ${this.name}`, err)\n }\n })\n\n this.worker.run()\n }\n\n abstract handle(job: Job<D>, opts: HandleOptions): Promise<any>\n\n async onFailed(job: Job<D>, error: Error) {}\n async onFinalFailed(job: Job<D>, error: Error) {}\n async onCompleted(job: Job<D>) {}\n\n async start(\n opts:\n | ({ groupId: string; data: D; jobId?: string; delay?: number } & Omit<AddOptions<D>, 'groupId' | 'data'>)\n | ({\n groupId: string\n data: D\n jobId?: string\n children: ({\n groupId: string\n data: D\n jobId?: string\n } & Omit<FlowJob<D>, 'groupId' | 'data'>)[]\n } & Omit<FlowJob<D>, 'groupId' | 'data'>),\n ) {\n let job: Job<D>\n\n if ('children' in opts) {\n if (opts.children.length === 0) {\n throw new Error('Flow must include at least one child job')\n }\n\n job = await this.queue.addFlow({\n parent: {\n ...opts,\n groupId: opts.groupId,\n data: opts.data,\n },\n children: opts.children.map((child) => ({\n ...child,\n groupId: child.groupId,\n data: child.data,\n })),\n })\n } else {\n job = await this.queue.add({\n groupId: opts.groupId,\n data: opts.data,\n jobId: opts.jobId,\n delay: opts.delay,\n })\n }\n\n // provide a BullMQ-like finished helper\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n job['finished'] = async () => {\n return job.waitUntilFinished()\n }\n\n return job as Job<D> & { finished: () => Promise<any> }\n }\n\n async close() {\n await this.worker.close()\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["connection: IORedis","Queue","Worker","options: RetryOptions","UnrecoverableError","job: Job<D>"],"sources":["../src/BaseTask.ts"],"sourcesContent":["import { FlowJob, Queue, Worker, Job, UnrecoverableError, AddOptions, WorkerOptions } from 'groupmq-plus'\nimport IORedis from 'ioredis'\n\nexport interface RetryOptions {\n error?: Error | unknown\n message?: string\n delay?: number\n}\n\nexport interface HandleOptions {\n token: string\n retryLater(options?: RetryOptions): void\n retryLater(errorOrMessage?: Error | string): void\n retryLater(arg?: RetryOptions | Error | string): void\n abort(message?: string): void\n}\n\nexport interface BaseTaskOptions {\n name?: string\n concurrency?: number\n strategyPollInterval?: number\n autoStart?: boolean\n queueOptions?: {\n keepCompleted?: number\n keepFailed?: number\n maxAttempts?: number\n jobTimeoutMs?: number\n orderingDelayMs?: number\n }\n debug?: boolean\n strategy?: WorkerOptions<any>['strategy']\n}\n\nexport class TaskRetryLaterError extends Error {\n public customDelay?: number\n\n constructor(message?: string, delay?: number) {\n super(message)\n this.name = 'TaskRetryLaterError'\n this.customDelay = delay\n }\n}\n\nexport abstract class BaseTask<D = any> {\n public queue: Queue<D>\n public worker: Worker<D>\n protected opts: BaseTaskOptions\n\n get name() {\n return this.opts?.name || this.constructor.name\n }\n\n constructor(protected connection: IORedis, opts?: Partial<BaseTaskOptions>) {\n const normalizedOpts = opts ?? {}\n const defaultQueueOptions = {\n keepCompleted: 0,\n maxAttempts: 3,\n jobTimeoutMs: 30000,\n }\n this.opts = {\n concurrency: 1,\n debug: false,\n autoStart: true,\n ...normalizedOpts,\n queueOptions: {\n ...defaultQueueOptions,\n ...normalizedOpts.queueOptions,\n },\n }\n\n this.queue = new Queue<D>({\n redis: connection,\n namespace: this.name,\n jobTimeoutMs: this.opts.queueOptions?.jobTimeoutMs,\n maxAttempts: this.opts.queueOptions?.maxAttempts,\n keepCompleted: this.opts.queueOptions?.keepCompleted,\n keepFailed: this.opts.queueOptions?.keepFailed,\n orderingDelayMs: this.opts.queueOptions?.orderingDelayMs,\n })\n\n this.worker = new Worker<D>({\n queue: this.queue,\n name: this.name,\n concurrency: this.opts.concurrency,\n strategy: this.opts.strategy,\n strategyPollInterval: this.opts.strategyPollInterval,\n autoStart: this.opts.autoStart,\n handler: async (job) => {\n return this.handle(job, {\n token: job.id,\n retryLater: (arg: RetryOptions | Error | string) => {\n const currentAttempts = job.attemptsMade\n const maxAttempts = job.opts.attempts\n\n let options: RetryOptions = {}\n if (arg instanceof Error) {\n options = { error: arg, message: arg.message }\n } else if (typeof arg === 'string') {\n options = { message: arg }\n } else {\n options = arg || {}\n }\n\n const { error, delay } = options\n const message = options.message || (error instanceof Error ? error.message : undefined) || 'retry later'\n const errorObj = error instanceof Error ? error : undefined\n\n if (currentAttempts + 1 >= maxAttempts) {\n if (errorObj) throw errorObj\n throw new Error(message)\n }\n\n const retryErr = new TaskRetryLaterError(message, delay)\n if (errorObj) {\n retryErr.stack = errorObj.stack\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - preserve cause on older runtimes\n retryErr.cause = errorObj\n }\n throw retryErr\n },\n abort: (message?: string) => {\n throw new UnrecoverableError(message || 'failed')\n },\n })\n },\n backoff: (attempt: number, error: unknown) => {\n if (error instanceof TaskRetryLaterError && typeof error.customDelay === 'number') {\n return error.customDelay\n }\n return 1000 * Math.pow(2, attempt - 1)\n },\n })\n\n this.setupEventListeners()\n }\n\n private setupEventListeners() {\n this.worker.on('completed', (job) => {\n if (this.opts.debug) {\n console.log(`[COMPLETED] ${this.name}-${job.id} (Group: ${job.groupId})`)\n }\n this.onCompleted(job)\n })\n\n this.worker.on('failed', async (job) => {\n const isFinalFailure = job.attemptsMade >= job.opts.attempts\n const error = new Error(job.failedReason || 'task failed')\n if (job.stacktrace) {\n error.stack = job.stacktrace\n }\n\n if (this.opts.debug) {\n if (!isFinalFailure) {\n console.log(`[RETRY] ${this.name}-${job.id}: ${error.message}`)\n } else {\n console.error(`[FINAL FAILED] ${this.name}-${job.id}: ${error.message}`)\n }\n }\n\n await this.onFailed(job, error)\n\n if (isFinalFailure) {\n await this.onFinalFailed(job, error)\n }\n })\n\n this.worker.on('error', (err) => {\n if (this.opts.debug) {\n console.error(`[WORKER ERROR] ${this.name}`, err)\n }\n })\n }\n\n async run() {\n return this.worker.run()\n }\n\n abstract handle(job: Job<D>, opts: HandleOptions): Promise<any>\n\n async onFailed(job: Job<D>, error: Error) {}\n async onFinalFailed(job: Job<D>, error: Error) {}\n async onCompleted(job: Job<D>) {}\n\n setGroupConfig(groupId: string, config: { priority?: number; concurrency?: number }) {\n return this.queue.setGroupConfig(groupId, config)\n }\n\n async start(\n opts:\n | ({ groupId: string; data: D; jobId?: string; delay?: number } & Omit<AddOptions<D>, 'groupId' | 'data'>)\n | ({\n groupId: string\n data: D\n jobId?: string\n children: ({\n groupId: string\n data: D\n jobId?: string\n } & Omit<FlowJob<D>, 'groupId' | 'data'>)[]\n } & Omit<FlowJob<D>, 'groupId' | 'data'>),\n ) {\n let job: Job<D>\n\n if ('children' in opts) {\n if (opts.children.length === 0) {\n throw new Error('Flow must include at least one child job')\n }\n\n job = await this.queue.addFlow({\n parent: {\n ...opts,\n groupId: opts.groupId,\n data: opts.data,\n },\n children: opts.children.map((child) => ({\n ...child,\n groupId: child.groupId,\n data: child.data,\n })),\n })\n } else {\n job = await this.queue.add({\n groupId: opts.groupId,\n data: opts.data,\n jobId: opts.jobId,\n delay: opts.delay,\n })\n }\n\n // provide a BullMQ-like finished helper\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n job['finished'] = async () => {\n return job.waitUntilFinished()\n }\n\n return job as Job<D> & { finished: () => Promise<any> }\n }\n\n async close() {\n await this.worker.close()\n }\n}\n"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{Job as e,Queue as t,Queue as n,UnrecoverableError as r,UnrecoverableError as i,Worker as a,Worker as o}from"groupmq-plus";var s=class extends Error{constructor(e,t){super(e),this.name=`TaskRetryLaterError`,this.customDelay=t}},c=class{get name(){return this.opts?.name||this.constructor.name}constructor(e,t){this.connection=e;let r=t??{},a={keepCompleted:0,maxAttempts:3,jobTimeoutMs:3e4};this.opts={concurrency:1,debug:!1,...r,queueOptions:{...a,...r.queueOptions}},this.queue=new n({redis:e,namespace:this.name,jobTimeoutMs:this.opts.queueOptions?.jobTimeoutMs,maxAttempts:this.opts.queueOptions?.maxAttempts,keepCompleted:this.opts.queueOptions?.keepCompleted,keepFailed:this.opts.queueOptions?.keepFailed,orderingDelayMs:this.opts.queueOptions?.orderingDelayMs}),this.worker=new o({queue:this.queue,name:this.name,concurrency:this.opts.concurrency,strategy:this.opts.strategy,handler:async e=>this.handle(e,{token:e.id,retryLater:t=>{let n=e.attemptsMade,r=e.opts.attempts,i={};i=t instanceof Error?{error:t,message:t.message}:typeof t==`string`?{message:t}:t||{};let{error:a,delay:o}=i,c=i.message||(a instanceof Error?a.message:void 0)||`retry later`,l=a instanceof Error?a:void 0;if(n+1>=r)throw l||Error(c);let u=new s(c,o);throw l&&(u.stack=l.stack,u.cause=l),u},abort:e=>{throw new i(e||`failed`)}}),backoff:(e,t)=>t instanceof s&&typeof t.customDelay==`number`?t.customDelay:1e3*2**(e-1)}),this.setupEventListeners()}setupEventListeners(){this.worker.on(`completed`,e=>{this.opts.debug&&console.log(`[COMPLETED] ${this.name}-${e.id} (Group: ${e.groupId})`),this.onCompleted(e)}),this.worker.on(`failed`,async e=>{let t=e.attemptsMade>=e.opts.attempts,n=Error(e.failedReason||`task failed`);e.stacktrace&&(n.stack=e.stacktrace),this.opts.debug&&(t?console.error(`[FINAL FAILED] ${this.name}-${e.id}: ${n.message}`):console.log(`[RETRY] ${this.name}-${e.id}: ${n.message}`)),await this.onFailed(e,n),t&&await this.onFinalFailed(e,n)}),this.worker.on(`error`,e=>{this.opts.debug&&console.error(`[WORKER ERROR] ${this.name}`,e)}),this.worker.run()}async onFailed(e,t){}async onFinalFailed(e,t){}async onCompleted(e){}async start(e){let t;if(`children`in e){if(e.children.length===0)throw Error(`Flow must include at least one child job`);t=await this.queue.addFlow({parent:{...e,groupId:e.groupId,data:e.data},children:e.children.map(e=>({...e,groupId:e.groupId,data:e.data}))})}else t=await this.queue.add({groupId:e.groupId,data:e.data,jobId:e.jobId,delay:e.delay});return t.finished=async()=>t.waitUntilFinished(),t}async close(){await this.worker.close()}};export{c as BaseTask,e as Job,t as Queue,s as TaskRetryLaterError,r as UnrecoverableError,a as Worker};
1
+ import{Job as e,Queue as t,Queue as n,UnrecoverableError as r,UnrecoverableError as i,Worker as a,Worker as o}from"groupmq-plus";var s=class extends Error{constructor(e,t){super(e),this.name=`TaskRetryLaterError`,this.customDelay=t}},c=class{get name(){return this.opts?.name||this.constructor.name}constructor(e,t){this.connection=e;let r=t??{},a={keepCompleted:0,maxAttempts:3,jobTimeoutMs:3e4};this.opts={concurrency:1,debug:!1,autoStart:!0,...r,queueOptions:{...a,...r.queueOptions}},this.queue=new n({redis:e,namespace:this.name,jobTimeoutMs:this.opts.queueOptions?.jobTimeoutMs,maxAttempts:this.opts.queueOptions?.maxAttempts,keepCompleted:this.opts.queueOptions?.keepCompleted,keepFailed:this.opts.queueOptions?.keepFailed,orderingDelayMs:this.opts.queueOptions?.orderingDelayMs}),this.worker=new o({queue:this.queue,name:this.name,concurrency:this.opts.concurrency,strategy:this.opts.strategy,strategyPollInterval:this.opts.strategyPollInterval,autoStart:this.opts.autoStart,handler:async e=>this.handle(e,{token:e.id,retryLater:t=>{let n=e.attemptsMade,r=e.opts.attempts,i={};i=t instanceof Error?{error:t,message:t.message}:typeof t==`string`?{message:t}:t||{};let{error:a,delay:o}=i,c=i.message||(a instanceof Error?a.message:void 0)||`retry later`,l=a instanceof Error?a:void 0;if(n+1>=r)throw l||Error(c);let u=new s(c,o);throw l&&(u.stack=l.stack,u.cause=l),u},abort:e=>{throw new i(e||`failed`)}}),backoff:(e,t)=>t instanceof s&&typeof t.customDelay==`number`?t.customDelay:1e3*2**(e-1)}),this.setupEventListeners()}setupEventListeners(){this.worker.on(`completed`,e=>{this.opts.debug&&console.log(`[COMPLETED] ${this.name}-${e.id} (Group: ${e.groupId})`),this.onCompleted(e)}),this.worker.on(`failed`,async e=>{let t=e.attemptsMade>=e.opts.attempts,n=Error(e.failedReason||`task failed`);e.stacktrace&&(n.stack=e.stacktrace),this.opts.debug&&(t?console.error(`[FINAL FAILED] ${this.name}-${e.id}: ${n.message}`):console.log(`[RETRY] ${this.name}-${e.id}: ${n.message}`)),await this.onFailed(e,n),t&&await this.onFinalFailed(e,n)}),this.worker.on(`error`,e=>{this.opts.debug&&console.error(`[WORKER ERROR] ${this.name}`,e)})}async run(){return this.worker.run()}async onFailed(e,t){}async onFinalFailed(e,t){}async onCompleted(e){}setGroupConfig(e,t){return this.queue.setGroupConfig(e,t)}async start(e){let t;if(`children`in e){if(e.children.length===0)throw Error(`Flow must include at least one child job`);t=await this.queue.addFlow({parent:{...e,groupId:e.groupId,data:e.data},children:e.children.map(e=>({...e,groupId:e.groupId,data:e.data}))})}else t=await this.queue.add({groupId:e.groupId,data:e.data,jobId:e.jobId,delay:e.delay});return t.finished=async()=>t.waitUntilFinished(),t}async close(){await this.worker.close()}};export{c as BaseTask,e as Job,t as Queue,s as TaskRetryLaterError,r as UnrecoverableError,a as Worker};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["connection: IORedis","Queue","Worker","options: RetryOptions","UnrecoverableError","job: Job<D>"],"sources":["../src/BaseTask.ts"],"sourcesContent":["import { FlowJob, Queue, Worker, Job, UnrecoverableError, AddOptions, WorkerOptions } from 'groupmq-plus'\nimport IORedis from 'ioredis'\n\nexport interface RetryOptions {\n error?: Error | unknown\n message?: string\n delay?: number\n}\n\nexport interface HandleOptions {\n token: string\n retryLater(options?: RetryOptions): void\n retryLater(errorOrMessage?: Error | string): void\n retryLater(arg?: RetryOptions | Error | string): void\n abort(message?: string): void\n}\n\nexport interface BaseTaskOptions {\n name?: string\n concurrency?: number\n queueOptions?: {\n keepCompleted?: number\n keepFailed?: number\n maxAttempts?: number\n jobTimeoutMs?: number\n orderingDelayMs?: number\n }\n debug?: boolean\n strategy?: WorkerOptions<any>['strategy']\n}\n\nexport class TaskRetryLaterError extends Error {\n public customDelay?: number\n\n constructor(message?: string, delay?: number) {\n super(message)\n this.name = 'TaskRetryLaterError'\n this.customDelay = delay\n }\n}\n\nexport abstract class BaseTask<D = any> {\n public queue: Queue<D>\n public worker: Worker<D>\n protected opts: BaseTaskOptions\n\n get name() {\n return this.opts?.name || this.constructor.name\n }\n\n constructor(protected connection: IORedis, opts?: Partial<BaseTaskOptions>) {\n const normalizedOpts = opts ?? {}\n const defaultQueueOptions = {\n keepCompleted: 0,\n maxAttempts: 3,\n jobTimeoutMs: 30000,\n }\n this.opts = {\n concurrency: 1,\n debug: false,\n ...normalizedOpts,\n queueOptions: {\n ...defaultQueueOptions,\n ...normalizedOpts.queueOptions,\n },\n }\n\n this.queue = new Queue<D>({\n redis: connection,\n namespace: this.name,\n jobTimeoutMs: this.opts.queueOptions?.jobTimeoutMs,\n maxAttempts: this.opts.queueOptions?.maxAttempts,\n keepCompleted: this.opts.queueOptions?.keepCompleted,\n keepFailed: this.opts.queueOptions?.keepFailed,\n orderingDelayMs: this.opts.queueOptions?.orderingDelayMs,\n })\n\n this.worker = new Worker<D>({\n queue: this.queue,\n name: this.name,\n concurrency: this.opts.concurrency,\n strategy: this.opts.strategy,\n handler: async (job) => {\n return this.handle(job, {\n token: job.id,\n retryLater: (arg: RetryOptions | Error | string) => {\n const currentAttempts = job.attemptsMade\n const maxAttempts = job.opts.attempts\n\n let options: RetryOptions = {}\n if (arg instanceof Error) {\n options = { error: arg, message: arg.message }\n } else if (typeof arg === 'string') {\n options = { message: arg }\n } else {\n options = arg || {}\n }\n\n const { error, delay } = options\n const message = options.message || (error instanceof Error ? error.message : undefined) || 'retry later'\n const errorObj = error instanceof Error ? error : undefined\n\n if (currentAttempts + 1 >= maxAttempts) {\n if (errorObj) throw errorObj\n throw new Error(message)\n }\n\n const retryErr = new TaskRetryLaterError(message, delay)\n if (errorObj) {\n retryErr.stack = errorObj.stack\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - preserve cause on older runtimes\n retryErr.cause = errorObj\n }\n throw retryErr\n },\n abort: (message?: string) => {\n throw new UnrecoverableError(message || 'failed')\n },\n })\n },\n backoff: (attempt: number, error: unknown) => {\n if (error instanceof TaskRetryLaterError && typeof error.customDelay === 'number') {\n return error.customDelay\n }\n return 1000 * Math.pow(2, attempt - 1)\n },\n })\n\n this.setupEventListeners()\n }\n\n private setupEventListeners() {\n this.worker.on('completed', (job) => {\n if (this.opts.debug) {\n console.log(`[COMPLETED] ${this.name}-${job.id} (Group: ${job.groupId})`)\n }\n this.onCompleted(job)\n })\n\n this.worker.on('failed', async (job) => {\n const isFinalFailure = job.attemptsMade >= job.opts.attempts\n const error = new Error(job.failedReason || 'task failed')\n if (job.stacktrace) {\n error.stack = job.stacktrace\n }\n\n if (this.opts.debug) {\n if (!isFinalFailure) {\n console.log(`[RETRY] ${this.name}-${job.id}: ${error.message}`)\n } else {\n console.error(`[FINAL FAILED] ${this.name}-${job.id}: ${error.message}`)\n }\n }\n\n await this.onFailed(job, error)\n\n if (isFinalFailure) {\n await this.onFinalFailed(job, error)\n }\n })\n\n this.worker.on('error', (err) => {\n if (this.opts.debug) {\n console.error(`[WORKER ERROR] ${this.name}`, err)\n }\n })\n\n this.worker.run()\n }\n\n abstract handle(job: Job<D>, opts: HandleOptions): Promise<any>\n\n async onFailed(job: Job<D>, error: Error) {}\n async onFinalFailed(job: Job<D>, error: Error) {}\n async onCompleted(job: Job<D>) {}\n\n async start(\n opts:\n | ({ groupId: string; data: D; jobId?: string; delay?: number } & Omit<AddOptions<D>, 'groupId' | 'data'>)\n | ({\n groupId: string\n data: D\n jobId?: string\n children: ({\n groupId: string\n data: D\n jobId?: string\n } & Omit<FlowJob<D>, 'groupId' | 'data'>)[]\n } & Omit<FlowJob<D>, 'groupId' | 'data'>),\n ) {\n let job: Job<D>\n\n if ('children' in opts) {\n if (opts.children.length === 0) {\n throw new Error('Flow must include at least one child job')\n }\n\n job = await this.queue.addFlow({\n parent: {\n ...opts,\n groupId: opts.groupId,\n data: opts.data,\n },\n children: opts.children.map((child) => ({\n ...child,\n groupId: child.groupId,\n data: child.data,\n })),\n })\n } else {\n job = await this.queue.add({\n groupId: opts.groupId,\n data: opts.data,\n jobId: opts.jobId,\n delay: opts.delay,\n })\n }\n\n // provide a BullMQ-like finished helper\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n job['finished'] = async () => {\n return job.waitUntilFinished()\n }\n\n return job as Job<D> & { finished: () => Promise<any> }\n }\n\n async close() {\n await this.worker.close()\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["connection: IORedis","Queue","Worker","options: RetryOptions","UnrecoverableError","job: Job<D>"],"sources":["../src/BaseTask.ts"],"sourcesContent":["import { FlowJob, Queue, Worker, Job, UnrecoverableError, AddOptions, WorkerOptions } from 'groupmq-plus'\nimport IORedis from 'ioredis'\n\nexport interface RetryOptions {\n error?: Error | unknown\n message?: string\n delay?: number\n}\n\nexport interface HandleOptions {\n token: string\n retryLater(options?: RetryOptions): void\n retryLater(errorOrMessage?: Error | string): void\n retryLater(arg?: RetryOptions | Error | string): void\n abort(message?: string): void\n}\n\nexport interface BaseTaskOptions {\n name?: string\n concurrency?: number\n strategyPollInterval?: number\n autoStart?: boolean\n queueOptions?: {\n keepCompleted?: number\n keepFailed?: number\n maxAttempts?: number\n jobTimeoutMs?: number\n orderingDelayMs?: number\n }\n debug?: boolean\n strategy?: WorkerOptions<any>['strategy']\n}\n\nexport class TaskRetryLaterError extends Error {\n public customDelay?: number\n\n constructor(message?: string, delay?: number) {\n super(message)\n this.name = 'TaskRetryLaterError'\n this.customDelay = delay\n }\n}\n\nexport abstract class BaseTask<D = any> {\n public queue: Queue<D>\n public worker: Worker<D>\n protected opts: BaseTaskOptions\n\n get name() {\n return this.opts?.name || this.constructor.name\n }\n\n constructor(protected connection: IORedis, opts?: Partial<BaseTaskOptions>) {\n const normalizedOpts = opts ?? {}\n const defaultQueueOptions = {\n keepCompleted: 0,\n maxAttempts: 3,\n jobTimeoutMs: 30000,\n }\n this.opts = {\n concurrency: 1,\n debug: false,\n autoStart: true,\n ...normalizedOpts,\n queueOptions: {\n ...defaultQueueOptions,\n ...normalizedOpts.queueOptions,\n },\n }\n\n this.queue = new Queue<D>({\n redis: connection,\n namespace: this.name,\n jobTimeoutMs: this.opts.queueOptions?.jobTimeoutMs,\n maxAttempts: this.opts.queueOptions?.maxAttempts,\n keepCompleted: this.opts.queueOptions?.keepCompleted,\n keepFailed: this.opts.queueOptions?.keepFailed,\n orderingDelayMs: this.opts.queueOptions?.orderingDelayMs,\n })\n\n this.worker = new Worker<D>({\n queue: this.queue,\n name: this.name,\n concurrency: this.opts.concurrency,\n strategy: this.opts.strategy,\n strategyPollInterval: this.opts.strategyPollInterval,\n autoStart: this.opts.autoStart,\n handler: async (job) => {\n return this.handle(job, {\n token: job.id,\n retryLater: (arg: RetryOptions | Error | string) => {\n const currentAttempts = job.attemptsMade\n const maxAttempts = job.opts.attempts\n\n let options: RetryOptions = {}\n if (arg instanceof Error) {\n options = { error: arg, message: arg.message }\n } else if (typeof arg === 'string') {\n options = { message: arg }\n } else {\n options = arg || {}\n }\n\n const { error, delay } = options\n const message = options.message || (error instanceof Error ? error.message : undefined) || 'retry later'\n const errorObj = error instanceof Error ? error : undefined\n\n if (currentAttempts + 1 >= maxAttempts) {\n if (errorObj) throw errorObj\n throw new Error(message)\n }\n\n const retryErr = new TaskRetryLaterError(message, delay)\n if (errorObj) {\n retryErr.stack = errorObj.stack\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - preserve cause on older runtimes\n retryErr.cause = errorObj\n }\n throw retryErr\n },\n abort: (message?: string) => {\n throw new UnrecoverableError(message || 'failed')\n },\n })\n },\n backoff: (attempt: number, error: unknown) => {\n if (error instanceof TaskRetryLaterError && typeof error.customDelay === 'number') {\n return error.customDelay\n }\n return 1000 * Math.pow(2, attempt - 1)\n },\n })\n\n this.setupEventListeners()\n }\n\n private setupEventListeners() {\n this.worker.on('completed', (job) => {\n if (this.opts.debug) {\n console.log(`[COMPLETED] ${this.name}-${job.id} (Group: ${job.groupId})`)\n }\n this.onCompleted(job)\n })\n\n this.worker.on('failed', async (job) => {\n const isFinalFailure = job.attemptsMade >= job.opts.attempts\n const error = new Error(job.failedReason || 'task failed')\n if (job.stacktrace) {\n error.stack = job.stacktrace\n }\n\n if (this.opts.debug) {\n if (!isFinalFailure) {\n console.log(`[RETRY] ${this.name}-${job.id}: ${error.message}`)\n } else {\n console.error(`[FINAL FAILED] ${this.name}-${job.id}: ${error.message}`)\n }\n }\n\n await this.onFailed(job, error)\n\n if (isFinalFailure) {\n await this.onFinalFailed(job, error)\n }\n })\n\n this.worker.on('error', (err) => {\n if (this.opts.debug) {\n console.error(`[WORKER ERROR] ${this.name}`, err)\n }\n })\n }\n\n async run() {\n return this.worker.run()\n }\n\n abstract handle(job: Job<D>, opts: HandleOptions): Promise<any>\n\n async onFailed(job: Job<D>, error: Error) {}\n async onFinalFailed(job: Job<D>, error: Error) {}\n async onCompleted(job: Job<D>) {}\n\n setGroupConfig(groupId: string, config: { priority?: number; concurrency?: number }) {\n return this.queue.setGroupConfig(groupId, config)\n }\n\n async start(\n opts:\n | ({ groupId: string; data: D; jobId?: string; delay?: number } & Omit<AddOptions<D>, 'groupId' | 'data'>)\n | ({\n groupId: string\n data: D\n jobId?: string\n children: ({\n groupId: string\n data: D\n jobId?: string\n } & Omit<FlowJob<D>, 'groupId' | 'data'>)[]\n } & Omit<FlowJob<D>, 'groupId' | 'data'>),\n ) {\n let job: Job<D>\n\n if ('children' in opts) {\n if (opts.children.length === 0) {\n throw new Error('Flow must include at least one child job')\n }\n\n job = await this.queue.addFlow({\n parent: {\n ...opts,\n groupId: opts.groupId,\n data: opts.data,\n },\n children: opts.children.map((child) => ({\n ...child,\n groupId: child.groupId,\n data: child.data,\n })),\n })\n } else {\n job = await this.queue.add({\n groupId: opts.groupId,\n data: opts.data,\n jobId: opts.jobId,\n delay: opts.delay,\n })\n }\n\n // provide a BullMQ-like finished helper\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n job['finished'] = async () => {\n return job.waitUntilFinished()\n }\n\n return job as Job<D> & { finished: () => Promise<any> }\n }\n\n async close() {\n await this.worker.close()\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@libeilong/mq",
3
3
  "author": "lblblong",
4
- "version": "0.1.1",
4
+ "version": "0.1.2",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -20,7 +20,7 @@
20
20
  "node": ">=18"
21
21
  },
22
22
  "dependencies": {
23
- "groupmq-plus": "^1.2.1"
23
+ "groupmq-plus": "^1.2.2"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "ioredis": "^5.7.0"
@@ -31,6 +31,6 @@
31
31
  "scripts": {
32
32
  "dev": "tsdown --watch",
33
33
  "build": "tsdown",
34
- "test": "vitest"
34
+ "test": "vitest run --no-file-parallelism"
35
35
  }
36
36
  }