@nlozgachev/pipelined 0.6.5 → 0.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.
Files changed (56) hide show
  1. package/esm/src/Core/Arr.js +36 -1
  2. package/esm/src/Core/Deferred.js +8 -4
  3. package/esm/src/Core/Logged.js +111 -0
  4. package/esm/src/Core/Option.js +6 -5
  5. package/esm/src/Core/Predicate.js +133 -0
  6. package/esm/src/Core/Refinement.js +115 -0
  7. package/esm/src/Core/RemoteData.js +1 -1
  8. package/esm/src/Core/Result.js +8 -6
  9. package/esm/src/Core/State.js +181 -0
  10. package/esm/src/Core/Task.js +36 -0
  11. package/esm/src/Core/Validation.js +1 -1
  12. package/esm/src/Core/index.js +4 -0
  13. package/package.json +1 -1
  14. package/script/src/Core/Arr.js +36 -1
  15. package/script/src/Core/Deferred.js +8 -4
  16. package/script/src/Core/Logged.js +114 -0
  17. package/script/src/Core/Option.js +6 -5
  18. package/script/src/Core/Predicate.js +136 -0
  19. package/script/src/Core/Refinement.js +118 -0
  20. package/script/src/Core/RemoteData.js +1 -1
  21. package/script/src/Core/Result.js +8 -6
  22. package/script/src/Core/State.js +184 -0
  23. package/script/src/Core/Task.js +36 -0
  24. package/script/src/Core/Validation.js +1 -1
  25. package/script/src/Core/index.js +4 -0
  26. package/types/src/Core/Arr.d.ts +27 -1
  27. package/types/src/Core/Arr.d.ts.map +1 -1
  28. package/types/src/Core/Deferred.d.ts.map +1 -1
  29. package/types/src/Core/InternalTypes.d.ts +3 -0
  30. package/types/src/Core/InternalTypes.d.ts.map +1 -1
  31. package/types/src/Core/Logged.d.ts +126 -0
  32. package/types/src/Core/Logged.d.ts.map +1 -0
  33. package/types/src/Core/Option.d.ts +5 -4
  34. package/types/src/Core/Option.d.ts.map +1 -1
  35. package/types/src/Core/Predicate.d.ts +161 -0
  36. package/types/src/Core/Predicate.d.ts.map +1 -0
  37. package/types/src/Core/Refinement.d.ts +138 -0
  38. package/types/src/Core/Refinement.d.ts.map +1 -0
  39. package/types/src/Core/RemoteData.d.ts +1 -1
  40. package/types/src/Core/RemoteData.d.ts.map +1 -1
  41. package/types/src/Core/Result.d.ts +8 -6
  42. package/types/src/Core/Result.d.ts.map +1 -1
  43. package/types/src/Core/State.d.ts +192 -0
  44. package/types/src/Core/State.d.ts.map +1 -0
  45. package/types/src/Core/Task.d.ts +30 -0
  46. package/types/src/Core/Task.d.ts.map +1 -1
  47. package/types/src/Core/TaskOption.d.ts +1 -1
  48. package/types/src/Core/TaskOption.d.ts.map +1 -1
  49. package/types/src/Core/TaskResult.d.ts +1 -1
  50. package/types/src/Core/TaskResult.d.ts.map +1 -1
  51. package/types/src/Core/TaskValidation.d.ts +1 -1
  52. package/types/src/Core/TaskValidation.d.ts.map +1 -1
  53. package/types/src/Core/Validation.d.ts +1 -1
  54. package/types/src/Core/Validation.d.ts.map +1 -1
  55. package/types/src/Core/index.d.ts +4 -0
  56. package/types/src/Core/index.d.ts.map +1 -1
