@allratestoday/sdk 1.4.5 → 1.5.0
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/CHANGELOG.md +30 -0
- package/README.md +277 -412
- package/dist/index.d.mts +118 -33
- package/dist/index.d.ts +118 -33
- package/dist/index.js +204 -98
- package/dist/index.mjs +204 -98
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -28,10 +28,13 @@ module.exports = __toCommonJS(index_exports);
|
|
|
28
28
|
var DEFAULT_BASE_URL = "https://allratestoday.com";
|
|
29
29
|
var DEFAULT_TIMEOUT = 1e4;
|
|
30
30
|
var AllRatesTodayError = class extends Error {
|
|
31
|
-
constructor(message, status) {
|
|
31
|
+
constructor(message, status, code, details, options) {
|
|
32
32
|
super(message);
|
|
33
33
|
this.name = "AllRatesTodayError";
|
|
34
34
|
this.status = status;
|
|
35
|
+
this.code = code ?? (status !== void 0 ? "http" : "invalid_argument");
|
|
36
|
+
this.details = details;
|
|
37
|
+
if (options?.cause !== void 0) this.cause = options.cause;
|
|
35
38
|
}
|
|
36
39
|
};
|
|
37
40
|
var AllRatesToday = class {
|
|
@@ -56,7 +59,9 @@ var AllRatesToday = class {
|
|
|
56
59
|
const key = override || this.apiKey;
|
|
57
60
|
if (!key) {
|
|
58
61
|
throw new AllRatesTodayError(
|
|
59
|
-
"API key is required. Register for free at https://allratestoday.com/register"
|
|
62
|
+
"API key is required. Register for free at https://allratestoday.com/register",
|
|
63
|
+
void 0,
|
|
64
|
+
"missing_key"
|
|
60
65
|
);
|
|
61
66
|
}
|
|
62
67
|
return key;
|
|
@@ -73,24 +78,82 @@ var AllRatesToday = class {
|
|
|
73
78
|
if (apiKey) {
|
|
74
79
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
75
80
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
let response;
|
|
82
|
+
try {
|
|
83
|
+
response = await fetch(url.toString(), {
|
|
84
|
+
headers,
|
|
85
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
86
|
+
});
|
|
87
|
+
} catch (err) {
|
|
88
|
+
const name = err?.name;
|
|
89
|
+
if (name === "TimeoutError" || name === "AbortError") {
|
|
90
|
+
throw new AllRatesTodayError(`Request timed out after ${this.timeout} ms`, void 0, "timeout", void 0, { cause: err });
|
|
91
|
+
}
|
|
92
|
+
throw new AllRatesTodayError(
|
|
93
|
+
`Network error: ${err?.message ?? String(err)}`,
|
|
94
|
+
void 0,
|
|
95
|
+
"network",
|
|
96
|
+
void 0,
|
|
97
|
+
{ cause: err }
|
|
98
|
+
);
|
|
99
|
+
}
|
|
80
100
|
if (!response.ok) {
|
|
81
|
-
const
|
|
101
|
+
const body = await response.json().catch(() => ({ error: response.statusText }));
|
|
102
|
+
const details = body && typeof body === "object" ? body : void 0;
|
|
82
103
|
throw new AllRatesTodayError(
|
|
83
|
-
error
|
|
84
|
-
response.status
|
|
104
|
+
details?.error || `HTTP ${response.status}`,
|
|
105
|
+
response.status,
|
|
106
|
+
"http",
|
|
107
|
+
details
|
|
85
108
|
);
|
|
86
109
|
}
|
|
87
110
|
return response.json();
|
|
88
111
|
}
|
|
89
112
|
formatDate(date) {
|
|
90
|
-
|
|
91
|
-
|
|
113
|
+
const str = date instanceof Date ? date.toISOString().split("T")[0] : date;
|
|
114
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(str) || Number.isNaN(Date.parse(`${str}T00:00:00Z`))) {
|
|
115
|
+
throw new AllRatesTodayError(`Invalid date: ${String(date)}. Use YYYY-MM-DD or a Date.`);
|
|
116
|
+
}
|
|
117
|
+
return str;
|
|
118
|
+
}
|
|
119
|
+
requireSymbols(symbols, method) {
|
|
120
|
+
if (!symbols?.length) {
|
|
121
|
+
throw new AllRatesTodayError(`${method}() needs symbols, e.g. { symbols: ['EUR'] }. Each symbol is one request.`);
|
|
92
122
|
}
|
|
93
|
-
return
|
|
123
|
+
return symbols;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Smallest history window that reaches back to `start`. Market history is
|
|
127
|
+
* served as windows ending today (7 days, 30 days, 1 year), so older dates
|
|
128
|
+
* are not available from the market-rate API.
|
|
129
|
+
*/
|
|
130
|
+
windowFor(start) {
|
|
131
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
132
|
+
const days = Math.round((Date.parse(`${today}T00:00:00Z`) - Date.parse(`${start}T00:00:00Z`)) / 864e5);
|
|
133
|
+
if (days < 0) throw new AllRatesTodayError(`Date ${start} is in the future.`);
|
|
134
|
+
if (days <= 6) return "7d";
|
|
135
|
+
if (days <= 29) return "30d";
|
|
136
|
+
if (days <= 364) return "1y";
|
|
137
|
+
throw new AllRatesTodayError(
|
|
138
|
+
`Market-rate history covers the last 365 days; ${start} is older. For older dates use official central-bank rates: GET /api/v1/central-bank/{bank}/{date} (e.g. ECB from 1999).`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
async history(source, target, period, key) {
|
|
142
|
+
const raw = await this.request(
|
|
143
|
+
"/api/historical-rates",
|
|
144
|
+
{ source, target, period },
|
|
145
|
+
key
|
|
146
|
+
);
|
|
147
|
+
return (raw.data ?? []).slice().sort((a, b) => a.timestamp - b.timestamp);
|
|
148
|
+
}
|
|
149
|
+
/** Last point on or before `date` (YYYY-MM-DD). */
|
|
150
|
+
pointOn(points, date) {
|
|
151
|
+
let hit;
|
|
152
|
+
for (const p of points) {
|
|
153
|
+
if (p.date.slice(0, 10) <= date) hit = p;
|
|
154
|
+
else break;
|
|
155
|
+
}
|
|
156
|
+
return hit;
|
|
94
157
|
}
|
|
95
158
|
// -------------------------------------------------------------------------
|
|
96
159
|
// Public API
|
|
@@ -128,89 +191,69 @@ var AllRatesToday = class {
|
|
|
128
191
|
return { base: params.source, date, rates };
|
|
129
192
|
}
|
|
130
193
|
/**
|
|
131
|
-
* Get
|
|
194
|
+
* Get mid-market rates for a past date within the last 365 days.
|
|
132
195
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
196
|
+
* One history request per symbol. Billed per request by window: dates up to
|
|
197
|
+
* 29 days ago cost 1 call per symbol, older dates (up to 1 year) cost 12,
|
|
198
|
+
* because the 1-year window is weekly points. If a day has no point, the
|
|
199
|
+
* closest earlier one is used and reported in `rateDates`.
|
|
136
200
|
*
|
|
137
|
-
*
|
|
138
|
-
* const data = await client.forDate(
|
|
139
|
-
*
|
|
140
|
-
* // With options
|
|
141
|
-
* const data = await client.forDate('2026-01-15', { base: 'EUR', symbols: ['USD', 'GBP'] });
|
|
201
|
+
* ```ts
|
|
202
|
+
* const data = await client.forDate('2026-10-01', { base: 'USD', symbols: ['EUR', 'GBP'] });
|
|
203
|
+
* // { base: 'USD', date: '2026-10-01', rates: { EUR: 0.85, GBP: 0.74 }, rateDates: { ... } }
|
|
142
204
|
* ```
|
|
143
205
|
*/
|
|
144
|
-
async forDate(date, options
|
|
145
|
-
const key = this.resolveKey(options
|
|
206
|
+
async forDate(date, options) {
|
|
207
|
+
const key = this.resolveKey(options?.apiKey);
|
|
146
208
|
const dateStr = this.formatDate(date);
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
to: dateStr
|
|
151
|
-
};
|
|
152
|
-
if (options.symbols?.length) {
|
|
153
|
-
params.target = options.symbols.join(",");
|
|
154
|
-
}
|
|
155
|
-
const raw = await this.request(
|
|
156
|
-
"/api/historical-rates",
|
|
157
|
-
params,
|
|
158
|
-
key
|
|
159
|
-
);
|
|
209
|
+
const symbols = this.requireSymbols(options?.symbols, "forDate");
|
|
210
|
+
const base = (options.base || "USD").toUpperCase();
|
|
211
|
+
const period = this.windowFor(dateStr);
|
|
160
212
|
const rates = {};
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
213
|
+
const rateDates = {};
|
|
214
|
+
for (const sym of symbols) {
|
|
215
|
+
const target = sym.toUpperCase();
|
|
216
|
+
const point = this.pointOn(await this.history(base, target, period, key), dateStr);
|
|
217
|
+
if (point) {
|
|
218
|
+
rates[target] = point.rate;
|
|
219
|
+
rateDates[target] = point.date.slice(0, 10);
|
|
164
220
|
}
|
|
165
221
|
}
|
|
166
|
-
|
|
167
|
-
rates[raw.target] = raw.current.rate;
|
|
168
|
-
}
|
|
169
|
-
return { base: params.source, date: dateStr, rates };
|
|
222
|
+
return { base, date: dateStr, rates, rateDates };
|
|
170
223
|
}
|
|
171
224
|
/**
|
|
172
|
-
*
|
|
225
|
+
* Daily mid-market rates between two dates within the last 365 days.
|
|
173
226
|
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* symbols: ['EUR', 'GBP'],
|
|
178
|
-
* });
|
|
227
|
+
* One history request per symbol. Ranges starting up to 29 days ago return
|
|
228
|
+
* daily points (1 call per symbol); older starts use the 1-year window,
|
|
229
|
+
* which has weekly points and costs 12 calls per symbol.
|
|
179
230
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
231
|
+
* ```ts
|
|
232
|
+
* const data = await client.timeSeries('2026-10-01', '2026-10-10', { symbols: ['EUR', 'GBP'] });
|
|
233
|
+
* console.log(data.rates['2026-10-05']); // { EUR: 0.86, GBP: 0.75 }
|
|
182
234
|
* ```
|
|
183
235
|
*/
|
|
184
|
-
async timeSeries(startDate, endDate, options
|
|
185
|
-
const key = this.resolveKey(options
|
|
236
|
+
async timeSeries(startDate, endDate, options) {
|
|
237
|
+
const key = this.resolveKey(options?.apiKey);
|
|
186
238
|
const start = this.formatDate(startDate);
|
|
187
239
|
const end = this.formatDate(endDate);
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
to: end
|
|
193
|
-
};
|
|
194
|
-
if (options.symbols?.length) {
|
|
195
|
-
params.target = options.symbols.join(",");
|
|
196
|
-
}
|
|
197
|
-
const raw = await this.request(
|
|
198
|
-
"/api/historical-rates",
|
|
199
|
-
params,
|
|
200
|
-
key
|
|
201
|
-
);
|
|
240
|
+
if (start > end) throw new AllRatesTodayError(`startDate ${start} is after endDate ${end}.`);
|
|
241
|
+
const symbols = this.requireSymbols(options?.symbols, "timeSeries");
|
|
242
|
+
const base = (options.base || "USD").toUpperCase();
|
|
243
|
+
const period = this.windowFor(start);
|
|
202
244
|
const rates = {};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
245
|
+
for (const sym of symbols) {
|
|
246
|
+
const target = sym.toUpperCase();
|
|
247
|
+
for (const point of await this.history(base, target, period, key)) {
|
|
248
|
+
const day = point.date.slice(0, 10);
|
|
249
|
+
if (day < start || day > end) continue;
|
|
250
|
+
(rates[day] ?? (rates[day] = {}))[target] = point.rate;
|
|
208
251
|
}
|
|
209
252
|
}
|
|
210
|
-
return { base
|
|
253
|
+
return { base, startDate: start, endDate: end, rates };
|
|
211
254
|
}
|
|
212
255
|
/**
|
|
213
|
-
* Get all supported currency
|
|
256
|
+
* Get all supported currency codes and names. **No API key needed.**
|
|
214
257
|
*
|
|
215
258
|
* ```ts
|
|
216
259
|
* const { symbols } = await client.symbols();
|
|
@@ -222,8 +265,15 @@ var AllRatesToday = class {
|
|
|
222
265
|
* ```
|
|
223
266
|
*/
|
|
224
267
|
async symbols(options = {}) {
|
|
225
|
-
const
|
|
226
|
-
|
|
268
|
+
const raw = await this.request(
|
|
269
|
+
"/api/v1/symbols",
|
|
270
|
+
{},
|
|
271
|
+
options.apiKey
|
|
272
|
+
);
|
|
273
|
+
const currencies = raw.currencies ?? [];
|
|
274
|
+
const symbols = {};
|
|
275
|
+
for (const c of currencies) symbols[c.code] = c.name;
|
|
276
|
+
return { symbols, currencies, count: raw.count ?? currencies.length };
|
|
227
277
|
}
|
|
228
278
|
/**
|
|
229
279
|
* Latest table of OFFICIAL rates published by a central bank or tax
|
|
@@ -236,12 +286,12 @@ var AllRatesToday = class {
|
|
|
236
286
|
* const table = await client.officialRates('ecb');
|
|
237
287
|
* console.log(table.rate_date, table.rates);
|
|
238
288
|
*
|
|
239
|
-
* // One pair —
|
|
289
|
+
* // One pair — published as-is or the inverse of a published rate (`derived: true`)
|
|
240
290
|
* const usd = await client.officialRates('ecb', { source: 'EUR', target: 'USD' });
|
|
241
291
|
* console.log(`ECB ${usd.rate_date}: 1 EUR = ${usd.rate} USD`);
|
|
242
292
|
* ```
|
|
243
293
|
*
|
|
244
|
-
*
|
|
294
|
+
* Cross pairs and past dates need a key: use `officialRatesOn()`.
|
|
245
295
|
*/
|
|
246
296
|
async officialRates(bank, options = {}) {
|
|
247
297
|
const code = bank.trim().toLowerCase();
|
|
@@ -262,6 +312,39 @@ var AllRatesToday = class {
|
|
|
262
312
|
options.apiKey
|
|
263
313
|
);
|
|
264
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Official table (or one pair) as published on a given date. Needs an API
|
|
317
|
+
* key; 1 call. On weekends and holidays the last earlier publication is
|
|
318
|
+
* returned (`published_on_requested_date: false`). Unlike the keyless
|
|
319
|
+
* `officialRates()`, any pair inside the table can be cross-computed
|
|
320
|
+
* (`method: 'cross'`), e.g. USD/GBP from the ECB's EUR table.
|
|
321
|
+
*
|
|
322
|
+
* ```ts
|
|
323
|
+
* const r = await client.officialRatesOn('rbi', '2026-09-30', { source: 'USD', target: 'INR' });
|
|
324
|
+
* console.log(r.rate_date, r.rate); // 2026-09-30 95.9832
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
async officialRatesOn(bank, date, options = {}) {
|
|
328
|
+
const key = this.resolveKey(options.apiKey);
|
|
329
|
+
const code = bank.trim().toLowerCase();
|
|
330
|
+
if (!/^[a-z0-9_-]{2,20}$/.test(code)) {
|
|
331
|
+
throw new AllRatesTodayError(`Invalid source code: ${bank}. Call officialSources() for the list.`);
|
|
332
|
+
}
|
|
333
|
+
if (Boolean(options.source) !== Boolean(options.target)) {
|
|
334
|
+
throw new AllRatesTodayError("Provide both source and target, or neither.");
|
|
335
|
+
}
|
|
336
|
+
const dateStr = this.formatDate(date);
|
|
337
|
+
const params = {};
|
|
338
|
+
if (options.source && options.target) {
|
|
339
|
+
params.source = options.source.toUpperCase();
|
|
340
|
+
params.target = options.target.toUpperCase();
|
|
341
|
+
}
|
|
342
|
+
return this.request(
|
|
343
|
+
`/api/v1/central-bank/${encodeURIComponent(code)}/${dateStr}`,
|
|
344
|
+
params,
|
|
345
|
+
key
|
|
346
|
+
);
|
|
347
|
+
}
|
|
265
348
|
/**
|
|
266
349
|
* Every covered official source with its code, name, latest publication
|
|
267
350
|
* date and whether it is behind schedule. **No API key needed.**
|
|
@@ -290,7 +373,20 @@ var AllRatesToday = class {
|
|
|
290
373
|
const key = this.resolveKey(options.apiKey);
|
|
291
374
|
const params = { source: from, target: to };
|
|
292
375
|
if (amount !== void 0) params.amount = String(amount);
|
|
293
|
-
|
|
376
|
+
const raw = await this.request("/api/v1/rates", params, key);
|
|
377
|
+
if (Array.isArray(raw) || !("from" in raw)) {
|
|
378
|
+
const r = Array.isArray(raw) ? raw[0] : raw;
|
|
379
|
+
if (!r) throw new AllRatesTodayError(`No rate returned for ${from}/${to}`);
|
|
380
|
+
const qty = amount ?? 1;
|
|
381
|
+
return {
|
|
382
|
+
from: { currency: r.source, amount: qty },
|
|
383
|
+
to: { currency: r.target, amount: Number((qty * r.rate).toFixed(6)) },
|
|
384
|
+
rate: r.rate,
|
|
385
|
+
source: "interbank",
|
|
386
|
+
time: r.time
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return raw;
|
|
294
390
|
}
|
|
295
391
|
/**
|
|
296
392
|
* Get authenticated exchange rates with metadata.
|
|
@@ -302,10 +398,12 @@ var AllRatesToday = class {
|
|
|
302
398
|
*/
|
|
303
399
|
async getRates(source, target, options = {}) {
|
|
304
400
|
const key = this.resolveKey(options.apiKey);
|
|
305
|
-
|
|
401
|
+
const raw = await this.request("/api/v1/rates", { source, target }, key);
|
|
402
|
+
return Array.isArray(raw) ? raw : [raw];
|
|
306
403
|
}
|
|
307
404
|
/**
|
|
308
|
-
*
|
|
405
|
+
* Mid-market history for a preset window ending today: `1d` (hourly),
|
|
406
|
+
* `7d` / `30d` (daily) or `1y` (weekly). `1y` is billed as 12 calls.
|
|
309
407
|
*
|
|
310
408
|
* ```ts
|
|
311
409
|
* const history = await client.getHistoricalRates('USD', 'EUR', '30d');
|
|
@@ -316,11 +414,17 @@ var AllRatesToday = class {
|
|
|
316
414
|
*/
|
|
317
415
|
async getHistoricalRates(source, target, period = "7d", options = {}) {
|
|
318
416
|
const key = this.resolveKey(options.apiKey);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
417
|
+
const data = await this.history(source, target, period, key);
|
|
418
|
+
const rates = data.map((p) => ({ rate: p.rate, time: p.date }));
|
|
419
|
+
const last = rates[rates.length - 1];
|
|
420
|
+
return {
|
|
421
|
+
source: source.toUpperCase(),
|
|
422
|
+
target: target.toUpperCase(),
|
|
423
|
+
period,
|
|
424
|
+
current: last ?? { rate: 0, time: "" },
|
|
425
|
+
rates,
|
|
426
|
+
data
|
|
427
|
+
};
|
|
324
428
|
}
|
|
325
429
|
/**
|
|
326
430
|
* Convert an amount between two currencies.
|
|
@@ -339,19 +443,21 @@ var AllRatesToday = class {
|
|
|
339
443
|
const key = this.resolveKey(options.apiKey);
|
|
340
444
|
if (options.date) {
|
|
341
445
|
const dateStr = this.formatDate(options.date);
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
to
|
|
446
|
+
const target = to.toUpperCase();
|
|
447
|
+
const day = await this.forDate(dateStr, { base: from, symbols: [target], apiKey: key });
|
|
448
|
+
const rate = day.rates[target];
|
|
449
|
+
if (rate === void 0) {
|
|
450
|
+
throw new AllRatesTodayError(`No ${from}/${to} rate available on or before ${dateStr}`);
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
from,
|
|
454
|
+
to,
|
|
455
|
+
amount,
|
|
456
|
+
result: Number((amount * rate).toFixed(6)),
|
|
457
|
+
rate,
|
|
458
|
+
date: dateStr,
|
|
459
|
+
rateDate: day.rateDates[target]
|
|
347
460
|
};
|
|
348
|
-
const raw = await this.request(
|
|
349
|
-
"/api/historical-rates",
|
|
350
|
-
params,
|
|
351
|
-
key
|
|
352
|
-
);
|
|
353
|
-
const rate = raw.current?.rate ?? raw.rates?.[0]?.rate ?? 0;
|
|
354
|
-
return { from, to, amount, result: Number((amount * rate).toFixed(6)), rate, date: dateStr };
|
|
355
461
|
}
|
|
356
462
|
const data = await this.request(
|
|
357
463
|
"/api/v1/rates",
|