@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.
- package/esm/src/Core/Arr.js +36 -1
- package/esm/src/Core/Deferred.js +8 -4
- package/esm/src/Core/Logged.js +111 -0
- package/esm/src/Core/Option.js +6 -5
- package/esm/src/Core/Predicate.js +133 -0
- package/esm/src/Core/Refinement.js +115 -0
- package/esm/src/Core/RemoteData.js +1 -1
- package/esm/src/Core/Result.js +8 -6
- package/esm/src/Core/State.js +181 -0
- package/esm/src/Core/Task.js +36 -0
- package/esm/src/Core/Validation.js +1 -1
- package/esm/src/Core/index.js +4 -0
- package/package.json +1 -1
- package/script/src/Core/Arr.js +36 -1
- package/script/src/Core/Deferred.js +8 -4
- package/script/src/Core/Logged.js +114 -0
- package/script/src/Core/Option.js +6 -5
- package/script/src/Core/Predicate.js +136 -0
- package/script/src/Core/Refinement.js +118 -0
- package/script/src/Core/RemoteData.js +1 -1
- package/script/src/Core/Result.js +8 -6
- package/script/src/Core/State.js +184 -0
- package/script/src/Core/Task.js +36 -0
- package/script/src/Core/Validation.js +1 -1
- package/script/src/Core/index.js +4 -0
- package/types/src/Core/Arr.d.ts +27 -1
- package/types/src/Core/Arr.d.ts.map +1 -1
- package/types/src/Core/Deferred.d.ts.map +1 -1
- package/types/src/Core/InternalTypes.d.ts +3 -0
- package/types/src/Core/InternalTypes.d.ts.map +1 -1
- package/types/src/Core/Logged.d.ts +126 -0
- package/types/src/Core/Logged.d.ts.map +1 -0
- package/types/src/Core/Option.d.ts +5 -4
- package/types/src/Core/Option.d.ts.map +1 -1
- package/types/src/Core/Predicate.d.ts +161 -0
- package/types/src/Core/Predicate.d.ts.map +1 -0
- package/types/src/Core/Refinement.d.ts +138 -0
- package/types/src/Core/Refinement.d.ts.map +1 -0
- package/types/src/Core/RemoteData.d.ts +1 -1
- package/types/src/Core/RemoteData.d.ts.map +1 -1
- package/types/src/Core/Result.d.ts +8 -6
- package/types/src/Core/Result.d.ts.map +1 -1
- package/types/src/Core/State.d.ts +192 -0
- package/types/src/Core/State.d.ts.map +1 -0
- package/types/src/Core/Task.d.ts +30 -0
- package/types/src/Core/Task.d.ts.map +1 -1
- package/types/src/Core/TaskOption.d.ts +1 -1
- package/types/src/Core/TaskOption.d.ts.map +1 -1
- package/types/src/Core/TaskResult.d.ts +1 -1
- package/types/src/Core/TaskResult.d.ts.map +1 -1
- package/types/src/Core/TaskValidation.d.ts +1 -1
- package/types/src/Core/TaskValidation.d.ts.map +1 -1
- package/types/src/Core/Validation.d.ts +1 -1
- package/types/src/Core/Validation.d.ts.map +1 -1
- package/types/src/Core/index.d.ts +4 -0
- 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 = {}));
|
package/esm/src/Core/Task.js
CHANGED
|
@@ -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
|
*
|
package/esm/src/Core/index.js
CHANGED
|
@@ -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
package/script/src/Core/Arr.js
CHANGED
|
@@ -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
|
|
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) =>
|
|
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)
|
|
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 = {}));
|