@ls-stack/utils 3.7.0 → 3.9.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.
@@ -0,0 +1,311 @@
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/arrayUtils.ts
46
+ function truncateArray(array, maxLength, appendIfTruncated) {
47
+ const truncate = array.length > maxLength;
48
+ const result = truncate ? [...array.slice(0, maxLength)] : array;
49
+ if (truncate && appendIfTruncated) {
50
+ if (isFunction(appendIfTruncated)) {
51
+ return [...result, appendIfTruncated(array.length - maxLength)];
52
+ }
53
+ return [...result, appendIfTruncated];
54
+ }
55
+ return result;
56
+ }
57
+
58
+ // src/sleep.ts
59
+ function sleep(ms) {
60
+ return new Promise((resolve) => setTimeout(resolve, ms));
61
+ }
62
+
63
+ // src/stringUtils.ts
64
+ function truncateString(str, length, ellipsis = "\u2026") {
65
+ if (str.length <= length) return str;
66
+ return str.slice(0, length - 1) + ellipsis;
67
+ }
68
+
69
+ // src/concurrentCalls.ts
70
+ var ConcurrentCalls = class {
71
+ #pendingCalls = [];
72
+ #alreadyRun = false;
73
+ add(...calls) {
74
+ this.#pendingCalls.push(...calls);
75
+ return this;
76
+ }
77
+ resultifyAdd(...calls) {
78
+ const processedCalls = calls.map((call) => {
79
+ if (isPromise(call)) {
80
+ return call.then((value) => import_t_result.Result.ok(value)).catch((err) => import_t_result.Result.err((0, import_t_result.unknownToError)(err)));
81
+ } else {
82
+ const originalFn = call;
83
+ return async () => {
84
+ try {
85
+ const valueOrPromise = originalFn();
86
+ const value = isPromise(valueOrPromise) ? await valueOrPromise : valueOrPromise;
87
+ return import_t_result.Result.ok(value);
88
+ } catch (err) {
89
+ return import_t_result.Result.err((0, import_t_result.unknownToError)(err));
90
+ }
91
+ };
92
+ }
93
+ });
94
+ const correctlyTypedProcessedCalls = processedCalls;
95
+ return this.add(...correctlyTypedProcessedCalls);
96
+ }
97
+ async runAll({ delayStart } = {}) {
98
+ invariant(!this.#alreadyRun, "Already run");
99
+ this.#alreadyRun = true;
100
+ try {
101
+ const asyncResults = await Promise.all(
102
+ this.#pendingCalls.map(async (fn, i) => {
103
+ try {
104
+ const isP = isPromise(fn);
105
+ if (delayStart) {
106
+ invariant(
107
+ !isP,
108
+ "Delay start is not supported direct promise calls"
109
+ );
110
+ await sleep(delayStart(i));
111
+ }
112
+ const result = await (isP ? fn : fn());
113
+ if (!result.ok) throw result.error;
114
+ return result.value;
115
+ } catch (exception) {
116
+ throw (0, import_t_result.unknownToError)(exception);
117
+ }
118
+ })
119
+ );
120
+ return import_t_result.Result.ok(asyncResults);
121
+ } catch (exception) {
122
+ return import_t_result.Result.err((0, import_t_result.unknownToError)(exception));
123
+ } finally {
124
+ this.#pendingCalls = [];
125
+ }
126
+ }
127
+ async runAllSettled({ delayStart } = {}) {
128
+ invariant(!this.#alreadyRun, "Already run");
129
+ this.#alreadyRun = true;
130
+ const total = this.#pendingCalls.length;
131
+ const settledResults = await Promise.allSettled(
132
+ this.#pendingCalls.map(async (callOrPromise, i) => {
133
+ try {
134
+ const isP = isPromise(callOrPromise);
135
+ if (delayStart) {
136
+ invariant(
137
+ !isP,
138
+ "Delay start is not supported for direct promise calls"
139
+ );
140
+ await sleep(delayStart(i));
141
+ }
142
+ const result = await (isP ? callOrPromise : callOrPromise());
143
+ if (!result.ok) {
144
+ throw result.error;
145
+ }
146
+ return result.value;
147
+ } catch (exception) {
148
+ throw (0, import_t_result.unknownToError)(exception);
149
+ }
150
+ })
151
+ );
152
+ const succeeded = [];
153
+ const failed = [];
154
+ for (const settledResult of settledResults) {
155
+ if (settledResult.status === "fulfilled") {
156
+ succeeded.push(settledResult.value);
157
+ } else {
158
+ failed.push(settledResult.reason);
159
+ }
160
+ }
161
+ const allFailed = failed.length === total;
162
+ const aggregatedError = failed.length > 0 ? new AggregateError(failed, "One or more calls failed") : null;
163
+ this.#pendingCalls = [];
164
+ return {
165
+ succeeded,
166
+ failures: failed,
167
+ allFailed,
168
+ total,
169
+ aggregatedError
170
+ };
171
+ }
172
+ };
173
+ function concurrentCalls() {
174
+ return new ConcurrentCalls();
175
+ }
176
+ var ConcurrentCallsWithMetadata = class {
177
+ #pendingCalls = [];
178
+ #alreadyRun = false;
179
+ add(...calls) {
180
+ invariant(
181
+ !this.#alreadyRun,
182
+ "Cannot add calls after execution has started."
183
+ );
184
+ this.#pendingCalls.push(...calls);
185
+ return this;
186
+ }
187
+ resultifyAdd(...items) {
188
+ const processedItems = items.map(({ fn, metadata }) => {
189
+ const cb = isPromise(fn) ? (0, import_t_result.resultify)(fn) : () => (0, import_t_result.resultify)(async () => {
190
+ const result = await fn();
191
+ return result;
192
+ });
193
+ return {
194
+ fn: cb,
195
+ metadata
196
+ };
197
+ });
198
+ return this.add(...processedItems);
199
+ }
200
+ async runAll({ delayStart } = {}) {
201
+ invariant(!this.#alreadyRun, "Already run");
202
+ this.#alreadyRun = true;
203
+ const currentCalls = [...this.#pendingCalls];
204
+ this.#pendingCalls = [];
205
+ try {
206
+ const successfulResults = [];
207
+ for (let i = 0; i < currentCalls.length; i++) {
208
+ const call = currentCalls[i];
209
+ try {
210
+ if (delayStart) {
211
+ await sleep(delayStart(i));
212
+ }
213
+ const result = await (isPromise(call.fn) ? call.fn : call.fn());
214
+ if (!result.ok) {
215
+ throw { metadata: call.metadata, error: result.error };
216
+ }
217
+ successfulResults.push({
218
+ value: result.value,
219
+ metadata: call.metadata
220
+ });
221
+ } catch (exception) {
222
+ if (typeof exception === "object" && exception !== null && "metadata" in exception && "error" in exception && exception.metadata === call.metadata) {
223
+ throw exception;
224
+ } else {
225
+ throw {
226
+ metadata: call.metadata,
227
+ error: (0, import_t_result.unknownToError)(exception)
228
+ };
229
+ }
230
+ }
231
+ }
232
+ return import_t_result.Result.ok(successfulResults);
233
+ } catch (errorPayload) {
234
+ return import_t_result.Result.err(errorPayload);
235
+ }
236
+ }
237
+ async runAllSettled({ delayStart } = {}) {
238
+ invariant(!this.#alreadyRun, "Already run");
239
+ this.#alreadyRun = true;
240
+ const currentCalls = [...this.#pendingCalls];
241
+ this.#pendingCalls = [];
242
+ const total = currentCalls.length;
243
+ if (total === 0) {
244
+ return {
245
+ succeeded: [],
246
+ failed: [],
247
+ results: [],
248
+ allFailed: false,
249
+ total: 0,
250
+ aggregatedError: null
251
+ };
252
+ }
253
+ const settledOutcomes = await Promise.allSettled(
254
+ currentCalls.map(async (call, i) => {
255
+ try {
256
+ if (delayStart) {
257
+ await sleep(delayStart(i));
258
+ }
259
+ const result = await (isPromise(call.fn) ? call.fn : call.fn());
260
+ if (!result.ok) {
261
+ throw result.error;
262
+ }
263
+ return result.value;
264
+ } catch (innerException) {
265
+ throw (0, import_t_result.unknownToError)(innerException);
266
+ }
267
+ })
268
+ );
269
+ const succeededProcessing = [];
270
+ const failedProcessing = [];
271
+ const resultsProcessing = [];
272
+ for (let i = 0; i < settledOutcomes.length; i++) {
273
+ const outcome = settledOutcomes[i];
274
+ const { metadata } = currentCalls[i];
275
+ if (outcome.status === "fulfilled") {
276
+ const value = outcome.value;
277
+ succeededProcessing.push({ value, metadata });
278
+ resultsProcessing.push({ ok: true, value, metadata, error: false });
279
+ } else {
280
+ const error = outcome.reason;
281
+ failedProcessing.push({ error, metadata });
282
+ resultsProcessing.push({ ok: false, error, metadata });
283
+ }
284
+ }
285
+ const allFailed = failedProcessing.length === total;
286
+ const aggregatedError = failedProcessing.length > 0 ? new AggregateError(
287
+ failedProcessing.map((f) => f.error),
288
+ `${failedProcessing.length}/${total} calls failed: ${truncateArray(
289
+ failedProcessing.map((f) => truncateString(f.error.message, 20)),
290
+ 5,
291
+ (count) => `+${count} more`
292
+ ).join("; ")}`
293
+ ) : null;
294
+ return {
295
+ succeeded: succeededProcessing,
296
+ failed: failedProcessing,
297
+ results: resultsProcessing,
298
+ allFailed,
299
+ total,
300
+ aggregatedError
301
+ };
302
+ }
303
+ };
304
+ function concurrentCallsWithMetadata() {
305
+ return new ConcurrentCallsWithMetadata();
306
+ }
307
+ // Annotate the CommonJS export names for ESM import in node:
308
+ 0 && (module.exports = {
309
+ concurrentCalls,
310
+ concurrentCallsWithMetadata
311
+ });
@@ -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 };