validate.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.validate = validate;
  6. Object.defineProperty(exports, "ValidationError", {
  7. enumerable: true,
  8. get: function () {
  9. return _ValidationError.default;
  10. }
  11. });
  12. var _absolutePath = _interopRequireDefault(require("./keywords/absolutePath"));
  13. var _ValidationError = _interopRequireDefault(require("./ValidationError"));
  14. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  15. /**
  16. * @template T
  17. * @param fn {(function(): any) | undefined}
  18. * @returns {function(): T}
  19. */
  20. const memoize = fn => {
  21. let cache = false;
  22. /** @type {T} */
  23. let result;
  24. return () => {
  25. if (cache) {
  26. return result;
  27. }
  28. result =
  29. /** @type {function(): any} */
  30. fn();
  31. cache = true; // Allow to clean up memory for fn
  32. // and all dependent resources
  33. // eslint-disable-next-line no-undefined, no-param-reassign
  34. fn = undefined;
  35. return result;
  36. };
  37. };
  38. const getAjv = memoize(() => {
  39. // Use CommonJS require for ajv libs so TypeScript consumers aren't locked into esModuleInterop (see #110).
  40. const Ajv = require("ajv");
  41. const ajvKeywords = require("ajv-keywords");
  42. const ajv = new Ajv({
  43. allErrors: true,
  44. verbose: true,
  45. $data: true
  46. });
  47. ajvKeywords(ajv, ["instanceof", "formatMinimum", "formatMaximum", "patternRequired"]); // Custom keywords
  48. (0, _absolutePath.default)(ajv);
  49. return ajv;
  50. });
  51. /** @typedef {import("json-schema").JSONSchema4} JSONSchema4 */
  52. /** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
  53. /** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */
  54. /** @typedef {import("ajv").ErrorObject} ErrorObject */
  55. /**
  56. * @typedef {Object} Extend
  57. * @property {number=} formatMinimum
  58. * @property {number=} formatMaximum
  59. * @property {boolean=} formatExclusiveMinimum
  60. * @property {boolean=} formatExclusiveMaximum
  61. * @property {string=} link
  62. */
  63. /** @typedef {(JSONSchema4 | JSONSchema6 | JSONSchema7) & Extend} Schema */
  64. /** @typedef {ErrorObject & { children?: Array<ErrorObject>}} SchemaUtilErrorObject */
  65. /**
  66. * @callback PostFormatter
  67. * @param {string} formattedError
  68. * @param {SchemaUtilErrorObject} error
  69. * @returns {string}
  70. */
  71. /**
  72. * @typedef {Object} ValidationErrorConfiguration
  73. * @property {string=} name
  74. * @property {string=} baseDataPath
  75. * @property {PostFormatter=} postFormatter
  76. */
  77. /**
  78. * @param {Schema} schema
  79. * @param {Array<object> | object} options
  80. * @param {ValidationErrorConfiguration=} configuration
  81. * @returns {void}
  82. */
  83. function validate(schema, options, configuration) {
  84. let errors = [];
  85. if (Array.isArray(options)) {
  86. errors = Array.from(options, nestedOptions => validateObject(schema, nestedOptions));
  87. errors.forEach((list, idx) => {
  88. const applyPrefix =
  89. /**
  90. * @param {SchemaUtilErrorObject} error
  91. */
  92. error => {
  93. // eslint-disable-next-line no-param-reassign
  94. error.dataPath = `[${idx}]${error.dataPath}`;
  95. if (error.children) {
  96. error.children.forEach(applyPrefix);
  97. }
  98. };
  99. list.forEach(applyPrefix);
  100. });
  101. errors = errors.reduce((arr, items) => {
  102. arr.push(...items);
  103. return arr;
  104. }, []);
  105. } else {
  106. errors = validateObject(schema, options);
  107. }
  108. if (errors.length > 0) {
  109. throw new _ValidationError.default(errors, schema, configuration);
  110. }
  111. }
  112. /**
  113. * @param {Schema} schema
  114. * @param {Array<object> | object} options
  115. * @returns {Array<SchemaUtilErrorObject>}
  116. */
  117. function validateObject(schema, options) {
  118. const compiledSchema = getAjv().compile(schema);
  119. const valid = compiledSchema(options);
  120. if (valid) return [];
  121. return compiledSchema.errors ? filterErrors(compiledSchema.errors) : [];
  122. }
  123. /**
  124. * @param {Array<ErrorObject>} errors
  125. * @returns {Array<SchemaUtilErrorObject>}
  126. */
  127. function filterErrors(errors) {
  128. /** @type {Array<SchemaUtilErrorObject>} */
  129. let newErrors = [];
  130. for (const error of
  131. /** @type {Array<SchemaUtilErrorObject>} */
  132. errors) {
  133. const {
  134. dataPath
  135. } = error;
  136. /** @type {Array<SchemaUtilErrorObject>} */
  137. let children = [];
  138. newErrors = newErrors.filter(oldError => {
  139. if (oldError.dataPath.includes(dataPath)) {
  140. if (oldError.children) {
  141. children = children.concat(oldError.children.slice(0));
  142. } // eslint-disable-next-line no-undefined, no-param-reassign
  143. oldError.children = undefined;
  144. children.push(oldError);
  145. return false;
  146. }
  147. return true;
  148. });
  149. if (children.length) {
  150. error.children = children;
  151. }
  152. newErrors.push(error);
  153. }
  154. return newErrors;
  155. }