@nicnocquee/dataqueue 1.24.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/src/db-util.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Pool } from 'pg';
2
- import { JobQueueConfig } from './types.js';
2
+ import { PostgresJobQueueConfig } from './types.js';
3
3
  import { parse } from 'pg-connection-string';
4
4
  import fs from 'fs';
5
5
 
@@ -26,7 +26,9 @@ function loadPemOrFile(value?: string): string | undefined {
26
26
  * rejectUnauthorized: true
27
27
  * }
28
28
  */
29
- export const createPool = (config: JobQueueConfig['databaseConfig']): Pool => {
29
+ export const createPool = (
30
+ config: PostgresJobQueueConfig['databaseConfig'],
31
+ ): Pool => {
30
32
  let searchPath: string | undefined;
31
33
  let ssl: any = undefined;
32
34
  let customCA: string | undefined;
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({ foo: 'bar' }, expect.any(Object));
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
 
@@ -434,6 +438,7 @@ describe('index integration', () => {
434
438
  expect(handler).toHaveBeenCalledWith(
435
439
  { foo: 'updated@example.com' },
436
440
  expect.any(Object),
441
+ expect.any(Object),
437
442
  );
438
443
  const job = await jobQueue.getJob(jobId);
439
444
  expect(job?.status).toBe('completed');
package/src/index.ts CHANGED
@@ -1,18 +1,8 @@
1
1
  import {
2
- addJob,
3
- getJob,
4
- getJobsByStatus,
5
- retryJob,
6
- cleanupOldJobs,
7
- cancelJob,
8
- cancelAllUpcomingJobs,
9
- getAllJobs,
10
- reclaimStuckJobs,
11
- getJobEvents,
12
- getJobsByTags,
13
- getJobs,
14
- editJob,
15
- editAllPendingJobs,
2
+ createWaitpoint,
3
+ completeWaitpoint,
4
+ getWaitpoint,
5
+ expireTimedOutWaitpoints,
16
6
  } from './queue.js';
17
7
  import { createProcessor } from './processor.js';
18
8
  import {
@@ -22,42 +12,70 @@ import {
22
12
  ProcessorOptions,
23
13
  JobHandlers,
24
14
  JobType,
15
+ PostgresJobQueueConfig,
16
+ RedisJobQueueConfig,
25
17
  } from './types.js';
18
+ import { QueueBackend } from './backend.js';
26
19
  import { setLogContext } from './log-context.js';
27
20
  import { createPool } from './db-util.js';
21
+ import { PostgresBackend } from './backends/postgres.js';
22
+ import { RedisBackend } from './backends/redis.js';
28
23
 
29
24
  /**
30
- * Initialize the job queue system
25
+ * Initialize the job queue system.
26
+ *
27
+ * Defaults to PostgreSQL when `backend` is omitted.
31
28
  */
32
29
  export const initJobQueue = <PayloadMap = any>(
33
30
  config: JobQueueConfig,
34
31
  ): JobQueue<PayloadMap> => {
35
- const { databaseConfig } = config;
32
+ const backendType = config.backend ?? 'postgres';
33
+ setLogContext(config.verbose ?? false);
36
34
 
37
- // Create database pool
38
- const pool = createPool(databaseConfig);
35
+ let backend: QueueBackend;
36
+ let pool: import('pg').Pool | undefined;
39
37
 
40
- setLogContext(config.verbose ?? false);
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
+ };
41
58
 
42
59
  // Return the job queue API
43
60
  return {
44
61
  // Job queue operations
45
62
  addJob: withLogContext(
46
- (job: JobOptions<PayloadMap, any>) => addJob<PayloadMap, any>(pool, job),
63
+ (job: JobOptions<PayloadMap, any>) =>
64
+ backend.addJob<PayloadMap, any>(job),
47
65
  config.verbose ?? false,
48
66
  ),
49
67
  getJob: withLogContext(
50
- (id: number) => getJob<PayloadMap, any>(pool, id),
68
+ (id: number) => backend.getJob<PayloadMap, any>(id),
51
69
  config.verbose ?? false,
52
70
  ),
53
71
  getJobsByStatus: withLogContext(
54
72
  (status: string, limit?: number, offset?: number) =>
55
- getJobsByStatus<PayloadMap, any>(pool, status, limit, offset),
73
+ backend.getJobsByStatus<PayloadMap, any>(status, limit, offset),
56
74
  config.verbose ?? false,
57
75
  ),
58
76
  getAllJobs: withLogContext(
59
77
  (limit?: number, offset?: number) =>
60
- getAllJobs<PayloadMap, any>(pool, limit, offset),
78
+ backend.getAllJobs<PayloadMap, any>(limit, offset),
61
79
  config.verbose ?? false,
62
80
  ),
63
81
  getJobs: withLogContext(
@@ -72,20 +90,22 @@ export const initJobQueue = <PayloadMap = any>(
72
90
  },
73
91
  limit?: number,
74
92
  offset?: number,
75
- ) => getJobs<PayloadMap, any>(pool, filters, limit, offset),
93
+ ) => backend.getJobs<PayloadMap, any>(filters, limit, offset),
76
94
  config.verbose ?? false,
77
95
  ),
78
- retryJob: (jobId: number) => retryJob(pool, jobId),
79
- cleanupOldJobs: (daysToKeep?: number) => cleanupOldJobs(pool, daysToKeep),
96
+ retryJob: (jobId: number) => backend.retryJob(jobId),
97
+ cleanupOldJobs: (daysToKeep?: number) => backend.cleanupOldJobs(daysToKeep),
98
+ cleanupOldJobEvents: (daysToKeep?: number) =>
99
+ backend.cleanupOldJobEvents(daysToKeep),
80
100
  cancelJob: withLogContext(
81
- (jobId: number) => cancelJob(pool, jobId),
101
+ (jobId: number) => backend.cancelJob(jobId),
82
102
  config.verbose ?? false,
83
103
  ),
84
104
  editJob: withLogContext(
85
105
  <T extends JobType<PayloadMap>>(
86
106
  jobId: number,
87
107
  updates: import('./types.js').EditJobOptions<PayloadMap, T>,
88
- ) => editJob<PayloadMap, T>(pool, jobId, updates as any),
108
+ ) => backend.editJob(jobId, updates as import('./backend.js').JobUpdates),
89
109
  config.verbose ?? false,
90
110
  ),
91
111
  editAllPendingJobs: withLogContext(
@@ -104,7 +124,11 @@ export const initJobQueue = <PayloadMap = any>(
104
124
  }
105
125
  | undefined,
106
126
  updates: import('./types.js').EditJobOptions<PayloadMap, T>,
107
- ) => editAllPendingJobs<PayloadMap, T>(pool, filters, updates as any),
127
+ ) =>
128
+ backend.editAllPendingJobs(
129
+ filters,
130
+ updates as import('./backend.js').JobUpdates,
131
+ ),
108
132
  config.verbose ?? false,
109
133
  ),
110
134
  cancelAllUpcomingJobs: withLogContext(
@@ -115,17 +139,17 @@ export const initJobQueue = <PayloadMap = any>(
115
139
  | Date
116
140
  | { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };
117
141
  tags?: { values: string[]; mode?: import('./types.js').TagQueryMode };
118
- }) => cancelAllUpcomingJobs(pool, filters),
142
+ }) => backend.cancelAllUpcomingJobs(filters),
119
143
  config.verbose ?? false,
