@nlozgachev/pipelined 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/utils.js CHANGED
@@ -68,6 +68,15 @@ var Result;
68
68
  return data;
69
69
  };
70
70
  Result2.fromPredicate = (pred, onFalse) => (a) => pred(a) ? (0, Result2.ok)(a) : (0, Result2.error)(onFalse(a));
71
+ Result2.fromNullable = (onNull) => (value) => value === null || value === void 0 ? (0, Result2.error)(onNull()) : (0, Result2.ok)(value);
72
+ Result2.fromMaybe = (onNone) => (maybe) => Maybe.isNone(maybe) ? (0, Result2.error)(onNone()) : (0, Result2.ok)(maybe.value);
73
+ Result2.fromThrowable = (f, onError) => (...args) => {
74
+ try {
75
+ return (0, Result2.ok)(f(...args));
76
+ } catch (e) {
77
+ return (0, Result2.error)(onError(e));
78
+ }
79
+ };
71
80
  Result2.recover = (fallback) => (data) => (0, Result2.isOk)(data) ? data : fallback(data.error);
72
81
  Result2.recoverUnless = (isBlocked, fallback) => (data) => (0, Result2.isError)(data) && !isBlocked(data.error) ? fallback() : data;
73
82
  Result2.toMaybe = (data) => (0, Result2.isOk)(data) ? Maybe.some(data.value) : Maybe.none();
@@ -102,8 +111,34 @@ var Maybe;
102
111
  Maybe2.ap = (arg) => (data) => (0, Maybe2.isSome)(data) && (0, Maybe2.isSome)(arg) ? (0, Maybe2.some)(data.value(arg.value)) : (0, Maybe2.none)();
103
112
  })(Maybe || (Maybe = {}));
104
113
 
114
+ // src/Types/Brand.ts
115
+ var Brand;
116
+ ((Brand2) => {
117
+ Brand2.wrap = () => (value) => value;
118
+ Brand2.unwrap = (branded) => branded;
119
+ })(Brand || (Brand = {}));
120
+
121
+ // src/Types/Duration.ts
122
+ var Duration;
123
+ ((Duration2) => {
124
+ const wrap = Brand.wrap();
125
+ Duration2.milliseconds = (ms) => wrap(ms);
126
+ Duration2.seconds = (s) => wrap(s * 1e3);
127
+ Duration2.minutes = (m) => wrap(m * 60 * 1e3);
128
+ Duration2.hours = (h) => wrap(h * 60 * 60 * 1e3);
129
+ Duration2.days = (d) => wrap(d * 24 * 60 * 60 * 1e3);
130
+ Duration2.toMilliseconds = (d) => Brand.unwrap(d);
131
+ Duration2.toSeconds = (d) => Brand.unwrap(d) / 1e3;
132
+ Duration2.toMinutes = (d) => Brand.unwrap(d) / (60 * 1e3);
133
+ Duration2.toHours = (d) => Brand.unwrap(d) / (60 * 60 * 1e3);
134
+ Duration2.toDays = (d) => Brand.unwrap(d) / (24 * 60 * 60 * 1e3);
135
+ Duration2.add = (other) => (self) => wrap(Brand.unwrap(self) + Brand.unwrap(other));
136
+ Duration2.subtract = (other) => (self) => wrap(Brand.unwrap(self) - Brand.unwrap(other));
137
+ })(Duration || (Duration = {}));
138
+
105
139
  // src/Core/Task.ts
106
140
  var toPromise = (task, signal) => Deferred.toPromise(task(signal));
141
+ var getMs = (ms) => typeof ms === "number" ? ms : Duration.toMilliseconds(ms);
107
142
  var Task;
