UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

139 lines 5.48 kB
/** * Schema Parser * @description Parses TypeScript schema files to extract JSON schemas * * Purpose: Safely parse fields.generated.ts without using eval() * This parser extracts schema definitions from TypeScript code. * * @author Optimizely MCP Server * @version 1.0.0 */ import { readFileSync } from 'fs'; import { getLogger } from '../logging/Logger.js'; export class SchemaParser { logger = getLogger(); /** * Parse schemas from TypeScript file content */ parseSchemas(content) { this.logger.debug('Parsing schemas from TypeScript content'); try { // Find the schemas export const schemasMatch = content.match(/export\s+const\s+schemas\s*=\s*({[\s\S]*?})\s*(?:as\s+const\s*)?;/); if (!schemasMatch) { throw new Error('Could not find schemas export'); } let schemasText = schemasMatch[1]; // Remove TypeScript type annotations schemasText = this.removeTypeAnnotations(schemasText); // Convert to valid JSON schemasText = this.convertToJson(schemasText); // Parse the JSON const schemas = JSON.parse(schemasText); this.logger.info(`Successfully parsed ${Object.keys(schemas).length} schemas`); return schemas; } catch (error) { this.logger.error({ error }, 'Failed to parse schemas'); // Fallback: Try to extract individual schema objects return this.extractIndividualSchemas(content); } } /** * Remove TypeScript type annotations */ removeTypeAnnotations(text) { // Remove type annotations like ": OpenAPIV3.SchemaObject" text = text.replace(/:\s*OpenAPIV3\.\w+/g, ''); text = text.replace(/:\s*\w+\[\]/g, ''); // Remove array type annotations text = text.replace(/:\s*\w+/g, ''); // Remove simple type annotations text = text.replace(/as\s+const/g, ''); // Remove "as const" text = text.replace(/as\s+\w+/g, ''); // Remove other "as" type assertions return text; } /** * Convert TypeScript object to JSON */ convertToJson(text) { // Replace single quotes with double quotes text = text.replace(/'/g, '"'); // Handle unquoted property names text = text.replace(/(\w+):/g, '"$1":'); // Fix any double-quoted quotes text = text.replace(/""/g, '"'); // Handle trailing commas text = text.replace(/,(\s*[}\]])/g, '$1'); // Handle undefined values text = text.replace(/:\s*undefined/g, ': null'); // Handle template literals (convert to regular strings) text = text.replace(/`([^`]*)`/g, '"$1"'); // Handle multi-line strings text = text.replace(/\n/g, '\\n'); return text; } /** * Extract individual schemas as fallback */ extractIndividualSchemas(content) { const schemas = {}; // Pattern to match individual schema definitions const schemaPattern = /(\w+):\s*{[^}]*type:\s*["'](\w+)["'][^}]*}/g; let match; while ((match = schemaPattern.exec(content)) !== null) { const schemaName = match[1]; const schemaType = match[2]; // Create a basic schema object schemas[schemaName] = { type: schemaType, properties: {} }; } // Try to extract more detailed schemas const detailedPattern = /(\w+):\s*({[\s\S]*?^\s*})/gm; content.split('\n').forEach((line, index, lines) => { if (line.includes(': {') && line.trim().match(/^\w+:/)) { const schemaName = line.trim().split(':')[0].trim(); let braceCount = 1; let schemaText = '{'; for (let i = index + 1; i < lines.length && braceCount > 0; i++) { const currentLine = lines[i]; schemaText += '\n' + currentLine; // Count braces for (const char of currentLine) { if (char === '{') braceCount++; if (char === '}') braceCount--; } } if (braceCount === 0) { try { const cleanedSchema = this.removeTypeAnnotations(schemaText); const jsonSchema = this.convertToJson(cleanedSchema); schemas[schemaName] = JSON.parse(jsonSchema); } catch (e) { this.logger.warn({ error: e, schemaName }, `Failed to parse schema ${schemaName}`); } } } }); this.logger.info(`Extracted ${Object.keys(schemas).length} schemas using fallback method`); return schemas; } /** * Load and parse schemas from file */ loadSchemasFromFile(filePath) { try { const content = readFileSync(filePath, 'utf-8'); return this.parseSchemas(content); } catch (error) { this.logger.error({ error, filePath }, `Failed to load schemas from ${filePath}`); throw error; } } } export const schemaParser = new SchemaParser(); //# sourceMappingURL=SchemaParser.js.map