import { readFileSync } from 'node:fs'; import { parse } from 'yaml'; interface Endpoint { method: string; path: string; summary: string; tag: string; } function extractEndpoints(specPath: string): Endpoint[] { const spec = parse(readFileSync(specPath, 'utf-8')); const endpoints: Endpoint[] = []; for (const [path, methods] of Object.entries(spec.paths as Record>)) { for (const [method, details] of Object.entries(methods)) { if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) { const op = details as { summary?: string; tags?: string[] }; endpoints.push({ method: method.toUpperCase(), path, summary: op.summary ?? '', tag: op.tags?.[0] ?? 'Untagged', }); } } } return endpoints.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method)); } function endpointKey(e: Endpoint): string { return `${e.method} ${e.path}`; } const specPath = new URL('../openapi.yaml', import.meta.url).pathname; const manifestPath = new URL('../endpoint-manifest.json', import.meta.url).pathname; const current = extractEndpoints(specPath); let manifest: Endpoint[]; try { manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')); } catch { console.error('Error: endpoint-manifest.json not found. Run `npm run extract-endpoints > endpoint-manifest.json` first.'); process.exit(1); } const manifestKeys = new Set(manifest.map(endpointKey)); const currentKeys = new Set(current.map(endpointKey)); const added = current.filter((e) => !manifestKeys.has(endpointKey(e))); const removed = manifest.filter((e) => !currentKeys.has(endpointKey(e))); if (added.length === 0 && removed.length === 0) { console.log(`No endpoint drift detected. ${current.length} endpoints match the manifest.`); process.exit(0); } console.log('Endpoint drift detected!\n'); if (added.length > 0) { console.log(`New endpoints (${added.length}):`); for (const e of added) { console.log(` + ${e.method} ${e.path} — ${e.summary} [${e.tag}]`); } console.log(); } if (removed.length > 0) { console.log(`Removed endpoints (${removed.length}):`); for (const e of removed) { console.log(` - ${e.method} ${e.path} — ${e.summary} [${e.tag}]`); } console.log(); } console.log('Update endpoint-manifest.json: npm run extract-endpoints > endpoint-manifest.json'); process.exit(1);