108
143
  ((Task2) => {
109
144
  Task2.resolve = (value) => () => Deferred.fromPromise(Promise.resolve(value));
@@ -127,60 +162,169 @@ var Task;
127
162
  (signal) => Promise.all(tasks.map((t) => toPromise(t, signal)))
128
163
  );
129
164
  Task2.delay = (ms) => (data) => (0, Task2.from)(
130
- (signal) => new Promise(
131
- (resolve2) => setTimeout(
132
- () => toPromise(data, signal).then(resolve2),
133
- ms
134
- )
135
- )
165
+ (signal) => new Promise((resolve2) => {
166
+ let timerId = void 0;
167
+ const onAbort = () => {
168
+ if (timerId !== void 0) {
169
+ clearTimeout(timerId);
170
+ }
171
+ resolve2(toPromise(data, signal));
172
+ };
173
+ if (signal) {
174
+ if (signal.aborted) {
175
+ return resolve2(toPromise(data, signal));
176
+ }
177
+ signal.addEventListener("abort", onAbort, { once: true });
178
+ }
179
+ timerId = setTimeout(() => {
180
+ signal?.removeEventListener("abort", onAbort);
181
+ resolve2(toPromise(data, signal));
182
+ }, getMs(ms));
183
+ })
136
184
  );
137
185
  Task2.repeat = (options) => (task) => (0, Task2.from)((signal) => {
138
186
  const { times, delay: ms } = options;
139
187
  if (times <= 0) return Promise.resolve([]);
140
188
  const results = [];
141
- const wait = () => ms !== void 0 && ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
142
- const run2 = (left) => toPromise(task, signal).then((a) => {
143
- results.push(a);
144
- if (left <= 1) return results;
145
- return wait().then(() => run2(left - 1));
146
- });
189
+ const wait = () => {
190
+ if (signal?.aborted) return Promise.resolve();
191
+ return new Promise((r) => {
192
+ let timerId = void 0;
193
+ const onAbort = () => {
194
+ if (timerId !== void 0) {
195
+ clearTimeout(timerId);
196
+ }
197
+ r();
198
+ };
199
+ if (signal) {
200
+ signal.addEventListener("abort", onAbort, { once: true });
201
+ }
202
+ timerId = setTimeout(() => {
203
+ signal?.removeEventListener("abort", onAbort);
204
+ r();
205
+ }, getMs(ms || 0));
206
+ });
207
+ };
208
+ const run2 = (left) => {
209
+ if (signal?.aborted) {
210
+ return Promise.resolve(results);
211
+ }
212
+ return toPromise(task, signal).then((a) => {
213
+ results.push(a);
214
+ if (left <= 1 || signal?.aborted) return results;
215
+ return wait().then(() => run2(left - 1));
216
+ });
217
+ };
147
218
  return run2(times);
148
219
  });
149
220
  Task2.repeatUntil = (options) => (task) => (0, Task2.from)((signal) => {
150
221
  const { when: predicate, delay: ms, maxAttempts } = options;
151
- const wait = () => ms !== void 0 && ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
152
- const run2 = (attempt) => toPromise(task, signal).then((a) => {
153
- if (predicate(a)) return a;
154
- if (maxAttempts !== void 0 && attempt >= maxAttempts) return a;
155
- return wait().then(() => run2(attempt + 1));
156
- });
222
+ const wait = () => {
223
+ if (signal?.aborted) return Promise.resolve();
224
+ return new Promise((r) => {
225
+ let timerId = void 0;
226
+ const onAbort = () => {
227
+ if (timerId !== void 0) {
228
+ clearTimeout(timerId);
229
+ }
230
+ r();
231
+ };
232
+ if (signal) {
233
+ signal.addEventListener("abort", onAbort, { once: true });
234
+ }
235
+ timerId = setTimeout(() => {
236
+ signal?.removeEventListener("abort", onAbort);
237
+ r();
238
+ }, getMs(ms || 0));
239
+ });
240
+ };
241
+ const run2 = (attempt, lastValue) => {
242
+ if (signal?.aborted && lastValue !== void 0) {
243
+ return Promise.resolve(lastValue);
244
+ }
245
+ return toPromise(task, signal).then((a) => {
246
+ if (predicate(a)) return a;
247
+ if (maxAttempts !== void 0 && attempt >= maxAttempts) return a;
248
+ if (signal?.aborted) return a;
249
+ return wait().then(() => run2(attempt + 1, a));
250
+ });
251
+ };
157
252
  return run2(1);
158
253
  });
159
- Task2.race = (tasks) => (0, Task2.from)((signal) => Promise.race(tasks.map((t) => toPromise(t, signal))));
254
+ Task2.race = (tasks) => {
255
+ if (tasks.length === 0) {
256
+ return () => Deferred.fromPromise(new Promise(() => {
257
+ }));
258
+ }
259
+ return (0, Task2.from)((outerSignal) => {
260
+ const controllers = tasks.map(() => new AbortController());
261
+ const onOuterAbort = () => {
262
+ for (const ctrl of controllers) ctrl.abort();
263
+ };
264
+ if (outerSignal) {
265
+ if (outerSignal.aborted) {
266
+ onOuterAbort();
267
+ } else {
268
+ outerSignal.addEventListener("abort", onOuterAbort, { once: true });
269
+ }
270
+ }
271
+ const promises = tasks.map((task, idx) => {
272
+ const ctrl = controllers[idx];
273
+ return toPromise(task, ctrl.signal).then((result) => {
274
+ for (let i = 0; i < controllers.length; i++) {
275
+ if (i !== idx) {
276
+ controllers[i].abort();
277
+ }
278
+ }
279
+ outerSignal?.removeEventListener("abort", onOuterAbort);
280
+ return result;
281
+ });
282
+ });
283
+ return Promise.race(promises);
284
+ });
285
+ };
286
+ Task2.sequence = (tasks) => (0, Task2.from)((signal) => Promise.all(tasks.map((t) => toPromise(t, signal))));
160
287
  Task2.sequential = (tasks) => (0, Task2.from)(async (signal) => {
161
288
  const results = [];
162
289
  for (const task of tasks) {
290
+ if (signal?.aborted) {
291
+ break;
292
+ }
163
293
  results.push(await toPromise(task, signal));
164
294
  }
165
295
  return results;
166
296
  });
167
297
  Task2.timeout = (ms, onTimeout) => (task) => (0, Task2.from)((outerSignal) => {
168
298
  const controller = new AbortController();
169
- const onOuterAbort = () => controller.abort();
170
- outerSignal?.addEventListener("abort", onOuterAbort, { once: true });
171
299
  let timerId;
300
+ const cleanUp = () => {
301
+ if (timerId !== void 0) {
302
+ clearTimeout(timerId);
303
+ }
304
+ outerSignal?.removeEventListener("abort", onOuterAbort);
305
+ };
306
+ const onOuterAbort = () => {
307
+ cleanUp();
308
+ controller.abort();
309
+ };
310
+ if (outerSignal) {
311
+ if (outerSignal.aborted) {
312
+ controller.abort();
313
+ } else {
314
+ outerSignal.addEventListener("abort", onOuterAbort, { once: true });
315
+ }
316
+ }
172
317
  return Promise.race([
173
318
  toPromise(task, controller.signal).then((a) => {
174
- clearTimeout(timerId);
175
- outerSignal?.removeEventListener("abort", onOuterAbort);
319
+ cleanUp();
176
320
  return Result.ok(a);
177
321
  }),
178
322
  new Promise((resolve2) => {
179
323
  timerId = setTimeout(() => {
180
324
  controller.abort();
181
- outerSignal?.removeEventListener("abort", onOuterAbort);
325
+ cleanUp();
182
326
  resolve2(Result.error(onTimeout()));
183
- }, ms);
327
+ }, getMs(ms));
184
328
  })
185
329
  ]);
186
330
  });
