easy-currencies 1.10.2 → 2.0.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.
@@ -1,6 +1,8 @@
1
+ import { RetryOptions } from "./parts/requester";
1
2
  import { Provider, ProviderReference } from "./parts/providers";
2
- import { Config, ProxyConfiguration } from "./parts/config";
3
+ import { Config } from "./parts/config";
3
4
  export { Chainer as Convert } from "./parts/chainer";
5
+ import { HttpClient } from "./parts/client";
4
6
  /**
5
7
  * A simple map object for rates
6
8
  *
@@ -18,6 +20,17 @@ export interface rateObject {
18
20
  * as before.
19
21
  */
20
22
  export declare const RATES_BASE: unique symbol;
23
+ /**
24
+ * The enumerable twin of `RATES_BASE`.
25
+ *
26
+ * A symbol is invisible to `JSON.stringify`, spread, `Object.assign` and
27
+ * `structuredClone`, so a table that has been through a cache loses the marker
28
+ * and a conversion against the wrong base is then accepted silently. That is
29
+ * the caching workflow this guard exists for, so the base also travels as an
30
+ * ordinary key. Underscores are not valid in a currency code, see
31
+ * `CURRENCY_CODE`, so this can never collide with a rate.
32
+ */
33
+ export declare const RATES_BASE_KEY = "__base";
21
34
  /**
22
35
  * Regular converter class definition.
23
36
  *
@@ -32,6 +45,22 @@ export declare class Converter {
32
45
  * @memberof Converter
33
46
  */
34
47
  config: Config;
48
+ /**
49
+ * Called with each handled provider error before the next provider is tried.
50
+ *
51
+ * A library does not own the consumer's stderr, so this is the single point
52
+ * where that reporting happens. It defaults to the previous behaviour;
53
+ * assign your own to route the errors elsewhere, or assign a no-op to
54
+ * silence them entirely.
55
+ *
56
+ * @example
57
+ * const converter = new Converter();
58
+ * converter.onError = () => {}; // silence
59
+ * converter.onError = (e) => logger.warn(e); // or route
60
+ *
61
+ * @memberof Converter
62
+ */
63
+ onError: (error: unknown) => void;
35
64
  /**
36
65
  * Creates an instance of Converter.
37
66
  * @param {(...ProviderReference[] | undefined[] | string[])} config
@@ -48,15 +77,43 @@ export declare class Converter {
48
77
  get providers(): Provider[];
49
78
  get active(): Provider[];
50
79
  add: Config["add"];
51
- addProvider: Config["add"];
52
80
  addMultiple: Config["addMultiple"];
53
- addMultipleProviders: Config["addMultiple"];
54
81
  remove: Config["remove"];
55
82
  /**
56
- * Method to set the proxy configuration.
57
- * @param proxyConfiguration The proxy configuration.
83
+ * Replaces the HTTP client.
84
+ *
85
+ * The default client uses the global fetch, which has no proxy option, so
86
+ * proxying (or a custom agent, or instrumentation) means supplying your own.
87
+ *
88
+ * @example
89
+ * import { ProxyAgent } from "undici";
90
+ * const dispatcher = new ProxyAgent("http://proxy:8080");
91
+ * converter.setClient({
92
+ * get: (url) =>
93
+ * fetch(url, { dispatcher } as any).then(async (r) => ({
94
+ * status: r.status,
95
+ * data: await r.json()
96
+ * }))
97
+ * });
98
+ *
99
+ * @param {HttpClient} client - the client to use
100
+ */
101
+ setClient: (client: HttpClient) => void;
102
+ /**
103
+ * Tunes retries and the time a conversion may take.
104
+ *
105
+ * `budgetMs` is wall clock for the whole call, spent across every provider
106
+ * rather than reset for each one, and it covers the requests themselves: a
107
+ * client that never settles cannot hold a conversion open past it. Fields
108
+ * merge, so one can be set without restating the rest.
109
+ *
110
+ * @example
111
+ * converter.setRetryOptions({ budgetMs: 5000 }); // an HTTP handler
112
+ * converter.setRetryOptions({ maxRetries: 0 }); // never retry a 429
113
+ *
114
+ * @param {RetryOptions} options - the tuning to apply
58
115
  */
59
- setProxyConfiguration: (proxyConfiguration: ProxyConfiguration) => void;
116
+ setRetryOptions: (options: RetryOptions) => void;
60
117
  /**
61
118
  * Conversion function (non chainable).
62
119
  *
@@ -69,23 +126,35 @@ export declare class Converter {
69
126
  * @param {string} from - base currency
70
127
  * @param {string} to - conversion currency
71
128
  * @param {any} rates - conversion rates, if they were pre-fetched
129
+ * @throws {Error} - if the amount is not finite, or a currency is missing
72
130
  * @returns {Promise<number>} - converted amount
73
131
  */
74
132
  convert: (amount: number, from: string, to: string, rates?: any) => Promise<number>;
75
133
  /**
76
134
  * Performs safe multiplication to get the result amount.
135
+ *
136
+ * A usable rate is finite and greater than zero; anything else multiplies
137
+ * into a plausible-looking wrong amount.
138
+ *
77
139
  * @param {number} amount - amount to be converted
78
140
  * @param {string} to - conversion currency
79
141
  * @param {any} rates - conversion rates, if they were pre-fetched
80
- * @returns
142
+ * @throws {Error} - if the amount, the currency, the rates or the rate is invalid
143
+ * @returns {number} - converted amount
81
144
  */
82
145
  convertRate: (amount: number, to: string, rates?: any) => number;
83
146
  /**
84
- * Rate fetch function
147
+ * Rate fetch function.
148
+ *
149
+ * Walks a snapshot of the chain, so concurrent calls do not shrink each
150
+ * other's list. A failure applies to the call, not the chain: no provider is
151
+ * removed, so one unknown currency cannot degrade a long-lived converter.
152
+ *
85
153
  * @param {string} from - base currency
86
154
  * @param {string} to - conversion currency
87
155
  * @param {boolean} multiple - determines conversion mode
88
- * @returns
156
+ * @throws {Error} - if a currency is missing, or every provider failed
157
+ * @returns {Promise<rateObject>} - the fetched rates
89
158
  */
90
159
  getRates: (from: string, to: string, multiple?: boolean) => Promise<rateObject>;
91
160
  }
package/dist/converter.js CHANGED
@@ -8,17 +8,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.Converter = exports.RATES_BASE = exports.Convert = void 0;
12
+ exports.Converter = exports.RATES_BASE_KEY = exports.RATES_BASE = exports.Convert = void 0;
16
13
  const requester_1 = require("./parts/requester");
