From 1d2a8227d0553ccf1865517d84ca0915f89505cc Mon Sep 17 00:00:00 2001 From: Paul Puey Date: Tue, 20 Dec 2022 23:22:28 -0800 Subject: [PATCH 1/9] Move db utility functions out of uidEngine --- src/exchangeRateRouter.ts | 3 +-- src/rates.ts | 10 ++++++++-- src/ratesEngine.ts | 9 +-------- src/uidEngine.ts | 27 +++++---------------------- src/utils/dbUtils.ts | 18 +++++++++++++----- 5 files changed, 28 insertions(+), 39 deletions(-) diff --git a/src/exchangeRateRouter.ts b/src/exchangeRateRouter.ts index 7bb1dca1..6c9eda67 100644 --- a/src/exchangeRateRouter.ts +++ b/src/exchangeRateRouter.ts @@ -6,9 +6,8 @@ import promisify from 'promisify-node' import { config } from './config' import { asReturnGetRate, getExchangeRates } from './rates' -import { hmgetAsync } from './uidEngine' import { asExtendedReq } from './utils/asExtendedReq' -import { DbDoc } from './utils/dbUtils' +import { DbDoc, hmgetAsync } from './utils/dbUtils' import { addIso, fromCode, diff --git a/src/rates.ts b/src/rates.ts index c3d5e0cf..0b812b75 100644 --- a/src/rates.ts +++ b/src/rates.ts @@ -26,8 +26,14 @@ import { import { nomics } from './providers/nomics' import { openExchangeRates } from './providers/openExchangeRates' import { wazirx } from './providers/wazirx' -import { hgetallAsync, hsetAsync } from './uidEngine' -import { asDbDoc, DbDoc, getFromDb, saveToDb } from './utils/dbUtils' +import { + asDbDoc, + DbDoc, + getFromDb, + hgetallAsync, + hsetAsync, + saveToDb +} from './utils/dbUtils' import { checkConstantCode, currencyCodeArray, diff --git a/src/ratesEngine.ts b/src/ratesEngine.ts index bd7c86a1..c1dd077d 100644 --- a/src/ratesEngine.ts +++ b/src/ratesEngine.ts @@ -2,10 +2,7 @@ import fetch from 'node-fetch' import { config } from './config' import { ExchangeRateReq } from './exchangeRateRouter' -import { existsAsync, hgetallAsync, setAsync } from './uidEngine' -import { createThrottledMessage } from './utils/createThrottledMessage' -import { getEdgeAssetDoc } from './utils/dbUtils' -import { slackPoster } from './utils/postToSlack' +import { getEdgeAssetDoc, hgetallAsync, slackMessage } from './utils/dbUtils' import { logger, normalizeDate, snooze } from './utils/utils' const { @@ -17,10 +14,6 @@ const { } = config const endPoint = `${ratesServerAddress}/v2/exchangeRates` -const slackMessage = createThrottledMessage( - { set: setAsync, exists: existsAsync }, - slackPoster -) const LOOP_DELAY = 1000 * 30 // Delay 30 seconds const bridgeCurrency = DEFAULT_FIAT diff --git a/src/uidEngine.ts b/src/uidEngine.ts index b9fdfb90..f71b1cc6 100644 --- a/src/uidEngine.ts +++ b/src/uidEngine.ts @@ -1,28 +1,15 @@ -import { createClient } from 'redis' - import { coincapAssets } from './providers/coincap' import { coingeckoAssets } from './providers/coingecko' import { coinMarketCapAssets } from './providers/coinMarketCap' import { nomicsAssets } from './providers/nomics' -import { createThrottledMessage } from './utils/createThrottledMessage' import currencyCodeMaps from './utils/currencyCodeMaps.json' -import { wrappedGetFromDb, wrappedSaveToDb } from './utils/dbUtils' -import { slackPoster } from './utils/postToSlack' +import { + slackMessage, + wrappedGetFromDb, + wrappedSaveToDb +} from './utils/dbUtils' import { logger } from './utils/utils' -const client = createClient() -client.connect().catch(e => logger('redis connect error: ', e)) - -export const hsetAsync = client.hSet.bind(client) -export const hgetallAsync = client.hGetAll.bind(client) -export const hmgetAsync = client.hmGet.bind(client) -export const existsAsync = client.exists.bind(client) -export const delAsync = client.del.bind(client) -// Set type to `any` to avoid the TS4023 error -export const setAsync: any = client.set.bind(client) - -const slackMessage = createThrottledMessage(client, slackPoster) - const providerAssets = { coincap: coincapAssets, coinMarketCap: coinMarketCapAssets, @@ -31,10 +18,6 @@ const providerAssets = { } export const uidEngine = async (): Promise => { - client.on('error', function(error) { - logger('redis client error', error) - }) - logger('Updating UID Cache') try { const edgeDoc = (await wrappedGetFromDb(['currencyCodeMaps']))[0] diff --git a/src/utils/dbUtils.ts b/src/utils/dbUtils.ts index 6a3ee320..159f7d4a 100644 --- a/src/utils/dbUtils.ts +++ b/src/utils/dbUtils.ts @@ -9,20 +9,28 @@ import { import { syncedDocument } from 'edge-server-tools' import nano from 'nano' import promisify from 'promisify-node' +import { createClient } from 'redis' -import { delAsync, existsAsync, hsetAsync, setAsync } from '../uidEngine' import { config } from './../config' import { createThrottledMessage } from './createThrottledMessage' import currencyCodeMaps from './currencyCodeMaps.json' import { slackPoster } from './postToSlack' import { logger, memoize } from './utils' +const client = createClient() +client.connect().catch(e => logger('redis connect error: ', e)) + +export const hsetAsync = client.hSet.bind(client) +export const hgetallAsync = client.hGetAll.bind(client) +export const hmgetAsync = client.hmGet.bind(client) +export const existsAsync = client.exists.bind(client) +export const delAsync = client.del.bind(client) +// Set type to `any` to avoid the TS4023 error +export const setAsync: any = client.set.bind(client) + const ONE_HOUR = 1000 * 60 * 60 -const slackMessage = createThrottledMessage( - { set: setAsync, exists: existsAsync }, - slackPoster -) +export const slackMessage = createThrottledMessage(client, slackPoster) export interface DbDoc extends nano.IdentifiedDocument, From b0fad9441b9f8f0f2406630313563fa1ee4cebc1 Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Wed, 21 Dec 2022 18:38:43 -0800 Subject: [PATCH 2/9] Add node fetch ts types --- package.json | 1 + src/providers/coinMarketCap.ts | 3 ++- src/providers/coincap.ts | 13 ++++++---- yarn.lock | 46 ++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index ab7d6c26..811ee600 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@babel/preset-react": "^7.8.3", "@types/chai": "^4.2.9", "@types/mocha": "^7.0.1", + "@types/node-fetch": "^2.6.2", "@types/react": "^16.9.22", "@types/react-dom": "^16.9.5", "@typescript-eslint/eslint-plugin": ">=2.0.0", diff --git a/src/providers/coinMarketCap.ts b/src/providers/coinMarketCap.ts index d28ad3a5..a9298cab 100644 --- a/src/providers/coinMarketCap.ts +++ b/src/providers/coinMarketCap.ts @@ -230,7 +230,8 @@ export const coinMarketCapAssets = async (): Promise => { continue // retry } if (response.ok === false) { - throw new Error(response.status) + const text = await response.text() + throw new Error(text) } return assetMapReducer( diff --git a/src/providers/coincap.ts b/src/providers/coincap.ts index 2ae2c1e7..076c1354 100644 --- a/src/providers/coincap.ts +++ b/src/providers/coincap.ts @@ -74,10 +74,11 @@ const currentQuery = async ( const response = await fetch(url, OPTIONS) const json = asCoincapCurrentResponse(await response.json()) if (response.ok === false) { + const text = await response.text() logger( - `coincapCurrent returned code ${response.status} for ${codes} at ${date}` + `coincapCurrent returned code ${response.status} for ${codes} at ${date}: ${text}` ) - throw new Error(response.status) + throw new Error(text) } // Add to return object @@ -105,10 +106,11 @@ const historicalQuery = async ( OPTIONS ) if (response.ok === false) { + const text = await response.text() logger( - `coincapHistorical returned code ${response.status} for ${id} at ${date}` + `coincapHistorical returned code ${response.status.toString()} for ${id} at ${date}: ${text}` ) - throw new Error(response.status) + throw new Error(text) } const json = asCoincapHistoricalResponse(await response.json()) if (json.data.length === 0) @@ -179,7 +181,8 @@ export const coincapAssets = async (): Promise => { continue // retry } if (response.ok === false) { - throw new Error(response.status) + const text = await response.text() + throw new Error(text) } return assetMapReducer(asCoincapAssetResponse(await response.json()).data) } diff --git a/yarn.lock b/yarn.lock index 08b75fe0..8a89bdd1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -295,6 +295,14 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.1.tgz#5d7ec2a789a1f77c59b7ad071b9d50bf1abbfc9e" integrity sha512-L/Nw/2e5KUaprNJoRA33oly+M8X8n0K+FwLTbYqwTcR14wdPWeRkigBLfSFpN/Asf9ENZTMZwLxjtjeYucAA4Q== +"@types/node-fetch@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" + integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*": version "14.0.13" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.13.tgz#ee1128e881b874c371374c1f72201893616417c9" @@ -538,6 +546,11 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + axios-cookiejar-support@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/axios-cookiejar-support/-/axios-cookiejar-support-1.0.1.tgz#7b32af7d932508546c68b1fc5ba8f562884162e1" @@ -807,6 +820,13 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^4.0.0, commander@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -970,6 +990,11 @@ define-properties@^1.1.2, define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -1482,6 +1507,15 @@ follow-redirects@^1.14.8: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -2130,11 +2164,23 @@ mime-db@1.44.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + "mime-db@>= 1.43.0 < 2": version "1.48.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" From 1686d0829c047ab504076d52a7659377c8c4a9da Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Thu, 22 Dec 2022 00:06:32 -0800 Subject: [PATCH 3/9] Add coinrank engine to query coingecko every 45s --- src/coinrankEngine.ts | 51 ++++++++++++++++++++++++++++++++++ src/constants.ts | 1 + src/indexEngines.ts | 2 ++ src/types.ts | 64 +++++++++++++++++++++++++++++++++++++++++++ src/utils/dbUtils.ts | 1 + 5 files changed, 119 insertions(+) create mode 100644 src/coinrankEngine.ts create mode 100644 src/constants.ts create mode 100644 src/types.ts diff --git a/src/coinrankEngine.ts b/src/coinrankEngine.ts new file mode 100644 index 00000000..076ad5a2 --- /dev/null +++ b/src/coinrankEngine.ts @@ -0,0 +1,51 @@ +import fetch from 'node-fetch' +import { createClient } from 'redis' + +import { config } from './config' +import { REDIS_COINRANK_KEY_PREFIX } from './constants' +import { asCoingeckoMarkets, CoinrankRedis } from './types' +import { setAsync, slackMessage } from './utils/dbUtils' +import { logger, snooze } from './utils/utils' + +const client = createClient() +client.connect().catch(e => logger('redis connect error: ', e)) + +const PAGE_SIZE = 250 +const LOOP_DELAY = 45000 + +export const coinrankEngine = async (): Promise => { + await console.log('hello') + + logger('Updating Coinrank Cache') + try { + const lastUpdate = new Date().toISOString() + const { uri } = config.providers.coingecko + let markets: any = [] + for (let page = 1; page <= 8; page++) { + const url = `${uri}/api/v3/coins/markets?vs_currency=USD&page=${page}&per_page=${PAGE_SIZE}&price_change_percentage=1h,24h,7d,14d,30d,1y` + const response = await fetch(url) + if (response.ok === false) { + const text = await response.text() + throw new Error(text) + } + const reply = await response.json() + try { + const marketsPage = asCoingeckoMarkets(reply) + markets = [...markets, ...marketsPage] + } catch (e) { + console.log('ugh') + } + } + const data: CoinrankRedis = { lastUpdate, markets } + await setAsync(`${REDIS_COINRANK_KEY_PREFIX}_USD`, JSON.stringify(data)) + } catch (e) { + const err: any = e // Weird TS issue causing :any to get removed from above line + const message = `coinrankEngine failure: ${err.message}` + slackMessage(message).catch(e => logger(e)) + logger(message) + } finally { + logger('COINRANK ENGINE SNOOZING **********************') + await snooze(LOOP_DELAY) + coinrankEngine().catch(e => logger(e)) + } +} diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 00000000..d53a1895 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1 @@ +export const REDIS_COINRANK_KEY_PREFIX = 'coinrank' diff --git a/src/indexEngines.ts b/src/indexEngines.ts index 80a59dcd..cfd05a35 100644 --- a/src/indexEngines.ts +++ b/src/indexEngines.ts @@ -1,5 +1,6 @@ import { setupDatabase } from 'edge-server-tools' +import { coinrankEngine } from './coinrankEngine' import { config } from './config' import { ratesEngine } from './ratesEngine' import { uidEngine } from './uidEngine' @@ -11,6 +12,7 @@ async function initDb(): Promise { await setupDatabase(config.couchUri, ratesDbSetup) ratesEngine().catch(e => logger('ratesEnginee failure', e)) uidEngine().catch(e => logger('uidEnginee failure', e)) + coinrankEngine().catch(e => logger('uidEnginee failure', e)) } initDb().catch(e => logger('initDbe failure', e)) diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..80427d0a --- /dev/null +++ b/src/types.ts @@ -0,0 +1,64 @@ +/* eslint-disable @typescript-eslint/camelcase */ + +import { asArray, asNumber, asObject, asOptional, asString } from 'cleaners' + +// Force TS to derive the type of the return value since it is pretty obvious +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +const asCoingeckoAsset = (raw: any) => { + const asset = asObject({ + symbol: asString, + name: asString, + image: asString, + current_price: asOptional(asNumber), + market_cap: asOptional(asNumber), + market_cap_rank: asNumber, + total_volume: asOptional(asNumber), + price_change_percentage_1h_in_currency: asOptional(asNumber), + price_change_percentage_24h_in_currency: asOptional(asNumber), + price_change_percentage_7d_in_currency: asOptional(asNumber), + price_change_percentage_30d_in_currency: asOptional(asNumber), + price_change_percentage_1y_in_currency: asOptional(asNumber) + })(raw) + + const { + symbol, + name, + image, + current_price, + market_cap, + market_cap_rank, + total_volume, + price_change_percentage_1h_in_currency, + price_change_percentage_24h_in_currency, + price_change_percentage_7d_in_currency, + price_change_percentage_30d_in_currency, + price_change_percentage_1y_in_currency + } = asset + + const out = { + currencyCode: symbol, + currencyName: name, + imageUrl: image, + marketCap: market_cap ?? 0, + percentChange: { + hours1: price_change_percentage_1h_in_currency ?? 0, + hours24: price_change_percentage_24h_in_currency ?? 0, + days7: price_change_percentage_7d_in_currency ?? 0, + days30: price_change_percentage_30d_in_currency ?? 0, + year1: price_change_percentage_1y_in_currency ?? 0 + }, + price: current_price ?? 0, + rank: market_cap_rank, + volume24h: total_volume ?? 0 + } + + return out +} + +export const asCoingeckoMarkets = asArray(asCoingeckoAsset) + +export type CoinrankMarkets = ReturnType +export interface CoinrankRedis { + lastUpdate: string + markets: CoinrankMarkets +} diff --git a/src/utils/dbUtils.ts b/src/utils/dbUtils.ts index 159f7d4a..da9c533b 100644 --- a/src/utils/dbUtils.ts +++ b/src/utils/dbUtils.ts @@ -27,6 +27,7 @@ export const existsAsync = client.exists.bind(client) export const delAsync = client.del.bind(client) // Set type to `any` to avoid the TS4023 error export const setAsync: any = client.set.bind(client) +export const getAsync: any = client.get.bind(client) const ONE_HOUR = 1000 * 60 * 60 From b9a37eb3f438d3e49a3ce9c22af1549c2c215bab Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Wed, 4 Jan 2023 22:03:31 -0800 Subject: [PATCH 4/9] fixup! Add coinrank engine to query coingecko every 45s --- src/coinrankEngine.ts | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/coinrankEngine.ts b/src/coinrankEngine.ts index 076ad5a2..9705efc6 100644 --- a/src/coinrankEngine.ts +++ b/src/coinrankEngine.ts @@ -1,26 +1,21 @@ import fetch from 'node-fetch' -import { createClient } from 'redis' import { config } from './config' import { REDIS_COINRANK_KEY_PREFIX } from './constants' -import { asCoingeckoMarkets, CoinrankRedis } from './types' +import { asCoingeckoMarkets, CoinrankMarkets, CoinrankRedis } from './types' import { setAsync, slackMessage } from './utils/dbUtils' import { logger, snooze } from './utils/utils' -const client = createClient() -client.connect().catch(e => logger('redis connect error: ', e)) - const PAGE_SIZE = 250 const LOOP_DELAY = 45000 +const { defaultFiatCode } = config export const coinrankEngine = async (): Promise => { - await console.log('hello') - logger('Updating Coinrank Cache') try { const lastUpdate = new Date().toISOString() const { uri } = config.providers.coingecko - let markets: any = [] + let markets: CoinrankMarkets = [] for (let page = 1; page <= 8; page++) { const url = `${uri}/api/v3/coins/markets?vs_currency=USD&page=${page}&per_page=${PAGE_SIZE}&price_change_percentage=1h,24h,7d,14d,30d,1y` const response = await fetch(url) @@ -29,15 +24,14 @@ export const coinrankEngine = async (): Promise => { throw new Error(text) } const reply = await response.json() - try { - const marketsPage = asCoingeckoMarkets(reply) - markets = [...markets, ...marketsPage] - } catch (e) { - console.log('ugh') - } + const marketsPage = asCoingeckoMarkets(reply) + markets = [...markets, ...marketsPage] } const data: CoinrankRedis = { lastUpdate, markets } - await setAsync(`${REDIS_COINRANK_KEY_PREFIX}_USD`, JSON.stringify(data)) + await setAsync( + `${REDIS_COINRANK_KEY_PREFIX}_${defaultFiatCode}`, + JSON.stringify(data) + ) } catch (e) { const err: any = e // Weird TS issue causing :any to get removed from above line const message = `coinrankEngine failure: ${err.message}` From d6a85013e969fdb8d22dbd0c407536ef5ce02434 Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Thu, 22 Dec 2022 00:06:53 -0800 Subject: [PATCH 5/9] Add coinrank endpoint --- src/exchangeRateRouter.ts | 115 +++++++++++++++++++++++++++++++++++++- src/types.ts | 27 ++++++++- 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/src/exchangeRateRouter.ts b/src/exchangeRateRouter.ts index 6c9eda67..b48e011d 100644 --- a/src/exchangeRateRouter.ts +++ b/src/exchangeRateRouter.ts @@ -2,12 +2,20 @@ import { mul } from 'biggystring' import { asArray, asMaybe, asObject, asOptional, asString } from 'cleaners' import express from 'express' import nano from 'nano' +import fetch from 'node-fetch' import promisify from 'promisify-node' import { config } from './config' +import { REDIS_COINRANK_KEY_PREFIX } from './constants' import { asReturnGetRate, getExchangeRates } from './rates' +import { + asCoinrankReq, + asExchangeRateResponse, + CoinrankRedis, + CoinrankReq +} from './types' import { asExtendedReq } from './utils/asExtendedReq' -import { DbDoc, hmgetAsync } from './utils/dbUtils' +import { DbDoc, getAsync, hmgetAsync, setAsync } from './utils/dbUtils' import { addIso, fromCode, @@ -19,6 +27,7 @@ import { toIsoPair } from './utils/utils' +const EXPIRE_TIME = 60000 export interface ExchangeRateReq { currency_pair: string date: string @@ -301,6 +310,108 @@ const sendExchangeRates: express.RequestHandler = (req, res, next): void => { res.json({ data: exReq.requestedRatesResult.data }) } +const sendCoinranks: express.RequestHandler = async ( + req, + res, + next +): Promise => { + const { ratesServerAddress } = config + const now = new Date() + const nowTimestamp = now.getTime() + + const exReq = req as ExpressRequest + if (exReq == null) return next(500) + + let query: CoinrankReq + try { + query = asCoinrankReq(req.query) + } catch (e) { + res + .status(400) + .send(`Error: ${e instanceof Error ? e.message : 'Unknown error'}`) + return + } + const { fiatCode, start, length } = query + + try { + if (start < 1 || start > 2000) { + res + .status(400) + .send(`Invalid start param: ${start}. Must be between 1-2000`) + return + } + if (length < 1 || length > 100) { + res + .status(400) + .send(`Invalid length param: ${length}. Must be between 1-100`) + return + } + const fiatSuffix = fiatCode.split(':')[1] + const jsonString = await getAsync( + `${REDIS_COINRANK_KEY_PREFIX}_${fiatSuffix}` + ) + + let redisResult: CoinrankRedis = JSON.parse(jsonString) + + if (fiatCode !== 'iso:USD') { + const lastUpdated = + redisResult != null ? new Date(redisResult.lastUpdate).getTime() : 0 + + // If no result in redis or it's out of date + if (redisResult == null || nowTimestamp - lastUpdated > EXPIRE_TIME) { + // Attempt to scale prices by foreign exchange rate + + // Get exchange rate + const result = await fetch( + `${ratesServerAddress}/v2/exchangeRate?currency_pair=iso:USD_${fiatCode}` + ) + const resultJson = await result.json() + const { exchangeRate } = asExchangeRateResponse(resultJson) + const rate = Number(exchangeRate) + + // Get USD rankings + const jsonString = await getAsync(`${REDIS_COINRANK_KEY_PREFIX}_USD`) + redisResult = JSON.parse(jsonString) + const { markets } = redisResult + let data = markets.slice(start - 1, start + length) + + // Modify the prices, mcap, & volume + data = data.map(m => ({ + ...m, + price: m.price * rate, + marketCap: m.marketCap * rate, + volume24h: m.volume24h * rate + })) + + res.json({ data }) + + // Update redis cache + const redisData: CoinrankRedis = { + markets: data, + lastUpdate: now.toISOString() + } + await setAsync( + `${REDIS_COINRANK_KEY_PREFIX}_${fiatSuffix}`, + JSON.stringify(redisData) + ) + return + } + } + if (redisResult == null) { + res.status(400).send(`Unable to get results for fiatCode ${fiatCode}`) + return + } + + const { markets } = redisResult + const data = markets.slice(start - 1, start + length) + + res.json({ data }) + } catch (e) { + const err: any = e + res.status(500).send(err.message) + } +} + // *** ROUTES *** export const exchangeRateRouterV1 = (): express.Router => { @@ -346,6 +457,8 @@ export const exchangeRateRouterV2 = (): express.Router => { sendExchangeRates ]) + router.get('/coinrank', [sendCoinranks]) + return router } diff --git a/src/types.ts b/src/types.ts index 80427d0a..3e011207 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,25 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { asArray, asNumber, asObject, asOptional, asString } from 'cleaners' +import { + asArray, + asDate, + asNumber, + asObject, + asOptional, + asString +} from 'cleaners' + +const asStringNum = (raw: any): number => { + return Number(asString(raw)) +} + +export const asCoinrankReq = asObject({ + fiatCode: asOptional(asString, 'iso:USD'), + start: asOptional(asStringNum, 1), + length: asOptional(asStringNum, 100) +}) + +export type CoinrankReq = ReturnType // Force TS to derive the type of the return value since it is pretty obvious // eslint-disable-next-line @typescript-eslint/explicit-function-return-type @@ -62,3 +81,9 @@ export interface CoinrankRedis { lastUpdate: string markets: CoinrankMarkets } + +export const asExchangeRateResponse = asObject({ + currency_pair: asString, + date: asDate, + exchangeRate: asString +}) From 85f7c1588e56bc55c4d3508b7718329e0d8754a6 Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Mon, 9 Jan 2023 18:29:25 -0800 Subject: [PATCH 6/9] fixup! Add coinrank endpoint --- src/exchangeRateRouter.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/exchangeRateRouter.ts b/src/exchangeRateRouter.ts index b48e011d..e45d3faa 100644 --- a/src/exchangeRateRouter.ts +++ b/src/exchangeRateRouter.ts @@ -372,22 +372,22 @@ const sendCoinranks: express.RequestHandler = async ( // Get USD rankings const jsonString = await getAsync(`${REDIS_COINRANK_KEY_PREFIX}_USD`) redisResult = JSON.parse(jsonString) - const { markets } = redisResult - let data = markets.slice(start - 1, start + length) + let { markets } = redisResult // Modify the prices, mcap, & volume - data = data.map(m => ({ + markets = markets.map(m => ({ ...m, price: m.price * rate, marketCap: m.marketCap * rate, volume24h: m.volume24h * rate })) + const data = markets.slice(start - 1, start + length) res.json({ data }) // Update redis cache const redisData: CoinrankRedis = { - markets: data, + markets, lastUpdate: now.toISOString() } await setAsync( From 6f67c17a8de118d9f32e39c41c2578e9018819b9 Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Tue, 10 Jan 2023 09:58:17 -0800 Subject: [PATCH 7/9] fixup! Add coinrank endpoint --- src/exchangeRateRouter.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/exchangeRateRouter.ts b/src/exchangeRateRouter.ts index e45d3faa..82f7776e 100644 --- a/src/exchangeRateRouter.ts +++ b/src/exchangeRateRouter.ts @@ -27,6 +27,7 @@ import { toIsoPair } from './utils/utils' +const { defaultFiatCode } = config const EXPIRE_TIME = 60000 export interface ExchangeRateReq { currency_pair: string @@ -346,14 +347,13 @@ const sendCoinranks: express.RequestHandler = async ( .send(`Invalid length param: ${length}. Must be between 1-100`) return } - const fiatSuffix = fiatCode.split(':')[1] const jsonString = await getAsync( - `${REDIS_COINRANK_KEY_PREFIX}_${fiatSuffix}` + `${REDIS_COINRANK_KEY_PREFIX}_${fiatCode}` ) let redisResult: CoinrankRedis = JSON.parse(jsonString) - if (fiatCode !== 'iso:USD') { + if (fiatCode !== defaultFiatCode) { const lastUpdated = redisResult != null ? new Date(redisResult.lastUpdate).getTime() : 0 @@ -363,14 +363,16 @@ const sendCoinranks: express.RequestHandler = async ( // Get exchange rate const result = await fetch( - `${ratesServerAddress}/v2/exchangeRate?currency_pair=iso:USD_${fiatCode}` + `${ratesServerAddress}/v2/exchangeRate?currency_pair=${defaultFiatCode}_${fiatCode}` ) const resultJson = await result.json() const { exchangeRate } = asExchangeRateResponse(resultJson) const rate = Number(exchangeRate) // Get USD rankings - const jsonString = await getAsync(`${REDIS_COINRANK_KEY_PREFIX}_USD`) + const jsonString = await getAsync( + `${REDIS_COINRANK_KEY_PREFIX}_${defaultFiatCode}` + ) redisResult = JSON.parse(jsonString) let { markets } = redisResult @@ -391,7 +393,7 @@ const sendCoinranks: express.RequestHandler = async ( lastUpdate: now.toISOString() } await setAsync( - `${REDIS_COINRANK_KEY_PREFIX}_${fiatSuffix}`, + `${REDIS_COINRANK_KEY_PREFIX}_${fiatCode}`, JSON.stringify(redisData) ) return From e34ebade82407898a3596e01fded0b3945768307 Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Wed, 21 Dec 2022 23:45:55 -0800 Subject: [PATCH 8/9] Remove Nomics --- src/config.ts | 9 --- src/providers/nomics.ts | 119 ---------------------------- src/rates.ts | 2 - src/uidEngine.ts | 4 +- src/utils/currencyCodeMaps.json | 132 +------------------------------- src/utils/dbUtils.ts | 1 - 6 files changed, 2 insertions(+), 265 deletions(-) delete mode 100644 src/providers/nomics.ts diff --git a/src/config.ts b/src/config.ts index 81440877..b442e869 100644 --- a/src/config.ts +++ b/src/config.ts @@ -14,7 +14,6 @@ const { COIN_MARKET_CAP_HISTORICAL_API_KEY = '', SLACK_WEBHOOK_URL = '', OPEN_EXCHANGE_RATES_API_KEY, - NOMICS_API_KEY, DEFAULT_FIAT = 'iso:USD' } = process.env @@ -38,10 +37,6 @@ const proivderDefaults = { uri: 'https://openexchangerates.org', apiKey: OPEN_EXCHANGE_RATES_API_KEY }, - nomics: { - uri: 'https://api.nomics.com', - apiKey: NOMICS_API_KEY - }, coingecko: { uri: 'https://api.coingecko.com' }, @@ -129,10 +124,6 @@ export const asConfig = asObject({ uri: asString, apiKey: asString }), - nomics: asObject({ - uri: asString, - apiKey: asString - }), coingecko: asObject({ uri: asString }), diff --git a/src/providers/nomics.ts b/src/providers/nomics.ts deleted file mode 100644 index 067f97ca..00000000 --- a/src/providers/nomics.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { asArray, asObject, asString } from 'cleaners' -import fetch from 'node-fetch' - -import { config } from '../config' -import { AssetMap, NewRates, ReturnRate } from '../rates' -import { - assetMapReducer, - createReducedRateMapArray, - fromCode, - fromCryptoToFiatCurrencyPair, - hasUniqueId, - isIsoCode, - logger, - subIso -} from './../utils/utils' - -const { - providers: { - nomics: { uri, apiKey } - }, - defaultFiatCode: DEFAULT_FIAT -} = config - -const asNomicsQuote = asObject({ - price: asString, - symbol: asString -}) - -const asNomicsResponse = asArray(asNomicsQuote) - -const nomicsQuote = (code: ReturnType): string => - code.price - -const nomicsPair = (code: ReturnType): string => - fromCryptoToFiatCurrencyPair(code.symbol.toUpperCase()) - -const nomicsRateMap = createReducedRateMapArray(nomicsPair, nomicsQuote) - -export const nomics = async ( - requestedRates: ReturnRate[], - currentTime: string, - edgeAssetMap: AssetMap -): Promise => { - const rates = { [currentTime]: {} } - - if (apiKey == null) { - logger('No Nomics API key') - return rates - } - - // Gather codes - const codesWanted: string[] = [] - for (const request of requestedRates) { - if (request.date !== currentTime) continue - const fromCurrency = fromCode(request.currency_pair) - if (!isIsoCode(fromCurrency) && hasUniqueId(fromCurrency, edgeAssetMap)) - codesWanted.push(edgeAssetMap[fromCurrency]) - } - - // Query - if (codesWanted.length > 0) - try { - const ids = codesWanted.join(',') - const response = await fetch( - `${uri}/v1/currencies/ticker?key=${apiKey}&ids=${ids}&convert=${subIso( - DEFAULT_FIAT - )}` - ) - if ( - response.status === 429 || - response.status === 401 || - response.ok === false - ) { - logger( - `nomics returned code ${response.status} for ${ids} at ${currentTime}` - ) - throw new Error(response.statusText) - } - const json = asNomicsResponse(await response.json()) - - // Create return object - rates[currentTime] = nomicsRateMap(json) - } catch (e) { - logger('No Nomics quote:', e) - } - return rates -} - -const asNomicsAssetResponse = asArray( - asObject({ - id: asString, - symbol: asString - }) -) - -export const nomicsAssets = async (): Promise => { - let page = 1 - let out: ReturnType = [] - while (true) { - const response = await fetch( - `${uri}/v1/currencies/ticker?key=${apiKey}&sort=rank&status=active&page=${page}` - ) - if (response.status === 429) continue // retry. 1 req/sec so no need to delay - if (response.status === 401 || response.ok === false) { - logger(`nomicsAssets returned code ${response.status}`) - throw new Error(response.statusText) - } - const json = asNomicsAssetResponse(await response.json()) - out = [...out, ...json] - if (Object.keys(json).length < 100) break - // It's a long process so we should log the progress - logger( - `Querying nomicsAssets page ${page}. Found ${out.length} assets so far` - ) - page++ - } - logger(`Finished nomicsAssets query found ${out.length} assets`) - return assetMapReducer(out) -} diff --git a/src/rates.ts b/src/rates.ts index 0b812b75..dd4069aa 100644 --- a/src/rates.ts +++ b/src/rates.ts @@ -23,7 +23,6 @@ import { fallbackConstantRates, zeroRates } from './providers/hardcodedProviders' -import { nomics } from './providers/nomics' import { openExchangeRates } from './providers/openExchangeRates' import { wazirx } from './providers/wazirx' import { @@ -136,7 +135,6 @@ const getRatesFromProviders = async ( coingecko, coincap, coinMarketCap, - nomics, compound, fallbackConstantRates, currencyConverter, diff --git a/src/uidEngine.ts b/src/uidEngine.ts index f71b1cc6..c9af870a 100644 --- a/src/uidEngine.ts +++ b/src/uidEngine.ts @@ -1,7 +1,6 @@ import { coincapAssets } from './providers/coincap' import { coingeckoAssets } from './providers/coingecko' import { coinMarketCapAssets } from './providers/coinMarketCap' -import { nomicsAssets } from './providers/nomics' import currencyCodeMaps from './utils/currencyCodeMaps.json' import { slackMessage, @@ -13,8 +12,7 @@ import { logger } from './utils/utils' const providerAssets = { coincap: coincapAssets, coinMarketCap: coinMarketCapAssets, - coingecko: coingeckoAssets, - nomics: nomicsAssets + coingecko: coingeckoAssets } export const uidEngine = async (): Promise => { diff --git a/src/utils/currencyCodeMaps.json b/src/utils/currencyCodeMaps.json index df80902f..5e1f5239 100644 --- a/src/utils/currencyCodeMaps.json +++ b/src/utils/currencyCodeMaps.json @@ -466,136 +466,6 @@ "SUN": "sun-token", "USDJ": "just-stablecoin" }, - "nomics": { - "BTC": "BTC", - "BCH": "BCH", - "BTG": "BTG", - "BSV": "BSV", - "DASH": "DASH", - "DGB": "DGB", - "DOGE": "DOGE", - "DOT": "DOT", - "EBST": "EBST", - "FTC": "FTC", - "GRS": "GRS", - "LTC": "LTC", - "QTUM": "QTUM", - "RVN": "RVN", - "SMART": "SMART", - "UFO": "UFO", - "VTC": "VTC", - "XMR": "XMR", - "XRP": "XRP", - "XTZ": "XTZ", - "XLM": "XLM", - "FIO": "FIO", - "EOS": "EOS", - "BNB": "BNB", - "ETH": "ETH", - "ETC": "ETC", - "REP": "REP", - "DAI": "DAI", - "SAI": "SAI", - "WINGS": "WINGS", - "USDT": "USDT", - "HUR": "HUR", - "ANTV1": "ANTOLD", - "ANT": "ANT", - "BAT": "BAT", - "BNT": "BNT", - "KNC": "KNC", - "POLY": "POLY", - "STORJ": "STORJ", - "USDC": "USDC", - "TUSD": "TUSD", - "ZRX": "ZRX", - "GNO": "GNO", - "OMG": "OMG", - "NMR": "NMR", - "MKR": "MKR", - "GUSD": "GUSD", - "PAX": "PAX", - "SALT": "SALT", - "MANA": "MANA", - "NEXO": "NEXO", - "FUN": "FUN", - "KIN": "KIN", - "LINK": "LINK", - "OXT": "OXT", - "COMP": "COMP", - "MET": "MET", - "SNX": "SNX", - "AAVE": "AAVE", - "WBTC": "WBTC", - "YFI": "YFI", - "CRV": "CRV", - "BAL": "BAL", - "SUSHI": "SUSHI", - "UMA": "UMA", - "NXM": "NXM", - "PICKLE": "PICKLE", - "BAD": "BAD", - "BADGER": "BADGER", - "BOO": "BOO4", - "BRZ": "BRZ", - "CBAT": "CBAT", - "CDAI": "CDAI", - "CETH": "CETH", - "CEUR": "CUER", - "COMBO": "COMBO", - "BAND": "BAND", - "REN": "REN", - "AMPL": "AMPL", - "OCEAN": "OCEAN", - "GLM": "GLM", - "UNI": "UNI", - "CUSDC": "CUSDC", - "CVP": "CVP", - "DOUGH": "DOUGH", - "DPI": "DPI", - "ETHBNT": "ETHBNT", - "FIRO": "FIRO", - "GNT": "GNT", - "HERC": "HERC", - "IDLE": "IDLE", - "IND": "IND", - "INDEX": "INDEX2", - "PEFI": "PEFI", - "RBTC": "RBTC", - "RENBCH": "RENBCH", - "RENBTC": "RENBTC", - "RENZEC": "RENZEC", - "ROOK": "K3D", - "SPELL": "SPELL", - "FTM": "FTM", - "WAX": "WAXP", - "SUSD": "SUSD", - "HBAR": "HBAR", - "ZEC": "ZEC", - "MATIC": "MATIC", - "AVAX": "AVAX", - "PNG": "PNG2", - "TBTC": "TBTC", - "XAVA": "XAVA", - "BIFI": "BIFI2", - "TIME": "TIME5", - "JOE": "JOE", - "TLOS": "TLOS", - "TOMB": "TOMB", - "FXS": "FXS", - "MIM": "MIM", - "BUSD": "BUSDBEP20", - "TSHARE": "TSHARE2", - "CELO": "CELO", - "CUSD": "CUSD3", - "USDS": "USDS", - "WETH": "WETH", - "YAK": "YAK", - "YETI": "YETI", - "SOL": "SOL", - "ETHW": "ETHW2", - "RIF": "RIF" - }, "allEdgeCurrencies": [ "BTC", "BCH", @@ -902,4 +772,4 @@ "iso:XAG", "iso:ZWL" ] -} \ No newline at end of file +} diff --git a/src/utils/dbUtils.ts b/src/utils/dbUtils.ts index da9c533b..27df108b 100644 --- a/src/utils/dbUtils.ts +++ b/src/utils/dbUtils.ts @@ -150,7 +150,6 @@ const asCurrencyCodeMapsCleaner = asObject({ coinMarketCap: asMaybe(asObject(asString)), coincap: asMaybe(asObject(asString)), coingecko: asMaybe(asObject(asString)), - nomics: asMaybe(asObject(asString)), allEdgeCurrencies: asMaybe(asArray(asString)), fiatCurrencyCodes: asMaybe(asArray(asString)) }) From ca2ffd919d64d2f9b8aa39150935f981d07e4bd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 23:56:40 +0000 Subject: [PATCH 9/9] Bump express from 4.17.1 to 4.17.3 Bumps [express](https://github.com/expressjs/express) from 4.17.1 to 4.17.3. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.17.1...4.17.3) --- updated-dependencies: - dependency-name: express dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 219 ++++++++++++++++++++++++--------------------------- 2 files changed, 104 insertions(+), 117 deletions(-) diff --git a/package.json b/package.json index 811ee600..5e433c80 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "compression": "^1.7.4", "cors": "^2.8.5", "edge-server-tools": "^0.2.10", - "express": "^4.17.1", + "express": "^4.17.3", "morgan": "^1.10.0", "nano": "10.0.0", "node-fetch": "^2.6.0", diff --git a/yarn.lock b/yarn.lock index 8a89bdd1..6c0363bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -399,13 +399,13 @@ semver "^6.3.0" tsutils "^3.17.1" -accepts@~1.3.5, accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== +accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" + mime-types "~2.1.34" + negotiator "0.6.3" acorn-jsx@^5.1.0: version "5.1.0" @@ -602,21 +602,21 @@ bn.js@^4.11.7: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== +body-parser@1.19.2: + version "1.19.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" + integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== dependencies: - bytes "3.1.0" + bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" depd "~1.1.2" - http-errors "1.7.2" + http-errors "1.8.1" iconv-lite "0.4.24" on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" + qs "6.9.7" + raw-body "2.4.3" + type-is "~1.6.18" brace-expansion@^1.1.7: version "1.1.11" @@ -643,10 +643,10 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== call-bind@^1.0.0: version "1.0.2" @@ -862,12 +862,12 @@ contains-path@^0.1.0: resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: - safe-buffer "5.1.2" + safe-buffer "5.2.1" content-type@~1.0.4: version "1.0.4" @@ -886,10 +886,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== cors@^2.8.5: version "2.8.5" @@ -1348,17 +1348,17 @@ execa@^3.4.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== +express@^4.17.3: + version "4.17.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" + integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== dependencies: - accepts "~1.3.7" + accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" + body-parser "1.19.2" + content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.0" + cookie "0.4.2" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" @@ -1372,13 +1372,13 @@ express@^4.17.1: on-finished "~2.3.0" parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" + proxy-addr "~2.0.7" + qs "6.9.7" range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" + safe-buffer "5.2.1" + send "0.17.2" + serve-static "1.14.2" + setprototypeof "1.2.0" statuses "~1.5.0" type-is "~1.6.18" utils-merge "1.0.1" @@ -1516,10 +1516,10 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fresh@0.5.2: version "0.5.2" @@ -1679,27 +1679,16 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" inherits "2.0.4" - setprototypeof "1.1.1" + setprototypeof "1.2.0" statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + toidentifier "1.0.1" human-signals@^1.1.1: version "1.1.1" @@ -1759,11 +1748,6 @@ inherits@2, inherits@2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - inquirer@^7.0.0: version "7.0.4" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" @@ -2174,7 +2158,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== -mime-types@^2.1.12: +mime-types@^2.1.12, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -2283,6 +2267,11 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -2329,10 +2318,10 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== nice-try@^1.0.4: version "1.0.5" @@ -2753,12 +2742,12 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: - forwarded "~0.1.2" + forwarded "0.2.0" ipaddr.js "1.9.1" psl@^1.1.33: @@ -2779,37 +2768,30 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@6.9.7: + version "6.9.7" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" + integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== -qs@^6.10.3: +qs@^6.10.3, qs@^6.9.4: version "6.10.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== dependencies: side-channel "^1.0.4" -qs@^6.9.4: - version "6.10.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" - integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== - dependencies: - side-channel "^1.0.4" - range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== +raw-body@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" + integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== dependencies: - bytes "3.1.0" - http-errors "1.7.2" + bytes "3.1.2" + http-errors "1.8.1" iconv-lite "0.4.24" unpipe "1.0.0" @@ -2981,6 +2963,11 @@ safe-buffer@5.1.2, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -3009,10 +2996,10 @@ semver@^6.1.0, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== +send@0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== dependencies: debug "2.6.9" depd "~1.1.2" @@ -3021,32 +3008,32 @@ send@0.17.1: escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.7.2" + http-errors "1.8.1" mime "1.6.0" - ms "2.1.1" + ms "2.1.3" on-finished "~2.3.0" range-parser "~1.2.1" statuses "~1.5.0" -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== +serve-static@1.14.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.1" + send "0.17.2" set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shebang-command@^1.2.0: version "1.2.0" @@ -3394,10 +3381,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tough-cookie@^4.0.0: version "4.0.0" @@ -3442,7 +3429,7 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==