@nlozgachev/pipekit 0.4.1 → 0.5.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 +6 -0
- package/esm/src/Core/Arr.js +3 -1
- package/esm/src/Core/Deferred.js +28 -0
- package/esm/src/Core/Optional.js +2 -6
- package/esm/src/Core/Reader.js +134 -0
- package/esm/src/Core/Task.js +34 -26
- package/esm/src/Core/TaskOption.js +7 -3
- package/esm/src/Core/TaskResult.js +9 -8
- package/esm/src/Core/TaskValidation.js +7 -3
- package/esm/src/Core/index.js +2 -0
- package/package.json +1 -1
- package/script/src/Core/Arr.js +3 -1
- package/script/src/Core/Deferred.js +31 -0
- package/script/src/Core/Optional.js +2 -6
- package/script/src/Core/Reader.js +137 -0
- package/script/src/Core/Task.js +34 -26
- package/script/src/Core/TaskOption.js +7 -3
- package/script/src/Core/TaskResult.js +9 -8
- package/script/src/Core/TaskValidation.js +7 -3
- package/script/src/Core/index.js +2 -0
- package/types/src/Core/Arr.d.ts.map +1 -1
- package/types/src/Core/Deferred.d.ts +40 -0
- package/types/src/Core/Deferred.d.ts.map +1 -0
- package/types/src/Core/Lens.d.ts.map +1 -1
- package/types/src/Core/Optional.d.ts.map +1 -1
- package/types/src/Core/Reader.d.ts +156 -0
- package/types/src/Core/Reader.d.ts.map +1 -0
- package/types/src/Core/Task.d.ts +27 -10
- package/types/src/Core/Task.d.ts.map +1 -1
- package/types/src/Core/TaskOption.d.ts.map +1 -1
- package/types/src/Core/TaskResult.d.ts.map +1 -1
- package/types/src/Core/TaskValidation.d.ts.map +1 -1
- package/types/src/Core/These.d.ts.map +1 -1
- package/types/src/Core/index.d.ts +2 -0
- package/types/src/Core/index.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
A TypeScript toolkit for writing code that means exactly what it says.
|
|
6
6
|
|
|
7
|
+
> **Note:** pipekit is pre-1.0. The API may change between minor versions until the 1.0 release.
|
|
8
|
+
|
|
7
9
|
```sh
|
|
8
10
|
# npm / pnpm / yarn / bun
|
|
9
11
|
npm add @nlozgachev/pipekit
|
|
@@ -36,6 +38,10 @@ No FP jargon required. You won't find `Monad`, `Functor`, or `Applicative` in th
|
|
|
36
38
|
- **`These<E, A>`** — an inclusive OR: holds an error, a value, or both at once.
|
|
37
39
|
- **`RemoteData<E, A>`** — the four states of a data fetch: `NotAsked`, `Loading`, `Failure`,
|
|
38
40
|
`Success`.
|
|
41
|
+
- **`Lens<S, A>`** — focus on a required field in a nested structure. Read, set, and modify
|
|
42
|
+
immutably.
|
|
43
|
+
- **`Optional<S, A>`** — like `Lens`, but the target may be absent (nullable fields, array indices).
|
|
44
|
+
- **`Reader<R, A>`** — a computation that depends on an environment `R`, resolved later.
|
|
39
45
|
- **`Arr`** — array utilities that return `Option` instead of `undefined`.
|
|
40
46
|
- **`Rec`** — record/object utilities.
|
|
41
47
|
|
package/esm/src/Core/Arr.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { Deferred } from "./Deferred.js";
|
|
1
2
|
import { Option } from "./Option.js";
|
|
2
3
|
import { Result } from "./Result.js";
|
|
4
|
+
import { Task } from "./Task.js";
|
|
3
5
|
import { isNonEmptyList } from "../Types/NonEmptyList.js";
|
|
4
6
|
/**
|
|
5
7
|
* Functional array utilities that compose well with pipe.
|
|
@@ -353,7 +355,7 @@ export var Arr;
|
|
|
353
355
|
* )(); // Promise<[2, 4, 6]>
|
|
354
356
|
* ```
|
|
355
357
|
*/
|
|
356
|
-
Arr.traverseTask = (f) => (data) => () => Promise.all(data.map((a) => f(a)()));
|
|
358
|
+
Arr.traverseTask = (f) => (data) => Task.from(() => Promise.all(data.map((a) => Deferred.toPromise(f(a)()))));
|
|
357
359
|
/**
|
|
358
360
|
* Collects an array of Options into an Option of array.
|
|
359
361
|
* Returns None if any element is None.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export var Deferred;
|
|
2
|
+
(function (Deferred) {
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a `Promise` into a `Deferred`, hiding `.catch()`, `.finally()`,
|
|
5
|
+
* and chainable `.then()`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const d = Deferred.fromPromise(Promise.resolve("hello"));
|
|
10
|
+
* const value = await d; // "hello"
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
Deferred.fromPromise = (p) => ({
|
|
14
|
+
then: (f) => {
|
|
15
|
+
p.then(f);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Converts a `Deferred` back into a `Promise`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const p = Deferred.toPromise(Deferred.fromPromise(Promise.resolve(42)));
|
|
24
|
+
* // p is Promise<42>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
Deferred.toPromise = (d) => new Promise((resolve) => d.then(resolve));
|
|
28
|
+
})(Deferred || (Deferred = {}));
|
package/esm/src/Core/Optional.js
CHANGED
|
@@ -26,9 +26,7 @@ export var Optional;
|
|
|
26
26
|
*/
|
|
27
27
|
Optional.prop = () => (key) => Optional.make((s) => {
|
|
28
28
|
const val = s[key];
|
|
29
|
-
return val != null
|
|
30
|
-
? Option.some(val)
|
|
31
|
-
: Option.none();
|
|
29
|
+
return val != null ? Option.some(val) : Option.none();
|
|
32
30
|
}, (a) => (s) => ({ ...s, [key]: a }));
|
|
33
31
|
/**
|
|
34
32
|
* Creates an Optional that focuses on an element at a given index in an array.
|
|
@@ -154,9 +152,7 @@ export var Optional;
|
|
|
154
152
|
*/
|
|
155
153
|
Optional.andThenLens = (inner) => (outer) => Optional.make((s) => {
|
|
156
154
|
const mid = outer.get(s);
|
|
157
|
-
return mid.kind === "None"
|
|
158
|
-
? Option.none()
|
|
159
|
-
: Option.some(inner.get(mid.value));
|
|
155
|
+
return mid.kind === "None" ? Option.none() : Option.some(inner.get(mid.value));
|
|
160
156
|
}, (b) => (s) => {
|
|
161
157
|
const mid = outer.get(s);
|
|
162
158
|
return mid.kind === "None" ? s : outer.set(inner.set(b)(mid.value))(s);
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
export var Reader;
|
|
2
|
+
(function (Reader) {
|
|
3
|
+
/**
|
|
4
|
+
* Lifts a pure value into a Reader. The environment is ignored.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const always42: Reader<Config, number> = Reader.resolve(42);
|
|
9
|
+
* always42(anyConfig); // 42
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
Reader.resolve = (value) => (_env) => value;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the full environment as the result.
|
|
15
|
+
* The fundamental way to access the environment in a pipeline.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* pipe(
|
|
20
|
+
* Reader.ask<Config>(),
|
|
21
|
+
* Reader.map(config => config.baseUrl)
|
|
22
|
+
* )(appConfig); // "https://api.example.com"
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
Reader.ask = () => (env) => env;
|
|
26
|
+
/**
|
|
27
|
+
* Projects a value from the environment using a selector function.
|
|
28
|
+
* Equivalent to `pipe(Reader.ask(), Reader.map(f))` but more direct.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const getBaseUrl: Reader<Config, string> = Reader.asks(c => c.baseUrl);
|
|
33
|
+
* getBaseUrl(appConfig); // "https://api.example.com"
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
Reader.asks = (f) => (env) => f(env);
|
|
37
|
+
/**
|
|
38
|
+
* Transforms the value produced by a Reader.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* pipe(
|
|
43
|
+
* Reader.asks((c: Config) => c.baseUrl),
|
|
44
|
+
* Reader.map(url => url.toUpperCase())
|
|
45
|
+
* )(appConfig); // "HTTPS://API.EXAMPLE.COM"
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
Reader.map = (f) => (data) => (env) => f(data(env));
|
|
49
|
+
/**
|
|
50
|
+
* Sequences two Readers. Both see the same environment.
|
|
51
|
+
* The output of the first is passed to `f`, which returns the next Reader.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const buildUrl = (path: string): Reader<Config, string> =>
|
|
56
|
+
* Reader.asks(c => `${c.baseUrl}${path}`);
|
|
57
|
+
*
|
|
58
|
+
* const addAuth = (url: string): Reader<Config, string> =>
|
|
59
|
+
* Reader.asks(c => `${url}?key=${c.apiKey}`);
|
|
60
|
+
*
|
|
61
|
+
* pipe(
|
|
62
|
+
* buildUrl("/items"),
|
|
63
|
+
* Reader.chain(addAuth)
|
|
64
|
+
* )(appConfig); // "https://api.example.com/items?key=secret"
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
Reader.chain = (f) => (data) => (env) => f(data(env))(env);
|
|
68
|
+
/**
|
|
69
|
+
* Applies a function wrapped in a Reader to a value wrapped in a Reader.
|
|
70
|
+
* Both Readers see the same environment.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const add = (a: number) => (b: number) => a + b;
|
|
75
|
+
* pipe(
|
|
76
|
+
* Reader.resolve<Config, typeof add>(add),
|
|
77
|
+
* Reader.ap(Reader.asks(c => c.timeout)),
|
|
78
|
+
* Reader.ap(Reader.resolve(5))
|
|
79
|
+
* )(appConfig);
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
Reader.ap = (arg) => (data) => (env) => data(env)(arg(env));
|
|
83
|
+
/**
|
|
84
|
+
* Executes a side effect on the produced value without changing the Reader.
|
|
85
|
+
* Useful for logging or debugging inside a pipeline.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* pipe(
|
|
90
|
+
* buildUrl("/users"),
|
|
91
|
+
* Reader.tap(url => console.log("Requesting:", url)),
|
|
92
|
+
* Reader.chain(addAuth)
|
|
93
|
+
* )(appConfig);
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
Reader.tap = (f) => (data) => (env) => {
|
|
97
|
+
const a = data(env);
|
|
98
|
+
f(a);
|
|
99
|
+
return a;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Adapts a Reader to work with a different (typically wider) environment
|
|
103
|
+
* by transforming the environment before passing it to the Reader.
|
|
104
|
+
* This lets you compose Readers that expect different environments.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* type AppEnv = { db: DbPool; config: Config; logger: Logger };
|
|
109
|
+
*
|
|
110
|
+
* // buildUrl only needs Config
|
|
111
|
+
* const buildUrl: Reader<Config, string> = Reader.asks(c => c.baseUrl);
|
|
112
|
+
*
|
|
113
|
+
* // Zoom in from AppEnv to Config
|
|
114
|
+
* const buildUrlFromApp: Reader<AppEnv, string> =
|
|
115
|
+
* pipe(buildUrl, Reader.local((env: AppEnv) => env.config));
|
|
116
|
+
*
|
|
117
|
+
* buildUrlFromApp(appEnv); // works with the full AppEnv
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
Reader.local = (f) => (data) => (env) => data(f(env));
|
|
121
|
+
/**
|
|
122
|
+
* Runs a Reader by supplying the environment. Use this at the edge of your
|
|
123
|
+
* program where the environment is available.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* pipe(
|
|
128
|
+
* buildEndpoint("/users"),
|
|
129
|
+
* Reader.run(appConfig)
|
|
130
|
+
* ); // "https://api.example.com/users?key=secret"
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
Reader.run = (env) => (data) => data(env);
|
|
134
|
+
})(Reader || (Reader = {}));
|
package/esm/src/Core/Task.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { Deferred } from "./Deferred.js";
|
|
1
2
|
import { Result } from "./Result.js";
|
|
3
|
+
// Internal helper — not exported. Runs a Task and converts the result to a Promise
|
|
4
|
+
// so that combinators can use Promise chaining (.then, Promise.all, Promise.race, etc.)
|
|
5
|
+
// internally without leaking that primitive through the public API.
|
|
6
|
+
const toPromise = (task) => Deferred.toPromise(task());
|
|
2
7
|
export var Task;
|
|
3
8
|
(function (Task) {
|
|
4
9
|
/**
|
|
@@ -7,20 +12,19 @@ export var Task;
|
|
|
7
12
|
* @example
|
|
8
13
|
* ```ts
|
|
9
14
|
* const task = Task.resolve(42);
|
|
10
|
-
* task()
|
|
15
|
+
* const value = await task(); // 42
|
|
11
16
|
* ```
|
|
12
17
|
*/
|
|
13
|
-
Task.resolve = (value) => () => Promise.resolve(value);
|
|
18
|
+
Task.resolve = (value) => () => Deferred.fromPromise(Promise.resolve(value));
|
|
14
19
|
/**
|
|
15
20
|
* Creates a Task from a function that returns a Promise.
|
|
16
|
-
* Alias for directly creating a Task.
|
|
17
21
|
*
|
|
18
22
|
* @example
|
|
19
23
|
* ```ts
|
|
20
24
|
* const getTimestamp = Task.from(() => Promise.resolve(Date.now()));
|
|
21
25
|
* ```
|
|
22
26
|
*/
|
|
23
|
-
Task.from = (f) => f;
|
|
27
|
+
Task.from = (f) => () => Deferred.fromPromise(f());
|
|
24
28
|
/**
|
|
25
29
|
* Transforms the value inside a Task.
|
|
26
30
|
*
|
|
@@ -29,25 +33,26 @@ export var Task;
|
|
|
29
33
|
* pipe(
|
|
30
34
|
* Task.resolve(5),
|
|
31
35
|
* Task.map(n => n * 2)
|
|
32
|
-
* )(); //
|
|
36
|
+
* )(); // Deferred<10>
|
|
33
37
|
* ```
|
|
34
38
|
*/
|
|
35
|
-
Task.map = (f) => (data) => () => data
|
|
39
|
+
Task.map = (f) => (data) => Task.from(() => toPromise(data).then(f));
|
|
36
40
|
/**
|
|
37
41
|
* Chains Task computations. Passes the resolved value of the first Task to f.
|
|
38
42
|
*
|
|
39
43
|
* @example
|
|
40
44
|
* ```ts
|
|
41
|
-
* const readUserId: Task<string> =
|
|
42
|
-
* const loadPrefs = (id: string): Task<Preferences> =>
|
|
45
|
+
* const readUserId: Task<string> = Task.resolve(session.userId);
|
|
46
|
+
* const loadPrefs = (id: string): Task<Preferences> =>
|
|
47
|
+
* Task.resolve(prefsCache.get(id));
|
|
43
48
|
*
|
|
44
49
|
* pipe(
|
|
45
50
|
* readUserId,
|
|
46
51
|
* Task.chain(loadPrefs)
|
|
47
|
-
* )(); //
|
|
52
|
+
* )(); // Deferred<Preferences>
|
|
48
53
|
* ```
|
|
49
54
|
*/
|
|
50
|
-
Task.chain = (f) => (data) => () => data
|
|
55
|
+
Task.chain = (f) => (data) => Task.from(() => toPromise(data).then((a) => toPromise(f(a))));
|
|
51
56
|
/**
|
|
52
57
|
* Applies a function wrapped in a Task to a value wrapped in a Task.
|
|
53
58
|
* Both Tasks run in parallel.
|
|
@@ -59,10 +64,13 @@ export var Task;
|
|
|
59
64
|
* Task.resolve(add),
|
|
60
65
|
* Task.ap(Task.resolve(5)),
|
|
61
66
|
* Task.ap(Task.resolve(3))
|
|
62
|
-
* )(); //
|
|
67
|
+
* )(); // Deferred<8>
|
|
63
68
|
* ```
|
|
64
69
|
*/
|
|
65
|
-
Task.ap = (arg) => (data) => () => Promise.all([
|
|
70
|
+
Task.ap = (arg) => (data) => Task.from(() => Promise.all([
|
|
71
|
+
toPromise(data),
|
|
72
|
+
toPromise(arg),
|
|
73
|
+
]).then(([f, a]) => f(a)));
|
|
66
74
|
/**
|
|
67
75
|
* Executes a side effect on the value without changing the Task.
|
|
68
76
|
* Useful for logging or debugging.
|
|
@@ -76,20 +84,20 @@ export var Task;
|
|
|
76
84
|
* );
|
|
77
85
|
* ```
|
|
78
86
|
*/
|
|
79
|
-
Task.tap = (f) => (data) => () => data
|
|
87
|
+
Task.tap = (f) => (data) => Task.from(() => toPromise(data).then((a) => {
|
|
80
88
|
f(a);
|
|
81
89
|
return a;
|
|
82
|
-
});
|
|
90
|
+
}));
|
|
83
91
|
/**
|
|
84
92
|
* Runs multiple Tasks in parallel and collects their results.
|
|
85
93
|
*
|
|
86
94
|
* @example
|
|
87
95
|
* ```ts
|
|
88
96
|
* Task.all([loadConfig, detectLocale, loadTheme])();
|
|
89
|
-
* //
|
|
97
|
+
* // Deferred<[Config, string, Theme]>
|
|
90
98
|
* ```
|
|
91
99
|
*/
|
|
92
|
-
Task.all = (tasks) => () => Promise.all(tasks.map((t) => t
|
|
100
|
+
Task.all = (tasks) => Task.from(() => Promise.all(tasks.map((t) => toPromise(t))));
|
|
93
101
|
/**
|
|
94
102
|
* Delays the execution of a Task by the specified milliseconds.
|
|
95
103
|
* Useful for debouncing or rate limiting.
|
|
@@ -102,7 +110,7 @@ export var Task;
|
|
|
102
110
|
* )(); // Resolves after 1 second
|
|
103
111
|
* ```
|
|
104
112
|
*/
|
|
105
|
-
Task.delay = (ms) => (data) => () => new Promise((resolve
|
|
113
|
+
Task.delay = (ms) => (data) => Task.from(() => new Promise((resolve) => setTimeout(() => toPromise(data).then(resolve), ms)));
|
|
106
114
|
/**
|
|
107
115
|
* Runs a Task a fixed number of times sequentially, collecting all results into an array.
|
|
108
116
|
* An optional delay (ms) can be inserted between runs.
|
|
@@ -115,20 +123,20 @@ export var Task;
|
|
|
115
123
|
* )(); // Task<Reading[]> — 5 readings, one per second
|
|
116
124
|
* ```
|
|
117
125
|
*/
|
|
118
|
-
Task.repeat = (options) => (task) => () => {
|
|
126
|
+
Task.repeat = (options) => (task) => Task.from(() => {
|
|
119
127
|
const { times, delay: ms } = options;
|
|
120
128
|
if (times <= 0)
|
|
121
129
|
return Promise.resolve([]);
|
|
122
130
|
const results = [];
|
|
123
131
|
const wait = () => ms !== undefined && ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
|
|
124
|
-
const run = (left) => task
|
|
132
|
+
const run = (left) => toPromise(task).then((a) => {
|
|
125
133
|
results.push(a);
|
|
126
134
|
if (left <= 1)
|
|
127
135
|
return results;
|
|
128
136
|
return wait().then(() => run(left - 1));
|
|
129
137
|
});
|
|
130
138
|
return run(times);
|
|
131
|
-
};
|
|
139
|
+
});
|
|
132
140
|
/**
|
|
133
141
|
* Runs a Task repeatedly until the result satisfies a predicate, returning that result.
|
|
134
142
|
* An optional delay (ms) can be inserted between runs.
|
|
@@ -141,16 +149,16 @@ export var Task;
|
|
|
141
149
|
* )(); // polls every 500ms until status is "ready"
|
|
142
150
|
* ```
|
|
143
151
|
*/
|
|
144
|
-
Task.repeatUntil = (options) => (task) => () => {
|
|
152
|
+
Task.repeatUntil = (options) => (task) => Task.from(() => {
|
|
145
153
|
const { when: predicate, delay: ms } = options;
|
|
146
154
|
const wait = () => ms !== undefined && ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
|
|
147
|
-
const run = () => task
|
|
155
|
+
const run = () => toPromise(task).then((a) => {
|
|
148
156
|
if (predicate(a))
|
|
149
157
|
return a;
|
|
150
158
|
return wait().then(run);
|
|
151
159
|
});
|
|
152
160
|
return run();
|
|
153
|
-
};
|
|
161
|
+
});
|
|
154
162
|
/**
|
|
155
163
|
* Converts a `Task<A>` into a `Task<Result<E, A>>`, resolving to `Err` if the
|
|
156
164
|
* Task does not complete within the given time.
|
|
@@ -164,10 +172,10 @@ export var Task;
|
|
|
164
172
|
* );
|
|
165
173
|
* ```
|
|
166
174
|
*/
|
|
167
|
-
Task.timeout = (ms, onTimeout) => (task) => () => {
|
|
175
|
+
Task.timeout = (ms, onTimeout) => (task) => Task.from(() => {
|
|
168
176
|
let timerId;
|
|
169
177
|
return Promise.race([
|
|
170
|
-
task
|
|
178
|
+
toPromise(task).then((a) => {
|
|
171
179
|
clearTimeout(timerId);
|
|
172
180
|
return Result.ok(a);
|
|
173
181
|
}),
|
|
@@ -175,5 +183,5 @@ export var Task;
|
|
|
175
183
|
timerId = setTimeout(() => resolve(Result.err(onTimeout())), ms);
|
|
176
184
|
}),
|
|
177
185
|
]);
|
|
178
|
-
};
|
|
186
|
+
});
|
|
179
187
|
})(Task || (Task = {}));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Deferred } from "./Deferred.js";
|
|
1
2
|
import { Option } from "./Option.js";
|
|
2
3
|
import { Task } from "./Task.js";
|
|
3
4
|
export var TaskOption;
|
|
@@ -29,9 +30,9 @@ export var TaskOption;
|
|
|
29
30
|
* );
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
|
-
TaskOption.tryCatch = (f) => () => f()
|
|
33
|
+
TaskOption.tryCatch = (f) => Task.from(() => f()
|
|
33
34
|
.then(Option.some)
|
|
34
|
-
.catch(() => Option.none());
|
|
35
|
+
.catch(() => Option.none()));
|
|
35
36
|
/**
|
|
36
37
|
* Transforms the value inside a TaskOption.
|
|
37
38
|
*/
|
|
@@ -53,7 +54,10 @@ export var TaskOption;
|
|
|
53
54
|
* Applies a function wrapped in a TaskOption to a value wrapped in a TaskOption.
|
|
54
55
|
* Both Tasks run in parallel.
|
|
55
56
|
*/
|
|
56
|
-
TaskOption.ap = (arg) => (data) => () => Promise.all([
|
|
57
|
+
TaskOption.ap = (arg) => (data) => Task.from(() => Promise.all([
|
|
58
|
+
Deferred.toPromise(data()),
|
|
59
|
+
Deferred.toPromise(arg()),
|
|
60
|
+
]).then(([of_, oa]) => Option.ap(oa)(of_)));
|
|
57
61
|
/**
|
|
58
62
|
* Extracts a value from a TaskOption by providing handlers for both cases.
|
|
59
63
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Deferred } from "./Deferred.js";
|
|
1
2
|
import { Result } from "./Result.js";
|
|
2
3
|
import { Task } from "./Task.js";
|
|
3
4
|
export var TaskResult;
|
|
@@ -23,9 +24,9 @@ export var TaskResult;
|
|
|
23
24
|
* );
|
|
24
25
|
* ```
|
|
25
26
|
*/
|
|
26
|
-
TaskResult.tryCatch = (f, onError) => () => f()
|
|
27
|
+
TaskResult.tryCatch = (f, onError) => Task.from(() => f()
|
|
27
28
|
.then(Result.ok)
|
|
28
|
-
.catch((e) => Result.err(onError(e)));
|
|
29
|
+
.catch((e) => Result.err(onError(e))));
|
|
29
30
|
/**
|
|
30
31
|
* Transforms the success value inside a TaskResult.
|
|
31
32
|
*/
|
|
@@ -82,10 +83,10 @@ export var TaskResult;
|
|
|
82
83
|
* );
|
|
83
84
|
* ```
|
|
84
85
|
*/
|
|
85
|
-
TaskResult.retry = (options) => (data) => () => {
|
|
86
|
+
TaskResult.retry = (options) => (data) => Task.from(() => {
|
|
86
87
|
const { attempts, backoff, when: shouldRetry } = options;
|
|
87
88
|
const getDelay = (n) => backoff === undefined ? 0 : typeof backoff === "function" ? backoff(n) : backoff;
|
|
88
|
-
const run = (left) => data().then((result) => {
|
|
89
|
+
const run = (left) => Deferred.toPromise(data()).then((result) => {
|
|
89
90
|
if (Result.isOk(result))
|
|
90
91
|
return result;
|
|
91
92
|
if (left <= 1)
|
|
@@ -97,7 +98,7 @@ export var TaskResult;
|
|
|
97
98
|
return (ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve()).then(() => run(left - 1));
|
|
98
99
|
});
|
|
99
100
|
return run(attempts);
|
|
100
|
-
};
|
|
101
|
+
});
|
|
101
102
|
/**
|
|
102
103
|
* Fails a TaskResult with a typed error if it does not resolve within the given time.
|
|
103
104
|
*
|
|
@@ -109,10 +110,10 @@ export var TaskResult;
|
|
|
109
110
|
* );
|
|
110
111
|
* ```
|
|
111
112
|
*/
|
|
112
|
-
TaskResult.timeout = (ms, onTimeout) => (data) => () => {
|
|
113
|
+
TaskResult.timeout = (ms, onTimeout) => (data) => Task.from(() => {
|
|
113
114
|
let timerId;
|
|
114
115
|
return Promise.race([
|
|
115
|
-
data().then((result) => {
|
|
116
|
+
Deferred.toPromise(data()).then((result) => {
|
|
116
117
|
clearTimeout(timerId);
|
|
117
118
|
return result;
|
|
118
119
|
}),
|
|
@@ -120,5 +121,5 @@ export var TaskResult;
|
|
|
120
121
|
timerId = setTimeout(() => resolve(Result.err(onTimeout())), ms);
|
|
121
122
|
}),
|
|
122
123
|
]);
|
|
123
|
-
};
|
|
124
|
+
});
|
|
124
125
|
})(TaskResult || (TaskResult = {}));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Deferred } from "./Deferred.js";
|
|
1
2
|
import { Task } from "./Task.js";
|
|
2
3
|
import { Validation } from "./Validation.js";
|
|
3
4
|
export var TaskValidation;
|
|
@@ -31,9 +32,9 @@ export var TaskValidation;
|
|
|
31
32
|
* );
|
|
32
33
|
* ```
|
|
33
34
|
*/
|
|
34
|
-
TaskValidation.tryCatch = (f, onError) => () => f()
|
|
35
|
+
TaskValidation.tryCatch = (f, onError) => Task.from(() => f()
|
|
35
36
|
.then((Validation.valid))
|
|
36
|
-
.catch((e) => Validation.invalid(onError(e)));
|
|
37
|
+
.catch((e) => Validation.invalid(onError(e))));
|
|
37
38
|
/**
|
|
38
39
|
* Transforms the success value inside a TaskValidation.
|
|
39
40
|
*/
|
|
@@ -61,7 +62,10 @@ export var TaskValidation;
|
|
|
61
62
|
* )();
|
|
62
63
|
* ```
|
|
63
64
|
*/
|
|
64
|
-
TaskValidation.ap = (arg) => (data) => () => Promise.all([
|
|
65
|
+
TaskValidation.ap = (arg) => (data) => Task.from(() => Promise.all([
|
|
66
|
+
Deferred.toPromise(data()),
|
|
67
|
+
Deferred.toPromise(arg()),
|
|
68
|
+
]).then(([vf, va]) => Validation.ap(va)(vf)));
|
|
65
69
|
/**
|
|
66
70
|
* Extracts a value from a TaskValidation by providing handlers for both cases.
|
|
67
71
|
*/
|
package/esm/src/Core/index.js
CHANGED
package/package.json
CHANGED
package/script/src/Core/Arr.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Arr = void 0;
|
|
4
|
+
const Deferred_js_1 = require("./Deferred.js");
|
|
4
5
|
const Option_js_1 = require("./Option.js");
|
|
5
6
|
const Result_js_1 = require("./Result.js");
|
|
7
|
+
const Task_js_1 = require("./Task.js");
|
|
6
8
|
const NonEmptyList_js_1 = require("../Types/NonEmptyList.js");
|
|
7
9
|
/**
|
|
8
10
|
* Functional array utilities that compose well with pipe.
|
|
@@ -356,7 +358,7 @@ var Arr;
|
|
|
356
358
|
* )(); // Promise<[2, 4, 6]>
|
|
357
359
|
* ```
|
|
358
360
|
*/
|
|
359
|
-
Arr.traverseTask = (f) => (data) => () => Promise.all(data.map((a) => f(a)()));
|
|
361
|
+
Arr.traverseTask = (f) => (data) => Task_js_1.Task.from(() => Promise.all(data.map((a) => Deferred_js_1.Deferred.toPromise(f(a)()))));
|
|
360
362
|
/**
|
|
361
363
|
* Collects an array of Options into an Option of array.
|
|
362
364
|
* Returns None if any element is None.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Deferred = void 0;
|
|
4
|
+
var Deferred;
|
|
5
|
+
(function (Deferred) {
|
|
6
|
+
/**
|
|
7
|
+
* Wraps a `Promise` into a `Deferred`, hiding `.catch()`, `.finally()`,
|
|
8
|
+
* and chainable `.then()`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const d = Deferred.fromPromise(Promise.resolve("hello"));
|
|
13
|
+
* const value = await d; // "hello"
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
Deferred.fromPromise = (p) => ({
|
|
17
|
+
then: (f) => {
|
|
18
|
+
p.then(f);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Converts a `Deferred` back into a `Promise`.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const p = Deferred.toPromise(Deferred.fromPromise(Promise.resolve(42)));
|
|
27
|
+
* // p is Promise<42>
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
Deferred.toPromise = (d) => new Promise((resolve) => d.then(resolve));
|
|
31
|
+
})(Deferred || (exports.Deferred = Deferred = {}));
|
|
@@ -29,9 +29,7 @@ var Optional;
|
|
|
29
29
|
*/
|
|
30
30
|
Optional.prop = () => (key) => Optional.make((s) => {
|
|
31
31
|
const val = s[key];
|
|
32
|
-
return val != null
|
|
33
|
-
? Option_js_1.Option.some(val)
|
|
34
|
-
: Option_js_1.Option.none();
|
|
32
|
+
return val != null ? Option_js_1.Option.some(val) : Option_js_1.Option.none();
|
|
35
33
|
}, (a) => (s) => ({ ...s, [key]: a }));
|
|
36
34
|
/**
|
|
37
35
|
* Creates an Optional that focuses on an element at a given index in an array.
|
|
@@ -157,9 +155,7 @@ var Optional;
|
|
|
157
155
|
*/
|
|
158
156
|
Optional.andThenLens = (inner) => (outer) => Optional.make((s) => {
|
|
159
157
|
const mid = outer.get(s);
|
|
160
|
-
return mid.kind === "None"
|
|
161
|
-
? Option_js_1.Option.none()
|
|
162
|
-
: Option_js_1.Option.some(inner.get(mid.value));
|
|
158
|
+
return mid.kind === "None" ? Option_js_1.Option.none() : Option_js_1.Option.some(inner.get(mid.value));
|
|
163
159
|
}, (b) => (s) => {
|
|
164
160
|
const mid = outer.get(s);
|
|
165
161
|
return mid.kind === "None" ? s : outer.set(inner.set(b)(mid.value))(s);
|