UNPKG

spaps

Version:

Sweet Potato Authentication & Payment Service CLI - Zero-config local development with built-in admin middleware and permission utilities

271 lines (250 loc) • 7.87 kB
/** * SPAPS CLI Error Handler * Provides verbose error messages with exact fix commands */ const chalk = require('chalk'); const ERROR_FIXES = { // Port errors EADDRINUSE: (error, context = {}) => ({ title: 'Port Already In Use', description: `Port ${context.port || 3300} is being used by another process`, causes: [ 'Another SPAPS server is already running', 'Another application is using this port', 'A previous SPAPS process didn\'t shut down cleanly' ], fixes: [ { command: `npx spaps local --port ${(parseInt(context.port || 3300) + 1)}`, description: 'Use a different port' }, { command: `lsof -ti:${context.port || 3300} | xargs kill -9`, description: 'Kill the process using this port (macOS/Linux)', platform: ['darwin', 'linux'] }, { command: `netstat -ano | findstr :${context.port || 3300}`, description: 'Find process using this port (Windows)', platform: ['win32'] }, { command: 'npx spaps local --port 0', description: 'Let the system choose an available port' } ] }), // Module errors MODULE_NOT_FOUND: (error, context = {}) => { const module = error.message.match(/Cannot find module '(.+?)'/)?.[1] || 'unknown'; return { title: 'Missing Dependencies', description: `Required module "${module}" is not installed`, causes: [ 'Dependencies were not installed', 'Package installation was interrupted', 'Node modules were deleted or corrupted' ], fixes: [ { command: 'npm install', description: 'Install all dependencies' }, { command: `npm install ${module}`, description: `Install missing module "${module}"` }, { command: 'rm -rf node_modules package-lock.json && npm install', description: 'Clean reinstall all dependencies' } ] }; }, // Permission errors EACCES: (error, context = {}) => ({ title: 'Permission Denied', description: 'Insufficient permissions to perform this operation', causes: [ 'Trying to bind to a privileged port (< 1024)', 'File system permissions issue', 'Node.js doesn\'t have necessary permissions' ], fixes: [ { command: 'npx spaps local --port 3000', description: 'Use a non-privileged port (>= 1024)' }, { command: `sudo npx spaps ${process.argv.slice(2).join(' ')}`, description: 'Run with elevated permissions (use with caution)', warning: true }, { command: 'ls -la ./', description: 'Check file permissions in current directory' } ] }), // Network errors ECONNREFUSED: (error, context = {}) => ({ title: 'Connection Refused', description: `Cannot connect to ${context.url || 'server'}`, causes: [ 'Server is not running', 'Server is running on a different port', 'Firewall is blocking the connection' ], fixes: [ { command: 'npx spaps local', description: 'Start the local SPAPS server' }, { command: 'curl http://localhost:3300/health', description: 'Check if server is responding' }, { command: 'netstat -an | grep LISTEN', description: 'List all listening ports' } ] }), // File system errors ENOENT: (error, context = {}) => { const file = error.path || context.file || 'file'; return { title: 'File or Directory Not Found', description: `Cannot find "${file}"`, causes: [ 'File does not exist', 'Wrong directory', 'File was deleted or moved' ], fixes: [ { command: 'pwd', description: 'Check current directory' }, { command: 'ls -la', description: 'List files in current directory' }, { command: 'npx spaps init', description: 'Initialize SPAPS in this directory' } ] }; }, // Generic errors DEFAULT: (error, context = {}) => ({ title: 'Unexpected Error', description: error.message || 'An unexpected error occurred', causes: [ 'Unknown issue', 'Corrupted installation', 'System-specific problem' ], fixes: [ { command: 'npm cache clean --force && npm install', description: 'Clear npm cache and reinstall' }, { command: 'node --version && npm --version', description: 'Check Node.js and npm versions' }, { command: 'npx spaps@latest --version', description: 'Check SPAPS version' }, { command: 'npm update spaps', description: 'Update SPAPS to latest version' } ] }) }; function formatError(error, context = {}) { const errorCode = error.code || 'DEFAULT'; const errorInfo = ERROR_FIXES[errorCode] ? ERROR_FIXES[errorCode](error, context) : ERROR_FIXES.DEFAULT(error, context); const output = []; // Title output.push(chalk.red.bold(`\n❌ ${errorInfo.title}`)); output.push(chalk.white(errorInfo.description)); // Possible causes if (errorInfo.causes && errorInfo.causes.length > 0) { output.push(chalk.yellow('\n📋 Possible causes:')); errorInfo.causes.forEach(cause => { output.push(chalk.gray(` • ${cause}`)); }); } // Fixes if (errorInfo.fixes && errorInfo.fixes.length > 0) { output.push(chalk.green('\n✨ How to fix:')); errorInfo.fixes.forEach((fix, index) => { // Filter by platform if specified if (fix.platform && !fix.platform.includes(process.platform)) { return; } output.push(chalk.cyan(`\n ${index + 1}. ${fix.description}`)); output.push(chalk.bgBlack.white(` $ ${fix.command}`)); if (fix.warning) { output.push(chalk.yellow(` ⚠️ Use with caution`)); } }); } // Additional help output.push(chalk.gray('\n📚 For more help:')); output.push(chalk.gray(' • Documentation: https://sweetpotato.dev/docs')); output.push(chalk.gray(' • GitHub Issues: https://github.com/yourusername/sweet-potato/issues')); output.push(chalk.gray(' • Discord: https://discord.gg/sweetpotato\n')); return output.join('\n'); } function formatJsonError(error, context = {}) { const errorCode = error.code || 'DEFAULT'; const errorInfo = ERROR_FIXES[errorCode] ? ERROR_FIXES[errorCode](error, context) : ERROR_FIXES.DEFAULT(error, context); return { success: false, error: { code: errorCode, title: errorInfo.title, message: errorInfo.description, causes: errorInfo.causes, fixes: errorInfo.fixes.map(fix => ({ command: fix.command, description: fix.description, warning: fix.warning || false })), stack: process.env.DEBUG ? error.stack : undefined }, help: { docs: 'https://sweetpotato.dev/docs', issues: 'https://github.com/yourusername/sweet-potato/issues', discord: 'https://discord.gg/sweetpotato' } }; } module.exports = { formatError, formatJsonError, handleError: (error, context = {}, options = {}) => { if (options.json) { console.log(JSON.stringify(formatJsonError(error, context), null, 2)); } else { console.error(formatError(error, context)); } // Exit with appropriate code const exitCode = error.code === 'EADDRINUSE' ? 98 : error.code === 'EACCES' ? 77 : error.code === 'ENOENT' ? 66 : error.code === 'MODULE_NOT_FOUND' ? 127 : 1; process.exit(exitCode); } };