Initial Commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user