i18ntk
Version:
i18n Tool Kit - Zero-dependency internationalization toolkit for setup, scanning, analysis, validation, auto translation, fixing, reporting, and runtime translation loading.
313 lines (264 loc) • 11.9 kB
JavaScript
/**
* I18NTK COMMAND ROUTER
*
* Central command routing system for i18n operations.
* Handles command execution, authentication, and completion management.
*/
const path = require('path');
const { t } = require('../../../utils/i18n-helper');
const cliHelper = require('../../../utils/cli-helper');
// Import command handlers
const InitCommand = require('./InitCommand');
const AnalyzeCommand = require('./AnalyzeCommand');
const ValidateCommand = require('./ValidateCommand');
const CompleteCommand = require('./CompleteCommand');
const SummaryCommand = require('./SummaryCommand');
const SizingCommand = require('./SizingCommand');
const UsageCommand = require('./UsageCommand');
const BackupCommand = require('./BackupCommand');
const DoctorCommand = require('./DoctorCommand');
const FixerCommand = require('./FixerCommand');
const ScannerCommand = require('./ScannerCommand');
const TranslateCommand = require('./TranslateCommand');
const ReportCommand = require('./ReportCommand');
class CommandRouter {
constructor(config = {}, ui = null, adminAuth = null) {
this.config = config;
this.ui = ui;
this.adminAuth = adminAuth;
this.prompt = null;
this.isNonInteractiveMode = false;
this.safeClose = null;
// Initialize command handlers
this.commandHandlers = {
'init': new InitCommand(config, ui),
'analyze': new AnalyzeCommand(config, ui),
'validate': new ValidateCommand(config, ui),
'complete': new CompleteCommand(config, ui),
'summary': new SummaryCommand(config, ui),
'sizing': new SizingCommand(config, ui),
'usage': new UsageCommand(config, ui),
'backup': new BackupCommand(config, ui),
'doctor': new DoctorCommand(config, ui),
'fix': new FixerCommand(config, ui),
'scanner': new ScannerCommand(config, ui),
'translate': new TranslateCommand(config, ui),
'report': new ReportCommand(config, ui)
};
}
/**
* Set runtime dependencies for interactive operations
*/
setRuntimeDependencies(prompt, isNonInteractiveMode, safeClose) {
this.prompt = prompt;
this.isNonInteractiveMode = isNonInteractiveMode;
this.safeClose = safeClose;
// Update command handlers with runtime dependencies
Object.values(this.commandHandlers).forEach(handler => {
if (typeof handler.setRuntimeDependencies === 'function') {
handler.setRuntimeDependencies(prompt, isNonInteractiveMode, safeClose);
}
});
}
/**
* Determine execution context based on options and environment
*/
getExecutionContext(options = {}) {
// Check if called from interactive menu
if (options.fromMenu === true) {
return { type: 'manager', source: 'interactive_menu' };
}
// Check if called from workflow/autorun
if (options.fromWorkflow === true) {
return { type: 'workflow', source: 'autorun_script' };
}
// Check if this is a direct command line execution
if (process.argv.some(arg => arg.startsWith('--command='))) {
return { type: 'direct', source: 'command_line' };
}
// Default to direct execution
return { type: 'direct', source: 'unknown' };
}
/**
* Check admin authentication for protected commands
*/
async checkAdminAuth(adminPin = null) {
if (!this.adminAuth) {
return true; // No auth service available, allow execution
}
const isRequired = await this.adminAuth.isAuthRequired();
if (!isRequired) {
return true;
}
let pin = adminPin;
if (!pin) {
console.log(t('adminCli.authRequired'));
pin = await cliHelper.promptPin(t('adminCli.enterPin'));
}
const isValid = await this.adminAuth.verifyPin(pin);
if (!isValid) {
console.log(t('adminCli.invalidPin'));
return false;
}
console.log(t('adminCli.authSuccess'));
return true;
}
/**
* Execute a command with proper routing and error handling
*/
async executeCommand(command, options = {}) {
console.log('\n' + t('menu.executingCommand', { command }));
// Enhanced context detection
const executionContext = this.getExecutionContext(options);
const isDirectCommand = executionContext.type === 'direct';
const isWorkflowExecution = executionContext.type === 'workflow';
const isManagerExecution = executionContext.type === 'manager';
// Ensure UI language is refreshed from settings for workflow and direct execution
if (isWorkflowExecution || isDirectCommand) {
if (this.ui && typeof this.ui.refreshLanguageFromSettings === 'function') {
this.ui.refreshLanguageFromSettings();
}
}
// Check admin authentication for protected commands
const authRequiredCommands = [
'init', 'analyze', 'validate', 'usage', 'scanner',
'complete', 'fix', 'sizing', 'workflow', 'status',
'delete', 'settings', 'debug', 'backup', 'doctor'
];
if (authRequiredCommands.includes(command)) {
const authPassed = await this.checkAdminAuth(options.adminPin || null);
if (!authPassed) {
if (!this.isNonInteractiveMode && !isDirectCommand && this.prompt) {
await this.prompt(t('menu.pressEnterToContinue'));
// Return to menu would be handled by caller
}
return;
}
}
try {
// Route command to appropriate handler
const result = await this.routeCommand(command, options, executionContext);
if (result && result.success === false) {
throw new Error(result.error || result.message || `${command} command failed`);
}
// Handle command completion based on execution context
console.log('\n' + t('operations.completed'));
if (isManagerExecution && !this.isNonInteractiveMode && this.prompt) {
// Interactive menu execution - return to menu
await this.prompt(t('menu.returnToMainMenu'));
// Menu return would be handled by caller
} else {
// Direct commands, non-interactive mode, or workflow execution - exit immediately
console.log(t('workflow.exitingCompleted'));
if (this.safeClose) this.safeClose();
process.exit(0);
}
} catch (error) {
console.error(t('common.errorExecutingCommand', { error: error.message }));
if (isManagerExecution && !this.isNonInteractiveMode && this.prompt) {
// Interactive menu execution - show error and return to menu
await this.prompt(t('menu.pressEnterToContinue'));
// Menu return would be handled by caller
} else if (isDirectCommand && !this.isNonInteractiveMode && this.prompt) {
// Direct command execution - show "enter to continue" and exit with error
await this.prompt(t('menu.pressEnterToContinue'));
if (this.safeClose) this.safeClose();
process.exit(1);
} else {
// Non-interactive mode or workflow execution - exit immediately with error
if (this.safeClose) this.safeClose();
process.exit(1);
}
}
}
/**
* Route command to the appropriate handler
*/
async routeCommand(command, options, executionContext) {
const isManagerExecution = executionContext.type === 'manager';
switch (command) {
case 'init':
return await this.commandHandlers.init.execute(options);
case 'analyze':
return await this.commandHandlers.analyze.execute(options);
case 'validate':
return await this.commandHandlers.validate.execute(options);
case 'complete':
return await this.commandHandlers.complete.execute(options);
case 'summary':
return await this.commandHandlers.summary.execute(options);
case 'sizing':
return await this.commandHandlers.sizing.execute(options);
case 'usage':
return await this.commandHandlers.usage.execute(options);
case 'backup':
return await this.commandHandlers.backup.execute(options);
case 'doctor':
return await this.commandHandlers.doctor.execute(options);
case 'fix':
return await this.commandHandlers.fix.execute(options);
case 'scanner':
return await this.commandHandlers.scanner.execute(options);
case 'translate':
return await this.commandHandlers.translate.execute(options);
case 'report':
return await this.commandHandlers.report.execute(options);
case 'debug':
console.log('Debug functionality is not available in this version.');
return { success: false, message: 'Debug not available' };
case 'help':
this.showHelp();
if (isManagerExecution && !this.isNonInteractiveMode && this.prompt) {
await this.prompt(t('menu.pressEnterToContinue'));
// Menu return would be handled by caller
} else {
console.log(t('workflow.exitingCompleted'));
if (this.safeClose) this.safeClose();
process.exit(0);
}
return { success: true, command: 'help' };
default:
console.log(t('menu.unknownCommand', { command }));
this.showHelp();
return { success: false, error: 'Unknown command' };
}
}
/**
* Show help information
*/
showHelp() {
const localT = this.ui && this.ui.t ? this.ui.t.bind(this.ui) : (key) => key;
console.log(t('help.usage'));
console.log(t('help.interactiveMode'));
console.log(t('help.initProject'));
console.log(t('help.analyzeTranslations'));
console.log(t('help.validateTranslations'));
console.log(t('help.checkUsage'));
console.log(t('help.showHelp'));
console.log(t('help.availableCommands'));
console.log(t('help.initCommand'));
console.log(t('help.analyzeCommand'));
console.log(t('help.validateCommand'));
console.log(t('help.usageCommand'));
console.log(t('help.sizingCommand'));
console.log(t('help.completeCommand'));
console.log(t('help.summaryCommand'));
console.log(t('help.debugCommand'));
console.log(t('help.scannerCommand'));
console.log(t('help.translateCommand'));
}
/**
* Get list of available commands
*/
getAvailableCommands() {
return Object.keys(this.commandHandlers);
}
/**
* Check if a command is available
*/
isCommandAvailable(command) {
return command in this.commandHandlers;
}
}
module.exports = CommandRouter;