Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
# Error codes — `@suaveplan/sms-providers`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<SmsProvidersErrorCode>` 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.