async function loadSpec() { const response = await fetch('/openapi.json'); const spec = await response.json(); if (!response.ok) { throw new Error(spec.error || 'Failed to load OpenAPI spec'); } return spec; } function methodPayload(operation) { const body = operation.requestBody?.content?.['application/json']?.schema; if (!body) return ''; return JSON.stringify(body.example || body.properties || body, null, 2); } function renderSpec(spec) { const root = document.getElementById('api-docs'); const sectionTemplate = document.getElementById('api-section-template'); const methodTemplate = document.getElementById('api-method-template'); root.innerHTML = ''; Object.entries(spec.paths || {}).forEach(([path, methods]) => { const section = sectionTemplate.content.firstElementChild.cloneNode(true); section.querySelector('.api-path').textContent = path; const methodsRoot = section.querySelector('.api-methods'); Object.entries(methods).forEach(([method, operation]) => { const node = methodTemplate.content.firstElementChild.cloneNode(true); node.querySelector('.api-verb').textContent = method.toUpperCase(); node.querySelector('.api-summary').textContent = operation.summary || ''; node.querySelector('.api-description').textContent = operation.description || ''; const body = methodPayload(operation); const bodyNode = node.querySelector('.api-body'); if (body) { bodyNode.textContent = body; } else { bodyNode.remove(); } methodsRoot.appendChild(node); }); root.appendChild(section); }); } loadSpec() .then(renderSpec) .catch((error) => { console.error(error); document.getElementById('api-docs').innerHTML = `
${error.message}
`; });