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,206 @@
# Slim @suaveplan/currency-providers to a thin plugin core
**Package**: `@suaveplan/currency-providers`
**Status**: PROPOSED
**Type**: refactor (modification of the just-shipped 2026-05-17 migration)
- **Design**: ./design.md
- **Spec delta**: ./specs/currency-providers/spec.md
## Why
The first shipped shape of `@suaveplan/currency-providers` bundled nine concrete
providers (ECB, Fixer, Open Exchange Rates, Binance, CoinGecko, CoinMarketCap,
CryptoCompare, Kraken, Virtual) into a single npm artefact. That bundling
violates the foundation-with-sibling-plugins shape that `@suaveplan/network`
and `@suaveplan/drive` established for genesis: every concrete plugin is its
own publishable package so consumers install only what they wire in, and each
plugin can iterate on its own version cadence without a coordinated bump of
the umbrella.
Consumers that want only ECB pay the install + tree-shake-graph cost of eight
unused providers. Worse, an upstream API change in (say) CoinMarketCap forces
a rev of the umbrella that every other provider also ships under. Splitting
into `currency-providers` (the contract + registry + shared HTTP helper) and
nine `currency-provider-<name>` siblings (in the follow-up wave) gives
consumers granular installs, gives each provider an independent surface to
evolve, and matches the established `network-plugin-*` / `drive-plugin-*`
shape.
This change is the **slim**: it deletes the nine concrete provider modules
from this package, adds the `CurrencyProviderRegistry` + `defineCurrencyProvider`
factory + `PROVIDER_NOT_REGISTERED` code that the nine sibling packages will
consume, and factors the small HTTP utilities (`httpRequest`,
`composeAbortSignal`) that every provider repeated by hand into a single
shared helper. The nine sibling packages land in the follow-up wave; each
will depend on this slimmed core and re-implement only its own provider.
## What changes
### REMOVED
- `EcbProvider` — moves to `@suaveplan/currency-provider-ecb`.
- `FixerProvider` — moves to `@suaveplan/currency-provider-fixer`.
- `OpenExchangeRatesProvider` — moves to `@suaveplan/currency-provider-open-exchange-rates`.
- `BinanceProvider` — moves to `@suaveplan/currency-provider-binance`.
- `CoinGeckoProvider` + `getCoinGeckoId` — move to `@suaveplan/currency-provider-coingecko`.
- `CoinMarketCapProvider` — moves to `@suaveplan/currency-provider-coinmarketcap`.
- `CryptoCompareProvider` — moves to `@suaveplan/currency-provider-cryptocompare`.
- `KrakenProvider` — moves to `@suaveplan/currency-provider-kraken`.
- `VirtualCurrencyProvider` + `VirtualCurrencyProviderConfig` — move to
`@suaveplan/currency-provider-virtual`.
- All nine `./providers/<name>/<name>` subpath exports drop from
`package.json`.
### ADDED
- `CurrencyProviderRegistry` — in-memory registry of `CurrencyProvider`
instances keyed by `name`. Methods: `register(provider)`, `get(name)`,
`has(name)`, `list()`, `unregister(name)`. `get(unknown)` throws
`CurrencyProviderError(PROVIDER_NOT_REGISTERED)`; `register(duplicate)`
throws `CurrencyProviderError(PROVIDER_ALREADY_REGISTERED)`.
- `defineCurrencyProvider(definition)` — factory that returns a stable-shape
`CurrencyProvider`. Lets sibling packages declare a provider as a plain
object literal without re-implementing the class boilerplate.
- `httpRequest()` shared helper — issues a request via the injected
`FetchLike`, applies `HttpFetchOptions.timeoutMs` and `headers`, composes
the caller's `AbortSignal` with the timeout signal, throws
`CurrencyProviderError(HTTP_ERROR)` on non-2xx with `status` populated.
- `composeAbortSignal()` shared helper — merges an optional caller signal
with an optional timeout-derived signal into a single `AbortSignal`.
- `PROVIDER_NOT_REGISTERED` and `PROVIDER_ALREADY_REGISTERED` codes added to
`CURRENCY_PROVIDER_CODES`.
### KEPT (unchanged)
- `ApiKeyConfig`, `NoKeyConfig`, `HttpFetchOptions`, `ProviderConfigBase`
shared config types — every sibling provider extends these.
- `CURRENCY_PROVIDER_CODES` registry (now 8 codes: original 6 plus the 2
registry codes) + `CurrencyProviderError` class with
`providerName` / `status` / `upstreamMessage` discriminants.
- Universal-tier purity, `sideEffects: false`, explicit named barrels,
`@suaveplan/testing/runner` test imports, 100/100/100/100 coverage gate.
## Source mapping
This refactor is a MODIFICATION of `openspec/archive/2026-05-17-add-currency-providers/`.
The provider implementations now slated for removal are at:
| Removed module | Source in just-shipped archive |
|---|---|
| `EcbProvider` | `src/providers/ecb/ecb.ts` L1-L111 (per archived spec L84-L122) |
| `FixerProvider` | `src/providers/fixer/fixer.ts` (per archived spec L126-L158) |
| `OpenExchangeRatesProvider` | `src/providers/open-exchange-rates/open-exchange-rates.ts` (per archived spec L162-L187) |
| `BinanceProvider` | `src/providers/binance/binance.ts` (per archived spec L191-L217) |
| `CoinGeckoProvider` + `getCoinGeckoId` | `src/providers/coingecko/coingecko.ts` (per archived spec L221-L249) |
| `CoinMarketCapProvider` | `src/providers/coinmarketcap/coinmarketcap.ts` (per archived spec L253-L276) |
| `CryptoCompareProvider` | `src/providers/cryptocompare/cryptocompare.ts` (per archived spec L280-L299) |
| `KrakenProvider` | `src/providers/kraken/kraken.ts` (per archived spec L303-L325) |
| `VirtualCurrencyProvider` | `src/providers/virtual/virtual.ts` (per archived spec L329-L375) |
The new registry / factory / HTTP helper surface is genesis-original, modelled
on the established foundation-with-sibling-plugins shapes:
| New module | Reference shape |
|---|---|
| `CurrencyProviderRegistry` | `packages/network/network/src/manager/` `PluginManager` |
| `defineCurrencyProvider` | `packages/drive/drive/src/plugin/` factory pattern |
| `httpRequest` + `composeAbortSignal` | extracted from the duplicated `fetchImpl(url); if (!response.ok) throw HTTP_ERROR` block present in every provider being removed |
The enterprise audit reference is `../suaveplan/enterprise/currency-providers.md`;
the slim shape was not present upstream — it is a genesis-native architectural
correction.
## Impact
### Affected dependents
Run `bunx turbo ls --filter=...@suaveplan/currency-providers` to enumerate.
At time of writing there are no current dependents (the package shipped on
2026-05-17 and the commerce stack downstream is still pending migration):
- No current dependents (verified by `bunx turbo ls --filter=...@suaveplan/currency-providers`).
The future wave of sibling provider packages will all depend on this slimmed
core. Each is listed here so the next reviewer can see the blast radius the
follow-up wave will land in:
- `@suaveplan/currency-provider-ecb` — [x] consumes registry / factory / httpRequest / configs / error
- `@suaveplan/currency-provider-fixer` — [x] consumes registry / factory / httpRequest / configs / error
- `@suaveplan/currency-provider-open-exchange-rates` — [x] consumes registry / factory / httpRequest / configs / error
- `@suaveplan/currency-provider-binance` — [x] consumes registry / factory / httpRequest / configs / error
- `@suaveplan/currency-provider-coingecko` — [x] consumes registry / factory / httpRequest / configs / error
- `@suaveplan/currency-provider-coinmarketcap` — [x] consumes registry / factory / httpRequest / configs / error
- `@suaveplan/currency-provider-cryptocompare` — [x] consumes registry / factory / httpRequest / configs / error
- `@suaveplan/currency-provider-kraken` — [x] consumes registry / factory / httpRequest / configs / error
- `@suaveplan/currency-provider-virtual` — [x] consumes registry / factory / configs / error (no HTTP)
- `@suaveplan/currency` — [ ] tests run / [x] docs reviewed / [x] no action needed (the `CurrencyProvider` contract lives in `@suaveplan/currency` and is unchanged by this refactor; the slim core depends on it, not the other way around)
## Migration
The nine concrete provider classes are gone from `@suaveplan/currency-providers`
and now live in dedicated sibling packages. Consumers must install the
specific provider package they need and either instantiate it directly or
register it with the new `CurrencyProviderRegistry`.
### Before
```ts
import { CurrencyConverter } from "@suaveplan/currency";
import { EcbProvider, CoinGeckoProvider } from "@suaveplan/currency-providers";
const converter = new CurrencyConverter({
provider: new EcbProvider(),
fallbackProviders: [new CoinGeckoProvider()],
});
```
### After
```ts
import { CurrencyConverter } from "@suaveplan/currency";
import { CurrencyProviderRegistry } from "@suaveplan/currency-providers";
import { EcbProvider } from "@suaveplan/currency-provider-ecb";
import { CoinGeckoProvider } from "@suaveplan/currency-provider-coingecko";
const registry = new CurrencyProviderRegistry();
registry.register(new EcbProvider());
registry.register(new CoinGeckoProvider());
const converter = new CurrencyConverter({
provider: registry.get("ecb"),
fallbackProviders: [registry.get("coingecko")],
});
```
Direct instantiation without the registry is equally supported — the registry
is a convenience for callers that wire many providers and want a single
lookup surface. Sibling packages remain `CurrencyProvider`-compatible so the
old `new EcbProvider()` form still works once the sibling package is installed.
Consumers should run this search command to find call sites that need to add
sibling-package imports:
```bash
grep -rln "from ['\"]@suaveplan/currency-providers['\"]" \
packages/ apps/ src/ 2>/dev/null | grep -v "/dist/\|/coverage/"
```
Every hit that uses one of `EcbProvider`, `FixerProvider`,
`OpenExchangeRatesProvider`, `BinanceProvider`, `CoinGeckoProvider`,
`CoinMarketCapProvider`, `CryptoCompareProvider`, `KrakenProvider`,
`VirtualCurrencyProvider`, `VirtualCurrencyProviderConfig`, or
`getCoinGeckoId` needs its import rewritten to the matching
`@suaveplan/currency-provider-<name>` package.
## Out of scope
- The nine sibling `@suaveplan/currency-provider-<name>` packages — each
ships under its own OpenSpec change in the follow-up wave per AGENTS.md
§0c "Multi-package / cross-cutting feature sets" (one change per package;
cross-cutting design lives once, in this package's `design.md`).
- Rate-limit / retry / historical-rate / WebSocket / on-chain oracle /
aggregator / health-monitor / OTel surfaces — same deferral as the
2026-05-17 archive's `## Out of scope`. Nothing in this slim refactor
changes those plans; once those wrappers land they consume the
`CurrencyProvider` contract through the registry exactly the same way.