# Error codes — `@suaveplan/sms-providers` _(no submodules)_ ## Purpose Defines the stable, namespaced error-code registry `SMS_PROVIDERS_CODES` and the `SmsProvidersError` discriminated subclass used by every public surface of `@suaveplan/sms-providers`. Every failure path the package can take — a non-2xx HTTP response from a provider, an upstream request timeout, a JSON parse failure on a response body, a missing configuration value, a webhook signature mismatch, an invalid E.164 number, a registry name collision, a missing registry lookup — raises one of these eight codes so downstream loggers, problem+json responders, retry wrappers, and analytics pipelines see a consistent shape. Codes are minted through `@suaveplan/error`'s `defineCodes("SMS_PROVIDERS", […])` so they sit in a non-colliding namespace (`SMS_PROVIDERS.*`) distinct from `@suaveplan/sms`'s `SMS.*` codes. The `SmsProvidersError` class extends `SuaveplanError` so `instanceof SmsProvidersError` narrows `error.code` to the union; optional `provider`, `httpStatus`, `url`, and `bodyExcerpt` discriminants are carried so consumers can branch without re-parsing the message. ## Features - Eight codes covering every public-surface failure path. - Six factory helpers (`httpError`, `timeoutError`, `parseError`, `configMissing`, `signatureInvalid`, `invalidNumber`, `providerNotRegistered`, `providerAlreadyRegistered`). - Response bodies are truncated to 256 chars on the `bodyExcerpt` discriminant — bounded log volume regardless of payload size. - `cause` is preserved for any wrapped exception (timeout, parse failure) so root-cause traversal still works. ## Usage: Basic ```ts import { SmsProvidersError, SMS_PROVIDERS_CODES, httpError, } from "@suaveplan/sms-providers"; try { /* call provider */ } catch (err) { if ( err instanceof SmsProvidersError && err.code === SMS_PROVIDERS_CODES.HTTP_ERROR ) { console.warn(`HTTP ${err.httpStatus} from ${err.provider}`); } } const e = httpError("twilio", 422, "https://api.twilio.com/x", "{}", "invalid"); ``` ## Usage: Advanced ```ts import { SmsProvidersError, SMS_PROVIDERS_CODES, signatureInvalid, } from "@suaveplan/sms-providers"; // A custom webhook router that maps `SMS_PROVIDERS.SIGNATURE_INVALID` to a 401. export function verifyOrThrow( provider: string, ok: boolean, reason: string ): void { if (!ok) { throw signatureInvalid(provider, reason); } } // Branch on every code for a problem+json renderer. function statusFor(err: SmsProvidersError): number { switch (err.code) { case SMS_PROVIDERS_CODES.SIGNATURE_INVALID: return 401; case SMS_PROVIDERS_CODES.CONFIG_MISSING: return 500; case SMS_PROVIDERS_CODES.INVALID_NUMBER: return 400; case SMS_PROVIDERS_CODES.PROVIDER_NOT_REGISTERED: return 404; case SMS_PROVIDERS_CODES.PROVIDER_ALREADY_REGISTERED: return 409; case SMS_PROVIDERS_CODES.TIMEOUT: case SMS_PROVIDERS_CODES.HTTP_ERROR: case SMS_PROVIDERS_CODES.PARSE_ERROR: return err.httpStatus ?? 502; default: return 500; } } ``` ## API Reference | Symbol | Kind | Notes | |---|---|---| | `SMS_PROVIDERS_CODES` | const record | Frozen map produced by `defineCodes("SMS_PROVIDERS", […])` | | `SmsProvidersErrorCode` | type | Union of every code value | | `SmsProvidersError` | class | Extends `SuaveplanError` with optional `provider` / `httpStatus` / `url` / `bodyExcerpt` | | `httpError(provider, status, url, body, message?)` | factory | `HTTP_ERROR` | | `timeoutError(provider, url, timeoutMs, cause?)` | factory | `TIMEOUT` | | `parseError(provider, url, body, cause)` | factory | `PARSE_ERROR` | | `configMissing(provider, key)` | factory | `CONFIG_MISSING` | | `signatureInvalid(provider, reason)` | factory | `SIGNATURE_INVALID` | | `invalidNumber(provider, number)` | factory | `INVALID_NUMBER` | | `providerNotRegistered(name)` | factory | `PROVIDER_NOT_REGISTERED` | | `providerAlreadyRegistered(name)` | factory | `PROVIDER_ALREADY_REGISTERED` | ## Implementation Notes `bodyExcerpt` truncation keeps the elision sentinel `…` (U+2026) — distinguishable from a literal three-dot ASCII triplet so log scrapers can tell when truncation fired. The factories never construct errors from raw `Error`; they always go through `new SmsProvidersError(...)` so the prototype chain stays intact under transpiler-emitted `__extends` shims.