first commit
This commit is contained in:
106
docs/money/finance/currency/src/schemas/schemas.md
Normal file
106
docs/money/finance/currency/src/schemas/schemas.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# `schemas` — Zod boundary for `@suaveplan/currency`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The schemas module is the package's input-validation boundary. Every public
|
||||
options bundle that crosses into a `CurrencyConverter` method or a reference
|
||||
provider is parsed against a Zod schema first; failures raise a typed
|
||||
`CurrencyError` with code `CURRENCY.INVALID_CONFIG` (constructor arguments,
|
||||
virtual-currency entries) or `CURRENCY.INVALID_OPTIONS` (per-call options,
|
||||
`RateFetchOptions`). The Zod issue array is attached to
|
||||
`error.context.issues` so downstream loggers and APIs can surface field-level
|
||||
diagnostics without re-running validation.
|
||||
|
||||
Centralising validation here keeps the converter and provider implementations
|
||||
focused on behaviour: by the time control enters `convert` or `fetchRates`,
|
||||
every option is already the shape the implementation expects, and the
|
||||
implementation can rely on TypeScript's structural typing without runtime
|
||||
double-checks.
|
||||
|
||||
## Features
|
||||
|
||||
- `ConverterConfigSchema` — validates the constructor argument (provider
|
||||
shape, optional fallback array, positive integer TTL, currency-code
|
||||
default base, virtual-currency record).
|
||||
- `ConvertOptionsSchema` — validates per-call options (`rounding` enum,
|
||||
optional `fresh: boolean`, optional `AbortSignal`).
|
||||
- `VirtualCurrencyConfigSchema` — `rate > 0`, non-empty `name` / `symbol`,
|
||||
optional `exponent ∈ [0, 18]` / `issuerId`.
|
||||
- `RateFetchOptionsSchema` — optional `base`, `targets` array of currency
|
||||
codes, optional `AbortSignal`.
|
||||
- `parseConverterConfig` / `parseConvertOptions` /
|
||||
`parseVirtualCurrencyConfig` / `parseRateFetchOptions` — safe-parse
|
||||
helpers that raise the appropriate `CurrencyError` on failure.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { parseConvertOptions } from "@suaveplan/currency";
|
||||
|
||||
const options = parseConvertOptions({ rounding: "half-up", fresh: false });
|
||||
// options is typed as ConvertOptions; bad shapes raise CURRENCY.INVALID_OPTIONS.
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
A consumer wraps API input through the schemas before delegating to the
|
||||
converter, surfacing Zod issues as user-facing field errors:
|
||||
|
||||
```ts
|
||||
import {
|
||||
CurrencyError,
|
||||
CURRENCY_CODES,
|
||||
parseConverterConfig,
|
||||
CurrencyConverter,
|
||||
} from "@suaveplan/currency";
|
||||
|
||||
export function makeConverter(raw: unknown) {
|
||||
try {
|
||||
const config = parseConverterConfig(raw);
|
||||
return new CurrencyConverter(config);
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof CurrencyError &&
|
||||
err.code === CURRENCY_CODES.INVALID_CONFIG
|
||||
) {
|
||||
const issues = (err.context?.issues as { path: (string | number)[]; message: string }[] | undefined) ?? [];
|
||||
throw new Error(
|
||||
`Bad converter config: ${issues
|
||||
.map((i) => `${i.path.join(".")}: ${i.message}`)
|
||||
.join(", ")}`
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Export | Kind | Description |
|
||||
|---|---|---|
|
||||
| `ConverterConfigSchema` | Zod schema | Validates `ConverterConfig`. |
|
||||
| `ConvertOptionsSchema` | Zod schema | Validates `ConvertOptions`. |
|
||||
| `VirtualCurrencyConfigSchema` | Zod schema | Validates a single `VirtualCurrencyConfig`. |
|
||||
| `RateFetchOptionsSchema` | Zod schema | Validates `RateFetchOptions`. |
|
||||
| `parseConverterConfig(input)` | `(unknown) => ConverterConfig` | Raises `CurrencyError({ code: CURRENCY.INVALID_CONFIG })` on failure. |
|
||||
| `parseConvertOptions(input)` | `(unknown) => ConvertOptions` | Raises `CurrencyError({ code: CURRENCY.INVALID_OPTIONS })` on failure. |
|
||||
| `parseVirtualCurrencyConfig(input)` | `(unknown) => VirtualCurrencyConfig` | Raises `CurrencyError({ code: CURRENCY.INVALID_CONFIG })` on failure. |
|
||||
| `parseRateFetchOptions(input)` | `(unknown) => RateFetchOptions` | Raises `CurrencyError({ code: CURRENCY.INVALID_OPTIONS })` on failure. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `signal` field uses a `z.custom<AbortSignal>` because `AbortSignal` is a
|
||||
host object — Zod cannot introspect it. The custom predicate accepts any
|
||||
object exposing an `aborted` property, which matches both the platform
|
||||
`AbortSignal` and consumer-side polyfills. `provider` is validated
|
||||
structurally (`name`, `supportsCrypto`, `supportsFiat`, optional
|
||||
`supportsVirtual`, function-typed `fetchRates`); we deliberately do not
|
||||
descend into the function body. Currency codes are validated as non-empty
|
||||
strings — the converter checks against the `@suaveplan/money` registry at
|
||||
use time, so unknown codes raise `CURRENCY.UNSUPPORTED_CURRENCY` (via
|
||||
`getCurrencyOrThrow`) rather than failing schema validation.
|
||||
Reference in New Issue
Block a user