@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.
Files changed (46) hide show
  1. package/AGENT.md +33 -0
  2. package/LICENSE +7 -0
  3. package/dist/adapters/bull.d.ts +103 -0
  4. package/dist/adapters/bull.d.ts.map +1 -0
  5. package/dist/adapters/bull.js +349 -0
  6. package/dist/adapters/bull.js.map +1 -0
  7. package/dist/adapters/bullmq.d.ts +85 -0
  8. package/dist/adapters/bullmq.d.ts.map +1 -0
  9. package/dist/adapters/bullmq.js +391 -0
  10. package/dist/adapters/bullmq.js.map +1 -0
  11. package/dist/adapters/cloud-tasks.d.ts +110 -0
  12. package/dist/adapters/cloud-tasks.d.ts.map +1 -0
  13. package/dist/adapters/cloud-tasks.js +336 -0
  14. package/dist/adapters/cloud-tasks.js.map +1 -0
  15. package/dist/adapters/postgres.d.ts +55 -0
  16. package/dist/adapters/postgres.d.ts.map +1 -0
  17. package/dist/adapters/postgres.js +437 -0
  18. package/dist/adapters/postgres.js.map +1 -0
  19. package/dist/adapters/sqlite.d.ts +44 -0
  20. package/dist/adapters/sqlite.d.ts.map +1 -0
  21. package/dist/adapters/sqlite.js +323 -0
  22. package/dist/adapters/sqlite.js.map +1 -0
  23. package/dist/adapters/sqs.d.ts +112 -0
  24. package/dist/adapters/sqs.d.ts.map +1 -0
  25. package/dist/adapters/sqs.js +411 -0
  26. package/dist/adapters/sqs.js.map +1 -0
  27. package/dist/base-store.d.ts +69 -0
  28. package/dist/base-store.d.ts.map +1 -0
  29. package/dist/chunks/base-store-DlNksWvQ.js +324 -0
  30. package/dist/chunks/base-store-DlNksWvQ.js.map +1 -0
  31. package/dist/cli/claude-context.d.ts +3 -0
  32. package/dist/cli/claude-context.d.ts.map +1 -0
  33. package/dist/cli/claude-context.js +21 -0
  34. package/dist/cli/claude-context.js.map +1 -0
  35. package/dist/index.d.ts +16 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +252 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/retry.d.ts +84 -0
  40. package/dist/retry.d.ts.map +1 -0
  41. package/dist/types.d.ts +311 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/worker.d.ts +74 -0
  44. package/dist/worker.d.ts.map +1 -0
  45. package/metadata.json +34 -0
  46. package/package.json +114 -0
