@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
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"}
|