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,86 @@
# Add @suaveplan/currency-provider-open-exchange-rates
**Package**: `@suaveplan/currency-provider-open-exchange-rates`
**Status**: PROPOSED
**Type**: new-package
- **Spec delta**: ./specs/currency-provider-open-exchange-rates/spec.md
## Why
The 2026-05-17 slim of `@suaveplan/currency-providers` removed the nine bundled
provider implementations and committed to landing each as an independent
sibling package on the foundation-with-sibling-plugins shape established by
`@suaveplan/network-plugin-*` and `@suaveplan/drive-plugin-*`. This change
lands the Open Exchange Rates (OXR) provider so consumers that only need OXR
install one small package instead of the bundled umbrella, and OXR can iterate
on its own version cadence when the upstream API changes (free-plan base-only,
historical endpoints, currency-list updates).
## What changes
- ADDED: `OpenExchangeRatesProvider` — class implementing `CurrencyProvider`
from `@suaveplan/currency`; consumes `httpRequest()` from
`@suaveplan/currency-providers`; raises `CurrencyProviderError` with codes
`HTTP_ERROR`, `PARSE_ERROR`, `UPSTREAM_ERROR`, `MISSING_CONFIG`,
`UNSUPPORTED_BASE`, `UNKNOWN_CURRENCY`.
- ADDED: subpath export `./provider/provider` for tree-shakeable import.
- ADDED: package root barrel re-exporting `OpenExchangeRatesProvider` and the
type alias `OpenExchangeRatesProviderConfig` (= `ApiKeyConfig` from
`@suaveplan/currency-providers`).
## Out of scope
- Historical-rate endpoint (`/historical/<date>.json`) — deferred to a
follow-up change. The upstream slim refactor's `## Out of scope` block
explicitly defers historical rates for every provider in the wave; this
package keeps parity with that boundary.
- Retry / backoff / rate-limit-header surfacing / OTel — deferred to the same
cross-cutting follow-up that the slim core deferred them to.
- The five other OXR endpoints (`/currencies.json`, `/usage.json`,
`/convert/{value}/{from}/{to}`, `/ohlc.json`, `/time-series.json`) — the
`CurrencyProvider` contract only exposes bulk-fetch via `/latest.json`; the
rest are out of contract.
## Source mapping
This change derives from `../suaveplan/enterprise/currency-providers.md` and
the upstream OXR implementation at
`../suaveplan/packages/finance/currency-providers/src/fiat/open-exchange-rates.ts`.
| Requirement | Source lines | Notes |
|---|---|---|
| OXR-001 implements `CurrencyProvider` with `supportsFiat=true`, `supportsCrypto=false`, `name="open-exchange-rates"` | `open-exchange-rates.ts` L15-L19 | verbatim, plus tier purity (no Node-only APIs) |
| OXR-002 fetches `/latest.json?app_id=<key>&base=<base>` via `httpRequest()` | `open-exchange-rates.ts` L28-L37 | reworded — upstream called `fetch()` directly; genesis routes through slim-core `httpRequest()` per slim spec |
| OXR-003 maps response `{ base, rates, timestamp }` to `RateFetchResult` with `timestamp * 1000` to convert OXR's Unix seconds to ms | `open-exchange-rates.ts` L39-L43, L56-L60 | verbatim |
| OXR-004 honours `RateFetchOptions.targets` by filtering `rates` map | `open-exchange-rates.ts` L46-L54 | verbatim |
| OXR-005 free-tier limits base to USD; explicit non-USD base raises `UNSUPPORTED_BASE` | enterprise audit L12, OXR.md L11-L13, L41-L43 | extended — upstream silently let upstream API reject; genesis pre-rejects with a stable error code (the `UNSUPPORTED_BASE` code is already registered in slim core) |
### Requirements with no source line
| Requirement | Justification |
|---|---|
| OXR-006 raises `MISSING_CONFIG` on empty `apiKey` | `MISSING_CONFIG` code exists in slim-core registry per archived slim spec; surfacing it from constructor is the genesis baseline for all api-key providers |
| OXR-007 raises `PARSE_ERROR` when upstream payload is not the expected shape | Defensive validation — upstream library threw a generic JSON-parse error; genesis maps to typed `PARSE_ERROR` |
| OXR-008 raises `UPSTREAM_ERROR` when upstream returns 2xx with `error: true` body | OXR documented `error/status/message` payload shape; upstream library missed it. Genesis surfaces typed `UPSTREAM_ERROR` with `upstreamMessage` populated |
| OXR-009 raises `UNKNOWN_CURRENCY` when `options.targets` requests a code not in upstream response | `UNKNOWN_CURRENCY` code exists in slim-core registry; surfacing it instead of silently dropping the target gives callers a typed handle |
| OXR-010 honours `RateFetchOptions.signal` for cancellation | Universal-tier abort propagation — upstream did not honour signal at all; genesis composes via `httpRequest()` which forwards to the injected fetch |
| OXR-011 universal-tier purity — no Node APIs, no DOM APIs | Genesis-wide tier policy |
| OXR-012 100% test coverage, sibling `.md` on every implementation file | Genesis-wide quality policy |
## Verification approach
Every requirement carries a `**Verified by:**` line in the spec delta pointing
at concrete `it(...)` titles. The test surface is:
- `src/provider/provider.test.ts` — covers OXR-001 through OXR-011 with
injected `FetchLike` recording calls and returning canned responses.
- `tests/dep-graph.test.ts` — pins dependency invariants (only consumes
`@suaveplan/currency`, `@suaveplan/currency-providers`, `@suaveplan/error`,
`@suaveplan/types`).
## Migration impact
None — additive only. No current consumers reference this package; the slim
core's `## Affected dependents` block listed this package as one of nine
sibling consumers that would land in the follow-up wave.