mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-20 02:27:45 +00:00
refactor(errors): centralize error handling infrastructure
- add errors/ directory with error types, handler, cleanup registry - extract claude-spawner.ts from auth modules - update imports across auth, ccs, web-server Phase 2 & 4: Error handling consolidation
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Centralized error handler for CCS CLI
|
||||
*
|
||||
* Provides unified error handling with:
|
||||
* - Consistent error formatting
|
||||
* - Exit code management
|
||||
* - Cleanup callback execution
|
||||
* - Debug mode support
|
||||
*/
|
||||
|
||||
import { ExitCode, EXIT_CODE_DESCRIPTIONS } from './exit-codes';
|
||||
import { isCCSError } from './error-types';
|
||||
import { runCleanup } from './cleanup-registry';
|
||||
|
||||
/**
|
||||
* Debug mode flag - set via CCS_DEBUG environment variable
|
||||
*/
|
||||
const isDebugMode = (): boolean => {
|
||||
return process.env['CCS_DEBUG'] === '1' || process.env['CCS_DEBUG'] === 'true';
|
||||
};
|
||||
|
||||
/**
|
||||
* Format error message for display
|
||||
* Uses ASCII-only formatting per codebase standards (no emojis)
|
||||
*/
|
||||
function formatErrorMessage(error: unknown): string {
|
||||
if (isCCSError(error)) {
|
||||
return `[X] ${error.message}`;
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return `[X] ${error.message}`;
|
||||
}
|
||||
|
||||
if (typeof error === 'string') {
|
||||
return `[X] ${error}`;
|
||||
}
|
||||
|
||||
return '[X] An unexpected error occurred';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exit code from error
|
||||
* CCSError types have their own codes, others default to GENERAL_ERROR
|
||||
*/
|
||||
function getExitCode(error: unknown): ExitCode {
|
||||
if (isCCSError(error)) {
|
||||
return error.code;
|
||||
}
|
||||
return ExitCode.GENERAL_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log debug information for an error
|
||||
* Only outputs when CCS_DEBUG is enabled
|
||||
*/
|
||||
function logDebugInfo(error: unknown, code: ExitCode): void {
|
||||
if (!isDebugMode()) return;
|
||||
|
||||
console.error('');
|
||||
console.error('[i] Debug information:');
|
||||
console.error(` Exit code: ${code} (${EXIT_CODE_DESCRIPTIONS[code] || 'Unknown'})`);
|
||||
|
||||
if (error instanceof Error) {
|
||||
console.error(` Error type: ${error.constructor.name}`);
|
||||
if (error.stack) {
|
||||
console.error(' Stack trace:');
|
||||
const stackLines = error.stack.split('\n').slice(1, 6);
|
||||
for (const line of stackLines) {
|
||||
console.error(` ${line.trim()}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log additional properties for CCSError types
|
||||
if (isCCSError(error)) {
|
||||
console.error(` Recoverable: ${error.recoverable}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Central error handler
|
||||
* Formats error, runs cleanup, and exits with appropriate code
|
||||
*
|
||||
* @param error - The error to handle
|
||||
* @returns never - Always exits the process
|
||||
*/
|
||||
export function handleError(error: unknown): never {
|
||||
// Run cleanup callbacks first
|
||||
runCleanup();
|
||||
|
||||
const code = getExitCode(error);
|
||||
const message = formatErrorMessage(error);
|
||||
|
||||
// Output error message to stderr
|
||||
console.error(message);
|
||||
|
||||
// Log debug info if enabled
|
||||
logDebugInfo(error, code);
|
||||
|
||||
// Exit with appropriate code
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit with an error message and code
|
||||
* Convenience function for simple error exits
|
||||
*
|
||||
* @param message - Error message to display
|
||||
* @param code - Exit code (defaults to GENERAL_ERROR)
|
||||
* @returns never - Always exits the process
|
||||
*/
|
||||
export function exitWithError(message: string, code: ExitCode = ExitCode.GENERAL_ERROR): never {
|
||||
runCleanup();
|
||||
console.error(`[X] ${message}`);
|
||||
|
||||
if (isDebugMode()) {
|
||||
console.error('');
|
||||
console.error(`[i] Exit code: ${code} (${EXIT_CODE_DESCRIPTIONS[code] || 'Unknown'})`);
|
||||
}
|
||||
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit with success
|
||||
* Runs cleanup and exits with SUCCESS code
|
||||
*
|
||||
* @param message - Optional success message to display
|
||||
* @returns never - Always exits the process
|
||||
*/
|
||||
export function exitWithSuccess(message?: string): never {
|
||||
runCleanup();
|
||||
if (message) {
|
||||
console.log(`[OK] ${message}`);
|
||||
}
|
||||
process.exit(ExitCode.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped error handler for async operations
|
||||
* Wraps an async function to catch and handle errors
|
||||
*/
|
||||
export function withErrorHandling<T extends unknown[]>(
|
||||
fn: (...args: T) => Promise<void>
|
||||
): (...args: T) => Promise<void> {
|
||||
return async (...args: T): Promise<void> => {
|
||||
try {
|
||||
await fn(...args);
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a condition, throwing a CCSError if false
|
||||
*/
|
||||
export function assertOrExit(
|
||||
condition: boolean,
|
||||
message: string,
|
||||
code: ExitCode = ExitCode.GENERAL_ERROR
|
||||
): asserts condition {
|
||||
if (!condition) {
|
||||
exitWithError(message, code);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user