flowx-control 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 +443 -0
- package/dist/batch.d.mts +37 -0
- package/dist/batch.d.ts +37 -0
- package/dist/batch.js +75 -0
- package/dist/batch.js.map +1 -0
- package/dist/batch.mjs +73 -0
- package/dist/batch.mjs.map +1 -0
- package/dist/bulkhead.d.mts +30 -0
- package/dist/bulkhead.d.ts +30 -0
- package/dist/bulkhead.js +85 -0
- package/dist/bulkhead.js.map +1 -0
- package/dist/bulkhead.mjs +83 -0
- package/dist/bulkhead.mjs.map +1 -0
- package/dist/circuit-breaker.d.mts +44 -0
- package/dist/circuit-breaker.d.ts +44 -0
- package/dist/circuit-breaker.js +132 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/circuit-breaker.mjs +130 -0
- package/dist/circuit-breaker.mjs.map +1 -0
- package/dist/debounce.d.mts +30 -0
- package/dist/debounce.d.ts +30 -0
- package/dist/debounce.js +96 -0
- package/dist/debounce.js.map +1 -0
- package/dist/debounce.mjs +94 -0
- package/dist/debounce.mjs.map +1 -0
- package/dist/deferred.d.mts +27 -0
- package/dist/deferred.d.ts +27 -0
- package/dist/deferred.js +42 -0
- package/dist/deferred.js.map +1 -0
- package/dist/deferred.mjs +40 -0
- package/dist/deferred.mjs.map +1 -0
- package/dist/fallback.d.mts +33 -0
- package/dist/fallback.d.ts +33 -0
- package/dist/fallback.js +43 -0
- package/dist/fallback.js.map +1 -0
- package/dist/fallback.mjs +40 -0
- package/dist/fallback.mjs.map +1 -0
- package/dist/hedge.d.mts +18 -0
- package/dist/hedge.d.ts +18 -0
- package/dist/hedge.js +47 -0
- package/dist/hedge.js.map +1 -0
- package/dist/hedge.mjs +45 -0
- package/dist/hedge.mjs.map +1 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1151 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1122 -0
- package/dist/index.mjs.map +1 -0
- package/dist/memo.d.mts +35 -0
- package/dist/memo.d.ts +35 -0
- package/dist/memo.js +74 -0
- package/dist/memo.js.map +1 -0
- package/dist/memo.mjs +72 -0
- package/dist/memo.mjs.map +1 -0
- package/dist/mutex.d.mts +24 -0
- package/dist/mutex.d.ts +24 -0
- package/dist/mutex.js +46 -0
- package/dist/mutex.js.map +1 -0
- package/dist/mutex.mjs +44 -0
- package/dist/mutex.mjs.map +1 -0
- package/dist/pipeline.d.mts +42 -0
- package/dist/pipeline.d.ts +42 -0
- package/dist/pipeline.js +30 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/pipeline.mjs +27 -0
- package/dist/pipeline.mjs.map +1 -0
- package/dist/poll.d.mts +35 -0
- package/dist/poll.d.ts +35 -0
- package/dist/poll.js +111 -0
- package/dist/poll.js.map +1 -0
- package/dist/poll.mjs +109 -0
- package/dist/poll.mjs.map +1 -0
- package/dist/queue.d.mts +47 -0
- package/dist/queue.d.ts +47 -0
- package/dist/queue.js +121 -0
- package/dist/queue.js.map +1 -0
- package/dist/queue.mjs +119 -0
- package/dist/queue.mjs.map +1 -0
- package/dist/rate-limit.d.mts +28 -0
- package/dist/rate-limit.d.ts +28 -0
- package/dist/rate-limit.js +94 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/rate-limit.mjs +92 -0
- package/dist/rate-limit.mjs.map +1 -0
- package/dist/retry.d.mts +45 -0
- package/dist/retry.d.ts +45 -0
- package/dist/retry.js +111 -0
- package/dist/retry.js.map +1 -0
- package/dist/retry.mjs +108 -0
- package/dist/retry.mjs.map +1 -0
- package/dist/semaphore.d.mts +27 -0
- package/dist/semaphore.d.ts +27 -0
- package/dist/semaphore.js +47 -0
- package/dist/semaphore.js.map +1 -0
- package/dist/semaphore.mjs +45 -0
- package/dist/semaphore.mjs.map +1 -0
- package/dist/throttle.d.mts +25 -0
- package/dist/throttle.d.ts +25 -0
- package/dist/throttle.js +97 -0
- package/dist/throttle.js.map +1 -0
- package/dist/throttle.mjs +95 -0
- package/dist/throttle.mjs.map +1 -0
- package/dist/timeout.d.mts +19 -0
- package/dist/timeout.d.ts +19 -0
- package/dist/timeout.js +79 -0
- package/dist/timeout.js.map +1 -0
- package/dist/timeout.mjs +77 -0
- package/dist/timeout.mjs.map +1 -0
- package/dist/types-BsCO2J40.d.mts +35 -0
- package/dist/types-BsCO2J40.d.ts +35 -0
- package/package.json +167 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1122 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var FlowXError = class extends Error {
|
|
3
|
+
constructor(message, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "FlowXError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var TimeoutError = class extends FlowXError {
|
|
11
|
+
constructor(message = "Operation timed out") {
|
|
12
|
+
super(message, "ERR_TIMEOUT");
|
|
13
|
+
this.name = "TimeoutError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var CircuitBreakerError = class extends FlowXError {
|
|
17
|
+
constructor(message = "Circuit breaker is open") {
|
|
18
|
+
super(message, "ERR_CIRCUIT_OPEN");
|
|
19
|
+
this.name = "CircuitBreakerError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var BulkheadError = class extends FlowXError {
|
|
23
|
+
constructor(message = "Bulkhead capacity exceeded") {
|
|
24
|
+
super(message, "ERR_BULKHEAD_FULL");
|
|
25
|
+
this.name = "BulkheadError";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var AbortError = class extends FlowXError {
|
|
29
|
+
constructor(message = "Operation aborted") {
|
|
30
|
+
super(message, "ERR_ABORTED");
|
|
31
|
+
this.name = "AbortError";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var RateLimitError = class extends FlowXError {
|
|
35
|
+
constructor(message = "Rate limit exceeded") {
|
|
36
|
+
super(message, "ERR_RATE_LIMIT");
|
|
37
|
+
this.name = "RateLimitError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
function sleep(ms, signal) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
if (signal?.aborted) {
|
|
43
|
+
reject(new AbortError());
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let onAbort;
|
|
47
|
+
const timer = setTimeout(() => {
|
|
48
|
+
if (signal && onAbort) {
|
|
49
|
+
signal.removeEventListener("abort", onAbort);
|
|
50
|
+
}
|
|
51
|
+
resolve();
|
|
52
|
+
}, ms);
|
|
53
|
+
if (signal) {
|
|
54
|
+
onAbort = () => {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
reject(new AbortError());
|
|
57
|
+
};
|
|
58
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function calculateDelay(attempt, baseDelay, strategy, jitter = false) {
|
|
63
|
+
let delay;
|
|
64
|
+
if (typeof strategy === "function") {
|
|
65
|
+
delay = strategy(attempt, baseDelay);
|
|
66
|
+
} else {
|
|
67
|
+
switch (strategy) {
|
|
68
|
+
case "fixed":
|
|
69
|
+
delay = baseDelay;
|
|
70
|
+
break;
|
|
71
|
+
case "linear":
|
|
72
|
+
delay = baseDelay * attempt;
|
|
73
|
+
break;
|
|
74
|
+
case "exponential":
|
|
75
|
+
delay = baseDelay * Math.pow(2, attempt - 1);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (jitter) {
|
|
80
|
+
const factor = typeof jitter === "number" ? jitter : 1;
|
|
81
|
+
delay = delay * (1 - factor * 0.5 + Math.random() * factor);
|
|
82
|
+
}
|
|
83
|
+
return Math.max(0, Math.floor(delay));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/retry.ts
|
|
87
|
+
async function retry(fn, options) {
|
|
88
|
+
const {
|
|
89
|
+
retries = 3,
|
|
90
|
+
delay = 1e3,
|
|
91
|
+
backoff = "exponential",
|
|
92
|
+
jitter = false,
|
|
93
|
+
maxDelay = 3e4,
|
|
94
|
+
shouldRetry,
|
|
95
|
+
onRetry,
|
|
96
|
+
signal
|
|
97
|
+
} = options ?? {};
|
|
98
|
+
if (retries < 0) {
|
|
99
|
+
throw new RangeError("retries must be >= 0");
|
|
100
|
+
}
|
|
101
|
+
let lastError;
|
|
102
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
103
|
+
if (signal?.aborted) {
|
|
104
|
+
throw new AbortError("Retry aborted");
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const result = await fn();
|
|
108
|
+
return result;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
111
|
+
if (attempt === retries) break;
|
|
112
|
+
if (shouldRetry) {
|
|
113
|
+
const shouldContinue = await shouldRetry(lastError, attempt + 1);
|
|
114
|
+
if (!shouldContinue) break;
|
|
115
|
+
}
|
|
116
|
+
if (onRetry) {
|
|
117
|
+
await onRetry(lastError, attempt + 1);
|
|
118
|
+
}
|
|
119
|
+
let waitTime = calculateDelay(attempt + 1, delay, backoff, jitter);
|
|
120
|
+
waitTime = Math.min(waitTime, maxDelay);
|
|
121
|
+
await sleep(waitTime, signal);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
throw lastError;
|
|
125
|
+
}
|
|
126
|
+
function retryable(fn, options) {
|
|
127
|
+
return (...args) => retry(() => fn(...args), options);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/circuit-breaker.ts
|
|
131
|
+
function createCircuitBreaker(fn, options) {
|
|
132
|
+
const {
|
|
133
|
+
failureThreshold = 5,
|
|
134
|
+
resetTimeout = 3e4,
|
|
135
|
+
halfOpenLimit = 1,
|
|
136
|
+
shouldTrip,
|
|
137
|
+
onStateChange,
|
|
138
|
+
successThreshold = 1
|
|
139
|
+
} = options ?? {};
|
|
140
|
+
let state = "closed";
|
|
141
|
+
let failureCount = 0;
|
|
142
|
+
let successCount = 0;
|
|
143
|
+
let halfOpenActive = 0;
|
|
144
|
+
let resetTimer = null;
|
|
145
|
+
function transition(to) {
|
|
146
|
+
if (state === to) return;
|
|
147
|
+
const from = state;
|
|
148
|
+
state = to;
|
|
149
|
+
onStateChange?.(from, to);
|
|
150
|
+
}
|
|
151
|
+
function scheduleReset() {
|
|
152
|
+
if (resetTimer) clearTimeout(resetTimer);
|
|
153
|
+
resetTimer = setTimeout(() => {
|
|
154
|
+
resetTimer = null;
|
|
155
|
+
transition("half-open");
|
|
156
|
+
halfOpenActive = 0;
|
|
157
|
+
successCount = 0;
|
|
158
|
+
}, resetTimeout);
|
|
159
|
+
}
|
|
160
|
+
function reset() {
|
|
161
|
+
if (resetTimer) {
|
|
162
|
+
clearTimeout(resetTimer);
|
|
163
|
+
resetTimer = null;
|
|
164
|
+
}
|
|
165
|
+
failureCount = 0;
|
|
166
|
+
successCount = 0;
|
|
167
|
+
halfOpenActive = 0;
|
|
168
|
+
transition("closed");
|
|
169
|
+
}
|
|
170
|
+
function manualOpen() {
|
|
171
|
+
if (resetTimer) {
|
|
172
|
+
clearTimeout(resetTimer);
|
|
173
|
+
resetTimer = null;
|
|
174
|
+
}
|
|
175
|
+
failureCount = 0;
|
|
176
|
+
successCount = 0;
|
|
177
|
+
halfOpenActive = 0;
|
|
178
|
+
transition("open");
|
|
179
|
+
scheduleReset();
|
|
180
|
+
}
|
|
181
|
+
async function fire(...args) {
|
|
182
|
+
if (state === "open") {
|
|
183
|
+
throw new CircuitBreakerError();
|
|
184
|
+
}
|
|
185
|
+
if (state === "half-open" && halfOpenActive >= halfOpenLimit) {
|
|
186
|
+
throw new CircuitBreakerError("Circuit breaker is half-open \u2014 limit reached");
|
|
187
|
+
}
|
|
188
|
+
if (state === "half-open") {
|
|
189
|
+
halfOpenActive++;
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const result = await fn(...args);
|
|
193
|
+
if (state === "half-open") {
|
|
194
|
+
successCount++;
|
|
195
|
+
halfOpenActive--;
|
|
196
|
+
if (successCount >= successThreshold) {
|
|
197
|
+
reset();
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
failureCount = 0;
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
205
|
+
if (shouldTrip && !shouldTrip(err)) {
|
|
206
|
+
if (state === "half-open") {
|
|
207
|
+
halfOpenActive--;
|
|
208
|
+
}
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
211
|
+
if (state === "half-open") {
|
|
212
|
+
halfOpenActive--;
|
|
213
|
+
transition("open");
|
|
214
|
+
scheduleReset();
|
|
215
|
+
} else {
|
|
216
|
+
failureCount++;
|
|
217
|
+
if (failureCount >= failureThreshold) {
|
|
218
|
+
transition("open");
|
|
219
|
+
scheduleReset();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
throw err;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
fire,
|
|
227
|
+
get state() {
|
|
228
|
+
return state;
|
|
229
|
+
},
|
|
230
|
+
get failureCount() {
|
|
231
|
+
return failureCount;
|
|
232
|
+
},
|
|
233
|
+
get successCount() {
|
|
234
|
+
return successCount;
|
|
235
|
+
},
|
|
236
|
+
reset,
|
|
237
|
+
open: manualOpen
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/bulkhead.ts
|
|
242
|
+
function createBulkhead(options) {
|
|
243
|
+
const { concurrency = 10, queueSize = 10, queueTimeout = 0 } = options ?? {};
|
|
244
|
+
if (concurrency < 1) throw new RangeError("concurrency must be >= 1");
|
|
245
|
+
if (queueSize < 0) throw new RangeError("queueSize must be >= 0");
|
|
246
|
+
let active = 0;
|
|
247
|
+
const queue = [];
|
|
248
|
+
function tryDequeue() {
|
|
249
|
+
while (queue.length > 0 && active < concurrency) {
|
|
250
|
+
const item = queue.shift();
|
|
251
|
+
if (item.timer) clearTimeout(item.timer);
|
|
252
|
+
run(item);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function run(item) {
|
|
256
|
+
active++;
|
|
257
|
+
try {
|
|
258
|
+
const value = await item.fn();
|
|
259
|
+
active--;
|
|
260
|
+
tryDequeue();
|
|
261
|
+
item.resolve(value);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
active--;
|
|
264
|
+
tryDequeue();
|
|
265
|
+
item.reject(error instanceof Error ? error : new Error(String(error)));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function execute(fn) {
|
|
269
|
+
if (active < concurrency) {
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
run({ fn, resolve, reject });
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
if (queue.length >= queueSize) {
|
|
275
|
+
return Promise.reject(new BulkheadError());
|
|
276
|
+
}
|
|
277
|
+
return new Promise((resolve, reject) => {
|
|
278
|
+
const item = { fn, resolve, reject };
|
|
279
|
+
if (queueTimeout > 0) {
|
|
280
|
+
item.timer = setTimeout(() => {
|
|
281
|
+
const idx = queue.indexOf(item);
|
|
282
|
+
if (idx !== -1) {
|
|
283
|
+
queue.splice(idx, 1);
|
|
284
|
+
reject(new BulkheadError("Bulkhead queue timeout exceeded"));
|
|
285
|
+
}
|
|
286
|
+
}, queueTimeout);
|
|
287
|
+
}
|
|
288
|
+
queue.push(item);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
execute,
|
|
293
|
+
get active() {
|
|
294
|
+
return active;
|
|
295
|
+
},
|
|
296
|
+
get queued() {
|
|
297
|
+
return queue.length;
|
|
298
|
+
},
|
|
299
|
+
get available() {
|
|
300
|
+
return Math.max(0, concurrency - active);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/fallback.ts
|
|
306
|
+
async function withFallback(fn, fallback, options) {
|
|
307
|
+
try {
|
|
308
|
+
return await fn();
|
|
309
|
+
} catch (error) {
|
|
310
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
311
|
+
if (options?.shouldFallback && !options.shouldFallback(err)) {
|
|
312
|
+
throw err;
|
|
313
|
+
}
|
|
314
|
+
options?.onFallback?.(err, 0);
|
|
315
|
+
if (typeof fallback === "function") {
|
|
316
|
+
return await fallback();
|
|
317
|
+
}
|
|
318
|
+
return fallback;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async function fallbackChain(fns, options) {
|
|
322
|
+
if (fns.length === 0) {
|
|
323
|
+
throw new Error("fallbackChain requires at least one function");
|
|
324
|
+
}
|
|
325
|
+
let lastError;
|
|
326
|
+
for (let i = 0; i < fns.length; i++) {
|
|
327
|
+
try {
|
|
328
|
+
return await fns[i]();
|
|
329
|
+
} catch (error) {
|
|
330
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
331
|
+
if (options?.shouldFallback && !options.shouldFallback(lastError)) {
|
|
332
|
+
throw lastError;
|
|
333
|
+
}
|
|
334
|
+
if (i < fns.length - 1) {
|
|
335
|
+
options?.onFallback?.(lastError, i + 1);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
throw lastError;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/hedge.ts
|
|
343
|
+
function hedge(fn, options) {
|
|
344
|
+
const { delay = 500, maxHedges = 1 } = options ?? {};
|
|
345
|
+
if (delay < 0) throw new RangeError("delay must be >= 0");
|
|
346
|
+
if (maxHedges < 1) throw new RangeError("maxHedges must be >= 1");
|
|
347
|
+
return new Promise((resolve, reject) => {
|
|
348
|
+
let settled = false;
|
|
349
|
+
let completedCount = 0;
|
|
350
|
+
const totalAttempts = 1 + maxHedges;
|
|
351
|
+
const errors = [];
|
|
352
|
+
const timers = [];
|
|
353
|
+
function onResult(value) {
|
|
354
|
+
if (settled) return;
|
|
355
|
+
settled = true;
|
|
356
|
+
for (const t of timers) clearTimeout(t);
|
|
357
|
+
resolve(value);
|
|
358
|
+
}
|
|
359
|
+
function onError(error) {
|
|
360
|
+
errors.push(error);
|
|
361
|
+
completedCount++;
|
|
362
|
+
if (completedCount >= totalAttempts && !settled) {
|
|
363
|
+
settled = true;
|
|
364
|
+
reject(errors[0]);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
fn().then(onResult, (err) => onError(err instanceof Error ? err : new Error(String(err))));
|
|
368
|
+
for (let i = 0; i < maxHedges; i++) {
|
|
369
|
+
const timer = setTimeout(
|
|
370
|
+
() => {
|
|
371
|
+
if (settled) return;
|
|
372
|
+
fn().then(
|
|
373
|
+
onResult,
|
|
374
|
+
(err) => onError(err instanceof Error ? err : new Error(String(err)))
|
|
375
|
+
);
|
|
376
|
+
},
|
|
377
|
+
delay * (i + 1)
|
|
378
|
+
);
|
|
379
|
+
timers.push(timer);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/semaphore.ts
|
|
385
|
+
function createSemaphore(permits) {
|
|
386
|
+
if (permits < 1) throw new RangeError("permits must be >= 1");
|
|
387
|
+
let available = permits;
|
|
388
|
+
const waiters = [];
|
|
389
|
+
function release() {
|
|
390
|
+
available++;
|
|
391
|
+
if (waiters.length > 0) {
|
|
392
|
+
available--;
|
|
393
|
+
const next = waiters.shift();
|
|
394
|
+
next.resolve(release);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function acquire() {
|
|
398
|
+
if (available > 0) {
|
|
399
|
+
available--;
|
|
400
|
+
return Promise.resolve(release);
|
|
401
|
+
}
|
|
402
|
+
return new Promise((resolve) => {
|
|
403
|
+
waiters.push({ resolve });
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
async function runExclusive(fn) {
|
|
407
|
+
const releaseFn = await acquire();
|
|
408
|
+
try {
|
|
409
|
+
return await fn();
|
|
410
|
+
} finally {
|
|
411
|
+
releaseFn();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
acquire,
|
|
416
|
+
runExclusive,
|
|
417
|
+
get available() {
|
|
418
|
+
return available;
|
|
419
|
+
},
|
|
420
|
+
get waiting() {
|
|
421
|
+
return waiters.length;
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/mutex.ts
|
|
427
|
+
function createMutex() {
|
|
428
|
+
let locked = false;
|
|
429
|
+
const waitQueue = [];
|
|
430
|
+
function release() {
|
|
431
|
+
if (waitQueue.length > 0) {
|
|
432
|
+
const next = waitQueue.shift();
|
|
433
|
+
next(release);
|
|
434
|
+
} else {
|
|
435
|
+
locked = false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function lock() {
|
|
439
|
+
if (!locked) {
|
|
440
|
+
locked = true;
|
|
441
|
+
return Promise.resolve(release);
|
|
442
|
+
}
|
|
443
|
+
return new Promise((resolve) => {
|
|
444
|
+
waitQueue.push(resolve);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
async function runExclusive(fn) {
|
|
448
|
+
const releaseFn = await lock();
|
|
449
|
+
try {
|
|
450
|
+
return await fn();
|
|
451
|
+
} finally {
|
|
452
|
+
releaseFn();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
lock,
|
|
457
|
+
runExclusive,
|
|
458
|
+
get isLocked() {
|
|
459
|
+
return locked;
|
|
460
|
+
},
|
|
461
|
+
get waiting() {
|
|
462
|
+
return waitQueue.length;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/queue.ts
|
|
468
|
+
function createQueue(options) {
|
|
469
|
+
const { concurrency = 1, autoStart = true, timeout = 0 } = options ?? {};
|
|
470
|
+
if (concurrency < 1) throw new RangeError("concurrency must be >= 1");
|
|
471
|
+
let paused = !autoStart;
|
|
472
|
+
let active = 0;
|
|
473
|
+
const entries = [];
|
|
474
|
+
const emptyCallbacks = [];
|
|
475
|
+
const idleCallbacks = [];
|
|
476
|
+
function notifyIdle() {
|
|
477
|
+
if (active === 0 && entries.length === 0) {
|
|
478
|
+
for (const cb of idleCallbacks.splice(0)) cb();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function notifyEmpty() {
|
|
482
|
+
if (entries.length === 0) {
|
|
483
|
+
for (const cb of emptyCallbacks.splice(0)) cb();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function tryRun() {
|
|
487
|
+
if (paused) return;
|
|
488
|
+
while (active < concurrency && entries.length > 0) {
|
|
489
|
+
const entry = entries.shift();
|
|
490
|
+
active++;
|
|
491
|
+
let timer;
|
|
492
|
+
const run = async () => {
|
|
493
|
+
try {
|
|
494
|
+
let result;
|
|
495
|
+
if (timeout > 0) {
|
|
496
|
+
result = await Promise.race([
|
|
497
|
+
entry.fn(),
|
|
498
|
+
new Promise((_, rej) => {
|
|
499
|
+
timer = setTimeout(() => rej(new Error("Queue task timeout")), timeout);
|
|
500
|
+
})
|
|
501
|
+
]);
|
|
502
|
+
} else {
|
|
503
|
+
result = await entry.fn();
|
|
504
|
+
}
|
|
505
|
+
if (timer) clearTimeout(timer);
|
|
506
|
+
entry.resolve(result);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
if (timer) clearTimeout(timer);
|
|
509
|
+
entry.reject(error instanceof Error ? error : new Error(String(error)));
|
|
510
|
+
} finally {
|
|
511
|
+
active--;
|
|
512
|
+
notifyEmpty();
|
|
513
|
+
tryRun();
|
|
514
|
+
notifyIdle();
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
run();
|
|
518
|
+
}
|
|
519
|
+
notifyEmpty();
|
|
520
|
+
notifyIdle();
|
|
521
|
+
}
|
|
522
|
+
function add(fn, addOptions) {
|
|
523
|
+
const priority = addOptions?.priority ?? 0;
|
|
524
|
+
return new Promise((resolve, reject) => {
|
|
525
|
+
const entry = { fn, priority, resolve, reject };
|
|
526
|
+
let inserted = false;
|
|
527
|
+
for (let i = 0; i < entries.length; i++) {
|
|
528
|
+
if (priority < entries[i].priority) {
|
|
529
|
+
entries.splice(i, 0, entry);
|
|
530
|
+
inserted = true;
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (!inserted) {
|
|
535
|
+
entries.push(entry);
|
|
536
|
+
}
|
|
537
|
+
tryRun();
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
function addAll(fns, addOptions) {
|
|
541
|
+
return Promise.all(fns.map((fn) => add(fn, addOptions)));
|
|
542
|
+
}
|
|
543
|
+
function pause() {
|
|
544
|
+
paused = true;
|
|
545
|
+
}
|
|
546
|
+
function resume() {
|
|
547
|
+
paused = false;
|
|
548
|
+
tryRun();
|
|
549
|
+
}
|
|
550
|
+
function clear() {
|
|
551
|
+
for (const entry of entries.splice(0)) {
|
|
552
|
+
entry.reject(new Error("Queue cleared"));
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function onEmpty() {
|
|
556
|
+
if (entries.length === 0) return Promise.resolve();
|
|
557
|
+
return new Promise((resolve) => emptyCallbacks.push(resolve));
|
|
558
|
+
}
|
|
559
|
+
function onIdle() {
|
|
560
|
+
if (active === 0 && entries.length === 0) return Promise.resolve();
|
|
561
|
+
return new Promise((resolve) => idleCallbacks.push(resolve));
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
add,
|
|
565
|
+
addAll,
|
|
566
|
+
pause,
|
|
567
|
+
resume,
|
|
568
|
+
clear,
|
|
569
|
+
onEmpty,
|
|
570
|
+
onIdle,
|
|
571
|
+
get size() {
|
|
572
|
+
return entries.length;
|
|
573
|
+
},
|
|
574
|
+
get pending() {
|
|
575
|
+
return active;
|
|
576
|
+
},
|
|
577
|
+
get isPaused() {
|
|
578
|
+
return paused;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/rate-limit.ts
|
|
584
|
+
function createRateLimiter(options) {
|
|
585
|
+
const { limit, interval, strategy = "queue" } = options;
|
|
586
|
+
if (limit < 1) throw new RangeError("limit must be >= 1");
|
|
587
|
+
if (interval < 1) throw new RangeError("interval must be >= 1");
|
|
588
|
+
let tokens = limit;
|
|
589
|
+
let lastRefill = Date.now();
|
|
590
|
+
const queue = [];
|
|
591
|
+
let drainTimer = null;
|
|
592
|
+
function refill() {
|
|
593
|
+
const now = Date.now();
|
|
594
|
+
const elapsed = now - lastRefill;
|
|
595
|
+
const refillCount = Math.floor(elapsed / interval) * limit;
|
|
596
|
+
if (refillCount > 0) {
|
|
597
|
+
tokens = Math.min(limit, tokens + refillCount);
|
|
598
|
+
lastRefill = now;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function drainQueue() {
|
|
602
|
+
refill();
|
|
603
|
+
while (queue.length > 0 && tokens > 0) {
|
|
604
|
+
tokens--;
|
|
605
|
+
const item = queue.shift();
|
|
606
|
+
item.resolve();
|
|
607
|
+
}
|
|
608
|
+
if (queue.length > 0) {
|
|
609
|
+
scheduleDrain();
|
|
610
|
+
} else {
|
|
611
|
+
drainTimer = null;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function scheduleDrain() {
|
|
615
|
+
if (drainTimer) return;
|
|
616
|
+
const timeToNextToken = interval / limit;
|
|
617
|
+
drainTimer = setTimeout(() => {
|
|
618
|
+
drainTimer = null;
|
|
619
|
+
drainQueue();
|
|
620
|
+
}, timeToNextToken);
|
|
621
|
+
}
|
|
622
|
+
async function execute(fn) {
|
|
623
|
+
refill();
|
|
624
|
+
if (tokens > 0) {
|
|
625
|
+
tokens--;
|
|
626
|
+
return await fn();
|
|
627
|
+
}
|
|
628
|
+
if (strategy === "reject") {
|
|
629
|
+
throw new RateLimitError();
|
|
630
|
+
}
|
|
631
|
+
await new Promise((resolve) => {
|
|
632
|
+
queue.push({ resolve });
|
|
633
|
+
scheduleDrain();
|
|
634
|
+
});
|
|
635
|
+
return await fn();
|
|
636
|
+
}
|
|
637
|
+
function reset() {
|
|
638
|
+
tokens = limit;
|
|
639
|
+
lastRefill = Date.now();
|
|
640
|
+
if (drainTimer) {
|
|
641
|
+
clearTimeout(drainTimer);
|
|
642
|
+
drainTimer = null;
|
|
643
|
+
}
|
|
644
|
+
drainQueue();
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
execute,
|
|
648
|
+
reset,
|
|
649
|
+
get remaining() {
|
|
650
|
+
refill();
|
|
651
|
+
return tokens;
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// src/timeout.ts
|
|
657
|
+
async function withTimeout(fn, ms, options) {
|
|
658
|
+
if (ms <= 0) {
|
|
659
|
+
throw new RangeError("Timeout must be > 0");
|
|
660
|
+
}
|
|
661
|
+
if (options?.signal?.aborted) {
|
|
662
|
+
throw new AbortError();
|
|
663
|
+
}
|
|
664
|
+
const fnResult = fn();
|
|
665
|
+
return Promise.race([
|
|
666
|
+
Promise.resolve(fnResult),
|
|
667
|
+
new Promise((_, reject) => {
|
|
668
|
+
const timer = setTimeout(() => {
|
|
669
|
+
if (options?.fallback !== void 0) {
|
|
670
|
+
const fb = options.fallback;
|
|
671
|
+
if (typeof fb === "function") {
|
|
672
|
+
try {
|
|
673
|
+
const result = fb();
|
|
674
|
+
Promise.resolve(result).then(
|
|
675
|
+
(v) => reject({ __resolved: true, value: v }),
|
|
676
|
+
reject
|
|
677
|
+
);
|
|
678
|
+
} catch (err) {
|
|
679
|
+
reject(err);
|
|
680
|
+
}
|
|
681
|
+
} else {
|
|
682
|
+
reject({ __resolved: true, value: fb });
|
|
683
|
+
}
|
|
684
|
+
} else {
|
|
685
|
+
reject(new TimeoutError(options?.message));
|
|
686
|
+
}
|
|
687
|
+
}, ms);
|
|
688
|
+
if (options?.signal) {
|
|
689
|
+
const onAbort = () => {
|
|
690
|
+
clearTimeout(timer);
|
|
691
|
+
reject(new AbortError());
|
|
692
|
+
};
|
|
693
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
694
|
+
}
|
|
695
|
+
Promise.resolve(fnResult).then(
|
|
696
|
+
() => clearTimeout(timer),
|
|
697
|
+
() => clearTimeout(timer)
|
|
698
|
+
);
|
|
699
|
+
})
|
|
700
|
+
]).catch((err) => {
|
|
701
|
+
if (err && typeof err === "object" && "__resolved" in err) {
|
|
702
|
+
return err.value;
|
|
703
|
+
}
|
|
704
|
+
throw err;
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/debounce.ts
|
|
709
|
+
function debounce(fn, wait, options) {
|
|
710
|
+
const { leading = false, trailing = true, maxWait } = options ?? {};
|
|
711
|
+
if (wait < 0) throw new RangeError("wait must be >= 0");
|
|
712
|
+
let timer = null;
|
|
713
|
+
let maxTimer = null;
|
|
714
|
+
let lastArgs = null;
|
|
715
|
+
const pendingResolvers = [];
|
|
716
|
+
let isPending = false;
|
|
717
|
+
async function invoke() {
|
|
718
|
+
const args = lastArgs;
|
|
719
|
+
const resolvers = pendingResolvers.splice(0);
|
|
720
|
+
lastArgs = null;
|
|
721
|
+
isPending = false;
|
|
722
|
+
if (timer) {
|
|
723
|
+
clearTimeout(timer);
|
|
724
|
+
timer = null;
|
|
725
|
+
}
|
|
726
|
+
if (maxTimer) {
|
|
727
|
+
clearTimeout(maxTimer);
|
|
728
|
+
maxTimer = null;
|
|
729
|
+
}
|
|
730
|
+
try {
|
|
731
|
+
const result = await fn(...args);
|
|
732
|
+
for (const r of resolvers) r.resolve(result);
|
|
733
|
+
} catch (error) {
|
|
734
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
735
|
+
for (const r of resolvers) r.reject(err);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function debounced(...args) {
|
|
739
|
+
lastArgs = args;
|
|
740
|
+
return new Promise((resolve, reject) => {
|
|
741
|
+
pendingResolvers.push({ resolve, reject });
|
|
742
|
+
if (leading && !isPending) {
|
|
743
|
+
isPending = true;
|
|
744
|
+
invoke();
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
isPending = true;
|
|
748
|
+
if (timer) clearTimeout(timer);
|
|
749
|
+
if (trailing) {
|
|
750
|
+
timer = setTimeout(() => {
|
|
751
|
+
timer = null;
|
|
752
|
+
invoke();
|
|
753
|
+
}, wait);
|
|
754
|
+
}
|
|
755
|
+
if (maxWait !== void 0 && !maxTimer) {
|
|
756
|
+
maxTimer = setTimeout(() => {
|
|
757
|
+
maxTimer = null;
|
|
758
|
+
if (timer) {
|
|
759
|
+
clearTimeout(timer);
|
|
760
|
+
timer = null;
|
|
761
|
+
}
|
|
762
|
+
invoke();
|
|
763
|
+
}, maxWait);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
debounced.cancel = () => {
|
|
768
|
+
if (timer) {
|
|
769
|
+
clearTimeout(timer);
|
|
770
|
+
timer = null;
|
|
771
|
+
}
|
|
772
|
+
if (maxTimer) {
|
|
773
|
+
clearTimeout(maxTimer);
|
|
774
|
+
maxTimer = null;
|
|
775
|
+
}
|
|
776
|
+
lastArgs = null;
|
|
777
|
+
isPending = false;
|
|
778
|
+
const resolvers = pendingResolvers.splice(0);
|
|
779
|
+
for (const r of resolvers) {
|
|
780
|
+
r.reject(new Error("Debounced call cancelled"));
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
debounced.flush = async () => {
|
|
784
|
+
if (!isPending) return void 0;
|
|
785
|
+
const promise = new Promise((resolve, reject) => {
|
|
786
|
+
pendingResolvers.push({ resolve, reject });
|
|
787
|
+
});
|
|
788
|
+
await invoke();
|
|
789
|
+
return promise;
|
|
790
|
+
};
|
|
791
|
+
Object.defineProperty(debounced, "pending", {
|
|
792
|
+
get() {
|
|
793
|
+
return isPending;
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
return debounced;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// src/throttle.ts
|
|
800
|
+
function throttle(fn, wait, options) {
|
|
801
|
+
const { leading = true, trailing = true } = options ?? {};
|
|
802
|
+
if (wait < 0) throw new RangeError("wait must be >= 0");
|
|
803
|
+
let lastCallTime = null;
|
|
804
|
+
let timer = null;
|
|
805
|
+
let lastArgs = null;
|
|
806
|
+
let isPending = false;
|
|
807
|
+
const trailingResolvers = [];
|
|
808
|
+
async function invokeTrailing() {
|
|
809
|
+
const args = lastArgs;
|
|
810
|
+
const resolvers = trailingResolvers.splice(0);
|
|
811
|
+
lastArgs = null;
|
|
812
|
+
isPending = false;
|
|
813
|
+
timer = null;
|
|
814
|
+
if (!args) {
|
|
815
|
+
for (const r of resolvers) r.resolve(void 0);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
try {
|
|
819
|
+
const result = await fn(...args);
|
|
820
|
+
for (const r of resolvers) r.resolve(result);
|
|
821
|
+
} catch (error) {
|
|
822
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
823
|
+
for (const r of resolvers) r.reject(err);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
function throttled(...args) {
|
|
827
|
+
const now = Date.now();
|
|
828
|
+
lastArgs = args;
|
|
829
|
+
const timeSinceLastCall = lastCallTime === null ? wait : now - lastCallTime;
|
|
830
|
+
const shouldCallLeading = leading && timeSinceLastCall >= wait;
|
|
831
|
+
if (shouldCallLeading) {
|
|
832
|
+
lastCallTime = now;
|
|
833
|
+
lastArgs = null;
|
|
834
|
+
if (timer) {
|
|
835
|
+
clearTimeout(timer);
|
|
836
|
+
timer = null;
|
|
837
|
+
}
|
|
838
|
+
const prevResolvers = trailingResolvers.splice(0);
|
|
839
|
+
const resultPromise = (async () => {
|
|
840
|
+
try {
|
|
841
|
+
const result = await fn(...args);
|
|
842
|
+
for (const r of prevResolvers) r.resolve(result);
|
|
843
|
+
return result;
|
|
844
|
+
} catch (error) {
|
|
845
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
846
|
+
for (const r of prevResolvers) r.reject(err);
|
|
847
|
+
throw err;
|
|
848
|
+
}
|
|
849
|
+
})();
|
|
850
|
+
if (trailing) {
|
|
851
|
+
timer = setTimeout(() => {
|
|
852
|
+
lastCallTime = Date.now();
|
|
853
|
+
invokeTrailing();
|
|
854
|
+
}, wait);
|
|
855
|
+
}
|
|
856
|
+
return resultPromise;
|
|
857
|
+
}
|
|
858
|
+
isPending = true;
|
|
859
|
+
return new Promise((resolve, reject) => {
|
|
860
|
+
trailingResolvers.push({ resolve, reject });
|
|
861
|
+
if (!timer && trailing) {
|
|
862
|
+
const remaining = wait - timeSinceLastCall;
|
|
863
|
+
timer = setTimeout(() => {
|
|
864
|
+
lastCallTime = Date.now();
|
|
865
|
+
invokeTrailing();
|
|
866
|
+
}, remaining);
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
throttled.cancel = () => {
|
|
871
|
+
if (timer) {
|
|
872
|
+
clearTimeout(timer);
|
|
873
|
+
timer = null;
|
|
874
|
+
}
|
|
875
|
+
lastArgs = null;
|
|
876
|
+
lastCallTime = null;
|
|
877
|
+
isPending = false;
|
|
878
|
+
const resolvers = trailingResolvers.splice(0);
|
|
879
|
+
for (const r of resolvers) {
|
|
880
|
+
r.reject(new Error("Throttled call cancelled"));
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
Object.defineProperty(throttled, "pending", {
|
|
884
|
+
get() {
|
|
885
|
+
return isPending;
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
return throttled;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// src/batch.ts
|
|
892
|
+
async function batch(items, fn, options) {
|
|
893
|
+
const { concurrency = Infinity, batchSize = 1, onProgress, signal } = options ?? {};
|
|
894
|
+
if (batchSize < 1) throw new RangeError("batchSize must be >= 1");
|
|
895
|
+
if (concurrency < 1) throw new RangeError("concurrency must be >= 1");
|
|
896
|
+
const results = new Array(items.length);
|
|
897
|
+
const errors = /* @__PURE__ */ new Map();
|
|
898
|
+
let completed = 0;
|
|
899
|
+
let succeeded = 0;
|
|
900
|
+
let failed = 0;
|
|
901
|
+
const batches = [];
|
|
902
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
903
|
+
batches.push({
|
|
904
|
+
items: items.slice(i, i + batchSize),
|
|
905
|
+
startIndex: i
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
let batchIndex = 0;
|
|
909
|
+
async function processBatch() {
|
|
910
|
+
while (batchIndex < batches.length) {
|
|
911
|
+
if (signal?.aborted) throw new AbortError();
|
|
912
|
+
const currentBatch = batches[batchIndex++];
|
|
913
|
+
for (let i = 0; i < currentBatch.items.length; i++) {
|
|
914
|
+
if (signal?.aborted) throw new AbortError();
|
|
915
|
+
const globalIndex = currentBatch.startIndex + i;
|
|
916
|
+
try {
|
|
917
|
+
results[globalIndex] = await fn(currentBatch.items[i], globalIndex);
|
|
918
|
+
succeeded++;
|
|
919
|
+
} catch (error) {
|
|
920
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
921
|
+
errors.set(globalIndex, err);
|
|
922
|
+
results[globalIndex] = void 0;
|
|
923
|
+
failed++;
|
|
924
|
+
}
|
|
925
|
+
completed++;
|
|
926
|
+
onProgress?.(completed, items.length);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const workers = [];
|
|
931
|
+
const workerCount = Math.min(concurrency, batches.length);
|
|
932
|
+
for (let i = 0; i < workerCount; i++) {
|
|
933
|
+
workers.push(processBatch());
|
|
934
|
+
}
|
|
935
|
+
await Promise.all(workers);
|
|
936
|
+
return {
|
|
937
|
+
results,
|
|
938
|
+
total: items.length,
|
|
939
|
+
succeeded,
|
|
940
|
+
failed,
|
|
941
|
+
errors
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// src/pipeline.ts
|
|
946
|
+
function pipeline(...steps) {
|
|
947
|
+
if (steps.length === 0) {
|
|
948
|
+
throw new Error("pipeline requires at least one step");
|
|
949
|
+
}
|
|
950
|
+
return async (input) => {
|
|
951
|
+
let current = input;
|
|
952
|
+
for (const step of steps) {
|
|
953
|
+
current = await step(current);
|
|
954
|
+
}
|
|
955
|
+
return current;
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
async function pipe(input, ...steps) {
|
|
959
|
+
if (steps.length === 0) {
|
|
960
|
+
throw new Error("pipe requires at least one step");
|
|
961
|
+
}
|
|
962
|
+
let current = input;
|
|
963
|
+
for (const step of steps) {
|
|
964
|
+
current = await step(current);
|
|
965
|
+
}
|
|
966
|
+
return current;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// src/poll.ts
|
|
970
|
+
function poll(fn, options) {
|
|
971
|
+
const {
|
|
972
|
+
interval = 1e3,
|
|
973
|
+
until,
|
|
974
|
+
maxAttempts = Infinity,
|
|
975
|
+
backoff = "fixed",
|
|
976
|
+
signal
|
|
977
|
+
} = options ?? {};
|
|
978
|
+
if (interval < 0) throw new RangeError("interval must be >= 0");
|
|
979
|
+
let stopped = false;
|
|
980
|
+
let rejectFn = null;
|
|
981
|
+
const result = (async () => {
|
|
982
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
983
|
+
if (stopped || signal?.aborted) {
|
|
984
|
+
throw new AbortError("Polling stopped");
|
|
985
|
+
}
|
|
986
|
+
const value = await fn();
|
|
987
|
+
if (until ? until(value) : true) {
|
|
988
|
+
return value;
|
|
989
|
+
}
|
|
990
|
+
if (attempt < maxAttempts) {
|
|
991
|
+
const waitTime = calculateDelay(attempt, interval, backoff);
|
|
992
|
+
await sleep(waitTime, signal).catch((err) => {
|
|
993
|
+
if (stopped) throw new AbortError("Polling stopped");
|
|
994
|
+
throw err;
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
throw new Error("Polling exceeded maximum attempts");
|
|
999
|
+
})();
|
|
1000
|
+
const wrappedResult = new Promise((resolve, reject) => {
|
|
1001
|
+
rejectFn = reject;
|
|
1002
|
+
result.then(resolve, reject);
|
|
1003
|
+
});
|
|
1004
|
+
function stop() {
|
|
1005
|
+
stopped = true;
|
|
1006
|
+
rejectFn?.(new AbortError("Polling stopped"));
|
|
1007
|
+
}
|
|
1008
|
+
return {
|
|
1009
|
+
result: wrappedResult,
|
|
1010
|
+
stop
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// src/deferred.ts
|
|
1015
|
+
function createDeferred() {
|
|
1016
|
+
let isSettled = false;
|
|
1017
|
+
let isResolved = false;
|
|
1018
|
+
let isRejected = false;
|
|
1019
|
+
let resolveFn;
|
|
1020
|
+
let rejectFn;
|
|
1021
|
+
const promise = new Promise((resolve, reject) => {
|
|
1022
|
+
resolveFn = resolve;
|
|
1023
|
+
rejectFn = reject;
|
|
1024
|
+
});
|
|
1025
|
+
return {
|
|
1026
|
+
promise,
|
|
1027
|
+
resolve: (value) => {
|
|
1028
|
+
if (isSettled) return;
|
|
1029
|
+
isSettled = true;
|
|
1030
|
+
isResolved = true;
|
|
1031
|
+
resolveFn(value);
|
|
1032
|
+
},
|
|
1033
|
+
reject: (reason) => {
|
|
1034
|
+
if (isSettled) return;
|
|
1035
|
+
isSettled = true;
|
|
1036
|
+
isRejected = true;
|
|
1037
|
+
rejectFn(reason);
|
|
1038
|
+
},
|
|
1039
|
+
get isSettled() {
|
|
1040
|
+
return isSettled;
|
|
1041
|
+
},
|
|
1042
|
+
get isResolved() {
|
|
1043
|
+
return isResolved;
|
|
1044
|
+
},
|
|
1045
|
+
get isRejected() {
|
|
1046
|
+
return isRejected;
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/memo.ts
|
|
1052
|
+
function memo(fn, options) {
|
|
1053
|
+
const {
|
|
1054
|
+
ttl = 0,
|
|
1055
|
+
maxSize = 0,
|
|
1056
|
+
keyFn = (...args) => JSON.stringify(args),
|
|
1057
|
+
cacheErrors = false
|
|
1058
|
+
} = options ?? {};
|
|
1059
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1060
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1061
|
+
function isExpired(entry) {
|
|
1062
|
+
if (ttl <= 0) return false;
|
|
1063
|
+
return Date.now() - entry.timestamp > ttl;
|
|
1064
|
+
}
|
|
1065
|
+
function evict() {
|
|
1066
|
+
if (maxSize <= 0 || cache.size < maxSize) return;
|
|
1067
|
+
const firstKey = cache.keys().next().value;
|
|
1068
|
+
if (firstKey !== void 0) {
|
|
1069
|
+
cache.delete(firstKey);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
async function memoized(...args) {
|
|
1073
|
+
const key = keyFn(...args);
|
|
1074
|
+
const cached = cache.get(key);
|
|
1075
|
+
if (cached && !isExpired(cached)) {
|
|
1076
|
+
cache.delete(key);
|
|
1077
|
+
cache.set(key, cached);
|
|
1078
|
+
if (cached.isError) {
|
|
1079
|
+
throw cached.value;
|
|
1080
|
+
}
|
|
1081
|
+
return cached.value;
|
|
1082
|
+
}
|
|
1083
|
+
if (cached) {
|
|
1084
|
+
cache.delete(key);
|
|
1085
|
+
}
|
|
1086
|
+
const inflight = pending.get(key);
|
|
1087
|
+
if (inflight) return inflight;
|
|
1088
|
+
const promise = fn(...args).then((value) => {
|
|
1089
|
+
evict();
|
|
1090
|
+
cache.set(key, { value, timestamp: Date.now(), isError: false });
|
|
1091
|
+
pending.delete(key);
|
|
1092
|
+
return value;
|
|
1093
|
+
}).catch((error) => {
|
|
1094
|
+
pending.delete(key);
|
|
1095
|
+
if (cacheErrors) {
|
|
1096
|
+
evict();
|
|
1097
|
+
cache.set(key, { value: error, timestamp: Date.now(), isError: true });
|
|
1098
|
+
}
|
|
1099
|
+
throw error;
|
|
1100
|
+
});
|
|
1101
|
+
pending.set(key, promise);
|
|
1102
|
+
return promise;
|
|
1103
|
+
}
|
|
1104
|
+
memoized.clear = () => {
|
|
1105
|
+
cache.clear();
|
|
1106
|
+
pending.clear();
|
|
1107
|
+
};
|
|
1108
|
+
memoized.delete = (...args) => {
|
|
1109
|
+
const key = keyFn(...args);
|
|
1110
|
+
return cache.delete(key);
|
|
1111
|
+
};
|
|
1112
|
+
Object.defineProperty(memoized, "size", {
|
|
1113
|
+
get() {
|
|
1114
|
+
return cache.size;
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
return memoized;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
export { AbortError, BulkheadError, CircuitBreakerError, FlowXError, RateLimitError, TimeoutError, batch, calculateDelay, createBulkhead, createCircuitBreaker, createDeferred, createMutex, createQueue, createRateLimiter, createSemaphore, debounce, fallbackChain, hedge, memo, pipe, pipeline, poll, retry, retryable, sleep, throttle, withFallback, withTimeout };
|
|
1121
|
+
//# sourceMappingURL=index.mjs.map
|
|
1122
|
+
//# sourceMappingURL=index.mjs.map
|