@readme/openapi-parser
Version:
Swagger 2.0 and OpenAPI 3.x parser and validator for Node and browsers
840 lines (828 loc) • 29.3 kB
JavaScript
// src/index.ts
import { $RefParser, dereferenceInternal, MissingPointerError } from "@apidevtools/json-schema-ref-parser";
// src/lib/index.ts
var pathParameterTemplateRegExp = /\{([^/}]+)}/g;
var supportedHTTPMethods = ["get", "post", "put", "delete", "patch", "options", "head", "trace"];
var swaggerHTTPMethods = ["get", "put", "post", "delete", "options", "head", "patch"];
function isSwagger(schema) {
return "swagger" in schema && schema.swagger !== void 0;
}
function isOpenAPI(schema) {
return "openapi" in schema && schema.openapi !== void 0;
}
function isOpenAPI30(schema) {
return "openapi" in schema && schema.openapi !== void 0 && schema.openapi.startsWith("3.0");
}
function isOpenAPI31(schema) {
return "openapi" in schema && schema.openapi !== void 0 && schema.openapi.startsWith("3.1");
}
function getSpecificationName(api) {
return isSwagger(api) ? "Swagger" : "OpenAPI";
}
// src/util.ts
import { getJsonSchemaRefParserDefaultOptions } from "@apidevtools/json-schema-ref-parser";
// src/repair.ts
function fixServers(server, path) {
if (server && "url" in server && server.url && server.url.startsWith("/")) {
try {
const inUrl = new URL(path);
server.url = `${inUrl.protocol}//${inUrl.hostname}${server.url}`;
} catch {
}
}
}
function fixOasRelativeServers(schema, filePath) {
if (!schema || !isOpenAPI(schema) || !filePath || !filePath.startsWith("http:") && !filePath.startsWith("https:")) {
return;
}
if (schema.servers) {
schema.servers.map((server) => fixServers(server, filePath));
}
["paths", "webhooks"].forEach((component) => {
if (component in schema) {
const schemaElement = schema.paths || {};
Object.keys(schemaElement).forEach((path) => {
const pathItem = schemaElement[path] || {};
Object.keys(pathItem).forEach((opItem) => {
const pathItemElement = pathItem[opItem];
if (!pathItemElement) {
return;
}
if (opItem === "servers" && Array.isArray(pathItemElement)) {
pathItemElement.forEach((server) => {
fixServers(server, filePath);
});
return;
}
if (supportedHTTPMethods.includes(opItem) && typeof pathItemElement === "object" && "servers" in pathItemElement && Array.isArray(pathItemElement.servers)) {
pathItemElement.servers.forEach((server) => {
fixServers(server, filePath);
});
}
});
});
}
});
}
// src/util.ts
function repairSchema(schema, filePath) {
if (isOpenAPI(schema)) {
fixOasRelativeServers(schema, filePath);
}
}
function normalizeArguments(api) {
return {
path: typeof api === "string" ? api : "",
schema: typeof api === "object" ? api : void 0
};
}
function convertOptionsForParser(options) {
const parserOptions = getJsonSchemaRefParserDefaultOptions();
return {
...parserOptions,
dereference: {
...parserOptions.dereference,
circular: options?.dereference && "circular" in options.dereference ? options.dereference.circular : parserOptions.dereference.circular,
onCircular: options?.dereference?.onCircular || parserOptions.dereference.onCircular,
onDereference: options?.dereference?.onDereference || parserOptions.dereference.onDereference,
// OpenAPI 3.1 allows for `summary` and `description` properties at the same level as a `$ref`
// pointer to be preserved when that `$ref` pointer is dereferenced. The default behavior of
// `json-schema-ref-parser` is to discard these properties but this option allows us to
// override that behavior.
preservedProperties: ["summary", "description"]
},
resolve: {
...parserOptions.resolve,
external: options?.resolve && "external" in options.resolve ? options.resolve.external : parserOptions.resolve.external,
file: options?.resolve && "file" in options.resolve ? options.resolve.file : parserOptions.resolve.file,
http: {
...typeof parserOptions.resolve.http === "object" ? parserOptions.resolve.http : {},
timeout: options?.resolve?.http && "timeout" in options.resolve.http ? options.resolve.http.timeout : 5e3
}
},
timeoutMs: options?.timeoutMs
};
}
// src/validators/schema.ts
import betterAjvErrors from "@readme/better-ajv-errors";
import { openapi } from "@readme/openapi-schemas";
import Ajv from "ajv/dist/2020.js";
import AjvDraft4 from "ajv-draft-04";
// src/lib/reduceAjvErrors.ts
function reduceAjvErrors(errors) {
const flattened = /* @__PURE__ */ new Map();
errors.forEach((err) => {
if (["must have required property '$ref'", "must match exactly one schema in oneOf"].includes(err.message)) {
return;
}
if (!flattened.size) {
flattened.set(err.instancePath, err);
return;
} else if (flattened.has(err.instancePath)) {
return;
}
let shouldRecordError = true;
flattened.forEach((flat) => {
if (flat.instancePath.includes(err.instancePath)) {
shouldRecordError = false;
}
});
if (shouldRecordError) {
flattened.set(err.instancePath, err);
}
});
if (!flattened.size) {
return errors;
}
return [...flattened.values()];
}
// src/validators/schema.ts
var LARGE_SPEC_ERROR_CAP = 20;
var LARGE_SPEC_SIZE_CAP = 5e6;
function initializeAjv(draft04 = true) {
const opts = {
allErrors: true,
strict: false,
validateFormats: false
};
if (draft04) {
return new AjvDraft4(opts);
}
return new Ajv(opts);
}
function validateSchema(api, options = {}) {
let ajv;
let schema;
const specificationName = getSpecificationName(api);
if (isSwagger(api)) {
schema = openapi.v2;
ajv = initializeAjv();
} else if (isOpenAPI31(api)) {
schema = openapi.v31legacy;
const schemaDynamicRef = schema.$defs.schema;
if ("$dynamicAnchor" in schemaDynamicRef) {
delete schemaDynamicRef.$dynamicAnchor;
}
schema.$defs.components.properties.schemas.additionalProperties = schemaDynamicRef;
schema.$defs.header.dependentSchemas.schema.properties.schema = schemaDynamicRef;
schema.$defs["media-type"].properties.schema = schemaDynamicRef;
schema.$defs.parameter.properties.schema = schemaDynamicRef;
ajv = initializeAjv(false);
} else {
schema = openapi.v3;
ajv = initializeAjv();
}
const isValid = ajv.validate(schema, api);
if (isValid) {
return { valid: true, warnings: [], specification: specificationName };
}
let additionalErrors = 0;
let reducedErrors = reduceAjvErrors(ajv.errors);
if (reducedErrors.length >= LARGE_SPEC_ERROR_CAP) {
try {
if (JSON.stringify(api).length >= LARGE_SPEC_SIZE_CAP) {
additionalErrors = reducedErrors.length - 20;
reducedErrors = reducedErrors.slice(0, 20);
}
} catch (error) {
}
}
try {
const errors = betterAjvErrors(schema, api, reducedErrors, {
format: "cli-array",
colorize: options?.validate?.errors?.colorize || false,
indent: 2
});
return {
valid: false,
errors,
warnings: [],
additionalErrors,
specification: specificationName
};
} catch (err) {
return {
valid: false,
errors: [{ message: err.message }],
warnings: [],
additionalErrors,
specification: specificationName
};
}
}
// src/validators/spec/index.ts
var SpecificationValidator = class {
errors = [];
warnings = [];
reportError(message) {
this.errors.push({ message });
}
reportWarning(message) {
this.warnings.push({ message });
}
};
// src/validators/spec/openapi.ts
var OpenAPISpecificationValidator = class extends SpecificationValidator {
api;
rules;
constructor(api, rules) {
super();
this.api = api;
this.rules = rules;
}
run() {
const operationIds = [];
Object.keys(this.api.paths || {}).forEach((pathName) => {
const path = this.api.paths[pathName];
const pathId = `/paths${pathName}`;
if (path && pathName.startsWith("/")) {
this.validatePath(path, pathId, operationIds);
}
});
if (isOpenAPI30(this.api)) {
if (this.api.components) {
Object.keys(this.api.components).forEach((componentType) => {
Object.keys(this.api.components[componentType]).forEach((componentName) => {
if (!/^[a-zA-Z0-9.\-_]+$/.test(componentName)) {
const componentId = `/components/${componentType}/${componentName}`;
this.reportError(
`\`${componentId}\` has an invalid name. Component names should match against: /^[a-zA-Z0-9.-_]+$/`
);
}
});
});
}
}
if (isOpenAPI31(this.api)) {
if (!Object.keys(this.api.paths || {}).length && !Object.keys(this.api.webhooks || {}).length) {
this.reportError("OpenAPI 3.1 definitions must contain at least one entry in either `paths` or `webhook`.");
}
}
}
/**
* Validates the given path.
*
*/
validatePath(path, pathId, operationIds) {
supportedHTTPMethods.forEach((operationName) => {
const operation = path[operationName];
const operationId = `${pathId}/${operationName}`;
if (operation) {
const declaredOperationId = operation.operationId;
if (declaredOperationId) {
if (!operationIds.includes(declaredOperationId)) {
operationIds.push(declaredOperationId);
} else if (this.rules["duplicate-operation-id"] === "warning") {
this.reportWarning(`The operationId \`${declaredOperationId}\` is duplicated and should be made unique.`);
} else {
this.reportError(`The operationId \`${declaredOperationId}\` is duplicated and must be made unique.`);
}
}
this.validateParameters(path, pathId, operation, operationId);
Object.keys(operation.responses || {}).forEach((responseCode) => {
const response = operation.responses[responseCode];
const responseId = `${operationId}/responses/${responseCode}`;
if (response && !("$ref" in response)) {
this.validateResponse(response, responseId);
}
});
}
});
}
/**
* Validates the parameters for the given operation.
*
*/
validateParameters(path, pathId, operation, operationId) {
const pathParams = path.parameters || [];
const operationParams = operation.parameters || [];
this.checkForDuplicates(pathParams, pathId);
this.checkForDuplicates(operationParams, operationId);
const params = pathParams.reduce((combinedParams, value) => {
const duplicate = combinedParams.some((param) => {
if ("$ref" in param || "$ref" in value) {
return false;
}
return param.in === value.in && param.name === value.name;
});
if (!duplicate) {
combinedParams.push(value);
}
return combinedParams;
}, operationParams.slice());
this.validatePathParameters(params, pathId, operationId);
this.validateParameterTypes(params, operationId);
}
/**
* Validates path parameters for the given path.
*
*/
validatePathParameters(params, pathId, operationId) {
const placeholders = [...new Set(pathId.match(pathParameterTemplateRegExp) || [])];
params.filter((param) => "in" in param).filter((param) => param.in === "path").forEach((param) => {
if (param.required !== true) {
if (this.rules["non-optional-path-parameters"] === "warning") {
this.reportWarning(
`Path parameters should not be optional. Set \`required=true\` for the \`${param.name}\` parameter at \`${operationId}\`.`
);
} else {
this.reportError(
`Path parameters cannot be optional. Set \`required=true\` for the \`${param.name}\` parameter at \`${operationId}\`.`
);
}
}
const match = placeholders.indexOf(`{${param.name}}`);
if (match === -1) {
const error = `\`${operationId}\` has a path parameter named \`${param.name}\`, but there is no corresponding \`{${param.name}}\` in the path string.`;
if (this.rules["path-parameters-not-in-path"] === "warning") {
this.reportWarning(error);
} else {
this.reportError(error);
}
}
placeholders.splice(match, 1);
});
if (placeholders.length > 0) {
const list = new Intl.ListFormat("en", { style: "long", type: "conjunction" }).format(
placeholders.map((placeholder) => `\`${placeholder}\``)
);
const error = `\`${operationId}\` is missing path parameter(s) for ${list}.`;
if (this.rules["path-parameters-not-in-parameters"] === "warning") {
this.reportWarning(error);
} else {
this.reportError(error);
}
}
}
/**
* Validates data types of parameters for the given operation.
*
*/
validateParameterTypes(params, operationId) {
params.forEach((param) => {
if ("$ref" in param) {
return;
}
if (!param.schema && param.content) {
return;
} else if ("$ref" in param.schema) {
return;
}
const parameterId = `${operationId}/parameters/${param.name}`;
this.validateSchema(param.schema, parameterId);
});
}
/**
* Validates the given response object.
*
*/
validateResponse(response, responseId) {
Object.keys(response.headers || {}).forEach((headerName) => {
const header = response.headers[headerName];
const headerId = `${responseId}/headers/${headerName}`;
if ("$ref" in header) {
return;
}
if (header.schema) {
if (!("$ref" in header.schema)) {
this.validateSchema(header.schema, headerId);
}
} else if (header.content) {
Object.keys(header.content).forEach((mediaType) => {
if (header.content[mediaType].schema) {
if (!("$ref" in header.content[mediaType].schema)) {
this.validateSchema(header.content[mediaType].schema || {}, `${headerId}/content/${mediaType}/schema`);
}
}
});
}
});
if (response.content) {
Object.keys(response.content).forEach((mediaType) => {
if (response.content[mediaType].schema) {
if (!("$ref" in response.content[mediaType].schema)) {
this.validateSchema(response.content[mediaType].schema || {}, `${responseId}/content/${mediaType}/schema`);
}
}
});
}
}
/**
* Validates the given Swagger schema object.
*
*/
validateSchema(schema, schemaId) {
if (schema.type === "array" && !schema.items) {
if (this.rules["array-without-items"] === "warning") {
this.reportWarning(`\`${schemaId}\` is an array, so it should include an \`items\` schema.`);
} else {
this.reportError(`\`${schemaId}\` is an array, so it must include an \`items\` schema.`);
}
}
}
/**
* Checks the given parameter list for duplicates.
*
*/
checkForDuplicates(params, schemaId) {
for (let i = 0; i < params.length - 1; i++) {
const outer = params[i];
for (let j = i + 1; j < params.length; j++) {
const inner = params[j];
if ("$ref" in outer || "$ref" in inner) {
continue;
}
if (outer.name === inner.name && outer.in === inner.in) {
const error = `Found multiple \`${outer.in}\` parameters named \`${outer.name}\` in \`${schemaId}\`.`;
if (this.rules["duplicate-non-request-body-parameters"] === "warning") {
this.reportWarning(error);
} else {
this.reportError(error);
}
}
}
}
}
};
// src/validators/spec/swagger.ts
var SwaggerSpecificationValidator = class extends SpecificationValidator {
api;
constructor(api) {
super();
this.api = api;
}
run() {
const operationIds = [];
Object.keys(this.api.paths || {}).forEach((pathName) => {
const path = this.api.paths[pathName];
const pathId = `/paths${pathName}`;
if (path && pathName.startsWith("/")) {
this.validatePath(path, pathId, operationIds);
}
});
Object.keys(this.api.definitions || {}).forEach((definitionName) => {
const definition = this.api.definitions[definitionName];
const definitionId = `/definitions/${definitionName}`;
if (!/^[a-zA-Z0-9.\-_]+$/.test(definitionName)) {
this.reportError(
`\`${definitionId}\` has an invalid name. Definition names should match against: /^[a-zA-Z0-9.-_]+$/`
);
}
this.validateRequiredPropertiesExist(definition, definitionId);
});
}
/**
* Validates the given path.
*
*/
validatePath(path, pathId, operationIds) {
swaggerHTTPMethods.forEach((operationName) => {
const operation = path[operationName];
const operationId = `${pathId}/${operationName}`;
if (operation) {
const declaredOperationId = operation.operationId;
if (declaredOperationId) {
if (!operationIds.includes(declaredOperationId)) {
operationIds.push(declaredOperationId);
} else {
this.reportError(`The operationId \`${declaredOperationId}\` is duplicated and must be made unique.`);
}
}
this.validateParameters(path, pathId, operation, operationId);
Object.keys(operation.responses || {}).forEach((responseName) => {
const response = operation.responses[responseName];
if ("$ref" in response || !response) {
return;
}
const responseId = `${operationId}/responses/${responseName}`;
this.validateResponse(responseName, response, responseId);
});
}
});
}
/**
* Validates the parameters for the given operation.
*
*/
validateParameters(path, pathId, operation, operationId) {
const pathParams = (path.parameters || []).filter((param) => !("$ref" in param));
const operationParams = (operation.parameters || []).filter(
(param) => !("$ref" in param)
);
this.checkForDuplicates(pathParams, pathId);
this.checkForDuplicates(operationParams, operationId);
const params = pathParams.reduce((combinedParams, value) => {
const duplicate = combinedParams.some((param) => {
if ("$ref" in param || "$ref" in value) {
return false;
}
return param.in === value.in && param.name === value.name;
});
if (!duplicate) {
combinedParams.push(value);
}
return combinedParams;
}, operationParams.slice());
this.validateBodyParameters(params, operationId);
this.validatePathParameters(params, pathId, operationId);
this.validateParameterTypes(params, operation, operationId);
}
/**
* Validates body and formData parameters for the given operation.
*
*/
validateBodyParameters(params, operationId) {
const bodyParams = params.filter((param) => param.in === "body");
const formParams = params.filter((param) => param.in === "formData");
if (bodyParams.length > 1) {
this.reportError(`\`${operationId}\` has ${bodyParams.length} body parameters. Only one is allowed.`);
} else if (bodyParams.length > 0 && formParams.length > 0) {
this.reportError(
`\`${operationId}\` has \`body\` and \`formData\` parameters. Only one or the other is allowed.`
);
}
}
/**
* Validates path parameters for the given path.
*
*/
validatePathParameters(params, pathId, operationId) {
const placeholders = pathId.match(pathParameterTemplateRegExp) || [];
for (let i = 0; i < placeholders.length; i++) {
for (let j = i + 1; j < placeholders.length; j++) {
if (placeholders[i] === placeholders[j]) {
this.reportError(`\`${operationId}\` has multiple path placeholders named \`${placeholders[i]}\`.`);
}
}
}
params.filter((param) => param.in === "path").forEach((param) => {
if (param.required !== true) {
this.reportError(
`Path parameters cannot be optional. Set \`required=true\` for the \`${param.name}\` parameter at \`${operationId}\`.`
);
}
const match = placeholders.indexOf(`{${param.name}}`);
if (match === -1) {
this.reportError(
`\`${operationId}\` has a path parameter named \`${param.name}\`, but there is no corresponding \`{${param.name}}\` in the path string.`
);
}
placeholders.splice(match, 1);
});
if (placeholders.length > 0) {
const list = new Intl.ListFormat("en", { style: "long", type: "conjunction" }).format(
placeholders.map((placeholder) => `\`${placeholder}\``)
);
this.reportError(`\`${operationId}\` is missing path parameter(s) for ${list}.`);
}
}
/**
* Validates data types of parameters for the given operation.
*
*/
validateParameterTypes(params, operation, operationId) {
params.forEach((param) => {
const parameterId = `${operationId}/parameters/${param.name}`;
let schema;
switch (param.in) {
case "body":
schema = param.schema;
break;
case "formData":
schema = param;
break;
default:
schema = param;
}
this.validateSchema(schema, parameterId);
this.validateRequiredPropertiesExist(schema, parameterId);
if (schema.type === "file") {
const formData = /multipart\/(.*\+)?form-data/;
const urlEncoded = /application\/(.*\+)?x-www-form-urlencoded/;
const consumes = operation.consumes || this.api.consumes || [];
const hasValidMimeType = consumes.some((consume) => {
return formData.test(consume) || urlEncoded.test(consume);
});
if (!hasValidMimeType) {
this.reportError(
`\`${operationId}\` has a file parameter, so it must consume \`multipart/form-data\` or \`application/x-www-form-urlencoded\`.`
);
}
}
});
}
/**
* Validates the given response object.
*
*/
validateResponse(code, response, responseId) {
if (code !== "default") {
if (typeof code === "number" && (code < 100 || code > 599) || typeof code === "string" && (Number(code) < 100 || Number(code) > 599)) {
this.reportError(`\`${responseId}\` has an invalid response code: ${code}`);
}
}
Object.keys(response.headers || {}).forEach((headerName) => {
const header = response.headers[headerName];
const headerId = `${responseId}/headers/${headerName}`;
this.validateSchema(header, headerId);
});
if (response.schema) {
if ("$ref" in response.schema) {
return;
}
this.validateSchema(response.schema, `${responseId}/schema`);
}
}
/**
* Validates the given Swagger schema object.
*
*/
validateSchema(schema, schemaId) {
if (schema.type === "array" && !schema.items) {
this.reportError(`\`${schemaId}\` is an array, so it must include an \`items\` schema.`);
}
}
/**
* Validates that the declared properties of the given Swagger schema object actually exist.
*
*/
validateRequiredPropertiesExist(schema, schemaId) {
function collectProperties(schemaObj, props) {
if (schemaObj.properties) {
Object.keys(schemaObj.properties).forEach((property) => {
if (schemaObj.properties.hasOwnProperty(property)) {
props[property] = schemaObj.properties[property];
}
});
}
if (schemaObj.allOf) {
schemaObj.allOf.forEach((parent) => {
collectProperties(parent, props);
});
}
}
if (schema.required && Array.isArray(schema.required)) {
const props = {};
collectProperties(schema, props);
schema.required.forEach((requiredProperty) => {
if (!props[requiredProperty]) {
this.reportError(
`Property \`${requiredProperty}\` is listed as required but does not exist in \`${schemaId}\`.`
);
}
});
}
}
/**
* Checks the given parameter list for duplicates.
*
*/
checkForDuplicates(params, schemaId) {
for (let i = 0; i < params.length - 1; i++) {
const outer = params[i];
for (let j = i + 1; j < params.length; j++) {
const inner = params[j];
if (outer.name === inner.name && outer.in === inner.in) {
this.reportError(`Found multiple \`${outer.in}\` parameters named \`${outer.name}\` in \`${schemaId}\`.`);
}
}
}
}
};
// src/validators/spec.ts
function validateSpec(api, rules) {
let validator;
const specificationName = getSpecificationName(api);
if (isOpenAPI(api)) {
validator = new OpenAPISpecificationValidator(api, rules.openapi);
} else {
validator = new SwaggerSpecificationValidator(api);
}
validator.run();
if (!validator.errors.length) {
return {
valid: true,
warnings: validator.warnings,
specification: specificationName
};
}
return {
valid: false,
errors: validator.errors,
warnings: validator.warnings,
additionalErrors: 0,
specification: specificationName
};
}
// src/index.ts
async function parse(api, options) {
const args = normalizeArguments(api);
const parserOptions = convertOptionsForParser(options);
const parser = new $RefParser();
const schema = await parser.parse(args.path, args.schema, parserOptions);
repairSchema(schema, args.path);
return schema;
}
async function bundle(api, options) {
const args = normalizeArguments(api);
const parserOptions = convertOptionsForParser(options);
const parser = new $RefParser();
await parser.bundle(args.path, args.schema, parserOptions);
repairSchema(parser.schema, args.path);
return parser.schema;
}
async function dereference(api, options) {
const args = normalizeArguments(api);
const parserOptions = convertOptionsForParser(options);
const parser = new $RefParser();
await parser.dereference(args.path, args.schema, parserOptions);
repairSchema(parser.schema, args.path);
return parser.schema;
}
async function validate(api, options) {
const args = normalizeArguments(api);
const parserOptions = convertOptionsForParser(options);
let result;
const circular$RefOption = parserOptions.dereference.circular;
parserOptions.dereference.circular = "ignore";
const parser = new $RefParser();
try {
await parser.dereference(args.path, args.schema, parserOptions);
} catch (err) {
if (err instanceof MissingPointerError) {
return {
valid: false,
errors: [{ message: err.message }],
warnings: [],
additionalErrors: 0,
specification: null
};
}
throw err;
}
if (!isSwagger(parser.schema) && !isOpenAPI(parser.schema)) {
return {
valid: false,
errors: [{ message: "Supplied schema is not a valid API definition." }],
warnings: [],
additionalErrors: 0,
specification: null
};
}
parserOptions.dereference.circular = circular$RefOption;
result = validateSchema(parser.schema, options);
if (!result.valid) {
return result;
}
if (parser.$refs?.circular) {
if (circular$RefOption === true) {
dereferenceInternal(parser, parserOptions);
} else if (circular$RefOption === false) {
throw new ReferenceError(
"The API contains circular references but the validator is configured to not permit them."
);
}
}
const rules = options?.validate?.rules?.openapi;
result = validateSpec(parser.schema, {
openapi: {
"array-without-items": rules?.["array-without-items"] || "error",
"duplicate-non-request-body-parameters": rules?.["duplicate-non-request-body-parameters"] || "error",
"duplicate-operation-id": rules?.["duplicate-operation-id"] || "error",
"non-optional-path-parameters": rules?.["non-optional-path-parameters"] || "error",
"path-parameters-not-in-parameters": rules?.["path-parameters-not-in-parameters"] || "error",
"path-parameters-not-in-path": rules?.["path-parameters-not-in-path"] || "error"
}
});
return result;
}
function compileErrors(result) {
const specName = result.specification || "API definition";
const status = !result.valid ? "failed" : "succeeded, but with warnings";
const message = [`${specName} schema validation ${status}.`];
if (result.valid === false) {
if (result.errors.length) {
message.push(...result.errors.map((err) => err.message));
}
}
if (result.warnings.length) {
if (result.valid === false && result.errors.length) {
message.push("We have also found some additional warnings:");
}
message.push(...result.warnings.map((warn) => warn.message));
}
if (result.valid === false && result.additionalErrors > 0) {
message.push(
`Plus an additional ${result.additionalErrors} errors. Please resolve the above and re-run validation to see more.`
);
}
return message.join("\n\n");
}
export {
bundle,
compileErrors,
dereference,
parse,
validate
};
//# sourceMappingURL=index.js.map