@happyvertical/jobs 0.74.8
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/AGENT.md +33 -0
- package/LICENSE +7 -0
- package/dist/adapters/bull.d.ts +103 -0
- package/dist/adapters/bull.d.ts.map +1 -0
- package/dist/adapters/bull.js +349 -0
- package/dist/adapters/bull.js.map +1 -0
- package/dist/adapters/bullmq.d.ts +85 -0
- package/dist/adapters/bullmq.d.ts.map +1 -0
- package/dist/adapters/bullmq.js +391 -0
- package/dist/adapters/bullmq.js.map +1 -0
- package/dist/adapters/cloud-tasks.d.ts +110 -0
- package/dist/adapters/cloud-tasks.d.ts.map +1 -0
- package/dist/adapters/cloud-tasks.js +336 -0
- package/dist/adapters/cloud-tasks.js.map +1 -0
- package/dist/adapters/postgres.d.ts +55 -0
- package/dist/adapters/postgres.d.ts.map +1 -0
- package/dist/adapters/postgres.js +437 -0
- package/dist/adapters/postgres.js.map +1 -0
- package/dist/adapters/sqlite.d.ts +44 -0
- package/dist/adapters/sqlite.d.ts.map +1 -0
- package/dist/adapters/sqlite.js +323 -0
- package/dist/adapters/sqlite.js.map +1 -0
- package/dist/adapters/sqs.d.ts +112 -0
- package/dist/adapters/sqs.d.ts.map +1 -0
- package/dist/adapters/sqs.js +411 -0
- package/dist/adapters/sqs.js.map +1 -0
- package/dist/base-store.d.ts +69 -0
- package/dist/base-store.d.ts.map +1 -0
- package/dist/chunks/base-store-DlNksWvQ.js +324 -0
- package/dist/chunks/base-store-DlNksWvQ.js.map +1 -0
- package/dist/cli/claude-context.d.ts +3 -0
- package/dist/cli/claude-context.d.ts.map +1 -0
- package/dist/cli/claude-context.js +21 -0
- package/dist/cli/claude-context.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +252 -0
- package/dist/index.js.map +1 -0
- package/dist/retry.d.ts +84 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/types.d.ts +311 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/worker.d.ts +74 -0
- package/dist/worker.d.ts.map +1 -0
- package/metadata.json +34 -0
- package/package.json +114 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
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
|
+
}
|
|
383
|
+
function createBullMQJobStore(config) {
|
|
384
|
+
return new BullMQJobStore(config);
|
|
385
|
+
}
|
|
386
|
+
export {
|
|
387
|
+
BullMQJobStore,
|
|
388
|
+
createBullMQJobStore,
|
|
389
|
+
BullMQJobStore as default
|
|
390
|
+
};
|
|
391
|
+
//# sourceMappingURL=bullmq.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bullmq.js","sources":["../../src/adapters/bullmq.ts"],"sourcesContent":["/**\n * BullMQ (Redis) Job Store Adapter\n *\n * Uses BullMQ library for Redis-based job storage with real-time updates.\n * BullMQ is the successor to Bull with improved performance and TypeScript support.\n *\n * @example\n * ```typescript\n * import { BullMQJobStore } from '@happyvertical/jobs/adapters/bullmq';\n *\n * const store = new BullMQJobStore({\n * connection: {\n * host: 'localhost',\n * port: 6379,\n * },\n * defaultJobOptions: {\n * removeOnComplete: 100,\n * removeOnFail: 500,\n * },\n * });\n *\n * await store.initialize();\n * ```\n *\n * Note: This adapter requires the `bullmq` package as a peer dependency.\n * Install it with: npm install bullmq\n */\n\nimport { createId } from '@happyvertical/utils';\nimport type {\n Job as BullMQJob,\n JobsOptions as BullMQJobOptions,\n Queue as BullMQQueue,\n QueueEvents as BullMQQueueEvents,\n ConnectionOptions,\n} from 'bullmq';\nimport { BaseJobStore, priorityToNumber } from '../base-store.js';\nimport type {\n CleanupOptions,\n Job,\n JobCreateOptions,\n JobFilter,\n QueueStats,\n} from '../types.js';\n\n/**\n * BullMQ adapter configuration\n */\nexport interface BullMQJobStoreConfig {\n /** Redis connection options */\n connection?: ConnectionOptions;\n /** Default queue name prefix */\n prefix?: string;\n /** Default job options */\n defaultJobOptions?: BullMQJobOptions;\n}\n\n/**\n * Job data stored in BullMQ\n */\ninterface BullMQJobData {\n /** Original job payload */\n payload: Job['payload'];\n /** Additional metadata */\n meta: {\n maxAttempts: number;\n timeout: number;\n timeoutBehavior: Job['timeoutBehavior'];\n retryStrategy: Job['retryStrategy'];\n workerId: string | null;\n };\n}\n\n/**\n * Queue wrapper to hold queue and events\n */\ninterface QueueWrapper {\n queue: BullMQQueue<BullMQJobData>;\n events: BullMQQueueEvents;\n}\n\n/**\n * BullMQ-based job store implementation\n */\nexport class BullMQJobStore extends BaseJobStore {\n private config: BullMQJobStoreConfig;\n private queues: Map<string, QueueWrapper> = new Map();\n // biome-ignore lint/style/useNamingConvention: bullmq module reference\n private bullmqModule: typeof import('bullmq') | null = null;\n\n constructor(config: BullMQJobStoreConfig = {}) {\n super();\n this.config = {\n prefix: 'smrt_jobs',\n defaultJobOptions: {\n removeOnComplete: 100,\n removeOnFail: 500,\n },\n ...config,\n };\n }\n\n /**\n * Initialize the store - dynamically imports BullMQ\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n try {\n // Dynamic import to avoid requiring bullmq as a hard dependency\n this.bullmqModule = await import('bullmq');\n } catch {\n throw new Error(\n 'BullMQ is required for BullMQJobStore. Install it with: npm install bullmq',\n );\n }\n\n this.initialized = true;\n }\n\n /**\n * Get or create a BullMQ queue for a queue name\n */\n private getQueueWrapper(queueName: string): QueueWrapper {\n if (!this.bullmqModule) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n if (!this.queues.has(queueName)) {\n // Build options conditionally - BullMQ types require connection but it can be omitted for local Redis\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const queueOptions: any = {\n prefix: this.config.prefix,\n defaultJobOptions: this.config.defaultJobOptions,\n };\n if (this.config.connection) {\n queueOptions.connection = this.config.connection;\n }\n\n const queue = new this.bullmqModule.Queue<BullMQJobData>(\n queueName,\n queueOptions,\n ) as BullMQQueue<BullMQJobData>;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const eventsOptions: any = {\n prefix: this.config.prefix,\n };\n if (this.config.connection) {\n eventsOptions.connection = this.config.connection;\n }\n\n const events = new this.bullmqModule.QueueEvents(\n queueName,\n eventsOptions,\n );\n\n // Set up event listeners\n events.on('completed', async ({ jobId }) => {\n const job = await this.get(jobId);\n if (job) {\n await this.emitEvent('job.completed', job);\n }\n });\n\n events.on('failed', async ({ jobId, failedReason }) => {\n const job = await this.get(jobId);\n if (job) {\n job.lastError = failedReason;\n await this.emitEvent('job.failed', job, { error: failedReason });\n }\n });\n\n this.queues.set(queueName, { queue, events });\n }\n\n return this.queues.get(queueName)!;\n }\n\n /**\n * Convert BullMQ job to our Job format\n */\n private async bullMQJobToJob(\n bullMQJob: BullMQJob<BullMQJobData>,\n queueName: string,\n ): Promise<Job> {\n const data = bullMQJob.data;\n const state = await bullMQJob.getState();\n const status = this.mapBullMQStatus(state);\n\n return {\n id: bullMQJob.id ?? createId(),\n queue: queueName,\n payload: data.payload,\n status,\n priority: bullMQJob.opts.priority ?? 50,\n attempts: bullMQJob.attemptsMade,\n maxAttempts: data.meta.maxAttempts,\n runAt: bullMQJob.opts.delay\n ? new Date(bullMQJob.timestamp + bullMQJob.opts.delay)\n : new Date(bullMQJob.timestamp),\n startedAt: bullMQJob.processedOn ? new Date(bullMQJob.processedOn) : null,\n completedAt: bullMQJob.finishedOn ? new Date(bullMQJob.finishedOn) : null,\n timeout: data.meta.timeout,\n timeoutBehavior: data.meta.timeoutBehavior,\n lastError: bullMQJob.failedReason ?? null,\n resultPointer: bullMQJob.returnvalue?.resultPointer ?? null,\n retryStrategy: data.meta.retryStrategy,\n workerId: data.meta.workerId,\n workerHeartbeat: null,\n createdAt: new Date(bullMQJob.timestamp),\n updatedAt: new Date(),\n };\n }\n\n /**\n * Map BullMQ job state to our JobStatus\n */\n private mapBullMQStatus(\n state:\n | 'completed'\n | 'failed'\n | 'delayed'\n | 'active'\n | 'waiting'\n | 'waiting-children'\n | 'prioritized'\n | 'unknown',\n ): Job['status'] {\n switch (state) {\n case 'completed':\n return 'completed';\n case 'failed':\n return 'failed';\n case 'active':\n return 'running';\n case 'waiting':\n case 'delayed':\n case 'prioritized':\n case 'waiting-children':\n default:\n return 'pending';\n }\n }\n\n /**\n * Enqueue a new job\n */\n async enqueue(options: JobCreateOptions): Promise<Job> {\n if (!this.initialized) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n const queueName = options.queue ?? 'default';\n const { queue } = this.getQueueWrapper(queueName);\n\n const jobData: BullMQJobData = {\n payload: options.payload,\n meta: {\n maxAttempts: options.maxAttempts ?? 3,\n timeout: options.timeout ?? 300000,\n timeoutBehavior: options.timeoutBehavior ?? 'fail',\n retryStrategy:\n options.retryStrategy && 'toConfig' in options.retryStrategy\n ? options.retryStrategy.toConfig()\n : ((options.retryStrategy as Job['retryStrategy']) ?? {\n type: 'exponential',\n config: { initialDelay: 1000, multiplier: 2 },\n }),\n workerId: null,\n },\n };\n\n const bullMQJobOptions: BullMQJobOptions = {\n priority: priorityToNumber(options.priority),\n attempts: options.maxAttempts ?? 3,\n delay: options.runAt\n ? Math.max(0, options.runAt.getTime() - Date.now())\n : undefined,\n jobId: createId(),\n };\n\n const bullMQJob = await queue.add(\n options.payload.method,\n jobData,\n bullMQJobOptions,\n );\n const job = await this.bullMQJobToJob(bullMQJob, queueName);\n\n await this.emitEvent('job.created', job);\n return job;\n }\n\n /**\n * Dequeue jobs ready for processing\n */\n async dequeue(\n queues: string[],\n limit: number,\n workerId: string,\n ): Promise<Job[]> {\n if (!this.initialized) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n // BullMQ handles job claiming internally through its Worker\n // This method is primarily for compatibility with the interface\n\n const jobs: Job[] = [];\n\n for (const queueName of queues) {\n if (jobs.length >= limit) break;\n\n const { queue } = this.getQueueWrapper(queueName);\n const waiting = await queue.getWaiting(0, limit - jobs.length - 1);\n\n for (const bullMQJob of waiting) {\n // Update worker ID in job data\n bullMQJob.data.meta.workerId = workerId;\n await bullMQJob.updateData(bullMQJob.data);\n\n jobs.push(await this.bullMQJobToJob(bullMQJob, queueName));\n }\n }\n\n return jobs;\n }\n\n /**\n * Update a job\n */\n async update(id: string, updates: Partial<Job>): Promise<Job> {\n if (!this.initialized) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n // Find the job across queues\n for (const [queueName, { queue }] of this.queues) {\n const bullMQJob = await queue.getJob(id);\n if (bullMQJob) {\n // Update the job data\n const newData = { ...bullMQJob.data };\n if (updates.payload) {\n newData.payload = updates.payload;\n }\n if (updates.maxAttempts !== undefined) {\n newData.meta.maxAttempts = updates.maxAttempts;\n }\n if (updates.timeout !== undefined) {\n newData.meta.timeout = updates.timeout;\n }\n if (updates.workerId !== undefined) {\n newData.meta.workerId = updates.workerId;\n }\n\n await bullMQJob.updateData(newData);\n return await this.bullMQJobToJob(bullMQJob, queueName);\n }\n }\n\n throw new Error(`Job ${id} not found`);\n }\n\n /**\n * Get a job by ID\n */\n async get(id: string): Promise<Job | null> {\n if (!this.initialized) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n // Search across all queues\n for (const [queueName, { queue }] of this.queues) {\n const bullMQJob = await queue.getJob(id);\n if (bullMQJob) {\n return await this.bullMQJobToJob(bullMQJob, queueName);\n }\n }\n\n return null;\n }\n\n /**\n * List jobs with filtering\n */\n async list(filter: JobFilter): Promise<Job[]> {\n if (!this.initialized) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n const jobs: Job[] = [];\n const limit = filter.limit ?? 100;\n const offset = filter.offset ?? 0;\n\n const targetQueues = filter.queue\n ? [\n {\n name: filter.queue,\n wrapper: this.getQueueWrapper(filter.queue),\n },\n ]\n : Array.from(this.queues.entries()).map(([name, wrapper]) => ({\n name,\n wrapper,\n }));\n\n for (const { name, wrapper } of targetQueues) {\n const { queue } = wrapper;\n\n // Get jobs by status\n const statuses = filter.status\n ? Array.isArray(filter.status)\n ? filter.status\n : [filter.status]\n : ['pending', 'running', 'completed', 'failed'];\n\n for (const status of statuses) {\n let bullMQJobs: BullMQJob<BullMQJobData>[] = [];\n\n switch (status) {\n case 'pending':\n bullMQJobs = [\n ...(await queue.getWaiting(offset, offset + limit - 1)),\n ...(await queue.getDelayed(offset, offset + limit - 1)),\n ];\n break;\n case 'running':\n bullMQJobs = await queue.getActive(offset, offset + limit - 1);\n break;\n case 'completed':\n bullMQJobs = await queue.getCompleted(offset, offset + limit - 1);\n break;\n case 'failed':\n bullMQJobs = await queue.getFailed(offset, offset + limit - 1);\n break;\n }\n\n for (const bullMQJob of bullMQJobs) {\n const job = await this.bullMQJobToJob(bullMQJob, name);\n\n // Apply additional filters\n if (filter.objectType && job.payload.objectType !== filter.objectType)\n continue;\n if (filter.method && job.payload.method !== filter.method) continue;\n if (filter.createdAfter && job.createdAt < filter.createdAfter)\n continue;\n if (filter.createdBefore && job.createdAt > filter.createdBefore)\n continue;\n\n jobs.push(job);\n\n if (jobs.length >= limit) break;\n }\n\n if (jobs.length >= limit) break;\n }\n }\n\n return jobs.slice(0, limit);\n }\n\n /**\n * Cancel a job\n */\n async cancel(id: string): Promise<void> {\n if (!this.initialized) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n for (const [queueName, { queue }] of this.queues) {\n const bullMQJob = await queue.getJob(id);\n if (bullMQJob) {\n await bullMQJob.remove();\n const job = await this.bullMQJobToJob(bullMQJob, queueName);\n job.status = 'cancelled';\n await this.emitEvent('job.cancelled', job);\n return;\n }\n }\n\n throw new Error(`Job ${id} not found`);\n }\n\n /**\n * Clean up old jobs\n */\n async cleanup(options: CleanupOptions): Promise<number> {\n if (!this.initialized) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n let cleaned = 0;\n\n for (const { queue } of this.queues.values()) {\n if (options.completedBefore) {\n const grace = Date.now() - options.completedBefore.getTime();\n const result = await queue.clean(\n grace,\n options.limit ?? 1000,\n 'completed',\n );\n cleaned += result.length;\n }\n\n if (options.failedBefore) {\n const grace = Date.now() - options.failedBefore.getTime();\n const result = await queue.clean(\n grace,\n options.limit ?? 1000,\n 'failed',\n );\n cleaned += result.length;\n }\n }\n\n return cleaned;\n }\n\n /**\n * Update worker heartbeat (no-op for BullMQ - handled internally)\n */\n async heartbeat(_jobId: string, _workerId: string): Promise<void> {\n // BullMQ handles heartbeat/stalled job detection internally\n }\n\n /**\n * Get queue statistics\n */\n async stats(queue?: string): Promise<QueueStats> {\n if (!this.initialized) {\n throw new Error('BullMQJobStore not initialized');\n }\n\n const totals: QueueStats = {\n pending: 0,\n running: 0,\n completed: 0,\n failed: 0,\n cancelled: 0,\n avgDuration: null,\n };\n\n const targetQueues = queue\n ? [this.getQueueWrapper(queue).queue]\n : Array.from(this.queues.values()).map((w) => w.queue);\n\n for (const q of targetQueues) {\n const counts = await q.getJobCounts(\n 'waiting',\n 'active',\n 'completed',\n 'failed',\n 'delayed',\n );\n totals.pending += counts.waiting + counts.delayed;\n totals.running += counts.active;\n totals.completed += counts.completed;\n totals.failed += counts.failed;\n }\n\n return totals;\n }\n\n /**\n * Close all queues\n */\n async close(): Promise<void> {\n for (const { queue, events } of this.queues.values()) {\n await events.close();\n await queue.close();\n }\n this.queues.clear();\n this.initialized = false;\n }\n}\n\n/**\n * Create a BullMQ job store instance\n */\nexport function createBullMQJobStore(\n config?: BullMQJobStoreConfig,\n): BullMQJobStore {\n return new BullMQJobStore(config);\n}\n\nexport default BullMQJobStore;\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { BaseJobStore } from '../base-store.js';
|
|
2
|
+
import { CleanupOptions, Job, JobCreateOptions, JobFilter, QueueStats } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Cloud Tasks adapter configuration
|
|
5
|
+
*/
|
|
6
|
+
export interface CloudTasksJobStoreConfig {
|
|
7
|
+
/** GCP project ID */
|
|
8
|
+
projectId: string;
|
|
9
|
+
/** GCP location (e.g., 'us-central1') */
|
|
10
|
+
location: string;
|
|
11
|
+
/** Queue name prefix */
|
|
12
|
+
queuePrefix?: string;
|
|
13
|
+
/** HTTP handler URL for job execution */
|
|
14
|
+
handlerUrl: string;
|
|
15
|
+
/** Service account email for OIDC authentication */
|
|
16
|
+
serviceAccountEmail?: string;
|
|
17
|
+
/** Default retry configuration */
|
|
18
|
+
defaultRetry?: {
|
|
19
|
+
maxAttempts?: number;
|
|
20
|
+
minBackoff?: string;
|
|
21
|
+
maxBackoff?: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Cloud Tasks-based job store implementation
|
|
26
|
+
*
|
|
27
|
+
* Note: Cloud Tasks operates differently from traditional job queues:
|
|
28
|
+
* - Jobs are HTTP tasks sent to your handler URL
|
|
29
|
+
* - No pull-based dequeue (Cloud Tasks pushes to your handler)
|
|
30
|
+
* - Updates are limited (tasks can be deleted but not modified)
|
|
31
|
+
*/
|
|
32
|
+
export declare class CloudTasksJobStore extends BaseJobStore {
|
|
33
|
+
private config;
|
|
34
|
+
private client;
|
|
35
|
+
private cloudTasksModule;
|
|
36
|
+
private jobStates;
|
|
37
|
+
constructor(config: CloudTasksJobStoreConfig);
|
|
38
|
+
/**
|
|
39
|
+
* Initialize the store - dynamically imports Google Cloud Tasks
|
|
40
|
+
*/
|
|
41
|
+
initialize(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Get the full queue path
|
|
44
|
+
*/
|
|
45
|
+
private getQueuePath;
|
|
46
|
+
/**
|
|
47
|
+
* Get the full task path
|
|
48
|
+
*/
|
|
49
|
+
private getTaskPath;
|
|
50
|
+
/**
|
|
51
|
+
* Enqueue a new job
|
|
52
|
+
*/
|
|
53
|
+
enqueue(options: JobCreateOptions): Promise<Job>;
|
|
54
|
+
/**
|
|
55
|
+
* Dequeue jobs - NOT SUPPORTED in Cloud Tasks
|
|
56
|
+
* Cloud Tasks is push-based; jobs are delivered to your handler URL
|
|
57
|
+
*/
|
|
58
|
+
dequeue(_queues: string[], _limit: number, _workerId: string): Promise<Job[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Update a job - LIMITED SUPPORT in Cloud Tasks
|
|
61
|
+
*/
|
|
62
|
+
update(_id: string, _updates: Partial<Job>): Promise<Job>;
|
|
63
|
+
/**
|
|
64
|
+
* Get a job by ID
|
|
65
|
+
*/
|
|
66
|
+
get(id: string): Promise<Job | null>;
|
|
67
|
+
/**
|
|
68
|
+
* List jobs with filtering
|
|
69
|
+
* Note: Only returns jobs from in-memory state
|
|
70
|
+
*/
|
|
71
|
+
list(filter: JobFilter): Promise<Job[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Cancel a job by deleting its Cloud Task
|
|
74
|
+
*/
|
|
75
|
+
cancel(id: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Mark job as started (called by your handler when it receives the job)
|
|
78
|
+
*/
|
|
79
|
+
markStarted(id: string, workerId: string): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Mark job as completed (called by your handler after successful execution)
|
|
82
|
+
*/
|
|
83
|
+
markCompleted(id: string, resultPointer?: string): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Mark job as failed (called by your handler after failed execution)
|
|
86
|
+
*/
|
|
87
|
+
markFailed(id: string, error: string): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Clean up old jobs from in-memory state
|
|
90
|
+
*/
|
|
91
|
+
cleanup(options: CleanupOptions): Promise<number>;
|
|
92
|
+
/**
|
|
93
|
+
* Update worker heartbeat - stored in memory only
|
|
94
|
+
*/
|
|
95
|
+
heartbeat(jobId: string, _workerId: string): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Get queue statistics from in-memory state
|
|
98
|
+
*/
|
|
99
|
+
stats(queue?: string): Promise<QueueStats>;
|
|
100
|
+
/**
|
|
101
|
+
* Close the Cloud Tasks client
|
|
102
|
+
*/
|
|
103
|
+
close(): Promise<void>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create a Cloud Tasks job store instance
|
|
107
|
+
*/
|
|
108
|
+
export declare function createCloudTasksJobStore(config: CloudTasksJobStoreConfig): CloudTasksJobStore;
|
|
109
|
+
export default CloudTasksJobStore;
|
|
110
|
+
//# sourceMappingURL=cloud-tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-tasks.d.ts","sourceRoot":"","sources":["../../src/adapters/cloud-tasks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,YAAY,EAAoB,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EACV,cAAc,EACd,GAAG,EACH,gBAAgB,EAChB,SAAS,EACT,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kCAAkC;IAClC,YAAY,CAAC,EAAE;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAyBD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAClD,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,MAAM,CAAqC;IAEnD,OAAO,CAAC,gBAAgB,CAAqD;IAC7E,OAAO,CAAC,SAAS,CAAoC;gBAEzC,MAAM,EAAE,wBAAwB;IAa5C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBjC;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC;IAmGtD;;;OAGG;IACG,OAAO,CACX,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,EAAE,CAAC;IAQjB;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAO/D;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAW1C;;;OAGG;IACG,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IA6B7C;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BvC;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc9D;;OAEG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAatE;;OAEG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1D;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IA6CvD;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhE;;OAEG;IACG,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAmChD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,kBAAkB,CAEpB;AAED,eAAe,kBAAkB,CAAC"}
|