@@ -0,0 +1,181 @@
1
+ export var State;
2
+ (function (State) {
3
+ /**
4
+ * Lifts a pure value into a State computation. The state passes through unchanged.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * State.run(10)(State.resolve(42)); // [42, 10] — value 42, state unchanged
9
+ * ```
10
+ */
11
+ State.resolve = (value) => (s) => [value, s];
12
+ /**
13
+ * Produces the current state as the value, without modifying it.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const readStack: State<string[], string[]> = State.get();
18
+ * State.run(["a", "b"])(readStack); // [["a", "b"], ["a", "b"]]
19
+ * ```
20
+ */
21
+ State.get = () => (s) => [s, s];
22
+ /**
23
+ * Reads a projection of the state without modifying it.
24
+ * Equivalent to `pipe(State.get(), State.map(f))` but more direct.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * type AppState = { count: number; label: string };
29
+ * const readCount: State<AppState, number> = State.gets(s => s.count);
30
+ * State.run({ count: 5, label: "x" })(readCount); // [5, { count: 5, label: "x" }]
31
+ * ```
32
+ */
33
+ State.gets = (f) => (s) => [f(s), s];
34
+ /**
35
+ * Replaces the current state with a new value. Produces no meaningful value.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const reset: State<number, undefined> = State.put(0);
40
+ * State.run(99)(reset); // [undefined, 0]
41
+ * ```
42
+ */
43
+ State.put = (newState) => (_s) => [undefined, newState];
44
+ /**
45
+ * Applies a function to the current state to produce the next state.
46
+ * Produces no meaningful value.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const push = (item: string): State<string[], undefined> =>
51
+ * State.modify(stack => [...stack, item]);
52
+ *
53
+ * State.run(["a"])(push("b")); // [undefined, ["a", "b"]]
54
+ * ```
55
+ */
56
+ State.modify = (f) => (s) => [undefined, f(s)];
57
+ /**
58
+ * Transforms the value produced by a State computation.
59
+ * The state transformation is unchanged.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const readLength: State<string[], number> = pipe(
64
+ * State.get<string[]>(),
65
+ * State.map(stack => stack.length),
66
+ * );
67
+ *
68
+ * State.run(["a", "b", "c"])(readLength); // [3, ["a", "b", "c"]]
69
+ * ```
70
+ */
71
+ State.map = (f) => (st) => (s) => {
72
+ const [a, s1] = st(s);
73
+ return [f(a), s1];
74
+ };
75
+ /**
76
+ * Sequences two State computations. The state output of the first is passed
77
+ * as the state input to the second.
78
+ *
79
+ * Data-last — the first computation is the data being piped.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const push = (item: string): State<string[], undefined> =>
84
+ * State.modify(stack => [...stack, item]);
85
+ *
86
+ * const program = pipe(
87
+ * push("a"),
88
+ * State.chain(() => push("b")),
89
+ * State.chain(() => State.get<string[]>()),
90
+ * );
91
+ *
92
+ * State.evaluate([])(program); // ["a", "b"]
93
+ * ```
94
+ */
95
+ State.chain = (f) => (st) => (s) => {
96
+ const [a, s1] = st(s);
97
+ return f(a)(s1);
98
+ };
99
+ /**
100
+ * Applies a function wrapped in a State to a value wrapped in a State.
101
+ * The function computation runs first; its output state is the input to the
102
+ * argument computation.
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * const addCounted = (n: number) => (m: number) => n + m;
107
+ * const program = pipe(
108
+ * State.resolve<number, typeof addCounted>(addCounted),
109
+ * State.ap(State.gets((s: number) => s * 2)),
110
+ * State.ap(State.gets((s: number) => s)),
111
+ * );
112
+ *
113
+ * State.evaluate(3)(program); // 6 + 3 = 9
114
+ * ```
115
+ */
116
+ State.ap = (arg) => (fn) => (s) => {
117
+ const [f, s1] = fn(s);
118
+ const [a, s2] = arg(s1);
119
+ return [f(a), s2];
120
+ };
121
+ /**
122
+ * Runs a side effect on the produced value without changing the State computation.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * pipe(
127
+ * State.get<number>(),
128
+ * State.tap(n => console.log("current:", n)),
129
+ * State.chain(() => State.modify(n => n + 1)),
130
+ * );
131
+ * ```
132
+ */
133
+ State.tap = (f) => (st) => (s) => {
134
+ const [a, s1] = st(s);
135
+ f(a);
136
+ return [a, s1];
137
+ };
138
+ /**
139
+ * Runs a State computation with an initial state, returning both the
140
+ * produced value and the final state as a pair.
141
+ *
142
+ * Data-last — the computation is the data being piped.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * const program = pipe(
147
+ * State.modify<number>(n => n + 1),
148
+ * State.chain(() => State.get<number>()),
149
+ * );
150
+ *
151
+ * State.run(0)(program); // [1, 1]
152
+ * ```
153
+ */
154
+ State.run = (initialState) => (st) => st(initialState);
155
+ /**
156
+ * Runs a State computation with an initial state, returning only the
157
+ * produced value (discarding the final state).
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * State.evaluate([])(pipe(
162
+ * State.modify<string[]>(s => [...s, "x"]),
163
+ * State.chain(() => State.get<string[]>()),
164
+ * )); // ["x"]
165
+ * ```
166
+ */
167
+ State.evaluate = (initialState) => (st) => st(initialState)[0];
168
+ /**
169
+ * Runs a State computation with an initial state, returning only the
170
+ * final state (discarding the produced value).
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * State.execute(0)(pipe(
175
+ * State.modify<number>(n => n + 10),
176
+ * State.chain(() => State.modify<number>(n => n * 2)),
177
+ * )); // 20
178
+ * ```
179
+ */
180
+ State.execute = (initialState) => (st) => st(initialState)[1];
181
+ })(State || (State = {}));
@@ -159,6 +159,42 @@ export var Task;
159
159
  });
