@libeilong/mq 0.2.6 → 0.2.7

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
@@ -27,16 +27,15 @@ interface HandleOptions {
27
27
  retryLater(options?: RetryOptions): Error;
28
28
  retryLater(errorOrMessage?: Error | string): Error;
29
29
  retryLater(arg?: RetryOptions | Error | string): Error;
30
- abort(message?: string): Error;
30
+ abort(arg?: Error | string | number): Error;
31
31
  }
32
32
  interface BaseTaskOptions {
33
33
  name?: string;
34
- autoStart?: boolean;
35
34
  queueOptions?: Partial<QueueOptions>;
36
35
  workerOptions?: Omit<Partial<WorkerOptions<any>>, 'backoff'> & {
37
36
  backoff?: BackoffOptions | ((attempt: number, error: unknown) => number);
38
37
  };
39
- debug?: boolean;
38
+ logger?: boolean;
40
39
  }
41
40
  declare class TaskRetryLaterError extends Error {
42
41
  customDelay?: number;
@@ -50,7 +49,6 @@ declare abstract class BaseTask<D = any> {
50
49
  get name(): string;
51
50
  constructor(connection: IORedis, opts?: Partial<BaseTaskOptions>);
52
51
  private setupEventListeners;
53
- run(): Promise<void>;
54
52
  abstract handle(job: Job$1<D>, opts: HandleOptions): Promise<any>;
55
53
  onFailed(job: Job$1<D>, error: Error): Promise<void>;
56
54
  onFinalFailed(job: Job$1<D>, error: Error): Promise<void>;
@@ -62,6 +60,7 @@ declare abstract class BaseTask<D = any> {
62
60
  start(opts: AddOptions$1<D> | FlowOptions$1<D, D>): Promise<Job$1<D> & {
63
61
  finished: (timeoutMs?: number) => Promise<any>;
64
62
  }>;
63
+ run(): Promise<void>;
65
64
  close(): Promise<void>;
66
65
  }
67
66
  //#endregion
package/dist/index.d.ts CHANGED
@@ -27,16 +27,15 @@ interface HandleOptions {
27
27
  retryLater(options?: RetryOptions): Error;
28
28
  retryLater(errorOrMessage?: Error | string): Error;
29
29
  retryLater(arg?: RetryOptions | Error | string): Error;
30
- abort(message?: string): Error;
30
+ abort(arg?: Error | string | number): Error;
31
31
  }
32
32
  interface BaseTaskOptions {
33
33
  name?: string;
34
- autoStart?: boolean;
35
34
  queueOptions?: Partial<QueueOptions>;
36
35
  workerOptions?: Omit<Partial<WorkerOptions<any>>, 'backoff'> & {
37
36
  backoff?: BackoffOptions | ((attempt: number, error: unknown) => number);
38
37
  };
39
- debug?: boolean;
38
+ logger?: boolean;
40
39
  }
41
40
  declare class TaskRetryLaterError extends Error {
42
41
  customDelay?: number;
@@ -50,7 +49,6 @@ declare abstract class BaseTask<D = any> {
50
49
  get name(): string;
51
50
  constructor(connection: IORedis, opts?: Partial<BaseTaskOptions>);
52
51
  private setupEventListeners;
53
- run(): Promise<void>;
54
52
  abstract handle(job: Job$1<D>, opts: HandleOptions): Promise<any>;
55
53
  onFailed(job: Job$1<D>, error: Error): Promise<void>;
56
54
  onFinalFailed(job: Job$1<D>, error: Error): Promise<void>;
@@ -62,6 +60,7 @@ declare abstract class BaseTask<D = any> {
62
60
  start(opts: AddOptions$1<D> | FlowOptions$1<D, D>): Promise<Job$1<D> & {
63
61
  finished: (timeoutMs?: number) => Promise<any>;
64
62
  }>;
63
+ run(): Promise<void>;
65
64
  close(): Promise<void>;
66
65
  }
67
66
  //#endregion
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,this.opts=Object.assign({debug:!1,autoStart:!0,queueOptions:{},workerOptions:{}},t),this.opts.queueOptions=Object.assign({redis:e,namespace:this.name,keepCompleted:0,maxAttempts:3,jobTimeoutMs:5e3},this.opts.queueOptions),this.opts.workerOptions=Object.assign({concurrency:1,name:this.name},this.opts.workerOptions),this.queue=new c.Queue(this.opts.queueOptions),this.worker=new c.Worker({...this.opts.workerOptions,autoStart:this.opts.autoStart,queue:this.queue,handler:async e=>{try{return await this.handle(e,{token:e.token,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)return c||Error(s);let u=new l(s,o);return c&&(u.stack=c.stack,u.cause=c),u},abort:e=>new c.UnrecoverableError(e||`failed`)})}catch(e){throw e instanceof Error&&Object.defineProperty(e,`__isJobError`,{value:!0,enumerable:!1}),e}},backoff:(e,t)=>{if(t instanceof l&&typeof t.customDelay==`number`)return t.customDelay;let n=this.opts.workerOptions?.backoff;if(typeof n==`function`)return n(e,t);if(n&&typeof n==`object`){let{type:t,delay:r=1e3,jitter:i=0}=n,a;if(a=t===`fixed`?r:t===`exponential`?r*2**(e-1):1e3*2**(e-1),i>0){let e=a*i,t=Math.random()*e*2-e;a+=t}return Math.max(0,Math.round(a))}return 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.isFailed(),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=>{e instanceof l||e instanceof Error&&e.__isJobError||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.groups.setConfig(e,t)}async start(e){let t;if(`children`in e&&Array.isArray(e.children)){if(!e.parent)throw Error(`Flow must include a parent job`);if(e.children.length===0)throw Error(`Flow must include at least one child job`);t=await this.queue.addFlow(e)}else t=await this.queue.add(e);return t.finished=async e=>t.waitUntilFinished(e),t}async close(){await this.worker.close()}};exports.BaseTask=u,Object.defineProperty(exports,`Job`,{enumerable:!0,get:function(){return c.Job}}),Object.defineProperty(exports,`PriorityStrategy`,{enumerable:!0,get:function(){return c.PriorityStrategy}}),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);let l=require(`@libeilong/func`);l=s(l);var u=class extends Error{constructor(e,t){super(e),this.name=`TaskRetryLaterError`,this.customDelay=t}},d=class{get name(){return this.opts?.name||this.constructor.name}constructor(e,t){this.connection=e,this.opts=Object.assign({logger:!1,queueOptions:{},workerOptions:{}},t),this.opts.queueOptions=Object.assign({redis:e,namespace:this.name},this.opts.queueOptions),this.opts.workerOptions=Object.assign({name:this.name},this.opts.workerOptions),this.queue=new c.Queue(this.opts.queueOptions),this.worker=new c.Worker({...this.opts.workerOptions,queue:this.queue,handler:async e=>{try{return await this.handle(e,{token:e.token,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)return c||Error(s);let l=new u(s,o);return c&&(l.stack=c.stack,l.cause=c),l},abort:e=>{let t=new c.UnrecoverableError((0,l.extractErrorMessage)(e)||`failed`);return e instanceof Error&&(t.stack=e.stack,t.cause=e),t}})}catch(e){throw e instanceof Error&&Object.defineProperty(e,`__isJobError`,{value:!0,enumerable:!1}),e}},backoff:(e,t)=>{if(t instanceof u&&typeof t.customDelay==`number`)return t.customDelay;let n=this.opts.workerOptions?.backoff;if(typeof n==`function`)return n(e,t);if(n&&typeof n==`object`){let{type:t,delay:r=1e3,jitter:i=0}=n,a;if(a=t===`fixed`?r:t===`exponential`?r*2**(e-1):1e3*2**(e-1),i>0){let e=a*i,t=Math.random()*e*2-e;a+=t}return Math.max(0,Math.round(a))}return 1e3*2**(e-1)}}),this.setupEventListeners()}setupEventListeners(){this.worker.on(`completed`,e=>{this.opts.logger&&console.log(`[COMPLETED] ${this.name}-${e.id} (Group: ${e.groupId})`),this.onCompleted(e)}),this.worker.on(`failed`,async e=>{let t=e.isFailed(),n=Error(e.failedReason||`task failed`);e.stacktrace&&(n.stack=e.stacktrace),this.opts.logger&&(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=>{e instanceof u||e instanceof Error&&e.__isJobError||this.opts.logger&&console.error(`[WORKER ERROR] ${this.name}`,e)})}async onFailed(e,t){}async onFinalFailed(e,t){}async onCompleted(e){}setGroupConfig(e,t){return this.queue.groups.setConfig(e,t)}async start(e){let t;if(`children`in e&&Array.isArray(e.children)){if(!e.parent)throw Error(`Flow must include a parent job`);if(e.children.length===0)throw Error(`Flow must include at least one child job`);t=await this.queue.addFlow(e)}else t=await this.queue.add(e);return t.finished=async e=>t.waitUntilFinished(e),t}async run(){return this.worker.run()}async close(){await this.worker.close()}};exports.BaseTask=d,Object.defineProperty(exports,`Job`,{enumerable:!0,get:function(){return c.Job}}),Object.defineProperty(exports,`PriorityStrategy`,{enumerable:!0,get:function(){return c.PriorityStrategy}}),Object.defineProperty(exports,`Queue`,{enumerable:!0,get:function(){return c.Queue}}),exports.TaskRetryLaterError=u,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","baseDelay: number","job: Job<D>"],"sources":["../src/BaseTask.ts"],"sourcesContent":["import {\n AddOptions,\n FlowJob,\n FlowOptions,\n GroupOptions,\n Job,\n Queue,\n QueueOptions,\n UnrecoverableError,\n Worker,\n WorkerOptions,\n} from 'groupmq-plus'\nimport IORedis from 'ioredis'\n\nexport type { FlowChildResult } from 'groupmq-plus'\n\nexport interface BackoffOptions {\n /** 延迟策略:固定间隔、指数递增 */\n type: 'fixed' | 'exponential'\n /** 延迟时间为毫秒 */\n delay?: number\n /**\n * 添加到延迟中的随机抖动比例,取值范围为 0 到 1 之间的小数。\n * 抖动用于增加随机性,以防止因多个任务同时重试而导致的惊群效应。\n * 最终的延迟时间将在 `delay ± (delay * jitter)` 的范围内随机波动。\n * @defaultValue 0\n * @example 0.1 // 为延迟添加 ±10% 的随机抖动\n * @example 0.25 // 为延迟添加 ±25% 的随机抖动\n */\n jitter?: number\n}\n\nexport interface RetryOptions {\n error?: Error | unknown\n message?: string\n delay?: number\n}\n\n// [修复] 修改返回值类型为 Error,否则外层无法使用 throw opts.retryLater(...)\nexport interface HandleOptions {\n token: string\n retryLater(options?: RetryOptions): Error\n retryLater(errorOrMessage?: Error | string): Error\n retryLater(arg?: RetryOptions | Error | string): Error\n abort(message?: string): Error\n}\n\nexport interface BaseTaskOptions {\n name?: string\n autoStart?: boolean\n queueOptions?: Partial<QueueOptions>\n workerOptions?: Omit<Partial<WorkerOptions<any>>, 'backoff'> & {\n backoff?: BackoffOptions | ((attempt: number, error: unknown) => number)\n }\n debug?: boolean\n}\n\n/**\n * 任务启动选项,用于单个任务或 Flow 中的子任务\n */\nexport type TaskStartOptions<D> = {\n groupId: string\n data: D\n jobId?: string\n delay?: number\n groupConfig?: GroupOptions\n} & Omit<AddOptions<D>, 'groupId' | 'data' | 'groupConfig'>\n\n/**\n * Flow 任务选项\n */\nexport type TaskFlowOptions<D> = {\n groupId: string\n data: D\n jobId?: string\n groupConfig?: GroupOptions\n children: Array<\n {\n groupId: string\n data: D\n jobId?: string\n groupConfig?: GroupOptions\n } & Omit<FlowJob<D>, 'groupId' | 'data'>\n >\n} & Omit<FlowJob<D>, 'groupId' | 'data'>\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 this.opts = Object.assign(\n {\n debug: false,\n autoStart: true,\n queueOptions: {},\n workerOptions: {},\n },\n opts,\n )\n\n this.opts.queueOptions = Object.assign(\n {\n redis: connection,\n namespace: this.name,\n keepCompleted: 0,\n maxAttempts: 3,\n jobTimeoutMs: 5000,\n },\n this.opts.queueOptions,\n )\n\n this.opts.workerOptions = Object.assign(\n {\n concurrency: 1,\n name: this.name,\n },\n this.opts.workerOptions,\n )\n\n this.queue = new Queue<D>(this.opts.queueOptions as QueueOptions)\n\n this.worker = new Worker<D>({\n ...(this.opts.workerOptions as WorkerOptions<D>),\n autoStart: this.opts.autoStart,\n queue: this.queue,\n handler: async (job) => {\n try {\n return await this.handle(job, {\n token: job.token!,\n retryLater: (arg: RetryOptions | Error | string): Error => {\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 // [修改] 返回错误而不是抛出\n if (errorObj) return errorObj\n return 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\n retryErr.cause = errorObj\n }\n // [修改] 返回错误\n return retryErr\n },\n abort: (message?: string) => {\n // [修改] 返回错误\n return new UnrecoverableError(message || 'failed')\n },\n })\n } catch (err) {\n if (err instanceof Error) {\n Object.defineProperty(err, '__isJobError', { value: true, enumerable: false })\n }\n throw err\n }\n },\n backoff: (attempt: number, error: unknown) => {\n if (error instanceof TaskRetryLaterError && typeof error.customDelay === 'number') {\n return error.customDelay\n }\n\n const backoffConfig = this.opts.workerOptions?.backoff\n\n if (typeof backoffConfig === 'function') {\n return backoffConfig(attempt, error)\n }\n\n if (backoffConfig && typeof backoffConfig === 'object') {\n const { type, delay = 1000, jitter = 0 } = backoffConfig\n let baseDelay: number\n\n if (type === 'fixed') {\n baseDelay = delay\n } else if (type === 'exponential') {\n baseDelay = delay * Math.pow(2, attempt - 1)\n } else {\n baseDelay = 1000 * Math.pow(2, attempt - 1)\n }\n\n if (jitter > 0) {\n const jitterAmount = baseDelay * jitter\n const randomJitter = Math.random() * jitterAmount * 2 - jitterAmount\n baseDelay = baseDelay + randomJitter\n }\n\n return Math.max(0, Math.round(baseDelay))\n }\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.isFailed()\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 (err instanceof TaskRetryLaterError) return\n if (err instanceof Error && (err as any).__isJobError) return\n\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.groups.setConfig(groupId, config)\n }\n\n async start(opts: AddOptions<D> | FlowOptions<D, D>) {\n let job: Job<D>\n\n if ('children' in opts && Array.isArray(opts.children)) {\n if (!opts.parent) {\n throw new Error('Flow must include a parent job')\n }\n\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(opts)\n } else {\n job = await this.queue.add(opts as AddOptions<D>)\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n job['finished'] = async (timeoutMs?: number) => {\n return job.waitUntilFinished(timeoutMs)\n }\n\n return job as Job<D> & { finished: (timeoutMs?: number) => Promise<any> }\n }\n\n async close() {\n await this.worker.close()\n }\n}"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["connection: IORedis","Queue","Worker","options: RetryOptions","UnrecoverableError","baseDelay: number","job: Job<D>"],"sources":["../src/BaseTask.ts"],"sourcesContent":["import { extractErrorMessage } from '@libeilong/func'\nimport {\n AddOptions,\n FlowJob,\n FlowOptions,\n GroupOptions,\n Job,\n Queue,\n QueueOptions,\n UnrecoverableError,\n Worker,\n WorkerOptions,\n} from 'groupmq-plus'\nimport IORedis from 'ioredis'\n\nexport type { FlowChildResult } from 'groupmq-plus'\n\nexport interface BackoffOptions {\n /** 延迟策略:固定间隔、指数递增 */\n type: 'fixed' | 'exponential'\n /** 延迟时间为毫秒 */\n delay?: number\n /**\n * 添加到延迟中的随机抖动比例,取值范围为 0 到 1 之间的小数。\n * 抖动用于增加随机性,以防止因多个任务同时重试而导致的惊群效应。\n * 最终的延迟时间将在 `delay ± (delay * jitter)` 的范围内随机波动。\n * @defaultValue 0\n * @example 0.1 // 为延迟添加 ±10% 的随机抖动\n * @example 0.25 // 为延迟添加 ±25% 的随机抖动\n */\n jitter?: number\n}\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): Error\n retryLater(errorOrMessage?: Error | string): Error\n retryLater(arg?: RetryOptions | Error | string): Error\n abort(arg?: Error | string | number): Error\n}\n\nexport interface BaseTaskOptions {\n name?: string\n queueOptions?: Partial<QueueOptions>\n workerOptions?: Omit<Partial<WorkerOptions<any>>, 'backoff'> & {\n backoff?: BackoffOptions | ((attempt: number, error: unknown) => number)\n }\n logger?: boolean\n}\n\n/**\n * 任务启动选项,用于单个任务或 Flow 中的子任务\n */\nexport type TaskStartOptions<D> = {\n groupId: string\n data: D\n jobId?: string\n delay?: number\n groupConfig?: GroupOptions\n} & Omit<AddOptions<D>, 'groupId' | 'data' | 'groupConfig'>\n\n/**\n * Flow 任务选项\n */\nexport type TaskFlowOptions<D> = {\n groupId: string\n data: D\n jobId?: string\n groupConfig?: GroupOptions\n children: Array<\n {\n groupId: string\n data: D\n jobId?: string\n groupConfig?: GroupOptions\n } & Omit<FlowJob<D>, 'groupId' | 'data'>\n >\n} & Omit<FlowJob<D>, 'groupId' | 'data'>\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 this.opts = Object.assign(\n {\n logger: false,\n queueOptions: {},\n workerOptions: {},\n },\n opts,\n )\n\n this.opts.queueOptions = Object.assign(\n {\n redis: connection,\n namespace: this.name,\n },\n this.opts.queueOptions,\n )\n\n this.opts.workerOptions = Object.assign({ name: this.name }, this.opts.workerOptions)\n\n this.queue = new Queue<D>(this.opts.queueOptions as QueueOptions)\n\n this.worker = new Worker<D>({\n ...(this.opts.workerOptions as WorkerOptions<D>),\n queue: this.queue,\n handler: async (job) => {\n try {\n return await this.handle(job, {\n token: job.token!,\n retryLater: (arg: RetryOptions | Error | string): Error => {\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 // [修改] 返回错误而不是抛出\n if (errorObj) return errorObj\n return 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\n retryErr.cause = errorObj\n }\n // [修改] 返回错误\n return retryErr\n },\n abort: (arg?: Error | string | number) => {\n const error = new UnrecoverableError(extractErrorMessage(arg) || 'failed')\n if (arg instanceof Error) {\n error.stack = arg.stack\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n error.cause = arg\n }\n return error\n },\n })\n } catch (err) {\n if (err instanceof Error) {\n Object.defineProperty(err, '__isJobError', { value: true, enumerable: false })\n }\n throw err\n }\n },\n backoff: (attempt: number, error: unknown) => {\n if (error instanceof TaskRetryLaterError && typeof error.customDelay === 'number') {\n return error.customDelay\n }\n\n const backoffConfig = this.opts.workerOptions?.backoff\n\n if (typeof backoffConfig === 'function') {\n return backoffConfig(attempt, error)\n }\n\n if (backoffConfig && typeof backoffConfig === 'object') {\n const { type, delay = 1000, jitter = 0 } = backoffConfig\n let baseDelay: number\n\n if (type === 'fixed') {\n baseDelay = delay\n } else if (type === 'exponential') {\n baseDelay = delay * Math.pow(2, attempt - 1)\n } else {\n baseDelay = 1000 * Math.pow(2, attempt - 1)\n }\n\n if (jitter > 0) {\n const jitterAmount = baseDelay * jitter\n const randomJitter = Math.random() * jitterAmount * 2 - jitterAmount\n baseDelay = baseDelay + randomJitter\n }\n\n return Math.max(0, Math.round(baseDelay))\n }\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.logger) {\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.isFailed()\n const error = new Error(job.failedReason || 'task failed')\n if (job.stacktrace) {\n error.stack = job.stacktrace\n }\n\n if (this.opts.logger) {\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 (err instanceof TaskRetryLaterError) return\n if (err instanceof Error && (err as any).__isJobError) return\n\n if (this.opts.logger) {\n console.error(`[WORKER ERROR] ${this.name}`, err)\n }\n })\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.groups.setConfig(groupId, config)\n }\n\n async start(opts: AddOptions<D> | FlowOptions<D, D>) {\n let job: Job<D>\n\n if ('children' in opts && Array.isArray(opts.children)) {\n if (!opts.parent) {\n throw new Error('Flow must include a parent job')\n }\n\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(opts)\n } else {\n job = await this.queue.add(opts as AddOptions<D>)\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n job['finished'] = async (timeoutMs?: number) => {\n return job.waitUntilFinished(timeoutMs)\n }\n\n return job as Job<D> & { finished: (timeoutMs?: number) => Promise<any> }\n }\n\n async run() {\n return this.worker.run()\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,PriorityStrategy as t,Queue as n,Queue as r,UnrecoverableError as i,UnrecoverableError as a,Worker as o,Worker as s}from"groupmq-plus";var c=class extends Error{constructor(e,t){super(e),this.name=`TaskRetryLaterError`,this.customDelay=t}},l=class{get name(){return this.opts?.name||this.constructor.name}constructor(e,t){this.connection=e,this.opts=Object.assign({debug:!1,autoStart:!0,queueOptions:{},workerOptions:{}},t),this.opts.queueOptions=Object.assign({redis:e,namespace:this.name,keepCompleted:0,maxAttempts:3,jobTimeoutMs:5e3},this.opts.queueOptions),this.opts.workerOptions=Object.assign({concurrency:1,name:this.name},this.opts.workerOptions),this.queue=new r(this.opts.queueOptions),this.worker=new s({...this.opts.workerOptions,autoStart:this.opts.autoStart,queue:this.queue,handler:async e=>{try{return await this.handle(e,{token:e.token,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`,l=a instanceof Error?a:void 0;if(n+1>=r)return l||Error(s);let u=new c(s,o);return l&&(u.stack=l.stack,u.cause=l),u},abort:e=>new a(e||`failed`)})}catch(e){throw e instanceof Error&&Object.defineProperty(e,`__isJobError`,{value:!0,enumerable:!1}),e}},backoff:(e,t)=>{if(t instanceof c&&typeof t.customDelay==`number`)return t.customDelay;let n=this.opts.workerOptions?.backoff;if(typeof n==`function`)return n(e,t);if(n&&typeof n==`object`){let{type:t,delay:r=1e3,jitter:i=0}=n,a;if(a=t===`fixed`?r:t===`exponential`?r*2**(e-1):1e3*2**(e-1),i>0){let e=a*i,t=Math.random()*e*2-e;a+=t}return Math.max(0,Math.round(a))}return 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.isFailed(),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=>{e instanceof c||e instanceof Error&&e.__isJobError||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.groups.setConfig(e,t)}async start(e){let t;if(`children`in e&&Array.isArray(e.children)){if(!e.parent)throw Error(`Flow must include a parent job`);if(e.children.length===0)throw Error(`Flow must include at least one child job`);t=await this.queue.addFlow(e)}else t=await this.queue.add(e);return t.finished=async e=>t.waitUntilFinished(e),t}async close(){await this.worker.close()}};export{l as BaseTask,e as Job,t as PriorityStrategy,n as Queue,c as TaskRetryLaterError,i as UnrecoverableError,o as Worker};
1
+ import{Job as e,PriorityStrategy as t,Queue as n,Queue as r,UnrecoverableError as i,UnrecoverableError as a,Worker as o,Worker as s}from"groupmq-plus";import{extractErrorMessage as c}from"@libeilong/func";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,this.opts=Object.assign({logger:!1,queueOptions:{},workerOptions:{}},t),this.opts.queueOptions=Object.assign({redis:e,namespace:this.name},this.opts.queueOptions),this.opts.workerOptions=Object.assign({name:this.name},this.opts.workerOptions),this.queue=new r(this.opts.queueOptions),this.worker=new s({...this.opts.workerOptions,queue:this.queue,handler:async e=>{try{return await this.handle(e,{token:e.token,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)return c||Error(s);let u=new l(s,o);return c&&(u.stack=c.stack,u.cause=c),u},abort:e=>{let t=new a(c(e)||`failed`);return e instanceof Error&&(t.stack=e.stack,t.cause=e),t}})}catch(e){throw e instanceof Error&&Object.defineProperty(e,`__isJobError`,{value:!0,enumerable:!1}),e}},backoff:(e,t)=>{if(t instanceof l&&typeof t.customDelay==`number`)return t.customDelay;let n=this.opts.workerOptions?.backoff;if(typeof n==`function`)return n(e,t);if(n&&typeof n==`object`){let{type:t,delay:r=1e3,jitter:i=0}=n,a;if(a=t===`fixed`?r:t===`exponential`?r*2**(e-1):1e3*2**(e-1),i>0){let e=a*i,t=Math.random()*e*2-e;a+=t}return Math.max(0,Math.round(a))}return 1e3*2**(e-1)}}),this.setupEventListeners()}setupEventListeners(){this.worker.on(`completed`,e=>{this.opts.logger&&console.log(`[COMPLETED] ${this.name}-${e.id} (Group: ${e.groupId})`),this.onCompleted(e)}),this.worker.on(`failed`,async e=>{let t=e.isFailed(),n=Error(e.failedReason||`task failed`);e.stacktrace&&(n.stack=e.stacktrace),this.opts.logger&&(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=>{e instanceof l||e instanceof Error&&e.__isJobError||this.opts.logger&&console.error(`[WORKER ERROR] ${this.name}`,e)})}async onFailed(e,t){}async onFinalFailed(e,t){}async onCompleted(e){}setGroupConfig(e,t){return this.queue.groups.setConfig(e,t)}async start(e){let t;if(`children`in e&&Array.isArray(e.children)){if(!e.parent)throw Error(`Flow must include a parent job`);if(e.children.length===0)throw Error(`Flow must include at least one child job`);t=await this.queue.addFlow(e)}else t=await this.queue.add(e);return t.finished=async e=>t.waitUntilFinished(e),t}async run(){return this.worker.run()}async close(){await this.worker.close()}};export{u as BaseTask,e as Job,t as PriorityStrategy,n as Queue,l as TaskRetryLaterError,i as UnrecoverableError,o as Worker};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["connection: IORedis","Queue","Worker","options: RetryOptions","UnrecoverableError","baseDelay: number","job: Job<D>"],"sources":["../src/BaseTask.ts"],"sourcesContent":["import {\n AddOptions,\n FlowJob,\n FlowOptions,\n GroupOptions,\n Job,\n Queue,\n QueueOptions,\n UnrecoverableError,\n Worker,\n WorkerOptions,\n} from 'groupmq-plus'\nimport IORedis from 'ioredis'\n\nexport type { FlowChildResult } from 'groupmq-plus'\n\nexport interface BackoffOptions {\n /** 延迟策略:固定间隔、指数递增 */\n type: 'fixed' | 'exponential'\n /** 延迟时间为毫秒 */\n delay?: number\n /**\n * 添加到延迟中的随机抖动比例,取值范围为 0 到 1 之间的小数。\n * 抖动用于增加随机性,以防止因多个任务同时重试而导致的惊群效应。\n * 最终的延迟时间将在 `delay ± (delay * jitter)` 的范围内随机波动。\n * @defaultValue 0\n * @example 0.1 // 为延迟添加 ±10% 的随机抖动\n * @example 0.25 // 为延迟添加 ±25% 的随机抖动\n */\n jitter?: number\n}\n\nexport interface RetryOptions {\n error?: Error | unknown\n message?: string\n delay?: number\n}\n\n// [修复] 修改返回值类型为 Error,否则外层无法使用 throw opts.retryLater(...)\nexport interface HandleOptions {\n token: string\n retryLater(options?: RetryOptions): Error\n retryLater(errorOrMessage?: Error | string): Error\n retryLater(arg?: RetryOptions | Error | string): Error\n abort(message?: string): Error\n}\n\nexport interface BaseTaskOptions {\n name?: string\n autoStart?: boolean\n queueOptions?: Partial<QueueOptions>\n workerOptions?: Omit<Partial<WorkerOptions<any>>, 'backoff'> & {\n backoff?: BackoffOptions | ((attempt: number, error: unknown) => number)\n }\n debug?: boolean\n}\n\n/**\n * 任务启动选项,用于单个任务或 Flow 中的子任务\n */\nexport type TaskStartOptions<D> = {\n groupId: string\n data: D\n jobId?: string\n delay?: number\n groupConfig?: GroupOptions\n} & Omit<AddOptions<D>, 'groupId' | 'data' | 'groupConfig'>\n\n/**\n * Flow 任务选项\n */\nexport type TaskFlowOptions<D> = {\n groupId: string\n data: D\n jobId?: string\n groupConfig?: GroupOptions\n children: Array<\n {\n groupId: string\n data: D\n jobId?: string\n groupConfig?: GroupOptions\n } & Omit<FlowJob<D>, 'groupId' | 'data'>\n >\n} & Omit<FlowJob<D>, 'groupId' | 'data'>\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 this.opts = Object.assign(\n {\n debug: false,\n autoStart: true,\n queueOptions: {},\n workerOptions: {},\n },\n opts,\n )\n\n this.opts.queueOptions = Object.assign(\n {\n redis: connection,\n namespace: this.name,\n keepCompleted: 0,\n maxAttempts: 3,\n jobTimeoutMs: 5000,\n },\n this.opts.queueOptions,\n )\n\n this.opts.workerOptions = Object.assign(\n {\n concurrency: 1,\n name: this.name,\n },\n this.opts.workerOptions,\n )\n\n this.queue = new Queue<D>(this.opts.queueOptions as QueueOptions)\n\n this.worker = new Worker<D>({\n ...(this.opts.workerOptions as WorkerOptions<D>),\n autoStart: this.opts.autoStart,\n queue: this.queue,\n handler: async (job) => {\n try {\n return await this.handle(job, {\n token: job.token!,\n retryLater: (arg: RetryOptions | Error | string): Error => {\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 // [修改] 返回错误而不是抛出\n if (errorObj) return errorObj\n return 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\n retryErr.cause = errorObj\n }\n // [修改] 返回错误\n return retryErr\n },\n abort: (message?: string) => {\n // [修改] 返回错误\n return new UnrecoverableError(message || 'failed')\n },\n })\n } catch (err) {\n if (err instanceof Error) {\n Object.defineProperty(err, '__isJobError', { value: true, enumerable: false })\n }\n throw err\n }\n },\n backoff: (attempt: number, error: unknown) => {\n if (error instanceof TaskRetryLaterError && typeof error.customDelay === 'number') {\n return error.customDelay\n }\n\n const backoffConfig = this.opts.workerOptions?.backoff\n\n if (typeof backoffConfig === 'function') {\n return backoffConfig(attempt, error)\n }\n\n if (backoffConfig && typeof backoffConfig === 'object') {\n const { type, delay = 1000, jitter = 0 } = backoffConfig\n let baseDelay: number\n\n if (type === 'fixed') {\n baseDelay = delay\n } else if (type === 'exponential') {\n baseDelay = delay * Math.pow(2, attempt - 1)\n } else {\n baseDelay = 1000 * Math.pow(2, attempt - 1)\n }\n\n if (jitter > 0) {\n const jitterAmount = baseDelay * jitter\n const randomJitter = Math.random() * jitterAmount * 2 - jitterAmount\n baseDelay = baseDelay + randomJitter\n }\n\n return Math.max(0, Math.round(baseDelay))\n }\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.isFailed()\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 (err instanceof TaskRetryLaterError) return\n if (err instanceof Error && (err as any).__isJobError) return\n\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.groups.setConfig(groupId, config)\n }\n\n async start(opts: AddOptions<D> | FlowOptions<D, D>) {\n let job: Job<D>\n\n if ('children' in opts && Array.isArray(opts.children)) {\n if (!opts.parent) {\n throw new Error('Flow must include a parent job')\n }\n\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(opts)\n } else {\n job = await this.queue.add(opts as AddOptions<D>)\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n job['finished'] = async (timeoutMs?: number) => {\n return job.waitUntilFinished(timeoutMs)\n }\n\n return job as Job<D> & { finished: (timeoutMs?: number) => Promise<any> }\n }\n\n async close() {\n await this.worker.close()\n }\n}"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["connection: IORedis","Queue","Worker","options: RetryOptions","UnrecoverableError","baseDelay: number","job: Job<D>"],"sources":["../src/BaseTask.ts"],"sourcesContent":["import { extractErrorMessage } from '@libeilong/func'\nimport {\n AddOptions,\n FlowJob,\n FlowOptions,\n GroupOptions,\n Job,\n Queue,\n QueueOptions,\n UnrecoverableError,\n Worker,\n WorkerOptions,\n} from 'groupmq-plus'\nimport IORedis from 'ioredis'\n\nexport type { FlowChildResult } from 'groupmq-plus'\n\nexport interface BackoffOptions {\n /** 延迟策略:固定间隔、指数递增 */\n type: 'fixed' | 'exponential'\n /** 延迟时间为毫秒 */\n delay?: number\n /**\n * 添加到延迟中的随机抖动比例,取值范围为 0 到 1 之间的小数。\n * 抖动用于增加随机性,以防止因多个任务同时重试而导致的惊群效应。\n * 最终的延迟时间将在 `delay ± (delay * jitter)` 的范围内随机波动。\n * @defaultValue 0\n * @example 0.1 // 为延迟添加 ±10% 的随机抖动\n * @example 0.25 // 为延迟添加 ±25% 的随机抖动\n */\n jitter?: number\n}\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): Error\n retryLater(errorOrMessage?: Error | string): Error\n retryLater(arg?: RetryOptions | Error | string): Error\n abort(arg?: Error | string | number): Error\n}\n\nexport interface BaseTaskOptions {\n name?: string\n queueOptions?: Partial<QueueOptions>\n workerOptions?: Omit<Partial<WorkerOptions<any>>, 'backoff'> & {\n backoff?: BackoffOptions | ((attempt: number, error: unknown) => number)\n }\n logger?: boolean\n}\n\n/**\n * 任务启动选项,用于单个任务或 Flow 中的子任务\n */\nexport type TaskStartOptions<D> = {\n groupId: string\n data: D\n jobId?: string\n delay?: number\n groupConfig?: GroupOptions\n} & Omit<AddOptions<D>, 'groupId' | 'data' | 'groupConfig'>\n\n/**\n * Flow 任务选项\n */\nexport type TaskFlowOptions<D> = {\n groupId: string\n data: D\n jobId?: string\n groupConfig?: GroupOptions\n children: Array<\n {\n groupId: string\n data: D\n jobId?: string\n groupConfig?: GroupOptions\n } & Omit<FlowJob<D>, 'groupId' | 'data'>\n >\n} & Omit<FlowJob<D>, 'groupId' | 'data'>\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 this.opts = Object.assign(\n {\n logger: false,\n queueOptions: {},\n workerOptions: {},\n },\n opts,\n )\n\n this.opts.queueOptions = Object.assign(\n {\n redis: connection,\n namespace: this.name,\n },\n this.opts.queueOptions,\n )\n\n this.opts.workerOptions = Object.assign({ name: this.name }, this.opts.workerOptions)\n\n this.queue = new Queue<D>(this.opts.queueOptions as QueueOptions)\n\n this.worker = new Worker<D>({\n ...(this.opts.workerOptions as WorkerOptions<D>),\n queue: this.queue,\n handler: async (job) => {\n try {\n return await this.handle(job, {\n token: job.token!,\n retryLater: (arg: RetryOptions | Error | string): Error => {\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 // [修改] 返回错误而不是抛出\n if (errorObj) return errorObj\n return 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\n retryErr.cause = errorObj\n }\n // [修改] 返回错误\n return retryErr\n },\n abort: (arg?: Error | string | number) => {\n const error = new UnrecoverableError(extractErrorMessage(arg) || 'failed')\n if (arg instanceof Error) {\n error.stack = arg.stack\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n error.cause = arg\n }\n return error\n },\n })\n } catch (err) {\n if (err instanceof Error) {\n Object.defineProperty(err, '__isJobError', { value: true, enumerable: false })\n }\n throw err\n }\n },\n backoff: (attempt: number, error: unknown) => {\n if (error instanceof TaskRetryLaterError && typeof error.customDelay === 'number') {\n return error.customDelay\n }\n\n const backoffConfig = this.opts.workerOptions?.backoff\n\n if (typeof backoffConfig === 'function') {\n return backoffConfig(attempt, error)\n }\n\n if (backoffConfig && typeof backoffConfig === 'object') {\n const { type, delay = 1000, jitter = 0 } = backoffConfig\n let baseDelay: number\n\n if (type === 'fixed') {\n baseDelay = delay\n } else if (type === 'exponential') {\n baseDelay = delay * Math.pow(2, attempt - 1)\n } else {\n baseDelay = 1000 * Math.pow(2, attempt - 1)\n }\n\n if (jitter > 0) {\n const jitterAmount = baseDelay * jitter\n const randomJitter = Math.random() * jitterAmount * 2 - jitterAmount\n baseDelay = baseDelay + randomJitter\n }\n\n return Math.max(0, Math.round(baseDelay))\n }\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.logger) {\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.isFailed()\n const error = new Error(job.failedReason || 'task failed')\n if (job.stacktrace) {\n error.stack = job.stacktrace\n }\n\n if (this.opts.logger) {\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 (err instanceof TaskRetryLaterError) return\n if (err instanceof Error && (err as any).__isJobError) return\n\n if (this.opts.logger) {\n console.error(`[WORKER ERROR] ${this.name}`, err)\n }\n })\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.groups.setConfig(groupId, config)\n }\n\n async start(opts: AddOptions<D> | FlowOptions<D, D>) {\n let job: Job<D>\n\n if ('children' in opts && Array.isArray(opts.children)) {\n if (!opts.parent) {\n throw new Error('Flow must include a parent job')\n }\n\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(opts)\n } else {\n job = await this.queue.add(opts as AddOptions<D>)\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n job['finished'] = async (timeoutMs?: number) => {\n return job.waitUntilFinished(timeoutMs)\n }\n\n return job as Job<D> & { finished: (timeoutMs?: number) => Promise<any> }\n }\n\n async run() {\n return this.worker.run()\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.2.6",
4
+ "version": "0.2.7",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -20,7 +20,8 @@
20
20
  "node": ">=18"
21
21
  },
22
22
  "dependencies": {
23
- "groupmq-plus": "^1.4.2"
23
+ "groupmq-plus": "^1.4.2",
24
+ "@libeilong/func": "^0.33.1"
24
25
  },
25
26
  "peerDependencies": {
26
27
  "ioredis": "^5.8.2"