@nlozgachev/pipekit 0.2.0 → 0.3.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/README.md +18 -13
- package/esm/src/Composition/flip.js +2 -2
- package/esm/src/Composition/fn.js +1 -1
- package/esm/src/Composition/tap.js +1 -1
- package/esm/src/Core/Arr.js +4 -4
- package/esm/src/Core/Option.js +13 -22
- package/esm/src/Core/RemoteData.js +9 -13
- package/esm/src/Core/Result.js +12 -21
- package/esm/src/Core/Task.js +26 -23
- package/esm/src/Core/TaskOption.js +8 -6
- package/esm/src/Core/TaskResult.js +10 -6
- package/esm/src/Core/TaskValidation.js +12 -8
- package/esm/src/Core/These.js +6 -6
- package/esm/src/Core/Validation.js +42 -43
- package/package.json +1 -1
- package/script/src/Composition/flip.js +2 -2
- package/script/src/Composition/fn.js +1 -1
- package/script/src/Composition/tap.js +1 -1
- package/script/src/Core/Arr.js +4 -4
- package/script/src/Core/Option.js +13 -22
- package/script/src/Core/RemoteData.js +9 -13
- package/script/src/Core/Result.js +12 -21
- package/script/src/Core/Task.js +26 -23
- package/script/src/Core/TaskOption.js +8 -6
- package/script/src/Core/TaskResult.js +10 -6
- package/script/src/Core/TaskValidation.js +12 -8
- package/script/src/Core/These.js +6 -6
- package/script/src/Core/Validation.js +42 -43
- package/types/src/Composition/flip.d.ts +2 -2
- package/types/src/Composition/fn.d.ts +1 -1
- package/types/src/Composition/pipe.d.ts +1 -1
- package/types/src/Composition/tap.d.ts +1 -1
- package/types/src/Composition/uncurry.d.ts +3 -3
- package/types/src/Core/Arr.d.ts +4 -4
- package/types/src/Core/Option.d.ts +14 -23
- package/types/src/Core/Option.d.ts.map +1 -1
- package/types/src/Core/RemoteData.d.ts +9 -13
- package/types/src/Core/RemoteData.d.ts.map +1 -1
- package/types/src/Core/Result.d.ts +12 -21
- package/types/src/Core/Result.d.ts.map +1 -1
- package/types/src/Core/Task.d.ts +35 -32
- 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 +10 -6
- package/types/src/Core/TaskValidation.d.ts.map +1 -1
- package/types/src/Core/These.d.ts +6 -6
- package/types/src/Core/These.d.ts.map +1 -1
- package/types/src/Core/Validation.d.ts +38 -42
- package/types/src/Core/Validation.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @nlozgachev/pipekit
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@nlozgachev/pipekit)[](https://jsr.io/@nlozgachev/pipekit)[](https://www.typescriptlang.org)[](https://deno.com)
|
|
3
|
+
[](https://www.npmjs.com/package/@nlozgachev/pipekit)[](https://jsr.io/@nlozgachev/pipekit)[](https://github.com/nlozgachev/pipekit/actions/workflows/publish.yml)[](https://app.codecov.io/github/nlozgachev/pipekit)[](https://www.typescriptlang.org)[](https://deno.com)
|
|
4
4
|
|
|
5
5
|
A TypeScript toolkit for writing code that means exactly what it says.
|
|
6
6
|
|
|
@@ -14,7 +14,10 @@ deno add jsr:@nlozgachev/pipekit
|
|
|
14
14
|
|
|
15
15
|
## What is this?
|
|
16
16
|
|
|
17
|
-
A toolkit for expressing uncertainty precisely. Instead of `T | null`, `try/catch`, and loading
|
|
17
|
+
A toolkit for expressing uncertainty precisely. Instead of `T | null`, `try/catch`, and loading
|
|
18
|
+
state flag soup, you get types that name every possible state and make invalid ones unrepresentable.
|
|
19
|
+
Each type comes with a consistent set of operations — `map`, `chain`, `match`, `getOrElse` — that
|
|
20
|
+
compose with `pipe` and `flow`.
|
|
18
21
|
|
|
19
22
|
No FP jargon required. You won't find `Monad`, `Functor`, or `Applicative` in the API.
|
|
20
23
|
|
|
@@ -24,13 +27,15 @@ No FP jargon required. You won't find `Monad`, `Functor`, or `Applicative` in th
|
|
|
24
27
|
|
|
25
28
|
- **`Option<A>`** — a value that might not exist. Replaces `T | null | undefined`.
|
|
26
29
|
- **`Result<E, A>`** — an operation that succeeds or fails. Replaces `try/catch`.
|
|
27
|
-
- **`Validation<E, A>`** — like `Result`, but accumulates all errors instead of stopping at the
|
|
30
|
+
- **`Validation<E, A>`** — like `Result`, but accumulates all errors instead of stopping at the
|
|
31
|
+
first.
|
|
28
32
|
- **`Task<A>`** — a lazy async operation that doesn't run until called.
|
|
29
33
|
- **`TaskResult<E, A>`** — `Task` + `Result`. A lazy async operation that can fail.
|
|
30
34
|
- **`TaskOption<A>`** — `Task` + `Option`. Replaces `Promise<T | null>`.
|
|
31
35
|
- **`TaskValidation<E, A>`** — `Task` + `Validation`. For async checks that all need to run.
|
|
32
36
|
- **`These<E, A>`** — an inclusive OR: holds an error, a value, or both at once.
|
|
33
|
-
- **`RemoteData<E, A>`** — the four states of a data fetch: `NotAsked`, `Loading`, `Failure`,
|
|
37
|
+
- **`RemoteData<E, A>`** — the four states of a data fetch: `NotAsked`, `Loading`, `Failure`,
|
|
38
|
+
`Success`.
|
|
34
39
|
- **`Arr`** — array utilities that return `Option` instead of `undefined`.
|
|
35
40
|
- **`Rec`** — record/object utilities.
|
|
36
41
|
|
|
@@ -52,19 +57,19 @@ import { pipe } from "@nlozgachev/pipekit/Composition";
|
|
|
52
57
|
|
|
53
58
|
// Chain nullable lookups without nested null checks
|
|
54
59
|
const city = pipe(
|
|
55
|
-
getUser(userId),
|
|
56
|
-
Option.fromNullable,
|
|
57
|
-
Option.chain((u) => Option.fromNullable(u.address))
|
|
58
|
-
Option.chain((a) => Option.fromNullable(a.city)),
|
|
59
|
-
Option.map((c) => c.toUpperCase()),
|
|
60
|
-
Option.getOrElse("UNKNOWN"),
|
|
60
|
+
getUser(userId), // User | null
|
|
61
|
+
Option.fromNullable, // Option<User>
|
|
62
|
+
Option.chain((u) => Option.fromNullable(u.address)), // Option<Address>
|
|
63
|
+
Option.chain((a) => Option.fromNullable(a.city)), // Option<string>
|
|
64
|
+
Option.map((c) => c.toUpperCase()), // Option<string>
|
|
65
|
+
Option.getOrElse("UNKNOWN"), // string
|
|
61
66
|
);
|
|
62
67
|
|
|
63
68
|
// Parse input and look up a record — both steps can fail
|
|
64
69
|
const record = pipe(
|
|
65
|
-
parseId(rawInput),
|
|
66
|
-
Result.chain((id) => db.find(id)),
|
|
67
|
-
Result.map((r) => r.name),
|
|
70
|
+
parseId(rawInput), // Result<ParseError, number>
|
|
71
|
+
Result.chain((id) => db.find(id)), // Result<ParseError | NotFoundError, Record>
|
|
72
|
+
Result.map((r) => r.name), // Result<ParseError | NotFoundError, string>
|
|
68
73
|
);
|
|
69
74
|
```
|
|
70
75
|
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
* ```ts
|
|
7
7
|
* // Original data-last (for pipe)
|
|
8
8
|
* pipe(
|
|
9
|
-
* Option.
|
|
9
|
+
* Option.some(5),
|
|
10
10
|
* Option.map(n => n * 2)
|
|
11
11
|
* ); // Some(10)
|
|
12
12
|
*
|
|
13
13
|
* // Flipped to data-first
|
|
14
14
|
* const mapFirst = flip(Option.map);
|
|
15
|
-
* mapFirst(Option.
|
|
15
|
+
* mapFirst(Option.some(5))(n => n * 2); // Some(10)
|
|
16
16
|
* ```
|
|
17
17
|
*
|
|
18
18
|
* @see {@link uncurry} for converting curried functions to multi-argument functions
|
package/esm/src/Core/Arr.js
CHANGED
|
@@ -303,7 +303,7 @@ export var Arr;
|
|
|
303
303
|
* ```ts
|
|
304
304
|
* const parseNum = (s: string): Option<number> => {
|
|
305
305
|
* const n = Number(s);
|
|
306
|
-
* return isNaN(n) ? Option.none() : Option.
|
|
306
|
+
* return isNaN(n) ? Option.none() : Option.some(n);
|
|
307
307
|
* };
|
|
308
308
|
*
|
|
309
309
|
* pipe(["1", "2", "3"], Arr.traverse(parseNum)); // Some([1, 2, 3])
|
|
@@ -349,7 +349,7 @@ export var Arr;
|
|
|
349
349
|
* ```ts
|
|
350
350
|
* pipe(
|
|
351
351
|
* [1, 2, 3],
|
|
352
|
-
* Arr.traverseTask(n => Task.
|
|
352
|
+
* Arr.traverseTask(n => Task.resolve(n * 2))
|
|
353
353
|
* )(); // Promise<[2, 4, 6]>
|
|
354
354
|
* ```
|
|
355
355
|
*/
|
|
@@ -360,8 +360,8 @@ export var Arr;
|
|
|
360
360
|
*
|
|
361
361
|
* @example
|
|
362
362
|
* ```ts
|
|
363
|
-
* Arr.sequence([Option.
|
|
364
|
-
* Arr.sequence([Option.
|
|
363
|
+
* Arr.sequence([Option.some(1), Option.some(2)]); // Some([1, 2])
|
|
364
|
+
* Arr.sequence([Option.some(1), Option.none()]); // None
|
|
365
365
|
* ```
|
|
366
366
|
*/
|
|
367
367
|
Arr.sequence = (data) => Arr.traverse((a) => a)(data);
|
package/esm/src/Core/Option.js
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import { Result } from "./Result.js";
|
|
2
2
|
export var Option;
|
|
3
3
|
(function (Option) {
|
|
4
|
-
/**
|
|
5
|
-
* Wraps a value in a Some.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* Option.of(42); // Some(42)
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
Option.of = (value) => Option.some(value);
|
|
13
4
|
/**
|
|
14
5
|
* Creates a Some containing the given value.
|
|
15
6
|
*/
|
|
@@ -57,7 +48,7 @@ export var Option;
|
|
|
57
48
|
* @example
|
|
58
49
|
* ```ts
|
|
59
50
|
* pipe(
|
|
60
|
-
* Option.
|
|
51
|
+
* Option.some(42),
|
|
61
52
|
* Option.toResult(() => "Value was missing")
|
|
62
53
|
* ); // Ok(42)
|
|
63
54
|
*
|
|
@@ -84,7 +75,7 @@ export var Option;
|
|
|
84
75
|
*
|
|
85
76
|
* @example
|
|
86
77
|
* ```ts
|
|
87
|
-
* pipe(Option.
|
|
78
|
+
* pipe(Option.some(5), Option.map(n => n * 2)); // Some(10)
|
|
88
79
|
* pipe(Option.none(), Option.map(n => n * 2)); // None
|
|
89
80
|
* ```
|
|
90
81
|
*/
|
|
@@ -97,11 +88,11 @@ export var Option;
|
|
|
97
88
|
* ```ts
|
|
98
89
|
* const parseNumber = (s: string): Option<number> => {
|
|
99
90
|
* const n = parseInt(s, 10);
|
|
100
|
-
* return isNaN(n) ? Option.none() : Option.
|
|
91
|
+
* return isNaN(n) ? Option.none() : Option.some(n);
|
|
101
92
|
* };
|
|
102
93
|
*
|
|
103
|
-
* pipe(Option.
|
|
104
|
-
* pipe(Option.
|
|
94
|
+
* pipe(Option.some("42"), Option.chain(parseNumber)); // Some(42)
|
|
95
|
+
* pipe(Option.some("abc"), Option.chain(parseNumber)); // None
|
|
105
96
|
* ```
|
|
106
97
|
*/
|
|
107
98
|
Option.chain = (f) => (data) => Option.isSome(data) ? f(data.value) : data;
|
|
@@ -111,7 +102,7 @@ export var Option;
|
|
|
111
102
|
* @example
|
|
112
103
|
* ```ts
|
|
113
104
|
* pipe(
|
|
114
|
-
* Option.
|
|
105
|
+
* Option.some(5),
|
|
115
106
|
* Option.fold(
|
|
116
107
|
* () => "No value",
|
|
117
108
|
* n => `Value: ${n}`
|
|
@@ -140,7 +131,7 @@ export var Option;
|
|
|
140
131
|
*
|
|
141
132
|
* @example
|
|
142
133
|
* ```ts
|
|
143
|
-
* pipe(Option.
|
|
134
|
+
* pipe(Option.some(5), Option.getOrElse(0)); // 5
|
|
144
135
|
* pipe(Option.none(), Option.getOrElse(0)); // 0
|
|
145
136
|
* ```
|
|
146
137
|
*/
|
|
@@ -152,7 +143,7 @@ export var Option;
|
|
|
152
143
|
* @example
|
|
153
144
|
* ```ts
|
|
154
145
|
* pipe(
|
|
155
|
-
* Option.
|
|
146
|
+
* Option.some(5),
|
|
156
147
|
* Option.tap(n => console.log("Value:", n)),
|
|
157
148
|
* Option.map(n => n * 2)
|
|
158
149
|
* );
|
|
@@ -169,8 +160,8 @@ export var Option;
|
|
|
169
160
|
*
|
|
170
161
|
* @example
|
|
171
162
|
* ```ts
|
|
172
|
-
* pipe(Option.
|
|
173
|
-
* pipe(Option.
|
|
163
|
+
* pipe(Option.some(5), Option.filter(n => n > 3)); // Some(5)
|
|
164
|
+
* pipe(Option.some(2), Option.filter(n => n > 3)); // None
|
|
174
165
|
* ```
|
|
175
166
|
*/
|
|
176
167
|
Option.filter = (predicate) => (data) => Option.isSome(data) && predicate(data.value) ? data : Option.none();
|
|
@@ -185,9 +176,9 @@ export var Option;
|
|
|
185
176
|
* ```ts
|
|
186
177
|
* const add = (a: number) => (b: number) => a + b;
|
|
187
178
|
* pipe(
|
|
188
|
-
* Option.
|
|
189
|
-
* Option.ap(Option.
|
|
190
|
-
* Option.ap(Option.
|
|
179
|
+
* Option.some(add),
|
|
180
|
+
* Option.ap(Option.some(5)),
|
|
181
|
+
* Option.ap(Option.some(3))
|
|
191
182
|
* ); // Some(8)
|
|
192
183
|
* ```
|
|
193
184
|
*/
|
|
@@ -22,10 +22,6 @@ export var RemoteData;
|
|
|
22
22
|
kind: "Success",
|
|
23
23
|
value,
|
|
24
24
|
});
|
|
25
|
-
/**
|
|
26
|
-
* Wraps a value in a Success RemoteData. Alias for `success`.
|
|
27
|
-
*/
|
|
28
|
-
RemoteData.of = RemoteData.success;
|
|
29
25
|
/**
|
|
30
26
|
* Type guard that checks if a RemoteData is NotAsked.
|
|
31
27
|
*/
|
|
@@ -47,7 +43,7 @@ export var RemoteData;
|
|
|
47
43
|
*
|
|
48
44
|
* @example
|
|
49
45
|
* ```ts
|
|
50
|
-
* pipe(RemoteData.
|
|
46
|
+
* pipe(RemoteData.success(5), RemoteData.map(n => n * 2)); // Success(10)
|
|
51
47
|
* pipe(RemoteData.loading(), RemoteData.map(n => n * 2)); // Loading
|
|
52
48
|
* ```
|
|
53
49
|
*/
|
|
@@ -68,8 +64,8 @@ export var RemoteData;
|
|
|
68
64
|
* @example
|
|
69
65
|
* ```ts
|
|
70
66
|
* pipe(
|
|
71
|
-
* RemoteData.
|
|
72
|
-
* RemoteData.chain(n => n > 0 ? RemoteData.
|
|
67
|
+
* RemoteData.success(5),
|
|
68
|
+
* RemoteData.chain(n => n > 0 ? RemoteData.success(n) : RemoteData.failure("negative"))
|
|
73
69
|
* );
|
|
74
70
|
* ```
|
|
75
71
|
*/
|
|
@@ -81,9 +77,9 @@ export var RemoteData;
|
|
|
81
77
|
* ```ts
|
|
82
78
|
* const add = (a: number) => (b: number) => a + b;
|
|
83
79
|
* pipe(
|
|
84
|
-
* RemoteData.
|
|
85
|
-
* RemoteData.ap(RemoteData.
|
|
86
|
-
* RemoteData.ap(RemoteData.
|
|
80
|
+
* RemoteData.success(add),
|
|
81
|
+
* RemoteData.ap(RemoteData.success(5)),
|
|
82
|
+
* RemoteData.ap(RemoteData.success(3))
|
|
87
83
|
* ); // Success(8)
|
|
88
84
|
* ```
|
|
89
85
|
*/
|
|
@@ -160,7 +156,7 @@ export var RemoteData;
|
|
|
160
156
|
*
|
|
161
157
|
* @example
|
|
162
158
|
* ```ts
|
|
163
|
-
* pipe(RemoteData.
|
|
159
|
+
* pipe(RemoteData.success(5), RemoteData.getOrElse(0)); // 5
|
|
164
160
|
* pipe(RemoteData.loading(), RemoteData.getOrElse(0)); // 0
|
|
165
161
|
* ```
|
|
166
162
|
*/
|
|
@@ -171,7 +167,7 @@ export var RemoteData;
|
|
|
171
167
|
* @example
|
|
172
168
|
* ```ts
|
|
173
169
|
* pipe(
|
|
174
|
-
* RemoteData.
|
|
170
|
+
* RemoteData.success(5),
|
|
175
171
|
* RemoteData.tap(n => console.log("Value:", n)),
|
|
176
172
|
* RemoteData.map(n => n * 2)
|
|
177
173
|
* );
|
|
@@ -199,7 +195,7 @@ export var RemoteData;
|
|
|
199
195
|
* @example
|
|
200
196
|
* ```ts
|
|
201
197
|
* pipe(
|
|
202
|
-
* RemoteData.
|
|
198
|
+
* RemoteData.success(42),
|
|
203
199
|
* RemoteData.toResult(() => "not loaded")
|
|
204
200
|
* ); // Ok(42)
|
|
205
201
|
* ```
|
package/esm/src/Core/Result.js
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
export var Result;
|
|
2
2
|
(function (Result) {
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* Result.of(42); // Ok(42)
|
|
9
|
-
* ```
|
|
4
|
+
* Creates a successful Result with the given value.
|
|
10
5
|
*/
|
|
11
|
-
Result.
|
|
6
|
+
Result.ok = (value) => ({ kind: "Ok", value });
|
|
12
7
|
/**
|
|
13
8
|
* Creates a failed Result with the given error.
|
|
14
9
|
*/
|
|
15
10
|
Result.err = (error) => ({ kind: "Error", error });
|
|
16
|
-
/**
|
|
17
|
-
* Creates a successful Result with the given value.
|
|
18
|
-
*/
|
|
19
|
-
Result.ok = (value) => ({ kind: "Ok", value });
|
|
20
11
|
/**
|
|
21
12
|
* Type guard that checks if an Result is Ok.
|
|
22
13
|
*/
|
|
@@ -51,7 +42,7 @@ export var Result;
|
|
|
51
42
|
*
|
|
52
43
|
* @example
|
|
53
44
|
* ```ts
|
|
54
|
-
* pipe(Result.
|
|
45
|
+
* pipe(Result.ok(5), Result.map(n => n * 2)); // Ok(10)
|
|
55
46
|
* pipe(Result.err("error"), Result.map(n => n * 2)); // Err("error")
|
|
56
47
|
* ```
|
|
57
48
|
*/
|
|
@@ -72,10 +63,10 @@ export var Result;
|
|
|
72
63
|
* @example
|
|
73
64
|
* ```ts
|
|
74
65
|
* const validatePositive = (n: number): Result<string, number> =>
|
|
75
|
-
* n > 0 ? Result.
|
|
66
|
+
* n > 0 ? Result.ok(n) : Result.err("Must be positive");
|
|
76
67
|
*
|
|
77
|
-
* pipe(Result.
|
|
78
|
-
* pipe(Result.
|
|
68
|
+
* pipe(Result.ok(5), Result.chain(validatePositive)); // Ok(5)
|
|
69
|
+
* pipe(Result.ok(-1), Result.chain(validatePositive)); // Err("Must be positive")
|
|
79
70
|
* ```
|
|
80
71
|
*/
|
|
81
72
|
Result.chain = (f) => (data) => Result.isOk(data) ? f(data.value) : data;
|
|
@@ -85,7 +76,7 @@ export var Result;
|
|
|
85
76
|
* @example
|
|
86
77
|
* ```ts
|
|
87
78
|
* pipe(
|
|
88
|
-
* Result.
|
|
79
|
+
* Result.ok(5),
|
|
89
80
|
* Result.fold(
|
|
90
81
|
* e => `Error: ${e}`,
|
|
91
82
|
* n => `Value: ${n}`
|
|
@@ -114,7 +105,7 @@ export var Result;
|
|
|
114
105
|
*
|
|
115
106
|
* @example
|
|
116
107
|
* ```ts
|
|
117
|
-
* pipe(Result.
|
|
108
|
+
* pipe(Result.ok(5), Result.getOrElse(0)); // 5
|
|
118
109
|
* pipe(Result.err("error"), Result.getOrElse(0)); // 0
|
|
119
110
|
* ```
|
|
120
111
|
*/
|
|
@@ -126,7 +117,7 @@ export var Result;
|
|
|
126
117
|
* @example
|
|
127
118
|
* ```ts
|
|
128
119
|
* pipe(
|
|
129
|
-
* Result.
|
|
120
|
+
* Result.ok(5),
|
|
130
121
|
* Result.tap(n => console.log("Value:", n)),
|
|
131
122
|
* Result.map(n => n * 2)
|
|
132
123
|
* );
|
|
@@ -163,9 +154,9 @@ export var Result;
|
|
|
163
154
|
* ```ts
|
|
164
155
|
* const add = (a: number) => (b: number) => a + b;
|
|
165
156
|
* pipe(
|
|
166
|
-
* Result.
|
|
167
|
-
* Result.ap(Result.
|
|
168
|
-
* Result.ap(Result.
|
|
157
|
+
* Result.ok(add),
|
|
158
|
+
* Result.ap(Result.ok(5)),
|
|
159
|
+
* Result.ap(Result.ok(3))
|
|
169
160
|
* ); // Ok(8)
|
|
170
161
|
* ```
|
|
171
162
|
*/
|
package/esm/src/Core/Task.js
CHANGED
|
@@ -2,22 +2,22 @@ import { Result } from "./Result.js";
|
|
|
2
2
|
export var Task;
|
|
3
3
|
(function (Task) {
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Creates a Task that immediately resolves to the given value.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```ts
|
|
9
|
-
* const task = Task.
|
|
9
|
+
* const task = Task.resolve(42);
|
|
10
10
|
* task().then(console.log); // 42
|
|
11
11
|
* ```
|
|
12
12
|
*/
|
|
13
|
-
Task.
|
|
13
|
+
Task.resolve = (value) => () => Promise.resolve(value);
|
|
14
14
|
/**
|
|
15
15
|
* Creates a Task from a function that returns a Promise.
|
|
16
16
|
* Alias for directly creating a Task.
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
20
|
-
* const
|
|
20
|
+
* const getTimestamp = Task.from(() => Promise.resolve(Date.now()));
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
Task.from = (f) => f;
|
|
@@ -27,7 +27,7 @@ export var Task;
|
|
|
27
27
|
* @example
|
|
28
28
|
* ```ts
|
|
29
29
|
* pipe(
|
|
30
|
-
* Task.
|
|
30
|
+
* Task.resolve(5),
|
|
31
31
|
* Task.map(n => n * 2)
|
|
32
32
|
* )(); // Promise<10>
|
|
33
33
|
* ```
|
|
@@ -38,13 +38,13 @@ export var Task;
|
|
|
38
38
|
*
|
|
39
39
|
* @example
|
|
40
40
|
* ```ts
|
|
41
|
-
* const
|
|
42
|
-
* const
|
|
41
|
+
* const readUserId: Task<string> = () => Promise.resolve(session.userId);
|
|
42
|
+
* const loadPrefs = (id: string): Task<Preferences> => () => Promise.resolve(prefsCache.get(id));
|
|
43
43
|
*
|
|
44
44
|
* pipe(
|
|
45
|
-
*
|
|
46
|
-
* Task.chain(
|
|
47
|
-
* )(); // Promise<
|
|
45
|
+
* readUserId,
|
|
46
|
+
* Task.chain(loadPrefs)
|
|
47
|
+
* )(); // Promise<Preferences>
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
50
|
Task.chain = (f) => (data) => () => data().then((a) => f(a)());
|
|
@@ -56,9 +56,9 @@ export var Task;
|
|
|
56
56
|
* ```ts
|
|
57
57
|
* const add = (a: number) => (b: number) => a + b;
|
|
58
58
|
* pipe(
|
|
59
|
-
* Task.
|
|
60
|
-
* Task.ap(Task.
|
|
61
|
-
* Task.ap(Task.
|
|
59
|
+
* Task.resolve(add),
|
|
60
|
+
* Task.ap(Task.resolve(5)),
|
|
61
|
+
* Task.ap(Task.resolve(3))
|
|
62
62
|
* )(); // Promise<8>
|
|
63
63
|
* ```
|
|
64
64
|
*/
|
|
@@ -70,9 +70,9 @@ export var Task;
|
|
|
70
70
|
* @example
|
|
71
71
|
* ```ts
|
|
72
72
|
* pipe(
|
|
73
|
-
*
|
|
74
|
-
* Task.tap(
|
|
75
|
-
* Task.map(
|
|
73
|
+
* loadConfig,
|
|
74
|
+
* Task.tap(cfg => console.log("Config:", cfg)),
|
|
75
|
+
* Task.map(buildReport)
|
|
76
76
|
* );
|
|
77
77
|
* ```
|
|
78
78
|
*/
|
|
@@ -85,8 +85,8 @@ export var Task;
|
|
|
85
85
|
*
|
|
86
86
|
* @example
|
|
87
87
|
* ```ts
|
|
88
|
-
* Task.all([
|
|
89
|
-
* // Promise<[
|
|
88
|
+
* Task.all([loadConfig, detectLocale, loadTheme])();
|
|
89
|
+
* // Promise<[Config, string, Theme]>
|
|
90
90
|
* ```
|
|
91
91
|
*/
|
|
92
92
|
Task.all = (tasks) => () => Promise.all(tasks.map((t) => t()));
|
|
@@ -97,7 +97,7 @@ export var Task;
|
|
|
97
97
|
* @example
|
|
98
98
|
* ```ts
|
|
99
99
|
* pipe(
|
|
100
|
-
* Task.
|
|
100
|
+
* Task.resolve(42),
|
|
101
101
|
* Task.delay(1000)
|
|
102
102
|
* )(); // Resolves after 1 second
|
|
103
103
|
* ```
|
|
@@ -158,16 +158,19 @@ export var Task;
|
|
|
158
158
|
* @example
|
|
159
159
|
* ```ts
|
|
160
160
|
* pipe(
|
|
161
|
-
*
|
|
162
|
-
* Task.timeout(5000, () =>
|
|
163
|
-
* TaskResult.chain(
|
|
161
|
+
* heavyComputation,
|
|
162
|
+
* Task.timeout(5000, () => "timed out"),
|
|
163
|
+
* TaskResult.chain(processResult)
|
|
164
164
|
* );
|
|
165
165
|
* ```
|
|
166
166
|
*/
|
|
167
167
|
Task.timeout = (ms, onTimeout) => (task) => () => {
|
|
168
168
|
let timerId;
|
|
169
169
|
return Promise.race([
|
|
170
|
-
task().then((a) => {
|
|
170
|
+
task().then((a) => {
|
|
171
|
+
clearTimeout(timerId);
|
|
172
|
+
return Result.ok(a);
|
|
173
|
+
}),
|
|
171
174
|
new Promise((resolve) => {
|
|
172
175
|
timerId = setTimeout(() => resolve(Result.err(onTimeout())), ms);
|
|
173
176
|
}),
|
|
@@ -5,19 +5,19 @@ export var TaskOption;
|
|
|
5
5
|
/**
|
|
6
6
|
* Wraps a value in a Some inside a Task.
|
|
7
7
|
*/
|
|
8
|
-
TaskOption.
|
|
8
|
+
TaskOption.some = (value) => Task.resolve(Option.some(value));
|
|
9
9
|
/**
|
|
10
10
|
* Creates a TaskOption that resolves to None.
|
|
11
11
|
*/
|
|
12
|
-
TaskOption.none = () => Task.
|
|
12
|
+
TaskOption.none = () => Task.resolve(Option.none());
|
|
13
13
|
/**
|
|
14
14
|
* Lifts an Option into a TaskOption.
|
|
15
15
|
*/
|
|
16
|
-
TaskOption.fromOption = (option) => Task.
|
|
16
|
+
TaskOption.fromOption = (option) => Task.resolve(option);
|
|
17
17
|
/**
|
|
18
18
|
* Lifts a Task into a TaskOption by wrapping its result in Some.
|
|
19
19
|
*/
|
|
20
|
-
TaskOption.fromTask = (task) => Task.map(Option.
|
|
20
|
+
TaskOption.fromTask = (task) => Task.map(Option.some)(task);
|
|
21
21
|
/**
|
|
22
22
|
* Creates a TaskOption from a Promise-returning function.
|
|
23
23
|
* Returns Some if the promise resolves, None if it rejects.
|
|
@@ -29,7 +29,9 @@ export var TaskOption;
|
|
|
29
29
|
* );
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
TaskOption.tryCatch = (f) => () => f()
|
|
32
|
+
TaskOption.tryCatch = (f) => () => f()
|
|
33
|
+
.then(Option.some)
|
|
34
|
+
.catch(() => Option.none());
|
|
33
35
|
/**
|
|
34
36
|
* Transforms the value inside a TaskOption.
|
|
35
37
|
*/
|
|
@@ -46,7 +48,7 @@ export var TaskOption;
|
|
|
46
48
|
* )();
|
|
47
49
|
* ```
|
|
48
50
|
*/
|
|
49
|
-
TaskOption.chain = (f) => (data) => Task.chain((option) => Option.isSome(option) ? f(option.value) : Task.
|
|
51
|
+
TaskOption.chain = (f) => (data) => Task.chain((option) => Option.isSome(option) ? f(option.value) : Task.resolve(Option.none()))(data);
|
|
50
52
|
/**
|
|
51
53
|
* Applies a function wrapped in a TaskOption to a value wrapped in a TaskOption.
|
|
52
54
|
* Both Tasks run in parallel.
|
|
@@ -5,11 +5,11 @@ export var TaskResult;
|
|
|
5
5
|
/**
|
|
6
6
|
* Wraps a value in a successful TaskResult.
|
|
7
7
|
*/
|
|
8
|
-
TaskResult.
|
|
8
|
+
TaskResult.ok = (value) => Task.resolve(Result.ok(value));
|
|
9
9
|
/**
|
|
10
10
|
* Creates a failed TaskResult with the given error.
|
|
11
11
|
*/
|
|
12
|
-
TaskResult.err = (error) => Task.
|
|
12
|
+
TaskResult.err = (error) => Task.resolve(Result.err(error));
|
|
13
13
|
/**
|
|
14
14
|
* Creates a TaskResult from a function that may throw.
|
|
15
15
|
* Catches any errors and transforms them using the onError function.
|
|
@@ -38,7 +38,7 @@ export var TaskResult;
|
|
|
38
38
|
* Chains TaskResult computations. If the first succeeds, passes the value to f.
|
|
39
39
|
* If the first fails, propagates the error.
|
|
40
40
|
*/
|
|
41
|
-
TaskResult.chain = (f) => (data) => Task.chain((result) => Result.isOk(result) ? f(result.value) : Task.
|
|
41
|
+
TaskResult.chain = (f) => (data) => Task.chain((result) => Result.isOk(result) ? f(result.value) : Task.resolve(Result.err(result.error)))(data);
|
|
42
42
|
/**
|
|
43
43
|
* Extracts the value from a TaskResult by providing handlers for both cases.
|
|
44
44
|
*/
|
|
@@ -50,7 +50,7 @@ export var TaskResult;
|
|
|
50
50
|
/**
|
|
51
51
|
* Recovers from an error by providing a fallback TaskResult.
|
|
52
52
|
*/
|
|
53
|
-
TaskResult.recover = (fallback) => (data) => Task.chain((result) => Result.isErr(result) ? fallback(result.error) : Task.
|
|
53
|
+
TaskResult.recover = (fallback) => (data) => Task.chain((result) => Result.isErr(result) ? fallback(result.error) : Task.resolve(result))(data);
|
|
54
54
|
/**
|
|
55
55
|
* Returns the success value or a default value if the TaskResult is an error.
|
|
56
56
|
*/
|
|
@@ -90,8 +90,9 @@ export var TaskResult;
|
|
|
90
90
|
return result;
|
|
91
91
|
if (left <= 1)
|
|
92
92
|
return result;
|
|
93
|
-
if (shouldRetry !== undefined && !shouldRetry(result.error))
|
|
93
|
+
if (shouldRetry !== undefined && !shouldRetry(result.error)) {
|
|
94
94
|
return result;
|
|
95
|
+
}
|
|
95
96
|
const ms = getDelay(attempts - left + 1);
|
|
96
97
|
return (ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve()).then(() => run(left - 1));
|
|
97
98
|
});
|
|
@@ -111,7 +112,10 @@ export var TaskResult;
|
|
|
111
112
|
TaskResult.timeout = (ms, onTimeout) => (data) => () => {
|
|
112
113
|
let timerId;
|
|
113
114
|
return Promise.race([
|
|
114
|
-
data().then((result) => {
|
|
115
|
+
data().then((result) => {
|
|
116
|
+
clearTimeout(timerId);
|
|
117
|
+
return result;
|
|
118
|
+
}),
|
|
115
119
|
new Promise((resolve) => {
|
|
116
120
|
timerId = setTimeout(() => resolve(Result.err(onTimeout())), ms);
|
|
117
121
|
}),
|
|
@@ -5,15 +5,19 @@ export var TaskValidation;
|
|
|
5
5
|
/**
|
|
6
6
|
* Wraps a value in a valid TaskValidation.
|
|
7
7
|
*/
|
|
8
|
-
TaskValidation.
|
|
8
|
+
TaskValidation.valid = (value) => Task.resolve(Validation.valid(value));
|
|
9
9
|
/**
|
|
10
10
|
* Creates a failed TaskValidation with a single error.
|
|
11
11
|
*/
|
|
12
|
-
TaskValidation.
|
|
12
|
+
TaskValidation.invalid = (error) => Task.resolve(Validation.invalid(error));
|
|
13
|
+
/**
|
|
14
|
+
* Creates an invalid TaskValidation from multiple errors.
|
|
15
|
+
*/
|
|
16
|
+
TaskValidation.invalidAll = (errors) => Task.resolve(Validation.invalidAll(errors));
|
|
13
17
|
/**
|
|
14
18
|
* Lifts a Validation into a TaskValidation.
|
|
15
19
|
*/
|
|
16
|
-
TaskValidation.fromValidation = (validation) => Task.
|
|
20
|
+
TaskValidation.fromValidation = (validation) => Task.resolve(validation);
|
|
17
21
|
/**
|
|
18
22
|
* Creates a TaskValidation from a Promise-returning function.
|
|
19
23
|
* Catches any errors and transforms them using the onError function.
|
|
@@ -28,8 +32,8 @@ export var TaskValidation;
|
|
|
28
32
|
* ```
|
|
29
33
|
*/
|
|
30
34
|
TaskValidation.tryCatch = (f, onError) => () => f()
|
|
31
|
-
.then((Validation.
|
|
32
|
-
.catch((e) => Validation.
|
|
35
|
+
.then((Validation.valid))
|
|
36
|
+
.catch((e) => Validation.invalid(onError(e)));
|
|
33
37
|
/**
|
|
34
38
|
* Transforms the success value inside a TaskValidation.
|
|
35
39
|
*/
|
|
@@ -42,7 +46,7 @@ export var TaskValidation;
|
|
|
42
46
|
*/
|
|
43
47
|
TaskValidation.chain = (f) => (data) => Task.chain((validation) => Validation.isValid(validation)
|
|
44
48
|
? f(validation.value)
|
|
45
|
-
: Task.
|
|
49
|
+
: Task.resolve(Validation.invalidAll(validation.errors)))(data);
|
|
46
50
|
/**
|
|
47
51
|
* Applies a function wrapped in a TaskValidation to a value wrapped in a
|
|
48
52
|
* TaskValidation. Both Tasks run in parallel and errors from both sides
|
|
@@ -51,7 +55,7 @@ export var TaskValidation;
|
|
|
51
55
|
* @example
|
|
52
56
|
* ```ts
|
|
53
57
|
* pipe(
|
|
54
|
-
* TaskValidation.
|
|
58
|
+
* TaskValidation.valid((name: string) => (age: number) => ({ name, age })),
|
|
55
59
|
* TaskValidation.ap(validateName(name)),
|
|
56
60
|
* TaskValidation.ap(validateAge(age))
|
|
57
61
|
* )();
|
|
@@ -89,5 +93,5 @@ export var TaskValidation;
|
|
|
89
93
|
/**
|
|
90
94
|
* Recovers from an Invalid state by providing a fallback TaskValidation.
|
|
91
95
|
*/
|
|
92
|
-
TaskValidation.recover = (fallback) => (data) => Task.chain((validation) => Validation.isValid(validation) ? Task.
|
|
96
|
+
TaskValidation.recover = (fallback) => (data) => Task.chain((validation) => Validation.isValid(validation) ? Task.resolve(validation) : fallback())(data);
|
|
93
97
|
})(TaskValidation || (TaskValidation = {}));
|