unthrown 4.2.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 +168 -129
- package/dist/index.d.cts +269 -279
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +269 -279
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +155 -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);
|
|
@@ -177,53 +206,46 @@ var Res = class {
|
|
|
177
206
|
return observerThrowToDefect(cause, this.cause);
|
|
178
207
|
}
|
|
179
208
|
}
|
|
209
|
+
tapFailure(f) {
|
|
210
|
+
if (this.tag === "Ok") return this;
|
|
211
|
+
try {
|
|
212
|
+
f(this);
|
|
213
|
+
return this;
|
|
214
|
+
} catch (cause) {
|
|
215
|
+
return observerThrowToDefect(cause, this.tag === "Err" ? this.error : this.cause);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
180
218
|
match(cases) {
|
|
181
219
|
switch (this.tag) {
|
|
182
220
|
case "Ok": return cases.ok(this.value);
|
|
183
|
-
case "Err": return cases.err(this.error);
|
|
221
|
+
case "Err": return cases.err((0, ts_pattern.match)(this.error)).run();
|
|
184
222
|
case "Defect": return cases.defect(this.cause);
|
|
185
223
|
}
|
|
186
224
|
}
|
|
187
225
|
get() {
|
|
188
226
|
switch (this.tag) {
|
|
189
227
|
case "Ok": return this.value;
|
|
190
|
-
case "Err": throw new
|
|
228
|
+
case "Err": throw new GetError(this.error);
|
|
191
229
|
case "Defect": throw this.cause;
|
|
192
230
|
}
|
|
193
231
|
}
|
|
194
|
-
/** @deprecated Use {@link Res.get}. */
|
|
195
|
-
unwrap() {
|
|
196
|
-
return this.get();
|
|
197
|
-
}
|
|
198
232
|
getErr() {
|
|
199
233
|
switch (this.tag) {
|
|
200
234
|
case "Err": return this.error;
|
|
201
|
-
case "Ok": throw new
|
|
235
|
+
case "Ok": throw new GetError(this.value);
|
|
202
236
|
case "Defect": throw this.cause;
|
|
203
237
|
}
|
|
204
238
|
}
|
|
205
|
-
/** @deprecated Use {@link Res.getErr}. */
|
|
206
|
-
unwrapErr() {
|
|
207
|
-
return this.getErr();
|
|
208
|
-
}
|
|
209
239
|
getOr(fallback) {
|
|
210
240
|
if (this.tag === "Ok") return this.value;
|
|
211
241
|
if (this.tag === "Defect") throw this.cause;
|
|
212
242
|
return fallback;
|
|
213
243
|
}
|
|
214
|
-
/** @deprecated Use {@link Res.getOr}. */
|
|
215
|
-
unwrapOr(fallback) {
|
|
216
|
-
return this.getOr(fallback);
|
|
217
|
-
}
|
|
218
244
|
getOrElse(f) {
|
|
219
245
|
if (this.tag === "Ok") return this.value;
|
|
220
246
|
if (this.tag === "Defect") throw this.cause;
|
|
221
247
|
return f(this.error);
|
|
222
248
|
}
|
|
223
|
-
/** @deprecated Use {@link Res.getOrElse}. */
|
|
224
|
-
unwrapOrElse(f) {
|
|
225
|
-
return this.getOrElse(f);
|
|
226
|
-
}
|
|
227
249
|
getOrNull() {
|
|
228
250
|
if (this.tag === "Ok") return this.value;
|
|
229
251
|
if (this.tag === "Defect") throw this.cause;
|
|
@@ -328,6 +350,19 @@ function passThrough(self) {
|
|
|
328
350
|
return self;
|
|
329
351
|
}
|
|
330
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
|
+
/**
|
|
331
366
|
* A throw inside a *failure observer* (`tapErr` / `tapDefect` / `flatTapErr`)
|
|
332
367
|
* must not destroy the failure being observed — that is the exact place (e.g. a
|
|
333
368
|
* failing error-logger) where losing the underlying failure hurts most. The
|
|
@@ -456,7 +491,9 @@ var AsyncRes = class AsyncRes {
|
|
|
456
491
|
return new AsyncRes(this.promise.then((r) => {
|
|
457
492
|
if (r.tag !== "Err") return passThrough(r);
|
|
458
493
|
try {
|
|
459
|
-
|
|
494
|
+
const out = runMatch(f, r.error);
|
|
495
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
496
|
+
return errRes(out);
|
|
460
497
|
} catch (cause) {
|
|
461
498
|
return defectRes(cause);
|
|
462
499
|
}
|
|
@@ -466,35 +503,31 @@ var AsyncRes = class AsyncRes {
|
|
|
466
503
|
return new AsyncRes(this.promise.then(async (r) => {
|
|
467
504
|
if (r.tag !== "Err") return passThrough(r);
|
|
468
505
|
try {
|
|
469
|
-
|
|
506
|
+
const out = runMatch(f, r.error);
|
|
507
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
508
|
+
return await out;
|
|
470
509
|
} catch (cause) {
|
|
471
510
|
return defectRes(cause);
|
|
472
511
|
}
|
|
473
512
|
}));
|
|
474
513
|
}
|
|
475
|
-
/** @deprecated Use {@link AsyncRes.flatMapErr}. */
|
|
476
|
-
orElse(f) {
|
|
477
|
-
return this.flatMapErr(f);
|
|
478
|
-
}
|
|
479
514
|
recoverErr(f) {
|
|
480
515
|
return new AsyncRes(this.promise.then((r) => {
|
|
481
516
|
if (r.tag !== "Err") return passThrough(r);
|
|
482
517
|
try {
|
|
483
|
-
|
|
518
|
+
const out = runMatch(f, r.error);
|
|
519
|
+
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
520
|
+
return okRes(out);
|
|
484
521
|
} catch (cause) {
|
|
485
522
|
return defectRes(cause);
|
|
486
523
|
}
|
|
487
524
|
}));
|
|
488
525
|
}
|
|
489
|
-
/** @deprecated Use {@link AsyncRes.recoverErr}. */
|
|
490
|
-
recover(f) {
|
|
491
|
-
return this.recoverErr(f);
|
|
492
|
-
}
|
|
493
526
|
tapErr(f) {
|
|
494
527
|
return new AsyncRes(this.promise.then((r) => {
|
|
495
528
|
if (r.tag !== "Err") return r;
|
|
496
529
|
try {
|
|
497
|
-
f
|
|
530
|
+
runMatch(f, r.error);
|
|
498
531
|
return r;
|
|
499
532
|
} catch (cause) {
|
|
500
533
|
return observerThrowToDefect(cause, r.error);
|
|
@@ -505,7 +538,7 @@ var AsyncRes = class AsyncRes {
|
|
|
505
538
|
return new AsyncRes(this.promise.then(async (r) => {
|
|
506
539
|
if (r.tag !== "Err") return passThrough(r);
|
|
507
540
|
try {
|
|
508
|
-
const inner = await f
|
|
541
|
+
const inner = await runMatch(f, r.error);
|
|
509
542
|
return inner.tag === "Ok" ? passThrough(r) : passThrough(inner);
|
|
510
543
|
} catch (cause) {
|
|
511
544
|
return observerThrowToDefect(cause, r.error);
|
|
@@ -533,37 +566,32 @@ var AsyncRes = class AsyncRes {
|
|
|
533
566
|
}
|
|
534
567
|
}));
|
|
535
568
|
}
|
|
569
|
+
tapFailure(f) {
|
|
570
|
+
return new AsyncRes(this.promise.then((r) => {
|
|
571
|
+
if (r.tag === "Ok") return r;
|
|
572
|
+
try {
|
|
573
|
+
f(r);
|
|
574
|
+
return r;
|
|
575
|
+
} catch (cause) {
|
|
576
|
+
return observerThrowToDefect(cause, r.tag === "Err" ? r.error : r.cause);
|
|
577
|
+
}
|
|
578
|
+
}));
|
|
579
|
+
}
|
|
536
580
|
match(cases) {
|
|
537
581
|
return this.promise.then((r) => r.match(cases));
|
|
538
582
|
}
|
|
539
583
|
get() {
|
|
540
584
|
return this.promise.then((r) => r.get());
|
|
541
585
|
}
|
|
542
|
-
/** @deprecated Use {@link AsyncRes.get}. */
|
|
543
|
-
unwrap() {
|
|
544
|
-
return this.get();
|
|
545
|
-
}
|
|
546
586
|
getErr() {
|
|
547
587
|
return this.promise.then((r) => r.getErr());
|
|
548
588
|
}
|
|
549
|
-
/** @deprecated Use {@link AsyncRes.getErr}. */
|
|
550
|
-
unwrapErr() {
|
|
551
|
-
return this.getErr();
|
|
552
|
-
}
|
|
553
589
|
getOr(fallback) {
|
|
554
590
|
return this.promise.then((r) => r.getOr(fallback));
|
|
555
591
|
}
|
|
556
|
-
/** @deprecated Use {@link AsyncRes.getOr}. */
|
|
557
|
-
unwrapOr(fallback) {
|
|
558
|
-
return this.getOr(fallback);
|
|
559
|
-
}
|
|
560
592
|
getOrElse(f) {
|
|
561
593
|
return this.promise.then((r) => r.getOrElse(f));
|
|
562
594
|
}
|
|
563
|
-
/** @deprecated Use {@link AsyncRes.getOrElse}. */
|
|
564
|
-
unwrapOrElse(f) {
|
|
565
|
-
return this.getOrElse(f);
|
|
566
|
-
}
|
|
567
595
|
getOrNull() {
|
|
568
596
|
return this.promise.then((r) => r.getOrNull());
|
|
569
597
|
}
|
|
@@ -740,36 +768,6 @@ function Do() {
|
|
|
740
768
|
return Ok({});
|
|
741
769
|
}
|
|
742
770
|
//#endregion
|
|
743
|
-
//#region src/defect.ts
|
|
744
|
-
const DEFECT = Symbol("unthrown/Defect");
|
|
745
|
-
/**
|
|
746
|
-
* Wrap a cause as a `Defect` marker — the value returned from a `qualify`
|
|
747
|
-
* function when a failure is **not** a modeled domain error. The boundary
|
|
748
|
-
* (`fromPromise` / `fromThrowable`) passes this in as `qualify`'s second
|
|
749
|
-
* argument, so domain code never imports it.
|
|
750
|
-
*
|
|
751
|
-
* @param cause - the original thrown/rejected value.
|
|
752
|
-
* @returns an opaque Defect marker carrying `cause`.
|
|
753
|
-
*
|
|
754
|
-
* @internal
|
|
755
|
-
*/
|
|
756
|
-
function defect(cause) {
|
|
757
|
-
return {
|
|
758
|
-
[DEFECT]: true,
|
|
759
|
-
cause
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* Internal guard for the qualify-time marker. Distinct from the public
|
|
764
|
-
* {@link isDefect} state guard — this one narrows the `E | Defect` union a
|
|
765
|
-
* `qualify` function returns, not a `Result`.
|
|
766
|
-
*
|
|
767
|
-
* @internal
|
|
768
|
-
*/
|
|
769
|
-
function isDefectMarker(x) {
|
|
770
|
-
return typeof x === "object" && x !== null && x[DEFECT] === true;
|
|
771
|
-
}
|
|
772
|
-
//#endregion
|
|
773
771
|
//#region src/interop.ts
|
|
774
772
|
/**
|
|
775
773
|
* Bridge a nullable value into a {@link Result}: absence becomes a **modeled**
|
|
@@ -971,15 +969,25 @@ function qualifyToResult(cause, qualify) {
|
|
|
971
969
|
*
|
|
972
970
|
* @internal
|
|
973
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
|
+
}
|
|
974
976
|
function foldArray(results) {
|
|
975
977
|
let firstErr;
|
|
976
978
|
let firstDefect;
|
|
977
979
|
const values = [];
|
|
978
|
-
for (const r of results)
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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
|
+
}
|
|
983
991
|
return firstDefect ?? firstErr ?? Ok(values);
|
|
984
992
|
}
|
|
985
993
|
/**
|
|
@@ -993,16 +1001,22 @@ function foldRecord(results) {
|
|
|
993
1001
|
let firstErr;
|
|
994
1002
|
let firstDefect;
|
|
995
1003
|
const values = {};
|
|
996
|
-
for (const [key, r] of Object.entries(results))
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
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
|
+
}
|
|
1006
1020
|
return firstDefect ?? firstErr ?? Ok(values);
|
|
1007
1021
|
}
|
|
1008
1022
|
/**
|
|
@@ -1074,7 +1088,7 @@ function allFromDict(results) {
|
|
|
1074
1088
|
* ```
|
|
1075
1089
|
*/
|
|
1076
1090
|
function allAsync(results) {
|
|
1077
|
-
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)));
|
|
1078
1092
|
}
|
|
1079
1093
|
/**
|
|
1080
1094
|
* The asynchronous counterpart of {@link allFromDict}: combine a record of
|
|
@@ -1099,7 +1113,7 @@ function allAsync(results) {
|
|
|
1099
1113
|
*/
|
|
1100
1114
|
function allFromDictAsync(results) {
|
|
1101
1115
|
const entries = Object.entries(results);
|
|
1102
|
-
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) => {
|
|
1103
1117
|
const byKey = Object.create(null);
|
|
1104
1118
|
entries.forEach(([key], i) => {
|
|
1105
1119
|
byKey[key] = resolved[i];
|
|
@@ -1203,8 +1217,10 @@ const AsyncResult = {
|
|
|
1203
1217
|
* rejected at compile time (and excluded from the instance type), so it can't
|
|
1204
1218
|
* shadow `Error.name`.
|
|
1205
1219
|
*
|
|
1206
|
-
* `_tag` is the discriminant
|
|
1207
|
-
*
|
|
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
|
|
1208
1224
|
* they can be **decoupled** with `options.name` — so a tag can be namespaced for
|
|
1209
1225
|
* collision-safety (`"@my-lib/RetryableError"`) without that slash-prefixed
|
|
1210
1226
|
* string leaking into `Error.name`:
|
|
@@ -1253,28 +1269,45 @@ function TaggedError(tag, options) {
|
|
|
1253
1269
|
}
|
|
1254
1270
|
return TaggedErrorBase;
|
|
1255
1271
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
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 };
|
|
1267
1294
|
}
|
|
1268
1295
|
//#endregion
|
|
1269
1296
|
exports.AsyncResult = AsyncResult;
|
|
1270
1297
|
exports.Do = Do;
|
|
1271
1298
|
exports.Err = Err;
|
|
1272
1299
|
exports.ErrAsync = ErrAsync;
|
|
1300
|
+
exports.GetError = GetError;
|
|
1273
1301
|
exports.Ok = Ok;
|
|
1274
1302
|
exports.OkAsync = OkAsync;
|
|
1303
|
+
Object.defineProperty(exports, "P", {
|
|
1304
|
+
enumerable: true,
|
|
1305
|
+
get: function() {
|
|
1306
|
+
return ts_pattern.P;
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1275
1309
|
exports.Result = Result;
|
|
1276
1310
|
exports.TaggedError = TaggedError;
|
|
1277
|
-
exports.UnwrapError = UnwrapError;
|
|
1278
1311
|
exports.all = all;
|
|
1279
1312
|
exports.allAsync = allAsync;
|
|
1280
1313
|
exports.allFromDict = allFromDict;
|
|
@@ -1288,4 +1321,10 @@ exports.isDefect = isDefect;
|
|
|
1288
1321
|
exports.isErr = isErr;
|
|
1289
1322
|
exports.isOk = isOk;
|
|
1290
1323
|
exports.isResult = isResult;
|
|
1291
|
-
exports
|
|
1324
|
+
Object.defineProperty(exports, "match", {
|
|
1325
|
+
enumerable: true,
|
|
1326
|
+
get: function() {
|
|
1327
|
+
return ts_pattern.match;
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
exports.tag = tag;
|