@ls-stack/utils 3.7.0 → 3.8.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/lib/asyncQueue.cjs +239 -0
- package/lib/asyncQueue.d.cts +56 -0
- package/lib/asyncQueue.d.ts +56 -0
- package/lib/asyncQueue.js +201 -0
- package/lib/cache.js +1 -1
- package/lib/chunk-DFXNVEH6.js +14 -0
- package/lib/concurrentCalls.cjs +288 -0
- package/lib/concurrentCalls.d.cts +75 -0
- package/lib/concurrentCalls.d.ts +75 -0
- package/lib/concurrentCalls.js +247 -0
- package/lib/createThrottleController.js +1 -1
- package/lib/parallelAsyncCalls.d.cts +2 -0
- package/lib/parallelAsyncCalls.d.ts +2 -0
- package/lib/promiseUtils.js +3 -10
- package/lib/yamlStringify.js +3 -3
- package/package.json +11 -2
|
@@ -0,0 +1,239 @@
|
|
|
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
|
+
// src/asyncQueue.ts
|
|
21
|
+
var asyncQueue_exports = {};
|
|
22
|
+
__export(asyncQueue_exports, {
|
|
23
|
+
createAsyncQueue: () => createAsyncQueue,
|
|
24
|
+
createAsyncQueueWithId: () => createAsyncQueueWithId
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(asyncQueue_exports);
|
|
27
|
+
var import_evtmitter = require("evtmitter");
|
|
28
|
+
var import_t_result = require("t-result");
|
|
29
|
+
|
|
30
|
+
// src/assertions.ts
|
|
31
|
+
function isObject(value) {
|
|
32
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
function isFunction(value) {
|
|
35
|
+
return typeof value === "function";
|
|
36
|
+
}
|
|
37
|
+
function isPromise(value) {
|
|
38
|
+
return isObject(value) && "then" in value && isFunction(value.then);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/promiseUtils.ts
|
|
42
|
+
function defer() {
|
|
43
|
+
let resolve;
|
|
44
|
+
let reject;
|
|
45
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
46
|
+
resolve = _resolve;
|
|
47
|
+
reject = _reject;
|
|
48
|
+
});
|
|
49
|
+
return { resolve, reject, promise };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/asyncQueue.ts
|
|
53
|
+
var AsyncQueue = class {
|
|
54
|
+
#queue = [];
|
|
55
|
+
#pending = 0;
|
|
56
|
+
#size = 0;
|
|
57
|
+
#concurrency;
|
|
58
|
+
#completed = 0;
|
|
59
|
+
#failed = 0;
|
|
60
|
+
#idleResolvers = [];
|
|
61
|
+
events = (0, import_evtmitter.evtmitter)();
|
|
62
|
+
#signal;
|
|
63
|
+
#taskTimeout;
|
|
64
|
+
constructor({
|
|
65
|
+
concurrency = 1,
|
|
66
|
+
signal,
|
|
67
|
+
timeout: taskTimeout
|
|
68
|
+
} = {}) {
|
|
69
|
+
this.#concurrency = concurrency;
|
|
70
|
+
this.#signal = signal;
|
|
71
|
+
this.#taskTimeout = taskTimeout;
|
|
72
|
+
}
|
|
73
|
+
#enqueue(task) {
|
|
74
|
+
this.#queue.push(task);
|
|
75
|
+
this.#size++;
|
|
76
|
+
}
|
|
77
|
+
add(fn, options) {
|
|
78
|
+
const deferred = defer();
|
|
79
|
+
const taskTimeout = this.#taskTimeout ?? options?.timeout;
|
|
80
|
+
const task = {
|
|
81
|
+
run: async (ctx) => {
|
|
82
|
+
if (isPromise(fn)) {
|
|
83
|
+
return fn;
|
|
84
|
+
}
|
|
85
|
+
return await fn(ctx);
|
|
86
|
+
},
|
|
87
|
+
resolve: deferred.resolve,
|
|
88
|
+
reject: deferred.reject,
|
|
89
|
+
signal: options?.signal,
|
|
90
|
+
id: options?.id,
|
|
91
|
+
timeout: taskTimeout
|
|
92
|
+
};
|
|
93
|
+
this.#enqueue(task);
|
|
94
|
+
this.#processQueue();
|
|
95
|
+
return deferred.promise;
|
|
96
|
+
}
|
|
97
|
+
resultifyAdd(fn, options) {
|
|
98
|
+
const cb = async (ctx) => {
|
|
99
|
+
if (isPromise(fn)) {
|
|
100
|
+
return await fn;
|
|
101
|
+
}
|
|
102
|
+
return fn(ctx);
|
|
103
|
+
};
|
|
104
|
+
return this.add((ctx) => (0, import_t_result.resultify)(cb(ctx)), options);
|
|
105
|
+
}
|
|
106
|
+
async #processQueue() {
|
|
107
|
+
if (this.#pending >= this.#concurrency || this.#queue.length === 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const task = this.#queue.shift();
|
|
111
|
+
if (!task) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.#pending++;
|
|
115
|
+
this.#size--;
|
|
116
|
+
const signals = [];
|
|
117
|
+
if (task.signal) {
|
|
118
|
+
signals.push(task.signal);
|
|
119
|
+
}
|
|
120
|
+
if (this.#signal) {
|
|
121
|
+
signals.push(this.#signal);
|
|
122
|
+
}
|
|
123
|
+
if (task.timeout) {
|
|
124
|
+
signals.push(AbortSignal.timeout(task.timeout));
|
|
125
|
+
}
|
|
126
|
+
const signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
|
|
127
|
+
let abortListener;
|
|
128
|
+
const signalAbortPromise = new Promise((_, reject) => {
|
|
129
|
+
if (signal) {
|
|
130
|
+
const error = signal.reason instanceof Error ? signal.reason : new DOMException("Aborted", "AbortError");
|
|
131
|
+
if (signal.aborted) {
|
|
132
|
+
reject(error);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
abortListener = () => {
|
|
136
|
+
reject(error);
|
|
137
|
+
};
|
|
138
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
try {
|
|
142
|
+
const taskRunPromise = task.run({ signal, id: task.id });
|
|
143
|
+
this.events.emit("start", { id: task.id });
|
|
144
|
+
const result = await Promise.race([taskRunPromise, signalAbortPromise]);
|
|
145
|
+
if ((0, import_t_result.isResult)(result)) {
|
|
146
|
+
task.resolve(result);
|
|
147
|
+
if (result.error) {
|
|
148
|
+
this.#failed++;
|
|
149
|
+
this.events.emit("error", { id: task.id, error: result.error });
|
|
150
|
+
} else {
|
|
151
|
+
this.#completed++;
|
|
152
|
+
this.events.emit("complete", { id: task.id, value: result.value });
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
const error = new Error("Response not a Result");
|
|
156
|
+
task.resolve(import_t_result.Result.err(error));
|
|
157
|
+
this.#failed++;
|
|
158
|
+
this.events.emit("error", {
|
|
159
|
+
id: task.id,
|
|
160
|
+
error: (0, import_t_result.unknownToError)(error)
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
task.resolve(import_t_result.Result.err(error));
|
|
165
|
+
this.#failed++;
|
|
166
|
+
this.events.emit("error", { id: task.id, error: (0, import_t_result.unknownToError)(error) });
|
|
167
|
+
} finally {
|
|
168
|
+
if (signal && abortListener) {
|
|
169
|
+
signal.removeEventListener("abort", abortListener);
|
|
170
|
+
}
|
|
171
|
+
this.#pending--;
|
|
172
|
+
this.#processQueue();
|
|
173
|
+
if (this.#pending === 0 && this.#size === 0) {
|
|
174
|
+
this.#resolveIdleWaiters();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
#resolveIdleWaiters() {
|
|
179
|
+
while (this.#idleResolvers.length > 0) {
|
|
180
|
+
const resolve = this.#idleResolvers.shift();
|
|
181
|
+
if (resolve) {
|
|
182
|
+
resolve();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async onIdle() {
|
|
187
|
+
if (this.#pending === 0 && this.#size === 0) {
|
|
188
|
+
return Promise.resolve();
|
|
189
|
+
}
|
|
190
|
+
return new Promise((resolve) => {
|
|
191
|
+
this.#idleResolvers.push(resolve);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
clear({ resetCounters = true } = {}) {
|
|
195
|
+
this.#queue = [];
|
|
196
|
+
this.#size = 0;
|
|
197
|
+
if (resetCounters) {
|
|
198
|
+
this.#completed = 0;
|
|
199
|
+
this.#failed = 0;
|
|
200
|
+
}
|
|
201
|
+
if (this.#pending === 0) {
|
|
202
|
+
this.#resolveIdleWaiters();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
get completed() {
|
|
206
|
+
return this.#completed;
|
|
207
|
+
}
|
|
208
|
+
get failed() {
|
|
209
|
+
return this.#failed;
|
|
210
|
+
}
|
|
211
|
+
get pending() {
|
|
212
|
+
return this.#pending;
|
|
213
|
+
}
|
|
214
|
+
get size() {
|
|
215
|
+
return this.#size;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var AsyncQueueWithId = class extends AsyncQueue {
|
|
219
|
+
constructor(options) {
|
|
220
|
+
super(options);
|
|
221
|
+
}
|
|
222
|
+
add(fn, options) {
|
|
223
|
+
return super.add(fn, options);
|
|
224
|
+
}
|
|
225
|
+
resultifyAdd(fn, options) {
|
|
226
|
+
return super.resultifyAdd(fn, options);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
function createAsyncQueue(options) {
|
|
230
|
+
return new AsyncQueue(options);
|
|
231
|
+
}
|
|
232
|
+
function createAsyncQueueWithId(options) {
|
|
233
|
+
return new AsyncQueueWithId(options);
|
|
234
|
+
}
|
|
235
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
236
|
+
0 && (module.exports = {
|
|
237
|
+
createAsyncQueue,
|
|
238
|
+
createAsyncQueueWithId
|
|
239
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as evtmitter from 'evtmitter';
|
|
2
|
+
import { ResultValidErrors, Result } from 't-result';
|
|
3
|
+
|
|
4
|
+
type AsyncQueueOptions = {
|
|
5
|
+
concurrency?: number;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
};
|
|
9
|
+
type AddOptions<I> = {
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
id?: I;
|
|
13
|
+
};
|
|
14
|
+
type RunCtx<I> = {
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
id?: I;
|
|
17
|
+
};
|
|
18
|
+
declare class AsyncQueue<T, E extends ResultValidErrors = Error, I = undefined> {
|
|
19
|
+
#private;
|
|
20
|
+
events: evtmitter.Emitter<{
|
|
21
|
+
error: {
|
|
22
|
+
id: I;
|
|
23
|
+
error: E | Error;
|
|
24
|
+
};
|
|
25
|
+
complete: {
|
|
26
|
+
id: I;
|
|
27
|
+
value: T;
|
|
28
|
+
};
|
|
29
|
+
start: {
|
|
30
|
+
id: I;
|
|
31
|
+
};
|
|
32
|
+
}>;
|
|
33
|
+
constructor({ concurrency, signal, timeout: taskTimeout, }?: AsyncQueueOptions);
|
|
34
|
+
add(fn: ((ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>) | Promise<Result<T, E>>, options?: AddOptions<I>): Promise<Result<T, E | Error>>;
|
|
35
|
+
resultifyAdd(fn: ((ctx: RunCtx<I>) => Promise<T> | T) | Promise<T>, options?: AddOptions<I>): Promise<Result<T, E | Error>>;
|
|
36
|
+
onIdle(): Promise<void>;
|
|
37
|
+
clear({ resetCounters }?: {
|
|
38
|
+
resetCounters?: boolean;
|
|
39
|
+
}): void;
|
|
40
|
+
get completed(): number;
|
|
41
|
+
get failed(): number;
|
|
42
|
+
get pending(): number;
|
|
43
|
+
get size(): number;
|
|
44
|
+
}
|
|
45
|
+
type AddOptionsWithId<I> = Omit<AddOptions<I>, 'id'> & {
|
|
46
|
+
id: I;
|
|
47
|
+
};
|
|
48
|
+
declare class AsyncQueueWithId<T, I, E extends ResultValidErrors = Error> extends AsyncQueue<T, E, I> {
|
|
49
|
+
constructor(options?: AsyncQueueOptions);
|
|
50
|
+
add(fn: ((ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>) | Promise<Result<T, E>>, options?: AddOptionsWithId<I>): Promise<Result<T, E | Error>>;
|
|
51
|
+
resultifyAdd(fn: ((ctx: RunCtx<I>) => Promise<T> | T) | Promise<T>, options?: AddOptionsWithId<I>): Promise<Result<T, E | Error>>;
|
|
52
|
+
}
|
|
53
|
+
declare function createAsyncQueue<T, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueue<T, E, undefined>;
|
|
54
|
+
declare function createAsyncQueueWithId<T, I, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueueWithId<T, I, E>;
|
|
55
|
+
|
|
56
|
+
export { createAsyncQueue, createAsyncQueueWithId };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as evtmitter from 'evtmitter';
|
|
2
|
+
import { ResultValidErrors, Result } from 't-result';
|
|
3
|
+
|
|
4
|
+
type AsyncQueueOptions = {
|
|
5
|
+
concurrency?: number;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
};
|
|
9
|
+
type AddOptions<I> = {
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
id?: I;
|
|
13
|
+
};
|
|
14
|
+
type RunCtx<I> = {
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
id?: I;
|
|
17
|
+
};
|
|
18
|
+
declare class AsyncQueue<T, E extends ResultValidErrors = Error, I = undefined> {
|
|
19
|
+
#private;
|
|
20
|
+
events: evtmitter.Emitter<{
|
|
21
|
+
error: {
|
|
22
|
+
id: I;
|
|
23
|
+
error: E | Error;
|
|
24
|
+
};
|
|
25
|
+
complete: {
|
|
26
|
+
id: I;
|
|
27
|
+
value: T;
|
|
28
|
+
};
|
|
29
|
+
start: {
|
|
30
|
+
id: I;
|
|
31
|
+
};
|
|
32
|
+
}>;
|
|
33
|
+
constructor({ concurrency, signal, timeout: taskTimeout, }?: AsyncQueueOptions);
|
|
34
|
+
add(fn: ((ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>) | Promise<Result<T, E>>, options?: AddOptions<I>): Promise<Result<T, E | Error>>;
|
|
35
|
+
resultifyAdd(fn: ((ctx: RunCtx<I>) => Promise<T> | T) | Promise<T>, options?: AddOptions<I>): Promise<Result<T, E | Error>>;
|
|
36
|
+
onIdle(): Promise<void>;
|
|
37
|
+
clear({ resetCounters }?: {
|
|
38
|
+
resetCounters?: boolean;
|
|
39
|
+
}): void;
|
|
40
|
+
get completed(): number;
|
|
41
|
+
get failed(): number;
|
|
42
|
+
get pending(): number;
|
|
43
|
+
get size(): number;
|
|
44
|
+
}
|
|
45
|
+
type AddOptionsWithId<I> = Omit<AddOptions<I>, 'id'> & {
|
|
46
|
+
id: I;
|
|
47
|
+
};
|
|
48
|
+
declare class AsyncQueueWithId<T, I, E extends ResultValidErrors = Error> extends AsyncQueue<T, E, I> {
|
|
49
|
+
constructor(options?: AsyncQueueOptions);
|
|
50
|
+
add(fn: ((ctx: RunCtx<I>) => Promise<Result<T, E>> | Result<T, E>) | Promise<Result<T, E>>, options?: AddOptionsWithId<I>): Promise<Result<T, E | Error>>;
|
|
51
|
+
resultifyAdd(fn: ((ctx: RunCtx<I>) => Promise<T> | T) | Promise<T>, options?: AddOptionsWithId<I>): Promise<Result<T, E | Error>>;
|
|
52
|
+
}
|
|
53
|
+
declare function createAsyncQueue<T, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueue<T, E, undefined>;
|
|
54
|
+
declare function createAsyncQueueWithId<T, I, E extends ResultValidErrors = Error>(options?: AsyncQueueOptions): AsyncQueueWithId<T, I, E>;
|
|
55
|
+
|
|
56
|
+
export { createAsyncQueue, createAsyncQueueWithId };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defer
|
|
3
|
+
} from "./chunk-DFXNVEH6.js";
|
|
4
|
+
import {
|
|
5
|
+
isPromise
|
|
6
|
+
} from "./chunk-3XCS7FVO.js";
|
|
7
|
+
|
|
8
|
+
// src/asyncQueue.ts
|
|
9
|
+
import { evtmitter } from "evtmitter";
|
|
10
|
+
import {
|
|
11
|
+
isResult,
|
|
12
|
+
Result,
|
|
13
|
+
resultify,
|
|
14
|
+
unknownToError
|
|
15
|
+
} from "t-result";
|
|
16
|
+
var AsyncQueue = class {
|
|
17
|
+
#queue = [];
|
|
18
|
+
#pending = 0;
|
|
19
|
+
#size = 0;
|
|
20
|
+
#concurrency;
|
|
21
|
+
#completed = 0;
|
|
22
|
+
#failed = 0;
|
|
23
|
+
#idleResolvers = [];
|
|
24
|
+
events = evtmitter();
|
|
25
|
+
#signal;
|
|
26
|
+
#taskTimeout;
|
|
27
|
+
constructor({
|
|
28
|
+
concurrency = 1,
|
|
29
|
+
signal,
|
|
30
|
+
timeout: taskTimeout
|
|
31
|
+
} = {}) {
|
|
32
|
+
this.#concurrency = concurrency;
|
|
33
|
+
this.#signal = signal;
|
|
34
|
+
this.#taskTimeout = taskTimeout;
|
|
35
|
+
}
|
|
36
|
+
#enqueue(task) {
|
|
37
|
+
this.#queue.push(task);
|
|
38
|
+
this.#size++;
|
|
39
|
+
}
|
|
40
|
+
add(fn, options) {
|
|
41
|
+
const deferred = defer();
|
|
42
|
+
const taskTimeout = this.#taskTimeout ?? options?.timeout;
|
|
43
|
+
const task = {
|
|
44
|
+
run: async (ctx) => {
|
|
45
|
+
if (isPromise(fn)) {
|
|
46
|
+
return fn;
|
|
47
|
+
}
|
|
48
|
+
return await fn(ctx);
|
|
49
|
+
},
|
|
50
|
+
resolve: deferred.resolve,
|
|
51
|
+
reject: deferred.reject,
|
|
52
|
+
signal: options?.signal,
|
|
53
|
+
id: options?.id,
|
|
54
|
+
timeout: taskTimeout
|
|
55
|
+
};
|
|
56
|
+
this.#enqueue(task);
|
|
57
|
+
this.#processQueue();
|
|
58
|
+
return deferred.promise;
|
|
59
|
+
}
|
|
60
|
+
resultifyAdd(fn, options) {
|
|
61
|
+
const cb = async (ctx) => {
|
|
62
|
+
if (isPromise(fn)) {
|
|
63
|
+
return await fn;
|
|
64
|
+
}
|
|
65
|
+
return fn(ctx);
|
|
66
|
+
};
|
|
67
|
+
return this.add((ctx) => resultify(cb(ctx)), options);
|
|
68
|
+
}
|
|
69
|
+
async #processQueue() {
|
|
70
|
+
if (this.#pending >= this.#concurrency || this.#queue.length === 0) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const task = this.#queue.shift();
|
|
74
|
+
if (!task) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
this.#pending++;
|
|
78
|
+
this.#size--;
|
|
79
|
+
const signals = [];
|
|
80
|
+
if (task.signal) {
|
|
81
|
+
signals.push(task.signal);
|
|
82
|
+
}
|
|
83
|
+
if (this.#signal) {
|
|
84
|
+
signals.push(this.#signal);
|
|
85
|
+
}
|
|
86
|
+
if (task.timeout) {
|
|
87
|
+
signals.push(AbortSignal.timeout(task.timeout));
|
|
88
|
+
}
|
|
89
|
+
const signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
|
|
90
|
+
let abortListener;
|
|
91
|
+
const signalAbortPromise = new Promise((_, reject) => {
|
|
92
|
+
if (signal) {
|
|
93
|
+
const error = signal.reason instanceof Error ? signal.reason : new DOMException("Aborted", "AbortError");
|
|
94
|
+
if (signal.aborted) {
|
|
95
|
+
reject(error);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
abortListener = () => {
|
|
99
|
+
reject(error);
|
|
100
|
+
};
|
|
101
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
try {
|
|
105
|
+
const taskRunPromise = task.run({ signal, id: task.id });
|
|
106
|
+
this.events.emit("start", { id: task.id });
|
|
107
|
+
const result = await Promise.race([taskRunPromise, signalAbortPromise]);
|
|
108
|
+
if (isResult(result)) {
|
|
109
|
+
task.resolve(result);
|
|
110
|
+
if (result.error) {
|
|
111
|
+
this.#failed++;
|
|
112
|
+
this.events.emit("error", { id: task.id, error: result.error });
|
|
113
|
+
} else {
|
|
114
|
+
this.#completed++;
|
|
115
|
+
this.events.emit("complete", { id: task.id, value: result.value });
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
const error = new Error("Response not a Result");
|
|
119
|
+
task.resolve(Result.err(error));
|
|
120
|
+
this.#failed++;
|
|
121
|
+
this.events.emit("error", {
|
|
122
|
+
id: task.id,
|
|
123
|
+
error: unknownToError(error)
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
task.resolve(Result.err(error));
|
|
128
|
+
this.#failed++;
|
|
129
|
+
this.events.emit("error", { id: task.id, error: unknownToError(error) });
|
|
130
|
+
} finally {
|
|
131
|
+
if (signal && abortListener) {
|
|
132
|
+
signal.removeEventListener("abort", abortListener);
|
|
133
|
+
}
|
|
134
|
+
this.#pending--;
|
|
135
|
+
this.#processQueue();
|
|
136
|
+
if (this.#pending === 0 && this.#size === 0) {
|
|
137
|
+
this.#resolveIdleWaiters();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
#resolveIdleWaiters() {
|
|
142
|
+
while (this.#idleResolvers.length > 0) {
|
|
143
|
+
const resolve = this.#idleResolvers.shift();
|
|
144
|
+
if (resolve) {
|
|
145
|
+
resolve();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async onIdle() {
|
|
150
|
+
if (this.#pending === 0 && this.#size === 0) {
|
|
151
|
+
return Promise.resolve();
|
|
152
|
+
}
|
|
153
|
+
return new Promise((resolve) => {
|
|
154
|
+
this.#idleResolvers.push(resolve);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
clear({ resetCounters = true } = {}) {
|
|
158
|
+
this.#queue = [];
|
|
159
|
+
this.#size = 0;
|
|
160
|
+
if (resetCounters) {
|
|
161
|
+
this.#completed = 0;
|
|
162
|
+
this.#failed = 0;
|
|
163
|
+
}
|
|
164
|
+
if (this.#pending === 0) {
|
|
165
|
+
this.#resolveIdleWaiters();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
get completed() {
|
|
169
|
+
return this.#completed;
|
|
170
|
+
}
|
|
171
|
+
get failed() {
|
|
172
|
+
return this.#failed;
|
|
173
|
+
}
|
|
174
|
+
get pending() {
|
|
175
|
+
return this.#pending;
|
|
176
|
+
}
|
|
177
|
+
get size() {
|
|
178
|
+
return this.#size;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
var AsyncQueueWithId = class extends AsyncQueue {
|
|
182
|
+
constructor(options) {
|
|
183
|
+
super(options);
|
|
184
|
+
}
|
|
185
|
+
add(fn, options) {
|
|
186
|
+
return super.add(fn, options);
|
|
187
|
+
}
|
|
188
|
+
resultifyAdd(fn, options) {
|
|
189
|
+
return super.resultifyAdd(fn, options);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
function createAsyncQueue(options) {
|
|
193
|
+
return new AsyncQueue(options);
|
|
194
|
+
}
|
|
195
|
+
function createAsyncQueueWithId(options) {
|
|
196
|
+
return new AsyncQueueWithId(options);
|
|
197
|
+
}
|
|
198
|
+
export {
|
|
199
|
+
createAsyncQueue,
|
|
200
|
+
createAsyncQueueWithId
|
|
201
|
+
};
|
package/lib/cache.js
CHANGED
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
durationObjToMs
|
|
3
3
|
} from "./chunk-5MNYPLZI.js";
|
|
4
4
|
import "./chunk-HTCYUMDR.js";
|
|
5
|
+
import "./chunk-II4R3VVX.js";
|
|
5
6
|
import {
|
|
6
7
|
isPromise
|
|
7
8
|
} from "./chunk-3XCS7FVO.js";
|
|
8
|
-
import "./chunk-II4R3VVX.js";
|
|
9
9
|
|
|
10
10
|
// src/cache.ts
|
|
11
11
|
function cachedGetter(getter) {
|
|
@@ -0,0 +1,288 @@
|
|
|
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
|
+
// src/concurrentCalls.ts
|
|
21
|
+
var concurrentCalls_exports = {};
|
|
22
|
+
__export(concurrentCalls_exports, {
|
|
23
|
+
concurrentCalls: () => concurrentCalls,
|
|
24
|
+
concurrentCallsWithMetadata: () => concurrentCallsWithMetadata
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(concurrentCalls_exports);
|
|
27
|
+
var import_t_result = require("t-result");
|
|
28
|
+
|
|
29
|
+
// src/assertions.ts
|
|
30
|
+
function invariant(condition, errorMsg = "Invariant violation") {
|
|
31
|
+
if (!condition) {
|
|
32
|
+
throw new Error(`Invariant violation: ${errorMsg}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function isObject(value) {
|
|
36
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
function isFunction(value) {
|
|
39
|
+
return typeof value === "function";
|
|
40
|
+
}
|
|
41
|
+
function isPromise(value) {
|
|
42
|
+
return isObject(value) && "then" in value && isFunction(value.then);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/sleep.ts
|
|
46
|
+
function sleep(ms) {
|
|
47
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/concurrentCalls.ts
|
|
51
|
+
var ConcurrentCalls = class {
|
|
52
|
+
#pendingCalls = [];
|
|
53
|
+
#alreadyRun = false;
|
|
54
|
+
add(...calls) {
|
|
55
|
+
this.#pendingCalls.push(...calls);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
resultifyAdd(...calls) {
|
|
59
|
+
const processedCalls = calls.map((call) => {
|
|
60
|
+
if (isPromise(call)) {
|
|
61
|
+
return call.then((value) => import_t_result.Result.ok(value)).catch((err) => import_t_result.Result.err((0, import_t_result.unknownToError)(err)));
|
|
62
|
+
} else {
|
|
63
|
+
const originalFn = call;
|
|
64
|
+
return async () => {
|
|
65
|
+
try {
|
|
66
|
+
const valueOrPromise = originalFn();
|
|
67
|
+
const value = isPromise(valueOrPromise) ? await valueOrPromise : valueOrPromise;
|
|
68
|
+
return import_t_result.Result.ok(value);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
return import_t_result.Result.err((0, import_t_result.unknownToError)(err));
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const correctlyTypedProcessedCalls = processedCalls;
|
|
76
|
+
return this.add(...correctlyTypedProcessedCalls);
|
|
77
|
+
}
|
|
78
|
+
async runAll({ delayStart } = {}) {
|
|
79
|
+
invariant(!this.#alreadyRun, "Already run");
|
|
80
|
+
this.#alreadyRun = true;
|
|
81
|
+
try {
|
|
82
|
+
const asyncResults = await Promise.all(
|
|
83
|
+
this.#pendingCalls.map(async (fn, i) => {
|
|
84
|
+
try {
|
|
85
|
+
const isP = isPromise(fn);
|
|
86
|
+
if (delayStart) {
|
|
87
|
+
invariant(
|
|
88
|
+
!isP,
|
|
89
|
+
"Delay start is not supported direct promise calls"
|
|
90
|
+
);
|
|
91
|
+
await sleep(delayStart(i));
|
|
92
|
+
}
|
|
93
|
+
const result = await (isP ? fn : fn());
|
|
94
|
+
if (!result.ok) throw result.error;
|
|
95
|
+
return result.value;
|
|
96
|
+
} catch (exception) {
|
|
97
|
+
throw (0, import_t_result.unknownToError)(exception);
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
return import_t_result.Result.ok(asyncResults);
|
|
102
|
+
} catch (exception) {
|
|
103
|
+
return import_t_result.Result.err((0, import_t_result.unknownToError)(exception));
|
|
104
|
+
} finally {
|
|
105
|
+
this.#pendingCalls = [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async runAllSettled({ delayStart } = {}) {
|
|
109
|
+
invariant(!this.#alreadyRun, "Already run");
|
|
110
|
+
this.#alreadyRun = true;
|
|
111
|
+
const total = this.#pendingCalls.length;
|
|
112
|
+
const settledResults = await Promise.allSettled(
|
|
113
|
+
this.#pendingCalls.map(async (callOrPromise, i) => {
|
|
114
|
+
try {
|
|
115
|
+
const isP = isPromise(callOrPromise);
|
|
116
|
+
if (delayStart) {
|
|
117
|
+
invariant(
|
|
118
|
+
!isP,
|
|
119
|
+
"Delay start is not supported for direct promise calls"
|
|
120
|
+
);
|
|
121
|
+
await sleep(delayStart(i));
|
|
122
|
+
}
|
|
123
|
+
const result = await (isP ? callOrPromise : callOrPromise());
|
|
124
|
+
if (!result.ok) {
|
|
125
|
+
throw result.error;
|
|
126
|
+
}
|
|
127
|
+
return result.value;
|
|
128
|
+
} catch (exception) {
|
|
129
|
+
throw (0, import_t_result.unknownToError)(exception);
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
const succeeded = [];
|
|
134
|
+
const failed = [];
|
|
135
|
+
for (const settledResult of settledResults) {
|
|
136
|
+
if (settledResult.status === "fulfilled") {
|
|
137
|
+
succeeded.push(settledResult.value);
|
|
138
|
+
} else {
|
|
139
|
+
failed.push(settledResult.reason);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const allFailed = failed.length === total;
|
|
143
|
+
const aggregatedError = failed.length > 0 ? new AggregateError(failed, "One or more calls failed") : null;
|
|
144
|
+
this.#pendingCalls = [];
|
|
145
|
+
return {
|
|
146
|
+
succeeded,
|
|
147
|
+
failures: failed,
|
|
148
|
+
allFailed,
|
|
149
|
+
total,
|
|
150
|
+
aggregatedError
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
function concurrentCalls() {
|
|
155
|
+
return new ConcurrentCalls();
|
|
156
|
+
}
|
|
157
|
+
var ConcurrentCallsWithMetadata = class {
|
|
158
|
+
#pendingCalls = [];
|
|
159
|
+
#alreadyRun = false;
|
|
160
|
+
add(...calls) {
|
|
161
|
+
invariant(
|
|
162
|
+
!this.#alreadyRun,
|
|
163
|
+
"Cannot add calls after execution has started."
|
|
164
|
+
);
|
|
165
|
+
this.#pendingCalls.push(...calls);
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
resultifyAdd(...items) {
|
|
169
|
+
const processedItems = items.map(({ fn, metadata }) => {
|
|
170
|
+
const cb = isPromise(fn) ? (0, import_t_result.resultify)(fn) : () => (0, import_t_result.resultify)(async () => {
|
|
171
|
+
const result = await fn();
|
|
172
|
+
return result;
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
fn: cb,
|
|
176
|
+
metadata
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
return this.add(...processedItems);
|
|
180
|
+
}
|
|
181
|
+
async runAll({ delayStart } = {}) {
|
|
182
|
+
invariant(!this.#alreadyRun, "Already run");
|
|
183
|
+
this.#alreadyRun = true;
|
|
184
|
+
const currentCalls = [...this.#pendingCalls];
|
|
185
|
+
this.#pendingCalls = [];
|
|
186
|
+
try {
|
|
187
|
+
const successfulResults = [];
|
|
188
|
+
for (let i = 0; i < currentCalls.length; i++) {
|
|
189
|
+
const call = currentCalls[i];
|
|
190
|
+
try {
|
|
191
|
+
if (delayStart) {
|
|
192
|
+
await sleep(delayStart(i));
|
|
193
|
+
}
|
|
194
|
+
const result = await (isPromise(call.fn) ? call.fn : call.fn());
|
|
195
|
+
if (!result.ok) {
|
|
196
|
+
throw { metadata: call.metadata, error: result.error };
|
|
197
|
+
}
|
|
198
|
+
successfulResults.push({
|
|
199
|
+
value: result.value,
|
|
200
|
+
metadata: call.metadata
|
|
201
|
+
});
|
|
202
|
+
} catch (exception) {
|
|
203
|
+
if (typeof exception === "object" && exception !== null && "metadata" in exception && "error" in exception && exception.metadata === call.metadata) {
|
|
204
|
+
throw exception;
|
|
205
|
+
} else {
|
|
206
|
+
throw {
|
|
207
|
+
metadata: call.metadata,
|
|
208
|
+
error: (0, import_t_result.unknownToError)(exception)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return import_t_result.Result.ok(successfulResults);
|
|
214
|
+
} catch (errorPayload) {
|
|
215
|
+
return import_t_result.Result.err(errorPayload);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async runAllSettled({ delayStart } = {}) {
|
|
219
|
+
invariant(!this.#alreadyRun, "Already run");
|
|
220
|
+
this.#alreadyRun = true;
|
|
221
|
+
const currentCalls = [...this.#pendingCalls];
|
|
222
|
+
this.#pendingCalls = [];
|
|
223
|
+
const total = currentCalls.length;
|
|
224
|
+
if (total === 0) {
|
|
225
|
+
return {
|
|
226
|
+
succeeded: [],
|
|
227
|
+
failed: [],
|
|
228
|
+
results: [],
|
|
229
|
+
allFailed: false,
|
|
230
|
+
total: 0,
|
|
231
|
+
aggregatedError: null
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const settledOutcomes = await Promise.allSettled(
|
|
235
|
+
currentCalls.map(async (call, i) => {
|
|
236
|
+
try {
|
|
237
|
+
if (delayStart) {
|
|
238
|
+
await sleep(delayStart(i));
|
|
239
|
+
}
|
|
240
|
+
const result = await (isPromise(call.fn) ? call.fn : call.fn());
|
|
241
|
+
if (!result.ok) {
|
|
242
|
+
throw result.error;
|
|
243
|
+
}
|
|
244
|
+
return result.value;
|
|
245
|
+
} catch (innerException) {
|
|
246
|
+
throw (0, import_t_result.unknownToError)(innerException);
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
);
|
|
250
|
+
const succeededProcessing = [];
|
|
251
|
+
const failedProcessing = [];
|
|
252
|
+
const resultsProcessing = [];
|
|
253
|
+
for (let i = 0; i < settledOutcomes.length; i++) {
|
|
254
|
+
const outcome = settledOutcomes[i];
|
|
255
|
+
const { metadata } = currentCalls[i];
|
|
256
|
+
if (outcome.status === "fulfilled") {
|
|
257
|
+
const value = outcome.value;
|
|
258
|
+
succeededProcessing.push({ value, metadata });
|
|
259
|
+
resultsProcessing.push({ ok: true, value, metadata, error: false });
|
|
260
|
+
} else {
|
|
261
|
+
const error = outcome.reason;
|
|
262
|
+
failedProcessing.push({ error, metadata });
|
|
263
|
+
resultsProcessing.push({ ok: false, error, metadata });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const allFailed = failedProcessing.length === total;
|
|
267
|
+
const aggregatedError = failedProcessing.length > 0 ? new AggregateError(
|
|
268
|
+
failedProcessing.map((f) => f.error),
|
|
269
|
+
"One or more calls failed"
|
|
270
|
+
) : null;
|
|
271
|
+
return {
|
|
272
|
+
succeeded: succeededProcessing,
|
|
273
|
+
failed: failedProcessing,
|
|
274
|
+
results: resultsProcessing,
|
|
275
|
+
allFailed,
|
|
276
|
+
total,
|
|
277
|
+
aggregatedError
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
function concurrentCallsWithMetadata() {
|
|
282
|
+
return new ConcurrentCallsWithMetadata();
|
|
283
|
+
}
|
|
284
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
285
|
+
0 && (module.exports = {
|
|
286
|
+
concurrentCalls,
|
|
287
|
+
concurrentCallsWithMetadata
|
|
288
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Result } from 't-result';
|
|
2
|
+
|
|
3
|
+
type ValidMetadata = string | number | boolean | Record<string, unknown>;
|
|
4
|
+
type RunProps = {
|
|
5
|
+
delayStart?: (index: number) => number;
|
|
6
|
+
};
|
|
7
|
+
type SucceededCall<R, M> = {
|
|
8
|
+
value: R;
|
|
9
|
+
metadata: M;
|
|
10
|
+
};
|
|
11
|
+
type FailedCall<M, E extends Error = Error> = {
|
|
12
|
+
metadata: M;
|
|
13
|
+
error: E;
|
|
14
|
+
};
|
|
15
|
+
type Action<R, E extends Error> = (() => Promise<Result<R, E>>) | Promise<Result<R, E>>;
|
|
16
|
+
type SettledResultWithMetadata<R, M, E extends Error = Error> = {
|
|
17
|
+
ok: true;
|
|
18
|
+
value: R;
|
|
19
|
+
metadata: M;
|
|
20
|
+
error: false;
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
error: E;
|
|
24
|
+
metadata: M;
|
|
25
|
+
};
|
|
26
|
+
declare class ConcurrentCalls<R = unknown, E extends Error = Error> {
|
|
27
|
+
#private;
|
|
28
|
+
add(...calls: Action<R, E>[]): this;
|
|
29
|
+
resultifyAdd(...calls: (Promise<R> | (() => R) | (() => Promise<R>))[]): this;
|
|
30
|
+
runAll({ delayStart }?: RunProps): Promise<Result<R[], E>>;
|
|
31
|
+
runAllSettled({ delayStart }?: RunProps): Promise<{
|
|
32
|
+
allFailed: boolean;
|
|
33
|
+
failures: E[];
|
|
34
|
+
succeeded: R[];
|
|
35
|
+
total: number;
|
|
36
|
+
aggregatedError: AggregateError | null;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Executes multiple asynchronous calls concurrently and collects the results in a easier to use format.
|
|
41
|
+
*
|
|
42
|
+
* @template R - The type of the result value.
|
|
43
|
+
* @template E - The type of the error.
|
|
44
|
+
*/
|
|
45
|
+
declare function concurrentCalls<R = unknown, E extends Error = Error>(): ConcurrentCalls<R, E>;
|
|
46
|
+
declare class ConcurrentCallsWithMetadata<M extends ValidMetadata, R = unknown, E extends Error = Error> {
|
|
47
|
+
#private;
|
|
48
|
+
add(...calls: {
|
|
49
|
+
fn: Action<R, E>;
|
|
50
|
+
metadata: M;
|
|
51
|
+
}[]): this;
|
|
52
|
+
resultifyAdd(...items: {
|
|
53
|
+
fn: (() => R) | (() => Promise<R>) | Promise<R>;
|
|
54
|
+
metadata: M;
|
|
55
|
+
}[]): this;
|
|
56
|
+
runAll({ delayStart }?: RunProps): Promise<Result<SucceededCall<R, M>[], FailedCall<M, E>>>;
|
|
57
|
+
runAllSettled({ delayStart }?: RunProps): Promise<{
|
|
58
|
+
allFailed: boolean;
|
|
59
|
+
failed: FailedCall<M, E>[];
|
|
60
|
+
succeeded: SucceededCall<R, M>[];
|
|
61
|
+
total: number;
|
|
62
|
+
results: SettledResultWithMetadata<R, M, E>[];
|
|
63
|
+
aggregatedError: AggregateError | null;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Executes multiple asynchronous calls concurrently and collects the results in a easier to use format.
|
|
68
|
+
*
|
|
69
|
+
* @template M - The type of the call metadata.
|
|
70
|
+
* @template R - The type of the result value.
|
|
71
|
+
* @template E - The type of the error from individual Result objects.
|
|
72
|
+
*/
|
|
73
|
+
declare function concurrentCallsWithMetadata<M extends ValidMetadata, R = unknown, E extends Error = Error>(): ConcurrentCallsWithMetadata<M, R, E>;
|
|
74
|
+
|
|
75
|
+
export { concurrentCalls, concurrentCallsWithMetadata };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Result } from 't-result';
|
|
2
|
+
|
|
3
|
+
type ValidMetadata = string | number | boolean | Record<string, unknown>;
|
|
4
|
+
type RunProps = {
|
|
5
|
+
delayStart?: (index: number) => number;
|
|
6
|
+
};
|
|
7
|
+
type SucceededCall<R, M> = {
|
|
8
|
+
value: R;
|
|
9
|
+
metadata: M;
|
|
10
|
+
};
|
|
11
|
+
type FailedCall<M, E extends Error = Error> = {
|
|
12
|
+
metadata: M;
|
|
13
|
+
error: E;
|
|
14
|
+
};
|
|
15
|
+
type Action<R, E extends Error> = (() => Promise<Result<R, E>>) | Promise<Result<R, E>>;
|
|
16
|
+
type SettledResultWithMetadata<R, M, E extends Error = Error> = {
|
|
17
|
+
ok: true;
|
|
18
|
+
value: R;
|
|
19
|
+
metadata: M;
|
|
20
|
+
error: false;
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
error: E;
|
|
24
|
+
metadata: M;
|
|
25
|
+
};
|
|
26
|
+
declare class ConcurrentCalls<R = unknown, E extends Error = Error> {
|
|
27
|
+
#private;
|
|
28
|
+
add(...calls: Action<R, E>[]): this;
|
|
29
|
+
resultifyAdd(...calls: (Promise<R> | (() => R) | (() => Promise<R>))[]): this;
|
|
30
|
+
runAll({ delayStart }?: RunProps): Promise<Result<R[], E>>;
|
|
31
|
+
runAllSettled({ delayStart }?: RunProps): Promise<{
|
|
32
|
+
allFailed: boolean;
|
|
33
|
+
failures: E[];
|
|
34
|
+
succeeded: R[];
|
|
35
|
+
total: number;
|
|
36
|
+
aggregatedError: AggregateError | null;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Executes multiple asynchronous calls concurrently and collects the results in a easier to use format.
|
|
41
|
+
*
|
|
42
|
+
* @template R - The type of the result value.
|
|
43
|
+
* @template E - The type of the error.
|
|
44
|
+
*/
|
|
45
|
+
declare function concurrentCalls<R = unknown, E extends Error = Error>(): ConcurrentCalls<R, E>;
|
|
46
|
+
declare class ConcurrentCallsWithMetadata<M extends ValidMetadata, R = unknown, E extends Error = Error> {
|
|
47
|
+
#private;
|
|
48
|
+
add(...calls: {
|
|
49
|
+
fn: Action<R, E>;
|
|
50
|
+
metadata: M;
|
|
51
|
+
}[]): this;
|
|
52
|
+
resultifyAdd(...items: {
|
|
53
|
+
fn: (() => R) | (() => Promise<R>) | Promise<R>;
|
|
54
|
+
metadata: M;
|
|
55
|
+
}[]): this;
|
|
56
|
+
runAll({ delayStart }?: RunProps): Promise<Result<SucceededCall<R, M>[], FailedCall<M, E>>>;
|
|
57
|
+
runAllSettled({ delayStart }?: RunProps): Promise<{
|
|
58
|
+
allFailed: boolean;
|
|
59
|
+
failed: FailedCall<M, E>[];
|
|
60
|
+
succeeded: SucceededCall<R, M>[];
|
|
61
|
+
total: number;
|
|
62
|
+
results: SettledResultWithMetadata<R, M, E>[];
|
|
63
|
+
aggregatedError: AggregateError | null;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Executes multiple asynchronous calls concurrently and collects the results in a easier to use format.
|
|
68
|
+
*
|
|
69
|
+
* @template M - The type of the call metadata.
|
|
70
|
+
* @template R - The type of the result value.
|
|
71
|
+
* @template E - The type of the error from individual Result objects.
|
|
72
|
+
*/
|
|
73
|
+
declare function concurrentCallsWithMetadata<M extends ValidMetadata, R = unknown, E extends Error = Error>(): ConcurrentCallsWithMetadata<M, R, E>;
|
|
74
|
+
|
|
75
|
+
export { concurrentCalls, concurrentCallsWithMetadata };
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sleep
|
|
3
|
+
} from "./chunk-5DZT3Z5Z.js";
|
|
4
|
+
import {
|
|
5
|
+
invariant,
|
|
6
|
+
isPromise
|
|
7
|
+
} from "./chunk-3XCS7FVO.js";
|
|
8
|
+
|
|
9
|
+
// src/concurrentCalls.ts
|
|
10
|
+
import { Result, resultify, unknownToError } from "t-result";
|
|
11
|
+
var ConcurrentCalls = class {
|
|
12
|
+
#pendingCalls = [];
|
|
13
|
+
#alreadyRun = false;
|
|
14
|
+
add(...calls) {
|
|
15
|
+
this.#pendingCalls.push(...calls);
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
resultifyAdd(...calls) {
|
|
19
|
+
const processedCalls = calls.map((call) => {
|
|
20
|
+
if (isPromise(call)) {
|
|
21
|
+
return call.then((value) => Result.ok(value)).catch((err) => Result.err(unknownToError(err)));
|
|
22
|
+
} else {
|
|
23
|
+
const originalFn = call;
|
|
24
|
+
return async () => {
|
|
25
|
+
try {
|
|
26
|
+
const valueOrPromise = originalFn();
|
|
27
|
+
const value = isPromise(valueOrPromise) ? await valueOrPromise : valueOrPromise;
|
|
28
|
+
return Result.ok(value);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return Result.err(unknownToError(err));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const correctlyTypedProcessedCalls = processedCalls;
|
|
36
|
+
return this.add(...correctlyTypedProcessedCalls);
|
|
37
|
+
}
|
|
38
|
+
async runAll({ delayStart } = {}) {
|
|
39
|
+
invariant(!this.#alreadyRun, "Already run");
|
|
40
|
+
this.#alreadyRun = true;
|
|
41
|
+
try {
|
|
42
|
+
const asyncResults = await Promise.all(
|
|
43
|
+
this.#pendingCalls.map(async (fn, i) => {
|
|
44
|
+
try {
|
|
45
|
+
const isP = isPromise(fn);
|
|
46
|
+
if (delayStart) {
|
|
47
|
+
invariant(
|
|
48
|
+
!isP,
|
|
49
|
+
"Delay start is not supported direct promise calls"
|
|
50
|
+
);
|
|
51
|
+
await sleep(delayStart(i));
|
|
52
|
+
}
|
|
53
|
+
const result = await (isP ? fn : fn());
|
|
54
|
+
if (!result.ok) throw result.error;
|
|
55
|
+
return result.value;
|
|
56
|
+
} catch (exception) {
|
|
57
|
+
throw unknownToError(exception);
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
return Result.ok(asyncResults);
|
|
62
|
+
} catch (exception) {
|
|
63
|
+
return Result.err(unknownToError(exception));
|
|
64
|
+
} finally {
|
|
65
|
+
this.#pendingCalls = [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async runAllSettled({ delayStart } = {}) {
|
|
69
|
+
invariant(!this.#alreadyRun, "Already run");
|
|
70
|
+
this.#alreadyRun = true;
|
|
71
|
+
const total = this.#pendingCalls.length;
|
|
72
|
+
const settledResults = await Promise.allSettled(
|
|
73
|
+
this.#pendingCalls.map(async (callOrPromise, i) => {
|
|
74
|
+
try {
|
|
75
|
+
const isP = isPromise(callOrPromise);
|
|
76
|
+
if (delayStart) {
|
|
77
|
+
invariant(
|
|
78
|
+
!isP,
|
|
79
|
+
"Delay start is not supported for direct promise calls"
|
|
80
|
+
);
|
|
81
|
+
await sleep(delayStart(i));
|
|
82
|
+
}
|
|
83
|
+
const result = await (isP ? callOrPromise : callOrPromise());
|
|
84
|
+
if (!result.ok) {
|
|
85
|
+
throw result.error;
|
|
86
|
+
}
|
|
87
|
+
return result.value;
|
|
88
|
+
} catch (exception) {
|
|
89
|
+
throw unknownToError(exception);
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
const succeeded = [];
|
|
94
|
+
const failed = [];
|
|
95
|
+
for (const settledResult of settledResults) {
|
|
96
|
+
if (settledResult.status === "fulfilled") {
|
|
97
|
+
succeeded.push(settledResult.value);
|
|
98
|
+
} else {
|
|
99
|
+
failed.push(settledResult.reason);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const allFailed = failed.length === total;
|
|
103
|
+
const aggregatedError = failed.length > 0 ? new AggregateError(failed, "One or more calls failed") : null;
|
|
104
|
+
this.#pendingCalls = [];
|
|
105
|
+
return {
|
|
106
|
+
succeeded,
|
|
107
|
+
failures: failed,
|
|
108
|
+
allFailed,
|
|
109
|
+
total,
|
|
110
|
+
aggregatedError
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function concurrentCalls() {
|
|
115
|
+
return new ConcurrentCalls();
|
|
116
|
+
}
|
|
117
|
+
var ConcurrentCallsWithMetadata = class {
|
|
118
|
+
#pendingCalls = [];
|
|
119
|
+
#alreadyRun = false;
|
|
120
|
+
add(...calls) {
|
|
121
|
+
invariant(
|
|
122
|
+
!this.#alreadyRun,
|
|
123
|
+
"Cannot add calls after execution has started."
|
|
124
|
+
);
|
|
125
|
+
this.#pendingCalls.push(...calls);
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
resultifyAdd(...items) {
|
|
129
|
+
const processedItems = items.map(({ fn, metadata }) => {
|
|
130
|
+
const cb = isPromise(fn) ? resultify(fn) : () => resultify(async () => {
|
|
131
|
+
const result = await fn();
|
|
132
|
+
return result;
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
fn: cb,
|
|
136
|
+
metadata
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
return this.add(...processedItems);
|
|
140
|
+
}
|
|
141
|
+
async runAll({ delayStart } = {}) {
|
|
142
|
+
invariant(!this.#alreadyRun, "Already run");
|
|
143
|
+
this.#alreadyRun = true;
|
|
144
|
+
const currentCalls = [...this.#pendingCalls];
|
|
145
|
+
this.#pendingCalls = [];
|
|
146
|
+
try {
|
|
147
|
+
const successfulResults = [];
|
|
148
|
+
for (let i = 0; i < currentCalls.length; i++) {
|
|
149
|
+
const call = currentCalls[i];
|
|
150
|
+
try {
|
|
151
|
+
if (delayStart) {
|
|
152
|
+
await sleep(delayStart(i));
|
|
153
|
+
}
|
|
154
|
+
const result = await (isPromise(call.fn) ? call.fn : call.fn());
|
|
155
|
+
if (!result.ok) {
|
|
156
|
+
throw { metadata: call.metadata, error: result.error };
|
|
157
|
+
}
|
|
158
|
+
successfulResults.push({
|
|
159
|
+
value: result.value,
|
|
160
|
+
metadata: call.metadata
|
|
161
|
+
});
|
|
162
|
+
} catch (exception) {
|
|
163
|
+
if (typeof exception === "object" && exception !== null && "metadata" in exception && "error" in exception && exception.metadata === call.metadata) {
|
|
164
|
+
throw exception;
|
|
165
|
+
} else {
|
|
166
|
+
throw {
|
|
167
|
+
metadata: call.metadata,
|
|
168
|
+
error: unknownToError(exception)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return Result.ok(successfulResults);
|
|
174
|
+
} catch (errorPayload) {
|
|
175
|
+
return Result.err(errorPayload);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async runAllSettled({ delayStart } = {}) {
|
|
179
|
+
invariant(!this.#alreadyRun, "Already run");
|
|
180
|
+
this.#alreadyRun = true;
|
|
181
|
+
const currentCalls = [...this.#pendingCalls];
|
|
182
|
+
this.#pendingCalls = [];
|
|
183
|
+
const total = currentCalls.length;
|
|
184
|
+
if (total === 0) {
|
|
185
|
+
return {
|
|
186
|
+
succeeded: [],
|
|
187
|
+
failed: [],
|
|
188
|
+
results: [],
|
|
189
|
+
allFailed: false,
|
|
190
|
+
total: 0,
|
|
191
|
+
aggregatedError: null
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const settledOutcomes = await Promise.allSettled(
|
|
195
|
+
currentCalls.map(async (call, i) => {
|
|
196
|
+
try {
|
|
197
|
+
if (delayStart) {
|
|
198
|
+
await sleep(delayStart(i));
|
|
199
|
+
}
|
|
200
|
+
const result = await (isPromise(call.fn) ? call.fn : call.fn());
|
|
201
|
+
if (!result.ok) {
|
|
202
|
+
throw result.error;
|
|
203
|
+
}
|
|
204
|
+
return result.value;
|
|
205
|
+
} catch (innerException) {
|
|
206
|
+
throw unknownToError(innerException);
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
);
|
|
210
|
+
const succeededProcessing = [];
|
|
211
|
+
const failedProcessing = [];
|
|
212
|
+
const resultsProcessing = [];
|
|
213
|
+
for (let i = 0; i < settledOutcomes.length; i++) {
|
|
214
|
+
const outcome = settledOutcomes[i];
|
|
215
|
+
const { metadata } = currentCalls[i];
|
|
216
|
+
if (outcome.status === "fulfilled") {
|
|
217
|
+
const value = outcome.value;
|
|
218
|
+
succeededProcessing.push({ value, metadata });
|
|
219
|
+
resultsProcessing.push({ ok: true, value, metadata, error: false });
|
|
220
|
+
} else {
|
|
221
|
+
const error = outcome.reason;
|
|
222
|
+
failedProcessing.push({ error, metadata });
|
|
223
|
+
resultsProcessing.push({ ok: false, error, metadata });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const allFailed = failedProcessing.length === total;
|
|
227
|
+
const aggregatedError = failedProcessing.length > 0 ? new AggregateError(
|
|
228
|
+
failedProcessing.map((f) => f.error),
|
|
229
|
+
"One or more calls failed"
|
|
230
|
+
) : null;
|
|
231
|
+
return {
|
|
232
|
+
succeeded: succeededProcessing,
|
|
233
|
+
failed: failedProcessing,
|
|
234
|
+
results: resultsProcessing,
|
|
235
|
+
allFailed,
|
|
236
|
+
total,
|
|
237
|
+
aggregatedError
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
function concurrentCallsWithMetadata() {
|
|
242
|
+
return new ConcurrentCallsWithMetadata();
|
|
243
|
+
}
|
|
244
|
+
export {
|
|
245
|
+
concurrentCalls,
|
|
246
|
+
concurrentCallsWithMetadata
|
|
247
|
+
};
|
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
durationObjToMs
|
|
6
6
|
} from "./chunk-5MNYPLZI.js";
|
|
7
7
|
import "./chunk-HTCYUMDR.js";
|
|
8
|
-
import "./chunk-3XCS7FVO.js";
|
|
9
8
|
import "./chunk-II4R3VVX.js";
|
|
9
|
+
import "./chunk-3XCS7FVO.js";
|
|
10
10
|
|
|
11
11
|
// src/createThrottleController.ts
|
|
12
12
|
function createThrottleController({
|
|
@@ -70,6 +70,8 @@ declare class ParallelAsyncResultCalls<M extends ValidMetadata | undefined = und
|
|
|
70
70
|
*
|
|
71
71
|
* @template R - The type of the result value.
|
|
72
72
|
* @template M - The type of the call metadata.
|
|
73
|
+
*
|
|
74
|
+
* @deprecated Use concurrentAsyncCalls instead.
|
|
73
75
|
*/
|
|
74
76
|
declare function parallelAsyncCalls<M extends ValidMetadata | undefined = undefined, R = unknown>(): ParallelAsyncResultCalls<M, R>;
|
|
75
77
|
|
|
@@ -70,6 +70,8 @@ declare class ParallelAsyncResultCalls<M extends ValidMetadata | undefined = und
|
|
|
70
70
|
*
|
|
71
71
|
* @template R - The type of the result value.
|
|
72
72
|
* @template M - The type of the call metadata.
|
|
73
|
+
*
|
|
74
|
+
* @deprecated Use concurrentAsyncCalls instead.
|
|
73
75
|
*/
|
|
74
76
|
declare function parallelAsyncCalls<M extends ValidMetadata | undefined = undefined, R = unknown>(): ParallelAsyncResultCalls<M, R>;
|
|
75
77
|
|
package/lib/promiseUtils.js
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
let reject;
|
|
5
|
-
const promise = new Promise((_resolve, _reject) => {
|
|
6
|
-
resolve = _resolve;
|
|
7
|
-
reject = _reject;
|
|
8
|
-
});
|
|
9
|
-
return { resolve, reject, promise };
|
|
10
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
defer
|
|
3
|
+
} from "./chunk-DFXNVEH6.js";
|
|
11
4
|
export {
|
|
12
5
|
defer
|
|
13
6
|
};
|
package/lib/yamlStringify.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
truncateString
|
|
3
3
|
} from "./chunk-4REIIZQY.js";
|
|
4
|
-
import {
|
|
5
|
-
bytesToHumanReadable
|
|
6
|
-
} from "./chunk-IATIXMCE.js";
|
|
7
4
|
import {
|
|
8
5
|
isObject,
|
|
9
6
|
isPlainObject
|
|
10
7
|
} from "./chunk-3XCS7FVO.js";
|
|
8
|
+
import {
|
|
9
|
+
bytesToHumanReadable
|
|
10
|
+
} from "./chunk-IATIXMCE.js";
|
|
11
11
|
|
|
12
12
|
// src/yamlStringify.ts
|
|
13
13
|
function yamlStringify(obj, {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ls-stack/utils",
|
|
3
3
|
"description": "Typescript utils",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.8.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
7
7
|
"lib"
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"import": "./lib/assertions.js",
|
|
27
27
|
"require": "./lib/assertions.cjs"
|
|
28
28
|
},
|
|
29
|
+
"./asyncQueue": {
|
|
30
|
+
"import": "./lib/asyncQueue.js",
|
|
31
|
+
"require": "./lib/asyncQueue.cjs"
|
|
32
|
+
},
|
|
29
33
|
"./cache": {
|
|
30
34
|
"import": "./lib/cache.js",
|
|
31
35
|
"require": "./lib/cache.cjs"
|
|
@@ -34,6 +38,10 @@
|
|
|
34
38
|
"import": "./lib/castValues.js",
|
|
35
39
|
"require": "./lib/castValues.cjs"
|
|
36
40
|
},
|
|
41
|
+
"./concurrentCalls": {
|
|
42
|
+
"import": "./lib/concurrentCalls.js",
|
|
43
|
+
"require": "./lib/concurrentCalls.cjs"
|
|
44
|
+
},
|
|
37
45
|
"./consoleFmt": {
|
|
38
46
|
"import": "./lib/consoleFmt.js",
|
|
39
47
|
"require": "./lib/consoleFmt.cjs"
|
|
@@ -177,7 +185,8 @@
|
|
|
177
185
|
"vitest": "^3.0.4"
|
|
178
186
|
},
|
|
179
187
|
"dependencies": {
|
|
180
|
-
"
|
|
188
|
+
"evtmitter": "^0.3.3",
|
|
189
|
+
"t-result": "^0.3.0"
|
|
181
190
|
},
|
|
182
191
|
"scripts": {
|
|
183
192
|
"test:ui": "vitest --ui",
|