export type ErrorDetail = Record>; export class HttpError extends Error { readonly status: number; /** Machine-readable context for clients (e.g. which meter ran out); never secrets. */ detail: ErrorDetail | null; constructor(status: number, message: string, detail: ErrorDetail | null = null) { super(message); this.name = "HttpError"; this.status = status; this.detail = detail; } } export const HTTP_PAYMENT_REQUIRED = 402; export type PaymentReason = "plan_limit" | "spending_limit" | "paid_feature"; /** * 402 Payment Required: the action is out of the account's budget. Clients show an upsell for `meter` * (its price, Pro, prepaid credit) and link to the account's billing page. */ export function paymentRequired( message: string, detail: { meter: string; reason: PaymentReason; accountId?: string | null }, ): HttpError { return new HttpError(HTTP_PAYMENT_REQUIRED, message, { code: "payment_required", meter: detail.meter, reason: detail.reason, accountId: detail.accountId ?? null, }); } export function badRequest(message: string): HttpError { return new HttpError(400, message); } export function unauthorized(message = "Authentication required"): HttpError { return new HttpError(401, message); } export function forbidden(message = "You do not have permission to do that"): HttpError { return new HttpError(403, message); } export function notFoundError(message = "Not found"): HttpError { return new HttpError(404, message); } export function conflict(message: string): HttpError { return new HttpError(409, message); }