Files
skills/docs/money/finance/currency/src/error-codes/error-codes.md
2026-05-29 08:20:56 +01:00

4.5 KiB

error-codes — CURRENCY_CODES + CurrencyError

(no submodules)

Purpose

@suaveplan/currency raises only typed errors. This module owns the stable CURRENCY_CODES registry (built with defineCodes("CURRENCY", [...]) from @suaveplan/error) and the CurrencyError subclass that every public failure path constructs. Consumers programmatically dispatch on the namespaced strings ("CURRENCY.PROVIDER_UNAVAILABLE", "CURRENCY.RATE_UNAVAILABLE", ...) and inspect the from / to / providerName discriminants to recover from or report on failures.

The class extends SuaveplanError<CurrencyErrorCode>, so instanceof CurrencyError narrows err.code to the literal union, and instanceof SuaveplanError carries the full project contract: .code, .cause, .context, .timestamp, .traceId. Seven codes are declared: INVALID_CONFIG, INVALID_OPTIONS, PROVIDER_UNAVAILABLE, UNSUPPORTED_CURRENCY, RATE_UNAVAILABLE, TRIANGULATION_IMPOSSIBLE, ABORTED. Every code is reachable from a real production call site — registry pollution is banned by repo policy.

Features

  • Stable namespaced codes via defineCodes("CURRENCY", [...] as const) — the registry is frozen, the namespace is encoded into each value.
  • CurrencyError extends SuaveplanError<CurrencyErrorCode> — instanceof narrows .code to the literal-string union; cause chains arbitrary throwables.
  • from / to / providerName discriminants on every error instance for programmatic diagnostics (err.from === "USD", err.providerName === "ecb").
  • Co-located test pins every code to a real call site so the registry never drifts ahead of the implementation.

Usage: Basic Example

import { CurrencyConverter, CurrencyError, CURRENCY_CODES } from "@suaveplan/currency";

try {
    await converter.convert(money, "XYZ");
} catch (err) {
    if (err instanceof CurrencyError && err.code === CURRENCY_CODES.RATE_UNAVAILABLE) {
        console.error(`No rate for ${err.from} → ${err.to}`);
    } else {
        throw err;
    }
}

Usage: Advanced Example

Wrap an upstream provider failure while keeping the original throwable on cause so downstream loggers can still report it, and annotate the typed error with the provider that triggered the fallback chain failure:

import { CurrencyError, CURRENCY_CODES } from "@suaveplan/currency";

async function safeFetch(provider) {
    try {
        return await provider.fetchRates({ base: "USD" });
    } catch (err) {
        throw new CurrencyError(
            CURRENCY_CODES.PROVIDER_UNAVAILABLE,
            `provider ${provider.name} failed`,
            { cause: err, providerName: provider.name, from: "USD" }
        );
    }
}

API Reference

Export Kind Description
CURRENCY_CODES const (frozen) Registry of seven namespaced codes built via defineCodes.
CurrencyErrorCode type Literal-string union of every registry value.
CurrencyErrorOptions interface Extends SuaveplanErrorOptions with optional from, to, providerName.
CurrencyError class extends SuaveplanError<CurrencyErrorCode> — constructor takes (code, message, options?).

CurrencyError properties

  • code: CurrencyErrorCode — the namespaced error code (e.g. "CURRENCY.PROVIDER_UNAVAILABLE").
  • from?: CurrencyCode — the source currency the operation was attempted against.
  • to?: CurrencyCode — the target currency the operation was attempted against.
  • providerName?: string — the provider whose call produced the error.
  • cause?: unknown — chained underlying throwable.
  • timestamp: number — captured at construction time via the injected clock.
  • context?: ErrorContext — bag of structured fields propagated to the formatter.
  • traceId?: string — propagated W3C trace identifier when supplied.

Implementation Notes

The constructor is public so any module in the package can throw it; the optional discriminants are assigned only when set so JSON.stringify does not emit "from": undefined. CurrencyError is instanceof SuaveplanError and instanceof Error; tests pin both. The registry is created with as const so TypeScript infers literal types — CURRENCY_CODES.PROVIDER_UNAVAILABLE is "CURRENCY.PROVIDER_UNAVAILABLE", not string. Adding a new code requires appending to the array in error-codes.ts, wiring it into a real production raise site, and updating the registry test to assert the literal value.