UNPKG

@jirutka/ajv-cli

Version:

CLI for Ajv JSON Schema Validator with human-friendly error messages

134 lines 4.54 kB
// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2023 Gabriel McAdams <https://github.com/ghmcadams> import levenshtein from 'damerau-levenshtein'; export const ofErrorKeywords = ['anyOf', 'oneOf']; // Add parents if one of these keywords export const parentKeywordsOk = [ 'minItems', 'maxItems', 'additionalItems', 'uniqueItems', 'minProperties', 'maxProperties', 'additionalProperties', 'required', 'contains', 'dependencies', 'propertyNames', 'unevaluatedProperties', 'unevaluatedItems', 'dependentRequired', ]; export function getDataReferencePath(dataVar, path) { const pointer = `${dataVar}${path}`; return pointer // Array items .replace(/\/(\d+)/g, '[$1]') // Object properties .replace(/\/(\w+)/g, '.$1'); } export function getOfChoiceIndex(parent, schemaPath) { let choiceIndex = parent.length; const thisSchemaParts = schemaPath.split('/'); for (let i = thisSchemaParts.length - 2; i > 0; i--) { if (ofErrorKeywords.includes(thisSchemaParts[i])) { choiceIndex = parseInt(thisSchemaParts[i + 1], 10); break; } } return choiceIndex; } export function getValueMatchScore(data, valuesToCheck) { const valuesArray = Array.isArray(valuesToCheck) ? valuesToCheck : [valuesToCheck]; return Math.max(...valuesArray.map(value => { if (data === value) { return 1; } if (getType(data) !== 'string' || getType(value) !== 'string') { return 0; } const lev = levenshtein(data, value); return lev.similarity; })); } export function getPropertyMatchScore(data, schema) { if (schema === undefined) { return 0; } const schemaTypes = getSchemaType(schema); if (!schemaTypes.includes('object')) { return 0; } const dataProperties = Object.keys(data); const matchScore = dataProperties.reduce((ret, dataKey) => { const dataType = getType(data[dataKey]); const schemaPropertyNames = schema.properties !== undefined ? Object.keys(schema.properties) : []; if (schemaPropertyNames.includes(dataKey)) { const schemaPropertyTypes = schema.properties !== undefined ? getSchemaType(schema.properties[dataKey]) : []; ret += schemaPropertyTypes.includes(dataType) ? 1 : 0; } else { // Get the score for the schema property that most closely matches the data property const keyScore = schemaPropertyNames.reduce((val, key) => { const lev = levenshtein(dataKey, key); return Math.max(val, lev.similarity); }, 0); // A score less than 0.7 is not a close enough match ret += keyScore > 0.7 ? keyScore : 0; } return ret; }, 0); return matchScore / dataProperties.length; } export function areErrorsEqual(error1, error2) { return (error1.instancePath === error2.instancePath && error1.keyword === error2.keyword && JSON.stringify(error1.params) === JSON.stringify(error2.params)); } export function getType(variable) { return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase(); } export function isArray(variable) { return getType(variable) === 'array'; } export function getSchemaType(entry) { if (entry === undefined) { return ['null']; } if (typeof entry === 'boolean') { return ['boolean']; } if (entry.hasOwnProperty('enum') && entry.enum !== undefined) { return entry.enum.map(getType); } if (entry.hasOwnProperty('const') && entry.const !== undefined) { return [getType(entry.const)]; } if (!entry.hasOwnProperty('type') || entry.type === undefined) { // INVALID schema: determine type based on other properties if (entry.hasOwnProperty('items')) { return ['array']; } } if (typeof entry.type === 'string') { const theType = schemaTypeMap[entry.type] ?? entry.type; return [theType]; } return ['null']; } const schemaTypeMap = { integer: 'number' }; export function getCommonPrefix(string1, string2) { let sharedPrefix = ''; for (let i = 0; i < string1.length && i < string2.length; i++) { if (string1[i] === string2[i]) { sharedPrefix += string1[i]; } else { break; } } return sharedPrefix; } //# sourceMappingURL=util.js.map