Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
# Add @suaveplan/currency-provider-fixer — Fixer.io fiat-rates sibling provider
**Package**: `@suaveplan/currency-provider-fixer`
**Status**: PROPOSED
**Type**: new package (genesis-original sibling of slimmed `@suaveplan/currency-providers` core)
- **Design**: ./design.md
- **Spec delta**: ./specs/currency-provider-fixer/spec.md
## Why
The 2026-05-17 slim refactor of `@suaveplan/currency-providers`
(`openspec/archive/2026-05-17-slim-currency-providers/`) split the nine
concrete providers out of the umbrella package and committed to landing each
as a dedicated `@suaveplan/currency-provider-<name>` sibling consuming the
slim core (registry / factory / `httpRequest` / `composeAbortSignal` / configs
/ `CurrencyProviderError`). This change lands the **Fixer.io** sibling — the
first API-keyed fiat-rates provider in the follow-up wave.
Fixer.io is the most widely deployed third-party fiat-rates API after ECB
(Reuters / payment-aggregator backbone for years); shipping it early gives
downstream commerce / accounting consumers an API-keyed alternative to the
key-free ECB feed (`@suaveplan/currency-provider-ecb`, landing alongside)
that supports 170+ currencies and is gateway-friendly.
## What changes
### ADDED
- **New package** `@suaveplan/currency-provider-fixer` (universal-tier,
`0.1.0`, `sideEffects: false`).
- **`FixerProvider` class** implementing the `CurrencyProvider` contract from
`@suaveplan/currency` (`name = "fixer"`, `supportsFiat = true`,
`supportsCrypto = false`). Constructor takes `ApiKeyConfig` (from the slim
core's `./types/configs`). Optional `baseUrl` override defaults to
`https://data.fixer.io/api`. Optional injectable `FetchLike` (from
`@suaveplan/types`) defaults to `globalThis.fetch`.
- **`fetchRates(options?)`** delegates HTTP to the slim core's `httpRequest`
helper (caller-signal composition, non-2xx → typed `HTTP_ERROR`, response
pass-through), then parses the upstream JSON envelope into the universal
`RateFetchResult` shape (`{ base, rates, timestamp }`).
- **Typed error mapping** from upstream `{ success: false, error: { code,
type, info } }` envelopes to the slim core's `CURRENCY_PROVIDER_*` codes:
- empty `apiKey` at construction → `MISSING_CONFIG`
- non-2xx HTTP response → `HTTP_ERROR` (raised by `httpRequest`)
- malformed / non-object JSON → `PARSE_ERROR`
- upstream code `105` (`base_currency_access_restricted`) →
`UNSUPPORTED_BASE`
- upstream code `201` (`invalid_base_currency`) or `202`
(`invalid_currency_codes`) → `UNKNOWN_CURRENCY`
- any other `success: false` envelope → `UPSTREAM_ERROR` carrying
`upstreamMessage`
- **No auto-register.** The package exports the class; callers wire it via
`new FixerProvider({ apiKey })` or
`registry.register(new FixerProvider({ apiKey }))` per the slim-core
design. There is no top-level side effect.
### KEPT (unchanged from slim core)
- The `CurrencyProvider` contract, `RateFetchOptions`, `RateFetchResult`
remain owned by `@suaveplan/currency`.
- `CURRENCY_PROVIDER_CODES`, `CurrencyProviderError`, `httpRequest`,
`composeAbortSignal`, and `ApiKeyConfig` remain owned by
`@suaveplan/currency-providers`.
## Source mapping
This package is a migration of the upstream `FixerProvider` previously
bundled in `@suaveplan/currency-providers`. The source was carved out by the
2026-05-17 slim refactor and is reconstituted here against the slim-core
contracts.
| Component | Source |
| --- | --- |
| `FixerProvider` class skeleton | `../suaveplan/packages/finance/currency-providers/src/fiat/fixer.ts` L1–L67 |
| README quick-start example | upstream `fixer.md` L17–L37 |
| Free-tier EUR-only constraint | upstream `fixer.md` L52–L54 + Fixer.io API docs error code `105` |
| Upstream JSON envelope shape (`success` / `error.code` / `error.info` / `base` / `rates` / `timestamp`) | upstream `fixer.ts` L47–L53 + Fixer.io public docs |
| HTTP / abort-signal / non-2xx mapping | `@suaveplan/currency-providers/http/http` (slim core) |
| Error code surface | `@suaveplan/currency-providers/error-codes/error-codes` (slim core) |
The genesis-native upgrades over the upstream skeleton:
- Replaces the bare `throw new Error(...)` in upstream with
`CurrencyProviderError` carrying stable codes from the slim core's registry
(upstream `fixer.ts` L42 / L57 → `MISSING_CONFIG` / `PARSE_ERROR` /
`UNSUPPORTED_BASE` / `UNKNOWN_CURRENCY` / `UPSTREAM_ERROR`).
- Routes HTTP through the slim core's shared `httpRequest` so caller-supplied
`AbortSignal` from `RateFetchOptions.signal` is honoured (upstream version
did not).
- Validates `apiKey` at construction (upstream silently accepted an empty
string and let the upstream API reject it on the wire).
- Adds the `FetchLike` injection seam from `@suaveplan/types` (upstream used
the bare `globalThis.fetch`).
## Parity Checklist
| Capability | Upstream | Migrated | Notes |
| --- | --- | --- | --- |
| `name`/`supportsFiat`/`supportsCrypto` identity flags | ✅ | ✅ | |
| `ApiKeyConfig` constructor (apiKey + optional baseUrl) | ✅ | ✅ | Inherited from slim core. |
| Default URL `https://data.fixer.io/api` | ✅ | ✅ | |
| `fetchRates({ base, targets })` query construction | ✅ | ✅ | `access_key`, `base`, `symbols`. |
| Default base `EUR` when none supplied | ✅ | ✅ | |
| `success: false` envelope detection | ✅ | ⬆️ | Now mapped to typed codes via upstream `error.code` discriminant instead of a bare `throw new Error()`. |
| Non-2xx → typed error | ✅ | ⬆️ | Routed through slim core's `httpRequest`; raises `HTTP_ERROR` with populated `status` + `providerName`. |
| Timestamp seconds → ms conversion | ✅ | ✅ | `data.timestamp * 1000`. |
| Symbol filter via `targets` | ✅ | ✅ | |
| `apiKey` non-empty validation at construction | 🚫 | ⬆️ | New: raises `MISSING_CONFIG` instead of letting the API reject on the wire. |
| `AbortSignal` propagation via `RateFetchOptions.signal` | 🚫 | ⬆️ | New: caller signal composed by slim-core `httpRequest`. |
| `FetchLike` injection seam | 🚫 | ⬆️ | New: optional `config.fetch` honoured for testability and proxying. |
No upstream capability is dropped. Every 🚫 row is an ADDITION on top of upstream.
## Impact
### Affected dependents
Run `bunx turbo ls --filter=...@suaveplan/currency-provider-fixer` to enumerate.
- No current dependents (package is new). The slim core
`@suaveplan/currency-providers` is depended on by this package, not the
reverse — no consumer rewrite is required.
## Out of scope
- Other sibling providers (ECB, OXR, Binance, CoinGecko, CMC, CryptoCompare,
Kraken, Virtual) — each ships under its own OpenSpec change per AGENTS.md
§0c.
- Historical rates (`/{date}` endpoint), time-series (`/timeseries`),
fluctuation (`/fluctuation`), conversion (`/convert`) endpoints — the
`CurrencyProvider` contract today exposes only `fetchRates`; the additional
endpoints will land once the contract is extended (carried over from the
2026-05-17 archive's `## Out of scope`).
- Rate-limit / retry / aggregator wrappers — same deferral as the slim core.