@boringnode/queue 0.3.0 → 0.3.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/README.md +28 -0
- package/build/{chunk-SMOKFZ46.js → chunk-3BIR4IQD.js} +34 -4
- package/build/chunk-3BIR4IQD.js.map +1 -0
- package/build/chunk-RO6VVBGK.js +1550 -0
- package/build/chunk-RO6VVBGK.js.map +1 -0
- package/build/{index-PDfE6h8d.d.ts → index-CoubP-c4.d.ts} +1 -1
- package/build/index.d.ts +28 -3
- package/build/index.js +11 -686
- package/build/index.js.map +1 -1
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/fake_adapter.d.ts +62 -0
- package/build/src/drivers/fake_adapter.js +10 -0
- package/build/src/drivers/fake_adapter.js.map +1 -0
- package/build/src/drivers/knex_adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.js +2 -4
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +1 -1
- package/build/src/drivers/redis_adapter.js +2 -4
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +1 -1
- package/build/src/drivers/sync_adapter.js +2 -2
- package/build/src/types/index.d.ts +1 -1
- package/build/src/types/main.d.ts +1 -1
- package/package.json +1 -1
- package/build/chunk-LI2ZMCNO.js +0 -371
- package/build/chunk-LI2ZMCNO.js.map +0 -1
- package/build/chunk-PBGPIFI5.js +0 -40
- package/build/chunk-PBGPIFI5.js.map +0 -1
- package/build/chunk-SMOKFZ46.js.map +0 -1
package/build/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
Job,
|
|
3
|
+
JobBatchDispatcher,
|
|
4
|
+
JobDispatcher,
|
|
5
5
|
Locator,
|
|
6
6
|
QueueManager,
|
|
7
|
+
ScheduleBuilder,
|
|
7
8
|
debug_default
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RO6VVBGK.js";
|
|
9
10
|
import {
|
|
10
11
|
DEFAULT_ERROR_RETRY_DELAY,
|
|
11
12
|
DEFAULT_IDLE_DELAY,
|
|
@@ -13,692 +14,16 @@ import {
|
|
|
13
14
|
DEFAULT_STALLED_INTERVAL,
|
|
14
15
|
DEFAULT_STALLED_THRESHOLD,
|
|
15
16
|
E_INVALID_BASE_DELAY,
|
|
16
|
-
E_INVALID_CRON_EXPRESSION,
|
|
17
17
|
E_INVALID_MAX_DELAY,
|
|
18
18
|
E_INVALID_MULTIPLIER,
|
|
19
|
-
E_INVALID_SCHEDULE_CONFIG,
|
|
20
19
|
E_JOB_MAX_ATTEMPTS_REACHED,
|
|
21
20
|
E_JOB_TIMEOUT,
|
|
22
|
-
exceptions_exports
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// src/job_dispatcher.ts
|
|
26
|
-
import { randomUUID } from "crypto";
|
|
27
|
-
var JobDispatcher = class {
|
|
28
|
-
#name;
|
|
29
|
-
#payload;
|
|
30
|
-
#queue = "default";
|
|
31
|
-
#adapter;
|
|
32
|
-
#delay;
|
|
33
|
-
#priority;
|
|
34
|
-
#groupId;
|
|
35
|
-
/**
|
|
36
|
-
* Create a new job dispatcher.
|
|
37
|
-
*
|
|
38
|
-
* @param name - The job class name (used to locate the class at runtime)
|
|
39
|
-
* @param payload - The data to pass to the job
|
|
40
|
-
*/
|
|
41
|
-
constructor(name, payload) {
|
|
42
|
-
this.#name = name;
|
|
43
|
-
this.#payload = payload;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Set the target queue for this job.
|
|
47
|
-
*
|
|
48
|
-
* @param queue - Queue name (default: 'default')
|
|
49
|
-
* @returns This dispatcher for chaining
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* ```typescript
|
|
53
|
-
* await SendEmailJob.dispatch(payload).toQueue('emails')
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
toQueue(queue) {
|
|
57
|
-
this.#queue = queue;
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Delay the job execution.
|
|
62
|
-
*
|
|
63
|
-
* The job will be stored in a delayed state and moved to pending
|
|
64
|
-
* after the delay expires.
|
|
65
|
-
*
|
|
66
|
-
* @param delay - Delay as milliseconds or duration string ('5s', '1h', '7d')
|
|
67
|
-
* @returns This dispatcher for chaining
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```typescript
|
|
71
|
-
* // Send reminder in 24 hours
|
|
72
|
-
* await ReminderJob.dispatch(payload).in('24h')
|
|
73
|
-
*
|
|
74
|
-
* // Process in 5 minutes
|
|
75
|
-
* await CleanupJob.dispatch(payload).in('5m')
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
in(delay) {
|
|
79
|
-
this.#delay = delay;
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Set the job priority.
|
|
84
|
-
*
|
|
85
|
-
* Lower numbers = higher priority. Jobs with lower priority values
|
|
86
|
-
* are processed before jobs with higher values.
|
|
87
|
-
*
|
|
88
|
-
* @param priority - Priority level (1-10, default: 5)
|
|
89
|
-
* @returns This dispatcher for chaining
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```typescript
|
|
93
|
-
* // High priority job
|
|
94
|
-
* await UrgentJob.dispatch(payload).priority(1)
|
|
95
|
-
*
|
|
96
|
-
* // Low priority job
|
|
97
|
-
* await BackgroundJob.dispatch(payload).priority(10)
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
priority(priority) {
|
|
101
|
-
this.#priority = priority;
|
|
102
|
-
return this;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Assign this job to a group.
|
|
106
|
-
*
|
|
107
|
-
* Jobs with the same groupId can be filtered and displayed together
|
|
108
|
-
* in monitoring UIs. Useful for batch operations like newsletters
|
|
109
|
-
* or bulk exports.
|
|
110
|
-
*
|
|
111
|
-
* @param groupId - Group identifier
|
|
112
|
-
* @returns This dispatcher for chaining
|
|
113
|
-
*
|
|
114
|
-
* @example
|
|
115
|
-
* ```typescript
|
|
116
|
-
* // Group newsletter jobs together
|
|
117
|
-
* await SendEmailJob.dispatch({ to: 'user@example.com' })
|
|
118
|
-
* .group('newsletter-jan-2025')
|
|
119
|
-
* .run()
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
group(groupId) {
|
|
123
|
-
this.#groupId = groupId;
|
|
124
|
-
return this;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Use a specific adapter for this job.
|
|
128
|
-
*
|
|
129
|
-
* @param adapter - Adapter name or factory function
|
|
130
|
-
* @returns This dispatcher for chaining
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* ```typescript
|
|
134
|
-
* // Use named adapter
|
|
135
|
-
* await Job.dispatch(payload).with('redis')
|
|
136
|
-
*
|
|
137
|
-
* // Use custom adapter instance
|
|
138
|
-
* await Job.dispatch(payload).with(() => new CustomAdapter())
|
|
139
|
-
* ```
|
|
140
|
-
*/
|
|
141
|
-
with(adapter) {
|
|
142
|
-
this.#adapter = adapter;
|
|
143
|
-
return this;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Dispatch the job to the queue.
|
|
147
|
-
*
|
|
148
|
-
* @returns A DispatchResult containing the jobId
|
|
149
|
-
*
|
|
150
|
-
* @example
|
|
151
|
-
* ```typescript
|
|
152
|
-
* const { jobId } = await SendEmailJob.dispatch(payload).run()
|
|
153
|
-
* console.log(`Dispatched job: ${jobId}`)
|
|
154
|
-
* ```
|
|
155
|
-
*/
|
|
156
|
-
async run() {
|
|
157
|
-
const id = randomUUID();
|
|
158
|
-
debug_default("dispatching job %s with id %s using payload %s", this.#name, id, this.#payload);
|
|
159
|
-
const adapter = this.#getAdapterInstance();
|
|
160
|
-
const payload = {
|
|
161
|
-
id,
|
|
162
|
-
name: this.#name,
|
|
163
|
-
payload: this.#payload,
|
|
164
|
-
attempts: 0,
|
|
165
|
-
priority: this.#priority,
|
|
166
|
-
groupId: this.#groupId
|
|
167
|
-
};
|
|
168
|
-
if (this.#delay) {
|
|
169
|
-
const parsedDelay = parse(this.#delay);
|
|
170
|
-
await adapter.pushLaterOn(this.#queue, payload, parsedDelay);
|
|
171
|
-
} else {
|
|
172
|
-
await adapter.pushOn(this.#queue, payload);
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
jobId: id
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Thenable implementation for auto-dispatch when awaited.
|
|
180
|
-
*
|
|
181
|
-
* Allows `await Job.dispatch(payload)` without explicit `.run()`.
|
|
182
|
-
*
|
|
183
|
-
* @param onFulfilled - Success callback
|
|
184
|
-
* @param onRejected - Error callback
|
|
185
|
-
* @returns Promise resolving to the DispatchResult
|
|
186
|
-
*/
|
|
187
|
-
then(onFulfilled, onRejected) {
|
|
188
|
-
return this.run().then(onFulfilled, onRejected);
|
|
189
|
-
}
|
|
190
|
-
#getAdapterInstance() {
|
|
191
|
-
if (!this.#adapter) {
|
|
192
|
-
return QueueManager.use();
|
|
193
|
-
}
|
|
194
|
-
if (typeof this.#adapter === "string") {
|
|
195
|
-
return QueueManager.use(this.#adapter);
|
|
196
|
-
}
|
|
197
|
-
return this.#adapter();
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
// src/job_batch_dispatcher.ts
|
|
202
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
203
|
-
var JobBatchDispatcher = class {
|
|
204
|
-
#name;
|
|
205
|
-
#payloads;
|
|
206
|
-
#queue = "default";
|
|
207
|
-
#adapter;
|
|
208
|
-
#priority;
|
|
209
|
-
#groupId;
|
|
210
|
-
/**
|
|
211
|
-
* Create a new batch job dispatcher.
|
|
212
|
-
*
|
|
213
|
-
* @param name - The job class name (used to locate the class at runtime)
|
|
214
|
-
* @param payloads - Array of data to pass to each job
|
|
215
|
-
*/
|
|
216
|
-
constructor(name, payloads) {
|
|
217
|
-
this.#name = name;
|
|
218
|
-
this.#payloads = payloads;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Set the target queue for all jobs.
|
|
222
|
-
*
|
|
223
|
-
* @param queue - Queue name (default: 'default')
|
|
224
|
-
* @returns This dispatcher for chaining
|
|
225
|
-
*
|
|
226
|
-
* @example
|
|
227
|
-
* ```typescript
|
|
228
|
-
* await SendEmailJob.dispatchMany(payloads).toQueue('emails')
|
|
229
|
-
* ```
|
|
230
|
-
*/
|
|
231
|
-
toQueue(queue) {
|
|
232
|
-
this.#queue = queue;
|
|
233
|
-
return this;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Set the priority for all jobs.
|
|
237
|
-
*
|
|
238
|
-
* Lower numbers = higher priority. Jobs with lower priority values
|
|
239
|
-
* are processed before jobs with higher values.
|
|
240
|
-
*
|
|
241
|
-
* @param priority - Priority level (1-10, default: 5)
|
|
242
|
-
* @returns This dispatcher for chaining
|
|
243
|
-
*
|
|
244
|
-
* @example
|
|
245
|
-
* ```typescript
|
|
246
|
-
* await UrgentJob.dispatchMany(payloads).priority(1)
|
|
247
|
-
* ```
|
|
248
|
-
*/
|
|
249
|
-
priority(priority) {
|
|
250
|
-
this.#priority = priority;
|
|
251
|
-
return this;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Assign all jobs to a group.
|
|
255
|
-
*
|
|
256
|
-
* Jobs with the same groupId can be filtered and displayed together
|
|
257
|
-
* in monitoring UIs. Useful for batch operations like newsletters
|
|
258
|
-
* or bulk exports.
|
|
259
|
-
*
|
|
260
|
-
* @param groupId - Group identifier
|
|
261
|
-
* @returns This dispatcher for chaining
|
|
262
|
-
*
|
|
263
|
-
* @example
|
|
264
|
-
* ```typescript
|
|
265
|
-
* await SendEmailJob.dispatchMany(recipients)
|
|
266
|
-
* .group('newsletter-jan-2025')
|
|
267
|
-
* .run()
|
|
268
|
-
* ```
|
|
269
|
-
*/
|
|
270
|
-
group(groupId) {
|
|
271
|
-
this.#groupId = groupId;
|
|
272
|
-
return this;
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Use a specific adapter for these jobs.
|
|
276
|
-
*
|
|
277
|
-
* @param adapter - Adapter name or factory function
|
|
278
|
-
* @returns This dispatcher for chaining
|
|
279
|
-
*
|
|
280
|
-
* @example
|
|
281
|
-
* ```typescript
|
|
282
|
-
* await Job.dispatchMany(payloads).with('redis')
|
|
283
|
-
* ```
|
|
284
|
-
*/
|
|
285
|
-
with(adapter) {
|
|
286
|
-
this.#adapter = adapter;
|
|
287
|
-
return this;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Dispatch all jobs to the queue.
|
|
291
|
-
*
|
|
292
|
-
* @returns A DispatchManyResult containing all jobIds
|
|
293
|
-
*
|
|
294
|
-
* @example
|
|
295
|
-
* ```typescript
|
|
296
|
-
* const { jobIds } = await SendEmailJob.dispatchMany(payloads).run()
|
|
297
|
-
* console.log(`Dispatched ${jobIds.length} jobs`)
|
|
298
|
-
* ```
|
|
299
|
-
*/
|
|
300
|
-
async run() {
|
|
301
|
-
debug_default("dispatching %d jobs of type %s", this.#payloads.length, this.#name);
|
|
302
|
-
const adapter = this.#getAdapterInstance();
|
|
303
|
-
const jobs = this.#payloads.map((payload) => ({
|
|
304
|
-
id: randomUUID2(),
|
|
305
|
-
name: this.#name,
|
|
306
|
-
payload,
|
|
307
|
-
attempts: 0,
|
|
308
|
-
priority: this.#priority,
|
|
309
|
-
groupId: this.#groupId
|
|
310
|
-
}));
|
|
311
|
-
await adapter.pushManyOn(this.#queue, jobs);
|
|
312
|
-
return {
|
|
313
|
-
jobIds: jobs.map((job) => job.id)
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Thenable implementation for auto-dispatch when awaited.
|
|
318
|
-
*
|
|
319
|
-
* Allows `await Job.dispatchMany(payloads)` without explicit `.run()`.
|
|
320
|
-
*
|
|
321
|
-
* @param onFulfilled - Success callback
|
|
322
|
-
* @param onRejected - Error callback
|
|
323
|
-
* @returns Promise resolving to the DispatchManyResult
|
|
324
|
-
*/
|
|
325
|
-
then(onFulfilled, onRejected) {
|
|
326
|
-
return this.run().then(onFulfilled, onRejected);
|
|
327
|
-
}
|
|
328
|
-
#getAdapterInstance() {
|
|
329
|
-
if (!this.#adapter) {
|
|
330
|
-
return QueueManager.use();
|
|
331
|
-
}
|
|
332
|
-
if (typeof this.#adapter === "string") {
|
|
333
|
-
return QueueManager.use(this.#adapter);
|
|
334
|
-
}
|
|
335
|
-
return this.#adapter();
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
// src/schedule_builder.ts
|
|
340
|
-
import { CronExpressionParser } from "cron-parser";
|
|
341
|
-
var ScheduleBuilder = class {
|
|
342
|
-
#name;
|
|
343
|
-
#payload;
|
|
344
|
-
#id;
|
|
345
|
-
#cronExpression;
|
|
346
|
-
#everyMs;
|
|
347
|
-
#timezone = "UTC";
|
|
348
|
-
#from;
|
|
349
|
-
#to;
|
|
350
|
-
#limit;
|
|
351
|
-
constructor(name, payload) {
|
|
352
|
-
this.#name = name;
|
|
353
|
-
this.#payload = payload;
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Set a custom schedule ID.
|
|
357
|
-
* If not specified, defaults to the job name.
|
|
358
|
-
* If a schedule with this ID exists, it will be updated (upsert).
|
|
359
|
-
*/
|
|
360
|
-
id(scheduleId) {
|
|
361
|
-
this.#id = scheduleId;
|
|
362
|
-
return this;
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Set a cron expression for the schedule.
|
|
366
|
-
* Mutually exclusive with `every()`.
|
|
367
|
-
*/
|
|
368
|
-
cron(expression) {
|
|
369
|
-
this.#cronExpression = expression;
|
|
370
|
-
return this;
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Set a repeating interval for the schedule.
|
|
374
|
-
* Mutually exclusive with `cron()`.
|
|
375
|
-
*/
|
|
376
|
-
every(interval) {
|
|
377
|
-
this.#everyMs = parse(interval);
|
|
378
|
-
return this;
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Set the timezone for cron evaluation.
|
|
382
|
-
* @default 'UTC'
|
|
383
|
-
*/
|
|
384
|
-
timezone(tz) {
|
|
385
|
-
this.#timezone = tz;
|
|
386
|
-
return this;
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Set the start boundary for the schedule.
|
|
390
|
-
* No jobs will be dispatched before this date.
|
|
391
|
-
*/
|
|
392
|
-
from(date) {
|
|
393
|
-
this.#from = date;
|
|
394
|
-
return this;
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Set the end boundary for the schedule.
|
|
398
|
-
* No jobs will be dispatched after this date.
|
|
399
|
-
*/
|
|
400
|
-
to(date) {
|
|
401
|
-
this.#to = date;
|
|
402
|
-
return this;
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Set both start and end boundaries for the schedule.
|
|
406
|
-
* Shorthand for `.from(start).to(end)`.
|
|
407
|
-
*/
|
|
408
|
-
between(from, to) {
|
|
409
|
-
return this.from(from).to(to);
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Set the maximum number of runs for this schedule.
|
|
413
|
-
*/
|
|
414
|
-
limit(maxRuns) {
|
|
415
|
-
this.#limit = maxRuns;
|
|
416
|
-
return this;
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* Create the schedule and return the schedule ID.
|
|
420
|
-
*/
|
|
421
|
-
async run() {
|
|
422
|
-
if (!this.#cronExpression && !this.#everyMs) {
|
|
423
|
-
throw new E_INVALID_SCHEDULE_CONFIG([
|
|
424
|
-
"Schedule must have either a cron expression or an interval"
|
|
425
|
-
]);
|
|
426
|
-
}
|
|
427
|
-
if (this.#cronExpression && this.#everyMs) {
|
|
428
|
-
throw new E_INVALID_SCHEDULE_CONFIG([
|
|
429
|
-
"Schedule cannot have both a cron expression and an interval"
|
|
430
|
-
]);
|
|
431
|
-
}
|
|
432
|
-
if (this.#cronExpression) {
|
|
433
|
-
try {
|
|
434
|
-
CronExpressionParser.parse(this.#cronExpression, { tz: this.#timezone });
|
|
435
|
-
} catch (error) {
|
|
436
|
-
throw new E_INVALID_CRON_EXPRESSION([this.#cronExpression, error.message]);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
const config = {
|
|
440
|
-
id: this.#id ?? this.#name,
|
|
441
|
-
name: this.#name,
|
|
442
|
-
payload: this.#payload,
|
|
443
|
-
cronExpression: this.#cronExpression,
|
|
444
|
-
everyMs: this.#everyMs,
|
|
445
|
-
timezone: this.#timezone,
|
|
446
|
-
from: this.#from,
|
|
447
|
-
to: this.#to,
|
|
448
|
-
limit: this.#limit
|
|
449
|
-
};
|
|
450
|
-
const adapter = QueueManager.use();
|
|
451
|
-
const scheduleId = await adapter.createSchedule(config);
|
|
452
|
-
const nextRunAt = this.#calculateNextRunAt();
|
|
453
|
-
await adapter.updateSchedule(scheduleId, { nextRunAt });
|
|
454
|
-
return { scheduleId };
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Calculate the next run time based on cron or interval.
|
|
458
|
-
*/
|
|
459
|
-
#calculateNextRunAt() {
|
|
460
|
-
const now = /* @__PURE__ */ new Date();
|
|
461
|
-
let nextRun;
|
|
462
|
-
if (this.#cronExpression) {
|
|
463
|
-
const cron = CronExpressionParser.parse(this.#cronExpression, {
|
|
464
|
-
currentDate: now,
|
|
465
|
-
tz: this.#timezone
|
|
466
|
-
});
|
|
467
|
-
nextRun = cron.next().toDate();
|
|
468
|
-
} else {
|
|
469
|
-
nextRun = new Date(now.getTime() + this.#everyMs);
|
|
470
|
-
}
|
|
471
|
-
if (this.#from && nextRun < this.#from) {
|
|
472
|
-
if (this.#cronExpression) {
|
|
473
|
-
const cron = CronExpressionParser.parse(this.#cronExpression, {
|
|
474
|
-
currentDate: this.#from,
|
|
475
|
-
tz: this.#timezone
|
|
476
|
-
});
|
|
477
|
-
nextRun = cron.next().toDate();
|
|
478
|
-
} else {
|
|
479
|
-
nextRun = this.#from;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
return nextRun;
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Implement PromiseLike to allow `await builder.every('5m')` syntax.
|
|
486
|
-
*/
|
|
487
|
-
then(onfulfilled, onrejected) {
|
|
488
|
-
return this.run().then(onfulfilled, onrejected);
|
|
489
|
-
}
|
|
490
|
-
};
|
|
491
|
-
|
|
492
|
-
// src/job.ts
|
|
493
|
-
var Job = class {
|
|
494
|
-
#payload;
|
|
495
|
-
#context;
|
|
496
|
-
#signal;
|
|
497
|
-
/**
|
|
498
|
-
* Static options for this job class.
|
|
499
|
-
*
|
|
500
|
-
* Override this property in subclasses to configure job behavior
|
|
501
|
-
* such as queue name, retry policy, timeout, and more.
|
|
502
|
-
*
|
|
503
|
-
* @example
|
|
504
|
-
* ```typescript
|
|
505
|
-
* class SendEmailJob extends Job<SendEmailPayload> {
|
|
506
|
-
* static options = {
|
|
507
|
-
* queue: 'emails',
|
|
508
|
-
* maxRetries: 3,
|
|
509
|
-
* timeout: '30s',
|
|
510
|
-
* }
|
|
511
|
-
* }
|
|
512
|
-
* ```
|
|
513
|
-
*/
|
|
514
|
-
static options = {};
|
|
515
|
-
/**
|
|
516
|
-
* The payload data passed to this job instance.
|
|
517
|
-
*
|
|
518
|
-
* Contains the data provided when the job was dispatched.
|
|
519
|
-
* Available after the job has been hydrated by the worker.
|
|
520
|
-
*
|
|
521
|
-
* @example
|
|
522
|
-
* ```typescript
|
|
523
|
-
* async execute() {
|
|
524
|
-
* const { to, subject, body } = this.payload
|
|
525
|
-
* await sendEmail(to, subject, body)
|
|
526
|
-
* }
|
|
527
|
-
* ```
|
|
528
|
-
*/
|
|
529
|
-
get payload() {
|
|
530
|
-
return this.#payload;
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* Context information for the current job execution.
|
|
534
|
-
*
|
|
535
|
-
* Provides metadata such as job ID, current attempt number,
|
|
536
|
-
* queue name, priority, and timing information.
|
|
537
|
-
*
|
|
538
|
-
* @example
|
|
539
|
-
* ```typescript
|
|
540
|
-
* async execute() {
|
|
541
|
-
* if (this.context.attempt > 1) {
|
|
542
|
-
* console.log(`Retry attempt ${this.context.attempt}`)
|
|
543
|
-
* }
|
|
544
|
-
* console.log(`Processing job ${this.context.jobId} on queue ${this.context.queue}`)
|
|
545
|
-
* }
|
|
546
|
-
* ```
|
|
547
|
-
*/
|
|
548
|
-
get context() {
|
|
549
|
-
return this.#context;
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* The abort signal for timeout handling.
|
|
553
|
-
*
|
|
554
|
-
* Check `signal.aborted` in long-running operations to handle timeouts gracefully.
|
|
555
|
-
*
|
|
556
|
-
* @example
|
|
557
|
-
* ```typescript
|
|
558
|
-
* async execute() {
|
|
559
|
-
* for (const item of this.payload.items) {
|
|
560
|
-
* if (this.signal?.aborted) {
|
|
561
|
-
* throw new Error('Job timed out')
|
|
562
|
-
* }
|
|
563
|
-
* await processItem(item)
|
|
564
|
-
* }
|
|
565
|
-
* }
|
|
566
|
-
* ```
|
|
567
|
-
*/
|
|
568
|
-
get signal() {
|
|
569
|
-
return this.#signal;
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Hydrate the job with payload, context, and optional abort signal.
|
|
573
|
-
*
|
|
574
|
-
* This method is called by the worker after instantiation to provide
|
|
575
|
-
* the job's runtime data. It should not be called directly by user code.
|
|
576
|
-
*
|
|
577
|
-
* @param payload - The data to be processed by this job
|
|
578
|
-
* @param context - The job execution context
|
|
579
|
-
* @param signal - Optional abort signal for timeout handling
|
|
580
|
-
*
|
|
581
|
-
* @internal
|
|
582
|
-
*/
|
|
583
|
-
$hydrate(payload, context, signal) {
|
|
584
|
-
this.#payload = payload;
|
|
585
|
-
this.#context = Object.freeze(context);
|
|
586
|
-
this.#signal = signal;
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Dispatch this job to the queue.
|
|
590
|
-
*
|
|
591
|
-
* Returns a JobDispatcher for fluent configuration before dispatching.
|
|
592
|
-
* The job is not actually dispatched until `.run()` is called or the
|
|
593
|
-
* dispatcher is awaited.
|
|
594
|
-
*
|
|
595
|
-
* @param payload - The data to pass to the job
|
|
596
|
-
* @returns A JobDispatcher for fluent configuration
|
|
597
|
-
*
|
|
598
|
-
* @example
|
|
599
|
-
* ```typescript
|
|
600
|
-
* // Simple dispatch
|
|
601
|
-
* await SendEmailJob.dispatch({ to: 'user@example.com', subject: 'Hello' })
|
|
602
|
-
*
|
|
603
|
-
* // With options
|
|
604
|
-
* await SendEmailJob.dispatch({ to: 'user@example.com' })
|
|
605
|
-
* .toQueue('high-priority')
|
|
606
|
-
* .priority(1)
|
|
607
|
-
* .in('5m')
|
|
608
|
-
* .run()
|
|
609
|
-
* ```
|
|
610
|
-
*/
|
|
611
|
-
static dispatch(payload) {
|
|
612
|
-
const options = this.options || {};
|
|
613
|
-
const jobName = options.name || this.name;
|
|
614
|
-
const dispatcher = new JobDispatcher(jobName, payload);
|
|
615
|
-
if (options.queue) {
|
|
616
|
-
dispatcher.toQueue(options.queue);
|
|
617
|
-
}
|
|
618
|
-
if (options.adapter) {
|
|
619
|
-
dispatcher.with(options.adapter);
|
|
620
|
-
}
|
|
621
|
-
if (options.priority !== void 0) {
|
|
622
|
-
dispatcher.priority(options.priority);
|
|
623
|
-
}
|
|
624
|
-
return dispatcher;
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Dispatch multiple jobs to the queue in a single batch.
|
|
628
|
-
*
|
|
629
|
-
* Returns a JobBatchDispatcher for fluent configuration before dispatching.
|
|
630
|
-
* The jobs are not actually dispatched until `.run()` is called or the
|
|
631
|
-
* dispatcher is awaited.
|
|
632
|
-
*
|
|
633
|
-
* This is more efficient than calling `dispatch()` multiple times as it
|
|
634
|
-
* uses batched operations (e.g., Redis pipeline, SQL batch insert).
|
|
635
|
-
*
|
|
636
|
-
* @param payloads - Array of data to pass to each job
|
|
637
|
-
* @returns A JobBatchDispatcher for fluent configuration
|
|
638
|
-
*
|
|
639
|
-
* @example
|
|
640
|
-
* ```typescript
|
|
641
|
-
* // Batch dispatch for newsletter
|
|
642
|
-
* const { jobIds } = await SendEmailJob.dispatchMany([
|
|
643
|
-
* { to: 'user1@example.com', subject: 'Newsletter' },
|
|
644
|
-
* { to: 'user2@example.com', subject: 'Newsletter' },
|
|
645
|
-
* ])
|
|
646
|
-
* .group('newsletter-jan-2025')
|
|
647
|
-
* .toQueue('emails')
|
|
648
|
-
* .run()
|
|
649
|
-
*
|
|
650
|
-
* console.log(`Dispatched ${jobIds.length} jobs`)
|
|
651
|
-
* ```
|
|
652
|
-
*/
|
|
653
|
-
static dispatchMany(payloads) {
|
|
654
|
-
const options = this.options || {};
|
|
655
|
-
const jobName = options.name || this.name;
|
|
656
|
-
const dispatcher = new JobBatchDispatcher(jobName, payloads);
|
|
657
|
-
if (options.queue) {
|
|
658
|
-
dispatcher.toQueue(options.queue);
|
|
659
|
-
}
|
|
660
|
-
if (options.adapter) {
|
|
661
|
-
dispatcher.with(options.adapter);
|
|
662
|
-
}
|
|
663
|
-
if (options.priority !== void 0) {
|
|
664
|
-
dispatcher.priority(options.priority);
|
|
665
|
-
}
|
|
666
|
-
return dispatcher;
|
|
667
|
-
}
|
|
668
|
-
/**
|
|
669
|
-
* Create a schedule for this job.
|
|
670
|
-
*
|
|
671
|
-
* Returns a ScheduleBuilder for fluent configuration before creating the schedule.
|
|
672
|
-
* The schedule is not actually created until `.run()` is called or the
|
|
673
|
-
* builder is awaited.
|
|
674
|
-
*
|
|
675
|
-
* @param payload - The data to pass to the job on each run
|
|
676
|
-
* @returns A ScheduleBuilder for fluent configuration
|
|
677
|
-
*
|
|
678
|
-
* @example
|
|
679
|
-
* ```typescript
|
|
680
|
-
* // Cron schedule
|
|
681
|
-
* await CleanupJob.schedule({ days: 30 })
|
|
682
|
-
* .id('cleanup-daily')
|
|
683
|
-
* .cron('0 0 * * *')
|
|
684
|
-
* .timezone('Europe/Paris')
|
|
685
|
-
* .run()
|
|
686
|
-
*
|
|
687
|
-
* // Interval schedule
|
|
688
|
-
* await SyncJob.schedule({ source: 'api' })
|
|
689
|
-
* .every('5m')
|
|
690
|
-
* .run()
|
|
691
|
-
* ```
|
|
692
|
-
*/
|
|
693
|
-
static schedule(payload) {
|
|
694
|
-
const options = this.options || {};
|
|
695
|
-
const jobName = options.name || this.name;
|
|
696
|
-
return new ScheduleBuilder(jobName, payload);
|
|
697
|
-
}
|
|
698
|
-
};
|
|
21
|
+
exceptions_exports,
|
|
22
|
+
parse
|
|
23
|
+
} from "./chunk-3BIR4IQD.js";
|
|
699
24
|
|
|
700
25
|
// src/worker.ts
|
|
701
|
-
import { randomUUID
|
|
26
|
+
import { randomUUID } from "crypto";
|
|
702
27
|
import { setTimeout } from "timers/promises";
|
|
703
28
|
|
|
704
29
|
// src/job_pool.ts
|
|
@@ -804,7 +129,7 @@ var Worker = class {
|
|
|
804
129
|
*/
|
|
805
130
|
constructor(config) {
|
|
806
131
|
this.#config = config;
|
|
807
|
-
this.#id =
|
|
132
|
+
this.#id = randomUUID();
|
|
808
133
|
this.#idleDelay = parse(config.worker?.idleDelay ?? DEFAULT_IDLE_DELAY);
|
|
809
134
|
this.#stalledInterval = parse(config.worker?.stalledInterval ?? DEFAULT_STALLED_INTERVAL);
|
|
810
135
|
this.#stalledThreshold = parse(config.worker?.stalledThreshold ?? DEFAULT_STALLED_THRESHOLD);
|
|
@@ -1152,7 +477,7 @@ var Worker = class {
|
|
|
1152
477
|
const JobClass = Locator.get(schedule.name);
|
|
1153
478
|
const queue = JobClass?.options?.queue ?? "default";
|
|
1154
479
|
await this.#adapter.pushOn(queue, {
|
|
1155
|
-
id:
|
|
480
|
+
id: randomUUID(),
|
|
1156
481
|
name: schedule.name,
|
|
1157
482
|
payload: schedule.payload,
|
|
1158
483
|
attempts: 0,
|