UNPKG

embedia

Version:

Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys

393 lines (341 loc) 14.3 kB
/** * Integration Orchestrator - Phase 2 Implementation * * ARCHITECTURAL PRINCIPLE: Smart Integration Strategy Selection * * This orchestrator coordinates different integration strategies based on * the binding contract and project characteristics. It selects the optimal * integrator and manages the integration process. */ const chalk = require('chalk'); const logger = require('../utils/logger'); const ReactComponentIntegrator = require('./ReactComponentIntegrator'); const WebComponentIntegrator = require('./WebComponentIntegrator'); class IntegrationOrchestrator { constructor(projectPath, contract) { this.projectPath = projectPath; this.contract = contract; } /** * Orchestrate the integration process based on contract requirements * @param {Array} generatedFiles - Generated files from server * @returns {Promise<Object>} Integration results */ async orchestrateIntegration(generatedFiles) { console.log(chalk.cyan('🎭 Orchestrating integration strategy selection...')); const results = { success: false, strategy: null, integrator: null, framework: this.contract.framework.name, componentType: this.contract.requirements.componentType, files: [], integrationFiles: [], errors: [], instructions: [], performance: { startTime: Date.now(), endTime: null, duration: null } }; try { // 1. Analyze generated code to understand what was actually provided const codeAnalysis = this.analyzeGeneratedCode(generatedFiles); console.log(chalk.gray(` Code Analysis: ${codeAnalysis.componentTypes.join(', ')}`)); // 2. Select optimal integration strategy const strategy = this.selectIntegrationStrategy(codeAnalysis, generatedFiles); results.strategy = strategy.name; results.integrator = strategy.integratorClass; console.log(chalk.cyan(`📋 Selected Strategy: ${strategy.name}`)); console.log(chalk.gray(` Rationale: ${strategy.rationale}`)); // 3. Execute integration with selected strategy const integrator = new strategy.integratorClass(this.projectPath, this.contract); const integrationResults = await integrator.integrate(generatedFiles); // 4. Merge results results.success = integrationResults.success; results.files = integrationResults.files || []; results.integrationFiles = integrationResults.integrationFiles || []; results.errors = integrationResults.errors || []; results.instructions = integrationResults.instructions || []; // 5. Add orchestrator-level instructions this.addOrchestratorInstructions(results, strategy, integrationResults); // 6. Performance tracking results.performance.endTime = Date.now(); results.performance.duration = results.performance.endTime - results.performance.startTime; if (results.success) { console.log(chalk.green(`✅ Integration orchestration completed successfully in ${results.performance.duration}ms`)); console.log(chalk.gray(` Files created: ${results.files.length}, Integration files: ${results.integrationFiles.length}`)); } else { console.log(chalk.yellow(`⚠️ Integration orchestration completed with ${results.errors.length} issues`)); } } catch (error) { results.errors.push({ type: 'orchestration_error', message: error.message, solution: 'Check integration orchestrator configuration' }); console.log(chalk.red(`❌ Integration orchestration failed: ${error.message}`)); } return results; } /** * Analyze generated code to understand available integration options * @param {Array} generatedFiles - Generated files from server * @returns {Object} Code analysis results */ analyzeGeneratedCode(generatedFiles) { const analysis = { componentTypes: [], hasReactComponents: false, hasWebComponents: false, hasAPIRoutes: false, fileTypes: { javascript: 0, typescript: 0, css: 0, html: 0, json: 0 }, frameworkSpecific: { nextjs: false, react: false, vue: false, angular: false } }; generatedFiles.forEach(file => { const content = file.content || ''; const path = file.path || ''; // Analyze file types if (path.endsWith('.js') || path.endsWith('.jsx')) { analysis.fileTypes.javascript++; } else if (path.endsWith('.ts') || path.endsWith('.tsx')) { analysis.fileTypes.typescript++; } else if (path.endsWith('.css')) { analysis.fileTypes.css++; } else if (path.endsWith('.html')) { analysis.fileTypes.html++; } else if (path.endsWith('.json')) { analysis.fileTypes.json++; } // Detect component types if (content.includes('React.') || content.includes('import React') || content.includes('useState') || content.includes('useEffect')) { analysis.hasReactComponents = true; analysis.componentTypes.push('react'); } // Detect web components - prioritize embedia-chatbot.js as the main web component bundle if (path.includes('embedia-chatbot.js') || content.includes('customElements.define') || content.includes('HTMLElement') || content.includes('web-component') || content.includes('class EmbediaChatbot extends HTMLElement')) { analysis.hasWebComponents = true; analysis.componentTypes.push('webcomponent'); } // Detect API routes if (path.includes('api/') || content.includes('export async function POST') || content.includes('export default function handler')) { analysis.hasAPIRoutes = true; } // Detect framework-specific code if (content.includes('next/') || content.includes('Next.js') || path.includes('next.config')) { analysis.frameworkSpecific.nextjs = true; } if (content.includes('vue') || content.includes('Vue') || path.includes('.vue')) { analysis.frameworkSpecific.vue = true; } if (content.includes('angular') || content.includes('Angular') || content.includes('@angular/')) { analysis.frameworkSpecific.angular = true; } }); // Remove duplicates analysis.componentTypes = [...new Set(analysis.componentTypes)]; return analysis; } /** * Select the optimal integration strategy based on contract and code analysis * @param {Object} codeAnalysis - Analysis of generated code * @param {Array} generatedFiles - Generated files from server * @returns {Object} Selected strategy */ selectIntegrationStrategy(codeAnalysis, generatedFiles) { const strategies = [ { name: 'react-component-integration', integratorClass: ReactComponentIntegrator, priority: 0, rationale: '', conditions: [] }, { name: 'web-component-integration', integratorClass: WebComponentIntegrator, priority: 0, rationale: '', conditions: [] } ]; // Strategy 1: React Component Integration const reactStrategy = strategies[0]; // Higher priority for React/Next.js projects with React components if ((this.contract.framework.name === 'nextjs' || this.contract.framework.name === 'react') && codeAnalysis.hasReactComponents) { reactStrategy.priority += 30; reactStrategy.conditions.push('Framework supports React components'); } // Higher priority if contract specifically requests React components if (this.contract.requirements.componentType === 'react') { reactStrategy.priority += 25; reactStrategy.conditions.push('Contract specifies React component type'); } // Higher priority for TypeScript projects (React integrator handles TS better) if (this.contract.language.typescript && codeAnalysis.hasReactComponents) { reactStrategy.priority += 15; reactStrategy.conditions.push('TypeScript project with React components'); } // Higher priority for Next.js with specific router types if (this.contract.framework.name === 'nextjs' && (this.contract.framework.router === 'app' || this.contract.framework.router === 'pages')) { reactStrategy.priority += 20; reactStrategy.conditions.push(`Next.js ${this.contract.framework.router} router detected`); } reactStrategy.rationale = `React integration (Priority: ${reactStrategy.priority}) - ${reactStrategy.conditions.join(', ')}`; // Strategy 2: Web Component Integration const webStrategy = strategies[1]; // Higher priority for universal compatibility if (this.contract.framework.name === 'static' || this.contract.framework.name === 'unknown') { webStrategy.priority += 40; webStrategy.conditions.push('Universal framework compatibility needed'); } // Higher priority if contract specifically requests web components if (this.contract.requirements.componentType === 'webcomponent') { webStrategy.priority += 35; webStrategy.conditions.push('Contract specifies web component type'); } // Higher priority if web components are available in generated code if (codeAnalysis.hasWebComponents) { webStrategy.priority += 25; webStrategy.conditions.push('Web components available in generated code'); } // Highest priority if we detect the embedia-chatbot.js bundle specifically const hasEmbediaChatbotBundle = generatedFiles.some(file => file.path.includes('embedia-chatbot.js') ); if (hasEmbediaChatbotBundle) { webStrategy.priority += 50; webStrategy.conditions.push('Universal embedia-chatbot.js bundle detected'); } // Higher priority for non-React frameworks if (!['nextjs', 'react'].includes(this.contract.framework.name)) { webStrategy.priority += 20; webStrategy.conditions.push('Non-React framework detected'); } // Fallback priority if no React components available if (!codeAnalysis.hasReactComponents && codeAnalysis.hasWebComponents) { webStrategy.priority += 30; webStrategy.conditions.push('Web components available, React components unavailable'); } webStrategy.rationale = `Web component integration (Priority: ${webStrategy.priority}) - ${webStrategy.conditions.join(', ')}`; // Select strategy with highest priority strategies.sort((a, b) => b.priority - a.priority); const selectedStrategy = strategies[0]; // Ensure we have a valid strategy if (selectedStrategy.priority === 0) { // Default to web component for maximum compatibility selectedStrategy.priority = 10; selectedStrategy.rationale = 'Default web component strategy for maximum compatibility'; selectedStrategy.integratorClass = WebComponentIntegrator; } return selectedStrategy; } /** * Add orchestrator-level instructions and guidance * @param {Object} results - Integration results * @param {Object} strategy - Selected strategy * @param {Object} integrationResults - Results from integrator */ addOrchestratorInstructions(results, strategy, integrationResults) { // Add strategy explanation at the beginning results.instructions.unshift( `🎭 Integration Strategy: ${strategy.name}`, `📋 ${strategy.rationale}`, '' ); // Add general next steps results.instructions.push( '', '📚 Next Steps:', '1. Install dependencies: npm install', '2. Configure your API key in .env.local', '3. Start your development server', '4. Test the chatbot functionality' ); // Add troubleshooting section if (results.errors.length > 0) { results.instructions.push( '', '🔧 Troubleshooting:', 'If you encounter issues, check:', '• Build errors with: npm run build', '• TypeScript errors with: npx tsc --noEmit', '• Browser console for runtime errors', '• Network tab for API request issues' ); } // Add framework-specific guidance if (this.contract.framework.name === 'nextjs') { results.instructions.push( '', '🚀 Next.js Specific:', '• The integration is optimized for your router type', '• Components will work with both SSR and client-side rendering', '• Check app/layout or pages/_app for integration points' ); } // Add performance recommendations results.instructions.push( '', '⚡ Performance Tips:', '• The chatbot loads asynchronously to avoid blocking page render', '• Components are lazy-loaded only when needed', '• Consider using loading states for better UX' ); } /** * Get integration strategy recommendations for a given contract * @param {Object} contract - Environment contract * @returns {Array} Array of strategy recommendations */ static getStrategyRecommendations(contract) { const recommendations = []; if (contract.framework.name === 'nextjs') { if (contract.requirements.componentType === 'react') { recommendations.push({ strategy: 'react-component-integration', confidence: 'high', reason: 'Next.js project with React component preference - optimal integration' }); } else { recommendations.push({ strategy: 'web-component-integration', confidence: 'medium', reason: 'Next.js project with web component - good universal compatibility' }); } } else if (contract.framework.name === 'react') { recommendations.push({ strategy: 'react-component-integration', confidence: 'high', reason: 'React project - native component integration' }); } else { recommendations.push({ strategy: 'web-component-integration', confidence: 'high', reason: 'Universal framework compatibility through web components' }); } return recommendations; } } module.exports = IntegrationOrchestrator;