import { HttpException, HttpStatus } from "@nestjs/common"; import { KavenegarNormalized, safeJsonStringify, unwrapKavenegarTransportError, } from "./kavenegar-response"; export type SmsOperation = "verifyLookup" | "send"; export class SmsProviderException extends HttpException { constructor( public readonly operation: SmsOperation, public readonly meta: { receptor: string; template?: string; providerBody: unknown; normalized: KavenegarNormalized; }, ) { super( "SMS delivery could not be confirmed by the provider.", HttpStatus.BAD_GATEWAY, ); } } export class SmsTransportException extends HttpException { constructor( public readonly operation: SmsOperation, public readonly meta: { receptor: string; template?: string }, public readonly causeUnknown: unknown, ) { super( "SMS service failed before a provider response was received.", HttpStatus.BAD_GATEWAY, ); } } /** Single-line detail for logs (provider vs transport vs unexpected). */ export function describeSmsError(err: unknown): string { if (err instanceof SmsProviderException) { const n = err.meta.normalized; return [ `SmsProviderException(${err.operation})`, `receptor=${err.meta.receptor}`, err.meta.template ? `template=${err.meta.template}` : "", `providerStatus=${n.httpLikeStatus ?? "unknown"}`, `providerMessage=${n.message ?? "n/a"}`, `body=${n.raw}`, ] .filter(Boolean) .join(" "); } if (err instanceof SmsTransportException) { return [ `SmsTransportException(${err.operation})`, `receptor=${err.meta.receptor}`, err.meta.template ? `template=${err.meta.template}` : "", `detail=${safeJsonStringify(unwrapKavenegarTransportError(err.causeUnknown))}`, ] .filter(Boolean) .join(" "); } if (err instanceof Error) { return `${err.name}: ${err.message}`; } return safeJsonStringify(err); }