#!/usr/bin/env node import fs from 'node:fs'; import path from 'node:path'; import yaml from 'yaml'; const ROOT = path.resolve('src/Api/StellaOps.Api.OpenApi'); const BASELINE = path.join(ROOT, 'baselines', 'stella-baseline.yaml'); const CURRENT = path.join(ROOT, 'stella.yaml'); const OUTPUT = path.join(ROOT, 'CHANGELOG.md'); function panic(message) { console.error(`[api:changelog] ${message}`); process.exit(1); } function loadSpec(file) { if (!fs.existsSync(file)) { panic(`Spec not found: ${file}`); } return yaml.parse(fs.readFileSync(file, 'utf8')); } function enumerateOps(spec) { const ops = new Map(); for (const [route, methods] of Object.entries(spec.paths || {})) { for (const [method, operation] of Object.entries(methods || {})) { const lower = method.toLowerCase(); if (!['get','post','put','delete','patch','head','options','trace'].includes(lower)) continue; const id = `${lower.toUpperCase()} ${route}`; ops.set(id, operation || {}); } } return ops; } function diffSpecs(oldSpec, newSpec) { const oldOps = enumerateOps(oldSpec); const newOps = enumerateOps(newSpec); const additive = []; const breaking = []; for (const id of newOps.keys()) { if (!oldOps.has(id)) { additive.push(id); } } for (const id of oldOps.keys()) { if (!newOps.has(id)) { breaking.push(id); } } return { additive: additive.sort(), breaking: breaking.sort() }; } function renderMarkdown(diff) { const lines = []; lines.push('# API Changelog'); lines.push(''); const date = new Date().toISOString(); lines.push(`Generated: ${date}`); lines.push(''); lines.push('## Additive Operations'); if (diff.additive.length === 0) { lines.push('- None'); } else { diff.additive.forEach((op) => lines.push(`- ${op}`)); } lines.push(''); lines.push('## Breaking Operations'); if (diff.breaking.length === 0) { lines.push('- None'); } else { diff.breaking.forEach((op) => lines.push(`- ${op}`)); } lines.push(''); return lines.join('\n'); } function main() { if (!fs.existsSync(BASELINE)) { console.log('[api:changelog] baseline missing; skipping'); return; } const diff = diffSpecs(loadSpec(BASELINE), loadSpec(CURRENT)); const markdown = renderMarkdown(diff); fs.writeFileSync(OUTPUT, markdown, 'utf8'); console.log(`[api:changelog] wrote changelog to ${OUTPUT}`); } main();