@igniter-js/jobs 0.1.0 → 0.1.1
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/AGENTS.md +115 -541
- package/CHANGELOG.md +5 -0
- package/README.md +118 -356
- package/dist/adapter-PiDCQWQd.d.mts +529 -0
- package/dist/adapter-PiDCQWQd.d.ts +529 -0
- package/dist/adapters/bullmq.adapter.d.mts +55 -110
- package/dist/adapters/bullmq.adapter.d.ts +55 -110
- package/dist/adapters/bullmq.adapter.js +431 -529
- package/dist/adapters/bullmq.adapter.js.map +1 -1
- package/dist/adapters/bullmq.adapter.mjs +431 -529
- package/dist/adapters/bullmq.adapter.mjs.map +1 -1
- package/dist/adapters/index.d.mts +3 -3
- package/dist/adapters/index.d.ts +3 -3
- package/dist/adapters/index.js +889 -960
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs +888 -959
- package/dist/adapters/index.mjs.map +1 -1
- package/dist/adapters/memory.adapter.d.mts +52 -98
- package/dist/adapters/memory.adapter.d.ts +52 -98
- package/dist/adapters/memory.adapter.js +471 -473
- package/dist/adapters/memory.adapter.js.map +1 -1
- package/dist/adapters/memory.adapter.mjs +471 -473
- package/dist/adapters/memory.adapter.mjs.map +1 -1
- package/dist/index.d.mts +485 -958
- package/dist/index.d.ts +485 -958
- package/dist/index.js +2053 -917
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2051 -917
- package/dist/index.mjs.map +1 -1
- package/package.json +34 -16
- package/dist/adapter-CcQCatSa.d.mts +0 -1411
- package/dist/adapter-CcQCatSa.d.ts +0 -1411
|
@@ -1,596 +1,498 @@
|
|
|
1
|
+
import { createBullMQAdapter } from '@igniter-js/adapter-bullmq';
|
|
1
2
|
import { IgniterError } from '@igniter-js/core';
|
|
2
3
|
|
|
3
|
-
// src/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (Error.captureStackTrace) {
|
|
19
|
-
Error.captureStackTrace(this, _IgniterJobsError);
|
|
20
|
-
}
|
|
4
|
+
// src/adapters/bullmq.adapter.ts
|
|
5
|
+
|
|
6
|
+
// src/utils/prefix.ts
|
|
7
|
+
var _IgniterJobsPrefix = class _IgniterJobsPrefix {
|
|
8
|
+
/**
|
|
9
|
+
* Builds a normalized queue name using the global prefix and queue id.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const name = IgniterJobsPrefix.buildQueueName('email')
|
|
14
|
+
* // -> igniter:jobs:email
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
static buildQueueName(queue) {
|
|
18
|
+
return `${_IgniterJobsPrefix.BASE_PREFIX}:${queue}`;
|
|
21
19
|
}
|
|
22
20
|
/**
|
|
23
|
-
*
|
|
21
|
+
* Builds the event channel used for pub/sub.
|
|
22
|
+
*
|
|
23
|
+
* Unscoped events are published to a global channel per service/environment.
|
|
24
|
+
* Scoped events are also published to an additional channel for that scope.
|
|
24
25
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
static buildEventsChannel(params) {
|
|
27
|
+
const base = `${_IgniterJobsPrefix.BASE_PREFIX}:events:${params.environment}:${params.service}`;
|
|
28
|
+
if (!params.scope) return base;
|
|
29
|
+
return `${base}:scope:${params.scope.type}:${params.scope.id}`;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
_IgniterJobsPrefix.BASE_PREFIX = "igniter:jobs";
|
|
33
|
+
var IgniterJobsPrefix = _IgniterJobsPrefix;
|
|
34
|
+
var IgniterJobsError = class extends IgniterError {
|
|
35
|
+
constructor(options) {
|
|
36
|
+
super(options);
|
|
34
37
|
}
|
|
35
38
|
};
|
|
36
39
|
|
|
37
40
|
// src/adapters/bullmq.adapter.ts
|
|
38
|
-
|
|
41
|
+
function toDateArray(values) {
|
|
42
|
+
if (!values) return void 0;
|
|
43
|
+
return values.map((v) => v instanceof Date ? v : new Date(v));
|
|
44
|
+
}
|
|
45
|
+
var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
|
|
39
46
|
constructor(options) {
|
|
40
|
-
this.
|
|
41
|
-
this.
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
47
|
+
this.subscribers = /* @__PURE__ */ new Map();
|
|
48
|
+
this.coreAdapter = null;
|
|
49
|
+
this.coreExecutor = null;
|
|
50
|
+
this.executorDirty = true;
|
|
51
|
+
this.jobsByQueue = /* @__PURE__ */ new Map();
|
|
52
|
+
this.cronsByQueue = /* @__PURE__ */ new Map();
|
|
44
53
|
this.redis = options.redis;
|
|
54
|
+
this.publisher = this.redis;
|
|
55
|
+
this.subscriber = this.redis.duplicate();
|
|
56
|
+
this.client = { redis: this.redis };
|
|
57
|
+
this.subscriber.on("message", (channel, message) => {
|
|
58
|
+
const set = this.subscribers.get(channel);
|
|
59
|
+
if (!set || set.size === 0) return;
|
|
60
|
+
let payload = message;
|
|
61
|
+
try {
|
|
62
|
+
payload = JSON.parse(message);
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
for (const handler of set) handler(payload);
|
|
66
|
+
});
|
|
67
|
+
this.queues = {
|
|
68
|
+
list: async () => this.listQueues(),
|
|
69
|
+
get: async (name) => this.getQueueInfo(name),
|
|
70
|
+
getJobCounts: async (name) => this.getQueueJobCounts(name),
|
|
71
|
+
getJobs: async (name, filter) => {
|
|
72
|
+
const full = this.toCoreQueueName(name);
|
|
73
|
+
return this.core().queues.getJobs(full, filter);
|
|
74
|
+
},
|
|
75
|
+
pause: async (name) => this.pauseQueue(name),
|
|
76
|
+
resume: async (name) => this.resumeQueue(name),
|
|
77
|
+
isPaused: async (name) => {
|
|
78
|
+
const full = this.toCoreQueueName(name);
|
|
79
|
+
return this.core().queues.isPaused(full);
|
|
80
|
+
},
|
|
81
|
+
drain: async (name) => this.drainQueue(name),
|
|
82
|
+
clean: async (name, options2) => this.cleanQueue(name, options2),
|
|
83
|
+
obliterate: async (name, options2) => this.obliterateQueue(name, options2)
|
|
84
|
+
};
|
|
45
85
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Create a new BullMQ adapter.
|
|
48
|
-
*
|
|
49
|
-
* @param options - Adapter options with Redis connection
|
|
50
|
-
* @returns A new BullMQAdapter instance
|
|
51
|
-
*/
|
|
52
86
|
static create(options) {
|
|
53
|
-
return new
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Get the underlying Redis client.
|
|
57
|
-
*/
|
|
58
|
-
get client() {
|
|
59
|
-
return this.redis;
|
|
87
|
+
return new _IgniterJobsBullMQAdapter(options);
|
|
60
88
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
return this.BullMQ;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Get or create a queue instance.
|
|
72
|
-
*/
|
|
73
|
-
async getOrCreateQueue(name) {
|
|
74
|
-
if (!this.queues.has(name)) {
|
|
75
|
-
const { Queue } = await this.getBullMQ();
|
|
76
|
-
const queue = new Queue(name, {
|
|
77
|
-
connection: this.redis
|
|
89
|
+
registerJob(queueName, jobName, definition) {
|
|
90
|
+
const map = this.jobsByQueue.get(queueName) ?? /* @__PURE__ */ new Map();
|
|
91
|
+
if (map.has(jobName)) {
|
|
92
|
+
throw new IgniterJobsError({
|
|
93
|
+
code: "JOBS_DUPLICATE_JOB",
|
|
94
|
+
message: `Job "${jobName}" is already registered in queue "${queueName}".`
|
|
78
95
|
});
|
|
79
|
-
this.queues.set(name, queue);
|
|
80
96
|
}
|
|
81
|
-
|
|
97
|
+
map.set(jobName, definition);
|
|
98
|
+
this.jobsByQueue.set(queueName, map);
|
|
99
|
+
this.executorDirty = true;
|
|
82
100
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const events = new QueueEvents(name, {
|
|
90
|
-
connection: this.redis
|
|
101
|
+
registerCron(queueName, cronName, definition) {
|
|
102
|
+
const map = this.cronsByQueue.get(queueName) ?? /* @__PURE__ */ new Map();
|
|
103
|
+
if (map.has(cronName)) {
|
|
104
|
+
throw new IgniterJobsError({
|
|
105
|
+
code: "JOBS_INVALID_CRON",
|
|
106
|
+
message: `Cron "${cronName}" is already registered in queue "${queueName}".`
|
|
91
107
|
});
|
|
92
|
-
this.queueEvents.set(name, events);
|
|
93
108
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
* Convert BullMQ job state to IgniterJobStatus.
|
|
98
|
-
*/
|
|
99
|
-
mapJobState(state) {
|
|
100
|
-
const stateMap = {
|
|
101
|
-
waiting: "waiting",
|
|
102
|
-
active: "active",
|
|
103
|
-
completed: "completed",
|
|
104
|
-
failed: "failed",
|
|
105
|
-
delayed: "delayed",
|
|
106
|
-
paused: "paused",
|
|
107
|
-
"waiting-children": "waiting"
|
|
108
|
-
};
|
|
109
|
-
return stateMap[state] || "waiting";
|
|
109
|
+
map.set(cronName, definition);
|
|
110
|
+
this.cronsByQueue.set(queueName, map);
|
|
111
|
+
this.executorDirty = true;
|
|
110
112
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Convert BullMQ job to IgniterJobInfo.
|
|
113
|
-
*/
|
|
114
|
-
async mapJobToInfo(job) {
|
|
115
|
-
const data = job.data;
|
|
116
|
-
const state = await job.getState();
|
|
117
|
-
return {
|
|
118
|
-
id: job.id,
|
|
119
|
-
name: job.name,
|
|
120
|
-
queue: job.queueName,
|
|
121
|
-
state: this.mapJobState(state || "waiting"),
|
|
122
|
-
data: data.input ?? data,
|
|
123
|
-
result: job.returnvalue,
|
|
124
|
-
error: job.failedReason,
|
|
125
|
-
progress: typeof job.progress === "number" ? job.progress : 0,
|
|
126
|
-
attempts: job.attemptsMade,
|
|
127
|
-
timestamp: job.timestamp,
|
|
128
|
-
processedOn: job.processedOn,
|
|
129
|
-
finishedOn: job.finishedOn,
|
|
130
|
-
delay: job.delay,
|
|
131
|
-
priority: job.opts?.priority,
|
|
132
|
-
scope: data.scope,
|
|
133
|
-
actor: data.actor,
|
|
134
|
-
metadata: data.metadata
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
// ==========================================
|
|
138
|
-
// JOB OPERATIONS
|
|
139
|
-
// ==========================================
|
|
140
113
|
async dispatch(params) {
|
|
141
|
-
const
|
|
142
|
-
const
|
|
114
|
+
const executor = await this.executor();
|
|
115
|
+
const namespace = executor[params.queue];
|
|
116
|
+
if (!namespace) {
|
|
117
|
+
throw new IgniterJobsError({
|
|
118
|
+
code: "JOBS_QUEUE_NOT_FOUND",
|
|
119
|
+
message: `Queue "${params.queue}" is not registered in the adapter.`
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return namespace.enqueue({
|
|
123
|
+
task: params.jobName,
|
|
124
|
+
input: params.input,
|
|
143
125
|
jobId: params.jobId,
|
|
144
126
|
delay: params.delay,
|
|
145
127
|
priority: params.priority,
|
|
146
|
-
attempts: params.attempts
|
|
147
|
-
|
|
148
|
-
type: params.backoff.type === "custom" ? "fixed" : params.backoff.type,
|
|
149
|
-
delay: params.backoff.type === "custom" ? params.backoff.delays[0] : params.backoff.delay
|
|
150
|
-
} : void 0,
|
|
128
|
+
attempts: params.attempts,
|
|
129
|
+
metadata: params.metadata,
|
|
151
130
|
removeOnComplete: params.removeOnComplete,
|
|
152
|
-
removeOnFail: params.removeOnFail
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
input: params.data,
|
|
156
|
-
scope: params.scope,
|
|
157
|
-
actor: params.actor
|
|
158
|
-
};
|
|
159
|
-
const job = await queue.add(params.name, jobData, jobOptions);
|
|
160
|
-
return job.id;
|
|
131
|
+
removeOnFail: params.removeOnFail,
|
|
132
|
+
limiter: params.limiter
|
|
133
|
+
});
|
|
161
134
|
}
|
|
162
135
|
async schedule(params) {
|
|
163
|
-
const
|
|
164
|
-
const
|
|
136
|
+
const executor = await this.executor();
|
|
137
|
+
const namespace = executor[params.queue];
|
|
138
|
+
if (!namespace) {
|
|
139
|
+
throw new IgniterJobsError({
|
|
140
|
+
code: "JOBS_QUEUE_NOT_FOUND",
|
|
141
|
+
message: `Queue "${params.queue}" is not registered in the adapter.`
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const schedule = {
|
|
165
145
|
jobId: params.jobId,
|
|
166
|
-
delay: params.
|
|
146
|
+
delay: params.delay,
|
|
167
147
|
priority: params.priority,
|
|
168
|
-
attempts: params.attempts
|
|
169
|
-
|
|
170
|
-
type: params.backoff.type === "custom" ? "fixed" : params.backoff.type,
|
|
171
|
-
delay: params.backoff.type === "custom" ? params.backoff.delays[0] : params.backoff.delay
|
|
172
|
-
} : void 0,
|
|
148
|
+
attempts: params.attempts,
|
|
149
|
+
metadata: params.metadata,
|
|
173
150
|
removeOnComplete: params.removeOnComplete,
|
|
174
151
|
removeOnFail: params.removeOnFail,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
every: params.every
|
|
152
|
+
limiter: params.limiter,
|
|
153
|
+
at: params.at,
|
|
154
|
+
repeat: params.cron || params.every || params.maxExecutions || params.skipWeekends || params.onlyBusinessHours || params.businessHours || params.onlyWeekdays || params.skipDates ? {
|
|
155
|
+
cron: params.cron,
|
|
156
|
+
every: params.every,
|
|
157
|
+
times: params.maxExecutions,
|
|
158
|
+
skipWeekends: params.skipWeekends,
|
|
159
|
+
onlyBusinessHours: params.onlyBusinessHours,
|
|
160
|
+
businessHours: params.businessHours,
|
|
161
|
+
onlyWeekdays: params.onlyWeekdays,
|
|
162
|
+
skipDates: toDateArray(params.skipDates)
|
|
180
163
|
} : void 0
|
|
181
164
|
};
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
};
|
|
187
|
-
const job = await queue.add(params.name, jobData, jobOptions);
|
|
188
|
-
return job.id;
|
|
189
|
-
}
|
|
190
|
-
async getJob(queue, jobId) {
|
|
191
|
-
const q = await this.getOrCreateQueue(queue);
|
|
192
|
-
const job = await q.getJob(jobId);
|
|
193
|
-
if (!job) return null;
|
|
194
|
-
return await this.mapJobToInfo(job);
|
|
195
|
-
}
|
|
196
|
-
async getJobState(queue, jobId) {
|
|
197
|
-
const q = await this.getOrCreateQueue(queue);
|
|
198
|
-
const job = await q.getJob(jobId);
|
|
199
|
-
if (!job) return null;
|
|
200
|
-
const state = await job.getState();
|
|
201
|
-
return this.mapJobState(state);
|
|
202
|
-
}
|
|
203
|
-
async getJobProgress(queue, jobId) {
|
|
204
|
-
const q = await this.getOrCreateQueue(queue);
|
|
205
|
-
const job = await q.getJob(jobId);
|
|
206
|
-
if (!job) return 0;
|
|
207
|
-
return typeof job.progress === "number" ? job.progress : 0;
|
|
208
|
-
}
|
|
209
|
-
async getJobLogs(queue, jobId) {
|
|
210
|
-
const q = await this.getOrCreateQueue(queue);
|
|
211
|
-
const job = await q.getJob(jobId);
|
|
212
|
-
if (!job) return [];
|
|
213
|
-
const { logs } = await q.getJobLogs(jobId);
|
|
214
|
-
return logs.map((log, index) => ({
|
|
215
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
216
|
-
message: log,
|
|
217
|
-
level: "info"
|
|
218
|
-
}));
|
|
219
|
-
}
|
|
220
|
-
async retryJob(queue, jobId) {
|
|
221
|
-
const q = await this.getOrCreateQueue(queue);
|
|
222
|
-
const job = await q.getJob(jobId);
|
|
223
|
-
if (!job) {
|
|
224
|
-
throw new IgniterJobsError({
|
|
225
|
-
code: "JOBS_JOB_NOT_FOUND",
|
|
226
|
-
message: `Job "${jobId}" not found in queue "${queue}"`,
|
|
227
|
-
statusCode: 404
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
await job.retry();
|
|
231
|
-
}
|
|
232
|
-
async removeJob(queue, jobId) {
|
|
233
|
-
const q = await this.getOrCreateQueue(queue);
|
|
234
|
-
const job = await q.getJob(jobId);
|
|
235
|
-
if (!job) return;
|
|
236
|
-
await job.remove();
|
|
237
|
-
}
|
|
238
|
-
async promoteJob(queue, jobId) {
|
|
239
|
-
const q = await this.getOrCreateQueue(queue);
|
|
240
|
-
const job = await q.getJob(jobId);
|
|
241
|
-
if (!job) {
|
|
242
|
-
throw new IgniterJobsError({
|
|
243
|
-
code: "JOBS_JOB_NOT_FOUND",
|
|
244
|
-
message: `Job "${jobId}" not found in queue "${queue}"`,
|
|
245
|
-
statusCode: 404
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
await job.promote();
|
|
165
|
+
return namespace.schedule({
|
|
166
|
+
task: params.jobName,
|
|
167
|
+
input: params.input,
|
|
168
|
+
...schedule
|
|
169
|
+
});
|
|
249
170
|
}
|
|
250
|
-
async
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
if (!job) {
|
|
254
|
-
throw new IgniterJobsError({
|
|
255
|
-
code: "JOBS_JOB_NOT_FOUND",
|
|
256
|
-
message: `Job "${jobId}" not found in queue "${queue}"`,
|
|
257
|
-
statusCode: 404
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
if (state === "failed") {
|
|
261
|
-
await job.moveToFailed(new Error(reason || "Manually moved to failed"), "manual");
|
|
262
|
-
} else {
|
|
263
|
-
await job.moveToCompleted(reason || "Manually completed", "manual");
|
|
264
|
-
}
|
|
171
|
+
async getJob(jobId, queue) {
|
|
172
|
+
const result = await this.core().job.get(jobId, queue ? this.toCoreQueueName(queue) : void 0);
|
|
173
|
+
return result ? this.mapJob(result, queue) : null;
|
|
265
174
|
}
|
|
266
|
-
async
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
175
|
+
async getJobState(jobId, queue) {
|
|
176
|
+
const state = await this.core().job.getState(jobId, queue ? this.toCoreQueueName(queue) : void 0);
|
|
177
|
+
return state;
|
|
178
|
+
}
|
|
179
|
+
async getJobLogs(jobId, queue) {
|
|
180
|
+
const logs = await this.core().job.getLogs(jobId, queue ? this.toCoreQueueName(queue) : void 0);
|
|
181
|
+
return logs;
|
|
182
|
+
}
|
|
183
|
+
async getJobProgress(jobId, queue) {
|
|
184
|
+
return this.core().job.getProgress(jobId, queue ? this.toCoreQueueName(queue) : void 0);
|
|
185
|
+
}
|
|
186
|
+
async retryJob(jobId, queue) {
|
|
187
|
+
await this.core().job.retry(jobId, queue ? this.toCoreQueueName(queue) : void 0);
|
|
188
|
+
}
|
|
189
|
+
async removeJob(jobId, queue) {
|
|
190
|
+
await this.core().job.remove(jobId, queue ? this.toCoreQueueName(queue) : void 0);
|
|
191
|
+
}
|
|
192
|
+
async promoteJob(jobId, queue) {
|
|
193
|
+
await this.core().job.promote(jobId, queue ? this.toCoreQueueName(queue) : void 0);
|
|
194
|
+
}
|
|
195
|
+
async moveJobToFailed(jobId, reason, queue) {
|
|
196
|
+
await this.core().job.moveToFailed(jobId, reason, queue ? this.toCoreQueueName(queue) : void 0);
|
|
197
|
+
}
|
|
198
|
+
async retryManyJobs(jobIds, queue) {
|
|
199
|
+
await this.core().job.retryMany(jobIds, queue ? this.toCoreQueueName(queue) : void 0);
|
|
200
|
+
}
|
|
201
|
+
async removeManyJobs(jobIds, queue) {
|
|
202
|
+
await this.core().job.removeMany(jobIds, queue ? this.toCoreQueueName(queue) : void 0);
|
|
203
|
+
}
|
|
204
|
+
async getQueueInfo(queue) {
|
|
205
|
+
const info = await this.core().queues.get(this.toCoreQueueName(queue));
|
|
206
|
+
if (!info) return null;
|
|
207
|
+
return this.mapQueueInfo(info);
|
|
208
|
+
}
|
|
209
|
+
async getQueueJobCounts(queue) {
|
|
210
|
+
const counts = await this.core().queues.getJobCounts(this.toCoreQueueName(queue));
|
|
211
|
+
return counts;
|
|
212
|
+
}
|
|
213
|
+
async listQueues() {
|
|
214
|
+
const list = await this.core().queues.list();
|
|
215
|
+
return list.map((q) => this.mapQueueInfo(q));
|
|
303
216
|
}
|
|
304
217
|
async pauseQueue(queue) {
|
|
305
|
-
|
|
306
|
-
await q.pause();
|
|
218
|
+
await this.core().queues.pause(this.toCoreQueueName(queue));
|
|
307
219
|
}
|
|
308
220
|
async resumeQueue(queue) {
|
|
309
|
-
|
|
310
|
-
await q.resume();
|
|
221
|
+
await this.core().queues.resume(this.toCoreQueueName(queue));
|
|
311
222
|
}
|
|
312
223
|
async drainQueue(queue) {
|
|
313
|
-
|
|
314
|
-
await q.drain();
|
|
315
|
-
return 0;
|
|
224
|
+
return this.core().queues.drain(this.toCoreQueueName(queue));
|
|
316
225
|
}
|
|
317
226
|
async cleanQueue(queue, options) {
|
|
318
|
-
|
|
319
|
-
const statuses = Array.isArray(options.status) ? options.status : [options.status];
|
|
320
|
-
let total = 0;
|
|
321
|
-
for (const status of statuses) {
|
|
322
|
-
const cleaned = await q.clean(
|
|
323
|
-
options.olderThan ?? 0,
|
|
324
|
-
options.limit ?? 1e3,
|
|
325
|
-
status
|
|
326
|
-
);
|
|
327
|
-
total += cleaned.length;
|
|
328
|
-
}
|
|
329
|
-
return total;
|
|
227
|
+
return this.core().queues.clean(this.toCoreQueueName(queue), options);
|
|
330
228
|
}
|
|
331
229
|
async obliterateQueue(queue, options) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
await Promise.all(failed.map((job) => job.retry()));
|
|
339
|
-
return failed.length;
|
|
340
|
-
}
|
|
341
|
-
async getJobCounts(queue) {
|
|
342
|
-
const q = await this.getOrCreateQueue(queue);
|
|
343
|
-
const counts = await q.getJobCounts();
|
|
344
|
-
return {
|
|
345
|
-
waiting: counts.waiting || 0,
|
|
346
|
-
active: counts.active || 0,
|
|
347
|
-
completed: counts.completed || 0,
|
|
348
|
-
failed: counts.failed || 0,
|
|
349
|
-
delayed: counts.delayed || 0,
|
|
350
|
-
paused: counts.paused || 0
|
|
351
|
-
};
|
|
230
|
+
await this.core().queues.obliterate(this.toCoreQueueName(queue), options);
|
|
231
|
+
}
|
|
232
|
+
async retryAllInQueue(queue) {
|
|
233
|
+
const jobs = await this.core().queues.getJobs(this.toCoreQueueName(queue), { status: ["failed"], limit: 1e3 });
|
|
234
|
+
await Promise.all(jobs.map((j) => this.core().job.retry(j.id, this.toCoreQueueName(queue))));
|
|
235
|
+
return jobs.length;
|
|
352
236
|
}
|
|
353
|
-
async listJobs(queue, options) {
|
|
354
|
-
const q = await this.getOrCreateQueue(queue);
|
|
355
|
-
const statuses = options?.status || ["waiting", "active", "completed", "failed", "delayed"];
|
|
356
|
-
const jobs = [];
|
|
357
|
-
for (const status of statuses) {
|
|
358
|
-
const statusJobs = await q.getJobs([status], options?.start, options?.end);
|
|
359
|
-
jobs.push(...statusJobs);
|
|
360
|
-
}
|
|
361
|
-
return Promise.all(jobs.map(async (job) => {
|
|
362
|
-
const data = job.data;
|
|
363
|
-
const state = await job.getState();
|
|
364
|
-
return {
|
|
365
|
-
id: job.id,
|
|
366
|
-
name: job.name,
|
|
367
|
-
queue: job.queueName,
|
|
368
|
-
state: this.mapJobState(state || "waiting"),
|
|
369
|
-
data: data.input ?? data,
|
|
370
|
-
result: job.returnvalue,
|
|
371
|
-
error: job.failedReason,
|
|
372
|
-
progress: typeof job.progress === "number" ? job.progress : 0,
|
|
373
|
-
attempts: job.attemptsMade,
|
|
374
|
-
timestamp: job.timestamp,
|
|
375
|
-
processedOn: job.processedOn,
|
|
376
|
-
finishedOn: job.finishedOn,
|
|
377
|
-
scope: data.scope,
|
|
378
|
-
actor: data.actor
|
|
379
|
-
};
|
|
380
|
-
}));
|
|
381
|
-
}
|
|
382
|
-
// ==========================================
|
|
383
|
-
// PAUSE/RESUME JOB TYPES
|
|
384
|
-
// ==========================================
|
|
385
237
|
async pauseJobType(queue, jobName) {
|
|
386
|
-
|
|
238
|
+
throw new IgniterJobsError({
|
|
239
|
+
code: "JOBS_QUEUE_OPERATION_FAILED",
|
|
240
|
+
message: "BullMQ backend does not support pausing a single job type; pause the queue or adjust worker filters."
|
|
241
|
+
});
|
|
387
242
|
}
|
|
388
243
|
async resumeJobType(queue, jobName) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
async
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
244
|
+
throw new IgniterJobsError({
|
|
245
|
+
code: "JOBS_QUEUE_OPERATION_FAILED",
|
|
246
|
+
message: "BullMQ backend does not support resuming a single job type; resume the queue or adjust worker filters."
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async searchJobs(filter) {
|
|
250
|
+
const queue = filter?.queue;
|
|
251
|
+
const status = filter?.status;
|
|
252
|
+
const limit = filter?.limit ?? 100;
|
|
253
|
+
const offset = filter?.offset ?? 0;
|
|
254
|
+
if (queue) {
|
|
255
|
+
const jobs = await this.core().queues.getJobs(this.toCoreQueueName(queue), { status, limit, offset });
|
|
256
|
+
return jobs.map((j) => this.mapJob(j, queue));
|
|
257
|
+
}
|
|
258
|
+
const queues = await this.listQueues();
|
|
259
|
+
const results = [];
|
|
260
|
+
for (const q of queues) {
|
|
261
|
+
const jobs = await this.core().queues.getJobs(this.toCoreQueueName(q.name), { status, limit, offset });
|
|
262
|
+
results.push(...jobs.map((j) => this.mapJob(j, q.name)));
|
|
263
|
+
if (results.length >= limit) break;
|
|
264
|
+
}
|
|
265
|
+
return results.slice(0, limit);
|
|
266
|
+
}
|
|
267
|
+
async searchQueues(filter) {
|
|
268
|
+
const all = await this.listQueues();
|
|
269
|
+
const name = filter?.name;
|
|
270
|
+
const isPaused = filter?.isPaused;
|
|
271
|
+
return all.filter((q) => name ? q.name.includes(name) : true).filter((q) => typeof isPaused === "boolean" ? q.isPaused === isPaused : true);
|
|
272
|
+
}
|
|
273
|
+
async searchWorkers(filter) {
|
|
274
|
+
const queue = filter?.queue;
|
|
275
|
+
const isRunning = filter?.isRunning;
|
|
276
|
+
const all = Array.from(this.core().getWorkers().values());
|
|
277
|
+
return all.filter((w) => {
|
|
278
|
+
if (!queue) return true;
|
|
279
|
+
const coreQueue = this.toCoreQueueName(queue);
|
|
280
|
+
const queues = w.config?.queues ?? [w.queueName];
|
|
281
|
+
return Array.isArray(queues) ? queues.includes(coreQueue) : false;
|
|
282
|
+
}).filter((w) => typeof isRunning === "boolean" ? isRunning ? w.isRunning() : !w.isRunning() : true).map((w) => this.mapWorker(w));
|
|
283
|
+
}
|
|
284
|
+
async createWorker(config) {
|
|
285
|
+
await this.executor();
|
|
286
|
+
const queuesSource = config.queues?.length ? config.queues : Array.from(/* @__PURE__ */ new Set([...this.jobsByQueue.keys(), ...this.cronsByQueue.keys()]));
|
|
287
|
+
const queues = queuesSource.map((q) => this.toCoreQueueName(q));
|
|
288
|
+
const coreConfig = {
|
|
289
|
+
queues,
|
|
290
|
+
concurrency: config.concurrency ?? 1,
|
|
291
|
+
limiter: config.limiter,
|
|
292
|
+
onActive: config.handlers?.onActive,
|
|
293
|
+
onSuccess: config.handlers?.onSuccess,
|
|
294
|
+
onFailure: config.handlers?.onFailure,
|
|
295
|
+
onIdle: config.handlers?.onIdle
|
|
296
|
+
};
|
|
297
|
+
const handle = await this.core().worker(coreConfig);
|
|
298
|
+
return this.mapWorker(handle);
|
|
299
|
+
}
|
|
300
|
+
getWorkers() {
|
|
301
|
+
const out = /* @__PURE__ */ new Map();
|
|
302
|
+
for (const [id, handle] of this.core().getWorkers()) out.set(id, this.mapWorker(handle));
|
|
303
|
+
return out;
|
|
304
|
+
}
|
|
305
|
+
async publishEvent(channel, payload) {
|
|
306
|
+
await this.publisher.publish(channel, JSON.stringify(payload));
|
|
307
|
+
}
|
|
308
|
+
async subscribeEvent(channel, handler) {
|
|
309
|
+
const set = this.subscribers.get(channel) ?? /* @__PURE__ */ new Set();
|
|
310
|
+
const wrapped = (payload) => void handler(payload);
|
|
311
|
+
set.add(wrapped);
|
|
312
|
+
this.subscribers.set(channel, set);
|
|
313
|
+
if (set.size === 1) {
|
|
314
|
+
await this.subscriber.subscribe(channel);
|
|
422
315
|
}
|
|
423
316
|
return async () => {
|
|
424
|
-
|
|
317
|
+
const current = this.subscribers.get(channel);
|
|
318
|
+
if (!current) return;
|
|
319
|
+
current.delete(wrapped);
|
|
320
|
+
if (current.size === 0) {
|
|
321
|
+
this.subscribers.delete(channel);
|
|
322
|
+
await this.subscriber.unsubscribe(channel);
|
|
323
|
+
}
|
|
425
324
|
};
|
|
426
325
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
)
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
// ==========================================
|
|
435
|
-
// WORKERS
|
|
436
|
-
// ==========================================
|
|
437
|
-
async createWorker(config, handler) {
|
|
438
|
-
const { Worker } = await this.getBullMQ();
|
|
439
|
-
const workerId = `worker-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
440
|
-
const workers = [];
|
|
441
|
-
for (const queueName of config.queues) {
|
|
442
|
-
const workerOptions = {
|
|
443
|
-
connection: this.redis,
|
|
444
|
-
concurrency: config.concurrency,
|
|
445
|
-
lockDuration: config.lockDuration,
|
|
446
|
-
limiter: config.limiter
|
|
447
|
-
};
|
|
448
|
-
const worker = new Worker(
|
|
449
|
-
queueName,
|
|
450
|
-
async (job) => {
|
|
451
|
-
const data = job.data;
|
|
452
|
-
return handler({
|
|
453
|
-
id: job.id,
|
|
454
|
-
name: job.name,
|
|
455
|
-
queue: job.queueName,
|
|
456
|
-
data: data.input ?? data,
|
|
457
|
-
attempt: job.attemptsMade + 1,
|
|
458
|
-
timestamp: job.timestamp,
|
|
459
|
-
scope: data.scope,
|
|
460
|
-
actor: data.actor,
|
|
461
|
-
log: async (level, message) => {
|
|
462
|
-
await job.log(`[${level.toUpperCase()}] ${message}`);
|
|
463
|
-
},
|
|
464
|
-
updateProgress: async (progress) => {
|
|
465
|
-
await job.updateProgress(progress);
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
},
|
|
469
|
-
workerOptions
|
|
470
|
-
);
|
|
471
|
-
if (config.onIdle) {
|
|
472
|
-
worker.on("drained", config.onIdle);
|
|
473
|
-
}
|
|
474
|
-
workers.push(worker);
|
|
475
|
-
this.workers.set(`${workerId}-${queueName}`, worker);
|
|
476
|
-
}
|
|
477
|
-
let isPaused = false;
|
|
478
|
-
const startTime = Date.now();
|
|
479
|
-
let processed = 0;
|
|
480
|
-
let failed = 0;
|
|
481
|
-
let completed = 0;
|
|
482
|
-
for (const worker of workers) {
|
|
483
|
-
worker.on("completed", () => {
|
|
484
|
-
processed++;
|
|
485
|
-
completed++;
|
|
486
|
-
});
|
|
487
|
-
worker.on("failed", () => {
|
|
488
|
-
processed++;
|
|
489
|
-
failed++;
|
|
326
|
+
async shutdown() {
|
|
327
|
+
await this.subscriber.quit();
|
|
328
|
+
}
|
|
329
|
+
core() {
|
|
330
|
+
if (!this.coreAdapter) {
|
|
331
|
+
this.coreAdapter = createBullMQAdapter({
|
|
332
|
+
store: { client: this.redis }
|
|
490
333
|
});
|
|
491
334
|
}
|
|
492
|
-
return
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
)?.[0];
|
|
508
|
-
if (key) this.workers.delete(key);
|
|
335
|
+
return this.coreAdapter;
|
|
336
|
+
}
|
|
337
|
+
async executor() {
|
|
338
|
+
if (!this.executorDirty && this.coreExecutor) return this.coreExecutor;
|
|
339
|
+
const routers = {};
|
|
340
|
+
const flattened = {};
|
|
341
|
+
const allQueues = /* @__PURE__ */ new Set([...this.jobsByQueue.keys(), ...this.cronsByQueue.keys()]);
|
|
342
|
+
for (const queueName of allQueues) {
|
|
343
|
+
const coreJobs = {};
|
|
344
|
+
const jobs = this.jobsByQueue.get(queueName);
|
|
345
|
+
if (jobs) {
|
|
346
|
+
for (const [jobName, def] of jobs.entries()) {
|
|
347
|
+
const queue = def.queue ? `${queueName}.${def.queue}` : queueName;
|
|
348
|
+
const fullQueue = IgniterJobsPrefix.buildQueueName(queue);
|
|
349
|
+
coreJobs[jobName] = this.toCoreJobDefinition(queueName, jobName, def, fullQueue);
|
|
509
350
|
}
|
|
510
|
-
},
|
|
511
|
-
isRunning: () => !isPaused && workers.every((w) => w.isRunning()),
|
|
512
|
-
isPaused: () => isPaused,
|
|
513
|
-
getMetrics: async () => ({
|
|
514
|
-
processed,
|
|
515
|
-
failed,
|
|
516
|
-
completed,
|
|
517
|
-
active: workers.reduce((sum, w) => sum + (w.isRunning() ? 1 : 0), 0),
|
|
518
|
-
uptime: Date.now() - startTime
|
|
519
|
-
})
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
// ==========================================
|
|
523
|
-
// SEARCH
|
|
524
|
-
// ==========================================
|
|
525
|
-
async searchJobs(filter) {
|
|
526
|
-
const results = [];
|
|
527
|
-
const queuesToSearch = filter.queue ? [filter.queue] : Array.from(this.queues.keys());
|
|
528
|
-
for (const queueName of queuesToSearch) {
|
|
529
|
-
const jobs = await this.listJobs(queueName, {
|
|
530
|
-
status: filter.status,
|
|
531
|
-
start: filter.offset,
|
|
532
|
-
end: filter.limit ? (filter.offset || 0) + filter.limit : void 0
|
|
533
|
-
});
|
|
534
|
-
for (const job of jobs) {
|
|
535
|
-
if (filter.jobName && job.name !== filter.jobName) continue;
|
|
536
|
-
if (filter.scopeId && job.scope?.id !== filter.scopeId) continue;
|
|
537
|
-
if (filter.actorId && job.actor?.id !== filter.actorId) continue;
|
|
538
|
-
if (filter.dateRange?.from && job.timestamp < filter.dateRange.from.getTime()) continue;
|
|
539
|
-
if (filter.dateRange?.to && job.timestamp > filter.dateRange.to.getTime()) continue;
|
|
540
|
-
results.push(job);
|
|
541
351
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
352
|
+
const crons = this.cronsByQueue.get(queueName);
|
|
353
|
+
if (crons) {
|
|
354
|
+
for (const [cronName, def] of crons.entries()) {
|
|
355
|
+
const fullQueue = IgniterJobsPrefix.buildQueueName(queueName);
|
|
356
|
+
coreJobs[cronName] = this.toCoreCronJobDefinition(queueName, cronName, def, fullQueue);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (Object.keys(coreJobs).length === 0) continue;
|
|
360
|
+
routers[queueName] = this.core().router({
|
|
361
|
+
jobs: coreJobs,
|
|
362
|
+
namespace: queueName
|
|
549
363
|
});
|
|
364
|
+
for (const [jobName, def] of Object.entries(coreJobs)) {
|
|
365
|
+
flattened[`${queueName}.${jobName}`] = def;
|
|
366
|
+
}
|
|
550
367
|
}
|
|
551
|
-
|
|
368
|
+
await this.core().bulkRegister(flattened);
|
|
369
|
+
this.coreExecutor = this.core().merge(routers);
|
|
370
|
+
this.executorDirty = false;
|
|
371
|
+
return this.coreExecutor;
|
|
552
372
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
373
|
+
toCoreQueueName(queueName) {
|
|
374
|
+
return IgniterJobsPrefix.buildQueueName(queueName);
|
|
375
|
+
}
|
|
376
|
+
mapQueueInfo(info) {
|
|
377
|
+
return {
|
|
378
|
+
name: this.fromCoreQueueName(info.name),
|
|
379
|
+
isPaused: info.isPaused,
|
|
380
|
+
jobCounts: info.jobCounts
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
fromCoreQueueName(full) {
|
|
384
|
+
const prefix = `${IgniterJobsPrefix.BASE_PREFIX}:`;
|
|
385
|
+
return full.startsWith(prefix) ? full.slice(prefix.length) : full;
|
|
386
|
+
}
|
|
387
|
+
mapJob(job, queue) {
|
|
388
|
+
const q = queue ?? this.fromCoreQueueName(job.metadata?.queue ?? job.queueName ?? "");
|
|
389
|
+
const scope = job.metadata?.__igniter_jobs_scope;
|
|
390
|
+
return {
|
|
391
|
+
id: job.id,
|
|
392
|
+
name: job.name,
|
|
393
|
+
queue: q,
|
|
394
|
+
status: job.status,
|
|
395
|
+
input: job.payload,
|
|
396
|
+
result: job.result,
|
|
397
|
+
error: job.error,
|
|
398
|
+
progress: 0,
|
|
399
|
+
attemptsMade: job.attemptsMade ?? 0,
|
|
400
|
+
priority: job.priority ?? 0,
|
|
401
|
+
createdAt: job.createdAt,
|
|
402
|
+
startedAt: job.processedAt,
|
|
403
|
+
completedAt: job.completedAt,
|
|
404
|
+
metadata: job.metadata,
|
|
405
|
+
scope
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
mapWorker(handle) {
|
|
409
|
+
const queues = handle.config?.queues ?? [handle.queueName];
|
|
410
|
+
return {
|
|
411
|
+
id: handle.id,
|
|
412
|
+
queues: queues.map((q) => this.fromCoreQueueName(q)),
|
|
413
|
+
pause: () => handle.pause(),
|
|
414
|
+
resume: () => handle.resume(),
|
|
415
|
+
close: () => handle.close(),
|
|
416
|
+
isRunning: () => handle.isRunning(),
|
|
417
|
+
isPaused: () => handle.isPaused(),
|
|
418
|
+
isClosed: () => handle.isClosed(),
|
|
419
|
+
getMetrics: async () => handle.getMetrics()
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
toCoreJobDefinition(queueName, jobName, def, fullQueueName) {
|
|
423
|
+
const handler = async (ctx) => {
|
|
424
|
+
return def.handler({
|
|
425
|
+
input: ctx.input,
|
|
426
|
+
context: ctx.context,
|
|
427
|
+
job: {
|
|
428
|
+
id: ctx.job.id,
|
|
429
|
+
name: jobName,
|
|
430
|
+
queue: queueName,
|
|
431
|
+
attemptsMade: ctx.job.attemptsMade,
|
|
432
|
+
createdAt: ctx.job.createdAt,
|
|
433
|
+
metadata: ctx.job.metadata
|
|
434
|
+
},
|
|
435
|
+
scope: ctx.job.metadata?.__igniter_jobs_scope
|
|
571
436
|
});
|
|
572
|
-
}
|
|
573
|
-
return
|
|
437
|
+
};
|
|
438
|
+
return {
|
|
439
|
+
name: jobName,
|
|
440
|
+
input: def.input,
|
|
441
|
+
handler,
|
|
442
|
+
queue: { name: fullQueueName },
|
|
443
|
+
attempts: def.attempts,
|
|
444
|
+
priority: def.priority,
|
|
445
|
+
delay: def.delay,
|
|
446
|
+
removeOnComplete: def.removeOnComplete,
|
|
447
|
+
removeOnFail: def.removeOnFail,
|
|
448
|
+
metadata: def.metadata,
|
|
449
|
+
limiter: def.limiter,
|
|
450
|
+
onStart: def.onStart,
|
|
451
|
+
onSuccess: def.onSuccess,
|
|
452
|
+
onFailure: def.onFailure,
|
|
453
|
+
onProgress: def.onProgress
|
|
454
|
+
};
|
|
574
455
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
}
|
|
590
|
-
|
|
456
|
+
toCoreCronJobDefinition(queueName, cronName, def, fullQueueName) {
|
|
457
|
+
const handler = async (ctx) => {
|
|
458
|
+
return def.handler({
|
|
459
|
+
context: ctx.context,
|
|
460
|
+
job: {
|
|
461
|
+
id: ctx.job.id,
|
|
462
|
+
name: cronName,
|
|
463
|
+
queue: queueName,
|
|
464
|
+
attemptsMade: ctx.job.attemptsMade,
|
|
465
|
+
createdAt: ctx.job.createdAt,
|
|
466
|
+
metadata: ctx.job.metadata
|
|
467
|
+
},
|
|
468
|
+
scope: ctx.job.metadata?.__igniter_jobs_scope
|
|
469
|
+
});
|
|
470
|
+
};
|
|
471
|
+
return {
|
|
472
|
+
name: cronName,
|
|
473
|
+
handler,
|
|
474
|
+
queue: { name: fullQueueName },
|
|
475
|
+
repeat: {
|
|
476
|
+
cron: def.cron,
|
|
477
|
+
tz: def.tz,
|
|
478
|
+
limit: def.maxExecutions,
|
|
479
|
+
startDate: def.startDate,
|
|
480
|
+
endDate: def.endDate
|
|
481
|
+
},
|
|
482
|
+
metadata: def.onlyBusinessHours || def.skipWeekends || def.businessHours || def.onlyWeekdays || def.skipDates || def.startDate && def.endDate ? {
|
|
483
|
+
advancedScheduling: {
|
|
484
|
+
onlyBusinessHours: def.onlyBusinessHours,
|
|
485
|
+
skipWeekends: def.skipWeekends,
|
|
486
|
+
businessHours: def.businessHours,
|
|
487
|
+
skipDates: toDateArray(def.skipDates),
|
|
488
|
+
onlyWeekdays: def.onlyWeekdays,
|
|
489
|
+
between: def.startDate && def.endDate ? [def.startDate, def.endDate] : void 0
|
|
490
|
+
}
|
|
491
|
+
} : void 0
|
|
492
|
+
};
|
|
591
493
|
}
|
|
592
494
|
};
|
|
593
495
|
|
|
594
|
-
export {
|
|
496
|
+
export { IgniterJobsBullMQAdapter };
|
|
595
497
|
//# sourceMappingURL=bullmq.adapter.mjs.map
|
|
596
498
|
//# sourceMappingURL=bullmq.adapter.mjs.map
|