resultar 3.3.1 → 3.5.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/README.md +58 -12
- package/dist/index.d.ts +948 -114
- package/dist/index.js +562 -213
- package/package.json +7 -7
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
//#region src/abort-error.ts
|
|
2
|
+
/**
|
|
3
|
+
* Standard cancellation error used by ResultAsync retry, race, timeout, and resource helpers.
|
|
4
|
+
*/
|
|
2
5
|
var AbortError = class extends Error {
|
|
6
|
+
/**
|
|
7
|
+
* Creates an AbortError.
|
|
8
|
+
*/
|
|
3
9
|
constructor(message = "Operation aborted", options) {
|
|
4
10
|
super(message, options);
|
|
5
11
|
this.name = "AbortError";
|
|
@@ -7,6 +13,9 @@ var AbortError = class extends Error {
|
|
|
7
13
|
};
|
|
8
14
|
const hasAbortName = (value) => typeof value === "object" && value !== null && "name" in value && value.name === "AbortError";
|
|
9
15
|
const hasAbortCode = (value) => typeof value === "object" && value !== null && "code" in value && value.code === "ABORT_ERR";
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether a value is an AbortError or abort-shaped platform error.
|
|
18
|
+
*/
|
|
10
19
|
const isAbortError = (value) => value instanceof AbortError || hasAbortName(value) || hasAbortCode(value);
|
|
11
20
|
//#endregion
|
|
12
21
|
//#region src/pipe.ts
|
|
@@ -64,8 +73,9 @@ const callTaggedHandler = (value, handlers) => {
|
|
|
64
73
|
return { value: handler(value) };
|
|
65
74
|
};
|
|
66
75
|
const callErrorHandler = (error, handlers) => {
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
const handler = handlers.Error;
|
|
77
|
+
if (handler === void 0) return noTaggedHandlerMatch;
|
|
78
|
+
return { value: handler(error) };
|
|
69
79
|
};
|
|
70
80
|
const callTaggedOrErrorHandler = (error, handlers) => {
|
|
71
81
|
const tagged = callTaggedHandler(error, handlers);
|
|
@@ -243,7 +253,8 @@ var ResultNamespace = class {
|
|
|
243
253
|
}
|
|
244
254
|
static validateAll(resultListOrItems, f) {
|
|
245
255
|
if (f === void 0) return combineResultListWithAllErrors(resultListOrItems);
|
|
246
|
-
|
|
256
|
+
const resultList = Array.from(resultListOrItems, f);
|
|
257
|
+
return combineResultListWithAllErrors(resultList);
|
|
247
258
|
}
|
|
248
259
|
static zip(left, right) {
|
|
249
260
|
return combineResultList([left, right]);
|
|
@@ -537,32 +548,64 @@ var Err = class extends ResultVariant {
|
|
|
537
548
|
var DisposableResult = class {
|
|
538
549
|
disposed = false;
|
|
539
550
|
finalizer;
|
|
551
|
+
/**
|
|
552
|
+
* Wrapped Result value.
|
|
553
|
+
*/
|
|
540
554
|
result;
|
|
555
|
+
/**
|
|
556
|
+
* Creates a disposable wrapper around a Result.
|
|
557
|
+
*/
|
|
541
558
|
constructor(result, finalizer) {
|
|
542
559
|
this.result = result;
|
|
543
560
|
this.finalizer = finalizer;
|
|
544
561
|
}
|
|
562
|
+
/**
|
|
563
|
+
* Ok value when present, otherwise undefined cast to `T` for compatibility.
|
|
564
|
+
*/
|
|
545
565
|
get value() {
|
|
546
566
|
return this.result.isOk() ? this.result.value : void 0;
|
|
547
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* Err value when present, otherwise undefined cast to `E` for compatibility.
|
|
570
|
+
*/
|
|
548
571
|
get error() {
|
|
549
572
|
return this.result.isErr() ? this.result.error : void 0;
|
|
550
573
|
}
|
|
574
|
+
/**
|
|
575
|
+
* Delegates to the wrapped Result `_unsafeUnwrap`.
|
|
576
|
+
*/
|
|
551
577
|
_unsafeUnwrap(config) {
|
|
552
578
|
return this.result._unsafeUnwrap(config);
|
|
553
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* Delegates to the wrapped Result `_unsafeUnwrapErr`.
|
|
582
|
+
*/
|
|
554
583
|
_unsafeUnwrapErr(config) {
|
|
555
584
|
return this.result._unsafeUnwrapErr(config);
|
|
556
585
|
}
|
|
586
|
+
/**
|
|
587
|
+
* Returns whether the wrapped Result is Ok.
|
|
588
|
+
*/
|
|
557
589
|
isOk() {
|
|
558
590
|
return this.result.isOk();
|
|
559
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* Returns whether the wrapped Result is Err.
|
|
594
|
+
*/
|
|
560
595
|
isErr() {
|
|
561
596
|
return this.result.isErr();
|
|
562
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Returns the wrapped Ok value, or `defaultValue` when Err.
|
|
600
|
+
*/
|
|
563
601
|
unwrapOr(defaultValue) {
|
|
564
602
|
return this.result.unwrapOr(defaultValue);
|
|
565
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* Runs the finalizer once.
|
|
606
|
+
*
|
|
607
|
+
* Finalizer exceptions are intentionally swallowed.
|
|
608
|
+
*/
|
|
566
609
|
[Symbol.dispose]() {
|
|
567
610
|
if (this.disposed) return;
|
|
568
611
|
this.disposed = true;
|
|
@@ -593,6 +636,11 @@ function ok(value) {
|
|
|
593
636
|
function err(error) {
|
|
594
637
|
return new Err(error);
|
|
595
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Wraps a throwing synchronous function so every later call returns a Result.
|
|
641
|
+
*
|
|
642
|
+
* Prefer `tryResult` when you want to run the operation immediately.
|
|
643
|
+
*/
|
|
596
644
|
const fromThrowable = Result.fromThrowable;
|
|
597
645
|
function tryResult(input, errorFn) {
|
|
598
646
|
const fn = typeof input === "function" ? input : input.try;
|
|
@@ -604,12 +652,269 @@ function tryResult(input, errorFn) {
|
|
|
604
652
|
return err(error);
|
|
605
653
|
}
|
|
606
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* Creates an Ok result with `undefined`.
|
|
657
|
+
*/
|
|
607
658
|
function unit() {
|
|
608
659
|
return ok(void 0);
|
|
609
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* Runs a `Result` at an application boundary and returns the success value.
|
|
663
|
+
*
|
|
664
|
+
* If the result is `Err`, this throws the error. Use it at final framework, CLI, test, or bootstrap
|
|
665
|
+
* boundaries after the error channel has already been modeled with Resultar.
|
|
666
|
+
*/
|
|
667
|
+
const runSync = (result) => result.unwrapOrThrow();
|
|
610
668
|
/** Compatibility alias. Prefer `tryResult` in new code. */
|
|
611
669
|
const tryCatch = tryResult;
|
|
612
670
|
//#endregion
|
|
671
|
+
//#region src/redacted.ts
|
|
672
|
+
const redactedValues = /* @__PURE__ */ new WeakMap();
|
|
673
|
+
const formatRedacted = (label) => label === void 0 ? "<redacted>" : `<redacted:${label}>`;
|
|
674
|
+
var RedactedValue = class {
|
|
675
|
+
__resultarRedacted = true;
|
|
676
|
+
label;
|
|
677
|
+
/**
|
|
678
|
+
* Stores a sensitive value behind a redacted string wrapper.
|
|
679
|
+
*/
|
|
680
|
+
constructor(value, label) {
|
|
681
|
+
this.label = label;
|
|
682
|
+
redactedValues.set(this, value);
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Serializes to the redacted placeholder.
|
|
686
|
+
*/
|
|
687
|
+
toJSON() {
|
|
688
|
+
return this.toString();
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Converts to the redacted placeholder.
|
|
692
|
+
*/
|
|
693
|
+
toString() {
|
|
694
|
+
return formatRedacted(this.label);
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
/**
|
|
698
|
+
* Wraps a sensitive value so tagged-error messages and JSON output show only a placeholder.
|
|
699
|
+
*/
|
|
700
|
+
const redact = (value, label) => new RedactedValue(value, label);
|
|
701
|
+
/**
|
|
702
|
+
* Checks whether a value was created by `redact`.
|
|
703
|
+
*/
|
|
704
|
+
const isRedacted = (value) => typeof value === "object" && value !== null && redactedValues.has(value);
|
|
705
|
+
/**
|
|
706
|
+
* Returns the original sensitive value from a Redacted wrapper.
|
|
707
|
+
*/
|
|
708
|
+
const revealRedacted = (value) => redactedValues.get(value);
|
|
709
|
+
/**
|
|
710
|
+
* Converts a Redacted wrapper to its placeholder string.
|
|
711
|
+
*/
|
|
712
|
+
const stringifyRedacted = (value) => value.toString();
|
|
713
|
+
//#endregion
|
|
714
|
+
//#region src/tagged-error.ts
|
|
715
|
+
const defaultBaseError = Error;
|
|
716
|
+
const defaultMessageTemplate = "$message";
|
|
717
|
+
const nativeError = Error;
|
|
718
|
+
const templateVariableMatcher = /\$(?<name>[a-zA-Z_][a-zA-Z0-9_]*)/gu;
|
|
719
|
+
const reservedTemplateVariables = /* @__PURE__ */ new Set([
|
|
720
|
+
"_tag",
|
|
721
|
+
"cause",
|
|
722
|
+
"fingerprint",
|
|
723
|
+
"message",
|
|
724
|
+
"messageTemplate",
|
|
725
|
+
"name",
|
|
726
|
+
"stack"
|
|
727
|
+
]);
|
|
728
|
+
function isError(value) {
|
|
729
|
+
return nativeError.isError?.(value) ?? value instanceof Error;
|
|
730
|
+
}
|
|
731
|
+
const stringifyDirectTemplateValue = String;
|
|
732
|
+
const stringifySymbolTemplateValue = (value) => {
|
|
733
|
+
const symbol = value;
|
|
734
|
+
return symbol.description ?? symbol.toString();
|
|
735
|
+
};
|
|
736
|
+
const stringifyFunctionTemplateValue = (value) => value.name;
|
|
737
|
+
const stringifyObjectTemplateValue = (value) => JSON.stringify(value) ?? Object.prototype.toString.call(value);
|
|
738
|
+
const templateValueStringifiers = {
|
|
739
|
+
bigint: stringifyDirectTemplateValue,
|
|
740
|
+
boolean: stringifyDirectTemplateValue,
|
|
741
|
+
function: stringifyFunctionTemplateValue,
|
|
742
|
+
number: stringifyDirectTemplateValue,
|
|
743
|
+
object: stringifyObjectTemplateValue,
|
|
744
|
+
string: stringifyDirectTemplateValue,
|
|
745
|
+
symbol: stringifySymbolTemplateValue
|
|
746
|
+
};
|
|
747
|
+
const stringifyTemplateValue = (value) => isRedacted(value) ? stringifyRedacted(value) : templateValueStringifiers[typeof value](value);
|
|
748
|
+
const compileMessageInterpolator = (template) => (values) => template.replaceAll(templateVariableMatcher, (variable, key) => {
|
|
749
|
+
const value = values?.[key];
|
|
750
|
+
return value === void 0 ? variable : stringifyTemplateValue(value);
|
|
751
|
+
});
|
|
752
|
+
const getTemplateVariableNames = (template) => {
|
|
753
|
+
templateVariableMatcher.lastIndex = 0;
|
|
754
|
+
return [...template.matchAll(templateVariableMatcher)].map((match) => String(match[1]));
|
|
755
|
+
};
|
|
756
|
+
const assertNoReservedTemplateVariables = (tag, template) => {
|
|
757
|
+
if (template === defaultMessageTemplate) return;
|
|
758
|
+
const reservedVariables = getTemplateVariableNames(template).filter((variable) => reservedTemplateVariables.has(variable));
|
|
759
|
+
if (reservedVariables.length > 0) throw new Error(`createTaggedError(${tag}): reserved template variable $${reservedVariables[0]} is not allowed`);
|
|
760
|
+
};
|
|
761
|
+
const getErrorOptions = (cause) => cause === void 0 ? {} : { cause };
|
|
762
|
+
const assignReadonly = (target, key, value) => {
|
|
763
|
+
Object.defineProperty(target, key, {
|
|
764
|
+
configurable: true,
|
|
765
|
+
enumerable: true,
|
|
766
|
+
value,
|
|
767
|
+
writable: false
|
|
768
|
+
});
|
|
769
|
+
};
|
|
770
|
+
const serializeTaggedValue = (value) => isRedacted(value) ? stringifyRedacted(value) : value;
|
|
771
|
+
const serializeCause = (cause, seen = /* @__PURE__ */ new Set()) => {
|
|
772
|
+
if (!isError(cause)) return serializeTaggedValue(cause);
|
|
773
|
+
if (seen.has(cause)) return {
|
|
774
|
+
cause: "[Circular]",
|
|
775
|
+
message: cause.message,
|
|
776
|
+
name: cause.name,
|
|
777
|
+
stack: cause.stack
|
|
778
|
+
};
|
|
779
|
+
seen.add(cause);
|
|
780
|
+
const json = {
|
|
781
|
+
message: cause.message,
|
|
782
|
+
name: cause.name,
|
|
783
|
+
stack: cause.stack
|
|
784
|
+
};
|
|
785
|
+
if (cause.cause !== void 0) json["cause"] = serializeCause(cause.cause, seen);
|
|
786
|
+
return json;
|
|
787
|
+
};
|
|
788
|
+
/**
|
|
789
|
+
* Finds the first cause in an Error cause chain that is an instance of `ErrorClass`.
|
|
790
|
+
*/
|
|
791
|
+
const findCause = (error, ErrorClass) => {
|
|
792
|
+
const seen = /* @__PURE__ */ new Set();
|
|
793
|
+
let current = error;
|
|
794
|
+
while (isError(current)) {
|
|
795
|
+
if (seen.has(current)) break;
|
|
796
|
+
seen.add(current);
|
|
797
|
+
if (current instanceof ErrorClass) return current;
|
|
798
|
+
current = current.cause;
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
/**
|
|
802
|
+
* Creates an Error class with a stable `_tag`, typed template props, cause support, and JSON output.
|
|
803
|
+
*
|
|
804
|
+
* Prefer tagged errors for domain failures that travel through Resultar error channels.
|
|
805
|
+
*
|
|
806
|
+
* @example
|
|
807
|
+
* ```ts
|
|
808
|
+
* class UserNotFoundError extends createTaggedError({
|
|
809
|
+
* name: "UserNotFoundError",
|
|
810
|
+
* message: "User $id was not found",
|
|
811
|
+
* }) {}
|
|
812
|
+
* ```
|
|
813
|
+
*/
|
|
814
|
+
function createTaggedError(options) {
|
|
815
|
+
const BaseError = options.extends ?? defaultBaseError;
|
|
816
|
+
const messageTemplate = options.message ?? defaultMessageTemplate;
|
|
817
|
+
assertNoReservedTemplateVariables(options.name, messageTemplate);
|
|
818
|
+
const interpolate = compileMessageInterpolator(messageTemplate);
|
|
819
|
+
class GeneratedTaggedError extends BaseError {
|
|
820
|
+
static tag = options.name;
|
|
821
|
+
_tag = options.name;
|
|
822
|
+
fingerprint = [options.name, messageTemplate];
|
|
823
|
+
messageTemplate = messageTemplate;
|
|
824
|
+
constructor(props) {
|
|
825
|
+
const message = interpolate(props);
|
|
826
|
+
super(message, getErrorOptions(props?.cause));
|
|
827
|
+
this.name = options.name;
|
|
828
|
+
if (props) {
|
|
829
|
+
for (const [key, value] of Object.entries(props)) if (!reservedTemplateVariables.has(key)) assignReadonly(this, key, value);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Narrows a value to this tagged error class.
|
|
834
|
+
*/
|
|
835
|
+
static is(value) {
|
|
836
|
+
return value instanceof this;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Creates an Err containing this tagged error.
|
|
840
|
+
*/
|
|
841
|
+
static err(...args) {
|
|
842
|
+
return err(new this(...args));
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Finds a matching cause in this error's cause chain.
|
|
846
|
+
*/
|
|
847
|
+
findCause(ErrorClass) {
|
|
848
|
+
return findCause(this, ErrorClass);
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Serializes the tagged error, metadata, and cause chain.
|
|
852
|
+
*/
|
|
853
|
+
toJSON() {
|
|
854
|
+
const json = {
|
|
855
|
+
_tag: this._tag,
|
|
856
|
+
fingerprint: this.fingerprint,
|
|
857
|
+
message: this.message,
|
|
858
|
+
messageTemplate: this.messageTemplate,
|
|
859
|
+
name: this.name
|
|
860
|
+
};
|
|
861
|
+
if (this.cause !== void 0) json["cause"] = serializeCause(this.cause);
|
|
862
|
+
for (const [key, value] of Object.entries(this)) if (!(key in json) && key !== "cause" && key !== "stack") json[key] = serializeTaggedValue(value);
|
|
863
|
+
return json;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return GeneratedTaggedError;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Creates a small tagged-union factory with member constructors, `$is`, and `$match`.
|
|
870
|
+
*/
|
|
871
|
+
const taggedEnum = () => {
|
|
872
|
+
return new Proxy({
|
|
873
|
+
$is(tag, value) {
|
|
874
|
+
return typeof value === "object" && value !== null && "_tag" in value && value._tag === tag;
|
|
875
|
+
},
|
|
876
|
+
$match(value, handlers) {
|
|
877
|
+
const handler = handlers[value._tag];
|
|
878
|
+
if (handler === void 0) throw new Error(`No tagged enum handler for ${String(value._tag)}`);
|
|
879
|
+
return handler(value);
|
|
880
|
+
}
|
|
881
|
+
}, { get(base, property, receiver) {
|
|
882
|
+
if (property in base) return Reflect.get(base, property, receiver);
|
|
883
|
+
if (typeof property !== "string") return;
|
|
884
|
+
return (props) => {
|
|
885
|
+
const value = { _tag: property };
|
|
886
|
+
if (props !== void 0) {
|
|
887
|
+
for (const [key, propValue] of Object.entries(props)) if (key !== "_tag") value[key] = propValue;
|
|
888
|
+
}
|
|
889
|
+
return Object.freeze(value);
|
|
890
|
+
};
|
|
891
|
+
} });
|
|
892
|
+
};
|
|
893
|
+
/**
|
|
894
|
+
* Matches an Error by tagged `_tag`, with optional native `Error` fallback.
|
|
895
|
+
*
|
|
896
|
+
* Throws the original error when no handler matches.
|
|
897
|
+
*/
|
|
898
|
+
const matchError = (error, handlers) => {
|
|
899
|
+
if ("_tag" in error && typeof error._tag === "string" && error._tag in handlers) {
|
|
900
|
+
const handler = handlers[error._tag];
|
|
901
|
+
return handler(error);
|
|
902
|
+
}
|
|
903
|
+
if (handlers.Error) return handlers.Error(error);
|
|
904
|
+
throw error;
|
|
905
|
+
};
|
|
906
|
+
/**
|
|
907
|
+
* Matches an Error by tagged `_tag`, then native `Error`, then the provided fallback.
|
|
908
|
+
*/
|
|
909
|
+
const matchErrorPartial = (error, handlers, fallback) => {
|
|
910
|
+
if ("_tag" in error && typeof error._tag === "string" && error._tag in handlers) {
|
|
911
|
+
const handler = handlers[error._tag];
|
|
912
|
+
if (handler) return handler(error);
|
|
913
|
+
}
|
|
914
|
+
if (handlers.Error) return handlers.Error(error);
|
|
915
|
+
return fallback(error);
|
|
916
|
+
};
|
|
917
|
+
//#endregion
|
|
613
918
|
//#region src/result-async.ts
|
|
614
919
|
function isTryResultAsyncOptions(input) {
|
|
615
920
|
return typeof input === "object" && input !== null && "try" in input;
|
|
@@ -725,11 +1030,84 @@ const runConcurrentResultAsyncItems = (options) => Promise.try(async () => {
|
|
|
725
1030
|
});
|
|
726
1031
|
const createResultAsyncRetryAbortError = (signal) => signal.reason === void 0 ? new AbortError("ResultAsync retry aborted") : new AbortError("ResultAsync retry aborted", { cause: signal.reason });
|
|
727
1032
|
const createResultAsyncResourceAbortError = (signal) => signal.reason === void 0 ? new AbortError("ResultAsync resource scope aborted") : new AbortError("ResultAsync resource scope aborted", { cause: signal.reason });
|
|
728
|
-
const
|
|
729
|
-
|
|
1033
|
+
const createResultAsyncCallbackAbortError = (signal) => signal.reason === void 0 ? new AbortError("ResultAsync callback aborted") : new AbortError("ResultAsync callback aborted", { cause: signal.reason });
|
|
1034
|
+
const emptyResultAsyncCallbackCleanup = () => void 0;
|
|
1035
|
+
const callResultAsyncCallbackCleanup = (cleanup) => {
|
|
1036
|
+
try {
|
|
1037
|
+
cleanup();
|
|
1038
|
+
} catch {}
|
|
730
1039
|
};
|
|
731
|
-
const
|
|
732
|
-
|
|
1040
|
+
const normalizeResultAsyncCallbackUnexpectedError = (error) => isError(error) ? error : new Error("ResultAsync callback error mapper threw a non-Error value", { cause: error });
|
|
1041
|
+
const completeResultAsyncCallback = (completion) => {
|
|
1042
|
+
if ("error" in completion.settlement) {
|
|
1043
|
+
completion.rejectResult(normalizeResultAsyncCallbackUnexpectedError(completion.settlement.error));
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
completion.resolveResult(completion.settlement.result);
|
|
1047
|
+
};
|
|
1048
|
+
const fromResultAsyncCallback = (options) => {
|
|
1049
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
1050
|
+
return new ResultAsync(new Promise((resolveResult, rejectResult) => {
|
|
1051
|
+
if (signal.aborted) {
|
|
1052
|
+
resolveResult(Result.err(createResultAsyncCallbackAbortError(signal)));
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
let cleanup = emptyResultAsyncCallbackCleanup;
|
|
1056
|
+
let settlement = void 0;
|
|
1057
|
+
let subscribed = false;
|
|
1058
|
+
const finalize = () => {
|
|
1059
|
+
if (!subscribed) return;
|
|
1060
|
+
const completed = settlement;
|
|
1061
|
+
if (completed === void 0) return;
|
|
1062
|
+
signal.removeEventListener("abort", abort);
|
|
1063
|
+
callResultAsyncCallbackCleanup(cleanup);
|
|
1064
|
+
completeResultAsyncCallback({
|
|
1065
|
+
rejectResult,
|
|
1066
|
+
resolveResult,
|
|
1067
|
+
settlement: completed
|
|
1068
|
+
});
|
|
1069
|
+
};
|
|
1070
|
+
const settle = (result) => {
|
|
1071
|
+
if (settlement !== void 0) return;
|
|
1072
|
+
settlement = { result };
|
|
1073
|
+
finalize();
|
|
1074
|
+
};
|
|
1075
|
+
const rejectUnexpected = (error) => {
|
|
1076
|
+
settlement = { error };
|
|
1077
|
+
finalize();
|
|
1078
|
+
};
|
|
1079
|
+
const abort = () => {
|
|
1080
|
+
settle(Result.err(createResultAsyncCallbackAbortError(signal)));
|
|
1081
|
+
};
|
|
1082
|
+
const context = {
|
|
1083
|
+
reject: (cause) => {
|
|
1084
|
+
if (settlement !== void 0) return;
|
|
1085
|
+
try {
|
|
1086
|
+
settle(Result.err(options.catch(cause)));
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
rejectUnexpected(error);
|
|
1089
|
+
}
|
|
1090
|
+
},
|
|
1091
|
+
resolve: (value) => {
|
|
1092
|
+
settle(Result.ok(value));
|
|
1093
|
+
},
|
|
1094
|
+
signal
|
|
1095
|
+
};
|
|
1096
|
+
signal.addEventListener("abort", abort);
|
|
1097
|
+
try {
|
|
1098
|
+
cleanup = options.subscribe(context) ?? emptyResultAsyncCallbackCleanup;
|
|
1099
|
+
} catch (error) {
|
|
1100
|
+
context.reject(error);
|
|
1101
|
+
}
|
|
1102
|
+
subscribed = true;
|
|
1103
|
+
finalize();
|
|
1104
|
+
}));
|
|
1105
|
+
};
|
|
1106
|
+
const validateResultAsyncRetryTimes = (times) => {
|
|
1107
|
+
if (!Number.isInteger(times) || times < 0) throw createIllegalArgumentException("ResultAsync retry times must be a non-negative integer");
|
|
1108
|
+
};
|
|
1109
|
+
const validateResultAsyncRetryDelayValue = (delayMs) => {
|
|
1110
|
+
if (!Number.isFinite(delayMs) || delayMs < 0) throw createIllegalArgumentException("ResultAsync retry delayMs must be a non-negative finite number");
|
|
733
1111
|
};
|
|
734
1112
|
const validateResultAsyncRetryJittered = (jittered) => {
|
|
735
1113
|
if (!Number.isFinite(jittered) || jittered < 0) throw createIllegalArgumentException("ResultAsync retry jittered must be a non-negative finite number");
|
|
@@ -1020,14 +1398,16 @@ const toResultAsync = (result) => {
|
|
|
1020
1398
|
};
|
|
1021
1399
|
const ifResultAsync = (condition, options) => {
|
|
1022
1400
|
return new ResultAsync(Promise.try(async () => {
|
|
1023
|
-
|
|
1401
|
+
const result = resolveResultAsyncBooleanCondition(condition) ? options.onTrue() : options.onFalse();
|
|
1402
|
+
return await toResultAsync(result);
|
|
1024
1403
|
}));
|
|
1025
1404
|
};
|
|
1026
1405
|
const ifResultAsyncWithCondition = (condition, options) => {
|
|
1027
1406
|
return new ResultAsync(Promise.try(async () => {
|
|
1028
1407
|
const conditionResult = await toResultAsync(condition);
|
|
1029
1408
|
if (conditionResult.isErr()) return Result.err(conditionResult.error);
|
|
1030
|
-
|
|
1409
|
+
const result = conditionResult.value ? options.onTrue() : options.onFalse();
|
|
1410
|
+
return await toResultAsync(result);
|
|
1031
1411
|
}));
|
|
1032
1412
|
};
|
|
1033
1413
|
const whenResultAsync = (condition, body) => {
|
|
@@ -1060,13 +1440,24 @@ var DisposableResultAsync = class {
|
|
|
1060
1440
|
innerPromise;
|
|
1061
1441
|
finalizer;
|
|
1062
1442
|
disposed = false;
|
|
1443
|
+
/**
|
|
1444
|
+
* Creates an async disposable wrapper around a ResultAsync promise.
|
|
1445
|
+
*/
|
|
1063
1446
|
constructor(res, finalizer) {
|
|
1064
1447
|
this.innerPromise = res;
|
|
1065
1448
|
this.finalizer = finalizer;
|
|
1066
1449
|
}
|
|
1450
|
+
/**
|
|
1451
|
+
* PromiseLike integration. Resolves to the wrapped Result.
|
|
1452
|
+
*/
|
|
1067
1453
|
then(successCallback, failureCallback) {
|
|
1068
1454
|
return this.innerPromise.then(successCallback, failureCallback);
|
|
1069
1455
|
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Runs the finalizer once after the wrapped Result resolves.
|
|
1458
|
+
*
|
|
1459
|
+
* Finalizer exceptions and rejections are intentionally swallowed.
|
|
1460
|
+
*/
|
|
1070
1461
|
async [Symbol.asyncDispose]() {
|
|
1071
1462
|
if (this.disposed) return;
|
|
1072
1463
|
this.disposed = true;
|
|
@@ -1107,7 +1498,7 @@ var DisposableResultAsync = class {
|
|
|
1107
1498
|
* but operating in an asynchronous context.
|
|
1108
1499
|
*
|
|
1109
1500
|
* The class includes methods for:
|
|
1110
|
-
* - Creating ResultAsync instances (okAsync, errAsync, fromPromise)
|
|
1501
|
+
* - Creating ResultAsync instances (okAsync, errAsync, fromPromise, fromCallback)
|
|
1111
1502
|
* - Transforming values (map, mapErr)
|
|
1112
1503
|
* - Chaining operations (andThen, orElse)
|
|
1113
1504
|
* - Error handling (tapError)
|
|
@@ -1130,16 +1521,28 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1130
1521
|
return new ResultAsync(Promise.resolve(err(error)));
|
|
1131
1522
|
}
|
|
1132
1523
|
static tryCatch(fn, errorFn) {
|
|
1133
|
-
|
|
1524
|
+
const newPromise = (typeof fn === "function" ? Promise.try(fn) : fn).then((value) => ok(value)).catch((error) => {
|
|
1134
1525
|
if (errorFn) return err(errorFn(error));
|
|
1135
1526
|
return err(error);
|
|
1136
|
-
})
|
|
1527
|
+
});
|
|
1528
|
+
return new ResultAsync(newPromise);
|
|
1137
1529
|
}
|
|
1138
1530
|
static fromSafePromise(promise) {
|
|
1139
|
-
|
|
1531
|
+
const newPromise = promise.then((value) => ok(value));
|
|
1532
|
+
return new ResultAsync(newPromise);
|
|
1140
1533
|
}
|
|
1141
1534
|
static fromPromise(promise, errorFn) {
|
|
1142
|
-
|
|
1535
|
+
const newPromise = promise.then((value) => ok(value)).catch((error) => err(errorFn(error)));
|
|
1536
|
+
return new ResultAsync(newPromise);
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Adapts a callback or subscription API into a cancellable ResultAsync.
|
|
1540
|
+
*
|
|
1541
|
+
* The returned cleanup function runs once after resolve, reject, cancellation, or synchronous
|
|
1542
|
+
* subscription failure. Cleanup errors are ignored.
|
|
1543
|
+
*/
|
|
1544
|
+
static fromCallback(options) {
|
|
1545
|
+
return fromResultAsyncCallback(options);
|
|
1143
1546
|
}
|
|
1144
1547
|
static combine(input) {
|
|
1145
1548
|
if (Array.isArray(input)) return combineResultAsyncList(input);
|
|
@@ -1155,24 +1558,52 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1155
1558
|
if (f === void 0) return combineResultAsyncListWithAllErrors(asyncResultListOrItems);
|
|
1156
1559
|
return validateAllResultAsyncItems(asyncResultListOrItems, f, options);
|
|
1157
1560
|
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Combines exactly two ResultAsync values into an Ok tuple or the first Err.
|
|
1563
|
+
*/
|
|
1158
1564
|
static zip(left, right) {
|
|
1159
1565
|
return combineResultAsyncList([left, right]);
|
|
1160
1566
|
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Retries a ResultAsync task according to a typed retry policy.
|
|
1569
|
+
*
|
|
1570
|
+
* The returned error channel includes the task error and `AbortError`.
|
|
1571
|
+
*/
|
|
1161
1572
|
static retry(task, options) {
|
|
1162
1573
|
return retryResultAsyncTask(task, options);
|
|
1163
1574
|
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Retries a ResultAsync task and falls back to another Resultar operation after retry exhaustion.
|
|
1577
|
+
*/
|
|
1164
1578
|
static retryOrElse(task, options) {
|
|
1165
1579
|
return retryOrElseResultAsyncTask(task, options);
|
|
1166
1580
|
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Acquires, uses, and releases a resource with cleanup on every path.
|
|
1583
|
+
*
|
|
1584
|
+
* Release errors are intentionally not part of the success/error channel; model cleanup failures
|
|
1585
|
+
* in `use` when they should affect control flow.
|
|
1586
|
+
*/
|
|
1167
1587
|
static withResource(options) {
|
|
1168
1588
|
return withResultAsyncResource(options);
|
|
1169
1589
|
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Races two cooperative ResultAsync tasks and returns the first settled Result.
|
|
1592
|
+
*/
|
|
1170
1593
|
static race(left, right) {
|
|
1171
1594
|
return raceResultAsyncTasks([left, right]);
|
|
1172
1595
|
}
|
|
1596
|
+
/**
|
|
1597
|
+
* Races multiple cooperative ResultAsync tasks and returns the first settled Result.
|
|
1598
|
+
*/
|
|
1173
1599
|
static raceAll(tasks) {
|
|
1174
1600
|
return raceResultAsyncTasks(tasks);
|
|
1175
1601
|
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Races two cooperative ResultAsync tasks and returns the first successful Ok.
|
|
1604
|
+
*
|
|
1605
|
+
* If both fail, the returned error is the final observed Err.
|
|
1606
|
+
*/
|
|
1176
1607
|
static raceFirst(left, right) {
|
|
1177
1608
|
return observeRaceResultAsyncTasks([left, right], (resolve, startedTasks) => ({
|
|
1178
1609
|
resolve,
|
|
@@ -1180,8 +1611,14 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1180
1611
|
state: { settled: false }
|
|
1181
1612
|
}), observeRaceFirstTask);
|
|
1182
1613
|
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Races two cooperative ResultAsync tasks and lets handlers decide what to do when either side
|
|
1616
|
+
* settles first.
|
|
1617
|
+
*
|
|
1618
|
+
* Handlers receive the settled Result and a handle to the still-running opposite task.
|
|
1619
|
+
*/
|
|
1183
1620
|
static raceWith(left, right, handlers) {
|
|
1184
|
-
|
|
1621
|
+
const promise = new Promise((resolve) => {
|
|
1185
1622
|
const leftTask = startRaceTask(left, 0);
|
|
1186
1623
|
const rightTask = startRaceTask(right, 1);
|
|
1187
1624
|
const leftHandle = createRaceHandle(leftTask);
|
|
@@ -1203,18 +1640,23 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1203
1640
|
resolve(handledResult);
|
|
1204
1641
|
});
|
|
1205
1642
|
});
|
|
1206
|
-
})
|
|
1643
|
+
});
|
|
1644
|
+
return new ResultAsync(promise);
|
|
1207
1645
|
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Runs a cooperative ResultAsync task with a typed timeout error.
|
|
1648
|
+
*/
|
|
1208
1649
|
static timeout(task, options) {
|
|
1209
1650
|
const timeoutTask = (signal) => {
|
|
1210
|
-
|
|
1651
|
+
const promise = new Promise((resolve) => {
|
|
1211
1652
|
const timeout = setTimeout(() => {
|
|
1212
1653
|
resolve(Result.err(options.onTimeout()));
|
|
1213
1654
|
}, options.timeoutMs);
|
|
1214
1655
|
signal.addEventListener("abort", () => {
|
|
1215
1656
|
clearTimeout(timeout);
|
|
1216
1657
|
});
|
|
1217
|
-
})
|
|
1658
|
+
});
|
|
1659
|
+
return new ResultAsync(promise);
|
|
1218
1660
|
};
|
|
1219
1661
|
return ResultAsync.raceWith(task, timeoutTask, {
|
|
1220
1662
|
onLeftDone: (result, timeoutHandle) => {
|
|
@@ -1227,6 +1669,9 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1227
1669
|
}
|
|
1228
1670
|
});
|
|
1229
1671
|
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Runs async candidates until the first Ok and returns the last Err if every candidate fails.
|
|
1674
|
+
*/
|
|
1230
1675
|
static firstSuccessOf(candidates) {
|
|
1231
1676
|
return firstSuccessOfAsyncCandidates(candidates);
|
|
1232
1677
|
}
|
|
@@ -1234,15 +1679,27 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1234
1679
|
if (typeof condition === "boolean" || typeof condition === "function") return ifResultAsync(condition, options);
|
|
1235
1680
|
return ifResultAsyncWithCondition(condition, options);
|
|
1236
1681
|
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Runs `body` only when the condition is true; otherwise returns Ok(undefined).
|
|
1684
|
+
*/
|
|
1237
1685
|
static when(condition, body) {
|
|
1238
1686
|
return whenResultAsync(condition, body);
|
|
1239
1687
|
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Runs `body` only when a Resultar boolean condition is Ok(true).
|
|
1690
|
+
*/
|
|
1240
1691
|
static whenResult(condition, body) {
|
|
1241
1692
|
return whenResultAsyncWithCondition(condition, body);
|
|
1242
1693
|
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Runs `body` only when the condition is false; otherwise returns Ok(undefined).
|
|
1696
|
+
*/
|
|
1243
1697
|
static unless(condition, body) {
|
|
1244
1698
|
return unlessResultAsync(condition, body);
|
|
1245
1699
|
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Runs `body` only when a Resultar boolean condition is Ok(false).
|
|
1702
|
+
*/
|
|
1246
1703
|
static unlessResult(condition, body) {
|
|
1247
1704
|
return unlessResultAsyncWithCondition(condition, body);
|
|
1248
1705
|
}
|
|
@@ -1256,12 +1713,9 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1256
1713
|
return forEachResultAsync(items, f, options);
|
|
1257
1714
|
}
|
|
1258
1715
|
/**
|
|
1259
|
-
* Wraps
|
|
1260
|
-
* arguments but returning `Ok` if successful, `Err` if the function throws
|
|
1716
|
+
* Wraps an async function so every later call returns a ResultAsync.
|
|
1261
1717
|
*
|
|
1262
|
-
*
|
|
1263
|
-
* @param errorFn when an error is thrown, this will wrap the error result if provided
|
|
1264
|
-
* @returns a new function that returns a `ResultAsync`
|
|
1718
|
+
* Prefer `tryResultAsync` when you want to run the operation immediately.
|
|
1265
1719
|
*/
|
|
1266
1720
|
static fromThrowable(fn, errorFn) {
|
|
1267
1721
|
return (...args) => new ResultAsync((async () => {
|
|
@@ -1273,25 +1727,45 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1273
1727
|
})());
|
|
1274
1728
|
}
|
|
1275
1729
|
innerPromise;
|
|
1730
|
+
/**
|
|
1731
|
+
* Creates a ResultAsync from a Promise that resolves to a Result.
|
|
1732
|
+
*
|
|
1733
|
+
* Most application code should prefer `tryResultAsync`, `fromPromise`, `okAsync`, or `errAsync`.
|
|
1734
|
+
*/
|
|
1276
1735
|
constructor(res) {
|
|
1277
1736
|
super();
|
|
1278
1737
|
this.innerPromise = res;
|
|
1279
1738
|
}
|
|
1739
|
+
/**
|
|
1740
|
+
* PromiseLike integration. Resolves to the underlying `Result<T, E>`.
|
|
1741
|
+
*
|
|
1742
|
+
* Prefer ResultAsync combinators for application flow; this exists so ResultAsync can be awaited
|
|
1743
|
+
* and used by Promise APIs.
|
|
1744
|
+
*/
|
|
1280
1745
|
then(successCallback, failureCallback) {
|
|
1281
1746
|
return this.innerPromise.then(successCallback, failureCallback);
|
|
1282
1747
|
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Maps the Err value while preserving the Ok value.
|
|
1750
|
+
*/
|
|
1283
1751
|
mapErr(f) {
|
|
1284
1752
|
return new ResultAsync(this.innerPromise.then(async (res) => {
|
|
1285
1753
|
if (res.isOk()) return ok(res.value);
|
|
1286
1754
|
return err(await f(res.error));
|
|
1287
1755
|
}));
|
|
1288
1756
|
}
|
|
1757
|
+
/**
|
|
1758
|
+
* Maps the Ok value while preserving the error channel.
|
|
1759
|
+
*/
|
|
1289
1760
|
map(f) {
|
|
1290
1761
|
return new ResultAsync(this.innerPromise.then(async (res) => {
|
|
1291
1762
|
if (res.isErr()) return err(res.error);
|
|
1292
1763
|
return ok(await f(res.value));
|
|
1293
1764
|
}));
|
|
1294
1765
|
}
|
|
1766
|
+
/**
|
|
1767
|
+
* Replaces the Ok value with a constant while preserving the error channel.
|
|
1768
|
+
*/
|
|
1295
1769
|
as(value) {
|
|
1296
1770
|
return new ResultAsync(this.innerPromise.then((res) => {
|
|
1297
1771
|
if (res.isErr()) return err(res.error);
|
|
@@ -1312,6 +1786,9 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1312
1786
|
return next instanceof ResultAsync ? next.innerPromise : next;
|
|
1313
1787
|
}));
|
|
1314
1788
|
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Branches from the Ok value into one of two async fallible Result branches.
|
|
1791
|
+
*/
|
|
1315
1792
|
if(fCondition) {
|
|
1316
1793
|
return { true: (fTrue) => ({ false: (fFalse) => new ResultAsync(this.innerPromise.then(async (res) => {
|
|
1317
1794
|
if (res.isOk()) return fCondition(res.value) ? fTrue(res.value) : fFalse(res.value);
|
|
@@ -1337,6 +1814,9 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1337
1814
|
return Result.err(res.error);
|
|
1338
1815
|
}));
|
|
1339
1816
|
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Recovers from multiple tagged errors by `_tag`.
|
|
1819
|
+
*/
|
|
1340
1820
|
catchTags(handlers) {
|
|
1341
1821
|
return new ResultAsync(this.innerPromise.then((res) => {
|
|
1342
1822
|
if (res.isOk()) return Result.ok(res.value);
|
|
@@ -1362,6 +1842,9 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1362
1842
|
return Result.err(res.error);
|
|
1363
1843
|
}));
|
|
1364
1844
|
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Recovers from multiple nested tagged `reason` variants on a specific tagged error.
|
|
1847
|
+
*/
|
|
1365
1848
|
catchReasons(errorTag, handlers) {
|
|
1366
1849
|
return new ResultAsync(this.innerPromise.then((res) => {
|
|
1367
1850
|
if (res.isOk()) return Result.ok(res.value);
|
|
@@ -1378,6 +1861,9 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1378
1861
|
return Result.err(res.error);
|
|
1379
1862
|
}));
|
|
1380
1863
|
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Moves the nested `reason` of a tagged error into the ResultAsync error channel.
|
|
1866
|
+
*/
|
|
1381
1867
|
unwrapReason(errorTag) {
|
|
1382
1868
|
return new ResultAsync(this.innerPromise.then((res) => {
|
|
1383
1869
|
if (res.isOk()) return Result.ok(res.value);
|
|
@@ -1388,6 +1874,9 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1388
1874
|
async match(input, fnErr) {
|
|
1389
1875
|
return this.innerPromise.then((res) => typeof input === "function" ? res.match(input, getMatchErrorHandler(input, fnErr)) : res.match(input));
|
|
1390
1876
|
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Converts this ResultAsync into a Promise of a plain value with exhaustive tagged-error handlers.
|
|
1879
|
+
*/
|
|
1391
1880
|
async matchTags(ok, handlers) {
|
|
1392
1881
|
return this.innerPromise.then((res) => matchResolvedResultTags({
|
|
1393
1882
|
result: res,
|
|
@@ -1398,6 +1887,10 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1398
1887
|
}
|
|
1399
1888
|
}));
|
|
1400
1889
|
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Converts this ResultAsync into a Promise of a plain value with partial tagged-error handlers and
|
|
1892
|
+
* a fallback.
|
|
1893
|
+
*/
|
|
1401
1894
|
async matchTagsPartial(ok, handlers, fallback) {
|
|
1402
1895
|
return this.innerPromise.then((res) => matchResolvedResultTags({
|
|
1403
1896
|
result: res,
|
|
@@ -1406,9 +1899,17 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1406
1899
|
fallback
|
|
1407
1900
|
}));
|
|
1408
1901
|
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Resolves to the Ok value, or `t` when this ResultAsync is Err.
|
|
1904
|
+
*/
|
|
1409
1905
|
async unwrapOr(t) {
|
|
1410
1906
|
return this.innerPromise.then((res) => res.unwrapOr(t));
|
|
1411
1907
|
}
|
|
1908
|
+
/**
|
|
1909
|
+
* Resolves to the Ok value or rejects with the Err value.
|
|
1910
|
+
*
|
|
1911
|
+
* Use this at final application boundaries after the error channel has been modeled.
|
|
1912
|
+
*/
|
|
1412
1913
|
async unwrapOrThrow() {
|
|
1413
1914
|
return this.innerPromise.then((res) => res.unwrapOrThrow());
|
|
1414
1915
|
}
|
|
@@ -1460,9 +1961,15 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1460
1961
|
return okAsync(res.value);
|
|
1461
1962
|
}));
|
|
1462
1963
|
}
|
|
1964
|
+
/**
|
|
1965
|
+
* Attaches an async disposal finalizer to this ResultAsync.
|
|
1966
|
+
*/
|
|
1463
1967
|
toAsyncDisposable(f) {
|
|
1464
1968
|
return new DisposableResultAsync(this.innerPromise, f);
|
|
1465
1969
|
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Enables `yield* resultAsync` inside async `safeTry` generators.
|
|
1972
|
+
*/
|
|
1466
1973
|
async *[Symbol.asyncIterator]() {
|
|
1467
1974
|
const result = await this.innerPromise;
|
|
1468
1975
|
if (result.isOk()) return result.value;
|
|
@@ -1471,11 +1978,35 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1471
1978
|
}
|
|
1472
1979
|
};
|
|
1473
1980
|
registerResultAsyncFactory((promise) => new ResultAsync(promise));
|
|
1981
|
+
/**
|
|
1982
|
+
* Creates an Ok ResultAsync.
|
|
1983
|
+
*/
|
|
1474
1984
|
const okAsync = ResultAsync.okAsync;
|
|
1985
|
+
/**
|
|
1986
|
+
* Creates an Err ResultAsync.
|
|
1987
|
+
*/
|
|
1475
1988
|
const errAsync = ResultAsync.errAsync;
|
|
1989
|
+
/**
|
|
1990
|
+
* Adapts a callback or subscription API into a cancellable ResultAsync.
|
|
1991
|
+
*/
|
|
1992
|
+
const fromCallback = ResultAsync.fromCallback;
|
|
1993
|
+
/**
|
|
1994
|
+
* Converts an existing promise into a ResultAsync and maps rejection into a typed Err.
|
|
1995
|
+
*/
|
|
1476
1996
|
const fromPromise = ResultAsync.fromPromise;
|
|
1997
|
+
/**
|
|
1998
|
+
* Converts an existing promise that is expected not to reject into a ResultAsync.
|
|
1999
|
+
*
|
|
2000
|
+
* Use `fromPromise` or `tryResultAsync` when rejection should become a typed Err.
|
|
2001
|
+
*/
|
|
1477
2002
|
const fromSafePromise = ResultAsync.fromSafePromise;
|
|
2003
|
+
/**
|
|
2004
|
+
* Creates an Ok ResultAsync with `undefined`.
|
|
2005
|
+
*/
|
|
1478
2006
|
const unitAsync = ResultAsync.unitAsync;
|
|
2007
|
+
/**
|
|
2008
|
+
* Wraps an async function so every later call returns a ResultAsync.
|
|
2009
|
+
*/
|
|
1479
2010
|
const fromThrowableAsync = ResultAsync.fromThrowable;
|
|
1480
2011
|
function tryResultAsync(input, errorFn) {
|
|
1481
2012
|
const promiseOrFn = isTryResultAsyncOptions(input) ? input.try : input;
|
|
@@ -1485,196 +2016,14 @@ function tryResultAsync(input, errorFn) {
|
|
|
1485
2016
|
return Result.err(error);
|
|
1486
2017
|
}));
|
|
1487
2018
|
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Runs a `ResultAsync` at an application boundary and returns the success value.
|
|
2021
|
+
*
|
|
2022
|
+
* If the result is `Err`, this rejects with the error. Use it at final framework, CLI, test, or
|
|
2023
|
+
* bootstrap boundaries after the error channel has already been modeled with Resultar.
|
|
2024
|
+
*/
|
|
2025
|
+
const runPromise = (result) => result.unwrapOrThrow();
|
|
1488
2026
|
/** Compatibility alias. Prefer `tryResultAsync` in new code. */
|
|
1489
2027
|
const tryCatchAsync = tryResultAsync;
|
|
1490
2028
|
//#endregion
|
|
1491
|
-
|
|
1492
|
-
const redactedValues = /* @__PURE__ */ new WeakMap();
|
|
1493
|
-
const formatRedacted = (label) => label === void 0 ? "<redacted>" : `<redacted:${label}>`;
|
|
1494
|
-
var RedactedValue = class {
|
|
1495
|
-
__resultarRedacted = true;
|
|
1496
|
-
label;
|
|
1497
|
-
constructor(value, label) {
|
|
1498
|
-
this.label = label;
|
|
1499
|
-
redactedValues.set(this, value);
|
|
1500
|
-
}
|
|
1501
|
-
toJSON() {
|
|
1502
|
-
return this.toString();
|
|
1503
|
-
}
|
|
1504
|
-
toString() {
|
|
1505
|
-
return formatRedacted(this.label);
|
|
1506
|
-
}
|
|
1507
|
-
};
|
|
1508
|
-
const redact = (value, label) => new RedactedValue(value, label);
|
|
1509
|
-
const isRedacted = (value) => typeof value === "object" && value !== null && redactedValues.has(value);
|
|
1510
|
-
const revealRedacted = (value) => redactedValues.get(value);
|
|
1511
|
-
const stringifyRedacted = (value) => value.toString();
|
|
1512
|
-
//#endregion
|
|
1513
|
-
//#region src/tagged-error.ts
|
|
1514
|
-
const defaultBaseError = Error;
|
|
1515
|
-
const defaultMessageTemplate = "$message";
|
|
1516
|
-
const nativeError = Error;
|
|
1517
|
-
const templateVariableMatcher = /\$([a-zA-Z_][a-zA-Z0-9_]*)/gu;
|
|
1518
|
-
const reservedTemplateVariables = new Set([
|
|
1519
|
-
"_tag",
|
|
1520
|
-
"cause",
|
|
1521
|
-
"fingerprint",
|
|
1522
|
-
"message",
|
|
1523
|
-
"messageTemplate",
|
|
1524
|
-
"name",
|
|
1525
|
-
"stack"
|
|
1526
|
-
]);
|
|
1527
|
-
function isError(value) {
|
|
1528
|
-
return nativeError.isError?.(value) ?? value instanceof Error;
|
|
1529
|
-
}
|
|
1530
|
-
const stringifyDirectTemplateValue = String;
|
|
1531
|
-
const stringifySymbolTemplateValue = (value) => {
|
|
1532
|
-
const symbol = value;
|
|
1533
|
-
return symbol.description ?? symbol.toString();
|
|
1534
|
-
};
|
|
1535
|
-
const stringifyFunctionTemplateValue = (value) => value.name;
|
|
1536
|
-
const stringifyObjectTemplateValue = (value) => JSON.stringify(value) ?? Object.prototype.toString.call(value);
|
|
1537
|
-
const templateValueStringifiers = {
|
|
1538
|
-
bigint: stringifyDirectTemplateValue,
|
|
1539
|
-
boolean: stringifyDirectTemplateValue,
|
|
1540
|
-
function: stringifyFunctionTemplateValue,
|
|
1541
|
-
number: stringifyDirectTemplateValue,
|
|
1542
|
-
object: stringifyObjectTemplateValue,
|
|
1543
|
-
string: stringifyDirectTemplateValue,
|
|
1544
|
-
symbol: stringifySymbolTemplateValue
|
|
1545
|
-
};
|
|
1546
|
-
const stringifyTemplateValue = (value) => isRedacted(value) ? stringifyRedacted(value) : templateValueStringifiers[typeof value](value);
|
|
1547
|
-
const compileMessageInterpolator = (template) => (values) => template.replaceAll(templateVariableMatcher, (variable, key) => {
|
|
1548
|
-
const value = values?.[key];
|
|
1549
|
-
return value === void 0 ? variable : stringifyTemplateValue(value);
|
|
1550
|
-
});
|
|
1551
|
-
const getTemplateVariableNames = (template) => {
|
|
1552
|
-
templateVariableMatcher.lastIndex = 0;
|
|
1553
|
-
return [...template.matchAll(templateVariableMatcher)].map((match) => String(match[1]));
|
|
1554
|
-
};
|
|
1555
|
-
const assertNoReservedTemplateVariables = (tag, template) => {
|
|
1556
|
-
if (template === defaultMessageTemplate) return;
|
|
1557
|
-
const reservedVariables = getTemplateVariableNames(template).filter((variable) => reservedTemplateVariables.has(variable));
|
|
1558
|
-
if (reservedVariables.length > 0) throw new Error(`createTaggedError(${tag}): reserved template variable $${reservedVariables[0]} is not allowed`);
|
|
1559
|
-
};
|
|
1560
|
-
const getErrorOptions = (cause) => cause === void 0 ? {} : { cause };
|
|
1561
|
-
const assignReadonly = (target, key, value) => {
|
|
1562
|
-
Object.defineProperty(target, key, {
|
|
1563
|
-
configurable: true,
|
|
1564
|
-
enumerable: true,
|
|
1565
|
-
value,
|
|
1566
|
-
writable: false
|
|
1567
|
-
});
|
|
1568
|
-
};
|
|
1569
|
-
const serializeTaggedValue = (value) => isRedacted(value) ? stringifyRedacted(value) : value;
|
|
1570
|
-
const serializeCause = (cause, seen = /* @__PURE__ */ new Set()) => {
|
|
1571
|
-
if (!isError(cause)) return serializeTaggedValue(cause);
|
|
1572
|
-
if (seen.has(cause)) return {
|
|
1573
|
-
cause: "[Circular]",
|
|
1574
|
-
message: cause.message,
|
|
1575
|
-
name: cause.name,
|
|
1576
|
-
stack: cause.stack
|
|
1577
|
-
};
|
|
1578
|
-
seen.add(cause);
|
|
1579
|
-
const json = {
|
|
1580
|
-
message: cause.message,
|
|
1581
|
-
name: cause.name,
|
|
1582
|
-
stack: cause.stack
|
|
1583
|
-
};
|
|
1584
|
-
if (cause.cause !== void 0) json["cause"] = serializeCause(cause.cause, seen);
|
|
1585
|
-
return json;
|
|
1586
|
-
};
|
|
1587
|
-
const findCause = (error, ErrorClass) => {
|
|
1588
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1589
|
-
let current = error;
|
|
1590
|
-
while (isError(current)) {
|
|
1591
|
-
if (seen.has(current)) break;
|
|
1592
|
-
seen.add(current);
|
|
1593
|
-
if (current instanceof ErrorClass) return current;
|
|
1594
|
-
current = current.cause;
|
|
1595
|
-
}
|
|
1596
|
-
};
|
|
1597
|
-
function createTaggedError(options) {
|
|
1598
|
-
const BaseError = options.extends ?? defaultBaseError;
|
|
1599
|
-
const messageTemplate = options.message ?? defaultMessageTemplate;
|
|
1600
|
-
assertNoReservedTemplateVariables(options.name, messageTemplate);
|
|
1601
|
-
const interpolate = compileMessageInterpolator(messageTemplate);
|
|
1602
|
-
class GeneratedTaggedError extends BaseError {
|
|
1603
|
-
static tag = options.name;
|
|
1604
|
-
_tag = options.name;
|
|
1605
|
-
fingerprint = [options.name, messageTemplate];
|
|
1606
|
-
messageTemplate = messageTemplate;
|
|
1607
|
-
constructor(props) {
|
|
1608
|
-
const message = interpolate(props);
|
|
1609
|
-
super(message, getErrorOptions(props?.cause));
|
|
1610
|
-
this.name = options.name;
|
|
1611
|
-
if (props) {
|
|
1612
|
-
for (const [key, value] of Object.entries(props)) if (!reservedTemplateVariables.has(key)) assignReadonly(this, key, value);
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
static is(value) {
|
|
1616
|
-
return value instanceof this;
|
|
1617
|
-
}
|
|
1618
|
-
static err(...args) {
|
|
1619
|
-
return err(new this(...args));
|
|
1620
|
-
}
|
|
1621
|
-
findCause(ErrorClass) {
|
|
1622
|
-
return findCause(this, ErrorClass);
|
|
1623
|
-
}
|
|
1624
|
-
toJSON() {
|
|
1625
|
-
const json = {
|
|
1626
|
-
_tag: this._tag,
|
|
1627
|
-
fingerprint: this.fingerprint,
|
|
1628
|
-
message: this.message,
|
|
1629
|
-
messageTemplate: this.messageTemplate,
|
|
1630
|
-
name: this.name
|
|
1631
|
-
};
|
|
1632
|
-
if (this.cause !== void 0) json["cause"] = serializeCause(this.cause);
|
|
1633
|
-
for (const [key, value] of Object.entries(this)) if (!(key in json) && key !== "cause" && key !== "stack") json[key] = serializeTaggedValue(value);
|
|
1634
|
-
return json;
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
return GeneratedTaggedError;
|
|
1638
|
-
}
|
|
1639
|
-
const taggedEnum = () => {
|
|
1640
|
-
return new Proxy({
|
|
1641
|
-
$is(tag, value) {
|
|
1642
|
-
return typeof value === "object" && value !== null && "_tag" in value && value._tag === tag;
|
|
1643
|
-
},
|
|
1644
|
-
$match(value, handlers) {
|
|
1645
|
-
const handler = handlers[value._tag];
|
|
1646
|
-
if (handler === void 0) throw new Error(`No tagged enum handler for ${String(value._tag)}`);
|
|
1647
|
-
return handler(value);
|
|
1648
|
-
}
|
|
1649
|
-
}, { get(base, property, receiver) {
|
|
1650
|
-
if (property in base) return Reflect.get(base, property, receiver);
|
|
1651
|
-
if (typeof property !== "string") return;
|
|
1652
|
-
return (props) => {
|
|
1653
|
-
const value = { _tag: property };
|
|
1654
|
-
if (props !== void 0) {
|
|
1655
|
-
for (const [key, propValue] of Object.entries(props)) if (key !== "_tag") value[key] = propValue;
|
|
1656
|
-
}
|
|
1657
|
-
return Object.freeze(value);
|
|
1658
|
-
};
|
|
1659
|
-
} });
|
|
1660
|
-
};
|
|
1661
|
-
const matchError = (error, handlers) => {
|
|
1662
|
-
if ("_tag" in error && typeof error._tag === "string" && error._tag in handlers) {
|
|
1663
|
-
const handler = handlers[error._tag];
|
|
1664
|
-
return handler(error);
|
|
1665
|
-
}
|
|
1666
|
-
if (handlers.Error) return handlers.Error(error);
|
|
1667
|
-
throw error;
|
|
1668
|
-
};
|
|
1669
|
-
const matchErrorPartial = (error, handlers, fallback) => {
|
|
1670
|
-
if ("_tag" in error && typeof error._tag === "string" && error._tag in handlers) {
|
|
1671
|
-
const handler = handlers[error._tag];
|
|
1672
|
-
if (handler) return handler(error);
|
|
1673
|
-
}
|
|
1674
|
-
if (handlers.Error) return handlers.Error(error);
|
|
1675
|
-
return fallback(error);
|
|
1676
|
-
};
|
|
1677
|
-
//#endregion
|
|
1678
|
-
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 };
|
|
1679
|
-
|
|
1680
|
-
//# sourceMappingURL=index.js.map
|
|
2029
|
+
export { AbortError, DisposableResult, DisposableResultAsync, Result, ResultAsync, 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 };
|