Skip to content

Commit

Permalink
fix(perf): cache compiled schema (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Jun 14, 2023
1 parent dd3bc4f commit 02aa068
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 53 deletions.
28 changes: 1 addition & 27 deletions declarations/validate.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type JSONSchema4 = import("json-schema").JSONSchema4;
export type JSONSchema6 = import("json-schema").JSONSchema6;
export type JSONSchema7 = import("json-schema").JSONSchema7;
export type ErrorObject = import("ajv").ErrorObject;
export type ValidateFunction = import("ajv").ValidateFunction;
export type Extend = {
formatMinimum?: number | undefined;
formatMaximum?: number | undefined;
Expand All @@ -23,33 +24,6 @@ export type ValidationErrorConfiguration = {
baseDataPath?: string | undefined;
postFormatter?: PostFormatter | undefined;
};
/** @typedef {import("json-schema").JSONSchema4} JSONSchema4 */
/** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
/** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */
/** @typedef {import("ajv").ErrorObject} ErrorObject */
/**
* @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<ErrorObject>}} SchemaUtilErrorObject */
/**
* @callback PostFormatter
* @param {string} formattedError
* @param {SchemaUtilErrorObject} error
* @returns {string}
*/
/**
* @typedef {Object} ValidationErrorConfiguration
* @property {string=} name
* @property {string=} baseDataPath
* @property {PostFormatter=} postFormatter
*/
/**
* @param {Schema} schema
* @param {Array<object> | object} options
Expand Down
58 changes: 32 additions & 26 deletions src/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const getAjv = memoize(() => {
/** @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
Expand Down Expand Up @@ -88,6 +89,22 @@ const getAjv = memoize(() => {
* @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;
}

/**
* @param {Schema} schema
* @param {Array<object> | object} options
Expand All @@ -98,31 +115,11 @@ function validate(schema, options, configuration) {
let errors = [];

if (Array.isArray(options)) {
errors = Array.from(options, (nestedOptions) =>
validateObject(schema, nestedOptions)
);

errors.forEach((list, idx) => {
const applyPrefix =
/**
* @param {SchemaUtilErrorObject} error
*/
(error) => {
// eslint-disable-next-line no-param-reassign
error.dataPath = `[${idx}]${error.dataPath}`;

if (error.children) {
error.children.forEach(applyPrefix);
}
};

list.forEach(applyPrefix);
});

errors = errors.reduce((arr, items) => {
arr.push(...items);
return arr;
}, []);
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);
}
Expand All @@ -132,13 +129,22 @@ function validate(schema, options, configuration) {
}
}

/** @typedef {WeakMap<Schema, ValidateFunction>} */
const schemaCache = new WeakMap();

/**
* @param {Schema} schema
* @param {Array<object> | object} options
* @returns {Array<SchemaUtilErrorObject>}
*/
function validateObject(schema, options) {
const compiledSchema = getAjv().compile(schema);
let compiledSchema = schemaCache.get(schema);

if (!compiledSchema) {
compiledSchema = getAjv().compile(schema);
schemaCache.set(schema, compiledSchema);
}

const valid = compiledSchema(options);

if (valid) return [];
Expand Down

0 comments on commit 02aa068

Please sign in to comment.