import { spawnSync } from "node:child_process"; import { resolve } from "node:path"; import openapiTS, { astToString, COMMENT_HEADER } from "openapi-typescript"; const requiredPaths = [ "/api/v1/demo/status", "/api/v1/demo/search", "/api/v1/retrieval/search", "/api/v1/chat/completions", "/api/v1/document-uploads", "/api/v1/document-uploads/{upload_id}/content", "/api/v1/document-uploads/{upload_id}/complete", "/api/v1/documents", "/api/v1/documents/{document_id}", "/api/v1/documents/{document_id}/review-bundle", "/api/v1/documents/{document_id}/review-decisions", "/api/v1/document-jobs/{job_id}", ]; async function schemaFromUrl(schemaUrl) { const response = await fetch(schemaUrl, { headers: { Accept: "application/json" }, signal: AbortSignal.timeout(10_000), }); if (!response.ok) throw new Error(`OpenAPI schema request failed with HTTP ${response.status}`); return response.json(); } function offlineSchema() { const backendDirectory = resolve("..", "backend"); const python = process.env.BACKEND_PYTHON ?? resolve(backendDirectory, ".venv/bin/python"); const result = spawnSync(python, ["-m", "app.tools.export_openapi"], { cwd: backendDirectory, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], }); if (result.status !== 0 || result.error || !result.stdout) { throw new Error("Offline OpenAPI export failed; run make backend-sync first"); } try { return JSON.parse(result.stdout); } catch { throw new Error("Offline OpenAPI export returned invalid JSON"); } } export async function loadOpenApiSchema() { const schema = process.env.OPENAPI_SCHEMA_URL ? await schemaFromUrl(process.env.OPENAPI_SCHEMA_URL) : offlineSchema(); if (typeof schema !== "object" || schema === null || !("paths" in schema)) { throw new Error("OpenAPI response does not contain a paths object"); } const paths = schema.paths; if (typeof paths !== "object" || paths === null) { throw new Error("OpenAPI paths value is invalid"); } for (const path of requiredPaths) { if (!(path in paths)) throw new Error(`Required API path is missing: ${path}`); } return schema; } export async function renderOpenApiTypes(schema) { const ast = await openapiTS(schema, { alphabetize: true, immutable: true }); return `${COMMENT_HEADER}${astToString(ast)}`; }