aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2021-04-27 14:29:07 -0500
committerGitHub <noreply@github.com>2021-04-27 14:29:07 -0500
commit4ce80d0af8f53e93aa3a936b1ad4c5b6c065c881 (patch)
treef5ffb4d7a23d7c6edee01fb605ff81348feea40d
parent562cd341f75bfb2701cc844cf30f1191e4170ca7 (diff)
downloadskyblock-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
-rw-r--r--.github/workflows/test.yml16
-rw-r--r--.gitignore4
-rw-r--r--build/database.js19
-rw-r--r--build/hypixelApi.js3
-rw-r--r--build/hypixelCached.js78
-rw-r--r--build/index.js4
-rw-r--r--build/util.js12
-rw-r--r--package-lock.json2879
-rw-r--r--package.json10
-rw-r--r--src/database.ts20
-rw-r--r--src/hypixelApi.ts2
-rw-r--r--src/hypixelCached.ts15
-rw-r--r--src/index.ts5
-rw-r--r--src/util.ts10
-rw-r--r--test-data-generator/index.ts44
-rw-r--r--test/data/mojang.json6
-rw-r--r--test/data/player/4133cab5a7534f3f9bb636fc06a1f0fd.json1063
-rw-r--r--test/data/player/6536bfed869548fd83a1ecd24cf2a0fd.json9451
-rw-r--r--test/data/player/e403573808ad45ddb5c48ec7c4db0144.json5569
-rw-r--r--test/data/player/ef3bb867eec048a1a9b92b451f0ffc66.json13505
-rw-r--r--test/data/skyblock/profiles/4133cab5a7534f3f9bb636fc06a1f0fd.json4
-rw-r--r--test/data/skyblock/profiles/6536bfed869548fd83a1ecd24cf2a0fd.json5197
-rw-r--r--test/data/skyblock/profiles/e403573808ad45ddb5c48ec7c4db0144.json658
-rw-r--r--test/data/skyblock/profiles/ef3bb867eec048a1a9b92b451f0ffc66.json7593
-rw-r--r--test/test.js146
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
diff --git a/.gitignore b/.gitignore
index 06be902..b062488 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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