import type { ZodType } from "zod"; /** Applikationsfel med HTTP-status och maskinläsbar kod. */ export class ApiError extends Error { constructor( public readonly statusCode: number, public readonly code: string, message: string, public readonly details?: unknown, ) { super(message); this.name = "ApiError"; } } export const errors = { badRequest: (msg: string, details?: unknown) => new ApiError(400, "BAD_REQUEST", msg, details), unauthorized: (msg = "Ogiltig eller saknad autentisering") => new ApiError(401, "UNAUTHORIZED", msg), forbidden: (msg = "Åtkomst nekad") => new ApiError(403, "FORBIDDEN", msg), notFound: (msg = "Resursen finns inte") => new ApiError(404, "NOT_FOUND", msg), conflict: (msg: string) => new ApiError(409, "CONFLICT", msg), quotaExceeded: (msg: string) => new ApiError(429, "QUOTA_EXCEEDED", msg), paymentRequired: (msg: string) => new ApiError(402, "PREMIUM_REQUIRED", msg), internal: (msg = "Internt fel") => new ApiError(500, "INTERNAL", msg), }; /** * Validera indata mot ett Zod-schema. Kastar 400 med detaljer vid fel. * Medvetet vald i stället för type-provider: färre rörliga delar i * major-versionsgränser, samma säkerhet (se docs/beslutslogg.md D-011). */ export function parse(schema: ZodType, data: unknown, what = "indata"): T { const result = schema.safeParse(data); if (!result.success) { throw new ApiError(400, "VALIDATION_ERROR", `Ogiltig ${what}`, result.error.issues); } return result.data; }