express-hale
Version:
š Interactive Express.js scaffold CLI with comprehensive error handling, TypeScript/JavaScript, database integrations, Git Flow, and development tools
447 lines (384 loc) ⢠12.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MiddlewareTemplates = void 0;
const base_renderer_1 = require("./base-renderer");
class MiddlewareTemplates extends base_renderer_1.BaseRenderer {
renderErrorHandlerMiddleware(language) {
if (language === 'typescript') {
return `import { Request, Response, NextFunction } from 'express';
// Custom Error Class
export class CustomError extends Error {
statusCode: number;
isOperational: boolean;
constructor(message: string, statusCode: number = 500) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// Async error wrapper
export const asyncHandler = (
fn: (req: Request, res: Response, next: NextFunction) => Promise<void>
) => {
return (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
// Global error handler
export const errorHandler = (
err: Error,
req: Request,
res: Response,
next: NextFunction
): void => {
let error = { ...err };
error.message = err.message;
// Log error
console.error('Error:', err);
// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = 'Resource not found';
error = new CustomError(message, 404);
}
// Mongoose duplicate key
if ((err as any).code === 11000) {
const message = 'Duplicate field value entered';
error = new CustomError(message, 400);
}
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values((err as any).errors).map((val: any) => val.message);
error = new CustomError(message.join(', '), 400);
}
res.status((error as CustomError).statusCode || 500).json({
status: 'error',
message: error.message || 'Server Error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
};
// 404 handler
export const notFoundHandler = (req: Request, res: Response, next: NextFunction): void => {
const error = new CustomError(\`Not found - \${req.originalUrl}\`, 404);
next(error);
};`;
}
else {
return `// Custom Error Class
class CustomError extends Error {
constructor(message, statusCode = 500) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// Async error wrapper
const asyncHandler = (fn) => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
// Global error handler
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
// Log error
console.error('Error:', err);
// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = 'Resource not found';
error = new CustomError(message, 404);
}
// Mongoose duplicate key
if (err.code === 11000) {
const message = 'Duplicate field value entered';
error = new CustomError(message, 400);
}
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(val => val.message);
error = new CustomError(message.join(', '), 400);
}
res.status(error.statusCode || 500).json({
status: 'error',
message: error.message || 'Server Error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
};
// 404 handler
const notFoundHandler = (req, res, next) => {
const error = new CustomError(\`Not found - \${req.originalUrl}\`, 404);
next(error);
};
module.exports = {
CustomError,
asyncHandler,
errorHandler,
notFoundHandler,
};`;
}
}
renderGracefulShutdownUtil(language) {
if (language === 'typescript') {
return `import { Server } from 'http';
export const setupGracefulShutdown = (server: Server): void => {
const gracefulShutdown = (signal: string) => {
console.log(\`\\nš” Received \${signal}. Starting graceful shutdown...\`);
server.close(() => {
console.log('š HTTP server closed.');
// Close database connections
// Add your database cleanup here
console.log('ā
Graceful shutdown completed.');
process.exit(0);
});
// Force close server after 30 seconds
setTimeout(() => {
console.error(
'ā Could not close connections in time, forcefully shutting down'
);
process.exit(1);
}, 30000);
};
// Listen for termination signals
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('ā Uncaught Exception:', error);
gracefulShutdown('uncaughtException');
});
// Handle unhandled promise rejections
process.on(
'unhandledRejection',
(reason, promise) => {
console.error('ā Unhandled Rejection at:', promise, 'reason:', reason);
gracefulShutdown('unhandledRejection');
}
);
};`;
}
else {
return `const setupGracefulShutdown = (server) => {
const gracefulShutdown = (signal) => {
console.log(\`\\nš” Received \${signal}. Starting graceful shutdown...\`);
server.close(() => {
console.log('š HTTP server closed.');
// Close database connections
// Add your database cleanup here
console.log('ā
Graceful shutdown completed.');
process.exit(0);
});
// Force close server after 30 seconds
setTimeout(() => {
console.error(
'ā Could not close connections in time, forcefully shutting down'
);
process.exit(1);
}, 30000);
};
// Listen for termination signals
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('ā Uncaught Exception:', error);
gracefulShutdown('uncaughtException');
});
// Handle unhandled promise rejections
process.on(
'unhandledRejection',
(reason, promise) => {
console.error('ā Unhandled Rejection at:', promise, 'reason:', reason);
gracefulShutdown('unhandledRejection');
}
);
};
module.exports = { setupGracefulShutdown };`;
}
}
renderErrorMonitoringConfig(language) {
if (language === 'typescript') {
return `export interface ErrorReport {
error: Error;
context: {
timestamp: string;
method?: string;
url?: string;
ip?: string;
userAgent?: string;
userId?: string;
requestId?: string;
};
level: 'error' | 'warning' | 'info';
tags?: string[];
}
interface RequestLike {
method?: string;
originalUrl?: string;
ip?: string;
get?: (header: string) => string | undefined;
user?: { id?: string };
id?: string;
headers?: { [key: string]: string | string[] | undefined };
}
export class ErrorMonitor {
private static instance: ErrorMonitor;
private isEnabled: boolean;
private constructor() {
this.isEnabled = process.env.NODE_ENV === 'production';
}
static getInstance(): ErrorMonitor {
if (!ErrorMonitor.instance) {
ErrorMonitor.instance = new ErrorMonitor();
}
return ErrorMonitor.instance;
}
async reportError(report: ErrorReport): Promise<void> {
if (!this.isEnabled) {
return;
}
try {
// Log to console (in production, this might go to a logging service)
console.error('Error Report:', JSON.stringify(report, null, 2));
// TODO: Send to external monitoring service
// await this.sendToMonitoringService(report);
} catch (monitoringError) {
console.error('Failed to report error:', monitoringError);
}
}
// Placeholder for external monitoring service integration
private async sendToMonitoringService(): Promise<void> {
// Example integrations:
// - Sentry: Sentry.captureException(report.error, { extra: report.context });
// - Bugsnag: Bugsnag.notify(report.error, report.context);
// - Custom webhook or API endpoint
console.log('Sending error report to monitoring service...');
}
// Create error report from Express error
createErrorReport(
error: Error,
req?: RequestLike,
level: 'error' | 'warning' | 'info' = 'error',
tags?: string[]
): ErrorReport {
return {
error,
context: {
timestamp: new Date().toISOString(),
method: req?.method,
url: req?.originalUrl,
ip: req?.ip,
userAgent: req?.get?.('User-Agent'),
userId: req?.user?.id,
requestId: req?.id || req?.headers?.['x-request-id'],
},
level,
tags,
};
}
}
// Global error monitoring setup
export const setupErrorMonitoring = (): void => {
const monitor = ErrorMonitor.getInstance();
// Monitor uncaught exceptions
process.on('uncaughtException', (error: Error) => {
const report = monitor.createErrorReport(
error,
null,
'error',
['uncaught-exception']
);
monitor.reportError(report);
});
// Monitor unhandled promise rejections
process.on('unhandledRejection', (reason: unknown) => {
const error = reason instanceof Error ? reason : new Error(String(reason));
const report = monitor.createErrorReport(
error,
null,
'error',
['unhandled-rejection']
);
monitor.reportError(report);
});
};`;
}
else {
return `class ErrorMonitor {
constructor() {
if (ErrorMonitor.instance) {
return ErrorMonitor.instance;
}
this.isEnabled = process.env.NODE_ENV === 'production';
ErrorMonitor.instance = this;
}
static getInstance() {
if (!ErrorMonitor.instance) {
ErrorMonitor.instance = new ErrorMonitor();
}
return ErrorMonitor.instance;
}
async reportError(report) {
if (!this.isEnabled) {
return;
}
try {
// Log to console (in production, this might go to a logging service)
console.error('Error Report:', JSON.stringify(report, null, 2));
// TODO: Send to external monitoring service
// await this.sendToMonitoringService(report);
} catch (monitoringError) {
console.error('Failed to report error:', monitoringError);
}
}
// Placeholder for external monitoring service integration
async sendToMonitoringService() {
// Example integrations:
// - Sentry: Sentry.captureException(report.error, { extra: report.context });
// - Bugsnag: Bugsnag.notify(report.error, report.context);
// - Custom webhook or API endpoint
console.log('Sending error report to monitoring service...');
}
// Create error report from Express error
createErrorReport(error, req = null, level = 'error', tags = []) {
return {
error,
context: {
timestamp: new Date().toISOString(),
method: req?.method,
url: req?.originalUrl,
ip: req?.ip,
userAgent: req?.get?.('User-Agent'),
userId: req?.user?.id,
requestId: req?.id || req?.headers?.['x-request-id']
},
level,
tags
};
}
}
// Global error monitoring setup
const setupErrorMonitoring = () => {
const monitor = ErrorMonitor.getInstance();
// Monitor uncaught exceptions
process.on('uncaughtException', (error) => {
const report = monitor.createErrorReport(error, null, 'error', ['uncaught-exception']);
monitor.reportError(report);
});
// Monitor unhandled promise rejections
process.on('unhandledRejection', (reason) => {
const error = reason instanceof Error ? reason : new Error(String(reason));
const report = monitor.createErrorReport(error, null, 'error', ['unhandled-rejection']);
monitor.reportError(report);
});
};
module.exports = {
ErrorMonitor,
setupErrorMonitoring,
};`;
}
}
}
exports.MiddlewareTemplates = MiddlewareTemplates;
//# sourceMappingURL=middleware-templates.js.map