@@ -265,6 +409,40 @@ var Arr;
265
409
  }
266
410
  return [pass, fail];
267
411
  };
412
+ Arr2.compact = (data) => {
413
+ const result = [];
414
+ for (const item of data) {
415
+ if (item.kind === "Some") {
416
+ result.push(item.value);
417
+ }
418
+ }
419
+ return result;
420
+ };
421
+ Arr2.separate = (data) => {
422
+ const errors = [];
423
+ const successes = [];
424
+ for (const item of data) {
425
+ if (item.kind === "Ok") {
426
+ successes.push(item.value);
427
+ } else {
428
+ errors.push(item.error);
429
+ }
430
+ }
431
+ return [errors, successes];
432
+ };
433
+ Arr2.partitionMap = (f) => (data) => {
434
+ const errors = [];
435
+ const successes = [];
436
+ for (const item of data) {
437
+ const mapped = f(item);
438
+ if (mapped.kind === "Ok") {
439
+ successes.push(mapped.value);
440
+ } else {
441
+ errors.push(mapped.error);
442
+ }
443
+ }
444
+ return [errors, successes];
445
+ };
268
446
  Arr2.groupBy = (f) => (data) => {
269
447
  const result = {};
270
448
  for (const a of data) {
@@ -289,11 +467,25 @@ var Arr;
289
467
  }
290
468
  return result;
291
469
  };
