resultar 3.6.0 → 3.7.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/LICENSE +1 -0
- package/README.md +141 -15
- package/dist/index.d.ts +297 -123
- package/dist/index.js +898 -279
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -673,6 +673,897 @@ const runSync = (result) => result.unwrapOrThrow();
|
|
|
673
673
|
/** Compatibility alias. Prefer `tryResult` in new code. */
|
|
674
674
|
const tryCatch = tryResult;
|
|
675
675
|
//#endregion
|
|
676
|
+
//#region src/task/scope.ts
|
|
677
|
+
/** Appends cleanup failures without replacing the original execution outcome. */
|
|
678
|
+
const appendExit = (exit, cleanup) => {
|
|
679
|
+
if (cleanup._tag === "Success") return exit;
|
|
680
|
+
if (exit._tag === "Success") return cleanup;
|
|
681
|
+
return {
|
|
682
|
+
_tag: "Failure",
|
|
683
|
+
cause: {
|
|
684
|
+
_tag: "Sequential",
|
|
685
|
+
left: exit.cause,
|
|
686
|
+
right: cleanup.cause
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
};
|
|
690
|
+
/** Private runtime scope; each run owns its own finalizer stack. */
|
|
691
|
+
var TaskScope = class {
|
|
692
|
+
finalizers = [];
|
|
693
|
+
closing;
|
|
694
|
+
add(finalizer) {
|
|
695
|
+
if (this.closing !== void 0) throw new Error("Cannot acquire into a closing ResultTask scope");
|
|
696
|
+
this.finalizers.push(finalizer);
|
|
697
|
+
}
|
|
698
|
+
adopt(child) {
|
|
699
|
+
if (this.closing !== void 0) throw new Error("Cannot acquire into a closing ResultTask scope");
|
|
700
|
+
this.finalizers.push(...child.finalizers.splice(0));
|
|
701
|
+
}
|
|
702
|
+
close(exit, cleanupOnly = false) {
|
|
703
|
+
this.closing ??= this.drain(exit, cleanupOnly);
|
|
704
|
+
return this.closing;
|
|
705
|
+
}
|
|
706
|
+
async drain(exit, cleanupOnly) {
|
|
707
|
+
let result = cleanupOnly ? {
|
|
708
|
+
_tag: "Success",
|
|
709
|
+
value: void 0
|
|
710
|
+
} : exit;
|
|
711
|
+
let finalizer = this.finalizers.pop();
|
|
712
|
+
while (finalizer !== void 0) {
|
|
713
|
+
try {
|
|
714
|
+
result = appendExit(result, await finalizer(exit));
|
|
715
|
+
} catch (error) {
|
|
716
|
+
result = appendExit(result, {
|
|
717
|
+
_tag: "Failure",
|
|
718
|
+
cause: {
|
|
719
|
+
_tag: "Die",
|
|
720
|
+
defect: error
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
finalizer = this.finalizers.pop();
|
|
725
|
+
}
|
|
726
|
+
return result;
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
//#endregion
|
|
730
|
+
//#region src/result-task.ts
|
|
731
|
+
/** Rejection used when a simplified boundary cannot represent multiple failures. */
|
|
732
|
+
var ResultTaskCauseError = class extends Error {
|
|
733
|
+
cause;
|
|
734
|
+
constructor(cause) {
|
|
735
|
+
super("ResultTask execution failed with multiple causes");
|
|
736
|
+
this.name = "ResultTaskCauseError";
|
|
737
|
+
Object.defineProperty(this, "cause", {
|
|
738
|
+
configurable: true,
|
|
739
|
+
enumerable: false,
|
|
740
|
+
value: cause,
|
|
741
|
+
writable: true
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
/** Type identifier symbol for nominal `ResultTask` branding and variance checks. */
|
|
746
|
+
const ResultTaskTypeId = Symbol.for("resultar/ResultTask");
|
|
747
|
+
/** Type identifier symbol for nominal `ServiceTag` branding. */
|
|
748
|
+
const ServiceTagTypeId = Symbol.for("resultar/ServiceTag");
|
|
749
|
+
/** Type identifier symbol for nominal `ResultTask` yieldable objects. */
|
|
750
|
+
const ResultTaskYieldTypeId = Symbol.for("resultar/ResultTaskYield");
|
|
751
|
+
/** Error thrown as a Die cause when a requested service is not provided in the environment. */
|
|
752
|
+
var MissingServiceError = class extends Error {
|
|
753
|
+
serviceIdentifier;
|
|
754
|
+
constructor(serviceIdentifier) {
|
|
755
|
+
super(`Missing ResultTask service: ${serviceIdentifier}`);
|
|
756
|
+
this.name = "MissingServiceError";
|
|
757
|
+
this.serviceIdentifier = serviceIdentifier;
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
const success = (value) => ({
|
|
761
|
+
_tag: "Success",
|
|
762
|
+
value
|
|
763
|
+
});
|
|
764
|
+
const failure = (cause) => ({
|
|
765
|
+
_tag: "Failure",
|
|
766
|
+
cause
|
|
767
|
+
});
|
|
768
|
+
const failed = (error) => failure({
|
|
769
|
+
_tag: "Fail",
|
|
770
|
+
error
|
|
771
|
+
});
|
|
772
|
+
const died = (defect) => failure({
|
|
773
|
+
_tag: "Die",
|
|
774
|
+
defect
|
|
775
|
+
});
|
|
776
|
+
const interrupted = (signal) => failure({
|
|
777
|
+
_tag: "Interrupt",
|
|
778
|
+
reason: signal.reason
|
|
779
|
+
});
|
|
780
|
+
const interruptSuccess = (exit, signal) => exit._tag === "Success" && signal.aborted ? interrupted(signal) : exit;
|
|
781
|
+
const throwDefect = (defect) => {
|
|
782
|
+
throw defect;
|
|
783
|
+
};
|
|
784
|
+
const isObjectLike = (value) => (typeof value === "object" || typeof value === "function") && value !== null;
|
|
785
|
+
const isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
786
|
+
/** Returns true if a value is a nominal `ServiceTag`. */
|
|
787
|
+
const isServiceTag = (value) => isObjectLike(value) && value[ServiceTagTypeId] === ServiceTagTypeId && value._tag === "ServiceTag" && typeof value.identifier === "string" && typeof value.key === "symbol" && typeof value[Symbol.iterator] === "function";
|
|
788
|
+
/** Returns true if a value is a `ResultTask` instance. */
|
|
789
|
+
const isResultTask = (value) => isObjectLike(value) && (value instanceof ResultTask || value[ResultTaskTypeId] !== void 0);
|
|
790
|
+
const isResultTaskYield = (value) => isObjectLike(value) && value[ResultTaskYieldTypeId] === ResultTaskYieldTypeId && value._tag === "ResultTask" && isResultTask(value.task);
|
|
791
|
+
const isServiceYield = (value) => isObjectLike(value) && value[ResultTaskYieldTypeId] === ResultTaskYieldTypeId && value._tag === "Service" && isServiceTag(value.tag);
|
|
792
|
+
const isErrResult = (value) => isObjectLike(value) && "error" in value && typeof value.isErr === "function" && value.isErr();
|
|
793
|
+
const lookupService = (tag, context) => {
|
|
794
|
+
if (context.services.has(tag.key)) return {
|
|
795
|
+
_tag: "Found",
|
|
796
|
+
value: context.services.get(tag.key)
|
|
797
|
+
};
|
|
798
|
+
if (context.namedServices.has(tag.identifier)) return {
|
|
799
|
+
_tag: "Found",
|
|
800
|
+
value: context.namedServices.get(tag.identifier)
|
|
801
|
+
};
|
|
802
|
+
return { _tag: "Missing" };
|
|
803
|
+
};
|
|
804
|
+
const resolveService = async (tag, context, executeTask) => {
|
|
805
|
+
const supplied = lookupService(tag, context);
|
|
806
|
+
if (supplied._tag === "Found") return supplied;
|
|
807
|
+
if (context.serviceResolver === void 0) return supplied;
|
|
808
|
+
let task = void 0;
|
|
809
|
+
try {
|
|
810
|
+
task = context.serviceResolver(tag);
|
|
811
|
+
} catch (error) {
|
|
812
|
+
return {
|
|
813
|
+
_tag: "Error",
|
|
814
|
+
error
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
if (task === void 0) return supplied;
|
|
818
|
+
const outcome = await executeTaskSafely(task, executeTask);
|
|
819
|
+
if ("error" in outcome) return {
|
|
820
|
+
_tag: "Error",
|
|
821
|
+
error: outcome.error
|
|
822
|
+
};
|
|
823
|
+
if (outcome._tag === "Failure") return {
|
|
824
|
+
_tag: "Failure",
|
|
825
|
+
exit: outcome
|
|
826
|
+
};
|
|
827
|
+
return {
|
|
828
|
+
_tag: "Found",
|
|
829
|
+
value: outcome.value
|
|
830
|
+
};
|
|
831
|
+
};
|
|
832
|
+
const executeTaskSafely = async (task, executeTask) => {
|
|
833
|
+
try {
|
|
834
|
+
return await executeTask(task);
|
|
835
|
+
} catch (error) {
|
|
836
|
+
return { error };
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
const closeGenerator = async (iterator, exit, runtime) => {
|
|
840
|
+
try {
|
|
841
|
+
let step = iterator.return(void 0);
|
|
842
|
+
let finalExit = exit;
|
|
843
|
+
while (step.done === false) if (isResultTaskYield(step.value)) {
|
|
844
|
+
const outcome = await executeTaskSafely(step.value.task, runtime.executeTask);
|
|
845
|
+
if ("error" in outcome) {
|
|
846
|
+
finalExit = died(outcome.error);
|
|
847
|
+
step = iterator.return(void 0);
|
|
848
|
+
} else if (outcome._tag === "Failure") {
|
|
849
|
+
finalExit = outcome;
|
|
850
|
+
step = iterator.return(void 0);
|
|
851
|
+
} else step = iterator.next(outcome);
|
|
852
|
+
} else if (isServiceYield(step.value)) {
|
|
853
|
+
const resolution = await resolveService(step.value.tag, runtime.context, runtime.executeTask);
|
|
854
|
+
if (resolution._tag === "Found") step = iterator.next(resolution.value);
|
|
855
|
+
else if (resolution._tag === "Failure") {
|
|
856
|
+
finalExit = resolution.exit;
|
|
857
|
+
step = iterator.return(void 0);
|
|
858
|
+
} else {
|
|
859
|
+
finalExit = died(resolution._tag === "Error" ? resolution.error : new MissingServiceError(step.value.tag.identifier));
|
|
860
|
+
step = iterator.return(void 0);
|
|
861
|
+
}
|
|
862
|
+
} else if (isErrResult(step.value)) {
|
|
863
|
+
finalExit = failed(step.value.error);
|
|
864
|
+
step = iterator.return(void 0);
|
|
865
|
+
} else {
|
|
866
|
+
finalExit = died(/* @__PURE__ */ new TypeError("ResultTask.gen yielded an unsupported value"));
|
|
867
|
+
step = iterator.return(void 0);
|
|
868
|
+
}
|
|
869
|
+
return finalExit;
|
|
870
|
+
} catch (error) {
|
|
871
|
+
return died(error);
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
const withServices = (context, services) => ({
|
|
875
|
+
scope: context.scope,
|
|
876
|
+
namedServices: context.namedServices,
|
|
877
|
+
services,
|
|
878
|
+
serviceResolver: context.serviceResolver,
|
|
879
|
+
signal: context.signal
|
|
880
|
+
});
|
|
881
|
+
const withScope = (context, scope, signal = context.signal) => ({
|
|
882
|
+
namedServices: context.namedServices,
|
|
883
|
+
services: context.services,
|
|
884
|
+
serviceResolver: context.serviceResolver,
|
|
885
|
+
signal,
|
|
886
|
+
scope
|
|
887
|
+
});
|
|
888
|
+
const withNamedServices = (context, namedServices) => ({
|
|
889
|
+
scope: context.scope,
|
|
890
|
+
namedServices,
|
|
891
|
+
services: context.services,
|
|
892
|
+
serviceResolver: context.serviceResolver,
|
|
893
|
+
signal: context.signal
|
|
894
|
+
});
|
|
895
|
+
const withServiceResolver = (context, serviceResolver) => ({
|
|
896
|
+
namedServices: context.namedServices,
|
|
897
|
+
services: context.services,
|
|
898
|
+
serviceResolver,
|
|
899
|
+
signal: context.signal,
|
|
900
|
+
scope: context.scope
|
|
901
|
+
});
|
|
902
|
+
var ServiceTagValue = class {
|
|
903
|
+
[ServiceTagTypeId] = ServiceTagTypeId;
|
|
904
|
+
_tag = "ServiceTag";
|
|
905
|
+
identifier;
|
|
906
|
+
key;
|
|
907
|
+
constructor(identifier) {
|
|
908
|
+
this.identifier = identifier;
|
|
909
|
+
this.key = Symbol(identifier);
|
|
910
|
+
}
|
|
911
|
+
*[Symbol.iterator]() {
|
|
912
|
+
return yield {
|
|
913
|
+
[ResultTaskYieldTypeId]: ResultTaskYieldTypeId,
|
|
914
|
+
_tag: "Service",
|
|
915
|
+
tag: this
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
/** Creates a service tag that can be requested with `yield*` inside `ResultTask.gen`. */
|
|
920
|
+
const serviceTag = (identifier) => new ServiceTagValue(identifier);
|
|
921
|
+
const applySuccessContinuation = (continuation, exit) => {
|
|
922
|
+
switch (continuation._tag) {
|
|
923
|
+
case "Map": try {
|
|
924
|
+
return {
|
|
925
|
+
current: void 0,
|
|
926
|
+
currentExit: success(continuation.f(exit.value))
|
|
927
|
+
};
|
|
928
|
+
} catch (error) {
|
|
929
|
+
return {
|
|
930
|
+
current: void 0,
|
|
931
|
+
currentExit: died(error)
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
case "FlatMap": try {
|
|
935
|
+
return {
|
|
936
|
+
current: continuation.f(exit.value),
|
|
937
|
+
currentExit: void 0
|
|
938
|
+
};
|
|
939
|
+
} catch (error) {
|
|
940
|
+
return {
|
|
941
|
+
current: void 0,
|
|
942
|
+
currentExit: died(error)
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
case "Tap": try {
|
|
946
|
+
const res = continuation.f(exit.value);
|
|
947
|
+
return ResultTask.toTapContinuationStep(res, () => ResultTask.succeed(exit.value), exit);
|
|
948
|
+
} catch (error) {
|
|
949
|
+
return {
|
|
950
|
+
current: void 0,
|
|
951
|
+
currentExit: died(error)
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
case "MapError": return {
|
|
955
|
+
current: void 0,
|
|
956
|
+
currentExit: exit
|
|
957
|
+
};
|
|
958
|
+
case "TapError": return {
|
|
959
|
+
current: void 0,
|
|
960
|
+
currentExit: exit
|
|
961
|
+
};
|
|
962
|
+
case "CatchAll": return {
|
|
963
|
+
current: void 0,
|
|
964
|
+
currentExit: exit
|
|
965
|
+
};
|
|
966
|
+
default: return {
|
|
967
|
+
current: void 0,
|
|
968
|
+
currentExit: exit
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
const applyFailureContinuation = (continuation, exit) => {
|
|
973
|
+
switch (continuation._tag) {
|
|
974
|
+
case "MapError":
|
|
975
|
+
if (exit.cause._tag === "Fail") try {
|
|
976
|
+
return {
|
|
977
|
+
current: void 0,
|
|
978
|
+
currentExit: failed(continuation.f(exit.cause.error))
|
|
979
|
+
};
|
|
980
|
+
} catch (error) {
|
|
981
|
+
return {
|
|
982
|
+
current: void 0,
|
|
983
|
+
currentExit: died(error)
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
return {
|
|
987
|
+
current: void 0,
|
|
988
|
+
currentExit: exit
|
|
989
|
+
};
|
|
990
|
+
case "TapError":
|
|
991
|
+
if (exit.cause._tag === "Fail") try {
|
|
992
|
+
const failError = exit.cause.error;
|
|
993
|
+
const res = continuation.f(failError);
|
|
994
|
+
return ResultTask.toTapContinuationStep(res, () => ResultTask.fail(failError), exit);
|
|
995
|
+
} catch (error) {
|
|
996
|
+
return {
|
|
997
|
+
current: void 0,
|
|
998
|
+
currentExit: died(error)
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
return {
|
|
1002
|
+
current: void 0,
|
|
1003
|
+
currentExit: exit
|
|
1004
|
+
};
|
|
1005
|
+
case "CatchAll":
|
|
1006
|
+
if (exit.cause._tag === "Sequential") return {
|
|
1007
|
+
current: void 0,
|
|
1008
|
+
currentExit: died(new ResultTaskCauseError(exit.cause))
|
|
1009
|
+
};
|
|
1010
|
+
if (exit.cause._tag === "Fail") try {
|
|
1011
|
+
return {
|
|
1012
|
+
current: continuation.f(exit.cause.error),
|
|
1013
|
+
currentExit: void 0
|
|
1014
|
+
};
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
return {
|
|
1017
|
+
current: void 0,
|
|
1018
|
+
currentExit: died(error)
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
return {
|
|
1022
|
+
current: void 0,
|
|
1023
|
+
currentExit: exit
|
|
1024
|
+
};
|
|
1025
|
+
case "Map": return {
|
|
1026
|
+
current: void 0,
|
|
1027
|
+
currentExit: exit
|
|
1028
|
+
};
|
|
1029
|
+
case "FlatMap": return {
|
|
1030
|
+
current: void 0,
|
|
1031
|
+
currentExit: exit
|
|
1032
|
+
};
|
|
1033
|
+
case "Tap": return {
|
|
1034
|
+
current: void 0,
|
|
1035
|
+
currentExit: exit
|
|
1036
|
+
};
|
|
1037
|
+
default: return {
|
|
1038
|
+
current: void 0,
|
|
1039
|
+
currentExit: exit
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
const applyContinuation = (continuation, exit) => exit._tag === "Success" ? applySuccessContinuation(continuation, exit) : applyFailureContinuation(continuation, exit);
|
|
1044
|
+
const executeInstruction = async (instruction, continuations, context) => {
|
|
1045
|
+
switch (instruction._tag) {
|
|
1046
|
+
case "FlatMap":
|
|
1047
|
+
case "Map":
|
|
1048
|
+
case "MapError":
|
|
1049
|
+
case "Tap":
|
|
1050
|
+
case "TapError":
|
|
1051
|
+
case "CatchAll":
|
|
1052
|
+
continuations.push(instruction);
|
|
1053
|
+
return {
|
|
1054
|
+
nextCurrent: instruction.task,
|
|
1055
|
+
exit: void 0
|
|
1056
|
+
};
|
|
1057
|
+
case "Succeed": return {
|
|
1058
|
+
nextCurrent: void 0,
|
|
1059
|
+
exit: success(instruction.value)
|
|
1060
|
+
};
|
|
1061
|
+
case "Fail": return {
|
|
1062
|
+
nextCurrent: void 0,
|
|
1063
|
+
exit: failed(instruction.error)
|
|
1064
|
+
};
|
|
1065
|
+
case "Sync": try {
|
|
1066
|
+
return {
|
|
1067
|
+
nextCurrent: void 0,
|
|
1068
|
+
exit: instruction.evaluate()
|
|
1069
|
+
};
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
return {
|
|
1072
|
+
nextCurrent: void 0,
|
|
1073
|
+
exit: died(error)
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
case "Async": try {
|
|
1077
|
+
return {
|
|
1078
|
+
nextCurrent: void 0,
|
|
1079
|
+
exit: await instruction.execute(context)
|
|
1080
|
+
};
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
return {
|
|
1083
|
+
nextCurrent: void 0,
|
|
1084
|
+
exit: died(error)
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
default: return {
|
|
1088
|
+
nextCurrent: void 0,
|
|
1089
|
+
exit: void 0
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
var ResultTask = class ResultTask extends Pipeable {
|
|
1094
|
+
instruction;
|
|
1095
|
+
execute;
|
|
1096
|
+
constructor(instructionOrExecute) {
|
|
1097
|
+
super();
|
|
1098
|
+
this.instruction = typeof instructionOrExecute === "function" ? {
|
|
1099
|
+
_tag: "Async",
|
|
1100
|
+
execute: instructionOrExecute
|
|
1101
|
+
} : instructionOrExecute;
|
|
1102
|
+
this.execute = (context) => ResultTask.runTaskLoop(this, context);
|
|
1103
|
+
}
|
|
1104
|
+
/** @internal */
|
|
1105
|
+
static toTapContinuationStep(res, onSuccess, passThroughExit) {
|
|
1106
|
+
if (res instanceof ResultTask) return {
|
|
1107
|
+
current: res.flatMap(onSuccess),
|
|
1108
|
+
currentExit: void 0
|
|
1109
|
+
};
|
|
1110
|
+
if (typeof res === "object" && res !== null && typeof res.then === "function") return {
|
|
1111
|
+
current: new ResultTask(async () => {
|
|
1112
|
+
try {
|
|
1113
|
+
await res;
|
|
1114
|
+
return success(void 0);
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
return died(error);
|
|
1117
|
+
}
|
|
1118
|
+
}).flatMap(onSuccess),
|
|
1119
|
+
currentExit: void 0
|
|
1120
|
+
};
|
|
1121
|
+
return {
|
|
1122
|
+
current: void 0,
|
|
1123
|
+
currentExit: passThroughExit
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
static async runTaskLoop(rootTask, context) {
|
|
1127
|
+
const continuations = [];
|
|
1128
|
+
let current = rootTask;
|
|
1129
|
+
let currentExit = void 0;
|
|
1130
|
+
while (current !== void 0 || continuations.length > 0) if (current === void 0) {
|
|
1131
|
+
if (currentExit !== void 0) {
|
|
1132
|
+
const nextContinuation = continuations.pop();
|
|
1133
|
+
if (nextContinuation === void 0) return currentExit;
|
|
1134
|
+
const resolvedExit = context.signal.aborted && currentExit._tag === "Success" ? interrupted(context.signal) : currentExit;
|
|
1135
|
+
const step = applyContinuation(nextContinuation, resolvedExit);
|
|
1136
|
+
current = step.current;
|
|
1137
|
+
currentExit = step.currentExit;
|
|
1138
|
+
}
|
|
1139
|
+
} else {
|
|
1140
|
+
if (context.signal.aborted) return interrupted(context.signal);
|
|
1141
|
+
const step = await executeInstruction(current.instruction, continuations, context);
|
|
1142
|
+
current = step.nextCurrent;
|
|
1143
|
+
currentExit = step.exit;
|
|
1144
|
+
}
|
|
1145
|
+
return currentExit ?? success(void 0);
|
|
1146
|
+
}
|
|
1147
|
+
static succeed(value) {
|
|
1148
|
+
return new ResultTask({
|
|
1149
|
+
_tag: "Succeed",
|
|
1150
|
+
value
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
/** Creates a task that fails with `error` when it is executed. */
|
|
1154
|
+
static fail(error) {
|
|
1155
|
+
return new ResultTask({
|
|
1156
|
+
_tag: "Fail",
|
|
1157
|
+
error
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
/** Lifts an already computed Result into a lazy task. */
|
|
1161
|
+
static fromResult(result) {
|
|
1162
|
+
return new ResultTask({
|
|
1163
|
+
_tag: "Sync",
|
|
1164
|
+
evaluate: () => result.isOk() ? success(result.value) : failed(result.error)
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
/** Creates a task for synchronous work. Thrown values are defects, not typed failures. */
|
|
1168
|
+
static sync(evaluate) {
|
|
1169
|
+
return new ResultTask({
|
|
1170
|
+
_tag: "Sync",
|
|
1171
|
+
evaluate: () => success(evaluate())
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
/** Creates a task that maps synchronous throws into the typed error channel. */
|
|
1175
|
+
static try(options) {
|
|
1176
|
+
return new ResultTask({
|
|
1177
|
+
_tag: "Sync",
|
|
1178
|
+
evaluate: () => {
|
|
1179
|
+
try {
|
|
1180
|
+
return success(options.try());
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
return failed(options.catch(error));
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
static tryPromise(options) {
|
|
1188
|
+
return new ResultTask(async (context) => {
|
|
1189
|
+
try {
|
|
1190
|
+
return success(await (typeof options === "function" ? options(context.signal) : options.try(context.signal)));
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
if (context.signal.aborted) return interrupted(context.signal);
|
|
1193
|
+
return failed(typeof options === "function" ? error : options.catch(error));
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Acquires lazily and registers release in the current scope. Release failures remain in the
|
|
1199
|
+
* scope requirement until `scoped` or a run boundary closes it; catchAll cannot erase them early.
|
|
1200
|
+
*/
|
|
1201
|
+
static acquireRelease(options) {
|
|
1202
|
+
return new ResultTask(async (context) => {
|
|
1203
|
+
const acquired = await options.acquire.execute(context);
|
|
1204
|
+
if (acquired._tag === "Failure") return acquired;
|
|
1205
|
+
context.scope.add(async (exit) => {
|
|
1206
|
+
const scope = new TaskScope();
|
|
1207
|
+
const cleanupContext = withScope(context, scope, new AbortController().signal);
|
|
1208
|
+
let released = success(void 0);
|
|
1209
|
+
try {
|
|
1210
|
+
released = await options.release(acquired.value, exit).execute(cleanupContext);
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
released = died(error);
|
|
1213
|
+
}
|
|
1214
|
+
return scope.close(released);
|
|
1215
|
+
});
|
|
1216
|
+
return acquired;
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
/** Closes a child scope before continuing, adding its deferred release errors to E. */
|
|
1220
|
+
static scoped(task) {
|
|
1221
|
+
return new ResultTask(async (context) => {
|
|
1222
|
+
const scope = new TaskScope();
|
|
1223
|
+
const exit = await task.execute(withScope(context, scope));
|
|
1224
|
+
return interruptSuccess(await scope.close(interruptSuccess(exit, context.signal)), context.signal);
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
/** Creates a service tag that can be requested with `yield*` inside `ResultTask.gen`. */
|
|
1228
|
+
static service(identifier) {
|
|
1229
|
+
return new ServiceTagValue(identifier);
|
|
1230
|
+
}
|
|
1231
|
+
/** Builds a task from a generator and short-circuits on the first failed task. */
|
|
1232
|
+
static gen(body) {
|
|
1233
|
+
return new ResultTask(async (context) => {
|
|
1234
|
+
try {
|
|
1235
|
+
const iterator = body();
|
|
1236
|
+
const executeTask = async (task) => interruptSuccess(await task.execute(context), context.signal);
|
|
1237
|
+
const closeRuntime = {
|
|
1238
|
+
context,
|
|
1239
|
+
executeTask: (task) => task.execute(withScope(context, context.scope, new AbortController().signal))
|
|
1240
|
+
};
|
|
1241
|
+
let step = iterator.next();
|
|
1242
|
+
while (step.done === false) if (isResultTaskYield(step.value)) {
|
|
1243
|
+
const outcome = await executeTaskSafely(step.value.task, executeTask);
|
|
1244
|
+
if ("error" in outcome) return await closeGenerator(iterator, died(outcome.error), closeRuntime);
|
|
1245
|
+
if (outcome._tag === "Failure") return await closeGenerator(iterator, outcome, closeRuntime);
|
|
1246
|
+
step = iterator.next(outcome);
|
|
1247
|
+
} else if (isServiceYield(step.value)) {
|
|
1248
|
+
const resolution = await resolveService(step.value.tag, context, executeTask);
|
|
1249
|
+
if (resolution._tag === "Found") step = iterator.next(resolution.value);
|
|
1250
|
+
else if (resolution._tag === "Failure") return await closeGenerator(iterator, resolution.exit, closeRuntime);
|
|
1251
|
+
else return await closeGenerator(iterator, died(resolution._tag === "Error" ? resolution.error : new MissingServiceError(step.value.tag.identifier)), closeRuntime);
|
|
1252
|
+
} else if (isErrResult(step.value)) return await closeGenerator(iterator, failed(step.value.error), closeRuntime);
|
|
1253
|
+
else return await closeGenerator(iterator, died(/* @__PURE__ */ new TypeError("ResultTask.gen yielded an unsupported value")), closeRuntime);
|
|
1254
|
+
return success(step.value);
|
|
1255
|
+
} catch (error) {
|
|
1256
|
+
return died(error);
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
/** Maps the success value without executing the task. */
|
|
1261
|
+
map(f) {
|
|
1262
|
+
return new ResultTask({
|
|
1263
|
+
_tag: "Map",
|
|
1264
|
+
task: this,
|
|
1265
|
+
f
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
/** Chains another task from the success value without executing either task immediately. */
|
|
1269
|
+
flatMap(f) {
|
|
1270
|
+
return new ResultTask({
|
|
1271
|
+
_tag: "FlatMap",
|
|
1272
|
+
task: this,
|
|
1273
|
+
f
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
/** Alias for `flatMap`, matching the existing Resultar vocabulary. */
|
|
1277
|
+
andThen(f) {
|
|
1278
|
+
return this.flatMap(f);
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Recovers a single typed failure. Composite causes become a Die(ResultTaskCauseError) so the
|
|
1282
|
+
* removed E cannot escape through runExit; the original tree remains available on error.cause.
|
|
1283
|
+
*/
|
|
1284
|
+
catchAll(f) {
|
|
1285
|
+
return new ResultTask({
|
|
1286
|
+
_tag: "CatchAll",
|
|
1287
|
+
task: this,
|
|
1288
|
+
f
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
/** Maps the error value without executing the task. */
|
|
1292
|
+
mapError(f) {
|
|
1293
|
+
return new ResultTask({
|
|
1294
|
+
_tag: "MapError",
|
|
1295
|
+
task: this,
|
|
1296
|
+
f
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Executes a side effect on the success value without modifying it.
|
|
1301
|
+
* If the callback returns a `ResultTask`, its requirements and errors are merged.
|
|
1302
|
+
*/
|
|
1303
|
+
tap(f) {
|
|
1304
|
+
return new ResultTask({
|
|
1305
|
+
_tag: "Tap",
|
|
1306
|
+
task: this,
|
|
1307
|
+
f
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Executes a side effect on the error value without modifying it.
|
|
1312
|
+
* If the callback returns a `ResultTask`, its requirements and errors are merged.
|
|
1313
|
+
*/
|
|
1314
|
+
tapError(f) {
|
|
1315
|
+
return new ResultTask({
|
|
1316
|
+
_tag: "TapError",
|
|
1317
|
+
task: this,
|
|
1318
|
+
f
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
/** Replaces the success value with a constant. */
|
|
1322
|
+
as(value) {
|
|
1323
|
+
return this.map(() => value);
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Transforms both success and failure cases into a single value type.
|
|
1327
|
+
*/
|
|
1328
|
+
match(handlers) {
|
|
1329
|
+
const onSuccess = "onSuccess" in handlers ? handlers.onSuccess : handlers.ok;
|
|
1330
|
+
const onFailure = "onFailure" in handlers ? handlers.onFailure : handlers.err;
|
|
1331
|
+
return this.map(onSuccess).catchAll((error) => ResultTask.succeed(onFailure(error)));
|
|
1332
|
+
}
|
|
1333
|
+
/** Converts this ResultTask into an eagerly started ResultAsync using the standard runtime. */
|
|
1334
|
+
toResultAsync() {
|
|
1335
|
+
return ResultTask.toResultAsync(this);
|
|
1336
|
+
}
|
|
1337
|
+
/** Provides one required service and removes it from the task's requirements. */
|
|
1338
|
+
provideService(tag, ...args) {
|
|
1339
|
+
if (args.length === 0) throw new TypeError("ResultTask.provideService requires a service implementation");
|
|
1340
|
+
return ResultTask.provideService(this, tag, args[0]);
|
|
1341
|
+
}
|
|
1342
|
+
/** Provides all requirements using an object keyed by service identifier. */
|
|
1343
|
+
provideServices(services) {
|
|
1344
|
+
return ResultTask.provideServices(this, services);
|
|
1345
|
+
}
|
|
1346
|
+
/** Provides a lazy resolver for service tags that are not already in the environment. */
|
|
1347
|
+
provideServiceResolver(resolvers) {
|
|
1348
|
+
return ResultTask.provideServiceResolver(this, resolvers);
|
|
1349
|
+
}
|
|
1350
|
+
/** Enables `yield* task` inside `ResultTask.gen` workflows. */
|
|
1351
|
+
*[Symbol.iterator]() {
|
|
1352
|
+
const exit = yield {
|
|
1353
|
+
[ResultTaskYieldTypeId]: ResultTaskYieldTypeId,
|
|
1354
|
+
_tag: "ResultTask",
|
|
1355
|
+
task: this
|
|
1356
|
+
};
|
|
1357
|
+
if (exit._tag === "Success") return exit.value;
|
|
1358
|
+
return throwDefect(/* @__PURE__ */ new Error("ResultTask generator received a failed task result"));
|
|
1359
|
+
}
|
|
1360
|
+
static provideService(...args) {
|
|
1361
|
+
if (args.length >= 3) {
|
|
1362
|
+
const [task, tag, service] = args;
|
|
1363
|
+
if (!isResultTask(task)) throw new TypeError("ResultTask.provideService expects a ResultTask as first argument");
|
|
1364
|
+
if (!isServiceTag(tag)) throw new TypeError("ResultTask.provideService requires a valid service tag");
|
|
1365
|
+
return new ResultTask(async (context) => {
|
|
1366
|
+
const services = new Map([...context.services, [tag.key, service]]);
|
|
1367
|
+
const namedServices = new Map([...context.namedServices, [tag.identifier, service]]);
|
|
1368
|
+
return task.execute(withNamedServices(withServices(context, services), namedServices));
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
if (args.length === 2 && !isResultTask(args[0])) {
|
|
1372
|
+
const [tag, service] = args;
|
|
1373
|
+
if (!isServiceTag(tag)) throw new TypeError("ResultTask.provideService requires a valid service tag");
|
|
1374
|
+
return (task) => ResultTask.provideService(task, tag, service);
|
|
1375
|
+
}
|
|
1376
|
+
throw new TypeError("ResultTask.provideService requires a service implementation");
|
|
1377
|
+
}
|
|
1378
|
+
static provideServiceResolver(...args) {
|
|
1379
|
+
if (args.length >= 2) {
|
|
1380
|
+
const [task, resolvers] = args;
|
|
1381
|
+
if (!isResultTask(task)) throw new TypeError("ResultTask.provideServiceResolver expects a ResultTask as first argument");
|
|
1382
|
+
if (!isPlainRecord(resolvers)) throw new TypeError("ResultTask.provideServiceResolver requires a resolvers object");
|
|
1383
|
+
return new ResultTask(async (context) => {
|
|
1384
|
+
const providers = resolvers;
|
|
1385
|
+
return task.execute(withServiceResolver(context, (tag) => Object.hasOwn(providers, tag.identifier) ? providers[tag.identifier]?.() : context.serviceResolver?.(tag)));
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
if (!isResultTask(args[0])) {
|
|
1389
|
+
const [resolvers] = args;
|
|
1390
|
+
if (!isPlainRecord(resolvers)) throw new TypeError("ResultTask.provideServiceResolver requires a resolvers object");
|
|
1391
|
+
return (task) => ResultTask.provideServiceResolver(task, resolvers);
|
|
1392
|
+
}
|
|
1393
|
+
throw new TypeError("ResultTask.provideServiceResolver requires a resolvers object");
|
|
1394
|
+
}
|
|
1395
|
+
/** Shares one in-flight execution and successful value. Failed executions can be retried. */
|
|
1396
|
+
static memoize(task) {
|
|
1397
|
+
let pending = void 0;
|
|
1398
|
+
return new ResultTask(async (context) => {
|
|
1399
|
+
pending ??= task.execute(context);
|
|
1400
|
+
const current = pending;
|
|
1401
|
+
const exit = await current;
|
|
1402
|
+
if (exit._tag === "Failure" && pending === current) pending = void 0;
|
|
1403
|
+
return exit;
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
/** Creates an explicit resource owner for adapters that manage long-lived scopes. */
|
|
1407
|
+
static makeScope() {
|
|
1408
|
+
const scope = new TaskScope();
|
|
1409
|
+
const controller = new AbortController();
|
|
1410
|
+
const pending = /* @__PURE__ */ new Set();
|
|
1411
|
+
let closing = void 0;
|
|
1412
|
+
return {
|
|
1413
|
+
use: (task) => new ResultTask(async (context) => {
|
|
1414
|
+
if (closing !== void 0) return died(/* @__PURE__ */ new Error("Cannot execute in a closed ResultTask scope"));
|
|
1415
|
+
const attempt = new TaskScope();
|
|
1416
|
+
const execution = task.execute(withScope(context, attempt, controller.signal)).then(async (exit) => {
|
|
1417
|
+
if (exit._tag === "Failure") return attempt.close(exit);
|
|
1418
|
+
scope.adopt(attempt);
|
|
1419
|
+
return exit;
|
|
1420
|
+
});
|
|
1421
|
+
pending.add(execution);
|
|
1422
|
+
execution.then(() => pending.delete(execution));
|
|
1423
|
+
return new Promise((resolve) => {
|
|
1424
|
+
const onAbort = () => {
|
|
1425
|
+
resolve(interrupted(context.signal));
|
|
1426
|
+
};
|
|
1427
|
+
context.signal.addEventListener("abort", onAbort, { once: true });
|
|
1428
|
+
if (context.signal.aborted) onAbort();
|
|
1429
|
+
execution.then((exit) => {
|
|
1430
|
+
context.signal.removeEventListener("abort", onAbort);
|
|
1431
|
+
resolve(interruptSuccess(exit, context.signal));
|
|
1432
|
+
});
|
|
1433
|
+
});
|
|
1434
|
+
}),
|
|
1435
|
+
close: (exit = success(void 0)) => new ResultTask(async () => {
|
|
1436
|
+
closing ??= (async () => {
|
|
1437
|
+
controller.abort("ResultTask scope closed");
|
|
1438
|
+
await Promise.all(pending);
|
|
1439
|
+
return scope.close(exit, true);
|
|
1440
|
+
})();
|
|
1441
|
+
return await closing;
|
|
1442
|
+
})
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
static provideServices(...args) {
|
|
1446
|
+
if (args.length >= 2) {
|
|
1447
|
+
const [task, services] = args;
|
|
1448
|
+
if (!isResultTask(task)) throw new TypeError("ResultTask.provideServices expects a ResultTask as first argument");
|
|
1449
|
+
if (!isPlainRecord(services)) throw new TypeError("ResultTask.provideServices requires a services object");
|
|
1450
|
+
return new ResultTask(async (context) => {
|
|
1451
|
+
const namedServices = new Map(context.namedServices);
|
|
1452
|
+
for (const [identifier, service] of Object.entries(services)) namedServices.set(identifier, service);
|
|
1453
|
+
return task.execute(withNamedServices(context, namedServices));
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
if (!isResultTask(args[0])) {
|
|
1457
|
+
const [services] = args;
|
|
1458
|
+
if (!isPlainRecord(services)) throw new TypeError("ResultTask.provideServices requires a services object");
|
|
1459
|
+
return (task) => ResultTask.provideServices(task, services);
|
|
1460
|
+
}
|
|
1461
|
+
throw new TypeError("ResultTask.provideServices requires a services object");
|
|
1462
|
+
}
|
|
1463
|
+
static map(taskOrF, f) {
|
|
1464
|
+
if (typeof taskOrF === "function") return (task) => task.map(taskOrF);
|
|
1465
|
+
if (f === void 0) throw new TypeError("ResultTask.map requires a mapping function");
|
|
1466
|
+
return taskOrF.map(f);
|
|
1467
|
+
}
|
|
1468
|
+
static mapError(taskOrF, f) {
|
|
1469
|
+
if (typeof taskOrF === "function") return (task) => task.mapError(taskOrF);
|
|
1470
|
+
if (f === void 0) throw new TypeError("ResultTask.mapError requires an error mapping function");
|
|
1471
|
+
return taskOrF.mapError(f);
|
|
1472
|
+
}
|
|
1473
|
+
static flatMap(taskOrF, f) {
|
|
1474
|
+
if (typeof taskOrF === "function") return (task) => task.flatMap(taskOrF);
|
|
1475
|
+
if (f === void 0) throw new TypeError("ResultTask.flatMap requires a continuation function");
|
|
1476
|
+
return taskOrF.flatMap(f);
|
|
1477
|
+
}
|
|
1478
|
+
static andThen(taskOrF, f) {
|
|
1479
|
+
if (typeof taskOrF === "function") return (task) => task.flatMap(taskOrF);
|
|
1480
|
+
if (f === void 0) throw new TypeError("ResultTask.andThen requires a continuation function");
|
|
1481
|
+
return taskOrF.flatMap(f);
|
|
1482
|
+
}
|
|
1483
|
+
static catchAll(taskOrF, f) {
|
|
1484
|
+
if (typeof taskOrF === "function") return (task) => task.catchAll(taskOrF);
|
|
1485
|
+
if (f === void 0) throw new TypeError("ResultTask.catchAll requires a recovery function");
|
|
1486
|
+
return taskOrF.catchAll(f);
|
|
1487
|
+
}
|
|
1488
|
+
static tap(taskOrF, f) {
|
|
1489
|
+
if (typeof taskOrF === "function") return (task) => task.tap(taskOrF);
|
|
1490
|
+
if (f === void 0) throw new TypeError("ResultTask.tap requires a side-effect function");
|
|
1491
|
+
return taskOrF.tap(f);
|
|
1492
|
+
}
|
|
1493
|
+
static tapError(taskOrF, f) {
|
|
1494
|
+
if (typeof taskOrF === "function") return (task) => task.tapError(taskOrF);
|
|
1495
|
+
if (f === void 0) throw new TypeError("ResultTask.tapError requires an error side-effect function");
|
|
1496
|
+
return taskOrF.tapError(f);
|
|
1497
|
+
}
|
|
1498
|
+
static as(taskOrValue, value) {
|
|
1499
|
+
if (taskOrValue instanceof ResultTask) {
|
|
1500
|
+
if (value === void 0) throw new TypeError("ResultTask.as requires a replacement value");
|
|
1501
|
+
return taskOrValue.as(value);
|
|
1502
|
+
}
|
|
1503
|
+
return (task) => task.as(taskOrValue);
|
|
1504
|
+
}
|
|
1505
|
+
static match(taskOrHandlers, handlers) {
|
|
1506
|
+
if (taskOrHandlers instanceof ResultTask) {
|
|
1507
|
+
if (handlers === void 0) throw new TypeError("ResultTask.match requires match handlers");
|
|
1508
|
+
return taskOrHandlers.match(handlers);
|
|
1509
|
+
}
|
|
1510
|
+
return (task) => task.match(taskOrHandlers);
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Eagerly executes a ResultTask using the standard runtime and returns a ResultAsync.
|
|
1514
|
+
*/
|
|
1515
|
+
static toResultAsync(task) {
|
|
1516
|
+
return createResultAsync(ResultTask.runResult(task));
|
|
1517
|
+
}
|
|
1518
|
+
/**
|
|
1519
|
+
* Captures an existing ResultAsync or creates a lazy task from a ResultAsync factory.
|
|
1520
|
+
*/
|
|
1521
|
+
static fromResultAsync(asyncResultOrFactory) {
|
|
1522
|
+
return new ResultTask(async (context) => {
|
|
1523
|
+
if (context.signal.aborted) return interrupted(context.signal);
|
|
1524
|
+
try {
|
|
1525
|
+
const result = await (typeof asyncResultOrFactory === "function" ? asyncResultOrFactory(context.signal) : asyncResultOrFactory);
|
|
1526
|
+
if (context.signal.aborted) return interrupted(context.signal);
|
|
1527
|
+
return result.isOk() ? success(result.value) : failed(result.error);
|
|
1528
|
+
} catch (error) {
|
|
1529
|
+
return died(error);
|
|
1530
|
+
}
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
static async runExitInternal(task, options) {
|
|
1534
|
+
const signal = options?.signal ?? new AbortController().signal;
|
|
1535
|
+
const suppliedServices = options !== void 0 && "services" in options ? options.services : void 0;
|
|
1536
|
+
const namedServices = new Map(Object.entries(suppliedServices ?? {}));
|
|
1537
|
+
const scope = new TaskScope();
|
|
1538
|
+
const context = {
|
|
1539
|
+
namedServices,
|
|
1540
|
+
services: /* @__PURE__ */ new Map(),
|
|
1541
|
+
serviceResolver: void 0,
|
|
1542
|
+
signal,
|
|
1543
|
+
scope
|
|
1544
|
+
};
|
|
1545
|
+
const exit = await task.execute(context);
|
|
1546
|
+
return interruptSuccess(await scope.close(interruptSuccess(exit, signal)), signal);
|
|
1547
|
+
}
|
|
1548
|
+
static async runResultInternal(task, options) {
|
|
1549
|
+
const exit = await ResultTask.runExitInternal(task, options);
|
|
1550
|
+
if (exit._tag === "Success") return ok(exit.value);
|
|
1551
|
+
if (exit.cause._tag === "Fail") return err(exit.cause.error);
|
|
1552
|
+
if (exit.cause._tag === "Die") return throwDefect(exit.cause.defect);
|
|
1553
|
+
if (exit.cause._tag === "Interrupt") throw new AbortError("ResultTask execution interrupted", { cause: exit.cause.reason });
|
|
1554
|
+
throw new ResultTaskCauseError(exit.cause);
|
|
1555
|
+
}
|
|
1556
|
+
static runExit(task, ...args) {
|
|
1557
|
+
return ResultTask.runExitInternal(task, args[0]);
|
|
1558
|
+
}
|
|
1559
|
+
static runResult(task, ...args) {
|
|
1560
|
+
return ResultTask.runResultInternal(task, args[0]);
|
|
1561
|
+
}
|
|
1562
|
+
static async runPromise(task, ...args) {
|
|
1563
|
+
return (await ResultTask.runResultInternal(task, args[0])).unwrapOrThrow();
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
//#endregion
|
|
676
1567
|
//#region src/redacted.ts
|
|
677
1568
|
const redactedValues = /* @__PURE__ */ new WeakMap();
|
|
678
1569
|
const formatRedacted = (label) => label === void 0 ? "<redacted>" : `<redacted:${label}>`;
|
|
@@ -1532,6 +2423,12 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1532
2423
|
const newPromise = promise.then((value) => ok(value));
|
|
1533
2424
|
return new ResultAsync(newPromise);
|
|
1534
2425
|
}
|
|
2426
|
+
/**
|
|
2427
|
+
* Eagerly executes a ResultTask using the standard runtime and wraps it in a ResultAsync.
|
|
2428
|
+
*/
|
|
2429
|
+
static fromTask(task) {
|
|
2430
|
+
return ResultTask.toResultAsync(task);
|
|
2431
|
+
}
|
|
1535
2432
|
static fromPromise(promise, errorFn) {
|
|
1536
2433
|
const newPromise = promise.then((value) => ok(value)).catch((error) => err(errorFn(error)));
|
|
1537
2434
|
return new ResultAsync(newPromise);
|
|
@@ -2027,282 +2924,4 @@ const runPromise = (result) => result.unwrapOrThrow();
|
|
|
2027
2924
|
/** Compatibility alias. Prefer `tryResultAsync` in new code. */
|
|
2028
2925
|
const tryCatchAsync = tryResultAsync;
|
|
2029
2926
|
//#endregion
|
|
2030
|
-
|
|
2031
|
-
const success = (value) => ({
|
|
2032
|
-
_tag: "Success",
|
|
2033
|
-
value
|
|
2034
|
-
});
|
|
2035
|
-
const failure = (cause) => ({
|
|
2036
|
-
_tag: "Failure",
|
|
2037
|
-
cause
|
|
2038
|
-
});
|
|
2039
|
-
const failed = (error) => failure({
|
|
2040
|
-
_tag: "Fail",
|
|
2041
|
-
error
|
|
2042
|
-
});
|
|
2043
|
-
const died = (defect) => failure({
|
|
2044
|
-
_tag: "Die",
|
|
2045
|
-
defect
|
|
2046
|
-
});
|
|
2047
|
-
const throwDefect = (defect) => {
|
|
2048
|
-
throw defect;
|
|
2049
|
-
};
|
|
2050
|
-
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
2051
|
-
const createMissingServiceError = (identifier) => {
|
|
2052
|
-
const error = /* @__PURE__ */ new Error(`Missing ResultTask service: ${identifier}`);
|
|
2053
|
-
error.name = "MissingServiceError";
|
|
2054
|
-
return error;
|
|
2055
|
-
};
|
|
2056
|
-
const isResultTaskYield = (value) => isRecord(value) && value["_tag"] === "ResultTask" && value["task"] instanceof ResultTask;
|
|
2057
|
-
const isServiceYield = (value) => isRecord(value) && value["_tag"] === "Service" && value["tag"] instanceof ServiceTagValue;
|
|
2058
|
-
const lookupService = (tag, context) => {
|
|
2059
|
-
if (context.services.has(tag.key)) return context.services.get(tag.key);
|
|
2060
|
-
return context.namedServices.get(tag.identifier);
|
|
2061
|
-
};
|
|
2062
|
-
const executeTaskSafely = async (task, executeTask) => {
|
|
2063
|
-
try {
|
|
2064
|
-
return await executeTask(task);
|
|
2065
|
-
} catch (error) {
|
|
2066
|
-
return { error };
|
|
2067
|
-
}
|
|
2068
|
-
};
|
|
2069
|
-
const closeGenerator = async (iterator, exit, runtime) => {
|
|
2070
|
-
try {
|
|
2071
|
-
let step = iterator.return(void 0);
|
|
2072
|
-
let finalExit = exit;
|
|
2073
|
-
while (step.done === false) if (isResultTaskYield(step.value)) {
|
|
2074
|
-
const outcome = await executeTaskSafely(step.value.task, runtime.executeTask);
|
|
2075
|
-
if ("error" in outcome) {
|
|
2076
|
-
finalExit = died(outcome.error);
|
|
2077
|
-
step = iterator.return(void 0);
|
|
2078
|
-
} else if (outcome._tag === "Failure") {
|
|
2079
|
-
finalExit = outcome;
|
|
2080
|
-
step = iterator.return(void 0);
|
|
2081
|
-
} else step = iterator.next(outcome);
|
|
2082
|
-
} else if (isServiceYield(step.value)) {
|
|
2083
|
-
const service = lookupService(step.value.tag, runtime.context);
|
|
2084
|
-
if (service === void 0) {
|
|
2085
|
-
finalExit = died(createMissingServiceError(step.value.tag.identifier));
|
|
2086
|
-
step = iterator.return(void 0);
|
|
2087
|
-
} else step = iterator.next(service);
|
|
2088
|
-
} else {
|
|
2089
|
-
finalExit = died(/* @__PURE__ */ new TypeError("ResultTask.gen yielded an unsupported value"));
|
|
2090
|
-
step = iterator.return(void 0);
|
|
2091
|
-
}
|
|
2092
|
-
return finalExit;
|
|
2093
|
-
} catch (error) {
|
|
2094
|
-
return died(error);
|
|
2095
|
-
}
|
|
2096
|
-
};
|
|
2097
|
-
const withServices = (context, services) => ({
|
|
2098
|
-
namedServices: context.namedServices,
|
|
2099
|
-
services,
|
|
2100
|
-
signal: context.signal
|
|
2101
|
-
});
|
|
2102
|
-
const withNamedServices = (context, namedServices) => ({
|
|
2103
|
-
namedServices,
|
|
2104
|
-
services: context.services,
|
|
2105
|
-
signal: context.signal
|
|
2106
|
-
});
|
|
2107
|
-
var ServiceTagValue = class {
|
|
2108
|
-
_tag = "ServiceTag";
|
|
2109
|
-
identifier;
|
|
2110
|
-
key;
|
|
2111
|
-
constructor(identifier) {
|
|
2112
|
-
this.identifier = identifier;
|
|
2113
|
-
this.key = Symbol(identifier);
|
|
2114
|
-
}
|
|
2115
|
-
*[Symbol.iterator]() {
|
|
2116
|
-
return yield {
|
|
2117
|
-
_tag: "Service",
|
|
2118
|
-
tag: this
|
|
2119
|
-
};
|
|
2120
|
-
}
|
|
2121
|
-
};
|
|
2122
|
-
/**
|
|
2123
|
-
* A lazy, composable workflow that can succeed with `A` or fail with `E`.
|
|
2124
|
-
*
|
|
2125
|
-
* Constructing a `ResultTask` never runs its work. Use `runResult`, `runExit`, or `runPromise` at an
|
|
2126
|
-
* explicit application boundary to execute it.
|
|
2127
|
-
*/
|
|
2128
|
-
var ResultTask = class ResultTask extends Pipeable {
|
|
2129
|
-
execute;
|
|
2130
|
-
constructor(execute) {
|
|
2131
|
-
super();
|
|
2132
|
-
this.execute = execute;
|
|
2133
|
-
}
|
|
2134
|
-
/** Creates a task that succeeds with `value` when it is executed. */
|
|
2135
|
-
static succeed(value) {
|
|
2136
|
-
return new ResultTask(() => Promise.resolve(success(value)));
|
|
2137
|
-
}
|
|
2138
|
-
/** Creates a task that fails with `error` when it is executed. */
|
|
2139
|
-
static fail(error) {
|
|
2140
|
-
return new ResultTask(() => Promise.resolve(failed(error)));
|
|
2141
|
-
}
|
|
2142
|
-
/** Lifts an already computed Result into a lazy task. */
|
|
2143
|
-
static fromResult(result) {
|
|
2144
|
-
return new ResultTask(() => Promise.resolve(result.isOk() ? success(result.value) : failed(result.error)));
|
|
2145
|
-
}
|
|
2146
|
-
/** Creates a task for synchronous work. Thrown values are defects, not typed failures. */
|
|
2147
|
-
static sync(evaluate) {
|
|
2148
|
-
return new ResultTask(() => Promise.resolve(success(evaluate())));
|
|
2149
|
-
}
|
|
2150
|
-
/** Creates a task that maps synchronous throws into the typed error channel. */
|
|
2151
|
-
static try(options) {
|
|
2152
|
-
return new ResultTask(() => {
|
|
2153
|
-
try {
|
|
2154
|
-
return Promise.resolve(success(options.try()));
|
|
2155
|
-
} catch (error) {
|
|
2156
|
-
return Promise.resolve(failed(options.catch(error)));
|
|
2157
|
-
}
|
|
2158
|
-
});
|
|
2159
|
-
}
|
|
2160
|
-
/** Creates a lazy task that maps synchronous throws and promise rejections into `E`. */
|
|
2161
|
-
static tryPromise(options) {
|
|
2162
|
-
return new ResultTask(async (context) => {
|
|
2163
|
-
try {
|
|
2164
|
-
return success(await options.try(context.signal));
|
|
2165
|
-
} catch (error) {
|
|
2166
|
-
return failed(options.catch(error));
|
|
2167
|
-
}
|
|
2168
|
-
});
|
|
2169
|
-
}
|
|
2170
|
-
/** Creates a service tag that can be requested with `yield*` inside `ResultTask.gen`. */
|
|
2171
|
-
static service(identifier) {
|
|
2172
|
-
return new ServiceTagValue(identifier);
|
|
2173
|
-
}
|
|
2174
|
-
/** Builds a task from a generator and short-circuits on the first failed task. */
|
|
2175
|
-
static gen(body) {
|
|
2176
|
-
return new ResultTask(async (context) => {
|
|
2177
|
-
try {
|
|
2178
|
-
const iterator = body();
|
|
2179
|
-
const executeTask = (task) => task.execute(context);
|
|
2180
|
-
let step = iterator.next();
|
|
2181
|
-
while (step.done === false) if (isResultTaskYield(step.value)) {
|
|
2182
|
-
const outcome = await executeTaskSafely(step.value.task, executeTask);
|
|
2183
|
-
if ("error" in outcome) return await closeGenerator(iterator, died(outcome.error), {
|
|
2184
|
-
context,
|
|
2185
|
-
executeTask
|
|
2186
|
-
});
|
|
2187
|
-
if (outcome._tag === "Failure") return await closeGenerator(iterator, outcome, {
|
|
2188
|
-
context,
|
|
2189
|
-
executeTask
|
|
2190
|
-
});
|
|
2191
|
-
step = iterator.next(outcome);
|
|
2192
|
-
} else if (isServiceYield(step.value)) {
|
|
2193
|
-
const service = lookupService(step.value.tag, context);
|
|
2194
|
-
if (service === void 0) return await closeGenerator(iterator, died(createMissingServiceError(step.value.tag.identifier)), {
|
|
2195
|
-
context,
|
|
2196
|
-
executeTask
|
|
2197
|
-
});
|
|
2198
|
-
step = iterator.next(service);
|
|
2199
|
-
} else return await closeGenerator(iterator, died(/* @__PURE__ */ new TypeError("ResultTask.gen yielded an unsupported value")), {
|
|
2200
|
-
context,
|
|
2201
|
-
executeTask
|
|
2202
|
-
});
|
|
2203
|
-
return success(step.value);
|
|
2204
|
-
} catch (error) {
|
|
2205
|
-
return died(error);
|
|
2206
|
-
}
|
|
2207
|
-
});
|
|
2208
|
-
}
|
|
2209
|
-
/** Maps the success value without executing the task. */
|
|
2210
|
-
map(f) {
|
|
2211
|
-
return new ResultTask(async (context) => {
|
|
2212
|
-
const exit = await this.execute(context);
|
|
2213
|
-
if (exit._tag === "Failure") return exit;
|
|
2214
|
-
return success(f(exit.value));
|
|
2215
|
-
});
|
|
2216
|
-
}
|
|
2217
|
-
/** Chains another task from the success value without executing either task immediately. */
|
|
2218
|
-
flatMap(f) {
|
|
2219
|
-
return new ResultTask(async (context) => {
|
|
2220
|
-
const exit = await this.execute(context);
|
|
2221
|
-
if (exit._tag === "Failure") return exit;
|
|
2222
|
-
return f(exit.value).execute(context);
|
|
2223
|
-
});
|
|
2224
|
-
}
|
|
2225
|
-
/** Alias for `flatMap`, matching the existing Resultar vocabulary. */
|
|
2226
|
-
andThen(f) {
|
|
2227
|
-
return this.flatMap(f);
|
|
2228
|
-
}
|
|
2229
|
-
/** Recovers from a typed failure without recovering from a runtime defect. */
|
|
2230
|
-
catchAll(f) {
|
|
2231
|
-
return new ResultTask(async (context) => {
|
|
2232
|
-
const exit = await this.execute(context);
|
|
2233
|
-
if (exit._tag === "Success") return exit;
|
|
2234
|
-
if (exit.cause._tag === "Die") return exit;
|
|
2235
|
-
return f(exit.cause.error).execute(context);
|
|
2236
|
-
});
|
|
2237
|
-
}
|
|
2238
|
-
/** Enables `yield* task` inside `ResultTask.gen` workflows. */
|
|
2239
|
-
*[Symbol.iterator]() {
|
|
2240
|
-
const exit = yield {
|
|
2241
|
-
_tag: "ResultTask",
|
|
2242
|
-
task: this
|
|
2243
|
-
};
|
|
2244
|
-
if (exit._tag === "Success") return exit.value;
|
|
2245
|
-
return throwDefect(/* @__PURE__ */ new Error("ResultTask generator received a failed task result"));
|
|
2246
|
-
}
|
|
2247
|
-
/** Provides one required service and removes it from the task's requirements. */
|
|
2248
|
-
static provideService(task, tag, service) {
|
|
2249
|
-
return new ResultTask(async (context) => {
|
|
2250
|
-
const services = new Map([...context.services, [tag.key, service]]);
|
|
2251
|
-
return task.execute(withServices(context, services));
|
|
2252
|
-
});
|
|
2253
|
-
}
|
|
2254
|
-
/** Provides all requirements using an object keyed by service identifier. */
|
|
2255
|
-
static provideServices(task, services) {
|
|
2256
|
-
return new ResultTask(async (context) => {
|
|
2257
|
-
const namedServices = new Map(context.namedServices);
|
|
2258
|
-
for (const [identifier, service] of Object.entries(services)) namedServices.set(identifier, service);
|
|
2259
|
-
return task.execute(withNamedServices(context, namedServices));
|
|
2260
|
-
});
|
|
2261
|
-
}
|
|
2262
|
-
/** Maps the success value using the canonical functional form. */
|
|
2263
|
-
static map(task, f) {
|
|
2264
|
-
return task.map(f);
|
|
2265
|
-
}
|
|
2266
|
-
/** Chains a task using the canonical functional form. */
|
|
2267
|
-
static flatMap(task, f) {
|
|
2268
|
-
return task.flatMap(f);
|
|
2269
|
-
}
|
|
2270
|
-
/** Recovers from a typed failure using the canonical functional form. */
|
|
2271
|
-
static catchAll(task, f) {
|
|
2272
|
-
return task.catchAll(f);
|
|
2273
|
-
}
|
|
2274
|
-
static async runExitInternal(task, options) {
|
|
2275
|
-
const signal = options?.signal ?? new AbortController().signal;
|
|
2276
|
-
const suppliedServices = options !== void 0 && "services" in options ? options.services : void 0;
|
|
2277
|
-
const context = {
|
|
2278
|
-
namedServices: new Map(Object.entries(suppliedServices ?? {})),
|
|
2279
|
-
services: /* @__PURE__ */ new Map(),
|
|
2280
|
-
signal
|
|
2281
|
-
};
|
|
2282
|
-
try {
|
|
2283
|
-
return await task.execute(context);
|
|
2284
|
-
} catch (error) {
|
|
2285
|
-
return died(error);
|
|
2286
|
-
}
|
|
2287
|
-
}
|
|
2288
|
-
static async runResultInternal(task, options) {
|
|
2289
|
-
const exit = await ResultTask.runExitInternal(task, options);
|
|
2290
|
-
if (exit._tag === "Success") return ok(exit.value);
|
|
2291
|
-
if (exit.cause._tag === "Fail") return err(exit.cause.error);
|
|
2292
|
-
return throwDefect(exit.cause.defect);
|
|
2293
|
-
}
|
|
2294
|
-
/** Runs a task and preserves success, typed failure, and runtime defects in an `Exit`. */
|
|
2295
|
-
static runExit(task, ...args) {
|
|
2296
|
-
return ResultTask.runExitInternal(task, args[0]);
|
|
2297
|
-
}
|
|
2298
|
-
/** Runs a task and returns a Result, rejecting only when a runtime defect occurs. */
|
|
2299
|
-
static runResult(task, ...args) {
|
|
2300
|
-
return ResultTask.runResultInternal(task, args[0]);
|
|
2301
|
-
}
|
|
2302
|
-
/** Runs a task and returns the success value, rejecting on typed failures or defects. */
|
|
2303
|
-
static async runPromise(task, ...args) {
|
|
2304
|
-
return (await ResultTask.runResultInternal(task, args[0])).unwrapOrThrow();
|
|
2305
|
-
}
|
|
2306
|
-
};
|
|
2307
|
-
//#endregion
|
|
2308
|
-
export { AbortError, DisposableResult, DisposableResultAsync, Result, ResultAsync, ResultTask, createTaggedError, tryResult as default, tryResult as try, tryResult, err, errAsync, findCause, fromCallback, fromPromise, fromSafePromise, fromThrowable, fromThrowableAsync, isAbortError, isError, isRedacted, matchError, matchErrorPartial, ok, okAsync, redact, revealRedacted, runPromise, runSync, safeTry, taggedEnum, tryResultAsync as tryAsync, tryResultAsync, tryCatch, tryCatchAsync, unit, unitAsync };
|
|
2927
|
+
export { AbortError, DisposableResult, DisposableResultAsync, MissingServiceError, Result, ResultAsync, ResultTask, ResultTaskCauseError, ResultTaskTypeId, ResultTaskYieldTypeId, ServiceTagTypeId, createTaggedError, tryResult as default, tryResult as try, tryResult, err, errAsync, findCause, fromCallback, fromPromise, fromSafePromise, fromThrowable, fromThrowableAsync, isAbortError, isError, isRedacted, isResultTask, isServiceTag, matchError, matchErrorPartial, ok, okAsync, redact, revealRedacted, runPromise, runSync, safeTry, serviceTag, taggedEnum, tryResultAsync as tryAsync, tryResultAsync, tryCatch, tryCatchAsync, unit, unitAsync };
|