feat: spec graph

This commit is contained in:
Patrick Balsiger
2025-10-31 10:41:43 +01:00
commit c0706c3a2b
8 changed files with 1726 additions and 0 deletions

80
server.js Normal file
View File

@@ -0,0 +1,80 @@
const express = require('express');
const path = require('path');
const yaml = require('js-yaml');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json({ limit: '2mb' }));
app.get('/api/health', (req, res) => {
res.json({ status: 'ok' });
});
function buildGraphFromPaths(pathsObject) {
const httpMethods = new Set(['get','put','post','delete','options','head','patch','trace']);
const nodeIdSet = new Set();
const linkKeySet = new Set();
const nodes = [];
const links = [];
const endpoints = [];
for (const pathKey of Object.keys(pathsObject || {})) {
const pathItem = pathsObject[pathKey] || {};
// Collect endpoints
for (const method of Object.keys(pathItem)) {
const lower = method.toLowerCase();
if (httpMethods.has(lower)) {
endpoints.push({ method: lower.toUpperCase(), path: pathKey });
}
}
// Build nodes/links from URL segments
const segments = pathKey.split('/').filter(Boolean);
if (segments.length === 0) continue;
// Ensure nodes
for (const seg of segments) {
if (!nodeIdSet.has(seg)) {
nodeIdSet.add(seg);
nodes.push({ id: seg });
}
}
// Create sequential links along the path
for (let i = 0; i < segments.length - 1; i++) {
const source = segments[i];
const target = segments[i + 1];
const key = `${source}|${target}`;
if (!linkKeySet.has(key)) {
linkKeySet.add(key);
links.push({ source, target });
}
}
}
return { nodes, links, endpoints };
}
app.post('/api/parse', (req, res) => {
try {
const { yaml: yamlText } = req.body || {};
if (typeof yamlText !== 'string' || yamlText.trim() === '') {
return res.status(400).json({ error: 'Missing yaml string in body' });
}
const spec = yaml.load(yamlText);
if (!spec || typeof spec !== 'object') {
return res.status(400).json({ error: 'Invalid YAML content' });
}
const pathsObject = spec.paths || {};
const graph = buildGraphFromPaths(pathsObject);
res.json({ graph, info: { title: spec.info && spec.info.title, version: spec.info && spec.info.version } });
} catch (err) {
res.status(400).json({ error: err.message || 'Failed to parse YAML' });
}
});
app.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
});