17
14
  const config_1 = require("./parts/config");
18
15
  var chainer_1 = require("./parts/chainer");
19
16
  Object.defineProperty(exports, "Convert", { enumerable: true, get: function () { return chainer_1.Chainer; } });
20
17
  const utils_1 = require("./parts/utils");
21
- const axios_1 = __importDefault(require("axios"));
22
18
  /**
23
19
  * Marks a rate map with the base currency it was fetched for.
24
20
  *
@@ -27,6 +23,149 @@ const axios_1 = __importDefault(require("axios"));
27
23
  * as before.
28
24
  */
29
25
  exports.RATES_BASE = Symbol.for("easy-currencies.ratesBase");
26
+ /**
27
+ * The enumerable twin of `RATES_BASE`.
28
+ *
29
+ * A symbol is invisible to `JSON.stringify`, spread, `Object.assign` and
30
+ * `structuredClone`, so a table that has been through a cache loses the marker
31
+ * and a conversion against the wrong base is then accepted silently. That is
32
+ * the caching workflow this guard exists for, so the base also travels as an
33
+ * ordinary key. Underscores are not valid in a currency code, see
34
+ * `CURRENCY_CODE`, so this can never collide with a rate.
35
+ */
36
+ exports.RATES_BASE_KEY = "__base";
37
+ /** The base a table was fetched for, or undefined for one built by hand. */
38
+ function baseOf(rates) {
39
+ var _a;
40
+ const marked = (_a = rates[exports.RATES_BASE]) !== null && _a !== void 0 ? _a : rates[exports.RATES_BASE_KEY];
41
+ return typeof marked === "string" ? marked : undefined;
42
+ }
43
+ /** A rejection that is not contract-shaped is a bug, not a provider failure, and must not trigger fallback. */
44
+ function isProviderFailure(value) {
45
+ return (typeof value === "object" &&
46
+ value !== null &&
47
+ typeof value.handled === "boolean");
48
+ }
49
+ /**
50
+ * Providers describe failures with strings and numeric codes; consumers write
51
+ * `catch (e) { log(e.message) }`. Wrap so that reads something, keeping the
52
+ * original on `cause`.
53
+ */
54
+ function asError(value) {
55
+ if (value instanceof Error) {
56
+ return value;
57
+ }
58
+ const message = typeof value === "string" && value.length > 0
59
+ ? value
60
+ : `Rate provider failed: ${String(value)}`;
61
+ const error = new Error(message);
62
+ error.cause = value;
63
+ return error;
64
+ }
65
+ /** Describes a rejected rate without dumping the whole object into the message. */
66
+ function describeRate(value) {
67
+ if (typeof value === "string") {
68
+ return JSON.stringify(value);
69
+ }
70
+ if (value === null || typeof value !== "object") {
71
+ return String(value);
72
+ }
73
+ return Array.isArray(value) ? "an array" : "an object";
74
+ }
75
+ /** A currency library must never hand back NaN. */
76
+ function requireAmount(amount) {
77
+ if (typeof amount !== "number" || !Number.isFinite(amount)) {
78
+ throw new Error(`Conversion amount must be a finite number, received ${describeRate(amount)}.`);
79
+ }
80
+ return amount;
81
+ }
82
+ /**
83
+ * Asserts that a currency argument is a non-empty string.
84
+ *
85
+ * Without this, a missing currency reached the vendor as the literal
86
+ * `undefined` and came back as a confusing "Currency not found".
87
+ *
88
+ * @param {unknown} currency - the value to check
89
+ * @param {string} label - the parameter name, so the error says which is missing
90
+ * @returns {string} - the validated currency
91
+ */
92
+ /**
93
+ * Every code the configured providers accept is alphanumeric: ISO 4217 is three
94
+ * letters, and the crypto tickers they carry include `USDT`, `1INCH`, `0G` and
95
+ * `00`, so neither "starts with a letter" nor "letters only" holds. AlphaVantage
96
+ * publishes `SBDf`, so the check is case-insensitive.
97
+ */
98
+ const CURRENCY_CODE = /^[A-Za-z0-9]{1,16}$/;
99
+ function requireCurrency(currency, label) {
100
+ if (typeof currency !== "string" || currency.trim().length === 0) {
101
+ throw new Error(`The '${label}' currency must be a non-empty string, received ${describeRate(currency)}.`);
102
+ }
103
+ // Encoding alone does not close this: `.` is unreserved in RFC 3986, so
104
+ // encodeURIComponent("..") is "..", and the URL parser then resolves the dot
105
+ // segment and climbs the path. Rejecting the value is what actually stops it.
106
+ if (!CURRENCY_CODE.test(currency)) {
107
+ throw new Error(`The '${label}' currency must be alphanumeric, received ${describeRate(currency)}.`);
108
+ }
109
+ return currency;
110
+ }
111
+ /**
112
+ * Whether a table holds at least one entry that will actually convert.
113
+ *
114
+ * Uses the same coercion as `convertRate`, so a table that passes here cannot
115
+ * fail every lookup for want of a usable value.
116
+ */
117
+ function hasUsableRate(rates) {
118
+ return Object.keys(rates).some((key) => {
119
+ const raw = rates[key];
120
+ const value = typeof raw === "number" || typeof raw === "string" ? Number(raw) : NaN;
121
+ return Number.isFinite(value) && value > 0;
122
+ });
123
+ }
124
+ /**
125
+ * Reads the rates out of a successful response.
126
+ *
127
+ * Returns the failure rather than throwing, so the caller can move to the next
128
+ * provider: a vendor answering for a different base, or a handler that trips on
129
+ * an unexpected shape, says nothing about the providers behind it.
130
+ */
131
+ function readRates(provider, data, from) {
132
+ // Some vendors truncate an unrecognised code to a valid prefix and answer for
133
+ // that instead: exchangerate-api turns "CNYqqqwwC" into "CNY".
134
+ const echoed = data && (data.base || data.source);
135
+ if (typeof echoed === "string" && echoed.toLowerCase() !== from.toLowerCase()) {
136
+ return [
137
+ new Error(`Provider answered for base '${echoed}', not the requested '${from}'.`),
138
+ {}
139
+ ];
140
+ }
141
+ let rates;
142
+ try {
143
+ rates = provider.handler(data);
144
+ }
145
+ catch (e) {
146
+ return [asError(e), {}];
147
+ }
148
+ if (!rates || typeof rates !== "object") {
149
+ return [new Error("Provider returned no usable rates."), {}];
150
+ }
151
+ // Three of the built-in handlers return {} by design when the body is not the
152
+ // shape they expect, and FloatRates turns a junk 200 into {undefined: NaN}.
153
+ // Treating that as a successful fetch of zero rates stops the chain with
154
+ // healthy providers still behind it, and hands raw mode a garbage table.
155
+ if (!hasUsableRate(rates)) {
156
+ return [new Error("Provider returned a rate table with no usable rates."), {}];
157
+ }
158
+ // Stamp a copy, not the handler's own object. A handler may return a frozen
159
+ // table, where writing throws, or a memoised one, where a later fetch for a
160
+ // different base rewrites the marker under a caller still holding it.
161
+ const marked = Object.assign(Object.assign({}, rates), { [exports.RATES_BASE_KEY]: from });
162
+ Object.defineProperty(marked, exports.RATES_BASE, {
163
+ value: from,
164
+ enumerable: false,
165
+ configurable: true
166
+ });
167
+ return [null, marked];
168
+ }
30
169
  /**
31
170
  * Regular converter class definition.
32
171
  *
@@ -41,11 +180,61 @@ class Converter {
41
180
  */
