UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

631 lines (630 loc) 26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandConflictResolver = exports.ConflictSeverity = exports.ConflictType = exports.ConflictResolutionStrategy = void 0; exports.createConflictResolver = createConflictResolver; exports.getConflictSeverityColor = getConflictSeverityColor; exports.formatConflictType = formatConflictType; const events_1 = require("events"); const error_handler_1 = require("./error-handler"); // Conflict resolution strategies var ConflictResolutionStrategy; (function (ConflictResolutionStrategy) { ConflictResolutionStrategy["FIRST_WINS"] = "first-wins"; ConflictResolutionStrategy["LAST_WINS"] = "last-wins"; ConflictResolutionStrategy["PRIORITY"] = "priority"; ConflictResolutionStrategy["NAMESPACE"] = "namespace"; ConflictResolutionStrategy["INTERACTIVE"] = "interactive"; ConflictResolutionStrategy["AUTO_MERGE"] = "auto-merge"; ConflictResolutionStrategy["DISABLE_ALL"] = "disable-all"; })(ConflictResolutionStrategy || (exports.ConflictResolutionStrategy = ConflictResolutionStrategy = {})); // Conflict types var ConflictType; (function (ConflictType) { ConflictType["COMMAND_NAME"] = "command-name"; ConflictType["ALIAS"] = "alias"; ConflictType["SUBCOMMAND"] = "subcommand"; ConflictType["OPTION"] = "option"; ConflictType["DESCRIPTION"] = "description"; })(ConflictType || (exports.ConflictType = ConflictType = {})); // Conflict severity levels var ConflictSeverity; (function (ConflictSeverity) { ConflictSeverity["LOW"] = "low"; ConflictSeverity["MEDIUM"] = "medium"; ConflictSeverity["HIGH"] = "high"; ConflictSeverity["CRITICAL"] = "critical"; })(ConflictSeverity || (exports.ConflictSeverity = ConflictSeverity = {})); // Command conflict resolver class CommandConflictResolver extends events_1.EventEmitter { constructor(priorityConfig, resolutionPolicy) { super(); this.conflicts = new Map(); this.commands = new Map(); this.resolutionHistory = []; this.autoResolutionCount = 0; this.priorityConfig = { pluginPriorities: new Map([ ['core', 1000], ['system', 900], ['official', 800], ['verified', 700], ['community', 500], ['user', 300] ]), categoryPriorities: new Map([ ['system', 1000], ['core', 900], ['dev-tools', 800], ['productivity', 700], ['utility', 600], ['extension', 500] ]), defaultPriority: 100, userOverrides: new Map(), systemCommands: new Set(['help', 'version', 'init', 'config']), ...priorityConfig }; this.resolutionPolicy = { defaultStrategy: ConflictResolutionStrategy.PRIORITY, strategyByType: new Map([ [ConflictType.COMMAND_NAME, ConflictResolutionStrategy.PRIORITY], [ConflictType.ALIAS, ConflictResolutionStrategy.NAMESPACE], [ConflictType.SUBCOMMAND, ConflictResolutionStrategy.AUTO_MERGE], [ConflictType.OPTION, ConflictResolutionStrategy.PRIORITY] ]), allowAutoResolution: true, requireConfirmation: false, maxAutoResolutions: 10, preserveSystemCommands: true, namespacePrefix: 'plugin', ...resolutionPolicy }; } // Register commands for conflict detection registerCommands(commands) { this.commands.clear(); commands.forEach(cmd => { this.commands.set(cmd.id, cmd); }); this.detectConflicts(); } // Detect all conflicts detectConflicts() { this.conflicts.clear(); const detectedConflicts = []; // Detect command name conflicts detectedConflicts.push(...this.detectCommandNameConflicts()); // Detect alias conflicts detectedConflicts.push(...this.detectAliasConflicts()); // Detect option conflicts detectedConflicts.push(...this.detectOptionConflicts()); // Store conflicts detectedConflicts.forEach(conflict => { this.conflicts.set(conflict.id, conflict); }); this.emit('conflicts-detected', detectedConflicts); return detectedConflicts; } // Detect command name conflicts detectCommandNameConflicts() { const conflicts = []; const nameGroups = new Map(); // Group commands by name Array.from(this.commands.values()).forEach(cmd => { const name = cmd.definition.name; if (!nameGroups.has(name)) { nameGroups.set(name, []); } nameGroups.get(name).push(cmd); }); // Find conflicts nameGroups.forEach((commands, name) => { if (commands.length > 1) { const conflict = this.createCommandNameConflict(name, commands); conflicts.push(conflict); } }); return conflicts; } // Detect alias conflicts detectAliasConflicts() { const conflicts = []; const aliasMap = new Map(); // Collect all aliases Array.from(this.commands.values()).forEach(cmd => { if (cmd.definition.aliases) { cmd.definition.aliases.forEach(alias => { if (!aliasMap.has(alias)) { aliasMap.set(alias, []); } aliasMap.get(alias).push(cmd); }); } }); // Find conflicts aliasMap.forEach((commands, alias) => { if (commands.length > 1) { const conflict = this.createAliasConflict(alias, commands); conflicts.push(conflict); } }); return conflicts; } // Detect option conflicts within commands detectOptionConflicts() { const conflicts = []; Array.from(this.commands.values()).forEach(cmd => { if (cmd.definition.options) { const optionFlags = new Map(); cmd.definition.options.forEach(option => { const flag = this.normalizeFlag(option.flag); optionFlags.set(flag, (optionFlags.get(flag) || 0) + 1); }); optionFlags.forEach((count, flag) => { if (count > 1) { const conflict = this.createOptionConflict(cmd, flag); conflicts.push(conflict); } }); } }); return conflicts; } // Create command name conflict createCommandNameConflict(name, commands) { const severity = this.priorityConfig.systemCommands.has(name) ? ConflictSeverity.CRITICAL : ConflictSeverity.HIGH; const suggestions = this.generateConflictSuggestions(name, commands, ConflictType.COMMAND_NAME); return { id: `cmd_${name}_${Date.now()}`, type: ConflictType.COMMAND_NAME, severity, conflictingCommands: commands.map(c => c.id), conflictingPlugins: [...new Set(commands.map(c => c.pluginName))], conflictValue: name, description: `Multiple commands registered with name '${name}'`, suggestions, autoResolvable: severity !== ConflictSeverity.CRITICAL && suggestions.some(s => s.autoApplicable), priority: this.calculateConflictPriority(commands), detectedAt: Date.now(), resolved: false }; } // Create alias conflict createAliasConflict(alias, commands) { const suggestions = this.generateConflictSuggestions(alias, commands, ConflictType.ALIAS); return { id: `alias_${alias}_${Date.now()}`, type: ConflictType.ALIAS, severity: ConflictSeverity.MEDIUM, conflictingCommands: commands.map(c => c.id), conflictingPlugins: [...new Set(commands.map(c => c.pluginName))], conflictValue: alias, description: `Multiple commands registered with alias '${alias}'`, suggestions, autoResolvable: suggestions.some(s => s.autoApplicable), priority: this.calculateConflictPriority(commands), detectedAt: Date.now(), resolved: false }; } // Create option conflict createOptionConflict(command, flag) { return { id: `opt_${command.id}_${flag}_${Date.now()}`, type: ConflictType.OPTION, severity: ConflictSeverity.LOW, conflictingCommands: [command.id], conflictingPlugins: [command.pluginName], conflictValue: flag, description: `Duplicate option flag '${flag}' in command '${command.definition.name}'`, suggestions: [{ type: 'rename', description: 'Rename duplicate option flags', action: `Rename conflicting '${flag}' options`, impact: 'low', autoApplicable: true, confidence: 0.9 }], autoResolvable: true, priority: 1, detectedAt: Date.now(), resolved: false }; } // Generate conflict suggestions generateConflictSuggestions(conflictValue, commands, type) { const suggestions = []; // Priority-based resolution if (commands.length === 2) { const priorities = commands.map(cmd => this.calculateCommandPriority(cmd)); const maxPriority = Math.max(...priorities); const hasUniqueHighest = priorities.filter(p => p === maxPriority).length === 1; if (hasUniqueHighest) { suggestions.push({ type: 'priority', description: 'Resolve based on plugin priority', action: 'Keep highest priority command, disable others', impact: 'medium', autoApplicable: true, confidence: 0.8 }); } } // Namespace resolution suggestions.push({ type: 'namespace', description: 'Add plugin namespace prefix', action: `Rename to ${this.resolutionPolicy.namespacePrefix}:pluginname:${conflictValue}`, impact: 'low', autoApplicable: type !== ConflictType.COMMAND_NAME, confidence: 0.9 }); // Rename suggestions commands.forEach((cmd, index) => { if (index > 0) { // Keep first command as-is suggestions.push({ type: 'rename', description: `Rename ${cmd.pluginName} command`, action: `Rename to ${conflictValue}-${cmd.pluginName.toLowerCase()}`, impact: 'medium', autoApplicable: false, confidence: 0.7 }); } }); // Disable resolution if (commands.length > 2) { suggestions.push({ type: 'disable', description: 'Disable lower priority commands', action: 'Keep highest priority, disable others', impact: 'high', autoApplicable: false, confidence: 0.6 }); } return suggestions; } // Calculate command priority calculateCommandPriority(command) { let priority = this.resolutionPolicy.preserveSystemCommands && this.priorityConfig.systemCommands.has(command.definition.name) ? 10000 : 0; // User overrides have highest priority const userOverride = this.priorityConfig.userOverrides.get(command.id); if (userOverride !== undefined) { return priority + userOverride; } // Plugin-based priority const pluginPriority = this.priorityConfig.pluginPriorities.get(command.pluginName) || this.priorityConfig.defaultPriority; priority += pluginPriority; // Category-based priority if (command.definition.category) { const categoryPriority = this.priorityConfig.categoryPriorities.get(command.definition.category) || 0; priority += categoryPriority * 0.1; // Category has less weight than plugin } // Command-specific priority priority += command.definition.priority || 0; // Registration time (earlier = higher priority) priority += Math.max(0, 1000 - (Date.now() - command.registeredAt) / 1000); return priority; } // Calculate conflict priority calculateConflictPriority(commands) { const priorities = commands.map(cmd => this.calculateCommandPriority(cmd)); return Math.max(...priorities); } // Resolve conflict async resolveConflict(conflictId, strategy, options = {}) { const conflict = this.conflicts.get(conflictId); if (!conflict) { throw new error_handler_1.ValidationError(`Conflict '${conflictId}' not found`); } if (conflict.resolved) { throw new error_handler_1.ValidationError(`Conflict '${conflictId}' already resolved`); } const resolveStrategy = strategy || this.resolutionPolicy.strategyByType.get(conflict.type) || this.resolutionPolicy.defaultStrategy; if (this.resolutionPolicy.requireConfirmation && !options.userConfirmed && !options.dryRun) { throw new error_handler_1.ValidationError('User confirmation required for conflict resolution'); } if (this.autoResolutionCount >= this.resolutionPolicy.maxAutoResolutions && !options.userConfirmed) { throw new error_handler_1.ValidationError('Maximum auto-resolution limit reached'); } const resolution = { strategy: resolveStrategy, appliedAt: Date.now(), appliedBy: options.userConfirmed ? 'user' : 'auto', actions: [], success: false, errors: [], reversible: true }; this.emit('conflict-resolution-started', { conflictId, strategy: resolveStrategy }); try { switch (resolveStrategy) { case ConflictResolutionStrategy.PRIORITY: resolution.actions = await this.resolveBypriority(conflict, options.dryRun); break; case ConflictResolutionStrategy.NAMESPACE: resolution.actions = await this.resolveByNamespace(conflict, options.dryRun); break; case ConflictResolutionStrategy.FIRST_WINS: resolution.actions = await this.resolveByFirstWins(conflict, options.dryRun); break; case ConflictResolutionStrategy.LAST_WINS: resolution.actions = await this.resolveByLastWins(conflict, options.dryRun); break; case ConflictResolutionStrategy.DISABLE_ALL: resolution.actions = await this.resolveByDisableAll(conflict, options.dryRun); break; default: throw new error_handler_1.ValidationError(`Unsupported resolution strategy: ${resolveStrategy}`); } resolution.success = resolution.actions.every(action => action.applied); if (resolution.success && !options.dryRun) { conflict.resolved = true; conflict.resolution = resolution; this.autoResolutionCount++; } this.resolutionHistory.push(resolution); this.emit('conflict-resolved', { conflictId, resolution }); } catch (error) { resolution.errors.push(error instanceof Error ? error.message : String(error)); this.emit('conflict-resolution-failed', { conflictId, error }); } return resolution; } // Resolve by priority async resolveBypriority(conflict, dryRun) { const actions = []; const commands = conflict.conflictingCommands.map(id => this.commands.get(id)); // Sort by priority (highest first) const sortedCommands = commands.sort((a, b) => this.calculateCommandPriority(b) - this.calculateCommandPriority(a)); // Keep highest priority, disable others for (let i = 1; i < sortedCommands.length; i++) { const action = { type: 'disable', target: sortedCommands[i].id, details: { reason: 'lower priority in conflict resolution' }, applied: false }; if (!dryRun) { try { // In real implementation, would disable the command sortedCommands[i].isActive = false; action.applied = true; } catch (error) { action.error = error instanceof Error ? error.message : String(error); } } else { action.applied = true; // Assume success for dry run } actions.push(action); } return actions; } // Resolve by namespace async resolveByNamespace(conflict, dryRun) { const actions = []; const commands = conflict.conflictingCommands.map(id => this.commands.get(id)); for (let i = 1; i < commands.length; i++) { // Keep first command unchanged const cmd = commands[i]; const newName = `${this.resolutionPolicy.namespacePrefix}:${cmd.pluginName}:${conflict.conflictValue}`; const action = { type: 'namespace', target: cmd.id, details: { originalName: conflict.conflictValue, newName, prefix: `${this.resolutionPolicy.namespacePrefix}:${cmd.pluginName}` }, applied: false }; if (!dryRun) { try { // In real implementation, would rename the command cmd.definition.name = newName; action.applied = true; } catch (error) { action.error = error instanceof Error ? error.message : String(error); } } else { action.applied = true; } actions.push(action); } return actions; } // Resolve by first wins async resolveByFirstWins(conflict, dryRun) { const actions = []; const commands = conflict.conflictingCommands.map(id => this.commands.get(id)); // Sort by registration time (earliest first) const sortedCommands = commands.sort((a, b) => a.registeredAt - b.registeredAt); // Disable all except first for (let i = 1; i < sortedCommands.length; i++) { const action = { type: 'disable', target: sortedCommands[i].id, details: { reason: 'first-wins policy' }, applied: false }; if (!dryRun) { try { sortedCommands[i].isActive = false; action.applied = true; } catch (error) { action.error = error instanceof Error ? error.message : String(error); } } else { action.applied = true; } actions.push(action); } return actions; } // Resolve by last wins async resolveByLastWins(conflict, dryRun) { const actions = []; const commands = conflict.conflictingCommands.map(id => this.commands.get(id)); // Sort by registration time (latest first) const sortedCommands = commands.sort((a, b) => b.registeredAt - a.registeredAt); // Disable all except last (first in sorted array) for (let i = 1; i < sortedCommands.length; i++) { const action = { type: 'disable', target: sortedCommands[i].id, details: { reason: 'last-wins policy' }, applied: false }; if (!dryRun) { try { sortedCommands[i].isActive = false; action.applied = true; } catch (error) { action.error = error instanceof Error ? error.message : String(error); } } else { action.applied = true; } actions.push(action); } return actions; } // Resolve by disabling all async resolveByDisableAll(conflict, dryRun) { const actions = []; const commands = conflict.conflictingCommands.map(id => this.commands.get(id)); for (const cmd of commands) { const action = { type: 'disable', target: cmd.id, details: { reason: 'disable-all policy' }, applied: false }; if (!dryRun) { try { cmd.isActive = false; action.applied = true; } catch (error) { action.error = error instanceof Error ? error.message : String(error); } } else { action.applied = true; } actions.push(action); } return actions; } // Auto-resolve all resolvable conflicts async autoResolveConflicts() { if (!this.resolutionPolicy.allowAutoResolution) { throw new error_handler_1.ValidationError('Auto-resolution is disabled'); } const resolutions = []; const autoResolvableConflicts = Array.from(this.conflicts.values()) .filter(c => !c.resolved && c.autoResolvable) .sort((a, b) => b.priority - a.priority); // Resolve highest priority first for (const conflict of autoResolvableConflicts) { if (this.autoResolutionCount >= this.resolutionPolicy.maxAutoResolutions) { break; } try { const resolution = await this.resolveConflict(conflict.id); resolutions.push(resolution); } catch (error) { this.emit('auto-resolution-failed', { conflictId: conflict.id, error }); } } return resolutions; } // Normalize flag for comparison normalizeFlag(flag) { return flag.replace(/^-+/, '').toLowerCase(); } // Get all conflicts getConflicts() { return Array.from(this.conflicts.values()); } // Get unresolved conflicts getUnresolvedConflicts() { return Array.from(this.conflicts.values()).filter(c => !c.resolved); } // Get conflicts by type getConflictsByType(type) { return Array.from(this.conflicts.values()).filter(c => c.type === type); } // Get conflicts by severity getConflictsBySeverity(severity) { return Array.from(this.conflicts.values()).filter(c => c.severity === severity); } // Get resolution history getResolutionHistory() { return [...this.resolutionHistory]; } // Set user priority override setUserPriorityOverride(commandId, priority) { this.priorityConfig.userOverrides.set(commandId, priority); this.emit('priority-override-set', { commandId, priority }); } // Remove user priority override removeUserPriorityOverride(commandId) { this.priorityConfig.userOverrides.delete(commandId); this.emit('priority-override-removed', { commandId }); } // Get conflict statistics getStats() { const conflicts = Array.from(this.conflicts.values()); return { total: conflicts.length, resolved: conflicts.filter(c => c.resolved).length, unresolved: conflicts.filter(c => !c.resolved).length, autoResolvable: conflicts.filter(c => c.autoResolvable && !c.resolved).length, byType: Object.values(ConflictType).reduce((acc, type) => { acc[type] = conflicts.filter(c => c.type === type).length; return acc; }, {}), bySeverity: Object.values(ConflictSeverity).reduce((acc, severity) => { acc[severity] = conflicts.filter(c => c.severity === severity).length; return acc; }, {}), resolutionHistory: this.resolutionHistory.length, autoResolutionCount: this.autoResolutionCount, priorityOverrides: this.priorityConfig.userOverrides.size }; } } exports.CommandConflictResolver = CommandConflictResolver; // Utility functions function createConflictResolver(priorityConfig, resolutionPolicy) { return new CommandConflictResolver(priorityConfig, resolutionPolicy); } function getConflictSeverityColor(severity) { switch (severity) { case ConflictSeverity.CRITICAL: return 'red'; case ConflictSeverity.HIGH: return 'magenta'; case ConflictSeverity.MEDIUM: return 'yellow'; case ConflictSeverity.LOW: return 'blue'; default: return 'gray'; } } function formatConflictType(type) { return type.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' '); }