@c9up/bay 0.1.13 → 0.2.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/README.md +122 -16
- package/dist/BayProvider.d.ts +39 -11
- package/dist/BayProvider.d.ts.map +1 -1
- package/dist/BayProvider.js +35 -13
- package/dist/BayProvider.js.map +1 -1
- package/dist/Job.d.ts +99 -0
- package/dist/Job.d.ts.map +1 -0
- package/dist/Job.js +78 -0
- package/dist/Job.js.map +1 -0
- package/dist/QueueManager.d.ts +140 -21
- package/dist/QueueManager.d.ts.map +1 -1
- package/dist/QueueManager.js +247 -53
- package/dist/QueueManager.js.map +1 -1
- package/dist/adapters.d.ts +68 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +56 -0
- package/dist/adapters.js.map +1 -0
- package/dist/augmentations.d.ts +28 -0
- package/dist/augmentations.d.ts.map +1 -0
- package/dist/augmentations.js +17 -0
- package/dist/augmentations.js.map +1 -0
- package/dist/configure.d.ts +1 -0
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +24 -7
- package/dist/configure.js.map +1 -1
- package/dist/console/contract.d.ts +60 -0
- package/dist/console/contract.d.ts.map +1 -0
- package/dist/console/contract.js +36 -0
- package/dist/console/contract.js.map +1 -0
- package/dist/console/index.d.ts +29 -0
- package/dist/console/index.d.ts.map +1 -0
- package/dist/console/index.js +45 -0
- package/dist/console/index.js.map +1 -0
- package/dist/console/makeJob.d.ts +32 -0
- package/dist/console/makeJob.d.ts.map +1 -0
- package/dist/console/makeJob.js +118 -0
- package/dist/console/makeJob.js.map +1 -0
- package/dist/console/queueWork.d.ts +18 -0
- package/dist/console/queueWork.d.ts.map +1 -0
- package/dist/console/queueWork.js +58 -0
- package/dist/console/queueWork.js.map +1 -0
- package/dist/drivers/MemoryDriver.d.ts +14 -8
- package/dist/drivers/MemoryDriver.d.ts.map +1 -1
- package/dist/drivers/MemoryDriver.js +61 -7
- package/dist/drivers/MemoryDriver.js.map +1 -1
- package/dist/drivers/RedisDriver.d.ts +60 -8
- package/dist/drivers/RedisDriver.d.ts.map +1 -1
- package/dist/drivers/RedisDriver.js +209 -29
- package/dist/drivers/RedisDriver.js.map +1 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +43 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +105 -0
- package/dist/jobs.js.map +1 -0
- package/dist/quasar.d.ts +1 -1
- package/dist/quasar.js +1 -1
- package/dist/testing/FakeQueue.d.ts +15 -9
- package/dist/testing/FakeQueue.d.ts.map +1 -1
- package/dist/testing/FakeQueue.js +13 -3
- package/dist/testing/FakeQueue.js.map +1 -1
- package/package.json +5 -3
- package/src/BayProvider.ts +79 -26
- package/src/Job.ts +137 -0
- package/src/QueueManager.ts +411 -56
- package/src/adapters.ts +75 -0
- package/src/augmentations.ts +31 -0
- package/src/configure.ts +25 -7
- package/src/console/contract.ts +94 -0
- package/src/console/index.ts +68 -0
- package/src/console/makeJob.ts +139 -0
- package/src/console/queueWork.ts +70 -0
- package/src/drivers/MemoryDriver.ts +66 -14
- package/src/drivers/RedisDriver.ts +298 -42
- package/src/index.ts +35 -6
- package/src/jobs.ts +111 -0
- package/src/quasar.ts +1 -1
- package/src/testing/FakeQueue.ts +25 -15
- package/dist/stores.d.ts +0 -41
- package/dist/stores.d.ts.map +0 -1
- package/dist/stores.js +0 -46
- package/dist/stores.js.map +0 -1
- package/src/stores.ts +0 -59
package/src/Job.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A job as a class — what an application writes, and what `dispatch` takes.
|
|
3
|
+
*
|
|
4
|
+
* The queue accepted a registered name and a payload:
|
|
5
|
+
*
|
|
6
|
+
* queue.register('send-email', new SendEmailHandler())
|
|
7
|
+
* await queue.dispatch('send-email', { to: '…' })
|
|
8
|
+
*
|
|
9
|
+
* Two places to keep in step, and nothing tying the payload to the handler that
|
|
10
|
+
* reads it. A class carries its own name, its own options and its payload type:
|
|
11
|
+
*
|
|
12
|
+
* export default class SendEmail extends Job<{ to: string }> {
|
|
13
|
+
* static options: JobOptions = { queue: 'emails', maxRetries: 5 }
|
|
14
|
+
*
|
|
15
|
+
* async execute() {
|
|
16
|
+
* await mail.send(this.payload.to)
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* async failed(error: Error) {
|
|
20
|
+
* // after the last retry, not after each one
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* await queue.dispatch(SendEmail, { to: 'user@example.com' })
|
|
25
|
+
*
|
|
26
|
+
* Registering by name still works, and is what a job whose name is computed at
|
|
27
|
+
* runtime still needs.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** Milliseconds, or a duration the way a config file writes one. */
|
|
31
|
+
export type Duration = number | string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* What a job class declares about how it should be run.
|
|
35
|
+
*
|
|
36
|
+
* Every field is optional, and the defaults are the manager's: the `default`
|
|
37
|
+
* queue, three attempts, no delay and no timeout.
|
|
38
|
+
*/
|
|
39
|
+
export interface JobOptions {
|
|
40
|
+
/** Named queue this job waits in. Default `"default"`. */
|
|
41
|
+
queue?: string;
|
|
42
|
+
/**
|
|
43
|
+
* How many times the handler may run before the job is filed as failed.
|
|
44
|
+
* Default `3`. Counts runs, not retries: `1` means one attempt and no
|
|
45
|
+
* second chance.
|
|
46
|
+
*/
|
|
47
|
+
maxRetries?: number;
|
|
48
|
+
/** Hold the job for this long before any worker may take it. */
|
|
49
|
+
delay?: Duration;
|
|
50
|
+
/**
|
|
51
|
+
* How long the handler gets. Past it the attempt is a failure and the job
|
|
52
|
+
* retries or fails like any other.
|
|
53
|
+
*
|
|
54
|
+
* The handler is not killed — nothing in Node can interrupt a running
|
|
55
|
+
* promise — so a job that ignores its timeout goes on burning CPU. What the
|
|
56
|
+
* timeout buys is that the WORKER stops waiting for it, which is what a
|
|
57
|
+
* stuck job otherwise costs: a worker that never picks anything up again.
|
|
58
|
+
*/
|
|
59
|
+
timeout?: Duration;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The queue a job goes to when nothing names one. */
|
|
63
|
+
export const DEFAULT_QUEUE = "default";
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A background job.
|
|
67
|
+
*
|
|
68
|
+
* `execute()` takes no argument: the payload is on the instance, typed by the
|
|
69
|
+
* class's own parameter, so a handler cannot read a field the dispatcher never
|
|
70
|
+
* sent.
|
|
71
|
+
*/
|
|
72
|
+
export abstract class Job<Payload = unknown> {
|
|
73
|
+
/** Overridden by a subclass to change queue, retries, delay or timeout. */
|
|
74
|
+
static options: JobOptions = {};
|
|
75
|
+
|
|
76
|
+
/** What `dispatch` was given, as the class declared it. */
|
|
77
|
+
declare readonly payload: Payload;
|
|
78
|
+
|
|
79
|
+
/** Do the work. Throwing is what makes the attempt fail. */
|
|
80
|
+
abstract execute(): Promise<void> | void;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Called once the last attempt has failed — for the cleanup or the alert,
|
|
84
|
+
* not for the retry. A throw here is reported and swallowed: the job is
|
|
85
|
+
* already failed, and failing to say so must not fail it twice.
|
|
86
|
+
*/
|
|
87
|
+
failed?(error: Error): Promise<void> | void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** A job class, as `dispatch` receives it. */
|
|
91
|
+
export interface JobClass<Payload = unknown> {
|
|
92
|
+
new (): Job<Payload>;
|
|
93
|
+
readonly name: string;
|
|
94
|
+
readonly options?: JobOptions;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Is this a job class rather than a name or a handler instance? */
|
|
98
|
+
export function isJobClass(value: unknown): value is JobClass {
|
|
99
|
+
return (
|
|
100
|
+
typeof value === "function" &&
|
|
101
|
+
value.prototype instanceof Job &&
|
|
102
|
+
typeof Reflect.get(value, "name") === "string"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Milliseconds from a number or a duration string.
|
|
108
|
+
*
|
|
109
|
+
* `'10s'`, `'1m'`, `'2h'`, `'500ms'`, `'1d'` — the spellings a config file
|
|
110
|
+
* uses. A number is already milliseconds. Anything else throws, rather than
|
|
111
|
+
* silently becoming `NaN` and then a job that never runs: a typo in `delay`
|
|
112
|
+
* would otherwise park the job forever with nothing to read about it.
|
|
113
|
+
*/
|
|
114
|
+
export function toMilliseconds(value: Duration, label: string): number {
|
|
115
|
+
if (typeof value === "number") {
|
|
116
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
117
|
+
throw new Error(`${label} must be a non-negative number of milliseconds`);
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
const match = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/.exec(value.trim());
|
|
122
|
+
if (!match) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`${label} must be a number of milliseconds or a duration like '10s', '1m', '2h' — got '${value}'`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const amount = Number(match[1]);
|
|
128
|
+
const unit = match[2];
|
|
129
|
+
const scale: Record<string, number> = {
|
|
130
|
+
ms: 1,
|
|
131
|
+
s: 1_000,
|
|
132
|
+
m: 60_000,
|
|
133
|
+
h: 3_600_000,
|
|
134
|
+
d: 86_400_000,
|
|
135
|
+
};
|
|
136
|
+
return amount * (scale[unit ?? "ms"] ?? 1);
|
|
137
|
+
}
|