4.4 KiB
error-codes — Typed error registry for currency providers
(no submodules)
Purpose
Defines the stable CURRENCY_PROVIDER_CODES registry and the
CurrencyProviderError class every provider in
@suaveplan/currency-providers raises. The registry guarantees that
downstream code — most importantly CurrencyConverter's fallback chain —
can programmatically dispatch on the namespaced code string instead of
parsing free-form error messages. This is the suaveplan-quality replacement
for the upstream package's plain throw new Error(...) pattern, which
gave fallback logic no way to tell "Fixer returned 401 — try the next
provider" apart from "Fixer URL was malformed — give up".
CurrencyProviderError extends SuaveplanError<CurrencyProviderErrorCode>,
preserving the full SuaveplanError contract — cause chaining,
toWire() redaction, OTel span enrichment, and i18n-key dispatch — while
adding three optional discriminants relevant to provider failures:
providerName (which provider hit the error), status (the HTTP status
when the failure came from an HTTP response), and upstreamMessage (the
free-form error string the upstream API supplied).
Features
- Eight stable error codes covering every provider failure mode plus the slim-core registry dispatch: HTTP_ERROR, PARSE_ERROR, UPSTREAM_ERROR, UNSUPPORTED_BASE, MISSING_CONFIG, UNKNOWN_CURRENCY, PROVIDER_NOT_REGISTERED, PROVIDER_ALREADY_REGISTERED
CurrencyProviderError extends SuaveplanError<CurrencyProviderErrorCode>— fullSuaveplanErrorcontract preserved- Optional
providerName/status/upstreamMessagediscriminants - Registered via
defineCodesso the code values are typed as the namespaced strings ("CURRENCY_PROVIDER.HTTP_ERROR"etc.) - The first six codes are reachable from sibling
@suaveplan/currency-provider-<name>packages (where the concrete HTTP call sites live); the twoPROVIDER_*codes are reachable from this package'sCurrencyProviderRegistry
Usage: Basic Example
import {
CURRENCY_PROVIDER_CODES,
CurrencyProviderError,
} from "@suaveplan/currency-providers";
throw new CurrencyProviderError(
CURRENCY_PROVIDER_CODES.HTTP_ERROR,
"ECB API error: 503 Service Unavailable",
{ providerName: "ecb", status: 503 }
);
Usage: Advanced Example
Use the codes to drive fallback-chain decisions in a custom converter:
import {
CURRENCY_PROVIDER_CODES,
CurrencyProviderError,
} from "@suaveplan/currency-providers";
async function fetchWithFallback(providers: readonly CurrencyProvider[]) {
let lastError: unknown;
for (const provider of providers) {
try {
return await provider.fetchRates();
} catch (error) {
lastError = error;
if (!(error instanceof CurrencyProviderError)) throw error;
// 4xx auth / config errors are not worth retrying with the next
// provider — re-throw to bubble up
if (error.code === CURRENCY_PROVIDER_CODES.MISSING_CONFIG) throw error;
if (error.status === 401 || error.status === 403) throw error;
// anything else: try next provider
}
}
throw lastError;
}
API Reference
const CURRENCY_PROVIDER_CODES— frozen registry mapping each code name to its namespaced string ("CURRENCY_PROVIDER.HTTP_ERROR", etc.).type CurrencyProviderErrorCode— union of the eight namespaced strings.interface CurrencyProviderErrorOptions extends SuaveplanErrorOptions— adds optionalproviderName: string,status: number,upstreamMessage: string.class CurrencyProviderError extends SuaveplanError<CurrencyProviderErrorCode>— constructor(code, message, options?). Read-onlyproviderName,status,upstreamMessageproperties.
Implementation Notes
- The discriminants are surfaced as top-level read-only properties (rather
than fields on
context) to make destructuredtry/catchhandling ergonomic —if (error instanceof CurrencyProviderError && error.status === 429)reads naturally. - Codes that don't have a natural HTTP shape (e.g.
MISSING_CONFIG,UNKNOWN_CURRENCY) simply omitstatusandupstreamMessage. The optional fields are populated only when meaningful. defineCodesenforces namespace prefix at runtime —CURRENCY_PROVIDER.Xcannot collide withCURRENCY.Xfrom@suaveplan/currencyor any other package's registry.