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
package/dist/flow.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FlowProducer: parent/children job trees, chains and fan-in flows.
|
|
3
|
+
* Mirrors the official client's TCP flow logic (bottom-up creation,
|
|
4
|
+
* UpdateParent fix-up, rollback via Cancel on failure).
|
|
5
|
+
*/
|
|
6
|
+
import { Connection } from './connection.js';
|
|
7
|
+
import { compact } from './frame.js';
|
|
8
|
+
import { Job } from './job.js';
|
|
9
|
+
import { jobPayload, wireJobOptions } from './types.js';
|
|
10
|
+
export class FlowProducer {
|
|
11
|
+
connection;
|
|
12
|
+
ownsConnection;
|
|
13
|
+
constructor(opts = {}) {
|
|
14
|
+
this.connection =
|
|
15
|
+
opts.connection ??
|
|
16
|
+
new Connection({ host: opts.host, port: opts.port, token: opts.token, tls: opts.tls });
|
|
17
|
+
this.ownsConnection = opts.connection === undefined;
|
|
18
|
+
}
|
|
19
|
+
/** Add a flow tree. Children are created (and processed) BEFORE their parent. */
|
|
20
|
+
async add(flow) {
|
|
21
|
+
const created = [];
|
|
22
|
+
try {
|
|
23
|
+
return await this.addNode(flow, null, created);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
await this.rollback(created);
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async addBulk(flows) {
|
|
31
|
+
const created = [];
|
|
32
|
+
const results = [];
|
|
33
|
+
try {
|
|
34
|
+
for (const flow of flows) {
|
|
35
|
+
results.push(await this.addNode(flow, null, created));
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
await this.rollback(created);
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Fetch a flow tree starting from a job id (recursive over childrenIds). */
|
|
45
|
+
getFlow(opts) {
|
|
46
|
+
return this.fetchNode(opts.id, opts.depth ?? Number.POSITIVE_INFINITY, opts.maxChildren);
|
|
47
|
+
}
|
|
48
|
+
/** Add a sequential chain: step[0] → step[1] → ... via dependsOn. */
|
|
49
|
+
async addChain(steps) {
|
|
50
|
+
if (steps.length === 0)
|
|
51
|
+
return { jobIds: [] };
|
|
52
|
+
const jobIds = [];
|
|
53
|
+
let prevId = null;
|
|
54
|
+
try {
|
|
55
|
+
for (const step of steps) {
|
|
56
|
+
const data = compact({
|
|
57
|
+
...jobPayload(step.name, step.data),
|
|
58
|
+
__flowParentId: prevId ?? undefined,
|
|
59
|
+
});
|
|
60
|
+
const response = await this.connection.call(compact({
|
|
61
|
+
cmd: 'PUSH',
|
|
62
|
+
queue: step.queueName,
|
|
63
|
+
data,
|
|
64
|
+
...wireJobOptions(step.opts),
|
|
65
|
+
dependsOn: prevId ? [prevId] : undefined,
|
|
66
|
+
}));
|
|
67
|
+
const id = String(response.id);
|
|
68
|
+
jobIds.push(id);
|
|
69
|
+
prevId = id;
|
|
70
|
+
}
|
|
71
|
+
return { jobIds };
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
await this.rollback(jobIds);
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Parallel jobs converging into a final fan-in job. */
|
|
79
|
+
async addBulkThen(parallel, final) {
|
|
80
|
+
const parallelIds = [];
|
|
81
|
+
try {
|
|
82
|
+
const results = await Promise.allSettled(parallel.map(async (step) => {
|
|
83
|
+
const response = await this.connection.call(compact({
|
|
84
|
+
cmd: 'PUSH',
|
|
85
|
+
queue: step.queueName,
|
|
86
|
+
data: jobPayload(step.name, step.data),
|
|
87
|
+
...wireJobOptions(step.opts),
|
|
88
|
+
}));
|
|
89
|
+
return String(response.id);
|
|
90
|
+
}));
|
|
91
|
+
const errors = [];
|
|
92
|
+
for (const r of results) {
|
|
93
|
+
if (r.status === 'fulfilled')
|
|
94
|
+
parallelIds.push(r.value);
|
|
95
|
+
else
|
|
96
|
+
errors.push(r.reason);
|
|
97
|
+
}
|
|
98
|
+
if (errors.length > 0) {
|
|
99
|
+
await this.rollback(parallelIds);
|
|
100
|
+
throw errors[0] instanceof Error ? errors[0] : new Error(String(errors[0]));
|
|
101
|
+
}
|
|
102
|
+
const finalData = {
|
|
103
|
+
...jobPayload(final.name, final.data),
|
|
104
|
+
__flowParentIds: parallelIds,
|
|
105
|
+
};
|
|
106
|
+
const finalId = await this.pushWithParent(final.queueName, finalData, final.opts, null, parallelIds);
|
|
107
|
+
return { parallelIds, finalId };
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
await this.rollback(parallelIds);
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
close() {
|
|
115
|
+
if (this.ownsConnection)
|
|
116
|
+
this.connection.close();
|
|
117
|
+
}
|
|
118
|
+
// ----------------------------------------------------------------- internals
|
|
119
|
+
async addNode(node, parentRef, created) {
|
|
120
|
+
const childNodes = [];
|
|
121
|
+
const childIds = [];
|
|
122
|
+
if (node.children && node.children.length > 0) {
|
|
123
|
+
const tempParent = { id: 'pending', queue: node.queueName };
|
|
124
|
+
const results = await Promise.all(node.children.map((child) => this.addNode(child, tempParent, created)));
|
|
125
|
+
for (const childNode of results) {
|
|
126
|
+
childNodes.push(childNode);
|
|
127
|
+
childIds.push(childNode.job.id);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const jobData = jobPayload(node.name, node.data);
|
|
131
|
+
if (parentRef) {
|
|
132
|
+
jobData.__parentId = parentRef.id;
|
|
133
|
+
jobData.__parentQueue = parentRef.queue;
|
|
134
|
+
}
|
|
135
|
+
if (childIds.length > 0)
|
|
136
|
+
jobData.__childrenIds = childIds;
|
|
137
|
+
const id = await this.pushWithParent(node.queueName, jobData, node.opts, parentRef, childIds);
|
|
138
|
+
created.push(id);
|
|
139
|
+
const job = new Job({ id, queue: node.queueName, data: jobData }, this.connection);
|
|
140
|
+
return { job, children: childNodes.length > 0 ? childNodes : undefined };
|
|
141
|
+
}
|
|
142
|
+
async pushWithParent(queue, data, opts, parentRef, childIds) {
|
|
143
|
+
const response = await this.connection.call(compact({
|
|
144
|
+
cmd: 'PUSH',
|
|
145
|
+
queue,
|
|
146
|
+
data,
|
|
147
|
+
...wireJobOptions(opts),
|
|
148
|
+
parentId: parentRef?.id,
|
|
149
|
+
childrenIds: childIds.length > 0 ? childIds : undefined,
|
|
150
|
+
dependsOn: childIds.length > 0 ? childIds : undefined,
|
|
151
|
+
}));
|
|
152
|
+
const parentJobId = String(response.id);
|
|
153
|
+
// fix up children with the real parent id (like the official client)
|
|
154
|
+
for (const childId of childIds) {
|
|
155
|
+
await this.connection.call({ cmd: 'UpdateParent', childId, parentId: parentJobId });
|
|
156
|
+
}
|
|
157
|
+
return parentJobId;
|
|
158
|
+
}
|
|
159
|
+
async fetchNode(id, depth, maxChildren) {
|
|
160
|
+
const response = await this.connection.call({ cmd: 'GetJob', id });
|
|
161
|
+
const raw = response.job;
|
|
162
|
+
if (!raw)
|
|
163
|
+
return null;
|
|
164
|
+
const job = new Job(raw, this.connection);
|
|
165
|
+
if (depth <= 0 || job.childrenIds.length === 0)
|
|
166
|
+
return { job };
|
|
167
|
+
const limit = maxChildren ?? job.childrenIds.length;
|
|
168
|
+
const children = [];
|
|
169
|
+
for (const childId of job.childrenIds.slice(0, limit)) {
|
|
170
|
+
const child = await this.fetchNode(childId, depth - 1, maxChildren);
|
|
171
|
+
if (child)
|
|
172
|
+
children.push(child);
|
|
173
|
+
}
|
|
174
|
+
return { job, children: children.length > 0 ? children : undefined };
|
|
175
|
+
}
|
|
176
|
+
async rollback(jobIds) {
|
|
177
|
+
await Promise.all(jobIds.map(async (id) => {
|
|
178
|
+
try {
|
|
179
|
+
await this.connection.call({ cmd: 'Cancel', id });
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// ignore cleanup errors
|
|
183
|
+
}
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
}
|
package/dist/frame.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire framing for the bunqueue TCP protocol: 4-byte big-endian length
|
|
3
|
+
* prefix + msgpack payload. Runtime-neutral (plain Buffer operations).
|
|
4
|
+
*/
|
|
5
|
+
export declare const PROTOCOL_VERSION = 1;
|
|
6
|
+
export declare const MAX_FRAME_SIZE: number;
|
|
7
|
+
/** Drop undefined-valued keys so the msgpack frame stays minimal. */
|
|
8
|
+
export declare function compact<T extends Record<string, unknown>>(obj: T): T;
|
|
9
|
+
/** Frame a msgpack payload with the 4-byte big-endian length prefix. */
|
|
10
|
+
export declare function frame(payload: Uint8Array): Buffer;
|
|
11
|
+
/** Incremental length-prefixed frame parser (O(total bytes) via offset cursor). */
|
|
12
|
+
export declare class FrameParser {
|
|
13
|
+
private buffer;
|
|
14
|
+
addData(data: Buffer): Buffer[];
|
|
15
|
+
clear(): void;
|
|
16
|
+
}
|
package/dist/frame.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire framing for the bunqueue TCP protocol: 4-byte big-endian length
|
|
3
|
+
* prefix + msgpack payload. Runtime-neutral (plain Buffer operations).
|
|
4
|
+
*/
|
|
5
|
+
import { ConnectionClosedError } from './errors.js';
|
|
6
|
+
export const PROTOCOL_VERSION = 1;
|
|
7
|
+
export const MAX_FRAME_SIZE = 64 * 1024 * 1024; // mirror server-side limit
|
|
8
|
+
/** Drop undefined-valued keys so the msgpack frame stays minimal. */
|
|
9
|
+
export function compact(obj) {
|
|
10
|
+
const out = {};
|
|
11
|
+
for (const key in obj) {
|
|
12
|
+
if (obj[key] !== undefined)
|
|
13
|
+
out[key] = obj[key];
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
/** Frame a msgpack payload with the 4-byte big-endian length prefix. */
|
|
18
|
+
export function frame(payload) {
|
|
19
|
+
const framed = Buffer.allocUnsafe(4 + payload.length);
|
|
20
|
+
framed.writeUInt32BE(payload.length, 0);
|
|
21
|
+
framed.set(payload, 4);
|
|
22
|
+
return framed;
|
|
23
|
+
}
|
|
24
|
+
/** Incremental length-prefixed frame parser (O(total bytes) via offset cursor). */
|
|
25
|
+
export class FrameParser {
|
|
26
|
+
buffer = Buffer.alloc(0);
|
|
27
|
+
addData(data) {
|
|
28
|
+
const buffer = this.buffer.length === 0 ? data : Buffer.concat([this.buffer, data]);
|
|
29
|
+
const frames = [];
|
|
30
|
+
let offset = 0;
|
|
31
|
+
while (buffer.length - offset >= 4) {
|
|
32
|
+
const length = buffer.readUInt32BE(offset);
|
|
33
|
+
if (length > MAX_FRAME_SIZE) {
|
|
34
|
+
this.buffer = Buffer.alloc(0);
|
|
35
|
+
throw new ConnectionClosedError(`frame size ${length} exceeds maximum`);
|
|
36
|
+
}
|
|
37
|
+
if (buffer.length - offset < 4 + length)
|
|
38
|
+
break;
|
|
39
|
+
// explicit copy so frames never alias the retained tail
|
|
40
|
+
frames.push(Buffer.from(buffer.subarray(offset + 4, offset + 4 + length)));
|
|
41
|
+
offset += 4 + length;
|
|
42
|
+
}
|
|
43
|
+
if (offset >= buffer.length) {
|
|
44
|
+
this.buffer = Buffer.alloc(0);
|
|
45
|
+
}
|
|
46
|
+
else if (offset === 0) {
|
|
47
|
+
this.buffer = buffer;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.buffer = Buffer.from(buffer.subarray(offset));
|
|
51
|
+
}
|
|
52
|
+
return frames;
|
|
53
|
+
}
|
|
54
|
+
clear() {
|
|
55
|
+
this.buffer = Buffer.alloc(0);
|
|
56
|
+
}
|
|
57
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bunqueue-client — cross-runtime TypeScript SDK for bunqueue (TCP mode).
|
|
3
|
+
* Works on Node.js ≥20, Bun and Deno ≥2 (uses only node: builtins + msgpackr).
|
|
4
|
+
*/
|
|
5
|
+
export type { Command, ConnectionOptions, Response, TlsOption } from './connection.js';
|
|
6
|
+
export { Connection } from './connection.js';
|
|
7
|
+
export { AuthError, BunqueueError, CommandError, CommandTimeoutError, ConnectionClosedError, UnrecoverableError, } from './errors.js';
|
|
8
|
+
export { FlowProducer } from './flow.js';
|
|
9
|
+
export type { FlowJob, FlowProducerOptions, FlowStep, GetFlowOptions, JobNode, } from './flow-types.js';
|
|
10
|
+
export { MAX_FRAME_SIZE, PROTOCOL_VERSION } from './frame.js';
|
|
11
|
+
export type { JobRaw } from './job.js';
|
|
12
|
+
export { Job } from './job.js';
|
|
13
|
+
export type { BulkJobEntry, QueueOptions } from './queue.js';
|
|
14
|
+
export { Queue } from './queue.js';
|
|
15
|
+
export type { SchedulerOptions } from './queue-admin.js';
|
|
16
|
+
export type { BackoffOptions, DeduplicationOptions, JobCounts, JobOptions, JobStateName, RepeatOptions, } from './types.js';
|
|
17
|
+
export { Worker } from './worker.js';
|
|
18
|
+
export type { Processor, WorkerOptions } from './worker-types.js';
|
|
19
|
+
export declare const __version__ = "0.1.0";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bunqueue-client — cross-runtime TypeScript SDK for bunqueue (TCP mode).
|
|
3
|
+
* Works on Node.js ≥20, Bun and Deno ≥2 (uses only node: builtins + msgpackr).
|
|
4
|
+
*/
|
|
5
|
+
export { Connection } from './connection.js';
|
|
6
|
+
export { AuthError, BunqueueError, CommandError, CommandTimeoutError, ConnectionClosedError, UnrecoverableError, } from './errors.js';
|
|
7
|
+
export { FlowProducer } from './flow.js';
|
|
8
|
+
export { MAX_FRAME_SIZE, PROTOCOL_VERSION } from './frame.js';
|
|
9
|
+
export { Job } from './job.js';
|
|
10
|
+
export { Queue } from './queue.js';
|
|
11
|
+
export { Worker } from './worker.js';
|
|
12
|
+
export const __version__ = '0.1.0';
|
package/dist/job.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job wrapper: read view over the server job record + per-job operations.
|
|
3
|
+
* Mirrors the official TS client's Job surface (TCP mode).
|
|
4
|
+
*/
|
|
5
|
+
import type { Connection } from './connection.js';
|
|
6
|
+
export type JobRaw = Record<string, unknown>;
|
|
7
|
+
export type ProgressHook = (job: Job, progress: number) => void;
|
|
8
|
+
export declare class Job<T = unknown> {
|
|
9
|
+
readonly raw: JobRaw;
|
|
10
|
+
readonly token: string | undefined;
|
|
11
|
+
private readonly conn;
|
|
12
|
+
private readonly onProgress;
|
|
13
|
+
constructor(raw: JobRaw, connection?: Connection, token?: string, onProgress?: ProgressHook);
|
|
14
|
+
get id(): string;
|
|
15
|
+
get queue(): string;
|
|
16
|
+
get data(): T;
|
|
17
|
+
/** Job name (stored inside `data.name`, mirroring the official client). */
|
|
18
|
+
get name(): string | undefined;
|
|
19
|
+
get priority(): number;
|
|
20
|
+
get attempts(): number;
|
|
21
|
+
get maxAttempts(): number;
|
|
22
|
+
get createdAt(): number | undefined;
|
|
23
|
+
get startedAt(): number | null;
|
|
24
|
+
get completedAt(): number | null;
|
|
25
|
+
get customId(): string | null;
|
|
26
|
+
get tags(): string[];
|
|
27
|
+
get groupId(): string | null;
|
|
28
|
+
get parentId(): string | null;
|
|
29
|
+
get childrenIds(): string[];
|
|
30
|
+
get dependsOn(): string[];
|
|
31
|
+
get progress(): number;
|
|
32
|
+
get stacktrace(): string[] | null;
|
|
33
|
+
get state(): string | undefined;
|
|
34
|
+
private call;
|
|
35
|
+
/** Report processing progress (0-100) to the server. */
|
|
36
|
+
updateProgress(progress: number, message?: string): Promise<void>;
|
|
37
|
+
/** Append a log row to the job. */
|
|
38
|
+
log(message: string, level?: 'info' | 'warn' | 'error'): Promise<void>;
|
|
39
|
+
/** Renew this job's lock (stall detection). */
|
|
40
|
+
heartbeat(): Promise<void>;
|
|
41
|
+
extendLock(durationMs: number): Promise<void>;
|
|
42
|
+
getState(): Promise<string>;
|
|
43
|
+
getChildrenValues<R = unknown>(): Promise<Record<string, R>>;
|
|
44
|
+
remove(): Promise<void>;
|
|
45
|
+
discard(): Promise<void>;
|
|
46
|
+
promote(): Promise<void>;
|
|
47
|
+
/** failed → waiting (BullMQ contract; MoveToWait over TCP). */
|
|
48
|
+
retry(): Promise<void>;
|
|
49
|
+
updateData(data: unknown): Promise<void>;
|
|
50
|
+
changePriority(opts: {
|
|
51
|
+
priority: number;
|
|
52
|
+
lifo?: boolean;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
changeDelay(delayMs: number): Promise<void>;
|
|
55
|
+
moveToDelayed(delayMs: number): Promise<void>;
|
|
56
|
+
}
|
package/dist/job.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job wrapper: read view over the server job record + per-job operations.
|
|
3
|
+
* Mirrors the official TS client's Job surface (TCP mode).
|
|
4
|
+
*/
|
|
5
|
+
export class Job {
|
|
6
|
+
raw;
|
|
7
|
+
token;
|
|
8
|
+
conn;
|
|
9
|
+
onProgress;
|
|
10
|
+
constructor(raw, connection, token, onProgress) {
|
|
11
|
+
this.raw = raw;
|
|
12
|
+
this.conn = connection;
|
|
13
|
+
this.token = token;
|
|
14
|
+
this.onProgress = onProgress;
|
|
15
|
+
}
|
|
16
|
+
// ------------------------------------------------------------- properties
|
|
17
|
+
get id() {
|
|
18
|
+
return String(this.raw.id);
|
|
19
|
+
}
|
|
20
|
+
get queue() {
|
|
21
|
+
return String(this.raw.queue);
|
|
22
|
+
}
|
|
23
|
+
get data() {
|
|
24
|
+
return this.raw.data;
|
|
25
|
+
}
|
|
26
|
+
/** Job name (stored inside `data.name`, mirroring the official client). */
|
|
27
|
+
get name() {
|
|
28
|
+
const data = this.raw.data;
|
|
29
|
+
if (data && typeof data === 'object' && 'name' in data) {
|
|
30
|
+
const name = data.name;
|
|
31
|
+
return name === undefined || name === null ? undefined : String(name);
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
get priority() {
|
|
36
|
+
return Number(this.raw.priority ?? 0);
|
|
37
|
+
}
|
|
38
|
+
get attempts() {
|
|
39
|
+
return Number(this.raw.attempts ?? 0);
|
|
40
|
+
}
|
|
41
|
+
get maxAttempts() {
|
|
42
|
+
return Number(this.raw.maxAttempts ?? 0);
|
|
43
|
+
}
|
|
44
|
+
get createdAt() {
|
|
45
|
+
return this.raw.createdAt === undefined ? undefined : Number(this.raw.createdAt);
|
|
46
|
+
}
|
|
47
|
+
get startedAt() {
|
|
48
|
+
return this.raw.startedAt === undefined || this.raw.startedAt === null
|
|
49
|
+
? null
|
|
50
|
+
: Number(this.raw.startedAt);
|
|
51
|
+
}
|
|
52
|
+
get completedAt() {
|
|
53
|
+
return this.raw.completedAt === undefined || this.raw.completedAt === null
|
|
54
|
+
? null
|
|
55
|
+
: Number(this.raw.completedAt);
|
|
56
|
+
}
|
|
57
|
+
get customId() {
|
|
58
|
+
return this.raw.customId === undefined || this.raw.customId === null
|
|
59
|
+
? null
|
|
60
|
+
: String(this.raw.customId);
|
|
61
|
+
}
|
|
62
|
+
get tags() {
|
|
63
|
+
return Array.isArray(this.raw.tags) ? this.raw.tags.map(String) : [];
|
|
64
|
+
}
|
|
65
|
+
get groupId() {
|
|
66
|
+
return this.raw.groupId === undefined || this.raw.groupId === null
|
|
67
|
+
? null
|
|
68
|
+
: String(this.raw.groupId);
|
|
69
|
+
}
|
|
70
|
+
get parentId() {
|
|
71
|
+
return this.raw.parentId === undefined || this.raw.parentId === null
|
|
72
|
+
? null
|
|
73
|
+
: String(this.raw.parentId);
|
|
74
|
+
}
|
|
75
|
+
get childrenIds() {
|
|
76
|
+
return Array.isArray(this.raw.childrenIds) ? this.raw.childrenIds.map(String) : [];
|
|
77
|
+
}
|
|
78
|
+
get dependsOn() {
|
|
79
|
+
return Array.isArray(this.raw.dependsOn) ? this.raw.dependsOn.map(String) : [];
|
|
80
|
+
}
|
|
81
|
+
get progress() {
|
|
82
|
+
return Number(this.raw.progress ?? 0);
|
|
83
|
+
}
|
|
84
|
+
get stacktrace() {
|
|
85
|
+
return Array.isArray(this.raw.stacktrace) ? this.raw.stacktrace.map(String) : null;
|
|
86
|
+
}
|
|
87
|
+
get state() {
|
|
88
|
+
return this.raw.state === undefined ? undefined : String(this.raw.state);
|
|
89
|
+
}
|
|
90
|
+
// ------------------------------------------------------------- operations
|
|
91
|
+
call(command) {
|
|
92
|
+
if (!this.conn)
|
|
93
|
+
return Promise.reject(new Error('Job is not bound to a connection'));
|
|
94
|
+
return this.conn.call(command);
|
|
95
|
+
}
|
|
96
|
+
/** Report processing progress (0-100) to the server. */
|
|
97
|
+
async updateProgress(progress, message) {
|
|
98
|
+
await this.call({ cmd: 'Progress', id: this.id, progress, message });
|
|
99
|
+
this.onProgress?.(this, progress);
|
|
100
|
+
}
|
|
101
|
+
/** Append a log row to the job. */
|
|
102
|
+
async log(message, level) {
|
|
103
|
+
await this.call({ cmd: 'AddLog', id: this.id, message, level });
|
|
104
|
+
}
|
|
105
|
+
/** Renew this job's lock (stall detection). */
|
|
106
|
+
async heartbeat() {
|
|
107
|
+
await this.call({ cmd: 'JobHeartbeat', id: this.id, token: this.token });
|
|
108
|
+
}
|
|
109
|
+
async extendLock(durationMs) {
|
|
110
|
+
await this.call({ cmd: 'ExtendLock', id: this.id, token: this.token, duration: durationMs });
|
|
111
|
+
}
|
|
112
|
+
async getState() {
|
|
113
|
+
const response = await this.call({ cmd: 'GetState', id: this.id });
|
|
114
|
+
return String(response.state);
|
|
115
|
+
}
|
|
116
|
+
async getChildrenValues() {
|
|
117
|
+
const response = await this.call({ cmd: 'GetChildrenValues', id: this.id });
|
|
118
|
+
const data = (response.data ?? {});
|
|
119
|
+
return (data.values ?? response.values ?? {});
|
|
120
|
+
}
|
|
121
|
+
async remove() {
|
|
122
|
+
await this.call({ cmd: 'Cancel', id: this.id });
|
|
123
|
+
}
|
|
124
|
+
async discard() {
|
|
125
|
+
await this.call({ cmd: 'Discard', id: this.id });
|
|
126
|
+
}
|
|
127
|
+
async promote() {
|
|
128
|
+
await this.call({ cmd: 'Promote', id: this.id });
|
|
129
|
+
}
|
|
130
|
+
/** failed → waiting (BullMQ contract; MoveToWait over TCP). */
|
|
131
|
+
async retry() {
|
|
132
|
+
await this.call({ cmd: 'MoveToWait', id: this.id });
|
|
133
|
+
}
|
|
134
|
+
async updateData(data) {
|
|
135
|
+
await this.call({ cmd: 'Update', id: this.id, data });
|
|
136
|
+
}
|
|
137
|
+
async changePriority(opts) {
|
|
138
|
+
await this.call({
|
|
139
|
+
cmd: 'ChangePriority',
|
|
140
|
+
id: this.id,
|
|
141
|
+
priority: opts.priority,
|
|
142
|
+
lifo: opts.lifo,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async changeDelay(delayMs) {
|
|
146
|
+
await this.call({ cmd: 'ChangeDelay', id: this.id, delay: delayMs });
|
|
147
|
+
}
|
|
148
|
+
async moveToDelayed(delayMs) {
|
|
149
|
+
await this.call({ cmd: 'MoveToDelayed', id: this.id, delay: delayMs });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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 type { Queue } from './queue.js';
|
|
6
|
+
import { type JobOptions } from './types.js';
|
|
7
|
+
type Ctx = Queue<unknown>;
|
|
8
|
+
type Raw = Record<string, unknown>;
|
|
9
|
+
export interface SchedulerOptions {
|
|
10
|
+
pattern?: string;
|
|
11
|
+
every?: number;
|
|
12
|
+
tz?: string;
|
|
13
|
+
immediately?: boolean;
|
|
14
|
+
limit?: number;
|
|
15
|
+
skipMissedOnRestart?: boolean;
|
|
16
|
+
skipIfNoWorker?: boolean;
|
|
17
|
+
preventOverlap?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare const adminMethods: {
|
|
20
|
+
getDlq(this: Ctx, count?: number): Promise<Raw[]>;
|
|
21
|
+
retryDlq(this: Ctx, jobId?: string): Promise<number>;
|
|
22
|
+
purgeDlq(this: Ctx): Promise<number>;
|
|
23
|
+
setDlqConfig(this: Ctx, config: Raw): Promise<void>;
|
|
24
|
+
getDlqConfig(this: Ctx): Promise<Raw>;
|
|
25
|
+
setStallConfig(this: Ctx, config: Raw): Promise<void>;
|
|
26
|
+
getStallConfig(this: Ctx): Promise<Raw>;
|
|
27
|
+
/** `duration` accepted for signature parity; the TCP wire carries only `limit`. */
|
|
28
|
+
setGlobalRateLimit(this: Ctx, max: number, _duration?: number): Promise<void>;
|
|
29
|
+
removeGlobalRateLimit(this: Ctx): Promise<void>;
|
|
30
|
+
setGlobalConcurrency(this: Ctx, concurrency: number): Promise<void>;
|
|
31
|
+
removeGlobalConcurrency(this: Ctx): Promise<void>;
|
|
32
|
+
/** Create/update a recurring job scheduler (cron pattern or fixed interval). */
|
|
33
|
+
upsertJobScheduler(this: Ctx, schedulerId: string, repeat: SchedulerOptions, template?: {
|
|
34
|
+
name?: string;
|
|
35
|
+
data?: unknown;
|
|
36
|
+
opts?: JobOptions;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
removeJobScheduler(this: Ctx, schedulerId: string): Promise<void>;
|
|
39
|
+
getJobScheduler(this: Ctx, schedulerId: string): Promise<Raw | null>;
|
|
40
|
+
getJobSchedulers(this: Ctx): Promise<Raw[]>;
|
|
41
|
+
getJobSchedulersCount(this: Ctx): Promise<number>;
|
|
42
|
+
/** Helper: run `name` on a cron `schedule`. */
|
|
43
|
+
addCron(this: Ctx, name: string, schedule: string, data?: unknown, opts?: JobOptions): Promise<void>;
|
|
44
|
+
/** Helper: run `name` every `intervalMs` milliseconds. */
|
|
45
|
+
every(this: Ctx, name: string, intervalMs: number, data?: unknown, opts?: JobOptions): Promise<void>;
|
|
46
|
+
getWorkers(this: Ctx): Promise<Raw[]>;
|
|
47
|
+
getWorkersCount(this: Ctx): Promise<number>;
|
|
48
|
+
getStats(this: Ctx): Promise<Raw>;
|
|
49
|
+
getMetrics(this: Ctx): Promise<Raw>;
|
|
50
|
+
/** Server returns queue NAMES (strings). */
|
|
51
|
+
listQueues(this: Ctx): Promise<string[]>;
|
|
52
|
+
addWebhook(this: Ctx, opts: {
|
|
53
|
+
url: string;
|
|
54
|
+
events: string[];
|
|
55
|
+
queue?: string;
|
|
56
|
+
secret?: string;
|
|
57
|
+
}): Promise<Raw>;
|
|
58
|
+
removeWebhook(this: Ctx, webhookId: string): Promise<void>;
|
|
59
|
+
listWebhooks(this: Ctx): Promise<Raw[]>;
|
|
60
|
+
setWebhookEnabled(this: Ctx, webhookId: string, enabled: boolean): Promise<void>;
|
|
61
|
+
};
|
|
62
|
+
export type QueueAdminApi = typeof adminMethods;
|
|
63
|
+
export {};
|