embedia
Version:
Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys
393 lines (341 loc) • 14.3 kB
JavaScript
/**
* 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;