import SwaggerParser from '@apidevtools/swagger-parser'; import type { OpenAPI, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; import type { ParsedSpec, OpenAPISpec } from './types.js'; const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] as const; export async function parseSpec(specPath: string, options?: { dereference?: boolean }): Promise<{ spec: OpenAPISpec; metadata: ParsedSpec; dereferenced: boolean; }> { const shouldDereference = options?.dereference !== false; let spec: OpenAPISpec; let dereferenced = false; if (shouldDereference) { try { spec = await SwaggerParser.dereference(specPath); dereferenced = true; } catch { // Fallback to parse without dereferencing if refs are broken spec = await SwaggerParser.parse(specPath); } } else { spec = await SwaggerParser.parse(specPath); } const metadata = extractMetadata(spec); return { spec, metadata, dereferenced }; } export async function bundleSpec(specPath: string): Promise { return SwaggerParser.bundle(specPath); } function extractMetadata(spec: OpenAPISpec): ParsedSpec { const version = getSpecVersion(spec); const info = spec.info; const paths = spec.paths || {}; const pathCount = Object.keys(paths).length; let operationCount = 0; const tagsSet = new Set(); for (const pathItem of Object.values(paths)) { if (!pathItem) continue; for (const method of HTTP_METHODS) { const operation = (pathItem as Record)[method] as OpenAPIV3.OperationObject | undefined; if (operation) { operationCount++; if (operation.tags) { operation.tags.forEach(tag => tagsSet.add(tag)); } } } } const schemaCount = getSchemaCount(spec); const servers = getServers(spec); return { version, title: info.title, description: info.description, servers, pathCount, schemaCount, operationCount, tags: Array.from(tagsSet).sort(), }; } function getSpecVersion(spec: OpenAPISpec): string { if ('openapi' in spec) { return `OpenAPI ${spec.openapi}`; } if ('swagger' in spec) { return `Swagger ${spec.swagger}`; } return 'Unknown'; } function getSchemaCount(spec: OpenAPISpec): number { if ('components' in spec && spec.components?.schemas) { return Object.keys(spec.components.schemas).length; } if ('definitions' in spec && spec.definitions) { return Object.keys(spec.definitions).length; } return 0; } function getServers(spec: OpenAPISpec): string[] { if ('servers' in spec && spec.servers) { return spec.servers.map(s => s.url); } if ('host' in spec) { const swagger2 = spec as { host?: string; basePath?: string; schemes?: string[] }; const scheme = swagger2.schemes?.[0] || 'https'; const host = swagger2.host || 'localhost'; const basePath = swagger2.basePath || ''; return [`${scheme}://${host}${basePath}`]; } return []; }