@nlozgachev/pipelined 0.10.0 → 0.12.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.
Files changed (76) hide show
  1. package/README.md +9 -3
  2. package/esm/src/Core/Option.js +2 -1
  3. package/esm/src/Core/RemoteData.js +5 -5
  4. package/esm/src/Core/TaskValidation.js +1 -3
  5. package/esm/src/Core/Tuple.js +112 -0
  6. package/esm/src/Core/index.js +4 -5
  7. package/esm/src/{Core → Utils}/Arr.js +95 -23
  8. package/esm/src/Utils/Dict.js +421 -0
  9. package/esm/src/Utils/Num.js +124 -0
  10. package/esm/src/{Core → Utils}/Rec.js +85 -11
  11. package/esm/src/Utils/Str.js +134 -0
  12. package/esm/src/Utils/Uniq.js +265 -0
  13. package/esm/src/Utils/index.js +6 -0
  14. package/package.json +11 -1
  15. package/script/src/Core/Option.js +2 -1
  16. package/script/src/Core/RemoteData.js +5 -5
  17. package/script/src/Core/TaskValidation.js +1 -3
  18. package/script/src/Core/Tuple.js +115 -0
  19. package/script/src/Core/index.js +4 -5
  20. package/script/src/{Core → Utils}/Arr.js +95 -23
  21. package/script/src/Utils/Dict.js +424 -0
  22. package/script/src/Utils/Num.js +127 -0
  23. package/script/src/{Core → Utils}/Rec.js +85 -11
  24. package/script/src/Utils/Str.js +137 -0
  25. package/script/src/Utils/Uniq.js +268 -0
  26. package/script/src/Utils/index.js +22 -0
  27. package/types/src/Composition/compose.d.ts.map +1 -1
  28. package/types/src/Composition/converge.d.ts.map +1 -1
  29. package/types/src/Composition/curry.d.ts.map +1 -1
  30. package/types/src/Composition/flow.d.ts.map +1 -1
  31. package/types/src/Composition/fn.d.ts.map +1 -1
  32. package/types/src/Composition/juxt.d.ts.map +1 -1
  33. package/types/src/Composition/memoize.d.ts.map +1 -1
  34. package/types/src/Composition/not.d.ts.map +1 -1
  35. package/types/src/Composition/on.d.ts.map +1 -1
  36. package/types/src/Composition/pipe.d.ts.map +1 -1
  37. package/types/src/Composition/uncurry.d.ts.map +1 -1
  38. package/types/src/Core/Deferred.d.ts.map +1 -1
  39. package/types/src/Core/Lens.d.ts.map +1 -1
  40. package/types/src/Core/Logged.d.ts.map +1 -1
  41. package/types/src/Core/Option.d.ts.map +1 -1
  42. package/types/src/Core/Optional.d.ts.map +1 -1
  43. package/types/src/Core/Predicate.d.ts.map +1 -1
  44. package/types/src/Core/Reader.d.ts.map +1 -1
  45. package/types/src/Core/Refinement.d.ts.map +1 -1
  46. package/types/src/Core/RemoteData.d.ts.map +1 -1
  47. package/types/src/Core/Result.d.ts.map +1 -1
  48. package/types/src/Core/State.d.ts.map +1 -1
  49. package/types/src/Core/Task.d.ts.map +1 -1
  50. package/types/src/Core/TaskOption.d.ts.map +1 -1
  51. package/types/src/Core/TaskResult.d.ts.map +1 -1
  52. package/types/src/Core/TaskValidation.d.ts.map +1 -1
  53. package/types/src/Core/These.d.ts.map +1 -1
  54. package/types/src/Core/Tuple.d.ts +129 -0
  55. package/types/src/Core/Tuple.d.ts.map +1 -0
  56. package/types/src/Core/Validation.d.ts.map +1 -1
  57. package/types/src/Core/index.d.ts +4 -5
  58. package/types/src/Core/index.d.ts.map +1 -1
  59. package/types/src/Types/Brand.d.ts.map +1 -1
  60. package/types/src/Types/NonEmptyList.d.ts.map +1 -1
  61. package/types/src/{Core → Utils}/Arr.d.ts +25 -3
  62. package/types/src/Utils/Arr.d.ts.map +1 -0
  63. package/types/src/Utils/Dict.d.ts +310 -0
  64. package/types/src/Utils/Dict.d.ts.map +1 -0
  65. package/types/src/Utils/Num.d.ts +110 -0
  66. package/types/src/Utils/Num.d.ts.map +1 -0
  67. package/types/src/{Core → Utils}/Rec.d.ts +39 -1
  68. package/types/src/Utils/Rec.d.ts.map +1 -0
  69. package/types/src/Utils/Str.d.ts +128 -0
  70. package/types/src/Utils/Str.d.ts.map +1 -0
  71. package/types/src/Utils/Uniq.d.ts +179 -0
  72. package/types/src/Utils/Uniq.d.ts.map +1 -0
  73. package/types/src/Utils/index.d.ts +7 -0
  74. package/types/src/Utils/index.d.ts.map +1 -0
  75. package/types/src/Core/Arr.d.ts.map +0 -1
  76. package/types/src/Core/Rec.d.ts.map +0 -1