470
+ Arr2.uniqWith = (eq) => (data) => {
471
+ const result = [];
472
+ for (const a of data) {
473
+ if (!result.some((x) => eq(x, a))) {
474
+ result.push(a);
475
+ }
476
+ }
477
+ return result;
478
+ };
292
479
  Arr2.sortBy = (compare) => (data) => {
293
480
  const arr = data;
294
481
  if (typeof arr.toSorted === "function") return arr.toSorted(compare);
295
482
  return [...data].sort(compare);
296
483
  };
484
+ Arr2.sortWith = (ord) => (data) => {
485
+ const arr = data;
486
+ if (typeof arr.toSorted === "function") return arr.toSorted(ord);
487
+ return [...data].sort(ord);
488
+ };
297
489
  Arr2.zip = (other) => (data) => {
298
490
  const len = Math.min(data.length, other.length);
299
491
  const result = new Array(len);
@@ -326,7 +518,23 @@ var Arr;
326
518
  }
327
519
  return result;
328
520
  };
329
- Arr2.flatten = (data) => [].concat(...data);
521
+ Arr2.flatten = (data) => {
522
+ let totalLen = 0;
523
+ const outerLen = data.length;
524
+ for (let i = 0; i < outerLen; i++) {
525
+ totalLen += data[i].length;
526
+ }
527
+ const result = new Array(totalLen);
528
+ let idx = 0;
529
+ for (let i = 0; i < outerLen; i++) {
530
+ const chunk = data[i];
531
+ const innerLen = chunk.length;
532
+ for (let j = 0; j < innerLen; j++) {
533
+ result[idx++] = chunk[j];
534
+ }
535
+ }
536
+ return result;
537
+ };
330
538
  Arr2.flatMap = (f) => (data) => {
331
539
  const n = data.length;
332
540
  const result = [];
@@ -565,8 +773,8 @@ var Num;
565
773
  }
566
774
  return result;
567
775
  };
568
- Num2.clamp = (min, max) => (n) => Math.min(Math.max(n, min), max);
569
- Num2.between = (min, max) => (n) => n >= min && n <= max;
776
+ Num2.clamp = (min2, max2) => (n) => Math.min(Math.max(n, min2), max2);
777
+ Num2.between = (min2, max2) => (n) => n >= min2 && n <= max2;
570
778
  Num2.parse = (s) => {
571
779
  if (s.trim() === "") return Maybe.none();
572
780
  const n = Number(s);
@@ -582,6 +790,28 @@ var Num;
582
790
  Num2.floor = (n) => Math.floor(n);
583
791
  Num2.ceil = (n) => Math.ceil(n);
584
792
  Num2.remainder = (divisor) => (n) => divisor === 0 ? Maybe.none() : Maybe.some(n % divisor);
793
+ Num2.sum = (ns) => {
794
+ let result = 0;
795
+ for (let i = 0; i < ns.length; i++) result += ns[i];
796
+ return result;
797
+ };
798
+ Num2.mean = (ns) => ns.length === 0 ? Maybe.none() : Maybe.some((0, Num2.sum)(ns) / ns.length);
799
+ Num2.min = (ns) => {
800
+ if (ns.length === 0) return Maybe.none();
801
+ let [result] = ns;
802
+ for (let i = 1; i < ns.length; i++) {
803
+ if (ns[i] < result) result = ns[i];
804
+ }
805
+ return Maybe.some(result);
806
+ };
807
+ Num2.max = (ns) => {
808
+ if (ns.length === 0) return Maybe.none();
809
+ let [result] = ns;
810
+ for (let i = 1; i < ns.length; i++) {
811
+ if (ns[i] > result) result = ns[i];
812
+ }
813
+ return Maybe.some(result);
814
+ };
585
815
  })(Num || (Num = {}));
586
816
 
587
817
  // src/Utils/Rec.ts
@@ -705,6 +935,7 @@ var Str;
705
935
  Str2.endsWith = (suffix) => (s) => s.endsWith(suffix);
706
936
  Str2.toUpperCase = (s) => s.toUpperCase();
707
937
  Str2.toLowerCase = (s) => s.toLowerCase();
