Initial Commit
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# `error-codes` — Notification-channel error registry
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`error-codes.ts` is the single source of truth for every error the `@suaveplan/notification-channel` package can throw. It registers a stable namespace `NOTIFICATION_CHANNEL` with eleven reason codes via `defineCodes("NOTIFICATION_CHANNEL", […])` from `@suaveplan/error`, and exposes a typed `SuaveplanError` subclass for each reason. The registry preserves the literal-typed union through the public surface so downstream consumers — loggers, problem+json responders, retry wrappers — can `instanceof`-narrow on a specific subclass or match on the `.code` discriminant without unsafe casts. The module is side-effect-imported from `src/index.ts` so the codes are part of the package's public ABI even for consumers that pull a subpath export.
|
||||
|
||||
## Features
|
||||
|
||||
- Eleven stable reason codes: `NOT_FOUND`, `CHANNEL_DISABLED`, `TEMPLATE_NOT_FOUND`, `RATE_LIMIT_EXCEEDED`, `ROUTING_FAILED`, `DELIVERY_FAILED`, `PROVIDER_ERROR`, `RATE_LIMITED`, `INVALID_RECIPIENT`, `CHANNEL_UNAVAILABLE`, `TIMEOUT`.
|
||||
- One typed subclass per code extending an abstract `NotificationChannelError` base.
|
||||
- Compile-time-frozen `NOTIFICATION_CHANNEL_CODES` record whose values are literal `"NOTIFICATION_CHANNEL.REASON"` strings.
|
||||
- Every constructor accepts the standard `SuaveplanErrorOptions` bag (`cause`, `context`, `traceId`, `clock`).
|
||||
- `RateLimitedError` carries an additional `retryAfterMs` field merged into `context`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
NOTIFICATION_CHANNEL_CODES,
|
||||
ChannelDisabledError,
|
||||
type NotificationChannelErrorCode,
|
||||
} from "@suaveplan/notification-channel/error-codes";
|
||||
|
||||
try {
|
||||
/* ... call the router ... */
|
||||
} catch (err) {
|
||||
if (err instanceof ChannelDisabledError) {
|
||||
const code: NotificationChannelErrorCode = err.code;
|
||||
void code; // "NOTIFICATION_CHANNEL.CHANNEL_DISABLED"
|
||||
}
|
||||
}
|
||||
|
||||
void NOTIFICATION_CHANNEL_CODES.RATE_LIMITED;
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { RateLimitedError } from "@suaveplan/notification-channel/error-codes";
|
||||
|
||||
throw new RateLimitedError({
|
||||
message: "notification.errors.rateLimited",
|
||||
retryAfterMs: 30_000,
|
||||
context: { provider: "twilio" },
|
||||
traceId: "trace-abc",
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `NOTIFICATION_CHANNEL_CODES` — frozen record `{ readonly [K in Reason]: "NOTIFICATION_CHANNEL.${K}" }`.
|
||||
- `NotificationChannelErrorCode` — discriminated union of all eleven literal codes.
|
||||
- `NotificationChannelError` — abstract base extending `SuaveplanError<NotificationChannelErrorCode>`.
|
||||
- Concrete subclasses: `NotificationNotFoundError`, `ChannelDisabledError`, `TemplateNotFoundError`, `RateLimitExceededError`, `RoutingFailedError`, `DeliveryFailedError`, `ProviderErrorError`, `RateLimitedError`, `InvalidRecipientError`, `ChannelUnavailableError`, `DeliveryTimeoutError`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The registry uses `defineCodes` from `@suaveplan/error` rather than a runtime `ErrorCodes` registry. `defineCodes` is purely type-level plus a frozen object, so it has no module-load side effect beyond the freeze, and the literal-typed union survives tree-shaking. `pickOpts` discards undefined fields explicitly to remain compatible with `exactOptionalPropertyTypes: true` in `tsconfig.base.json`. The split between "local lifecycle errors" and "upstream-provider errors" upstream had two parallel registries (`error-codes.ts` and `errors/notification-errors.ts`) — those are unified here so consumers see a single canonical code list. `RateLimitedError` is the one place the constructor accepts a typed sidecar (`retryAfterMs`) — it merges into `context` so downstream HTTP `Retry-After` mapping is straightforward.
|
||||
Reference in New Issue
Block a user