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}
`; });