@molecule/api-fx-rates 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +342 -0
- package/package.json +7 -6
package/README.md
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
3
|
+
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
|
+
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
|
+
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
+
Generated: 2026-08-04T01:48:15.200Z
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @molecule/api-fx-rates
|
|
10
|
+
|
|
11
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
12
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
13
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
14
|
+
|
|
15
|
+
Provider-agnostic foreign-exchange rates interface for molecule.dev.
|
|
16
|
+
|
|
17
|
+
Defines the `FxRatesProvider` interface for currency conversion and daily
|
|
18
|
+
reference-rate lookups. Bond packages (ECB, OpenExchange, etc.) implement
|
|
19
|
+
this interface. Application code uses the convenience functions
|
|
20
|
+
(`getRate`, `getDailyRates`, `convert`, `listSupportedCurrencies`) which
|
|
21
|
+
delegate to the bonded provider.
|
|
22
|
+
|
|
23
|
+
Rates are normalized as plain `number` ratios: `1 unit of FROM = rate units of TO`.
|
|
24
|
+
Currency codes are ISO 4217 three-letter strings (e.g. `'USD'`, `'EUR'`, `'JPY'`).
|
|
25
|
+
Amounts are integer minor units (cents) to avoid floating-point drift.
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { setProvider, getRate, convert } from '@molecule/api-fx-rates'
|
|
31
|
+
import { provider as ecb } from '@molecule/api-fx-rates-ecb'
|
|
32
|
+
|
|
33
|
+
setProvider(ecb)
|
|
34
|
+
const eurUsd = await getRate('EUR', 'USD')
|
|
35
|
+
const usdCents = await convert(10_000, 'EUR', 'USD') // 10000 EUR cents -> USD cents
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Type
|
|
39
|
+
|
|
40
|
+
`core`
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install @molecule/api-fx-rates @molecule/api-bond @molecule/api-i18n
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## API
|
|
49
|
+
|
|
50
|
+
### Interfaces
|
|
51
|
+
|
|
52
|
+
#### `FxDailyRates`
|
|
53
|
+
|
|
54
|
+
A daily snapshot of reference rates, all expressed against a common pivot.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
interface FxDailyRates {
|
|
58
|
+
/**
|
|
59
|
+
* The pivot currency the snapshot is quoted against
|
|
60
|
+
* (e.g. `'EUR'` for ECB, `'USD'` for most paid feeds).
|
|
61
|
+
*/
|
|
62
|
+
pivot: CurrencyCode
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Date of the daily snapshot.
|
|
66
|
+
*/
|
|
67
|
+
asOf: Date
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Map from currency code to rate: `1 unit of pivot = rates[code] units of code`.
|
|
71
|
+
* The pivot itself is conventionally included with rate `1`.
|
|
72
|
+
*/
|
|
73
|
+
rates: Record<CurrencyCode, number>
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### `FxRate`
|
|
78
|
+
|
|
79
|
+
A single FX rate quote: `1 unit of {@link from} = rate units of {@link to}`,
|
|
80
|
+
as observed at {@link asOf}.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
interface FxRate {
|
|
84
|
+
/**
|
|
85
|
+
* Source currency (ISO 4217).
|
|
86
|
+
*/
|
|
87
|
+
from: CurrencyCode
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Target currency (ISO 4217).
|
|
91
|
+
*/
|
|
92
|
+
to: CurrencyCode
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Conversion ratio: `1 unit of {from} = rate units of {to}`.
|
|
96
|
+
*/
|
|
97
|
+
rate: number
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Timestamp the rate was observed/published.
|
|
101
|
+
*/
|
|
102
|
+
asOf: Date
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `FxRatesOptions`
|
|
107
|
+
|
|
108
|
+
Options accepted by all FX-rates provider methods.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
interface FxRatesOptions {
|
|
112
|
+
/**
|
|
113
|
+
* Date the rate should be observed at. Defaults to "latest"
|
|
114
|
+
* if omitted. Implementations that do not support historical
|
|
115
|
+
* lookups MAY throw if a non-latest date is requested.
|
|
116
|
+
*/
|
|
117
|
+
asOf?: Date
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### `FxRatesProvider`
|
|
122
|
+
|
|
123
|
+
Foreign-exchange rates provider interface.
|
|
124
|
+
|
|
125
|
+
All FX-rate providers (ECB, OpenExchange, fixtures, etc.) implement this
|
|
126
|
+
interface. The interface is deliberately minimal so providers with very
|
|
127
|
+
different upstream APIs can satisfy it identically.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
interface FxRatesProvider {
|
|
131
|
+
/**
|
|
132
|
+
* Looks up the conversion rate `1 unit of from = rate units of to`.
|
|
133
|
+
*
|
|
134
|
+
* Implementations SHOULD compute cross-rates through their pivot when
|
|
135
|
+
* neither side equals the pivot.
|
|
136
|
+
*
|
|
137
|
+
* @param from - Source currency (ISO 4217).
|
|
138
|
+
* @param to - Target currency (ISO 4217).
|
|
139
|
+
* @param options - Optional asOf date for historical rates.
|
|
140
|
+
* @returns The conversion ratio as a plain number.
|
|
141
|
+
*/
|
|
142
|
+
getRate(from: CurrencyCode, to: CurrencyCode, options?: FxRatesOptions): Promise<number>
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Returns all reference rates the provider publishes for the given day,
|
|
146
|
+
* normalized against the provider's pivot currency.
|
|
147
|
+
*
|
|
148
|
+
* @param options - Optional asOf date for the daily snapshot.
|
|
149
|
+
* @returns The full daily snapshot.
|
|
150
|
+
*/
|
|
151
|
+
getDailyRates(options?: FxRatesOptions): Promise<FxDailyRates>
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Converts an integer minor-unit amount (e.g. cents) from one currency
|
|
155
|
+
* to another, returning an integer minor-unit amount in the target.
|
|
156
|
+
*
|
|
157
|
+
* Implementations are expected to handle currencies with non-cent minor
|
|
158
|
+
* units (e.g. JPY has 0 decimals) consistently with the inputs.
|
|
159
|
+
*
|
|
160
|
+
* @param amountMinor - Amount in minor units of {@link from} (e.g. cents).
|
|
161
|
+
* @param from - Source currency (ISO 4217).
|
|
162
|
+
* @param to - Target currency (ISO 4217).
|
|
163
|
+
* @param options - Optional asOf date for historical rates.
|
|
164
|
+
* @returns Converted amount in minor units of {@link to}.
|
|
165
|
+
*/
|
|
166
|
+
convert(
|
|
167
|
+
amountMinor: number,
|
|
168
|
+
from: CurrencyCode,
|
|
169
|
+
to: CurrencyCode,
|
|
170
|
+
options?: FxRatesOptions,
|
|
171
|
+
): Promise<number>
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Lists every currency the provider currently supports.
|
|
175
|
+
*
|
|
176
|
+
* @returns Array of ISO 4217 currency codes the provider can quote.
|
|
177
|
+
*/
|
|
178
|
+
listSupportedCurrencies(): Promise<CurrencyCode[]>
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Types
|
|
183
|
+
|
|
184
|
+
#### `CurrencyCode`
|
|
185
|
+
|
|
186
|
+
ISO 4217 three-letter currency code (e.g. `'USD'`, `'EUR'`, `'JPY'`).
|
|
187
|
+
|
|
188
|
+
Kept as a plain `string` alias rather than a string-literal union so
|
|
189
|
+
providers can support whatever set of currencies they expose. Use
|
|
190
|
+
{@link FxRatesProvider.listSupportedCurrencies} to discover what a given
|
|
191
|
+
provider supports at runtime.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
type CurrencyCode = string
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Functions
|
|
198
|
+
|
|
199
|
+
#### `convert(amountMinor, from, to, options)`
|
|
200
|
+
|
|
201
|
+
Converts an integer minor-unit amount (e.g. cents) from one currency to
|
|
202
|
+
another using the bonded provider.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
function convert(
|
|
206
|
+
amountMinor: number,
|
|
207
|
+
from: string,
|
|
208
|
+
to: string,
|
|
209
|
+
options?: FxRatesOptions,
|
|
210
|
+
): Promise<number>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- `amountMinor` — Amount in minor units of {@link from} (e.g. cents).
|
|
214
|
+
- `from` — Source currency (ISO 4217).
|
|
215
|
+
- `to` — Target currency (ISO 4217).
|
|
216
|
+
- `options` — Optional asOf date for historical rates.
|
|
217
|
+
|
|
218
|
+
**Returns:** Converted amount in minor units of {@link to}.
|
|
219
|
+
|
|
220
|
+
#### `getDailyRates(options)`
|
|
221
|
+
|
|
222
|
+
Returns all reference rates the bonded provider publishes for the given day,
|
|
223
|
+
normalized against the provider's pivot currency.
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
function getDailyRates(options?: FxRatesOptions): Promise<FxDailyRates>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- `options` — Optional asOf date for the daily snapshot.
|
|
230
|
+
|
|
231
|
+
**Returns:** The full daily snapshot.
|
|
232
|
+
|
|
233
|
+
#### `getProvider()`
|
|
234
|
+
|
|
235
|
+
Retrieves the bonded FX-rates provider, throwing if none is configured.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
function getProvider(): FxRatesProvider
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Returns:** The bonded FX-rates provider.
|
|
242
|
+
|
|
243
|
+
#### `getRate(from, to, options)`
|
|
244
|
+
|
|
245
|
+
Looks up the conversion rate `1 unit of from = rate units of to`.
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
function getRate(from: string, to: string, options?: FxRatesOptions): Promise<number>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
- `from` — Source currency (ISO 4217).
|
|
252
|
+
- `to` — Target currency (ISO 4217).
|
|
253
|
+
- `options` — Optional asOf date for historical rates.
|
|
254
|
+
|
|
255
|
+
**Returns:** The conversion ratio as a plain number.
|
|
256
|
+
|
|
257
|
+
#### `hasProvider()`
|
|
258
|
+
|
|
259
|
+
Checks whether an FX-rates provider is currently bonded.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
function hasProvider(): boolean
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Returns:** `true` if an FX-rates provider is bonded.
|
|
266
|
+
|
|
267
|
+
#### `listSupportedCurrencies()`
|
|
268
|
+
|
|
269
|
+
Lists every currency the bonded provider currently supports.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
function listSupportedCurrencies(): Promise<string[]>
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Returns:** Array of ISO 4217 currency codes.
|
|
276
|
+
|
|
277
|
+
#### `setProvider(provider)`
|
|
278
|
+
|
|
279
|
+
Registers an FX-rates provider as the active singleton. Called by bond
|
|
280
|
+
packages (e.g. `@molecule/api-fx-rates-ecb`) during application startup.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
function setProvider(provider: FxRatesProvider): void
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
- `provider` — The FX-rates provider implementation to bond.
|
|
287
|
+
|
|
288
|
+
## Available Providers
|
|
289
|
+
|
|
290
|
+
| Provider | Package |
|
|
291
|
+
| ----------------- | ------------------------------------- |
|
|
292
|
+
| ECB FX Rates | `@molecule/api-fx-rates-ecb` |
|
|
293
|
+
| OpenExchangeRates | `@molecule/api-fx-rates-openexchange` |
|
|
294
|
+
|
|
295
|
+
## Injection Notes
|
|
296
|
+
|
|
297
|
+
### Requirements
|
|
298
|
+
|
|
299
|
+
Peer dependencies:
|
|
300
|
+
|
|
301
|
+
- `@molecule/api-bond` ^1.0.1
|
|
302
|
+
- `@molecule/api-i18n` ^1.0.1
|
|
303
|
+
|
|
304
|
+
### Runtime Dependencies
|
|
305
|
+
|
|
306
|
+
- `@molecule/api-bond`
|
|
307
|
+
- `@molecule/api-i18n`
|
|
308
|
+
|
|
309
|
+
## E2E Tests
|
|
310
|
+
|
|
311
|
+
Integration checklist — drive the real UI (live preview, no mocks), adapt
|
|
312
|
+
each item to this app's actual currency/pricing screens, and check every box
|
|
313
|
+
off one by one. This is money: a wrong rate silently corrupts every price, so
|
|
314
|
+
verify the NUMBERS the app computes, not just that a value rendered. A box
|
|
315
|
+
you can't check is an integration bug to fix — not a skip:
|
|
316
|
+
|
|
317
|
+
- [ ] A known pair returns a PLAUSIBLE rate: `getRate('USD', 'EUR')` yields a
|
|
318
|
+
real ratio (roughly 0.8-1.0 for USD->EUR), never 0, null, NaN, negative, or
|
|
319
|
+
an absurd value like 1e9 — and the UI shows it as an actual number.
|
|
320
|
+
- [ ] `convert` does the CORRECT MATH: converting 100 USD (amountMinor
|
|
321
|
+
`10_000` cents) USD->EUR returns approximately `10_000 * rate` in the
|
|
322
|
+
target's minor units, rounded sensibly for that currency (integer cents;
|
|
323
|
+
JPY has 0 decimals), and the UI shows that converted amount — not the
|
|
324
|
+
untouched original.
|
|
325
|
+
- [ ] Round-trip consistency: the inverse pair is reciprocal —
|
|
326
|
+
`getRate('EUR', 'USD')` is approximately `1 / getRate('USD', 'EUR')` — and
|
|
327
|
+
same-currency is identity: `getRate('USD', 'USD')` is exactly `1` and
|
|
328
|
+
`convert(x, 'USD', 'USD')` returns `x` unchanged.
|
|
329
|
+
- [ ] Changing the selected pair changes the displayed result (USD->EUR vs
|
|
330
|
+
USD->JPY give visibly different converted amounts) — the screen is not
|
|
331
|
+
pinned to one hardcoded rate.
|
|
332
|
+
- [ ] An unknown/unsupported code (e.g. `'ZZZ'`, absent from
|
|
333
|
+
`listSupportedCurrencies()`) surfaces a clear error in the UI — NEVER a
|
|
334
|
+
silent rate of 0 (which zeroes the price) or a pass-through of 1.
|
|
335
|
+
- [ ] If a historical lookup is exposed (`options.asOf`), a past date returns
|
|
336
|
+
that day's rate (different from latest for a volatile pair), not today's.
|
|
337
|
+
- [ ] A provider/network failure surfaces gracefully (a visible error or
|
|
338
|
+
retry) and the amount is left unconverted — the app NEVER falls back to
|
|
339
|
+
converting at rate 0 or 1, which would corrupt the price shown or charged.
|
|
340
|
+
- [ ] The provider API key stays server-side — fx-rates is server-only; the
|
|
341
|
+
key never appears in the browser bundle, the network tab, or any client
|
|
342
|
+
response.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/api-fx-rates",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Foreign exchange rates core interface for molecule.dev",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
-
"dist"
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
21
22
|
],
|
|
22
23
|
"keywords": [
|
|
23
24
|
"molecule",
|
|
@@ -29,12 +30,12 @@
|
|
|
29
30
|
],
|
|
30
31
|
"license": "Apache-2.0",
|
|
31
32
|
"peerDependencies": {
|
|
32
|
-
"@molecule/api-bond": "^1.0.
|
|
33
|
-
"@molecule/api-i18n": "^1.0.
|
|
33
|
+
"@molecule/api-bond": "^1.0.1",
|
|
34
|
+
"@molecule/api-i18n": "^1.0.1"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@molecule/api-bond": "1.0.
|
|
37
|
-
"@molecule/api-i18n": "1.0.
|
|
37
|
+
"@molecule/api-bond": "1.0.1",
|
|
38
|
+
"@molecule/api-i18n": "1.0.1",
|
|
38
39
|
"@types/node": "26.1.2",
|
|
39
40
|
"typescript": "6.0.3",
|
|
40
41
|
"vitest": "4.1.10"
|