diff options
47 files changed, 2852 insertions, 957 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d797ab9..ed1fc94 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ jobs: uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '14.15.0' + node-version: '16.0.0' - run: npm install - name: Run tests diff --git a/build/cleaners/player.js b/build/cleaners/player.js index 42c76b8..214be1f 100644 --- a/build/cleaners/player.js +++ b/build/cleaners/player.js @@ -1,22 +1,17 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanPlayerResponse = void 0; -const profiles_1 = require("./skyblock/profiles"); -const socialmedia_1 = require("./socialmedia"); -const rank_1 = require("./rank"); -const util_1 = require("../util"); -async function cleanPlayerResponse(data) { - var _a, _b; +import { cleanPlayerSkyblockProfiles } from './skyblock/profiles.js'; +import { cleanSocialMedia } from './socialmedia.js'; +import { cleanRank } from './rank.js'; +import { undashUuid } from '../util.js'; +export async function cleanPlayerResponse(data) { // Cleans up a 'player' api response if (!data) return null; // bruh return { - uuid: util_1.undashUuid(data.uuid), + uuid: undashUuid(data.uuid), username: data.displayname, - rank: rank_1.cleanRank(data), - socials: socialmedia_1.cleanSocialMedia(data), + rank: cleanRank(data), + socials: cleanSocialMedia(data), // first_join: data.firstLogin / 1000, - profiles: profiles_1.cleanPlayerSkyblockProfiles((_b = (_a = data.stats) === null || _a === void 0 ? void 0 : _a.SkyBlock) === null || _b === void 0 ? void 0 : _b.profiles) + profiles: cleanPlayerSkyblockProfiles(data.stats?.SkyBlock?.profiles) }; } -exports.cleanPlayerResponse = cleanPlayerResponse; diff --git a/build/cleaners/rank.js b/build/cleaners/rank.js index 19e4938..1d80d2c 100644 --- a/build/cleaners/rank.js +++ b/build/cleaners/rank.js @@ -1,7 +1,4 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanRank = void 0; -const util_1 = require("../util"); +import { colorCodeFromName, minecraftColorCodes } from '../util.js'; const rankColors = { 'NONE': '7', 'VIP': 'a', @@ -16,15 +13,14 @@ const rankColors = { 'ADMIN': 'c' }; /** Response cleaning (reformatting to be nicer) */ -function cleanRank({ packageRank, newPackageRank, monthlyPackageRank, rankPlusColor, rank, prefix }) { - var _a; +export function cleanRank({ packageRank, newPackageRank, monthlyPackageRank, rankPlusColor, rank, prefix }) { let name; let color; let colored; let bracketColor; if (prefix) { // derive values from prefix colored = prefix; - color = util_1.minecraftColorCodes[colored.match(/§./)[0][1]]; + color = minecraftColorCodes[colored.match(/§./)[0][1]]; name = colored.replace(/§./g, '').replace(/[\[\]]/g, ''); } else { @@ -33,7 +29,8 @@ function cleanRank({ packageRank, newPackageRank, monthlyPackageRank, rankPlusCo else if (rank && rank !== 'NORMAL') name = rank; else - name = (_a = newPackageRank === null || newPackageRank === void 0 ? void 0 : newPackageRank.replace('_PLUS', '+')) !== null && _a !== void 0 ? _a : packageRank === null || packageRank === void 0 ? void 0 : packageRank.replace('_PLUS', '+'); + name = newPackageRank?.replace('_PLUS', '+') + ?? packageRank?.replace('_PLUS', '+'); switch (name) { // MVP++ is called Superstar for some reason case 'SUPERSTAR': @@ -54,8 +51,8 @@ function cleanRank({ packageRank, newPackageRank, monthlyPackageRank, rankPlusCo name = 'NONE'; break; } - const plusColor = rankPlusColor ? util_1.colorCodeFromName(rankPlusColor) : null; - color = util_1.minecraftColorCodes[rankColors[name]]; + const plusColor = rankPlusColor ? colorCodeFromName(rankPlusColor) : null; + color = minecraftColorCodes[rankColors[name]]; let rankColorPrefix = rankColors[name] ? '§' + rankColors[name] : ''; // the text is white, but only in the prefix if (name === 'YOUTUBE') @@ -82,4 +79,3 @@ function cleanRank({ packageRank, newPackageRank, monthlyPackageRank, rankPlusCo colored }; } -exports.cleanRank = cleanRank; diff --git a/build/cleaners/skyblock/bank.js b/build/cleaners/skyblock/bank.js index 9836685..fb08af5 100644 --- a/build/cleaners/skyblock/bank.js +++ b/build/cleaners/skyblock/bank.js @@ -1,13 +1,8 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanBank = void 0; -function cleanBank(data) { - var _a, _b; +export function cleanBank(data) { return { - balance: (_b = (_a = data === null || data === void 0 ? void 0 : data.banking) === null || _a === void 0 ? void 0 : _a.balance) !== null && _b !== void 0 ? _b : 0, + balance: data?.banking?.balance ?? 0, // TODO: make transactions good // history: data?.banking?.transactions ?? [] history: [] }; } -exports.cleanBank = cleanBank; diff --git a/build/cleaners/skyblock/collections.js b/build/cleaners/skyblock/collections.js index 0bb4cb5..4a5b34e 100644 --- a/build/cleaners/skyblock/collections.js +++ b/build/cleaners/skyblock/collections.js @@ -1,7 +1,4 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanCollections = void 0; -const itemId_1 = require("./itemId"); +import { cleanItemId } from './itemId.js'; const COLLECTIONS = { 'farming': [ 'wheat', @@ -83,15 +80,14 @@ function getCategory(collectionName) { return categoryName; } } -function cleanCollections(data) { - var _a, _b, _c; +export function cleanCollections(data) { // collection tiers show up like this: [ GRAVEL_3, GOLD_INGOT_2, MELON_-1, LOG_2:1_7, RAW_FISH:3_-1] // these tiers are the same for all players in a coop - const playerCollectionTiersRaw = (_a = data === null || data === void 0 ? void 0 : data.unlocked_coll_tiers) !== null && _a !== void 0 ? _a : []; + const playerCollectionTiersRaw = data?.unlocked_coll_tiers ?? []; const playerCollectionTiers = {}; for (const collectionTierNameValueRaw of playerCollectionTiersRaw) { const [collectionTierNameRaw, collectionTierValueRaw] = collectionTierNameValueRaw.split(/_(?=-?\d+$)/); - const collectionName = itemId_1.cleanItemId(collectionTierNameRaw); + const collectionName = cleanItemId(collectionTierNameRaw); // ensure it's at least 0 const collectionValue = Math.max(parseInt(collectionTierValueRaw), 0); // if the collection hasn't been checked yet, or the new value is higher than the old, replace it @@ -100,13 +96,13 @@ function cleanCollections(data) { } // collection names show up like this: { LOG: 49789, LOG:2: 26219, MUSHROOM_COLLECTION: 2923} // these values are different for each player in a coop - const playerCollectionXpsRaw = (_b = data === null || data === void 0 ? void 0 : data.collection) !== null && _b !== void 0 ? _b : {}; + const playerCollectionXpsRaw = data?.collection ?? {}; const playerCollections = []; for (const collectionNameRaw in playerCollectionXpsRaw) { const collectionXp = playerCollectionXpsRaw[collectionNameRaw]; - const collectionName = itemId_1.cleanItemId(collectionNameRaw); + const collectionName = cleanItemId(collectionNameRaw); const collectionLevel = playerCollectionTiers[collectionName]; - const collectionCategory = (_c = getCategory(collectionName)) !== null && _c !== void 0 ? _c : 'unknown'; + const collectionCategory = getCategory(collectionName) ?? 'unknown'; // in some very weird cases the collection level will be undefined, we should ignore these collections if (collectionLevel !== undefined) playerCollections.push({ @@ -118,4 +114,3 @@ function cleanCollections(data) { } return playerCollections; } -exports.cleanCollections = cleanCollections; diff --git a/build/cleaners/skyblock/fairysouls.js b/build/cleaners/skyblock/fairysouls.js index 1c6cef6..8ec8078 100644 --- a/build/cleaners/skyblock/fairysouls.js +++ b/build/cleaners/skyblock/fairysouls.js @@ -1,12 +1,7 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanFairySouls = void 0; -function cleanFairySouls(data) { - var _a, _b, _c; +export function cleanFairySouls(data) { return { - total: (_a = data === null || data === void 0 ? void 0 : data.fairy_souls_collected) !== null && _a !== void 0 ? _a : 0, - unexchanged: (_b = data === null || data === void 0 ? void 0 : data.fairy_souls) !== null && _b !== void 0 ? _b : 0, - exchanges: (_c = data === null || data === void 0 ? void 0 : data.fairy_exchanges) !== null && _c !== void 0 ? _c : 0, + total: data?.fairy_souls_collected ?? 0, + unexchanged: data?.fairy_souls ?? 0, + exchanges: data?.fairy_exchanges ?? 0, }; } -exports.cleanFairySouls = cleanFairySouls; diff --git a/build/cleaners/skyblock/inventory.js b/build/cleaners/skyblock/inventory.js index 098b8d4..714a302 100644 --- a/build/cleaners/skyblock/inventory.js +++ b/build/cleaners/skyblock/inventory.js @@ -1,32 +1,9 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanInventories = exports.INVENTORIES = exports.cleanInventory = void 0; // maybe todo?: create a fast replacement for prismarine-nbt -const nbt = __importStar(require("prismarine-nbt")); +import * as nbt from 'prismarine-nbt'; function base64decode(base64) { return Buffer.from(base64, 'base64'); } function cleanItem(rawItem) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o; // if the item doesn't have an id, it isn't an item if (rawItem.id === undefined) return null; @@ -34,13 +11,13 @@ function cleanItem(rawItem) { const itemCount = rawItem.Count; const damageValue = rawItem.Damage; const itemTag = rawItem.tag; - const extraAttributes = (_a = itemTag === null || itemTag === void 0 ? void 0 : itemTag.ExtraAttributes) !== null && _a !== void 0 ? _a : {}; + const extraAttributes = itemTag?.ExtraAttributes ?? {}; let headId; if (vanillaId === 397) { - const headDataBase64 = (_e = (_d = (_c = (_b = itemTag === null || itemTag === void 0 ? void 0 : itemTag.SkullOwner) === null || _b === void 0 ? void 0 : _b.Properties) === null || _c === void 0 ? void 0 : _c.textures) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.Value; + const headDataBase64 = itemTag?.SkullOwner?.Properties?.textures?.[0]?.Value; if (headDataBase64) { const headData = JSON.parse(base64decode(headDataBase64).toString()); - const headDataUrl = (_g = (_f = headData === null || headData === void 0 ? void 0 : headData.textures) === null || _f === void 0 ? void 0 : _f.SKIN) === null || _g === void 0 ? void 0 : _g.url; + const headDataUrl = headData?.textures?.SKIN?.url; if (headDataUrl) { const splitUrl = headDataUrl.split('/'); headId = splitUrl[splitUrl.length - 1]; @@ -48,26 +25,26 @@ function cleanItem(rawItem) { } } return { - id: (_h = extraAttributes === null || extraAttributes === void 0 ? void 0 : extraAttributes.id) !== null && _h !== void 0 ? _h : null, - count: itemCount !== null && itemCount !== void 0 ? itemCount : 1, + id: extraAttributes?.id ?? null, + count: itemCount ?? 1, vanillaId: damageValue ? `${vanillaId}:${damageValue}` : vanillaId.toString(), display: { - name: (_k = (_j = itemTag === null || itemTag === void 0 ? void 0 : itemTag.display) === null || _j === void 0 ? void 0 : _j.Name) !== null && _k !== void 0 ? _k : 'null', - lore: (_m = (_l = itemTag === null || itemTag === void 0 ? void 0 : itemTag.display) === null || _l === void 0 ? void 0 : _l.Lore) !== null && _m !== void 0 ? _m : [], + name: itemTag?.display?.Name ?? 'null', + lore: itemTag?.display?.Lore ?? [], // if it has an ench value in the tag, then it should have an enchant glint effect - glint: ((_o = itemTag === null || itemTag === void 0 ? void 0 : itemTag.ench) !== null && _o !== void 0 ? _o : []).length > 0 + glint: (itemTag?.ench ?? []).length > 0 }, - reforge: extraAttributes === null || extraAttributes === void 0 ? void 0 : extraAttributes.modifier, - enchantments: extraAttributes === null || extraAttributes === void 0 ? void 0 : extraAttributes.enchantments, - anvil_uses: extraAttributes === null || extraAttributes === void 0 ? void 0 : extraAttributes.anvil_uses, - timestamp: extraAttributes === null || extraAttributes === void 0 ? void 0 : extraAttributes.timestamp, + reforge: extraAttributes?.modifier, + enchantments: extraAttributes?.enchantments, + anvil_uses: extraAttributes?.anvil_uses, + timestamp: extraAttributes?.timestamp, head_texture: headId, }; } function cleanItems(rawItems) { return rawItems.map(cleanItem); } -function cleanInventory(encodedNbt) { +export function cleanInventory(encodedNbt) { return new Promise(resolve => { const base64Data = base64decode(encodedNbt); nbt.parse(base64Data, false, (err, value) => { @@ -77,8 +54,7 @@ function cleanInventory(encodedNbt) { }); }); } -exports.cleanInventory = cleanInventory; -exports.INVENTORIES = { +export const INVENTORIES = { armor: 'inv_armor', inventory: 'inv_contents', ender_chest: 'ender_chest_contents', @@ -89,12 +65,11 @@ exports.INVENTORIES = { trick_or_treat_bag: 'candy_inventory_contents', wardrobe: 'wardrobe_contents' }; -async function cleanInventories(data) { - var _a; +export async function cleanInventories(data) { const cleanInventories = {}; - for (const cleanInventoryName in exports.INVENTORIES) { - const hypixelInventoryName = exports.INVENTORIES[cleanInventoryName]; - const encodedInventoryContents = (_a = data[hypixelInventoryName]) === null || _a === void 0 ? void 0 : _a.data; + for (const cleanInventoryName in INVENTORIES) { + const hypixelInventoryName = INVENTORIES[cleanInventoryName]; + const encodedInventoryContents = data[hypixelInventoryName]?.data; let inventoryContents; if (encodedInventoryContents) { inventoryContents = await cleanInventory(encodedInventoryContents); @@ -106,4 +81,3 @@ async function cleanInventories(data) { } return cleanInventories; } -exports.cleanInventories = cleanInventories; diff --git a/build/cleaners/skyblock/itemId.js b/build/cleaners/skyblock/itemId.js index 7bf5402..ea94771 100644 --- a/build/cleaners/skyblock/itemId.js +++ b/build/cleaners/skyblock/itemId.js @@ -1,6 +1,3 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanItemId = void 0; // change weird item names to be more consistent with vanilla const ITEMS = { 'log': 'oak_log', @@ -55,8 +52,6 @@ const ITEMS = { 'enchanted_glistering_melon': 'enchanted_glistering_melon_slice' }; /** Clean an item with a weird name (log_2:1) and make it have a better name (dark_oak_log) */ -function cleanItemId(itemId) { - var _a; - return (_a = ITEMS[itemId.toLowerCase()]) !== null && _a !== void 0 ? _a : itemId.toLowerCase(); +export function cleanItemId(itemId) { + return ITEMS[itemId.toLowerCase()] ?? itemId.toLowerCase(); } -exports.cleanItemId = cleanItemId; diff --git a/build/cleaners/skyblock/member.js b/build/cleaners/skyblock/member.js index cc66488..2242828 100644 --- a/build/cleaners/skyblock/member.js +++ b/build/cleaners/skyblock/member.js @@ -1,37 +1,15 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanSkyBlockProfileMemberResponse = exports.cleanSkyBlockProfileMemberResponseBasic = void 0; -const collections_1 = require("./collections"); -const inventory_1 = require("./inventory"); -const fairysouls_1 = require("./fairysouls"); -const objectives_1 = require("./objectives"); -const stats_1 = require("./stats"); -const minions_1 = require("./minions"); -const slayers_1 = require("./slayers"); -const zones_1 = require("./zones"); -const skills_1 = require("./skills"); -const cached = __importStar(require("../../hypixelCached")); -const constants = __importStar(require("../../constants")); -async function cleanSkyBlockProfileMemberResponseBasic(member) { +import { cleanCollections } from './collections.js'; +import { cleanInventories } from './inventory.js'; +import { cleanFairySouls } from './fairysouls.js'; +import { cleanObjectives } from './objectives.js'; +import { cleanProfileStats } from './stats.js'; +import { cleanMinions } from './minions.js'; +import { cleanSlayers } from './slayers.js'; +import { cleanVisitedZones } from './zones.js'; +import { cleanSkills } from './skills.js'; +import * as cached from '../../hypixelCached.js'; +import * as constants from '../../constants.js'; +export async function cleanSkyBlockProfileMemberResponseBasic(member) { const player = await cached.fetchPlayer(member.uuid); if (!player) return null; @@ -43,18 +21,16 @@ async function cleanSkyBlockProfileMemberResponseBasic(member) { rank: player.rank }; } -exports.cleanSkyBlockProfileMemberResponseBasic = cleanSkyBlockProfileMemberResponseBasic; /** Cleans up a member (from skyblock/profile) */ -async function cleanSkyBlockProfileMemberResponse(member, included = undefined) { - var _a; +export async function cleanSkyBlockProfileMemberResponse(member, included = undefined) { // profiles.members[] const inventoriesIncluded = included === undefined || included.includes('inventories'); const player = await cached.fetchPlayer(member.uuid); if (!player) return null; - const fairySouls = fairysouls_1.cleanFairySouls(member); + const fairySouls = cleanFairySouls(member); const { max_fairy_souls: maxFairySouls } = await constants.fetchConstantValues(); - if (fairySouls.total > (maxFairySouls !== null && maxFairySouls !== void 0 ? maxFairySouls : 0)) + if (fairySouls.total > (maxFairySouls ?? 0)) await constants.setConstantValues({ max_fairy_souls: fairySouls.total }); return { uuid: member.uuid, @@ -63,17 +39,16 @@ async function cleanSkyBlockProfileMemberResponse(member, included = undefined) first_join: member.first_join / 1000, rank: player.rank, purse: member.coin_purse, - stats: stats_1.cleanProfileStats(member), + stats: cleanProfileStats(member), // this is used for leaderboards - rawHypixelStats: (_a = member.stats) !== null && _a !== void 0 ? _a : {}, - minions: await minions_1.cleanMinions(member), + rawHypixelStats: member.stats ?? {}, + minions: await cleanMinions(member), fairy_souls: fairySouls, - inventories: inventoriesIncluded ? await inventory_1.cleanInventories(member) : undefined, - objectives: objectives_1.cleanObjectives(member), - skills: await skills_1.cleanSkills(member), - visited_zones: zones_1.cleanVisitedZones(member), - collections: collections_1.cleanCollections(member), - slayers: slayers_1.cleanSlayers(member) + inventories: inventoriesIncluded ? await cleanInventories(member) : undefined, + objectives: cleanObjectives(member), + skills: await cleanSkills(member), + visited_zones: cleanVisitedZones(member), + collections: cleanCollections(member), + slayers: cleanSlayers(member) }; } -exports.cleanSkyBlockProfileMemberResponse = cleanSkyBlockProfileMemberResponse; diff --git a/build/cleaners/skyblock/minions.js b/build/cleaners/skyblock/minions.js index d2bfc41..5c0bd9a 100644 --- a/build/cleaners/skyblock/minions.js +++ b/build/cleaners/skyblock/minions.js @@ -1,36 +1,13 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.countUniqueMinions = exports.combineMinionArrays = exports.cleanMinions = void 0; -const hypixel_1 = require("../../hypixel"); -const constants = __importStar(require("../../constants")); +import { maxMinion } from '../../hypixel.js'; +import * as constants from '../../constants.js'; /** * Clean the minions provided by Hypixel * @param minionsRaw The minion data provided by the Hypixel API */ -async function cleanMinions(member) { - var _a; +export async function cleanMinions(member) { const minions = []; const processedMinionNames = new Set(); - for (const minionRaw of (_a = member === null || member === void 0 ? void 0 : member.crafted_generators) !== null && _a !== void 0 ? _a : []) { + for (const minionRaw of member?.crafted_generators ?? []) { // do some regex magic to get the minion name and level // examples of potential minion names: CLAY_11, PIG_1, MAGMA_CUBE_4 const minionName = minionRaw.split(/_\d/)[0].toLowerCase(); @@ -40,7 +17,7 @@ async function cleanMinions(member) { // if the minion doesnt already exist in the minions array, then create it matchingMinion = { name: minionName, - levels: new Array(hypixel_1.maxMinion).fill(false) + levels: new Array(maxMinion).fill(false) }; minions.push(matchingMinion); } @@ -63,18 +40,17 @@ async function cleanMinions(member) { processedMinionNames.add(minionName); minions.push({ name: minionName, - levels: new Array(hypixel_1.maxMinion).fill(false) + levels: new Array(maxMinion).fill(false) }); } } return minions.sort((a, b) => a.name > b.name ? 1 : (a.name < b.name ? -1 : 0)); } -exports.cleanMinions = cleanMinions; /** * Combine multiple arrays of minions into one, useful when getting the minions for members * @param minions An array of arrays of minions */ -function combineMinionArrays(minions) { +export function combineMinionArrays(minions) { const resultMinions = []; for (const memberMinions of minions) { for (const minion of memberMinions) { @@ -99,8 +75,7 @@ function combineMinionArrays(minions) { } return resultMinions; } -exports.combineMinionArrays = combineMinionArrays; -function countUniqueMinions(minions) { +export function countUniqueMinions(minions) { let uniqueMinions = 0; for (const minion of minions) { // find the number of times `true` is in the list and add it to uniqueMinions @@ -108,4 +83,3 @@ function countUniqueMinions(minions) { } return uniqueMinions; } -exports.countUniqueMinions = countUniqueMinions; diff --git a/build/cleaners/skyblock/objectives.js b/build/cleaners/skyblock/objectives.js index d45307c..52e92db 100644 --- a/build/cleaners/skyblock/objectives.js +++ b/build/cleaners/skyblock/objectives.js @@ -1,8 +1,5 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanObjectives = void 0; -function cleanObjectives(data) { - const rawObjectives = (data === null || data === void 0 ? void 0 : data.objectives) || {}; +export function cleanObjectives(data) { + const rawObjectives = data?.objectives || {}; const objectives = []; for (const rawObjectiveName in rawObjectives) { const rawObjectiveValue = rawObjectives[rawObjectiveName]; @@ -13,4 +10,3 @@ function cleanObjectives(data) { } return objectives; } -exports.cleanObjectives = cleanObjectives; diff --git a/build/cleaners/skyblock/profile.js b/build/cleaners/skyblock/profile.js index 6460c3b..42e26b3 100644 --- a/build/cleaners/skyblock/profile.js +++ b/build/cleaners/skyblock/profile.js @@ -1,38 +1,16 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanSkyblockProfileResponse = exports.cleanSkyblockProfileResponseLighter = void 0; -const member_1 = require("./member"); -const minions_1 = require("./minions"); -const bank_1 = require("./bank"); -const constants = __importStar(require("../../constants")); +import { cleanSkyBlockProfileMemberResponse, cleanSkyBlockProfileMemberResponseBasic } from './member.js'; +import { combineMinionArrays, countUniqueMinions } from './minions.js'; +import * as constants from '../../constants.js'; +import { cleanBank } from './bank.js'; /** Return a `CleanProfile` instead of a `CleanFullProfile`, useful when we need to get members but don't want to waste much ram */ -async function cleanSkyblockProfileResponseLighter(data) { +export async function cleanSkyblockProfileResponseLighter(data) { // We use Promise.all so it can fetch all the usernames at once instead of waiting for the previous promise to complete const promises = []; for (const memberUUID in data.members) { const memberRaw = data.members[memberUUID]; memberRaw.uuid = memberUUID; // we pass an empty array to make it not check stats - promises.push(member_1.cleanSkyBlockProfileMemberResponseBasic(memberRaw)); + promises.push(cleanSkyBlockProfileMemberResponseBasic(memberRaw)); } const cleanedMembers = (await Promise.all(promises)).filter(m => m); return { @@ -41,11 +19,10 @@ async function cleanSkyblockProfileResponseLighter(data) { members: cleanedMembers, }; } -exports.cleanSkyblockProfileResponseLighter = cleanSkyblockProfileResponseLighter; /** * This function is somewhat costly and shouldn't be called often. Use cleanSkyblockProfileResponseLighter if you don't need all the data */ -async function cleanSkyblockProfileResponse(data, options) { +export async function cleanSkyblockProfileResponse(data, options) { // We use Promise.all so it can fetch all the users at once instead of waiting for the previous promise to complete const promises = []; if (!data) @@ -53,13 +30,13 @@ async function cleanSkyblockProfileResponse(data, options) { for (const memberUUID in data.members) { const memberRaw = data.members[memberUUID]; memberRaw.uuid = memberUUID; - promises.push(member_1.cleanSkyBlockProfileMemberResponse(memberRaw, [ - !(options === null || options === void 0 ? void 0 : options.basic) ? 'stats' : undefined, - (options === null || options === void 0 ? void 0 : options.mainMemberUuid) === memberUUID ? 'inventories' : undefined + promises.push(cleanSkyBlockProfileMemberResponse(memberRaw, [ + !options?.basic ? 'stats' : undefined, + options?.mainMemberUuid === memberUUID ? 'inventories' : undefined ])); } const cleanedMembers = (await Promise.all(promises)).filter(m => m !== null && m !== undefined); - if (options === null || options === void 0 ? void 0 : options.basic) { + if (options?.basic) { return { uuid: data.profile_id, name: data.cute_name, @@ -70,19 +47,18 @@ async function cleanSkyblockProfileResponse(data, options) { for (const member of cleanedMembers) { memberMinions.push(member.minions); } - const minions = minions_1.combineMinionArrays(memberMinions); + const minions = combineMinionArrays(memberMinions); const { max_minions: maxUniqueMinions } = await constants.fetchConstantValues(); - const uniqueMinions = minions_1.countUniqueMinions(minions); - if (uniqueMinions > (maxUniqueMinions !== null && maxUniqueMinions !== void 0 ? maxUniqueMinions : 0)) + const uniqueMinions = countUniqueMinions(minions); + if (uniqueMinions > (maxUniqueMinions ?? 0)) await constants.setConstantValues({ max_minions: uniqueMinions }); // return more detailed info return { uuid: data.profile_id, name: data.cute_name, members: cleanedMembers, - bank: bank_1.cleanBank(data), + bank: cleanBank(data), minions: minions, minion_count: uniqueMinions }; } -exports.cleanSkyblockProfileResponse = cleanSkyblockProfileResponse; diff --git a/build/cleaners/skyblock/profiles.js b/build/cleaners/skyblock/profiles.js index b916209..a85b1f3 100644 --- a/build/cleaners/skyblock/profiles.js +++ b/build/cleaners/skyblock/profiles.js @@ -1,10 +1,7 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanSkyblockProfilesResponse = exports.cleanPlayerSkyblockProfiles = void 0; -const profile_1 = require("./profile"); -function cleanPlayerSkyblockProfiles(rawProfiles) { +import { cleanSkyblockProfileResponse } from './profile.js'; +export function cleanPlayerSkyblockProfiles(rawProfiles) { let profiles = []; - for (const profile of Object.values(rawProfiles !== null && rawProfiles !== void 0 ? rawProfiles : {})) { + for (const profile of Object.values(rawProfiles ?? {})) { profiles.push({ uuid: profile.profile_id, name: profile.cute_name @@ -12,15 +9,13 @@ function cleanPlayerSkyblockProfiles(rawProfiles) { } return profiles; } -exports.cleanPlayerSkyblockProfiles = cleanPlayerSkyblockProfiles; /** Convert an array of raw profiles into clean profiles */ -async function cleanSkyblockProfilesResponse(data) { +export async function cleanSkyblockProfilesResponse(data) { const promises = []; - for (const profile of data !== null && data !== void 0 ? data : []) { + for (const profile of data ?? []) { // let cleanedProfile = await cleanSkyblockProfileResponseLighter(profile) - promises.push(profile_1.cleanSkyblockProfileResponse(profile)); + promises.push(cleanSkyblockProfileResponse(profile)); } const cleanedProfiles = (await Promise.all(promises)).filter(p => p); return cleanedProfiles; } -exports.cleanSkyblockProfilesResponse = cleanSkyblockProfilesResponse; diff --git a/build/cleaners/skyblock/skills.js b/build/cleaners/skyblock/skills.js index 2518d60..e9b19f8 100644 --- a/build/cleaners/skyblock/skills.js +++ b/build/cleaners/skyblock/skills.js @@ -1,6 +1,3 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanSkills = exports.levelForSkillXp = void 0; // the highest level you can have in each skill // numbers taken from https://hypixel-skyblock.fandom.com/wiki/Skills const skillsMaxLevel = { @@ -113,21 +110,19 @@ const skillsDefaultMaxLevel = 50; * @param xp The xp we're finding the level for * @param easierLevel Whether it should use the alternate leveling xp table (used for cosmetic skills and dungeoneering) */ -function levelForSkillXp(xp, maxLevel) { +export function levelForSkillXp(xp, maxLevel) { const xpTable = (maxLevel <= 25 ? skillXpTableEasier : skillXpTable).slice(0, maxLevel); const skillLevel = [...xpTable].reverse().findIndex(levelXp => xp >= levelXp); return skillLevel === -1 ? 0 : xpTable.length - skillLevel; } -exports.levelForSkillXp = levelForSkillXp; -async function cleanSkills(data) { - var _a; +export async function cleanSkills(data) { const skills = []; for (const item in data) { if (item.startsWith('experience_skill_')) { const skillName = item.substr('experience_skill_'.length); // the amount of total xp you have in this skill const skillXp = data[item]; - const skillMaxLevel = (_a = skillsMaxLevel[skillName]) !== null && _a !== void 0 ? _a : skillsDefaultMaxLevel; + const skillMaxLevel = skillsMaxLevel[skillName] ?? skillsDefaultMaxLevel; const xpTable = (skillMaxLevel <= 25 ? skillXpTableEasier : skillXpTable).slice(0, skillMaxLevel); // the level you're at for this skill const skillLevel = levelForSkillXp(skillXp, skillMaxLevel); @@ -149,4 +144,3 @@ async function cleanSkills(data) { } return skills; } -exports.cleanSkills = cleanSkills; diff --git a/build/cleaners/skyblock/slayers.js b/build/cleaners/skyblock/slayers.js index 8575d43..75894f7 100644 --- a/build/cleaners/skyblock/slayers.js +++ b/build/cleaners/skyblock/slayers.js @@ -1,30 +1,26 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanSlayers = exports.slayerLevels = void 0; -exports.slayerLevels = 5; +export const slayerLevels = 5; const SLAYER_NAMES = { spider: 'tarantula', zombie: 'revenant', wolf: 'sven' }; -function cleanSlayers(data) { - var _a, _b; +export function cleanSlayers(data) { const slayers = []; - const slayersDataRaw = data === null || data === void 0 ? void 0 : data.slayer_bosses; + const slayersDataRaw = data?.slayer_bosses; let totalXp = 0; let totalKills = 0; for (const slayerNameRaw in slayersDataRaw) { const slayerDataRaw = slayersDataRaw[slayerNameRaw]; // convert name provided by api (spider) to the real name (tarantula) const slayerName = SLAYER_NAMES[slayerNameRaw]; - const slayerXp = (_a = slayerDataRaw.xp) !== null && _a !== void 0 ? _a : 0; + const slayerXp = slayerDataRaw.xp ?? 0; let slayerKills = 0; const slayerTiers = []; for (const slayerDataKey in slayerDataRaw) { // if a key starts with boss_kills_tier_ (boss_kills_tier_1), get the last number if (slayerDataKey.startsWith('boss_kills_tier_')) { const slayerTierRaw = parseInt(slayerDataKey.substr('boss_kills_tier_'.length)); - const slayerTierKills = (_b = slayerDataRaw[slayerDataKey]) !== null && _b !== void 0 ? _b : 0; + const slayerTierKills = slayerDataRaw[slayerDataKey] ?? 0; // add 1 since hypixel is using 0 indexed tiers const slayerTier = slayerTierRaw + 1; slayerTiers.push({ @@ -37,7 +33,7 @@ function cleanSlayers(data) { } } // if the slayer tier length is less than the max, add more empty ones - while (slayerTiers.length < exports.slayerLevels) + while (slayerTiers.length < slayerLevels) slayerTiers.push({ tier: slayerTiers.length + 1, kills: 0 @@ -46,7 +42,7 @@ function cleanSlayers(data) { name: slayerName, raw_name: slayerNameRaw, tiers: slayerTiers, - xp: slayerXp !== null && slayerXp !== void 0 ? slayerXp : 0, + xp: slayerXp ?? 0, kills: slayerKills }; slayers.push(slayer); @@ -62,4 +58,3 @@ function cleanSlayers(data) { bosses: slayers }; } -exports.cleanSlayers = cleanSlayers; diff --git a/build/cleaners/skyblock/stats.js b/build/cleaners/skyblock/stats.js index 1f72201..361f1ba 100644 --- a/build/cleaners/skyblock/stats.js +++ b/build/cleaners/skyblock/stats.js @@ -1,6 +1,3 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanProfileStats = exports.getStatUnit = exports.statUnits = exports.categorizeStat = void 0; const statCategories = { 'deaths': ['deaths_', 'deaths'], 'kills': ['kills_', 'kills'], @@ -13,7 +10,7 @@ const statCategories = { 'slayer': ['slayer_'], 'misc': null // everything else goes here }; -function categorizeStat(statNameRaw) { +export function categorizeStat(statNameRaw) { // 'deaths_void' for (const statCategory in statCategories) { // 'deaths' @@ -56,15 +53,14 @@ function categorizeStat(statNameRaw) { name: statNameRaw }; } -exports.categorizeStat = categorizeStat; -exports.statUnits = { +export const statUnits = { time: ['_best_time', '_best_time_2'], date: ['first_join'], coins: ['purse'], leaderboards: ['leaderboards_count', 'top_1_leaderboards_count'] }; -function getStatUnit(name) { - for (const [unitName, statMatchers] of Object.entries(exports.statUnits)) { +export function getStatUnit(name) { + for (const [unitName, statMatchers] of Object.entries(statUnits)) { for (const statMatch of statMatchers) { let trailingEnd = statMatch[0] === '_'; let trailingStart = statMatch.substr(-1) === '_'; @@ -76,23 +72,20 @@ function getStatUnit(name) { } return null; } -exports.getStatUnit = getStatUnit; -function cleanProfileStats(data) { - var _a, _b; +export function cleanProfileStats(data) { // TODO: add type for statsRaw (probably in hypixelApi.ts since its coming from there) const stats = []; - const rawStats = (_a = data === null || data === void 0 ? void 0 : data.stats) !== null && _a !== void 0 ? _a : {}; + const rawStats = data?.stats ?? {}; for (const statNameRaw in rawStats) { const statValue = rawStats[statNameRaw]; let { category: statCategory, name: statName } = categorizeStat(statNameRaw); stats.push({ - categorizedName: statName !== null && statName !== void 0 ? statName : 'total', + categorizedName: statName ?? 'total', value: statValue, rawName: statNameRaw, category: statCategory, - unit: (_b = getStatUnit(statNameRaw)) !== null && _b !== void 0 ? _b : null + unit: getStatUnit(statNameRaw) ?? null }); } return stats; } -exports.cleanProfileStats = cleanProfileStats; diff --git a/build/cleaners/skyblock/zones.js b/build/cleaners/skyblock/zones.js index fb79441..ef7015e 100644 --- a/build/cleaners/skyblock/zones.js +++ b/build/cleaners/skyblock/zones.js @@ -1,8 +1,5 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanVisitedZones = void 0; -function cleanVisitedZones(data) { - const rawZones = (data === null || data === void 0 ? void 0 : data.visited_zones) || []; +export function cleanVisitedZones(data) { + const rawZones = data?.visited_zones || []; // TODO: store all the zones that exist in SkyBlock, add add those to the array with visited being false const zones = []; for (const rawZoneName of rawZones) { @@ -13,4 +10,3 @@ function cleanVisitedZones(data) { } return zones; } -exports.cleanVisitedZones = cleanVisitedZones; diff --git a/build/cleaners/socialmedia.js b/build/cleaners/socialmedia.js index 4883cc4..c901725 100644 --- a/build/cleaners/socialmedia.js +++ b/build/cleaners/socialmedia.js @@ -1,11 +1,6 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanSocialMedia = void 0; -function cleanSocialMedia(data) { - var _a, _b, _c, _d; +export function cleanSocialMedia(data) { return { - discord: ((_b = (_a = data === null || data === void 0 ? void 0 : data.socialMedia) === null || _a === void 0 ? void 0 : _a.links) === null || _b === void 0 ? void 0 : _b.DISCORD) || null, - forums: ((_d = (_c = data === null || data === void 0 ? void 0 : data.socialMedia) === null || _c === void 0 ? void 0 : _c.links) === null || _d === void 0 ? void 0 : _d.HYPIXEL) || null + discord: data?.socialMedia?.links?.DISCORD || null, + forums: data?.socialMedia?.links?.HYPIXEL || null }; } -exports.cleanSocialMedia = cleanSocialMedia; diff --git a/build/constants.js b/build/constants.js index 414a6c5..7dcb210 100644 --- a/build/constants.js +++ b/build/constants.js @@ -1,46 +1,21 @@ -"use strict"; /** * Fetch and edit constants from the skyblock-constants repo */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.setConstantValues = exports.fetchConstantValues = exports.addMinions = exports.fetchSkillXpEasier = exports.fetchSkillXp = exports.fetchMinions = exports.addSlayers = exports.fetchSlayers = exports.addZones = exports.fetchZones = exports.addSkills = exports.fetchSkills = exports.addCollections = exports.fetchCollections = exports.addStats = exports.fetchStats = exports.addJSONConstants = exports.fetchJSONConstant = void 0; // we have to do this so we can mock the function from the tests properly -const constants = __importStar(require("./constants")); -const node_cache_1 = __importDefault(require("node-cache")); -const queue_promise_1 = __importDefault(require("queue-promise")); -const node_fetch_1 = __importDefault(require("node-fetch")); -const https_1 = require("https"); -const _1 = require("."); -const httpsAgent = new https_1.Agent({ +import * as constants from './constants.js'; +import NodeCache from 'node-cache'; +import { debug } from './index.js'; +import Queue from 'queue-promise'; +import fetch from 'node-fetch'; +import { Agent } from 'https'; +const httpsAgent = new Agent({ keepAlive: true }); const githubApiBase = 'https://api.github.com'; const owner = 'skyblockstats'; const repo = 'skyblock-constants'; // we use a queue for editing so it always utilizes the cache if possible, and to avoid hitting the github rateimit -const queue = new queue_promise_1.default({ +const queue = new Queue({ concurrent: 1, interval: 10 }); @@ -53,9 +28,9 @@ const queue = new queue_promise_1.default({ */ async function fetchGithubApi(method, route, headers, json) { try { - if (_1.debug) + if (debug) console.debug('fetching github api', method, route); - const data = await node_fetch_1.default(githubApiBase + route, { + const data = await fetch(githubApiBase + route, { agent: () => httpsAgent, body: json ? JSON.stringify(json) : undefined, method, @@ -63,7 +38,7 @@ async function fetchGithubApi(method, route, headers, json) { 'Authorization': `token ${process.env.github_token}` }, headers), }); - if (_1.debug) + if (debug) console.debug('fetched github api', method, route); return data; } @@ -74,7 +49,7 @@ async function fetchGithubApi(method, route, headers, json) { } } // cache files for an hour -const fileCache = new node_cache_1.default({ +const fileCache = new NodeCache({ stdTTL: 60 * 60, checkperiod: 60, useClones: false, @@ -122,7 +97,7 @@ async function editFile(file, message, newContent) { sha: data.content.sha }); } -async function fetchJSONConstant(filename) { +export let fetchJSONConstant = async function fetchJSONConstant(filename) { const file = await fetchFile(filename); try { return JSON.parse(file.content); @@ -131,10 +106,9 @@ async function fetchJSONConstant(filename) { // probably invalid json, return an empty array return []; } -} -exports.fetchJSONConstant = fetchJSONConstant; +}; /** Add stats to skyblock-constants. This has caching so it's fine to call many times */ -async function addJSONConstants(filename, addingValues, unit = 'stat') { +export let addJSONConstants = async function addJSONConstants(filename, addingValues, unit = 'stat') { if (addingValues.length === 0) return; // no stats provided, just return let file = await fetchFile(filename); @@ -166,81 +140,65 @@ async function addJSONConstants(filename, addingValues, unit = 'stat') { file = await fetchFile(filename); await editFile(file, commitMessage, JSON.stringify(updatedStats, null, 2)); } -} -exports.addJSONConstants = addJSONConstants; +}; /** Fetch all the known SkyBlock stats as an array of strings */ -async function fetchStats() { +export async function fetchStats() { return await constants.fetchJSONConstant('stats.json'); } -exports.fetchStats = fetchStats; /** Add stats to skyblock-constants. This has caching so it's fine to call many times */ -async function addStats(addingStats) { +export async function addStats(addingStats) { await constants.addJSONConstants('stats.json', addingStats, 'stat'); } -exports.addStats = addStats; /** Fetch all the known SkyBlock collections as an array of strings */ -async function fetchCollections() { +export async function fetchCollections() { return await constants.fetchJSONConstant('collections.json'); } -exports.fetchCollections = fetchCollections; /** Add collections to skyblock-constants. This has caching so it's fine to call many times */ -async function addCollections(addingCollections) { +export async function addCollections(addingCollections) { await constants.addJSONConstants('collections.json', addingCollections, 'collection'); } -exports.addCollections = addCollections; /** Fetch all the known SkyBlock collections as an array of strings */ -async function fetchSkills() { +export async function fetchSkills() { return await constants.fetchJSONConstant('skills.json'); } -exports.fetchSkills = fetchSkills; /** Add skills to skyblock-constants. This has caching so it's fine to call many times */ -async function addSkills(addingSkills) { +export async function addSkills(addingSkills) { await constants.addJSONConstants('skills.json', addingSkills, 'skill'); } -exports.addSkills = addSkills; /** Fetch all the known SkyBlock collections as an array of strings */ -async function fetchZones() { +export async function fetchZones() { return await constants.fetchJSONConstant('zones.json'); } -exports.fetchZones = fetchZones; /** Add skills to skyblock-constants. This has caching so it's fine to call many times */ -async function addZones(addingZones) { +export async function addZones(addingZones) { await constants.addJSONConstants('zones.json', addingZones, 'zone'); } -exports.addZones = addZones; /** Fetch all the known SkyBlock slayer names as an array of strings */ -async function fetchSlayers() { +export async function fetchSlayers() { return await constants.fetchJSONConstant('slayers.json'); } -exports.fetchSlayers = fetchSlayers; /** Add skills to skyblock-constants. This has caching so it's fine to call many times */ -async function addSlayers(addingSlayers) { +export async function addSlayers(addingSlayers) { await constants.addJSONConstants('slayers.json', addingSlayers, 'slayer'); } -exports.addSlayers = addSlayers; /** Fetch all the known SkyBlock slayer names as an array of strings */ -async function fetchMinions() { +export async function fetchMinions() { return await constants.fetchJSONConstant('minions.json'); } -exports.fetchMinions = fetchMinions; -async function fetchSkillXp() { +export async function fetchSkillXp() { return await constants.fetchJSONConstant('manual/skill_xp.json'); } -exports.fetchSkillXp = fetchSkillXp; -async function fetchSkillXpEasier() { +export async function fetchSkillXpEasier() { return await constants.fetchJSONConstant('manual/skill_xp_easier.json'); } -exports.fetchSkillXpEasier = fetchSkillXpEasier; /** Add skills to skyblock-constants. This has caching so it's fine to call many times */ -async function addMinions(addingMinions) { +export async function addMinions(addingMinions) { await constants.addJSONConstants('minions.json', addingMinions, 'minion'); } -exports.addMinions = addMinions; -async function fetchConstantValues() { +export async function fetchConstantValues() { return await constants.fetchJSONConstant('values.json'); } -exports.fetchConstantValues = fetchConstantValues; -async function setConstantValues(newValues) { +export async function setConstantValues(newValues) { let file = await fetchFile('values.json'); if (!file.path) return; @@ -263,4 +221,6 @@ async function setConstantValues(newValues) { } catch { } } -exports.setConstantValues = setConstantValues; +// this is necessary for mocking in the tests because es6 +export function mockAddJSONConstants($value) { addJSONConstants = $value; } +export function mockFetchJSONConstant($value) { fetchJSONConstant = $value; } diff --git a/build/database.js b/build/database.js index a12fc9e..b55a4ae 100644 --- a/build/database.js +++ b/build/database.js @@ -1,54 +1,29 @@ -"use strict"; /** * Store data about members for leaderboards */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.updateAccount = exports.fetchAccountFromDiscord = exports.fetchAccount = exports.fetchSession = exports.createSession = exports.finishedCachingRawLeaderboards = exports.queueUpdateDatabaseProfile = exports.queueUpdateDatabaseMember = exports.leaderboardUpdateProfileQueue = exports.leaderboardUpdateMemberQueue = exports.updateDatabaseProfile = exports.updateDatabaseMember = exports.fetchMemberLeaderboardSpots = exports.fetchLeaderboard = exports.fetchProfileLeaderboard = exports.fetchMemberLeaderboard = exports.fetchAllMemberLeaderboardAttributes = exports.fetchSlayerLeaderboards = exports.fetchAllLeaderboardsCategorized = exports.cachedRawLeaderboards = void 0; -const stats_1 = require("./cleaners/skyblock/stats"); -const slayers_1 = require("./cleaners/skyblock/slayers"); -const mongodb_1 = require("mongodb"); -const cached = __importStar(require("./hypixelCached")); -const constants = __importStar(require("./constants")); -const util_1 = require("./util"); -const node_cache_1 = __importDefault(require("node-cache")); -const uuid_1 = require("uuid"); -const queue_promise_1 = __importDefault(require("queue-promise")); -const _1 = require("."); +import { categorizeStat, getStatUnit } from './cleaners/skyblock/stats.js'; +import { slayerLevels } from './cleaners/skyblock/slayers.js'; +import { MongoClient } from 'mongodb'; +import * as cached from './hypixelCached.js'; +import * as constants from './constants.js'; +import { shuffle, sleep } from './util.js'; +import NodeCache from 'node-cache'; +import { v4 as uuid4 } from 'uuid'; +import Queue from 'queue-promise'; +import { debug } from './index.js'; // don't update the user for 3 minutes -const recentlyUpdated = new node_cache_1.default({ +const recentlyUpdated = new NodeCache({ stdTTL: 60 * 3, checkperiod: 60, useClones: false, }); // don't add stuff to the queue within the same 5 minutes -const recentlyQueued = new node_cache_1.default({ +const recentlyQueued = new NodeCache({ stdTTL: 60 * 5, checkperiod: 60, useClones: false, }); -exports.cachedRawLeaderboards = new Map(); +export const cachedRawLeaderboards = new Map(); const leaderboardMax = 100; const reversedLeaderboards = [ 'first_join', @@ -71,7 +46,7 @@ async function connect() { return console.warn('Warning: db_uri was not found in .env. Features that utilize the database such as leaderboards won\'t work.'); if (!process.env.db_name) return console.warn('Warning: db_name was not found in .env. Features that utilize the database such as leaderboards won\'t work.'); - client = await mongodb_1.MongoClient.connect(process.env.db_uri, { useNewUrlParser: true, useUnifiedTopology: true }); + client = await MongoClient.connect(process.env.db_uri); database = client.db(process.env.db_name); memberLeaderboardsCollection = database.collection('member-leaderboards'); profileLeaderboardsCollection = database.collection('profile-leaderboards'); @@ -132,12 +107,12 @@ function getProfileLeaderboardAttributes(profile) { unique_minions: profile.minion_count }; } -async function fetchAllLeaderboardsCategorized() { +export async function fetchAllLeaderboardsCategorized() { const memberLeaderboardAttributes = await fetchAllMemberLeaderboardAttributes(); const profileLeaderboardAttributes = await fetchAllProfileLeaderboardAttributes(); const categorizedLeaderboards = {}; for (const leaderboard of [...memberLeaderboardAttributes, ...profileLeaderboardAttributes]) { - const { category } = stats_1.categorizeStat(leaderboard); + const { category } = categorizeStat(leaderboard); if (category) { if (!categorizedLeaderboards[category]) categorizedLeaderboards[category] = []; @@ -150,9 +125,8 @@ async function fetchAllLeaderboardsCategorized() { categorizedLeaderboards.misc = misc; return categorizedLeaderboards; } -exports.fetchAllLeaderboardsCategorized = fetchAllLeaderboardsCategorized; /** Fetch the raw names for the slayer leaderboards */ -async function fetchSlayerLeaderboards() { +export async function fetchSlayerLeaderboards() { const rawSlayerNames = await constants.fetchSlayers(); let leaderboardNames = [ 'slayer_total_xp', @@ -162,15 +136,14 @@ async function fetchSlayerLeaderboards() { for (const slayerNameRaw of rawSlayerNames) { leaderboardNames.push(`slayer_${slayerNameRaw}_total_xp`); leaderboardNames.push(`slayer_${slayerNameRaw}_total_kills`); - for (let slayerTier = 1; slayerTier <= slayers_1.slayerLevels; slayerTier++) { + for (let slayerTier = 1; slayerTier <= slayerLevels; slayerTier++) { leaderboardNames.push(`slayer_${slayerNameRaw}_${slayerTier}_kills`); } } return leaderboardNames; } -exports.fetchSlayerLeaderboards = fetchSlayerLeaderboards; /** Fetch the names of all the leaderboards that rank members */ -async function fetchAllMemberLeaderboardAttributes() { +export async function fetchAllMemberLeaderboardAttributes() { return [ // we use the raw stat names rather than the clean stats in case hypixel adds a new stat and it takes a while for us to clean it ...await constants.fetchStats(), @@ -188,7 +161,6 @@ async function fetchAllMemberLeaderboardAttributes() { 'top_1_leaderboards_count' ]; } -exports.fetchAllMemberLeaderboardAttributes = fetchAllMemberLeaderboardAttributes; /** Fetch the names of all the leaderboards that rank profiles */ async function fetchAllProfileLeaderboardAttributes() { return [ @@ -211,14 +183,14 @@ const fetchingRawLeaderboardNames = new Set(); async function fetchMemberLeaderboardRaw(name) { if (!client) throw Error('Client isn\'t initialized yet'); - if (exports.cachedRawLeaderboards.has(name)) - return exports.cachedRawLeaderboards.get(name); + if (cachedRawLeaderboards.has(name)) + return cachedRawLeaderboards.get(name); // if it's currently being fetched, check every 100ms until it's in cachedRawLeaderboards if (fetchingRawLeaderboardNames.has(name)) { while (true) { - await util_1.sleep(100); - if (exports.cachedRawLeaderboards.has(name)) - return exports.cachedRawLeaderboards.get(name); + await sleep(100); + if (cachedRawLeaderboards.has(name)) + return cachedRawLeaderboards.get(name); } } // typescript forces us to make a new variable and set it this way because it gives an error otherwise @@ -240,18 +212,18 @@ async function fetchMemberLeaderboardRaw(name) { }; }); fetchingRawLeaderboardNames.delete(name); - exports.cachedRawLeaderboards.set(name, leaderboardRaw); + cachedRawLeaderboards.set(name, leaderboardRaw); return leaderboardRaw; } async function fetchProfileLeaderboardRaw(name) { - if (exports.cachedRawLeaderboards.has(name)) - return exports.cachedRawLeaderboards.get(name); + if (cachedRawLeaderboards.has(name)) + return cachedRawLeaderboards.get(name); // if it's currently being fetched, check every 100ms until it's in cachedRawLeaderboards if (fetchingRawLeaderboardNames.has(name)) { while (true) { - await util_1.sleep(100); - if (exports.cachedRawLeaderboards.has(name)) - return exports.cachedRawLeaderboards.get(name); + await sleep(100); + if (cachedRawLeaderboards.has(name)) + return cachedRawLeaderboards.get(name); } } // typescript forces us to make a new variable and set it this way because it gives an error otherwise @@ -273,12 +245,11 @@ async function fetchProfileLeaderboardRaw(name) { }; }); fetchingRawLeaderboardNames.delete(name); - exports.cachedRawLeaderboards.set(name, leaderboardRaw); + cachedRawLeaderboards.set(name, leaderboardRaw); return leaderboardRaw; } /** Fetch a leaderboard that ranks members, as opposed to profiles */ -async function fetchMemberLeaderboard(name) { - var _a; +export async function fetchMemberLeaderboard(name) { const leaderboardRaw = await fetchMemberLeaderboardRaw(name); const fetchLeaderboardPlayer = async (i) => { const player = await cached.fetchBasicPlayer(i.uuid); @@ -295,14 +266,12 @@ async function fetchMemberLeaderboard(name) { const leaderboard = await Promise.all(promises); return { name: name, - unit: (_a = stats_1.getStatUnit(name)) !== null && _a !== void 0 ? _a : null, + unit: getStatUnit(name) ?? null, list: leaderboard }; } -exports.fetchMemberLeaderboard = fetchMemberLeaderboard; /** Fetch a leaderboard that ranks profiles, as opposed to members */ -async function fetchProfileLeaderboard(name) { - var _a; +export async function fetchProfileLeaderboard(name) { const leaderboardRaw = await fetchProfileLeaderboardRaw(name); const fetchLeaderboardProfile = async (i) => { const players = []; @@ -324,13 +293,12 @@ async function fetchProfileLeaderboard(name) { const leaderboard = await Promise.all(promises); return { name: name, - unit: (_a = stats_1.getStatUnit(name)) !== null && _a !== void 0 ? _a : null, + unit: getStatUnit(name) ?? null, list: leaderboard }; } -exports.fetchProfileLeaderboard = fetchProfileLeaderboard; /** Fetch a leaderboard */ -async function fetchLeaderboard(name) { +export async function fetchLeaderboard(name) { const profileLeaderboards = await fetchAllProfileLeaderboardAttributes(); let leaderboard; if (profileLeaderboards.includes(name)) { @@ -343,10 +311,8 @@ async function fetchLeaderboard(name) { leaderboard.info = leaderboardInfos[name]; return leaderboard; } -exports.fetchLeaderboard = fetchLeaderboard; /** Get the leaderboard positions a member is on. This may take a while depending on whether stuff is cached */ -async function fetchMemberLeaderboardSpots(player, profile) { - var _a; +export async function fetchMemberLeaderboardSpots(player, profile) { const fullProfile = await cached.fetchProfile(player, profile); if (!fullProfile) return null; @@ -364,14 +330,12 @@ async function fetchMemberLeaderboardSpots(player, profile) { name: leaderboardName, positionIndex: leaderboardPositionIndex, value: applicableAttributes[leaderboardName], - unit: (_a = stats_1.getStatUnit(leaderboardName)) !== null && _a !== void 0 ? _a : null + unit: getStatUnit(leaderboardName) ?? null }); } return memberLeaderboardSpots; } -exports.fetchMemberLeaderboardSpots = fetchMemberLeaderboardSpots; async function getLeaderboardRequirement(name, leaderboardType) { - var _a, _b, _c, _d; let leaderboard; if (leaderboardType === 'member') leaderboard = await fetchMemberLeaderboardRaw(name); @@ -379,8 +343,8 @@ async function getLeaderboardRequirement(name, leaderboardType) { leaderboard = await fetchProfileLeaderboardRaw(name); // if there's more than 100 items, return the 100th. if there's less, return null return { - top_100: (_b = (_a = leaderboard[leaderboardMax - 1]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null, - top_1: (_d = (_c = leaderboard[1]) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : null + top_100: leaderboard[leaderboardMax - 1]?.value ?? null, + top_1: leaderboard[1]?.value ?? null }; } /** Get the attributes for the member, but only ones that would put them on the top 100 for leaderboards */ @@ -438,17 +402,17 @@ async function getApplicableProfileLeaderboardAttributes(profile) { return applicableAttributes; } /** Update the member's leaderboard data on the server if applicable */ -async function updateDatabaseMember(member, profile) { +export async function updateDatabaseMember(member, profile) { if (!client) return; // the db client hasn't been initialized - if (_1.debug) + if (debug) console.debug('updateDatabaseMember', member.username); // the member's been updated too recently, just return if (recentlyUpdated.get(profile.uuid + member.uuid)) return; // store the member in recentlyUpdated so it cant update for 3 more minutes recentlyUpdated.set(profile.uuid + member.uuid, true); - if (_1.debug) + if (debug) console.debug('adding member to leaderboards', member.username); if (member.rawHypixelStats) await constants.addStats(Object.keys(member.rawHypixelStats)); @@ -456,10 +420,10 @@ async function updateDatabaseMember(member, profile) { await constants.addSkills(member.skills.map(skill => skill.name)); await constants.addZones(member.visited_zones.map(zone => zone.name)); await constants.addSlayers(member.slayers.bosses.map(s => s.raw_name)); - if (_1.debug) + if (debug) console.debug('done constants..'); const leaderboardAttributes = await getApplicableMemberLeaderboardAttributes(member); - if (_1.debug) + if (debug) console.debug('done getApplicableMemberLeaderboardAttributes..', leaderboardAttributes, member.username, profile.name); await memberLeaderboardsCollection.updateOne({ uuid: member.uuid, @@ -483,30 +447,29 @@ async function updateDatabaseMember(member, profile) { }]) .sort((a, b) => leaderboardReverse ? a.value - b.value : b.value - a.value) .slice(0, 100); - exports.cachedRawLeaderboards.set(attributeName, newRawLeaderboard); + cachedRawLeaderboards.set(attributeName, newRawLeaderboard); } - if (_1.debug) + if (debug) console.debug('added member to leaderboards', member.username, leaderboardAttributes); } -exports.updateDatabaseMember = updateDatabaseMember; /** * Update the profiles's leaderboard data on the server if applicable. * This will not also update the members, you have to call updateDatabaseMember separately for that */ -async function updateDatabaseProfile(profile) { +export async function updateDatabaseProfile(profile) { if (!client) return; // the db client hasn't been initialized - if (_1.debug) + if (debug) console.debug('updateDatabaseProfile', profile.name); // the profile's been updated too recently, just return if (recentlyUpdated.get(profile.uuid + 'profile')) return; // store the profile in recentlyUpdated so it cant update for 3 more minutes recentlyUpdated.set(profile.uuid + 'profile', true); - if (_1.debug) + if (debug) console.debug('adding profile to leaderboards', profile.name); const leaderboardAttributes = await getApplicableProfileLeaderboardAttributes(profile); - if (_1.debug) + if (debug) console.debug('done getApplicableProfileLeaderboardAttributes..', leaderboardAttributes, profile.name); await profileLeaderboardsCollection.updateOne({ uuid: profile.uuid @@ -531,47 +494,44 @@ async function updateDatabaseProfile(profile) { }]) .sort((a, b) => leaderboardReverse ? a.value - b.value : b.value - a.value) .slice(0, 100); - exports.cachedRawLeaderboards.set(attributeName, newRawLeaderboard); + cachedRawLeaderboards.set(attributeName, newRawLeaderboard); } - if (_1.debug) + if (debug) console.debug('added profile to leaderboards', profile.name, leaderboardAttributes); } -exports.updateDatabaseProfile = updateDatabaseProfile; -exports.leaderboardUpdateMemberQueue = new queue_promise_1.default({ +export const leaderboardUpdateMemberQueue = new Queue({ concurrent: 1, interval: 50 }); -exports.leaderboardUpdateProfileQueue = new queue_promise_1.default({ +export const leaderboardUpdateProfileQueue = new Queue({ concurrent: 1, interval: 500 }); /** Queue an update for the member's leaderboard data on the server if applicable */ -function queueUpdateDatabaseMember(member, profile) { +export function queueUpdateDatabaseMember(member, profile) { if (recentlyQueued.get(profile.uuid + member.uuid)) return; else recentlyQueued.set(profile.uuid + member.uuid, true); - exports.leaderboardUpdateMemberQueue.enqueue(async () => await updateDatabaseMember(member, profile)); + leaderboardUpdateMemberQueue.enqueue(async () => await updateDatabaseMember(member, profile)); } -exports.queueUpdateDatabaseMember = queueUpdateDatabaseMember; /** Queue an update for the profile's leaderboard data on the server if applicable */ -function queueUpdateDatabaseProfile(profile) { +export function queueUpdateDatabaseProfile(profile) { if (recentlyQueued.get(profile.uuid + 'profile')) return; else recentlyQueued.set(profile.uuid + 'profile', true); - exports.leaderboardUpdateProfileQueue.enqueue(async () => await updateDatabaseProfile(profile)); + leaderboardUpdateProfileQueue.enqueue(async () => await updateDatabaseProfile(profile)); } -exports.queueUpdateDatabaseProfile = queueUpdateDatabaseProfile; /** * Remove leaderboard attributes for members that wouldn't actually be on the leaderboard. This saves a lot of storage space */ async function removeBadMemberLeaderboardAttributes() { const leaderboards = await fetchAllMemberLeaderboardAttributes(); // shuffle so if the application is restarting many times itll still be useful - for (const leaderboard of util_1.shuffle(leaderboards)) { + for (const leaderboard of shuffle(leaderboards)) { // wait 10 seconds so it doesnt use as much ram - await util_1.sleep(10 * 1000); + await sleep(10 * 1000); const unsetValue = {}; unsetValue[leaderboard] = ''; const filter = {}; @@ -588,19 +548,19 @@ async function removeBadMemberLeaderboardAttributes() { await memberLeaderboardsCollection.deleteMany({ stats: {} }); await profileLeaderboardsCollection.deleteMany({ stats: {} }); } -exports.finishedCachingRawLeaderboards = false; +export let finishedCachingRawLeaderboards = false; /** Fetch all the leaderboards, used for caching. Don't call this often! */ async function fetchAllLeaderboards(fast) { const leaderboards = await fetchAllMemberLeaderboardAttributes(); - if (_1.debug) + if (debug) console.debug('Caching raw leaderboards!'); - for (const leaderboard of util_1.shuffle(leaderboards)) + for (const leaderboard of shuffle(leaderboards)) await fetchMemberLeaderboardRaw(leaderboard); - exports.finishedCachingRawLeaderboards = true; + finishedCachingRawLeaderboards = true; } -async function createSession(refreshToken, userData) { - const sessionId = uuid_1.v4(); - await (sessionsCollection === null || sessionsCollection === void 0 ? void 0 : sessionsCollection.insertOne({ +export async function createSession(refreshToken, userData) { + const sessionId = uuid4(); + await sessionsCollection?.insertOne({ _id: sessionId, refresh_token: refreshToken, discord_user: { @@ -608,29 +568,25 @@ async function createSession(refreshToken, userData) { name: userData.username + '#' + userData.discriminator }, lastUpdated: new Date() - })); + }); return sessionId; } -exports.createSession = createSession; -async function fetchSession(sessionId) { - return await (sessionsCollection === null || sessionsCollection === void 0 ? void 0 : sessionsCollection.findOne({ _id: sessionId })); +export async function fetchSession(sessionId) { + return await sessionsCollection?.findOne({ _id: sessionId }); } -exports.fetchSession = fetchSession; -async function fetchAccount(minecraftUuid) { - return await (accountsCollection === null || accountsCollection === void 0 ? void 0 : accountsCollection.findOne({ minecraftUuid })); +export async function fetchAccount(minecraftUuid) { + return await accountsCollection?.findOne({ minecraftUuid }); } -exports.fetchAccount = fetchAccount; -async function fetchAccountFromDiscord(discordId) { - return await (accountsCollection === null || accountsCollection === void 0 ? void 0 : accountsCollection.findOne({ discordId })); +export async function fetchAccountFromDiscord(discordId) { + return await accountsCollection?.findOne({ discordId }); } -exports.fetchAccountFromDiscord = fetchAccountFromDiscord; -async function updateAccount(discordId, schema) { - await (accountsCollection === null || accountsCollection === void 0 ? void 0 : accountsCollection.updateOne({ +export async function updateAccount(discordId, schema) { + await accountsCollection?.updateOne({ discordId - }, { $set: schema }, { upsert: true })); + }, { $set: schema }, { upsert: true }); } -exports.updateAccount = updateAccount; // make sure it's not in a test +console.log('global.isTest', globalThis.isTest); if (!globalThis.isTest) { connect().then(() => { // when it connects, cache the leaderboards and remove bad members diff --git a/build/discord.js b/build/discord.js index ba9f3eb..ca910b2 100644 --- a/build/discord.js +++ b/build/discord.js @@ -1,16 +1,10 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getUser = exports.exchangeCode = void 0; -const node_fetch_1 = __importDefault(require("node-fetch")); -const https_1 = require("https"); +import fetch from 'node-fetch'; +import { Agent } from 'https'; const DISCORD_CLIENT_ID = '656634948148527107'; -const httpsAgent = new https_1.Agent({ +const httpsAgent = new Agent({ keepAlive: true }); -async function exchangeCode(redirectUri, code) { +export async function exchangeCode(redirectUri, code) { const API_ENDPOINT = 'https://discord.com/api/v6'; const CLIENT_SECRET = process.env.discord_client_secret; if (!CLIENT_SECRET) { @@ -25,7 +19,7 @@ async function exchangeCode(redirectUri, code) { 'redirect_uri': redirectUri, 'scope': 'identify' }; - const fetchResponse = await node_fetch_1.default(API_ENDPOINT + '/oauth2/token', { + const fetchResponse = await fetch(API_ENDPOINT + '/oauth2/token', { method: 'POST', agent: () => httpsAgent, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, @@ -33,13 +27,11 @@ async function exchangeCode(redirectUri, code) { }); return await fetchResponse.json(); } -exports.exchangeCode = exchangeCode; -async function getUser(accessToken) { +export async function getUser(accessToken) { const API_ENDPOINT = 'https://discord.com/api/v6'; - const response = await node_fetch_1.default(API_ENDPOINT + '/users/@me', { + const response = await fetch(API_ENDPOINT + '/users/@me', { headers: { 'Authorization': 'Bearer ' + accessToken }, agent: () => httpsAgent, }); - return response.json(); + return await response.json(); } -exports.getUser = getUser; diff --git a/build/hypixel.js b/build/hypixel.js index 414139e..6a1a916 100644 --- a/build/hypixel.js +++ b/build/hypixel.js @@ -1,58 +1,35 @@ -"use strict"; /** * Fetch the clean Hypixel API */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.fetchMemberProfilesUncached = exports.fetchBasicProfileFromUuidUncached = exports.fetchMemberProfileUncached = exports.fetchMemberProfile = exports.fetchUser = exports.sendCleanApiRequest = exports.maxMinion = exports.saveInterval = void 0; -const profile_1 = require("./cleaners/skyblock/profile"); -const database_1 = require("./database"); -const hypixelApi_1 = require("./hypixelApi"); -const profiles_1 = require("./cleaners/skyblock/profiles"); -const player_1 = require("./cleaners/player"); -const cached = __importStar(require("./hypixelCached")); -const _1 = require("."); +import { cleanSkyblockProfileResponse } from './cleaners/skyblock/profile.js'; +import { fetchAccount, queueUpdateDatabaseMember, queueUpdateDatabaseProfile } from './database.js'; +import { chooseApiKey, sendApiRequest } from './hypixelApi.js'; +import { cleanSkyblockProfilesResponse } from './cleaners/skyblock/profiles.js'; +import { cleanPlayerResponse } from './cleaners/player.js'; +import * as cached from './hypixelCached.js'; +import { debug } from './index.js'; // the interval at which the "last_save" parameter updates in the hypixel api, this is 3 minutes -exports.saveInterval = 60 * 3 * 1000; +export const saveInterval = 60 * 3 * 1000; // the highest level a minion can be -exports.maxMinion = 11; +export const maxMinion = 11; /** Sends an API request to Hypixel and cleans it up. */ -async function sendCleanApiRequest({ path, args }, included, options) { - const key = await hypixelApi_1.chooseApiKey(); - const rawResponse = await hypixelApi_1.sendApiRequest({ path, key, args }); +export async function sendCleanApiRequest({ path, args }, included, options) { + const key = await chooseApiKey(); + const rawResponse = await sendApiRequest({ path, key, args }); if (rawResponse.throttled) { // if it's throttled, wait a second and try again await new Promise(resolve => setTimeout(resolve, 1000)); return await sendCleanApiRequest({ path, args }, included, options); } // clean the response - return await cleanResponse({ path, data: rawResponse }, options !== null && options !== void 0 ? options : {}); + return await cleanResponse({ path, data: rawResponse }, options ?? {}); } -exports.sendCleanApiRequest = sendCleanApiRequest; async function cleanResponse({ path, data }, options) { // Cleans up an api response switch (path) { - case 'player': return await player_1.cleanPlayerResponse(data.player); - case 'skyblock/profile': return await profile_1.cleanSkyblockProfileResponse(data.profile, options); - case 'skyblock/profiles': return await profiles_1.cleanSkyblockProfilesResponse(data.profiles); + case 'player': return await cleanPlayerResponse(data.player); + case 'skyblock/profile': return await cleanSkyblockProfileResponse(data.profile, options); + case 'skyblock/profiles': return await cleanSkyblockProfilesResponse(data.profiles); } } /** @@ -61,21 +38,20 @@ async function cleanResponse({ path, data }, options) { * @param included lets you choose what is returned, so there's less processing required on the backend * used inclusions: player, profiles */ -async function fetchUser({ user, uuid, username }, included = ['player'], customization) { - var _a, _b; +export async function fetchUser({ user, uuid, username }, included = ['player'], customization) { if (!uuid) { // If the uuid isn't provided, get it if (!username && !user) return null; - uuid = await cached.uuidFromUser((user !== null && user !== void 0 ? user : username)); + uuid = await cached.uuidFromUser((user ?? username)); } if (!uuid) { // the user doesn't exist. - if (_1.debug) + if (debug) console.debug('error:', user, 'doesnt exist'); return null; } - const websiteAccountPromise = customization ? database_1.fetchAccount(uuid) : null; + const websiteAccountPromise = customization ? fetchAccount(uuid) : null; const includePlayers = included.includes('player'); const includeProfiles = included.includes('profiles'); let profilesData; @@ -85,7 +61,7 @@ async function fetchUser({ user, uuid, username }, included = ['player'], custom playerData = await cached.fetchPlayer(uuid); // if not including profiles, include lightweight profiles just in case if (!includeProfiles) - basicProfilesData = playerData === null || playerData === void 0 ? void 0 : playerData.profiles; + basicProfilesData = playerData?.profiles; if (playerData) delete playerData.profiles; } @@ -95,7 +71,7 @@ async function fetchUser({ user, uuid, username }, included = ['player'], custom let lastOnline = 0; if (includeProfiles) { for (const profile of profilesData) { - const member = (_a = profile.members) === null || _a === void 0 ? void 0 : _a.find(member => member.uuid === uuid); + const member = profile.members?.find(member => member.uuid === uuid); if (member && member.last_save > lastOnline) { lastOnline = member.last_save; activeProfile = profile; @@ -107,13 +83,12 @@ async function fetchUser({ user, uuid, username }, included = ['player'], custom websiteAccount = await websiteAccountPromise; return { player: playerData, - profiles: profilesData !== null && profilesData !== void 0 ? profilesData : basicProfilesData, - activeProfile: includeProfiles ? (_b = activeProfile) === null || _b === void 0 ? void 0 : _b.uuid : undefined, - online: includeProfiles ? lastOnline > (Date.now() - exports.saveInterval) : undefined, - customization: websiteAccount === null || websiteAccount === void 0 ? void 0 : websiteAccount.customization + profiles: profilesData ?? basicProfilesData, + activeProfile: includeProfiles ? activeProfile?.uuid : undefined, + online: includeProfiles ? lastOnline > (Date.now() - saveInterval) : undefined, + customization: websiteAccount?.customization }; } -exports.fetchUser = fetchUser; /** * Fetch a CleanMemberProfile from a user and string * This is safe to use many times as the results are cached! @@ -121,12 +96,12 @@ exports.fetchUser = fetchUser; * @param profile A profile name or profile uuid * @param customization Whether stuff like the user's custom background will be returned */ -async function fetchMemberProfile(user, profile, customization) { +export async function fetchMemberProfile(user, profile, customization) { const playerUuid = await cached.uuidFromUser(user); if (!playerUuid) return null; // we don't await the promise immediately so it can load while we do other stuff - const websiteAccountPromise = customization ? database_1.fetchAccount(playerUuid) : null; + const websiteAccountPromise = customization ? fetchAccount(playerUuid) : null; const profileUuid = await cached.fetchProfileUuid(user, profile); // if the profile or player doesn't have an id, just return if (!profileUuid) @@ -164,41 +139,38 @@ async function fetchMemberProfile(user, profile, customization) { ...player }, profile: cleanProfile, - customization: websiteAccount === null || websiteAccount === void 0 ? void 0 : websiteAccount.customization + customization: websiteAccount?.customization }; } -exports.fetchMemberProfile = fetchMemberProfile; /** * Fetches the Hypixel API to get a CleanFullProfile. This doesn't do any caching and you should use hypixelCached.fetchProfile instead * @param playerUuid The UUID of the Minecraft player * @param profileUuid The UUID of the Hypixel SkyBlock profile */ -async function fetchMemberProfileUncached(playerUuid, profileUuid) { +export async function fetchMemberProfileUncached(playerUuid, profileUuid) { const profile = await sendCleanApiRequest({ path: 'skyblock/profile', args: { profile: profileUuid } }, undefined, { mainMemberUuid: playerUuid }); // queue updating the leaderboard positions for the member, eventually for (const member of profile.members) - database_1.queueUpdateDatabaseMember(member, profile); - database_1.queueUpdateDatabaseProfile(profile); + queueUpdateDatabaseMember(member, profile); + queueUpdateDatabaseProfile(profile); return profile; } -exports.fetchMemberProfileUncached = fetchMemberProfileUncached; /** * Fetches the Hypixel API to get a CleanProfile from its id. This doesn't do any caching and you should use hypixelCached.fetchBasicProfileFromUuid instead * @param playerUuid The UUID of the Minecraft player * @param profileUuid The UUID of the Hypixel SkyBlock profile */ -async function fetchBasicProfileFromUuidUncached(profileUuid) { +export async function fetchBasicProfileFromUuidUncached(profileUuid) { const profile = await sendCleanApiRequest({ path: 'skyblock/profile', args: { profile: profileUuid } }, undefined, { basic: true }); return profile; } -exports.fetchBasicProfileFromUuidUncached = fetchBasicProfileFromUuidUncached; -async function fetchMemberProfilesUncached(playerUuid) { +export async function fetchMemberProfilesUncached(playerUuid) { const profiles = await sendCleanApiRequest({ path: 'skyblock/profiles', args: { @@ -210,10 +182,9 @@ async function fetchMemberProfilesUncached(playerUuid) { }); for (const profile of profiles) { for (const member of profile.members) { - database_1.queueUpdateDatabaseMember(member, profile); + queueUpdateDatabaseMember(member, profile); } - database_1.queueUpdateDatabaseProfile(profile); + queueUpdateDatabaseProfile(profile); } return profiles; } -exports.fetchMemberProfilesUncached = fetchMemberProfilesUncached; diff --git a/build/hypixelApi.js b/build/hypixelApi.js index a35dd64..da1be16 100644 --- a/build/hypixelApi.js +++ b/build/hypixelApi.js @@ -1,33 +1,26 @@ -"use strict"; -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.getKeyUsage = exports.chooseApiKey = void 0; /** * Fetch the raw Hypixel API */ -const node_fetch_1 = __importDefault(require("node-fetch")); -const util_1 = require("./util"); -const https_1 = require("https"); +import fetch from 'node-fetch'; +import { jsonToQuery, shuffle } from './util.js'; +import { Agent } from 'https'; if (!process.env.hypixel_keys) // if there's no hypixel keys in env, run dotenv - require('dotenv').config(); + (await import('dotenv')).config(); // We need to create an agent to prevent memory leaks and to only do dns lookups once -const httpsAgent = new https_1.Agent({ +const httpsAgent = new Agent({ keepAlive: true }); /** This array should only ever contain one item because using multiple hypixel api keys isn't allowed :) */ -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 apiKeys = process.env?.hypixel_keys?.split(' ') ?? []; const apiKeyUsage = {}; const baseHypixelAPI = 'https://api.hypixel.net'; /** Choose the best current API key */ -function chooseApiKey() { +export function chooseApiKey() { // find the api key with the lowest amount of uses let bestKeyUsage = null; let bestKey = null; - for (let key of util_1.shuffle(apiKeys.slice())) { + for (let key of shuffle(apiKeys.slice())) { const keyUsage = apiKeyUsage[key]; // if the key has never been used before, use it if (!keyUsage) @@ -43,8 +36,7 @@ function chooseApiKey() { } return bestKey; } -exports.chooseApiKey = chooseApiKey; -function getKeyUsage() { +export function getKeyUsage() { let keyLimit = 0; let keyUsage = 0; for (let key of Object.values(apiKeyUsage)) { @@ -56,20 +48,18 @@ function getKeyUsage() { usage: keyUsage }; } -exports.getKeyUsage = getKeyUsage; /** Send an HTTP request to the Hypixel API */ -async function sendApiRequest({ path, key, args }) { +export let sendApiRequest = async function sendApiRequest({ path, key, args }) { // Send a raw http request to api.hypixel.net, and return the parsed json - var _a, _b, _c; if (key) // If there's an api key, add it to the arguments args.key = key; // Construct a url from the base api url, path, and arguments - const fetchUrl = baseHypixelAPI + '/' + path + '?' + util_1.jsonToQuery(args); + const fetchUrl = baseHypixelAPI + '/' + path + '?' + jsonToQuery(args); let fetchResponse; let fetchJsonParsed; try { - fetchResponse = await node_fetch_1.default(fetchUrl, { agent: () => httpsAgent }); + fetchResponse = await fetch(fetchUrl, { agent: () => httpsAgent }); fetchJsonParsed = await fetchResponse.json(); } catch { @@ -85,9 +75,9 @@ async function sendApiRequest({ path, key, args }) { if (fetchResponse.headers.get('ratelimit-limit')) // remember how many uses it has apiKeyUsage[key] = { - remaining: parseInt((_a = fetchResponse.headers.get('ratelimit-remaining')) !== null && _a !== void 0 ? _a : '0'), - limit: parseInt((_b = fetchResponse.headers.get('ratelimit-limit')) !== null && _b !== void 0 ? _b : '0'), - reset: Date.now() + parseInt((_c = fetchResponse.headers.get('ratelimit-reset')) !== null && _c !== void 0 ? _c : '0') * 1000 + remaining: parseInt(fetchResponse.headers.get('ratelimit-remaining') ?? '0'), + limit: parseInt(fetchResponse.headers.get('ratelimit-limit') ?? '0'), + reset: Date.now() + parseInt(fetchResponse.headers.get('ratelimit-reset') ?? '0') * 1000 }; if (fetchJsonParsed.throttle) { if (apiKeyUsage[key]) @@ -97,5 +87,6 @@ async function sendApiRequest({ path, key, args }) { return await sendApiRequest({ path, key, args }); } return fetchJsonParsed; -} -exports.sendApiRequest = sendApiRequest; +}; +// this is necessary for mocking in the tests because es6 +export function mockSendApiRequest($value) { sendApiRequest = $value; } diff --git a/build/hypixelCached.js b/build/hypixelCached.js index b03361f..69bcc62 100644 --- a/build/hypixelCached.js +++ b/build/hypixelCached.js @@ -1,72 +1,47 @@ -"use strict"; /** * Fetch the clean and cached Hypixel API */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.fetchProfileName = exports.fetchBasicProfileFromUuid = 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 lru_cache_1 = __importDefault(require("lru-cache")); -const mojang = __importStar(require("./mojang")); -const hypixel = __importStar(require("./hypixel")); -const util_1 = require("./util"); -const _1 = require("."); +import { isUuid, undashUuid } from './util.js'; +import * as hypixel from './hypixel.js'; +import * as mojang from './mojang.js'; +import NodeCache from 'node-cache'; +import { debug } from './index.js'; +import LRUCache from 'lru-cache'; // cache usernames for 30 minutes /** uuid: username */ -exports.usernameCache = new node_cache_1.default({ +export const usernameCache = new NodeCache({ // stdTTL: 60 * 60 * 4, stdTTL: 60 * 30, checkperiod: 60, useClones: false, }); -exports.usernameCache.setMaxListeners(50); -exports.basicProfilesCache = new node_cache_1.default({ +usernameCache.setMaxListeners(50); +export const basicProfilesCache = new NodeCache({ stdTTL: 60 * 10, checkperiod: 60, useClones: true, }); -exports.playerCache = new node_cache_1.default({ +export const playerCache = new NodeCache({ stdTTL: 60, checkperiod: 10, useClones: true, }); // cache "basic players" (players without profiles) for 20 minutes -exports.basicPlayerCache = new lru_cache_1.default({ +export const basicPlayerCache = new LRUCache({ max: 10000, maxAge: 60 * 20 * 1000, }); -exports.profileCache = new node_cache_1.default({ +export const profileCache = new NodeCache({ stdTTL: 30, checkperiod: 10, useClones: true, }); -exports.profilesCache = new node_cache_1.default({ +export const profilesCache = new NodeCache({ stdTTL: 60 * 3, checkperiod: 10, useClones: false, }); -exports.profileNameCache = new node_cache_1.default({ +export const profileNameCache = new NodeCache({ stdTTL: 60 * 60, checkperiod: 60, useClones: false, @@ -86,77 +61,74 @@ function waitForCacheSet(cache, key, value) { * Fetch the uuid from a user * @param user A user can be either a uuid or a username */ -async function uuidFromUser(user) { +export 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 (exports.usernameCache.has(util_1.undashUuid(user))) { + if (isUuid(user)) + return undashUuid(user); + if (usernameCache.has(undashUuid(user))) { // check if the uuid is a key - const username = exports.usernameCache.get(util_1.undashUuid(user)); + const username = usernameCache.get(undashUuid(user)); // sometimes the username will be null, return that if (username === null) return undefined; // 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; - exports.usernameCache.set(uuid, _username); + usernameCache.set(uuid, _username); return uuid; } else - return util_1.undashUuid(user); + return undashUuid(user); } // check if the username is a value - const uuidToUsername = exports.usernameCache.mget(exports.usernameCache.keys()); + const uuidToUsername = usernameCache.mget(usernameCache.keys()); for (const [uuid, username] of Object.entries(uuidToUsername)) { if (username && username.toLowerCase && user.toLowerCase() === username.toLowerCase()) return uuid; } - if (_1.debug) + if (debug) console.debug('Cache miss: uuidFromUser', user); - const undashedUser = util_1.undashUuid(user); + const undashedUser = undashUuid(user); // set it as waitForCacheSet (a promise) in case uuidFromUser gets called while its fetching mojang - exports.usernameCache.set(undashedUser, waitForCacheSet(exports.usernameCache, user, user)); + usernameCache.set(undashedUser, waitForCacheSet(usernameCache, user, user)); // not cached, actually fetch mojang api now let { uuid, username } = await mojang.profileFromUser(user); if (!uuid) { - exports.usernameCache.set(user, null); + usernameCache.set(user, null); return; } // remove dashes from the uuid so its more normal - uuid = util_1.undashUuid(uuid); - exports.usernameCache.del(undashedUser); - exports.usernameCache.set(uuid, username); + uuid = undashUuid(uuid); + usernameCache.del(undashedUser); + usernameCache.set(uuid, username); return uuid; } -exports.uuidFromUser = uuidFromUser; /** * Fetch the username from a user * @param user A user can be either a uuid or a username */ -async function usernameFromUser(user) { - var _a; - if (exports.usernameCache.has(util_1.undashUuid(user))) { - if (_1.debug) +export async function usernameFromUser(user) { + if (usernameCache.has(undashUuid(user))) { + if (debug) console.debug('Cache hit! usernameFromUser', user); - return (_a = exports.usernameCache.get(util_1.undashUuid(user))) !== null && _a !== void 0 ? _a : null; + return usernameCache.get(undashUuid(user)) ?? null; } - if (_1.debug) + if (debug) console.debug('Cache miss: usernameFromUser', user); let { uuid, username } = await mojang.profileFromUser(user); if (!uuid) return null; - uuid = util_1.undashUuid(uuid); - exports.usernameCache.set(uuid, username); + uuid = undashUuid(uuid); + usernameCache.set(uuid, username); return username; } -exports.usernameFromUser = usernameFromUser; let fetchingPlayers = new Set(); -async function fetchPlayer(user) { +export async function fetchPlayer(user) { const playerUuid = await uuidFromUser(user); if (!playerUuid) return null; - if (exports.playerCache.has(playerUuid)) - return exports.playerCache.get(playerUuid); + if (playerCache.has(playerUuid)) + return 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)) { @@ -173,21 +145,20 @@ async function fetchPlayer(user) { if (!cleanPlayer) return null; // clone in case it gets modified somehow later - exports.playerCache.set(playerUuid, cleanPlayer); - exports.usernameCache.set(playerUuid, cleanPlayer.username); + playerCache.set(playerUuid, cleanPlayer); + usernameCache.set(playerUuid, cleanPlayer.username); const cleanBasicPlayer = Object.assign({}, cleanPlayer); delete cleanBasicPlayer.profiles; - exports.basicPlayerCache.set(playerUuid, cleanBasicPlayer); + basicPlayerCache.set(playerUuid, cleanBasicPlayer); return cleanPlayer; } -exports.fetchPlayer = fetchPlayer; /** Fetch a player without their profiles. This is heavily cached. */ -async function fetchBasicPlayer(user) { +export async function fetchBasicPlayer(user) { const playerUuid = await uuidFromUser(user); if (!playerUuid) return null; - if (exports.basicPlayerCache.has(playerUuid)) - return exports.basicPlayerCache.get(playerUuid); + if (basicPlayerCache.has(playerUuid)) + return basicPlayerCache.get(playerUuid); const player = await fetchPlayer(playerUuid); if (!player) { console.debug('no player? this should never happen, perhaps the uuid is invalid or the player hasn\'t played hypixel', playerUuid); @@ -196,15 +167,13 @@ async function fetchBasicPlayer(user) { delete player.profiles; return player; } -exports.fetchBasicPlayer = fetchBasicPlayer; -async function fetchSkyblockProfiles(playerUuid) { - var _a; - if (exports.profilesCache.has(playerUuid)) { - if (_1.debug) +export async function fetchSkyblockProfiles(playerUuid) { + if (profilesCache.has(playerUuid)) { + if (debug) console.debug('Cache hit! fetchSkyblockProfiles', playerUuid); - return exports.profilesCache.get(playerUuid); + return profilesCache.get(playerUuid); } - if (_1.debug) + if (debug) console.debug('Cache miss: fetchSkyblockProfiles', playerUuid); const profiles = await hypixel.fetchMemberProfilesUncached(playerUuid); const basicProfiles = []; @@ -213,7 +182,7 @@ async function fetchSkyblockProfiles(playerUuid) { const basicProfile = { name: profile.name, uuid: profile.uuid, - members: (_a = profile.members) === null || _a === void 0 ? void 0 : _a.map(m => { + members: profile.members?.map(m => { return { uuid: m.uuid, username: m.username, @@ -226,21 +195,20 @@ async function fetchSkyblockProfiles(playerUuid) { basicProfiles.push(basicProfile); } // cache the profiles - exports.profilesCache.set(playerUuid, basicProfiles); + profilesCache.set(playerUuid, basicProfiles); return basicProfiles; } -exports.fetchSkyblockProfiles = fetchSkyblockProfiles; /** Fetch an array of `BasicProfile`s */ async function fetchBasicProfiles(user) { const playerUuid = await uuidFromUser(user); if (!playerUuid) return null; // invalid player, just return - if (exports.basicProfilesCache.has(playerUuid)) { - if (_1.debug) + if (basicProfilesCache.has(playerUuid)) { + if (debug) console.debug('Cache hit! fetchBasicProfiles', playerUuid); - return exports.basicProfilesCache.get(playerUuid); + return basicProfilesCache.get(playerUuid); } - if (_1.debug) + if (debug) console.debug('Cache miss: fetchBasicProfiles', user); const player = await fetchPlayer(playerUuid); if (!player) { @@ -248,12 +216,12 @@ async function fetchBasicProfiles(user) { return []; } const profiles = player.profiles; - exports.basicProfilesCache.set(playerUuid, profiles); + basicProfilesCache.set(playerUuid, profiles); if (!profiles) return null; // cache the profile names and uuids to profileNameCache because we can for (const profile of profiles) - exports.profileNameCache.set(`${playerUuid}.${profile.uuid}`, profile.name); + profileNameCache.set(`${playerUuid}.${profile.uuid}`, profile.name); return profiles; } /** @@ -261,48 +229,46 @@ async function fetchBasicProfiles(user) { * @param user A username or uuid * @param profile A profile name or profile uuid */ -async function fetchProfileUuid(user, profile) { - var _a; +export async function fetchProfileUuid(user, profile) { // if a profile wasn't provided, return if (!profile) { - if (_1.debug) + if (debug) console.debug('no profile provided?', user, profile); return null; } - if (_1.debug) + if (debug) console.debug('Cache miss: fetchProfileUuid', user, profile); const profiles = await fetchBasicProfiles(user); if (!profiles) return null; // user probably doesnt exist - const profileUuid = util_1.undashUuid(profile); + const profileUuid = undashUuid(profile); for (const p of profiles) { - if (((_a = p.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === profileUuid.toLowerCase()) - return util_1.undashUuid(p.uuid); - else if (util_1.undashUuid(p.uuid) === util_1.undashUuid(profileUuid)) - return util_1.undashUuid(p.uuid); + if (p.name?.toLowerCase() === profileUuid.toLowerCase()) + return undashUuid(p.uuid); + else if (undashUuid(p.uuid) === undashUuid(profileUuid)) + return undashUuid(p.uuid); } return null; } -exports.fetchProfileUuid = fetchProfileUuid; /** * Fetch an entire profile from the user and profile data * @param user A username or uuid * @param profile A profile name or profile uuid */ -async function fetchProfile(user, profile) { +export async function fetchProfile(user, profile) { const playerUuid = await uuidFromUser(user); if (!playerUuid) return null; const profileUuid = await fetchProfileUuid(playerUuid, profile); if (!profileUuid) return null; - if (exports.profileCache.has(profileUuid)) { + if (profileCache.has(profileUuid)) { // we have the profile cached, return it :) - if (_1.debug) + if (debug) console.debug('Cache hit! fetchProfile', profileUuid); - return exports.profileCache.get(profileUuid); + return profileCache.get(profileUuid); } - if (_1.debug) + if (debug) console.debug('Cache miss: fetchProfile', user, profile); const profileName = await fetchProfileName(user, profile); if (!profileName) @@ -310,20 +276,19 @@ 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; - exports.profileCache.set(profileUuid, cleanProfile); + profileCache.set(profileUuid, cleanProfile); return cleanProfile; } -exports.fetchProfile = fetchProfile; /** * Fetch a CleanProfile from the uuid * @param profileUuid A profile name or profile uuid */ -async function fetchBasicProfileFromUuid(profileUuid) { - if (exports.profileCache.has(profileUuid)) { +export async function fetchBasicProfileFromUuid(profileUuid) { + if (profileCache.has(profileUuid)) { // we have the profile cached, return it :) - if (_1.debug) + if (debug) console.debug('Cache hit! fetchBasicProfileFromUuid', profileUuid); - const profile = exports.profileCache.get(profileUuid); + const profile = profileCache.get(profileUuid); if (!profile) return undefined; return { @@ -341,14 +306,12 @@ async function fetchBasicProfileFromUuid(profileUuid) { // TODO: cache this return await hypixel.fetchBasicProfileFromUuidUncached(profileUuid); } -exports.fetchBasicProfileFromUuid = fetchBasicProfileFromUuid; /** * Fetch the name of a profile from the user and profile uuid * @param user A player uuid or username * @param profile A profile uuid or name */ -async function fetchProfileName(user, profile) { - var _a, _b; +export 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); if (!profileUuid) @@ -356,13 +319,13 @@ async function fetchProfileName(user, profile) { const playerUuid = await uuidFromUser(user); if (!playerUuid) return null; - if (exports.profileNameCache.has(`${playerUuid}.${profileUuid}`)) { + if (profileNameCache.has(`${playerUuid}.${profileUuid}`)) { // Return the profile name if it's cached - if (_1.debug) + if (debug) console.debug('Cache hit! fetchProfileName', profileUuid); - return (_a = exports.profileNameCache.get(`${playerUuid}.${profileUuid}`)) !== null && _a !== void 0 ? _a : null; + return profileNameCache.get(`${playerUuid}.${profileUuid}`) ?? null; } - if (_1.debug) + if (debug) console.debug('Cache miss: fetchProfileName', user, profile); const basicProfiles = await fetchBasicProfiles(playerUuid); if (!basicProfiles) @@ -370,8 +333,7 @@ async function fetchProfileName(user, profile) { let profileName = null; for (const basicProfile of basicProfiles) if (basicProfile.uuid === playerUuid) - profileName = (_b = basicProfile.name) !== null && _b !== void 0 ? _b : null; - exports.profileNameCache.set(`${playerUuid}.${profileUuid}`, profileName); + profileName = basicProfile.name ?? null; + profileNameCache.set(`${playerUuid}.${profileUuid}`, profileName); return profileName; } -exports.fetchProfileName = fetchProfileName; diff --git a/build/index.js b/build/index.js index 8bb2040..8521a1c 100644 --- a/build/index.js +++ b/build/index.js @@ -1,51 +1,25 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.debug = void 0; -const database_1 = require("./database"); -const hypixel_1 = require("./hypixel"); -const express_rate_limit_1 = __importDefault(require("express-rate-limit")); -const constants = __importStar(require("./constants")); -const discord = __importStar(require("./discord")); -const express_1 = __importDefault(require("express")); -const app = express_1.default(); -exports.debug = false; +import { createSession, fetchAccountFromDiscord, fetchAllLeaderboardsCategorized, fetchLeaderboard, fetchMemberLeaderboardSpots, fetchSession, finishedCachingRawLeaderboards, leaderboardUpdateMemberQueue, leaderboardUpdateProfileQueue, updateAccount } from './database.js'; +import { fetchMemberProfile, fetchUser } from './hypixel.js'; +import rateLimit from 'express-rate-limit'; +import * as constants from './constants.js'; +import * as discord from './discord.js'; +import express from 'express'; +const app = express(); +export const debug = false; const mainSiteUrl = 'https://skyblock.matdoes.dev'; // 200 requests over 5 minutes -const limiter = express_rate_limit_1.default({ +const limiter = rateLimit({ windowMs: 60 * 1000 * 5, max: 200, skip: (req) => { return req.headers.key === process.env.key; }, keyGenerator: (req) => { - var _a; - return ((_a = req.headers['cf-connecting-ip']) !== null && _a !== void 0 ? _a : req.ip).toString(); + return (req.headers['cf-connecting-ip'] ?? req.ip).toString(); } }); app.use(limiter); -app.use(express_1.default.json()); +app.use(express.json()); app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*'); next(); @@ -56,15 +30,15 @@ app.get('/', async (req, res) => { res.json({ ok: true, uptimeHours: (currentTime - startTime) / 1000 / 60 / 60, - finishedCachingRawLeaderboards: database_1.finishedCachingRawLeaderboards, - leaderboardUpdateMemberQueueSize: database_1.leaderboardUpdateMemberQueue.size, - leaderboardUpdateProfileQueueSize: database_1.leaderboardUpdateProfileQueue.size, + finishedCachingRawLeaderboards, + leaderboardUpdateMemberQueueSize: leaderboardUpdateMemberQueue.size, + leaderboardUpdateProfileQueueSize: leaderboardUpdateProfileQueue.size, // key: getKeyUsage() }); }); app.get('/player/:user', async (req, res) => { try { - const user = await hypixel_1.fetchUser({ user: req.params.user }, [req.query.basic === 'true' ? undefined : 'profiles', 'player'], req.query.customization === 'true'); + const user = await fetchUser({ user: req.params.user }, [req.query.basic === 'true' ? undefined : 'profiles', 'player'], req.query.customization === 'true'); if (user) res.json(user); else @@ -77,7 +51,7 @@ app.get('/player/:user', async (req, res) => { }); app.get('/discord/:id', async (req, res) => { try { - res.json(await database_1.fetchAccountFromDiscord(req.params.id)); + res.json(await fetchAccountFromDiscord(req.params.id)); } catch (err) { console.error(err); @@ -86,7 +60,7 @@ app.get('/discord/:id', async (req, res) => { }); app.get('/player/:user/:profile', async (req, res) => { try { - const profile = await hypixel_1.fetchMemberProfile(req.params.user, req.params.profile, req.query.customization === 'true'); + const profile = await fetchMemberProfile(req.params.user, req.params.profile, req.query.customization === 'true'); if (profile) res.json(profile); else @@ -99,7 +73,7 @@ app.get('/player/:user/:profile', async (req, res) => { }); app.get('/player/:user/:profile/leaderboards', async (req, res) => { try { - res.json(await database_1.fetchMemberLeaderboardSpots(req.params.user, req.params.profile)); + res.json(await fetchMemberLeaderboardSpots(req.params.user, req.params.profile)); } catch (err) { console.error(err); @@ -108,7 +82,7 @@ app.get('/player/:user/:profile/leaderboards', async (req, res) => { }); app.get('/leaderboard/:name', async (req, res) => { try { - res.json(await database_1.fetchLeaderboard(req.params.name)); + res.json(await fetchLeaderboard(req.params.name)); } catch (err) { console.error(err); @@ -117,7 +91,7 @@ app.get('/leaderboard/:name', async (req, res) => { }); app.get('/leaderboards', async (req, res) => { try { - res.json(await database_1.fetchAllLeaderboardsCategorized()); + res.json(await fetchAllLeaderboardsCategorized()); } catch (err) { console.error(err); @@ -146,7 +120,7 @@ app.post('/accounts/createsession', async (req, res) => { // access token is invalid :( return res.json({ ok: false }); const userData = await discord.getUser(accessToken); - const sessionId = await database_1.createSession(refreshToken, userData); + const sessionId = await createSession(refreshToken, userData); res.json({ ok: true, session_id: sessionId }); } catch (err) { @@ -156,10 +130,10 @@ app.post('/accounts/createsession', async (req, res) => { app.post('/accounts/session', async (req, res) => { try { const { uuid } = req.body; - const session = await database_1.fetchSession(uuid); + const session = await fetchSession(uuid); if (!session) return res.json({ ok: false }); - const account = await database_1.fetchAccountFromDiscord(session.discord_user.id); + const account = await fetchAccountFromDiscord(session.discord_user.id); res.json({ session, account }); } catch (err) { @@ -172,7 +146,7 @@ app.post('/accounts/update', async (req, res) => { if (req.headers.key !== process.env.key) return console.log('bad key!'); try { - await database_1.updateAccount(req.body.discordId, req.body); + await updateAccount(req.body.discordId, req.body); res.json({ ok: true }); } catch (err) { diff --git a/build/mojang.js b/build/mojang.js index 2d2f694..7682839 100644 --- a/build/mojang.js +++ b/build/mojang.js @@ -1,28 +1,22 @@ -"use strict"; /** * Fetch the Mojang username API through api.ashcon.app */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.profileFromUser = exports.profileFromUsernameAlternative = exports.profileFromUsername = exports.profileFromUuid = void 0; -const node_fetch_1 = __importDefault(require("node-fetch")); -const https_1 = require("https"); -const util_1 = require("./util"); +import { isUuid, undashUuid } from './util.js'; +import fetch from 'node-fetch'; +import { Agent } from 'https'; // We need to create an agent to prevent memory leaks -const httpsAgent = new https_1.Agent({ +const httpsAgent = new Agent({ keepAlive: true }); /** * Get mojang api data from the session server */ -async function profileFromUuid(uuid) { +export let profileFromUuid = async function profileFromUuid(uuid) { let fetchResponse; try { - fetchResponse = await node_fetch_1.default( + fetchResponse = await fetch( // using mojang directly is faster than ashcon lol, also mojang removed the ratelimits from here - `https://sessionserver.mojang.com/session/minecraft/profile/${util_1.undashUuid(uuid)}`, { agent: () => httpsAgent }); + `https://sessionserver.mojang.com/session/minecraft/profile/${undashUuid(uuid)}`, { agent: () => httpsAgent }); } catch { // if there's an error, wait a second and try again @@ -48,13 +42,12 @@ async function profileFromUuid(uuid) { uuid: data.id, username: data.name }; -} -exports.profileFromUuid = profileFromUuid; -async function profileFromUsername(username) { +}; +export let profileFromUsername = async function profileFromUsername(username) { // since we don't care about anything other than the uuid, we can use /uuid/ instead of /user/ let fetchResponse; try { - fetchResponse = await node_fetch_1.default(`https://api.mojang.com/users/profiles/minecraft/${username}`, { agent: () => httpsAgent }); + fetchResponse = await fetch(`https://api.mojang.com/users/profiles/minecraft/${username}`, { agent: () => httpsAgent }); } catch { // if there's an error, wait a second and try again @@ -67,7 +60,7 @@ async function profileFromUsername(username) { data = JSON.parse(rawData); } catch { } - if (!(data === null || data === void 0 ? void 0 : data.id)) { + if (!data?.id) { // return { uuid: null, username: null } return await profileFromUsernameAlternative(username); } @@ -75,12 +68,11 @@ async function profileFromUsername(username) { uuid: data.id, username: data.name }; -} -exports.profileFromUsername = profileFromUsername; -async function profileFromUsernameAlternative(username) { +}; +export async function profileFromUsernameAlternative(username) { let fetchResponse; try { - fetchResponse = await node_fetch_1.default(`https://api.ashcon.app/mojang/v2/user/${username}`, { agent: () => httpsAgent }); + fetchResponse = await fetch(`https://api.ashcon.app/mojang/v2/user/${username}`, { agent: () => httpsAgent }); } catch { // if there's an error, wait a second and try again @@ -97,16 +89,18 @@ async function profileFromUsernameAlternative(username) { if (!data.uuid) return { uuid: null, username: null }; return { - uuid: util_1.undashUuid(data.uuid), + uuid: undashUuid(data.uuid), username: data.username }; } -exports.profileFromUsernameAlternative = profileFromUsernameAlternative; -async function profileFromUser(user) { - if (util_1.isUuid(user)) { +export let profileFromUser = async function profileFromUser(user) { + if (isUuid(user)) { return await profileFromUuid(user); } else return await profileFromUsername(user); -} -exports.profileFromUser = profileFromUser; +}; +// this is necessary for mocking in the tests because es6 +export function mockProfileFromUuid($value) { profileFromUuid = $value; } +export function mockProfileFromUsername($value) { profileFromUsername = $value; } +export function mockProfileFromUser($value) { profileFromUser = $value; } diff --git a/build/util.js b/build/util.js index c0bbd42..e78e390 100644 --- a/build/util.js +++ b/build/util.js @@ -1,26 +1,20 @@ -"use strict"; /** * 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.undashUuid = void 0; -function undashUuid(uuid) { +export function undashUuid(uuid) { return uuid.replace(/-/g, '').toLowerCase(); } -exports.undashUuid = undashUuid; -function jsonToQuery(data) { +export function jsonToQuery(data) { return Object.entries(data || {}).map(e => e.join('=')).join('&'); } -exports.jsonToQuery = jsonToQuery; -function shuffle(a) { +export function shuffle(a) { for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } -exports.shuffle = shuffle; -exports.minecraftColorCodes = { +export const minecraftColorCodes = { '0': '#000000', '1': '#0000be', '2': '#00be00', @@ -59,21 +53,18 @@ exports.minecraftColorCodes = { * For example: blue -> 9 * @param colorName The name of the color (blue, red, aqua, etc) */ -function colorCodeFromName(colorName) { - const hexColor = exports.minecraftColorCodes[colorName.toLowerCase()]; - for (const key in exports.minecraftColorCodes) { - const value = exports.minecraftColorCodes[key]; +export function colorCodeFromName(colorName) { + const hexColor = minecraftColorCodes[colorName.toLowerCase()]; + for (const key in minecraftColorCodes) { + const value = minecraftColorCodes[key]; if (key.length === 1 && value === hexColor) return key; } } -exports.colorCodeFromName = colorCodeFromName; -async function sleep(ms) { +export async function sleep(ms) { await new Promise(resolve => setTimeout(resolve, ms)); } -exports.sleep = sleep; /** Returns whether a string is a UUID4 (Minecraft uuid) */ -function isUuid(string) { +export function isUuid(string) { return undashUuid(string).length === 32; } -exports.isUuid = isUuid; diff --git a/package-lock.json b/package-lock.json index aa8d221..4837ce7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,2236 @@ { "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.3.0", + "lru-cache": "^6.0.0", + "mongodb": "^4.1.1", + "node-cache": "^5.1.2", + "node-fetch": "^3.0.0", + "prismarine-nbt": "^1.6.0", + "queue-promise": "^2.2.1", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@types/express": "^4.17.13", + "@types/express-rate-limit": "^5.1.3", + "@types/lru-cache": "^5.1.1", + "@types/mocha": "^9.0.0", + "@types/mongodb": "^4.0.7", + "@types/node": "^16.7.10", + "dotenv": "^10.0.0", + "mocha": "^9.1.1", + "ts-node": "^10.2.1", + "typescript": "^4.4.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", + "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "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.3", + "resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.3.tgz", + "integrity": "sha512-H+TYy3K53uPU2TqPGFYaiWc2xJV6+bIFkDd/Ma2/h67Pa6ARk9kWE0p/K9OH1Okm0et9Sfm66fmXoAxsH2PHXg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.23.tgz", + "integrity": "sha512-WYqTtTPTJn9kXMdnAH5HPPb7ctXvBpP4PfuOb8MV4OHPQWHhDZixGlhgR159lJPpKm23WOdoCkt2//cCEaOJkw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true + }, + "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": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "dev": true + }, + "node_modules/@types/mongodb": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-4.0.7.tgz", + "integrity": "sha512-lPUYPpzA43baXqnd36cZ9xxorprybxXDzteVKCPAdp14ppHtFJHnXYvNpmBvtMUTb5fKXVv6sVbzo1LHkWhJlw==", + "deprecated": "mongodb provides its own types. @types/mongodb is no longer needed.", + "dev": true, + "dependencies": { + "mongodb": "*" + } + }, + "node_modules/@types/node": { + "version": "16.7.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", + "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==" + }, + "node_modules/@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", + "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.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", + "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz", + "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "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/acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", + "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "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/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/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "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/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": "4.5.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.5.1.tgz", + "integrity": "sha512-XqFP74pbTVLyLy5KFxVfTUyRrC1mgOlmu/iXHfXqfCKT59jyP9lwbotGfbN59cHBRbJSamZNkrSopjv+N0SqAA==", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-rate-limit": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.3.0.tgz", + "integrity": "sha512-qJhfEgCnmteSeZAeuOKQ2WEIFTX5ajrzE0xS6gCOBCoRQcU+xEzQmgYQQTpzCcqUAAzTEtu4YEih4pnLfvNtew==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fetch-blob": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.2.tgz", + "integrity": "sha512-hunJbvy/6OLjCD0uuhLdp0mMPzP/yd2ssd1t2FCJsaA7wkWhpbp9xfuNVpv7Ll4jFhzp6T4LAupSiV9uOeg0VQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "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" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "dependencies": { + "mime-db": "1.45.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.23", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mongodb": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.1.1.tgz", + "integrity": "sha512-fbACrWEyvr6yl0sSiCGV0sqEiBwTtDJ8iSojmkDjAfw9JnOZSAkUyv9seFSPYhPPKwxp1PDtyjvBNfMDz0WBLQ==", + "dependencies": { + "bson": "^4.5.1", + "denque": "^1.5.0", + "mongodb-connection-string-url": "^2.0.0" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "saslprep": "^1.0.0" + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.0.0.tgz", + "integrity": "sha512-M0I1vyLoq5+HQTuPSJWbt+hIXsMCfE8sS1fS5mvP9R2DOMoi2ZD32yWqgBIITyu0dFu4qtS50erxKjvUeBiyog==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^9.1.0" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/node-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz", + "integrity": "sha512-bKMI+C7/T/SPU1lKnbQbwxptpCrG9ashG+VkytmXCPZyuM9jB6VU+hY0oi4lC8LxTtAeWdckNCTa3nrGsAdA3Q==", + "dependencies": { + "data-uri-to-buffer": "^3.0.1", + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prismarine-nbt": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/prismarine-nbt/-/prismarine-nbt-1.6.0.tgz", + "integrity": "sha512-h0ECvIjjwjMOtsmHxHc8hNY7kzktnKqKXmOHF0AkmH7OjkcHNAFsWRiZNvfc76rOhNonRutHvTVAlh/eLtK0oA==", + "dependencies": { + "protodef": "^1.9.0" + } + }, + "node_modules/protodef": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/protodef/-/protodef-1.14.0.tgz", + "integrity": "sha512-rL1WRlBC8LbAgBTa401eHMqnkX6zy1pHgS4kTSJVJ8rwP/AgVuWijGE3S3XHRkRjB/+4U1jMTqRdmtGdIqVOKQ==", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.reduce": "^4.6.0", + "protodef-validator": "^1.3.0", + "readable-stream": "^3.0.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/protodef-validator": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/protodef-validator/-/protodef-validator-1.3.1.tgz", + "integrity": "sha512-lZ5FWKZYR9xOjpMw1+EfZRfCjzNRQWPq+Dk+jki47Sikl2EeWEPnTfnJERwnU/EwFq6us+0zqHHzSsmLeYX+Lg==", + "dependencies": { + "ajv": "^6.5.4" + }, + "bin": { + "protodef-validator": "cli.js" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/queue-promise": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/queue-promise/-/queue-promise-2.2.1.tgz", + "integrity": "sha512-C3eyRwLF9m6dPV4MtqMVFX+Xmc7keZ9Ievm3jJ/wWM5t3uVbFnGsJXwpYzZ4LaIEcX9bss/mdaKzyrO6xheRuA==", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-node": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", + "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.6.1", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.0.tgz", + "integrity": "sha512-wO9r1YnYe7kFBLHyyVEhV1H8VRWoNiNnuP+v/HUUmSTaRF8F93Kmd3JMrETx0f11GXxRek6OcL2QtjFIdc5WYw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-url": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-9.1.0.tgz", + "integrity": "sha512-CQ0UcrPHyomtlOCot1TL77WyMIm/bCwrJ2D6AOKGwEczU9EpyoqAokfqrf/MioU9kHcMsmJZcg1egXix2KYEsA==", + "dependencies": { + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, "dependencies": { "@cspotcode/source-map-consumer": { "version": "0.8.0", @@ -126,15 +2354,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz", "integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==" }, - "@types/node-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-3.0.3.tgz", - "integrity": "sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==", - "dev": true, - "requires": { - "node-fetch": "*" - } - }, "@types/qs": { "version": "6.9.6", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", @@ -481,6 +2700,11 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -612,6 +2836,14 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fetch-blob": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.2.tgz", + "integrity": "sha512-hunJbvy/6OLjCD0uuhLdp0mMPzP/yd2ssd1t2FCJsaA7wkWhpbp9xfuNVpv7Ll4jFhzp6T4LAupSiV9uOeg0VQ==", + "requires": { + "web-streams-polyfill": "^3.0.3" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1028,9 +3260,13 @@ } }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz", + "integrity": "sha512-bKMI+C7/T/SPU1lKnbQbwxptpCrG9ashG+VkytmXCPZyuM9jB6VU+hY0oi4lC8LxTtAeWdckNCTa3nrGsAdA3Q==", + "requires": { + "data-uri-to-buffer": "^3.0.1", + "fetch-blob": "^3.1.2" + } }, "normalize-path": { "version": "3.0.0", @@ -1287,16 +3523,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -1312,6 +3538,16 @@ } } }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -1434,6 +3670,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "web-streams-polyfill": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.0.tgz", + "integrity": "sha512-wO9r1YnYe7kFBLHyyVEhV1H8VRWoNiNnuP+v/HUUmSTaRF8F93Kmd3JMrETx0f11GXxRek6OcL2QtjFIdc5WYw==" + }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/package.json b/package.json index ec6faf6..3870277 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "npm i --only=prod; node build/index.js" }, "engines": { - "node" : ">=16.0.0" + "node": ">=16.0.0" }, "repository": { "type": "git", @@ -33,22 +33,22 @@ "lru-cache": "^6.0.0", "mongodb": "^4.1.1", "node-cache": "^5.1.2", - "node-fetch": "^2.6.1", + "node-fetch": "^3.0.0", "prismarine-nbt": "^1.6.0", - "uuid": "^8.3.2", - "queue-promise": "^2.2.1" + "queue-promise": "^2.2.1", + "uuid": "^8.3.2" }, "devDependencies": { "@types/express": "^4.17.13", "@types/express-rate-limit": "^5.1.3", + "@types/lru-cache": "^5.1.1", "@types/mocha": "^9.0.0", - "@types/node": "^16.7.10", "@types/mongodb": "^4.0.7", - "@types/lru-cache": "^5.1.1", - "@types/node-fetch": "^3.0.3", + "@types/node": "^16.7.10", "dotenv": "^10.0.0", "mocha": "^9.1.1", "ts-node": "^10.2.1", "typescript": "^4.4.2" - } + }, + "type": "module" } diff --git a/src/cleaners/player.ts b/src/cleaners/player.ts index b2dfc77..802cdd5 100644 --- a/src/cleaners/player.ts +++ b/src/cleaners/player.ts @@ -1,9 +1,9 @@ -import { cleanPlayerSkyblockProfiles } from './skyblock/profiles' -import { cleanSocialMedia, CleanSocialMedia } from './socialmedia' -import { CleanBasicProfile } from './skyblock/profile' -import { cleanRank, CleanRank } from './rank' -import { HypixelPlayer } from '../hypixelApi' -import { undashUuid } from '../util' +import { cleanPlayerSkyblockProfiles } from './skyblock/profiles.js' +import { cleanSocialMedia, CleanSocialMedia } from './socialmedia.js' +import { CleanBasicProfile } from './skyblock/profile.js' +import { cleanRank, CleanRank } from './rank.js' +import { HypixelPlayer } from '../hypixelApi.js' +import { undashUuid } from '../util.js' export interface CleanBasicPlayer { uuid: string diff --git a/src/cleaners/rank.ts b/src/cleaners/rank.ts index 4892bf2..417c8f1 100644 --- a/src/cleaners/rank.ts +++ b/src/cleaners/rank.ts @@ -1,5 +1,5 @@ -import { colorCodeFromName, minecraftColorCodes } from '../util' -import { HypixelPlayer } from '../hypixelApi' +import { colorCodeFromName, minecraftColorCodes } from '../util.js' +import { HypixelPlayer } from '../hypixelApi.js' const rankColors: { [ name: string ]: string } = { 'NONE': '7', diff --git a/src/cleaners/skyblock/collections.ts b/src/cleaners/skyblock/collections.ts index 99fdc27..635bfce 100644 --- a/src/cleaners/skyblock/collections.ts +++ b/src/cleaners/skyblock/collections.ts @@ -1,4 +1,4 @@ -import { cleanItemId, hypixelItemNames } from "./itemId" +import { cleanItemId, hypixelItemNames } from './itemId.js' const COLLECTIONS = { 'farming': [ diff --git a/src/cleaners/skyblock/member.ts b/src/cleaners/skyblock/member.ts index cee1864..a9a12b8 100644 --- a/src/cleaners/skyblock/member.ts +++ b/src/cleaners/skyblock/member.ts @@ -1,20 +1,20 @@ -import { cleanCollections, Collection } from './collections' -import { cleanInventories, Inventories } from './inventory' -import { cleanFairySouls, FairySouls } from './fairysouls' -import { cleanObjectives, Objective } from './objectives' -import { CleanFullProfileBasicMembers } from './profile' -import { cleanProfileStats, StatItem } from './stats' -import { CleanMinion, cleanMinions } from './minions' -import { AccountCustomization } from '../../database' -import { cleanSlayers, SlayerData } from './slayers' -import { cleanVisitedZones, Zone } from './zones' -import { cleanSkills, Skill } from './skills' -import * as cached from '../../hypixelCached' -import * as constants from '../../constants' -import { Included } from '../../hypixel' -import { CleanPlayer } from '../player' -import { CleanRank } from '../rank' -import { Bank } from './bank' +import { cleanCollections, Collection } from './collections.js' +import { cleanInventories, Inventories } from './inventory.js' +import { cleanFairySouls, FairySouls } from './fairysouls.js' +import { cleanObjectives, Objective } from './objectives.js' +import { CleanFullProfileBasicMembers } from './profile.js' +import { cleanProfileStats, StatItem } from './stats.js' +import { CleanMinion, cleanMinions } from './minions.js' +import { cleanSlayers, SlayerData } from './slayers.js' +import { AccountCustomization } from '../../database.js' +import { cleanVisitedZones, Zone } from './zones.js' +import { cleanSkills, Skill } from './skills.js' +import * as cached from '../../hypixelCached.js' +import * as constants from '../../constants.js' +import { Included } from '../../hypixel.js' +import { CleanPlayer } from '../player.js' +import { CleanRank } from '../rank.js' +import { Bank } from './bank.js' export interface CleanBasicMember { uuid: string diff --git a/src/cleaners/skyblock/minions.ts b/src/cleaners/skyblock/minions.ts index d045a7e..2e51896 100644 --- a/src/cleaners/skyblock/minions.ts +++ b/src/cleaners/skyblock/minions.ts @@ -1,5 +1,5 @@ -import { maxMinion } from '../../hypixel' -import * as constants from '../../constants' +import { maxMinion } from '../../hypixel.js' +import * as constants from '../../constants.js' export interface CleanMinion { name: string, diff --git a/src/cleaners/skyblock/profile.ts b/src/cleaners/skyblock/profile.ts index e4d2a94..34c895a 100644 --- a/src/cleaners/skyblock/profile.ts +++ b/src/cleaners/skyblock/profile.ts @@ -1,8 +1,8 @@ -import { CleanBasicMember, CleanMember, cleanSkyBlockProfileMemberResponse, cleanSkyBlockProfileMemberResponseBasic } from './member' -import { CleanMinion, combineMinionArrays, countUniqueMinions } from './minions' -import { ApiOptions } from '../../hypixel' -import { Bank, cleanBank } from './bank' -import * as constants from '../../constants' +import { CleanBasicMember, CleanMember, cleanSkyBlockProfileMemberResponse, cleanSkyBlockProfileMemberResponseBasic } from './member.js' +import { CleanMinion, combineMinionArrays, countUniqueMinions } from './minions.js' +import * as constants from '../../constants.js' +import { ApiOptions } from '../../hypixel.js' +import { Bank, cleanBank } from './bank.js' export interface CleanProfile extends CleanBasicProfile { members?: CleanBasicMember[] diff --git a/src/cleaners/skyblock/profiles.ts b/src/cleaners/skyblock/profiles.ts index a068dae..ec026de 100644 --- a/src/cleaners/skyblock/profiles.ts +++ b/src/cleaners/skyblock/profiles.ts @@ -1,10 +1,10 @@ -import { HypixelPlayerStatsSkyBlockProfiles } from '../../hypixelApi' +import { HypixelPlayerStatsSkyBlockProfiles } from '../../hypixelApi.js' import { CleanBasicProfile, CleanFullProfile, CleanProfile, cleanSkyblockProfileResponse -} from './profile' +} from './profile.js' export function cleanPlayerSkyblockProfiles(rawProfiles: HypixelPlayerStatsSkyBlockProfiles): CleanBasicProfile[] { let profiles: CleanBasicProfile[] = [] diff --git a/src/cleaners/skyblock/skills.ts b/src/cleaners/skyblock/skills.ts index 7aa4e2c..886c59c 100644 --- a/src/cleaners/skyblock/skills.ts +++ b/src/cleaners/skyblock/skills.ts @@ -1,4 +1,4 @@ -import { fetchSkillXp, fetchSkillXpEasier } from '../../constants' +import { fetchSkillXp, fetchSkillXpEasier } from '../../constants.js' export interface Skill { name: string diff --git a/src/constants.ts b/src/constants.ts index dcb9acf..13fc21d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,14 +3,14 @@ */ // we have to do this so we can mock the function from the tests properly -import * as constants from './constants' +import * as constants from './constants.js' import * as nodeFetch from 'node-fetch' import NodeCache from 'node-cache' +import { debug } from './index.js' import Queue from 'queue-promise' import fetch from 'node-fetch' import { Agent } from 'https' -import { debug } from '.' const httpsAgent = new Agent({ keepAlive: true @@ -87,7 +87,7 @@ function fetchFile(path: string): Promise<GithubFile> { 'Accept': 'application/vnd.github.v3+json', }, ) - const data = await r.json() + const data = await r.json() as any const file = { path: data.path, @@ -118,7 +118,7 @@ async function editFile(file: GithubFile, message: string, newContent: string): branch: 'main' } ) - const data = await r.json() + const data = await r.json() as any fileCache.set(file.path, { path: data.content.path, content: newContent, @@ -126,7 +126,7 @@ async function editFile(file: GithubFile, message: string, newContent: string): }) } -export async function fetchJSONConstant(filename: string): Promise<any> { +export let fetchJSONConstant = async function fetchJSONConstant(filename: string): Promise<any> { const file = await fetchFile(filename) try { return JSON.parse(file.content) @@ -137,7 +137,7 @@ export async function fetchJSONConstant(filename: string): Promise<any> { } /** Add stats to skyblock-constants. This has caching so it's fine to call many times */ -export async function addJSONConstants(filename: string, addingValues: string[], unit: string='stat'): Promise<void> { +export let addJSONConstants = async function addJSONConstants(filename: string, addingValues: string[], unit: string='stat'): Promise<void> { if (addingValues.length === 0) return // no stats provided, just return let file: GithubFile = await fetchFile(filename) @@ -269,4 +269,9 @@ export async function setConstantValues(newValues: constantValues) { try { await editFile(file, commitMessage, JSON.stringify(updatedStats, null, 2)) } catch {} -}
\ No newline at end of file +} + + +// this is necessary for mocking in the tests because es6 +export function mockAddJSONConstants($value) { addJSONConstants = $value } +export function mockFetchJSONConstant($value) { fetchJSONConstant = $value } diff --git a/src/database.ts b/src/database.ts index 0e16d48..5cd10db 100644 --- a/src/database.ts +++ b/src/database.ts @@ -2,20 +2,20 @@ * Store data about members for leaderboards */ -import { categorizeStat, getStatUnit } from './cleaners/skyblock/stats' -import { CleanFullProfile } from './cleaners/skyblock/profile' -import { slayerLevels } from './cleaners/skyblock/slayers' -import { CleanMember } from './cleaners/skyblock/member' +import { categorizeStat, getStatUnit } from './cleaners/skyblock/stats.js' +import { CleanFullProfile } from './cleaners/skyblock/profile.js' +import { slayerLevels } from './cleaners/skyblock/slayers.js' +import { CleanMember } from './cleaners/skyblock/member.js' import { Collection, Db, MongoClient } from 'mongodb' -import { CleanPlayer } from './cleaners/player' -import * as cached from './hypixelCached' -import * as constants from './constants' -import { shuffle, sleep } from './util' -import * as discord from './discord' +import { CleanPlayer } from './cleaners/player.js' +import * as cached from './hypixelCached.js' +import * as constants from './constants.js' +import { shuffle, sleep } from './util.js' +import * as discord from './discord.js' import NodeCache from 'node-cache' import { v4 as uuid4 } from 'uuid' import Queue from 'queue-promise' -import { debug } from '.' +import { debug } from './index.js' // don't update the user for 3 minutes const recentlyUpdated = new NodeCache({ @@ -121,7 +121,7 @@ async function connect(): Promise<void> { return console.warn('Warning: db_uri was not found in .env. Features that utilize the database such as leaderboards won\'t work.') if (!process.env.db_name) return console.warn('Warning: db_name was not found in .env. Features that utilize the database such as leaderboards won\'t work.') - client = await MongoClient.connect(process.env.db_uri, { useNewUrlParser: true, useUnifiedTopology: true }) + client = await MongoClient.connect(process.env.db_uri) database = client.db(process.env.db_name) memberLeaderboardsCollection = database.collection('member-leaderboards') profileLeaderboardsCollection = database.collection('profile-leaderboards') @@ -803,6 +803,7 @@ export async function updateAccount(discordId: string, schema: AccountSchema) { } // make sure it's not in a test +console.log('global.isTest', globalThis.isTest) if (!globalThis.isTest) { connect().then(() => { // when it connects, cache the leaderboards and remove bad members diff --git a/src/discord.ts b/src/discord.ts index fac5438..067b899 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -50,7 +50,7 @@ export async function exchangeCode(redirectUri: string, code: string): Promise<T body: new URLSearchParams(data).toString() } ) - return await fetchResponse.json() + return await fetchResponse.json() as TokenResponse | null } @@ -63,5 +63,5 @@ export async function getUser(accessToken: string): Promise<DiscordUser> { agent: () => httpsAgent, } ) - return response.json() + return await response.json() as DiscordUser } diff --git a/src/hypixel.ts b/src/hypixel.ts index d6f9661..8811624 100644 --- a/src/hypixel.ts +++ b/src/hypixel.ts @@ -2,14 +2,26 @@ * Fetch the clean Hypixel API */ -import { cleanSkyblockProfileResponse, CleanProfile, CleanBasicProfile, CleanFullProfile, CleanFullProfileBasicMembers } from './cleaners/skyblock/profile' -import { AccountCustomization, AccountSchema, fetchAccount, queueUpdateDatabaseMember, queueUpdateDatabaseProfile } from './database' -import { CleanBasicMember, CleanMemberProfile } from './cleaners/skyblock/member' -import { chooseApiKey, HypixelResponse, sendApiRequest } from './hypixelApi' -import { cleanSkyblockProfilesResponse } from './cleaners/skyblock/profiles' -import { CleanPlayer, cleanPlayerResponse } from './cleaners/player' -import * as cached from './hypixelCached' -import { debug } from '.' +import { + cleanSkyblockProfileResponse, + CleanProfile, + CleanBasicProfile, + CleanFullProfile, + CleanFullProfileBasicMembers +} from './cleaners/skyblock/profile.js' +import { + AccountCustomization, + AccountSchema, + fetchAccount, + queueUpdateDatabaseMember, + queueUpdateDatabaseProfile +} from './database.js' +import { CleanBasicMember, CleanMemberProfile } from './cleaners/skyblock/member.js' +import { chooseApiKey, HypixelResponse, sendApiRequest } from './hypixelApi.js' +import { cleanSkyblockProfilesResponse } from './cleaners/skyblock/profiles.js' +import { CleanPlayer, cleanPlayerResponse } from './cleaners/player.js' +import * as cached from './hypixelCached.js' +import { debug } from './index.js' export type Included = 'profiles' | 'player' | 'stats' | 'inventories' | undefined diff --git a/src/hypixelApi.ts b/src/hypixelApi.ts index 1b57d8f..dd5e08c 100644 --- a/src/hypixelApi.ts +++ b/src/hypixelApi.ts @@ -3,12 +3,12 @@ */ import fetch from 'node-fetch' import * as nodeFetch from 'node-fetch' -import { jsonToQuery, shuffle } from './util' +import { jsonToQuery, shuffle } from './util.js' import { Agent } from 'https' if (!process.env.hypixel_keys) // if there's no hypixel keys in env, run dotenv - require('dotenv').config() + (await import('dotenv')).config() // We need to create an agent to prevent memory leaks and to only do dns lookups once const httpsAgent = new Agent({ @@ -139,7 +139,7 @@ export interface HypixelPlayer { } /** Send an HTTP request to the Hypixel API */ -export async function sendApiRequest({ path, key, args }): Promise<HypixelResponse> { +export let sendApiRequest = async function sendApiRequest({ path, key, args }): Promise<HypixelResponse> { // Send a raw http request to api.hypixel.net, and return the parsed json if (key) @@ -188,3 +188,5 @@ export async function sendApiRequest({ path, key, args }): Promise<HypixelRespon return fetchJsonParsed } +// this is necessary for mocking in the tests because es6 +export function mockSendApiRequest($value) { sendApiRequest = $value }
\ No newline at end of file diff --git a/src/hypixelCached.ts b/src/hypixelCached.ts index 51dca5f..73e75aa 100644 --- a/src/hypixelCached.ts +++ b/src/hypixelCached.ts @@ -2,14 +2,14 @@ * Fetch the clean and cached Hypixel API */ +import { CleanProfile, CleanFullProfile, CleanBasicProfile } from './cleaners/skyblock/profile.js' +import { CleanPlayer } from './cleaners/player.js' +import { isUuid, undashUuid } from './util.js' +import * as hypixel from './hypixel.js' +import * as mojang from './mojang.js' import NodeCache from 'node-cache' +import { debug } from './index.js' import LRUCache from 'lru-cache' -import * as mojang from './mojang' -import * as hypixel from './hypixel' -import { CleanPlayer } from './cleaners/player' -import { isUuid, undashUuid } from './util' -import { CleanProfile, CleanFullProfile, CleanBasicProfile } from './cleaners/skyblock/profile' -import { debug } from '.' // cache usernames for 30 minutes /** uuid: username */ diff --git a/src/index.ts b/src/index.ts index 647038f..1327771 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,10 @@ -import { createSession, fetchAccountFromDiscord, fetchAllLeaderboardsCategorized, fetchLeaderboard, fetchMemberLeaderboardSpots, fetchSession, finishedCachingRawLeaderboards, leaderboardUpdateMemberQueue, leaderboardUpdateProfileQueue, updateAccount } from './database' -import { fetchMemberProfile, fetchUser } from './hypixel' +import { createSession, fetchAccountFromDiscord, fetchAllLeaderboardsCategorized, fetchLeaderboard, fetchMemberLeaderboardSpots, fetchSession, finishedCachingRawLeaderboards, leaderboardUpdateMemberQueue, leaderboardUpdateProfileQueue, updateAccount } from './database.js' +import { fetchMemberProfile, fetchUser } from './hypixel.js' import rateLimit from 'express-rate-limit' -import * as constants from './constants' -import * as discord from './discord' +import * as constants from './constants.js' +import * as discord from './discord.js' import express from 'express' -import { getKeyUsage } from './hypixelApi' +import { getKeyUsage } from './hypixelApi.js' const app = express() diff --git a/src/mojang.ts b/src/mojang.ts index 84c8c7d..9b224d6 100644 --- a/src/mojang.ts +++ b/src/mojang.ts @@ -2,10 +2,10 @@ * Fetch the Mojang username API through api.ashcon.app */ -import fetch from 'node-fetch' +import { isUuid, undashUuid } from './util.js' import * as nodeFetch from 'node-fetch' +import fetch from 'node-fetch' import { Agent } from 'https' -import { isUuid, undashUuid } from './util' // We need to create an agent to prevent memory leaks const httpsAgent = new Agent({ @@ -21,7 +21,7 @@ interface MojangApiResponse { /** * Get mojang api data from the session server */ -export async function profileFromUuid(uuid: string): Promise<MojangApiResponse> { +export let profileFromUuid = async function profileFromUuid(uuid: string): Promise<MojangApiResponse> { let fetchResponse: nodeFetch.Response try { @@ -56,7 +56,7 @@ export async function profileFromUuid(uuid: string): Promise<MojangApiResponse> } -export async function profileFromUsername(username: string): Promise<MojangApiResponse> { +export let profileFromUsername = async function profileFromUsername(username: string): Promise<MojangApiResponse> { // since we don't care about anything other than the uuid, we can use /uuid/ instead of /user/ let fetchResponse: nodeFetch.Response @@ -118,9 +118,15 @@ export async function profileFromUsernameAlternative(username: string): Promise< } } -export async function profileFromUser(user: string): Promise<MojangApiResponse> { +export let profileFromUser = async function profileFromUser(user: string): Promise<MojangApiResponse> { if (isUuid(user)) { return await profileFromUuid(user) } else return await profileFromUsername(user) } + + +// this is necessary for mocking in the tests because es6 +export function mockProfileFromUuid($value) { profileFromUuid = $value } +export function mockProfileFromUsername($value) { profileFromUsername = $value } +export function mockProfileFromUser($value) { profileFromUser = $value } diff --git a/test/test.js b/test/test.js index bb96849..88c2ba9 100644 --- a/test/test.js +++ b/test/test.js @@ -1,15 +1,17 @@ globalThis.isTest = true +console.log('set globalThis.isTest to true', globalThis.isTest) -const { levelForSkillXp } = require('../build/cleaners/skyblock/skills') -const hypixelCached = require('../build/hypixelCached') -const hypixelApi = require('../build/hypixelApi') -const constants = require('../build/constants') -const hypixel = require('../build/hypixel') -const mojang = require('../build/mojang') -const util = require('../build/util') -const assert = require('assert') -const path = require('path') -const fs = require('fs') +// we use `await import` instead of `import from` so globalThis.isTest is set before importing the modules +const { levelForSkillXp } = await import('../build/cleaners/skyblock/skills.js') +const hypixelCached = await import('../build/hypixelCached.js') +const hypixelApi = await import('../build/hypixelApi.js') +const constants = await import('../build/constants.js') +const hypixel = await import('../build/hypixel.js') +const mojang = await import('../build/mojang.js') +const util = await import('../build/util.js') +const assert = await import('assert') +const path = await import('path') +const fs = await import('fs') const cachedJsonData = {} @@ -26,7 +28,7 @@ async function readJsonData(dir) { return parsedData } -hypixelApi.sendApiRequest = async ({ path, key, args }) => { +hypixelApi.mockSendApiRequest(async ({ path, key, args }) => { requestsSent ++ switch (path) { case 'player': { @@ -37,32 +39,32 @@ hypixelApi.sendApiRequest = async ({ path, key, args }) => { } } console.log(path, args) -} +}) -mojang.profileFromUuid = async (uuid) => { +mojang.mockProfileFromUuid(async (uuid) => { requestsSent ++ const uuidToUsername = await readJsonData('mojang') const undashedUuid = undashUuid(uuid) const username = uuidToUsername[undashUuid(undashedUuid)] return { username, uuid: undashedUuid } -} -mojang.profileFromUsername = async (username) => { +}) +mojang.mockProfileFromUsername(async (username) => { requestsSent ++ const uuidToUsername = await readJsonData('mojang') const uuid = Object.keys(uuidToUsername).find(uuid => uuidToUsername[uuid] === username) return { username, uuid } -} -mojang.profileFromUser = async (user) => { +}) +mojang.mockProfileFromUser(async (user) => { if (util.isUuid(user)) return await mojang.profileFromUuid(user) else return await mojang.profileFromUsername(user) -} +}) -constants.addJSONConstants = async(filename, addingValues, unit) => {} -constants.fetchJSONConstant = async(filename) => { +constants.mockAddJSONConstants(async(filename, addingValues, unit) => {}) +constants.mockFetchJSONConstant(async(filename) => { return await readJsonData('constants/' + filename.slice(0, filename.length - '.json'.length)) -} +}) /** Clear all the current caches and stuff */ diff --git a/tsconfig.json b/tsconfig.json index d54082d..927f459 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,12 @@ { "compilerOptions": { - "module": "commonjs", + "module": "ESNext", "lib": ["esnext", "dom", "DOM.Iterable"], - "target": "es2019", + "target": "ESNext", "esModuleInterop": true, "outDir": "build", - "strictNullChecks": true + "strictNullChecks": true, + "moduleResolution": "node" }, "include": ["src"], "exclude": ["**/node_modules"], |