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