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.
282 lines (275 loc) • 7.84 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
CbrfProvider: () => CbrfProvider,
Currzy: () => Currzy
});
module.exports = __toCommonJS(index_exports);
// src/providers/cbrf/index.ts
var import_ofetch = require("ofetch");
var import_fast_xml_parser = require("fast-xml-parser");
// src/cache/drivers.ts
var import_promises = __toESM(require("fs/promises"), 1);
var import_path = __toESM(require("path"), 1);
var FileCacheDriver = class {
constructor(providerName) {
this.cacheFile = import_path.default.resolve("cache", `${providerName}-cache.json`);
}
async load() {
try {
const raw = await import_promises.default.readFile(this.cacheFile, "utf-8");
return JSON.parse(raw);
} catch {
return null;
}
}
async save(data) {
await import_promises.default.mkdir(import_path.default.dirname(this.cacheFile), { recursive: true });
await import_promises.default.writeFile(this.cacheFile, JSON.stringify(data, null, 2), "utf-8");
}
async clear() {
try {
await import_promises.default.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 (0, import_ofetch.ofetch)(this.url, { responseType: "text" });
const parser = new import_fast_xml_parser.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();
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CbrfProvider,
Currzy
});
//# sourceMappingURL=index.cjs.map