938
+ Str2.capitalize = (s) => s.length === 0 ? "" : s.charAt(0).toUpperCase() + s.slice(1);
708
939
  Str2.lines = (s) => s.split(/\r?\n|\r/);
709
940
  Str2.words = (s) => s.trim().split(/\s+/).filter(Boolean);
710
941
  Str2.isEmpty = (s) => s.length === 0;
@@ -743,6 +974,13 @@ var Str;
743
974
  return isNaN(n) ? Maybe.none() : Maybe.some(n);
744
975
  }
745
976
  };
977
+ Str2.parseJson = (s) => {
978
+ try {
979
+ return Result.ok(JSON.parse(s));
980
+ } catch (e) {
981
+ return Result.error(e);
982
+ }
983
+ };
746
984
  })(Str || (Str = {}));
747
985
 
748
986
  // src/Utils/Uniq.ts
package/dist/utils.mjs CHANGED
@@ -5,9 +5,10 @@ import {
5
5
  Rec,
6
6
  Str,
7
7
  Uniq
8
- } from "./chunk-FWYOEWJ2.mjs";
9
- import "./chunk-SDGDJ7CU.mjs";
8
+ } from "./chunk-W53ZYTLX.mjs";
9
+ import "./chunk-GSTKY7MF.mjs";
10
10
  import "./chunk-DBIC62UV.mjs";
11
+ import "./chunk-VWVPHDZO.mjs";
11
12
  export {
12
13
  Arr,
13
14
  Dict,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nlozgachev/pipelined",
3
- "version": "0.31.0",
3
+ "version": "0.33.0",
4
4
  "description": "Opinionated functional abstractions for TypeScript",
5
5
  "license": "BSD-3-Clause",
6
6
  "homepage": "https://pipelined.lozgachev.dev",
@@ -1,30 +0,0 @@
1
- /**
2
- * A list that is guaranteed to have at least one element.
3
- * Useful for ensuring functions receive non-empty input and for
4
- * accumulating errors in Validation.
5
- *
6
- * @example
7
- * ```ts
8
- * const errors: NonEmptyList<string> = ["First error", "Second error"];
9
- *
10
- * // TypeScript ensures at least one element:
11
- * const invalid: NonEmptyList<string> = []; // Error!
12
- * ```
13
- */
14
- type NonEmptyList<A> = readonly [A, ...A[]];
15
- /**
16
- * Type guard that checks if an array is non-empty.
17
- *
18
- * @example
19
- * ```ts
20
- * const items: string[] = getItems();
21
- *
22
- * if (isNonEmptyList(items)) {
23
- * // TypeScript knows items has at least one element
24
- * const first = items[0]; // string, not string | undefined
25
- * }
26
- * ```
27
- */
28
- declare const isNonEmptyList: <A>(list: readonly A[]) => list is NonEmptyList<A>;
29
-
30
- export { type NonEmptyList as N, isNonEmptyList as i };
@@ -1,30 +0,0 @@
1
- /**
2
- * A list that is guaranteed to have at least one element.
3
- * Useful for ensuring functions receive non-empty input and for
4
- * accumulating errors in Validation.
5
- *
6
- * @example
7
- * ```ts
8
- * const errors: NonEmptyList<string> = ["First error", "Second error"];
9
- *
10
- * // TypeScript ensures at least one element:
11
- * const invalid: NonEmptyList<string> = []; // Error!
12
- * ```
13
- */
14
- type NonEmptyList<A> = readonly [A, ...A[]];
15
- /**
16
- * Type guard that checks if an array is non-empty.
17
- *
18
- * @example
19
- * ```ts
20
- * const items: string[] = getItems();
21
- *
22
- * if (isNonEmptyList(items)) {
23
- * // TypeScript knows items has at least one element
24
- * const first = items[0]; // string, not string | undefined
25
- * }
26
- * ```
27
- */
28
- declare const isNonEmptyList: <A>(list: readonly A[]) => list is NonEmptyList<A>;
29
-
30
- export { type NonEmptyList as N, isNonEmptyList as i };
@@ -1,10 +0,0 @@
1
- // src/Types/Brand.ts
2
- var Brand;
3
- ((Brand2) => {
4
- Brand2.wrap = () => (value) => value;
5
- Brand2.unwrap = (branded) => branded;
6
- })(Brand || (Brand = {}));
7
-
8
- export {
9
- Brand
10
- };