@shirudo/result 0.0.2 → 0.0.4

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 CHANGED
@@ -263,6 +263,8 @@ const result = await matchAsync({
263
263
  - `.serialize()`: Convert to `{ isSuccess, data?, error? }`.
264
264
  - `.toUserFriendly()`: User-friendly serialization with error messages.
265
265
 
266
+ **Error types:** Methods that throw (e.g., `.unwrap()`, `.unwrapErr()`, `.expect()`, `.expectErr()`, and invalid-state checks) now use custom error classes with stable `code` values like `ERR_UNWRAP_ON_ERR` and `ERR_INVALID_STATE`. These classes and constants are exported from the package for programmatic handling.
267
+
266
268
  ### Utilities
267
269
 
268
270
  Type guards and helper functions:
@@ -350,7 +352,7 @@ swap(err("error")); // Ok("error")
350
352
  - `sequenceRecord(record)`: Like `sequence`, but for objects (`{ a: Result, b: Result }` → `Result<{ a, b }>`).
351
353
  - `collectFirstOk(results)`: Find the first success, or return all errors.
352
354
  - `collectFirstOkAsync(results)`: Async version - find the first success.
353
- - `collectFirstOkRaceAsync(results)`: Race variant - first success wins, all rejections continue.
355
+ - `collectFirstOkParallelAsync(results)`: Parallel variant - first success wins, all rejections continue.
354
356
  - `collectAllErrors(results)`: Returns `Ok(values)` only if all are Ok, otherwise collects _all_ errors.
355
357
  - `partition(results)`: Separate a list into arrays of `[oks, errs]`.
356
358
  - `flatten(result)`: Flattens a nested `Result<Result<T, E>, E>` into `Result<T, E>`.
package/dist/index.cjs CHANGED
@@ -13,6 +13,88 @@ var Pipeable = class {
13
13
  }
14
14
  };
15
15
 
16
+ //#endregion
17
+ //#region src/errors.ts
18
+ const ERR_INVALID_STATE = "ERR_INVALID_STATE";
19
+ const ERR_TASK_YIELD_NOT_RESULT = "ERR_TASK_YIELD_NOT_RESULT";
20
+ const ERR_MATCH_ON_OK = "ERR_MATCH_ON_OK";
21
+ const ERR_UNWRAP_ON_ERR = "ERR_UNWRAP_ON_ERR";
22
+ const ERR_UNWRAP_ERR_ON_OK = "ERR_UNWRAP_ERR_ON_OK";
23
+ const ERR_EXPECT_OK = "ERR_EXPECT_OK";
24
+ const ERR_EXPECT_ERR = "ERR_EXPECT_ERR";
25
+ const formatResultErrorMessage = (code, message, context) => {
26
+ if (context) return `${code}: ${message} (context: ${context})`;
27
+ return `${code}: ${message}`;
28
+ };
29
+ var ResultError = class extends Error {
30
+ code;
31
+ context;
32
+ constructor(message, code, context) {
33
+ super(formatResultErrorMessage(code, message, context));
34
+ this.code = code;
35
+ this.context = context;
36
+ this.name = new.target.name;
37
+ Object.setPrototypeOf(this, new.target.prototype);
38
+ }
39
+ };
40
+ var ResultTypeError = class extends TypeError {
41
+ code;
42
+ context;
43
+ constructor(message, code, context) {
44
+ super(formatResultErrorMessage(code, message, context));
45
+ this.code = code;
46
+ this.context = context;
47
+ this.name = new.target.name;
48
+ Object.setPrototypeOf(this, new.target.prototype);
49
+ }
50
+ };
51
+ const INVALID_RESULT_STATE_MESSAGE = "Unreachable: Result is neither Ok nor Err";
52
+ var InvalidResultStateError = class extends ResultError {
53
+ constructor(context) {
54
+ super(INVALID_RESULT_STATE_MESSAGE, ERR_INVALID_STATE, context);
55
+ }
56
+ };
57
+ var TaskYieldNotResultError = class extends ResultTypeError {
58
+ yieldedValue;
59
+ constructor(yieldedValue) {
60
+ super("task() expected yielded values to be Result. Use `yield*` on a Result.", ERR_TASK_YIELD_NOT_RESULT);
61
+ this.yieldedValue = yieldedValue;
62
+ }
63
+ };
64
+ var MatchOnOkError = class extends ResultTypeError {
65
+ constructor() {
66
+ super("match() can only be called on Err results. Use `if (result.isErr()) { ... }` first.", ERR_MATCH_ON_OK);
67
+ }
68
+ };
69
+ var UnwrapOnErrError = class extends ResultTypeError {
70
+ errorValue;
71
+ constructor(errorValue) {
72
+ super(`Called unwrap() on Err: ${String(errorValue)}`, ERR_UNWRAP_ON_ERR);
73
+ this.errorValue = errorValue;
74
+ }
75
+ };
76
+ var UnwrapErrOnOkError = class extends ResultTypeError {
77
+ okValue;
78
+ constructor(okValue) {
79
+ super(`Called unwrapErr() on Ok: ${String(okValue)}`, ERR_UNWRAP_ERR_ON_OK);
80
+ this.okValue = okValue;
81
+ }
82
+ };
83
+ var ExpectOkError = class extends ResultError {
84
+ expectedMessage;
85
+ constructor(expectedMessage) {
86
+ super(expectedMessage, ERR_EXPECT_OK);
87
+ this.expectedMessage = expectedMessage;
88
+ }
89
+ };
90
+ var ExpectErrError = class extends ResultError {
91
+ expectedMessage;
92
+ constructor(expectedMessage) {
93
+ super(expectedMessage, ERR_EXPECT_ERR);
94
+ this.expectedMessage = expectedMessage;
95
+ }
96
+ };
97
+
16
98
  //#endregion
17
99
  //#region src/core/matcher.ts
18
100
  /**
@@ -75,7 +157,7 @@ var ErrMatchBuilder = class ErrMatchBuilder {
75
157
  static fromResult(result, makeErr) {
76
158
  if (result.isOk()) return new ErrMatchBuilder(makeErr, void 0, result);
77
159
  if (result.isErr()) return new ErrMatchBuilder(makeErr, result.error);
78
- throw new Error("Unreachable: Result is neither Ok nor Err");
160
+ throw new InvalidResultStateError("ErrMatchBuilder.fromResult");
79
161
  }
80
162
  when(ctor, handler) {
81
163
  if (this.#resolved) return this;
@@ -143,7 +225,7 @@ function mapBoth(mapOk, mapErr$1) {
143
225
  return (source) => {
144
226
  if (source.isOk()) return ok(mapOk(source.value));
145
227
  if (source.isErr()) return err(mapErr$1(source.error));
146
- throw new Error("Unreachable: Result is neither Ok nor Err");
228
+ throw new InvalidResultStateError("mapBoth");
147
229
  };
148
230
  }
149
231
  /**
@@ -170,7 +252,7 @@ function zipImpl(left, right) {
170
252
  if (left.isErr()) return left;
171
253
  if (right.isErr()) return right;
172
254
  if (left.isOk() && right.isOk()) return ok([left.value, right.value]);
173
- throw new Error("Unreachable: Result is neither Ok nor Err");
255
+ throw new InvalidResultStateError("zip");
174
256
  }
175
257
  function zip(...args) {
176
258
  if (args.length === 1) {
@@ -236,7 +318,7 @@ function match(handlers) {
236
318
  return (source) => {
237
319
  if (source.isOk()) return handlers.ok(source.value);
238
320
  if (source.isErr()) return handlers.err(source.error);
239
- throw new Error("Unreachable: Result is neither Ok nor Err");
321
+ throw new InvalidResultStateError("match");
240
322
  };
241
323
  }
242
324
 
@@ -250,7 +332,7 @@ function recover(defaultValue) {
250
332
  return (source) => {
251
333
  if (source.isOk()) return source;
252
334
  if (source.isErr()) return ok(defaultValue);
253
- throw new Error("Unreachable: Result is neither Ok nor Err");
335
+ throw new InvalidResultStateError("recover");
254
336
  };
255
337
  }
256
338
  /**
@@ -260,7 +342,7 @@ function recoverWith(fn) {
260
342
  return (source) => {
261
343
  if (source.isOk()) return source;
262
344
  if (source.isErr()) return ok(fn(source.error));
263
- throw new Error("Unreachable: Result is neither Ok nor Err");
345
+ throw new InvalidResultStateError("recoverWith");
264
346
  };
265
347
  }
266
348
 
@@ -273,7 +355,7 @@ function recoverWith(fn) {
273
355
  function swap(result) {
274
356
  if (result.isOk()) return err(result.value);
275
357
  if (result.isErr()) return ok(result.error);
276
- throw new Error("Unreachable: Result is neither Ok nor Err");
358
+ throw new InvalidResultStateError("swap");
277
359
  }
278
360
 
279
361
  //#endregion
@@ -304,7 +386,7 @@ function tryMap(project, errorMapper) {
304
386
  if (source.isErr()) return source;
305
387
  try {
306
388
  if (source.isOk()) return ok(project(source.value));
307
- throw new Error("Unreachable: Result is neither Ok nor Err");
389
+ throw new InvalidResultStateError("tryMap");
308
390
  } catch (error) {
309
391
  return err(errorMapper ? errorMapper(error) : error);
310
392
  }
@@ -326,7 +408,7 @@ function collectFirstOk(results) {
326
408
  errors.push(result.error);
327
409
  continue;
328
410
  }
329
- throw new Error("Unreachable: Result is neither Ok nor Err");
411
+ throw new InvalidResultStateError("collectFirstOk");
330
412
  }
331
413
  return err(errors);
332
414
  }
@@ -404,7 +486,7 @@ function matchAsync(handlers) {
404
486
  return async (source) => {
405
487
  if (source.isOk()) return await handlers.ok(source.value);
406
488
  if (source.isErr()) return await handlers.err(source.error);
407
- throw new Error("Unreachable: Result is neither Ok nor Err");
489
+ throw new InvalidResultStateError("matchAsync");
408
490
  };
409
491
  }
410
492
 
@@ -434,7 +516,7 @@ function tryMapAsync(project, errorMapper) {
434
516
  if (source.isErr()) return source;
435
517
  try {
436
518
  if (source.isOk()) return ok(await project(source.value));
437
- throw new Error("Unreachable: Result is neither Ok nor Err");
519
+ throw new InvalidResultStateError("tryMapAsync");
438
520
  } catch (error) {
439
521
  return err(errorMapper ? errorMapper(error) : error);
440
522
  }
@@ -459,7 +541,7 @@ var ResultBase = class extends Pipeable {
459
541
  fold(onOk, onErr) {
460
542
  if (this._tag === "Ok") return onOk(this.value);
461
543
  if (this._tag === "Err") return onErr(this.error);
462
- throw new Error("Unreachable: Result is neither Ok nor Err");
544
+ throw new InvalidResultStateError("Result.fold");
463
545
  }
464
546
  /**
465
547
  * Enables `yield* result` in generators (Do-notation).
@@ -479,7 +561,7 @@ var ResultBase = class extends Pipeable {
479
561
  */
480
562
  match() {
481
563
  if (this._tag === "Err") return new ErrorMatchBuilder(this.error);
482
- throw new Error("match() can only be called on Err results. Use `if (result.isErr()) { ... }` first.");
564
+ throw new MatchOnOkError();
483
565
  }
484
566
  /**
485
567
  * Matcht auf den Err-Wert, aber normalisiert jeden Branch zu einem `Result`:
@@ -614,7 +696,7 @@ async function task(makeGenerator, onThrow) {
614
696
  return err(onThrow(caught));
615
697
  }
616
698
  const yielded = step.value;
617
- if (!isResult(yielded)) throw new TypeError("task() expected yielded values to be Result. Use `yield*` on a Result.");
699
+ if (!isResult(yielded)) throw new TaskYieldNotResultError(yielded);
618
700
  if (yielded.isOk()) {
619
701
  input = yielded.value;
620
702
  continue;
@@ -628,7 +710,7 @@ async function task(makeGenerator, onThrow) {
628
710
  }
629
711
  return yielded;
630
712
  }
631
- throw new Error("Unreachable: Result is neither Ok nor Err");
713
+ throw new InvalidResultStateError("task");
632
714
  }
633
715
  }
634
716
  const gen = task;
@@ -645,7 +727,7 @@ function fold(handlers) {
645
727
  return (source) => {
646
728
  if (source.isOk()) return handlers.ok(source.value);
647
729
  if (source.isErr()) return handlers.err(source.error);
648
- throw new Error("Unreachable: Result is neither Ok nor Err");
730
+ throw new InvalidResultStateError("fold");
649
731
  };
650
732
  }
651
733
 
@@ -661,7 +743,7 @@ function foldAsync(handlers) {
661
743
  return async (source) => {
662
744
  if (source.isOk()) return await handlers.ok(source.value);
663
745
  if (source.isErr()) return await handlers.err(source.error);
664
- throw new Error("Unreachable: Result is neither Ok nor Err");
746
+ throw new InvalidResultStateError("foldAsync");
665
747
  };
666
748
  }
667
749
 
@@ -673,8 +755,8 @@ function foldAsync(handlers) {
673
755
  */
674
756
  function unwrap(result) {
675
757
  if (result.isOk()) return result.value;
676
- if (result.isErr()) throw new Error(`Called unwrap() on Err: ${String(result.error)}`);
677
- throw new Error("Unreachable: Result is neither Ok nor Err");
758
+ if (result.isErr()) throw new UnwrapOnErrError(result.error);
759
+ throw new InvalidResultStateError("unwrap");
678
760
  }
679
761
 
680
762
  //#endregion
@@ -697,7 +779,7 @@ function unwrapOr(result, defaultValue) {
697
779
  function unwrapOrElse(result, fn) {
698
780
  if (result.isOk()) return result.value;
699
781
  if (result.isErr()) return fn(result.error);
700
- throw new Error("Unreachable: Result is neither Ok nor Err");
782
+ throw new InvalidResultStateError("unwrapOrElse");
701
783
  }
702
784
 
703
785
  //#endregion
@@ -719,7 +801,7 @@ function unwrapOrDefault(result, defaultValue) {
719
801
  function unwrapOrThrow(result) {
720
802
  if (result.isOk()) return result.value;
721
803
  if (result.isErr()) throw result.error;
722
- throw new Error("Unreachable: Result is neither Ok nor Err");
804
+ throw new InvalidResultStateError("unwrapOrThrow");
723
805
  }
724
806
 
725
807
  //#endregion
@@ -730,8 +812,8 @@ function unwrapOrThrow(result) {
730
812
  */
731
813
  function unwrapErr(result) {
732
814
  if (result.isErr()) return result.error;
733
- if (result.isOk()) throw new Error(`Called unwrapErr() on Ok: ${String(result.value)}`);
734
- throw new Error("Unreachable: Result is neither Ok nor Err");
815
+ if (result.isOk()) throw new UnwrapErrOnOkError(result.value);
816
+ throw new InvalidResultStateError("unwrapErr");
735
817
  }
736
818
 
737
819
  //#endregion
@@ -742,7 +824,7 @@ function unwrapErr(result) {
742
824
  */
743
825
  function expectResult(result, message) {
744
826
  if (result.isOk()) return result.value;
745
- throw new Error(message);
827
+ throw new ExpectOkError(message);
746
828
  }
747
829
 
748
830
  //#endregion
@@ -753,7 +835,7 @@ function expectResult(result, message) {
753
835
  */
754
836
  function expectErr(result, message) {
755
837
  if (result.isErr()) return result.error;
756
- throw new Error(message);
838
+ throw new ExpectErrError(message);
757
839
  }
758
840
 
759
841
  //#endregion
@@ -785,7 +867,7 @@ function or(result, other) {
785
867
  function orElse(result, fn) {
786
868
  if (result.isOk()) return result;
787
869
  if (result.isErr()) return fn(result.error);
788
- throw new Error("Unreachable: Result is neither Ok nor Err");
870
+ throw new InvalidResultStateError("orElse");
789
871
  }
790
872
 
791
873
  //#endregion
@@ -808,7 +890,7 @@ function mapOr(result, defaultValue, fn) {
808
890
  function mapOrElse(result, defaultFn, fn) {
809
891
  if (result.isOk()) return fn(result.value);
810
892
  if (result.isErr()) return defaultFn(result.error);
811
- throw new Error("Unreachable: Result is neither Ok nor Err");
893
+ throw new InvalidResultStateError("mapOrElse");
812
894
  }
813
895
 
814
896
  //#endregion
@@ -826,7 +908,7 @@ function sequence(results) {
826
908
  continue;
827
909
  }
828
910
  if (result.isErr()) return result;
829
- throw new Error("Unreachable: Result is neither Ok nor Err");
911
+ throw new InvalidResultStateError("sequence");
830
912
  }
831
913
  return ok(values);
832
914
  }
@@ -853,7 +935,7 @@ function sequenceRecord(record) {
853
935
  continue;
854
936
  }
855
937
  if (result.isErr()) return result;
856
- throw new Error("Unreachable: Result is neither Ok nor Err");
938
+ throw new InvalidResultStateError("sequenceRecord");
857
939
  }
858
940
  return ok(out);
859
941
  }
@@ -876,7 +958,7 @@ async function collectFirstOkAsync(inputs) {
876
958
  errors.push(result.error);
877
959
  continue;
878
960
  }
879
- throw new Error("Unreachable: Result is neither Ok nor Err");
961
+ throw new InvalidResultStateError("collectFirstOkAsync");
880
962
  } catch (error) {
881
963
  errors.push(error);
882
964
  }
@@ -884,53 +966,36 @@ async function collectFirstOkAsync(inputs) {
884
966
  }
885
967
 
886
968
  //#endregion
887
- //#region src/core/collectFirstOkRaceAsync.ts
969
+ //#region src/core/collectFirstOkParallelAsync.ts
888
970
  /**
889
- * Parallel/Race-Variante von `collectFirstOkAsync`.
971
+ * Parallel-Variante von `collectFirstOkAsync`.
890
972
  *
891
973
  * - Startet alle Inputs sofort (Promises oder Thunks).
892
974
  * - Gibt das erste `Ok` zurück, sobald es verfügbar ist.
893
975
  * - Wenn kein `Ok` gefunden wird, gibt ein `Err` mit allen Error-Werten (in Input-Reihenfolge) zurück.
976
+ * - Rejections werden als `ErrValue` behandelt (`caught as ErrValue`).
977
+ * - Wenn mehrere Inputs ein `Ok` liefern, gewinnt das zuerst abgeschlossene Ergebnis.
978
+ * Bei gleichzeitiger Completion gewinnt das zuerst beobachtete Ergebnis.
979
+ * - Wenn kein `Ok` kommt und mindestens ein Input nie settled, bleibt das Promise offen.
894
980
  */
895
- async function collectFirstOkRaceAsync(inputs) {
981
+ async function collectFirstOkParallelAsync(inputs) {
896
982
  if (inputs.length === 0) return err([]);
897
- const UNSET = Symbol("unset");
898
- const errorsByIndex = Array(inputs.length).fill(UNSET);
899
- return new Promise((resolve) => {
900
- let done = false;
901
- let remaining = inputs.length;
902
- const finishAllErr = () => {
903
- const errors = [];
904
- for (const entry of errorsByIndex) if (entry !== UNSET) errors.push(entry);
905
- resolve(err(errors));
906
- };
907
- const settleError = (index, errorValue) => {
908
- if (done) return;
909
- errorsByIndex[index] = errorValue;
910
- remaining -= 1;
911
- if (remaining === 0) {
912
- done = true;
913
- finishAllErr();
914
- }
915
- };
916
- inputs.forEach((input, index) => {
917
- Promise.resolve().then(() => typeof input === "function" ? input() : input).then((result) => {
918
- if (done) return;
919
- if (result.isOk()) {
920
- done = true;
921
- resolve(ok(result.value));
922
- return;
923
- }
924
- if (result.isErr()) {
925
- settleError(index, result.error);
926
- return;
927
- }
928
- settleError(index, /* @__PURE__ */ new Error("Unreachable: Result is neither Ok nor Err"));
929
- }).catch((caught) => {
930
- settleError(index, caught);
931
- });
932
- });
983
+ const started = inputs.map((input) => typeof input === "function" ? Promise.resolve().then(input) : input);
984
+ const firstOk = new Promise((resolve) => {
985
+ for (const promise of started) promise.then((result) => {
986
+ if (result.isOk()) resolve(ok(result.value));
987
+ }).catch(() => {});
988
+ });
989
+ const allErrors = Promise.allSettled(started).then((settled) => {
990
+ const errors = [];
991
+ for (const entry of settled) if (entry.status === "fulfilled") {
992
+ const result = entry.value;
993
+ if (result.isErr()) errors.push(result.error);
994
+ else if (!result.isOk()) errors.push(new InvalidResultStateError("collectFirstOkParallelAsync"));
995
+ } else errors.push(entry.reason);
996
+ return err(errors);
933
997
  });
998
+ return Promise.race([firstOk, allErrors]);
934
999
  }
935
1000
 
936
1001
  //#endregion
@@ -951,7 +1016,7 @@ function collectAllErrors(results) {
951
1016
  errors.push(result.error);
952
1017
  continue;
953
1018
  }
954
- throw new Error("Unreachable: Result is neither Ok nor Err");
1019
+ throw new InvalidResultStateError("collectAllErrors");
955
1020
  }
956
1021
  return errors.length === 0 ? ok(values) : err(errors);
957
1022
  }
@@ -973,7 +1038,7 @@ function partition(results) {
973
1038
  errs.push(result.error);
974
1039
  continue;
975
1040
  }
976
- throw new Error("Unreachable: Result is neither Ok nor Err");
1041
+ throw new InvalidResultStateError("partition");
977
1042
  }
978
1043
  return [oks, errs];
979
1044
  }
@@ -999,7 +1064,7 @@ function flatten(result) {
999
1064
  function toPromise(result) {
1000
1065
  if (result.isOk()) return Promise.resolve(result.value);
1001
1066
  if (result.isErr()) return Promise.reject(result.error);
1002
- throw new Error("Unreachable: Result is neither Ok nor Err");
1067
+ throw new InvalidResultStateError("toPromise");
1003
1068
  }
1004
1069
 
1005
1070
  //#endregion
@@ -1056,16 +1121,32 @@ function containsErr(result, error) {
1056
1121
  }
1057
1122
 
1058
1123
  //#endregion
1124
+ exports.ERR_EXPECT_ERR = ERR_EXPECT_ERR;
1125
+ exports.ERR_EXPECT_OK = ERR_EXPECT_OK;
1126
+ exports.ERR_INVALID_STATE = ERR_INVALID_STATE;
1127
+ exports.ERR_MATCH_ON_OK = ERR_MATCH_ON_OK;
1128
+ exports.ERR_TASK_YIELD_NOT_RESULT = ERR_TASK_YIELD_NOT_RESULT;
1129
+ exports.ERR_UNWRAP_ERR_ON_OK = ERR_UNWRAP_ERR_ON_OK;
1130
+ exports.ERR_UNWRAP_ON_ERR = ERR_UNWRAP_ON_ERR;
1059
1131
  exports.Err = Err;
1132
+ exports.ExpectErrError = ExpectErrError;
1133
+ exports.ExpectOkError = ExpectOkError;
1134
+ exports.InvalidResultStateError = InvalidResultStateError;
1135
+ exports.MatchOnOkError = MatchOnOkError;
1060
1136
  exports.Ok = Ok;
1061
1137
  exports.Result = Result;
1138
+ exports.ResultError = ResultError;
1139
+ exports.ResultTypeError = ResultTypeError;
1140
+ exports.TaskYieldNotResultError = TaskYieldNotResultError;
1141
+ exports.UnwrapErrOnOkError = UnwrapErrOnOkError;
1142
+ exports.UnwrapOnErrError = UnwrapOnErrError;
1062
1143
  exports.all = all;
1063
1144
  exports.and = and;
1064
1145
  exports.bimap = bimap;
1065
1146
  exports.collectAllErrors = collectAllErrors;
1066
1147
  exports.collectFirstOk = collectFirstOk;
1067
1148
  exports.collectFirstOkAsync = collectFirstOkAsync;
1068
- exports.collectFirstOkRaceAsync = collectFirstOkRaceAsync;
1149
+ exports.collectFirstOkParallelAsync = collectFirstOkParallelAsync;
1069
1150
  exports.combine = combine;
1070
1151
  exports.contains = contains;
1071
1152
  exports.containsErr = containsErr;