onlyfuncs 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +138 -2
- package/package.json +29 -4
- package/src/function/index.js +109 -0
- package/src/index.js +10 -0
- package/src/monoid/index.js +121 -0
- package/src/option/index.js +321 -0
- package/src/predicate/index.js +72 -0
- package/src/readonly-array/index.js +136 -0
- package/src/result/index.js +455 -0
- package/src/semigroup/index.js +112 -0
- package/src/task/index.js +246 -0
- package/src/task-result/index.js +754 -0
- package/src/validation/index.js +122 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A computation invoked without arguments to obtain a Promise.
|
|
3
|
+
* Call the task to run it; calling it again reruns its body. Tasks do not cache
|
|
4
|
+
* results automatically. A captured promise may already be running or settled.
|
|
5
|
+
*
|
|
6
|
+
* @template A
|
|
7
|
+
* @typedef {() => Promise<A>} Task
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a task for a captured value, preserving object references.
|
|
12
|
+
* Functions remain data. Promises and thenables are adopted when the task runs;
|
|
13
|
+
* an already-created promise is not restarted or made lazy.
|
|
14
|
+
*
|
|
15
|
+
* @template A
|
|
16
|
+
* @param {A | PromiseLike<A>} value - The value or promise-like result to capture.
|
|
17
|
+
* @returns {Task<A>} The task for the captured value.
|
|
18
|
+
* @example
|
|
19
|
+
* const hero = of("Link");
|
|
20
|
+
* await hero(); // "Link"
|
|
21
|
+
*/
|
|
22
|
+
export const of = (value) => () => Promise.resolve(value);
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Defers a function call until task execution, repeating it on every run.
|
|
27
|
+
* Adopts returned promises and thenables. Synchronous throws become rejections.
|
|
28
|
+
*
|
|
29
|
+
* @template A
|
|
30
|
+
* @param {() => A | PromiseLike<A>} func - Called without arguments on each execution.
|
|
31
|
+
* @returns {Task<A>} The deferred task.
|
|
32
|
+
* @example
|
|
33
|
+
* let calls = 0;
|
|
34
|
+
* const count = defer(() => ++calls);
|
|
35
|
+
* await count(); // 1
|
|
36
|
+
* await count(); // 2
|
|
37
|
+
*/
|
|
38
|
+
export const defer = (func) => async() =>
|
|
39
|
+
func();
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates a delay task. Each execution schedules a new timer.
|
|
44
|
+
* Validates the delay immediately, before any task is run.
|
|
45
|
+
*
|
|
46
|
+
* @param {number} ms - An integer delay in milliseconds, from 0 through 2147483647.
|
|
47
|
+
* @returns {Task<undefined>} A task resolving to undefined after the delay.
|
|
48
|
+
* @throws {RangeError} If ms is not an integer in the supported range.
|
|
49
|
+
* @example
|
|
50
|
+
* await sleep(0)(); // undefined
|
|
51
|
+
*/
|
|
52
|
+
export const sleep = (ms) => {
|
|
53
|
+
if (!Number.isInteger(ms) || ms < 0 || ms > 2147483647) {
|
|
54
|
+
throw new RangeError("Milliseconds must be an integer between 0 and 2147483647");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return () =>
|
|
58
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Transforms a task's resolved value, awaiting a returned promise or thenable.
|
|
64
|
+
* Repeats the source and transform on each execution; a rejection skips transform.
|
|
65
|
+
* A callback throw rejects the promise; a source throw before returning a promise
|
|
66
|
+
* throws directly.
|
|
67
|
+
*
|
|
68
|
+
* @template A, B
|
|
69
|
+
* @param {(value: A) => B | PromiseLike<B>} transform - Called once with the fulfilled value.
|
|
70
|
+
* @returns {(task: Task<A>) => Task<B>} A function mapping a task.
|
|
71
|
+
* @example
|
|
72
|
+
* const heal = map(async health => health + 1)(of(3));
|
|
73
|
+
* await heal(); // 4
|
|
74
|
+
*/
|
|
75
|
+
export const map = (transform) => (task) => () =>
|
|
76
|
+
task().then(transform);
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Chains a resolved value into another task and runs it.
|
|
81
|
+
* The callback may return the next task or a promise of it. Repeats the chain on
|
|
82
|
+
* each execution; a source rejection skips the callback. Callback and next-task
|
|
83
|
+
* failures reject the promise; a source throw before returning a promise throws directly.
|
|
84
|
+
*
|
|
85
|
+
* @template A, B
|
|
86
|
+
* @param {(value: A) => Task<B> | PromiseLike<Task<B>>} transform -
|
|
87
|
+
* Supplies the next task for the resolved value.
|
|
88
|
+
* @returns {(task: Task<A>) => Task<B>} A function chaining tasks.
|
|
89
|
+
* @example
|
|
90
|
+
* const heal = flatMap(health => of(health + 1))(of(3));
|
|
91
|
+
* await heal(); // 4
|
|
92
|
+
*/
|
|
93
|
+
export const flatMap = (transform) => (task) => () =>
|
|
94
|
+
task().then(transform).then((t) => t());
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Removes one nested Task layer by running the outer task, then the resolved task.
|
|
99
|
+
* Runs the inner task only if the outer task fulfills. Rejections propagate.
|
|
100
|
+
* An outer-task throw before returning a promise throws directly; an inner-task
|
|
101
|
+
* throw rejects the returned promise.
|
|
102
|
+
*
|
|
103
|
+
* @template A
|
|
104
|
+
* @param {Task<Task<A>>} task - A task resolving to another task.
|
|
105
|
+
* @returns {Task<A>} The task for the inner result.
|
|
106
|
+
* @example
|
|
107
|
+
* await flatten(of(of("Link")))(); // "Link"
|
|
108
|
+
*/
|
|
109
|
+
export const flatten = (task) => () =>
|
|
110
|
+
task().then((t) => t());
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Runs an effect after fulfillment and preserves the value; skips the effect on rejection.
|
|
115
|
+
* Repeats on each execution. Ignores the effect's return value without awaiting it;
|
|
116
|
+
* use tapAsync for async effects. An effect throw rejects the promise; a source throw
|
|
117
|
+
* before returning a promise throws directly.
|
|
118
|
+
*
|
|
119
|
+
* @template A
|
|
120
|
+
* @param {(value: A) => unknown} effect - Observes the resolved value.
|
|
121
|
+
* @returns {(task: Task<A>) => Task<A>} A function adding a synchronous effect.
|
|
122
|
+
* @example
|
|
123
|
+
* const seen = [];
|
|
124
|
+
* const hero = tap(name => { seen.push(name); })(of("Link"));
|
|
125
|
+
* await hero(); // "Link"
|
|
126
|
+
* seen; // ["Link"]
|
|
127
|
+
*/
|
|
128
|
+
export const tap = (effect) => (task) => () =>
|
|
129
|
+
task().then(result => {
|
|
130
|
+
effect(result);
|
|
131
|
+
return result;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Awaits an effect after fulfillment and preserves the value; skips it on failure.
|
|
137
|
+
* Repeats on each execution. Source or effect throws and rejections reject the task.
|
|
138
|
+
*
|
|
139
|
+
* @template A
|
|
140
|
+
* @param {(value: A) => unknown} effect - Observes the resolved value; may return a promise.
|
|
141
|
+
* @returns {(task: Task<A>) => Task<A>} A function adding an awaited effect.
|
|
142
|
+
* @example
|
|
143
|
+
* const seen = [];
|
|
144
|
+
* const hero = tapAsync(async name => {
|
|
145
|
+
* await Promise.resolve();
|
|
146
|
+
* seen.push(name);
|
|
147
|
+
* })(of("Link"));
|
|
148
|
+
* await hero(); // "Link"
|
|
149
|
+
* seen; // ["Link"]
|
|
150
|
+
*/
|
|
151
|
+
export const tapAsync = (effect) => (task) => async () => {
|
|
152
|
+
const result = await task();
|
|
153
|
+
|
|
154
|
+
await effect(result);
|
|
155
|
+
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Runs tasks concurrently and collects their results in input order.
|
|
162
|
+
* Each execution calls the supplied task functions again and produces a new array.
|
|
163
|
+
* Empty input resolves to an empty array. Rejections do not cancel started tasks.
|
|
164
|
+
* A synchronous task throw rejects the combined task and prevents later tasks
|
|
165
|
+
* from starting.
|
|
166
|
+
* Array holes become undefined entries in the output.
|
|
167
|
+
*
|
|
168
|
+
* @template A
|
|
169
|
+
* @param {ReadonlyArray<Task<A>>} tasks - Tasks invoked without arguments in array order.
|
|
170
|
+
* @returns {Task<(A | undefined)[]>} The task collecting all values.
|
|
171
|
+
* @example
|
|
172
|
+
* await all([of("Link"), of("Zelda")])(); // ["Link", "Zelda"]
|
|
173
|
+
*/
|
|
174
|
+
export const all = (tasks) => async () => {
|
|
175
|
+
const promises = tasks.map((task) => task());
|
|
176
|
+
return Promise.all(promises);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Creates and runs tasks concurrently, collecting values in input order.
|
|
182
|
+
* Repeats transforms and tasks on each execution. Empty input resolves to [].
|
|
183
|
+
* Rejections do not cancel running tasks. A synchronous transform or task throw rejects
|
|
184
|
+
* the combined task and skips later inputs. Array holes skip transform and produce undefined.
|
|
185
|
+
*
|
|
186
|
+
* @template A, B
|
|
187
|
+
* @param {(value: A) => Task<B>} transform - Creates a task from each value, without an index.
|
|
188
|
+
* @returns {(values: ReadonlyArray<A>) => Task<(B | undefined)[]>}
|
|
189
|
+
* A function collecting transformed tasks.
|
|
190
|
+
* @example
|
|
191
|
+
* const healAll = traverse(health => of(health + 1))([2, 3]);
|
|
192
|
+
* await healAll(); // [3, 4]
|
|
193
|
+
*/
|
|
194
|
+
export const traverse = (transform) => (values) => async () => {
|
|
195
|
+
const promises = values.map((value) => {
|
|
196
|
+
const task = transform(value);
|
|
197
|
+
return task();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
return Promise.all(promises);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Awaits cleanup after the source succeeds, rejects, or throws synchronously.
|
|
206
|
+
* Repeats the source and cleanup on each execution. Successful cleanup preserves
|
|
207
|
+
* the original outcome. Cleanup failure after source success rejects with that
|
|
208
|
+
* failure. If both fail, rejects with an AggregateError whose errors array holds
|
|
209
|
+
* [sourceError, cleanupError].
|
|
210
|
+
*
|
|
211
|
+
* @param {() => unknown} cleanup - Cleanup to run and await without arguments.
|
|
212
|
+
* @returns {<A>(task: Task<A>) => Task<A>} A function attaching cleanup.
|
|
213
|
+
* @example
|
|
214
|
+
* const events = [];
|
|
215
|
+
* const loadHero = defer(() => { events.push("loaded"); return "Link"; });
|
|
216
|
+
* const hero = withCleanup(() => { events.push("cleaned"); })(loadHero);
|
|
217
|
+
* await hero(); // "Link"
|
|
218
|
+
* events; // ["loaded", "cleaned"]
|
|
219
|
+
*/
|
|
220
|
+
export const withCleanup = (cleanup) => (task) => async () => {
|
|
221
|
+
let result;
|
|
222
|
+
|
|
223
|
+
let taskError;
|
|
224
|
+
let taskFailed = false;
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
result = await task();
|
|
228
|
+
} catch (error) {
|
|
229
|
+
taskError = error;
|
|
230
|
+
taskFailed = true;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
await cleanup();
|
|
235
|
+
} catch (error) {
|
|
236
|
+
throw taskFailed
|
|
237
|
+
? new AggregateError([taskError, error])
|
|
238
|
+
: error;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (taskFailed) {
|
|
242
|
+
throw taskError;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return result;
|
|
246
|
+
}
|