Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"preset": "recommended",
"complexity": { "useLiteralKeys": "off" },
"style": {
"noParameterAssign": "error",
Expand Down
70 changes: 30 additions & 40 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { readFileSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { fileURLToPath, URL } from "node:url";
import Ajv2020 from "ajv/dist/2020.js";
import Ajv04 from "ajv-draft-04";
import addFormatsModule from "ajv-formats";
Expand All @@ -10,8 +8,8 @@ import { checkRefs, replaceRefs } from "./resolve.js";
/** @typedef {import("ajv/dist/core.js").default} AjvCore */
/** @typedef {typeof import("ajv/dist/core.js").default} AjvCoreConstructor */

/** @typedef {import("./index.js").Validator} ValidatorDecl */
/** @typedef {import("./index.js").SpecData} SpecData */
/** @typedef {import("./index.d.ts").Validator} ValidatorDecl */
/** @typedef {import("./index.d.ts").SpecData} SpecData */

// ajv-formats is a CommonJS module,
// so we need to access its default property to get the correct typed value
Expand All @@ -29,22 +27,6 @@ const ajvVersions = {

const inlinedRefs = "x-inlined-refs";

/**
* @param {string} fileName
* @returns {string}
*/
function localFile(fileName) {
return fileURLToPath(new URL(fileName, import.meta.url));
}

/**
* @param {string} file
* @returns {{ $schema: string, [x: string]: unknown }}
*/
function importJSON(file) {
return JSON.parse(readFileSync(localFile(file), "utf-8"));
}

/**
*
* @param {{ swagger?: string, openapi?: string }} specification
Expand Down Expand Up @@ -132,6 +114,29 @@ async function loadSpecFromData(data) {
}
}

/**
* @param {Partial<Record<string, import("ajv/dist/core.js").AnyValidateFunction<unknown>>>} ajvValidators
* @param {Ajv2020.Options | undefined} ajvOptions
* @param {string} version
*/
async function getAjvValidator(ajvValidators, ajvOptions, version) {
if (!ajvValidators[version]) {
const { default: schema } = await import(
`./schemas/v${version}/schema.json`,
{ with: { type: "json" } }
);
const schemaVersion = schema.$schema;
const AjvClass = /** @type {AjvCoreConstructor} */ (
ajvVersions[schemaVersion]
);
const ajv = new AjvClass(ajvOptions);
addFormats(ajv);
ajv.addFormat("media-range", true); // used in 3.1
ajvValidators[version] = ajv.compile(schema);
}
return ajvValidators[version];
}

export class Validator {
/**
* @param {import('./index.js').ValidatorOptions} ajvOptions
Expand Down Expand Up @@ -204,7 +209,11 @@ export class Validator {
"Cannot find supported swagger/openapi version in specification, version must be a string.",
};
}
const validateSchema = this.getAjvValidator(version);
const validateSchema = await getAjvValidator(
this.ajvValidators,
this.ajvOptions,
version,
);
// check if the specification matches the JSONschema
const schemaResult = validateSchema(specification);
// check if the references are valid as those can't be validated bu JSONschema
Expand Down Expand Up @@ -260,23 +269,4 @@ export class Validator {
}
return this.validate(/** @type {SpecFromData} */ (specification));
}

/**
* @param {string} version
* @returns {ReturnType<AjvCore['compile']>}
*/
getAjvValidator(version) {
if (!this.ajvValidators[version]) {
const schema = importJSON(`./schemas/v${version}/schema.json`);
const schemaVersion = schema.$schema;
const AjvClass = /** @type {AjvCoreConstructor} */ (
ajvVersions[schemaVersion]
);
const ajv = new AjvClass(this.ajvOptions);
addFormats(ajv);
ajv.addFormat("media-range", true); // used in 3.1
this.ajvValidators[version] = ajv.compile(schema);
}
return this.ajvValidators[version];
}
}
Loading