unthrown 4.3.0 → 5.0.0-beta.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 +5 -4
- package/dist/index.cjs +148 -129
- package/dist/index.d.cts +211 -279
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +211 -279
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +135 -128
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ pnpm add unthrown
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import { fromPromise, TaggedError } from "unthrown";
|
|
14
|
+
import { fromPromise, P, TaggedError } from "unthrown";
|
|
15
15
|
|
|
16
16
|
class NotFound extends TaggedError("NotFound") {} // our modeled domain failure
|
|
17
17
|
class NotFoundError extends Error {} // what `fetchUser` rejects with on a 404
|
|
@@ -22,7 +22,7 @@ const user = fromPromise(fetchUser(id), (cause, defect) =>
|
|
|
22
22
|
|
|
23
23
|
const status = await user.match({
|
|
24
24
|
ok: () => 200,
|
|
25
|
-
err: () => 404,
|
|
25
|
+
err: (matcher) => matcher.with(P._, () => 404), // `err` takes the exhaustive matcher
|
|
26
26
|
defect: () => 500,
|
|
27
27
|
});
|
|
28
28
|
```
|
|
@@ -32,8 +32,9 @@ const status = await user.match({
|
|
|
32
32
|
observable only via `match` / `recoverDefect`.
|
|
33
33
|
- **Qualification at every boundary** — `fromPromise` / `fromThrowable` force you
|
|
34
34
|
to triage each failure into a modeled error or a defect.
|
|
35
|
-
- **Tagged errors** — `TaggedError(tag)` +
|
|
36
|
-
|
|
35
|
+
- **Tagged errors** — `TaggedError(tag)` + `tag(t)`, folded exhaustively through
|
|
36
|
+
`match`'s ts-pattern error matcher.
|
|
37
|
+
- One tiny runtime dependency (`ts-pattern`), ESM-first, dual CJS/ESM.
|
|
37
38
|
|
|
38
39
|
See the [full documentation](https://btravstack.github.io/unthrown/) for the guide
|
|
39
40
|
and complete API.
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,35 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let ts_pattern = require("ts-pattern");
|
|
3
|
+
//#region src/defect.ts
|
|
4
|
+
const DEFECT = Symbol("unthrown/Defect");
|
|
5
|
+
/**
|
|
6
|
+
* Wrap a cause as a `Defect` marker — the value returned from a `qualify`
|
|
7
|
+
* function when a failure is **not** a modeled domain error. The boundary
|
|
8
|
+
* (`fromPromise` / `fromThrowable`) passes this in as `qualify`'s second
|
|
9
|
+
* argument, so domain code never imports it.
|
|
10
|
+
*
|
|
11
|
+
* @param cause - the original thrown/rejected value.
|
|
12
|
+
* @returns an opaque Defect marker carrying `cause`.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
function defect(cause) {
|
|
17
|
+
return {
|
|
18
|
+
[DEFECT]: true,
|
|
19
|
+
cause
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Internal guard for the qualify-time marker. Distinct from the public
|
|
24
|
+
* {@link isDefect} state guard — this one narrows the `E | Defect` union a
|
|
25
|
+
* `qualify` function returns, not a `Result`.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
function isDefectMarker(x) {
|
|
30
|
+
return typeof x === "object" && x !== null && x[DEFECT] === true;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
2
33
|
//#region src/core.ts
|
|
3
34
|
/**
|
|
4
35
|
* Thrown by a {@link Result}'s `get` / `getErr` when the assertion is
|
|
@@ -6,12 +37,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
6
37
|
* `Ok`.
|
|
7
38
|
*
|
|
8
39
|
* @remarks
|
|
9
|
-
* The offending value is exposed two ways: the typed {@link
|
|
40
|
+
* The offending value is exposed two ways: the typed {@link GetError.error}
|
|
10
41
|
* property for programmatic access, and the standard `Error.cause` for the
|
|
11
42
|
* runtime and devtools to chain — when `E` is an `Error` (e.g. a `TaggedError`)
|
|
12
43
|
* its original stack is printed under "caused by".
|
|
13
44
|
*
|
|
14
|
-
* A `Defect` is never wrapped in
|
|
45
|
+
* A `Defect` is never wrapped in a `GetError`: its original cause is
|
|
15
46
|
* re-thrown (with its original stack) instead.
|
|
16
47
|
*
|
|
17
48
|
* `get()` and `getErr()` are type-gated (`this: Result<T, never>` /
|
|
@@ -19,19 +50,19 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
19
50
|
* unreachable through well-typed code — it remains only as a defensive guard
|
|
20
51
|
* against unsound runtime misuse (e.g. an `as` cast past the gate).
|
|
21
52
|
*
|
|
22
|
-
* @typeParam E - the type of the {@link
|
|
53
|
+
* @typeParam E - the type of the {@link GetError.error} it carries.
|
|
23
54
|
*
|
|
24
55
|
* @category Errors
|
|
25
56
|
*/
|
|
26
|
-
var
|
|
57
|
+
var GetError = class extends Error {
|
|
27
58
|
/**
|
|
28
59
|
* The offending value: the `Err` error for `get()`, or the `Ok` value for
|
|
29
60
|
* `getErr()`.
|
|
30
61
|
*/
|
|
31
62
|
error;
|
|
32
63
|
constructor(error) {
|
|
33
|
-
super("unthrown: called
|
|
34
|
-
this.name = "
|
|
64
|
+
super("unthrown: get() / getErr() called on a non-matching Result variant", { cause: error });
|
|
65
|
+
this.name = "GetError";
|
|
35
66
|
this.error = error;
|
|
36
67
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
37
68
|
}
|
|
@@ -113,7 +144,9 @@ var Res = class {
|
|
|
113
144
|
mapErr(f) {
|
|
114
145
|
if (this.tag !== "Err") return passThrough(this);
|
|
115
146
|
try {
|
|
116
|
-
|
|
147
|
+
const out = runMatch(f, this.error);
|
|
148
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
149
|
+
return errRes(out);
|
|
117
150
|
} catch (cause) {
|
|
118
151
|
return defectRes(cause);
|
|
119
152
|
}
|
|
@@ -121,31 +154,27 @@ var Res = class {
|
|
|
121
154
|
flatMapErr(f) {
|
|
122
155
|
if (this.tag !== "Err") return passThrough(this);
|
|
123
156
|
try {
|
|
124
|
-
|
|
157
|
+
const out = runMatch(f, this.error);
|
|
158
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
159
|
+
return out;
|
|
125
160
|
} catch (cause) {
|
|
126
161
|
return defectRes(cause);
|
|
127
162
|
}
|
|
128
163
|
}
|
|
129
|
-
/** @deprecated Use {@link Res.flatMapErr}. */
|
|
130
|
-
orElse(f) {
|
|
131
|
-
return this.flatMapErr(f);
|
|
132
|
-
}
|
|
133
164
|
recoverErr(f) {
|
|
134
165
|
if (this.tag !== "Err") return passThrough(this);
|
|
135
166
|
try {
|
|
136
|
-
|
|
167
|
+
const out = runMatch(f, this.error);
|
|
168
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
169
|
+
return okRes(out);
|
|
137
170
|
} catch (cause) {
|
|
138
171
|
return defectRes(cause);
|
|
139
172
|
}
|
|
140
173
|
}
|
|
141
|
-
/** @deprecated Use {@link Res.recoverErr}. */
|
|
142
|
-
recover(f) {
|
|
143
|
-
return this.recoverErr(f);
|
|
144
|
-
}
|
|
145
174
|
tapErr(f) {
|
|
146
175
|
if (this.tag !== "Err") return this;
|
|
147
176
|
try {
|
|
148
|
-
f
|
|
177
|
+
runMatch(f, this.error);
|
|
149
178
|
return this;
|
|
150
179
|
} catch (cause) {
|
|
151
180
|
return observerThrowToDefect(cause, this.error);
|
|
@@ -154,7 +183,7 @@ var Res = class {
|
|
|
154
183
|
flatTapErr(f) {
|
|
155
184
|
if (this.tag !== "Err") return this;
|
|
156
185
|
try {
|
|
157
|
-
const r = f
|
|
186
|
+
const r = runMatch(f, this.error);
|
|
158
187
|
return r.tag === "Ok" ? this : passThrough(r);
|
|
159
188
|
} catch (cause) {
|
|
160
189
|
return observerThrowToDefect(cause, this.error);
|
|
@@ -189,50 +218,34 @@ var Res = class {
|
|
|
189
218
|
match(cases) {
|
|
190
219
|
switch (this.tag) {
|
|
191
220
|
case "Ok": return cases.ok(this.value);
|
|
192
|
-
case "Err": return cases.err(this.error);
|
|
221
|
+
case "Err": return cases.err((0, ts_pattern.match)(this.error)).run();
|
|
193
222
|
case "Defect": return cases.defect(this.cause);
|
|
194
223
|
}
|
|
195
224
|
}
|
|
196
225
|
get() {
|
|
197
226
|
switch (this.tag) {
|
|
198
227
|
case "Ok": return this.value;
|
|
199
|
-
case "Err": throw new
|
|
228
|
+
case "Err": throw new GetError(this.error);
|
|
200
229
|
case "Defect": throw this.cause;
|
|
201
230
|
}
|
|
202
231
|
}
|
|
203
|
-
/** @deprecated Use {@link Res.get}. */
|
|
204
|
-
unwrap() {
|
|
205
|
-
return this.get();
|
|
206
|
-
}
|
|
207
232
|
getErr() {
|
|
208
233
|
switch (this.tag) {
|
|
209
234
|
case "Err": return this.error;
|
|
210
|
-
case "Ok": throw new
|
|
235
|
+
case "Ok": throw new GetError(this.value);
|
|
211
236
|
case "Defect": throw this.cause;
|
|
212
237
|
}
|
|
213
238
|
}
|
|
214
|
-
/** @deprecated Use {@link Res.getErr}. */
|
|
215
|
-
unwrapErr() {
|
|
216
|
-
return this.getErr();
|
|
217
|
-
}
|
|
218
239
|
getOr(fallback) {
|
|
219
240
|
if (this.tag === "Ok") return this.value;
|
|
220
241
|
if (this.tag === "Defect") throw this.cause;
|
|
221
242
|
return fallback;
|
|
222
243
|
}
|
|
223
|
-
/** @deprecated Use {@link Res.getOr}. */
|
|
224
|
-
unwrapOr(fallback) {
|
|
225
|
-
return this.getOr(fallback);
|
|
226
|
-
}
|
|
227
244
|
getOrElse(f) {
|
|
228
245
|
if (this.tag === "Ok") return this.value;
|
|
229
246
|
if (this.tag === "Defect") throw this.cause;
|
|
230
247
|
return f(this.error);
|
|
231
248
|
}
|
|
232
|
-
/** @deprecated Use {@link Res.getOrElse}. */
|
|
233
|
-
unwrapOrElse(f) {
|
|
234
|
-
return this.getOrElse(f);
|
|
235
|
-
}
|
|
236
249
|
getOrNull() {
|
|
237
250
|
if (this.tag === "Ok") return this.value;
|
|
238
251
|
if (this.tag === "Defect") throw this.cause;
|
|
@@ -337,6 +350,19 @@ function passThrough(self) {
|
|
|
337
350
|
return self;
|
|
338
351
|
}
|
|
339
352
|
/**
|
|
353
|
+
* Drive an error-combinator callback: build `match(error)`, hand it (plus the
|
|
354
|
+
* injected `defect`) to the callback, and `.run()` the returned exhaustive
|
|
355
|
+
* builder to its output. `.run()` executes `.exhaustive()` — type-forced
|
|
356
|
+
* exhaustive, so it always matches for well-typed callers; a value that slips
|
|
357
|
+
* through the types (a widened cast, a JS caller) throws `NonExhaustiveError`,
|
|
358
|
+
* which the caller's `try/catch` turns into a `Defect` — an unmodeled failure.
|
|
359
|
+
*
|
|
360
|
+
* @internal
|
|
361
|
+
*/
|
|
362
|
+
function runMatch(f, error) {
|
|
363
|
+
return f((0, ts_pattern.match)(error), defect).run();
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
340
366
|
* A throw inside a *failure observer* (`tapErr` / `tapDefect` / `flatTapErr`)
|
|
341
367
|
* must not destroy the failure being observed — that is the exact place (e.g. a
|
|
342
368
|
* failing error-logger) where losing the underlying failure hurts most. The
|
|
@@ -465,7 +491,9 @@ var AsyncRes = class AsyncRes {
|
|
|
465
491
|
return new AsyncRes(this.promise.then((r) => {
|
|
466
492
|
if (r.tag !== "Err") return passThrough(r);
|
|
467
493
|
try {
|
|
468
|
-
|
|
494
|
+
const out = runMatch(f, r.error);
|
|
495
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
496
|
+
return errRes(out);
|
|
469
497
|
} catch (cause) {
|
|
470
498
|
return defectRes(cause);
|
|
471
499
|
}
|
|
@@ -475,35 +503,31 @@ var AsyncRes = class AsyncRes {
|
|
|
475
503
|
return new AsyncRes(this.promise.then(async (r) => {
|
|
476
504
|
if (r.tag !== "Err") return passThrough(r);
|
|
477
505
|
try {
|
|
478
|
-
|
|
506
|
+
const out = runMatch(f, r.error);
|
|
507
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
508
|
+
return await out;
|
|
479
509
|
} catch (cause) {
|
|
480
510
|
return defectRes(cause);
|
|
481
511
|
}
|
|
482
512
|
}));
|
|
483
513
|
}
|
|
484
|
-
/** @deprecated Use {@link AsyncRes.flatMapErr}. */
|
|
485
|
-
orElse(f) {
|
|
486
|
-
return this.flatMapErr(f);
|
|
487
|
-
}
|
|
488
514
|
recoverErr(f) {
|
|
489
515
|
return new AsyncRes(this.promise.then((r) => {
|
|
490
516
|
if (r.tag !== "Err") return passThrough(r);
|
|
491
517
|
try {
|
|
492
|
-
|
|
518
|
+
const out = runMatch(f, r.error);
|
|
519
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
520
|
+
return okRes(out);
|
|
493
521
|
} catch (cause) {
|
|
494
522
|
return defectRes(cause);
|
|
495
523
|
}
|
|
496
524
|
}));
|
|
497
525
|
}
|
|
498
|
-
/** @deprecated Use {@link AsyncRes.recoverErr}. */
|
|
499
|
-
recover(f) {
|
|
500
|
-
return this.recoverErr(f);
|
|
501
|
-
}
|
|
502
526
|
tapErr(f) {
|
|
503
527
|
return new AsyncRes(this.promise.then((r) => {
|
|
504
528
|
if (r.tag !== "Err") return r;
|
|
505
529
|
try {
|
|
506
|
-
f
|
|
530
|
+
runMatch(f, r.error);
|
|
507
531
|
return r;
|
|
508
532
|
} catch (cause) {
|
|
509
533
|
return observerThrowToDefect(cause, r.error);
|
|
@@ -514,7 +538,7 @@ var AsyncRes = class AsyncRes {
|
|
|
514
538
|
return new AsyncRes(this.promise.then(async (r) => {
|
|
515
539
|
if (r.tag !== "Err") return passThrough(r);
|
|
516
540
|
try {
|
|
517
|
-
const inner = await f
|
|
541
|
+
const inner = await runMatch(f, r.error);
|
|
518
542
|
return inner.tag === "Ok" ? passThrough(r) : passThrough(inner);
|
|
519
543
|
} catch (cause) {
|
|
520
544
|
return observerThrowToDefect(cause, r.error);
|
|
@@ -559,31 +583,15 @@ var AsyncRes = class AsyncRes {
|
|
|
559
583
|
get() {
|
|
560
584
|
return this.promise.then((r) => r.get());
|
|
561
585
|
}
|
|
562
|
-
/** @deprecated Use {@link AsyncRes.get}. */
|
|
563
|
-
unwrap() {
|
|
564
|
-
return this.get();
|
|
565
|
-
}
|
|
566
586
|
getErr() {
|
|
567
587
|
return this.promise.then((r) => r.getErr());
|
|
568
588
|
}
|
|
569
|
-
/** @deprecated Use {@link AsyncRes.getErr}. */
|
|
570
|
-
unwrapErr() {
|
|
571
|
-
return this.getErr();
|
|
572
|
-
}
|
|
573
589
|
getOr(fallback) {
|
|
574
590
|
return this.promise.then((r) => r.getOr(fallback));
|
|
575
591
|
}
|
|
576
|
-
/** @deprecated Use {@link AsyncRes.getOr}. */
|
|
577
|
-
unwrapOr(fallback) {
|
|
578
|
-
return this.getOr(fallback);
|
|
579
|
-
}
|
|
580
592
|
getOrElse(f) {
|
|
581
593
|
return this.promise.then((r) => r.getOrElse(f));
|
|
582
594
|
}
|
|
583
|
-
/** @deprecated Use {@link AsyncRes.getOrElse}. */
|
|
584
|
-
unwrapOrElse(f) {
|
|
585
|
-
return this.getOrElse(f);
|
|
586
|
-
}
|
|
587
595
|
getOrNull() {
|
|
588
596
|
return this.promise.then((r) => r.getOrNull());
|
|
589
597
|
}
|
|
@@ -760,36 +768,6 @@ function Do() {
|
|
|
760
768
|
return Ok({});
|
|
761
769
|
}
|
|
762
770
|
//#endregion
|
|
763
|
-
//#region src/defect.ts
|
|
764
|
-
const DEFECT = Symbol("unthrown/Defect");
|
|
765
|
-
/**
|
|
766
|
-
* Wrap a cause as a `Defect` marker — the value returned from a `qualify`
|
|
767
|
-
* function when a failure is **not** a modeled domain error. The boundary
|
|
768
|
-
* (`fromPromise` / `fromThrowable`) passes this in as `qualify`'s second
|
|
769
|
-
* argument, so domain code never imports it.
|
|
770
|
-
*
|
|
771
|
-
* @param cause - the original thrown/rejected value.
|
|
772
|
-
* @returns an opaque Defect marker carrying `cause`.
|
|
773
|
-
*
|
|
774
|
-
* @internal
|
|
775
|
-
*/
|
|
776
|
-
function defect(cause) {
|
|
777
|
-
return {
|
|
778
|
-
[DEFECT]: true,
|
|
779
|
-
cause
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* Internal guard for the qualify-time marker. Distinct from the public
|
|
784
|
-
* {@link isDefect} state guard — this one narrows the `E | Defect` union a
|
|
785
|
-
* `qualify` function returns, not a `Result`.
|
|
786
|
-
*
|
|
787
|
-
* @internal
|
|
788
|
-
*/
|
|
789
|
-
function isDefectMarker(x) {
|
|
790
|
-
return typeof x === "object" && x !== null && x[DEFECT] === true;
|
|
791
|
-
}
|
|
792
|
-
//#endregion
|
|
793
771
|
//#region src/interop.ts
|
|
794
772
|
/**
|
|
795
773
|
* Bridge a nullable value into a {@link Result}: absence becomes a **modeled**
|
|
@@ -991,15 +969,25 @@ function qualifyToResult(cause, qualify) {
|
|
|
991
969
|
*
|
|
992
970
|
* @internal
|
|
993
971
|
*/
|
|
972
|
+
/** The Defect minted for an out-of-contract non-`Result` element in an aggregate. */
|
|
973
|
+
function nonResultDefect() {
|
|
974
|
+
return defectRes(/* @__PURE__ */ new TypeError("unthrown: aggregate received a non-Result element"));
|
|
975
|
+
}
|
|
994
976
|
function foldArray(results) {
|
|
995
977
|
let firstErr;
|
|
996
978
|
let firstDefect;
|
|
997
979
|
const values = [];
|
|
998
|
-
for (const r of results)
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
980
|
+
for (const r of results) {
|
|
981
|
+
if (!isResult(r)) {
|
|
982
|
+
firstDefect ??= nonResultDefect();
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
if (r.tag === "Defect") {
|
|
986
|
+
firstDefect ??= r;
|
|
987
|
+
break;
|
|
988
|
+
} else if (r.tag === "Err") firstErr ??= r;
|
|
989
|
+
else values.push(r.value);
|
|
990
|
+
}
|
|
1003
991
|
return firstDefect ?? firstErr ?? Ok(values);
|
|
1004
992
|
}
|
|
1005
993
|
/**
|
|
@@ -1013,16 +1001,22 @@ function foldRecord(results) {
|
|
|
1013
1001
|
let firstErr;
|
|
1014
1002
|
let firstDefect;
|
|
1015
1003
|
const values = {};
|
|
1016
|
-
for (const [key, r] of Object.entries(results))
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1004
|
+
for (const [key, r] of Object.entries(results)) {
|
|
1005
|
+
if (!isResult(r)) {
|
|
1006
|
+
firstDefect ??= nonResultDefect();
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
if (r.tag === "Defect") {
|
|
1010
|
+
firstDefect ??= r;
|
|
1011
|
+
break;
|
|
1012
|
+
} else if (r.tag === "Err") firstErr ??= r;
|
|
1013
|
+
else Object.defineProperty(values, key, {
|
|
1014
|
+
value: r.value,
|
|
1015
|
+
enumerable: true,
|
|
1016
|
+
writable: true,
|
|
1017
|
+
configurable: true
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1026
1020
|
return firstDefect ?? firstErr ?? Ok(values);
|
|
1027
1021
|
}
|
|
1028
1022
|
/**
|
|
@@ -1094,7 +1088,7 @@ function allFromDict(results) {
|
|
|
1094
1088
|
* ```
|
|
1095
1089
|
*/
|
|
1096
1090
|
function allAsync(results) {
|
|
1097
|
-
return new AsyncRes(Promise.all(results).then((resolved) => foldArray(resolved)));
|
|
1091
|
+
return new AsyncRes(Promise.all(results.map((r) => Promise.resolve(r).then((x) => x, (cause) => defectRes(cause)))).then((resolved) => foldArray(resolved)));
|
|
1098
1092
|
}
|
|
1099
1093
|
/**
|
|
1100
1094
|
* The asynchronous counterpart of {@link allFromDict}: combine a record of
|
|
@@ -1119,7 +1113,7 @@ function allAsync(results) {
|
|
|
1119
1113
|
*/
|
|
1120
1114
|
function allFromDictAsync(results) {
|
|
1121
1115
|
const entries = Object.entries(results);
|
|
1122
|
-
return new AsyncRes(Promise.all(entries.map(([, ar]) => ar)).then((resolved) => {
|
|
1116
|
+
return new AsyncRes(Promise.all(entries.map(([, ar]) => Promise.resolve(ar).then((x) => x, (cause) => defectRes(cause)))).then((resolved) => {
|
|
1123
1117
|
const byKey = Object.create(null);
|
|
1124
1118
|
entries.forEach(([key], i) => {
|
|
1125
1119
|
byKey[key] = resolved[i];
|
|
@@ -1223,8 +1217,10 @@ const AsyncResult = {
|
|
|
1223
1217
|
* rejected at compile time (and excluded from the instance type), so it can't
|
|
1224
1218
|
* shadow `Error.name`.
|
|
1225
1219
|
*
|
|
1226
|
-
* `_tag` is the discriminant
|
|
1227
|
-
*
|
|
1220
|
+
* `_tag` is the discriminant matched by {@link tag} in the error combinators
|
|
1221
|
+
* (`result.mapErr((matcher) => matcher.with(tag("NotFound"), …))`) and in
|
|
1222
|
+
* `match`; `Error.name` is the human-facing label in stack traces and logs. By
|
|
1223
|
+
* default they coincide, but
|
|
1228
1224
|
* they can be **decoupled** with `options.name` — so a tag can be namespaced for
|
|
1229
1225
|
* collision-safety (`"@my-lib/RetryableError"`) without that slash-prefixed
|
|
1230
1226
|
* string leaking into `Error.name`:
|
|
@@ -1273,28 +1269,45 @@ function TaggedError(tag, options) {
|
|
|
1273
1269
|
}
|
|
1274
1270
|
return TaggedErrorBase;
|
|
1275
1271
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1272
|
+
/**
|
|
1273
|
+
* A `ts-pattern` pattern matching any value whose `_tag` equals `value` — a
|
|
1274
|
+
* {@link TaggedError}, or any discriminated member. Equivalent to the object
|
|
1275
|
+
* pattern `{ _tag: value }`, but reads better inside an error-matching
|
|
1276
|
+
* combinator and narrows to the matching variant, payload included.
|
|
1277
|
+
*
|
|
1278
|
+
* @typeParam Tag - the string literal tag to match.
|
|
1279
|
+
* @param value - the `_tag` to match.
|
|
1280
|
+
*
|
|
1281
|
+
* @category Tagged errors
|
|
1282
|
+
*
|
|
1283
|
+
* @example
|
|
1284
|
+
* ```ts
|
|
1285
|
+
* result.mapErr((matcher) =>
|
|
1286
|
+
* matcher
|
|
1287
|
+
* .with(tag("NotFound"), () => new NotFoundException())
|
|
1288
|
+
* .with(tag("Conflict"), (e) => new ConflictException(e.key)),
|
|
1289
|
+
* );
|
|
1290
|
+
* ```
|
|
1291
|
+
*/
|
|
1292
|
+
function tag(value) {
|
|
1293
|
+
return { _tag: value };
|
|
1287
1294
|
}
|
|
1288
1295
|
//#endregion
|
|
1289
1296
|
exports.AsyncResult = AsyncResult;
|
|
1290
1297
|
exports.Do = Do;
|
|
1291
1298
|
exports.Err = Err;
|
|
1292
1299
|
exports.ErrAsync = ErrAsync;
|
|
1300
|
+
exports.GetError = GetError;
|
|
1293
1301
|
exports.Ok = Ok;
|
|
1294
1302
|
exports.OkAsync = OkAsync;
|
|
1303
|
+
Object.defineProperty(exports, "P", {
|
|
1304
|
+
enumerable: true,
|
|
1305
|
+
get: function() {
|
|
1306
|
+
return ts_pattern.P;
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1295
1309
|
exports.Result = Result;
|
|
1296
1310
|
exports.TaggedError = TaggedError;
|
|
1297
|
-
exports.UnwrapError = UnwrapError;
|
|
1298
1311
|
exports.all = all;
|
|
1299
1312
|
exports.allAsync = allAsync;
|
|
1300
1313
|
exports.allFromDict = allFromDict;
|
|
@@ -1308,4 +1321,10 @@ exports.isDefect = isDefect;
|
|
|
1308
1321
|
exports.isErr = isErr;
|
|
1309
1322
|
exports.isOk = isOk;
|
|
1310
1323
|
exports.isResult = isResult;
|
|
1311
|
-
exports
|
|
1324
|
+
Object.defineProperty(exports, "match", {
|
|
1325
|
+
enumerable: true,
|
|
1326
|
+
get: function() {
|
|
1327
|
+
return ts_pattern.match;
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
exports.tag = tag;
|