42
181
  constructor(...config) {
43
182
  /**
44
- * Method to set the proxy configuration.
45
- * @param proxyConfiguration The proxy configuration.
183
+ * Called with each handled provider error before the next provider is tried.
184
+ *
185
+ * A library does not own the consumer's stderr, so this is the single point
186
+ * where that reporting happens. It defaults to the previous behaviour;
187
+ * assign your own to route the errors elsewhere, or assign a no-op to
188
+ * silence them entirely.
189
+ *
190
+ * @example
191
+ * const converter = new Converter();
192
+ * converter.onError = () => {}; // silence
193
+ * converter.onError = (e) => logger.warn(e); // or route
194
+ *
195
+ * @memberof Converter
196
+ */
197
+ this.onError = (error) => {
198
+ console.error(error);
199
+ };
200
+ /**
201
+ * Replaces the HTTP client.
202
+ *
203
+ * The default client uses the global fetch, which has no proxy option, so
204
+ * proxying (or a custom agent, or instrumentation) means supplying your own.
205
+ *
206
+ * @example
207
+ * import { ProxyAgent } from "undici";
208
+ * const dispatcher = new ProxyAgent("http://proxy:8080");
209
+ * converter.setClient({
210
+ * get: (url) =>
211
+ * fetch(url, { dispatcher } as any).then(async (r) => ({
212
+ * status: r.status,
213
+ * data: await r.json()
214
+ * }))
215
+ * });
216
+ *
217
+ * @param {HttpClient} client - the client to use
218
+ */
219
+ this.setClient = (client) => {
220
+ this.config.setClient(client);
221
+ };
222
+ /**
223
+ * Tunes retries and the time a conversion may take.
224
+ *
225
+ * `budgetMs` is wall clock for the whole call, spent across every provider
226
+ * rather than reset for each one, and it covers the requests themselves: a
227
+ * client that never settles cannot hold a conversion open past it. Fields
228
+ * merge, so one can be set without restating the rest.
229
+ *
230
+ * @example
231
+ * converter.setRetryOptions({ budgetMs: 5000 }); // an HTTP handler
232
+ * converter.setRetryOptions({ maxRetries: 0 }); // never retry a 429
233
+ *
234
+ * @param {RetryOptions} options - the tuning to apply
46
235
  */
47
- this.setProxyConfiguration = (proxyConfiguration) => {
48
- this.config.setClient(axios_1.default.create({ proxy: proxyConfiguration }));
236
+ this.setRetryOptions = (options) => {
237
+ this.config.setRetryOptions(options);
49
238
  };
50
239
  /**
51
240
  * Conversion function (non chainable).
@@ -59,13 +248,19 @@ class Converter {
59
248
  * @param {string} from - base currency
60
249
  * @param {string} to - conversion currency
61
250
  * @param {any} rates - conversion rates, if they were pre-fetched
251
+ * @throws {Error} - if the amount is not finite, or a currency is missing
62
252
  * @returns {Promise<number>} - converted amount
63
253
  */
64
254
  this.convert = (amount_1, from_1, to_1, ...args_1) => __awaiter(this, [amount_1, from_1, to_1, ...args_1], void 0, function* (amount, from, to, rates = undefined) {
255
+ // Validating before anything is requested: an unset amount used to come
256
+ // back as NaN, and an unset currency reached the vendor as "undefined".
257
+ requireAmount(amount);
258
+ requireCurrency(from, "from");
259
+ requireCurrency(to, "to");
65
260
  // Returining conversion from provided rates
66
- if (typeof rates !== "undefined") {
67
- const base = rates[exports.RATES_BASE];
68
- if (typeof base === "string" &&
261
+ if (rates !== undefined && rates !== null) {
262
+ const base = baseOf(rates);
263
+ if (base !== undefined &&
69
264
  typeof from === "string" &&
70
265
  base.toLowerCase() !== from.toLowerCase()) {
71
266
  throw new Error(`Rates were fetched for base '${base}', but conversion asked for '${from}'.`);
@@ -75,7 +270,7 @@ class Converter {
75
270
  //Fetching conversion rates from the active provider
76
271
  const [err, data] = yield (0, utils_1._to)(this.getRates(from, to, false));
77
272
  if (err) {
78
- throw err;
273
+ throw asError(err);
79
274
  }
80
275
  if (!data || Object.keys(data).length == 0) {
81
276
  throw new Error("No data returned for rate fetch.");
@@ -85,86 +280,120 @@ class Converter {
85
280
  });
86
281
  /**
87
282
  * Performs safe multiplication to get the result amount.
283
+ *
284
+ * A usable rate is finite and greater than zero; anything else multiplies
285
+ * into a plausible-looking wrong amount.
286
+ *
88
287
  * @param {number} amount - amount to be converted
89
288
  * @param {string} to - conversion currency
90
289
  * @param {any} rates - conversion rates, if they were pre-fetched
91
- * @returns
290
+ * @throws {Error} - if the amount, the currency, the rates or the rate is invalid
291
+ * @returns {number} - converted amount
92
292
  */
93
293
  this.convertRate = (amount, to, rates = undefined) => {
94
- const keys = Object.keys(rates);
95
- const rateKey = keys.find(key => key.toLowerCase() === to.toLowerCase());
96
- const rate = rateKey ? rates[rateKey] : undefined;
97
- if (!rate) {
98
- throw new Error(`No '${to}' present in rates: ${JSON.stringify(rates, null, 2)}`);
294
+ var _a;
295
+ requireAmount(amount);
296
+ requireCurrency(to, "to");
297
+ if (typeof rates !== "object" || rates === null) {
298
+ throw new Error(`Rates must be an object mapping currency to rate, received ${describeRate(rates)}.`);
299
+ }
300
+ // The base marker rides along as an ordinary key; it is not a rate, so it
301
+ // is neither a lookup candidate nor part of the "N rates available" count.
302
+ const keys = Object.keys(rates).filter((key) => key !== exports.RATES_BASE_KEY);
303
+ // Exact match wins, so a response carrying both "EUR" and "eur" does not
304
+ // resolve differently depending on JSON key order.
305
+ const rateKey = (_a = keys.find((key) => key === to)) !== null && _a !== void 0 ? _a : keys.find((key) => key.toLowerCase() === to.toLowerCase());
306
+ // Truthiness would report a present-but-zero rate as missing.
307
+ if (rateKey === undefined) {
308
+ // The full table is ~4KB in production.
309
+ throw new Error(`No '${to}' present in rates (${keys.length} rate${keys.length === 1 ? "" : "s"} available).`);
310
+ }
311
+ const raw = rates[rateKey];
312
+ // Number(["0.9"]) === 0.9, so check the type before the value.
313
+ const numericRate = typeof raw === "number" || typeof raw === "string" ? Number(raw) : NaN;
314
+ if (!Number.isFinite(numericRate) || numericRate <= 0) {
315
+ throw new Error(`Invalid rate value for '${to}': expected a finite number greater than zero, received ${describeRate(raw)}.`);
99
316
  }
100
- const numericRate = parseFloat(rate);
101
- if (isNaN(numericRate)) {
102
- throw new Error(`Invalid rate value for '${to}': ${rate}`);
317
+ const converted = amount * numericRate;
318
+ // Both operands are already finite and the rate is positive, so a result
319
+ // that is not finite overflowed and a result of zero from a non-zero amount
320
+ // underflowed. Either way the product is not the answer, and returning it
321
+ // hands back a wrong number that looks like a right one.
322
+ if (!Number.isFinite(converted) || (converted === 0 && amount !== 0)) {
323
+ throw new Error(`Converting ${amount} at rate ${numericRate} is not representable as a number.`);
103
324
  }
104
- return amount * numericRate;
325
+ return converted;
105
326
  };
106
327
  /**
107
- * Rate fetch function
328
+ * Rate fetch function.
329
+ *
330
+ * Walks a snapshot of the chain, so concurrent calls do not shrink each
331
+ * other's list. A failure applies to the call, not the chain: no provider is
332
+ * removed, so one unknown currency cannot degrade a long-lived converter.
333
+ *
108
334
  * @param {string} from - base currency
109
335
  * @param {string} to - conversion currency
110
336
  * @param {boolean} multiple - determines conversion mode
111
- * @returns
337
+ * @throws {Error} - if a currency is missing, or every provider failed
338
+ * @returns {Promise<rateObject>} - the fetched rates
112
339
  */
113
- this.getRates = (from_2, to_2, ...args_2) => __awaiter(this, [from_2, to_2, ...args_2], void 0, function* (from, to, multiple = false) {
114
- // Getting the current active provider
115
- const provider = this.config.activeProvider();
340
+ this.getRates = (from_1, to_1, ...args_1) => __awaiter(this, [from_1, to_1, ...args_1], void 0, function* (from, to, multiple = false) {
341
+ requireCurrency(from, "from");
342
+ // In multiple mode the whole table is fetched for the base currency, so
343
+ // there is no target currency to validate.
344
+ if (!multiple) {
345
+ requireCurrency(to, "to");
346
+ }
347
+ // Per-call snapshot of the fallback chain.
348
+ const chain = [...this.config.providers];
349
+ if (chain.length === 0) {
350
+ throw new Error("No rate providers are configured.");
351
+ }
116
352
  // Getting the client
117
353
  const client = this.config.getClient();
118
- // Fetching conversion rates from the active provider.
119
- const [err, data] = yield (0, utils_1._to)((0, requester_1.fetchRates)(client, provider, {
120
- FROM: from,
121
- TO: to,
122
- multiple: multiple
123
- }));
124
- // error handling:
125
- // if the error is not in the registered list of errors (is undefined), then throw.
126
- // if the error is in the list, but there are no backup providers, then throw.
127
- // if the error is in the list and there is a backup, log the error and continue.
128
- if (!err) {
129
- // Some vendors truncate an unrecognised code to a valid prefix and answer
130
- // for that instead: exchangerate-api turns "CNYqqqwwC" into "CNY".
131
- const echoed = data && (data.base || data.source);
132
- if (typeof echoed === "string" &&
133
- typeof from === "string" &&
134
- echoed.toLowerCase() !== from.toLowerCase()) {
135
- throw new Error(`Provider answered for base '${echoed}', not the requested '${from}'.`);
354
+ // One deadline for the whole chain. Per provider it would let a
355
+ // three-provider chain spend three budgets, which is how a rate-limited
356
+ // conversion reached 102 seconds.
357
+ const retry = this.config.getRetryOptions();
358
+ const deadline = (0, requester_1.deadlineFrom)(retry);
359
+ let lastError = new Error("No rate providers were tried.");
360
+ for (let index = 0; index < chain.length; index++) {
361
+ const provider = chain[index];
362
+ // A whole-table fetch has no target currency, so a provider whose template
363
+ // needs one cannot serve it: the request would go out asking for an empty
364
+ // symbol list. That is this provider's limitation, so move to the next.
365
+ if (multiple && provider.endpoint.single.includes("%TO%")) {
366
+ lastError = new Error("Provider cannot fetch a whole rate table; it requires a target currency.");
367
+ this.onError(lastError);
368
+ continue;
136
369
  }
137
- const rates = provider.handler(data);
138
- if (rates && typeof rates === "object") {
139
- Object.defineProperty(rates, exports.RATES_BASE, {
140
- value: from,
141
- enumerable: false,
142
- configurable: true
143
- });
370
+ const [err, data] = yield (0, utils_1._to)((0, requester_1.fetchRates)(client, provider, { FROM: from, TO: to, multiple: multiple }, Object.assign(Object.assign({}, retry), { deadline })));
371
+ if (!err) {
372
+ // A provider answering wrongly, or a handler throwing on a shape it did
373
+ // not expect, is that provider's failure. Aborting here would give up on
374
+ // the healthy providers behind it.
375
+ const [failure, rates] = readRates(provider, data, from);
376
+ if (!failure) {
377
+ return rates;
378
+ }
379
+ this.onError(failure);
380
+ lastError = failure;
381
+ continue;
144
382
  }
145
- return rates;
146
- }
147
- // unrecognized error
148
- if (!err.handled) {
149
- throw err.error;
150
- }
151
- // logging existing error
152
- console.error(err.error);
153
- if (this.config.providers.length <= 1) {
154
- throw err.error;
383
+ // A rejection that does not follow the failure contract was not
384
+ // classified by the requester, so there is nothing to fall back for.
385
+ if (!isProviderFailure(err) || !err.handled) {
386
+ throw asError(isProviderFailure(err) ? err.error : err);
387
+ }
388
+ this.onError(err.error);
389
+ lastError = asError(err.error);
155
390
  }
156
- // removing current provider from active list
157
- this.config.remove(provider);
158
- // Retrying...
159
- return this.getRates(from, to, multiple);
391
+ // Every provider in the snapshot failed.
392
+ throw lastError;
160
393
  });
161
394
  this.config = new config_1.Config(...config);
162
- // Forwarding config adder function (with the alternative handle)
163
395
  this.add = this.config.add;
164
- this.addProvider = this.config.add;
165
- // Forwarding config multiple adder function (with the alternative handle)
166
396
  this.addMultiple = this.config.addMultiple;
167
- this.addMultipleProviders = this.config.addMultiple;
168
397
  this.remove = this.config.remove;
169
398
  }
170
399
  /**
@@ -175,11 +404,13 @@ class Converter {
175
404
  * @memberof Converter
176
405
  */
177
406
  get providers() {
178
- return this.config.providers;
407
+ // A copy: the readme tells callers to read this, and handing back the live
408
+ // array lets `providers.length = 0` empty the chain.
409
+ return [...this.config.providers];
179
410
  }
180
411
  get active() {
181
- return this.config.providers;
412
+ return [...this.config.providers];
182
413
  }
183
414
  }
184
415
  exports.Converter = Converter;
185
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udmVydGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NvbnZlcnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7QUFBQSxpREFBK0M7QUFFL0MsMkNBQTREO0FBQzVELDJDQUFxRDtBQUE1QyxrR0FBQSxPQUFPLE9BQVc7QUFDM0IseUNBQW9DO0FBQ3BDLGtEQUEwQjtBQVkxQjs7Ozs7O0dBTUc7QUFDVSxRQUFBLFVBQVUsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLDJCQUEyQixDQUFDLENBQUM7QUFFbEU7Ozs7O0dBS0c7QUFDSCxNQUFhLFNBQVM7SUFTcEI7Ozs7T0FJRztJQUNILFlBQVksR0FBRyxNQUFvRDtRQXFDbkU7OztXQUdHO1FBQ0gsMEJBQXFCLEdBQUcsQ0FBQyxrQkFBc0MsRUFBRSxFQUFFO1lBQ2pFLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLGVBQUssQ0FBQyxNQUFNLENBQUMsRUFBRSxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDckUsQ0FBQyxDQUFDO1FBRUY7Ozs7Ozs7Ozs7Ozs7V0FhRztRQUNILFlBQU8sR0FBRyxvQ0FLUyxFQUFFLHlFQUpuQixNQUFjLEVBQ2QsSUFBWSxFQUNaLEVBQVUsRUFDVixRQUFhLFNBQVM7WUFFdEIsNENBQTRDO1lBQzVDLElBQUksT0FBTyxLQUFLLEtBQUssV0FBVyxFQUFFLENBQUM7Z0JBQ2pDLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxrQkFBVSxDQUFDLENBQUM7Z0JBQy9CLElBQ0UsT0FBTyxJQUFJLEtBQUssUUFBUTtvQkFDeEIsT0FBTyxJQUFJLEtBQUssUUFBUTtvQkFDeEIsSUFBSSxDQUFDLFdBQVcsRUFBRSxLQUFLLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFDekMsQ0FBQztvQkFDRCxNQUFNLElBQUksS0FBSyxDQUNiLGdDQUFnQyxJQUFJLGdDQUFnQyxJQUFJLElBQUksQ0FDN0UsQ0FBQztnQkFDSixDQUFDO2dCQUNELE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzdDLENBQUM7WUFFRCxvREFBb0Q7WUFDcEQsTUFBTSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRyxNQUFNLElBQUEsV0FBRyxFQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO1lBRTlELElBQUksR0FBRyxFQUFFLENBQUM7Z0JBQ1IsTUFBTSxHQUFHLENBQUM7WUFDWixDQUFDO1lBRUQsSUFBSSxDQUFDLElBQUksSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDM0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxrQ0FBa0MsQ0FBQyxDQUFDO1lBQ3RELENBQUM7WUFFRCxtQ0FBbUM7WUFDbkMsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDNUMsQ0FBQyxDQUFBLENBQUM7UUFFRjs7Ozs7O1dBTUc7UUFDSCxnQkFBVyxHQUFHLENBQ1osTUFBYyxFQUNkLEVBQVUsRUFDVixRQUFhLFNBQVMsRUFDZCxFQUFFO1lBQ1YsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNoQyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxLQUFLLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO1lBQ3pFLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7WUFFbEQsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUNWLE1BQU0sSUFBSSxLQUFLLENBQUMsT0FBTyxFQUFFLHVCQUF1QixJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ3BGLENBQUM7WUFFRCxNQUFNLFdBQVcsR0FBRyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDckMsSUFBSSxLQUFLLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztnQkFDdkIsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsRUFBRSxNQUFNLElBQUksRUFBRSxDQUFDLENBQUM7WUFDN0QsQ0FBQztZQUVELE9BQU8sTUFBTSxHQUFHLFdBQVcsQ0FBQztRQUM5QixDQUFDLENBQUM7UUFFRjs7Ozs7O1dBTUc7UUFDSCxhQUFRLEdBQUcsMEJBSVksRUFBRSwrREFIdkIsSUFBWSxFQUNaLEVBQVUsRUFDVixXQUFvQixLQUFLO1lBRXpCLHNDQUFzQztZQUN0QyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBRTlDLHFCQUFxQjtZQUNyQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBRXZDLHNEQUFzRDtZQUN0RCxNQUFNLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHLE1BQVksSUFBQSxXQUFHLEVBQ2pDLElBQUEsc0JBQVUsRUFBQyxNQUFNLEVBQUUsUUFBUSxFQUFFO2dCQUMzQixJQUFJLEVBQUUsSUFBSTtnQkFDVixFQUFFLEVBQUUsRUFBRTtnQkFDTixRQUFRLEVBQUUsUUFBUTthQUNuQixDQUFDLENBQ0YsQ0FBQztZQUVILGtCQUFrQjtZQUNsQixtRkFBbUY7WUFDbkYsOEVBQThFO1lBQzlFLGlGQUFpRjtZQUNqRixJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7Z0JBQ1QsMEVBQTBFO2dCQUMxRSxtRUFBbUU7Z0JBQ25FLE1BQU0sTUFBTSxHQUFHLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNsRCxJQUNFLE9BQU8sTUFBTSxLQUFLLFFBQVE7b0JBQzFCLE9BQU8sSUFBSSxLQUFLLFFBQVE7b0JBQ3hCLE1BQU0sQ0FBQyxXQUFXLEVBQUUsS0FBSyxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQzNDLENBQUM7b0JBQ0QsTUFBTSxJQUFJLEtBQUssQ0FDYiwrQkFBK0IsTUFBTSx5QkFBeUIsSUFBSSxJQUFJLENBQ3ZFLENBQUM7Z0JBQ0osQ0FBQztnQkFFRCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNyQyxJQUFJLEtBQUssSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztvQkFDdkMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsa0JBQVUsRUFBRTt3QkFDdkMsS0FBSyxFQUFFLElBQUk7d0JBQ1gsVUFBVSxFQUFFLEtBQUs7d0JBQ2pCLFlBQVksRUFBRSxJQUFJO3FCQUNuQixDQUFDLENBQUM7Z0JBQ0wsQ0FBQztnQkFDRCxPQUFPLEtBQUssQ0FBQztZQUNmLENBQUM7WUFFRCxxQkFBcUI7WUFDckIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDakIsTUFBTSxHQUFHLENBQUMsS0FBSyxDQUFDO1lBQ2xCLENBQUM7WUFFRCx5QkFBeUI7WUFDekIsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7WUFFekIsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxNQUFNLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQ3RDLE1BQU0sR0FBRyxDQUFDLEtBQUssQ0FBQztZQUNsQixDQUFDO1lBRUQsNkNBQTZDO1lBQzdDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBRTdCLGNBQWM7WUFDZCxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUMzQyxDQUFDLENBQUEsQ0FBQztRQW5NQSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksZUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUM7UUFFcEMsaUVBQWlFO1FBQ2pFLElBQUksQ0FBQyxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7UUFDM0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQztRQUVuQywwRUFBMEU7UUFDMUUsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQztRQUMzQyxJQUFJLENBQUMsb0JBQW9CLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUM7UUFFcEQsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNuQyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsSUFBSSxTQUFTO1FBQ1gsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQztJQUMvQixDQUFDO0lBQ0QsSUFBSSxNQUFNO1FBQ1IsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQztJQUMvQixDQUFDO0NBMktGO0FBbk5ELDhCQW1OQyJ9
416
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udmVydGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NvbnZlcnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQSxpREFBMkU7QUFFM0UsMkNBQXdDO0FBQ3hDLDJDQUFxRDtBQUE1QyxrR0FBQSxPQUFPLE9BQVc7QUFDM0IseUNBQW9DO0FBYXBDOzs7Ozs7R0FNRztBQUNVLFFBQUEsVUFBVSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLENBQUMsQ0FBQztBQUVsRTs7Ozs7Ozs7O0dBU0c7QUFDVSxRQUFBLGNBQWMsR0FBRyxRQUFRLENBQUM7QUFFdkMsNEVBQTRFO0FBQzVFLFNBQVMsTUFBTSxDQUFDLEtBQVU7O0lBQ3hCLE1BQU0sTUFBTSxHQUFHLE1BQUEsS0FBSyxDQUFDLGtCQUFVLENBQUMsbUNBQUksS0FBSyxDQUFDLHNCQUFjLENBQUMsQ0FBQztJQUMxRCxPQUFPLE9BQU8sTUFBTSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDekQsQ0FBQztBQVlELCtHQUErRztBQUMvRyxTQUFTLGlCQUFpQixDQUFDLEtBQWM7SUFDdkMsT0FBTyxDQUNMLE9BQU8sS0FBSyxLQUFLLFFBQVE7UUFDekIsS0FBSyxLQUFLLElBQUk7UUFDZCxPQUFRLEtBQXlCLENBQUMsT0FBTyxLQUFLLFNBQVMsQ0FDeEQsQ0FBQztBQUNKLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBUyxPQUFPLENBQUMsS0FBYztJQUM3QixJQUFJLEtBQUssWUFBWSxLQUFLLEVBQUUsQ0FBQztRQUMzQixPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxNQUFNLE9BQU8sR0FDWCxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDO1FBQzNDLENBQUMsQ0FBQyxLQUFLO1FBQ1AsQ0FBQyxDQUFDLHlCQUF5QixNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztJQUUvQyxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNoQyxLQUFhLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUM3QixPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRCxtRkFBbUY7QUFDbkYsU0FBUyxZQUFZLENBQUMsS0FBYztJQUNsQyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzlCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMvQixDQUFDO0lBQ0QsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2hELE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3ZCLENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDO0FBQ3pELENBQUM7QUFFRCxtREFBbUQ7QUFDbkQsU0FBUyxhQUFhLENBQUMsTUFBZTtJQUNwQyxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUMzRCxNQUFNLElBQUksS0FBSyxDQUNiLHVEQUF1RCxZQUFZLENBQ2pFLE1BQU0sQ0FDUCxHQUFHLENBQ0wsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0g7Ozs7O0dBS0c7QUFDSCxNQUFNLGFBQWEsR0FBRyxxQkFBcUIsQ0FBQztBQUU1QyxTQUFTLGVBQWUsQ0FBQyxRQUFpQixFQUFFLEtBQWE7SUFDdkQsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUksUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNqRSxNQUFNLElBQUksS0FBSyxDQUNiLFFBQVEsS0FBSyxtREFBbUQsWUFBWSxDQUMxRSxRQUFRLENBQ1QsR0FBRyxDQUNMLENBQUM7SUFDSixDQUFDO0lBQ0Qsd0VBQXdFO0lBQ3hFLDZFQUE2RTtJQUM3RSw4RUFBOEU7SUFDOUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNsQyxNQUFNLElBQUksS0FBSyxDQUNiLFFBQVEsS0FBSyw2Q0FBNkMsWUFBWSxDQUNwRSxRQUFRLENBQ1QsR0FBRyxDQUNMLENBQUM7SUFDSixDQUFDO0lBQ0QsT0FBTyxRQUFRLENBQUM7QUFDbEIsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxhQUFhLENBQUMsS0FBVTtJQUMvQixPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUU7UUFDckMsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZCLE1BQU0sS0FBSyxHQUNULE9BQU8sR0FBRyxLQUFLLFFBQVEsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO1FBQ3pFLE9BQU8sTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQzdDLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILFNBQVMsU0FBUyxDQUNoQixRQUFrQixFQUNsQixJQUFTLEVBQ1QsSUFBWTtJQUVaLDhFQUE4RTtJQUM5RSwrREFBK0Q7SUFDL0QsTUFBTSxNQUFNLEdBQUcsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDbEQsSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLFdBQVcsRUFBRSxLQUFLLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDO1FBQzlFLE9BQU87WUFDTCxJQUFJLEtBQUssQ0FDUCwrQkFBK0IsTUFBTSx5QkFBeUIsSUFBSSxJQUFJLENBQ3ZFO1lBQ0QsRUFBRTtTQUNILENBQUM7SUFDSixDQUFDO0lBRUQsSUFBSSxLQUFVLENBQUM7SUFDZixJQUFJLENBQUM7UUFDSCxLQUFLLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNqQyxDQUFDO0lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUNYLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUVELElBQUksQ0FBQyxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDeEMsT0FBTyxDQUFDLElBQUksS0FBSyxDQUFDLG9DQUFvQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDL0QsQ0FBQztJQUVELDhFQUE4RTtJQUM5RSw0RUFBNEU7SUFDNUUseUVBQXlFO0lBQ3pFLHlFQUF5RTtJQUN6RSxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDMUIsT0FBTyxDQUFDLElBQUksS0FBSyxDQUFDLHNEQUFzRCxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDakYsQ0FBQztJQUVELDRFQUE0RTtJQUM1RSw0RUFBNEU7SUFDNUUsc0VBQXNFO0lBQ3RFLE1BQU0sTUFBTSxtQ0FBYSxLQUFLLEtBQUUsQ0FBQyxzQkFBYyxDQUFDLEVBQUUsSUFBSSxHQUFFLENBQUM7SUFDekQsTUFBTSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsa0JBQVUsRUFBRTtRQUN4QyxLQUFLLEVBQUUsSUFBSTtRQUNYLFVBQVUsRUFBRSxLQUFLO1FBQ2pCLFlBQVksRUFBRSxJQUFJO0tBQ25CLENBQUMsQ0FBQztJQUVILE9BQU8sQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDeEIsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBYSxTQUFTO0lBNEJwQjs7OztPQUlHO0lBQ0gsWUFBWSxHQUFHLE1BQW9EO1FBeEJuRTs7Ozs7Ozs7Ozs7Ozs7V0FjRztRQUNILFlBQU8sR0FBNkIsQ0FBQyxLQUFjLEVBQUUsRUFBRTtZQUNyRCxPQUFPLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3ZCLENBQUMsQ0FBQztRQXVDRjs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBa0JHO1FBQ0gsY0FBUyxHQUFHLENBQUMsTUFBa0IsRUFBRSxFQUFFO1lBQ2pDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLENBQUMsQ0FBQztRQUVGOzs7Ozs7Ozs7Ozs7O1dBYUc7UUFDSCxvQkFBZSxHQUFHLENBQUMsT0FBcUIsRUFBRSxFQUFFO1lBQzFDLElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3ZDLENBQUMsQ0FBQztRQUVGOzs7Ozs7Ozs7Ozs7OztXQWNHO1FBQ0gsWUFBTyxHQUFHLG9DQUtTLEVBQUUseUVBSm5CLE1BQWMsRUFDZCxJQUFZLEVBQ1osRUFBVSxFQUNWLFFBQWEsU0FBUztZQUV0Qix3RUFBd0U7WUFDeEUsd0VBQXdFO1lBQ3hFLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUN0QixlQUFlLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQzlCLGVBQWUsQ0FBQyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFFMUIsNENBQTRDO1lBQzVDLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxLQUFLLEtBQUssSUFBSSxFQUFFLENBQUM7Z0JBQzFDLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDM0IsSUFDRSxJQUFJLEtBQUssU0FBUztvQkFDbEIsT0FBTyxJQUFJLEtBQUssUUFBUTtvQkFDeEIsSUFBSSxDQUFDLFdBQVcsRUFBRSxLQUFLLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFDekMsQ0FBQztvQkFDRCxNQUFNLElBQUksS0FBSyxDQUNiLGdDQUFnQyxJQUFJLGdDQUFnQyxJQUFJLElBQUksQ0FDN0UsQ0FBQztnQkFDSixDQUFDO2dCQUNELE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzdDLENBQUM7WUFFRCxvREFBb0Q7WUFDcEQsTUFBTSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRyxNQUFNLElBQUEsV0FBRyxFQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO1lBRTlELElBQUksR0FBRyxFQUFFLENBQUM7Z0JBQ1IsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDckIsQ0FBQztZQUVELElBQUksQ0FBQyxJQUFJLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQzNDLE1BQU0sSUFBSSxLQUFLLENBQUMsa0NBQWtDLENBQUMsQ0FBQztZQUN0RCxDQUFDO1lBRUQsbUNBQW1DO1lBQ25DLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQzVDLENBQUMsQ0FBQSxDQUFDO1FBRUY7Ozs7Ozs7Ozs7O1dBV0c7UUFDSCxnQkFBVyxHQUFHLENBQ1osTUFBYyxFQUNkLEVBQVUsRUFDVixRQUFhLFNBQVMsRUFDZCxFQUFFOztZQUNWLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUN0QixlQUFlLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBRTFCLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDaEQsTUFBTSxJQUFJLEtBQUssQ0FDYiw4REFBOEQsWUFBWSxDQUN4RSxLQUFLLENBQ04sR0FBRyxDQUNMLENBQUM7WUFDSixDQUFDO1lBRUQsMEVBQTBFO1lBQzFFLDJFQUEyRTtZQUMzRSxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsR0FBRyxLQUFLLHNCQUFjLENBQUMsQ0FBQztZQUN4RSx5RUFBeUU7WUFDekUsbURBQW1EO1lBQ25ELE1BQU0sT0FBTyxHQUNYLE1BQUEsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsR0FBRyxLQUFLLEVBQUUsQ0FBQyxtQ0FDOUIsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxLQUFLLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO1lBRTdELDhEQUE4RDtZQUM5RCxJQUFJLE9BQU8sS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDMUIsd0NBQXdDO2dCQUN4QyxNQUFNLElBQUksS0FBSyxDQUNiLE9BQU8sRUFBRSx1QkFBdUIsSUFBSSxDQUFDLE1BQU0sUUFDekMsSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FDM0IsY0FBYyxDQUNmLENBQUM7WUFDSixDQUFDO1lBRUQsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBRTNCLCtEQUErRDtZQUMvRCxNQUFNLFdBQVcsR0FDZixPQUFPLEdBQUcsS0FBSyxRQUFRLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztZQUV6RSxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsSUFBSSxXQUFXLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQ3RELE1BQU0sSUFBSSxLQUFLLENBQ2IsMkJBQTJCLEVBQUUsMkRBQTJELFlBQVksQ0FDbEcsR0FBRyxDQUNKLEdBQUcsQ0FDTCxDQUFDO1lBQ0osQ0FBQztZQUVELE1BQU0sU0FBUyxHQUFHLE1BQU0sR0FBRyxXQUFXLENBQUM7WUFFdkMseUVBQXlFO1lBQ3pFLDRFQUE0RTtZQUM1RSwwRUFBMEU7WUFDMUUseURBQXlEO1lBQ3pELElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxLQUFLLENBQUMsSUFBSSxNQUFNLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDckUsTUFBTSxJQUFJLEtBQUssQ0FDYixjQUFjLE1BQU0sWUFBWSxXQUFXLG9DQUFvQyxDQUNoRixDQUFDO1lBQ0osQ0FBQztZQUVELE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUMsQ0FBQztRQUVGOzs7Ozs7Ozs7Ozs7V0FZRztRQUNILGFBQVEsR0FBRywwQkFJWSxFQUFFLCtEQUh2QixJQUFZLEVBQ1osRUFBVSxFQUNWLFdBQW9CLEtBQUs7WUFFekIsZUFBZSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztZQUU5Qix3RUFBd0U7WUFDeEUsMkNBQTJDO1lBQzNDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztnQkFDZCxlQUFlLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQzVCLENBQUM7WUFFRCwyQ0FBMkM7WUFDM0MsTUFBTSxLQUFLLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFekMsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUN2QixNQUFNLElBQUksS0FBSyxDQUFDLG1DQUFtQyxDQUFDLENBQUM7WUFDdkQsQ0FBQztZQUVELHFCQUFxQjtZQUNyQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBRXZDLGdFQUFnRTtZQUNoRSx3RUFBd0U7WUFDeEUsa0NBQWtDO1lBQ2xDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDNUMsTUFBTSxRQUFRLEdBQUcsSUFBQSx3QkFBWSxFQUFDLEtBQUssQ0FBQyxDQUFDO1lBRXJDLElBQUksU0FBUyxHQUFVLElBQUksS0FBSyxDQUFDLCtCQUErQixDQUFDLENBQUM7WUFFbEUsS0FBSyxJQUFJLEtBQUssR0FBRyxDQUFDLEVBQUUsS0FBSyxHQUFHLEtBQUssQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQztnQkFDbEQsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUU5QiwyRUFBMkU7Z0JBQzNFLDBFQUEwRTtnQkFDMUUsd0VBQXdFO2dCQUN4RSxJQUFJLFFBQVEsSUFBSSxRQUFRLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztvQkFDMUQsU0FBUyxHQUFHLElBQUksS0FBSyxDQUNuQiwwRUFBMEUsQ0FDM0UsQ0FBQztvQkFDRixJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO29CQUN4QixTQUFTO2dCQUNYLENBQUM7Z0JBRUQsTUFBTSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRyxNQUFZLElBQUEsV0FBRyxFQUNqQyxJQUFBLHNCQUFVLEVBQ1IsTUFBTSxFQUNOLFFBQVEsRUFDUixFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLGtDQUNyQyxLQUFLLEtBQUUsUUFBUSxJQUNyQixDQUNELENBQUM7Z0JBRUgsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO29CQUNULHdFQUF3RTtvQkFDeEUseUVBQXlFO29CQUN6RSxtQ0FBbUM7b0JBQ25DLE1BQU0sQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLEdBQUcsU0FBUyxDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7b0JBQ3pELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQzt3QkFDYixPQUFPLEtBQUssQ0FBQztvQkFDZixDQUFDO29CQUNELElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7b0JBQ3RCLFNBQVMsR0FBRyxPQUFPLENBQUM7b0JBQ3BCLFNBQVM7Z0JBQ1gsQ0FBQztnQkFFRCxnRUFBZ0U7Z0JBQ2hFLHFFQUFxRTtnQkFDckUsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxDQUFDO29CQUM1QyxNQUFNLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQzFELENBQUM7Z0JBRUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ3hCLFNBQVMsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ2pDLENBQUM7WUFFRCx5Q0FBeUM7WUFDekMsTUFBTSxTQUFTLENBQUM7UUFDbEIsQ0FBQyxDQUFBLENBQUM7UUF6U0EsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLGVBQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDO1FBRXBDLElBQUksQ0FBQyxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7UUFDM0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQztRQUUzQyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ25DLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxJQUFJLFNBQVM7UUFDWCwyRUFBMkU7UUFDM0UscURBQXFEO1FBQ3JELE9BQU8sQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUNELElBQUksTUFBTTtRQUNSLE9BQU8sQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDcEMsQ0FBQztDQW9SRjtBQTVVRCw4QkE0VUMifQ==
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
- export { Converter, Convert } from "./converter";
1
+ export { Converter, Convert, RATES_BASE_KEY } from "./converter";
2
2
  export { providers } from "./parts/providers";
3
3
  export type { rateObject } from "./converter";
4
4
  export type { Provider, ProviderErrors, ProviderReference, Providers, UserDefinedProvider } from "./parts/providers";
5
- export type { Config, ProxyConfiguration } from "./parts/config";
5
+ export type { Config } from "./parts/config";
6
+ export type { HttpClient, HttpResponse, HttpError, ClientOptions } from "./parts/client";
7
+ export { createClient } from "./parts/client";
6
8
  export type { chainableConverter } from "./parts/chainer";
9
+ export type { RetryOptions } from "./parts/requester";