160
160
  return run();
161
161
  });
162
+ /**
163
+ * Resolves with the value of the first Task to complete. All Tasks start
164
+ * immediately; the rest are abandoned once one resolves.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const fast = Task.from(() => new Promise<string>(r => setTimeout(() => r("fast"), 10)));
169
+ * const slow = Task.from(() => new Promise<string>(r => setTimeout(() => r("slow"), 200)));
170
+ *
171
+ * await Task.race([fast, slow])(); // "fast"
172
+ * ```
173
+ */
174
+ Task.race = (tasks) => Task.from(() => Promise.race(tasks.map(toPromise)));
175
+ /**
176
+ * Runs an array of Tasks one at a time in order, collecting all results.
177
+ * Each Task starts only after the previous one resolves.
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * let log: number[] = [];
182
+ * const makeTask = (n: number) => Task.from(() => {
183
+ * log.push(n);
184
+ * return Promise.resolve(n);
185
+ * });
186
+ *
187
+ * await Task.sequential([makeTask(1), makeTask(2), makeTask(3)])();
188
+ * // log = [1, 2, 3] — tasks ran in order
189
+ * ```
190
+ */
191
+ Task.sequential = (tasks) => Task.from(async () => {
192
+ const results = [];
193
+ for (const task of tasks) {
194
+ results.push(await toPromise(task));
195
+ }
196
+ return results;
197
+ });
162
198
  /**
163
199
  * Converts a `Task<A>` into a `Task<Result<E, A>>`, resolving to `Err` if the
164
200
  * Task does not complete within the given time.
@@ -141,7 +141,7 @@ export var Validation;
141
141
  * pipe(Validation.invalid("oops"), Validation.getOrElse(null)); // null — typed as number | null
142
142
  * ```
143
143
  */