package/README.md CHANGED
@@ -17,8 +17,6 @@ state flag soup, you get types that name every possible state and make invalid o
17
17
  Each type comes with a consistent set of operations — `map`, `chain`, `match`, `getOrElse` — that
18
18
  compose with `pipe` and `flow`.
19
19
 
20
- No FP jargon required. You won't find `Monad`, `Functor`, or `Applicative` in the API.
21
-
22
20
  ## What's included?
23
21
 
24
22
  ### pipelined/core
@@ -39,8 +37,16 @@ No FP jargon required. You won't find `Monad`, `Functor`, or `Applicative` in th
39
37
  - **`Optional<S, A>`** — like `Lens`, but the target may be absent (nullable fields, array indices).
40
38
  - **`Reader<R, A>`** — a computation that depends on an environment `R`, supplied once at the
41
39
  boundary.
40
+
41
+
42
+ ### pipelined/utils
43
+
44
+ Everyday utilities for built-in JS types.
45
+
42
46
  - **`Arr`** — array utilities, data-last, returning `Option` instead of `undefined`.
43
- - **`Rec`** — record utilities, data-last, with `Option`-returning key lookup.
47
+ - **`Rec`** — record/object utilities, data-last, with `Option`-returning key lookup.
48
+ - **`Num`** — number utilities: `range`, `clamp`, `between`, safe `parse`, and curried arithmetic.
49
+ - **`Str`** — string utilities: `split`, `trim`, `words`, `lines`, and safe `parse.int` / `parse.float`.
44
50
 
45
51
  ### pipelined/types
46
52
 
@@ -1,4 +1,5 @@
1
1
  import { Result } from "./Result.js";
2
+ const _none = { kind: "None" };
2
3
  export var Option;
