first commit
This commit is contained in:
105
docs/money/finance/currency/src/error-codes/error-codes.md
Normal file
105
docs/money/finance/currency/src/error-codes/error-codes.md
Normal 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.
|
||||
Reference in New Issue
Block a user