// Build the static API reference from the canonical Markdown, without runtime dependencies.
// Supports the headings, paragraphs, lists, links, rules, and fenced code used by this reference.
import { readFileSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
const source = new URL('../docs/API_Documentation.md', import.meta.url);
const output = new URL('../meshview/static/api.html', import.meta.url);
const markdown = readFileSync(source, 'utf8');
const themeStyles = readFileSync(new URL('../meshview/templates/theme-styles.html', import.meta.url), 'utf8');
const escape = text => text.replace(/[&<>"']/g, char => ({
'&':'&', '<':'<', '>':'>', '"':'"', "'":'''
}[char]));
// Mark prose fragments, keeping code samples, routes, and identifiers unchanged.
const copy = text => {
const value = text.trim().replace(/\s+/g, ' ');
if (!/[a-z]{2}/i.test(value)) return escape(text);
const leading = text.match(/^\s*/)[0], trailing = text.match(/\s*$/)[0];
return `${leading}${escape(value)}${trailing}`;
};
const slug = text => text.toLowerCase().replace(/`/g, '').replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
function inline(text) {
const tokens = /`([^`]+)`|\[([^\]]+)\]\(([^)]+)\)|\*\*([^*]+)\*\*/g;
let html = '', cursor = 0;
for (const match of text.matchAll(tokens)) {
html += copy(text.slice(cursor, match.index));
if (match[1] !== undefined) html += `${escape(match[1])}`;
else if (match[2] !== undefined && /^(#|\/|https?:\/\/)/.test(match[3])) {
html += `${copy(match[2])}`;
} else if (match[4] !== undefined) html += `${copy(match[4])}`;
else html += copy(match[0]);
cursor = match.index + match[0].length;
}
return html + copy(text.slice(cursor));
}
const endpoints = [];
const blocks = [];
const lines = markdown.split('\n');
let sectionOpen = false;
for (let i = 0; i < lines.length;) {
const line = lines[i];
if (!line.trim()) { i++; continue; }
if (line.startsWith('```')) {
const language = line.slice(3).trim(); const code = []; i++;
while (i < lines.length && !lines[i].startsWith('```')) code.push(lines[i++]);
if (i === lines.length) throw new Error('Unclosed code fence');
if (language === 'json') JSON.parse(code.join('\n'));
blocks.push(`
${escape(code.join('\n'))}`);
i++; continue;
}
const heading = line.match(/^(#{1,3})\s+(.+)$/);
if (heading) {
if (sectionOpen) { blocks.push(''); sectionOpen = false; }
const level = heading[1].length; const text = heading[2];
if (level === 3 && text.startsWith('GET ')) {
const id = slug(text); endpoints.push({text:text.replace(/`/g, ''),id});
blocks.push(`${inline(paragraph.join(' '))}
`); } if (sectionOpen) blocks.push('