@aedge-io/grugway 0.0.0 → 0.2.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 +1 -1
- package/esm/async/task.js +334 -94
- package/esm/core/assert.js +1 -15
- package/esm/core/errors.js +9 -9
- package/esm/core/option.js +12 -19
- package/esm/core/result.js +12 -26
- package/esm/mod.js +3 -4
- package/package.json +5 -1
- package/types/async/task.d.ts +245 -41
- package/types/async/task.d.ts.map +1 -1
- package/types/core/assert.d.ts +0 -7
- package/types/core/assert.d.ts.map +1 -1
- package/types/core/errors.d.ts +6 -6
- package/types/core/errors.d.ts.map +1 -1
- package/types/core/option.d.ts +51 -60
- package/types/core/option.d.ts.map +1 -1
- package/types/core/result.d.ts +51 -63
- package/types/core/result.d.ts.map +1 -1
- package/types/core/type_utils.d.ts +1 -1
- package/types/mod.d.ts +4 -5
- package/types/mod.d.ts.map +1 -1
- package/esm/adapters/web/fetch/mod.js +0 -118
- package/esm/async/_internal.js +0 -156
- package/types/adapters/web/fetch/mod.d.ts +0 -98
- package/types/adapters/web/fetch/mod.d.ts.map +0 -1
- package/types/async/_internal.d.ts +0 -82
- package/types/async/_internal.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://codecov.io/github/aedge-io/grugway)
|
|
4
4
|
|
|
5
|
-
> Safe abstractions for fallible flows — for humans
|
|
5
|
+
> Safe abstractions for fallible flows — for humans, their clankers and foes.
|
|
6
6
|
|
|
7
7
|
This is a ~~fork~~ rework of an old, personal project
|
|
8
8
|
[eitherway](https://github.com/realpha/eitherway).
|
package/esm/async/task.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { asInfallible, Err, Ok } from "../core/result.js";
|
|
2
|
-
import { andEnsureTask, chainTaskFailure, chainTaskSuccess, cloneTask, inspectTaskFailure, inspectTaskSuccess, iterTask, mapTaskFailure, mapTaskSuccess, mapTaskSuccessOr, mapTaskSuccessOrElse, orEnsureTask, tapTask, unwrapTask, unwrapTaskOr, unwrapTaskOrElse, zipTask, } from "./_internal.js";
|
|
3
2
|
/**
|
|
4
3
|
* # Task<T, E>
|
|
5
4
|
*
|
|
6
|
-
* `Task<T, E>` is a composeable
|
|
5
|
+
* `Task<T, E>` is a composeable equivalent of `Promise<Result<T, E>>`
|
|
7
6
|
*
|
|
8
|
-
* It
|
|
9
|
-
*
|
|
7
|
+
* It never rejects, but always resolves. Either with an `Ok<T>` or an `Err<E>`
|
|
8
|
+
*
|
|
9
|
+
* It implements the full interface of `Promise<Result<T, E>>` and can be used
|
|
10
|
+
* as a drop-in replacement if desired.
|
|
10
11
|
*
|
|
11
12
|
* It supports almost the same API as {@linkcode Result} and allows for
|
|
12
13
|
* the same composition patterns as {@linkcode Result}
|
|
@@ -16,33 +17,35 @@ import { andEnsureTask, chainTaskFailure, chainTaskSuccess, cloneTask, inspectTa
|
|
|
16
17
|
*
|
|
17
18
|
* @category Task#Basic
|
|
18
19
|
*/
|
|
19
|
-
export class Task
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
export class Task {
|
|
21
|
+
#promise;
|
|
22
|
+
constructor(promise) {
|
|
23
|
+
this.#promise = promise;
|
|
22
24
|
}
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
26
|
+
* This is done to provide drop-in parity with native Promises, as some libraries
|
|
27
|
+
* are (IMO needlessly) invariant over `PromiseLike` types and test for `thenability`
|
|
28
|
+
* via `value instanceof Promise`
|
|
27
29
|
*/
|
|
30
|
+
static {
|
|
31
|
+
Object.setPrototypeOf(Task.prototype, Promise.prototype);
|
|
32
|
+
}
|
|
28
33
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```typescript
|
|
35
|
-
* import { Ok, Result, Task } from "../mod.ts";
|
|
36
|
-
*
|
|
37
|
-
* async function produceRes(): Promise<Result<number, TypeError>> {
|
|
38
|
-
* return Ok(42);
|
|
39
|
-
* }
|
|
40
|
-
*
|
|
41
|
-
* const task = Task.of(produceRes());
|
|
42
|
-
* ```
|
|
34
|
+
* =======================
|
|
35
|
+
* PROMISE INTERFACE
|
|
36
|
+
* =======================
|
|
43
37
|
*/
|
|
38
|
+
then(onfulfilled, onrejected) {
|
|
39
|
+
return this.#promise.then(onfulfilled, onrejected);
|
|
40
|
+
}
|
|
41
|
+
catch(onrejected) {
|
|
42
|
+
return this.#promise.catch(onrejected);
|
|
43
|
+
}
|
|
44
|
+
finally(onfinally) {
|
|
45
|
+
return this.#promise.finally(onfinally);
|
|
46
|
+
}
|
|
44
47
|
static of(value) {
|
|
45
|
-
return new Task(
|
|
48
|
+
return new Task(Promise.resolve(value));
|
|
46
49
|
}
|
|
47
50
|
/**
|
|
48
51
|
* Use this to create a `Task` which always succeeds with a value `<T>`
|
|
@@ -51,13 +54,13 @@ export class Task extends Promise {
|
|
|
51
54
|
*
|
|
52
55
|
* @example
|
|
53
56
|
* ```typescript
|
|
54
|
-
* import { Task } from "
|
|
57
|
+
* import { Task } from "@aedge-io/grugway";
|
|
55
58
|
*
|
|
56
59
|
* const task: Task<number, never> = Task.succeed(42);
|
|
57
60
|
* ```
|
|
58
61
|
*/
|
|
59
62
|
static succeed(value) {
|
|
60
|
-
return new Task(
|
|
63
|
+
return new Task(Promise.resolve(Ok(value)));
|
|
61
64
|
}
|
|
62
65
|
/**
|
|
63
66
|
* Use this to create a `Task` which always fails with a value `<E>`
|
|
@@ -66,13 +69,13 @@ export class Task extends Promise {
|
|
|
66
69
|
*
|
|
67
70
|
* @example
|
|
68
71
|
* ```typescript
|
|
69
|
-
* import { Task } from "
|
|
72
|
+
* import { Task } from "@aedge-io/grugway";
|
|
70
73
|
*
|
|
71
74
|
* const task: Task<never, number> = Task.fail(1);
|
|
72
75
|
* ```
|
|
73
76
|
*/
|
|
74
77
|
static fail(error) {
|
|
75
|
-
return new Task(
|
|
78
|
+
return new Task(Promise.resolve(Err(error)));
|
|
76
79
|
}
|
|
77
80
|
/**
|
|
78
81
|
* Use this to create a deferred `Task<T, E>` which will either succeed with
|
|
@@ -87,7 +90,7 @@ export class Task extends Promise {
|
|
|
87
90
|
*
|
|
88
91
|
* @example
|
|
89
92
|
* ```typescript
|
|
90
|
-
* import { Task } from "
|
|
93
|
+
* import { Task } from "@aedge-io/grugway";
|
|
91
94
|
*
|
|
92
95
|
* class TimeoutError extends Error {}
|
|
93
96
|
*
|
|
@@ -106,9 +109,10 @@ export class Task extends Promise {
|
|
|
106
109
|
*/
|
|
107
110
|
static deferred() {
|
|
108
111
|
let resolveBinding;
|
|
109
|
-
const
|
|
112
|
+
const promise = new Promise((resolve) => {
|
|
110
113
|
resolveBinding = resolve;
|
|
111
114
|
});
|
|
115
|
+
const task = new Task(promise);
|
|
112
116
|
const succeed = (value) => resolveBinding(Ok(value));
|
|
113
117
|
const fail = (error) => resolveBinding(Err(error));
|
|
114
118
|
return { task, succeed, fail };
|
|
@@ -125,7 +129,7 @@ export class Task extends Promise {
|
|
|
125
129
|
*
|
|
126
130
|
* @example
|
|
127
131
|
* ```typescript
|
|
128
|
-
* import { Ok, Result, Task } from "
|
|
132
|
+
* import { Ok, Result, Task } from "@aedge-io/grugway";
|
|
129
133
|
*
|
|
130
134
|
* async function produceRes(): Promise<Result<number, TypeError>> {
|
|
131
135
|
* return Ok(42);
|
|
@@ -137,7 +141,7 @@ export class Task extends Promise {
|
|
|
137
141
|
static from(fn) {
|
|
138
142
|
const p = new Promise((resolve) => resolve(fn()))
|
|
139
143
|
.catch(asInfallible);
|
|
140
|
-
return new Task(
|
|
144
|
+
return new Task(p);
|
|
141
145
|
}
|
|
142
146
|
/**
|
|
143
147
|
* Use this to create a `Task<T, E>` from a `Promise<T>`.
|
|
@@ -152,7 +156,7 @@ export class Task extends Promise {
|
|
|
152
156
|
*
|
|
153
157
|
* @example
|
|
154
158
|
* ```typescript
|
|
155
|
-
* import { asInfallible, Task } from "
|
|
159
|
+
* import { asInfallible, Task } from "@aedge-io/grugway";
|
|
156
160
|
*
|
|
157
161
|
* const willBeString = Promise.resolve("42");
|
|
158
162
|
*
|
|
@@ -165,7 +169,7 @@ export class Task extends Promise {
|
|
|
165
169
|
* ```
|
|
166
170
|
*/
|
|
167
171
|
static fromPromise(promise, errorMapFn) {
|
|
168
|
-
return Task.
|
|
172
|
+
return new Task(promise.then((v) => Ok(v), (e) => Err(errorMapFn(e))));
|
|
169
173
|
}
|
|
170
174
|
/**
|
|
171
175
|
* Use this to construct a `Task<T, E>` from the return value of a fallible
|
|
@@ -175,7 +179,7 @@ export class Task extends Promise {
|
|
|
175
179
|
*
|
|
176
180
|
* @example
|
|
177
181
|
* ```typescript
|
|
178
|
-
* import { Task } from "
|
|
182
|
+
* import { Task } from "@aedge-io/grugway";
|
|
179
183
|
*
|
|
180
184
|
* async function rand(): Promise<number> {
|
|
181
185
|
* throw new TypeError("Oops");
|
|
@@ -195,7 +199,7 @@ export class Task extends Promise {
|
|
|
195
199
|
static fromFallible(fn, errorMapFn) {
|
|
196
200
|
const p = new Promise((resolve) => resolve(fn()))
|
|
197
201
|
.then((v) => Ok(v), (e) => Err(errorMapFn(e)));
|
|
198
|
-
return new Task(
|
|
202
|
+
return new Task(p);
|
|
199
203
|
}
|
|
200
204
|
/**
|
|
201
205
|
* Use this lift a function into a `Task` context, by composing the wrapped
|
|
@@ -210,7 +214,7 @@ export class Task extends Promise {
|
|
|
210
214
|
*
|
|
211
215
|
* @example
|
|
212
216
|
* ```
|
|
213
|
-
* import { Err, Ok, Result, Task } from "
|
|
217
|
+
* import { Err, Ok, Result, Task } from "@aedge-io/grugway";
|
|
214
218
|
*
|
|
215
219
|
* async function toSpecialString(s: string): Promise<string> {
|
|
216
220
|
* if (s.length % 3 === 0) return s;
|
|
@@ -231,7 +235,7 @@ export class Task extends Promise {
|
|
|
231
235
|
return function (...args) {
|
|
232
236
|
const p = new Promise((resolve) => resolve(fn(...args)))
|
|
233
237
|
.then((v) => ctor(v), (e) => Err(errorMapFn(e)));
|
|
234
|
-
return new Task(
|
|
238
|
+
return new Task(p);
|
|
235
239
|
};
|
|
236
240
|
}
|
|
237
241
|
/**
|
|
@@ -251,26 +255,175 @@ export class Task extends Promise {
|
|
|
251
255
|
id() {
|
|
252
256
|
return this;
|
|
253
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Use this to obtain a deep clone of `Task<T, E>`
|
|
260
|
+
*
|
|
261
|
+
* Under the hood, this uses the `structuredClone` algorithm
|
|
262
|
+
*
|
|
263
|
+
* @category Task#Basic
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* import { assert } from "@std/assert";
|
|
268
|
+
* import { Task } from "@aedge-io/grugway";
|
|
269
|
+
*
|
|
270
|
+
* const task = Task.succeed({ a: 1 });
|
|
271
|
+
* const cloned = task.clone();
|
|
272
|
+
*
|
|
273
|
+
* const res = await task;
|
|
274
|
+
* const clonedRes = await cloned;
|
|
275
|
+
*
|
|
276
|
+
* assert(res.unwrap() !== clonedRes.unwrap())
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
254
279
|
clone() {
|
|
255
|
-
return Task.
|
|
280
|
+
return new Task(this.#promise.then((res) => res.clone()));
|
|
256
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Use this to asynchronously map the encapsulated value `<T>` to `<T2>`
|
|
284
|
+
*
|
|
285
|
+
* In case of `Err<E>`, this method short-circuits.
|
|
286
|
+
* See {@linkcode Task#mapErr} for the opposite case.
|
|
287
|
+
*
|
|
288
|
+
* @category Task#Basic
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```typescript
|
|
292
|
+
* import { Task } from "@aedge-io/grugway";
|
|
293
|
+
*
|
|
294
|
+
* const task = Task.succeed(21).map((n) => n * 2);
|
|
295
|
+
* const res = await task; // Ok(42)
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
257
298
|
map(mapFn) {
|
|
258
|
-
return Task.
|
|
299
|
+
return new Task(this.#promise.then(async (res) => {
|
|
300
|
+
if (res.isErr())
|
|
301
|
+
return res;
|
|
302
|
+
return Ok(await mapFn(res.unwrap()));
|
|
303
|
+
}));
|
|
259
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Same as {@linkcode Task#map} but returns the provided `orValue` asynchronously
|
|
307
|
+
* as a fallback in case of `Err<E>`
|
|
308
|
+
*
|
|
309
|
+
* @category Task#Intermediate
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* import { Task } from "@aedge-io/grugway";
|
|
314
|
+
*
|
|
315
|
+
* const t1 = Task.succeed(5).mapOr((n) => n * 2, 0); // Task<number, never>
|
|
316
|
+
* const t2 = Task.fail("x").mapOr((n) => n * 2, 0); // Task<number, never>
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
260
319
|
mapOr(mapFn, orValue) {
|
|
261
|
-
return Task.
|
|
320
|
+
return new Task(this.#promise.then(async (res) => {
|
|
321
|
+
const mapped = res.isErr() ? await orValue : await mapFn(res.unwrap());
|
|
322
|
+
return Ok(mapped);
|
|
323
|
+
}));
|
|
262
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Same as {@linkcode Task#map} but applies `orFn` asynchronously to the
|
|
327
|
+
* error value in case of `Err<E>`
|
|
328
|
+
*
|
|
329
|
+
* Use this if the fallback value is expensive to produce.
|
|
330
|
+
*
|
|
331
|
+
* @category Task#Intermediate
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```typescript
|
|
335
|
+
* import { Task } from "@aedge-io/grugway";
|
|
336
|
+
*
|
|
337
|
+
* const t1 = Task.succeed(5).mapOrElse((n) => n * 2, (e) => -1); // Ok(10)
|
|
338
|
+
* const t2 = Task.fail("x").mapOrElse((n) => n * 2, (e) => -1); // Ok(-1)
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
263
341
|
mapOrElse(mapFn, orFn) {
|
|
264
|
-
return Task.
|
|
342
|
+
return new Task(this.#promise.then(async (res) => {
|
|
343
|
+
const mapped = res.isErr()
|
|
344
|
+
? await orFn(res.unwrap())
|
|
345
|
+
: await mapFn(res.unwrap());
|
|
346
|
+
return Ok(mapped);
|
|
347
|
+
}));
|
|
265
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Use this to asynchronously map the encapsulated error `<E>` to `<E2>`
|
|
351
|
+
*
|
|
352
|
+
* In case of `Ok<T>`, this method short-circuits.
|
|
353
|
+
* See {@linkcode Task#map} for the opposite case.
|
|
354
|
+
*
|
|
355
|
+
* @category Task#Basic
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```typescript
|
|
359
|
+
* import { Task } from "@aedge-io/grugway";
|
|
360
|
+
*
|
|
361
|
+
* const task = Task.fail(Error("oops"))
|
|
362
|
+
* .mapErr((e) => TypeError(e.message));
|
|
363
|
+
*
|
|
364
|
+
* const res = await task; // Err<TypeError>
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
266
367
|
mapErr(mapFn) {
|
|
267
|
-
return Task.
|
|
368
|
+
return new Task(this.#promise.then(async (res) => {
|
|
369
|
+
if (res.isOk())
|
|
370
|
+
return res;
|
|
371
|
+
return Err(await mapFn(res.unwrap()));
|
|
372
|
+
}));
|
|
268
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Use this to produce a new `Task<T2, E2>` from the encapsulated
|
|
376
|
+
* value `<T>`. Canonical `.flatMap()` or `.chain()` method.
|
|
377
|
+
*
|
|
378
|
+
* In case of `Err<E>`, this method short-circuits.
|
|
379
|
+
* See {@linkcode Task#orElse} for the opposite case.
|
|
380
|
+
*
|
|
381
|
+
* @category Task#Intermediate
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* import { Err, Ok, Task } from "@aedge-io/grugway";
|
|
386
|
+
*
|
|
387
|
+
* const safeParse = async (s: string) => {
|
|
388
|
+
* const n = Number(s);
|
|
389
|
+
* return Number.isNaN(n) ? Err(TypeError("NaN")) : Ok(n);
|
|
390
|
+
* };
|
|
391
|
+
*
|
|
392
|
+
* const t = Task.succeed("42").andThen(safeParse); // Task<number, TypeError>
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
269
395
|
andThen(thenFn) {
|
|
270
|
-
return Task.
|
|
396
|
+
return new Task(this.#promise.then((res) => {
|
|
397
|
+
if (res.isErr())
|
|
398
|
+
return res;
|
|
399
|
+
return thenFn(res.unwrap());
|
|
400
|
+
}));
|
|
271
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Use this to produce a new `Task<T2, E2>` from the encapsulated
|
|
404
|
+
* error `<E>`. Useful for recovery or error transformation.
|
|
405
|
+
*
|
|
406
|
+
* In case of `Ok<T>`, this method short-circuits.
|
|
407
|
+
* See {@linkcode Task#andThen} for the opposite case.
|
|
408
|
+
*
|
|
409
|
+
* @category Task#Intermediate
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```typescript
|
|
413
|
+
* import { Ok, Task } from "@aedge-io/grugway";
|
|
414
|
+
*
|
|
415
|
+
* const task = Task.fail(Error("oops"))
|
|
416
|
+
* .orElse((e) => Ok(e.message));
|
|
417
|
+
*
|
|
418
|
+
* const res = await task; // Ok<string>
|
|
419
|
+
* ```
|
|
420
|
+
*/
|
|
272
421
|
orElse(elseFn) {
|
|
273
|
-
return Task.
|
|
422
|
+
return new Task(this.#promise.then((res) => {
|
|
423
|
+
if (res.isOk())
|
|
424
|
+
return res;
|
|
425
|
+
return elseFn(res.unwrap());
|
|
426
|
+
}));
|
|
274
427
|
}
|
|
275
428
|
/**
|
|
276
429
|
* Use this to conditionally pass-through the encapsulated value `<T>`
|
|
@@ -297,7 +450,7 @@ export class Task extends Promise {
|
|
|
297
450
|
*
|
|
298
451
|
* @example
|
|
299
452
|
* ```typescript
|
|
300
|
-
* import { Task } from "
|
|
453
|
+
* import { Task } from "@aedge-io/grugway";
|
|
301
454
|
*
|
|
302
455
|
* function getPath(): Task<string, Error> { return Task.succeed("/home")};
|
|
303
456
|
* function isReadableDir(path: string): Task<void, TypeError> { return Task.succeed(undefined) };
|
|
@@ -311,7 +464,12 @@ export class Task extends Promise {
|
|
|
311
464
|
* ```
|
|
312
465
|
*/
|
|
313
466
|
andEnsure(ensureFn) {
|
|
314
|
-
return Task.
|
|
467
|
+
return new Task(this.#promise.then(async (original) => {
|
|
468
|
+
if (original.isErr())
|
|
469
|
+
return original;
|
|
470
|
+
const res = await ensureFn(original.unwrap());
|
|
471
|
+
return res.and(original);
|
|
472
|
+
}));
|
|
315
473
|
}
|
|
316
474
|
/**
|
|
317
475
|
* Use this to conditionally pass-through the encapsulated value `<E>`
|
|
@@ -338,7 +496,7 @@ export class Task extends Promise {
|
|
|
338
496
|
*
|
|
339
497
|
* @example
|
|
340
498
|
* ```typescript
|
|
341
|
-
* import { Task } from "
|
|
499
|
+
* import { Task } from "@aedge-io/grugway";
|
|
342
500
|
*
|
|
343
501
|
* function getConfig(): Task<string, RangeError> { return Task.succeed("secret")};
|
|
344
502
|
* function getFallback(err: RangeError): Task<string, Error> { return Task.succeed("default")};
|
|
@@ -352,31 +510,110 @@ export class Task extends Promise {
|
|
|
352
510
|
* ```
|
|
353
511
|
*/
|
|
354
512
|
orEnsure(ensureFn) {
|
|
355
|
-
return Task.
|
|
513
|
+
return new Task(this.#promise.then(async (original) => {
|
|
514
|
+
if (original.isOk())
|
|
515
|
+
return original;
|
|
516
|
+
const res = await ensureFn(original.unwrap());
|
|
517
|
+
return res.or(original);
|
|
518
|
+
}));
|
|
356
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Use this to asynchronously zip the encapsulated values of two `Ok`
|
|
522
|
+
* instances into a new `Task`
|
|
523
|
+
*
|
|
524
|
+
* If either side is `Err`, the respective `Err` is returned.
|
|
525
|
+
*
|
|
526
|
+
* |**LHS zip RHS** |**RHS: Ok<T2>**|**RHS: Err<E2>**|
|
|
527
|
+
* |:--------------:|:-------------:|:--------------:|
|
|
528
|
+
* | **LHS: Ok<T>** | Ok<[T, T2]> | Err<E2> |
|
|
529
|
+
* | **LHS: Err<E>**| Err<E> | Err<E> |
|
|
530
|
+
*
|
|
531
|
+
* @category Task#Advanced
|
|
532
|
+
*
|
|
533
|
+
* @example
|
|
534
|
+
* ```typescript
|
|
535
|
+
* import { Ok, Task } from "@aedge-io/grugway";
|
|
536
|
+
*
|
|
537
|
+
* const task = Task.succeed(1).zip(Task.of(Ok("two")));
|
|
538
|
+
* const res = await task; // Ok([1, "two"])
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
357
541
|
zip(rhs) {
|
|
358
|
-
return Task.
|
|
542
|
+
return new Task(this.#promise.then(async (res) => {
|
|
543
|
+
return res.zip(await rhs);
|
|
544
|
+
}));
|
|
359
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* Use this to perform asynchronous side-effects transparently.
|
|
548
|
+
*
|
|
549
|
+
* The `tapFn` receives a deep clone of the `Result<T, E>` to ensure
|
|
550
|
+
* the original value cannot be mutated.
|
|
551
|
+
*
|
|
552
|
+
* @category Task#Intermediate
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```typescript
|
|
556
|
+
* import { Task } from "@aedge-io/grugway";
|
|
557
|
+
*
|
|
558
|
+
* const task = Task.succeed(42)
|
|
559
|
+
* .tap((res) => console.log("got:", res.unwrap()));
|
|
560
|
+
* ```
|
|
561
|
+
*/
|
|
360
562
|
tap(tapFn) {
|
|
361
|
-
return Task.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
inspectErr(inspectFn) {
|
|
367
|
-
return Task.of(inspectTaskFailure(this, inspectFn));
|
|
563
|
+
return new Task(this.#promise.then(async (res) => {
|
|
564
|
+
await tapFn(res.clone());
|
|
565
|
+
return res;
|
|
566
|
+
}));
|
|
368
567
|
}
|
|
369
568
|
/**
|
|
370
|
-
*
|
|
569
|
+
* Use this to asynchronously inspect the encapsulated value `<T>`.
|
|
570
|
+
*
|
|
571
|
+
* Mainly used for debugging and logging.
|
|
572
|
+
*
|
|
573
|
+
* In case of `Err<E>`, this method short-circuits.
|
|
574
|
+
* See {@linkcode Task#inspectErr} for the opposite case.
|
|
575
|
+
*
|
|
576
|
+
* @category Task#Basic
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```typescript
|
|
580
|
+
* import { Task } from "@aedge-io/grugway";
|
|
581
|
+
*
|
|
582
|
+
* const task = Task.succeed(42)
|
|
583
|
+
* .inspect((n) => console.log("value:", n));
|
|
584
|
+
* ```
|
|
371
585
|
*/
|
|
372
|
-
|
|
373
|
-
return Task.
|
|
586
|
+
inspect(inspectFn) {
|
|
587
|
+
return new Task(this.#promise.then(async (res) => {
|
|
588
|
+
if (res.isOk())
|
|
589
|
+
await inspectFn(res.unwrap());
|
|
590
|
+
return res;
|
|
591
|
+
}));
|
|
374
592
|
}
|
|
375
593
|
/**
|
|
376
|
-
*
|
|
594
|
+
* Use this to asynchronously inspect the encapsulated error `<E>`.
|
|
595
|
+
*
|
|
596
|
+
* Mainly used for debugging and logging.
|
|
597
|
+
*
|
|
598
|
+
* In case of `Ok<T>`, this method short-circuits.
|
|
599
|
+
* See {@linkcode Task#inspect} for the opposite case.
|
|
600
|
+
*
|
|
601
|
+
* @category Task#Basic
|
|
602
|
+
*
|
|
603
|
+
* @example
|
|
604
|
+
* ```typescript
|
|
605
|
+
* import { Task } from "@aedge-io/grugway";
|
|
606
|
+
*
|
|
607
|
+
* const task = Task.fail(Error("oops"))
|
|
608
|
+
* .inspectErr((e) => console.error("error:", e.message));
|
|
609
|
+
* ```
|
|
377
610
|
*/
|
|
378
|
-
|
|
379
|
-
return Task.
|
|
611
|
+
inspectErr(inspectFn) {
|
|
612
|
+
return new Task(this.#promise.then(async (res) => {
|
|
613
|
+
if (res.isErr())
|
|
614
|
+
await inspectFn(res.unwrap());
|
|
615
|
+
return res;
|
|
616
|
+
}));
|
|
380
617
|
}
|
|
381
618
|
/**
|
|
382
619
|
* Use this to get the wrapped value out of an `Task<T, E>` instance
|
|
@@ -391,9 +628,8 @@ export class Task extends Promise {
|
|
|
391
628
|
*
|
|
392
629
|
* @example
|
|
393
630
|
* ```typescript
|
|
394
|
-
* import { assert } from "
|
|
395
|
-
* import { Result } from "
|
|
396
|
-
* import { Task } from "./task.ts";
|
|
631
|
+
* import { assert } from "@std/assert";
|
|
632
|
+
* import { Result, Task } from "@aedge-io/grugway";
|
|
397
633
|
*
|
|
398
634
|
* const ok = Result(42) as Result<number, string>;
|
|
399
635
|
* const task = Task.of(ok);
|
|
@@ -403,8 +639,8 @@ export class Task extends Promise {
|
|
|
403
639
|
* assert(union === 42);
|
|
404
640
|
* ```
|
|
405
641
|
*/
|
|
406
|
-
unwrap() {
|
|
407
|
-
return
|
|
642
|
+
async unwrap() {
|
|
643
|
+
return (await this.#promise).unwrap();
|
|
408
644
|
}
|
|
409
645
|
/**
|
|
410
646
|
* Same as {@linkcode Task#unwrap} but returns a default value in case the
|
|
@@ -414,9 +650,8 @@ export class Task extends Promise {
|
|
|
414
650
|
*
|
|
415
651
|
* @example
|
|
416
652
|
* ```typescript
|
|
417
|
-
* import { assert } from "
|
|
418
|
-
* import { Result } from "
|
|
419
|
-
* import { Task } from "./task.ts";
|
|
653
|
+
* import { assert } from "@std/assert";
|
|
654
|
+
* import { Result, Task } from "@aedge-io/grugway";
|
|
420
655
|
*
|
|
421
656
|
* const err = Result(Error()) as Result<number, Error>;
|
|
422
657
|
* const task = Task.of(err);
|
|
@@ -426,8 +661,11 @@ export class Task extends Promise {
|
|
|
426
661
|
* assert(union === "foo");
|
|
427
662
|
* ```
|
|
428
663
|
*/
|
|
429
|
-
unwrapOr(orValue) {
|
|
430
|
-
|
|
664
|
+
async unwrapOr(orValue) {
|
|
665
|
+
const res = await this.#promise;
|
|
666
|
+
if (res.isOk())
|
|
667
|
+
return res.unwrap();
|
|
668
|
+
return await orValue;
|
|
431
669
|
}
|
|
432
670
|
/**
|
|
433
671
|
* Same as {@linkcode Task#unwrap} but returns a fallback value, which can based
|
|
@@ -437,9 +675,8 @@ export class Task extends Promise {
|
|
|
437
675
|
*
|
|
438
676
|
* @example
|
|
439
677
|
* ```typescript
|
|
440
|
-
* import { assert } from "
|
|
441
|
-
* import { Result } from "
|
|
442
|
-
* import { Task } from "./task.ts";
|
|
678
|
+
* import { assert } from "@std/assert";
|
|
679
|
+
* import { Result, Task } from "@aedge-io/grugway";
|
|
443
680
|
*
|
|
444
681
|
* const err = Result(Error("foo")) as Result<number, Error>;
|
|
445
682
|
* const task = Task.of(err);
|
|
@@ -451,8 +688,11 @@ export class Task extends Promise {
|
|
|
451
688
|
* assert(union === "foo");
|
|
452
689
|
* ```
|
|
453
690
|
*/
|
|
454
|
-
unwrapOrElse(orFn) {
|
|
455
|
-
|
|
691
|
+
async unwrapOrElse(orFn) {
|
|
692
|
+
const res = await this.#promise;
|
|
693
|
+
if (res.isOk())
|
|
694
|
+
return res.unwrap();
|
|
695
|
+
return await orFn(res.unwrap());
|
|
456
696
|
}
|
|
457
697
|
/**
|
|
458
698
|
* Use this to obtain an async iterator of the encapsulated value `<T>`
|
|
@@ -463,8 +703,8 @@ export class Task extends Promise {
|
|
|
463
703
|
*
|
|
464
704
|
* @example
|
|
465
705
|
* ```typescript
|
|
466
|
-
* import { assert } from "
|
|
467
|
-
* import { Err, Ok, Result, Task } from "
|
|
706
|
+
* import { assert } from "@std/assert"
|
|
707
|
+
* import { Err, Ok, Result, Task } from "@aedge-io/grugway";
|
|
468
708
|
*
|
|
469
709
|
* const success = Task.succeed(42);
|
|
470
710
|
* const failure = Task.fail(Error());
|
|
@@ -498,8 +738,11 @@ export class Task extends Promise {
|
|
|
498
738
|
* main().then(() => console.log("Done"));
|
|
499
739
|
* ```
|
|
500
740
|
*/
|
|
501
|
-
iter() {
|
|
502
|
-
|
|
741
|
+
async *iter() {
|
|
742
|
+
const res = await this.#promise;
|
|
743
|
+
if (res.isErr())
|
|
744
|
+
return;
|
|
745
|
+
yield res.unwrap();
|
|
503
746
|
}
|
|
504
747
|
/**
|
|
505
748
|
* ============================
|
|
@@ -516,12 +759,12 @@ export class Task extends Promise {
|
|
|
516
759
|
*
|
|
517
760
|
* @example
|
|
518
761
|
* ```typescript
|
|
519
|
-
* import { assert } from "
|
|
520
|
-
* import { Task } from "
|
|
762
|
+
* import { assert } from "@std/assert";
|
|
763
|
+
* import { Task } from "@aedge-io/grugway"
|
|
521
764
|
*
|
|
522
765
|
* const tag = Task.succeed(42).toString();
|
|
523
766
|
*
|
|
524
|
-
* assert(tag === "[object
|
|
767
|
+
* assert(tag === "[object grugway.Task]");
|
|
525
768
|
* ```
|
|
526
769
|
*/
|
|
527
770
|
toString() {
|
|
@@ -541,22 +784,19 @@ export class Task extends Promise {
|
|
|
541
784
|
*
|
|
542
785
|
* @example
|
|
543
786
|
* ```typescript
|
|
544
|
-
* import { assert } from "
|
|
545
|
-
* import { Task } from "
|
|
787
|
+
* import { assert } from "@std/assert";
|
|
788
|
+
* import { Task } from "@aedge-io/grugway"
|
|
546
789
|
*
|
|
547
790
|
* const task = Task.succeed({ a: 1, b: 2 });
|
|
548
791
|
*
|
|
549
792
|
* const toString = Object.prototype.toString;
|
|
550
793
|
*
|
|
551
|
-
* assert(toString.call(task) === "[object
|
|
552
|
-
* assert(toString.call(Task) === "[object
|
|
794
|
+
* assert(toString.call(task) === "[object grugway.Task]");
|
|
795
|
+
* assert(toString.call(Task) === "[object grugway.Task]");
|
|
553
796
|
* ```
|
|
554
797
|
*/
|
|
555
798
|
get [Symbol.toStringTag]() {
|
|
556
|
-
return "
|
|
557
|
-
}
|
|
558
|
-
static get [Symbol.toStringTag]() {
|
|
559
|
-
return "aetherway.Task";
|
|
799
|
+
return "grugway.Task";
|
|
560
800
|
}
|
|
561
801
|
/**
|
|
562
802
|
* In case of success AND that the encapsulated value `<T>` implements the
|