// ── SDK v2 — Error types ─────────────────────────────────────────────────────── // // All SDK errors extend KaibanError so callers can do: // catch (err) { if (err instanceof KaibanError) ... } // // RFC 9457 problem details are preserved in `problem` when the API returns them. /** RFC 9457 problem detail shape returned by the API on errors. */ export interface ProblemDetail { type: string; title: string; status: number; detail?: string; instance?: string; [key: string]: unknown; } // ── Base ─────────────────────────────────────────────────────────────────────── /** Base class for all Kaiban SDK errors. */ export class KaibanError extends Error { readonly status: number; readonly problem: ProblemDetail | undefined; constructor(message: string, status: number, problem?: ProblemDetail) { super(message); this.name = "KaibanError"; this.status = status; this.problem = problem; } } // ── HTTP error subclasses ────────────────────────────────────────────────────── /** 400 — malformed request or invalid DSL query. */ export class BadRequestError extends KaibanError { constructor(problem?: ProblemDetail) { super(problem?.detail ?? "Bad request", 400, problem); this.name = "BadRequestError"; } } /** 401 — missing or invalid authentication credentials. */ export class UnauthorizedError extends KaibanError { constructor(problem?: ProblemDetail) { super(problem?.detail ?? "Unauthorized", 401, problem); this.name = "UnauthorizedError"; } } /** 403 — authenticated but insufficient permissions. */ export class ForbiddenError extends KaibanError { constructor(problem?: ProblemDetail) { super(problem?.detail ?? "Forbidden", 403, problem); this.name = "ForbiddenError"; } } /** 404 — resource not found. */ export class NotFoundError extends KaibanError { constructor(problem?: ProblemDetail) { super(problem?.detail ?? "Not found", 404, problem); this.name = "NotFoundError"; } } /** 409 — conflict (e.g. alias already in use). */ export class ConflictError extends KaibanError { constructor(problem?: ProblemDetail) { super(problem?.detail ?? "Conflict", 409, problem); this.name = "ConflictError"; } } /** 422 — validation error. Includes `errors` array from the API response. */ export class ValidationError extends KaibanError { readonly errors: Array<{ path: string[]; message: string }>; constructor(problem?: ProblemDetail & { errors?: Array<{ path: string[]; message: string }> }) { super(problem?.detail ?? "Validation error", 422, problem); this.name = "ValidationError"; this.errors = problem?.errors ?? []; } } /** 429 — rate limit exceeded. */ export class RateLimitError extends KaibanError { constructor(problem?: ProblemDetail) { super(problem?.detail ?? "Rate limit exceeded", 429, problem); this.name = "RateLimitError"; } } /** 500 — internal server error. */ export class ServerError extends KaibanError { constructor(problem?: ProblemDetail) { super(problem?.detail ?? "Internal server error", 500, problem); this.name = "ServerError"; } } /** 503 — service unavailable (e.g. cold start, DB not ready). */ export class ServiceUnavailableError extends KaibanError { constructor(problem?: ProblemDetail) { super(problem?.detail ?? "Service unavailable", 503, problem); this.name = "ServiceUnavailableError"; } } /** Request timed out before the server responded. */ export class TimeoutError extends KaibanError { constructor() { super("Request timed out", 0); this.name = "TimeoutError"; } } /** Request was cancelled via AbortSignal. */ export class AbortedError extends KaibanError { constructor() { super("Request aborted", 0); this.name = "AbortedError"; } } // ── Factory ──────────────────────────────────────────────────────────────────── /** Maps an HTTP status code + optional problem body to the correct error subclass. */ export function mapStatusToError(status: number, problem?: ProblemDetail): KaibanError { switch (status) { case 400: return new BadRequestError(problem); case 401: return new UnauthorizedError(problem); case 403: return new ForbiddenError(problem); case 404: return new NotFoundError(problem); case 409: return new ConflictError(problem); case 422: return new ValidationError(problem as any); case 429: return new RateLimitError(problem); case 503: return new ServiceUnavailableError(problem); default: if (status >= 500) return new ServerError(problem); return new KaibanError(problem?.detail ?? `HTTP ${status}`, status, problem); } }