diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2021-04-27 14:29:07 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-27 14:29:07 -0500 |
| commit | 4ce80d0af8f53e93aa3a936b1ad4c5b6c065c881 (patch) | |
| tree | f5ffb4d7a23d7c6edee01fb605ff81348feea40d | |
| parent | 562cd341f75bfb2701cc844cf30f1191e4170ca7 (diff) | |
| download | skyblock-api-4ce80d0af8f53e93aa3a936b1ad4c5b6c065c881.tar.gz skyblock-api-4ce80d0af8f53e93aa3a936b1ad4c5b6c065c881.tar.bz2 skyblock-api-4ce80d0af8f53e93aa3a936b1ad4c5b6c065c881.zip | |
Add unit tests (#12)
* start adding unit tests
* add more to test/data/mojang.json
* fix sending http requests in tests when it shouldn't
* add a few more tests
* try to add a github action to run tests
* Update test.yml
25 files changed, 46219 insertions, 94 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e152ab3 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,16 @@ +on: [ push, pull_request ] + +jobs: + build: + runs-on: ubuntu-latest + name: Run tests + steps: + - name: Checkout + uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '14.15.0' + - run: npm install + + - name: Run tests + run: npm test @@ -1,3 +1,5 @@ .env .vscode -node_modules
\ No newline at end of file +node_modules +.nyc_output +coverage
\ No newline at end of file diff --git a/build/database.js b/build/database.js index 8a29686..be193e9 100644 --- a/build/database.js +++ b/build/database.js @@ -358,11 +358,14 @@ async function fetchAllLeaderboards(fast) { if (_1.debug) console.log('Finished caching leaderboards!'); } -connect().then(() => { - // when it connects, cache the leaderboards and remove bad members - removeBadMemberLeaderboardAttributes(); - // cache leaderboards on startup so its faster later on - fetchAllLeaderboards(true); - // cache leaderboard players again every 4 hours - setInterval(fetchAllLeaderboards, 4 * 60 * 60 * 1000); -}); +// make sure it's not in a test +if (typeof global.it !== 'function') { + connect().then(() => { + // when it connects, cache the leaderboards and remove bad members + removeBadMemberLeaderboardAttributes(); + // cache leaderboards on startup so its faster later on + fetchAllLeaderboards(true); + // cache leaderboard players again every 4 hours + setInterval(fetchAllLeaderboards, 4 * 60 * 60 * 1000); + }); +} diff --git a/build/hypixelApi.js b/build/hypixelApi.js index 2100bfc..ccae09b 100644 --- a/build/hypixelApi.js +++ b/build/hypixelApi.js @@ -2,6 +2,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; +var _a, _b, _c; Object.defineProperty(exports, "__esModule", { value: true }); exports.sendApiRequest = exports.chooseApiKey = void 0; /** @@ -18,7 +19,7 @@ const httpsAgent = new https_1.Agent({ keepAlive: true }); /** This array should only ever contain one item because using multiple hypixel api keys isn't allowed :) */ -const apiKeys = process.env.hypixel_keys.split(' '); +const apiKeys = (_c = (_b = (_a = process.env) === null || _a === void 0 ? void 0 : _a.hypixel_keys) === null || _b === void 0 ? void 0 : _b.split(' ')) !== null && _c !== void 0 ? _c : []; const apiKeyUsage = {}; const baseHypixelAPI = 'https://api.hypixel.net'; /** Choose the best current API key */ diff --git a/build/hypixelCached.js b/build/hypixelCached.js index 8a35482..8693032 100644 --- a/build/hypixelCached.js +++ b/build/hypixelCached.js @@ -25,7 +25,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.fetchProfileName = exports.fetchProfile = exports.fetchProfileUuid = exports.fetchSkyblockProfiles = exports.fetchBasicPlayer = exports.fetchPlayer = exports.usernameFromUser = exports.uuidFromUser = void 0; +exports.fetchProfileName = exports.fetchProfile = exports.fetchProfileUuid = exports.fetchSkyblockProfiles = exports.fetchBasicPlayer = exports.fetchPlayer = exports.usernameFromUser = exports.uuidFromUser = exports.profileNameCache = exports.profilesCache = exports.profileCache = exports.basicPlayerCache = exports.playerCache = exports.basicProfilesCache = exports.usernameCache = void 0; const node_cache_1 = __importDefault(require("node-cache")); const mojang = __importStar(require("./mojang")); const hypixel = __importStar(require("./hypixel")); @@ -33,38 +33,38 @@ const util_1 = require("./util"); const _1 = require("."); // cache usernames for 4 hours /** uuid: username */ -const usernameCache = new node_cache_1.default({ +exports.usernameCache = new node_cache_1.default({ stdTTL: 60 * 60 * 4, checkperiod: 60, useClones: false, }); -const basicProfilesCache = new node_cache_1.default({ +exports.basicProfilesCache = new node_cache_1.default({ stdTTL: 60 * 10, checkperiod: 60, useClones: true, }); -const playerCache = new node_cache_1.default({ +exports.playerCache = new node_cache_1.default({ stdTTL: 60, checkperiod: 10, useClones: true, }); // cache "basic players" (players without profiles) for 4 hours -const basicPlayerCache = new node_cache_1.default({ +exports.basicPlayerCache = new node_cache_1.default({ stdTTL: 60 * 60 * 4, checkperiod: 60 * 10, useClones: true }); -const profileCache = new node_cache_1.default({ +exports.profileCache = new node_cache_1.default({ stdTTL: 30, checkperiod: 10, useClones: true, }); -const profilesCache = new node_cache_1.default({ +exports.profilesCache = new node_cache_1.default({ stdTTL: 60 * 3, checkperiod: 10, useClones: false, }); -const profileNameCache = new node_cache_1.default({ +exports.profileNameCache = new node_cache_1.default({ stdTTL: 60 * 60, checkperiod: 60, useClones: false, @@ -88,23 +88,23 @@ async function uuidFromUser(user) { // if the user is 32 characters long, it has to be a uuid if (util_1.isUuid(user)) return util_1.undashUuid(user); - if (usernameCache.has(util_1.undashUuid(user))) { + if (exports.usernameCache.has(util_1.undashUuid(user))) { // check if the uuid is a key - const username = usernameCache.get(util_1.undashUuid(user)); + const username = exports.usernameCache.get(util_1.undashUuid(user)); // sometimes the username will be null, return that if (username === null) return username; // if it has .then, then that means its a waitForCacheSet promise. This is done to prevent requests made while it is already requesting if (username.then) { const { key: uuid, value: _username } = await username; - usernameCache.set(uuid, _username); + exports.usernameCache.set(uuid, _username); return uuid; } else return util_1.undashUuid(user); } // check if the username is a value - const uuidToUsername = usernameCache.mget(usernameCache.keys()); + const uuidToUsername = exports.usernameCache.mget(exports.usernameCache.keys()); for (const [uuid, username] of Object.entries(uuidToUsername)) { if (username && username.toLowerCase && user.toLowerCase() === username.toLowerCase()) return uuid; @@ -112,18 +112,18 @@ async function uuidFromUser(user) { if (_1.debug) console.log('Cache miss: uuidFromUser', user); // set it as waitForCacheSet (a promise) in case uuidFromUser gets called while its fetching mojang - usernameCache.set(util_1.undashUuid(user), waitForCacheSet(usernameCache, user, user)); + exports.usernameCache.set(util_1.undashUuid(user), waitForCacheSet(exports.usernameCache, user, user)); // not cached, actually fetch mojang api now let { uuid, username } = await mojang.profileFromUser(user); if (!uuid) { - usernameCache.set(user, null); + exports.usernameCache.set(user, null); return; } // remove dashes from the uuid so its more normal uuid = util_1.undashUuid(uuid); if (user !== uuid) - usernameCache.del(user); - usernameCache.set(uuid, username); + exports.usernameCache.del(user); + exports.usernameCache.set(uuid, username); return uuid; } exports.uuidFromUser = uuidFromUser; @@ -132,24 +132,24 @@ exports.uuidFromUser = uuidFromUser; * @param user A user can be either a uuid or a username */ async function usernameFromUser(user) { - if (usernameCache.has(util_1.undashUuid(user))) { + if (exports.usernameCache.has(util_1.undashUuid(user))) { if (_1.debug) console.log('Cache hit! usernameFromUser', user); - return usernameCache.get(util_1.undashUuid(user)); + return exports.usernameCache.get(util_1.undashUuid(user)); } if (_1.debug) console.log('Cache miss: usernameFromUser', user); let { uuid, username } = await mojang.profileFromUser(user); uuid = util_1.undashUuid(uuid); - usernameCache.set(uuid, username); + exports.usernameCache.set(uuid, username); return username; } exports.usernameFromUser = usernameFromUser; let fetchingPlayers = new Set(); async function fetchPlayer(user) { const playerUuid = await uuidFromUser(user); - if (playerCache.has(playerUuid)) - return playerCache.get(playerUuid); + if (exports.playerCache.has(playerUuid)) + return exports.playerCache.get(playerUuid); // if it's already in the process of fetching, check every 100ms until it's not fetching the player anymore and fetch it again, since it'll be cached now if (fetchingPlayers.has(playerUuid)) { while (fetchingPlayers.has(playerUuid)) { @@ -166,29 +166,29 @@ async function fetchPlayer(user) { if (!cleanPlayer) return; // clone in case it gets modified somehow later - playerCache.set(playerUuid, cleanPlayer); - usernameCache.set(playerUuid, cleanPlayer.username); + exports.playerCache.set(playerUuid, cleanPlayer); + exports.usernameCache.set(playerUuid, cleanPlayer.username); const cleanBasicPlayer = Object.assign({}, cleanPlayer); delete cleanBasicPlayer.profiles; - basicPlayerCache.set(playerUuid, cleanBasicPlayer); + exports.basicPlayerCache.set(playerUuid, cleanBasicPlayer); return cleanPlayer; } exports.fetchPlayer = fetchPlayer; /** Fetch a player without their profiles. This is heavily cached. */ async function fetchBasicPlayer(user) { const playerUuid = await uuidFromUser(user); - if (basicPlayerCache.has(playerUuid)) - return basicPlayerCache.get(playerUuid); + if (exports.basicPlayerCache.has(playerUuid)) + return exports.basicPlayerCache.get(playerUuid); const player = await fetchPlayer(playerUuid); delete player.profiles; return player; } exports.fetchBasicPlayer = fetchBasicPlayer; async function fetchSkyblockProfiles(playerUuid) { - if (profilesCache.has(playerUuid)) { + if (exports.profilesCache.has(playerUuid)) { if (_1.debug) console.log('Cache hit! fetchSkyblockProfiles', playerUuid); - return profilesCache.get(playerUuid); + return exports.profilesCache.get(playerUuid); } if (_1.debug) console.log('Cache miss: fetchSkyblockProfiles', playerUuid); @@ -212,7 +212,7 @@ async function fetchSkyblockProfiles(playerUuid) { basicProfiles.push(basicProfile); } // cache the profiles - profilesCache.set(playerUuid, basicProfiles); + exports.profilesCache.set(playerUuid, basicProfiles); return basicProfiles; } exports.fetchSkyblockProfiles = fetchSkyblockProfiles; @@ -221,19 +221,19 @@ async function fetchBasicProfiles(user) { const playerUuid = await uuidFromUser(user); if (!playerUuid) return; // invalid player, just return - if (basicProfilesCache.has(playerUuid)) { + if (exports.basicProfilesCache.has(playerUuid)) { if (_1.debug) console.log('Cache hit! fetchBasicProfiles', playerUuid); - return basicProfilesCache.get(playerUuid); + return exports.basicProfilesCache.get(playerUuid); } if (_1.debug) console.log('Cache miss: fetchBasicProfiles', user); const player = await fetchPlayer(playerUuid); const profiles = player.profiles; - basicProfilesCache.set(playerUuid, profiles); + exports.basicProfilesCache.set(playerUuid, profiles); // cache the profile names and uuids to profileNameCache because we can for (const profile of profiles) - profileNameCache.set(`${playerUuid}.${profile.uuid}`, profile.name); + exports.profileNameCache.set(`${playerUuid}.${profile.uuid}`, profile.name); return profiles; } /** @@ -270,11 +270,11 @@ exports.fetchProfileUuid = fetchProfileUuid; async function fetchProfile(user, profile) { const playerUuid = await uuidFromUser(user); const profileUuid = await fetchProfileUuid(playerUuid, profile); - if (profileCache.has(profileUuid)) { + if (exports.profileCache.has(profileUuid)) { // we have the profile cached, return it :) if (_1.debug) console.log('Cache hit! fetchProfile', profileUuid); - return profileCache.get(profileUuid); + return exports.profileCache.get(profileUuid); } if (_1.debug) console.log('Cache miss: fetchProfile', user, profile); @@ -282,7 +282,7 @@ async function fetchProfile(user, profile) { const cleanProfile = await hypixel.fetchMemberProfileUncached(playerUuid, profileUuid); // we know the name from fetchProfileName, so set it here cleanProfile.name = profileName; - profileCache.set(profileUuid, cleanProfile); + exports.profileCache.set(profileUuid, cleanProfile); return cleanProfile; } exports.fetchProfile = fetchProfile; @@ -295,11 +295,11 @@ async function fetchProfileName(user, profile) { // we're fetching the profile and player uuid again in case we were given a name, but it's cached so it's not much of a problem const profileUuid = await fetchProfileUuid(user, profile); const playerUuid = await uuidFromUser(user); - if (profileNameCache.has(`${playerUuid}.${profileUuid}`)) { + if (exports.profileNameCache.has(`${playerUuid}.${profileUuid}`)) { // Return the profile name if it's cached if (_1.debug) console.log('Cache hit! fetchProfileName', profileUuid); - return profileNameCache.get(`${playerUuid}.${profileUuid}`); + return exports.profileNameCache.get(`${playerUuid}.${profileUuid}`); } if (_1.debug) console.log('Cache miss: fetchProfileName', user, profile); @@ -308,7 +308,7 @@ async function fetchProfileName(user, profile) { for (const basicProfile of basicProfiles) if (basicProfile.uuid === playerUuid) profileName = basicProfile.name; - profileNameCache.set(`${playerUuid}.${profileUuid}`, profileName); + exports.profileNameCache.set(`${playerUuid}.${profileUuid}`, profileName); return profileName; } exports.fetchProfileName = fetchProfileName; diff --git a/build/index.js b/build/index.js index 4dbe69a..30d2553 100644 --- a/build/index.js +++ b/build/index.js @@ -45,4 +45,6 @@ app.get('/leaderboard/:name', async (req, res) => { app.get('/leaderboards', async (req, res) => { res.json(await database_1.fetchAllLeaderboardsCategorized()); }); -app.listen(8080, () => console.log('App started :)')); +// only run the server if it's not doing tests +if (typeof global.it !== 'function') + app.listen(8080, () => console.log('App started :)')); diff --git a/build/util.js b/build/util.js index e7ec9a6..c0bbd42 100644 --- a/build/util.js +++ b/build/util.js @@ -3,21 +3,11 @@ * Random utility functions that are not related to Hypixel */ Object.defineProperty(exports, "__esModule", { value: true }); -exports.isUuid = exports.sleep = exports.colorCodeFromName = exports.minecraftColorCodes = exports.shuffle = exports.jsonToQuery = exports.queryToJson = exports.undashUuid = void 0; +exports.isUuid = exports.sleep = exports.colorCodeFromName = exports.minecraftColorCodes = exports.shuffle = exports.jsonToQuery = exports.undashUuid = void 0; function undashUuid(uuid) { return uuid.replace(/-/g, '').toLowerCase(); } exports.undashUuid = undashUuid; -function queryToJson(queryString) { - const query = {}; - const pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&'); - for (let i = 0; i < pairs.length; i++) { - const pair = pairs[i].split('='); - query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || ''); - } - return query; -} -exports.queryToJson = queryToJson; function jsonToQuery(data) { return Object.entries(data || {}).map(e => e.join('=')).join('&'); } diff --git a/package-lock.json b/package-lock.json index a1cf876..9c5b6ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,2062 @@ { "name": "skyblock-api", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.17.1", + "express-rate-limit": "^5.2.6", + "mongodb": "^3.6.6", + "node-cache": "^5.1.2", + "node-fetch": "^2.6.1", + "prismarine-nbt": "^1.5.0", + "queue-promise": "^2.1.0" + }, + "devDependencies": { + "@types/express": "^4.17.11", + "@types/express-rate-limit": "^5.1.1", + "@types/mocha": "^8.2.2", + "@types/mongodb": "^3.6.12", + "@types/node": "^14.14.41", + "@types/node-fetch": "^2.5.10", + "dotenv": "^8.2.0", + "mocha": "^8.3.2", + "ts-node": "^9.1.1", + "typescript": "^4.2.4" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bson": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", + "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-rate-limit": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.1.tgz", + "integrity": "sha512-6oMYZBLlhxC5sdcRXXz528QyfGz3zTy9YdHwqlxLfgx5Cd3zwYaUjjPpJcaTtHmRefLi9P8kLBPz2wB7yz4JtQ==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.18", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz", + "integrity": "sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", + "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", + "dev": true + }, + "node_modules/@types/mongodb": { + "version": "3.6.12", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.12.tgz", + "integrity": "sha512-49aEzQD5VdHPxyd5dRyQdqEveAg9LanwrH8RQipnMuulwzKmODXIZRp0umtxi1eBUfEusRkoy8AVOMr+kVuFog==", + "dev": true, + "dependencies": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "14.14.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", + "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.5.10", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.10.tgz", + "integrity": "sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "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 + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved |
