@molecule/api-fx-rates 1.0.0 → 1.0.2

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.
Files changed (2) hide show
  1. package/README.md +342 -0
  2. package/package.json +9 -8
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.0",
3
+ "version": "1.0.2",
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,22 +30,22 @@
29
30
  ],
30
31
  "license": "Apache-2.0",
31
32
  "peerDependencies": {
32
- "@molecule/api-bond": "^1.0.0",
33
- "@molecule/api-i18n": "^1.0.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.0",
37
- "@molecule/api-i18n": "1.0.0",
37
+ "@molecule/api-bond": "1.0.2",
38
+ "@molecule/api-i18n": "1.0.3",
38
39
  "@types/node": "26.1.2",
39
40
  "typescript": "6.0.3",
40
- "vitest": "4.1.10"
41
+ "vitest": "4.1.11"
41
42
  },
42
43
  "repository": {
43
44
  "type": "git",
44
45
  "url": "https://github.com/molecule-dev/molecule.git",
45
46
  "directory": "packages/api/core/fx-rates"
46
47
  },
47
- "homepage": "https://github.com/molecule-dev/molecule/tree/main/packages/api/core/fx-rates",
48
+ "homepage": "https://www.molecule.dev/packages/api-fx-rates",
48
49
  "bugs": "https://github.com/molecule-dev/molecule/issues",
49
50
  "publishConfig": {
50
51
  "access": "public"