ZodError.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { util } from "./helpers/util.js";
  2. export const ZodIssueCode = util.arrayToEnum([
  3. "invalid_type",
  4. "invalid_literal",
  5. "custom",
  6. "invalid_union",
  7. "invalid_union_discriminator",
  8. "invalid_enum_value",
  9. "unrecognized_keys",
  10. "invalid_arguments",
  11. "invalid_return_type",
  12. "invalid_date",
  13. "invalid_string",
  14. "too_small",
  15. "too_big",
  16. "invalid_intersection_types",
  17. "not_multiple_of",
  18. "not_finite",
  19. ]);
  20. export const quotelessJson = (obj) => {
  21. const json = JSON.stringify(obj, null, 2);
  22. return json.replace(/"([^"]+)":/g, "$1:");
  23. };
  24. export class ZodError extends Error {
  25. get errors() {
  26. return this.issues;
  27. }
  28. constructor(issues) {
  29. super();
  30. this.issues = [];
  31. this.addIssue = (sub) => {
  32. this.issues = [...this.issues, sub];
  33. };
  34. this.addIssues = (subs = []) => {
  35. this.issues = [...this.issues, ...subs];
  36. };
  37. const actualProto = new.target.prototype;
  38. if (Object.setPrototypeOf) {
  39. // eslint-disable-next-line ban/ban
  40. Object.setPrototypeOf(this, actualProto);
  41. }
  42. else {
  43. this.__proto__ = actualProto;
  44. }
  45. this.name = "ZodError";
  46. this.issues = issues;
  47. }
  48. format(_mapper) {
  49. const mapper = _mapper ||
  50. function (issue) {
  51. return issue.message;
  52. };
  53. const fieldErrors = { _errors: [] };
  54. const processError = (error) => {
  55. for (const issue of error.issues) {
  56. if (issue.code === "invalid_union") {
  57. issue.unionErrors.map(processError);
  58. }
  59. else if (issue.code === "invalid_return_type") {
  60. processError(issue.returnTypeError);
  61. }
  62. else if (issue.code === "invalid_arguments") {
  63. processError(issue.argumentsError);
  64. }
  65. else if (issue.path.length === 0) {
  66. fieldErrors._errors.push(mapper(issue));
  67. }
  68. else {
  69. let curr = fieldErrors;
  70. let i = 0;
  71. while (i < issue.path.length) {
  72. const el = issue.path[i];
  73. const terminal = i === issue.path.length - 1;
  74. if (!terminal) {
  75. curr[el] = curr[el] || { _errors: [] };
  76. // if (typeof el === "string") {
  77. // curr[el] = curr[el] || { _errors: [] };
  78. // } else if (typeof el === "number") {
  79. // const errorArray: any = [];
  80. // errorArray._errors = [];
  81. // curr[el] = curr[el] || errorArray;
  82. // }
  83. }
  84. else {
  85. curr[el] = curr[el] || { _errors: [] };
  86. curr[el]._errors.push(mapper(issue));
  87. }
  88. curr = curr[el];
  89. i++;
  90. }
  91. }
  92. }
  93. };
  94. processError(this);
  95. return fieldErrors;
  96. }
  97. static assert(value) {
  98. if (!(value instanceof ZodError)) {
  99. throw new Error(`Not a ZodError: ${value}`);
  100. }
  101. }
  102. toString() {
  103. return this.message;
  104. }
  105. get message() {
  106. return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2);
  107. }
  108. get isEmpty() {
  109. return this.issues.length === 0;
  110. }
  111. flatten(mapper = (issue) => issue.message) {
  112. const fieldErrors = {};
  113. const formErrors = [];
  114. for (const sub of this.issues) {
  115. if (sub.path.length > 0) {
  116. fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];
  117. fieldErrors[sub.path[0]].push(mapper(sub));
  118. }
  119. else {
  120. formErrors.push(mapper(sub));
  121. }
  122. }
  123. return { formErrors, fieldErrors };
  124. }
  125. get formErrors() {
  126. return this.flatten();
  127. }
  128. }
  129. ZodError.create = (issues) => {
  130. const error = new ZodError(issues);
  131. return error;
  132. };