Files
2026-05-27 23:47:53 +01:00

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> — full SuaveplanError contract preserved
  • Optional providerName / status / upstreamMessage discriminants
  • Registered via defineCodes so 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 two PROVIDER_* codes are reachable from this package's CurrencyProviderRegistry

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 optional providerName: string, status: number, upstreamMessage: string.
  • class CurrencyProviderError extends SuaveplanError<CurrencyProviderErrorCode> — constructor (code, message, options?). Read-only providerName, status, upstreamMessage properties.

Implementation Notes

  • The discriminants are surfaced as top-level read-only properties (rather than fields on context) to make destructured try/catch handling 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 omit status and upstreamMessage. The optional fields are populated only when meaningful.
  • defineCodes enforces namespace prefix at runtime — CURRENCY_PROVIDER.X cannot collide with CURRENCY.X from @suaveplan/currency or any other package's registry.