UNPKG

mcp-swagger-parser

Version:

Enterprise-grade OpenAPI/Swagger specification parser for Model Context Protocol (MCP) projects

259 lines 10.7 kB
"use strict"; /** * Core OpenAPI parser */ Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenAPIParser = void 0; exports.parseFromUrl = parseFromUrl; exports.parseFromFile = parseFromFile; exports.parseFromString = parseFromString; exports.validate = validate; const index_1 = require("../errors/index"); const url_parser_1 = require("../parsers/url-parser"); const file_parser_1 = require("../parsers/file-parser"); const text_parser_1 = require("../parsers/text-parser"); const validator_1 = require("./validator"); const normalizer_1 = require("./normalizer"); const version_detector_1 = require("./version-detector"); const swagger2openapi_converter_1 = require("./swagger2openapi-converter"); /** * Default parser configuration */ const DEFAULT_CONFIG = { validateSchema: true, resolveReferences: true, allowEmptyPaths: false, strictMode: false, customValidators: [], autoConvert: true, autoFix: true, swagger2Options: { patch: true, warnOnly: false, resolveInternal: false, targetVersion: '3.0.0', preserveRefs: true, warnProperty: 'x-s2o-warning', debug: false } }; /** * Main OpenAPI parser class */ class OpenAPIParser { constructor(config = {}) { this.config = { ...DEFAULT_CONFIG, ...config }; // Initialize sub-parsers this.urlParser = new url_parser_1.UrlParser(); this.fileParser = new file_parser_1.FileParser(); this.textParser = new text_parser_1.TextParser(); this.validator = new validator_1.Validator(this.config); this.normalizer = new normalizer_1.Normalizer(this.config); // Initialize Swagger 2.0 converter this.swagger2Converter = new swagger2openapi_converter_1.Swagger2OpenAPIConverter({ patch: this.config.swagger2Options.patch, warnOnly: this.config.swagger2Options.warnOnly, resolveInternal: this.config.swagger2Options.resolveInternal, targetVersion: this.config.swagger2Options.targetVersion, preserveRefs: this.config.swagger2Options.preserveRefs, warnProperty: this.config.swagger2Options.warnProperty, debug: this.config.swagger2Options.debug }); } /** * Parse OpenAPI specification from a URL */ async parseFromUrl(url, options) { try { const startTime = Date.now(); // Parse spec from URL const spec = await this.urlParser.parse(url, options); // Process the parsed spec return await this.processSpec(spec, { sourceType: 'url', sourceLocation: url, parsedAt: new Date(), parsingDuration: Date.now() - startTime }); } catch (error) { throw this.handleError(error, 'parseFromUrl', { url }); } } /** * Parse OpenAPI specification from a file */ async parseFromFile(filePath, options) { try { const startTime = Date.now(); // Parse spec from file const spec = await this.fileParser.parse(filePath, options); // Process the parsed spec return await this.processSpec(spec, { sourceType: 'file', sourceLocation: filePath, parsedAt: new Date(), parsingDuration: Date.now() - startTime }); } catch (error) { throw this.handleError(error, 'parseFromFile', { filePath }); } } /** * Parse OpenAPI specification from text content */ async parseFromString(content, options) { try { const startTime = Date.now(); const sourceInfo = options?.filename || 'string'; // Parse spec from text const spec = await this.textParser.parse(content, options); // Process the parsed spec return await this.processSpec(spec, { sourceType: 'text', sourceLocation: sourceInfo, parsedAt: new Date(), parsingDuration: Date.now() - startTime }); } catch (error) { throw this.handleError(error, 'parseFromString', { content: content.substring(0, 100) + '...' }); } } /** * Validate an OpenAPI specification */ async validate(spec) { return await this.validator.validate(spec); } /** * Process and validate the parsed specification */ async processSpec(spec, metadata) { let processedSpec = spec; let conversionResult = null; // Detect API specification version if (this.config.autoConvert) { const version = version_detector_1.VersionDetector.detect(spec); if (version === 'swagger2') { console.log('检测到 Swagger 2.0 规范,正在转换为 OpenAPI 3.0...'); try { conversionResult = await this.swagger2Converter.convert(spec); processedSpec = conversionResult.openapi; // Update metadata with conversion info metadata.conversionPerformed = true; metadata.originalVersion = conversionResult.originalVersion; metadata.targetVersion = conversionResult.targetVersion; metadata.conversionDuration = conversionResult.duration; metadata.patchesApplied = conversionResult.patches; metadata.conversionWarnings = conversionResult.warnings; console.log(`✓ 转换完成: ${metadata.originalVersion} -> ${metadata.targetVersion} (${metadata.conversionDuration}ms)`); if (conversionResult.patches && conversionResult.patches > 0) { console.log(`✓ 应用了 ${conversionResult.patches} 个补丁修复`); } if (conversionResult.warnings && conversionResult.warnings.length > 0) { console.log(`⚠ 转换过程中产生了 ${conversionResult.warnings.length} 个警告`); } } catch (error) { if (error instanceof index_1.Swagger2OpenAPIConversionError || error instanceof index_1.UnsupportedVersionError) { throw error; } const errorMessage = error instanceof Error ? error.message : 'Unknown error'; throw new index_1.Swagger2OpenAPIConversionError(`Failed to convert Swagger 2.0 specification: ${errorMessage}`, error instanceof Error ? error : new Error(String(error))); } } else if (version === 'unknown') { throw new index_1.UnsupportedVersionError(spec.swagger || spec.openapi || 'unknown'); } // If version is 'openapi3', no conversion needed } // Normalize the specification if (this.config.resolveReferences) { processedSpec = await this.normalizer.normalize(processedSpec); } // Validate the specification const validation = await this.validate(processedSpec); if (!validation.valid && this.config.strictMode) { throw new index_1.OpenAPIValidationError('Specification validation failed', validation.errors, validation.warnings); } // Generate complete metadata const completeMetadata = this.generateMetadata(processedSpec, metadata); // Create parsed spec const parsedSpec = { ...processedSpec, metadata: completeMetadata }; return { spec: parsedSpec, validation, metadata: completeMetadata }; } /** * Generate metadata for the parsed specification */ generateMetadata(spec, partial) { const pathCount = spec.paths ? Object.keys(spec.paths).length : 0; let operationCount = 0; if (spec.paths) { for (const pathItem of Object.values(spec.paths)) { const methods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace']; operationCount += methods.filter(method => pathItem[method]).length; } } const schemaCount = spec.components?.schemas ? Object.keys(spec.components.schemas).length : 0; const securitySchemeCount = spec.components?.securitySchemes ? Object.keys(spec.components.securitySchemes).length : 0; return { sourceType: partial.sourceType || 'text', sourceLocation: partial.sourceLocation || 'unknown', parsedAt: partial.parsedAt || new Date(), parsingDuration: partial.parsingDuration || 0, endpointCount: operationCount, pathCount, schemaCount, securitySchemeCount, openApiVersion: spec.openapi, parserVersion: '1.0.0', // TODO: Get from package.json // Enhanced metadata conversionPerformed: partial.conversionPerformed || false, originalVersion: partial.originalVersion, targetVersion: partial.targetVersion, conversionDuration: partial.conversionDuration, patchesApplied: partial.patchesApplied, conversionWarnings: partial.conversionWarnings }; } /** * Handle and transform errors */ handleError(error, method, context) { if (error instanceof index_1.OpenAPIParseError || error instanceof index_1.OpenAPIValidationError) { return error; } const message = error instanceof Error ? error.message : String(error); return new index_1.OpenAPIParseError(`Failed in ${method}: ${message}`, index_1.ERROR_CODES.PARSE_ERROR, undefined, error instanceof Error ? error : undefined); } } exports.OpenAPIParser = OpenAPIParser; /** * Convenience functions for quick parsing */ async function parseFromUrl(url, config) { const parser = new OpenAPIParser(config); return parser.parseFromUrl(url); } async function parseFromFile(filePath, config) { const parser = new OpenAPIParser(config); return parser.parseFromFile(filePath); } async function parseFromString(content, config) { const parser = new OpenAPIParser(config); return parser.parseFromString(content); } async function validate(spec, config) { const parser = new OpenAPIParser(config); return parser.validate(spec); } //# sourceMappingURL=parser.js.map