3.7 KiB
error-codes — Notification-channel error registry
(no submodules)
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
NotificationChannelErrorbase. - Compile-time-frozen
NOTIFICATION_CHANNEL_CODESrecord whose values are literal"NOTIFICATION_CHANNEL.REASON"strings. - Every constructor accepts the standard
SuaveplanErrorOptionsbag (cause,context,traceId,clock). RateLimitedErrorcarries an additionalretryAfterMsfield merged intocontext.
Usage: Basic
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
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 extendingSuaveplanError<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.