// Copyright (c) 2026 Gordon Bolton. MIT License. /** * Generates the merged OpenAPI spec by bootstrapping the Express app * and fetching the spec from /api/swagger.json. * * This ensures the generated spec matches exactly what users see in production. */ const { createApp } = require('../server/app.js'); const http = require('http'); const fs = require('fs'); const path = require('path'); const PORT = 34567; // Use a different port to avoid conflicts const OUTPUT_DIR = path.join(process.cwd(), 'dist'); const OUTPUT_FILE = path.join(OUTPUT_DIR, 'openapi-merged.json'); async function generateOpenApiSpec() { // Ensure output directory exists if (!fs.existsSync(OUTPUT_DIR)) { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); } console.log('Bootstrapping Express app in test mode...'); const app = createApp({ skipRateLimits: true }); return new Promise((resolve, reject) => { const server = http.createServer(app); server.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); // Fetch the merged spec const options = { hostname: 'localhost', port: PORT, path: '/api/swagger.json', method: 'GET' }; const req = http.request(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { try { const spec = JSON.parse(data); // Validate it's a proper OpenAPI spec if (!spec.openapi || !spec.info) { throw new Error('Invalid OpenAPI spec: missing openapi or info field'); } // Write to file fs.writeFileSync(OUTPUT_FILE, JSON.stringify(spec, null, 2)); console.log(`✓ OpenAPI spec written to ${OUTPUT_FILE}`); console.log(` Version: ${spec.openapi}`); console.log(` Title: ${spec.info.title}`); server.close(() => { resolve(); }); } catch (error) { console.error('Error processing OpenAPI spec:', error.message); server.close(() => { reject(error); }); } }); }); req.on('error', (error) => { console.error('Error fetching spec:', error.message); server.close(() => { reject(error); }); }); req.end(); }); server.on('error', (error) => { if (error.code === 'EADDRINUSE') { reject(new Error(`Port ${PORT} is already in use`)); } else { reject(error); } }); }); } // Run if executed directly if (require.main === module) { generateOpenApiSpec() .then(() => { console.log('OpenAPI spec generation complete'); process.exit(0); }) .catch((error) => { console.error('Failed to generate OpenAPI spec:', error); process.exit(1); }); } module.exports = { generateOpenApiSpec };