# `error-codes` _(no submodules)_ ## Purpose The error-codes module owns the canonical `SMS.*` registry for `@suaveplan/sms`. Every error surfaced by the package — invalid phone number, missing provider, exhausted retries, opted-out recipient, rate limit, scheduled-message cancellation, abort — carries one of the thirteen stable codes registered here. This keeps downstream consumers (loggers, problem+json responders, retry wrappers, dashboards) free from re-implementing the classifier. The thirteen codes are declared with the `defineCodes("SMS", [...])` helper, which produces a frozen `Namespace.Reason` string union. Each code has a dedicated leaf-error class extending the package-local abstract `BaseSmsError` (itself a thin `SuaveplanError` subclass), so consumers can `instanceof` the class as well as match on `.code`. ## Features - Thirteen `SMS.*` codes: `INVALID_NUMBER`, `INVALID_CONFIG`, `PROVIDER_NOT_CONFIGURED`, `PROVIDER_ERROR`, `SEND_FAILED`, `TEMPLATE_NOT_FOUND`, `RECIPIENT_OPTED_OUT`, `RATE_LIMITED`, `FORBIDDEN`, `CONFIG_MISSING`, `SCHEDULE_CANCELLED`, `ABORTED`, `RECEIPT_NOT_FOUND`. - One leaf class per code, all extending `BaseSmsError`. - `BaseOpts`-typed constructors that accept `message`, `context`, `cause`, `traceId`, with `pickOpts` stripping undefined keys so the `exactOptionalPropertyTypes` contract holds. - `SmsInvalidConfigError` merges Zod issues into `context.issues`. ## Usage: Basic ```ts import { SMS_CODES, SmsRateLimitedError, } from "@suaveplan/sms/error-codes"; try { throw new SmsRateLimitedError({ context: { phoneNumber: "+15551234567" }, }); } catch (err) { if (err instanceof SmsRateLimitedError) { console.log(err.code === SMS_CODES.RATE_LIMITED); } } ``` ## Usage: Advanced ```ts import { BaseSmsError, SmsInvalidConfigError, } from "@suaveplan/sms/error-codes"; function classifyAndLog(err: unknown): void { if (err instanceof SmsInvalidConfigError) { for (const issue of (err.context?.issues ?? []) as ReadonlyArray< Record >) { console.error("zod issue", issue); } return; } if (err instanceof BaseSmsError) { console.error(`${err.code}: ${err.message}`, err.context ?? {}); } } ``` ## API Reference - `SMS_CODES: Readonly>` — frozen registry returned from `defineCodes("SMS", [...])`. - `SmsCode = (typeof SMS_CODES)[keyof typeof SMS_CODES]` — union of every fully-qualified code. - `abstract class BaseSmsError extends SuaveplanError` — base class for every typed leaf error. - Leaf classes (each takes a `BaseOpts` argument and forwards a default messageKey for the i18n resolver): `SmsInvalidNumberError`, `SmsInvalidConfigError`, `SmsProviderNotConfiguredError`, `SmsProviderError`, `SmsSendFailedError`, `SmsTemplateNotFoundError`, `SmsRecipientOptedOutError`, `SmsRateLimitedError`, `SmsForbiddenError`, `SmsConfigMissingError`, `SmsScheduleCancelledError`, `SmsAbortedError`, `SmsReceiptNotFoundError`. ## Implementation Notes The leaf-class pattern (one class per code) was chosen over a single generic class so callers can `instanceof Xxx` to discriminate without inspecting `.code`. The `pickOpts` helper guards against the `exactOptionalPropertyTypes` contract — copying a `cause: undefined` into the super options table would fail to typecheck against `SuaveplanErrorOptions`. Codes are reserved at module load via `defineCodes` (which returns a frozen object) so the registry cannot drift across module reloads.