@ls-stack/utils 3.6.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.
@@ -76,6 +76,30 @@ function findBeforeIndex(array, index, predicate) {
76
76
  function rejectArrayUndefinedValues(array) {
77
77
  return array.filter((item) => item !== void 0);
78
78
  }
79
+ function hasDuplicates(array, getKey = (item) => item) {
80
+ const seen = /* @__PURE__ */ new Set();
81
+ for (const item of array) {
82
+ const key = getKey(item);
83
+ if (seen.has(key)) {
84
+ return true;
85
+ }
86
+ seen.add(key);
87
+ }
88
+ return false;
89
+ }
90
+ function rejectDuplicates(array, getKey = (item) => item) {
91
+ const seen = /* @__PURE__ */ new Set();
92
+ const result = [];
93
+ for (const item of array) {
94
+ const key = getKey(item);
95
+ if (seen.has(key)) {
96
+ continue;
97
+ }
98
+ seen.add(key);
99
+ result.push(item);
100
+ }
101
+ return result;
102
+ }
79
103
 
80
104
  export {
81
105
  filterAndMap,
@@ -85,5 +109,7 @@ export {
85
109
  isInArray,
86
110
  findAfterIndex,
87
111
  findBeforeIndex,
88
- rejectArrayUndefinedValues
112
+ rejectArrayUndefinedValues,
113
+ hasDuplicates,
114
+ rejectDuplicates
89
115
  };
@@ -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 };