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
JavaScript
/**
* 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);
}
};