package/AGENT.md ADDED
@@ -0,0 +1,33 @@
1
+ # @happyvertical/jobs
2
+
3
+ <!-- BEGIN AGENT:GENERATED -->
4
+ ## Purpose
5
+ Job queue abstraction with multiple backend adapters (SQLite, PostgreSQL, Bull, SQS)
6
+
7
+ ## Package Map
8
+ - Package: `@happyvertical/jobs`
9
+ - Hierarchy path: `@happyvertical/sdk > packages > jobs`
10
+ - Workspace position: `16 of 30` local packages
11
+ - Internal dependencies: `@happyvertical/sql`, `@happyvertical/utils`
12
+ - Internal dependents: none
13
+ - Knowledge graph files: `AGENT.md`, `metadata.json`, `ecosystem-manifest.json`
14
+
15
+ ## Build & Test
16
+ ```bash
17
+ pnpm --filter @happyvertical/jobs build
18
+ pnpm --filter @happyvertical/jobs test
19
+ pnpm --filter @happyvertical/jobs clean
20
+ ```
21
+
22
+ ## Agent Correction Loops
23
+ - If module resolution or export errors mention a workspace dependency, build the dependency first (`pnpm --filter @happyvertical/sql build`, `pnpm --filter @happyvertical/utils build`) and then rerun `pnpm --filter @happyvertical/jobs build`.
24
+ - If tests or exports fail after API, type, or bundle changes, run `pnpm --filter @happyvertical/jobs clean` followed by `pnpm --filter @happyvertical/jobs build` and `pnpm --filter @happyvertical/jobs test`.
25
+ - If failures span multiple packages or Turborepo ordering looks wrong, run `pnpm build` and `pnpm typecheck` from the repo root before retrying package-scoped commands.
26
+
27
+ ## Ecosystem Relationships
28
+ - Provides: Job queue abstraction with multiple backend adapters (SQLite, PostgreSQL, Bull, SQS)
29
+ - Implements: none
30
+ - Requires: @happyvertical/sql, @happyvertical/utils, @aws-sdk/client-sqs, @google-cloud/tasks, bull, bullmq
31
+ - Stability: stable (Primary package surface is described as implemented and production-oriented.)
32
+ <!-- END AGENT:GENERATED -->
33
+
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright <2025> <Happy Vertical Corporation>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,103 @@
1
+ import { JobOptions as BullJobOptions } from 'bull';
2
+ import { BaseJobStore } from '../base-store.js';
3
+ import { CleanupOptions, Job, JobCreateOptions, JobFilter, QueueStats } from '../types.js';
4
+ /**
5
+ * Redis connection options
6
+ */
7
+ export interface RedisOptions {
8
+ host?: string;
9
+ port?: number;
10
+ password?: string;
11
+ db?: number;
12
+ url?: string;
13
+ }
14
+ /**
15
+ * Bull adapter configuration
16
+ */
17
+ export interface BullJobStoreConfig {
18
+ /** Redis connection options */
19
+ redis?: RedisOptions;
20
+ /** Default queue name prefix */
21
+ prefix?: string;
22
+ /** Default job options */
23
+ defaultJobOptions?: BullJobOptions;
24
+ }
25
+ /**
26
+ * Bull-based job store implementation
27
+ */
28
+ export declare class BullJobStore extends BaseJobStore {
29
+ private config;
30
+ private queues;
31
+ private bullModule;
32
+ constructor(config?: BullJobStoreConfig);
33
+ /**
34
+ * Initialize the store - dynamically imports Bull
35
+ */
36
+ initialize(): Promise<void>;
37
+ /**
38
+ * Get or create a Bull queue for a queue name
39
+ */
40
+ private getQueue;
41
+ /**
42
+ * Handle Bull job completed event
43
+ */
44
+ private handleJobCompleted;
45
+ /**
46
+ * Handle Bull job failed event
47
+ */
48
+ private handleJobFailed;
49
+ /**
50
+ * Convert Bull job to our Job format
51
+ */
52
+ private bullJobToJob;
53
+ /**
54
+ * Map Bull job state to our JobStatus
55
+ */
56
+ private mapBullStatus;
57
+ /**
58
+ * Enqueue a new job
59
+ */
60
+ enqueue(options: JobCreateOptions): Promise<Job>;
61
+ /**
62
+ * Dequeue jobs ready for processing
63
+ */
64
+ dequeue(queues: string[], limit: number, workerId: string): Promise<Job[]>;
65
+ /**
66
+ * Update a job
67
+ */
68
+ update(id: string, updates: Partial<Job>): Promise<Job>;
69
+ /**
70
+ * Get a job by ID
71
+ */
72
+ get(id: string): Promise<Job | null>;
73
+ /**
74
+ * List jobs with filtering
75
+ */
76
+ list(filter: JobFilter): Promise<Job[]>;
77
+ /**
78
+ * Cancel a job
79
+ */
80
+ cancel(id: string): Promise<void>;
81
+ /**
82
+ * Clean up old jobs
83
+ */
84
+ cleanup(options: CleanupOptions): Promise<number>;
85
+ /**
86
+ * Update worker heartbeat (no-op for Bull - handled internally)
87
+ */
88
+ heartbeat(_jobId: string, _workerId: string): Promise<void>;
89
+ /**
90
+ * Get queue statistics
91
+ */
92
+ stats(queue?: string): Promise<QueueStats>;
93
+ /**
94
+ * Close all queues
95
+ */
96
+ close(): Promise<void>;
97
+ }
98
+ /**
99
+ * Create a Bull job store instance
100
+ */
101
+ export declare function createBullJobStore(config?: BullJobStoreConfig): BullJobStore;
102
+ export default BullJobStore;
103
+ //# sourceMappingURL=bull.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bull.d.ts","sourceRoot":"","sources":["../../src/adapters/bull.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAEV,UAAU,IAAI,cAAc,EAE7B,MAAM,MAAM,CAAC;AACd,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,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,iBAAiB,CAAC,EAAE,cAAc,CAAC;CACpC;AAkBD;;GAEG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,MAAM,CAAkD;IAEhE,OAAO,CAAC,UAAU,CAAsC;gBAE5C,MAAM,GAAE,kBAAuB;IAY3C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC;;OAEG;IACH,OAAO,CAAC,QAAQ;IA+BhB;;OAEG;YACW,kBAAkB;IAQhC;;OAEG;YACW,eAAe;IAU7B;;OAEG;IACH,OAAO,CAAC,YAAY;IA6BpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC;IA0CtD;;OAEG;IACG,OAAO,CACX,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,GAAG,EAAE,CAAC;IA6BjB;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IA+B7D;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAgB1C;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAmE7C;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBvC;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBvD;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE;;OAEG;IACG,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA6BhD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO7B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,YAAY,CAE5E;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,349 @@
1
+ import { createId } from "@happyvertical/utils";
2
+ import { B as BaseJobStore, p as priorityToNumber } from "../chunks/base-store-DlNksWvQ.js";
3
+ class BullJobStore extends BaseJobStore {
4
+ config;
5
+ queues = /* @__PURE__ */ new Map();
6
+ // biome-ignore lint/style/useNamingConvention: bull module reference
7
+ bullModule = 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 Bull
21
+ */
22
+ async initialize() {
23
+ if (this.initialized) return;
24
+ try {
25
+ this.bullModule = (await import("bull")).default;
26
+ } catch {
27
+ throw new Error(
28
+ "Bull is required for BullJobStore. Install it with: npm install bull"
29
+ );
30
+ }
31
+ this.initialized = true;
32
+ }
33
+ /**
34
+ * Get or create a Bull queue for a queue name
35
+ */
36
+ getQueue(queueName) {
37
+ if (!this.bullModule) {
38
+ throw new Error("BullJobStore not initialized");
39
+ }
40
+ if (!this.queues.has(queueName)) {
41
+ const queue = new this.bullModule(queueName, {
42
+ redis: this.config.redis,
43
+ prefix: this.config.prefix,
44
+ defaultJobOptions: this.config.defaultJobOptions
45
+ });
46
+ queue.on("completed", (bullJob) => {
47
+ this.handleJobCompleted(bullJob, queueName);
48
+ });
49
+ queue.on("failed", (bullJob, error) => {
50
+ this.handleJobFailed(bullJob, error, queueName);
51
+ });
52
+ this.queues.set(queueName, queue);
53
+ }
54
+ const result = this.queues.get(queueName);
55
+ if (!result) {
56
+ throw new Error(`Queue ${queueName} was not found after initialization`);
57
+ }
58
+ return result;
59
+ }
60
+ /**
61
+ * Handle Bull job completed event
62
+ */
63
+ async handleJobCompleted(bullJob, queueName) {
64
+ const job = this.bullJobToJob(bullJob, queueName);
65
+ await this.emitEvent("job.completed", job);
66
+ }
67
+ /**
68
+ * Handle Bull job failed event
69
+ */
70
+ async handleJobFailed(bullJob, error, queueName) {
71
+ const job = this.bullJobToJob(bullJob, queueName);
72
+ job.lastError = error.message;
73
+ await this.emitEvent("job.failed", job, { error: error.message });
74
+ }
75
+ /**
76
+ * Convert Bull job to our Job format
77
+ */
78
+ bullJobToJob(bullJob, queueName) {
79
+ const data = bullJob.data;
80
+ const status = this.mapBullStatus(bullJob);
81
+ return {
82
+ id: String(bullJob.id),
83
+ queue: queueName,
84
+ payload: data.payload,
85
+ status,
86
+ priority: bullJob.opts.priority ?? 50,
87
+ attempts: bullJob.attemptsMade,
88
+ maxAttempts: data.meta.maxAttempts,
89
+ runAt: bullJob.opts.delay ? new Date(bullJob.timestamp + bullJob.opts.delay) : new Date(bullJob.timestamp),
90
+ startedAt: bullJob.processedOn ? new Date(bullJob.processedOn) : null,
91
+ completedAt: bullJob.finishedOn ? new Date(bullJob.finishedOn) : null,
92
+ timeout: data.meta.timeout,
93
+ timeoutBehavior: data.meta.timeoutBehavior,
94
+ lastError: bullJob.failedReason ?? null,
95
+ resultPointer: bullJob.returnvalue?.resultPointer ?? null,
96
+ retryStrategy: data.meta.retryStrategy,
97
+ workerId: data.meta.workerId,
98
+ workerHeartbeat: null,
99
+ createdAt: new Date(bullJob.timestamp),
100
+ updatedAt: /* @__PURE__ */ new Date()
101
+ };
102
+ }
103
+ /**
104
+ * Map Bull job state to our JobStatus
105
+ */
106
+ mapBullStatus(bullJob) {
107
+ if (bullJob.finishedOn) {
108
+ return bullJob.failedReason ? "failed" : "completed";
109
+ }
110
+ if (bullJob.processedOn) {
111
+ return "running";
112
+ }
113
+ return "pending";
114
+ }
115
+ /**
116
+ * Enqueue a new job
117
+ */
118
+ async enqueue(options) {
119
+ if (!this.initialized) {
120
+ throw new Error("BullJobStore not initialized");
121
+ }
122
+ const queueName = options.queue ?? "default";
123
+ const queue = this.getQueue(queueName);
124
+ const jobData = {
125
+ payload: options.payload,
126
+ meta: {
127
+ maxAttempts: options.maxAttempts ?? 3,
128
+ timeout: options.timeout ?? 3e5,
129
+ timeoutBehavior: options.timeoutBehavior ?? "fail",
130
+ retryStrategy: options.retryStrategy && "toConfig" in options.retryStrategy ? options.retryStrategy.toConfig() : options.retryStrategy ?? {
131
+ type: "exponential",
132
+ config: { initialDelay: 1e3, multiplier: 2 }
133
+ },
134
+ workerId: null
135
+ }
136
+ };
137
+ const bullJobOptions = {
138
+ priority: priorityToNumber(options.priority),
139
+ attempts: options.maxAttempts ?? 3,
140
+ timeout: options.timeout ?? 3e5,
141
+ delay: options.runAt ? Math.max(0, options.runAt.getTime() - Date.now()) : void 0,
142
+ jobId: createId()
143
+ };
144
+ const bullJob = await queue.add(jobData, bullJobOptions);
145
+ const job = this.bullJobToJob(bullJob, queueName);
146
+ await this.emitEvent("job.created", job);
147
+ return job;
148
+ }
149
+ /**
150
+ * Dequeue jobs ready for processing
151
+ */
152
+ async dequeue(queues, limit, workerId) {
153
+ if (!this.initialized) {
154
+ throw new Error("BullJobStore not initialized");
155
+ }
156
+ const jobs = [];
157
+ for (const queueName of queues) {
158
+ if (jobs.length >= limit) break;
159
+ const queue = this.getQueue(queueName);
160
+ const waiting = await queue.getWaiting(0, limit - jobs.length - 1);
161
+ for (const bullJob of waiting) {
162
+ bullJob.data.meta.workerId = workerId;
163
+ await bullJob.update(bullJob.data);
164
+ jobs.push(this.bullJobToJob(bullJob, queueName));
165
+ }
166
+ }
167
+ return jobs;
168
+ }
169
+ /**
170
+ * Update a job
171
+ */
172
+ async update(id, updates) {
173
+ if (!this.initialized) {
174
+ throw new Error("BullJobStore not initialized");
175
+ }
176
+ for (const [queueName, queue] of this.queues) {
177
+ const bullJob = await queue.getJob(id);
178
+ if (bullJob) {
179
+ if (updates.payload) {
180
+ bullJob.data.payload = updates.payload;
181
+ }
182
+ if (updates.maxAttempts !== void 0) {
183
+ bullJob.data.meta.maxAttempts = updates.maxAttempts;
184
+ }
185
+ if (updates.timeout !== void 0) {
186
+ bullJob.data.meta.timeout = updates.timeout;
187
+ }
188
+ if (updates.workerId !== void 0) {
189
+ bullJob.data.meta.workerId = updates.workerId;
190
+ }
191
+ await bullJob.update(bullJob.data);
192
+ return this.bullJobToJob(bullJob, queueName);
193
+ }
194
+ }
195
+ throw new Error(`Job ${id} not found`);
196
+ }
197
+ /**
198
+ * Get a job by ID
199
+ */
200
+ async get(id) {
201
+ if (!this.initialized) {
202
+ throw new Error("BullJobStore not initialized");
203
+ }
204
+ for (const [queueName, queue] of this.queues) {
205
+ const bullJob = await queue.getJob(id);
206
+ if (bullJob) {
207
+ return this.bullJobToJob(bullJob, queueName);
208
+ }
209
+ }
210
+ return null;
211
+ }
212
+ /**
213
+ * List jobs with filtering
214
+ */
215
+ async list(filter) {
216
+ if (!this.initialized) {
217
+ throw new Error("BullJobStore not initialized");
218
+ }
219
+ const jobs = [];
220
+ const limit = filter.limit ?? 100;
221
+ const offset = filter.offset ?? 0;
222
+ const targetQueues = filter.queue ? [this.getQueue(filter.queue)] : Array.from(this.queues.values());
223
+ for (const queue of targetQueues) {
224
+ const queueName = Array.from(this.queues.entries()).find(([, q]) => q === queue)?.[0] ?? "unknown";
225
+ const statuses = filter.status ? Array.isArray(filter.status) ? filter.status : [filter.status] : ["pending", "running", "completed", "failed"];
226
+ for (const status of statuses) {
227
+ let bullJobs = [];
228
+ switch (status) {
229
+ case "pending":
230
+ bullJobs = await queue.getWaiting(offset, offset + limit - 1);
231
+ break;
232
+ case "running":
233
+ bullJobs = await queue.getActive(offset, offset + limit - 1);
234
+ break;
235
+ case "completed":
236
+ bullJobs = await queue.getCompleted(offset, offset + limit - 1);
237
+ break;
238
+ case "failed":
239
+ bullJobs = await queue.getFailed(offset, offset + limit - 1);
240
+ break;
241
+ }
242
+ for (const bullJob of bullJobs) {
243
+ const job = this.bullJobToJob(bullJob, queueName);
244
+ if (filter.objectType && job.payload.objectType !== filter.objectType)
245
+ continue;
246
+ if (filter.method && job.payload.method !== filter.method) continue;
247
+ if (filter.createdAfter && job.createdAt < filter.createdAfter)
248
+ continue;
249
+ if (filter.createdBefore && job.createdAt > filter.createdBefore)
250
+ continue;
251
+ jobs.push(job);
252
+ if (jobs.length >= limit) break;
253
+ }
254
+ if (jobs.length >= limit) break;
255
+ }
256
+ }
257
+ return jobs.slice(0, limit);
258
+ }
259
+ /**
260
+ * Cancel a job
261
+ */
262
+ async cancel(id) {
263
+ if (!this.initialized) {
264
+ throw new Error("BullJobStore not initialized");
265
+ }
266
+ for (const [queueName, queue] of this.queues) {
267
+ const bullJob = await queue.getJob(id);
268
+ if (bullJob) {
269
+ await bullJob.remove();
270
+ const job = this.bullJobToJob(bullJob, queueName);
271
+ job.status = "cancelled";
272
+ await this.emitEvent("job.cancelled", job);
273
+ return;
274
+ }
275
+ }
276
+ throw new Error(`Job ${id} not found`);
277
+ }
278
+ /**
279
+ * Clean up old jobs
280
+ */
281
+ async cleanup(options) {
282
+ if (!this.initialized) {
283
+ throw new Error("BullJobStore not initialized");
284
+ }
285
+ let cleaned = 0;
286
+ for (const queue of this.queues.values()) {
287
+ if (options.completedBefore) {
288
+ const grace = Date.now() - options.completedBefore.getTime();
289
+ const result = await queue.clean(grace, "completed", options.limit);
290
+ cleaned += result.length;
291
+ }
292
+ if (options.failedBefore) {
293
+ const grace = Date.now() - options.failedBefore.getTime();
294
+ const result = await queue.clean(grace, "failed", options.limit);
295
+ cleaned += result.length;
296
+ }
297
+ }
298
+ return cleaned;
299
+ }
300
+ /**
301
+ * Update worker heartbeat (no-op for Bull - handled internally)
302
+ */
303
+ async heartbeat(_jobId, _workerId) {
304
+ }
305
+ /**
306
+ * Get queue statistics
307
+ */
308
+ async stats(queue) {
309
+ if (!this.initialized) {
310
+ throw new Error("BullJobStore not initialized");
311
+ }
312
+ const totals = {
313
+ pending: 0,
314
+ running: 0,
315
+ completed: 0,
316
+ failed: 0,
317
+ cancelled: 0,
318
+ avgDuration: null
319
+ };
320
+ const targetQueues = queue ? [this.getQueue(queue)] : Array.from(this.queues.values());
321
+ for (const q of targetQueues) {
322
+ const counts = await q.getJobCounts();
323
+ totals.pending += counts.waiting + counts.delayed;
324
+ totals.running += counts.active;
325
+ totals.completed += counts.completed;
326
+ totals.failed += counts.failed;
327
+ }
328
+ return totals;
329
+ }
330
+ /**
331
+ * Close all queues
332
+ */
333
+ async close() {
334
+ for (const queue of this.queues.values()) {
335
+ await queue.close();
336
+ }
337
+ this.queues.clear();
338
+ this.initialized = false;
339
+ }
340
+ }
341
+ function createBullJobStore(config) {
342
+ return new BullJobStore(config);
343
+ }
344
+ export {
345
+ BullJobStore,
346
+ createBullJobStore,
347
+ BullJobStore as default
348
+ };
349
+ //# sourceMappingURL=bull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bull.js","sources":["../../src/adapters/bull.ts"],"sourcesContent":["/**\n * Bull (Redis) Job Store Adapter\n *\n * Uses Bull queue library for Redis-based job storage with real-time updates.\n * Bull provides robust job processing with features like priority queues,\n * delayed jobs, retries, and job events.\n *\n * @example\n * ```typescript\n * import { BullJobStore } from '@happyvertical/jobs/adapters/bull';\n *\n * const store = new BullJobStore({\n * redis: {\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 `bull` package as a peer dependency.\n * Install it with: npm install bull @types/bull\n */\n\nimport { createId } from '@happyvertical/utils';\nimport type {\n Job as BullJob,\n JobOptions as BullJobOptions,\n Queue as BullQueue,\n} from 'bull';\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 * Redis connection options\n */\nexport interface RedisOptions {\n host?: string;\n port?: number;\n password?: string;\n db?: number;\n url?: string;\n}\n\n/**\n * Bull adapter configuration\n */\nexport interface BullJobStoreConfig {\n /** Redis connection options */\n redis?: RedisOptions;\n /** Default queue name prefix */\n prefix?: string;\n /** Default job options */\n defaultJobOptions?: BullJobOptions;\n}\n\n/**\n * Job data stored in Bull\n */\ninterface BullJobData {\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 * Bull-based job store implementation\n */\nexport class BullJobStore extends BaseJobStore {\n private config: BullJobStoreConfig;\n private queues: Map<string, BullQueue<BullJobData>> = new Map();\n // biome-ignore lint/style/useNamingConvention: bull module reference\n private bullModule: typeof import('bull') | null = null;\n\n constructor(config: BullJobStoreConfig = {}) {\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 Bull\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n try {\n // Dynamic import to avoid requiring bull as a hard dependency\n this.bullModule = (await import('bull')).default;\n } catch {\n throw new Error(\n 'Bull is required for BullJobStore. Install it with: npm install bull',\n );\n }\n\n this.initialized = true;\n }\n\n /**\n * Get or create a Bull queue for a queue name\n */\n private getQueue(queueName: string): BullQueue<BullJobData> {\n if (!this.bullModule) {\n throw new Error('BullJobStore not initialized');\n }\n\n if (!this.queues.has(queueName)) {\n const queue = new this.bullModule<BullJobData>(queueName, {\n redis: this.config.redis,\n prefix: this.config.prefix,\n defaultJobOptions: this.config.defaultJobOptions,\n });\n\n // Set up event listeners\n queue.on('completed', (bullJob) => {\n this.handleJobCompleted(bullJob, queueName);\n });\n\n queue.on('failed', (bullJob, error) => {\n this.handleJobFailed(bullJob, error, queueName);\n });\n\n this.queues.set(queueName, queue);\n }\n\n const result = this.queues.get(queueName);\n if (!result) {\n throw new Error(`Queue ${queueName} was not found after initialization`);\n }\n return result;\n }\n\n /**\n * Handle Bull job completed event\n */\n private async handleJobCompleted(\n bullJob: BullJob<BullJobData>,\n queueName: string,\n ): Promise<void> {\n const job = this.bullJobToJob(bullJob, queueName);\n await this.emitEvent('job.completed', job);\n }\n\n /**\n * Handle Bull job failed event\n */\n private async handleJobFailed(\n bullJob: BullJob<BullJobData>,\n error: Error,\n queueName: string,\n ): Promise<void> {\n const job = this.bullJobToJob(bullJob, queueName);\n job.lastError = error.message;\n await this.emitEvent('job.failed', job, { error: error.message });\n }\n\n /**\n * Convert Bull job to our Job format\n */\n private bullJobToJob(bullJob: BullJob<BullJobData>, queueName: string): Job {\n const data = bullJob.data;\n const status = this.mapBullStatus(bullJob);\n\n return {\n id: String(bullJob.id),\n queue: queueName,\n payload: data.payload,\n status,\n priority: bullJob.opts.priority ?? 50,\n attempts: bullJob.attemptsMade,\n maxAttempts: data.meta.maxAttempts,\n runAt: bullJob.opts.delay\n ? new Date(bullJob.timestamp + bullJob.opts.delay)\n : new Date(bullJob.timestamp),\n startedAt: bullJob.processedOn ? new Date(bullJob.processedOn) : null,\n completedAt: bullJob.finishedOn ? new Date(bullJob.finishedOn) : null,\n timeout: data.meta.timeout,\n timeoutBehavior: data.meta.timeoutBehavior,\n lastError: bullJob.failedReason ?? null,\n resultPointer: bullJob.returnvalue?.resultPointer ?? null,\n retryStrategy: data.meta.retryStrategy,\n workerId: data.meta.workerId,\n workerHeartbeat: null,\n createdAt: new Date(bullJob.timestamp),\n updatedAt: new Date(),\n };\n }\n\n /**\n * Map Bull job state to our JobStatus\n */\n private mapBullStatus(bullJob: BullJob<BullJobData>): Job['status'] {\n // Bull doesn't expose state directly, so we check various properties\n if (bullJob.finishedOn) {\n return bullJob.failedReason ? 'failed' : 'completed';\n }\n if (bullJob.processedOn) {\n return 'running';\n }\n return 'pending';\n }\n\n /**\n * Enqueue a new job\n */\n async enqueue(options: JobCreateOptions): Promise<Job> {\n if (!this.initialized) {\n throw new Error('BullJobStore not initialized');\n }\n\n const queueName = options.queue ?? 'default';\n const queue = this.getQueue(queueName);\n\n const jobData: BullJobData = {\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 bullJobOptions: BullJobOptions = {\n priority: priorityToNumber(options.priority),\n attempts: options.maxAttempts ?? 3,\n timeout: options.timeout ?? 300000,\n delay: options.runAt\n ? Math.max(0, options.runAt.getTime() - Date.now())\n : undefined,\n jobId: createId(),\n };\n\n const bullJob = await queue.add(jobData, bullJobOptions);\n const job = this.bullJobToJob(bullJob, 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('BullJobStore not initialized');\n }\n\n // Bull handles job claiming internally through its processor\n // This method is primarily for compatibility with the interface\n // In Bull, you would use queue.process() instead\n\n const jobs: Job[] = [];\n\n for (const queueName of queues) {\n if (jobs.length >= limit) break;\n\n const queue = this.getQueue(queueName);\n const waiting = await queue.getWaiting(0, limit - jobs.length - 1);\n\n for (const bullJob of waiting) {\n // Update worker ID\n bullJob.data.meta.workerId = workerId;\n await bullJob.update(bullJob.data);\n\n jobs.push(this.bullJobToJob(bullJob, 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('BullJobStore not initialized');\n }\n\n // Find the job across queues\n for (const [queueName, queue] of this.queues) {\n const bullJob = await queue.getJob(id);\n if (bullJob) {\n // Update the job data\n if (updates.payload) {\n bullJob.data.payload = updates.payload;\n }\n if (updates.maxAttempts !== undefined) {\n bullJob.data.meta.maxAttempts = updates.maxAttempts;\n }\n if (updates.timeout !== undefined) {\n bullJob.data.meta.timeout = updates.timeout;\n }\n if (updates.workerId !== undefined) {\n bullJob.data.meta.workerId = updates.workerId;\n }\n\n await bullJob.update(bullJob.data);\n return this.bullJobToJob(bullJob, 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('BullJobStore not initialized');\n }\n\n // Search across all queues\n for (const [queueName, queue] of this.queues) {\n const bullJob = await queue.getJob(id);\n if (bullJob) {\n return this.bullJobToJob(bullJob, 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('BullJobStore 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 ? [this.getQueue(filter.queue)]\n : Array.from(this.queues.values());\n\n for (const queue of targetQueues) {\n const queueName =\n Array.from(this.queues.entries()).find(([, q]) => q === queue)?.[0] ??\n 'unknown';\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 bullJobs: BullJob<BullJobData>[] = [];\n\n switch (status) {\n case 'pending':\n bullJobs = await queue.getWaiting(offset, offset + limit - 1);\n break;\n case 'running':\n bullJobs = await queue.getActive(offset, offset + limit - 1);\n break;\n case 'completed':\n bullJobs = await queue.getCompleted(offset, offset + limit - 1);\n break;\n case 'failed':\n bullJobs = await queue.getFailed(offset, offset + limit - 1);\n break;\n }\n\n for (const bullJob of bullJobs) {\n const job = this.bullJobToJob(bullJob, queueName);\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('BullJobStore not initialized');\n }\n\n for (const [queueName, queue] of this.queues) {\n const bullJob = await queue.getJob(id);\n if (bullJob) {\n await bullJob.remove();\n const job = this.bullJobToJob(bullJob, 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('BullJobStore 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(grace, 'completed', options.limit);\n cleaned += result.length;\n }\n\n if (options.failedBefore) {\n const grace = Date.now() - options.failedBefore.getTime();\n const result = await queue.clean(grace, 'failed', options.limit);\n cleaned += result.length;\n }\n }\n\n return cleaned;\n }\n\n /**\n * Update worker heartbeat (no-op for Bull - handled internally)\n */\n async heartbeat(_jobId: string, _workerId: string): Promise<void> {\n // Bull 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('BullJobStore 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.getQueue(queue)]\n : Array.from(this.queues.values());\n\n for (const q of targetQueues) {\n const counts = await q.getJobCounts();\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 of this.queues.values()) {\n await queue.close();\n }\n this.queues.clear();\n this.initialized = false;\n }\n}\n\n/**\n * Create a Bull job store instance\n */\nexport function createBullJobStore(config?: BullJobStoreConfig): BullJobStore {\n return new BullJobStore(config);\n}\n\nexport default BullJobStore;\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import { JobsOptions as BullMQJobOptions, ConnectionOptions } from 'bullmq';
2
+ import { BaseJobStore } from '../base-store.js';
3
+ import { CleanupOptions, Job, JobCreateOptions, JobFilter, QueueStats } from '../types.js';
4
+ /**
5
+ * BullMQ adapter configuration
6
+ */
7
+ export interface BullMQJobStoreConfig {
8
+ /** Redis connection options */
9
+ connection?: ConnectionOptions;
10
+ /** Default queue name prefix */
11
+ prefix?: string;
12
+ /** Default job options */
13
+ defaultJobOptions?: BullMQJobOptions;
14
+ }
15
+ /**
16
+ * BullMQ-based job store implementation
17
+ */
18
+ export declare class BullMQJobStore extends BaseJobStore {
19
+ private config;
20
+ private queues;
21
+ private bullmqModule;
22
+ constructor(config?: BullMQJobStoreConfig);
23
+ /**
24
+ * Initialize the store - dynamically imports BullMQ
25
+ */
26
+ initialize(): Promise<void>;
27
+ /**
28
+ * Get or create a BullMQ queue for a queue name
29
+ */
30
+ private getQueueWrapper;
31
+ /**
32
+ * Convert BullMQ job to our Job format
33
+ */
34
+ private bullMQJobToJob;
35
+ /**
36
+ * Map BullMQ job state to our JobStatus
37
+ */
38
+ private mapBullMQStatus;
39
+ /**
40
+ * Enqueue a new job
41
+ */
42
+ enqueue(options: JobCreateOptions): Promise<Job>;
43
+ /**
44
+ * Dequeue jobs ready for processing
45
+ */
46
+ dequeue(queues: string[], limit: number, workerId: string): Promise<Job[]>;
47
+ /**
48
+ * Update a job
49
+ */
50
+ update(id: string, updates: Partial<Job>): Promise<Job>;
51
+ /**
52
+ * Get a job by ID
53
+ */
54
+ get(id: string): Promise<Job | null>;
55
+ /**
56
+ * List jobs with filtering
57
+ */
58
+ list(filter: JobFilter): Promise<Job[]>;
59
+ /**
60
+ * Cancel a job
61
+ */
62
+ cancel(id: string): Promise<void>;
63
+ /**
64
+ * Clean up old jobs
65
+ */
66
+ cleanup(options: CleanupOptions): Promise<number>;
67
+ /**
68
+ * Update worker heartbeat (no-op for BullMQ - handled internally)
69
+ */
70
+ heartbeat(_jobId: string, _workerId: string): Promise<void>;
71
+ /**
72
+ * Get queue statistics
73
+ */
74
+ stats(queue?: string): Promise<QueueStats>;
75
+ /**
76
+ * Close all queues
77
+ */
78
+ close(): Promise<void>;
79
+ }
80
+ /**
81
+ * Create a BullMQ job store instance
82
+ */
83
+ export declare function createBullMQJobStore(config?: BullMQJobStoreConfig): BullMQJobStore;
84
+ export default BullMQJobStore;
85
+ //# sourceMappingURL=bullmq.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bullmq.d.ts","sourceRoot":"","sources":["../../src/adapters/bullmq.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAEV,WAAW,IAAI,gBAAgB,EAG/B,iBAAiB,EAClB,MAAM,QAAQ,CAAC;AAChB,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,oBAAoB;IACnC,+BAA+B;IAC/B,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;CACtC;AA0BD;;GAEG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAwC;IAEtD,OAAO,CAAC,YAAY,CAAwC;gBAEhD,MAAM,GAAE,oBAAyB;IAY7C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC;;OAEG;IACH,OAAO,CAAC,eAAe;IAwDvB;;OAEG;YACW,cAAc;IAiC5B;;OAEG;IACH,OAAO,CAAC,eAAe;IA2BvB;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC;IA6CtD;;OAEG;IACG,OAAO,CACX,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,GAAG,EAAE,CAAC;IA4BjB;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAgC7D;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAgB1C;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IA4E7C;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBvC;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAgCvD;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE;;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,oBAAoB,CAClC,MAAM,CAAC,EAAE,oBAAoB,GAC5B,cAAc,CAEhB;AAED,eAAe,cAAc,CAAC"}