@arkstack/queue 0.18.3 → 0.18.4
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/dist/{QueueManager-C1GIAwK2.js → QueueManager-Dm9FLISk.js} +30 -0
- package/dist/commands/QueueClearCommand.js +1 -1
- package/dist/commands/QueueWorkCommand.d.ts +14 -0
- package/dist/commands/QueueWorkCommand.js +43 -12
- package/dist/index.d.ts +34 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
|
@@ -145,6 +145,13 @@ var QueueContract = class {
|
|
|
145
145
|
this.connectionName = name;
|
|
146
146
|
return this;
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* The queue this connection works when none is named. Drivers with a
|
|
150
|
+
* configurable queue override it.
|
|
151
|
+
*/
|
|
152
|
+
getDefaultQueue() {
|
|
153
|
+
return "default";
|
|
154
|
+
}
|
|
148
155
|
};
|
|
149
156
|
//#endregion
|
|
150
157
|
//#region src/drivers/DatabaseQueue.ts
|
|
@@ -167,6 +174,10 @@ var DatabaseQueue = class extends QueueContract {
|
|
|
167
174
|
this.options = options;
|
|
168
175
|
}
|
|
169
176
|
get defaultQueue() {
|
|
177
|
+
return this.getDefaultQueue();
|
|
178
|
+
}
|
|
179
|
+
/** The configured queue this connection works when none is named. */
|
|
180
|
+
getDefaultQueue() {
|
|
170
181
|
return this.options.queue ?? "default";
|
|
171
182
|
}
|
|
172
183
|
async database() {
|
|
@@ -263,6 +274,10 @@ var RedisQueue = class extends QueueContract {
|
|
|
263
274
|
this.options = options;
|
|
264
275
|
}
|
|
265
276
|
get defaultQueue() {
|
|
277
|
+
return this.getDefaultQueue();
|
|
278
|
+
}
|
|
279
|
+
/** The configured queue this connection works when none is named. */
|
|
280
|
+
getDefaultQueue() {
|
|
266
281
|
return this.options.queue ?? "default";
|
|
267
282
|
}
|
|
268
283
|
get retryAfter() {
|
|
@@ -415,6 +430,7 @@ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
415
430
|
var Worker = class {
|
|
416
431
|
connection;
|
|
417
432
|
shouldStop = false;
|
|
433
|
+
handlers = {};
|
|
418
434
|
constructor(connection) {
|
|
419
435
|
this.connection = connection;
|
|
420
436
|
}
|
|
@@ -423,6 +439,16 @@ var Worker = class {
|
|
|
423
439
|
this.shouldStop = true;
|
|
424
440
|
}
|
|
425
441
|
/**
|
|
442
|
+
* Observe what the worker does with each job. Handlers merge, so they can be
|
|
443
|
+
* registered in more than one call.
|
|
444
|
+
*
|
|
445
|
+
* @param handlers The callbacks to add.
|
|
446
|
+
*/
|
|
447
|
+
on(handlers) {
|
|
448
|
+
Object.assign(this.handlers, handlers);
|
|
449
|
+
return this;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
426
452
|
* Pop and process the next job. Returns `true` if a job was processed,
|
|
427
453
|
* `false` when the queue was empty.
|
|
428
454
|
*/
|
|
@@ -437,8 +463,10 @@ var Worker = class {
|
|
|
437
463
|
*/
|
|
438
464
|
async process(job) {
|
|
439
465
|
try {
|
|
466
|
+
await this.handlers.onProcessing?.(job);
|
|
440
467
|
await (await resolveJob(job.payload)).handle();
|
|
441
468
|
await job.delete();
|
|
469
|
+
await this.handlers.onProcessed?.(job);
|
|
442
470
|
} catch (error) {
|
|
443
471
|
await this.handleFailure(job, error);
|
|
444
472
|
}
|
|
@@ -469,9 +497,11 @@ var Worker = class {
|
|
|
469
497
|
await (await resolveJob(job.payload)).failed?.(error);
|
|
470
498
|
} catch {}
|
|
471
499
|
await job.delete();
|
|
500
|
+
await this.handlers.onFailed?.(job, error, false);
|
|
472
501
|
return;
|
|
473
502
|
}
|
|
474
503
|
await job.release(job.backoff());
|
|
504
|
+
await this.handlers.onFailed?.(job, error, true);
|
|
475
505
|
}
|
|
476
506
|
};
|
|
477
507
|
//#endregion
|
|
@@ -7,6 +7,20 @@ declare class QueueWorkCommand extends Command {
|
|
|
7
7
|
protected signature: string;
|
|
8
8
|
protected description: string;
|
|
9
9
|
handle(): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Read a multi-word flag. Musket hands over the parsed options camelCased,
|
|
12
|
+
* so `--stop-when-empty` arrives as `stopWhenEmpty`; the kebab-case name is
|
|
13
|
+
* accepted too so the flag can't go quietly missing again.
|
|
14
|
+
*
|
|
15
|
+
* @param camel The camelCase key.
|
|
16
|
+
* @param kebab The flag as written in the signature.
|
|
17
|
+
*/
|
|
18
|
+
private flag;
|
|
19
|
+
/**
|
|
20
|
+
* Register the application's job classes with `@arkstack/jobs` when it is
|
|
21
|
+
* installed. Without it a payload cannot be turned back into a job.
|
|
22
|
+
*/
|
|
23
|
+
private registerJobs;
|
|
10
24
|
}
|
|
11
25
|
//#endregion
|
|
12
26
|
export { QueueWorkCommand };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { t as Queue } from "../QueueManager-
|
|
1
|
+
import { r as Worker, t as Queue } from "../QueueManager-Dm9FLISk.js";
|
|
2
2
|
import { Command } from "@h3ravel/musket";
|
|
3
3
|
import { bootArkorm } from "@arkstack/database";
|
|
4
4
|
//#region src/commands/QueueWorkCommand.ts
|
|
5
|
+
/** The message of a thrown value, whatever it is. */
|
|
6
|
+
const reason = (error) => error instanceof Error ? error.message : String(error);
|
|
5
7
|
/**
|
|
6
8
|
* Process jobs from a queue connection.
|
|
7
9
|
*/
|
|
@@ -16,25 +18,54 @@ var QueueWorkCommand = class extends Command {
|
|
|
16
18
|
`;
|
|
17
19
|
description = "Start processing jobs on the queue as a daemon.";
|
|
18
20
|
async handle() {
|
|
19
|
-
const connection = this.argument("connection");
|
|
20
|
-
const worker =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
const connection = Queue.connection(this.argument("connection"));
|
|
22
|
+
const worker = new Worker(connection).on({
|
|
23
|
+
onProcessed: (job) => {
|
|
24
|
+
this.success(`Processed: ${job.name()}`);
|
|
25
|
+
},
|
|
26
|
+
onFailed: (job, error, released) => {
|
|
27
|
+
this.error(released ? `Failed (attempt ${job.attempts()}, retrying): ${job.name()} — ${reason(error)}` : `Failed permanently: ${job.name()} — ${reason(error)}`);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const queue = this.option("queue") ?? connection.getDefaultQueue();
|
|
28
31
|
try {
|
|
29
32
|
bootArkorm();
|
|
30
33
|
} catch {}
|
|
34
|
+
await this.registerJobs();
|
|
35
|
+
if (this.option("once")) {
|
|
36
|
+
if (!await worker.runNextJob(queue)) this.info("No jobs available.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.info(`Processing jobs from the [${connection.getConnectionName()}] connection on the [${queue}] queue.`);
|
|
31
40
|
await worker.daemon({
|
|
32
41
|
queue,
|
|
33
42
|
sleep: Number(this.option("sleep") ?? 3),
|
|
34
|
-
maxJobs: Number(this.
|
|
35
|
-
stopWhenEmpty: Boolean(this.
|
|
43
|
+
maxJobs: Number(this.flag("maxJobs", "max-jobs") ?? 0),
|
|
44
|
+
stopWhenEmpty: Boolean(this.flag("stopWhenEmpty", "stop-when-empty"))
|
|
36
45
|
});
|
|
37
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Read a multi-word flag. Musket hands over the parsed options camelCased,
|
|
49
|
+
* so `--stop-when-empty` arrives as `stopWhenEmpty`; the kebab-case name is
|
|
50
|
+
* accepted too so the flag can't go quietly missing again.
|
|
51
|
+
*
|
|
52
|
+
* @param camel The camelCase key.
|
|
53
|
+
* @param kebab The flag as written in the signature.
|
|
54
|
+
*/
|
|
55
|
+
flag(camel, kebab) {
|
|
56
|
+
return this.option(camel) ?? this.option(kebab);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Register the application's job classes with `@arkstack/jobs` when it is
|
|
60
|
+
* installed. Without it a payload cannot be turned back into a job.
|
|
61
|
+
*/
|
|
62
|
+
async registerJobs() {
|
|
63
|
+
try {
|
|
64
|
+
const { loadJobs } = await import("@arkstack/jobs");
|
|
65
|
+
const names = await loadJobs();
|
|
66
|
+
if (names.length) this.line(`Loaded ${names.length} job class(es).`);
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
38
69
|
};
|
|
39
70
|
//#endregion
|
|
40
71
|
export { QueueWorkCommand };
|
package/dist/index.d.ts
CHANGED
|
@@ -67,6 +67,11 @@ declare abstract class QueueContract {
|
|
|
67
67
|
/** The name this connection was resolved under. */
|
|
68
68
|
getConnectionName(): string;
|
|
69
69
|
setConnectionName(name: string): this;
|
|
70
|
+
/**
|
|
71
|
+
* The queue this connection works when none is named. Drivers with a
|
|
72
|
+
* configurable queue override it.
|
|
73
|
+
*/
|
|
74
|
+
getDefaultQueue(): string;
|
|
70
75
|
/**
|
|
71
76
|
* Push a job onto the queue. Returns the job id.
|
|
72
77
|
*/
|
|
@@ -241,6 +246,22 @@ interface WorkerOptions {
|
|
|
241
246
|
/** Stop the daemon as soon as the queue drains. */
|
|
242
247
|
stopWhenEmpty?: boolean;
|
|
243
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Observers for what a worker does with each job. A worker executes jobs the
|
|
251
|
+
* caller can't see — a dedicated worker process reports what happened through
|
|
252
|
+
* these, so a job that keeps failing isn't indistinguishable from an idle queue.
|
|
253
|
+
*/
|
|
254
|
+
interface WorkerHandlers {
|
|
255
|
+
/** A job has been reserved and is about to run. */
|
|
256
|
+
onProcessing?: (job: Job) => void | Promise<void>;
|
|
257
|
+
/** A job completed and has been removed from the queue. */
|
|
258
|
+
onProcessed?: (job: Job) => void | Promise<void>;
|
|
259
|
+
/**
|
|
260
|
+
* A job threw. `released` is `true` when it goes back on the queue for
|
|
261
|
+
* another attempt, `false` when it has exhausted its tries and failed.
|
|
262
|
+
*/
|
|
263
|
+
onFailed?: (job: Job, error: unknown, released: boolean) => void | Promise<void>;
|
|
264
|
+
}
|
|
244
265
|
/**
|
|
245
266
|
* Pulls jobs from a connection and executes them.
|
|
246
267
|
*
|
|
@@ -251,9 +272,17 @@ interface WorkerOptions {
|
|
|
251
272
|
declare class Worker {
|
|
252
273
|
private readonly connection;
|
|
253
274
|
private shouldStop;
|
|
275
|
+
private readonly handlers;
|
|
254
276
|
constructor(connection: QueueContract);
|
|
255
277
|
/** Signal a running {@link daemon} loop to stop after the current job. */
|
|
256
278
|
stop(): void;
|
|
279
|
+
/**
|
|
280
|
+
* Observe what the worker does with each job. Handlers merge, so they can be
|
|
281
|
+
* registered in more than one call.
|
|
282
|
+
*
|
|
283
|
+
* @param handlers The callbacks to add.
|
|
284
|
+
*/
|
|
285
|
+
on(handlers: WorkerHandlers): this;
|
|
257
286
|
/**
|
|
258
287
|
* Pop and process the next job. Returns `true` if a job was processed,
|
|
259
288
|
* `false` when the queue was empty.
|
|
@@ -400,6 +429,8 @@ declare class DatabaseQueue extends QueueContract {
|
|
|
400
429
|
private db?;
|
|
401
430
|
constructor(options: DatabaseConnectionConfig);
|
|
402
431
|
private get defaultQueue();
|
|
432
|
+
/** The configured queue this connection works when none is named. */
|
|
433
|
+
getDefaultQueue(): string;
|
|
403
434
|
private database;
|
|
404
435
|
push(job: Queueable, queue?: string): Promise<string>;
|
|
405
436
|
pushRaw(payload: JobPayload, queue?: string, availableAt?: number): Promise<string>;
|
|
@@ -432,6 +463,8 @@ declare class RedisQueue extends QueueContract {
|
|
|
432
463
|
private client?;
|
|
433
464
|
constructor(options: RedisConnectionConfig);
|
|
434
465
|
private get defaultQueue();
|
|
466
|
+
/** The configured queue this connection works when none is named. */
|
|
467
|
+
getDefaultQueue(): string;
|
|
435
468
|
private get retryAfter();
|
|
436
469
|
private key;
|
|
437
470
|
private connection;
|
|
@@ -449,4 +482,4 @@ declare class RedisQueue extends QueueContract {
|
|
|
449
482
|
private migrate;
|
|
450
483
|
}
|
|
451
484
|
//#endregion
|
|
452
|
-
export { CustomQueueConnectionRegistry, DatabaseConnectionConfig, DatabaseFacade, DatabaseQueue, Job, JobHandlers, JobPayload, JobResolver, JobRow, JobSerializer, Query, Queue, QueueConfig, QueueConnectionConfig, QueueConnectionFactory, QueueContract, Queueable, RedisClient, RedisConnectionConfig, RedisQueue, SyncConnectionConfig, SyncQueue, Worker, WorkerOptions, configure, resetSerialization, resolveJob, serializeJob, setResolver, setSerializer };
|
|
485
|
+
export { CustomQueueConnectionRegistry, DatabaseConnectionConfig, DatabaseFacade, DatabaseQueue, Job, JobHandlers, JobPayload, JobResolver, JobRow, JobSerializer, Query, Queue, QueueConfig, QueueConnectionConfig, QueueConnectionFactory, QueueContract, Queueable, RedisClient, RedisConnectionConfig, RedisQueue, SyncConnectionConfig, SyncQueue, Worker, WorkerHandlers, WorkerOptions, configure, resetSerialization, resolveJob, serializeJob, setResolver, setSerializer };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as RedisQueue, c as Job, d as serializeJob, f as setResolver, i as SyncQueue, l as resetSerialization, n as configure, o as DatabaseQueue, p as setSerializer, r as Worker, s as QueueContract, t as Queue, u as resolveJob } from "./QueueManager-
|
|
1
|
+
import { a as RedisQueue, c as Job, d as serializeJob, f as setResolver, i as SyncQueue, l as resetSerialization, n as configure, o as DatabaseQueue, p as setSerializer, r as Worker, s as QueueContract, t as Queue, u as resolveJob } from "./QueueManager-Dm9FLISk.js";
|
|
2
2
|
export { DatabaseQueue, Job, Queue, QueueContract, RedisQueue, SyncQueue, Worker, configure, resetSerialization, resolveJob, serializeJob, setResolver, setSerializer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkstack/queue",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Queue module for Arkstack, providing a driver based queue transport and worker for background processing.",
|
|
6
6
|
"homepage": "https://arkstack.toneflix.net/guide/queue",
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"./package.json": "./package.json"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@arkstack/common": "^0.18.
|
|
37
|
+
"@arkstack/common": "^0.18.4"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@h3ravel/musket": "^2.2.13",
|
|
41
41
|
"ioredis": "^5.4.1",
|
|
42
|
-
"@arkstack/contract": "^0.18.
|
|
43
|
-
"@arkstack/database": "^0.18.
|
|
42
|
+
"@arkstack/contract": "^0.18.4",
|
|
43
|
+
"@arkstack/database": "^0.18.4"
|
|
44
44
|
},
|
|
45
45
|
"peerDependenciesMeta": {
|
|
46
46
|
"@arkstack/database": {
|