3
4
  (function (Option) {
4
5
  /**
@@ -12,7 +13,7 @@ export var Option;
12
13
  /**
13
14
  * Creates a None (empty Option).
14
15
  */
15
- Option.none = () => ({ kind: "None" });
16
+ Option.none = () => _none;
16
17
  /**
17
18
  * Type guard that checks if a Option is None.
18
19
  */
@@ -1,15 +1,17 @@
1
1
  import { Option } from "./Option.js";
2
2
  import { Result } from "./Result.js";
3
+ const _notAsked = { kind: "NotAsked" };
4
+ const _loading = { kind: "Loading" };
3
5
  export var RemoteData;
4
6
  (function (RemoteData) {
5
7
  /**
6
8
  * Creates a NotAsked RemoteData.
7
9
  */
8
- RemoteData.notAsked = () => ({ kind: "NotAsked" });
10
+ RemoteData.notAsked = () => _notAsked;
9
11
  /**
10
12
  * Creates a Loading RemoteData.
11
13
  */
12
- RemoteData.loading = () => ({ kind: "Loading" });
14
+ RemoteData.loading = () => _loading;
13
15
  /**
14
16
  * Creates a Failure RemoteData with the given error.
15
17
  */
@@ -205,7 +207,5 @@ export var RemoteData;
205
207
  * ); // Ok(42)
206
208
  * ```
207
209
  */
208
- RemoteData.toResult = (onNotReady) => (data) => RemoteData.isSuccess(data)
209
- ? Result.ok(data.value)
210
- : Result.err(RemoteData.isFailure(data) ? data.error : onNotReady());
210
+ RemoteData.toResult = (onNotReady) => (data) => RemoteData.isSuccess(data) ? Result.ok(data.value) : Result.err(RemoteData.isFailure(data) ? data.error : onNotReady());
211
211
  })(RemoteData || (RemoteData = {}));
@@ -91,9 +91,7 @@ export var TaskValidation;
91
91
  * The fallback receives the accumulated error list so callers can inspect which errors occurred.
92
92
  * The fallback can produce a different success type, widening the result to `TaskValidation<E, A | B>`.
93
93
  */
94
- TaskValidation.recover = (fallback) => (data) => Task.chain((validation) => Validation.isValid(validation)
95
- ? Task.resolve(validation)
96
- : fallback(validation.errors))(data);
94
+ TaskValidation.recover = (fallback) => (data) => Task.chain((validation) => Validation.isValid(validation) ? Task.resolve(validation) : fallback(validation.errors))(data);
97
95
  /**
98
96
  * Runs two TaskValidations concurrently and combines their results into a tuple.
99
97
  * If both are Valid, returns Valid with both values. If either fails, accumulates
@@ -0,0 +1,112 @@
1
+ export var Tuple;
2
+ (function (Tuple) {
3
+ /**
4
+ * Creates a pair from two values.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * Tuple.make("Paris", 2_161_000); // ["Paris", 2161000]
9
+ * ```
10
+ */
11
+ Tuple.make = (first, second) => [first, second];
12
+ /**
13
+ * Returns the first value from the pair.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * Tuple.first(Tuple.make("Paris", 2_161_000)); // "Paris"
18
+ * ```
19
+ */
20
+ Tuple.first = (tuple) => tuple[0];
21
+ /**
22
+ * Returns the second value from the pair.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * Tuple.second(Tuple.make("Paris", 2_161_000)); // 2161000
27
+ * ```
28
+ */
29
+ Tuple.second = (tuple) => tuple[1];
30
+ /**
31
+ * Transforms the first value, leaving the second unchanged.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * pipe(Tuple.make("alice", 42), Tuple.mapFirst((s) => s.toUpperCase())); // ["ALICE", 42]
36
+ * ```
37
+ */
38
+ Tuple.mapFirst = (f) => (tuple) => [f(tuple[0]), tuple[1]];
39
+ /**
40
+ * Transforms the second value, leaving the first unchanged.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * pipe(Tuple.make("alice", 42), Tuple.mapSecond((n) => n * 2)); // ["alice", 84]
45
+ * ```
46
+ */
47
+ Tuple.mapSecond = (f) => (tuple) => [tuple[0], f(tuple[1])];
48
+ /**
49
+ * Transforms both values independently in a single step.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * pipe(
54
+ * Tuple.make("alice", 42),
55
+ * Tuple.mapBoth(
56
+ * (name) => name.toUpperCase(),
57
+ * (score) => score * 2,
58
+ * ),
59
+ * ); // ["ALICE", 84]
60
+ * ```
61
+ */
62
+ Tuple.mapBoth = (onFirst, onSecond) => (tuple) => [
63
+ onFirst(tuple[0]),
64
+ onSecond(tuple[1]),
65
+ ];
66
+ /**
67
+ * Applies a binary function to both values, collapsing the pair into a single value.
68
+ * Useful as the final step when consuming a pair in a pipeline.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * pipe(Tuple.make("Alice", 100), Tuple.fold((name, score) => `${name}: ${score}`));
73
+ * // "Alice: 100"
74
+ * ```
75
+ */
76
+ Tuple.fold = (f) => (tuple) => f(tuple[0], tuple[1]);
77
+ /**
78
+ * Swaps the two values: `[A, B]` becomes `[B, A]`.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * Tuple.swap(Tuple.make("key", 1)); // [1, "key"]
83
+ * ```
84
+ */
85
+ Tuple.swap = (tuple) => [tuple[1], tuple[0]];
86
+ /**
87
+ * Converts the pair to a heterogeneous readonly array `readonly (A | B)[]`.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * Tuple.toArray(Tuple.make("hello", 42)); // ["hello", 42]
92
+ * ```
93
+ */
94
+ Tuple.toArray = (tuple) => [...tuple];
95
+ /**
96
+ * Runs a side effect with both values without changing the pair.
97
+ * Useful for logging or debugging in the middle of a pipeline.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * pipe(
102
+ * Tuple.make("Paris", 2_161_000),
103
+ * Tuple.tap((city, pop) => console.log(`${city}: ${pop}`)),
104
+ * Tuple.mapSecond((n) => n / 1_000_000),
105
+ * ); // logs "Paris: 2161000", returns ["Paris", 2.161]
106
+ * ```
107
+ */
108
+ Tuple.tap = (f) => (tuple) => {
109
+ f(tuple[0], tuple[1]);
110
+ return tuple;
111
+ };
112
+ })(Tuple || (Tuple = {}));
@@ -1,19 +1,18 @@
1
- export * from "./Arr.js";
2
- export * from "./Logged.js";
3
1
  export * from "./Deferred.js";
4
2
  export * from "./Lens.js";
3
+ export * from "./Logged.js";
5
4
  export * from "./Option.js";
6
- export * from "./Reader.js";
7
5
  export * from "./Optional.js";
8
- export * from "./Rec.js";
9
6
  export * from "./Predicate.js";
7
+ export * from "./Reader.js";
10
8
  export * from "./Refinement.js";
11
9
  export * from "./RemoteData.js";
12
- export * from "./State.js";
13
10
  export * from "./Result.js";
11
+ export * from "./State.js";
14
12
  export * from "./Task.js";
15
13
  export * from "./TaskOption.js";
16
14
  export * from "./TaskResult.js";
17
15
  export * from "./TaskValidation.js";
18
16
  export * from "./These.js";
17
+ export * from "./Tuple.js";
19
18
  export * from "./Validation.js";
@@ -1,7 +1,7 @@
1
- import { Deferred } from "./Deferred.js";
2
- import { Option } from "./Option.js";
3
- import { Result } from "./Result.js";
4
- import { Task } from "./Task.js";
1
+ import { Deferred } from "../Core/Deferred.js";
2
+ import { Option } from "../Core/Option.js";
3
+ import { Result } from "../Core/Result.js";
4
+ import { Task } from "../Core/Task.js";
5
5
  import { isNonEmptyList } from "../Types/NonEmptyList.js";
6
6
  /**
7
7
  * Functional array utilities that compose well with pipe.
@@ -110,7 +110,13 @@ export var Arr;
110
110
  * pipe([1, 2, 3], Arr.map(n => n * 2)); // [2, 4, 6]
111
111
  * ```
112
112
  */
113
- Arr.map = (f) => (data) => data.map(f);
113
+ Arr.map = (f) => (data) => {
114
+ const n = data.length;
115
+ const result = new Array(n);
116
+ for (let i = 0; i < n; i++)
117
+ result[i] = f(data[i]);
118
+ return result;
119
+ };
114
120
  /**
115
121
  * Filters elements that satisfy the predicate.
116
122
  *
@@ -119,7 +125,15 @@ export var Arr;
119
125
  * pipe([1, 2, 3, 4], Arr.filter(n => n % 2 === 0)); // [2, 4]
120
126
  * ```
121
127
  */
122
- Arr.filter = (predicate) => (data) => data.filter(predicate);
128
+ Arr.filter = (predicate) => (data) => {
129
+ const n = data.length;
130
+ const result = [];
131
+ for (let i = 0; i < n; i++) {
132
+ if (predicate(data[i]))
133
+ result.push(data[i]);
134
+ }
135
+ return result;
136
+ };
123
137
  /**
124
138
  * Splits an array into two groups based on a predicate.
125
139
  * First group contains elements that satisfy the predicate,
@@ -213,9 +227,9 @@ export var Arr;
213
227
  */
214
228
  Arr.zip = (other) => (data) => {
215
229
  const len = Math.min(data.length, other.length);
216
- const result = [];
230
+ const result = new Array(len);
217
231
  for (let i = 0; i < len; i++) {
218
- result.push([data[i], other[i]]);
232
+ result[i] = [data[i], other[i]];
219
233
  }
220
234
  return result;
221
235
  };
@@ -229,9 +243,9 @@ export var Arr;
229
243
  */
230
244
  Arr.zipWith = (f) => (other) => (data) => {
231
245
  const len = Math.min(data.length, other.length);
232
- const result = [];
246
+ const result = new Array(len);
233
247
  for (let i = 0; i < len; i++) {
234
- result.push(f(data[i], other[i]));
248
+ result[i] = f(data[i], other[i]);
235
249
  }
236
250
  return result;
237
251
  };
@@ -286,7 +300,17 @@ export var Arr;
286
300
  * pipe([1, 2, 3], Arr.flatMap(n => [n, n * 10])); // [1, 10, 2, 20, 3, 30]
287
301
  * ```
288
302
  */
289
- Arr.flatMap = (f) => (data) => [].concat(...data.map(f));
303
+ Arr.flatMap = (f) => (data) => {
304
+ const n = data.length;
305
+ const result = [];
306
+ for (let i = 0; i < n; i++) {
307
+ const chunk = f(data[i]);
308
+ const m = chunk.length;
309
+ for (let j = 0; j < m; j++)
310
+ result.push(chunk[j]);
311
+ }
312
+ return result;
313
+ };
290
314
  /**
291
315
  * Reduces an array from the left.
292
316
  *
@@ -313,12 +337,13 @@ export var Arr;
313
337
  * ```
314
338
  */
315
339
  Arr.traverse = (f) => (data) => {
316
- const result = [];
317
- for (const a of data) {
318
- const mapped = f(a);
319
- if (Option.isNone(mapped))
340
+ const n = data.length;
341
+ const result = new Array(n);
342
+ for (let i = 0; i < n; i++) {
343
+ const mapped = f(data[i]);
344
+ if (mapped.kind === "None")
320
345
  return Option.none();
321
- result.push(mapped.value);
346
+ result[i] = mapped.value;
322
347
  }
323
348
  return Option.some(result);
324
349
  };
@@ -335,12 +360,13 @@ export var Arr;
335
360
  * ```
336
361
  */
337
362
  Arr.traverseResult = (f) => (data) => {
338
- const result = [];
339
- for (const a of data) {
340
- const mapped = f(a);
341
- if (Result.isErr(mapped))
363
+ const n = data.length;
364
+ const result = new Array(n);
365
+ for (let i = 0; i < n; i++) {
366
+ const mapped = f(data[i]);
367
+ if (mapped.kind === "Error")
342
368
  return mapped;
343
- result.push(mapped.value);
369
+ result[i] = mapped.value;
344
370
  }
345
371
  return Result.ok(result);
346
372
  };
@@ -427,7 +453,13 @@ export var Arr;
427
453
  * pipe([1, 2, 3], Arr.some(n => n > 2)); // true
428
454
  * ```
429
455
  */
430
- Arr.some = (predicate) => (data) => data.some(predicate);
456
+ Arr.some = (predicate) => (data) => {
457
+ const n = data.length;
458
+ for (let i = 0; i < n; i++)
459
+ if (predicate(data[i]))
460
+ return true;
461
+ return false;
462
+ };
431
463
  /**
432
464
  * Returns true if all elements satisfy the predicate.
433
465
  *
@@ -436,7 +468,13 @@ export var Arr;
436
468
  * pipe([1, 2, 3], Arr.every(n => n > 0)); // true
437
469
  * ```
438
470
  */
439
- Arr.every = (predicate) => (data) => data.every(predicate);
471
+ Arr.every = (predicate) => (data) => {
472
+ const n = data.length;
473
+ for (let i = 0; i < n; i++)
474
+ if (!predicate(data[i]))
475
+ return false;
476
+ return true;
477
+ };
440
478
  /**
441
479
  * Reverses an array. Returns a new array.
442
480
  *
@@ -495,4 +533,38 @@ export var Arr;
495
533
  i++;
496
534
  return data.slice(i);
497
535
  };
536
+ /**
537
+ * Like `reduce`, but returns every intermediate accumulator as an array.
538
+ * The initial value is not included — the output has the same length as the input.
539
+ *
540
+ * @example
541
+ * ```ts
542
+ * pipe([1, 2, 3], Arr.scan(0, (acc, n) => acc + n)); // [1, 3, 6]
543
+ * ```
544
+ */
545
+ Arr.scan = (initial, f) => (data) => {
546
+ const n = data.length;
547
+ const result = new Array(n);
548
+ let acc = initial;
549
+ for (let i = 0; i < n; i++) {
550
+ acc = f(acc, data[i]);
551
+ result[i] = acc;
552
+ }
553
+ return result;
554
+ };
555
+ /**
556
+ * Splits an array at an index into a `[before, after]` tuple.
557
+ * Negative indices clamp to 0; indices beyond the array length clamp to the end.
558
+ *
559
+ * @example
560
+ * ```ts
561
+ * pipe([1, 2, 3, 4], Arr.splitAt(2)); // [[1, 2], [3, 4]]
562
+ * pipe([1, 2, 3], Arr.splitAt(0)); // [[], [1, 2, 3]]
563
+ * pipe([1, 2, 3], Arr.splitAt(10)); // [[1, 2, 3], []]
564
+ * ```
565
+ */
566
+ Arr.splitAt = (index) => (data) => {
567
+ const i = Math.max(0, index);
568
+ return [data.slice(0, i), data.slice(i)];
569
+ };
498
570
  })(Arr || (Arr = {}));