unthrown 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  //#region src/core.ts
3
3
  /**
4
- * Thrown by a {@link Result}'s `unwrap` / `unwrapErr` when the assertion is
5
- * wrong on a *modeled* result — `unwrap()` on an `Err`, or `unwrapErr()` on an
4
+ * Thrown by a {@link Result}'s `get` / `getErr` when the assertion is
5
+ * wrong on a *modeled* result — `get()` on an `Err`, or `getErr()` on an
6
6
  * `Ok`.
7
7
  *
8
8
  * @remarks
@@ -14,7 +14,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
14
14
  * A `Defect` is never wrapped in an `UnwrapError`: its original cause is
15
15
  * re-thrown (with its original stack) instead.
16
16
  *
17
- * `unwrap()` and `unwrapErr()` are type-gated (`this: Result<T, never>` /
17
+ * `get()` and `getErr()` are type-gated (`this: Result<T, never>` /
18
18
  * `Result<never, E>`), so the wrong-variant branch that throws this is
19
19
  * unreachable through well-typed code — it remains only as a defensive guard
20
20
  * against unsound runtime misuse (e.g. an `as` cast past the gate).
@@ -25,8 +25,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
25
25
  */
26
26
  var UnwrapError = class extends Error {
27
27
  /**
28
- * The offending value: the `Err` error for `unwrap()`, or the `Ok` value for
29
- * `unwrapErr()`.
28
+ * The offending value: the `Err` error for `get()`, or the `Ok` value for
29
+ * `getErr()`.
30
30
  */
31
31
  error;
32
32
  constructor(error) {
@@ -114,7 +114,7 @@ var Res = class {
114
114
  return defectRes(cause);
115
115
  }
116
116
  }
117
- orElse(f) {
117
+ flatMapErr(f) {
118
118
  if (this.tag !== "Err") return passThrough(this);
119
119
  try {
120
120
  return f(this.error);
@@ -122,7 +122,11 @@ var Res = class {
122
122
  return defectRes(cause);
123
123
  }
124
124
  }
125
- recover(f) {
125
+ /** @deprecated Use {@link Res.flatMapErr}. */
126
+ orElse(f) {
127
+ return this.flatMapErr(f);
128
+ }
129
+ recoverErr(f) {
126
130
  if (this.tag !== "Err") return passThrough(this);
127
131
  try {
128
132
  return okRes(f(this.error));
@@ -130,6 +134,10 @@ var Res = class {
130
134
  return defectRes(cause);
131
135
  }
132
136
  }
137
+ /** @deprecated Use {@link Res.recoverErr}. */
138
+ recover(f) {
139
+ return this.recoverErr(f);
140
+ }
133
141
  tapErr(f) {
134
142
  if (this.tag !== "Err") return this;
135
143
  try {
@@ -172,30 +180,46 @@ var Res = class {
172
180
  case "Defect": return cases.defect(this.cause);
173
181
  }
174
182
  }
175
- unwrap() {
183
+ get() {
176
184
  switch (this.tag) {
177
185
  case "Ok": return this.value;
178
186
  case "Err": throw new UnwrapError(this.error);
179
187
  case "Defect": throw this.cause;
180
188
  }
181
189
  }
182
- unwrapErr() {
190
+ /** @deprecated Use {@link Res.get}. */
191
+ unwrap() {
192
+ return this.get();
193
+ }
194
+ getErr() {
183
195
  switch (this.tag) {
184
196
  case "Err": return this.error;
185
197
  case "Ok": throw new UnwrapError(this.value);
186
198
  case "Defect": throw this.cause;
187
199
  }
188
200
  }
189
- unwrapOr(fallback) {
201
+ /** @deprecated Use {@link Res.getErr}. */
202
+ unwrapErr() {
203
+ return this.getErr();
204
+ }
205
+ getOr(fallback) {
190
206
  if (this.tag === "Ok") return this.value;
191
207
  if (this.tag === "Defect") throw this.cause;
192
208
  return fallback;
193
209
  }
194
- unwrapOrElse(f) {
210
+ /** @deprecated Use {@link Res.getOr}. */
211
+ unwrapOr(fallback) {
212
+ return this.getOr(fallback);
213
+ }
214
+ getOrElse(f) {
195
215
  if (this.tag === "Ok") return this.value;
196
216
  if (this.tag === "Defect") throw this.cause;
197
217
  return f(this.error);
198
218
  }
219
+ /** @deprecated Use {@link Res.getOrElse}. */
220
+ unwrapOrElse(f) {
221
+ return this.getOrElse(f);
222
+ }
199
223
  getOrNull() {
200
224
  if (this.tag === "Ok") return this.value;
201
225
  if (this.tag === "Defect") throw this.cause;
@@ -205,6 +229,11 @@ var Res = class {
205
229
  if (this.tag === "Ok") return this.value;
206
230
  if (this.tag === "Defect") throw this.cause;
207
231
  }
232
+ getOrThrow() {
233
+ if (this.tag === "Ok") return this.value;
234
+ if (this.tag === "Defect") throw this.cause;
235
+ throw this.error;
236
+ }
208
237
  isOk() {
209
238
  return this.tag === "Ok";
210
239
  }
@@ -426,7 +455,7 @@ var AsyncRes = class AsyncRes {
426
455
  }
427
456
  }));
428
457
  }
429
- orElse(f) {
458
+ flatMapErr(f) {
430
459
  return new AsyncRes(this.promise.then(async (r) => {
431
460
  if (r.tag !== "Err") return passThrough(r);
432
461
  try {
@@ -436,7 +465,11 @@ var AsyncRes = class AsyncRes {
436
465
  }
437
466
  }));
438
467
  }
439
- recover(f) {
468
+ /** @deprecated Use {@link AsyncRes.flatMapErr}. */
469
+ orElse(f) {
470
+ return this.flatMapErr(f);
471
+ }
472
+ recoverErr(f) {
440
473
  return new AsyncRes(this.promise.then((r) => {
441
474
  if (r.tag !== "Err") return passThrough(r);
442
475
  try {
@@ -446,6 +479,10 @@ var AsyncRes = class AsyncRes {
446
479
  }
447
480
  }));
448
481
  }
482
+ /** @deprecated Use {@link AsyncRes.recoverErr}. */
483
+ recover(f) {
484
+ return this.recoverErr(f);
485
+ }
449
486
  tapErr(f) {
450
487
  return new AsyncRes(this.promise.then((r) => {
451
488
  if (r.tag !== "Err") return r;
@@ -492,17 +529,33 @@ var AsyncRes = class AsyncRes {
492
529
  match(cases) {
493
530
  return this.promise.then((r) => r.match(cases));
494
531
  }
532
+ get() {
533
+ return this.promise.then((r) => r.get());
534
+ }
535
+ /** @deprecated Use {@link AsyncRes.get}. */
495
536
  unwrap() {
496
- return this.promise.then((r) => r.unwrap());
537
+ return this.get();
497
538
  }
539
+ getErr() {
540
+ return this.promise.then((r) => r.getErr());
541
+ }
542
+ /** @deprecated Use {@link AsyncRes.getErr}. */
498
543
  unwrapErr() {
499
- return this.promise.then((r) => r.unwrapErr());
544
+ return this.getErr();
545
+ }
546
+ getOr(fallback) {
547
+ return this.promise.then((r) => r.getOr(fallback));
500
548
  }
549
+ /** @deprecated Use {@link AsyncRes.getOr}. */
501
550
  unwrapOr(fallback) {
502
- return this.promise.then((r) => r.unwrapOr(fallback));
551
+ return this.getOr(fallback);
552
+ }
553
+ getOrElse(f) {
554
+ return this.promise.then((r) => r.getOrElse(f));
503
555
  }
556
+ /** @deprecated Use {@link AsyncRes.getOrElse}. */
504
557
  unwrapOrElse(f) {
505
- return this.promise.then((r) => r.unwrapOrElse(f));
558
+ return this.getOrElse(f);
506
559
  }
507
560
  getOrNull() {
508
561
  return this.promise.then((r) => r.getOrNull());
@@ -510,6 +563,9 @@ var AsyncRes = class AsyncRes {
510
563
  getOrUndefined() {
511
564
  return this.promise.then((r) => r.getOrUndefined());
512
565
  }
566
+ getOrThrow() {
567
+ return this.promise.then((r) => r.getOrThrow());
568
+ }
513
569
  };
514
570
  //#endregion
515
571
  //#region src/constructors.ts
@@ -524,7 +580,7 @@ var AsyncRes = class AsyncRes {
524
580
  * import { Ok } from "unthrown";
525
581
  *
526
582
  * Ok(2).map((n) => n + 1); // => Ok(3)
527
- * Ok(42).unwrap(); // => 42
583
+ * Ok(42).get(); // => 42
528
584
  * ```
529
585
  *
530
586
  * @category Constructors
@@ -543,7 +599,7 @@ function Ok(value) {
543
599
  * import { Err } from "unthrown";
544
600
  *
545
601
  * Err("not_found").map((n) => n + 1); // => Err("not_found") (map skipped)
546
- * Err("not_found").unwrapErr(); // => "not_found"
602
+ * Err("not_found").getErr(); // => "not_found"
547
603
  * ```
548
604
  *
549
605
  * @category Constructors
@@ -552,6 +608,58 @@ function Err(error) {
552
608
  return errRes(error);
553
609
  }
554
610
  /**
611
+ * Construct a successful {@link AsyncResult} from a pure value — the pre-lifted
612
+ * form of {@link Ok}, sparing you `Ok(value).toAsync()`.
613
+ *
614
+ * @remarks
615
+ * Reach for this on the synchronous/early branch of an `AsyncResult`-returning
616
+ * function, so both branches share one return type without a trailing
617
+ * `.toAsync()`. Named with the `Async` suffix the async free functions carry
618
+ * (`allAsync`, `allFromDictAsync`); the {@link AsyncResult} companion aliases it
619
+ * as `AsyncResult.Ok` (the namespace already says "async", so the suffix drops).
620
+ *
621
+ * @typeParam T - the success value type.
622
+ * @param value - the success value to wrap.
623
+ *
624
+ * @example
625
+ * ```ts
626
+ * import { OkAsync, type AsyncResult } from "unthrown";
627
+ *
628
+ * function loadItems(ids: string[]): AsyncResult<Item[], never> {
629
+ * if (ids.length === 0) return OkAsync([]); // no more Ok([]).toAsync()
630
+ * return itemRepository.load(ids);
631
+ * }
632
+ * ```
633
+ *
634
+ * @category Constructors
635
+ */
636
+ function OkAsync(value) {
637
+ return Ok(value).toAsync();
638
+ }
639
+ /**
640
+ * Construct a failed {@link AsyncResult} carrying a **modeled** error — the
641
+ * pre-lifted form of {@link Err}, sparing you `Err(error).toAsync()`.
642
+ *
643
+ * @remarks
644
+ * The error-channel mirror of {@link OkAsync}; see it for the naming and the
645
+ * `AsyncResult.Err` companion alias.
646
+ *
647
+ * @typeParam E - the modeled error type.
648
+ * @param error - the domain error to wrap.
649
+ *
650
+ * @example
651
+ * ```ts
652
+ * import { ErrAsync } from "unthrown";
653
+ *
654
+ * ErrAsync("not_found"); // AsyncResult<never, string>
655
+ * ```
656
+ *
657
+ * @category Constructors
658
+ */
659
+ function ErrAsync(error) {
660
+ return Err(error).toAsync();
661
+ }
662
+ /**
555
663
  * Type guard: narrow a {@link Result} to its `Ok` variant, exposing `.value`.
556
664
  *
557
665
  * @returns `true` when `r` is `Ok`.
@@ -718,9 +826,9 @@ function isDefectMarker(x) {
718
826
  * import { fromNullable } from "unthrown";
719
827
  *
720
828
  * const map = new Map([["a", 1]]);
721
- * fromNullable(map.get("a"), () => "absent").unwrapOr(0); // => 1
829
+ * fromNullable(map.get("a"), () => "absent").getOr(0); // => 1
722
830
  * fromNullable(map.get("z"), () => "absent"); // => Err("absent")
723
- * fromNullable(0, () => "absent").unwrapOr(-1); // => 0 (falsy but present)
831
+ * fromNullable(0, () => "absent").getOr(-1); // => 0 (falsy but present)
724
832
  * ```
725
833
  */
726
834
  function fromNullable(value, onAbsent) {
@@ -740,7 +848,7 @@ function fromNullable(value, onAbsent) {
740
848
  * `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
741
849
  * `qualify` that returns *only* `defect(cause)` yields `E = never` (a Defect is
742
850
  * out-of-band and must not pollute the error channel); reach for
743
- * {@link fromSafePromise} when every failure is a Defect.
851
+ * {@link fromSafeThrowable} when every throw is a Defect.
744
852
  *
745
853
  * @typeParam A - the wrapped function's argument tuple.
746
854
  * @typeParam T - the wrapped function's return type.
@@ -764,7 +872,7 @@ function fromNullable(value, onAbsent) {
764
872
  * cause instanceof SyntaxError ? ("invalid_json" as const) : defect(cause),
765
873
  * );
766
874
  *
767
- * parse('{"ok":true}').unwrapOr(null); // => { ok: true }
875
+ * parse('{"ok":true}').getOr(null); // => { ok: true }
768
876
  * parse("nope"); // => Err("invalid_json")
769
877
  * ```
770
878
  */
@@ -779,6 +887,44 @@ function fromThrowable(fn, qualify) {
779
887
  };
780
888
  }
781
889
  /**
890
+ * Wrap a throwing synchronous function asserted **not** to fail in any modeled
891
+ * way: any throw becomes a `Defect`.
892
+ *
893
+ * @remarks
894
+ * The synchronous counterpart of {@link fromSafePromise}. Use it only when a
895
+ * throw genuinely indicates a bug rather than an anticipated outcome — the
896
+ * error channel is `never`, so there is nothing to triage; there is no
897
+ * `qualify`. When some throws *are* anticipated, reach for
898
+ * {@link fromThrowable} and triage them.
899
+ *
900
+ * @typeParam A - the wrapped function's argument tuple.
901
+ * @typeParam T - the wrapped function's return type.
902
+ * @param fn - the throwing function to wrap.
903
+ * @returns a function with the same arguments returning `Result<T, never>`.
904
+ *
905
+ * @category Interop
906
+ *
907
+ * @example
908
+ * ```ts
909
+ * import { fromSafeThrowable } from "unthrown";
910
+ *
911
+ * // A decode failure here is a bug (the row came from our own schema), so
912
+ * // every throw is a defect — no throwaway `(cause, defect) => defect(cause)`.
913
+ * const decode = fromSafeThrowable((row: Row) => userSchema.parse(row));
914
+ *
915
+ * decode(row); // => Result<User, never> — a throw becomes a Defect
916
+ * ```
917
+ */
918
+ function fromSafeThrowable(fn) {
919
+ return (...args) => {
920
+ try {
921
+ return Ok(fn(...args));
922
+ } catch (cause) {
923
+ return defectRes(cause);
924
+ }
925
+ };
926
+ }
927
+ /**
782
928
  * Wrap a `Promise` (or a thunk producing one) as an {@link AsyncResult}, forcing
783
929
  * every rejection to be triaged.
784
930
  *
@@ -826,7 +972,8 @@ function fromPromise(promise, qualify) {
826
972
  * @remarks
827
973
  * Use this only when a rejection genuinely indicates a bug rather than an
828
974
  * anticipated outcome — the error channel is `never`, so there is nothing to
829
- * triage. (`await`-ing still yields a `Result`; it never throws.)
975
+ * triage. (`await`-ing still yields a `Result`; it never throws.) The
976
+ * synchronous counterpart is {@link fromSafeThrowable}.
830
977
  *
831
978
  * @typeParam T - the resolved value type.
832
979
  * @param promise - the promise, or a thunk returning one.
@@ -837,7 +984,7 @@ function fromPromise(promise, qualify) {
837
984
  * ```ts
838
985
  * import { fromSafePromise } from "unthrown";
839
986
  *
840
- * (await fromSafePromise(Promise.resolve(3))).unwrap(); // => 3
987
+ * (await fromSafePromise(Promise.resolve(3))).get(); // => 3
841
988
  * // a rejection becomes a Defect (never a modeled Err):
842
989
  * await fromSafePromise(Promise.reject(new Error("boom"))); // => Defect(Error("boom"))
843
990
  * ```
@@ -911,7 +1058,7 @@ function foldRecord(results) {
911
1058
  * ```ts
912
1059
  * import { all, Ok, Err } from "unthrown";
913
1060
  *
914
- * all([Ok(1), Ok("a"), Ok(true)]).unwrap(); // => [1, "a", true] (typed [number, string, boolean])
1061
+ * all([Ok(1), Ok("a"), Ok(true)]).get(); // => [1, "a", true] (typed [number, string, boolean])
915
1062
  * all([Ok(1), Err("e"), Ok(3)]); // => Err("e") (short-circuits on the first Err)
916
1063
  * ```
917
1064
  */
@@ -934,7 +1081,7 @@ function all(results) {
934
1081
  * ```ts
935
1082
  * import { allFromDict, Ok, Err } from "unthrown";
936
1083
  *
937
- * allFromDict({ id: Ok(1), name: Ok("ada") }).unwrap(); // => { id: 1, name: "ada" }
1084
+ * allFromDict({ id: Ok(1), name: Ok("ada") }).get(); // => { id: 1, name: "ada" }
938
1085
  * allFromDict({ id: Ok(1), name: Err("missing") }); // => Err("missing")
939
1086
  * ```
940
1087
  */
@@ -958,7 +1105,7 @@ function allFromDict(results) {
958
1105
  * import { allAsync, fromSafePromise } from "unthrown";
959
1106
  *
960
1107
  * const both = allAsync([fromSafePromise(Promise.resolve(1)), fromSafePromise(Promise.resolve(2))]);
961
- * (await both).unwrap(); // => [1, 2]
1108
+ * (await both).get(); // => [1, 2]
962
1109
  * ```
963
1110
  */
964
1111
  function allAsync(results) {
@@ -982,7 +1129,7 @@ function allAsync(results) {
982
1129
  * a: fromSafePromise(Promise.resolve(1)),
983
1130
  * b: fromSafePromise(Promise.resolve("x")),
984
1131
  * });
985
- * (await both).unwrap(); // => { a: 1, b: "x" }
1132
+ * (await both).get(); // => { a: 1, b: "x" }
986
1133
  * ```
987
1134
  */
988
1135
  function allFromDictAsync(results) {
@@ -1001,8 +1148,9 @@ function allFromDictAsync(results) {
1001
1148
  * Companion object grouping the **`Result`-producing** entry points under a
1002
1149
  * single, discoverable namespace: {@link Result.Ok}, {@link Result.Err},
1003
1150
  * {@link Result.Do}, {@link Result.fromNullable}, {@link Result.fromThrowable},
1004
- * {@link Result.all}, {@link Result.allFromDict}, {@link Result.isOk},
1005
- * {@link Result.isErr}, {@link Result.isDefect}, {@link Result.isResult}.
1151
+ * {@link Result.fromSafeThrowable}, {@link Result.all},
1152
+ * {@link Result.allFromDict}, {@link Result.isOk}, {@link Result.isErr},
1153
+ * {@link Result.isDefect}, {@link Result.isResult}.
1006
1154
  *
1007
1155
  * @remarks
1008
1156
  * Purely additive sugar — each member **is** the corresponding free function.
@@ -1019,7 +1167,7 @@ function allFromDictAsync(results) {
1019
1167
  * @example
1020
1168
  * ```ts
1021
1169
  * import { Result } from "unthrown";
1022
- * Result.Ok(1).flatMap((n) => Result.Ok(n + 1)).unwrap(); // => 2
1170
+ * Result.Ok(1).flatMap((n) => Result.Ok(n + 1)).get(); // => 2
1023
1171
  * ```
1024
1172
  */
1025
1173
  const Result = {
@@ -1028,6 +1176,7 @@ const Result = {
1028
1176
  Do,
1029
1177
  fromNullable,
1030
1178
  fromThrowable,
1179
+ fromSafeThrowable,
1031
1180
  all,
1032
1181
  allFromDict,
1033
1182
  isOk,
@@ -1037,18 +1186,20 @@ const Result = {
1037
1186
  };
1038
1187
  /**
1039
1188
  * Companion object grouping the **`AsyncResult`-producing** entry points under
1040
- * the matching namespace: {@link AsyncResult.fromPromise},
1041
- * {@link AsyncResult.fromSafePromise}, {@link AsyncResult.all},
1042
- * {@link AsyncResult.allFromDict}.
1189
+ * the matching namespace: {@link AsyncResult.Ok}, {@link AsyncResult.Err},
1190
+ * {@link AsyncResult.fromPromise}, {@link AsyncResult.fromSafePromise},
1191
+ * {@link AsyncResult.all}, {@link AsyncResult.allFromDict}.
1043
1192
  *
1044
1193
  * @remarks
1045
1194
  * The async sibling of {@link Result}. Statics are grouped by what they
1046
- * **return**, so `fromPromise`/`fromSafePromise` and the async aggregates sit
1047
- * here rather than on {@link Result}; the namespace already conveys "async", so
1048
- * the aggregates drop the `Async` suffix (`AsyncResult.all` is the free function
1049
- * `allAsync`; `AsyncResult.allFromDict` is `allFromDictAsync`). Like
1050
- * {@link Result}, the free functions remain the primary, tree-shakeable API; the
1051
- * value `AsyncResult` and the type {@link AsyncResult} share one name.
1195
+ * **return**, so the pre-lifted constructors, `fromPromise`/`fromSafePromise`,
1196
+ * and the async aggregates sit here rather than on {@link Result}; the namespace
1197
+ * already conveys "async", so the members drop the `Async` suffix their free
1198
+ * functions carry (`AsyncResult.Ok` is `OkAsync`; `AsyncResult.Err` is
1199
+ * `ErrAsync`; `AsyncResult.all` is `allAsync`; `AsyncResult.allFromDict` is
1200
+ * `allFromDictAsync`). Like {@link Result}, the free functions remain the
1201
+ * primary, tree-shakeable API; the value `AsyncResult` and the type
1202
+ * {@link AsyncResult} share one name.
1052
1203
  *
1053
1204
  * @category Facade
1054
1205
  *
@@ -1056,10 +1207,12 @@ const Result = {
1056
1207
  * ```ts
1057
1208
  * import { AsyncResult } from "unthrown";
1058
1209
  * const user = await AsyncResult.fromPromise(fetchUser(id), (c, defect) => defect(c));
1059
- * user.unwrap(); // => the fetched user (on success)
1210
+ * user.get(); // => the fetched user (on success)
1060
1211
  * ```
1061
1212
  */
1062
1213
  const AsyncResult = {
1214
+ Ok: OkAsync,
1215
+ Err: ErrAsync,
1063
1216
  fromPromise,
1064
1217
  fromSafePromise,
1065
1218
  all: allAsync,
@@ -1151,7 +1304,9 @@ function matchTags(result, handlers) {
1151
1304
  exports.AsyncResult = AsyncResult;
1152
1305
  exports.Do = Do;
1153
1306
  exports.Err = Err;
1307
+ exports.ErrAsync = ErrAsync;
1154
1308
  exports.Ok = Ok;
1309
+ exports.OkAsync = OkAsync;
1155
1310
  exports.Result = Result;
1156
1311
  exports.TaggedError = TaggedError;
1157
1312
  exports.UnwrapError = UnwrapError;
@@ -1162,6 +1317,7 @@ exports.allFromDictAsync = allFromDictAsync;
1162
1317
  exports.fromNullable = fromNullable;
1163
1318
  exports.fromPromise = fromPromise;
1164
1319
  exports.fromSafePromise = fromSafePromise;
1320
+ exports.fromSafeThrowable = fromSafeThrowable;
1165
1321
  exports.fromThrowable = fromThrowable;
1166
1322
  exports.isDefect = isDefect;
1167
1323
  exports.isErr = isErr;