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,105 @@
# `error-codes` — `CURRENCY_CODES` + `CurrencyError`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```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<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.