@nicnocquee/dataqueue 1.22.0 → 1.25.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 +44 -0
- package/dist/index.cjs +2822 -583
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +589 -12
- package/dist/index.d.ts +589 -12
- package/dist/index.js +2818 -584
- package/dist/index.js.map +1 -1
- package/migrations/1751131910825_add_timeout_seconds_to_job_queue.sql +2 -2
- package/migrations/1751186053000_add_job_events_table.sql +12 -8
- package/migrations/1751984773000_add_tags_to_job_queue.sql +1 -1
- package/migrations/1765809419000_add_force_kill_on_timeout_to_job_queue.sql +6 -0
- package/migrations/1771100000000_add_idempotency_key_to_job_queue.sql +7 -0
- package/migrations/1781200000000_add_wait_support.sql +12 -0
- package/migrations/1781200000001_create_waitpoints_table.sql +18 -0
- package/migrations/1781200000002_add_performance_indexes.sql +34 -0
- package/migrations/1781200000003_add_progress_to_job_queue.sql +7 -0
- package/package.json +20 -6
- package/src/backend.ts +163 -0
- package/src/backends/postgres.ts +1111 -0
- package/src/backends/redis-scripts.ts +533 -0
- package/src/backends/redis.test.ts +543 -0
- package/src/backends/redis.ts +834 -0
- package/src/db-util.ts +4 -2
- package/src/handler-validation.test.ts +414 -0
- package/src/handler-validation.ts +168 -0
- package/src/index.test.ts +230 -1
- package/src/index.ts +128 -32
- package/src/processor.test.ts +612 -16
- package/src/processor.ts +759 -47
- package/src/queue.test.ts +736 -3
- package/src/queue.ts +346 -660
- package/src/test-util.ts +32 -0
- package/src/types.ts +451 -16
- package/src/wait.test.ts +698 -0
package/src/index.test.ts
CHANGED
|
@@ -81,7 +81,11 @@ describe('index integration', () => {
|
|
|
81
81
|
await new Promise((r) => setTimeout(r, 300));
|
|
82
82
|
processor.stop();
|
|
83
83
|
const job = await jobQueue.getJob(jobId);
|
|
84
|
-
expect(handler).toHaveBeenCalledWith(
|
|
84
|
+
expect(handler).toHaveBeenCalledWith(
|
|
85
|
+
{ foo: 'bar' },
|
|
86
|
+
expect.any(Object),
|
|
87
|
+
expect.any(Object),
|
|
88
|
+
);
|
|
85
89
|
expect(job?.status).toBe('completed');
|
|
86
90
|
});
|
|
87
91
|
|
|
@@ -228,6 +232,87 @@ describe('index integration', () => {
|
|
|
228
232
|
expect(job2?.status).toBe('cancelled');
|
|
229
233
|
});
|
|
230
234
|
|
|
235
|
+
it('should edit all pending jobs via JobQueue API', async () => {
|
|
236
|
+
// Add three pending jobs
|
|
237
|
+
const jobId1 = await jobQueue.addJob({
|
|
238
|
+
jobType: 'email',
|
|
239
|
+
payload: { to: 'batch1@example.com' },
|
|
240
|
+
priority: 0,
|
|
241
|
+
});
|
|
242
|
+
const jobId2 = await jobQueue.addJob({
|
|
243
|
+
jobType: 'email',
|
|
244
|
+
payload: { to: 'batch2@example.com' },
|
|
245
|
+
priority: 0,
|
|
246
|
+
});
|
|
247
|
+
const jobId3 = await jobQueue.addJob({
|
|
248
|
+
jobType: 'email',
|
|
249
|
+
payload: { to: 'batch3@example.com' },
|
|
250
|
+
priority: 0,
|
|
251
|
+
});
|
|
252
|
+
// Add a completed job
|
|
253
|
+
const jobId4 = await jobQueue.addJob({
|
|
254
|
+
jobType: 'email',
|
|
255
|
+
payload: { to: 'done@example.com' },
|
|
256
|
+
priority: 0,
|
|
257
|
+
});
|
|
258
|
+
await pool.query(
|
|
259
|
+
`UPDATE job_queue SET status = 'completed' WHERE id = $1`,
|
|
260
|
+
[jobId4],
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
// Edit all pending jobs
|
|
264
|
+
const editedCount = await jobQueue.editAllPendingJobs(undefined, {
|
|
265
|
+
priority: 10,
|
|
266
|
+
});
|
|
267
|
+
expect(editedCount).toBeGreaterThanOrEqual(3);
|
|
268
|
+
|
|
269
|
+
// Check that all pending jobs are updated
|
|
270
|
+
const job1 = await jobQueue.getJob(jobId1);
|
|
271
|
+
const job2 = await jobQueue.getJob(jobId2);
|
|
272
|
+
const job3 = await jobQueue.getJob(jobId3);
|
|
273
|
+
expect(job1?.priority).toBe(10);
|
|
274
|
+
expect(job2?.priority).toBe(10);
|
|
275
|
+
expect(job3?.priority).toBe(10);
|
|
276
|
+
|
|
277
|
+
// Completed job should remain unchanged
|
|
278
|
+
const completedJob = await jobQueue.getJob(jobId4);
|
|
279
|
+
expect(completedJob?.priority).toBe(0);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should edit pending jobs with filters via JobQueue API', async () => {
|
|
283
|
+
const emailJobId1 = await jobQueue.addJob({
|
|
284
|
+
jobType: 'email',
|
|
285
|
+
payload: { to: 'email1@example.com' },
|
|
286
|
+
priority: 0,
|
|
287
|
+
});
|
|
288
|
+
const emailJobId2 = await jobQueue.addJob({
|
|
289
|
+
jobType: 'email',
|
|
290
|
+
payload: { to: 'email2@example.com' },
|
|
291
|
+
priority: 0,
|
|
292
|
+
});
|
|
293
|
+
const smsJobId = await jobQueue.addJob({
|
|
294
|
+
jobType: 'sms',
|
|
295
|
+
payload: { to: 'sms@example.com' },
|
|
296
|
+
priority: 0,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// Edit only email jobs
|
|
300
|
+
const editedCount = await jobQueue.editAllPendingJobs(
|
|
301
|
+
{ jobType: 'email' },
|
|
302
|
+
{
|
|
303
|
+
priority: 5,
|
|
304
|
+
},
|
|
305
|
+
);
|
|
306
|
+
expect(editedCount).toBeGreaterThanOrEqual(2);
|
|
307
|
+
|
|
308
|
+
const emailJob1 = await jobQueue.getJob(emailJobId1);
|
|
309
|
+
const emailJob2 = await jobQueue.getJob(emailJobId2);
|
|
310
|
+
const smsJob = await jobQueue.getJob(smsJobId);
|
|
311
|
+
expect(emailJob1?.priority).toBe(5);
|
|
312
|
+
expect(emailJob2?.priority).toBe(5);
|
|
313
|
+
expect(smsJob?.priority).toBe(0);
|
|
314
|
+
});
|
|
315
|
+
|
|
231
316
|
it('should cancel all upcoming jobs by runAt', async () => {
|
|
232
317
|
const runAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour in future
|
|
233
318
|
const jobId1 = await jobQueue.addJob({
|
|
@@ -302,4 +387,148 @@ describe('index integration', () => {
|
|
|
302
387
|
expect(job1?.status).toBe('cancelled');
|
|
303
388
|
expect(job2?.status).toBe('pending');
|
|
304
389
|
});
|
|
390
|
+
|
|
391
|
+
it('should edit a pending job via JobQueue API', async () => {
|
|
392
|
+
const jobId = await jobQueue.addJob({
|
|
393
|
+
jobType: 'email',
|
|
394
|
+
payload: { to: 'original@example.com' },
|
|
395
|
+
priority: 0,
|
|
396
|
+
maxAttempts: 3,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
await jobQueue.editJob(jobId, {
|
|
400
|
+
payload: { to: 'updated@example.com' },
|
|
401
|
+
priority: 10,
|
|
402
|
+
maxAttempts: 5,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const job = await jobQueue.getJob(jobId);
|
|
406
|
+
expect(job?.payload).toEqual({ to: 'updated@example.com' });
|
|
407
|
+
expect(job?.priority).toBe(10);
|
|
408
|
+
expect(job?.maxAttempts).toBe(5);
|
|
409
|
+
expect(job?.status).toBe('pending');
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should edit a job and then process it correctly', async () => {
|
|
413
|
+
const handler = vi.fn(async (payload: { foo: string }, _signal) => {
|
|
414
|
+
expect(payload.foo).toBe('updated@example.com');
|
|
415
|
+
});
|
|
416
|
+
const jobId = await jobQueue.addJob({
|
|
417
|
+
jobType: 'test',
|
|
418
|
+
payload: { foo: 'original@example.com' },
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Edit the job before processing
|
|
422
|
+
await jobQueue.editJob(jobId, {
|
|
423
|
+
payload: { foo: 'updated@example.com' },
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const processor = jobQueue.createProcessor(
|
|
427
|
+
{
|
|
428
|
+
email: vi.fn(async () => {}),
|
|
429
|
+
sms: vi.fn(async () => {}),
|
|
430
|
+
test: handler,
|
|
431
|
+
},
|
|
432
|
+
{ pollInterval: 100 },
|
|
433
|
+
);
|
|
434
|
+
processor.start();
|
|
435
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
436
|
+
processor.stop();
|
|
437
|
+
|
|
438
|
+
expect(handler).toHaveBeenCalledWith(
|
|
439
|
+
{ foo: 'updated@example.com' },
|
|
440
|
+
expect.any(Object),
|
|
441
|
+
expect.any(Object),
|
|
442
|
+
);
|
|
443
|
+
const job = await jobQueue.getJob(jobId);
|
|
444
|
+
expect(job?.status).toBe('completed');
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('should silently fail when editing non-pending jobs', async () => {
|
|
448
|
+
// Try to edit a completed job
|
|
449
|
+
const jobId1 = await jobQueue.addJob({
|
|
450
|
+
jobType: 'email',
|
|
451
|
+
payload: { to: 'original@example.com' },
|
|
452
|
+
});
|
|
453
|
+
const processor = jobQueue.createProcessor(
|
|
454
|
+
{
|
|
455
|
+
email: vi.fn(async () => {}),
|
|
456
|
+
sms: vi.fn(async () => {}),
|
|
457
|
+
test: vi.fn(async () => {}),
|
|
458
|
+
},
|
|
459
|
+
{ pollInterval: 100 },
|
|
460
|
+
);
|
|
461
|
+
processor.start();
|
|
462
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
463
|
+
processor.stop();
|
|
464
|
+
|
|
465
|
+
const originalJob = await jobQueue.getJob(jobId1);
|
|
466
|
+
expect(originalJob?.status).toBe('completed');
|
|
467
|
+
|
|
468
|
+
await jobQueue.editJob(jobId1, {
|
|
469
|
+
payload: { to: 'updated@example.com' },
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
const job = await jobQueue.getJob(jobId1);
|
|
473
|
+
expect(job?.status).toBe('completed');
|
|
474
|
+
expect(job?.payload).toEqual({ to: 'original@example.com' });
|
|
475
|
+
|
|
476
|
+
// Try to edit a processing job
|
|
477
|
+
// Use a handler that takes longer to ensure job stays in processing state
|
|
478
|
+
const slowHandler = vi.fn(async (payload: { to: string }, _signal) => {
|
|
479
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
480
|
+
});
|
|
481
|
+
const slowHandlerTest = vi.fn(async (payload: { foo: string }, _signal) => {
|
|
482
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
483
|
+
});
|
|
484
|
+
const processor2 = jobQueue.createProcessor(
|
|
485
|
+
{
|
|
486
|
+
email: slowHandler,
|
|
487
|
+
sms: slowHandler,
|
|
488
|
+
test: slowHandlerTest,
|
|
489
|
+
},
|
|
490
|
+
{ pollInterval: 100 },
|
|
491
|
+
);
|
|
492
|
+
const jobId2 = await jobQueue.addJob({
|
|
493
|
+
jobType: 'email',
|
|
494
|
+
payload: { to: 'processing@example.com' },
|
|
495
|
+
});
|
|
496
|
+
processor2.start();
|
|
497
|
+
// Wait a bit for job to be picked up
|
|
498
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
499
|
+
// Job should be processing now
|
|
500
|
+
const processingJob = await jobQueue.getJob(jobId2);
|
|
501
|
+
if (processingJob?.status === 'processing') {
|
|
502
|
+
await jobQueue.editJob(jobId2, {
|
|
503
|
+
payload: { to: 'updated@example.com' },
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const job2 = await jobQueue.getJob(jobId2);
|
|
507
|
+
// If still processing, payload should be unchanged
|
|
508
|
+
if (job2?.status === 'processing') {
|
|
509
|
+
expect(job2?.payload).toEqual({ to: 'processing@example.com' });
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
processor2.stop();
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('should record edited event when editing via JobQueue API', async () => {
|
|
516
|
+
const jobId = await jobQueue.addJob({
|
|
517
|
+
jobType: 'email',
|
|
518
|
+
payload: { to: 'original@example.com' },
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
await jobQueue.editJob(jobId, {
|
|
522
|
+
payload: { to: 'updated@example.com' },
|
|
523
|
+
priority: 10,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const events = await jobQueue.getJobEvents(jobId);
|
|
527
|
+
const editEvent = events.find((e) => e.eventType === 'edited');
|
|
528
|
+
expect(editEvent).not.toBeUndefined();
|
|
529
|
+
expect(editEvent?.metadata).toMatchObject({
|
|
530
|
+
payload: { to: 'updated@example.com' },
|
|
531
|
+
priority: 10,
|
|
532
|
+
});
|
|
533
|
+
});
|
|
305
534
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
cleanupOldJobs,
|
|
7
|
-
cancelJob,
|
|
8
|
-
cancelAllUpcomingJobs,
|
|
9
|
-
getAllJobs,
|
|
10
|
-
reclaimStuckJobs,
|
|
11
|
-
getJobEvents,
|
|
12
|
-
getJobsByTags,
|
|
13
|
-
getJobs,
|
|
2
|
+
createWaitpoint,
|
|
3
|
+
completeWaitpoint,
|
|
4
|
+
getWaitpoint,
|
|
5
|
+
expireTimedOutWaitpoints,
|
|
14
6
|
} from './queue.js';
|
|
15
7
|
import { createProcessor } from './processor.js';
|
|
16
8
|
import {
|
|
@@ -19,42 +11,71 @@ import {
|
|
|
19
11
|
JobOptions,
|
|
20
12
|
ProcessorOptions,
|
|
21
13
|
JobHandlers,
|
|
14
|
+
JobType,
|
|
15
|
+
PostgresJobQueueConfig,
|
|
16
|
+
RedisJobQueueConfig,
|
|
22
17
|
} from './types.js';
|
|
18
|
+
import { QueueBackend } from './backend.js';
|
|
23
19
|
import { setLogContext } from './log-context.js';
|
|
24
20
|
import { createPool } from './db-util.js';
|
|
21
|
+
import { PostgresBackend } from './backends/postgres.js';
|
|
22
|
+
import { RedisBackend } from './backends/redis.js';
|
|
25
23
|
|
|
26
24
|
/**
|
|
27
|
-
* Initialize the job queue system
|
|
25
|
+
* Initialize the job queue system.
|
|
26
|
+
*
|
|
27
|
+
* Defaults to PostgreSQL when `backend` is omitted.
|
|
28
28
|
*/
|
|
29
29
|
export const initJobQueue = <PayloadMap = any>(
|
|
30
30
|
config: JobQueueConfig,
|
|
31
31
|
): JobQueue<PayloadMap> => {
|
|
32
|
-
const
|
|
32
|
+
const backendType = config.backend ?? 'postgres';
|
|
33
|
+
setLogContext(config.verbose ?? false);
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
let backend: QueueBackend;
|
|
36
|
+
let pool: import('pg').Pool | undefined;
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
if (backendType === 'postgres') {
|
|
39
|
+
const pgConfig = config as PostgresJobQueueConfig;
|
|
40
|
+
pool = createPool(pgConfig.databaseConfig);
|
|
41
|
+
backend = new PostgresBackend(pool);
|
|
42
|
+
} else if (backendType === 'redis') {
|
|
43
|
+
const redisConfig = (config as RedisJobQueueConfig).redisConfig;
|
|
44
|
+
// RedisBackend constructor will throw if ioredis is not installed
|
|
45
|
+
backend = new RedisBackend(redisConfig);
|
|
46
|
+
} else {
|
|
47
|
+
throw new Error(`Unknown backend: ${backendType}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const requirePool = () => {
|
|
51
|
+
if (!pool) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
'Wait/Token features require the PostgreSQL backend. Configure with backend: "postgres" to use these features.',
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return pool;
|
|
57
|
+
};
|
|
38
58
|
|
|
39
59
|
// Return the job queue API
|
|
40
60
|
return {
|
|
41
61
|
// Job queue operations
|
|
42
62
|
addJob: withLogContext(
|
|
43
|
-
(job: JobOptions<PayloadMap, any>) =>
|
|
63
|
+
(job: JobOptions<PayloadMap, any>) =>
|
|
64
|
+
backend.addJob<PayloadMap, any>(job),
|
|
44
65
|
config.verbose ?? false,
|
|
45
66
|
),
|
|
46
67
|
getJob: withLogContext(
|
|
47
|
-
(id: number) => getJob<PayloadMap, any>(
|
|
68
|
+
(id: number) => backend.getJob<PayloadMap, any>(id),
|
|
48
69
|
config.verbose ?? false,
|
|
49
70
|
),
|
|
50
71
|
getJobsByStatus: withLogContext(
|
|
51
72
|
(status: string, limit?: number, offset?: number) =>
|
|
52
|
-
getJobsByStatus<PayloadMap, any>(
|
|
73
|
+
backend.getJobsByStatus<PayloadMap, any>(status, limit, offset),
|
|
53
74
|
config.verbose ?? false,
|
|
54
75
|
),
|
|
55
76
|
getAllJobs: withLogContext(
|
|
56
77
|
(limit?: number, offset?: number) =>
|
|
57
|
-
getAllJobs<PayloadMap, any>(
|
|
78
|
+
backend.getAllJobs<PayloadMap, any>(limit, offset),
|
|
58
79
|
config.verbose ?? false,
|
|
59
80
|
),
|
|
60
81
|
getJobs: withLogContext(
|
|
@@ -69,13 +90,45 @@ export const initJobQueue = <PayloadMap = any>(
|
|
|
69
90
|
},
|
|
70
91
|
limit?: number,
|
|
71
92
|
offset?: number,
|
|
72
|
-
) => getJobs<PayloadMap, any>(
|
|
93
|
+
) => backend.getJobs<PayloadMap, any>(filters, limit, offset),
|
|
73
94
|
config.verbose ?? false,
|
|
74
95
|
),
|
|
75
|
-
retryJob: (jobId: number) => retryJob(
|
|
76
|
-
cleanupOldJobs: (daysToKeep?: number) => cleanupOldJobs(
|
|
96
|
+
retryJob: (jobId: number) => backend.retryJob(jobId),
|
|
97
|
+
cleanupOldJobs: (daysToKeep?: number) => backend.cleanupOldJobs(daysToKeep),
|
|
98
|
+
cleanupOldJobEvents: (daysToKeep?: number) =>
|
|
99
|
+
backend.cleanupOldJobEvents(daysToKeep),
|
|
77
100
|
cancelJob: withLogContext(
|
|
78
|
-
(jobId: number) => cancelJob(
|
|
101
|
+
(jobId: number) => backend.cancelJob(jobId),
|
|
102
|
+
config.verbose ?? false,
|
|
103
|
+
),
|
|
104
|
+
editJob: withLogContext(
|
|
105
|
+
<T extends JobType<PayloadMap>>(
|
|
106
|
+
jobId: number,
|
|
107
|
+
updates: import('./types.js').EditJobOptions<PayloadMap, T>,
|
|
108
|
+
) => backend.editJob(jobId, updates as import('./backend.js').JobUpdates),
|
|
109
|
+
config.verbose ?? false,
|
|
110
|
+
),
|
|
111
|
+
editAllPendingJobs: withLogContext(
|
|
112
|
+
<T extends JobType<PayloadMap>>(
|
|
113
|
+
filters:
|
|
114
|
+
| {
|
|
115
|
+
jobType?: string;
|
|
116
|
+
priority?: number;
|
|
117
|
+
runAt?:
|
|
118
|
+
| Date
|
|
119
|
+
| { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };
|
|
120
|
+
tags?: {
|
|
121
|
+
values: string[];
|
|
122
|
+
mode?: import('./types.js').TagQueryMode;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
| undefined,
|
|
126
|
+
updates: import('./types.js').EditJobOptions<PayloadMap, T>,
|
|
127
|
+
) =>
|
|
128
|
+
backend.editAllPendingJobs(
|
|
129
|
+
filters,
|
|
130
|
+
updates as import('./backend.js').JobUpdates,
|
|
131
|
+
),
|
|
79
132
|
config.verbose ?? false,
|
|
80
133
|
),
|
|
81
134
|
cancelAllUpcomingJobs: withLogContext(
|
|
@@ -86,17 +139,17 @@ export const initJobQueue = <PayloadMap = any>(
|
|
|
86
139
|
| Date
|
|
87
140
|
| { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };
|
|
88
141
|
tags?: { values: string[]; mode?: import('./types.js').TagQueryMode };
|
|
89
|
-
}) => cancelAllUpcomingJobs(
|
|
142
|
+
}) => backend.cancelAllUpcomingJobs(filters),
|
|
90
143
|
config.verbose ?? false,
|
|
91
144
|
),
|
|
92
145
|
reclaimStuckJobs: withLogContext(
|
|
93
146
|
(maxProcessingTimeMinutes?: number) =>
|
|
94
|
-
reclaimStuckJobs(
|
|
147
|
+
backend.reclaimStuckJobs(maxProcessingTimeMinutes),
|
|
95
148
|
config.verbose ?? false,
|
|
96
149
|
),
|
|
97
150
|
getJobsByTags: withLogContext(
|
|
98
151
|
(tags: string[], mode = 'all', limit?: number, offset?: number) =>
|
|
99
|
-
getJobsByTags<PayloadMap, any>(
|
|
152
|
+
backend.getJobsByTags<PayloadMap, any>(tags, mode, limit, offset),
|
|
100
153
|
config.verbose ?? false,
|
|
101
154
|
),
|
|
102
155
|
|
|
@@ -104,14 +157,51 @@ export const initJobQueue = <PayloadMap = any>(
|
|
|
104
157
|
createProcessor: (
|
|
105
158
|
handlers: JobHandlers<PayloadMap>,
|
|
106
159
|
options?: ProcessorOptions,
|
|
107
|
-
) => createProcessor<PayloadMap>(
|
|
108
|
-
|
|
109
|
-
getPool: () => pool,
|
|
160
|
+
) => createProcessor<PayloadMap>(backend, handlers, options),
|
|
161
|
+
|
|
110
162
|
// Job events
|
|
111
163
|
getJobEvents: withLogContext(
|
|
112
|
-
(jobId: number) => getJobEvents(
|
|
164
|
+
(jobId: number) => backend.getJobEvents(jobId),
|
|
113
165
|
config.verbose ?? false,
|
|
114
166
|
),
|
|
167
|
+
|
|
168
|
+
// Wait / Token support (PostgreSQL-only for now)
|
|
169
|
+
createToken: withLogContext(
|
|
170
|
+
(options?: import('./types.js').CreateTokenOptions) =>
|
|
171
|
+
createWaitpoint(requirePool(), null, options),
|
|
172
|
+
config.verbose ?? false,
|
|
173
|
+
),
|
|
174
|
+
completeToken: withLogContext(
|
|
175
|
+
(tokenId: string, data?: any) =>
|
|
176
|
+
completeWaitpoint(requirePool(), tokenId, data),
|
|
177
|
+
config.verbose ?? false,
|
|
178
|
+
),
|
|
179
|
+
getToken: withLogContext(
|
|
180
|
+
(tokenId: string) => getWaitpoint(requirePool(), tokenId),
|
|
181
|
+
config.verbose ?? false,
|
|
182
|
+
),
|
|
183
|
+
expireTimedOutTokens: withLogContext(
|
|
184
|
+
() => expireTimedOutWaitpoints(requirePool()),
|
|
185
|
+
config.verbose ?? false,
|
|
186
|
+
),
|
|
187
|
+
|
|
188
|
+
// Advanced access
|
|
189
|
+
getPool: () => {
|
|
190
|
+
if (backendType !== 'postgres') {
|
|
191
|
+
throw new Error(
|
|
192
|
+
'getPool() is only available with the PostgreSQL backend.',
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return (backend as PostgresBackend).getPool();
|
|
196
|
+
},
|
|
197
|
+
getRedisClient: () => {
|
|
198
|
+
if (backendType !== 'redis') {
|
|
199
|
+
throw new Error(
|
|
200
|
+
'getRedisClient() is only available with the Redis backend.',
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return (backend as RedisBackend).getClient();
|
|
204
|
+
},
|
|
115
205
|
};
|
|
116
206
|
};
|
|
117
207
|
|
|
@@ -123,3 +213,9 @@ const withLogContext =
|
|
|
123
213
|
};
|
|
124
214
|
|
|
125
215
|
export * from './types.js';
|
|
216
|
+
export { QueueBackend } from './backend.js';
|
|
217
|
+
export { PostgresBackend } from './backends/postgres.js';
|
|
218
|
+
export {
|
|
219
|
+
validateHandlerSerializable,
|
|
220
|
+
testHandlerSerialization,
|
|
221
|
+
} from './handler-validation.js';
|