Files

80 lines
2.2 KiB
JavaScript

#!/usr/bin/env node
// Lightweight, dependency-free OpenAPI consistency check: valid YAML, every internal
// $ref resolves, and every path/schema referenced by a $ref actually exists. This is
// not a full OpenAPI 3.1 schema validator, but it catches the class of error that
// actually breaks generated clients and docs sites: dangling references and typos.
import fs from "node:fs";
import process from "node:process";
import yaml from "js-yaml";
const path = "docs/api/openapi.yaml";
const document = yaml.load(fs.readFileSync(path, "utf8"));
const errors = [];
function resolveRef(ref) {
if (!ref.startsWith("#/")) {
errors.push(`External or malformed $ref not supported: ${ref}`);
return;
}
const segments = ref
.slice(2)
.split("/")
.map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
let cursor = document;
for (const segment of segments) {
if (cursor === undefined || cursor === null || !(segment in cursor)) {
errors.push(`Dangling $ref: ${ref}`);
return;
}
cursor = cursor[segment];
}
}
function walk(node) {
if (Array.isArray(node)) {
for (const item of node) {
walk(item);
}
return;
}
if (node && typeof node === "object") {
for (const [key, value] of Object.entries(node)) {
if (key === "$ref" && typeof value === "string") {
resolveRef(value);
} else {
walk(value);
}
}
}
}
if (!document || typeof document !== "object") {
errors.push("Document did not parse to an object.");
} else {
if (document.openapi === undefined) {
errors.push("Missing top-level 'openapi' version field.");
}
if (!document.paths || Object.keys(document.paths).length === 0) {
errors.push("No paths defined.");
}
walk(document);
}
if (errors.length > 0) {
console.error("OpenAPI validation failed:");
for (const error of errors) {
console.error(`- ${error}`);
}
process.exit(1);
}
const pathCount = Object.keys(document.paths ?? {}).length;
const schemaCount = Object.keys(document.components?.schemas ?? {}).length;
console.log(`OpenAPI document is well-formed: ${pathCount} paths, ${schemaCount} schemas, all $ref pointers resolve.`);