/** * The envelope every endpoint answers with, and the error codes it carries. */ export const APIErrorCode = { BAD_REQUEST: "BAD_REQUEST", NOT_FOUND: "NOT_FOUND", SERVER_ERROR: "SERVER_ERROR", TIMEOUT: "TIMEOUT", UNAUTHORIZED: "UNAUTHORIZED", VALIDATION_ERROR: "VALIDATION_ERROR", } as const; export type APIErrorCode = typeof APIErrorCode[keyof typeof APIErrorCode]; export interface APIError { code: APIErrorCode; message: string; } export interface APISuccess { success: true; data: T; error?: never; } export interface APIFailure { success: false; data?: never; error: APIError; } export type APIResponse = APISuccess | APIFailure; export interface PaginatedData { items: T[]; hasMore: boolean; /** The whole list's size — only where it's shown (search results). */ total?: number; } /** * `T` as it arrives over JSON (HTTP, the socket, localStorage): every Date is * an ISO string. */ export type Wire = T extends Date ? string : T extends readonly (infer U)[] ? Wire[] : T extends object ? { [K in keyof T]: Wire } : T;