resultar 3.4.0 → 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/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
- return combineResultListWithAllErrors(Array.from(resultListOrItems, f));
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
- return await toResultAsync(resolveResultAsyncBooleanCondition(condition) ? options.onTrue() : options.onFalse());
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
- return await toResultAsync(conditionResult.value ? options.onTrue() : options.onFalse());
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
- return new ResultAsync((typeof fn === "function" ? Promise.try(fn) : fn).then((value) => ok(value)).catch((error) => {
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
- return new ResultAsync(promise.then((value) => ok(value)));
1531
+ const newPromise = promise.then((value) => ok(value));
1532
+ return new ResultAsync(newPromise);
1208
1533
  }
1209
1534
  static fromPromise(promise, errorFn) {
1210
- return new ResultAsync(promise.then((value) => ok(value)).catch((error) => err(errorFn(error))));
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
- return new ResultAsync(new Promise((resolve) => {
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
- return new ResultAsync(new Promise((resolve) => {
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
- //#region src/redacted.ts
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 };