first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
# `error-codes` — Typed error registry for currency providers
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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:
```ts
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.