144
- Validation.getOrElse = (defaultValue) => (data) => Validation.isValid(data) ? data.value : defaultValue;
144
+ Validation.getOrElse = (defaultValue) => (data) => Validation.isValid(data) ? data.value : defaultValue();
145
145
  /**
146
146
  * Executes a side effect on the success value without changing the Validation.
147
147
  *
@@ -1,11 +1,15 @@
1
1
  export * from "./Arr.js";
2
+ export * from "./Logged.js";
2
3
  export * from "./Deferred.js";
3
4
  export * from "./Lens.js";
4
5
  export * from "./Option.js";
5
6
  export * from "./Reader.js";
6
7
  export * from "./Optional.js";
7
8
  export * from "./Rec.js";
9
+ export * from "./Predicate.js";
10
+ export * from "./Refinement.js";
8
11
  export * from "./RemoteData.js";
12
+ export * from "./State.js";
9
13
  export * from "./Result.js";
10
14
  export * from "./Task.js";
11
15
  export * from "./TaskOption.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nlozgachev/pipelined",
3
- "version": "0.6.5",
3
+ "version": "0.8.0",
4
4
  "description": "Simple functional programming toolkit for TypeScript",
5
5
  "keywords": [
6
6
  "functional",
@@ -230,7 +230,7 @@ var Arr;
230
230
  * pipe([1, 2], Arr.zipWith((a, b) => a + b, ["a", "b"])); // ["1a", "2b"]
231
231
  * ```
232
232
  */
