Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# @suaveplan/currency-provider-cryptocompare - Universal CryptoCompare crypto-rates provider for @suaveplan/currency. Sibling provider package on the slim @suaveplan/currency-providers core (httpRequest, defineCurrencyProvider, CurrencyProviderError, NoKeyConfig). Optional API key — free tier works keyless; key boosts the rate limit and is sent via `authorization: Apikey <key>` header.
|
||||
|
||||
Universal CryptoCompare crypto-rates provider for `@suaveplan/currency`.
|
||||
Sibling provider package on top of the slim `@suaveplan/currency-providers`
|
||||
core (`httpRequest`, `CurrencyProviderError`, `NoKeyConfig`). Optional API
|
||||
key — CryptoCompare's free tier works fully keyless; supplying a key boosts
|
||||
the upstream rate limit and is sent on every request via the
|
||||
`authorization: Apikey <key>` header.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [cryptocompare](./src/cryptocompare/cryptocompare.md) — `CryptoCompareProvider` implements the `CurrencyProvider` contract from `@suaveplan/currency` on top of CryptoCompare's…
|
||||
- [cryptocompare](./src/cryptocompare/cryptocompare.md) — `CryptoCompareProvider` implements the `CurrencyProvider` contract from `@suaveplan/currency` on top of CryptoCompare's…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/currency-provider-cryptocompare \
|
||||
@suaveplan/currency \
|
||||
@suaveplan/currency-providers
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { CurrencyConverter } from "@suaveplan/currency";
|
||||
import { CryptoCompareProvider } from "@suaveplan/currency-provider-cryptocompare";
|
||||
|
||||
const provider = new CryptoCompareProvider({
|
||||
apiKey: process.env.CRYPTOCOMPARE_API_KEY, // optional
|
||||
});
|
||||
|
||||
const converter = new CurrencyConverter({
|
||||
provider,
|
||||
staleDurationMs: 60_000,
|
||||
});
|
||||
|
||||
const conversion = await converter.convert(amount, "BTC");
|
||||
```
|
||||
|
||||
Or register through the slim-core registry:
|
||||
|
||||
```ts
|
||||
import { CurrencyProviderRegistry } from "@suaveplan/currency-providers";
|
||||
import { CryptoCompareProvider } from "@suaveplan/currency-provider-cryptocompare";
|
||||
|
||||
const registry = new CurrencyProviderRegistry();
|
||||
registry.register(new CryptoCompareProvider());
|
||||
const provider = registry.get("cryptocompare");
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
- [`cryptocompare/cryptocompare`](./src/cryptocompare/cryptocompare.md) —
|
||||
`CryptoCompareProvider` class, `CryptoCompareConfig` interface, error
|
||||
mapping for `HTTP_ERROR` / `PARSE_ERROR` / `UPSTREAM_ERROR` /
|
||||
`UNKNOWN_CURRENCY`.
|
||||
|
||||
## Errors
|
||||
|
||||
Every failure path raises `CurrencyProviderError` from the slim core:
|
||||
|
||||
| Code | Raised when |
|
||||
|---|---|
|
||||
| `CURRENCY_PROVIDER.HTTP_ERROR` | Non-2xx HTTP response (shared `httpRequest`) |
|
||||
| `CURRENCY_PROVIDER.PARSE_ERROR` | Body not valid JSON, or payload not a JSON object |
|
||||
| `CURRENCY_PROVIDER.UPSTREAM_ERROR` | Payload carries `Response: "Error"` |
|
||||
| `CURRENCY_PROVIDER.UNKNOWN_CURRENCY` | No requested symbol produced a finite positive quote |
|
||||
|
||||
Dispatch on `error.code`, not the message string. The instance also
|
||||
carries `providerName: "cryptocompare"`, optional `status` (for HTTP_ERROR),
|
||||
and optional `upstreamMessage` (for UPSTREAM_ERROR).
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
# `cryptocompare` — CryptoCompare crypto-rates provider
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`CryptoCompareProvider` implements the `CurrencyProvider` contract from
|
||||
`@suaveplan/currency` on top of CryptoCompare's public
|
||||
`min-api.cryptocompare.com/data/pricemulti` endpoint. It returns inverted
|
||||
spot prices (`1 / price`) for a configurable set of crypto symbols against
|
||||
a fiat or crypto base currency, suitable for direct registration with
|
||||
`CurrencyProviderRegistry` or instantiation into `CurrencyConverter`.
|
||||
|
||||
The provider is sibling to the slim `@suaveplan/currency-providers` core:
|
||||
all HTTP plumbing (timeout / abort composition / typed-error mapping) lives
|
||||
in the shared `httpRequest` helper, the typed error class and code registry
|
||||
come from `CurrencyProviderError` + `CURRENCY_PROVIDER_CODES`, and the
|
||||
config shape extends `NoKeyConfig` with one extra optional field. The
|
||||
provider is universal — no Node, DOM, or framework dependencies — so
|
||||
it runs untouched in browsers, Workers, Bun, and Node 18+.
|
||||
|
||||
## Features
|
||||
|
||||
- Sibling crypto-rates provider — `name: "cryptocompare"`,
|
||||
`supportsCrypto: true`, `supportsFiat: false`
|
||||
- Optional `apiKey` — free tier works fully keyless; key boosts the
|
||||
upstream rate limit and is sent via `authorization: Apikey <key>` header
|
||||
- 15 sensible default crypto symbols (BTC, ETH, SOL, USDT, USDC, BNB, XRP,
|
||||
ADA, DOGE, DOT, AVAX, LINK, UNI, ATOM, LTC) when callers omit `targets`
|
||||
- Injectable `FetchLike` from `@suaveplan/types` — tests pass a controlled
|
||||
closure without `vi.stubGlobal('fetch', ...)`
|
||||
- Typed errors via the slim core registry: `HTTP_ERROR`, `PARSE_ERROR`,
|
||||
`UPSTREAM_ERROR`, `UNKNOWN_CURRENCY` — every failure path carries
|
||||
`providerName: "cryptocompare"` for log dispatch
|
||||
- Caller `AbortSignal` from `RateFetchOptions.signal` is composed through
|
||||
`httpRequest`; whitespace-only `apiKey` strings are treated as absent
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { CryptoCompareProvider } from "@suaveplan/currency-provider-cryptocompare";
|
||||
|
||||
const provider = new CryptoCompareProvider();
|
||||
const result = await provider.fetchRates({ base: "USD" });
|
||||
console.log(result.rates.BTC); // 1 / spot price of BTC in USD
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Register the provider with `CurrencyProviderRegistry`, use the keyed
|
||||
upstream for higher rate limits, and dispatch on typed errors:
|
||||
|
||||
```ts
|
||||
import { CurrencyConverter } from "@suaveplan/currency";
|
||||
import {
|
||||
CURRENCY_PROVIDER_CODES,
|
||||
CurrencyProviderError,
|
||||
CurrencyProviderRegistry,
|
||||
} from "@suaveplan/currency-providers";
|
||||
import { CryptoCompareProvider } from "@suaveplan/currency-provider-cryptocompare";
|
||||
|
||||
const registry = new CurrencyProviderRegistry();
|
||||
registry.register(
|
||||
new CryptoCompareProvider({
|
||||
apiKey: process.env.CRYPTOCOMPARE_API_KEY,
|
||||
baseUrl: "https://min-api.cryptocompare.com/data",
|
||||
}),
|
||||
);
|
||||
|
||||
const converter = new CurrencyConverter({
|
||||
provider: registry.get("cryptocompare"),
|
||||
staleDurationMs: 60_000,
|
||||
});
|
||||
|
||||
try {
|
||||
const conversion = await converter.convert(amount, "BTC");
|
||||
console.log(conversion);
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof CurrencyProviderError &&
|
||||
error.code === CURRENCY_PROVIDER_CODES.UPSTREAM_ERROR
|
||||
) {
|
||||
console.warn("CryptoCompare upstream:", error.upstreamMessage);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface CryptoCompareConfig extends NoKeyConfig`
|
||||
- `apiKey?: string` — optional API key. Sent on every request via
|
||||
`authorization: Apikey <key>` when present and non-whitespace.
|
||||
Whitespace-only strings are treated as absent.
|
||||
- `baseUrl?: string` — overrides the default
|
||||
`https://min-api.cryptocompare.com/data` endpoint.
|
||||
- `fetch?: FetchLike` — injectable WHATWG-shape fetch. Defaults to
|
||||
`globalThis.fetch`.
|
||||
- `class CryptoCompareProvider implements CurrencyProvider`
|
||||
- `name: "cryptocompare"`, `supportsCrypto: true`,
|
||||
`supportsFiat: false`.
|
||||
- `constructor(config?: CryptoCompareConfig)` — every config field is
|
||||
optional. Constructing without a config wires `globalThis.fetch`, the
|
||||
default base URL, and no API key.
|
||||
- `fetchRates(options?: RateFetchOptions): Promise<RateFetchResult>` —
|
||||
issues a single `GET <baseUrl>/pricemulti?fsyms=<targets>&tsyms=<base>`,
|
||||
inverts each price, and returns `{ base, rates, timestamp }`.
|
||||
`options.base` defaults to `"USD"`; `options.targets` defaults to the
|
||||
15 built-in symbols when omitted or empty; `options.signal` is
|
||||
forwarded into the shared `httpRequest` helper.
|
||||
- Raised `CurrencyProviderError` codes:
|
||||
- `HTTP_ERROR` — non-2xx response (raised by shared `httpRequest`).
|
||||
- `PARSE_ERROR` — body is not valid JSON, or the parsed payload is not
|
||||
a JSON object.
|
||||
- `UPSTREAM_ERROR` — payload carries `Response: "Error"`; the
|
||||
`Message` field (or `"unknown upstream error"` when absent) is copied
|
||||
to `error.upstreamMessage`.
|
||||
- `UNKNOWN_CURRENCY` — no requested symbol produced a finite positive
|
||||
quote in the requested base currency.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- CryptoCompare's `pricemulti` endpoint returns HTTP 200 even for
|
||||
per-symbol upstream errors — the payload carries `Response: "Error"` and
|
||||
a human-readable `Message`. The provider distinguishes that from a
|
||||
successful (possibly empty) quote map and surfaces it as
|
||||
`UPSTREAM_ERROR` rather than masquerading as `HTTP_ERROR`.
|
||||
- The free tier permits ~100,000 calls per month without a key; supplying
|
||||
an `apiKey` boosts that quota. The provider authenticates via the
|
||||
`authorization: Apikey <key>` header per CryptoCompare's documentation
|
||||
(Apikey scheme, not Bearer). Empty or whitespace-only keys are silently
|
||||
treated as absent so callers can pass `process.env.CRYPTOCOMPARE_API_KEY`
|
||||
directly without a guard.
|
||||
- Prices are inverted (`1 / price`) so the result map answers "one unit of
|
||||
`base` is worth X units of `symbol`" — consistent with every other
|
||||
sibling provider. Non-finite or non-positive quotes are skipped.
|
||||
- The default symbol list covers the top 15 crypto assets by market cap at
|
||||
package-publish time; callers wanting a different set always pass
|
||||
`targets` explicitly. The defaults exist only as a developer-friendly
|
||||
fallback for ad-hoc usage.
|
||||
- The provider does not wrap retry, backoff, or rate-limit semantics —
|
||||
compose `@suaveplan/retry` / future `RateLimitedProvider` wrappers at a
|
||||
higher layer, exactly the same way as every other sibling provider.
|
||||
Reference in New Issue
Block a user