"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validate = validate; exports.enableValidation = enableValidation; exports.disableValidation = disableValidation; exports.needValidate = needValidate; Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return _ValidationError.default; } }); var _absolutePath = _interopRequireDefault(require("./keywords/absolutePath")); var _undefinedAsNull = _interopRequireDefault(require("./keywords/undefinedAsNull")); var _ValidationError = _interopRequireDefault(require("./ValidationError")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @template T * @param fn {(function(): any) | undefined} * @returns {function(): T} */ const memoize = fn => { let cache = false; /** @type {T} */ let result; return () => { if (cache) { return result; } result = /** @type {function(): any} */ fn(); cache = true; // Allow to clean up memory for fn // and all dependent resources // eslint-disable-next-line no-undefined, no-param-reassign fn = undefined; return result; }; }; const getAjv = memoize(() => { // Use CommonJS require for ajv libs so TypeScript consumers aren't locked into esModuleInterop (see #110). // eslint-disable-next-line global-require const Ajv = require("ajv"); // eslint-disable-next-line global-require const ajvKeywords = require("ajv-keywords"); const ajv = new Ajv({ allErrors: true, verbose: true, $data: true }); ajvKeywords(ajv, ["instanceof", "formatMinimum", "formatMaximum", "patternRequired"]); // Custom keywords (0, _absolutePath.default)(ajv); (0, _undefinedAsNull.default)(ajv); return ajv; }); /** @typedef {import("json-schema").JSONSchema4} JSONSchema4 */ /** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */ /** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */ /** @typedef {import("ajv").ErrorObject} ErrorObject */ /** @typedef {import("ajv").ValidateFunction} ValidateFunction */ /** * @typedef {Object} Extend * @property {number=} formatMinimum * @property {number=} formatMaximum * @property {boolean=} formatExclusiveMinimum * @property {boolean=} formatExclusiveMaximum * @property {string=} link * @property {boolean=} undefinedAsNull */ /** @typedef {(JSONSchema4 | JSONSchema6 | JSONSchema7) & Extend} Schema */ /** @typedef {ErrorObject & { children?: Array}} SchemaUtilErrorObject */ /** * @callback PostFormatter * @param {string} formattedError * @param {SchemaUtilErrorObject} error * @returns {string} */ /** * @typedef {Object} ValidationErrorConfiguration * @property {string=} name * @property {string=} baseDataPath * @property {PostFormatter=} postFormatter */ /** * @param {SchemaUtilErrorObject} error * @param {number} idx * @returns {SchemaUtilErrorObject} */ function applyPrefix(error, idx) { // eslint-disable-next-line no-param-reassign error.dataPath = `[${idx}]${error.dataPath}`; if (error.children) { error.children.forEach(err => applyPrefix(err, idx)); } return error; } let skipValidation = false; // We use `process.env.SKIP_VALIDATION` because you can have multiple `schema-utils` with different version, // so we want to disable it globally, `process.env` doesn't supported by browsers, so we have the local `skipValidation` variables // Enable validation function enableValidation() { skipValidation = false; // Disable validation for any versions if (process && process.env) { process.env.SKIP_VALIDATION = "n"; } } // Disable validation function disableValidation() { skipValidation = true; if (process && process.env) { process.env.SKIP_VALIDATION = "y"; } } // Check if we need to confirm function needValidate() { if (skipValidation) { return false; } if (process && process.env && process.env.SKIP_VALIDATION) { const value = process.env.SKIP_VALIDATION.trim(); if (/^(?:y|yes|true|1|on)$/i.test(value)) { return false; } if (/^(?:n|no|false|0|off)$/i.test(value)) { return true; } } return true; } /** * @param {Schema} schema * @param {Array | object} options * @param {ValidationErrorConfiguration=} configuration * @returns {void} */ function validate(schema, options, configuration) { if (!needValidate()) { return; } let errors = []; if (Array.isArray(options)) { for (let i = 0; i <= options.length - 1; i++) { errors.push(...validateObject(schema, options[i]).map(err => applyPrefix(err, i))); } } else { errors = validateObject(schema, options); } if (errors.length > 0) { throw new _ValidationError.default(errors, schema, configuration); } } /** @typedef {WeakMap} */ const schemaCache = new WeakMap(); /** * @param {Schema} schema * @param {Array | object} options * @returns {Array} */ function validateObject(schema, options) { let compiledSchema = schemaCache.get(schema); if (!compiledSchema) { compiledSchema = getAjv().compile(schema); schemaCache.set(schema, compiledSchema); } const valid = compiledSchema(options); if (valid) return []; return compiledSchema.errors ? filterErrors(compiledSchema.errors) : []; } /** * @param {Array} errors * @returns {Array} */ function filterErrors(errors) { /** @type {Array} */ let newErrors = []; for (const error of /** @type {Array} */ errors) { const { dataPath } = error; /** @type {Array} */ let children = []; newErrors = newErrors.filter(oldError => { if (oldError.dataPath.includes(dataPath)) { if (oldError.children) { children = children.concat(oldError.children.slice(0)); } // eslint-disable-next-line no-undefined, no-param-reassign oldError.children = undefined; children.push(oldError); return false; } return true; }); if (children.length) { error.children = children; } newErrors.push(error); } return newErrors; }