unthrown 5.0.0-beta.0 → 5.0.0-beta.2
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/dist/index.cjs +176 -46
- package/dist/index.d.cts +141 -28
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +141 -28
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +176 -47
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -14,10 +14,10 @@ const DEFECT = Symbol("unthrown/Defect");
|
|
|
14
14
|
* @internal
|
|
15
15
|
*/
|
|
16
16
|
function defect(cause) {
|
|
17
|
-
return {
|
|
17
|
+
return Object.freeze({
|
|
18
18
|
[DEFECT]: true,
|
|
19
19
|
cause
|
|
20
|
-
};
|
|
20
|
+
});
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* Internal guard for the qualify-time marker. Distinct from the public
|
|
@@ -86,7 +86,8 @@ var Res = class {
|
|
|
86
86
|
flatMap(f) {
|
|
87
87
|
if (this.tag !== "Ok") return passThrough(this);
|
|
88
88
|
try {
|
|
89
|
-
|
|
89
|
+
const r = f(this.value);
|
|
90
|
+
return isResult(r) ? r : nonResultCallbackDefect();
|
|
90
91
|
} catch (cause) {
|
|
91
92
|
return defectRes(cause);
|
|
92
93
|
}
|
|
@@ -104,6 +105,7 @@ var Res = class {
|
|
|
104
105
|
if (this.tag !== "Ok") return this;
|
|
105
106
|
try {
|
|
106
107
|
const r = f(this.value);
|
|
108
|
+
if (!isResult(r)) return nonResultCallbackDefect();
|
|
107
109
|
return r.tag === "Ok" ? this : passThrough(r);
|
|
108
110
|
} catch (cause) {
|
|
109
111
|
return defectRes(cause);
|
|
@@ -113,6 +115,7 @@ var Res = class {
|
|
|
113
115
|
if (this.tag !== "Ok") return passThrough(this);
|
|
114
116
|
try {
|
|
115
117
|
const r = f(this.value);
|
|
118
|
+
if (!isResult(r)) return nonResultCallbackDefect();
|
|
116
119
|
if (r.tag !== "Ok") return passThrough(r);
|
|
117
120
|
return okRes({
|
|
118
121
|
...scopeOf(this.value),
|
|
@@ -141,6 +144,14 @@ var Res = class {
|
|
|
141
144
|
if (this.tag !== "Ok") return passThrough(this);
|
|
142
145
|
return okRes(void 0);
|
|
143
146
|
}
|
|
147
|
+
ensure(predicate, onFail) {
|
|
148
|
+
if (this.tag !== "Ok") return passThrough(this);
|
|
149
|
+
try {
|
|
150
|
+
return predicate(this.value) ? this : errRes(onFail(this.value));
|
|
151
|
+
} catch (cause) {
|
|
152
|
+
return defectRes(cause);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
144
155
|
mapErr(f) {
|
|
145
156
|
if (this.tag !== "Err") return passThrough(this);
|
|
146
157
|
try {
|
|
@@ -156,6 +167,7 @@ var Res = class {
|
|
|
156
167
|
try {
|
|
157
168
|
const out = runMatch(f, this.error);
|
|
158
169
|
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
170
|
+
if (!isResult(out)) return nonResultCallbackDefect();
|
|
159
171
|
return out;
|
|
160
172
|
} catch (cause) {
|
|
161
173
|
return defectRes(cause);
|
|
@@ -184,6 +196,7 @@ var Res = class {
|
|
|
184
196
|
if (this.tag !== "Err") return this;
|
|
185
197
|
try {
|
|
186
198
|
const r = runMatch(f, this.error);
|
|
199
|
+
if (!isResult(r)) return nonResultCallbackDefect();
|
|
187
200
|
return r.tag === "Ok" ? this : passThrough(r);
|
|
188
201
|
} catch (cause) {
|
|
189
202
|
return observerThrowToDefect(cause, this.error);
|
|
@@ -192,7 +205,8 @@ var Res = class {
|
|
|
192
205
|
recoverDefect(f) {
|
|
193
206
|
if (this.tag !== "Defect") return this;
|
|
194
207
|
try {
|
|
195
|
-
|
|
208
|
+
const r = f(this.cause);
|
|
209
|
+
return isResult(r) ? r : nonResultCallbackDefect();
|
|
196
210
|
} catch (cause) {
|
|
197
211
|
return defectRes(cause);
|
|
198
212
|
}
|
|
@@ -273,7 +287,19 @@ var Res = class {
|
|
|
273
287
|
return new AsyncRes(Promise.resolve(this));
|
|
274
288
|
}
|
|
275
289
|
};
|
|
290
|
+
/**
|
|
291
|
+
* Cross-copy brand: `Symbol.for` yields the same symbol in every copy of the
|
|
292
|
+
* library (the dual CJS/ESM build, a duplicated install, another realm), so
|
|
293
|
+
* {@link isResult} can recognise a `Result` built by another copy whose `Res`
|
|
294
|
+
* class fails the `instanceof` check. Defined non-enumerably on the prototype,
|
|
295
|
+
* before it is frozen.
|
|
296
|
+
*
|
|
297
|
+
* @internal
|
|
298
|
+
*/
|
|
299
|
+
const RESULT_BRAND = Symbol.for("unthrown.Result");
|
|
276
300
|
const RESULT_PROTO = Res.prototype;
|
|
301
|
+
Object.defineProperty(RESULT_PROTO, RESULT_BRAND, { value: true });
|
|
302
|
+
Object.freeze(RESULT_PROTO);
|
|
277
303
|
/**
|
|
278
304
|
* Construct an `Ok` result — a plain object on the {@link Res} prototype.
|
|
279
305
|
*
|
|
@@ -313,9 +339,12 @@ function defectRes(cause) {
|
|
|
313
339
|
* @remarks
|
|
314
340
|
* Unlike {@link isOk} / {@link isErr} / {@link isDefect}, which narrow a value
|
|
315
341
|
* already known to be a `Result`, this narrows from `unknown` — useful at an
|
|
316
|
-
* untyped boundary. It checks the value carries the `Result` prototype
|
|
317
|
-
*
|
|
318
|
-
*
|
|
342
|
+
* untyped boundary. It checks the value carries the `Result` prototype
|
|
343
|
+
* (`instanceof` first, falling back to the `Symbol.for("unthrown.Result")`
|
|
344
|
+
* brand the prototype carries — so a `Result` built by **another copy** of
|
|
345
|
+
* unthrown, e.g. the CJS and ESM builds loaded side by side, is still
|
|
346
|
+
* recognised). A look-alike plain object (`{ tag: "Ok" }`) carries neither and
|
|
347
|
+
* is **not** matched. An `AsyncResult` is not a `Result` and returns `false`.
|
|
319
348
|
*
|
|
320
349
|
* @returns `true` when `x` is a `Result` produced by this library.
|
|
321
350
|
*
|
|
@@ -334,7 +363,12 @@ function defectRes(cause) {
|
|
|
334
363
|
* @category Guards
|
|
335
364
|
*/
|
|
336
365
|
function isResult(x) {
|
|
337
|
-
|
|
366
|
+
if (x instanceof Res) return true;
|
|
367
|
+
try {
|
|
368
|
+
return (typeof x === "object" || typeof x === "function") && x !== null && Reflect.get(x, RESULT_BRAND) === true;
|
|
369
|
+
} catch {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
338
372
|
}
|
|
339
373
|
/**
|
|
340
374
|
* Reuse a non-matching variant (an `Err` or `Defect`) as a differently-typed
|
|
@@ -350,6 +384,19 @@ function passThrough(self) {
|
|
|
350
384
|
return self;
|
|
351
385
|
}
|
|
352
386
|
/**
|
|
387
|
+
* The Defect minted when a callback constrained to return a `Result` returns
|
|
388
|
+
* something else — reachable only from untyped/cast callers (in typed code the
|
|
389
|
+
* constraint is a compile error). The combinator-side sibling of the
|
|
390
|
+
* aggregates' non-`Result`-element guard: surface the out-of-contract value as
|
|
391
|
+
* a `Defect` here rather than letting a poison value throw a raw `TypeError`
|
|
392
|
+
* further down the pipeline.
|
|
393
|
+
*
|
|
394
|
+
* @internal
|
|
395
|
+
*/
|
|
396
|
+
function nonResultCallbackDefect() {
|
|
397
|
+
return defectRes(/* @__PURE__ */ new TypeError("unthrown: a combinator callback returned a non-Result value"));
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
353
400
|
* Drive an error-combinator callback: build `match(error)`, hand it (plus the
|
|
354
401
|
* injected `defect`) to the callback, and `.run()` the returned exhaustive
|
|
355
402
|
* builder to its output. `.run()` executes `.exhaustive()` — type-forced
|
|
@@ -404,15 +451,15 @@ function scopeOf(value) {
|
|
|
404
451
|
* @internal
|
|
405
452
|
*/
|
|
406
453
|
var AsyncRes = class AsyncRes {
|
|
407
|
-
promise;
|
|
454
|
+
#promise;
|
|
408
455
|
constructor(promise) {
|
|
409
|
-
this
|
|
456
|
+
this.#promise = promise;
|
|
410
457
|
}
|
|
411
458
|
then(onfulfilled, onrejected) {
|
|
412
|
-
return this
|
|
459
|
+
return this.#promise.then(onfulfilled, onrejected);
|
|
413
460
|
}
|
|
414
461
|
map(f) {
|
|
415
|
-
return new AsyncRes(this
|
|
462
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
416
463
|
if (r.tag !== "Ok") return passThrough(r);
|
|
417
464
|
try {
|
|
418
465
|
return okRes(f(r.value));
|
|
@@ -422,17 +469,18 @@ var AsyncRes = class AsyncRes {
|
|
|
422
469
|
}));
|
|
423
470
|
}
|
|
424
471
|
flatMap(f) {
|
|
425
|
-
return new AsyncRes(this
|
|
472
|
+
return new AsyncRes(this.#promise.then(async (r) => {
|
|
426
473
|
if (r.tag !== "Ok") return passThrough(r);
|
|
427
474
|
try {
|
|
428
|
-
|
|
475
|
+
const inner = await f(r.value);
|
|
476
|
+
return isResult(inner) ? inner : nonResultCallbackDefect();
|
|
429
477
|
} catch (cause) {
|
|
430
478
|
return defectRes(cause);
|
|
431
479
|
}
|
|
432
480
|
}));
|
|
433
481
|
}
|
|
434
482
|
tap(f) {
|
|
435
|
-
return new AsyncRes(this
|
|
483
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
436
484
|
if (r.tag !== "Ok") return r;
|
|
437
485
|
try {
|
|
438
486
|
f(r.value);
|
|
@@ -443,10 +491,11 @@ var AsyncRes = class AsyncRes {
|
|
|
443
491
|
}));
|
|
444
492
|
}
|
|
445
493
|
flatTap(f) {
|
|
446
|
-
return new AsyncRes(this
|
|
494
|
+
return new AsyncRes(this.#promise.then(async (r) => {
|
|
447
495
|
if (r.tag !== "Ok") return passThrough(r);
|
|
448
496
|
try {
|
|
449
497
|
const inner = await f(r.value);
|
|
498
|
+
if (!isResult(inner)) return nonResultCallbackDefect();
|
|
450
499
|
return inner.tag === "Ok" ? r : passThrough(inner);
|
|
451
500
|
} catch (cause) {
|
|
452
501
|
return defectRes(cause);
|
|
@@ -454,10 +503,11 @@ var AsyncRes = class AsyncRes {
|
|
|
454
503
|
}));
|
|
455
504
|
}
|
|
456
505
|
bind(name, f) {
|
|
457
|
-
return new AsyncRes(this
|
|
506
|
+
return new AsyncRes(this.#promise.then(async (r) => {
|
|
458
507
|
if (r.tag !== "Ok") return passThrough(r);
|
|
459
508
|
try {
|
|
460
509
|
const inner = await f(r.value);
|
|
510
|
+
if (!isResult(inner)) return nonResultCallbackDefect();
|
|
461
511
|
if (inner.tag !== "Ok") return passThrough(inner);
|
|
462
512
|
return okRes({
|
|
463
513
|
...scopeOf(r.value),
|
|
@@ -469,7 +519,7 @@ var AsyncRes = class AsyncRes {
|
|
|
469
519
|
}));
|
|
470
520
|
}
|
|
471
521
|
let(name, f) {
|
|
472
|
-
return new AsyncRes(this
|
|
522
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
473
523
|
if (r.tag !== "Ok") return passThrough(r);
|
|
474
524
|
try {
|
|
475
525
|
return okRes({
|
|
@@ -482,13 +532,23 @@ var AsyncRes = class AsyncRes {
|
|
|
482
532
|
}));
|
|
483
533
|
}
|
|
484
534
|
as(value) {
|
|
485
|
-
return new AsyncRes(this
|
|
535
|
+
return new AsyncRes(this.#promise.then((r) => r.tag === "Ok" ? okRes(value) : passThrough(r)));
|
|
486
536
|
}
|
|
487
537
|
discard() {
|
|
488
|
-
return new AsyncRes(this
|
|
538
|
+
return new AsyncRes(this.#promise.then((r) => r.tag === "Ok" ? okRes(void 0) : passThrough(r)));
|
|
539
|
+
}
|
|
540
|
+
ensure(predicate, onFail) {
|
|
541
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
542
|
+
if (r.tag !== "Ok") return passThrough(r);
|
|
543
|
+
try {
|
|
544
|
+
return predicate(r.value) ? r : errRes(onFail(r.value));
|
|
545
|
+
} catch (cause) {
|
|
546
|
+
return defectRes(cause);
|
|
547
|
+
}
|
|
548
|
+
}));
|
|
489
549
|
}
|
|
490
550
|
mapErr(f) {
|
|
491
|
-
return new AsyncRes(this
|
|
551
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
492
552
|
if (r.tag !== "Err") return passThrough(r);
|
|
493
553
|
try {
|
|
494
554
|
const out = runMatch(f, r.error);
|
|
@@ -500,19 +560,21 @@ var AsyncRes = class AsyncRes {
|
|
|
500
560
|
}));
|
|
501
561
|
}
|
|
502
562
|
flatMapErr(f) {
|
|
503
|
-
return new AsyncRes(this
|
|
563
|
+
return new AsyncRes(this.#promise.then(async (r) => {
|
|
504
564
|
if (r.tag !== "Err") return passThrough(r);
|
|
505
565
|
try {
|
|
506
566
|
const out = runMatch(f, r.error);
|
|
507
567
|
if (isDefectMarker(out)) return defectRes(out.cause);
|
|
508
|
-
|
|
568
|
+
const inner = await out;
|
|
569
|
+
if (!isResult(inner)) return nonResultCallbackDefect();
|
|
570
|
+
return inner;
|
|
509
571
|
} catch (cause) {
|
|
510
572
|
return defectRes(cause);
|
|
511
573
|
}
|
|
512
574
|
}));
|
|
513
575
|
}
|
|
514
576
|
recoverErr(f) {
|
|
515
|
-
return new AsyncRes(this
|
|
577
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
516
578
|
if (r.tag !== "Err") return passThrough(r);
|
|
517
579
|
try {
|
|
518
580
|
const out = runMatch(f, r.error);
|
|
@@ -524,7 +586,7 @@ var AsyncRes = class AsyncRes {
|
|
|
524
586
|
}));
|
|
525
587
|
}
|
|
526
588
|
tapErr(f) {
|
|
527
|
-
return new AsyncRes(this
|
|
589
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
528
590
|
if (r.tag !== "Err") return r;
|
|
529
591
|
try {
|
|
530
592
|
runMatch(f, r.error);
|
|
@@ -535,10 +597,11 @@ var AsyncRes = class AsyncRes {
|
|
|
535
597
|
}));
|
|
536
598
|
}
|
|
537
599
|
flatTapErr(f) {
|
|
538
|
-
return new AsyncRes(this
|
|
600
|
+
return new AsyncRes(this.#promise.then(async (r) => {
|
|
539
601
|
if (r.tag !== "Err") return passThrough(r);
|
|
540
602
|
try {
|
|
541
603
|
const inner = await runMatch(f, r.error);
|
|
604
|
+
if (!isResult(inner)) return nonResultCallbackDefect();
|
|
542
605
|
return inner.tag === "Ok" ? passThrough(r) : passThrough(inner);
|
|
543
606
|
} catch (cause) {
|
|
544
607
|
return observerThrowToDefect(cause, r.error);
|
|
@@ -546,17 +609,18 @@ var AsyncRes = class AsyncRes {
|
|
|
546
609
|
}));
|
|
547
610
|
}
|
|
548
611
|
recoverDefect(f) {
|
|
549
|
-
return new AsyncRes(this
|
|
612
|
+
return new AsyncRes(this.#promise.then(async (r) => {
|
|
550
613
|
if (r.tag !== "Defect") return r;
|
|
551
614
|
try {
|
|
552
|
-
|
|
615
|
+
const inner = await f(r.cause);
|
|
616
|
+
return isResult(inner) ? inner : nonResultCallbackDefect();
|
|
553
617
|
} catch (cause) {
|
|
554
618
|
return defectRes(cause);
|
|
555
619
|
}
|
|
556
620
|
}));
|
|
557
621
|
}
|
|
558
622
|
tapDefect(f) {
|
|
559
|
-
return new AsyncRes(this
|
|
623
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
560
624
|
if (r.tag !== "Defect") return r;
|
|
561
625
|
try {
|
|
562
626
|
f(r.cause);
|
|
@@ -567,7 +631,7 @@ var AsyncRes = class AsyncRes {
|
|
|
567
631
|
}));
|
|
568
632
|
}
|
|
569
633
|
tapFailure(f) {
|
|
570
|
-
return new AsyncRes(this
|
|
634
|
+
return new AsyncRes(this.#promise.then((r) => {
|
|
571
635
|
if (r.tag === "Ok") return r;
|
|
572
636
|
try {
|
|
573
637
|
f(r);
|
|
@@ -578,30 +642,31 @@ var AsyncRes = class AsyncRes {
|
|
|
578
642
|
}));
|
|
579
643
|
}
|
|
580
644
|
match(cases) {
|
|
581
|
-
return this
|
|
645
|
+
return this.#promise.then((r) => r.match(cases));
|
|
582
646
|
}
|
|
583
647
|
get() {
|
|
584
|
-
return this
|
|
648
|
+
return this.#promise.then((r) => r.get());
|
|
585
649
|
}
|
|
586
650
|
getErr() {
|
|
587
|
-
return this
|
|
651
|
+
return this.#promise.then((r) => r.getErr());
|
|
588
652
|
}
|
|
589
653
|
getOr(fallback) {
|
|
590
|
-
return this
|
|
654
|
+
return this.#promise.then((r) => r.getOr(fallback));
|
|
591
655
|
}
|
|
592
656
|
getOrElse(f) {
|
|
593
|
-
return this
|
|
657
|
+
return this.#promise.then((r) => r.getOrElse(f));
|
|
594
658
|
}
|
|
595
659
|
getOrNull() {
|
|
596
|
-
return this
|
|
660
|
+
return this.#promise.then((r) => r.getOrNull());
|
|
597
661
|
}
|
|
598
662
|
getOrUndefined() {
|
|
599
|
-
return this
|
|
663
|
+
return this.#promise.then((r) => r.getOrUndefined());
|
|
600
664
|
}
|
|
601
665
|
getOrThrow() {
|
|
602
|
-
return this
|
|
666
|
+
return this.#promise.then((r) => r.getOrThrow());
|
|
603
667
|
}
|
|
604
668
|
};
|
|
669
|
+
Object.freeze(AsyncRes.prototype);
|
|
605
670
|
//#endregion
|
|
606
671
|
//#region src/constructors.ts
|
|
607
672
|
function Ok(value) {
|
|
@@ -767,6 +832,33 @@ function isDefect(r) {
|
|
|
767
832
|
function Do() {
|
|
768
833
|
return Ok({});
|
|
769
834
|
}
|
|
835
|
+
/**
|
|
836
|
+
* Start an **asynchronous** do-notation chain with an empty object scope — the
|
|
837
|
+
* pre-lifted form of {@link Do}, sparing you `Do().toAsync()`.
|
|
838
|
+
*
|
|
839
|
+
* @remarks
|
|
840
|
+
* From here a `bind` may return a `Result` **or** an `AsyncResult`; the scope
|
|
841
|
+
* accumulates exactly as in a sync {@link Do} chain, and a throw in any step
|
|
842
|
+
* becomes a `Defect`. Named with the `Async` suffix the async free functions
|
|
843
|
+
* carry (`OkAsync`, `allAsync`); the {@link AsyncResult} companion aliases it as
|
|
844
|
+
* `AsyncResult.Do` (the namespace already says "async", so the suffix drops).
|
|
845
|
+
*
|
|
846
|
+
* @example
|
|
847
|
+
* ```ts
|
|
848
|
+
* import { DoAsync, Ok } from "unthrown";
|
|
849
|
+
*
|
|
850
|
+
* const result = await DoAsync()
|
|
851
|
+
* .bind("user", () => findUser(id)) // AsyncResult<User, NotFound>
|
|
852
|
+
* .bind("plan", ({ user }) => Ok(user.plan)) // a sync Result is accepted too
|
|
853
|
+
* .let("label", ({ user, plan }) => `${user.name} on ${plan}`);
|
|
854
|
+
* // Result<{ user: User; plan: Plan; label: string }, NotFound>
|
|
855
|
+
* ```
|
|
856
|
+
*
|
|
857
|
+
* @category Do-notation
|
|
858
|
+
*/
|
|
859
|
+
function DoAsync() {
|
|
860
|
+
return Do().toAsync();
|
|
861
|
+
}
|
|
770
862
|
//#endregion
|
|
771
863
|
//#region src/interop.ts
|
|
772
864
|
/**
|
|
@@ -805,7 +897,10 @@ function fromNullable(value, onAbsent) {
|
|
|
805
897
|
* `qualify` **must** triage every thrown cause into a modeled error `E` or a
|
|
806
898
|
* `Defect` (via the injected `defect` helper, its second argument) — there is no
|
|
807
899
|
* path that leaves `unknown` in `E`. A throw inside `qualify` itself is treated
|
|
808
|
-
* as a `Defect`.
|
|
900
|
+
* as a `Defect`. `qualify` is **synchronous**: an `async` qualify is rejected at
|
|
901
|
+
* compile time ({@link NotThenable}) — its `Promise` would land in `E` un-triaged
|
|
902
|
+
* — and a thenable slipped past the types at runtime becomes a `Defect` (never
|
|
903
|
+
* an `Err(Promise)`), its orphaned rejection silenced.
|
|
809
904
|
*
|
|
810
905
|
* The modeled error type is `Exclude<R, Defect>` — the `Defect` arm of
|
|
811
906
|
* `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
|
|
@@ -895,7 +990,10 @@ function fromSafeThrowable(fn) {
|
|
|
895
990
|
* `qualify` **must** map each rejection cause into a modeled error `E` or a
|
|
896
991
|
* `Defect` (via the injected `defect` helper, its second argument). The returned
|
|
897
992
|
* `AsyncResult`'s internal promise never rejects; `await`-ing it always yields a
|
|
898
|
-
* `Result`. A throw inside `qualify` is itself a `Defect`.
|
|
993
|
+
* `Result`. A throw inside `qualify` is itself a `Defect`. `qualify` is
|
|
994
|
+
* **synchronous**: an `async` qualify is rejected at compile time
|
|
995
|
+
* ({@link NotThenable}), and a thenable slipped past the types at runtime
|
|
996
|
+
* becomes a `Defect` (never an `Err(Promise)`), its orphaned rejection silenced.
|
|
899
997
|
*
|
|
900
998
|
* The modeled error type is `Exclude<R, Defect>` — the `Defect` arm of
|
|
901
999
|
* `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
|
|
@@ -958,12 +1056,26 @@ function fromSafePromise(promise) {
|
|
|
958
1056
|
function qualifyToResult(cause, qualify) {
|
|
959
1057
|
try {
|
|
960
1058
|
const q = qualify(cause, defect);
|
|
961
|
-
|
|
1059
|
+
if (isDefectMarker(q)) return defectRes(q.cause);
|
|
1060
|
+
if (isThenable(q)) {
|
|
1061
|
+
Promise.resolve(q).then(void 0, () => void 0);
|
|
1062
|
+
return defectRes(/* @__PURE__ */ new TypeError("unthrown: qualify must be synchronous — it returned a thenable; triage the cause without awaiting"));
|
|
1063
|
+
}
|
|
1064
|
+
return errRes(q);
|
|
962
1065
|
} catch (qErr) {
|
|
963
1066
|
return defectRes(qErr);
|
|
964
1067
|
}
|
|
965
1068
|
}
|
|
966
1069
|
/**
|
|
1070
|
+
* Runtime thenable probe for the belt-and-braces guard above. Called inside the
|
|
1071
|
+
* caller's `try`, so even a hostile `.then` getter lands on the Defect path.
|
|
1072
|
+
*
|
|
1073
|
+
* @internal
|
|
1074
|
+
*/
|
|
1075
|
+
function isThenable(x) {
|
|
1076
|
+
return (typeof x === "object" || typeof x === "function") && x !== null && typeof x.then === "function";
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
967
1079
|
* Fold an array of settled `Result`s: first `Err` wins, any `Defect` dominates,
|
|
968
1080
|
* else `Ok` of the values array.
|
|
969
1081
|
*
|
|
@@ -1166,8 +1278,9 @@ const Result = {
|
|
|
1166
1278
|
/**
|
|
1167
1279
|
* Companion object grouping the **`AsyncResult`-producing** entry points under
|
|
1168
1280
|
* the matching namespace: {@link AsyncResult.Ok}, {@link AsyncResult.Err},
|
|
1169
|
-
* {@link AsyncResult.
|
|
1170
|
-
* {@link AsyncResult.
|
|
1281
|
+
* {@link AsyncResult.Do}, {@link AsyncResult.fromPromise},
|
|
1282
|
+
* {@link AsyncResult.fromSafePromise}, {@link AsyncResult.all},
|
|
1283
|
+
* {@link AsyncResult.allFromDict}.
|
|
1171
1284
|
*
|
|
1172
1285
|
* @remarks
|
|
1173
1286
|
* The async sibling of {@link Result}. Statics are grouped by what they
|
|
@@ -1175,7 +1288,8 @@ const Result = {
|
|
|
1175
1288
|
* and the async aggregates sit here rather than on {@link Result}; the namespace
|
|
1176
1289
|
* already conveys "async", so the members drop the `Async` suffix their free
|
|
1177
1290
|
* functions carry (`AsyncResult.Ok` is `OkAsync`; `AsyncResult.Err` is
|
|
1178
|
-
* `ErrAsync`; `AsyncResult.
|
|
1291
|
+
* `ErrAsync`; `AsyncResult.Do` is `DoAsync`; `AsyncResult.all` is `allAsync`;
|
|
1292
|
+
* `AsyncResult.allFromDict` is
|
|
1179
1293
|
* `allFromDictAsync`). Like {@link Result}, the free functions remain the
|
|
1180
1294
|
* primary, tree-shakeable API; the value `AsyncResult` and the type
|
|
1181
1295
|
* {@link AsyncResult} share one name.
|
|
@@ -1192,6 +1306,7 @@ const Result = {
|
|
|
1192
1306
|
const AsyncResult = {
|
|
1193
1307
|
Ok: OkAsync,
|
|
1194
1308
|
Err: ErrAsync,
|
|
1309
|
+
Do: DoAsync,
|
|
1195
1310
|
fromPromise,
|
|
1196
1311
|
fromSafePromise,
|
|
1197
1312
|
all: allAsync,
|
|
@@ -1215,7 +1330,11 @@ const AsyncResult = {
|
|
|
1215
1330
|
* `tag` and cannot be overridden by the payload. `name` is likewise reserved —
|
|
1216
1331
|
* it is the display label (set it with `options.name`); a payload `name` is
|
|
1217
1332
|
* rejected at compile time (and excluded from the instance type), so it can't
|
|
1218
|
-
* shadow `Error.name`.
|
|
1333
|
+
* shadow `Error.name`. `stack` is reserved the same way — it is `Error`'s
|
|
1334
|
+
* trace, and even an untyped payload `stack` cannot clobber the real one.
|
|
1335
|
+
* `cause` is deliberately **not** reserved: `Error.cause` is typed `unknown`,
|
|
1336
|
+
* so a payload `cause` (e.g. a wrapped driver error) is a legitimate,
|
|
1337
|
+
* *narrowing* structured field.
|
|
1219
1338
|
*
|
|
1220
1339
|
* `_tag` is the discriminant matched by {@link tag} in the error combinators
|
|
1221
1340
|
* (`result.mapErr((matcher) => matcher.with(tag("NotFound"), …))`) and in
|
|
@@ -1260,7 +1379,17 @@ function TaggedError(tag, options) {
|
|
|
1260
1379
|
_tag;
|
|
1261
1380
|
constructor(props) {
|
|
1262
1381
|
super();
|
|
1263
|
-
if (props)
|
|
1382
|
+
if (props) {
|
|
1383
|
+
const stack = this.stack;
|
|
1384
|
+
Object.assign(this, props);
|
|
1385
|
+
delete this.stack;
|
|
1386
|
+
if (stack !== void 0) Object.defineProperty(this, "stack", {
|
|
1387
|
+
value: stack,
|
|
1388
|
+
writable: true,
|
|
1389
|
+
enumerable: false,
|
|
1390
|
+
configurable: true
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1264
1393
|
this._tag = tag;
|
|
1265
1394
|
this.name = displayName;
|
|
1266
1395
|
delete this.message;
|
|
@@ -1295,6 +1424,7 @@ function tag(value) {
|
|
|
1295
1424
|
//#endregion
|
|
1296
1425
|
exports.AsyncResult = AsyncResult;
|
|
1297
1426
|
exports.Do = Do;
|
|
1427
|
+
exports.DoAsync = DoAsync;
|
|
1298
1428
|
exports.Err = Err;
|
|
1299
1429
|
exports.ErrAsync = ErrAsync;
|
|
1300
1430
|
exports.GetError = GetError;
|