@anishhs/retryq 1.0.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 +254 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
## @anishhs/retryq
|
|
2
|
+
|
|
3
|
+
A tiny, dependency-free retry queue manager for handling multiple concurrent async jobs with priorities, exponential backoff, jitter, cancellation, and simple introspection.
|
|
4
|
+
|
|
5
|
+
- **Concurrency control**: limit how many jobs run at once
|
|
6
|
+
- **Exponential backoff** with configurable base delay, multiplier, jitter
|
|
7
|
+
- **Global time cap** per job via `maxTime`
|
|
8
|
+
- **Priority queueing**: higher priority jobs run first
|
|
9
|
+
- **Cancellation**: cancel pending retries and sleep waits
|
|
10
|
+
- **Introspection**: list jobs, find by id/label
|
|
11
|
+
- **TypeScript** ready with bundled types
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @anishhs/retryq
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Node.js 16+ recommended.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Quick start
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { RetryQManager } from "@anishhs/retryq";
|
|
27
|
+
|
|
28
|
+
// Allow up to 3 jobs to run concurrently
|
|
29
|
+
const retryQ = new RetryQManager(3);
|
|
30
|
+
|
|
31
|
+
// Any function returning a Promise can be a job
|
|
32
|
+
async function flakyTask() {
|
|
33
|
+
// ... do something that may fail
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const job = retryQ.createJob(flakyTask, {
|
|
37
|
+
label: "sync-user",
|
|
38
|
+
priority: 10,
|
|
39
|
+
retries: 5,
|
|
40
|
+
delay: 500, // ms
|
|
41
|
+
backoff: 2, // exponential factor
|
|
42
|
+
jitter: 0.1, // ±10%
|
|
43
|
+
maxTime: 5000, // total time window per job (ms)
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Get the actual result or throw last error
|
|
47
|
+
job.promise
|
|
48
|
+
.then((value) => console.log("completed", value))
|
|
49
|
+
.catch((err) => console.error("failed", err));
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### How it works (in short)
|
|
54
|
+
- Jobs are queued and sorted by `priority` (higher first).
|
|
55
|
+
- Up to `maxConcurrent` jobs can run simultaneously.
|
|
56
|
+
- Each job retries up to `retries` times with exponential backoff starting from `delay` and multiplying by `backoff`.
|
|
57
|
+
- A random jitter (±`jitter` fraction) is applied to each wait to avoid thundering-herd patterns.
|
|
58
|
+
- The total elapsed time per job is capped by `maxTime`.
|
|
59
|
+
- `job.promise` resolves with the function’s resolved value, or rejects with the last error.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## API
|
|
63
|
+
|
|
64
|
+
### `class RetryQManager`
|
|
65
|
+
|
|
66
|
+
#### `constructor(maxConcurrent?: number)`
|
|
67
|
+
- **maxConcurrent**: maximum number of jobs allowed to run at once. Default: `Infinity`.
|
|
68
|
+
|
|
69
|
+
#### `createJob(fn: () => Promise<any>, options?: RetryQJobOptions): RetryQJob`
|
|
70
|
+
Queues and starts a job. Returns a `RetryQJob` with a `promise` that resolves with the function’s return value or rejects after exhausting retries.
|
|
71
|
+
|
|
72
|
+
- `fn`: async function to execute (must return a Promise)
|
|
73
|
+
- `options`: optional behavior overrides (see below)
|
|
74
|
+
|
|
75
|
+
#### `cancelJob(id: string): void`
|
|
76
|
+
Cancels a job by id. If the job is sleeping between retries, the sleep is interrupted. If the job’s function is currently executing, it will not be forcibly aborted (user code should be cooperative if needed), but further retries are stopped and the job is marked `cancelled`.
|
|
77
|
+
|
|
78
|
+
#### `listJobs()`
|
|
79
|
+
Returns a snapshot of jobs grouped by state:
|
|
80
|
+
```ts
|
|
81
|
+
{
|
|
82
|
+
pending: Array<{ id, label, state, retriesLeft, priority }>,
|
|
83
|
+
running: Array<{ id, label, state, retriesLeft, priority }>,
|
|
84
|
+
failed: Array<{ id, label, state, retriesLeft, priority }>,
|
|
85
|
+
completed: Array<{ id, label, state, retriesLeft, priority }>,
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `findJobById(id: string)`
|
|
90
|
+
Returns the `RetryQJob` if found in any state, otherwise `null`.
|
|
91
|
+
|
|
92
|
+
#### `findJobsByLabel(label: string)`
|
|
93
|
+
Returns an array of `RetryQJob` with the given label across any state.
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
### Types
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
export type RetryQJobOptions = {
|
|
100
|
+
retries?: number; // default 3
|
|
101
|
+
delay?: number; // initial delay in ms, default 1000
|
|
102
|
+
backoff?: number; // multiplier, default 2
|
|
103
|
+
maxTime?: number; // total allowed time in ms, default 5000
|
|
104
|
+
jitter?: number; // fraction, e.g., 0.1 = ±10%, default 0.1
|
|
105
|
+
label?: string; // human-readable tag
|
|
106
|
+
priority?: number; // higher runs sooner, default 1
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type JobState =
|
|
110
|
+
| "pending"
|
|
111
|
+
| "running"
|
|
112
|
+
| "completed"
|
|
113
|
+
| "failed"
|
|
114
|
+
| "cancelled";
|
|
115
|
+
|
|
116
|
+
export interface RetryQJob {
|
|
117
|
+
id: string;
|
|
118
|
+
label: string;
|
|
119
|
+
state: JobState;
|
|
120
|
+
priority: number;
|
|
121
|
+
retriesLeft: number;
|
|
122
|
+
promise: Promise<any>; // resolves to your function’s actual value
|
|
123
|
+
cancel: () => void; // convenience wrapper for cancelJob
|
|
124
|
+
fn: () => Promise<any>;
|
|
125
|
+
options: RetryQJobOptions;
|
|
126
|
+
createdAt: number;
|
|
127
|
+
startedAt?: number;
|
|
128
|
+
finishedAt?: number;
|
|
129
|
+
error?: any;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
## Usage patterns
|
|
135
|
+
|
|
136
|
+
### Priorities and concurrency
|
|
137
|
+
```ts
|
|
138
|
+
const q = new RetryQManager(2); // two at a time
|
|
139
|
+
|
|
140
|
+
q.createJob(taskA, { label: "A", priority: 5 });
|
|
141
|
+
q.createJob(taskB, { label: "B", priority: 1 });
|
|
142
|
+
q.createJob(taskC, { label: "C", priority: 10 });
|
|
143
|
+
|
|
144
|
+
// C and A start first (highest priorities), then B.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Custom backoff and jitter
|
|
148
|
+
```ts
|
|
149
|
+
q.createJob(fetchWithRetry, {
|
|
150
|
+
retries: 4,
|
|
151
|
+
delay: 250,
|
|
152
|
+
backoff: 1.5,
|
|
153
|
+
jitter: 0.2, // ±20%
|
|
154
|
+
maxTime: 8000,
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Cancellation
|
|
159
|
+
```ts
|
|
160
|
+
const job = q.createJob(sendEmail, { label: "email#42" });
|
|
161
|
+
|
|
162
|
+
// later
|
|
163
|
+
q.cancelJob(job.id);
|
|
164
|
+
// or
|
|
165
|
+
job.cancel();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
If the job is sleeping between retries, the sleep is aborted immediately. If it’s executing your function, it will not be forcibly interrupted—cooperative cancellation is advised for long-running tasks.
|
|
169
|
+
|
|
170
|
+
### Introspection
|
|
171
|
+
```ts
|
|
172
|
+
const { pending, running, failed, completed } = q.listJobs();
|
|
173
|
+
|
|
174
|
+
const maybe = q.findJobById("job-123");
|
|
175
|
+
const allEmailJobs = q.findJobsByLabel("email#42");
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## Error handling
|
|
180
|
+
- When a job ultimately fails (retries exhausted or `maxTime` exceeded), its `promise` rejects with the last captured error.
|
|
181
|
+
- Failed and cancelled jobs are tracked in `listJobs()` for post-mortem or metrics.
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
## Defaults
|
|
185
|
+
- `retries`: 3
|
|
186
|
+
- `delay`: 1000 ms
|
|
187
|
+
- `backoff`: 2
|
|
188
|
+
- `maxTime`: 5000 ms
|
|
189
|
+
- `jitter`: 0.1 (±10%)
|
|
190
|
+
- `priority`: 1
|
|
191
|
+
- `maxConcurrent`: `Infinity` (constructor)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
## Common recipes
|
|
195
|
+
|
|
196
|
+
### Retrying HTTP with fetch/axios
|
|
197
|
+
```ts
|
|
198
|
+
async function getJson(url: string) {
|
|
199
|
+
const res = await fetch(url);
|
|
200
|
+
if (!res.ok) throw new Error("Network error " + res.status);
|
|
201
|
+
return res.json();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const q = new RetryQManager(4);
|
|
205
|
+
const job = q.createJob(() => getJson("https://api.example.com/data"), {
|
|
206
|
+
retries: 6,
|
|
207
|
+
delay: 300,
|
|
208
|
+
backoff: 2,
|
|
209
|
+
jitter: 0.15,
|
|
210
|
+
maxTime: 5000,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const data = await job.promise;
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Queueing many tasks with labels
|
|
217
|
+
```ts
|
|
218
|
+
const q = new RetryQManager(5);
|
|
219
|
+
|
|
220
|
+
for (const userId of users) {
|
|
221
|
+
q.createJob(() => syncUser(userId), {
|
|
222
|
+
label: `sync-user:${userId}`,
|
|
223
|
+
priority: 5,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
## Notes and caveats
|
|
230
|
+
- Cancellation does not forcibly abort your `fn` while it’s running; design long-running tasks to be cancellable if required.
|
|
231
|
+
- `maxTime` is a soft cap applied across the job’s lifetime. If elapsed time exceeds `maxTime`, the manager stops retrying and marks the job failed.
|
|
232
|
+
- The manager generates ids like `job-<timestamp>-<random>`; you can set a human-readable `label` for easier lookups.
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
## Development
|
|
236
|
+
|
|
237
|
+
Scripts:
|
|
238
|
+
```bash
|
|
239
|
+
# build TypeScript to dist/
|
|
240
|
+
npm run build
|
|
241
|
+
|
|
242
|
+
# run compiled output
|
|
243
|
+
npm start
|
|
244
|
+
|
|
245
|
+
# dev-run directly from src/
|
|
246
|
+
npm run dev
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`tsconfig.json` emits `dist/index.js` and type declarations in `dist/index.d.ts`.
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
ISC © Anish Shekh
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export type RetryQJobOptions = {
|
|
2
|
+
retries?: number;
|
|
3
|
+
delay?: number;
|
|
4
|
+
backoff?: number;
|
|
5
|
+
maxTime?: number;
|
|
6
|
+
jitter?: number;
|
|
7
|
+
label?: string;
|
|
8
|
+
priority?: number;
|
|
9
|
+
};
|
|
10
|
+
export type JobState = "pending" | "running" | "completed" | "failed" | "cancelled";
|
|
11
|
+
export interface RetryQJob {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
state: JobState;
|
|
15
|
+
priority: number;
|
|
16
|
+
retriesLeft: number;
|
|
17
|
+
promise: Promise<any>;
|
|
18
|
+
cancel: () => void;
|
|
19
|
+
fn: () => Promise<any>;
|
|
20
|
+
options: RetryQJobOptions;
|
|
21
|
+
createdAt: number;
|
|
22
|
+
startedAt?: number;
|
|
23
|
+
finishedAt?: number;
|
|
24
|
+
error?: any;
|
|
25
|
+
}
|
|
26
|
+
export interface CancelableFunction {
|
|
27
|
+
(): void;
|
|
28
|
+
cancelSleep?: () => void;
|
|
29
|
+
}
|
|
30
|
+
export declare class RetryQManager {
|
|
31
|
+
private pendingQueue;
|
|
32
|
+
private runningJobs;
|
|
33
|
+
private failedJobs;
|
|
34
|
+
private completedJobs;
|
|
35
|
+
private maxConcurrent;
|
|
36
|
+
private registry;
|
|
37
|
+
constructor(maxConcurrent?: number);
|
|
38
|
+
createJob(fn: () => Promise<any>, options?: RetryQJobOptions): RetryQJob;
|
|
39
|
+
private _sortQueue;
|
|
40
|
+
private _processQueue;
|
|
41
|
+
private _runJob;
|
|
42
|
+
cancelJob(id: string): void;
|
|
43
|
+
listJobs(): {
|
|
44
|
+
pending: {
|
|
45
|
+
id: string;
|
|
46
|
+
label: string;
|
|
47
|
+
state: JobState;
|
|
48
|
+
retriesLeft: number;
|
|
49
|
+
priority: number;
|
|
50
|
+
}[];
|
|
51
|
+
running: {
|
|
52
|
+
id: string;
|
|
53
|
+
label: string;
|
|
54
|
+
state: JobState;
|
|
55
|
+
retriesLeft: number;
|
|
56
|
+
priority: number;
|
|
57
|
+
}[];
|
|
58
|
+
failed: {
|
|
59
|
+
id: string;
|
|
60
|
+
label: string;
|
|
61
|
+
state: JobState;
|
|
62
|
+
retriesLeft: number;
|
|
63
|
+
priority: number;
|
|
64
|
+
}[];
|
|
65
|
+
completed: {
|
|
66
|
+
id: string;
|
|
67
|
+
label: string;
|
|
68
|
+
state: JobState;
|
|
69
|
+
retriesLeft: number;
|
|
70
|
+
priority: number;
|
|
71
|
+
}[];
|
|
72
|
+
};
|
|
73
|
+
findJobById(id: string): RetryQJob | null;
|
|
74
|
+
findJobsByLabel(label: string): RetryQJob[];
|
|
75
|
+
private _jobSummary;
|
|
76
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RetryQManager = void 0;
|
|
13
|
+
// Custom ID generator using timestamp + larger random suffix
|
|
14
|
+
function randomId() {
|
|
15
|
+
return `job-${Date.now()}-${Math.random().toString(36).slice(2, 12)}`;
|
|
16
|
+
}
|
|
17
|
+
function sleep(ms, cancelFn) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const timer = setTimeout(() => resolve(), ms);
|
|
20
|
+
if (cancelFn) {
|
|
21
|
+
cancelFn.cancelSleep = () => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
reject(new Error("RetryQ job cancelled"));
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
class RetryQManager {
|
|
29
|
+
constructor(maxConcurrent = Infinity) {
|
|
30
|
+
this.pendingQueue = [];
|
|
31
|
+
this.runningJobs = new Map();
|
|
32
|
+
this.failedJobs = new Map();
|
|
33
|
+
this.completedJobs = new Map();
|
|
34
|
+
this.registry = new Map();
|
|
35
|
+
this.maxConcurrent = maxConcurrent;
|
|
36
|
+
}
|
|
37
|
+
createJob(fn, options = {}) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const id = randomId();
|
|
40
|
+
const job = {
|
|
41
|
+
id,
|
|
42
|
+
label: options.label || id,
|
|
43
|
+
state: "pending",
|
|
44
|
+
priority: (_a = options.priority) !== null && _a !== void 0 ? _a : 1,
|
|
45
|
+
retriesLeft: (_b = options.retries) !== null && _b !== void 0 ? _b : 3,
|
|
46
|
+
promise: Promise.resolve(), // placeholder
|
|
47
|
+
cancel: () => this.cancelJob(id),
|
|
48
|
+
fn,
|
|
49
|
+
options,
|
|
50
|
+
createdAt: Date.now(),
|
|
51
|
+
};
|
|
52
|
+
job.promise = this._runJob(job);
|
|
53
|
+
this.pendingQueue.push(job);
|
|
54
|
+
this._sortQueue();
|
|
55
|
+
this._processQueue();
|
|
56
|
+
return job;
|
|
57
|
+
}
|
|
58
|
+
_sortQueue() {
|
|
59
|
+
this.pendingQueue.sort((a, b) => b.priority - a.priority);
|
|
60
|
+
}
|
|
61
|
+
_processQueue() {
|
|
62
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
+
while (this.runningJobs.size < this.maxConcurrent &&
|
|
64
|
+
this.pendingQueue.length > 0) {
|
|
65
|
+
const job = this.pendingQueue.shift();
|
|
66
|
+
// job.promise already set in createJob
|
|
67
|
+
this.runningJobs.set(job.id, job);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
_runJob(job) {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
var _a;
|
|
74
|
+
job.state = "running";
|
|
75
|
+
job.startedAt = Date.now();
|
|
76
|
+
const cancelFn = () => { };
|
|
77
|
+
this.registry.set(job.id, cancelFn);
|
|
78
|
+
const { delay = 1000, backoff = 2, maxTime = 5000, jitter = 0.1, } = job.options;
|
|
79
|
+
let currentDelay = delay;
|
|
80
|
+
while (job.retriesLeft > 0) {
|
|
81
|
+
const elapsed = Date.now() - ((_a = job.startedAt) !== null && _a !== void 0 ? _a : Date.now());
|
|
82
|
+
if (elapsed >= maxTime)
|
|
83
|
+
break;
|
|
84
|
+
try {
|
|
85
|
+
const result = yield job.fn();
|
|
86
|
+
job.state = "completed";
|
|
87
|
+
job.finishedAt = Date.now();
|
|
88
|
+
this.completedJobs.set(job.id, job);
|
|
89
|
+
this.runningJobs.delete(job.id);
|
|
90
|
+
this._processQueue();
|
|
91
|
+
return result; // returns actual value
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
job.retriesLeft--;
|
|
95
|
+
job.error = err;
|
|
96
|
+
if (job.retriesLeft <= 0)
|
|
97
|
+
break;
|
|
98
|
+
// jitter
|
|
99
|
+
const jitterAmount = currentDelay * jitter;
|
|
100
|
+
let adjustedDelay = currentDelay + (Math.random() * 2 - 1) * jitterAmount;
|
|
101
|
+
adjustedDelay = Math.min(adjustedDelay, maxTime - elapsed);
|
|
102
|
+
if (adjustedDelay > 0)
|
|
103
|
+
yield sleep(adjustedDelay, cancelFn);
|
|
104
|
+
currentDelay *= backoff;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!["cancelled", "failed"].includes(job.state))
|
|
108
|
+
job.state = "failed";
|
|
109
|
+
job.finishedAt = Date.now();
|
|
110
|
+
this.runningJobs.delete(job.id);
|
|
111
|
+
this.failedJobs.set(job.id, job);
|
|
112
|
+
this._processQueue();
|
|
113
|
+
throw job.error || new Error("Job failed");
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
cancelJob(id) {
|
|
117
|
+
const job = this.runningJobs.get(id) || this.pendingQueue.find((j) => j.id === id);
|
|
118
|
+
if (!job)
|
|
119
|
+
return;
|
|
120
|
+
job.state = "cancelled";
|
|
121
|
+
job.error = new Error("Job cancelled");
|
|
122
|
+
job.finishedAt = Date.now();
|
|
123
|
+
this.runningJobs.delete(id);
|
|
124
|
+
this.pendingQueue = this.pendingQueue.filter((j) => j.id !== id);
|
|
125
|
+
this.failedJobs.set(id, job);
|
|
126
|
+
const cancelFn = this.registry.get(id);
|
|
127
|
+
if (cancelFn === null || cancelFn === void 0 ? void 0 : cancelFn.cancelSleep)
|
|
128
|
+
cancelFn.cancelSleep();
|
|
129
|
+
}
|
|
130
|
+
listJobs() {
|
|
131
|
+
return {
|
|
132
|
+
pending: this.pendingQueue.map((j) => this._jobSummary(j)),
|
|
133
|
+
running: Array.from(this.runningJobs.values()).map((j) => this._jobSummary(j)),
|
|
134
|
+
failed: Array.from(this.failedJobs.values()).map((j) => this._jobSummary(j)),
|
|
135
|
+
completed: Array.from(this.completedJobs.values()).map((j) => this._jobSummary(j)),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
findJobById(id) {
|
|
139
|
+
return (this.runningJobs.get(id) ||
|
|
140
|
+
this.pendingQueue.find((j) => j.id === id) ||
|
|
141
|
+
this.failedJobs.get(id) ||
|
|
142
|
+
this.completedJobs.get(id) ||
|
|
143
|
+
null);
|
|
144
|
+
}
|
|
145
|
+
findJobsByLabel(label) {
|
|
146
|
+
const all = [
|
|
147
|
+
...Array.from(this.runningJobs.values()),
|
|
148
|
+
...this.pendingQueue,
|
|
149
|
+
...Array.from(this.failedJobs.values()),
|
|
150
|
+
...Array.from(this.completedJobs.values()),
|
|
151
|
+
];
|
|
152
|
+
return all.filter((j) => j.label === label);
|
|
153
|
+
}
|
|
154
|
+
_jobSummary(job) {
|
|
155
|
+
return {
|
|
156
|
+
id: job.id,
|
|
157
|
+
label: job.label,
|
|
158
|
+
state: job.state,
|
|
159
|
+
retriesLeft: job.retriesLeft,
|
|
160
|
+
priority: job.priority,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.RetryQManager = RetryQManager;
|
|
165
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anishhs/retryq",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Retry manager for handling multiple concurrent jobs",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"prepare": "npm run build",
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"dev": "ts-node src/index.ts"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"retryq",
|
|
18
|
+
"retry",
|
|
19
|
+
"queue",
|
|
20
|
+
"manager",
|
|
21
|
+
"priority",
|
|
22
|
+
"jobs",
|
|
23
|
+
"asynchronous",
|
|
24
|
+
"concurrent",
|
|
25
|
+
"typescript",
|
|
26
|
+
"anishhs"
|
|
27
|
+
],
|
|
28
|
+
"author": "Anish Shekh",
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^24.6.1",
|
|
32
|
+
"ts-node": "^10.9.2",
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
}
|
|
35
|
+
}
|