bunqueue-client 0.1.0
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/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/connection-types.d.ts +24 -0
- package/dist/connection-types.js +2 -0
- package/dist/connection.d.ts +44 -0
- package/dist/connection.js +229 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +25 -0
- package/dist/flow-types.d.ts +34 -0
- package/dist/flow-types.js +2 -0
- package/dist/flow.d.ts +31 -0
- package/dist/flow.js +186 -0
- package/dist/frame.d.ts +16 -0
- package/dist/frame.js +57 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +12 -0
- package/dist/job.d.ts +56 -0
- package/dist/job.js +151 -0
- package/dist/queue-admin.d.ts +63 -0
- package/dist/queue-admin.js +143 -0
- package/dist/queue-control.d.ts +44 -0
- package/dist/queue-control.js +85 -0
- package/dist/queue-query.d.ts +58 -0
- package/dist/queue-query.js +165 -0
- package/dist/queue.d.ts +50 -0
- package/dist/queue.js +77 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.js +50 -0
- package/dist/worker-base.d.ts +51 -0
- package/dist/worker-base.js +122 -0
- package/dist/worker-types.d.ts +27 -0
- package/dist/worker-types.js +5 -0
- package/dist/worker.d.ts +20 -0
- package/dist/worker.js +151 -0
- package/package.json +64 -0
- package/src/connection-types.ts +21 -0
- package/src/connection.ts +242 -0
- package/src/errors.ts +26 -0
- package/src/flow-types.ts +40 -0
- package/src/flow.ts +236 -0
- package/src/frame.ts +62 -0
- package/src/index.ts +41 -0
- package/src/job.ts +194 -0
- package/src/queue-admin.ts +213 -0
- package/src/queue-control.ts +145 -0
- package/src/queue-query.ts +220 -0
- package/src/queue.ts +112 -0
- package/src/types.ts +133 -0
- package/src/worker-base.ts +132 -0
- package/src/worker-types.ts +32 -0
- package/src/worker.ts +174 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue admin surface: DLQ, stall/DLQ configs, rate limits, schedulers,
|
|
3
|
+
* monitoring and webhooks. Merged onto Queue.prototype by queue.ts.
|
|
4
|
+
*/
|
|
5
|
+
import { compact } from './frame.js';
|
|
6
|
+
import { jobPayload, wireJobOptions } from './types.js';
|
|
7
|
+
export const adminMethods = {
|
|
8
|
+
// ---------------------------------------------------------------------- dlq
|
|
9
|
+
async getDlq(count) {
|
|
10
|
+
const response = await this.call(compact({ cmd: 'Dlq', queue: this.name, count }));
|
|
11
|
+
return (response.jobs ?? response.data ?? []);
|
|
12
|
+
},
|
|
13
|
+
async retryDlq(jobId) {
|
|
14
|
+
const response = await this.call(compact({ cmd: 'RetryDlq', queue: this.name, jobId }));
|
|
15
|
+
return Number(response.count ?? 0);
|
|
16
|
+
},
|
|
17
|
+
async purgeDlq() {
|
|
18
|
+
const response = await this.call({ cmd: 'PurgeDlq', queue: this.name });
|
|
19
|
+
return Number(response.count ?? 0);
|
|
20
|
+
},
|
|
21
|
+
async setDlqConfig(config) {
|
|
22
|
+
await this.call({ cmd: 'SetDlqConfig', queue: this.name, config });
|
|
23
|
+
},
|
|
24
|
+
async getDlqConfig() {
|
|
25
|
+
const response = await this.call({ cmd: 'GetDlqConfig', queue: this.name });
|
|
26
|
+
return (response.config ?? response.data ?? {});
|
|
27
|
+
},
|
|
28
|
+
// -------------------------------------------------------------------- stall
|
|
29
|
+
async setStallConfig(config) {
|
|
30
|
+
await this.call({ cmd: 'SetStallConfig', queue: this.name, config });
|
|
31
|
+
},
|
|
32
|
+
async getStallConfig() {
|
|
33
|
+
const response = await this.call({ cmd: 'GetStallConfig', queue: this.name });
|
|
34
|
+
return (response.config ?? response.data ?? {});
|
|
35
|
+
},
|
|
36
|
+
// -------------------------------------------------- rate limit / concurrency
|
|
37
|
+
/** `duration` accepted for signature parity; the TCP wire carries only `limit`. */
|
|
38
|
+
async setGlobalRateLimit(max, _duration) {
|
|
39
|
+
await this.call({ cmd: 'RateLimit', queue: this.name, limit: max });
|
|
40
|
+
},
|
|
41
|
+
async removeGlobalRateLimit() {
|
|
42
|
+
await this.call({ cmd: 'RateLimitClear', queue: this.name });
|
|
43
|
+
},
|
|
44
|
+
async setGlobalConcurrency(concurrency) {
|
|
45
|
+
await this.call({ cmd: 'SetConcurrency', queue: this.name, limit: concurrency });
|
|
46
|
+
},
|
|
47
|
+
async removeGlobalConcurrency() {
|
|
48
|
+
await this.call({ cmd: 'ClearConcurrency', queue: this.name });
|
|
49
|
+
},
|
|
50
|
+
// ---------------------------------------------------------------- scheduler
|
|
51
|
+
/** Create/update a recurring job scheduler (cron pattern or fixed interval). */
|
|
52
|
+
async upsertJobScheduler(schedulerId, repeat, template = {}) {
|
|
53
|
+
await this.call(compact({
|
|
54
|
+
cmd: 'Cron',
|
|
55
|
+
name: schedulerId,
|
|
56
|
+
queue: this.name,
|
|
57
|
+
data: jobPayload(template.name ?? schedulerId, template.data ?? {}),
|
|
58
|
+
schedule: repeat.pattern,
|
|
59
|
+
repeatEvery: repeat.every,
|
|
60
|
+
timezone: repeat.tz,
|
|
61
|
+
immediately: repeat.immediately,
|
|
62
|
+
maxLimit: repeat.limit,
|
|
63
|
+
skipMissedOnRestart: repeat.skipMissedOnRestart,
|
|
64
|
+
skipIfNoWorker: repeat.skipIfNoWorker,
|
|
65
|
+
preventOverlap: repeat.preventOverlap,
|
|
66
|
+
jobOptions: template.opts ? wireJobOptions(template.opts) : undefined,
|
|
67
|
+
}));
|
|
68
|
+
},
|
|
69
|
+
async removeJobScheduler(schedulerId) {
|
|
70
|
+
await this.call({ cmd: 'CronDelete', name: schedulerId });
|
|
71
|
+
},
|
|
72
|
+
async getJobScheduler(schedulerId) {
|
|
73
|
+
try {
|
|
74
|
+
const response = await this.call({ cmd: 'CronGet', name: schedulerId });
|
|
75
|
+
return (response.cron ?? response.data ?? null);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null; // not-found surfaces as a CommandError
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
async getJobSchedulers() {
|
|
82
|
+
const response = await this.call({ cmd: 'CronList' });
|
|
83
|
+
const crons = (response.crons ?? []);
|
|
84
|
+
return crons.filter((cron) => cron.queue === this.name);
|
|
85
|
+
},
|
|
86
|
+
async getJobSchedulersCount() {
|
|
87
|
+
return (await this.getJobSchedulers()).length;
|
|
88
|
+
},
|
|
89
|
+
/** Helper: run `name` on a cron `schedule`. */
|
|
90
|
+
addCron(name, schedule, data, opts) {
|
|
91
|
+
return this.upsertJobScheduler(name, { pattern: schedule }, { name, data, opts });
|
|
92
|
+
},
|
|
93
|
+
/** Helper: run `name` every `intervalMs` milliseconds. */
|
|
94
|
+
every(name, intervalMs, data, opts) {
|
|
95
|
+
return this.upsertJobScheduler(name, { every: intervalMs }, { name, data, opts });
|
|
96
|
+
},
|
|
97
|
+
// --------------------------------------------------------------- monitoring
|
|
98
|
+
async getWorkers() {
|
|
99
|
+
const response = await this.call({ cmd: 'ListWorkers' });
|
|
100
|
+
const data = (response.data ?? {});
|
|
101
|
+
return (data.workers ?? response.workers ?? []);
|
|
102
|
+
},
|
|
103
|
+
async getWorkersCount() {
|
|
104
|
+
return (await this.getWorkers()).length;
|
|
105
|
+
},
|
|
106
|
+
async getStats() {
|
|
107
|
+
const response = await this.call({ cmd: 'Stats' });
|
|
108
|
+
return (response.stats ?? {});
|
|
109
|
+
},
|
|
110
|
+
async getMetrics() {
|
|
111
|
+
const response = await this.call({ cmd: 'Metrics' });
|
|
112
|
+
return (response.metrics ?? {});
|
|
113
|
+
},
|
|
114
|
+
/** Server returns queue NAMES (strings). */
|
|
115
|
+
async listQueues() {
|
|
116
|
+
const response = await this.call({ cmd: 'ListQueues' });
|
|
117
|
+
const queues = (response.queues ?? []);
|
|
118
|
+
return queues.map((q) => (typeof q === 'string' ? q : String(q.name)));
|
|
119
|
+
},
|
|
120
|
+
// ----------------------------------------------------------------- webhooks
|
|
121
|
+
async addWebhook(opts) {
|
|
122
|
+
const response = await this.call(compact({
|
|
123
|
+
cmd: 'AddWebhook',
|
|
124
|
+
url: opts.url,
|
|
125
|
+
events: opts.events,
|
|
126
|
+
queue: opts.queue ?? this.name,
|
|
127
|
+
secret: opts.secret,
|
|
128
|
+
}));
|
|
129
|
+
const data = (response.data ?? {});
|
|
130
|
+
return (data.webhook ?? data);
|
|
131
|
+
},
|
|
132
|
+
async removeWebhook(webhookId) {
|
|
133
|
+
await this.call({ cmd: 'RemoveWebhook', webhookId });
|
|
134
|
+
},
|
|
135
|
+
async listWebhooks() {
|
|
136
|
+
const response = await this.call({ cmd: 'ListWebhooks' });
|
|
137
|
+
const data = (response.data ?? {});
|
|
138
|
+
return (data.webhooks ?? response.webhooks ?? []);
|
|
139
|
+
},
|
|
140
|
+
async setWebhookEnabled(webhookId, enabled) {
|
|
141
|
+
await this.call({ cmd: 'SetWebhookEnabled', webhookId, enabled });
|
|
142
|
+
},
|
|
143
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue control surface: pause/drain/clean, promotion, retry and per-job
|
|
3
|
+
* mutations. Methods are merged onto Queue.prototype by queue.ts.
|
|
4
|
+
*/
|
|
5
|
+
import type { Queue } from './queue.js';
|
|
6
|
+
import type { JobStateName } from './types.js';
|
|
7
|
+
type Ctx = Queue<unknown>;
|
|
8
|
+
export declare const controlMethods: {
|
|
9
|
+
pause(this: Ctx): Promise<void>;
|
|
10
|
+
resume(this: Ctx): Promise<void>;
|
|
11
|
+
isPaused(this: Ctx): Promise<boolean>;
|
|
12
|
+
drain(this: Ctx): Promise<void>;
|
|
13
|
+
obliterate(this: Ctx): Promise<void>;
|
|
14
|
+
/** Remove old jobs; returns how many were removed. */
|
|
15
|
+
clean(this: Ctx, graceMs: number, limit?: number, state?: JobStateName): Promise<number>;
|
|
16
|
+
remove(this: Ctx, id: string): Promise<void>;
|
|
17
|
+
discard(this: Ctx, id: string): Promise<void>;
|
|
18
|
+
promoteJob(this: Ctx, id: string): Promise<void>;
|
|
19
|
+
/** Promote delayed jobs to waiting; returns how many were promoted. */
|
|
20
|
+
promoteJobs(this: Ctx, opts?: {
|
|
21
|
+
count?: number;
|
|
22
|
+
}): Promise<number>;
|
|
23
|
+
/** BullMQ contract: failed → waiting. */
|
|
24
|
+
retryJob(this: Ctx, id: string): Promise<void>;
|
|
25
|
+
retryJobs(this: Ctx, opts?: {
|
|
26
|
+
state?: "failed" | "completed";
|
|
27
|
+
count?: number;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
retryCompleted(this: Ctx, id?: string): Promise<void>;
|
|
30
|
+
updateJobProgress(this: Ctx, id: string, progress: number, message?: string): Promise<void>;
|
|
31
|
+
updateJobData(this: Ctx, id: string, data: unknown): Promise<void>;
|
|
32
|
+
changeJobPriority(this: Ctx, id: string, opts: {
|
|
33
|
+
priority: number;
|
|
34
|
+
lifo?: boolean;
|
|
35
|
+
}): Promise<void>;
|
|
36
|
+
changeJobDelay(this: Ctx, id: string, delayMs: number): Promise<void>;
|
|
37
|
+
moveJobToWait(this: Ctx, id: string): Promise<void>;
|
|
38
|
+
moveJobToDelayed(this: Ctx, id: string, delayMs: number): Promise<void>;
|
|
39
|
+
extendJobLock(this: Ctx, id: string, token: string, durationMs: number): Promise<void>;
|
|
40
|
+
moveJobToCompleted(this: Ctx, id: string, returnValue: unknown, token?: string): Promise<void>;
|
|
41
|
+
moveJobToFailed(this: Ctx, id: string, error: Error | string, token?: string): Promise<void>;
|
|
42
|
+
};
|
|
43
|
+
export type QueueControlApi = typeof controlMethods;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue control surface: pause/drain/clean, promotion, retry and per-job
|
|
3
|
+
* mutations. Methods are merged onto Queue.prototype by queue.ts.
|
|
4
|
+
*/
|
|
5
|
+
import { compact } from './frame.js';
|
|
6
|
+
export const controlMethods = {
|
|
7
|
+
async pause() {
|
|
8
|
+
await this.call({ cmd: 'Pause', queue: this.name });
|
|
9
|
+
},
|
|
10
|
+
async resume() {
|
|
11
|
+
await this.call({ cmd: 'Resume', queue: this.name });
|
|
12
|
+
},
|
|
13
|
+
async isPaused() {
|
|
14
|
+
const response = await this.call({ cmd: 'IsPaused', queue: this.name });
|
|
15
|
+
return response.paused === true;
|
|
16
|
+
},
|
|
17
|
+
async drain() {
|
|
18
|
+
await this.call({ cmd: 'Drain', queue: this.name });
|
|
19
|
+
},
|
|
20
|
+
async obliterate() {
|
|
21
|
+
await this.call({ cmd: 'Obliterate', queue: this.name });
|
|
22
|
+
},
|
|
23
|
+
/** Remove old jobs; returns how many were removed. */
|
|
24
|
+
async clean(graceMs, limit, state) {
|
|
25
|
+
const response = await this.call(compact({ cmd: 'Clean', queue: this.name, grace: graceMs, limit, state }));
|
|
26
|
+
return Number(response.count ?? 0);
|
|
27
|
+
},
|
|
28
|
+
async remove(id) {
|
|
29
|
+
await this.call({ cmd: 'Cancel', id });
|
|
30
|
+
},
|
|
31
|
+
async discard(id) {
|
|
32
|
+
await this.call({ cmd: 'Discard', id });
|
|
33
|
+
},
|
|
34
|
+
async promoteJob(id) {
|
|
35
|
+
await this.call({ cmd: 'Promote', id });
|
|
36
|
+
},
|
|
37
|
+
/** Promote delayed jobs to waiting; returns how many were promoted. */
|
|
38
|
+
async promoteJobs(opts = {}) {
|
|
39
|
+
const response = await this.call(compact({ cmd: 'PromoteJobs', queue: this.name, count: opts.count }));
|
|
40
|
+
return Number(response.count ?? 0);
|
|
41
|
+
},
|
|
42
|
+
/** BullMQ contract: failed → waiting. */
|
|
43
|
+
async retryJob(id) {
|
|
44
|
+
await this.call({ cmd: 'MoveToWait', id });
|
|
45
|
+
},
|
|
46
|
+
async retryJobs(opts = {}) {
|
|
47
|
+
if (opts.state === 'completed') {
|
|
48
|
+
await this.call({ cmd: 'RetryCompleted', queue: this.name });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
await this.call(compact({ cmd: 'RetryDlq', queue: this.name, count: opts.count }));
|
|
52
|
+
},
|
|
53
|
+
async retryCompleted(id) {
|
|
54
|
+
await this.call(compact({ cmd: 'RetryCompleted', queue: this.name, id }));
|
|
55
|
+
},
|
|
56
|
+
// ----------------------------------------------------------- job mutations
|
|
57
|
+
async updateJobProgress(id, progress, message) {
|
|
58
|
+
await this.call(compact({ cmd: 'Progress', id, progress, message }));
|
|
59
|
+
},
|
|
60
|
+
async updateJobData(id, data) {
|
|
61
|
+
await this.call({ cmd: 'Update', id, data });
|
|
62
|
+
},
|
|
63
|
+
async changeJobPriority(id, opts) {
|
|
64
|
+
await this.call({ cmd: 'ChangePriority', id, priority: opts.priority, lifo: opts.lifo });
|
|
65
|
+
},
|
|
66
|
+
async changeJobDelay(id, delayMs) {
|
|
67
|
+
await this.call({ cmd: 'ChangeDelay', id, delay: delayMs });
|
|
68
|
+
},
|
|
69
|
+
async moveJobToWait(id) {
|
|
70
|
+
await this.call({ cmd: 'MoveToWait', id });
|
|
71
|
+
},
|
|
72
|
+
async moveJobToDelayed(id, delayMs) {
|
|
73
|
+
await this.call({ cmd: 'MoveToDelayed', id, delay: delayMs });
|
|
74
|
+
},
|
|
75
|
+
async extendJobLock(id, token, durationMs) {
|
|
76
|
+
await this.call({ cmd: 'ExtendLock', id, token, duration: durationMs });
|
|
77
|
+
},
|
|
78
|
+
async moveJobToCompleted(id, returnValue, token) {
|
|
79
|
+
await this.call(compact({ cmd: 'ACK', id, result: returnValue, token }));
|
|
80
|
+
},
|
|
81
|
+
async moveJobToFailed(id, error, token) {
|
|
82
|
+
const message = typeof error === 'string' ? error : error.message;
|
|
83
|
+
await this.call(compact({ cmd: 'FAIL', id, error: message, token }));
|
|
84
|
+
},
|
|
85
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue query surface: job lookup, state, results, counts, logs, children.
|
|
3
|
+
* Methods are merged onto Queue.prototype by queue.ts.
|
|
4
|
+
*/
|
|
5
|
+
import { Job } from './job.js';
|
|
6
|
+
import type { Queue } from './queue.js';
|
|
7
|
+
import type { JobCounts } from './types.js';
|
|
8
|
+
type Ctx = Queue<unknown>;
|
|
9
|
+
export declare const queryMethods: {
|
|
10
|
+
getJob<T = unknown>(this: Ctx, id: string): Promise<Job<T> | null>;
|
|
11
|
+
getJobByCustomId<T = unknown>(this: Ctx, customId: string): Promise<Job<T> | null>;
|
|
12
|
+
/** BullMQ v5: resolve the job id registered under a deduplication id. */
|
|
13
|
+
getDeduplicationJobId(this: Ctx, deduplicationId: string): Promise<string | null>;
|
|
14
|
+
getJobs<T = unknown>(this: Ctx, opts?: {
|
|
15
|
+
state?: string | string[];
|
|
16
|
+
start?: number;
|
|
17
|
+
end?: number;
|
|
18
|
+
}): Promise<Job<T>[]>;
|
|
19
|
+
getWaiting<T = unknown>(this: Ctx, start?: number, end?: number): Promise<Job<T>[]>;
|
|
20
|
+
getDelayed<T = unknown>(this: Ctx, start?: number, end?: number): Promise<Job<T>[]>;
|
|
21
|
+
getActive<T = unknown>(this: Ctx, start?: number, end?: number): Promise<Job<T>[]>;
|
|
22
|
+
getCompleted<T = unknown>(this: Ctx, start?: number, end?: number): Promise<Job<T>[]>;
|
|
23
|
+
getFailed<T = unknown>(this: Ctx, start?: number, end?: number): Promise<Job<T>[]>;
|
|
24
|
+
getPrioritized<T = unknown>(this: Ctx, start?: number, end?: number): Promise<Job<T>[]>;
|
|
25
|
+
getWaitingChildren<T = unknown>(this: Ctx, start?: number, end?: number): Promise<Job<T>[]>;
|
|
26
|
+
getJobState(this: Ctx, id: string): Promise<string>;
|
|
27
|
+
getResult<R = unknown>(this: Ctx, id: string): Promise<R>;
|
|
28
|
+
getChildrenValues<R = unknown>(this: Ctx, id: string): Promise<Record<string, R>>;
|
|
29
|
+
getFailedChildrenValues(this: Ctx, id: string): Promise<Record<string, string>>;
|
|
30
|
+
getIgnoredChildrenFailures(this: Ctx, id: string): Promise<Record<string, string>>;
|
|
31
|
+
/** Detach a child job from its parent's dependency list. */
|
|
32
|
+
removeChildDependency(this: Ctx, id: string): Promise<void>;
|
|
33
|
+
/** Remove all still-unprocessed children of a parent job. */
|
|
34
|
+
removeUnprocessedChildren(this: Ctx, id: string): Promise<void>;
|
|
35
|
+
/** Block until the job finishes; returns its result. */
|
|
36
|
+
waitForJob<R = unknown>(this: Ctx, id: string, ttlMs?: number): Promise<R>;
|
|
37
|
+
/** BullMQ v5 alias for waitForJob (queueEvents param unused over TCP). */
|
|
38
|
+
waitJobUntilFinished<R = unknown>(this: Ctx, id: string, _queueEvents?: unknown, ttlMs?: number): Promise<R>;
|
|
39
|
+
getProgress(this: Ctx, id: string): Promise<{
|
|
40
|
+
progress: number;
|
|
41
|
+
message: string | null;
|
|
42
|
+
}>;
|
|
43
|
+
getJobCounts(this: Ctx): Promise<JobCounts>;
|
|
44
|
+
getWaitingCount(this: Ctx): Promise<number>;
|
|
45
|
+
getActiveCount(this: Ctx): Promise<number>;
|
|
46
|
+
getCompletedCount(this: Ctx): Promise<number>;
|
|
47
|
+
getFailedCount(this: Ctx): Promise<number>;
|
|
48
|
+
getDelayedCount(this: Ctx): Promise<number>;
|
|
49
|
+
getPrioritizedCount(this: Ctx): Promise<number>;
|
|
50
|
+
getWaitingChildrenCount(this: Ctx): Promise<number>;
|
|
51
|
+
count(this: Ctx): Promise<number>;
|
|
52
|
+
getCountsPerPriority(this: Ctx): Promise<Record<string, number>>;
|
|
53
|
+
addJobLog(this: Ctx, id: string, message: string): Promise<void>;
|
|
54
|
+
getJobLogs(this: Ctx, id: string, start?: number, end?: number): Promise<string[]>;
|
|
55
|
+
clearJobLogs(this: Ctx, id: string, keepLogs?: number): Promise<void>;
|
|
56
|
+
};
|
|
57
|
+
export type QueueQueryApi = typeof queryMethods;
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue query surface: job lookup, state, results, counts, logs, children.
|
|
3
|
+
* Methods are merged onto Queue.prototype by queue.ts.
|
|
4
|
+
*/
|
|
5
|
+
import { CommandError } from './errors.js';
|
|
6
|
+
import { compact } from './frame.js';
|
|
7
|
+
import { Job } from './job.js';
|
|
8
|
+
function unwrapValues(response) {
|
|
9
|
+
const data = (response.data ?? {});
|
|
10
|
+
return (data.values ?? response.values ?? data ?? {});
|
|
11
|
+
}
|
|
12
|
+
export const queryMethods = {
|
|
13
|
+
async getJob(id) {
|
|
14
|
+
try {
|
|
15
|
+
const response = await this.call({ cmd: 'GetJob', id });
|
|
16
|
+
const raw = response.job;
|
|
17
|
+
return raw ? new Job(raw, this.connection) : null;
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
if (err instanceof CommandError)
|
|
21
|
+
return null; // server: 'Job not found'
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
async getJobByCustomId(customId) {
|
|
26
|
+
try {
|
|
27
|
+
const response = await this.call({ cmd: 'GetJobByCustomId', customId });
|
|
28
|
+
const raw = response.job;
|
|
29
|
+
return raw ? new Job(raw, this.connection) : null;
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (err instanceof CommandError)
|
|
33
|
+
return null;
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
/** BullMQ v5: resolve the job id registered under a deduplication id. */
|
|
38
|
+
async getDeduplicationJobId(deduplicationId) {
|
|
39
|
+
const job = await this.getJobByCustomId(deduplicationId);
|
|
40
|
+
return job ? job.id : null;
|
|
41
|
+
},
|
|
42
|
+
async getJobs(opts = {}) {
|
|
43
|
+
const start = opts.start ?? 0;
|
|
44
|
+
const end = opts.end !== undefined && opts.end >= 0 ? opts.end : 1000;
|
|
45
|
+
const response = await this.call(compact({
|
|
46
|
+
cmd: 'GetJobs',
|
|
47
|
+
queue: this.name,
|
|
48
|
+
state: opts.state,
|
|
49
|
+
offset: start,
|
|
50
|
+
limit: end - start,
|
|
51
|
+
}));
|
|
52
|
+
const jobs = (response.jobs ?? []);
|
|
53
|
+
return jobs.map((raw) => new Job(raw, this.connection));
|
|
54
|
+
},
|
|
55
|
+
getWaiting(start, end) {
|
|
56
|
+
return this.getJobs({ state: ['waiting', 'prioritized'], start, end });
|
|
57
|
+
},
|
|
58
|
+
getDelayed(start, end) {
|
|
59
|
+
return this.getJobs({ state: 'delayed', start, end });
|
|
60
|
+
},
|
|
61
|
+
getActive(start, end) {
|
|
62
|
+
return this.getJobs({ state: 'active', start, end });
|
|
63
|
+
},
|
|
64
|
+
getCompleted(start, end) {
|
|
65
|
+
return this.getJobs({ state: 'completed', start, end });
|
|
66
|
+
},
|
|
67
|
+
getFailed(start, end) {
|
|
68
|
+
return this.getJobs({ state: 'failed', start, end });
|
|
69
|
+
},
|
|
70
|
+
getPrioritized(start, end) {
|
|
71
|
+
return this.getJobs({ state: 'prioritized', start, end });
|
|
72
|
+
},
|
|
73
|
+
getWaitingChildren(start, end) {
|
|
74
|
+
return this.getJobs({ state: 'waiting-children', start, end });
|
|
75
|
+
},
|
|
76
|
+
async getJobState(id) {
|
|
77
|
+
const response = await this.call({ cmd: 'GetState', id });
|
|
78
|
+
return String(response.state);
|
|
79
|
+
},
|
|
80
|
+
async getResult(id) {
|
|
81
|
+
const response = await this.call({ cmd: 'GetResult', id });
|
|
82
|
+
return response.result;
|
|
83
|
+
},
|
|
84
|
+
async getChildrenValues(id) {
|
|
85
|
+
return unwrapValues(await this.call({ cmd: 'GetChildrenValues', id }));
|
|
86
|
+
},
|
|
87
|
+
async getFailedChildrenValues(id) {
|
|
88
|
+
return unwrapValues(await this.call({ cmd: 'GetFailedChildrenValues', id }));
|
|
89
|
+
},
|
|
90
|
+
async getIgnoredChildrenFailures(id) {
|
|
91
|
+
return unwrapValues(await this.call({ cmd: 'GetIgnoredChildrenFailures', id }));
|
|
92
|
+
},
|
|
93
|
+
/** Detach a child job from its parent's dependency list. */
|
|
94
|
+
async removeChildDependency(id) {
|
|
95
|
+
await this.call({ cmd: 'RemoveChildDependency', id });
|
|
96
|
+
},
|
|
97
|
+
/** Remove all still-unprocessed children of a parent job. */
|
|
98
|
+
async removeUnprocessedChildren(id) {
|
|
99
|
+
await this.call({ cmd: 'RemoveUnprocessedChildren', id });
|
|
100
|
+
},
|
|
101
|
+
/** Block until the job finishes; returns its result. */
|
|
102
|
+
async waitForJob(id, ttlMs = 30_000) {
|
|
103
|
+
const response = await this.call({ cmd: 'WaitJob', id, timeout: ttlMs }, ttlMs + 5000);
|
|
104
|
+
return response.result;
|
|
105
|
+
},
|
|
106
|
+
/** BullMQ v5 alias for waitForJob (queueEvents param unused over TCP). */
|
|
107
|
+
waitJobUntilFinished(id, _queueEvents, ttlMs) {
|
|
108
|
+
return this.waitForJob(id, ttlMs);
|
|
109
|
+
},
|
|
110
|
+
async getProgress(id) {
|
|
111
|
+
const response = await this.call({ cmd: 'GetProgress', id });
|
|
112
|
+
return {
|
|
113
|
+
progress: Number(response.progress ?? 0),
|
|
114
|
+
message: response.message ?? null,
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
// ------------------------------------------------------------------- counts
|
|
118
|
+
async getJobCounts() {
|
|
119
|
+
const response = await this.call({ cmd: 'GetJobCounts', queue: this.name });
|
|
120
|
+
return response.counts;
|
|
121
|
+
},
|
|
122
|
+
async getWaitingCount() {
|
|
123
|
+
const counts = await this.getJobCounts();
|
|
124
|
+
return counts.waiting + counts.prioritized;
|
|
125
|
+
},
|
|
126
|
+
async getActiveCount() {
|
|
127
|
+
return (await this.getJobCounts()).active;
|
|
128
|
+
},
|
|
129
|
+
async getCompletedCount() {
|
|
130
|
+
return (await this.getJobCounts()).completed;
|
|
131
|
+
},
|
|
132
|
+
async getFailedCount() {
|
|
133
|
+
return (await this.getJobCounts()).failed;
|
|
134
|
+
},
|
|
135
|
+
async getDelayedCount() {
|
|
136
|
+
return (await this.getJobCounts()).delayed;
|
|
137
|
+
},
|
|
138
|
+
async getPrioritizedCount() {
|
|
139
|
+
return (await this.getJobCounts()).prioritized;
|
|
140
|
+
},
|
|
141
|
+
async getWaitingChildrenCount() {
|
|
142
|
+
return (await this.getJobCounts())['waiting-children'];
|
|
143
|
+
},
|
|
144
|
+
async count() {
|
|
145
|
+
const response = await this.call({ cmd: 'Count', queue: this.name });
|
|
146
|
+
return Number(response.count ?? 0);
|
|
147
|
+
},
|
|
148
|
+
async getCountsPerPriority() {
|
|
149
|
+
const response = await this.call({ cmd: 'GetCountsPerPriority', queue: this.name });
|
|
150
|
+
return (response.counts ?? response.data ?? {});
|
|
151
|
+
},
|
|
152
|
+
// --------------------------------------------------------------------- logs
|
|
153
|
+
async addJobLog(id, message) {
|
|
154
|
+
await this.call({ cmd: 'AddLog', id, message });
|
|
155
|
+
},
|
|
156
|
+
async getJobLogs(id, start, end) {
|
|
157
|
+
const response = await this.call(compact({ cmd: 'GetLogs', id, start, end }));
|
|
158
|
+
const data = (response.data ?? {});
|
|
159
|
+
const logs = (data.logs ?? response.logs ?? []);
|
|
160
|
+
return logs.map((row) => (typeof row === 'string' ? row : String(row.message ?? row)));
|
|
161
|
+
},
|
|
162
|
+
async clearJobLogs(id, keepLogs) {
|
|
163
|
+
await this.call(compact({ cmd: 'ClearLogs', id, keepLogs }));
|
|
164
|
+
},
|
|
165
|
+
};
|
package/dist/queue.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue: producer + management API over the bunqueue TCP protocol.
|
|
3
|
+
* Method surface mirrors the official TS client (TCP mode).
|
|
4
|
+
*
|
|
5
|
+
* The class is composed from area modules kept under 250 lines each:
|
|
6
|
+
* queue-query.ts (query/counts/logs), queue-control.ts (state control,
|
|
7
|
+
* job mutations) and queue-admin.ts (DLQ, configs, schedulers, webhooks,
|
|
8
|
+
* monitoring). Methods are merged onto the prototype; declaration merging
|
|
9
|
+
* exposes them on the type.
|
|
10
|
+
*/
|
|
11
|
+
import { Connection, type Response, type TlsOption } from './connection.js';
|
|
12
|
+
import { Job } from './job.js';
|
|
13
|
+
import { type QueueAdminApi } from './queue-admin.js';
|
|
14
|
+
import { type QueueControlApi } from './queue-control.js';
|
|
15
|
+
import { type QueueQueryApi } from './queue-query.js';
|
|
16
|
+
import { type JobOptions } from './types.js';
|
|
17
|
+
export interface QueueOptions {
|
|
18
|
+
host?: string;
|
|
19
|
+
port?: number;
|
|
20
|
+
token?: string;
|
|
21
|
+
tls?: TlsOption;
|
|
22
|
+
connection?: Connection;
|
|
23
|
+
commandTimeoutMs?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface BulkJobEntry<T = unknown> {
|
|
26
|
+
name: string;
|
|
27
|
+
data: T;
|
|
28
|
+
opts?: JobOptions;
|
|
29
|
+
}
|
|
30
|
+
export declare class Queue<T = unknown> {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly connection: Connection;
|
|
33
|
+
private readonly ownsConnection;
|
|
34
|
+
constructor(name: string, opts?: QueueOptions);
|
|
35
|
+
/** Send a raw command on this queue's connection (used by area modules). */
|
|
36
|
+
call(command: Record<string, unknown> & {
|
|
37
|
+
cmd: string;
|
|
38
|
+
}, timeoutMs?: number): Promise<Response>;
|
|
39
|
+
/** Add a job; returns a Job stub carrying the assigned id. */
|
|
40
|
+
add(name: string, data: T, opts?: JobOptions): Promise<Job<T>>;
|
|
41
|
+
/** Add many jobs in one round-trip; returns Job stubs. */
|
|
42
|
+
addBulk(jobs: BulkJobEntry<T>[]): Promise<Job<T>[]>;
|
|
43
|
+
ping(): Promise<boolean>;
|
|
44
|
+
waitUntilReady(): Promise<void>;
|
|
45
|
+
close(): void;
|
|
46
|
+
/** BullMQ v5 alias for close(). */
|
|
47
|
+
disconnect(): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
export interface Queue<T = unknown> extends QueueQueryApi, QueueControlApi, QueueAdminApi {
|
|
50
|
+
}
|
package/dist/queue.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue: producer + management API over the bunqueue TCP protocol.
|
|
3
|
+
* Method surface mirrors the official TS client (TCP mode).
|
|
4
|
+
*
|
|
5
|
+
* The class is composed from area modules kept under 250 lines each:
|
|
6
|
+
* queue-query.ts (query/counts/logs), queue-control.ts (state control,
|
|
7
|
+
* job mutations) and queue-admin.ts (DLQ, configs, schedulers, webhooks,
|
|
8
|
+
* monitoring). Methods are merged onto the prototype; declaration merging
|
|
9
|
+
* exposes them on the type.
|
|
10
|
+
*/
|
|
11
|
+
import { Connection } from './connection.js';
|
|
12
|
+
import { Job } from './job.js';
|
|
13
|
+
import { adminMethods } from './queue-admin.js';
|
|
14
|
+
import { controlMethods } from './queue-control.js';
|
|
15
|
+
import { queryMethods } from './queue-query.js';
|
|
16
|
+
import { jobPayload, wireJobOptions } from './types.js';
|
|
17
|
+
// biome-ignore lint/suspicious/noUnsafeDeclarationMerging: prototype-mixin composition — Object.assign at the bottom installs exactly the methods the merged interface declares
|
|
18
|
+
export class Queue {
|
|
19
|
+
name;
|
|
20
|
+
connection;
|
|
21
|
+
ownsConnection;
|
|
22
|
+
constructor(name, opts = {}) {
|
|
23
|
+
this.name = name;
|
|
24
|
+
this.connection =
|
|
25
|
+
opts.connection ??
|
|
26
|
+
new Connection({
|
|
27
|
+
host: opts.host,
|
|
28
|
+
port: opts.port,
|
|
29
|
+
token: opts.token,
|
|
30
|
+
tls: opts.tls,
|
|
31
|
+
commandTimeoutMs: opts.commandTimeoutMs,
|
|
32
|
+
});
|
|
33
|
+
this.ownsConnection = opts.connection === undefined;
|
|
34
|
+
}
|
|
35
|
+
/** Send a raw command on this queue's connection (used by area modules). */
|
|
36
|
+
call(command, timeoutMs) {
|
|
37
|
+
return this.connection.call(command, timeoutMs);
|
|
38
|
+
}
|
|
39
|
+
// ------------------------------------------------------------------ produce
|
|
40
|
+
/** Add a job; returns a Job stub carrying the assigned id. */
|
|
41
|
+
async add(name, data, opts) {
|
|
42
|
+
const payload = jobPayload(name, data);
|
|
43
|
+
const response = await this.call({
|
|
44
|
+
cmd: 'PUSH',
|
|
45
|
+
queue: this.name,
|
|
46
|
+
data: payload,
|
|
47
|
+
...wireJobOptions(opts),
|
|
48
|
+
});
|
|
49
|
+
return new Job({ id: response.id, queue: this.name, data: payload }, this.connection);
|
|
50
|
+
}
|
|
51
|
+
/** Add many jobs in one round-trip; returns Job stubs. */
|
|
52
|
+
async addBulk(jobs) {
|
|
53
|
+
const inputs = jobs.map((entry) => ({
|
|
54
|
+
data: jobPayload(entry.name, entry.data),
|
|
55
|
+
...wireJobOptions(entry.opts),
|
|
56
|
+
}));
|
|
57
|
+
const response = await this.call({ cmd: 'PUSHB', queue: this.name, jobs: inputs });
|
|
58
|
+
const ids = (response.ids ?? []);
|
|
59
|
+
return ids.map((id, i) => new Job({ id, queue: this.name, data: inputs[i].data }, this.connection));
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------- lifecycle
|
|
62
|
+
ping() {
|
|
63
|
+
return this.connection.ping();
|
|
64
|
+
}
|
|
65
|
+
async waitUntilReady() {
|
|
66
|
+
await this.connection.connect();
|
|
67
|
+
}
|
|
68
|
+
close() {
|
|
69
|
+
if (this.ownsConnection)
|
|
70
|
+
this.connection.close();
|
|
71
|
+
}
|
|
72
|
+
/** BullMQ v5 alias for close(). */
|
|
73
|
+
async disconnect() {
|
|
74
|
+
this.close();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
Object.assign(Queue.prototype, queryMethods, controlMethods, adminMethods);
|