export type ErrorType = | "validation" | "not_found" | "permission_denied" | "unauthorized" | "conflict" | "business" | "database" | "bad_gateway" | "internal"; export interface ErrorDetails { [key: string]: unknown; } export abstract class ApplicationError extends Error { abstract readonly type: ErrorType; abstract readonly statusCode: number; readonly code: string; readonly details?: ErrorDetails; traceId?: string; constructor(message: string, code: string, details?: ErrorDetails) { super(message); this.name = this.constructor.name; this.code = code; this.details = details; } toJSON(): Record { return { success: false, error: { code: this.code, message: this.message, details: this.details, traceId: this.traceId, }, }; } } export class ValidationError extends ApplicationError { readonly type = "validation" as const; readonly statusCode = 400; constructor(message: string, details?: ErrorDetails) { super(message, "TEACHER_BFF_VALIDATION_ERROR", details); } } export class NotFoundError extends ApplicationError { readonly type = "not_found" as const; readonly statusCode = 404; constructor(resource: string, id: string) { super(`${resource} not found: ${id}`, "TEACHER_BFF_NOT_FOUND", { resource, id, }); } } export class PermissionDeniedError extends ApplicationError { readonly type = "permission_denied" as const; readonly statusCode = 403; constructor(permission: string) { super(`Permission denied: ${permission}`, "TEACHER_BFF_PERMISSION_DENIED", { permission, }); } } export class UnauthorizedError extends ApplicationError { readonly type = "unauthorized" as const; readonly statusCode = 401; constructor(message: string, details?: ErrorDetails) { super(message, "TEACHER_BFF_UNAUTHORIZED", details); } } export class ConflictError extends ApplicationError { readonly type = "conflict" as const; readonly statusCode = 409; constructor(message: string, details?: ErrorDetails) { super(message, "TEACHER_BFF_CONFLICT", details); } } export class BusinessError extends ApplicationError { readonly type = "business" as const; readonly statusCode = 422; constructor(message: string, details?: ErrorDetails) { super(message, "TEACHER_BFF_BUSINESS_ERROR", details); } } export class DatabaseError extends ApplicationError { readonly type = "database" as const; readonly statusCode = 500; constructor(message: string, details?: ErrorDetails) { super(message, "TEACHER_BFF_DATABASE_ERROR", details); } } export class BadGatewayError extends ApplicationError { readonly type = "bad_gateway" as const; readonly statusCode = 502; constructor(message: string, details?: ErrorDetails) { super(message, "TEACHER_BFF_BAD_GATEWAY", details); } } export class InternalError extends ApplicationError { readonly type = "internal" as const; readonly statusCode = 500; constructor(message: string, details?: ErrorDetails) { super(message, "TEACHER_BFF_INTERNAL_ERROR", details); } }