/** * Centralized Error Formatter Middleware * * Standard error response format: * { "error", "message", "field", "code" } */ import type { Req, Res, Next } from '../types/http'; const { ValidationError, formatError } = require('../utils/apiError.cts'); /** Extract field name from various validation error patterns */ function extractFieldFromError(message: any): string | null { if (!message) return null; const fieldMatch = message.match(/^(\w+)\s+(?:must be|is|are)\s+/i); if (fieldMatch) return fieldMatch[1]; const yearMonthMatch = message.match( /^(year|month|due_day|interest_rate|actual_amount|start_year|start_month|end_year|end_month)\s+must\b/i, ); if (yearMonthMatch) return yearMonthMatch[1]; const requiredMatch = message.match( /^(username|password|name|bill_id|category_id|due_day)\s+(?:is|are)\s+required/i, ); if (requiredMatch) return requiredMatch[1]; return null; } /** Convert a plain error message/string into a standardized error object */ function standardizeError(message: any, code = 'VALIDATION_ERROR', fieldOverride: any = null) { const field = fieldOverride || extractFieldFromError(message); return { error: code, message: typeof message === 'string' ? message : String(message), field: field || null, code: code, }; } /** Express middleware that ensures all error responses follow standard format */ function errorFormatter(req: Req, res: Res, next: Next): void { const originalJson = res.json; res.json = function (this: any, data: any) { if (data && typeof data === 'object' && !Array.isArray(data) && data.error && !data.success) { // Already standardized (machine-readable code + human message) — pass through if (typeof data.code === 'string' && data.code && typeof data.message === 'string') { return originalJson.call(this, data); } const message = typeof data.message === 'string' && data.message ? data.message : data.error; const code = typeof data.code === 'string' && data.code ? data.code : 'ERROR'; const standardized = standardizeError(message, code, data.field); return originalJson.call(this, standardized); } return originalJson.call(this, data); }; next(); } function formatValidationError(message: any, field?: any) { return standardizeError(message, 'VALIDATION_ERROR', field); } function formatNotFoundError(message: any, field?: any) { return standardizeError(message, 'NOT_FOUND', field); } function formatAuthError(message: any) { return standardizeError(message, 'AUTH_ERROR'); } function formatForbiddenError(message: any) { return standardizeError(message, 'FORBIDDEN'); } module.exports = { errorFormatter, standardizeError, formatValidationError, formatNotFoundError, formatAuthError, formatForbiddenError, extractFieldFromError, };