#!/usr/bin/env node /** * Generates openapi.json from JSDoc annotations in route files. * Run: node scripts/generate-openapi.mjs * Output: public/openapi.json */ import swaggerJsdoc from 'swagger-jsdoc'; import { writeFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const options = { definition: { openapi: '3.0.3', info: { title: 'DocFast API', version: '1.0.0', description: `Convert HTML, Markdown, and URLs to pixel-perfect PDFs. Built-in invoice & receipt templates. ## Authentication All conversion and template endpoints require an API key via \`Authorization: Bearer \` or \`X-API-Key: \` header. ## Demo Endpoints Try the API without signing up! Demo endpoints are public (no API key needed) but rate-limited to 5 requests/hour per IP and produce watermarked PDFs. ## Rate Limits - Demo: 5 PDFs/hour per IP (watermarked) - Pro tier: 5,000 PDFs/month, 30 req/min All rate-limited endpoints return \`X-RateLimit-Limit\`, \`X-RateLimit-Remaining\`, and \`X-RateLimit-Reset\` headers. On \`429\`, a \`Retry-After\` header indicates seconds until the next allowed request. ## Getting Started 1. Try the demo at \`POST /v1/demo/html\` — no signup needed 2. Subscribe to Pro at [docfast.dev](https://docfast.dev/#pricing) for clean PDFs 3. Use your API key to convert documents`, contact: { name: 'DocFast', url: 'https://docfast.dev', email: 'support@docfast.dev' } }, servers: [ { url: 'https://docfast.dev', description: 'Production' } ], tags: [ { name: 'Demo', description: 'Try the API without signing up — watermarked PDFs, rate-limited' }, { name: 'Conversion', description: 'Convert HTML, Markdown, or URLs to PDF (requires API key)' }, { name: 'Templates', description: 'Built-in document templates' }, { name: 'Account', description: 'Key recovery and email management' }, { name: 'Billing', description: 'Stripe-powered subscription management' }, { name: 'System', description: 'Health checks and usage stats' } ], components: { securitySchemes: { BearerAuth: { type: 'http', scheme: 'bearer', description: 'API key as Bearer token' }, ApiKeyHeader: { type: 'apiKey', in: 'header', name: 'X-API-Key', description: 'API key via X-API-Key header' } }, headers: { 'X-RateLimit-Limit': { description: 'The maximum number of requests allowed in the current time window', schema: { type: 'integer', example: 30 } }, 'X-RateLimit-Remaining': { description: 'The number of requests remaining in the current time window', schema: { type: 'integer', example: 29 } }, 'X-RateLimit-Reset': { description: 'Unix timestamp (seconds since epoch) when the rate limit window resets', schema: { type: 'integer', example: 1679875200 } }, 'Retry-After': { description: 'Number of seconds to wait before retrying the request (returned on 429 responses)', schema: { type: 'integer', example: 60 } } }, schemas: { PdfOptions: { type: 'object', properties: { format: { type: 'string', enum: ['A4', 'Letter', 'Legal', 'A3', 'A5', 'Tabloid'], default: 'A4', description: 'Page size' }, landscape: { type: 'boolean', default: false, description: 'Landscape orientation' }, margin: { type: 'object', properties: { top: { type: 'string', description: 'Top margin (e.g. "10mm", "1in")', default: '0' }, right: { type: 'string', description: 'Right margin', default: '0' }, bottom: { type: 'string', description: 'Bottom margin', default: '0' }, left: { type: 'string', description: 'Left margin', default: '0' } }, description: 'Page margins' }, printBackground: { type: 'boolean', default: true, description: 'Print background colors and images' }, filename: { type: 'string', description: 'Custom filename for Content-Disposition header', default: 'document.pdf' } } }, Error: { type: 'object', properties: { error: { type: 'string', description: 'Error message' } }, required: ['error'] } } } }, apis: [ join(__dirname, '../src/routes/*.ts'), join(__dirname, '../src/openapi-extra.yaml') ] }; const spec = swaggerJsdoc(options); const outPath = join(__dirname, '../public/openapi.json'); writeFileSync(outPath, JSON.stringify(spec, null, 2)); console.log(`✅ Generated ${outPath} (${Object.keys(spec.paths || {}).length} paths)`);