UNPKG

embedia

Version:

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

329 lines (276 loc) 9.8 kB
const inquirer = require('inquirer'); const chalk = require('chalk'); const fs = require('fs-extra'); const path = require('path'); class ConflictResolver { async resolveConflicts(conflicts, interactive = true) { const resolutions = []; // Group conflicts by type const grouped = this.groupConflictsByType(conflicts); console.log(chalk.yellow(`\n⚠️ Found ${conflicts.length} potential conflicts\n`)); for (const [type, typeConflicts] of Object.entries(grouped)) { const resolver = this.getResolverForType(type); if (resolver) { const resolution = await resolver.call(this, typeConflicts, interactive); resolutions.push(resolution); } } return resolutions; } groupConflictsByType(conflicts) { return conflicts.reduce((grouped, conflict) => { if (!grouped[conflict.type]) { grouped[conflict.type] = []; } grouped[conflict.type].push(conflict); return grouped; }, {}); } getResolverForType(type) { const resolvers = { 'api_route_conflict': this.resolveAPIConflicts, 'existing_chat': this.resolveExistingChatConflicts, 'style_conflict': this.resolveStyleConflicts, 'dependency_conflict': this.resolveDependencyConflicts, 'env_var_conflict': this.resolveEnvVarConflicts, 'port_conflict': this.resolvePortConflicts }; return resolvers[type]; } async resolveAPIConflicts(conflicts, interactive) { if (!interactive) { // Automatic resolution - use alternative endpoint return { type: 'api_route', action: 'use_alternative', endpoint: '/api/embedia-chat', message: 'Using alternative API endpoint to avoid conflicts', resolved: true }; } console.log(chalk.yellow('\n⚠️ API Route Conflicts Detected:')); conflicts.forEach(c => { console.log(` - ${c.file}`); }); // Automatically use alternative endpoint to avoid conflicts console.log(chalk.green('\n✓ Automatically using alternative endpoint: /api/embedia-chat')); return { type: 'api_route', action: 'use_alternative', endpoint: '/api/embedia-chat', message: 'Using alternative API endpoint to avoid conflicts', resolved: true }; } async resolveExistingChatConflicts(conflicts, interactive) { if (!interactive) { return { type: 'existing_chat', action: 'proceed_with_warning', message: 'Proceeding with installation. Existing chat implementation detected.' }; } console.log(chalk.yellow('\n💬 Existing Chat Implementation Detected:')); const uniqueFiles = [...new Set(conflicts.map(c => c.file))].slice(0, 5); uniqueFiles.forEach(f => { console.log(` - ${f}`); }); if (conflicts.length > 5) { console.log(` ... and ${conflicts.length - 5} more files`); } // Automatically install alongside existing chat console.log(chalk.green('\n✓ Installing Embedia alongside existing chat implementation')); return { type: 'existing_chat', action: 'install_alongside', message: 'Embedia will be installed without affecting existing chat', resolved: true }; } async resolveStyleConflicts(conflicts, interactive) { // Style conflicts are usually warnings const suggestions = conflicts.map(conflict => ({ file: conflict.file, issue: conflict.issue, suggestion: this.getStyleSuggestion(conflict.issue) })); if (interactive && conflicts.some(c => c.severity === 'error')) { console.log(chalk.yellow('\n🎨 Style Conflicts:')); conflicts.forEach(c => { const icon = c.severity === 'error' ? '❌' : '⚠️'; console.log(` ${icon} ${c.file}: ${c.issue}`); }); const { acknowledge } = await inquirer.prompt([ { type: 'confirm', name: 'acknowledge', message: 'Proceed with installation? (You may need to adjust styles later)', default: true } ]); if (!acknowledge) { throw new Error('Installation aborted by user'); } } return { type: 'style_conflict', action: 'inform', suggestions: suggestions, message: 'Embedia uses scoped styles, but review these potential conflicts', resolved: true }; } async resolveDependencyConflicts(conflicts, interactive) { const criticalConflicts = conflicts.filter(c => c.severity === 'error'); if (criticalConflicts.length === 0) { return { type: 'dependency_conflict', action: 'none_required', resolved: true }; } console.log(chalk.red('\n📦 Dependency Conflicts:')); criticalConflicts.forEach(c => { console.log(` ❌ ${c.dependency}: ${c.currentVersion} (requires ${c.requiredVersion})`); }); if (!interactive) { throw new Error('Critical dependency conflicts require manual resolution'); } const { resolution } = await inquirer.prompt([ { type: 'list', name: 'resolution', message: 'Critical dependency conflicts detected', choices: [ { name: 'Update dependencies automatically', value: 'auto_update' }, { name: 'I\'ll update them manually', value: 'manual' }, { name: 'Abort installation', value: 'abort' } ] } ]); switch (resolution) { case 'auto_update': return { type: 'dependency_conflict', action: 'auto_update', updates: criticalConflicts.map(c => ({ package: c.dependency, version: c.requiredVersion })), resolved: true }; case 'manual': return { type: 'dependency_conflict', action: 'manual', instructions: this.getDependencyInstructions(criticalConflicts), resolved: false }; case 'abort': throw new Error('Installation aborted by user'); } } async resolveEnvVarConflicts(conflicts, interactive) { // Environment variable conflicts are usually informational return { type: 'env_var_conflict', action: 'use_existing', message: 'Using existing environment variables', variables: conflicts.map(c => c.variable), resolved: true }; } async resolvePortConflicts(conflicts, interactive) { // Port conflicts for dev server return { type: 'port_conflict', action: 'use_alternative_ports', message: 'Dev server will use alternative ports if defaults are in use', resolved: true }; } getStyleSuggestion(issue) { const suggestions = { 'Global fixed positioning': 'Review z-index values to ensure chat widget appears on top', 'Body overflow hidden': 'May prevent scrolling when chat is open', 'Very high z-index values': 'Ensure Embedia chat widget z-index (9999) is higher', 'Chat-related class names': 'Consider namespacing existing chat classes to avoid conflicts' }; return suggestions[issue] || 'Review and test after installation'; } getManualInstructions(type) { const instructions = { 'api_route': ` ## Manual API Route Setup 1. Create a new API route at a non-conflicting path (e.g., /api/embedia-chat) 2. Copy the handler from components/generated/embedia-chat/api/chat/route.js 3. Update the config.json to point to your new endpoint: { "apiEndpoint": "/api/embedia-chat" } `, 'existing_chat': ` ## Removing Existing Chat Implementation 1. Remove or rename existing chat components 2. Update imports throughout your application 3. Remove related API routes 4. Clean up any global styles 5. Re-run the Embedia installation ` }; return instructions[type] || 'Please resolve conflicts manually and re-run installation.'; } getReplacementInstructions(conflicts) { const files = [...new Set(conflicts.map(c => c.file))]; return ` ## Replacing Existing Chat Implementation ### Files to review and potentially remove: ${files.map(f => `- ${f}`).join('\n')} ### Steps: 1. Back up your existing chat implementation 2. Remove or comment out chat components 3. Remove related API endpoints 4. Update any pages/components that import the old chat 5. Re-run: npx embedia init --token=YOUR_TOKEN ### Backup Command: \`\`\`bash # Create backup of existing files ${files.map(f => `cp ${f} ${f}.backup`).join(' && ')} \`\`\` `; } getDependencyInstructions(conflicts) { return ` ## Manual Dependency Update Required ### Required updates: ${conflicts.map(c => `- ${c.dependency}: ${c.currentVersion}${c.requiredVersion}`).join('\n')} ### Update commands: \`\`\`bash # Using npm ${conflicts.map(c => `npm install ${c.dependency}@^${c.requiredVersion}`).join('\n')} # Using yarn ${conflicts.map(c => `yarn add ${c.dependency}@^${c.requiredVersion}`).join('\n')} \`\`\` ### After updating: 1. Test your existing application 2. Re-run: npx embedia init --token=YOUR_TOKEN `; } showDetailedConflicts(conflicts) { console.log(chalk.cyan('\n📋 Detailed Conflict Report:\n')); const byFile = conflicts.reduce((acc, c) => { if (!acc[c.file]) acc[c.file] = []; acc[c.file].push(c); return acc; }, {}); Object.entries(byFile).forEach(([file, fileConflicts]) => { console.log(chalk.bold(`\n${file}:`)); fileConflicts.forEach(c => { console.log(` - Type: ${c.indicatorType}`); console.log(` - Pattern: ${c.pattern}`); }); }); console.log(''); } } module.exports = ConflictResolver;