UNPKG

currzy

Version:

Free, open-source library for fetching, managing, and converting up-to-date currency rates from multiple reliable sources with the ability to easily choose the source.

244 lines (239 loc) 5.97 kB
// src/providers/cbrf/index.ts import { ofetch } from "ofetch"; import { XMLParser } from "fast-xml-parser"; // src/cache/drivers.ts import fs from "fs/promises"; import path from "path"; var FileCacheDriver = class { constructor(providerName) { this.cacheFile = path.resolve("cache", `${providerName}-cache.json`); } async load() { try { const raw = await fs.readFile(this.cacheFile, "utf-8"); return JSON.parse(raw); } catch { return null; } } async save(data) { await fs.mkdir(path.dirname(this.cacheFile), { recursive: true }); await fs.writeFile(this.cacheFile, JSON.stringify(data, null, 2), "utf-8"); } async clear() { try { await fs.unlink(this.cacheFile); } catch { } } }; var LocalStorageCacheDriver = class { constructor(providerName) { this.key = `currenzy-${providerName}-cache`; } async load() { const raw = localStorage.getItem(this.key); return raw ? JSON.parse(raw) : null; } async save(data) { localStorage.setItem(this.key, JSON.stringify(data)); } async clear() { localStorage.removeItem(this.key); } }; // src/cache/index.ts function createCacheDriver(providerName) { if (typeof window !== "undefined" && "localStorage" in window) { return new LocalStorageCacheDriver(providerName); } else { return new FileCacheDriver(providerName); } } // src/providers/cbrf/types/currency.ts var AVAILABLE_CURRENCIES = [ "AUD", "AZN", "DZD", "GBP", "AMD", "BHD", "BYN", "BGN", "BOB", "BRL", "HUF", "VND", "HKD", "GEL", "DKK", "AED", "USD", "EUR", "EGP", "INR", "IDR", "IRR", "KZT", "CAD", "QAR", "KGS", "CNY", "CUP", "MDL", "MNT", "NGN", "NZD", "NOK", "OMR", "PLN", "SAR", "RON", "XDR", "SGD", "TJS", "THB", "BDT", "TRY", "TMT", "UZS", "UAH", "CZK", "SEK", "CHF", "ETB", "RSD", "ZAR", "KRW", "JPY", "MMK", "RUB" ]; function assertCurrency(code) { if (!AVAILABLE_CURRENCIES.includes(code)) { throw new Error(`Unknown currency: ${code}`); } } // src/providers/cbrf/index.ts var CbrfProvider = class { constructor() { this.url = "https://api.allorigins.win/raw?url=https://www.cbr.ru/scripts/XML_daily.asp"; this.rates = {}; this.lastUpdate = null; this.initialized = false; this.cacheTTL = 1e3 * 60 * 60; this.cache = createCacheDriver("cbrf"); this.availableCurrencies = AVAILABLE_CURRENCIES; } async loadCache() { const data = await this.cache.load(); if (data) { this.rates = data.payload.rates; this.lastUpdate = data.lastUpdate ? new Date(data.lastUpdate) : null; this.initialized = true; } } async saveCache() { await this.cache.save({ payload: { rates: this.rates }, lastUpdate: this.lastUpdate ? this.lastUpdate.toISOString() : null }); } async clearCache() { await this.cache.clear(); this.rates = {}; this.lastUpdate = null; this.initialized = false; } async fetchRates() { try { const xml = await ofetch(this.url, { responseType: "text" }); const parser = new XMLParser(); const data = parser.parse(xml); const items = Array.isArray(data.ValCurs.Valute) ? data.ValCurs.Valute : [data.ValCurs.Valute]; this.rates = {}; for (const item of items) { const nominal = Number(item.Nominal); const value = parseFloat(item.Value.replace(",", ".")); const vunitRate = value / nominal; const rate = { code: item.CharCode, nominal, value, vunitRate }; if (this.availableCurrencies.includes(rate.code)) { this.rates[rate.code] = rate; } } this.rates["RUB"] = { code: "RUB", nominal: 1, value: 1, vunitRate: 1 }; this.lastUpdate = /* @__PURE__ */ new Date(); this.initialized = true; await this.saveCache(); } catch (e) { console.error("Error fetching CBRF rates:", e); if (!this.initialized) { await this.loadCache(); if (!this.initialized) throw new Error("No CBRF data and no cache"); } } } async ensureInitialized() { if (!this.initialized) await this.loadCache(); const expired = this.lastUpdate === null || (/* @__PURE__ */ new Date()).getTime() - this.lastUpdate.getTime() > this.cacheTTL; if (!this.initialized || expired) { await this.fetchRates(); } } async getRate(code, base = "USD") { await this.ensureInitialized(); assertCurrency(code); assertCurrency(base); const rateInRub = this.rates[code].vunitRate; const baseRateInRub = this.rates[base].vunitRate; return baseRateInRub / rateInRub; } async convert(amount, from, to) { await this.ensureInitialized(); assertCurrency(from); assertCurrency(to); const fromRateInRub = this.rates[from].vunitRate; const toRateInRub = this.rates[to].vunitRate; return amount * fromRateInRub / toRateInRub; } async getAllRates(base = "USD") { await this.ensureInitialized(); assertCurrency(base); const result = {}; for (const code of this.availableCurrencies) { if (code === base) continue; result[code] = await this.getRate(code, base); } return result; } getLastUpdate() { return this.lastUpdate; } }; // src/index.ts var Currzy = class { constructor(providerName) { if (providerName === "cbrf") this.provider = new CbrfProvider(); else throw new Error("Unknown provider"); } async getRate(code) { return await this.provider.getRate(code); } async convert(amount, from, to) { return await this.provider.convert(amount, from, to); } async getAllRatesTo(code) { return await this.provider.getAllRates(code); } async clearCache() { return this.provider.clearCache(); } getLastUpdate() { return this.provider.getLastUpdate(); } }; export { CbrfProvider, Currzy }; //# sourceMappingURL=index.js.map