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/README.md +362 -23
- package/dist/index.d.ts +152 -3
- package/dist/index.js +567 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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(
|
|
227
|
-
return combineResultList(
|
|
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(
|
|
230
|
-
return combineResultListWithAllErrors(
|
|
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
|
|
564
|
-
return
|
|
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(
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
const
|
|
611
|
-
if (
|
|
612
|
-
if (options?.discard
|
|
613
|
-
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
|
|
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(
|
|
748
|
-
return combineResultAsyncList(
|
|
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(
|
|
751
|
-
return combineResultAsyncListWithAllErrors(
|
|
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
|
|
1047
|
-
|
|
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
|