export {
parseOpenAPI,
parseFromUrl,
parseFromFile,
parseContent,
extractOperations,
} from './openapi.js';
export {
parsePostman,
parsePostmanFromUrl,
parsePostmanFromFile,
parsePostmanContent,
} from './postman.js';
export {
ensureUniqueOperationIds,
sortOperations,
simplifySchema,
isDangerousOperation,
filterByMethod,
filterDeprecated,
getOperationStats,
} from './normalize.js';
import { parseOpenAPI } from './openapi.js';
import { parsePostman } from './postman.js';
import type { NormalizedOperation, SpecFormat } from '../types/index.js';
import { log } from '../utils/logger.js';
/**
* Detect spec format from content
* Returns 'openapi' or 'postman' based on content structure
*/
export function detectFormat(content: string): SpecFormat {
try {
const parsed: unknown = JSON.parse(content);
// Type guard for object check
if (parsed && typeof parsed === 'object') {
const obj = parsed as Record<string, unknown>;
// Check for Postman collection schema (v2.0 uses _postman_schema, v2.1 uses schema)
const info = obj.info as Record<string, unknown> | undefined;
const schemaField = info?.schema ?? info?._postman_schema;
if (typeof schemaField === 'string' && (schemaField.includes('postman') || schemaField.includes('getpostman'))) {
return 'postman';
}
// Check for OpenAPI markers
if (obj.openapi || obj.swagger) {
return 'openapi';
}
}
// Default to OpenAPI for YAML content or unknown JSON
return 'openapi';
} catch {
// Not valid JSON, try YAML detection
if (content.includes('openapi:') || content.includes('swagger:')) {
return 'openapi';
}
// Default to OpenAPI
return 'openapi';
}
}
/**
* Parse spec from source, auto-detecting or using specified format
*/
export async function parseSpec(
source: { url?: string; file?: string; inline?: string },
format?: SpecFormat
): Promise<NormalizedOperation[]> {
// If format is specified, use it directly
if (format === 'postman') {
log.info('Parsing as Postman collection (format specified)');
return parsePostman(source);
}
if (format === 'openapi') {
log.info('Parsing as OpenAPI specification (format specified)');
return parseOpenAPI(source);
}
// Auto-detect format from content
let content: string | undefined;
if (source.inline) {
content = source.inline;
} else if (source.url) {
const response = await fetch(source.url);
if (!response.ok) {
throw new Error(`Failed to fetch spec: ${response.status} ${response.statusText}`);
}
content = await response.text();
// Update source to use inline to avoid double fetch
source = { inline: content };
} else if (source.file) {
const { readFile } = await import('node:fs/promises');
content = await readFile(source.file, 'utf-8');
source = { inline: content };
}
if (!content) {
throw new Error('No spec source provided');
}
const detectedFormat = detectFormat(content);
log.info('Auto-detected spec format', { format: detectedFormat });
if (detectedFormat === 'postman') {
return parsePostman(source);
}
return parseOpenAPI(source);
}