120
144
  ),
121
145
  reclaimStuckJobs: withLogContext(
122
146
  (maxProcessingTimeMinutes?: number) =>
123
- reclaimStuckJobs(pool, maxProcessingTimeMinutes),
147
+ backend.reclaimStuckJobs(maxProcessingTimeMinutes),
124
148
  config.verbose ?? false,
125
149
  ),
126
150
  getJobsByTags: withLogContext(
127
151
  (tags: string[], mode = 'all', limit?: number, offset?: number) =>
128
- getJobsByTags<PayloadMap, any>(pool, tags, mode, limit, offset),
152
+ backend.getJobsByTags<PayloadMap, any>(tags, mode, limit, offset),
129
153
  config.verbose ?? false,
130
154
  ),
131
155
 
@@ -133,14 +157,51 @@ export const initJobQueue = <PayloadMap = any>(
133
157
  createProcessor: (
134
158
  handlers: JobHandlers<PayloadMap>,
135
159
  options?: ProcessorOptions,
136
- ) => createProcessor<PayloadMap>(pool, handlers, options),
137
- // Advanced access (for custom operations)
138
- getPool: () => pool,
160
+ ) => createProcessor<PayloadMap>(backend, handlers, options),
161
+
139
162
  // Job events
140
163
  getJobEvents: withLogContext(
141
- (jobId: number) => getJobEvents(pool, jobId),
164
+ (jobId: number) => backend.getJobEvents(jobId),
165
+ config.verbose ?? false,
166
+ ),
167
+
168
+ // Wait / Token support (PostgreSQL-only for now)
169
+ createToken: withLogContext(
170
+ (options?: import('./types.js').CreateTokenOptions) =>
171
+ createWaitpoint(requirePool(), null, options),
142
172
  config.verbose ?? false,
143
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
+ },
144
205
  };
145
206
  };
146
207
 
@@ -152,6 +213,8 @@ const withLogContext =
152
213
  };
153
214
 
154
215
  export * from './types.js';
216
+ export { QueueBackend } from './backend.js';
217
+ export { PostgresBackend } from './backends/postgres.js';
155
218
  export {
156
219
  validateHandlerSerializable,
157
220
  testHandlerSerialization,