@anishhs/retryq 1.0.0 → 1.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 +856 -167
- package/dist/cjs/index.d.ts +9 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +13 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/manager.d.ts +139 -0
- package/dist/cjs/manager.d.ts.map +1 -0
- package/dist/cjs/manager.js +431 -0
- package/dist/cjs/manager.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/types.d.ts +237 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils.d.ts +43 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +71 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/cjs/validation.d.ts +37 -0
- package/dist/cjs/validation.d.ts.map +1 -0
- package/dist/cjs/validation.js +69 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/manager.d.ts +139 -0
- package/dist/esm/manager.d.ts.map +1 -0
- package/dist/esm/manager.js +427 -0
- package/dist/esm/manager.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/types.d.ts +237 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils.d.ts +43 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +64 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/validation.d.ts +37 -0
- package/dist/esm/validation.d.ts.map +1 -0
- package/dist/esm/validation.js +65 -0
- package/dist/esm/validation.js.map +1 -0
- package/package.json +29 -8
- package/dist/index.d.ts +0 -76
- package/dist/index.js +0 -165
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle state of a job managed by {@link RetryQManager}.
|
|
3
|
+
*
|
|
4
|
+
* ```
|
|
5
|
+
* pending → running → completed
|
|
6
|
+
* → failed
|
|
7
|
+
* → cancelled
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export type JobState = "pending" | "running" | "completed" | "failed" | "cancelled";
|
|
11
|
+
/**
|
|
12
|
+
* Information passed to {@link RetryQJobOptions.onRetry} and emitted with the
|
|
13
|
+
* manager's `retry` event each time an attempt fails and another is scheduled.
|
|
14
|
+
*/
|
|
15
|
+
export interface RetryInfo {
|
|
16
|
+
/** 1-based index of the attempt that just failed. */
|
|
17
|
+
attempt: number;
|
|
18
|
+
/** The error thrown by the failed attempt. */
|
|
19
|
+
error: unknown;
|
|
20
|
+
/** Delay in milliseconds before the next attempt runs (after jitter/caps). */
|
|
21
|
+
nextDelay: number;
|
|
22
|
+
/** Number of attempts still remaining after this failure. */
|
|
23
|
+
retriesLeft: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Per-job configuration accepted by {@link RetryQManager.createJob}.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam T - Resolved value produced by the job function.
|
|
29
|
+
*/
|
|
30
|
+
export type RetryQJobOptions<T = unknown> = {
|
|
31
|
+
/**
|
|
32
|
+
* Number of retries **after** the initial attempt (total attempts =
|
|
33
|
+
* `retries + 1`). Must be between `0` and `100`.
|
|
34
|
+
* @defaultValue 3
|
|
35
|
+
*/
|
|
36
|
+
retries?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Initial delay between attempts, in milliseconds. Must be `>= 0`.
|
|
39
|
+
* @defaultValue 1000
|
|
40
|
+
*/
|
|
41
|
+
delay?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Multiplier applied to the delay after each failed attempt (exponential
|
|
44
|
+
* backoff). Must be `>= 1`.
|
|
45
|
+
* @defaultValue 2
|
|
46
|
+
*/
|
|
47
|
+
backoff?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Total time budget for the job across all attempts, in milliseconds. Now
|
|
50
|
+
* enforced **during** execution: an in-flight attempt is aborted once the
|
|
51
|
+
* budget is exhausted. Must be `> 0`.
|
|
52
|
+
* @defaultValue 30000
|
|
53
|
+
*/
|
|
54
|
+
maxTime?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Upper bound for a single backoff delay, in milliseconds. Prevents the
|
|
57
|
+
* exponential delay from growing without limit. Must be `>= 0`.
|
|
58
|
+
* @defaultValue Infinity
|
|
59
|
+
*/
|
|
60
|
+
maxDelay?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Maximum duration of a single attempt, in milliseconds. The attempt is
|
|
63
|
+
* aborted (and counts as a failure) if it exceeds this. The effective bound
|
|
64
|
+
* is `min(attemptTimeout, remaining maxTime)`. Must be `> 0` when provided.
|
|
65
|
+
* @defaultValue Infinity
|
|
66
|
+
*/
|
|
67
|
+
attemptTimeout?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Random variation applied to each delay, as a fraction between `0` and `1`
|
|
70
|
+
* (e.g. `0.1` = ±10%).
|
|
71
|
+
* @defaultValue 0.1
|
|
72
|
+
*/
|
|
73
|
+
jitter?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Human-readable identifier used for grouping/lookup via
|
|
76
|
+
* {@link RetryQManager.findJobsByLabel}. Defaults to the generated job id.
|
|
77
|
+
*/
|
|
78
|
+
label?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Queue priority — higher values are dispatched before lower ones.
|
|
81
|
+
* @defaultValue 1
|
|
82
|
+
*/
|
|
83
|
+
priority?: number;
|
|
84
|
+
/**
|
|
85
|
+
* External {@link AbortSignal}. Aborting it force-cancels the job.
|
|
86
|
+
*/
|
|
87
|
+
signal?: AbortSignal;
|
|
88
|
+
/**
|
|
89
|
+
* Predicate deciding whether a thrown error should be retried. Return `false`
|
|
90
|
+
* to stop immediately and mark the job `failed`. When omitted, every error is
|
|
91
|
+
* retried until attempts are exhausted.
|
|
92
|
+
*
|
|
93
|
+
* @param error - The error thrown by the attempt.
|
|
94
|
+
* @param attempt - 1-based index of the attempt that just failed.
|
|
95
|
+
*/
|
|
96
|
+
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
97
|
+
/** Called after each failed attempt that schedules another try. */
|
|
98
|
+
onRetry?: (info: RetryInfo) => void;
|
|
99
|
+
/** Called once when the job completes successfully. */
|
|
100
|
+
onSuccess?: (result: T) => void;
|
|
101
|
+
/** Called once when the job fails after exhausting retries (or `shouldRetry`). */
|
|
102
|
+
onFailure?: (error: unknown) => void;
|
|
103
|
+
/** Called once when the job is cancelled. */
|
|
104
|
+
onCancel?: () => void;
|
|
105
|
+
};
|
|
106
|
+
/** Configuration for the {@link RetryQManager} itself. */
|
|
107
|
+
export type RetryQManagerConfig = {
|
|
108
|
+
/**
|
|
109
|
+
* Maximum number of jobs allowed to run concurrently.
|
|
110
|
+
* @defaultValue Infinity
|
|
111
|
+
*/
|
|
112
|
+
maxConcurrent?: number;
|
|
113
|
+
/**
|
|
114
|
+
* Maximum number of jobs retained per terminal-state history map
|
|
115
|
+
* (completed/failed/cancelled) before the oldest are evicted (LRU).
|
|
116
|
+
* @defaultValue 1000
|
|
117
|
+
*/
|
|
118
|
+
maxHistorySize?: number;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* A unit of work tracked by {@link RetryQManager}.
|
|
122
|
+
*
|
|
123
|
+
* @typeParam T - Resolved value produced by the job function.
|
|
124
|
+
*/
|
|
125
|
+
export interface RetryQJob<T = unknown> {
|
|
126
|
+
/** Unique identifier generated at creation. */
|
|
127
|
+
id: string;
|
|
128
|
+
/** Human-readable label (defaults to {@link RetryQJob.id}). */
|
|
129
|
+
label: string;
|
|
130
|
+
/** Current {@link JobState}. */
|
|
131
|
+
state: JobState;
|
|
132
|
+
/** Queue priority (higher runs first). */
|
|
133
|
+
priority: number;
|
|
134
|
+
/** Remaining attempts (initialised to `retries + 1`). */
|
|
135
|
+
retriesLeft: number;
|
|
136
|
+
/** Promise resolving with the job result or rejecting with the last error. */
|
|
137
|
+
promise: Promise<T>;
|
|
138
|
+
/**
|
|
139
|
+
* Cancel this job.
|
|
140
|
+
* @param force - When `true`, aborts the in-flight attempt via its signal.
|
|
141
|
+
*/
|
|
142
|
+
cancel: (force?: boolean) => void;
|
|
143
|
+
/** The async function executed (receives an {@link AbortSignal}). */
|
|
144
|
+
fn: (signal?: AbortSignal) => Promise<T>;
|
|
145
|
+
/** Resolved options the job was created with. */
|
|
146
|
+
options: RetryQJobOptions<T>;
|
|
147
|
+
/** Creation timestamp (ms since epoch). */
|
|
148
|
+
createdAt: number;
|
|
149
|
+
/** Timestamp execution started (ms since epoch), if started. */
|
|
150
|
+
startedAt?: number;
|
|
151
|
+
/** Timestamp the job reached a terminal state (ms since epoch), if finished. */
|
|
152
|
+
finishedAt?: number;
|
|
153
|
+
/** Last error encountered, if any. */
|
|
154
|
+
error?: unknown;
|
|
155
|
+
/** Internal controller used to abort in-flight execution. */
|
|
156
|
+
abortController?: AbortController;
|
|
157
|
+
}
|
|
158
|
+
/** Compact view of a job returned by {@link RetryQManager.listJobs}. */
|
|
159
|
+
export interface JobSummary {
|
|
160
|
+
/** Job id. */
|
|
161
|
+
id: string;
|
|
162
|
+
/** Job label. */
|
|
163
|
+
label: string;
|
|
164
|
+
/** Current state. */
|
|
165
|
+
state: JobState;
|
|
166
|
+
/** Remaining attempts. */
|
|
167
|
+
retriesLeft: number;
|
|
168
|
+
/** Queue priority. */
|
|
169
|
+
priority: number;
|
|
170
|
+
}
|
|
171
|
+
/** Snapshot of all jobs grouped by state, returned by {@link RetryQManager.listJobs}. */
|
|
172
|
+
export interface JobListSnapshot {
|
|
173
|
+
/** Jobs queued and waiting for a slot. */
|
|
174
|
+
pending: JobSummary[];
|
|
175
|
+
/** Jobs currently executing. */
|
|
176
|
+
running: JobSummary[];
|
|
177
|
+
/** Jobs that exhausted retries (or were rejected by `shouldRetry`). */
|
|
178
|
+
failed: JobSummary[];
|
|
179
|
+
/** Jobs that completed successfully. */
|
|
180
|
+
completed: JobSummary[];
|
|
181
|
+
/** Jobs that were cancelled. */
|
|
182
|
+
cancelled: JobSummary[];
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Internal no-op callback augmented with a {@link CancelableFunction.cancelSleep}
|
|
186
|
+
* hook that interrupts the delay between retries when a job is cancelled.
|
|
187
|
+
*/
|
|
188
|
+
export interface CancelableFunction {
|
|
189
|
+
(): void;
|
|
190
|
+
/** Interrupts the in-progress retry delay, if any. */
|
|
191
|
+
cancelSleep?: () => void;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Payload emitted with the manager's `retry` event.
|
|
195
|
+
*/
|
|
196
|
+
export interface RetryEvent {
|
|
197
|
+
/** The job being retried. */
|
|
198
|
+
job: RetryQJob;
|
|
199
|
+
/** Details of the failure and the scheduled retry. */
|
|
200
|
+
info: RetryInfo;
|
|
201
|
+
}
|
|
202
|
+
/** Payload emitted with the manager's `success` event. */
|
|
203
|
+
export interface SuccessEvent {
|
|
204
|
+
/** The job that completed. */
|
|
205
|
+
job: RetryQJob;
|
|
206
|
+
/** The resolved result. */
|
|
207
|
+
result: unknown;
|
|
208
|
+
}
|
|
209
|
+
/** Payload emitted with the manager's `failure` event. */
|
|
210
|
+
export interface FailureEvent {
|
|
211
|
+
/** The job that failed. */
|
|
212
|
+
job: RetryQJob;
|
|
213
|
+
/** The terminal error. */
|
|
214
|
+
error: unknown;
|
|
215
|
+
}
|
|
216
|
+
/** Payload emitted with the manager's `cancel` event. */
|
|
217
|
+
export interface CancelEvent {
|
|
218
|
+
/** The cancelled job. */
|
|
219
|
+
job: RetryQJob;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Typed event map for {@link RetryQManager}. Keys are event names; values are
|
|
223
|
+
* the corresponding listener signatures.
|
|
224
|
+
*/
|
|
225
|
+
export interface RetryQEvents {
|
|
226
|
+
/** Fired after a failed attempt schedules another try. */
|
|
227
|
+
retry: (payload: RetryEvent) => void;
|
|
228
|
+
/** Fired when a job completes successfully. */
|
|
229
|
+
success: (payload: SuccessEvent) => void;
|
|
230
|
+
/** Fired when a job fails terminally. */
|
|
231
|
+
failure: (payload: FailureEvent) => void;
|
|
232
|
+
/** Fired when a job is cancelled. */
|
|
233
|
+
cancel: (payload: CancelEvent) => void;
|
|
234
|
+
/** Fired when the queue transitions to fully idle (no pending/running jobs). */
|
|
235
|
+
idle: () => void;
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { CancelableFunction } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate a collision-resistant job id.
|
|
4
|
+
*
|
|
5
|
+
* Format: `job-{timestamp}-{counter}-{random1}{random2}`. The counter plus two
|
|
6
|
+
* independent random segments make same-millisecond collisions practically
|
|
7
|
+
* impossible even under high concurrency.
|
|
8
|
+
*
|
|
9
|
+
* @returns A unique job identifier.
|
|
10
|
+
*/
|
|
11
|
+
export declare function randomId(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Clamp a number into the inclusive range `[min, max]`.
|
|
14
|
+
*
|
|
15
|
+
* @param value - Value to clamp.
|
|
16
|
+
* @param min - Lower bound.
|
|
17
|
+
* @param max - Upper bound.
|
|
18
|
+
* @returns The clamped value.
|
|
19
|
+
*/
|
|
20
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
21
|
+
/**
|
|
22
|
+
* Sleep for `ms` milliseconds. If a {@link CancelableFunction} is supplied, a
|
|
23
|
+
* `cancelSleep` hook is attached to it so the delay can be interrupted early
|
|
24
|
+
* (used to abort the wait between retries when a job is cancelled).
|
|
25
|
+
*
|
|
26
|
+
* @param ms - Duration to sleep.
|
|
27
|
+
* @param cancelFn - Optional handle that receives a `cancelSleep` interrupter.
|
|
28
|
+
* @returns A promise that resolves after `ms`, or rejects if interrupted.
|
|
29
|
+
*/
|
|
30
|
+
export declare function sleep(ms: number, cancelFn?: CancelableFunction): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Error thrown when a single attempt exceeds its timeout (either an explicit
|
|
33
|
+
* {@link RetryQJobOptions.attemptTimeout} or the remaining `maxTime` budget).
|
|
34
|
+
*/
|
|
35
|
+
export declare class RetryQTimeoutError extends Error {
|
|
36
|
+
/** The timeout, in milliseconds, that was exceeded. */
|
|
37
|
+
readonly timeoutMs: number;
|
|
38
|
+
/**
|
|
39
|
+
* @param timeoutMs - The timeout that was exceeded, in milliseconds.
|
|
40
|
+
*/
|
|
41
|
+
constructor(timeoutMs: number);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKrD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAMjC;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9E;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,uDAAuD;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;OAEG;gBACS,SAAS,EAAE,MAAM;CAK9B"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RetryQTimeoutError = void 0;
|
|
4
|
+
exports.randomId = randomId;
|
|
5
|
+
exports.clamp = clamp;
|
|
6
|
+
exports.sleep = sleep;
|
|
7
|
+
/** Monotonic counter mixed into ids to harden against same-millisecond collisions. */
|
|
8
|
+
let idCounter = 0;
|
|
9
|
+
/**
|
|
10
|
+
* Generate a collision-resistant job id.
|
|
11
|
+
*
|
|
12
|
+
* Format: `job-{timestamp}-{counter}-{random1}{random2}`. The counter plus two
|
|
13
|
+
* independent random segments make same-millisecond collisions practically
|
|
14
|
+
* impossible even under high concurrency.
|
|
15
|
+
*
|
|
16
|
+
* @returns A unique job identifier.
|
|
17
|
+
*/
|
|
18
|
+
function randomId() {
|
|
19
|
+
const timestamp = Date.now();
|
|
20
|
+
const counter = (idCounter++ % 10000).toString(36);
|
|
21
|
+
const random1 = Math.random().toString(36).slice(2, 11);
|
|
22
|
+
const random2 = Math.random().toString(36).slice(2, 11);
|
|
23
|
+
return `job-${timestamp}-${counter}-${random1}${random2}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Clamp a number into the inclusive range `[min, max]`.
|
|
27
|
+
*
|
|
28
|
+
* @param value - Value to clamp.
|
|
29
|
+
* @param min - Lower bound.
|
|
30
|
+
* @param max - Upper bound.
|
|
31
|
+
* @returns The clamped value.
|
|
32
|
+
*/
|
|
33
|
+
function clamp(value, min, max) {
|
|
34
|
+
return Math.max(min, Math.min(value, max));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Sleep for `ms` milliseconds. If a {@link CancelableFunction} is supplied, a
|
|
38
|
+
* `cancelSleep` hook is attached to it so the delay can be interrupted early
|
|
39
|
+
* (used to abort the wait between retries when a job is cancelled).
|
|
40
|
+
*
|
|
41
|
+
* @param ms - Duration to sleep.
|
|
42
|
+
* @param cancelFn - Optional handle that receives a `cancelSleep` interrupter.
|
|
43
|
+
* @returns A promise that resolves after `ms`, or rejects if interrupted.
|
|
44
|
+
*/
|
|
45
|
+
function sleep(ms, cancelFn) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const timer = setTimeout(() => resolve(), ms);
|
|
48
|
+
if (cancelFn) {
|
|
49
|
+
cancelFn.cancelSleep = () => {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
reject(new Error("RetryQ job cancelled"));
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Error thrown when a single attempt exceeds its timeout (either an explicit
|
|
58
|
+
* {@link RetryQJobOptions.attemptTimeout} or the remaining `maxTime` budget).
|
|
59
|
+
*/
|
|
60
|
+
class RetryQTimeoutError extends Error {
|
|
61
|
+
/**
|
|
62
|
+
* @param timeoutMs - The timeout that was exceeded, in milliseconds.
|
|
63
|
+
*/
|
|
64
|
+
constructor(timeoutMs) {
|
|
65
|
+
super(`RetryQ attempt timed out after ${timeoutMs}ms`);
|
|
66
|
+
this.name = "RetryQTimeoutError";
|
|
67
|
+
this.timeoutMs = timeoutMs;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.RetryQTimeoutError = RetryQTimeoutError;
|
|
71
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":";;;AAcA,4BAMC;AAUD,sBAEC;AAWD,sBAUC;AAnDD,sFAAsF;AACtF,IAAI,SAAS,GAAG,CAAC,CAAC;AAElB;;;;;;;;GAQG;AACH,SAAgB,QAAQ;IACtB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,CAAC,SAAS,EAAE,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,KAAK,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,KAAK,CAAC,EAAU,EAAE,QAA6B;IAC7D,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,WAAW,GAAG,GAAG,EAAE;gBAC1B,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,MAAM,CAAC,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;YAC5C,CAAC,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAa,kBAAmB,SAAQ,KAAK;IAI3C;;OAEG;IACH,YAAY,SAAiB;QAC3B,KAAK,CAAC,kCAAkC,SAAS,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AAZD,gDAYC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { RetryQJobOptions } from "./types.js";
|
|
2
|
+
/** Default values applied to unspecified {@link RetryQJobOptions}. */
|
|
3
|
+
export declare const DEFAULT_OPTIONS: {
|
|
4
|
+
readonly retries: 3;
|
|
5
|
+
readonly delay: 1000;
|
|
6
|
+
readonly backoff: 2;
|
|
7
|
+
readonly maxTime: 30000;
|
|
8
|
+
readonly maxDelay: number;
|
|
9
|
+
readonly attemptTimeout: number;
|
|
10
|
+
readonly jitter: 0.1;
|
|
11
|
+
readonly priority: 1;
|
|
12
|
+
};
|
|
13
|
+
/** Hard cap on retries to guard against accidental denial-of-service. */
|
|
14
|
+
export declare const MAX_RETRIES = 100;
|
|
15
|
+
/**
|
|
16
|
+
* Fully-resolved numeric options used internally by the execution loop. Every
|
|
17
|
+
* field is guaranteed present after {@link resolveOptions}.
|
|
18
|
+
*/
|
|
19
|
+
export interface ResolvedOptions {
|
|
20
|
+
retries: number;
|
|
21
|
+
delay: number;
|
|
22
|
+
backoff: number;
|
|
23
|
+
maxTime: number;
|
|
24
|
+
maxDelay: number;
|
|
25
|
+
attemptTimeout: number;
|
|
26
|
+
jitter: number;
|
|
27
|
+
priority: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Validate and apply defaults to user-supplied job options.
|
|
31
|
+
*
|
|
32
|
+
* @param options - Raw options passed to {@link RetryQManager.createJob}.
|
|
33
|
+
* @returns The resolved numeric options with all defaults applied.
|
|
34
|
+
* @throws {Error} If any option is outside its allowed range.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveOptions(options: RetryQJobOptions<any>): ResolvedOptions;
|
|
37
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,sEAAsE;AACtE,eAAO,MAAM,eAAe;;;;;;;;;CASlB,CAAC;AAEX,yEAAyE;AACzE,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,eAAe,CA6C9E"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_RETRIES = exports.DEFAULT_OPTIONS = void 0;
|
|
4
|
+
exports.resolveOptions = resolveOptions;
|
|
5
|
+
/** Default values applied to unspecified {@link RetryQJobOptions}. */
|
|
6
|
+
exports.DEFAULT_OPTIONS = {
|
|
7
|
+
retries: 3,
|
|
8
|
+
delay: 1000,
|
|
9
|
+
backoff: 2,
|
|
10
|
+
maxTime: 30000,
|
|
11
|
+
maxDelay: Infinity,
|
|
12
|
+
attemptTimeout: Infinity,
|
|
13
|
+
jitter: 0.1,
|
|
14
|
+
priority: 1,
|
|
15
|
+
};
|
|
16
|
+
/** Hard cap on retries to guard against accidental denial-of-service. */
|
|
17
|
+
exports.MAX_RETRIES = 100;
|
|
18
|
+
/**
|
|
19
|
+
* Validate and apply defaults to user-supplied job options.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Raw options passed to {@link RetryQManager.createJob}.
|
|
22
|
+
* @returns The resolved numeric options with all defaults applied.
|
|
23
|
+
* @throws {Error} If any option is outside its allowed range.
|
|
24
|
+
*/
|
|
25
|
+
function resolveOptions(options) {
|
|
26
|
+
const retries = options.retries ?? exports.DEFAULT_OPTIONS.retries;
|
|
27
|
+
const delay = options.delay ?? exports.DEFAULT_OPTIONS.delay;
|
|
28
|
+
const backoff = options.backoff ?? exports.DEFAULT_OPTIONS.backoff;
|
|
29
|
+
const maxTime = options.maxTime ?? exports.DEFAULT_OPTIONS.maxTime;
|
|
30
|
+
const maxDelay = options.maxDelay ?? exports.DEFAULT_OPTIONS.maxDelay;
|
|
31
|
+
const attemptTimeout = options.attemptTimeout ?? exports.DEFAULT_OPTIONS.attemptTimeout;
|
|
32
|
+
const jitter = options.jitter ?? exports.DEFAULT_OPTIONS.jitter;
|
|
33
|
+
const priority = options.priority ?? exports.DEFAULT_OPTIONS.priority;
|
|
34
|
+
if (retries < 0) {
|
|
35
|
+
throw new Error("retries must be >= 0");
|
|
36
|
+
}
|
|
37
|
+
if (retries > exports.MAX_RETRIES) {
|
|
38
|
+
throw new Error(`retries cannot exceed ${exports.MAX_RETRIES} (DoS protection)`);
|
|
39
|
+
}
|
|
40
|
+
if (delay < 0) {
|
|
41
|
+
throw new Error("delay must be >= 0");
|
|
42
|
+
}
|
|
43
|
+
if (backoff < 1) {
|
|
44
|
+
throw new Error("backoff must be >= 1");
|
|
45
|
+
}
|
|
46
|
+
if (maxTime <= 0) {
|
|
47
|
+
throw new Error("maxTime must be > 0");
|
|
48
|
+
}
|
|
49
|
+
if (maxDelay < 0) {
|
|
50
|
+
throw new Error("maxDelay must be >= 0");
|
|
51
|
+
}
|
|
52
|
+
if (attemptTimeout <= 0) {
|
|
53
|
+
throw new Error("attemptTimeout must be > 0");
|
|
54
|
+
}
|
|
55
|
+
if (jitter < 0 || jitter > 1) {
|
|
56
|
+
throw new Error("jitter must be between 0 and 1");
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
retries,
|
|
60
|
+
delay,
|
|
61
|
+
backoff,
|
|
62
|
+
maxTime,
|
|
63
|
+
maxDelay,
|
|
64
|
+
attemptTimeout,
|
|
65
|
+
jitter,
|
|
66
|
+
priority,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/validation.ts"],"names":[],"mappings":";;;AAuCA,wCA6CC;AAlFD,sEAAsE;AACzD,QAAA,eAAe,GAAG;IAC7B,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,KAAK;IACd,QAAQ,EAAE,QAAQ;IAClB,cAAc,EAAE,QAAQ;IACxB,MAAM,EAAE,GAAG;IACX,QAAQ,EAAE,CAAC;CACH,CAAC;AAEX,yEAAyE;AAC5D,QAAA,WAAW,GAAG,GAAG,CAAC;AAiB/B;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,OAA8B;IAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,uBAAe,CAAC,OAAO,CAAC;IAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,uBAAe,CAAC,KAAK,CAAC;IACrD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,uBAAe,CAAC,OAAO,CAAC;IAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,uBAAe,CAAC,OAAO,CAAC;IAC3D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,uBAAe,CAAC,QAAQ,CAAC;IAC9D,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,uBAAe,CAAC,cAAc,CAAC;IAChF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,uBAAe,CAAC,MAAM,CAAC;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,uBAAe,CAAC,QAAQ,CAAC;IAE9D,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,OAAO,GAAG,mBAAW,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,yBAAyB,mBAAW,mBAAmB,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;IACD,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,OAAO;QACL,OAAO;QACP,KAAK;QACL,OAAO;QACP,OAAO;QACP,QAAQ;QACR,cAAc;QACd,MAAM;QACN,QAAQ;KACT,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@anishhs/retryq` — a production-ready, zero-dependency retry queue manager.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
export { RetryQManager } from "./manager.js";
|
|
7
|
+
export { RetryQTimeoutError } from "./utils.js";
|
|
8
|
+
export type { CancelableFunction, CancelEvent, FailureEvent, JobListSnapshot, JobState, JobSummary, RetryEvent, RetryInfo, RetryQEvents, RetryQJob, RetryQJobOptions, RetryQManagerConfig, SuccessEvent, } from "./types.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EACV,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,GACb,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { JobListSnapshot, JobState, RetryQEvents, RetryQJob, RetryQJobOptions, RetryQManagerConfig } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* In-memory retry queue manager with concurrency control, priority scheduling,
|
|
5
|
+
* exponential backoff with jitter, lifecycle events, cooperative/force
|
|
6
|
+
* cancellation, and bounded job history.
|
|
7
|
+
*
|
|
8
|
+
* The manager extends {@link EventEmitter}; subscribe to `retry`, `success`,
|
|
9
|
+
* `failure`, `cancel`, and `idle` events (see {@link RetryQEvents}).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const q = new RetryQManager({ maxConcurrent: 3 });
|
|
14
|
+
* q.on("failure", ({ job, error }) => console.error(job.label, error));
|
|
15
|
+
*
|
|
16
|
+
* const job = q.createJob(async (signal) => {
|
|
17
|
+
* const res = await fetch("https://api.example.com", { signal });
|
|
18
|
+
* if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
19
|
+
* return res.json();
|
|
20
|
+
* }, { retries: 5, shouldRetry: (e) => !`${e}`.includes("404") });
|
|
21
|
+
*
|
|
22
|
+
* await job.promise;
|
|
23
|
+
* await q.onIdle();
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare class RetryQManager extends EventEmitter {
|
|
27
|
+
private pendingQueue;
|
|
28
|
+
private runningJobs;
|
|
29
|
+
private failedJobs;
|
|
30
|
+
private completedJobs;
|
|
31
|
+
private cancelledJobs;
|
|
32
|
+
private maxConcurrent;
|
|
33
|
+
private maxHistorySize;
|
|
34
|
+
private registry;
|
|
35
|
+
/** Cleanup callbacks (e.g. external-signal listener removal) keyed by job id. */
|
|
36
|
+
private cleanups;
|
|
37
|
+
/** Resolvers waiting for the queue to become idle. */
|
|
38
|
+
private idleWaiters;
|
|
39
|
+
/** Tracks idle state so the `idle` event only fires on transition. */
|
|
40
|
+
private wasIdle;
|
|
41
|
+
/**
|
|
42
|
+
* @param config - Manager configuration, or a number for `maxConcurrent`
|
|
43
|
+
* (legacy form, kept for backwards compatibility).
|
|
44
|
+
*/
|
|
45
|
+
constructor(config?: RetryQManagerConfig | number);
|
|
46
|
+
/** Subscribe to a manager event. See {@link RetryQEvents}. */
|
|
47
|
+
on<E extends keyof RetryQEvents>(event: E, listener: RetryQEvents[E]): this;
|
|
48
|
+
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
49
|
+
/** Subscribe to a manager event once. See {@link RetryQEvents}. */
|
|
50
|
+
once<E extends keyof RetryQEvents>(event: E, listener: RetryQEvents[E]): this;
|
|
51
|
+
once(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
52
|
+
/** Remove a previously registered listener. See {@link RetryQEvents}. */
|
|
53
|
+
off<E extends keyof RetryQEvents>(event: E, listener: RetryQEvents[E]): this;
|
|
54
|
+
off(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
55
|
+
/** Emit a manager event. See {@link RetryQEvents}. */
|
|
56
|
+
emit<E extends keyof RetryQEvents>(event: E, ...args: Parameters<RetryQEvents[E]>): boolean;
|
|
57
|
+
emit(event: string | symbol, ...args: any[]): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Create and immediately enqueue a job.
|
|
60
|
+
*
|
|
61
|
+
* @typeParam T - Resolved value produced by `fn`.
|
|
62
|
+
* @param fn - Async function to run. Receives an {@link AbortSignal} that
|
|
63
|
+
* aborts on force-cancellation or attempt/`maxTime` timeout.
|
|
64
|
+
* @param options - Per-job configuration (see {@link RetryQJobOptions}).
|
|
65
|
+
* @returns The created {@link RetryQJob}. Await `job.promise` for the result.
|
|
66
|
+
* @throws {Error} If any option fails validation.
|
|
67
|
+
*/
|
|
68
|
+
createJob<T>(fn: (signal?: AbortSignal) => Promise<T>, options?: RetryQJobOptions<T>): RetryQJob<T>;
|
|
69
|
+
/**
|
|
70
|
+
* Cancel a pending or running job.
|
|
71
|
+
*
|
|
72
|
+
* Cooperative (default) cancellation stops future retries and interrupts the
|
|
73
|
+
* delay between them. Force cancellation additionally aborts the in-flight
|
|
74
|
+
* attempt via its {@link AbortSignal}.
|
|
75
|
+
*
|
|
76
|
+
* @param id - Id of the job to cancel.
|
|
77
|
+
* @param force - When `true`, abort in-progress execution. Defaults to `false`.
|
|
78
|
+
*/
|
|
79
|
+
cancelJob(id: string, force?: boolean): void;
|
|
80
|
+
/**
|
|
81
|
+
* Returns a promise that resolves when the queue is fully idle — no pending
|
|
82
|
+
* and no running jobs. Resolves immediately if already idle.
|
|
83
|
+
*
|
|
84
|
+
* @returns A promise that resolves on idle.
|
|
85
|
+
*/
|
|
86
|
+
onIdle(): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Alias for {@link RetryQManager.onIdle}.
|
|
89
|
+
* @returns A promise that resolves when the queue is idle.
|
|
90
|
+
*/
|
|
91
|
+
drain(): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Clear retained job history.
|
|
94
|
+
*
|
|
95
|
+
* @param state - Optional terminal state to clear (`completed`, `failed`, or
|
|
96
|
+
* `cancelled`). When omitted, all history is cleared.
|
|
97
|
+
*/
|
|
98
|
+
clearHistory(state?: JobState): void;
|
|
99
|
+
/**
|
|
100
|
+
* Snapshot of all jobs grouped by state.
|
|
101
|
+
* @returns A {@link JobListSnapshot}.
|
|
102
|
+
*/
|
|
103
|
+
listJobs(): JobListSnapshot;
|
|
104
|
+
/**
|
|
105
|
+
* Find a job by id across every state.
|
|
106
|
+
* @param id - Job id.
|
|
107
|
+
* @returns The job, or `null` if not found.
|
|
108
|
+
*/
|
|
109
|
+
findJobById(id: string): RetryQJob | null;
|
|
110
|
+
/**
|
|
111
|
+
* Find all jobs (across every state) with a matching label.
|
|
112
|
+
* @param label - Label to match.
|
|
113
|
+
* @returns Matching jobs.
|
|
114
|
+
*/
|
|
115
|
+
findJobsByLabel(label: string): RetryQJob[];
|
|
116
|
+
/** Evict the oldest entry from a history map when it reaches the size cap. */
|
|
117
|
+
private _evictOldest;
|
|
118
|
+
/** Sort the pending queue by descending priority (stable: FIFO within a tier). */
|
|
119
|
+
private _sortQueue;
|
|
120
|
+
/** Promote pending jobs into running while capacity allows (synchronous). */
|
|
121
|
+
private _processQueue;
|
|
122
|
+
private _isIdle;
|
|
123
|
+
/** Resolve idle waiters and emit `idle` once when the queue drains. */
|
|
124
|
+
private _checkIdle;
|
|
125
|
+
/** Run and remove a job's cleanup callback, if any. */
|
|
126
|
+
private _runCleanup;
|
|
127
|
+
/**
|
|
128
|
+
* Execute a single attempt, bounded by `timeoutMs`. A per-attempt
|
|
129
|
+
* AbortController is linked to the job's controller so force-cancellation
|
|
130
|
+
* still propagates, while a timeout aborts only this attempt (leaving the job
|
|
131
|
+
* free to retry). Uses `Promise.race` so the loop advances even if `fn`
|
|
132
|
+
* ignores the signal.
|
|
133
|
+
*/
|
|
134
|
+
private _runAttempt;
|
|
135
|
+
/** Core retry loop for a single job. */
|
|
136
|
+
private _runJob;
|
|
137
|
+
private _jobSummary;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"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"}
|