resultar 3.4.0 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +453 -846
- package/dist/index.d.ts +51 -3
- package/dist/index.js +354 -262
- package/package.json +10 -10
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -253,7 +253,8 @@ var ResultNamespace = class {
|
|
|
253
253
|
}
|
|
254
254
|
static validateAll(resultListOrItems, f) {
|
|
255
255
|
if (f === void 0) return combineResultListWithAllErrors(resultListOrItems);
|
|
256
|
-
|
|
256
|
+
const resultList = Array.from(resultListOrItems, f);
|
|
257
|
+
return combineResultListWithAllErrors(resultList);
|
|
257
258
|
}
|
|
258
259
|
static zip(left, right) {
|
|
259
260
|
return combineResultList([left, right]);
|
|
@@ -667,6 +668,253 @@ const runSync = (result) => result.unwrapOrThrow();
|
|
|
667
668
|
/** Compatibility alias. Prefer `tryResult` in new code. */
|
|
668
669
|
const tryCatch = tryResult;
|
|
669
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
|
|
670
918
|
//#region src/result-async.ts
|
|
671
919
|
function isTryResultAsyncOptions(input) {
|
|
672
920
|
return typeof input === "object" && input !== null && "try" in input;
|
|
@@ -782,6 +1030,79 @@ const runConcurrentResultAsyncItems = (options) => Promise.try(async () => {
|
|
|
782
1030
|
});
|
|
783
1031
|
const createResultAsyncRetryAbortError = (signal) => signal.reason === void 0 ? new AbortError("ResultAsync retry aborted") : new AbortError("ResultAsync retry aborted", { cause: signal.reason });
|
|
784
1032
|
const createResultAsyncResourceAbortError = (signal) => signal.reason === void 0 ? new AbortError("ResultAsync resource scope aborted") : new AbortError("ResultAsync resource scope aborted", { cause: signal.reason });
|
|
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 {}
|
|
1039
|
+
};
|
|
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
|
+
};
|
|
785
1106
|
const validateResultAsyncRetryTimes = (times) => {
|
|
786
1107
|
if (!Number.isInteger(times) || times < 0) throw createIllegalArgumentException("ResultAsync retry times must be a non-negative integer");
|
|
787
1108
|
};
|
|
@@ -1077,14 +1398,16 @@ const toResultAsync = (result) => {
|
|
|
1077
1398
|
};
|
|
1078
1399
|
const ifResultAsync = (condition, options) => {
|
|
1079
1400
|
return new ResultAsync(Promise.try(async () => {
|
|
1080
|
-
|
|
1401
|
+
const result = resolveResultAsyncBooleanCondition(condition) ? options.onTrue() : options.onFalse();
|
|
1402
|
+
return await toResultAsync(result);
|
|
1081
1403
|
}));
|
|
1082
1404
|
};
|
|
1083
1405
|
const ifResultAsyncWithCondition = (condition, options) => {
|
|
1084
1406
|
return new ResultAsync(Promise.try(async () => {
|
|
1085
1407
|
const conditionResult = await toResultAsync(condition);
|
|
1086
1408
|
if (conditionResult.isErr()) return Result.err(conditionResult.error);
|
|
1087
|
-
|
|
1409
|
+
const result = conditionResult.value ? options.onTrue() : options.onFalse();
|
|
1410
|
+
return await toResultAsync(result);
|
|
1088
1411
|
}));
|
|
1089
1412
|
};
|
|
1090
1413
|
const whenResultAsync = (condition, body) => {
|
|
@@ -1175,7 +1498,7 @@ var DisposableResultAsync = class {
|
|
|
1175
1498
|
* but operating in an asynchronous context.
|
|
1176
1499
|
*
|
|
1177
1500
|
* The class includes methods for:
|
|
1178
|
-
* - Creating ResultAsync instances (okAsync, errAsync, fromPromise)
|
|
1501
|
+
* - Creating ResultAsync instances (okAsync, errAsync, fromPromise, fromCallback)
|
|
1179
1502
|
* - Transforming values (map, mapErr)
|
|
1180
1503
|
* - Chaining operations (andThen, orElse)
|
|
1181
1504
|
* - Error handling (tapError)
|
|
@@ -1198,16 +1521,28 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1198
1521
|
return new ResultAsync(Promise.resolve(err(error)));
|
|
1199
1522
|
}
|
|
1200
1523
|
static tryCatch(fn, errorFn) {
|
|
1201
|
-
|
|
1524
|
+
const newPromise = (typeof fn === "function" ? Promise.try(fn) : fn).then((value) => ok(value)).catch((error) => {
|
|
1202
1525
|
if (errorFn) return err(errorFn(error));
|
|
1203
1526
|
return err(error);
|
|
1204
|
-
})
|
|
1527
|
+
});
|
|
1528
|
+
return new ResultAsync(newPromise);
|
|
1205
1529
|
}
|
|
1206
1530
|
static fromSafePromise(promise) {
|
|
1207
|
-
|
|
1531
|
+
const newPromise = promise.then((value) => ok(value));
|
|
1532
|
+
return new ResultAsync(newPromise);
|
|
1208
1533
|
}
|
|
1209
1534
|
static fromPromise(promise, errorFn) {
|
|
1210
|
-
|
|
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);
|
|
1211
1546
|
}
|
|
1212
1547
|
static combine(input) {
|
|
1213
1548
|
if (Array.isArray(input)) return combineResultAsyncList(input);
|
|
@@ -1283,7 +1618,7 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1283
1618
|
* Handlers receive the settled Result and a handle to the still-running opposite task.
|
|
1284
1619
|
*/
|
|
1285
1620
|
static raceWith(left, right, handlers) {
|
|
1286
|
-
|
|
1621
|
+
const promise = new Promise((resolve) => {
|
|
1287
1622
|
const leftTask = startRaceTask(left, 0);
|
|
1288
1623
|
const rightTask = startRaceTask(right, 1);
|
|
1289
1624
|
const leftHandle = createRaceHandle(leftTask);
|
|
@@ -1305,21 +1640,23 @@ var ResultAsync = class ResultAsync extends Pipeable {
|
|
|
1305
1640
|
resolve(handledResult);
|
|
1306
1641
|
});
|
|
1307
1642
|
});
|
|
1308
|
-
})
|
|
1643
|
+
});
|
|
1644
|
+
return new ResultAsync(promise);
|
|
1309
1645
|
}
|
|
1310
1646
|
/**
|
|
1311
1647
|
* Runs a cooperative ResultAsync task with a typed timeout error.
|
|
1312
1648
|
*/
|
|
1313
1649
|
static timeout(task, options) {
|
|
1314
1650
|
const timeoutTask = (signal) => {
|
|
1315
|
-
|
|
1651
|
+
const promise = new Promise((resolve) => {
|
|
1316
1652
|
const timeout = setTimeout(() => {
|
|
1317
1653
|
resolve(Result.err(options.onTimeout()));
|
|
1318
1654
|
}, options.timeoutMs);
|
|
1319
1655
|
signal.addEventListener("abort", () => {
|
|
1320
1656
|
clearTimeout(timeout);
|
|
1321
1657
|
});
|
|
1322
|
-
})
|
|
1658
|
+
});
|
|
1659
|
+
return new ResultAsync(promise);
|
|
1323
1660
|
};
|
|
1324
1661
|
return ResultAsync.raceWith(task, timeoutTask, {
|
|
1325
1662
|
onLeftDone: (result, timeoutHandle) => {
|
|
@@ -1650,6 +1987,10 @@ const okAsync = ResultAsync.okAsync;
|
|
|
1650
1987
|
*/
|
|
1651
1988
|
const errAsync = ResultAsync.errAsync;
|
|
1652
1989
|
/**
|
|
1990
|
+
* Adapts a callback or subscription API into a cancellable ResultAsync.
|
|
1991
|
+
*/
|
|
1992
|
+
const fromCallback = ResultAsync.fromCallback;
|
|
1993
|
+
/**
|
|
1653
1994
|
* Converts an existing promise into a ResultAsync and maps rejection into a typed Err.
|
|
1654
1995
|
*/
|
|
1655
1996
|
const fromPromise = ResultAsync.fromPromise;
|
|
@@ -1685,253 +2026,4 @@ const runPromise = (result) => result.unwrapOrThrow();
|
|
|
1685
2026
|
/** Compatibility alias. Prefer `tryResultAsync` in new code. */
|
|
1686
2027
|
const tryCatchAsync = tryResultAsync;
|
|
1687
2028
|
//#endregion
|
|
1688
|
-
|
|
1689
|
-
const redactedValues = /* @__PURE__ */ new WeakMap();
|
|
1690
|
-
const formatRedacted = (label) => label === void 0 ? "<redacted>" : `<redacted:${label}>`;
|
|
1691
|
-
var RedactedValue = class {
|
|
1692
|
-
__resultarRedacted = true;
|
|
1693
|
-
label;
|
|
1694
|
-
/**
|
|
1695
|
-
* Stores a sensitive value behind a redacted string wrapper.
|
|
1696
|
-
*/
|
|
1697
|
-
constructor(value, label) {
|
|
1698
|
-
this.label = label;
|
|
1699
|
-
redactedValues.set(this, value);
|
|
1700
|
-
}
|
|
1701
|
-
/**
|
|
1702
|
-
* Serializes to the redacted placeholder.
|
|
1703
|
-
*/
|
|
1704
|
-
toJSON() {
|
|
1705
|
-
return this.toString();
|
|
1706
|
-
}
|
|
1707
|
-
/**
|
|
1708
|
-
* Converts to the redacted placeholder.
|
|
1709
|
-
*/
|
|
1710
|
-
toString() {
|
|
1711
|
-
return formatRedacted(this.label);
|
|
1712
|
-
}
|
|
1713
|
-
};
|
|
1714
|
-
/**
|
|
1715
|
-
* Wraps a sensitive value so tagged-error messages and JSON output show only a placeholder.
|
|
1716
|
-
*/
|
|
1717
|
-
const redact = (value, label) => new RedactedValue(value, label);
|
|
1718
|
-
/**
|
|
1719
|
-
* Checks whether a value was created by `redact`.
|
|
1720
|
-
*/
|
|
1721
|
-
const isRedacted = (value) => typeof value === "object" && value !== null && redactedValues.has(value);
|
|
1722
|
-
/**
|
|
1723
|
-
* Returns the original sensitive value from a Redacted wrapper.
|
|
1724
|
-
*/
|
|
1725
|
-
const revealRedacted = (value) => redactedValues.get(value);
|
|
1726
|
-
/**
|
|
1727
|
-
* Converts a Redacted wrapper to its placeholder string.
|
|
1728
|
-
*/
|
|
1729
|
-
const stringifyRedacted = (value) => value.toString();
|
|
1730
|
-
//#endregion
|
|
1731
|
-
//#region src/tagged-error.ts
|
|
1732
|
-
const defaultBaseError = Error;
|
|
1733
|
-
const defaultMessageTemplate = "$message";
|
|
1734
|
-
const nativeError = Error;
|
|
1735
|
-
const templateVariableMatcher = /\$(?<name>[a-zA-Z_][a-zA-Z0-9_]*)/gu;
|
|
1736
|
-
const reservedTemplateVariables = new Set([
|
|
1737
|
-
"_tag",
|
|
1738
|
-
"cause",
|
|
1739
|
-
"fingerprint",
|
|
1740
|
-
"message",
|
|
1741
|
-
"messageTemplate",
|
|
1742
|
-
"name",
|
|
1743
|
-
"stack"
|
|
1744
|
-
]);
|
|
1745
|
-
function isError(value) {
|
|
1746
|
-
return nativeError.isError?.(value) ?? value instanceof Error;
|
|
1747
|
-
}
|
|
1748
|
-
const stringifyDirectTemplateValue = String;
|
|
1749
|
-
const stringifySymbolTemplateValue = (value) => {
|
|
1750
|
-
const symbol = value;
|
|
1751
|
-
return symbol.description ?? symbol.toString();
|
|
1752
|
-
};
|
|
1753
|
-
const stringifyFunctionTemplateValue = (value) => value.name;
|
|
1754
|
-
const stringifyObjectTemplateValue = (value) => JSON.stringify(value) ?? Object.prototype.toString.call(value);
|
|
1755
|
-
const templateValueStringifiers = {
|
|
1756
|
-
bigint: stringifyDirectTemplateValue,
|
|
1757
|
-
boolean: stringifyDirectTemplateValue,
|
|
1758
|
-
function: stringifyFunctionTemplateValue,
|
|
1759
|
-
number: stringifyDirectTemplateValue,
|
|
1760
|
-
object: stringifyObjectTemplateValue,
|
|
1761
|
-
string: stringifyDirectTemplateValue,
|
|
1762
|
-
symbol: stringifySymbolTemplateValue
|
|
1763
|
-
};
|
|
1764
|
-
const stringifyTemplateValue = (value) => isRedacted(value) ? stringifyRedacted(value) : templateValueStringifiers[typeof value](value);
|
|
1765
|
-
const compileMessageInterpolator = (template) => (values) => template.replaceAll(templateVariableMatcher, (variable, key) => {
|
|
1766
|
-
const value = values?.[key];
|
|
1767
|
-
return value === void 0 ? variable : stringifyTemplateValue(value);
|
|
1768
|
-
});
|
|
1769
|
-
const getTemplateVariableNames = (template) => {
|
|
1770
|
-
templateVariableMatcher.lastIndex = 0;
|
|
1771
|
-
return [...template.matchAll(templateVariableMatcher)].map((match) => String(match.groups?.["name"]));
|
|
1772
|
-
};
|
|
1773
|
-
const assertNoReservedTemplateVariables = (tag, template) => {
|
|
1774
|
-
if (template === defaultMessageTemplate) return;
|
|
1775
|
-
const reservedVariables = getTemplateVariableNames(template).filter((variable) => reservedTemplateVariables.has(variable));
|
|
1776
|
-
if (reservedVariables.length > 0) throw new Error(`createTaggedError(${tag}): reserved template variable $${reservedVariables[0]} is not allowed`);
|
|
1777
|
-
};
|
|
1778
|
-
const getErrorOptions = (cause) => cause === void 0 ? {} : { cause };
|
|
1779
|
-
const assignReadonly = (target, key, value) => {
|
|
1780
|
-
Object.defineProperty(target, key, {
|
|
1781
|
-
configurable: true,
|
|
1782
|
-
enumerable: true,
|
|
1783
|
-
value,
|
|
1784
|
-
writable: false
|
|
1785
|
-
});
|
|
1786
|
-
};
|
|
1787
|
-
const serializeTaggedValue = (value) => isRedacted(value) ? stringifyRedacted(value) : value;
|
|
1788
|
-
const serializeCause = (cause, seen = /* @__PURE__ */ new Set()) => {
|
|
1789
|
-
if (!isError(cause)) return serializeTaggedValue(cause);
|
|
1790
|
-
if (seen.has(cause)) return {
|
|
1791
|
-
cause: "[Circular]",
|
|
1792
|
-
message: cause.message,
|
|
1793
|
-
name: cause.name,
|
|
1794
|
-
stack: cause.stack
|
|
1795
|
-
};
|
|
1796
|
-
seen.add(cause);
|
|
1797
|
-
const json = {
|
|
1798
|
-
message: cause.message,
|
|
1799
|
-
name: cause.name,
|
|
1800
|
-
stack: cause.stack
|
|
1801
|
-
};
|
|
1802
|
-
if (cause.cause !== void 0) json["cause"] = serializeCause(cause.cause, seen);
|
|
1803
|
-
return json;
|
|
1804
|
-
};
|
|
1805
|
-
/**
|
|
1806
|
-
* Finds the first cause in an Error cause chain that is an instance of `ErrorClass`.
|
|
1807
|
-
*/
|
|
1808
|
-
const findCause = (error, ErrorClass) => {
|
|
1809
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1810
|
-
let current = error;
|
|
1811
|
-
while (isError(current)) {
|
|
1812
|
-
if (seen.has(current)) break;
|
|
1813
|
-
seen.add(current);
|
|
1814
|
-
if (current instanceof ErrorClass) return current;
|
|
1815
|
-
current = current.cause;
|
|
1816
|
-
}
|
|
1817
|
-
};
|
|
1818
|
-
/**
|
|
1819
|
-
* Creates an Error class with a stable `_tag`, typed template props, cause support, and JSON output.
|
|
1820
|
-
*
|
|
1821
|
-
* Prefer tagged errors for domain failures that travel through Resultar error channels.
|
|
1822
|
-
*
|
|
1823
|
-
* @example
|
|
1824
|
-
* ```ts
|
|
1825
|
-
* class UserNotFoundError extends createTaggedError({
|
|
1826
|
-
* name: "UserNotFoundError",
|
|
1827
|
-
* message: "User $id was not found",
|
|
1828
|
-
* }) {}
|
|
1829
|
-
* ```
|
|
1830
|
-
*/
|
|
1831
|
-
function createTaggedError(options) {
|
|
1832
|
-
const BaseError = options.extends ?? defaultBaseError;
|
|
1833
|
-
const messageTemplate = options.message ?? defaultMessageTemplate;
|
|
1834
|
-
assertNoReservedTemplateVariables(options.name, messageTemplate);
|
|
1835
|
-
const interpolate = compileMessageInterpolator(messageTemplate);
|
|
1836
|
-
class GeneratedTaggedError extends BaseError {
|
|
1837
|
-
static tag = options.name;
|
|
1838
|
-
_tag = options.name;
|
|
1839
|
-
fingerprint = [options.name, messageTemplate];
|
|
1840
|
-
messageTemplate = messageTemplate;
|
|
1841
|
-
constructor(props) {
|
|
1842
|
-
const message = interpolate(props);
|
|
1843
|
-
super(message, getErrorOptions(props?.cause));
|
|
1844
|
-
this.name = options.name;
|
|
1845
|
-
if (props) {
|
|
1846
|
-
for (const [key, value] of Object.entries(props)) if (!reservedTemplateVariables.has(key)) assignReadonly(this, key, value);
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
/**
|
|
1850
|
-
* Narrows a value to this tagged error class.
|
|
1851
|
-
*/
|
|
1852
|
-
static is(value) {
|
|
1853
|
-
return value instanceof this;
|
|
1854
|
-
}
|
|
1855
|
-
/**
|
|
1856
|
-
* Creates an Err containing this tagged error.
|
|
1857
|
-
*/
|
|
1858
|
-
static err(...args) {
|
|
1859
|
-
return err(new this(...args));
|
|
1860
|
-
}
|
|
1861
|
-
/**
|
|
1862
|
-
* Finds a matching cause in this error's cause chain.
|
|
1863
|
-
*/
|
|
1864
|
-
findCause(ErrorClass) {
|
|
1865
|
-
return findCause(this, ErrorClass);
|
|
1866
|
-
}
|
|
1867
|
-
/**
|
|
1868
|
-
* Serializes the tagged error, metadata, and cause chain.
|
|
1869
|
-
*/
|
|
1870
|
-
toJSON() {
|
|
1871
|
-
const json = {
|
|
1872
|
-
_tag: this._tag,
|
|
1873
|
-
fingerprint: this.fingerprint,
|
|
1874
|
-
message: this.message,
|
|
1875
|
-
messageTemplate: this.messageTemplate,
|
|
1876
|
-
name: this.name
|
|
1877
|
-
};
|
|
1878
|
-
if (this.cause !== void 0) json["cause"] = serializeCause(this.cause);
|
|
1879
|
-
for (const [key, value] of Object.entries(this)) if (!(key in json) && key !== "cause" && key !== "stack") json[key] = serializeTaggedValue(value);
|
|
1880
|
-
return json;
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
1883
|
-
return GeneratedTaggedError;
|
|
1884
|
-
}
|
|
1885
|
-
/**
|
|
1886
|
-
* Creates a small tagged-union factory with member constructors, `$is`, and `$match`.
|
|
1887
|
-
*/
|
|
1888
|
-
const taggedEnum = () => {
|
|
1889
|
-
return new Proxy({
|
|
1890
|
-
$is(tag, value) {
|
|
1891
|
-
return typeof value === "object" && value !== null && "_tag" in value && value._tag === tag;
|
|
1892
|
-
},
|
|
1893
|
-
$match(value, handlers) {
|
|
1894
|
-
const handler = handlers[value._tag];
|
|
1895
|
-
if (handler === void 0) throw new Error(`No tagged enum handler for ${String(value._tag)}`);
|
|
1896
|
-
return handler(value);
|
|
1897
|
-
}
|
|
1898
|
-
}, { get(base, property, receiver) {
|
|
1899
|
-
if (property in base) return Reflect.get(base, property, receiver);
|
|
1900
|
-
if (typeof property !== "string") return;
|
|
1901
|
-
return (props) => {
|
|
1902
|
-
const value = { _tag: property };
|
|
1903
|
-
if (props !== void 0) {
|
|
1904
|
-
for (const [key, propValue] of Object.entries(props)) if (key !== "_tag") value[key] = propValue;
|
|
1905
|
-
}
|
|
1906
|
-
return Object.freeze(value);
|
|
1907
|
-
};
|
|
1908
|
-
} });
|
|
1909
|
-
};
|
|
1910
|
-
/**
|
|
1911
|
-
* Matches an Error by tagged `_tag`, with optional native `Error` fallback.
|
|
1912
|
-
*
|
|
1913
|
-
* Throws the original error when no handler matches.
|
|
1914
|
-
*/
|
|
1915
|
-
const matchError = (error, handlers) => {
|
|
1916
|
-
if ("_tag" in error && typeof error._tag === "string" && error._tag in handlers) {
|
|
1917
|
-
const handler = handlers[error._tag];
|
|
1918
|
-
return handler(error);
|
|
1919
|
-
}
|
|
1920
|
-
if (handlers.Error) return handlers.Error(error);
|
|
1921
|
-
throw error;
|
|
1922
|
-
};
|
|
1923
|
-
/**
|
|
1924
|
-
* Matches an Error by tagged `_tag`, then native `Error`, then the provided fallback.
|
|
1925
|
-
*/
|
|
1926
|
-
const matchErrorPartial = (error, handlers, fallback) => {
|
|
1927
|
-
if ("_tag" in error && typeof error._tag === "string" && error._tag in handlers) {
|
|
1928
|
-
const handler = handlers[error._tag];
|
|
1929
|
-
if (handler) return handler(error);
|
|
1930
|
-
}
|
|
1931
|
-
if (handlers.Error) return handlers.Error(error);
|
|
1932
|
-
return fallback(error);
|
|
1933
|
-
};
|
|
1934
|
-
//#endregion
|
|
1935
|
-
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, runPromise, runSync, safeTry, taggedEnum, tryResultAsync as tryAsync, tryResultAsync, tryCatch, tryCatchAsync, unit, unitAsync };
|
|
1936
|
-
|
|
1937
|
-
//# 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 };
|