@elysiumjs/heracles 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -75
- package/dist/heracles.d.ts +13 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +2 -0
- package/dist/job.d.ts +249 -0
- package/dist/queue.d.ts +247 -0
- package/dist/transport.d.ts +427 -0
- package/dist/transports/redis.transport.d.ts +251 -0
- package/dist/utils.d.ts +34 -0
- package/dist/worker.d.ts +250 -0
- package/dist/workers/base.worker.d.ts +216 -0
- package/dist/workers/redis.worker.d.ts +57 -0
- package/package.json +13 -11
- package/tsconfig.json +34 -33
- package/USAGE.md +0 -527
- package/index.ts +0 -16
- package/src/heracles.ts +0 -68
- package/src/index.ts +0 -28
- package/src/job.ts +0 -413
- package/src/queue.ts +0 -567
- package/src/transport.ts +0 -518
- package/src/transports/redis.transport.ts +0 -1558
- package/src/transports/thread.transport.ts +0 -289
- package/src/utils.ts +0 -61
- package/src/worker.ts +0 -312
- package/src/worker_pool.ts +0 -393
- package/src/workers/base.worker.ts +0 -804
- package/src/workers/redis.worker.ts +0 -523
- package/src/workers/thread.worker.entry.ts +0 -171
- package/src/workers/thread.worker.ts +0 -525
- package/types.d.ts +0 -21
package/src/job.ts
DELETED
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import 'reflect-metadata';
|
|
16
|
-
|
|
17
|
-
import type { Class } from 'type-fest';
|
|
18
|
-
|
|
19
|
-
import { InteractsWithConsole, Service } from '@elysiumjs/core';
|
|
20
|
-
import { assign, uid } from 'radash';
|
|
21
|
-
|
|
22
|
-
import { Symbols } from './utils';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Defines how a job should behave when multiple instances with the same ID are dispatched.
|
|
26
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
27
|
-
*/
|
|
28
|
-
export enum JobOverlapBehavior {
|
|
29
|
-
/**
|
|
30
|
-
* Allow jobs with the same ID to execute concurrently.
|
|
31
|
-
*/
|
|
32
|
-
ALLOW_OVERLAP = 'allow_overlap',
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Only allow one job with a given ID to execute at a time.
|
|
36
|
-
* Additional jobs with the same ID will be queued and executed in sequence.
|
|
37
|
-
*/
|
|
38
|
-
NO_OVERLAP = 'no_overlap'
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Represents job metadata when registered.
|
|
43
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
44
|
-
*/
|
|
45
|
-
export interface JobMetadata {
|
|
46
|
-
/**
|
|
47
|
-
* The service name of the job in the container.
|
|
48
|
-
*/
|
|
49
|
-
name: string;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* The name of the queue the job is assigned to.
|
|
53
|
-
*/
|
|
54
|
-
queue: string;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Defines how the job handles overlap with other instances of the same job ID.
|
|
58
|
-
*/
|
|
59
|
-
overlapBehavior: JobOverlapBehavior;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* When using NO_OVERLAP behavior, specifies the delay in milliseconds
|
|
63
|
-
* between sequential executions of jobs with the same ID.
|
|
64
|
-
*/
|
|
65
|
-
overlapDelay?: number;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Additional job options.
|
|
69
|
-
*/
|
|
70
|
-
options?: Record<string, any>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Properties required when declaring a job using the `@Job.register()` decorator.
|
|
75
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
76
|
-
*/
|
|
77
|
-
export type JobProps = {
|
|
78
|
-
/**
|
|
79
|
-
* The name of the job.
|
|
80
|
-
*/
|
|
81
|
-
name?: string;
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* The queue on which the job should be processed.
|
|
85
|
-
*/
|
|
86
|
-
queue?: string;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* The maximum number of retries for this job type.
|
|
90
|
-
* This overrides queue-level retry settings.
|
|
91
|
-
* @default undefined - Uses queue setting
|
|
92
|
-
*/
|
|
93
|
-
maxRetries?: number;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* The delay between retries in milliseconds.
|
|
97
|
-
* This overrides queue-level retry delay settings.
|
|
98
|
-
* @default undefined - Uses queue setting
|
|
99
|
-
*/
|
|
100
|
-
retryDelay?: number;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Defines how the job handles overlap with other instances of the same job ID.
|
|
104
|
-
* @default JobOverlapBehavior.ALLOW_OVERLAP
|
|
105
|
-
*/
|
|
106
|
-
overlapBehavior?: JobOverlapBehavior;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* When using NO_OVERLAP behavior, specifies the delay in milliseconds
|
|
110
|
-
* between sequential executions of jobs with the same ID.
|
|
111
|
-
* @default 0
|
|
112
|
-
*/
|
|
113
|
-
overlapDelay?: number;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Additional job-specific configuration options.
|
|
117
|
-
*/
|
|
118
|
-
options?: Record<string, any>;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Type for any job classes.
|
|
123
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
124
|
-
*/
|
|
125
|
-
export type JobClass<T extends Job = Job, TClass extends Class<T> = Class<T>> = {
|
|
126
|
-
/**
|
|
127
|
-
* Generates the ID for instances of this Job.
|
|
128
|
-
*/
|
|
129
|
-
generateJobId(...args: ConstructorParameters<TClass>): string;
|
|
130
|
-
|
|
131
|
-
new (...args: ConstructorParameters<TClass>): T;
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Base class for background jobs.
|
|
136
|
-
*
|
|
137
|
-
* Jobs are classes that can be executed in the background. They provide
|
|
138
|
-
* an abstract execute method that serves as the entry point for the job.
|
|
139
|
-
*
|
|
140
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
141
|
-
*/
|
|
142
|
-
export abstract class Job extends InteractsWithConsole {
|
|
143
|
-
#startedAt?: Date;
|
|
144
|
-
#completedAt?: Date;
|
|
145
|
-
#status: JobStatus = JobStatus.PENDING;
|
|
146
|
-
#err?: Error;
|
|
147
|
-
#retries: number = 0;
|
|
148
|
-
#queueName?: string;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Marks a class as a job.
|
|
152
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
153
|
-
* @param options The decorator options.
|
|
154
|
-
*/
|
|
155
|
-
public static register(options: JobProps = {}) {
|
|
156
|
-
return function (target: JobClass) {
|
|
157
|
-
options = assign(
|
|
158
|
-
{
|
|
159
|
-
name: target.name,
|
|
160
|
-
queue: 'default',
|
|
161
|
-
overlapBehavior: JobOverlapBehavior.ALLOW_OVERLAP,
|
|
162
|
-
overlapDelay: 0
|
|
163
|
-
},
|
|
164
|
-
options
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
const name = `job.${options.name}`;
|
|
168
|
-
|
|
169
|
-
// Register job class as a service
|
|
170
|
-
Service.instance(name, target);
|
|
171
|
-
|
|
172
|
-
// Store metadata for queue assignment and configuration
|
|
173
|
-
const metadata: JobMetadata = {
|
|
174
|
-
name,
|
|
175
|
-
queue: options.queue!,
|
|
176
|
-
overlapBehavior: options.overlapBehavior!,
|
|
177
|
-
overlapDelay: options.overlapDelay,
|
|
178
|
-
options: {
|
|
179
|
-
maxRetries: options.maxRetries,
|
|
180
|
-
retryDelay: options.retryDelay,
|
|
181
|
-
...options.options
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
Reflect.defineMetadata(Symbols.job, metadata, target);
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Generate a random job ID.
|
|
191
|
-
* @param args Arguments passed to the constructor.
|
|
192
|
-
* @returns A random job ID.
|
|
193
|
-
*/
|
|
194
|
-
public static generateJobId(...args: any[]): string {
|
|
195
|
-
return `job_${Date.now()}_${uid(8)}`;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Unique identifier for the job.
|
|
200
|
-
*/
|
|
201
|
-
public readonly id: string;
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* The dispatch ID for this job instance.
|
|
205
|
-
* This is unique for each dispatch of the job, even if the job ID is the same.
|
|
206
|
-
*/
|
|
207
|
-
public readonly dispatchId: string;
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* The timestamp when the job was created.
|
|
211
|
-
*/
|
|
212
|
-
public readonly createdAt: Date;
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* The timestamp when the job started execution.
|
|
216
|
-
*/
|
|
217
|
-
public get startedAt(): Date | undefined {
|
|
218
|
-
return this.#startedAt;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* The timestamp when the job completed execution.
|
|
223
|
-
*/
|
|
224
|
-
public get completedAt(): Date | undefined {
|
|
225
|
-
return this.#completedAt;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* The current status of the job.
|
|
230
|
-
*/
|
|
231
|
-
public get status(): JobStatus {
|
|
232
|
-
return this.#status;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* The error that occurred during job execution, if any.
|
|
237
|
-
*/
|
|
238
|
-
public get lastError(): Error | undefined {
|
|
239
|
-
return this.#err;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* The number of times this job has been retried.
|
|
244
|
-
*/
|
|
245
|
-
public get retries(): number {
|
|
246
|
-
return this.#retries;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* The name of the queue this job is assigned to.
|
|
251
|
-
*/
|
|
252
|
-
public get queueName(): string | undefined {
|
|
253
|
-
return this.#queueName;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Set the queue for this job instance.
|
|
258
|
-
* @internal This should only be called by the queue implementation.
|
|
259
|
-
*/
|
|
260
|
-
public set queueName(value: string | undefined) {
|
|
261
|
-
this.#queueName = value;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Increment the retry count for this job.
|
|
266
|
-
* @internal This should only be called by the worker implementation.
|
|
267
|
-
*/
|
|
268
|
-
public incrementRetries(): void {
|
|
269
|
-
this.#retries++;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Creates a new job instance.
|
|
274
|
-
*
|
|
275
|
-
* @param id Optional job ID. If not provided, a random ID will be generated.
|
|
276
|
-
*/
|
|
277
|
-
constructor(id?: string, dispatchId?: string) {
|
|
278
|
-
super();
|
|
279
|
-
this.id = id ?? (this.constructor as JobClass).generateJobId();
|
|
280
|
-
this.dispatchId = dispatchId ?? `dispatch_${Date.now()}_${uid(8)}`;
|
|
281
|
-
this.createdAt = new Date();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Run the job.
|
|
286
|
-
* This method handles the job lifecycle and calls the execute method.
|
|
287
|
-
* @returns A promise that resolves when the job completes (successfully or not).
|
|
288
|
-
*/
|
|
289
|
-
public async run(): Promise<void> {
|
|
290
|
-
// Don't run cancelled jobs
|
|
291
|
-
if (this.#status === JobStatus.CANCELLED) {
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
try {
|
|
296
|
-
this.#status = JobStatus.RUNNING as JobStatus;
|
|
297
|
-
this.#startedAt = new Date();
|
|
298
|
-
|
|
299
|
-
const queueInfo = this.#queueName ? ` in queue '${this.#queueName}'` : '';
|
|
300
|
-
this.debug(`Job ${this.id}${queueInfo} started at ${this.#startedAt.toISOString()}`);
|
|
301
|
-
|
|
302
|
-
// Execute the job implementation
|
|
303
|
-
await this.execute();
|
|
304
|
-
|
|
305
|
-
if (this.#status !== JobStatus.CANCELLED) {
|
|
306
|
-
this.#status = JobStatus.COMPLETED;
|
|
307
|
-
this.#completedAt = new Date();
|
|
308
|
-
this.debug(
|
|
309
|
-
`Job ${this.id} completed successfully at ${this.#completedAt.toISOString()} after ${this.retries} retries`
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
} catch (err) {
|
|
313
|
-
this.#err = err instanceof Error ? err : new Error(String(err));
|
|
314
|
-
|
|
315
|
-
if (this.#status !== JobStatus.CANCELLED) {
|
|
316
|
-
this.#status = JobStatus.FAILED;
|
|
317
|
-
this.#completedAt = new Date();
|
|
318
|
-
this.error(
|
|
319
|
-
`Job ${this.id} failed at ${this.#completedAt.toISOString()} after ${this.retries} retries: ${this.#err.message}`
|
|
320
|
-
);
|
|
321
|
-
this.trace(this.#err);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Cancels the job.
|
|
328
|
-
* @returns True if the job was cancelled, false if it was already in a terminal state.
|
|
329
|
-
*/
|
|
330
|
-
public cancel(): boolean {
|
|
331
|
-
// Only cancel jobs that are not already in a terminal state
|
|
332
|
-
if (this.#status === JobStatus.COMPLETED || this.#status === JobStatus.FAILED) {
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const wasRunning = this.#status === JobStatus.RUNNING;
|
|
337
|
-
this.#status = JobStatus.CANCELLED;
|
|
338
|
-
|
|
339
|
-
if (wasRunning) {
|
|
340
|
-
this.#completedAt = new Date();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
this.info(`Job ${this.id} was cancelled${wasRunning ? ` while running` : ` before execution`}`);
|
|
344
|
-
return true;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* The main execution method for the job.
|
|
349
|
-
* This must be implemented by all job classes.
|
|
350
|
-
*/
|
|
351
|
-
protected abstract execute(): Promise<void>;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Wraps a Job class to assign a specific ID to all instances.
|
|
356
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
357
|
-
* @internal
|
|
358
|
-
* @param J The Job class.
|
|
359
|
-
* @param id The ID to assign to the job.
|
|
360
|
-
* @returns A Job class which creates job instances with the specified ID.
|
|
361
|
-
*/
|
|
362
|
-
export const WithId = <T extends Job, TClass extends Class<T> = Class<T>>(
|
|
363
|
-
J: TClass,
|
|
364
|
-
id: string,
|
|
365
|
-
dispatchId: string
|
|
366
|
-
): TClass => {
|
|
367
|
-
// @ts-expect-error Mixin class typing
|
|
368
|
-
return class extends J {
|
|
369
|
-
constructor(...args: ConstructorParameters<TClass>) {
|
|
370
|
-
super(...args);
|
|
371
|
-
// @ts-expect-error Readonly field
|
|
372
|
-
this.id = id;
|
|
373
|
-
// @ts-expect-error Readonly field
|
|
374
|
-
this.dispatchId = dispatchId;
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Possible statuses for a job.
|
|
381
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
382
|
-
*/
|
|
383
|
-
export enum JobStatus {
|
|
384
|
-
/**
|
|
385
|
-
* The job is in pending state; queued to run, but not started.
|
|
386
|
-
*/
|
|
387
|
-
PENDING = 'pending',
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* The job is currently running.
|
|
391
|
-
*/
|
|
392
|
-
RUNNING = 'running',
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* The job has completed successfully.
|
|
396
|
-
*/
|
|
397
|
-
COMPLETED = 'completed',
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* The job has failed to complete successfully. No retries left.
|
|
401
|
-
*/
|
|
402
|
-
FAILED = 'failed',
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* The job was cancelled and won't be retried or completed.
|
|
406
|
-
*/
|
|
407
|
-
CANCELLED = 'cancelled',
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* The job is scheduled to be retried after a failure.
|
|
411
|
-
*/
|
|
412
|
-
SCHEDULED_FOR_RETRY = 'scheduled_for_retry'
|
|
413
|
-
}
|