From 7febbbf8a9d7edad73b08bc64435b9f409a40cd7 Mon Sep 17 00:00:00 2001 From: aelassas Date: Wed, 11 Dec 2024 14:39:10 +0100 Subject: [PATCH] Add currency-converter --- backend/vite.config.ts | 1 + frontend/vite.config.ts | 1 + packages/bookcars-helper/index.ts | 16 + packages/bookcars-helper/package.json | 2 +- packages/bookcars-helper/tsconfig.json | 6 +- packages/bookcars-types/package.json | 2 +- packages/currency-converter/check.ts | 8 + packages/currency-converter/index.ts | 533 +++++++++++ packages/currency-converter/package-lock.json | 852 ++++++++++++++++++ packages/currency-converter/package.json | 21 + packages/currency-converter/tsconfig.json | 113 +++ 11 files changed, 1551 insertions(+), 4 deletions(-) create mode 100644 packages/currency-converter/check.ts create mode 100644 packages/currency-converter/index.ts create mode 100644 packages/currency-converter/package-lock.json create mode 100644 packages/currency-converter/package.json create mode 100644 packages/currency-converter/tsconfig.json diff --git a/backend/vite.config.ts b/backend/vite.config.ts index f25187126..f527ed69f 100644 --- a/backend/vite.config.ts +++ b/backend/vite.config.ts @@ -15,6 +15,7 @@ export default ({ mode }: { mode: string }) => { ':bookcars-types': path.resolve(__dirname, '../packages/bookcars-types'), ':bookcars-helper': path.resolve(__dirname, '../packages/bookcars-helper'), ':disable-react-devtools': path.resolve(__dirname, '../packages/disable-react-devtools'), + ':currency-converter': path.resolve(__dirname, '../packages/currency-converter'), }, }, diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 355b5e880..bdc4a8113 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -15,6 +15,7 @@ export default ({ mode }: { mode: string }) => { ':bookcars-types': path.resolve(__dirname, '../packages/bookcars-types'), ':bookcars-helper': path.resolve(__dirname, '../packages/bookcars-helper'), ':disable-react-devtools': path.resolve(__dirname, '../packages/disable-react-devtools'), + ':currency-converter': path.resolve(__dirname, '../packages/currency-converter'), }, }, diff --git a/packages/bookcars-helper/index.ts b/packages/bookcars-helper/index.ts index d0d219849..16a47ea60 100644 --- a/packages/bookcars-helper/index.ts +++ b/packages/bookcars-helper/index.ts @@ -1,4 +1,5 @@ import * as bookcarsTypes from ':bookcars-types' +import CurrencyConverter, { CurrencyCode } from ':currency-converter' /** * Format a number. @@ -336,6 +337,21 @@ export const calculateTotalPrice = (car: bookcarsTypes.Car, from: Date, to: Date return _price } +/** + * Convert price from a given currency to another. + * + * @async + * @param {number} amount + * @param {CurrencyCode} from + * @param {CurrencyCode} to + * @returns {Promise} + */ +export const convertPrice = async (amount: number, from: CurrencyCode, to: CurrencyCode): Promise => { + const cc = new CurrencyConverter({ from, to, amount }) + const res = await cc.convert() + return res +} + /** * Check whether language is french * diff --git a/packages/bookcars-helper/package.json b/packages/bookcars-helper/package.json index 30a933694..294bb3ead 100644 --- a/packages/bookcars-helper/package.json +++ b/packages/bookcars-helper/package.json @@ -5,7 +5,7 @@ "main": "index.js", "type": "module", "scripts": { - "build": "rimraf ./index.js ./index.d.ts && tsc" + "build": "rimraf ./tsconfig.tsbuildinfo ./index.js ./index.d.ts && tsc --build --verbose" }, "author": "Akram El Assas", "license": "ISC", diff --git a/packages/bookcars-helper/tsconfig.json b/packages/bookcars-helper/tsconfig.json index f90bb812b..c78c53beb 100644 --- a/packages/bookcars-helper/tsconfig.json +++ b/packages/bookcars-helper/tsconfig.json @@ -30,7 +30,8 @@ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ "paths": { - ":bookcars-types": ["../bookcars-types"] + ":bookcars-types": ["../bookcars-types"], + ":currency-converter": ["../currency-converter"] }, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ @@ -113,6 +114,7 @@ "node_modules" ], "references": [ - { "path": "../bookcars-types" } + { "path": "../bookcars-types" }, + { "path": "../currency-converter" } ] } diff --git a/packages/bookcars-types/package.json b/packages/bookcars-types/package.json index d83a13d40..ba933e537 100644 --- a/packages/bookcars-types/package.json +++ b/packages/bookcars-types/package.json @@ -5,7 +5,7 @@ "main": "index.js", "type": "module", "scripts": { - "build": "rimraf ./index.js ./index.d.ts && tsc" + "build": "rimraf ./tsconfig.tsbuildinfo ./index.js ./index.d.ts && tsc --build --verbose" }, "keywords": [], "author": "", diff --git a/packages/currency-converter/check.ts b/packages/currency-converter/check.ts new file mode 100644 index 000000000..b69f66322 --- /dev/null +++ b/packages/currency-converter/check.ts @@ -0,0 +1,8 @@ +import CurrencyConverter from './index.js' + +const amount = 100 +const currencyConverter = new CurrencyConverter({ from: 'USD', to: 'EUR', amount }) + +const res = await currencyConverter.convert() + +console.log(`${amount} USD = ${res} EUR`) diff --git a/packages/currency-converter/index.ts b/packages/currency-converter/index.ts new file mode 100644 index 000000000..e637ffe9a --- /dev/null +++ b/packages/currency-converter/index.ts @@ -0,0 +1,533 @@ +import * as cheerio from 'cheerio' + +export type CurrencyCode = + | 'AFN' + | 'ALL' + | 'DZD' + | 'AOA' + | 'ARS' + | 'AMD' + | 'AWG' + | 'AUD' + | 'AZN' + | 'BSD' + | 'BHD' + | 'BBD' + | 'BDT' + | 'BYN' + | 'BZD' + | 'BMD' + | 'BTN' + | 'XBT' + | 'BOB' + | 'BAM' + | 'BWP' + | 'BRL' + | 'BND' + | 'BGN' + | 'BIF' + | 'XPF' + | 'KHR' + | 'CAD' + | 'CVE' + | 'KYD' + | 'FCFA' + | 'CLP' + | 'CLF' + | 'CNY' + | 'CNY' + | 'COP' + | 'KMF' + | 'CF' + | 'CDF' + | 'CRC' + | 'HRK' + | 'CUC' + | 'CZK' + | 'DKK' + | 'DJF' + | 'DOP' + | 'XCD' + | 'EGP' + | 'ETB' + | 'FJD' + | 'GMD' + | 'GBP' + | 'GEL' + | 'GHS' + | 'GTQ' + | 'GNF' + | 'GYD' + | 'HTG' + | 'HNL' + | 'HKD' + | 'HUF' + | 'ISK' + | 'INR' + | 'IDR' + | 'IRR' + | 'IQD' + | 'ILS' + | 'JMD' + | 'JPY' + | 'JOD' + | 'KZT' + | 'KES' + | 'KWD' + | 'KGS' + | 'LAK' + | 'LBP' + | 'LSL' + | 'LRD' + | 'LYD' + | 'MOP' + | 'MKD' + | 'MGA' + | 'MWK' + | 'MYR' + | 'MVR' + | 'MRO' + | 'MUR' + | 'MXN' + | 'MDL' + | 'MAD' + | 'MZN' + | 'MMK' + | 'NAD' + | 'NPR' + | 'ANG' + | 'NZD' + | 'NIO' + | 'NGN' + | 'NOK' + | 'OMR' + | 'PKR' + | 'PAB' + | 'PGK' + | 'PYG' + | 'PHP' + | 'PLN' + | 'QAR' + | 'RON' + | 'RUB' + | 'RWF' + | 'SVC' + | 'SAR' + | 'RSD' + | 'SCR' + | 'SLL' + | 'SGD' + | 'SBD' + | 'SOS' + | 'ZAR' + | 'KRW' + | 'VES' + | 'LKR' + | 'SDG' + | 'SRD' + | 'SZL' + | 'SEK' + | 'CHF' + | 'TJS' + | 'TZS' + | 'THB' + | 'TOP' + | 'TTD' + | 'TND' + | 'TRY' + | 'TMT' + | 'UGX' + | 'UAH' + | 'AED' + | 'USD' + | 'UYU' + | 'UZS' + | 'VND' + | 'XAF' + | 'XOF' + | 'YER' + | 'ZMW' + | 'ETH' + | 'EUR' + | 'LTC' + | 'TWD' + | 'PEN' + +type CurrencyPair = { expiryDate: Date, rate: number } + +class CurrencyConverter { + currencyFrom: string + currencyTo: string + currencyAmount: number + convertedValue: number + isDecimalComma: boolean + isRatesCaching: boolean + ratesCacheDuration: number + ratesCache: Record + currencyCode: CurrencyCode[] = ['AFN', 'ALL', 'DZD', 'AOA', 'ARS', 'AMD', 'AWG', 'AUD', 'AZN', 'BSD', 'BHD', 'BBD', 'BDT', 'BYN', 'BZD', 'BMD', 'BTN', 'XBT', 'BOB', 'BAM', 'BWP', 'BRL', 'BND', 'BGN', 'BIF', 'XPF', 'KHR', 'CAD', 'CVE', 'KYD', 'FCFA', 'CLP', 'CLF', 'CNY', 'CNY', 'COP', 'CF', 'CHF', 'CDF', 'CRC', 'HRK', 'CUC', 'CZK', 'DKK', 'DJF', 'DOP', 'XCD', 'EGP', 'ETB', 'FJD', 'GMD', 'GBP', 'GEL', 'GHS', 'GTQ', 'GNF', 'GYD', 'HTG', 'HNL', 'HKD', 'HUF', 'ISK', 'INR', 'IDR', 'IRR', 'IQD', 'ILS', 'JMD', 'JPY', 'JOD', 'KMF', 'KZT', 'KES', 'KWD', 'KGS', 'LAK', 'LBP', 'LSL', 'LRD', 'LYD', 'MOP', 'MKD', 'MGA', 'MWK', 'MYR', 'MVR', 'MRO', 'MUR', 'MXN', 'MDL', 'MAD', 'MZN', 'MMK', 'NAD', 'NPR', 'ANG', 'NZD', 'NIO', 'NGN', 'NOK', 'OMR', 'PKR', 'PAB', 'PGK', 'PYG', 'PHP', 'PLN', 'QAR', 'RON', 'RUB', 'RWF', 'SVC', 'SAR', 'RSD', 'SCR', 'SLL', 'SGD', 'SBD', 'SOS', 'ZAR', 'KRW', 'VES', 'LKR', 'SDG', 'SRD', 'SZL', 'SEK', 'CHF', 'TJS', 'TZS', 'THB', 'TOP', 'TTD', 'TND', 'TRY', 'TMT', 'UGX', 'UAH', 'AED', 'USD', 'UYU', 'UZS', 'VND', 'XAF', 'XOF', 'YER', 'ZMW', 'ETH', 'EUR', 'LTC', 'TWD', 'PEN'] + currencies: Record = { + 'AFN': 'Afghan Afghani', + 'ALL': 'Albanian Lek', + 'DZD': 'Algerian Dinar', + 'AOA': 'Angolan Kwanza', + 'ARS': 'Argentine Peso', + 'AMD': 'Armenian Dram', + 'AWG': 'Aruban Florin', + 'AUD': 'Australian Dollar', + 'AZN': 'Azerbaijani M anat', + 'BSD': 'Bahamian Dollar', + 'BHD': 'Bahraini Dinar', + 'BBD': 'Bajan Dollar', + 'BDT': 'Bangladeshi Taka', + 'BYN': 'Belarusian Ruble', + 'BZD': 'Belize Dollar', + 'BMD': 'Bermudan Dollar', + 'BTN': 'Bhutan currency', + 'BOB': 'Bolivian Boliviano', + 'BAM': 'Bosnia-Herzegovina Convertible Mark', + 'BWP': 'Botswanan Pula', + 'BRL': 'Brazilian Real', + 'BND': 'Brunei Dollar', + 'BGN': 'Bulgarian Lev', + 'BIF': 'Burundian Fra nc', + 'XPF': 'CFP Franc', + 'KHR': 'Cambodian riel', + 'CAD': 'Canadian Dollar', + 'CVE': 'Cape Verdean Escudo', + 'KYD': 'Cayman Islands Dollar', + 'FCFA': 'Central African CFA Franc', + 'CLP': 'Chilean Peso', + 'CLF': 'Chilean Unit of Account (UF)', + 'CNY': 'Chinese Yuan', + 'COP': 'Colombian Peso', + 'KMF': 'Comorian Franc', + 'CDF': 'Congolese Franc', + 'CRC': 'Costa Rican Colón', + 'HRK': 'Croatian Kuna', + 'CUC': 'Cuban Peso', + 'CZK': 'Czech Koruna', + 'DKK': 'Danish Krone', + 'DJF': 'Djiboutian Franc', + 'DOP': 'Dominican Pe so', + 'XCD': 'East Caribbean Dollar', + 'EGP': 'Egyptian Pound', + 'ETB': 'Ethiopian Birr', + 'FJD': 'Fijian Dollar', + 'GMD': 'Gambian dalasi', + 'GEL': 'Georgian Lari', + 'GHS': 'Ghanaian Cedi', + 'GTQ': 'Guatemalan Quetzal', + 'GNF': 'Guinean Franc', + 'GYD': 'Guyanaese Dollar', + 'HTG': 'Haitian Gourde', + 'HNL': 'Honduran Lempira', + 'HKD': 'Hong Kong Dollar', + 'HUF': 'Hungarian Forint', + 'ISK': 'Icelandic Króna', + 'INR': 'Indian Rupee', + 'IDR': 'Indonesian Rupiah', + 'IRR': 'Iranian Rial', + 'IQD': 'Iraqi Dinar', + 'ILS': 'Israeli New Shekel', + 'JMD': 'Jamaican Dollar', + 'JPY': 'Japanese Yen', + 'JOD': 'Jordanian Dinar', + 'KZT': 'Kazakhstani Tenge', + 'KES': 'Kenyan Shilling', + 'KWD': 'Kuwaiti Dinar', + 'KGS': 'Kyrgystani Som', + 'LAK': 'Laotian Kip', + 'LBP': 'Lebanese pound', + 'LSL': 'Lesotho Loti', + 'LRD': 'Liberian Dollar', + 'LYD': 'Libyan Dinar', + 'MOP': 'Macanese Pataca', + 'MKD': 'Macedonian Denar', + 'MGA': 'Malagasy Ariary', + 'MWK': 'Malawian Kwacha', + 'MYR': 'Malaysian Ringgit', + 'MVR': 'Maldivian Rufiyaa', + 'MRO': 'Mauritanian Ouguiya', + 'MUR': 'Mauritian Rupee', + 'MXN': 'Mexican Peso', + 'MDL': 'Moldovan Leu', + 'MAD': 'Moroccan Dirham', + 'MZN': 'Mozambican metical', + 'MMK': 'Myanmar Kyat', + 'NAD': 'Namibian dol lar', + 'NPR': 'Nepalese Rupee', + 'ANG': 'Netherlands Antillean Guilder', + 'NZD': 'New Zealand Dollar', + 'NIO': 'Nicaraguan Córdoba', + 'NGN': 'Nigerian Naira', + 'NOK': 'Norwegian Krone', + 'OMR': 'Omani Rial', + 'PKR': 'Pakistani Rupee', + 'PAB': 'Panamanian Balboa', + 'PGK': 'Papua New Guinean Kina', + 'PYG': 'Paraguayan Guarani', + 'PHP': 'Philippine peso', + 'PLN': 'Poland Złoty', + 'GBP': 'Pound sterling', + 'QAR': 'Qatari Rial', + 'RON': 'Romania n Leu', + 'RUB': 'Russian Ruble', + 'RWF': 'Rwandan franc', + 'SVC': 'Salvadoran Colón', + 'SAR': 'Saudi Riyal', + 'RSD': 'Serbian Dinar', + 'SCR': 'Seychellois Rupee', + 'SLL': 'Sierra Leonean Leone', + 'SGD': 'Singapore Dollar', + 'SBD': 'Solomon Islands Dollar', + 'SOS': 'Somali Shilling', + 'ZAR': 'South African Rand', + 'KRW': 'South Korean won', + 'VES': 'Sovereign Bolivar', + 'LKR': 'Sri Lankan Rupee', + 'SDG': 'Sudanese pound', + 'SRD': 'Surinamese Dollar', + 'SZL': 'Swazi Lilangeni', + 'SEK': 'Swedish Krona', + 'CF': 'Swiss Franc', + 'CHF': 'Swiss Franc', + 'TJS': 'Tajikistani Somoni', + 'TZS': 'Tanzanian Shilling', + 'THB': 'Thai Baht', + 'TOP': 'Tongan Pa\'anga', + 'TTD': 'Trinidad and Tobago Dollar', + 'TND': 'Tunisian Dinar', + 'TRY': 'Turkish lira', + 'TMT': 'Turkmenistan manat', + 'UGX': 'Ugandan Shilling', + 'UAH': 'Ukrainian hryvnia', + 'AED': 'United Arab Emirates Dirham', + 'USD': 'United States Dollar', + 'UYU': 'Uruguayan Peso', + 'UZS': 'Uzbekistani Som', + 'VND': 'Vietnamese dong', + 'XAF': 'Central African CFA franc', + 'XOF': 'West African CFA franc', + 'YER': 'Yemeni Rial', + 'ZMW': 'Zambian Kwacha', + 'XBT': 'Bitcoin', + 'ETH': 'Ether', + 'EUR': 'Euro', + 'LTC': 'Litecoin', + 'TWD': 'NT$', + 'PEN': 'Peruvian Sol' + } + + constructor( + params: { + from: string, + to: string, + amount: number, + isDecimalComma?: boolean + }) { + this.currencyFrom = '' + this.currencyTo = '' + this.currencyAmount = 1 + this.convertedValue = 0 + this.isDecimalComma = false + this.isRatesCaching = false + this.ratesCacheDuration = 0 + this.ratesCache = {} + + if (params != undefined) { + if (params['from'] !== undefined) + this.from(params['from']) + + if (params['to'] !== undefined) + this.to(params['to']) + + if (params['amount'] !== undefined) + this.amount(params['amount']) + + if (params['isDecimalComma'] !== undefined) + this.setDecimalComma(params['isDecimalComma']) + } + } + + from(currencyFrom: string) { + if (typeof currencyFrom !== 'string') + throw new TypeError('currency code should be a string') + + if (!this.currencyCode.includes(currencyFrom.toUpperCase() as CurrencyCode)) + throw new Error(`${currencyFrom} is not a valid currency code`) + + this.currencyFrom = currencyFrom.toUpperCase() + return this + } + to(currencyTo: string) { + if (typeof currencyTo !== 'string') + throw new TypeError('currency code should be a string') + + if (!this.currencyCode.includes(currencyTo.toUpperCase() as CurrencyCode)) + throw new Error(`${currencyTo} is not a valid currency code`) + + this.currencyTo = currencyTo + return this + } + amount(currencyAmount: number) { + if (typeof currencyAmount !== 'number') + throw new TypeError('amount should be a number') + + if (currencyAmount <= 0) + throw new Error('amount should be a positive number') + + this.currencyAmount = currencyAmount + return this + } + + setDecimalComma(isDecimalComma: boolean) { + if (typeof isDecimalComma !== 'boolean') + throw new TypeError('isDecimalComma should be a boolean') + + this.isDecimalComma = isDecimalComma + return this + } + + replaceAll(text: string, queryString: string, replaceString: string) { + let text_ = '' + for (let i = 0; i < text.length; i++) { + if (text[i] === queryString) { + text_ += replaceString + } else { + text_ += text[i] + } + } + return text_ + } + + setupRatesCache(ratesCacheOptions: { isRatesCaching: boolean, ratesCacheDuration: number }) { + if (typeof ratesCacheOptions != 'object') + throw new TypeError('ratesCacheOptions should be an object') + + if (ratesCacheOptions.isRatesCaching === undefined) + throw new Error(`ratesCacheOptions should have a property called isRatesCaching`) + + if (typeof ratesCacheOptions.isRatesCaching != 'boolean') + throw new TypeError('ratesCacheOptions.isRatesCaching should be a boolean') + + if (typeof ratesCacheOptions.ratesCacheDuration != 'number') + throw new TypeError('ratesCacheOptions.ratesCacheDuration should be a number') + + if (ratesCacheOptions.ratesCacheDuration <= 0) + throw new Error('ratesCacheOptions.ratesCacheDuration should be a positive number of seconds') + + this.isRatesCaching = ratesCacheOptions.isRatesCaching + + if (ratesCacheOptions.ratesCacheDuration === undefined) + this.ratesCacheDuration = 3600 // Defaults to 3600 seconds (1 hour) + else + this.ratesCacheDuration = ratesCacheOptions.ratesCacheDuration + + return this + } + + rates(): Promise { + if (this.currencyFrom === this.currencyTo) { + return new Promise((resolve, _) => { + resolve(1) + }) + } else { + let currencyPair = this.currencyFrom.toUpperCase() + this.currencyTo.toUpperCase() + let now = new Date() + if (currencyPair in this.ratesCache && now < this.ratesCache[currencyPair].expiryDate) { + return new Promise((resolve, _) => { + resolve(this.ratesCache[currencyPair].rate) + }) + } else { + return new Promise((resolve, reject) => { + const url = `https://www.google.com/search?q=${this.currencyAmount}+${this.currencyFrom}+to+${this.currencyTo}+&hl=en` + // console.log('URL:', url) + fetch(url) + .then((res) => res.text()) + .then((html) => resolve(html)) + .catch((err) => reject(err)) + }).then((body) => { + return cheerio.load(body as string) + }) + .then(($) => { + return $('.iBp4i').text().split(' ')[0] + }) + .then((rates: string) => { + if (this.isDecimalComma) { + if (rates.includes('.')) + rates = this.replaceAll(rates, '.', '') + if (rates.includes(',')) + rates = this.replaceAll(rates, ',', '.') + } else { + if (rates.includes(',')) + rates = this.replaceAll(rates, ',', '') + } + const ratesNum = parseFloat(rates) / this.currencyAmount + if (this.isRatesCaching) { + this.addRateToRatesCache(currencyPair, ratesNum) + } + return ratesNum + }) + } + } + } + + convert(currencyAmount?: number) { + if (currencyAmount !== undefined) { + this.amount(currencyAmount) + } + + if (this.currencyFrom == '') + throw new Error('currency code cannot be an empty string') + + if (this.currencyTo == '') + throw new Error('currency code cannot be an empty string') + + if (this.currencyAmount == 0) + throw new Error('currency amount should be a positive value') + + return this.rates().then((rates) => { + this.convertedValue = rates * this.currencyAmount + return this.convertedValue + }) + } + + currencyName(currencyCode_: string) { + if (typeof currencyCode_ != 'string') + throw new TypeError('currency code should be a string') + + if (!this.currencyCode.includes(currencyCode_.toUpperCase() as CurrencyCode)) + throw new Error(`${currencyCode_} is not a valid currency code`) + + return this.currencies[currencyCode_ as CurrencyCode] + } + + addRateToRatesCache(currencyPair: string, rate_: number) { + if (typeof currencyPair != 'string') + throw new TypeError('currency pair should be a string') + + if (typeof rate_ != 'number') + throw new TypeError('rate should be a number') + + let now = new Date() + if (currencyPair in this.ratesCache) { + if (now > this.ratesCache[currencyPair].expiryDate) { + let newExpiry = new Date() + newExpiry.setSeconds(newExpiry.getSeconds() + this.ratesCacheDuration) + this.ratesCache[currencyPair] = { + rate: rate_, + expiryDate: newExpiry + } + } + } else { + let newExpiry = new Date() + newExpiry.setSeconds(newExpiry.getSeconds() + this.ratesCacheDuration) + this.ratesCache[currencyPair] = { + rate: rate_, + expiryDate: newExpiry + } + } + } +} + +export default CurrencyConverter diff --git a/packages/currency-converter/package-lock.json b/packages/currency-converter/package-lock.json new file mode 100644 index 000000000..f96ab8ae5 --- /dev/null +++ b/packages/currency-converter/package-lock.json @@ -0,0 +1,852 @@ +{ + "name": "currency-converter", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "currency-converter", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "cheerio": "^1.0.0" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "rimraf": "^6.0.1", + "typescript": "^5.7.2" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/node": { + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", + "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/packages/currency-converter/package.json b/packages/currency-converter/package.json new file mode 100644 index 000000000..9bfebe349 --- /dev/null +++ b/packages/currency-converter/package.json @@ -0,0 +1,21 @@ +{ + "name": "currency-converter", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "build": "rimraf ./tsconfig.tsbuildinfo ./index.js ./index.d.ts ./check.js ./check.d.ts && tsc --build --verbose", + "check": "npm run build && node check.js" + }, + "author": "Akram El Assas", + "license": "ISC", + "dependencies": { + "cheerio": "^1.0.0" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "rimraf": "^6.0.1", + "typescript": "^5.7.2" + } +} diff --git a/packages/currency-converter/tsconfig.json b/packages/currency-converter/tsconfig.json new file mode 100644 index 000000000..4d098fd01 --- /dev/null +++ b/packages/currency-converter/tsconfig.json @@ -0,0 +1,113 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": ["esnext"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "esnext", /* Specify what module code is generated. */ + "rootDir": "", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": false, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true, /* Skip type checking all .d.ts files. */ + "declaration": true + }, + "exclude": [ + "node_modules" + ] +}