@dcrosstech/dct-ts-utils 1.1.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/dist/index.cjs +523 -0
- package/dist/index.d.cts +142 -0
- package/dist/index.d.ts +142 -8
- package/dist/index.js +487 -8
- package/package.json +23 -12
- package/dist/lib/fifoqueue.d.ts +0 -12
- package/dist/lib/fifoqueue.js +0 -31
- package/dist/lib/heap.d.ts +0 -25
- package/dist/lib/heap.js +0 -73
- package/dist/lib/priorityqueue.d.ts +0 -16
- package/dist/lib/priorityqueue.js +0 -40
- package/dist/lib/queue.d.ts +0 -9
- package/dist/lib/queue.js +0 -1
- package/dist/lib/retry.d.ts +0 -19
- package/dist/lib/retry.js +0 -107
- package/dist/lib/semaphore.d.ts +0 -33
- package/dist/lib/semaphore.js +0 -121
- package/dist/lib/sleep.d.ts +0 -10
- package/dist/lib/sleep.js +0 -60
- package/dist/lib/stablepriorityqueue.d.ts +0 -13
- package/dist/lib/stablepriorityqueue.js +0 -40
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
BinaryHeap: () => BinaryHeap,
|
|
24
|
+
CancellationError: () => CancellationError,
|
|
25
|
+
FifoQueue: () => FifoQueue,
|
|
26
|
+
HeapPriorityQueue: () => HeapPriorityQueue,
|
|
27
|
+
RetryError: () => RetryError,
|
|
28
|
+
Semaphore: () => Semaphore,
|
|
29
|
+
StablePriorityQueue: () => StablePriorityQueue,
|
|
30
|
+
TerminalError: () => TerminalError,
|
|
31
|
+
executeWithExponentialBackoff: () => executeWithExponentialBackoff,
|
|
32
|
+
sleep: () => sleep
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// lib/fifoqueue.ts
|
|
37
|
+
var FifoQueue = class {
|
|
38
|
+
constructor() {
|
|
39
|
+
this.q = [];
|
|
40
|
+
this.head = 0;
|
|
41
|
+
}
|
|
42
|
+
size() {
|
|
43
|
+
return this.q.length - this.head;
|
|
44
|
+
}
|
|
45
|
+
isEmpty() {
|
|
46
|
+
return this.size() === 0;
|
|
47
|
+
}
|
|
48
|
+
peek() {
|
|
49
|
+
return this.isEmpty() ? void 0 : this.q[this.head];
|
|
50
|
+
}
|
|
51
|
+
enqueue(x) {
|
|
52
|
+
this.q.push(x);
|
|
53
|
+
}
|
|
54
|
+
dequeue() {
|
|
55
|
+
if (this.isEmpty()) return void 0;
|
|
56
|
+
const x = this.q[this.head++];
|
|
57
|
+
if (this.head > 64 && this.head * 2 > this.q.length) {
|
|
58
|
+
this.q = this.q.slice(this.head);
|
|
59
|
+
this.head = 0;
|
|
60
|
+
}
|
|
61
|
+
return x;
|
|
62
|
+
}
|
|
63
|
+
clear() {
|
|
64
|
+
this.q.length = 0;
|
|
65
|
+
this.head = 0;
|
|
66
|
+
}
|
|
67
|
+
toArray() {
|
|
68
|
+
return this.q.slice(this.head);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// lib/heap.ts
|
|
73
|
+
var BinaryHeap = class {
|
|
74
|
+
constructor(compare) {
|
|
75
|
+
this.heap = [];
|
|
76
|
+
this.compare = compare ?? ((a, b) => a - b);
|
|
77
|
+
}
|
|
78
|
+
size() {
|
|
79
|
+
return this.heap.length;
|
|
80
|
+
}
|
|
81
|
+
isEmpty() {
|
|
82
|
+
return this.heap.length === 0;
|
|
83
|
+
}
|
|
84
|
+
peek() {
|
|
85
|
+
return this.heap.length ? this.heap[0] : void 0;
|
|
86
|
+
}
|
|
87
|
+
push(value) {
|
|
88
|
+
this.heap.push(value);
|
|
89
|
+
this.siftUp(this.heap.length - 1);
|
|
90
|
+
return this.heap.length;
|
|
91
|
+
}
|
|
92
|
+
pop() {
|
|
93
|
+
const n = this.heap.length;
|
|
94
|
+
if (n === 0) return void 0;
|
|
95
|
+
const top = this.heap[0];
|
|
96
|
+
const last = this.heap.pop();
|
|
97
|
+
if (n > 1) {
|
|
98
|
+
this.heap[0] = last;
|
|
99
|
+
this.siftDown(0);
|
|
100
|
+
}
|
|
101
|
+
return top;
|
|
102
|
+
}
|
|
103
|
+
clear() {
|
|
104
|
+
this.heap.length = 0;
|
|
105
|
+
}
|
|
106
|
+
heapify(values) {
|
|
107
|
+
this.heap = Array.from(values);
|
|
108
|
+
for (let i = (this.heap.length >> 1) - 1; i >= 0; i--) {
|
|
109
|
+
this.siftDown(i);
|
|
110
|
+
}
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
toArray() {
|
|
114
|
+
return this.heap.slice();
|
|
115
|
+
}
|
|
116
|
+
siftUp(i) {
|
|
117
|
+
const { heap, compare } = this;
|
|
118
|
+
const x = heap[i];
|
|
119
|
+
while (i > 0) {
|
|
120
|
+
const p = i - 1 >> 1;
|
|
121
|
+
const pv = heap[p];
|
|
122
|
+
if (compare(x, pv) >= 0) break;
|
|
123
|
+
heap[i] = pv;
|
|
124
|
+
i = p;
|
|
125
|
+
}
|
|
126
|
+
heap[i] = x;
|
|
127
|
+
}
|
|
128
|
+
siftDown(i) {
|
|
129
|
+
const { heap, compare } = this;
|
|
130
|
+
const n = heap.length;
|
|
131
|
+
const x = heap[i];
|
|
132
|
+
while (true) {
|
|
133
|
+
const l = (i << 1) + 1;
|
|
134
|
+
if (l >= n) break;
|
|
135
|
+
const r = l + 1;
|
|
136
|
+
let c = l;
|
|
137
|
+
if (r < n && compare(heap[r], heap[l]) < 0) c = r;
|
|
138
|
+
if (compare(heap[c], x) >= 0) break;
|
|
139
|
+
heap[i] = heap[c];
|
|
140
|
+
i = c;
|
|
141
|
+
}
|
|
142
|
+
heap[i] = x;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// lib/priorityqueue.ts
|
|
147
|
+
var HeapPriorityQueue = class {
|
|
148
|
+
constructor() {
|
|
149
|
+
this.seq = 0;
|
|
150
|
+
this.heap = new BinaryHeap((a, b) => {
|
|
151
|
+
if (a.priority !== b.priority) return a.priority - b.priority;
|
|
152
|
+
return a.seq - b.seq;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
size() {
|
|
156
|
+
return this.heap.size();
|
|
157
|
+
}
|
|
158
|
+
isEmpty() {
|
|
159
|
+
return this.heap.isEmpty();
|
|
160
|
+
}
|
|
161
|
+
peek() {
|
|
162
|
+
return this.heap.peek()?.value;
|
|
163
|
+
}
|
|
164
|
+
enqueue(value, priority = 0) {
|
|
165
|
+
if (!Number.isFinite(priority)) {
|
|
166
|
+
throw new TypeError(`priority must be finite; got ${priority}`);
|
|
167
|
+
}
|
|
168
|
+
this.heap.push({
|
|
169
|
+
value,
|
|
170
|
+
priority: Math.trunc(priority),
|
|
171
|
+
seq: this.seq++
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
dequeue() {
|
|
175
|
+
return this.heap.pop()?.value;
|
|
176
|
+
}
|
|
177
|
+
clear() {
|
|
178
|
+
this.heap.clear();
|
|
179
|
+
this.seq = 0;
|
|
180
|
+
}
|
|
181
|
+
toArray() {
|
|
182
|
+
return this.heap.toArray().map((n) => n.value);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// lib/stablepriorityqueue.ts
|
|
187
|
+
var StablePriorityQueue = class {
|
|
188
|
+
constructor() {
|
|
189
|
+
this.seq = 0;
|
|
190
|
+
this.heap = new BinaryHeap((a, b) => {
|
|
191
|
+
if (a.priority !== b.priority) return a.priority - b.priority;
|
|
192
|
+
return a.seq - b.seq;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
size() {
|
|
196
|
+
return this.heap.size();
|
|
197
|
+
}
|
|
198
|
+
isEmpty() {
|
|
199
|
+
return this.heap.isEmpty();
|
|
200
|
+
}
|
|
201
|
+
peek() {
|
|
202
|
+
return this.heap.peek()?.value;
|
|
203
|
+
}
|
|
204
|
+
enqueue(value, priority = 0) {
|
|
205
|
+
if (!Number.isFinite(priority)) {
|
|
206
|
+
throw new TypeError(`priority must be finite; got ${priority}`);
|
|
207
|
+
}
|
|
208
|
+
this.heap.push({
|
|
209
|
+
value,
|
|
210
|
+
priority: Math.trunc(priority),
|
|
211
|
+
seq: this.seq++
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
dequeue() {
|
|
215
|
+
return this.heap.pop()?.value;
|
|
216
|
+
}
|
|
217
|
+
clear() {
|
|
218
|
+
this.heap.clear();
|
|
219
|
+
this.seq = 0;
|
|
220
|
+
}
|
|
221
|
+
toArray() {
|
|
222
|
+
return this.heap.toArray().map((n) => n.value);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// lib/sleep.ts
|
|
227
|
+
var CancellationError = class extends Error {
|
|
228
|
+
constructor(message, remainingMs) {
|
|
229
|
+
super(message);
|
|
230
|
+
this.name = "CancellationError";
|
|
231
|
+
this.remainingMs = remainingMs;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
function sleep(durationMs) {
|
|
235
|
+
if (!Number.isFinite(durationMs)) {
|
|
236
|
+
throw new TypeError(`durationMs must be finite; got ${durationMs}`);
|
|
237
|
+
}
|
|
238
|
+
const delay = Math.max(0, Math.trunc(durationMs));
|
|
239
|
+
const startMs = Date.now();
|
|
240
|
+
let completed = false;
|
|
241
|
+
let cancelled = false;
|
|
242
|
+
let executedAtMs = null;
|
|
243
|
+
let cancelledAtMs = null;
|
|
244
|
+
let timer = null;
|
|
245
|
+
let resolveFn;
|
|
246
|
+
let rejectFn;
|
|
247
|
+
const promise = new Promise((resolve, reject) => {
|
|
248
|
+
resolveFn = resolve;
|
|
249
|
+
rejectFn = reject;
|
|
250
|
+
timer = setTimeout(() => {
|
|
251
|
+
if (cancelled) return;
|
|
252
|
+
completed = true;
|
|
253
|
+
executedAtMs = Date.now();
|
|
254
|
+
resolve();
|
|
255
|
+
}, delay);
|
|
256
|
+
});
|
|
257
|
+
function remainingAt(nowMs) {
|
|
258
|
+
if (cancelled && cancelledAtMs != null) {
|
|
259
|
+
return delay - (cancelledAtMs - startMs);
|
|
260
|
+
}
|
|
261
|
+
if (completed) {
|
|
262
|
+
const exec = executedAtMs ?? startMs + delay;
|
|
263
|
+
return -(nowMs - exec);
|
|
264
|
+
}
|
|
265
|
+
const r = delay - (nowMs - startMs);
|
|
266
|
+
return r <= 0 ? 0 : r;
|
|
267
|
+
}
|
|
268
|
+
promise.cancel = (reason) => {
|
|
269
|
+
if (completed || cancelled) return false;
|
|
270
|
+
cancelled = true;
|
|
271
|
+
cancelledAtMs = Date.now();
|
|
272
|
+
if (timer) {
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
timer = null;
|
|
275
|
+
}
|
|
276
|
+
const remaining = remainingAt(cancelledAtMs);
|
|
277
|
+
rejectFn(
|
|
278
|
+
reason ?? new CancellationError(
|
|
279
|
+
`Sleep cancelled with ${remaining}ms remaining`,
|
|
280
|
+
remaining
|
|
281
|
+
)
|
|
282
|
+
);
|
|
283
|
+
return true;
|
|
284
|
+
};
|
|
285
|
+
promise.hasCompleted = () => completed;
|
|
286
|
+
promise.remainingTime = () => remainingAt(Date.now());
|
|
287
|
+
return promise;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// lib/semaphore.ts
|
|
291
|
+
var Semaphore = class {
|
|
292
|
+
constructor(max, opts = {}, initialCount) {
|
|
293
|
+
this.inFlightWeight = 0;
|
|
294
|
+
if (!Number.isFinite(max) || max < 0) {
|
|
295
|
+
throw new TypeError(`max must be finite and >= 0; got ${max}`);
|
|
296
|
+
}
|
|
297
|
+
this.max = Math.trunc(max);
|
|
298
|
+
const start = initialCount ?? this.max;
|
|
299
|
+
if (!Number.isFinite(start)) {
|
|
300
|
+
throw new TypeError(`initialCount must be finite; got ${start}`);
|
|
301
|
+
}
|
|
302
|
+
this.count = Math.trunc(start);
|
|
303
|
+
if (opts.usePriorityQueue) {
|
|
304
|
+
const pq = new StablePriorityQueue();
|
|
305
|
+
this.queue = pq;
|
|
306
|
+
this.pqueue = pq;
|
|
307
|
+
} else {
|
|
308
|
+
this.queue = new FifoQueue();
|
|
309
|
+
this.pqueue = null;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
getMax() {
|
|
313
|
+
return this.max;
|
|
314
|
+
}
|
|
315
|
+
getCount() {
|
|
316
|
+
return this.count;
|
|
317
|
+
}
|
|
318
|
+
setMax(newMax) {
|
|
319
|
+
if (!Number.isFinite(newMax) || newMax < 0) {
|
|
320
|
+
throw new TypeError(`newMax must be finite and >= 0; got ${newMax}`);
|
|
321
|
+
}
|
|
322
|
+
newMax = Math.trunc(newMax);
|
|
323
|
+
if (newMax < this.inFlightWeight) {
|
|
324
|
+
throw new RangeError(`newMax (${newMax}) < inFlightWeight (${this.inFlightWeight})`);
|
|
325
|
+
}
|
|
326
|
+
for (const item of this.queue.toArray()) {
|
|
327
|
+
if (item.weight > newMax) {
|
|
328
|
+
throw new RangeError(`queued weight ${item.weight} exceeds newMax ${newMax}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
this.max = newMax;
|
|
332
|
+
this.drain();
|
|
333
|
+
}
|
|
334
|
+
setCount(newCount) {
|
|
335
|
+
if (!Number.isFinite(newCount)) {
|
|
336
|
+
throw new TypeError(`newCount must be finite; got ${newCount}`);
|
|
337
|
+
}
|
|
338
|
+
this.count = Math.trunc(newCount);
|
|
339
|
+
for (const item of this.queue.toArray()) {
|
|
340
|
+
if (item.weight > this.max) {
|
|
341
|
+
throw new RangeError(`queued weight ${item.weight} exceeds max ${this.max}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
this.drain();
|
|
345
|
+
}
|
|
346
|
+
run(fn, opts = {}) {
|
|
347
|
+
const weight = Math.trunc(opts.weight ?? 1);
|
|
348
|
+
const priority = Math.trunc(opts.priority ?? 0);
|
|
349
|
+
if (!Number.isFinite(weight) || weight <= 0) {
|
|
350
|
+
throw new TypeError(`weight must be a finite integer > 0; got ${opts.weight}`);
|
|
351
|
+
}
|
|
352
|
+
if (!Number.isFinite(priority)) {
|
|
353
|
+
throw new TypeError(`priority must be finite; got ${opts.priority}`);
|
|
354
|
+
}
|
|
355
|
+
if (weight > this.max) {
|
|
356
|
+
throw new RangeError(`weight (${weight}) exceeds semaphore max (${this.max})`);
|
|
357
|
+
}
|
|
358
|
+
return new Promise((resolve, reject) => {
|
|
359
|
+
const item = { weight, priority, fn, resolve, reject };
|
|
360
|
+
if (this.queue.isEmpty() && this.count >= weight) {
|
|
361
|
+
this.start(item);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (this.pqueue) this.pqueue.enqueue(item, priority);
|
|
365
|
+
else this.queue.enqueue(item);
|
|
366
|
+
this.drain();
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
drain() {
|
|
370
|
+
while (true) {
|
|
371
|
+
const head = this.queue.peek();
|
|
372
|
+
if (!head) return;
|
|
373
|
+
if (this.count < head.weight) return;
|
|
374
|
+
this.start(this.queue.dequeue());
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
releaseAndScheduleDrain(weight) {
|
|
378
|
+
this.count += weight;
|
|
379
|
+
this.inFlightWeight -= weight;
|
|
380
|
+
this.drain();
|
|
381
|
+
}
|
|
382
|
+
completeResolve(item, value) {
|
|
383
|
+
this.releaseAndScheduleDrain(item.weight);
|
|
384
|
+
item.resolve(value);
|
|
385
|
+
}
|
|
386
|
+
completeReject(item, error) {
|
|
387
|
+
this.releaseAndScheduleDrain(item.weight);
|
|
388
|
+
item.reject(error);
|
|
389
|
+
}
|
|
390
|
+
start(item) {
|
|
391
|
+
this.count -= item.weight;
|
|
392
|
+
this.inFlightWeight += item.weight;
|
|
393
|
+
let r;
|
|
394
|
+
try {
|
|
395
|
+
r = item.fn();
|
|
396
|
+
} catch (e) {
|
|
397
|
+
this.completeReject(item, e);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (r && typeof r.then === "function") {
|
|
401
|
+
r.then(
|
|
402
|
+
(v) => this.completeResolve(item, v),
|
|
403
|
+
(e) => this.completeReject(item, e)
|
|
404
|
+
);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
this.completeResolve(item, r);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// lib/retry.ts
|
|
412
|
+
var RetryError = class extends Error {
|
|
413
|
+
constructor(message, retryArgs, finalError, retryAfter) {
|
|
414
|
+
super(message);
|
|
415
|
+
this.name = "RetryError";
|
|
416
|
+
this.retryArgs = retryArgs;
|
|
417
|
+
this.finalError = finalError;
|
|
418
|
+
this.retryAfter = retryAfter;
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
var TerminalError = class extends Error {
|
|
422
|
+
constructor(message) {
|
|
423
|
+
super(message);
|
|
424
|
+
this.name = "TerminalError";
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
function mustFinite(name, v, extra) {
|
|
428
|
+
if (!Number.isFinite(v)) throw new TypeError(`${name} must be finite; got ${v}`);
|
|
429
|
+
if (extra) throw new TypeError(`${name} ${extra}; got ${v}`);
|
|
430
|
+
}
|
|
431
|
+
function normalizeConfig(cfg) {
|
|
432
|
+
const quantumMs = cfg?.quantumMs ?? 10;
|
|
433
|
+
mustFinite("quantumMs", quantumMs, quantumMs < 0 ? "must be >= 0" : "");
|
|
434
|
+
const q = Math.trunc(quantumMs);
|
|
435
|
+
const minDelayMs = cfg?.minDelayMs ?? q;
|
|
436
|
+
mustFinite("minDelayMs", minDelayMs, minDelayMs < 0 ? "must be >= 0" : "");
|
|
437
|
+
const minD = Math.trunc(minDelayMs);
|
|
438
|
+
const maxDelayMs = cfg?.maxDelayMs;
|
|
439
|
+
if (maxDelayMs !== void 0) {
|
|
440
|
+
mustFinite("maxDelayMs", maxDelayMs, maxDelayMs < 0 ? "must be >= 0" : "");
|
|
441
|
+
}
|
|
442
|
+
const maxD = maxDelayMs === void 0 ? void 0 : Math.trunc(maxDelayMs);
|
|
443
|
+
const maxRetries = cfg?.maxRetries;
|
|
444
|
+
if (maxRetries !== void 0) {
|
|
445
|
+
mustFinite("maxRetries", maxRetries, maxRetries < 0 ? "must be >= 0" : "");
|
|
446
|
+
}
|
|
447
|
+
const maxR = maxRetries === void 0 ? void 0 : Math.trunc(maxRetries);
|
|
448
|
+
const factor = cfg?.factor ?? 2;
|
|
449
|
+
mustFinite("factor", factor, factor <= 0 ? "must be > 0" : "");
|
|
450
|
+
const f = factor;
|
|
451
|
+
const maxExponent = cfg?.maxExponent;
|
|
452
|
+
if (maxExponent !== void 0) {
|
|
453
|
+
mustFinite("maxExponent", maxExponent, maxExponent < 0 ? "must be >= 0" : "");
|
|
454
|
+
}
|
|
455
|
+
const maxE = maxExponent === void 0 ? void 0 : Math.trunc(maxExponent);
|
|
456
|
+
const jitter = cfg?.jitter;
|
|
457
|
+
if (jitter !== void 0) {
|
|
458
|
+
mustFinite("jitter", jitter, jitter < 0 || jitter > 1 ? "must be in [0,1]" : "");
|
|
459
|
+
}
|
|
460
|
+
const j = jitter;
|
|
461
|
+
if (maxD !== void 0 && maxD < minD) {
|
|
462
|
+
throw new RangeError(`maxDelayMs (${maxD}) < minDelayMs (${minD})`);
|
|
463
|
+
}
|
|
464
|
+
return { q, minD, maxD, maxR, f, maxE, j };
|
|
465
|
+
}
|
|
466
|
+
function computeDelayMs(q, minD, maxD, factor, maxE, jitter, retryAttempt) {
|
|
467
|
+
const k = maxE === void 0 ? retryAttempt : Math.min(retryAttempt, maxE);
|
|
468
|
+
const pow = Math.pow(factor, k);
|
|
469
|
+
const upperSteps = Number.isFinite(pow) ? Math.max(0, Math.floor(pow) - 1) : Number.MAX_SAFE_INTEGER;
|
|
470
|
+
const steps = upperSteps === 0 ? 0 : Math.floor(Math.random() * (upperSteps + 1));
|
|
471
|
+
let d = steps * q;
|
|
472
|
+
if (jitter !== void 0 && d > 0) {
|
|
473
|
+
const r = (Math.random() * 2 - 1) * jitter;
|
|
474
|
+
d = d * (1 + r);
|
|
475
|
+
}
|
|
476
|
+
if (d < minD) d = minD;
|
|
477
|
+
if (maxD !== void 0 && d > maxD) d = maxD;
|
|
478
|
+
return Math.trunc(d);
|
|
479
|
+
}
|
|
480
|
+
async function executeWithExponentialBackoff(cfg, fn, initialArgs) {
|
|
481
|
+
const { q, minD, maxD, maxR, f, maxE, j } = normalizeConfig(cfg);
|
|
482
|
+
let args = initialArgs;
|
|
483
|
+
let retryAfter;
|
|
484
|
+
for (let attempt = 0; ; attempt++) {
|
|
485
|
+
try {
|
|
486
|
+
return await fn(args);
|
|
487
|
+
} catch (err) {
|
|
488
|
+
if (err instanceof TerminalError) throw err;
|
|
489
|
+
let finalOnExhaustion;
|
|
490
|
+
if (err instanceof RetryError) {
|
|
491
|
+
args = err.retryArgs;
|
|
492
|
+
finalOnExhaustion = err.finalError;
|
|
493
|
+
retryAfter = err.retryAfter;
|
|
494
|
+
}
|
|
495
|
+
if (maxR !== void 0 && attempt >= maxR) {
|
|
496
|
+
if (finalOnExhaustion) {
|
|
497
|
+
throw finalOnExhaustion;
|
|
498
|
+
}
|
|
499
|
+
throw err;
|
|
500
|
+
}
|
|
501
|
+
const retryAttempt = attempt + 1;
|
|
502
|
+
if (!retryAfter) {
|
|
503
|
+
retryAfter = computeDelayMs(q, minD, maxD, f, maxE, j, retryAttempt);
|
|
504
|
+
}
|
|
505
|
+
if (retryAfter > 0) {
|
|
506
|
+
await sleep(retryAfter);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
512
|
+
0 && (module.exports = {
|
|
513
|
+
BinaryHeap,
|
|
514
|
+
CancellationError,
|
|
515
|
+
FifoQueue,
|
|
516
|
+
HeapPriorityQueue,
|
|
517
|
+
RetryError,
|
|
518
|
+
Semaphore,
|
|
519
|
+
StablePriorityQueue,
|
|
520
|
+
TerminalError,
|
|
521
|
+
executeWithExponentialBackoff,
|
|
522
|
+
sleep
|
|
523
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
interface Queue<T> {
|
|
2
|
+
size(): number;
|
|
3
|
+
isEmpty(): boolean;
|
|
4
|
+
peek(): T | undefined;
|
|
5
|
+
enqueue(x: T): void;
|
|
6
|
+
dequeue(): T | undefined;
|
|
7
|
+
clear(): void;
|
|
8
|
+
toArray(): T[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare class FifoQueue<T> implements Queue<T> {
|
|
12
|
+
private q;
|
|
13
|
+
private head;
|
|
14
|
+
size(): number;
|
|
15
|
+
isEmpty(): boolean;
|
|
16
|
+
peek(): T | undefined;
|
|
17
|
+
enqueue(x: T): void;
|
|
18
|
+
dequeue(): T | undefined;
|
|
19
|
+
clear(): void;
|
|
20
|
+
toArray(): T[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Heap<T> {
|
|
24
|
+
size(): number;
|
|
25
|
+
isEmpty(): boolean;
|
|
26
|
+
peek(): T | undefined;
|
|
27
|
+
push(value: T): number;
|
|
28
|
+
pop(): T | undefined;
|
|
29
|
+
clear(): void;
|
|
30
|
+
heapify(values: Iterable<T>): this;
|
|
31
|
+
toArray(): T[];
|
|
32
|
+
}
|
|
33
|
+
declare class BinaryHeap<T> implements Heap<T> {
|
|
34
|
+
private heap;
|
|
35
|
+
private readonly compare;
|
|
36
|
+
constructor(compare?: (a: T, b: T) => number);
|
|
37
|
+
size(): number;
|
|
38
|
+
isEmpty(): boolean;
|
|
39
|
+
peek(): T | undefined;
|
|
40
|
+
push(value: T): number;
|
|
41
|
+
pop(): T | undefined;
|
|
42
|
+
clear(): void;
|
|
43
|
+
heapify(values: Iterable<T>): this;
|
|
44
|
+
toArray(): T[];
|
|
45
|
+
private siftUp;
|
|
46
|
+
private siftDown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface PriorityQueue<T> extends Queue<T> {
|
|
50
|
+
enqueue(value: T, priority?: number): void;
|
|
51
|
+
}
|
|
52
|
+
declare class HeapPriorityQueue<T> implements PriorityQueue<T> {
|
|
53
|
+
private readonly heap;
|
|
54
|
+
private seq;
|
|
55
|
+
constructor();
|
|
56
|
+
size(): number;
|
|
57
|
+
isEmpty(): boolean;
|
|
58
|
+
peek(): T | undefined;
|
|
59
|
+
enqueue(value: T, priority?: number): void;
|
|
60
|
+
dequeue(): T | undefined;
|
|
61
|
+
clear(): void;
|
|
62
|
+
toArray(): T[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare class StablePriorityQueue<T> implements PriorityQueue<T> {
|
|
66
|
+
private readonly heap;
|
|
67
|
+
private seq;
|
|
68
|
+
constructor();
|
|
69
|
+
size(): number;
|
|
70
|
+
isEmpty(): boolean;
|
|
71
|
+
peek(): T | undefined;
|
|
72
|
+
enqueue(value: T, priority?: number): void;
|
|
73
|
+
dequeue(): T | undefined;
|
|
74
|
+
clear(): void;
|
|
75
|
+
toArray(): T[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare class CancellationError extends Error {
|
|
79
|
+
readonly remainingMs: number;
|
|
80
|
+
constructor(message: string, remainingMs: number);
|
|
81
|
+
}
|
|
82
|
+
interface SleepPromise extends Promise<void> {
|
|
83
|
+
cancel(reason?: unknown): boolean;
|
|
84
|
+
hasCompleted(): boolean;
|
|
85
|
+
remainingTime(): number;
|
|
86
|
+
}
|
|
87
|
+
declare function sleep(durationMs: number): SleepPromise;
|
|
88
|
+
|
|
89
|
+
type SemaphoreRunOptions = {
|
|
90
|
+
weight?: number;
|
|
91
|
+
priority?: number;
|
|
92
|
+
};
|
|
93
|
+
type SemaphoreOptions = {
|
|
94
|
+
usePriorityQueue?: boolean;
|
|
95
|
+
};
|
|
96
|
+
type WorkItem<R> = {
|
|
97
|
+
weight: number;
|
|
98
|
+
priority: number;
|
|
99
|
+
fn: () => Promise<R> | R;
|
|
100
|
+
resolve: (v: R) => void;
|
|
101
|
+
reject: (e: unknown) => void;
|
|
102
|
+
};
|
|
103
|
+
declare class Semaphore {
|
|
104
|
+
private max;
|
|
105
|
+
private count;
|
|
106
|
+
private inFlightWeight;
|
|
107
|
+
private readonly queue;
|
|
108
|
+
private readonly pqueue;
|
|
109
|
+
constructor(max: number, opts?: SemaphoreOptions, initialCount?: number);
|
|
110
|
+
getMax(): number;
|
|
111
|
+
getCount(): number;
|
|
112
|
+
setMax(newMax: number): void;
|
|
113
|
+
setCount(newCount: number): void;
|
|
114
|
+
run<R>(fn: () => Promise<R> | R, opts?: SemaphoreRunOptions): Promise<R>;
|
|
115
|
+
private drain;
|
|
116
|
+
protected releaseAndScheduleDrain(weight: number): void;
|
|
117
|
+
protected completeResolve<R>(item: WorkItem<R>, value: R): void;
|
|
118
|
+
protected completeReject<R>(item: WorkItem<R>, error: unknown): void;
|
|
119
|
+
private start;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
declare class RetryError<Args = unknown> extends Error {
|
|
123
|
+
readonly retryArgs: Args;
|
|
124
|
+
readonly finalError?: Error;
|
|
125
|
+
readonly retryAfter?: number;
|
|
126
|
+
constructor(message: string, retryArgs: Args, finalError?: Error, retryAfter?: number);
|
|
127
|
+
}
|
|
128
|
+
declare class TerminalError extends Error {
|
|
129
|
+
constructor(message: string);
|
|
130
|
+
}
|
|
131
|
+
type BackoffConfig = {
|
|
132
|
+
quantumMs?: number;
|
|
133
|
+
minDelayMs?: number;
|
|
134
|
+
maxDelayMs?: number;
|
|
135
|
+
maxRetries?: number;
|
|
136
|
+
factor?: number;
|
|
137
|
+
maxExponent?: number;
|
|
138
|
+
jitter?: number;
|
|
139
|
+
};
|
|
140
|
+
declare function executeWithExponentialBackoff<Args, R>(cfg: BackoffConfig | undefined, fn: (args: Args) => Promise<R> | R, initialArgs: Args): Promise<R>;
|
|
141
|
+
|
|
142
|
+
export { type BackoffConfig, BinaryHeap, CancellationError, FifoQueue, type Heap, HeapPriorityQueue, type PriorityQueue, type Queue, RetryError, Semaphore, type SemaphoreOptions, type SemaphoreRunOptions, type SleepPromise, StablePriorityQueue, TerminalError, executeWithExponentialBackoff, sleep };
|