@happyvertical/jobs 0.80.0 → 0.80.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/dist/adapters/bull.js +343 -346
- package/dist/adapters/bull.js.map +1 -1
- package/dist/adapters/bullmq.js +351 -388
- package/dist/adapters/bullmq.js.map +1 -1
- package/dist/adapters/cloud-tasks.js +307 -333
- package/dist/adapters/cloud-tasks.js.map +1 -1
- package/dist/adapters/postgres.js +288 -328
- package/dist/adapters/postgres.js.map +1 -1
- package/dist/adapters/sqlite.js +236 -258
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/adapters/sqs.js +363 -408
- package/dist/adapters/sqs.js.map +1 -1
- package/dist/chunks/base-store-DIasEzL0.js +386 -0
- package/dist/chunks/base-store-DIasEzL0.js.map +1 -0
- package/dist/cli/claude-context.js +17 -17
- package/dist/cli/claude-context.js.map +1 -1
- package/dist/index.js +219 -242
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/dist/chunks/base-store-DlNksWvQ.js +0 -324
- package/dist/chunks/base-store-DlNksWvQ.js.map +0 -1
package/dist/adapters/bullmq.js
CHANGED
|
@@ -1,391 +1,354 @@
|
|
|
1
|
+
import { n as priorityToNumber, t as BaseJobStore } from "../chunks/base-store-DIasEzL0.js";
|
|
1
2
|
import { createId } from "@happyvertical/utils";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const totals = {
|
|
348
|
-
pending: 0,
|
|
349
|
-
running: 0,
|
|
350
|
-
completed: 0,
|
|
351
|
-
failed: 0,
|
|
352
|
-
cancelled: 0,
|
|
353
|
-
avgDuration: null
|
|
354
|
-
};
|
|
355
|
-
const targetQueues = queue ? [this.getQueueWrapper(queue).queue] : Array.from(this.queues.values()).map((w) => w.queue);
|
|
356
|
-
for (const q of targetQueues) {
|
|
357
|
-
const counts = await q.getJobCounts(
|
|
358
|
-
"waiting",
|
|
359
|
-
"active",
|
|
360
|
-
"completed",
|
|
361
|
-
"failed",
|
|
362
|
-
"delayed"
|
|
363
|
-
);
|
|
364
|
-
totals.pending += counts.waiting + counts.delayed;
|
|
365
|
-
totals.running += counts.active;
|
|
366
|
-
totals.completed += counts.completed;
|
|
367
|
-
totals.failed += counts.failed;
|
|
368
|
-
}
|
|
369
|
-
return totals;
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Close all queues
|
|
373
|
-
*/
|
|
374
|
-
async close() {
|
|
375
|
-
for (const { queue, events } of this.queues.values()) {
|
|
376
|
-
await events.close();
|
|
377
|
-
await queue.close();
|
|
378
|
-
}
|
|
379
|
-
this.queues.clear();
|
|
380
|
-
this.initialized = false;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
3
|
+
//#region src/adapters/bullmq.ts
|
|
4
|
+
/**
|
|
5
|
+
* BullMQ (Redis) Job Store Adapter
|
|
6
|
+
*
|
|
7
|
+
* Uses BullMQ library for Redis-based job storage with real-time updates.
|
|
8
|
+
* BullMQ is the successor to Bull with improved performance and TypeScript support.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { BullMQJobStore } from '@happyvertical/jobs/adapters/bullmq';
|
|
13
|
+
*
|
|
14
|
+
* const store = new BullMQJobStore({
|
|
15
|
+
* connection: {
|
|
16
|
+
* host: 'localhost',
|
|
17
|
+
* port: 6379,
|
|
18
|
+
* },
|
|
19
|
+
* defaultJobOptions: {
|
|
20
|
+
* removeOnComplete: 100,
|
|
21
|
+
* removeOnFail: 500,
|
|
22
|
+
* },
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* await store.initialize();
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Note: This adapter requires the `bullmq` package as a peer dependency.
|
|
29
|
+
* Install it with: npm install bullmq
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* BullMQ-based job store implementation
|
|
33
|
+
*/
|
|
34
|
+
var BullMQJobStore = class extends BaseJobStore {
|
|
35
|
+
config;
|
|
36
|
+
queues = /* @__PURE__ */ new Map();
|
|
37
|
+
bullmqModule = null;
|
|
38
|
+
constructor(config = {}) {
|
|
39
|
+
super();
|
|
40
|
+
this.config = {
|
|
41
|
+
prefix: "smrt_jobs",
|
|
42
|
+
defaultJobOptions: {
|
|
43
|
+
removeOnComplete: 100,
|
|
44
|
+
removeOnFail: 500
|
|
45
|
+
},
|
|
46
|
+
...config
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Initialize the store - dynamically imports BullMQ
|
|
51
|
+
*/
|
|
52
|
+
async initialize() {
|
|
53
|
+
if (this.initialized) return;
|
|
54
|
+
try {
|
|
55
|
+
this.bullmqModule = await import("bullmq");
|
|
56
|
+
} catch {
|
|
57
|
+
throw new Error("BullMQ is required for BullMQJobStore. Install it with: npm install bullmq");
|
|
58
|
+
}
|
|
59
|
+
this.initialized = true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get or create a BullMQ queue for a queue name
|
|
63
|
+
*/
|
|
64
|
+
getQueueWrapper(queueName) {
|
|
65
|
+
if (!this.bullmqModule) throw new Error("BullMQJobStore not initialized");
|
|
66
|
+
if (!this.queues.has(queueName)) {
|
|
67
|
+
const queueOptions = {
|
|
68
|
+
prefix: this.config.prefix,
|
|
69
|
+
defaultJobOptions: this.config.defaultJobOptions
|
|
70
|
+
};
|
|
71
|
+
if (this.config.connection) queueOptions.connection = this.config.connection;
|
|
72
|
+
const queue = new this.bullmqModule.Queue(queueName, queueOptions);
|
|
73
|
+
const eventsOptions = { prefix: this.config.prefix };
|
|
74
|
+
if (this.config.connection) eventsOptions.connection = this.config.connection;
|
|
75
|
+
const events = new this.bullmqModule.QueueEvents(queueName, eventsOptions);
|
|
76
|
+
events.on("completed", async ({ jobId }) => {
|
|
77
|
+
const job = await this.get(jobId);
|
|
78
|
+
if (job) await this.emitEvent("job.completed", job);
|
|
79
|
+
});
|
|
80
|
+
events.on("failed", async ({ jobId, failedReason }) => {
|
|
81
|
+
const job = await this.get(jobId);
|
|
82
|
+
if (job) {
|
|
83
|
+
job.lastError = failedReason;
|
|
84
|
+
await this.emitEvent("job.failed", job, { error: failedReason });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
this.queues.set(queueName, {
|
|
88
|
+
queue,
|
|
89
|
+
events
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return this.queues.get(queueName);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Convert BullMQ job to our Job format
|
|
96
|
+
*/
|
|
97
|
+
async bullMQJobToJob(bullMQJob, queueName) {
|
|
98
|
+
const data = bullMQJob.data;
|
|
99
|
+
const state = await bullMQJob.getState();
|
|
100
|
+
const status = this.mapBullMQStatus(state);
|
|
101
|
+
return {
|
|
102
|
+
id: bullMQJob.id ?? createId(),
|
|
103
|
+
queue: queueName,
|
|
104
|
+
payload: data.payload,
|
|
105
|
+
status,
|
|
106
|
+
priority: bullMQJob.opts.priority ?? 50,
|
|
107
|
+
attempts: bullMQJob.attemptsMade,
|
|
108
|
+
maxAttempts: data.meta.maxAttempts,
|
|
109
|
+
runAt: bullMQJob.opts.delay ? new Date(bullMQJob.timestamp + bullMQJob.opts.delay) : new Date(bullMQJob.timestamp),
|
|
110
|
+
startedAt: bullMQJob.processedOn ? new Date(bullMQJob.processedOn) : null,
|
|
111
|
+
completedAt: bullMQJob.finishedOn ? new Date(bullMQJob.finishedOn) : null,
|
|
112
|
+
timeout: data.meta.timeout,
|
|
113
|
+
timeoutBehavior: data.meta.timeoutBehavior,
|
|
114
|
+
lastError: bullMQJob.failedReason ?? null,
|
|
115
|
+
resultPointer: bullMQJob.returnvalue?.resultPointer ?? null,
|
|
116
|
+
retryStrategy: data.meta.retryStrategy,
|
|
117
|
+
workerId: data.meta.workerId,
|
|
118
|
+
workerHeartbeat: null,
|
|
119
|
+
createdAt: new Date(bullMQJob.timestamp),
|
|
120
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Map BullMQ job state to our JobStatus
|
|
125
|
+
*/
|
|
126
|
+
mapBullMQStatus(state) {
|
|
127
|
+
switch (state) {
|
|
128
|
+
case "completed": return "completed";
|
|
129
|
+
case "failed": return "failed";
|
|
130
|
+
case "active": return "running";
|
|
131
|
+
default: return "pending";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Enqueue a new job
|
|
136
|
+
*/
|
|
137
|
+
async enqueue(options) {
|
|
138
|
+
if (!this.initialized) throw new Error("BullMQJobStore not initialized");
|
|
139
|
+
const queueName = options.queue ?? "default";
|
|
140
|
+
const { queue } = this.getQueueWrapper(queueName);
|
|
141
|
+
const jobData = {
|
|
142
|
+
payload: options.payload,
|
|
143
|
+
meta: {
|
|
144
|
+
maxAttempts: options.maxAttempts ?? 3,
|
|
145
|
+
timeout: options.timeout ?? 3e5,
|
|
146
|
+
timeoutBehavior: options.timeoutBehavior ?? "fail",
|
|
147
|
+
retryStrategy: options.retryStrategy && "toConfig" in options.retryStrategy ? options.retryStrategy.toConfig() : options.retryStrategy ?? {
|
|
148
|
+
type: "exponential",
|
|
149
|
+
config: {
|
|
150
|
+
initialDelay: 1e3,
|
|
151
|
+
multiplier: 2
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
workerId: null
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const bullMQJobOptions = {
|
|
158
|
+
priority: priorityToNumber(options.priority),
|
|
159
|
+
attempts: options.maxAttempts ?? 3,
|
|
160
|
+
delay: options.runAt ? Math.max(0, options.runAt.getTime() - Date.now()) : void 0,
|
|
161
|
+
jobId: createId()
|
|
162
|
+
};
|
|
163
|
+
const bullMQJob = await queue.add(options.payload.method, jobData, bullMQJobOptions);
|
|
164
|
+
const job = await this.bullMQJobToJob(bullMQJob, queueName);
|
|
165
|
+
await this.emitEvent("job.created", job);
|
|
166
|
+
return job;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Dequeue jobs ready for processing
|
|
170
|
+
*/
|
|
171
|
+
async dequeue(queues, limit, workerId) {
|
|
172
|
+
if (!this.initialized) throw new Error("BullMQJobStore not initialized");
|
|
173
|
+
const jobs = [];
|
|
174
|
+
for (const queueName of queues) {
|
|
175
|
+
if (jobs.length >= limit) break;
|
|
176
|
+
const { queue } = this.getQueueWrapper(queueName);
|
|
177
|
+
const waiting = await queue.getWaiting(0, limit - jobs.length - 1);
|
|
178
|
+
for (const bullMQJob of waiting) {
|
|
179
|
+
bullMQJob.data.meta.workerId = workerId;
|
|
180
|
+
await bullMQJob.updateData(bullMQJob.data);
|
|
181
|
+
jobs.push(await this.bullMQJobToJob(bullMQJob, queueName));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return jobs;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Update a job
|
|
188
|
+
*/
|
|
189
|
+
async update(id, updates) {
|
|
190
|
+
if (!this.initialized) throw new Error("BullMQJobStore not initialized");
|
|
191
|
+
for (const [queueName, { queue }] of this.queues) {
|
|
192
|
+
const bullMQJob = await queue.getJob(id);
|
|
193
|
+
if (bullMQJob) {
|
|
194
|
+
const newData = { ...bullMQJob.data };
|
|
195
|
+
if (updates.payload) newData.payload = updates.payload;
|
|
196
|
+
if (updates.maxAttempts !== void 0) newData.meta.maxAttempts = updates.maxAttempts;
|
|
197
|
+
if (updates.timeout !== void 0) newData.meta.timeout = updates.timeout;
|
|
198
|
+
if (updates.workerId !== void 0) newData.meta.workerId = updates.workerId;
|
|
199
|
+
await bullMQJob.updateData(newData);
|
|
200
|
+
return await this.bullMQJobToJob(bullMQJob, queueName);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
throw new Error(`Job ${id} not found`);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get a job by ID
|
|
207
|
+
*/
|
|
208
|
+
async get(id) {
|
|
209
|
+
if (!this.initialized) throw new Error("BullMQJobStore not initialized");
|
|
210
|
+
for (const [queueName, { queue }] of this.queues) {
|
|
211
|
+
const bullMQJob = await queue.getJob(id);
|
|
212
|
+
if (bullMQJob) return await this.bullMQJobToJob(bullMQJob, queueName);
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* List jobs with filtering
|
|
218
|
+
*/
|
|
219
|
+
async list(filter) {
|
|
220
|
+
if (!this.initialized) throw new Error("BullMQJobStore not initialized");
|
|
221
|
+
const jobs = [];
|
|
222
|
+
const limit = filter.limit ?? 100;
|
|
223
|
+
const offset = filter.offset ?? 0;
|
|
224
|
+
const targetQueues = filter.queue ? [{
|
|
225
|
+
name: filter.queue,
|
|
226
|
+
wrapper: this.getQueueWrapper(filter.queue)
|
|
227
|
+
}] : Array.from(this.queues.entries()).map(([name, wrapper]) => ({
|
|
228
|
+
name,
|
|
229
|
+
wrapper
|
|
230
|
+
}));
|
|
231
|
+
for (const { name, wrapper } of targetQueues) {
|
|
232
|
+
const { queue } = wrapper;
|
|
233
|
+
const statuses = filter.status ? Array.isArray(filter.status) ? filter.status : [filter.status] : [
|
|
234
|
+
"pending",
|
|
235
|
+
"running",
|
|
236
|
+
"completed",
|
|
237
|
+
"failed"
|
|
238
|
+
];
|
|
239
|
+
for (const status of statuses) {
|
|
240
|
+
let bullMQJobs = [];
|
|
241
|
+
switch (status) {
|
|
242
|
+
case "pending":
|
|
243
|
+
bullMQJobs = [...await queue.getWaiting(offset, offset + limit - 1), ...await queue.getDelayed(offset, offset + limit - 1)];
|
|
244
|
+
break;
|
|
245
|
+
case "running":
|
|
246
|
+
bullMQJobs = await queue.getActive(offset, offset + limit - 1);
|
|
247
|
+
break;
|
|
248
|
+
case "completed":
|
|
249
|
+
bullMQJobs = await queue.getCompleted(offset, offset + limit - 1);
|
|
250
|
+
break;
|
|
251
|
+
case "failed":
|
|
252
|
+
bullMQJobs = await queue.getFailed(offset, offset + limit - 1);
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
for (const bullMQJob of bullMQJobs) {
|
|
256
|
+
const job = await this.bullMQJobToJob(bullMQJob, name);
|
|
257
|
+
if (filter.objectType && job.payload.objectType !== filter.objectType) continue;
|
|
258
|
+
if (filter.method && job.payload.method !== filter.method) continue;
|
|
259
|
+
if (filter.createdAfter && job.createdAt < filter.createdAfter) continue;
|
|
260
|
+
if (filter.createdBefore && job.createdAt > filter.createdBefore) continue;
|
|
261
|
+
jobs.push(job);
|
|
262
|
+
if (jobs.length >= limit) break;
|
|
263
|
+
}
|
|
264
|
+
if (jobs.length >= limit) break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return jobs.slice(0, limit);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Cancel a job
|
|
271
|
+
*/
|
|
272
|
+
async cancel(id) {
|
|
273
|
+
if (!this.initialized) throw new Error("BullMQJobStore not initialized");
|
|
274
|
+
for (const [queueName, { queue }] of this.queues) {
|
|
275
|
+
const bullMQJob = await queue.getJob(id);
|
|
276
|
+
if (bullMQJob) {
|
|
277
|
+
await bullMQJob.remove();
|
|
278
|
+
const job = await this.bullMQJobToJob(bullMQJob, queueName);
|
|
279
|
+
job.status = "cancelled";
|
|
280
|
+
await this.emitEvent("job.cancelled", job);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
throw new Error(`Job ${id} not found`);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Clean up old jobs
|
|
288
|
+
*/
|
|
289
|
+
async cleanup(options) {
|
|
290
|
+
if (!this.initialized) throw new Error("BullMQJobStore not initialized");
|
|
291
|
+
let cleaned = 0;
|
|
292
|
+
for (const { queue } of this.queues.values()) {
|
|
293
|
+
if (options.completedBefore) {
|
|
294
|
+
const grace = Date.now() - options.completedBefore.getTime();
|
|
295
|
+
const result = await queue.clean(grace, options.limit ?? 1e3, "completed");
|
|
296
|
+
cleaned += result.length;
|
|
297
|
+
}
|
|
298
|
+
if (options.failedBefore) {
|
|
299
|
+
const grace = Date.now() - options.failedBefore.getTime();
|
|
300
|
+
const result = await queue.clean(grace, options.limit ?? 1e3, "failed");
|
|
301
|
+
cleaned += result.length;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return cleaned;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Update worker heartbeat (no-op for BullMQ - handled internally)
|
|
308
|
+
*/
|
|
309
|
+
async heartbeat(_jobId, _workerId) {}
|
|
310
|
+
/**
|
|
311
|
+
* Get queue statistics
|
|
312
|
+
*/
|
|
313
|
+
async stats(queue) {
|
|
314
|
+
if (!this.initialized) throw new Error("BullMQJobStore not initialized");
|
|
315
|
+
const totals = {
|
|
316
|
+
pending: 0,
|
|
317
|
+
running: 0,
|
|
318
|
+
completed: 0,
|
|
319
|
+
failed: 0,
|
|
320
|
+
cancelled: 0,
|
|
321
|
+
avgDuration: null
|
|
322
|
+
};
|
|
323
|
+
const targetQueues = queue ? [this.getQueueWrapper(queue).queue] : Array.from(this.queues.values()).map((w) => w.queue);
|
|
324
|
+
for (const q of targetQueues) {
|
|
325
|
+
const counts = await q.getJobCounts("waiting", "active", "completed", "failed", "delayed");
|
|
326
|
+
totals.pending += counts.waiting + counts.delayed;
|
|
327
|
+
totals.running += counts.active;
|
|
328
|
+
totals.completed += counts.completed;
|
|
329
|
+
totals.failed += counts.failed;
|
|
330
|
+
}
|
|
331
|
+
return totals;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Close all queues
|
|
335
|
+
*/
|
|
336
|
+
async close() {
|
|
337
|
+
for (const { queue, events } of this.queues.values()) {
|
|
338
|
+
await events.close();
|
|
339
|
+
await queue.close();
|
|
340
|
+
}
|
|
341
|
+
this.queues.clear();
|
|
342
|
+
this.initialized = false;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
/**
|
|
346
|
+
* Create a BullMQ job store instance
|
|
347
|
+
*/
|
|
383
348
|
function createBullMQJobStore(config) {
|
|
384
|
-
|
|
349
|
+
return new BullMQJobStore(config);
|
|
385
350
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
};
|
|
391
|
-
//# sourceMappingURL=bullmq.js.map
|
|
351
|
+
//#endregion
|
|
352
|
+
export { BullMQJobStore, BullMQJobStore as default, createBullMQJobStore };
|
|
353
|
+
|
|
354
|
+
//# sourceMappingURL=bullmq.js.map
|