# `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`, 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` — `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 ```ts 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: ```ts 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` — 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.