resultar 3.0.0-alpha.3 → 3.1.0-beta.1

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.js CHANGED
@@ -1,3 +1,14 @@
1
+ //#region src/abort-error.ts
2
+ var AbortError = class extends Error {
3
+ constructor(message = "Operation aborted", options) {
4
+ super(message, options);
5
+ this.name = "AbortError";
6
+ }
7
+ };
8
+ const hasAbortName = (value) => typeof value === "object" && value !== null && "name" in value && value.name === "AbortError";
9
+ const hasAbortCode = (value) => typeof value === "object" && value !== null && "code" in value && value.code === "ABORT_ERR";
10
+ const isAbortError = (value) => value instanceof AbortError || hasAbortName(value) || hasAbortCode(value);
11
+ //#endregion
1
12
  //#region src/pipe.ts
2
13
  const runPipe = (self, fns) => {
3
14
  const first = fns[0];
@@ -79,6 +90,14 @@ const combineResultList = (resultList) => {
79
90
  }
80
91
  return ok(values);
81
92
  };
93
+ const combineResultRecord = (resultRecord) => {
94
+ const values = {};
95
+ for (const [key, result] of Object.entries(resultRecord)) {
96
+ if (result.isErr()) return err(result.error);
97
+ values[key] = result.value;
98
+ }
99
+ return ok(values);
100
+ };
82
101
  const combineResultListWithAllErrors = (resultList) => {
83
102
  let acc = ok([]);
84
103
  for (const result of resultList) if (result.isErr() && acc.isErr()) acc.error.push(result.error);
@@ -86,6 +105,15 @@ const combineResultListWithAllErrors = (resultList) => {
86
105
  else if (result.isOk() && acc.isOk()) acc.value.push(result.value);
87
106
  return acc;
88
107
  };
108
+ const combineResultRecordWithAllErrors = (resultRecord) => {
109
+ const errors = [];
110
+ const values = {};
111
+ for (const [key, result] of Object.entries(resultRecord)) if (result.isErr()) errors.push(result.error);
112
+ else values[key] = result.value;
113
+ if (errors.length > 0) return err(errors);
114
+ return ok(values);
115
+ };
116
+ const isIterable$1 = (value) => typeof value === "object" && value !== null && Symbol.iterator in value;
89
117
  const validateAllResultItems = (items, f) => {
90
118
  return combineResultListWithAllErrors(Array.from(items, f));
91
119
  };
@@ -106,6 +134,7 @@ const firstSuccessOfResultCandidates = (candidates) => {
106
134
  if (!hasCandidates || latestError === void 0) throw createEmptyResultsCollectionError();
107
135
  return latestError;
108
136
  };
137
+ const getReason$1 = (value) => typeof value === "object" && value !== null && "reason" in value ? value.reason : void 0;
109
138
  const loopResult = (initial, options) => {
110
139
  let state = initial;
111
140
  const values = [];
@@ -223,11 +252,15 @@ var ResultNamespace = class {
223
252
  static err(error) {
224
253
  return err(error);
225
254
  }
226
- static combine(resultList) {
227
- return combineResultList(resultList);
255
+ static combine(input) {
256
+ if (Array.isArray(input)) return combineResultList(input);
257
+ if (isIterable$1(input)) return combineResultList([...input]);
258
+ return combineResultRecord(input);
228
259
  }
229
- static combineWithAllErrors(resultList) {
230
- return combineResultListWithAllErrors(resultList);
260
+ static combineWithAllErrors(input) {
261
+ if (Array.isArray(input)) return combineResultListWithAllErrors(input);
262
+ if (isIterable$1(input)) return combineResultListWithAllErrors([...input]);
263
+ return combineResultRecordWithAllErrors(input);
231
264
  }
232
265
  static validateAll(resultListOrItems, f) {
233
266
  if (f === void 0) return combineResultListWithAllErrors(resultListOrItems);
@@ -314,6 +347,15 @@ var Ok = class extends ResultVariant {
314
347
  catchTags(_handlers) {
315
348
  return ok(this.value);
316
349
  }
350
+ catchReason(_errorTag, _reasonTag, _f) {
351
+ return ok(this.value);
352
+ }
353
+ catchReasons(_errorTag, _handlers) {
354
+ return ok(this.value);
355
+ }
356
+ unwrapReason(_errorTag) {
357
+ return ok(this.value);
358
+ }
317
359
  branchResult(fCondition, fTrue, fFalse) {
318
360
  return fCondition(this.value) ? fTrue(this.value) : fFalse(this.value);
319
361
  }
@@ -415,6 +457,27 @@ var Err = class extends ResultVariant {
415
457
  if (handled.matched) return handled.value;
416
458
  return err(error);
417
459
  }
460
+ catchReason(errorTag, reasonTag, f) {
461
+ if (hasTag(this.error, errorTag)) {
462
+ const reason = getReason$1(this.error);
463
+ if (hasTag(reason, reasonTag)) return f(reason, this.error);
464
+ }
465
+ return err(this.error);
466
+ }
467
+ catchReasons(errorTag, handlers) {
468
+ if (hasTag(this.error, errorTag)) {
469
+ const reason = getReason$1(this.error);
470
+ if (typeof reason === "object" && reason !== null && "_tag" in reason) {
471
+ const handler = handlers[reason._tag];
472
+ if (handler !== void 0) return handler(reason, this.error);
473
+ }
474
+ }
475
+ return err(this.error);
476
+ }
477
+ unwrapReason(errorTag) {
478
+ if (hasTag(this.error, errorTag)) return err(getReason$1(this.error));
479
+ return err(this.error);
480
+ }
418
481
  asyncAndThen(_f) {
419
482
  return createResultAsync(Promise.resolve(err(this.error)));
420
483
  }
@@ -559,9 +622,223 @@ function isTryResultAsyncOptions(input) {
559
622
  return typeof input === "object" && input !== null && "try" in input;
560
623
  }
561
624
  const combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen((resultList) => Result.combine(resultList));
625
+ const collectResultAsyncRecord = async (asyncResultRecord) => {
626
+ const entries = Object.entries(asyncResultRecord);
627
+ const resultList = await Promise.all(entries.map((entry) => entry[1]));
628
+ const resultRecord = {};
629
+ for (const [index, [key]] of entries.entries()) {
630
+ const result = resultList[index];
631
+ if (result !== void 0) resultRecord[key] = result;
632
+ }
633
+ return resultRecord;
634
+ };
635
+ const combineResultAsyncRecord = (asyncResultRecord) => {
636
+ return new ResultAsync(collectResultAsyncRecord(asyncResultRecord).then((resultRecord) => Result.combine(resultRecord)));
637
+ };
562
638
  const combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen((resultList) => Result.combineWithAllErrors(resultList));
563
- const validateAllResultAsyncItems = (items, f) => {
564
- return combineResultAsyncListWithAllErrors(Array.from(items, f));
639
+ const combineResultAsyncRecordWithAllErrors = (asyncResultRecord) => {
640
+ return new ResultAsync(collectResultAsyncRecord(asyncResultRecord).then((resultRecord) => Result.combineWithAllErrors(resultRecord)));
641
+ };
642
+ const isIterable = (value) => typeof value === "object" && value !== null && Symbol.iterator in value;
643
+ const createIllegalArgumentException = (message) => {
644
+ const error = new Error(message);
645
+ error.name = "IllegalArgumentException";
646
+ return error;
647
+ };
648
+ const createInvalidResultAsyncConcurrencyError = () => createIllegalArgumentException("ResultAsync concurrency must be a positive integer or \"unbounded\"");
649
+ const normalizeResultAsyncConcurrency = (concurrency) => {
650
+ if (concurrency === void 0) return 1;
651
+ if (concurrency === "unbounded") return Number.POSITIVE_INFINITY;
652
+ if (Number.isInteger(concurrency) && concurrency > 0) return concurrency;
653
+ throw createInvalidResultAsyncConcurrencyError();
654
+ };
655
+ const toResultAsyncRejectionError = (error) => error instanceof Error ? error : new Error(String(error));
656
+ const sortIndexedResultAsyncErrors = (errors) => [...errors].toSorted((left, right) => left.index - right.index);
657
+ const createResultAsyncTraversalState = () => ({
658
+ activeCount: 0,
659
+ errors: [],
660
+ iteratorDone: false,
661
+ nextIndex: 0,
662
+ stopScheduling: false,
663
+ values: []
664
+ });
665
+ const runConcurrentResultAsyncItems = (options) => Promise.try(async () => {
666
+ const concurrency = normalizeResultAsyncConcurrency(options.concurrency);
667
+ const iterator = options.items[Symbol.iterator]();
668
+ const state = createResultAsyncTraversalState();
669
+ return new Promise((resolve, reject) => {
670
+ let settled = false;
671
+ const rejectOnce = (error) => {
672
+ if (!settled) {
673
+ settled = true;
674
+ reject(toResultAsyncRejectionError(error));
675
+ }
676
+ };
677
+ const resolveOnce = (result) => {
678
+ if (!settled) {
679
+ settled = true;
680
+ resolve(result);
681
+ }
682
+ };
683
+ const finishIfDone = () => {
684
+ const result = options.finish(state);
685
+ if (result !== void 0) resolveOnce(result);
686
+ };
687
+ const readNext = () => {
688
+ try {
689
+ return iterator.next();
690
+ } catch (error) {
691
+ rejectOnce(error);
692
+ return;
693
+ }
694
+ };
695
+ const schedule = () => {
696
+ if (settled) return;
697
+ if (state.stopScheduling) {
698
+ finishIfDone();
699
+ return;
700
+ }
701
+ while (!state.iteratorDone && state.activeCount < concurrency) {
702
+ const next = readNext();
703
+ if (next === void 0) return;
704
+ if (next.done === true) {
705
+ state.iteratorDone = true;
706
+ break;
707
+ }
708
+ const currentIndex = state.nextIndex;
709
+ state.nextIndex += 1;
710
+ startItem(next.value, currentIndex);
711
+ }
712
+ finishIfDone();
713
+ };
714
+ const startItem = (item, currentIndex) => {
715
+ state.activeCount += 1;
716
+ try {
717
+ const resultAsync = options.run(item, currentIndex);
718
+ Promise.resolve(resultAsync).then((result) => {
719
+ options.onResult(result, currentIndex, state);
720
+ }, rejectOnce).finally(() => {
721
+ if (settled) return;
722
+ state.activeCount -= 1;
723
+ schedule();
724
+ finishIfDone();
725
+ });
726
+ } catch (error) {
727
+ rejectOnce(error);
728
+ }
729
+ };
730
+ schedule();
731
+ });
732
+ });
733
+ const createResultAsyncRetryContext = (attempt, times) => ({
734
+ attempt,
735
+ nextAttempt: attempt + 1,
736
+ retriesRemaining: Math.max(0, times - attempt)
737
+ });
738
+ const createResultAsyncRetryAbortError = (signal) => signal.reason === void 0 ? new AbortError("ResultAsync retry aborted") : new AbortError("ResultAsync retry aborted", { cause: signal.reason });
739
+ const createResultAsyncResourceAbortError = (signal) => signal.reason === void 0 ? new AbortError("ResultAsync resource scope aborted") : new AbortError("ResultAsync resource scope aborted", { cause: signal.reason });
740
+ const validateResultAsyncRetryTimes = (times) => {
741
+ if (!Number.isInteger(times) || times < 0) throw createIllegalArgumentException("ResultAsync retry times must be a non-negative integer");
742
+ };
743
+ const validateResultAsyncRetryDelayValue = (delayMs) => {
744
+ if (!Number.isFinite(delayMs) || delayMs < 0) throw createIllegalArgumentException("ResultAsync retry delayMs must be a non-negative finite number");
745
+ };
746
+ const validateResultAsyncRetryStaticDelay = (delay) => {
747
+ if (typeof delay === "number") validateResultAsyncRetryDelayValue(delay);
748
+ };
749
+ const normalizeResultAsyncRetryDelay = (delay, context) => {
750
+ const delayMs = typeof delay === "function" ? delay(context) : delay ?? 0;
751
+ validateResultAsyncRetryDelayValue(delayMs);
752
+ return delayMs;
753
+ };
754
+ const waitResultAsyncRetryDelay = async (delayMs, signal) => {
755
+ if (signal.aborted) return Result.err(createResultAsyncRetryAbortError(signal));
756
+ if (delayMs === 0) return Result.ok(void 0);
757
+ return new Promise((resolve) => {
758
+ const timeout = setTimeout(() => {
759
+ signal.removeEventListener("abort", abort);
760
+ resolve(Result.ok(void 0));
761
+ }, delayMs);
762
+ const abort = () => {
763
+ clearTimeout(timeout);
764
+ resolve(Result.err(createResultAsyncRetryAbortError(signal)));
765
+ };
766
+ signal.addEventListener("abort", abort, { once: true });
767
+ });
768
+ };
769
+ const callResultAsyncRetryOnRetry = async (options, error, context) => {
770
+ try {
771
+ await options.onRetry?.(error, context);
772
+ } catch {}
773
+ };
774
+ const shouldRetryResultAsyncError = async (options, error, context) => options.while?.(error, context) ?? true;
775
+ const resolveResultAsyncRetryRecovery = async (recovered) => recovered instanceof ResultAsync ? recovered : recovered;
776
+ const runResultAsyncRetryAttempts = async (task, options, recover) => {
777
+ validateResultAsyncRetryTimes(options.times);
778
+ validateResultAsyncRetryStaticDelay(options.delayMs);
779
+ const signal = options.signal ?? new AbortController().signal;
780
+ let attempt = 0;
781
+ while (true) {
782
+ if (signal.aborted) return Result.err(createResultAsyncRetryAbortError(signal));
783
+ const result = await task(attempt, signal);
784
+ if (result.isOk()) return Result.ok(result.value);
785
+ const context = createResultAsyncRetryContext(attempt, options.times);
786
+ if (!await shouldRetryResultAsyncError(options, result.error, context) || attempt >= options.times) return resolveResultAsyncRetryRecovery(recover(result.error, context));
787
+ await callResultAsyncRetryOnRetry(options, result.error, context);
788
+ const delayResult = await waitResultAsyncRetryDelay(normalizeResultAsyncRetryDelay(options.delayMs, context), signal);
789
+ if (delayResult.isErr()) return Result.err(delayResult.error);
790
+ attempt += 1;
791
+ }
792
+ };
793
+ const retryResultAsyncTask = (task, options) => new ResultAsync(runResultAsyncRetryAttempts(task, options, (error) => Result.err(error)));
794
+ const retryOrElseResultAsyncTask = (task, options) => new ResultAsync(runResultAsyncRetryAttempts(task, options, (error, context) => options.orElse(error, context)));
795
+ const callResultAsyncResourceRelease = async (release, resource, context) => {
796
+ try {
797
+ await release(resource, context);
798
+ } catch {}
799
+ };
800
+ const runResultAsyncWithResource = async (options) => {
801
+ const signal = options.signal ?? new AbortController().signal;
802
+ if (signal.aborted) return Result.err(createResultAsyncResourceAbortError(signal));
803
+ const acquired = await options.acquire(signal);
804
+ if (acquired.isErr()) return Result.err(acquired.error);
805
+ let result = void 0;
806
+ try {
807
+ if (signal.aborted) {
808
+ result = Result.err(createResultAsyncResourceAbortError(signal));
809
+ return result;
810
+ }
811
+ result = await toResultAsync(options.use(acquired.value, signal));
812
+ return result;
813
+ } finally {
814
+ await callResultAsyncResourceRelease(options.release, acquired.value, {
815
+ result,
816
+ signal
817
+ });
818
+ }
819
+ };
820
+ const withResultAsyncResource = (options) => new ResultAsync(runResultAsyncWithResource(options));
821
+ const validateAllResultAsyncItems = (items, f, options) => {
822
+ return new ResultAsync(runConcurrentResultAsyncItems({
823
+ concurrency: options?.concurrency,
824
+ finish: (state) => {
825
+ if (!state.iteratorDone || state.activeCount > 0) return;
826
+ if (state.errors.length > 0) return Result.err(sortIndexedResultAsyncErrors(state.errors).map((entry) => entry.error));
827
+ return Result.ok(state.values);
828
+ },
829
+ items,
830
+ onResult: (result, currentIndex, state) => {
831
+ if (result.isErr()) {
832
+ state.errors.push({
833
+ error: result.error,
834
+ index: currentIndex
835
+ });
836
+ return;
837
+ }
838
+ state.values[currentIndex] = result.value;
839
+ },
840
+ run: f
841
+ }));
565
842
  };
566
843
  const firstSuccessOfAsyncCandidates = (candidates) => {
567
844
  return new ResultAsync(Promise.try(async () => {
@@ -577,6 +854,96 @@ const firstSuccessOfAsyncCandidates = (candidates) => {
577
854
  return latestError;
578
855
  }));
579
856
  };
857
+ const getReason = (value) => typeof value === "object" && value !== null && "reason" in value ? value.reason : void 0;
858
+ const createRaceAbortReason = () => new AbortError("ResultAsync race loser interrupted");
859
+ const startRaceTask = (task, index) => {
860
+ const controller = new AbortController();
861
+ const result = task(controller.signal);
862
+ return {
863
+ controller,
864
+ result,
865
+ settled: Promise.resolve(result).then((taskResult) => ({
866
+ index,
867
+ result: taskResult
868
+ }))
869
+ };
870
+ };
871
+ const abortStartedRaceTask = (task, reason = createRaceAbortReason()) => {
872
+ if (!task.controller.signal.aborted) task.controller.abort(reason);
873
+ };
874
+ const createRaceHandle = (task) => ({
875
+ get signal() {
876
+ return task.controller.signal;
877
+ },
878
+ abort(reason) {
879
+ abortStartedRaceTask(task, reason);
880
+ },
881
+ wait() {
882
+ return task.result;
883
+ }
884
+ });
885
+ const abortRaceLosers = (startedTasks, winnerIndex) => {
886
+ const winner = startedTasks[winnerIndex];
887
+ for (const loser of startedTasks) if (loser !== winner) abortStartedRaceTask(loser);
888
+ };
889
+ const abortPendingRaceLosers = (startedTasks, winnerIndex, completedIndexes) => {
890
+ const winner = startedTasks[winnerIndex];
891
+ for (const [index, loser] of startedTasks.entries()) if (loser !== winner && !completedIndexes.has(index)) abortStartedRaceTask(loser);
892
+ };
893
+ const observeRaceFirstTask = (task, context) => {
894
+ task.settled.then(({ index, result }) => {
895
+ if (context.state.settled) return;
896
+ context.state.settled = true;
897
+ abortRaceLosers(context.startedTasks, index);
898
+ context.resolve(result);
899
+ });
900
+ };
901
+ const observeRaceSuccessTask = (task, context) => {
902
+ task.settled.then(({ index, result }) => {
903
+ if (context.state.settled) return;
904
+ context.state.completedIndexes.add(index);
905
+ if (result.isOk()) {
906
+ context.state.settled = true;
907
+ abortPendingRaceLosers(context.startedTasks, index, context.state.completedIndexes);
908
+ context.resolve(result);
909
+ return;
910
+ }
911
+ context.state.completedCount += 1;
912
+ context.state.latestError = result;
913
+ if (context.state.completedCount === context.startedTasks.length && context.state.latestError !== void 0) {
914
+ context.state.settled = true;
915
+ context.resolve(context.state.latestError);
916
+ }
917
+ });
918
+ };
919
+ const observeRaceResultAsyncTasks = (tasks, createContext, observeTask) => {
920
+ const taskList = [...tasks];
921
+ if (taskList.length === 0) return new ResultAsync(Promise.resolve(Result.err(createEmptyResultsCollectionError())));
922
+ return new ResultAsync(new Promise((resolve) => {
923
+ const startedTasks = taskList.map((task, index) => startRaceTask(task, index));
924
+ const context = createContext(resolve, startedTasks);
925
+ for (const task of startedTasks) observeTask(task, context);
926
+ }));
927
+ };
928
+ const raceFirstResultAsyncTasks = (tasks) => observeRaceResultAsyncTasks(tasks, (resolve, startedTasks) => ({
929
+ resolve,
930
+ startedTasks,
931
+ state: { settled: false }
932
+ }), (task, context) => {
933
+ observeRaceFirstTask(task, context);
934
+ });
935
+ const raceResultAsyncTasks = (tasks) => observeRaceResultAsyncTasks(tasks, (resolve, startedTasks) => ({
936
+ resolve,
937
+ startedTasks,
938
+ state: {
939
+ completedCount: 0,
940
+ completedIndexes: /* @__PURE__ */ new Set(),
941
+ latestError: void 0,
942
+ settled: false
943
+ }
944
+ }), (task, context) => {
945
+ observeRaceSuccessTask(task, context);
946
+ });
580
947
  const loopResultAsync = (initial, options) => {
581
948
  return new ResultAsync(Promise.try(async () => {
582
949
  let state = initial;
@@ -603,17 +970,28 @@ const iterateResultAsync = (initial, options) => {
603
970
  }));
604
971
  };
605
972
  const forEachResultAsync = (items, f, options) => {
606
- return new ResultAsync(Promise.try(async () => {
607
- const values = [];
608
- let index = 0;
609
- for (const item of items) {
610
- const result = await f(item, index);
611
- if (result.isErr()) return Result.err(result.error);
612
- if (options?.discard !== true) values.push(result.value);
613
- index += 1;
614
- }
615
- if (options?.discard === true) return Result.ok(void 0);
616
- return Result.ok(values);
973
+ return new ResultAsync(runConcurrentResultAsyncItems({
974
+ concurrency: options?.concurrency,
975
+ finish: (state) => {
976
+ if (state.activeCount > 0 || !state.iteratorDone && !state.stopScheduling) return;
977
+ const firstError = sortIndexedResultAsyncErrors(state.errors)[0];
978
+ if (firstError !== void 0) return Result.err(firstError.error);
979
+ if (options?.discard === true) return Result.ok(void 0);
980
+ return Result.ok(state.values);
981
+ },
982
+ items,
983
+ onResult: (result, currentIndex, state) => {
984
+ if (result.isErr()) {
985
+ state.errors.push({
986
+ error: result.error,
987
+ index: currentIndex
988
+ });
989
+ state.stopScheduling = true;
990
+ return;
991
+ }
992
+ if (options?.discard !== true) state.values[currentIndex] = result.value;
993
+ },
994
+ run: f
617
995
  }));
618
996
  };
619
997
  const resolveResultAsyncBooleanCondition = (condition) => typeof condition === "function" ? condition() : condition;
@@ -744,19 +1122,89 @@ var ResultAsync = class ResultAsync extends Pipeable {
744
1122
  static fromPromise(promise, errorFn) {
745
1123
  return new ResultAsync(promise.then((value) => Result.ok(value)).catch((error) => Result.err(errorFn(error))));
746
1124
  }
747
- static combine(asyncResultList) {
748
- return combineResultAsyncList(asyncResultList);
1125
+ static combine(input) {
1126
+ if (Array.isArray(input)) return combineResultAsyncList(input);
1127
+ if (isIterable(input)) return combineResultAsyncList([...input]);
1128
+ return combineResultAsyncRecord(input);
749
1129
  }
750
- static combineWithAllErrors(asyncResultList) {
751
- return combineResultAsyncListWithAllErrors(asyncResultList);
1130
+ static combineWithAllErrors(input) {
1131
+ if (Array.isArray(input)) return combineResultAsyncListWithAllErrors(input);
1132
+ if (isIterable(input)) return combineResultAsyncListWithAllErrors([...input]);
1133
+ return combineResultAsyncRecordWithAllErrors(input);
752
1134
  }
753
- static validateAll(asyncResultListOrItems, f) {
1135
+ static validateAll(asyncResultListOrItems, f, options) {
754
1136
  if (f === void 0) return combineResultAsyncListWithAllErrors(asyncResultListOrItems);
755
- return validateAllResultAsyncItems(asyncResultListOrItems, f);
1137
+ return validateAllResultAsyncItems(asyncResultListOrItems, f, options);
756
1138
  }
757
1139
  static zip(left, right) {
758
1140
  return combineResultAsyncList([left, right]);
759
1141
  }
1142
+ static retry(task, options) {
1143
+ return retryResultAsyncTask(task, options);
1144
+ }
1145
+ static retryOrElse(task, options) {
1146
+ return retryOrElseResultAsyncTask(task, options);
1147
+ }
1148
+ static withResource(options) {
1149
+ return withResultAsyncResource(options);
1150
+ }
1151
+ static race(left, right) {
1152
+ return raceResultAsyncTasks([left, right]);
1153
+ }
1154
+ static raceAll(tasks) {
1155
+ return raceResultAsyncTasks(tasks);
1156
+ }
1157
+ static raceFirst(left, right) {
1158
+ return raceFirstResultAsyncTasks([left, right]);
1159
+ }
1160
+ static raceWith(left, right, handlers) {
1161
+ return new ResultAsync(new Promise((resolve) => {
1162
+ const leftTask = startRaceTask(left, 0);
1163
+ const rightTask = startRaceTask(right, 1);
1164
+ const leftHandle = createRaceHandle(leftTask);
1165
+ const rightHandle = createRaceHandle(rightTask);
1166
+ let settled = false;
1167
+ leftTask.settled.then(({ result }) => {
1168
+ if (settled) return;
1169
+ settled = true;
1170
+ const handled = handlers.onLeftDone(result, rightHandle);
1171
+ Promise.resolve(toResultAsync(handled)).then((handledResult) => {
1172
+ resolve(handledResult);
1173
+ });
1174
+ });
1175
+ rightTask.settled.then(({ result }) => {
1176
+ if (settled) return;
1177
+ settled = true;
1178
+ const handled = handlers.onRightDone(result, leftHandle);
1179
+ Promise.resolve(toResultAsync(handled)).then((handledResult) => {
1180
+ resolve(handledResult);
1181
+ });
1182
+ });
1183
+ }));
1184
+ }
1185
+ static timeout(task, options) {
1186
+ const timeoutTask = (signal) => {
1187
+ return new ResultAsync(new Promise((resolve) => {
1188
+ const timeout = setTimeout(() => {
1189
+ resolve(Result.err(options.onTimeout()));
1190
+ }, options.timeoutMs);
1191
+ signal.addEventListener("abort", () => {
1192
+ clearTimeout(timeout);
1193
+ }, { once: true });
1194
+ }));
1195
+ };
1196
+ return ResultAsync.raceWith(task, timeoutTask, {
1197
+ onLeftDone: (result, timeoutHandle) => {
1198
+ timeoutHandle.abort(createRaceAbortReason());
1199
+ return result;
1200
+ },
1201
+ onRightDone: (result, taskHandle) => {
1202
+ if (result.isErr()) taskHandle.abort(result.error);
1203
+ else taskHandle.abort(createRaceAbortReason());
1204
+ return result;
1205
+ }
1206
+ });
1207
+ }
760
1208
  static firstSuccessOf(candidates) {
761
1209
  return firstSuccessOfAsyncCandidates(candidates);
762
1210
  }
@@ -881,6 +1329,42 @@ var ResultAsync = class ResultAsync extends Pipeable {
881
1329
  return Result.err(error);
882
1330
  }));
883
1331
  }
1332
+ catchReason(errorTag, reasonTag, f) {
1333
+ return new ResultAsync(this.innerPromise.then((res) => {
1334
+ if (res.isOk()) return Result.ok(res.value);
1335
+ if (hasTag(res.error, errorTag)) {
1336
+ const reason = getReason(res.error);
1337
+ if (hasTag(reason, reasonTag)) {
1338
+ const next = f(reason, res.error);
1339
+ return next instanceof ResultAsync ? next.innerPromise : next;
1340
+ }
1341
+ }
1342
+ return Result.err(res.error);
1343
+ }));
1344
+ }
1345
+ catchReasons(errorTag, handlers) {
1346
+ return new ResultAsync(this.innerPromise.then((res) => {
1347
+ if (res.isOk()) return Result.ok(res.value);
1348
+ if (hasTag(res.error, errorTag)) {
1349
+ const reason = getReason(res.error);
1350
+ if (typeof reason === "object" && reason !== null && "_tag" in reason) {
1351
+ const handler = handlers[reason._tag];
1352
+ if (handler !== void 0) {
1353
+ const next = handler(reason, res.error);
1354
+ return next instanceof ResultAsync ? next.innerPromise : next;
1355
+ }
1356
+ }
1357
+ }
1358
+ return Result.err(res.error);
1359
+ }));
1360
+ }
1361
+ unwrapReason(errorTag) {
1362
+ return new ResultAsync(this.innerPromise.then((res) => {
1363
+ if (res.isOk()) return Result.ok(res.value);
1364
+ if (hasTag(res.error, errorTag)) return Result.err(getReason(res.error));
1365
+ return Result.err(res.error);
1366
+ }));
1367
+ }
884
1368
  async match(input, fnErr) {
885
1369
  return this.innerPromise.then((res) => typeof input === "function" ? res.match(input, getMatchErrorHandler(input, fnErr)) : res.match(input));
886
1370
  }
@@ -984,6 +1468,28 @@ function tryResultAsync(input, errorFn) {
984
1468
  /** Compatibility alias. Prefer `tryResultAsync` in new code. */
985
1469
  const tryCatchAsync = tryResultAsync;
986
1470
  //#endregion
1471
+ //#region src/redacted.ts
1472
+ const redactedValues = /* @__PURE__ */ new WeakMap();
1473
+ const formatRedacted = (label) => label === void 0 ? "<redacted>" : `<redacted:${label}>`;
1474
+ var RedactedValue = class {
1475
+ __resultarRedacted = true;
1476
+ label;
1477
+ constructor(value, label) {
1478
+ this.label = label;
1479
+ redactedValues.set(this, value);
1480
+ }
1481
+ toJSON() {
1482
+ return this.toString();
1483
+ }
1484
+ toString() {
1485
+ return formatRedacted(this.label);
1486
+ }
1487
+ };
1488
+ const redact = (value, label) => new RedactedValue(value, label);
1489
+ const isRedacted = (value) => typeof value === "object" && value !== null && redactedValues.has(value);
1490
+ const revealRedacted = (value) => redactedValues.get(value);
1491
+ const stringifyRedacted = (value) => value.toString();
1492
+ //#endregion
987
1493
  //#region src/tagged-error.ts
988
1494
  const defaultBaseError = Error;
989
1495
  const defaultMessageTemplate = "$message";
@@ -1020,7 +1526,7 @@ const templateValueStringifiers = {
1020
1526
  string: stringifyDirectTemplateValue,
1021
1527
  symbol: stringifySymbolTemplateValue
1022
1528
  };
1023
- const stringifyTemplateValue = (value) => templateValueStringifiers[typeof value]?.(value) ?? "";
1529
+ const stringifyTemplateValue = (value) => isRedacted(value) ? stringifyRedacted(value) : templateValueStringifiers[typeof value]?.(value) ?? "";
1024
1530
  const compileMessageInterpolator = (template) => (values) => template.replaceAll(templateVariableMatcher, (variable, key) => {
1025
1531
  const value = values?.[key];
1026
1532
  return value === void 0 ? variable : stringifyTemplateValue(value);
@@ -1043,13 +1549,23 @@ const assignReadonly = (target, key, value) => {
1043
1549
  writable: false
1044
1550
  });
1045
1551
  };
1046
- const serializeCause = (cause) => {
1047
- if (!isError(cause)) return cause;
1048
- return {
1552
+ const serializeTaggedValue = (value) => isRedacted(value) ? stringifyRedacted(value) : value;
1553
+ const serializeCause = (cause, seen = /* @__PURE__ */ new Set()) => {
1554
+ if (!isError(cause)) return serializeTaggedValue(cause);
1555
+ if (seen.has(cause)) return {
1556
+ cause: "[Circular]",
1049
1557
  message: cause.message,
1050
1558
  name: cause.name,
1051
1559
  stack: cause.stack
1052
1560
  };
1561
+ seen.add(cause);
1562
+ const json = {
1563
+ message: cause.message,
1564
+ name: cause.name,
1565
+ stack: cause.stack
1566
+ };
1567
+ if (cause.cause !== void 0) json["cause"] = serializeCause(cause.cause, seen);
1568
+ return json;
1053
1569
  };
1054
1570
  const findCause = (error, ErrorClass) => {
1055
1571
  const seen = /* @__PURE__ */ new Set();
@@ -1097,12 +1613,34 @@ function createTaggedError(options) {
1097
1613
  name: this.name
1098
1614
  };
1099
1615
  if (this.cause !== void 0) json["cause"] = serializeCause(this.cause);
1100
- for (const [key, value] of Object.entries(this)) if (!(key in json) && key !== "cause" && key !== "stack") json[key] = value;
1616
+ for (const [key, value] of Object.entries(this)) if (!(key in json) && key !== "cause" && key !== "stack") json[key] = serializeTaggedValue(value);
1101
1617
  return json;
1102
1618
  }
1103
1619
  }
1104
1620
  return GeneratedTaggedError;
1105
1621
  }
1622
+ const taggedEnum = () => {
1623
+ return new Proxy({
1624
+ $is(tag, value) {
1625
+ return typeof value === "object" && value !== null && "_tag" in value && value._tag === tag;
1626
+ },
1627
+ $match(value, handlers) {
1628
+ const handler = handlers[value._tag];
1629
+ if (handler === void 0) throw new Error(`No tagged enum handler for ${String(value._tag)}`);
1630
+ return handler(value);
1631
+ }
1632
+ }, { get(base, property, receiver) {
1633
+ if (property in base) return Reflect.get(base, property, receiver);
1634
+ if (typeof property !== "string") return;
1635
+ return (props) => {
1636
+ const value = { _tag: property };
1637
+ if (props !== void 0) {
1638
+ for (const [key, propValue] of Object.entries(props)) if (key !== "_tag") value[key] = propValue;
1639
+ }
1640
+ return Object.freeze(value);
1641
+ };
1642
+ } });
1643
+ };
1106
1644
  const matchError = (error, handlers) => {
1107
1645
  if ("_tag" in error && typeof error._tag === "string" && error._tag in handlers) {
1108
1646
  const handler = handlers[error._tag];
@@ -1120,6 +1658,6 @@ const matchErrorPartial = (error, handlers, fallback) => {
1120
1658
  return fallback(error);
1121
1659
  };
1122
1660
  //#endregion
1123
- export { DisposableResult, DisposableResultAsync, Result, ResultAsync, createTaggedError, tryResult as default, tryResult as try, tryResult, err, errAsync, findCause, fromPromise, fromSafePromise, fromThrowable, fromThrowableAsync, isError, matchError, matchErrorPartial, ok, okAsync, safeTry, tryResultAsync as tryAsync, tryResultAsync, tryCatch, tryCatchAsync, unit, unitAsync };
1661
+ export { AbortError, DisposableResult, DisposableResultAsync, Result, ResultAsync, createTaggedError, tryResult as default, tryResult as try, tryResult, err, errAsync, findCause, fromPromise, fromSafePromise, fromThrowable, fromThrowableAsync, isAbortError, isError, isRedacted, matchError, matchErrorPartial, ok, okAsync, redact, revealRedacted, safeTry, taggedEnum, tryResultAsync as tryAsync, tryResultAsync, tryCatch, tryCatchAsync, unit, unitAsync };
1124
1662
 
1125
1663
  //# sourceMappingURL=index.js.map