233
- Arr.zipWith = (f, other) => (data) => {
233
+ Arr.zipWith = (f) => (other) => (data) => {
234
234
  const len = Math.min(data.length, other.length);
235
235
  const result = [];
236
236
  for (let i = 0; i < len; i++) {
@@ -379,6 +379,41 @@ var Arr;
379
379
  * Collects an array of Tasks into a Task of array. Runs in parallel.
380
380
  */
381
381
  Arr.sequenceTask = (data) => Arr.traverseTask((a) => a)(data);
382
+ /**
383
+ * Maps each element to a TaskResult and runs them sequentially.
384
+ * Returns the first Err encountered, or Ok of all results if all succeed.
385
+ *
386
+ * @example
387
+ * ```ts
388
+ * const validate = (n: number): TaskResult<string, number> =>
389
+ * n > 0 ? TaskResult.ok(n) : TaskResult.err("non-positive");
390
+ *
391
+ * pipe(
392
+ * [1, 2, 3],
393
+ * Arr.traverseTaskResult(validate)
394
+ * )(); // Deferred<Ok([1, 2, 3])>
395
+ *
396
+ * pipe(
397
+ * [1, -1, 3],
398
+ * Arr.traverseTaskResult(validate)
399
+ * )(); // Deferred<Err("non-positive")>
400
+ * ```
401
+ */
402
+ Arr.traverseTaskResult = (f) => (data) => Task_js_1.Task.from(async () => {
403
+ const result = [];
404
+ for (const a of data) {
405
+ const r = await Deferred_js_1.Deferred.toPromise(f(a)());
406
+ if (Result_js_1.Result.isErr(r))
407
+ return r;
408
+ result.push(r.value);
409
+ }
410
+ return Result_js_1.Result.ok(result);
411
+ });
412
+ /**
413
+ * Collects an array of TaskResults into a TaskResult of array.
414
+ * Returns the first Err if any element is Err, runs sequentially.
415
+ */
416
+ Arr.sequenceTaskResult = (data) => Arr.traverseTaskResult((a) => a)(data);
382
417
  /**
383
418
  * Returns true if the array is non-empty (type guard).
384
419
  */
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Deferred = void 0;
4
+ const _store = new WeakMap();
4
5
  var Deferred;
5
6
  (function (Deferred) {
6
7
  /**
@@ -13,9 +14,11 @@ var Deferred;
13
14
  * const value = await d; // "hello"
14
15
  * ```
15
16
  */
16
- Deferred.fromPromise = (p) => ({
17
- then: ((f) => p.then(f)),
18
- });
17
+ Deferred.fromPromise = (p) => {
18
+ const d = ({ then: ((f) => p.then(f)) });
19
+ _store.set(d, p);
20
+ return d;
21
+ };
19
22
  /**
20
23
  * Converts a `Deferred` back into a `Promise`.
21
24
  *
@@ -25,5 +28,6 @@ var Deferred;
25
28
  * // p is Promise<42>
26
29
  * ```
27
30
  */
28
- Deferred.toPromise = (d) => new Promise((resolve) => d.then(resolve));
31
+ Deferred.toPromise = (d) => _store.get(d) ??
32
+ new Promise((resolve) => d.then(resolve));
29
33
  })(Deferred || (exports.Deferred = Deferred = {}));
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Logged = void 0;
4
+ var Logged;
5
+ (function (Logged) {
6
+ /**
7
+ * Wraps a pure value into a `Logged` with an empty log.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * Logged.make<string, number>(42); // { value: 42, log: [] }
12
+ * ```
13
+ */
14
+ Logged.make = (value) => ({ value, log: [] });
15
+ /**
16
+ * Creates a `Logged` that records a single log entry and produces no
17
+ * meaningful value. Use this to append to the log inside a `chain`.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * Logged.tell("operation completed"); // { value: undefined, log: ["operation completed"] }
22
+ * ```
23
+ */
24
+ Logged.tell = (entry) => ({ value: undefined, log: [entry] });
25
+ /**
26
+ * Transforms the value inside a `Logged` without affecting the log.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * pipe(
31
+ * Logged.make<string, number>(5),
32
+ * Logged.map(n => n * 2),
33
+ * ); // { value: 10, log: [] }
34
+ * ```
35
+ */
36
+ Logged.map = (f) => (data) => ({
37
+ value: f(data.value),
38
+ log: data.log,
39
+ });
40
+ /**
41
+ * Sequences two `Logged` computations, concatenating their logs.
42
+ * The value from the first is passed to `f`; the resulting log entries are
43
+ * appended after the entries from the first.
44
+ *
45
+ * Data-last — the first computation is the data being piped.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const result = pipe(
50
+ * Logged.make<string, number>(1),
51
+ * Logged.chain(n => pipe(Logged.tell("step"), Logged.map(() => n + 1))),
52
+ * Logged.chain(n => pipe(Logged.tell("done"), Logged.map(() => n * 10))),
53
+ * );
54
+ *
55
+ * Logged.run(result); // [20, ["step", "done"]]
56
+ * ```
57
+ */
58
+ Logged.chain = (f) => (data) => {
59
+ const next = f(data.value);
60
+ return { value: next.value, log: [...data.log, ...next.log] };
61
+ };
62
+ /**
63
+ * Applies a function wrapped in a `Logged` to a value wrapped in a `Logged`,
64
+ * concatenating both logs.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * const fn: Logged<string, (n: number) => number> = {
69
+ * value: n => n * 2,
70
+ * log: ["fn-loaded"],
71
+ * };
72
+ * const arg: Logged<string, number> = { value: 5, log: ["arg-loaded"] };
73
+ *
74
+ * const result = pipe(fn, Logged.ap(arg));
75
+ * Logged.run(result); // [10, ["fn-loaded", "arg-loaded"]]
76
+ * ```
77
+ */
78
+ Logged.ap = (arg) => (data) => ({
79
+ value: data.value(arg.value),
80
+ log: [...data.log, ...arg.log],
81
+ });
82
+ /**
83
+ * Runs a side effect on the value without changing the `Logged`.
84
+ * Useful for debugging or inspecting intermediate values.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * pipe(
89
+ * Logged.make<string, number>(42),
90
+ * Logged.tap(n => console.log("value:", n)),
91
+ * );
92
+ * ```
93
+ */
94
+ Logged.tap = (f) => (data) => {
95
+ f(data.value);
96
+ return data;
97
+ };
98
+ /**
99
+ * Extracts the value and log as a `readonly [A, ReadonlyArray<W>]` tuple.
100
+ * Use this at the boundary where you need to consume both.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const result = pipe(
105
+ * Logged.make<string, number>(1),
106
+ * Logged.chain(n => pipe(Logged.tell("incremented"), Logged.map(() => n + 1))),
107
+ * );
108
+ *
109
+ * const [value, log] = Logged.run(result);
110
+ * // value = 2, log = ["incremented"]
111
+ * ```
112
+ */
113
+ Logged.run = (data) => [data.value, data.log];
114
+ })(Logged || (exports.Logged = Logged = {}));
@@ -131,16 +131,17 @@ var Option;
131
131
  Option.match = (cases) => (data) => Option.isSome(data) ? cases.some(data.value) : cases.none();
132
132
  /**
133
133
  * Returns the value inside an Option, or a default value if None.
134
+ * The default is a thunk `() => B` — evaluated only when the Option is None.
134
135
  * The default can be a different type, widening the result to `A | B`.
135
136
  *
136
137
  * @example
137
138
  * ```ts
138
- * pipe(Option.some(5), Option.getOrElse(0)); // 5
139
- * pipe(Option.none(), Option.getOrElse(0)); // 0
140
- * pipe(Option.none<string>(), Option.getOrElse(null)); // null — typed as string | null
139
+ * pipe(Option.some(5), Option.getOrElse(() => 0)); // 5
140
+ * pipe(Option.none(), Option.getOrElse(() => 0)); // 0
141
+ * pipe(Option.none<string>(), Option.getOrElse(() => null)); // null — typed as string | null
141
142
  * ```
142
143
  */
143
- Option.getOrElse = (defaultValue) => (data) => Option.isSome(data) ? data.value : defaultValue;
144
+ Option.getOrElse = (defaultValue) => (data) => Option.isSome(data) ? data.value : defaultValue();
144
145
  /**
145
146
  * Executes a side effect on the value without changing the Option.
146
147
  * Useful for logging or debugging.
@@ -169,7 +170,7 @@ var Option;
169
170
  * pipe(Option.some(2), Option.filter(n => n > 3)); // None
170
171
  * ```
171
172
  */
172
- Option.filter = (predicate) => (data) => Option.isSome(data) && predicate(data.value) ? data : Option.none();
173
+ Option.filter = (predicate) => (data) => Option.isSome(data) ? (predicate(data.value) ? data : Option.none()) : data;
173
174
  /**
174
175
  * Recovers from a None by providing a fallback Option.
175
176
  * The fallback can produce a different type, widening the result to `Option<A | B>`.
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Predicate = void 0;
4
+ var Predicate;
5
+ (function (Predicate) {
6
+ /**
7
+ * Negates a predicate: the result passes exactly when the original fails.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const isBlank: Predicate<string> = s => s.trim().length === 0;
12
+ * const isNotBlank = Predicate.not(isBlank);
13
+ *
14
+ * isNotBlank("hello"); // true
15
+ * isNotBlank(" "); // false
16
+ * ```
17
+ */
18
+ Predicate.not = (p) => (a) => !p(a);
19
+ /**
20
+ * Combines two predicates with logical AND: passes only when both hold.
21
+ *
22
+ * Data-last — the first predicate is the data being piped.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const isPositive: Predicate<number> = n => n > 0;
27
+ * const isEven: Predicate<number> = n => n % 2 === 0;
28
+ *
29
+ * const isPositiveEven: Predicate<number> = pipe(isPositive, Predicate.and(isEven));
30
+ *
31
+ * isPositiveEven(4); // true
32
+ * isPositiveEven(3); // false — positive but odd
33
+ * isPositiveEven(-2); // false — even but not positive
34
+ * ```
35
+ */
36
+ Predicate.and = (second) => (first) => (a) => first(a) && second(a);
37
+ /**
38
+ * Combines two predicates with logical OR: passes when either holds.
39
+ *
40
+ * Data-last — the first predicate is the data being piped.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const isChild: Predicate<number> = n => n < 13;
45
+ * const isSenior: Predicate<number> = n => n >= 65;
46
+ *
47
+ * const getsDiscount: Predicate<number> = pipe(isChild, Predicate.or(isSenior));
48
+ *
49
+ * getsDiscount(8); // true
50
+ * getsDiscount(70); // true
51
+ * getsDiscount(30); // false
52
+ * ```
53
+ */
54
+ Predicate.or = (second) => (first) => (a) => first(a) || second(a);
55
+ /**
56
+ * Adapts a `Predicate<A>` to work on a different input type `B` by applying `f`
57
+ * to extract the relevant `A` from a `B` before running the check.
58
+ *
59
+ * Data-last — the predicate is the data being piped; `f` is the extractor.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * type User = { name: string; age: number };
64
+ *
65
+ * const isAdult: Predicate<number> = n => n >= 18;
66
+ *
67
+ * // Lift isAdult to work on Users by extracting the age field
68
+ * const isAdultUser: Predicate<User> = pipe(
69
+ * isAdult,
70
+ * Predicate.using((u: User) => u.age)
71
+ * );
72
+ *
73
+ * isAdultUser({ name: "Alice", age: 30 }); // true
74
+ * isAdultUser({ name: "Bob", age: 15 }); // false
75
+ * ```
76
+ */
77
+ Predicate.using = (f) => (p) => (b) => p(f(b));
78
+ /**
79
+ * Combines an array of predicates with AND: passes only when every predicate holds.
80
+ * Returns `true` for an empty array (vacuous truth).
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * const checks: Predicate<string>[] = [
85
+ * s => s.length > 0,
86
+ * s => s.length <= 100,
87
+ * s => !s.includes("<"),
88
+ * ];
89
+ *
90
+ * Predicate.all(checks)("hello"); // true
91
+ * Predicate.all(checks)(""); // false — too short
92
+ * Predicate.all(checks)("<b>"); // false — contains "<"
93
+ * Predicate.all([])("anything"); // true
94
+ * ```
95
+ */
96
+ Predicate.all = (predicates) => (a) => predicates.every((p) => p(a));
97
+ /**
98
+ * Combines an array of predicates with OR: passes when at least one holds.
99
+ * Returns `false` for an empty array.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const acceptedFormats: Predicate<string>[] = [
104
+ * s => s.endsWith(".jpg"),
105
+ * s => s.endsWith(".png"),
106
+ * s => s.endsWith(".webp"),
107
+ * ];
108
+ *
109
+ * Predicate.any(acceptedFormats)("photo.jpg"); // true
110
+ * Predicate.any(acceptedFormats)("photo.gif"); // false
111
+ * Predicate.any([])("anything"); // false
112
+ * ```
113
+ */
114
+ Predicate.any = (predicates) => (a) => predicates.some((p) => p(a));
115
+ /**
116
+ * Converts a `Refinement<A, B>` into a `Predicate<A>`, discarding the compile-time
117
+ * narrowing. Use this when you want to combine a type guard with plain predicates
118
+ * using `and`, `or`, or `all`.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * const isString: Refinement<unknown, string> =
123
+ * Refinement.make(x => typeof x === "string");
124
+ *
125
+ * const isShortString: Predicate<unknown> = pipe(
126
+ * Predicate.fromRefinement(isString),
127
+ * Predicate.and(x => (x as string).length < 10)
128
+ * );
129
+ *
130
+ * isShortString("hi"); // true
131
+ * isShortString("a very long string that exceeds ten characters"); // false
132
+ * isShortString(42); // false
133
+ * ```
134
+ */
135
+ Predicate.fromRefinement = (r) => r;
136
+ })(Predicate || (exports.Predicate = Predicate = {}));