From 5ed9d797150506226562da75daf72a98ca597ef6 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 26 Jan 2021 03:52:39 -0600 Subject: [PATCH 01/30] Changed command loading to use a glob pattern --- package-lock.json | 39 +++++---- package.json | 2 + src/commands/help.ts | 10 +-- src/commands/template.ts | 57 ++++++++++++ src/core/command.ts | 184 ++++++++++++++------------------------- src/events/message.ts | 10 +-- src/index.ts | 4 +- src/setup.ts | 15 +++- tsconfig.json | 3 +- 9 files changed, 170 insertions(+), 154 deletions(-) create mode 100644 src/commands/template.ts diff --git a/package-lock.json b/package-lock.json index 67cb5d2..7a9b4e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,16 @@ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "@types/inquirer": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", @@ -53,6 +63,12 @@ "rxjs": "^6.4.0" } }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, "@types/mocha": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", @@ -172,8 +188,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "binary-extensions": { "version": "2.1.0", @@ -185,7 +200,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -336,8 +350,7 @@ "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 + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "create-require": { "version": "1.1.1", @@ -515,8 +528,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.1.3", @@ -535,7 +547,6 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -583,7 +594,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -592,8 +602,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "inquirer": { "version": "7.3.3", @@ -738,7 +747,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -841,7 +849,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -897,8 +904,7 @@ "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 + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "3.1.1", @@ -1333,8 +1339,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { "version": "7.4.2", diff --git a/package.json b/package.json index b76715e..342f5e7 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,14 @@ "chalk": "^4.1.0", "discord.js": "^12.5.1", "discord.js-lavalink-lib": "^0.1.8", + "glob": "^7.1.6", "inquirer": "^7.3.3", "moment": "^2.29.1", "ms": "^2.1.3", "os": "^0.1.1" }, "devDependencies": { + "@types/glob": "^7.1.3", "@types/inquirer": "^6.5.0", "@types/mocha": "^8.2.0", "@types/ms": "^0.7.31", diff --git a/src/commands/help.ts b/src/commands/help.ts index cf6459b..4a6ff2f 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,6 +1,6 @@ import Command from "../core/command"; import {CommonLibrary} from "../core/lib"; -import {loadCommands, categories} from "../core/command"; +import {loadableCommands, categories} from "../core/command"; import {PermissionNames} from "../core/permissions"; export default new Command({ @@ -8,11 +8,11 @@ export default new Command({ usage: "([command, [subcommand/type], ...])", aliases: ["h"], async run($: CommonLibrary): Promise { - const commands = await loadCommands(); + const commands = await loadableCommands; let output = `Legend: \`\`, \`[list/of/stuff]\`, \`(optional)\`, \`()\`, \`([optional/list/...])\``; for (const [category, headers] of categories) { - output += `\n\n===[ ${category} ]===`; + output += `\n\n===[ ${$(category).toTitleCase()} ]===`; for (const header of headers) { if (header !== "test") { @@ -30,7 +30,7 @@ export default new Command({ }, any: new Command({ async run($: CommonLibrary): Promise { - const commands = await loadCommands(); + const commands = await loadableCommands; let header = $.args.shift() as string; let command = commands.get(header); @@ -51,7 +51,7 @@ export default new Command({ $.warn( `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.` ); - else selectedCategory = category; + else selectedCategory = $(category).toTitleCase(); } } diff --git a/src/commands/template.ts b/src/commands/template.ts new file mode 100644 index 0000000..832fecd --- /dev/null +++ b/src/commands/template.ts @@ -0,0 +1,57 @@ +import Command from "../core/command"; +import {CommonLibrary} from "../core/lib"; + +export default new Command({ + description: + 'This is a template/testing command providing common functionality. Remove what you don\'t need, and rename/delete this file to generate a fresh command file here. This command should be automatically excluded from the help command. The "usage" parameter (string) overrides the default usage for the help command. The "endpoint" parameter (boolean) prevents further arguments from being passed. Also, as long as you keep the run function async, it\'ll return a promise allowing the program to automatically catch any synchronous errors. However, you\'ll have to do manual error handling if you go the then and catch route.', + endpoint: false, + usage: "", + permission: null, + aliases: [], + async run($: CommonLibrary): Promise { + // code + }, + subcommands: { + layer: new Command({ + description: + 'This is a named subcommand, meaning that the key name is what determines the keyword to use. With default settings for example, "$test layer".', + endpoint: false, + usage: "", + permission: null, + aliases: [], + async run($: CommonLibrary): Promise { + // code + } + }) + }, + user: new Command({ + description: + 'This is the subcommand for getting users by pinging them or copying their ID. With default settings for example, "$test 237359961842253835". The argument will be a user object and won\'t run if no user is found by that ID.', + endpoint: false, + usage: "", + permission: null, + async run($: CommonLibrary): Promise { + // code + } + }), + number: new Command({ + description: + 'This is a numeric subcommand, meaning that any type of number (excluding Infinity/NaN) will route to this command if present. With default settings for example, "$test -5.2". The argument with the number is already parsed so you can just use it without converting it.', + endpoint: false, + usage: "", + permission: null, + async run($: CommonLibrary): Promise { + // code + } + }), + any: new Command({ + description: + "This is a generic subcommand, meaning that if there isn't a more specific subcommand that's called, it falls to this. With default settings for example, \"$test reeee\".", + endpoint: false, + usage: "", + permission: null, + async run($: CommonLibrary): Promise { + // code + } + }) +}); diff --git a/src/core/command.ts b/src/core/command.ts index 7ba0f02..fd4eb64 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,9 +1,8 @@ import $, {isType, parseVars, CommonLibrary} from "./lib"; import {Collection} from "discord.js"; -import {generateHandler} from "./storage"; -import {promises as ffs, existsSync, writeFile} from "fs"; import {PERMISSIONS} from "./permissions"; import {getPrefix} from "../core/structures"; +import glob from "glob"; interface CommandOptions { description?: string; @@ -148,133 +147,80 @@ export default class Command { } } -let commands: Collection | null = null; -export const categories: Collection = new Collection(); -export const aliases: Collection = new Collection(); // Top-level aliases only. +// Internally, it'll keep its original capitalization. It's up to you to convert it to title case when you make a help command. +export const categories = new Collection(); /** Returns the cache of the commands if it exists and searches the directory if not. */ -export async function loadCommands(): Promise> { - if (commands) return commands; +export const loadableCommands = (async () => { + const commands = new Collection(); + // Include all .ts files recursively in "src/commands/". + const files = await globP("src/commands/**/*.ts"); + // Extract the usable parts from "src/commands/" if: + // - The path is 1 to 2 subdirectories (a or a/b, not a/b/c) + // - Any leading directory isn't "modules" + // - The filename doesn't end in .test.ts (for jest testing) + // - The filename cannot be the hardcoded top-level "template.ts", reserved for generating templates + const pattern = /src\/commands\/(?!template\.ts)(?!modules\/)(\w+(?:\/\w+)?)(?:test\.)?\.ts/; + const lists: {[category: string]: string[]} = {}; - if (process.argv[2] === "dev" && !existsSync("src/commands/test.ts")) - writeFile( - "src/commands/test.ts", - template, - generateHandler('"test.ts" (testing/template command) successfully generated.') - ); + for (const path of files) { + const match = pattern.exec(path); - commands = new Collection(); - const dir = await ffs.opendir("dist/commands"); - const listMisc: string[] = []; - let selected; + if (match) { + const commandID = match[1]; // e.g. "utilities/info" + const slashIndex = commandID.indexOf("/"); + const isMiscCommand = slashIndex !== -1; + const category = isMiscCommand ? commandID.substring(0, slashIndex) : "miscellaneous"; + const commandName = isMiscCommand ? commandID.substring(slashIndex + 1) : commandID; // e.g. "info" + // If the dynamic import works, it must be an object at the very least. Then, just test to see if it's a proper instance. + const command = (await import(`../commands/${commandID}`)).default as unknown; - // There will only be one level of directory searching (per category). - while ((selected = await dir.read())) { - if (selected.isDirectory()) { - if (selected.name === "subcommands") continue; + if (command instanceof Command) { + command.originalCommandName = commandName; - const subdir = await ffs.opendir(`dist/commands/${selected.name}`); - const category = $(selected.name).toTitleCase(); - const list: string[] = []; - let cmd; + if (commands.has(commandName)) { + $.warn( + `Command "${commandName}" already exists! Make sure to make each command uniquely identifiable across categories!` + ); + } else { + commands.set(commandName, command); + } - while ((cmd = await subdir.read())) { - if (cmd.isDirectory()) { - if (cmd.name === "subcommands") continue; - else $.warn(`You can't have multiple levels of directories! From: "dist/commands/${cmd.name}"`); - } else loadCommand(cmd.name, list, selected.name); + for (const alias of command.aliases) { + if (commands.has(alias)) { + $.warn( + `Top-level alias "${alias}" from command "${commandID}" already exists either as a command or alias!` + ); + } else { + commands.set(alias, command); + } + } + + if (!(category in lists)) lists[category] = []; + lists[category].push(commandName); + + $.log(`Loading Command: ${commandID}`); + } else { + $.warn(`Command "${commandID}" has no default export which is a Command instance!`); } - - subdir.close(); - categories.set(category, list); - } else loadCommand(selected.name, listMisc); + } } - dir.close(); - categories.set("Miscellaneous", listMisc); + for (const category in lists) { + categories.set(category, lists[category]); + } return commands; +})(); + +function globP(path: string) { + return new Promise((resolve, reject) => { + glob(path, (error, files) => { + if (error) { + reject(error); + } else { + resolve(files); + } + }); + }); } - -async function loadCommand(filename: string, list: string[], category?: string) { - if (!commands) return $.error(`Function "loadCommand" was called without first initializing commands!`); - - const prefix = category ?? ""; - const header = filename.substring(0, filename.indexOf(".js")); - const command = (await import(`../commands/${prefix}/${header}`)).default as Command | undefined; - - if (!command) return $.warn(`Command "${header}" has no default export which is a Command instance!`); - - command.originalCommandName = header; - list.push(header); - - if (commands.has(header)) - $.warn( - `Command "${header}" already exists! Make sure to make each command uniquely identifiable across categories!` - ); - else commands.set(header, command); - - for (const alias of command.aliases) { - if (commands.has(alias)) - $.warn(`Top-level alias "${alias}" from command "${header}" already exists either as a command or alias!`); - else commands.set(alias, command); - } - - $.log(`Loading Command: ${header} (${category ? $(category).toTitleCase() : "Miscellaneous"})`); -} - -// The template should be built with a reductionist mentality. -// Provide everything the user needs and then let them remove whatever they want. -// That way, they aren't focusing on what's missing, but rather what they need for their command. -const template = `import Command from '../core/command'; -import {CommonLibrary} from '../core/lib'; - -export default new Command({ - description: "This is a template/testing command providing common functionality. Remove what you don't need, and rename/delete this file to generate a fresh command file here. This command should be automatically excluded from the help command. The \\"usage\\" parameter (string) overrides the default usage for the help command. The \\"endpoint\\" parameter (boolean) prevents further arguments from being passed. Also, as long as you keep the run function async, it'll return a promise allowing the program to automatically catch any synchronous errors. However, you'll have to do manual error handling if you go the then and catch route.", - endpoint: false, - usage: '', - permission: null, - aliases: [], - async run($: CommonLibrary): Promise { - - }, - subcommands: { - layer: new Command({ - description: "This is a named subcommand, meaning that the key name is what determines the keyword to use. With default settings for example, \\"$test layer\\".", - endpoint: false, - usage: '', - permission: null, - aliases: [], - async run($: CommonLibrary): Promise { - - } - }) - }, - user: new Command({ - description: "This is the subcommand for getting users by pinging them or copying their ID. With default settings for example, \\"$test 237359961842253835\\". The argument will be a user object and won't run if no user is found by that ID.", - endpoint: false, - usage: '', - permission: null, - async run($: CommonLibrary): Promise { - - } - }), - number: new Command({ - description: "This is a numeric subcommand, meaning that any type of number (excluding Infinity/NaN) will route to this command if present. With default settings for example, \\"$test -5.2\\". The argument with the number is already parsed so you can just use it without converting it.", - endpoint: false, - usage: '', - permission: null, - async run($: CommonLibrary): Promise { - - } - }), - any: new Command({ - description: "This is a generic subcommand, meaning that if there isn't a more specific subcommand that's called, it falls to this. With default settings for example, \\"$test reeee\\".", - endpoint: false, - usage: '', - permission: null, - async run($: CommonLibrary): Promise { - - } - }) -});`; diff --git a/src/events/message.ts b/src/events/message.ts index 107c8cf..dc9c6d5 100644 --- a/src/events/message.ts +++ b/src/events/message.ts @@ -1,17 +1,13 @@ import Event from "../core/event"; -import Command, {loadCommands} from "../core/command"; +import Command, {loadableCommands} from "../core/command"; import {hasPermission, getPermissionLevel, PermissionNames} from "../core/permissions"; -import {Permissions, Collection} from "discord.js"; +import {Permissions} from "discord.js"; import {getPrefix} from "../core/structures"; import $, {replyEventListeners} from "../core/lib"; -// It's a rather hacky solution, but since there's no top-level await, I just have to make the loading conditional. -let commands: Collection | null = null; - export default new Event<"message">({ async on(message) { - // Load commands if it hasn't already done so. Luckily, it's called once at most. - if (!commands) commands = await loadCommands(); + const commands = await loadableCommands; // Message Setup // if (message.author.bot) return; diff --git a/src/index.ts b/src/index.ts index 9721784..15d64f7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,6 @@ import {Client} from "discord.js"; import setup from "./setup"; import {Config} from "./core/structures"; -import {loadCommands} from "./core/command"; import {loadEvents} from "./core/event"; import "discord.js-lavalink-lib"; import LavalinkMusic from "discord.js-lavalink-lib"; @@ -30,9 +29,8 @@ export const client = new Client(); admins: ["717352467280691331"] }); -// Begin the command loading here rather than when it's needed like in the message event. +// Command loading will start as soon as an instance of "core/command" is loaded, which is loaded during "events/message". setup.init().then(() => { - loadCommands(); loadEvents(client); client.login(Config.token).catch(setup.again); }); diff --git a/src/setup.ts b/src/setup.ts index 64a854a..7edde85 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -1,9 +1,20 @@ -import {existsSync as exists} from "fs"; +import {existsSync as exists, readFileSync as read, writeFile as write} from "fs"; import inquirer from "inquirer"; -import Storage from "./core/storage"; +import Storage, {generateHandler} from "./core/storage"; import {Config} from "./core/structures"; import $, {setConsoleActivated} from "./core/lib"; +// The template should be built with a reductionist mentality. +// Provide everything the user needs and then let them remove whatever they want. +// That way, they aren't focusing on what's missing, but rather what they need for their command. +if (process.argv[2] === "dev" && !exists("src/commands/test.ts")) { + write( + "src/commands/test.ts", + read("src/commands/template.ts"), + generateHandler('"test.ts" (testing/template command) successfully generated.') + ); +} + // This file is called (or at least should be called) automatically as long as a config file doesn't exist yet. // And that file won't be written until the data is successfully initialized. const prompts = [ diff --git a/tsconfig.json b/tsconfig.json index ba4dd11..be15e8f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,8 @@ "strictNullChecks": true, "strictFunctionTypes": true, "strictPropertyInitialization": true, - "removeComments": true + "removeComments": true, + "sourceMap": true }, "exclude": ["test"] } From 1fd8634ef1e42673e7ac3926d632ec511bbca0b4 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 26 Jan 2021 07:22:23 -0600 Subject: [PATCH 02/30] Tinkered with pre-commit, jest, and tsconfig --- jest.config.js | 7 + package-lock.json | 5077 +++++++++++++++-- package.json | 23 +- src/commands/admin.ts | 2 +- src/commands/info.ts | 10 +- src/core/lib.ts | 6 +- test/wrappers.ts => src/core/wrappers.test.ts | 2 +- tsconfig.json | 47 +- tsconfig.prod.json | 8 + 9 files changed, 4629 insertions(+), 553 deletions(-) create mode 100644 jest.config.js rename test/wrappers.ts => src/core/wrappers.test.ts (97%) create mode 100644 tsconfig.prod.json diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..09097b3 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + roots: ["/src"], + testMatch: ["**/*.test.+(ts|tsx)"], + transform: { + "^.+\\.(ts|tsx)$": "ts-jest" + } +}; diff --git a/package-lock.json b/package-lock.json index 7a9b4e7..dcccab1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,414 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/core": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", + "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", + "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.11", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", + "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", + "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.7" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", + "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", + "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.7", + "@babel/helper-optimise-call-expression": "^7.12.10", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", + "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "dev": true, + "requires": { + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" + } + }, + "@babel/traverse": { + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", + "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.11", + "@babel/generator": "^7.12.11", + "@babel/helper-function-name": "^7.12.11", + "@babel/helper-split-export-declaration": "^7.12.11", + "@babel/parser": "^7.12.11", + "@babel/types": "^7.12.12", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", + "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, "@discordjs/collection": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", @@ -19,6 +427,256 @@ "mime-types": "^2.1.12" } }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + } + }, + "@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + } + }, + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, "@lavacord/discord.js": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/@lavacord/discord.js/-/discord.js-0.0.5.tgz", @@ -38,6 +696,65 @@ } } }, + "@sinonjs/commons": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", + "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", + "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -53,6 +770,15 @@ "@types/node": "*" } }, + "@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/inquirer": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", @@ -63,18 +789,46 @@ "rxjs": "^6.4.0" } }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "26.0.20", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", + "integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", + "dev": true, + "requires": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, - "@types/mocha": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", - "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", - "dev": true - }, "@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", @@ -87,6 +841,24 @@ "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==", "dev": true }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/prettier": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.6.tgz", + "integrity": "sha512-6gOkRe7OIioWAXfnO/2lFiv+SJichKVSys1mSsgyrYHSEjk8Ctv4tSR/Odvnu+HWlH2C8j53dahU03XmQdd5fA==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, "@types/through": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", @@ -105,10 +877,25 @@ "@types/node": "*" } }, - "@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==", + "@types/yargs": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", + "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, "abort-controller": { @@ -119,12 +906,40 @@ "event-target-shim": "^5.0.0" } }, - "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==", + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -157,12 +972,6 @@ "picomatch": "^2.0.4" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -172,11 +981,74 @@ "sprintf-js": "~1.0.2" } }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, "axios": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", @@ -185,16 +1057,153 @@ "follow-redirects": "^1.10.0" } }, + "babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "requires": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + } + } }, "brace-expansion": { "version": "1.1.11", @@ -214,24 +1223,80 @@ "fill-range": "^7.0.1" } }, - "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==", + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -241,25 +1306,50 @@ "supports-color": "^7.1.0" } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, - "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } } }, "cli-cursor": { @@ -275,55 +1365,26 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "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 - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color-convert": { @@ -347,15 +1408,56 @@ "delayed-stream": "~1.0.0" } }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "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==", + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, "cross-spawn": { @@ -369,6 +1471,49 @@ "which": "^2.0.1" } }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, "debug": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", @@ -392,15 +1537,86 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", "dev": true }, "discord.js": { @@ -429,28 +1645,104 @@ "lavacord": "^1.1.7" } }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true + }, "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==" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", @@ -471,6 +1763,169 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -481,6 +1936,104 @@ "tmp": "^0.0.33" } }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -498,27 +2051,43 @@ "to-regex-range": "^5.0.1" } }, - "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, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, "follow-redirects": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==" }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, "from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", @@ -537,12 +2106,54 @@ "dev": true, "optional": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, "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 }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -556,30 +2167,143 @@ "path-is-absolute": "^1.0.0" } }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "ajv": "^6.12.3", + "har-schema": "^2.0.0" } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, - "he": { + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-signature": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, "iconv-lite": { @@ -590,6 +2314,22 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -624,19 +2364,112 @@ "through": "^2.3.6" } }, - "is-binary-path": { + "ip-regex": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "binary-extensions": "^2.0.0" + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, - "is-extglob": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-docker": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "optional": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, "is-fullwidth-code-point": { @@ -644,14 +2477,11 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "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, - "requires": { - "is-extglob": "^2.1.1" - } + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true }, "is-number": { "version": "7.0.0", @@ -659,10 +2489,53 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "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==", + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isexe": { @@ -671,6 +2544,749 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "dependencies": { + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "dependencies": { + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + } + } + }, + "jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + } + }, + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + } + }, + "jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + } + }, + "jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + } + }, + "jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "requires": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + } + }, + "jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true + }, + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + } + }, + "jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + } + }, + "jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "dependencies": { + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "js-yaml": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", @@ -681,6 +3297,109 @@ "esprima": "^4.0.0" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "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==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, "lavacord": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/lavacord/-/lavacord-1.1.9.tgz", @@ -690,27 +3409,69 @@ "ws": "^7.3.0" } }, - "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==", + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "p-locate": "^5.0.0" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "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==", "dev": true, "requires": { - "chalk": "^4.0.0" + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "make-error": { @@ -719,12 +3480,52 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, "map-stream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", "dev": true }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, "mime-db": { "version": "1.45.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", @@ -751,62 +3552,39 @@ "brace-expansion": "^1.1.7" } }, - "mocha": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", - "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.14.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "2.0.0" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { - "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 - }, - "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 - }, - "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==", + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "is-plain-object": "^2.0.4" } } } }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, "moment": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", @@ -822,10 +3600,35 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "node-cleanup": { @@ -839,12 +3642,141 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", + "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "dev": true, + "optional": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "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 }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + }, + "dependencies": { + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + } + } + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -861,33 +3793,47 @@ "mimic-fn": "^2.1.0" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "os": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz", "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=" }, + "os-shim": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", + "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", + "dev": true + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "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, - "requires": { - "yocto-queue": "^0.1.0" - } + "p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true }, - "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, - "requires": { - "p-limit": "^3.0.2" - } + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true }, "p-try": { "version": "2.2.0", @@ -895,6 +3841,30 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -912,6 +3882,12 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -921,23 +3897,190 @@ "through": "~2.3" } }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "pre-commit": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz", + "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "spawn-sync": "^1.0.15", + "which": "1.2.x" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "which": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", + "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, "prettier": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", "dev": true }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, "prism-media": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.3.tgz", "integrity": "sha512-fSrR66n0l6roW9Rx4rSLMyTPTjRTiXy5RVqDOurACQ6si1rKHHKDU5gwBJoCsIV0R3o9gi+K50akl/qyw1C74A==" }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, "ps-tree": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", @@ -947,22 +4090,249 @@ "event-stream": "=3.3.4" } }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "safe-buffer": "^5.1.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, "requires": { - "picomatch": "^2.2.1" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "require-directory": { @@ -977,6 +4347,37 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -986,6 +4387,27 @@ "signal-exit": "^3.0.2" } }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -1005,26 +4427,205 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", "dev": true, "requires": { - "randombytes": "^2.1.0" + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } } }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -1045,17 +4646,177 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, "source-map-support": { "version": "0.5.19", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", @@ -1066,6 +4827,54 @@ "source-map": "^0.6.0" } }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spawn-sync": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", + "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", + "dev": true, + "requires": { + "concat-stream": "^1.4.7", + "os-shim": "^0.1.2" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, "split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", @@ -1075,12 +4884,90 @@ "through": "2" } }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + } + } + }, + "stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, "stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", @@ -1096,6 +4983,16 @@ "integrity": "sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==", "dev": true }, + "string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -1106,6 +5003,23 @@ "strip-ansi": "^6.0.0" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -1114,10 +5028,22 @@ "ansi-regex": "^5.0.0" } }, - "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==", + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "supports-color": { @@ -1128,6 +5054,49 @@ "has-flag": "^4.0.0" } }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -1141,6 +5110,50 @@ "os-tmpdir": "~1.0.2" } }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1150,18 +5163,60 @@ "is-number": "^7.0.0" } }, - "ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", "dev": true, "requires": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "ts-jest": { + "version": "26.4.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.4.tgz", + "integrity": "sha512-3lFWKbLxJm34QxyVNNCgXX1u4o/RV0myvA2y2Bxm46iGIjKlaY0own9gIckbjZJPn+WaJEnfPPJ20HHGpoq4yg==", + "dev": true, + "requires": { + "@types/jest": "26.x", + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^26.1.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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 + } } }, "tsc-watch": { @@ -1182,22 +5237,246 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typescript": { "version": "3.9.7", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true + }, + "v8-to-istanbul": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz", + "integrity": "sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1213,292 +5492,56 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "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, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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 - }, - "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", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "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 - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, "ws": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "y18n": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "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 - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "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, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "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 - } - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "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==", + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } diff --git a/package.json b/package.json index 342f5e7..8362778 100644 --- a/package.json +++ b/package.json @@ -17,24 +17,31 @@ "devDependencies": { "@types/glob": "^7.1.3", "@types/inquirer": "^6.5.0", - "@types/mocha": "^8.2.0", + "@types/jest": "^26.0.20", "@types/ms": "^0.7.31", "@types/node": "^14.14.20", "@types/ws": "^7.4.0", - "mocha": "^8.2.1", + "jest": "^26.6.3", + "pre-commit": "^1.2.2", "prettier": "2.1.2", - "ts-node": "^9.1.1", + "ts-jest": "^26.4.4", "tsc-watch": "^4.2.9", "typescript": "^3.9.7" }, "scripts": { - "build": "tsc && npm prune --production", - "start": "node dist/index.js", + "build": "tsc --project tsconfig.prod.json && npm prune --production", + "start": "node .", "once": "tsc && npm start", - "dev": "tsc-watch --onSuccess \"node dist/index.js dev\"", - "test": "mocha --require ts-node/register --extension ts --recursive", - "format": "prettier --write **/*" + "dev": "tsc-watch --onSuccess \"node . dev\"", + "test": "jest", + "format": "prettier --write **/*", + "precommit-message": "echo \"Running pre-commit formatting and testing...\"" }, + "pre-commit": [ + "precommit-message", + "test", + "format" + ], "keywords": [ "discord.js", "bot" diff --git a/src/commands/admin.ts b/src/commands/admin.ts index 47279f9..9f07dc3 100644 --- a/src/commands/admin.ts +++ b/src/commands/admin.ts @@ -148,7 +148,7 @@ export default new Command({ if (typeof evaled !== "string") evaled = require("util").inspect(evaled); channel.send(clean(evaled), {code: "js", split: true}); } catch (err) { - channel.send(`\`ERROR\` \`\`\`js\n${clean(err)}\n\`\`\``); + channel.send(clean(err), {code: "js", split: true}); } } }), diff --git a/src/commands/info.ts b/src/commands/info.ts index d87044f..d2af397 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -1,6 +1,4 @@ import {MessageEmbed, version as djsversion} from "discord.js"; -/// @ts-ignore -import {version} from "../../package.json"; import ms from "ms"; import os from "os"; import Command from "../core/command"; @@ -53,7 +51,7 @@ export default new Command({ `**❯ Channels:** ${$.client.channels.cache.size.toLocaleString()}`, `**❯ Creation Date:** ${utc($.client.user?.createdTimestamp).format("Do MMMM YYYY HH:mm:ss")}`, `**❯ Node.JS:** ${process.version}`, - `**❯ Version:** v${version}`, + `**❯ Version:** v${process.env.npm_package_version}`, `**❯ Discord.JS:** ${djsversion}`, "\u200b" ]) @@ -183,10 +181,10 @@ export default new Command({ `**❯ Server Join Date:** ${moment(member.joinedAt).format("LL LTS")}`, `**❯ Hoist Role:** ${member.roles.hoist ? member.roles.hoist.name : "None"}`, `**❯ Roles:** [${roles.length}]: ${ - roles.length < 10 + roles.length < 10 && roles.length > 0 ? roles.join(", ") - : roles.length > 10 - ? this.client.utils.trimArray(roles) + : roles.length >= 10 + ? roles.slice(0, 10) : "None" }` ]); diff --git a/src/core/lib.ts b/src/core/lib.ts index 42852d9..2ba6a91 100644 --- a/src/core/lib.ts +++ b/src/core/lib.ts @@ -47,11 +47,7 @@ export interface CommonLibrary { timeout?: number ) => void; askYesOrNo: (message: Message, senderID: string, timeout?: number) => Promise; - askMultipleChoice: ( - message: Message, - senderID: string, - callbackStack: (() => void)[] | ((choice: number) => void) - ) => void; + askMultipleChoice: (message: Message, senderID: string, callbackStack: (() => void)[], timeout?: number) => void; // Dynamic Properties // args: any[]; diff --git a/test/wrappers.ts b/src/core/wrappers.test.ts similarity index 97% rename from test/wrappers.ts rename to src/core/wrappers.test.ts index 7613ee3..e540c63 100644 --- a/test/wrappers.ts +++ b/src/core/wrappers.test.ts @@ -1,5 +1,5 @@ import {strict as assert} from "assert"; -import {NumberWrapper, StringWrapper, ArrayWrapper} from "../src/core/wrappers"; +import {NumberWrapper, StringWrapper, ArrayWrapper} from "./wrappers"; // I can't figure out a way to run the test suite while running the bot. describe("Wrappers", () => { diff --git a/tsconfig.json b/tsconfig.json index be15e8f..2b1714b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,35 @@ +// Reference: https://www.typescriptlang.org/tsconfig { + "include": ["src/**/*"], // This makes it so that the compiler won't compile anything outside of "src". + //"exclude": ["src/**/*.test.ts"], // Exclude .test.ts files since they're for Jest only. "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "target": "ES6", - "module": "CommonJS", - "moduleResolution": "node", - "esModuleInterop": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "removeComments": true, - "sourceMap": true - }, - "exclude": ["test"] + // Project Structure // + "rootDir": "src", // rootDir only affects the STRUCTURE of the folders, not what gets compiled. For extra measure, make sure the structure conforms to "src". + "outDir": "dist", // Likewise, outDir only chooses which folder to compile to. Prevent the compiler from creating JS files right next to source files. + "moduleResolution": "node", // Specify how the compiler resolves modules, like going for node_modules first then searching elsewhere. The official docs just say to use this instead of classic. + + // Type Settings // + "strict": true, // Enables all strict checks possible. + "noImplicitReturns": true, // Makes sure you don't accidentally return something + undefined. + "noFallthroughCasesInSwitch": true, // Prevents accidentally forgetting to break every switch case. Of course, if you know what you're doing, feel free to add a @ts-ignore, which also signals that it's not a mistake. + "forceConsistentCasingInFileNames": true, // Make import paths case-sensitive. "./tEst" is no longer the same as "./test". + "esModuleInterop": true, // Enables compatibility with Node.js' module system since the entire export can be whatever you want. allowSyntheticDefaultImports doesn't address runtime issues and is made redundant by this setting. + "resolveJsonModule": true, // Allows you to import JSON files just like how you can require() them. Do note that if you're accessing any JSON files outside of src, it'll mess up dist. + "lib": ["ES2020"], // Specifies what common libraries you have access to. If you're working in Node.js, you'll want to leave out the DOM library. But do make sure to include "@types/node" because otherwise, variables like "console" won't be defined. + + // Output // + "module": "CommonJS", // Compiles ES6 imports to require() syntax. + "removeComments": false, + "sourceMap": true, // Used for displaying the original source when debugging in webpack. Allows you to set breakpoints directly on TypeScript code for VSCode's debugger. + + // Library Building // + "declaration": false, // Exports declaration files in addition, used for exporting a module. + "declarationMap": false, // Allows the user to go to the source file when hitting a go-to-implementation key like F12 in VSCode for example. + //"declarationDir": "typings", // declarationDir allows you to separate the compiled code from the declaration files, used in conjunction with package.json's "types" property. + + // Web Compatibility // + "target": "ES2020", // ES2017 supports async/await, reducing the amount of compiled code, especially for async-heavy projects. ES2020 is from the Node 14 base (https://github.com/tsconfig/bases/blob/master/bases/node14.json) + "downlevelIteration": false, // This flag adds extra support when targeting ES3, but adds extra bloat otherwise. + "importHelpers": false // Reduce the amount of bloat that comes from downlevelIteration (when polyfills are redeclared). + } } diff --git a/tsconfig.prod.json b/tsconfig.prod.json new file mode 100644 index 0000000..5e86c14 --- /dev/null +++ b/tsconfig.prod.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/**/*.test.ts", "src/**/template.ts"], + "compilerOptions": { + "removeComments": true, + "sourceMap": false + } +} From 30697e502025098c518977674b61d4566fa11c0e Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 26 Jan 2021 09:57:05 -0600 Subject: [PATCH 03/30] First attempt at getting husky to work --- .husky/.gitignore | 1 + .husky/pre-commit | 6 ++ .prettierignore | 1 + package-lock.json | 162 ++-------------------------------------------- package.json | 10 +-- 5 files changed, 16 insertions(+), 164 deletions(-) create mode 100644 .husky/.gitignore create mode 100644 .husky/pre-commit diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000..31354ec --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..07eda3c --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,6 @@ +#!/bin/sh +. "$(dirname $0)/_/husky.sh" + +npm test +npm run format +git add -A diff --git a/.prettierignore b/.prettierignore index 960cb07..40343cc 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,6 +2,7 @@ .dockerignore .gitignore .prettierignore +.husky/ Dockerfile LICENSE diff --git a/package-lock.json b/package-lock.json index dcccab1..a12762e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1419,18 +1419,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -2306,6 +2294,12 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, + "husky": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/husky/-/husky-5.0.6.tgz", + "integrity": "sha512-SM+evfvcHT3rAYJKvPlatz3L5RqzgeM6xIvDjhs8VuhKj6iKqFDOt/Ov8sPjvWuE4FDB385gJBwWXRj7G3c1hg==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3812,12 +3806,6 @@ "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz", "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=" }, - "os-shim": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", - "dev": true - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -3972,70 +3960,6 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, - "pre-commit": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz", - "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "spawn-sync": "^1.0.15", - "which": "1.2.x" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -4065,12 +3989,6 @@ "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.3.tgz", "integrity": "sha512-fSrR66n0l6roW9Rx4rSLMyTPTjRTiXy5RVqDOurACQ6si1rKHHKDU5gwBJoCsIV0R3o9gi+K50akl/qyw1C74A==" }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, "prompts": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", @@ -4090,12 +4008,6 @@ "event-stream": "=3.3.4" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -4206,29 +4118,6 @@ } } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "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==", - "dev": true - } - } - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -4833,16 +4722,6 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, - "spawn-sync": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", - "dev": true, - "requires": { - "concat-stream": "^1.4.7", - "os-shim": "^0.1.2" - } - }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -5003,23 +4882,6 @@ "strip-ansi": "^6.0.0" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "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==", - "dev": true - } - } - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -5271,12 +5133,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -5365,12 +5221,6 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", diff --git a/package.json b/package.json index 8362778..ebbc126 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,6 @@ "version": "0.0.1", "description": "A Discord bot built on Discord.JS v12", "main": "dist/index.js", - "private": true, "dependencies": { "chalk": "^4.1.0", "discord.js": "^12.5.1", @@ -21,8 +20,8 @@ "@types/ms": "^0.7.31", "@types/node": "^14.14.20", "@types/ws": "^7.4.0", + "husky": "^5.0.6", "jest": "^26.6.3", - "pre-commit": "^1.2.2", "prettier": "2.1.2", "ts-jest": "^26.4.4", "tsc-watch": "^4.2.9", @@ -35,13 +34,8 @@ "dev": "tsc-watch --onSuccess \"node . dev\"", "test": "jest", "format": "prettier --write **/*", - "precommit-message": "echo \"Running pre-commit formatting and testing...\"" + "postinstall": "husky install" }, - "pre-commit": [ - "precommit-message", - "test", - "format" - ], "keywords": [ "discord.js", "bot" From 10c1cd9cff0e184ac037be27de8b034749119050 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 30 Mar 2021 03:58:21 -0500 Subject: [PATCH 04/30] Separated custom logger from command menu --- package-lock.json | 7794 +++++++++++++++++---- src/commands/admin.ts | 3 +- src/commands/fun/subcommands/eco-utils.ts | 2 +- src/commands/help.ts | 8 +- src/commands/scanemotes.ts | 4 +- src/core/command.ts | 20 +- src/core/event.ts | 5 +- src/core/lib.ts | 91 +- src/core/permissions.ts | 3 +- src/core/storage.ts | 9 +- src/core/structures.ts | 10 +- src/events/channelCreate.ts | 3 +- src/events/channelDelete.ts | 3 +- src/events/emojiCreate.ts | 3 +- src/events/emojiDelete.ts | 3 +- src/events/emojiUpdate.ts | 3 +- src/events/guildCreate.ts | 3 +- src/events/guildDelete.ts | 3 +- src/events/message.ts | 8 +- src/events/ready.ts | 3 +- src/globals.ts | 86 + src/index.ts | 1 + src/modules/message_embed.ts | 65 +- src/setup.ts | 18 +- 24 files changed, 6763 insertions(+), 1388 deletions(-) create mode 100644 src/globals.ts diff --git a/package-lock.json b/package-lock.json index 8d7b25b..aab9d3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,28 +7,532 @@ "": { "name": "d.js-v12-bot", "version": "0.0.1", + "hasInstallScript": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", "discord.js": "^12.5.1", "discord.js-lavalink-lib": "^0.1.8", + "glob": "^7.1.6", "inquirer": "^7.3.3", "moment": "^2.29.1", "ms": "^2.1.3" }, "devDependencies": { + "@types/glob": "^7.1.3", "@types/inquirer": "^6.5.0", - "@types/mocha": "^8.2.0", + "@types/jest": "^26.0.20", "@types/ms": "^0.7.31", "@types/node": "^14.14.20", "@types/ws": "^7.4.0", - "mocha": "^8.2.1", + "husky": "^5.0.6", + "jest": "^26.6.3", "prettier": "2.1.2", - "ts-node": "^9.1.1", + "ts-jest": "^26.4.4", "tsc-watch": "^4.2.9", "typescript": "^3.9.7" } }, + "node_modules/@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.12.13" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.12.tgz", + "integrity": "sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ==", + "dev": true + }, + "node_modules/@babel/core": { + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.14.tgz", + "integrity": "sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.9", + "@babel/helper-compilation-targets": "^7.13.13", + "@babel/helper-module-transforms": "^7.13.14", + "@babel/helpers": "^7.13.10", + "@babel/parser": "^7.13.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.13", + "@babel/types": "^7.13.14", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", + "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.13.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz", + "integrity": "sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.13.12", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", + "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.13", + "@babel/types": "^7.13.14" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", + "dev": true + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", + "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "dev": true + }, + "node_modules/@babel/helpers": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", + "integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", + "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/traverse": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz", + "integrity": "sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.9", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.13.13", + "@babel/types": "^7.13.13", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "node_modules/@babel/types": { + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", + "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "dependencies": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "bin": { + "watch": "cli.js" + }, + "engines": { + "node": ">=0.1.95" + } + }, "node_modules/@discordjs/collection": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", @@ -47,6 +551,310 @@ "node": ">= 6" } }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "node-notifier": "^8.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, "node_modules/@lavacord/discord.js": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/@lavacord/discord.js/-/discord.js-0.0.5.tgz", @@ -72,11 +880,89 @@ "npm": ">=5" } }, + "node_modules/@sinonjs/commons": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", + "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.14", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", + "integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz", + "integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, "node_modules/@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, + "node_modules/@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/inquirer": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", @@ -87,10 +973,44 @@ "rxjs": "^6.4.0" } }, - "node_modules/@types/mocha": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", - "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "26.0.22", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz", + "integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==", + "dev": true, + "dependencies": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "node_modules/@types/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, "node_modules/@types/ms": { @@ -105,6 +1025,24 @@ "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==", "dev": true }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", + "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, "node_modules/@types/through": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", @@ -123,10 +1061,25 @@ "@types/node": "*" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "node_modules/@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, "node_modules/abort-controller": { @@ -140,13 +1093,63 @@ "node": ">=6.5" } }, - "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==", + "node_modules/acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true, "engines": { - "node": ">=6" + "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==", + "dev": true, + "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-escapes": { @@ -197,7 +1200,9 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/argparse": { "version": "1.0.10", @@ -208,11 +1213,101 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, "node_modules/axios": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", @@ -221,26 +1316,152 @@ "follow-redirects": "^1.10.0" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "node_modules/babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", "dev": true, + "dependencies": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + }, "engines": { "node": ">=8" } }, + "node_modules/babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, "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" @@ -258,18 +1479,91 @@ "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==", + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, + "node_modules/browserslist": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001181", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.649", + "escalade": "^3.1.1", + "node-releases": "^1.1.70" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, "node_modules/buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -279,6 +1573,30 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001204", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz", + "integrity": "sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ==", + "dev": true + }, + "node_modules/capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "dependencies": { + "rsvp": "^4.8.4" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, "node_modules/chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -291,30 +1609,128 @@ "node": ">=10" } }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, - "node_modules/chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" }, - "optionalDependencies": { - "fsevents": "~2.1.2" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/cli-cursor": { @@ -336,65 +1752,33 @@ "node": ">= 10" } }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true, "engines": { - "node": ">=6" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", "dev": true }, - "node_modules/cliui/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/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, "node_modules/color-convert": { @@ -413,6 +1797,12 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -424,17 +1814,54 @@ "node": ">= 0.8" } }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "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=", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/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==", + "dev": true + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, "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 + "dev": true, + "optional": true, + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -450,6 +1877,56 @@ "node": ">= 8" } }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/debug": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", @@ -477,6 +1954,49 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -485,15 +2005,35 @@ "node": ">=0.4.0" } }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "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, + "optional": true, + "peer": true, "engines": { "node": ">=0.3.1" } }, + "node_modules/diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, "node_modules/discord.js": { "version": "12.5.1", "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.5.1.tgz", @@ -523,17 +2063,93 @@ "lavacord": "^1.1.7" } }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.3.702", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.702.tgz", + "integrity": "sha512-qJVUKFWQnF6wP7MmTngDkmm8/KPzaiTXNFOAg5j7DSa6J7kPou7mTBqC8jpUOxauQWwHR3pn4dMRdV8IE1xdtA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, "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==" }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "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-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -542,6 +2158,28 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -555,6 +2193,24 @@ "node": ">=4" } }, + "node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", @@ -578,6 +2234,217 @@ "node": ">=6" } }, + "node_modules/exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", + "dev": true + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -591,6 +2458,94 @@ "node": ">=4" } }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -614,28 +2569,6 @@ "node": ">=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" - } - }, - "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/follow-redirects": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", @@ -644,6 +2577,50 @@ "node": ">=4.0" } }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", @@ -653,8 +2630,7 @@ "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 + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/fsevents": { "version": "2.1.3", @@ -669,6 +2645,21 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.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", @@ -678,11 +2669,52 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -695,25 +2727,61 @@ "node": "*" } }, - "node_modules/glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=4" } }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true, "engines": { - "node": ">=4.x" + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" } }, "node_modules/has-flag": { @@ -724,13 +2792,137 @@ "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==", + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/husky": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-5.2.0.tgz", + "integrity": "sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/typicode" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/husky" + } + ], "bin": { - "he": "bin/he" + "husky": "lib/bin.js" + }, + "engines": { + "node": ">= 10" } }, "node_modules/iconv-lite": { @@ -744,11 +2936,35 @@ "node": ">=0.10.0" } }, + "node_modules/import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, "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" @@ -757,8 +2973,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/inquirer": { "version": "7.3.3", @@ -783,23 +2998,104 @@ "node": ">=8.0.0" } }, - "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==", + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "optional": true, + "bin": { + "is-docker": "cli.js" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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=", + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, "engines": { "node": ">=0.10.0" } @@ -812,16 +3108,13 @@ "node": ">=8" } }, - "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==", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, "node_modules/is-number": { @@ -833,21 +3126,946 @@ "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==", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true, "engines": { "node": ">=8" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "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/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-cli/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/jest-cli/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-cli/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "fsevents": "^2.1.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "dependencies": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "bin": { + "jest-runtime": "bin/jest-runtime.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runtime/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/jest-runtime/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-runtime/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate/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/jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "node_modules/js-yaml": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", @@ -861,6 +4079,141 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/jsdom": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.2.tgz", + "integrity": "sha512-JxNtPt9C1ut85boCbJmffaQ06NBnzkQY/MWO3YxPW8IWS38A26z+B1oBvA9LwKrytewdfymnhi4UNH3/RAgZrg==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.1.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.4", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/lavacord": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/lavacord/-/lavacord-1.1.9.tgz", @@ -874,47 +4227,127 @@ "npm": ">=5" } }, - "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==", + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" }, "engines": { - "node": ">=10" + "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "node_modules/lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, - "node_modules/log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "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==", "dev": true, "dependencies": { - "chalk": "^4.0.0" + "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "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/makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "dependencies": { + "tmpl": "1.0.x" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/map-stream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", "dev": true }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mime-db": { "version": "1.45.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", @@ -946,7 +4379,6 @@ "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" }, @@ -954,71 +4386,35 @@ "node": "*" } }, - "node_modules/mocha": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", - "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.14.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/mocha/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" - } - }, - "node_modules/mocha/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==", + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, - "node_modules/mocha/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==", + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/moment": { @@ -1039,18 +4435,40 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "node_modules/nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "engines": { - "node": "^10 || ^12 || >=13.7" + "node": ">=0.10.0" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "node_modules/node-cleanup": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", @@ -1065,6 +4483,79 @@ "node": "4.x || >=6.0.0" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node_modules/node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-notifier": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", + "dev": true, + "optional": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-releases": { + "version": "1.1.71", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", + "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1074,11 +4565,146 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "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" } @@ -1094,6 +4720,23 @@ "node": ">=6" } }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -1102,28 +4745,25 @@ "node": ">=0.10.0" } }, - "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==", + "node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, "engines": { - "node": ">=10" + "node": ">=8" + }, + "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==", + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, "engines": { - "node": ">=10" + "node": ">=4" } }, "node_modules/p-try": { @@ -1135,6 +4775,39 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1148,7 +4821,6 @@ "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" } @@ -1162,6 +4834,12 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -1171,6 +4849,12 @@ "through": "~2.3" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, "node_modules/picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -1180,6 +4864,100 @@ "node": ">=8.6" } }, + "node_modules/pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "dependencies": { + "node-modules-regexp": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", @@ -1192,11 +4970,39 @@ "node": ">=10.13.0" } }, + "node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/prism-media": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.3.tgz", "integrity": "sha512-fSrR66n0l6roW9Rx4rSLMyTPTjRTiXy5RVqDOurACQ6si1rKHHKDU5gwBJoCsIV0R3o9gi+K50akl/qyw1C74A==" }, + "node_modules/prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ps-tree": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", @@ -1212,25 +5018,283 @@ "node": ">= 0.10" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "dependencies": { - "safe-buffer": "^5.1.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" }, "engines": { - "node": ">=8.10.0" + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "bin": { + "uuid": "bin/uuid" } }, "node_modules/require-directory": { @@ -1248,6 +5312,47 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -1260,6 +5365,39 @@ "node": ">=8" } }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, + "engines": { + "node": "6.* || >= 7.*" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -1285,18 +5423,332 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, "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/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "node_modules/sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", "dev": true, "dependencies": { - "randombytes": "^2.1.0" + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "bin": { + "sane": "src/cli.js" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/sane/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/sane/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/sane/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sane/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sane/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sane/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/sane/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/sane/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, "node_modules/set-blocking": { @@ -1305,6 +5757,42 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -1331,11 +5819,230 @@ "node": ">=8" } }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, "node_modules/signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1345,6 +6052,19 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, "node_modules/source-map-support": { "version": "0.5.19", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", @@ -1355,6 +6075,44 @@ "source-map": "^0.6.0" } }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, "node_modules/split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", @@ -1367,12 +6125,181 @@ "node": "*" } }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", @@ -1391,6 +6318,19 @@ "node": ">=0.6.19" } }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -1415,15 +6355,33 @@ "node": ">=8" } }, - "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==", + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "engines": { "node": ">=8" } }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -1435,6 +6393,61 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -1451,6 +6464,60 @@ "node": ">=0.6.0" } }, + "node_modules/tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1463,11 +6530,91 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-jest": { + "version": "26.5.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.4.tgz", + "integrity": "sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^26.1.0", + "json5": "2.x", + "lodash": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "jest": ">=26 <27", + "typescript": ">=3.8 <5.0" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "arg": "^4.1.0", "create-require": "^1.1.0", @@ -1510,11 +6657,44 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", @@ -1523,6 +6703,15 @@ "node": ">=8" } }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, "node_modules/typescript": { "version": "3.9.9", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", @@ -1536,6 +6725,237 @@ "node": ">=4.2.0" } }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.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==", + "dev": true, + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz", + "integrity": "sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "dependencies": { + "makeerror": "1.0.x" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz", + "integrity": "sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1557,393 +6977,124 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "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/wide-align/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true, "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/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/wide-align/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/wide-align/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/workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/wrap-ansi/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/wrap-ansi/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } }, "node_modules/ws": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", - "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==", "engines": { "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "node_modules/y18n": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, - "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "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-unparser/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" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { + "node_modules/yallist": { "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" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/yargs/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/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/yargs/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, + "optional": true, + "peer": 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" - } } }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, + "@babel/compat-data": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.12.tgz", + "integrity": "sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ==", + "dev": true + }, "@babel/core": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", - "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.14.tgz", + "integrity": "sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.10", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.9", + "@babel/helper-compilation-targets": "^7.13.13", + "@babel/helper-module-transforms": "^7.13.14", + "@babel/helpers": "^7.13.10", + "@babel/parser": "^7.13.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.13", + "@babel/types": "^7.13.14", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", + "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "lodash": "^4.17.19", - "semver": "^5.4.1", + "semver": "^6.3.0", "source-map": "^0.5.0" }, "dependencies": { @@ -1956,12 +7107,12 @@ } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", + "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.13.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -1974,104 +7125,115 @@ } } }, - "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "@babel/helper-compilation-targets": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz", + "integrity": "sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/compat-data": "^7.13.12", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.13.12" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.13.12" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", + "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.13", + "@babel/types": "^7.13.14" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", "dev": true }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", + "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.12" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.13.12" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -2080,24 +7242,30 @@ "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", "dev": true }, + "@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "dev": true + }, "@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", + "integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", "dev": true, "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" } }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -2155,9 +7323,9 @@ } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", + "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -2179,12 +7347,12 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-import-meta": { @@ -2260,46 +7428,45 @@ } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", - "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz", + "integrity": "sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.9", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.13.13", + "@babel/types": "^7.13.13", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", + "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -2391,9 +7558,9 @@ } }, "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, "@jest/console": { @@ -2626,9 +7793,9 @@ } }, "@types/babel__core": { - "version": "7.1.12", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", - "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "version": "7.1.14", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", + "integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -2658,9 +7825,9 @@ } }, "@types/babel__traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", - "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz", + "integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -2682,9 +7849,9 @@ } }, "@types/graceful-fs": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", - "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", "dev": true, "requires": { "@types/node": "*" @@ -2725,9 +7892,9 @@ } }, "@types/jest": { - "version": "26.0.20", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", - "integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", + "version": "26.0.22", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz", + "integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==", "dev": true, "requires": { "jest-diff": "^26.0.0", @@ -2735,9 +7902,9 @@ } }, "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, "@types/ms": { @@ -2759,9 +7926,9 @@ "dev": true }, "@types/prettier": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.6.tgz", - "integrity": "sha512-6gOkRe7OIioWAXfnO/2lFiv+SJichKVSys1mSsgyrYHSEjk8Ctv4tSR/Odvnu+HWlH2C8j53dahU03XmQdd5fA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", + "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", "dev": true }, "@types/stack-utils": { @@ -2789,9 +7956,9 @@ } }, "@types/yargs": { - "version": "15.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", - "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2818,9 +7985,9 @@ } }, "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", "dev": true }, "acorn-globals": { @@ -2831,6 +7998,14 @@ "requires": { "acorn": "^7.1.1", "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } } }, "acorn-walk": { @@ -2883,6 +8058,14 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "optional": true, + "peer": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3067,35 +8250,6 @@ "requires": { "is-descriptor": "^1.0.0" } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } } } }, @@ -3140,6 +8294,19 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, + "browserslist": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001181", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.649", + "escalade": "^3.1.1", + "node-releases": "^1.1.70" + } + }, "bs-logger": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", @@ -3193,6 +8360,12 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "caniuse-lite": { + "version": "1.0.30001204", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz", + "integrity": "sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ==", + "dev": true + }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -3260,6 +8433,63 @@ "requires": { "is-descriptor": "^0.1.0" } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -3311,6 +8541,12 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3359,6 +8595,14 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "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, + "optional": true, + "peer": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3468,37 +8712,6 @@ "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } } }, "delayed-stream": { @@ -3512,6 +8725,14 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "optional": true, + "peer": true + }, "diff-sequences": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", @@ -3577,6 +8798,12 @@ "safer-buffer": "^2.1.0" } }, + "electron-to-chromium": { + "version": "1.3.702", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.702.tgz", + "integrity": "sha512-qJVUKFWQnF6wP7MmTngDkmm8/KPzaiTXNFOAg5j7DSa6J7kPou7mTBqC8jpUOxauQWwHR3pn4dMRdV8IE1xdtA==", + "dev": true + }, "emittery": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", @@ -3606,19 +8833,25 @@ "is-arrayish": "^0.2.1" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "requires": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" @@ -3631,9 +8864,9 @@ "dev": true }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", "dev": true }, "esutils": { @@ -3663,69 +8896,26 @@ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" }, "exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", "dev": true }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" } }, "exit": { @@ -3776,6 +8966,69 @@ "is-extendable": "^0.1.0" } }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -3812,17 +9065,6 @@ "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } } }, "external-editor": { @@ -3869,34 +9111,11 @@ "is-extendable": "^0.1.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true } } }, @@ -4030,9 +9249,9 @@ "dev": true }, "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -4073,9 +9292,9 @@ "dev": true }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "growly": { @@ -4206,9 +9425,9 @@ "dev": true }, "husky": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/husky/-/husky-5.0.6.tgz", - "integrity": "sha512-SM+evfvcHT3rAYJKvPlatz3L5RqzgeM6xIvDjhs8VuhKj6iKqFDOt/Ov8sPjvWuE4FDB385gJBwWXRj7G3c1hg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-5.2.0.tgz", + "integrity": "sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg==", "dev": true }, "iconv-lite": { @@ -4269,30 +9488,13 @@ "through": "^2.3.6" } }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "kind-of": "^6.0.0" } }, "is-arrayish": { @@ -4326,42 +9528,23 @@ } }, "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "kind-of": "^6.0.0" } }, "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-docker": { @@ -4372,10 +9555,13 @@ "optional": true }, "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -4410,9 +9596,9 @@ "dev": true }, "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, "is-typedarray": { @@ -4477,14 +9663,6 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "istanbul-lib-report": { @@ -4528,6 +9706,38 @@ "@jest/core": "^26.6.3", "import-local": "^3.0.2", "jest-cli": "^26.6.3" + } + }, + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + } + }, + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" }, "dependencies": { "cliui": { @@ -4551,27 +9761,6 @@ "path-exists": "^4.0.0" } }, - "jest-cli": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", - "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", - "dev": true, - "requires": { - "@jest/core": "^26.6.3", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.6.3", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "prompts": "^2.0.1", - "yargs": "^15.4.1" - } - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4641,60 +9830,6 @@ } } }, - "jest-changed-files": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", - "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "execa": "^4.0.0", - "throat": "^5.0.0" - }, - "dependencies": { - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - } - } - }, "jest-config": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", @@ -4891,7 +10026,8 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true + "dev": true, + "requires": {} }, "jest-regex-util": { "version": "26.0.0", @@ -5114,9 +10250,9 @@ }, "dependencies": { "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -5209,36 +10345,36 @@ "dev": true }, "jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.2.tgz", + "integrity": "sha512-JxNtPt9C1ut85boCbJmffaQ06NBnzkQY/MWO3YxPW8IWS38A26z+B1oBvA9LwKrytewdfymnhi4UNH3/RAgZrg==", "dev": true, "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", + "abab": "^2.0.5", + "acorn": "^8.1.0", "acorn-globals": "^6.0.0", "cssom": "^0.4.4", - "cssstyle": "^2.2.0", + "cssstyle": "^2.3.0", "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", + "decimal.js": "^10.2.1", "domexception": "^2.0.1", - "escodegen": "^1.14.1", + "escodegen": "^2.0.0", "html-encoding-sniffer": "^2.0.1", "is-potential-custom-element-name": "^1.0.0", "nwsapi": "^2.2.0", - "parse5": "5.1.1", + "parse5": "6.0.1", "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", + "tough-cookie": "^4.0.0", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", + "whatwg-url": "^8.5.0", + "ws": "^7.4.4", "xml-name-validator": "^3.0.0" } }, @@ -5273,9 +10409,9 @@ "dev": true }, "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -5341,18 +10477,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -5369,14 +10493,6 @@ "dev": true, "requires": { "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "make-error": { @@ -5471,17 +10587,6 @@ "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } } }, "mkdirp": { @@ -5560,9 +10665,9 @@ "dev": true }, "node-notifier": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", - "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", "dev": true, "optional": true, "requires": { @@ -5575,9 +10680,9 @@ }, "dependencies": { "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "optional": true, "requires": { @@ -5586,6 +10691,12 @@ } } }, + "node-releases": { + "version": "1.1.71", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", + "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "dev": true + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -5596,6 +10707,14 @@ "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "normalize-path": { @@ -5605,20 +10724,12 @@ "dev": true }, "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "path-key": "^2.0.0" - }, - "dependencies": { - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - } + "path-key": "^3.0.0" } }, "nwsapi": { @@ -5653,6 +10764,43 @@ "is-descriptor": "^0.1.0" } }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -5712,11 +10860,6 @@ "word-wrap": "~1.2.3" } }, - "os": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz", - "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=" - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -5753,9 +10896,9 @@ } }, "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "pascalcase": { @@ -5948,9 +11091,9 @@ "dev": true }, "react-is": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", - "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, "read-pkg": { @@ -6148,12 +11291,12 @@ "dev": true }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { - "is-core-module": "^2.1.0", + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -6297,6 +11440,34 @@ } } }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -6320,6 +11491,21 @@ } } }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -6340,6 +11526,12 @@ } } }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -6370,6 +11562,42 @@ "remove-trailing-separator": "^1.0.1" } }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -6379,6 +11607,15 @@ "is-number": "^3.0.0", "repeat-string": "^1.6.1" } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, @@ -6392,9 +11629,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "set-blocking": { @@ -6423,6 +11660,12 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true } } }, @@ -6513,6 +11756,69 @@ "is-extendable": "^0.1.0" } }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -6546,35 +11852,6 @@ "requires": { "is-descriptor": "^1.0.0" } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } } } }, @@ -6628,9 +11905,9 @@ } }, "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "dev": true }, "spdx-correct": { @@ -6749,6 +12026,63 @@ "requires": { "is-descriptor": "^0.1.0" } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -6774,9 +12108,9 @@ "dev": true }, "string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "requires": { "char-regex": "^1.0.2", @@ -6937,14 +12271,14 @@ } }, "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "dev": true, "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" } }, "tr46": { @@ -6957,18 +12291,17 @@ } }, "ts-jest": { - "version": "26.4.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.4.tgz", - "integrity": "sha512-3lFWKbLxJm34QxyVNNCgXX1u4o/RV0myvA2y2Bxm46iGIjKlaY0own9gIckbjZJPn+WaJEnfPPJ20HHGpoq4yg==", + "version": "26.5.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.4.tgz", + "integrity": "sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg==", "dev": true, "requires": { - "@types/jest": "26.x", "bs-logger": "0.x", "buffer-from": "1.x", "fast-json-stable-stringify": "2.x", "jest-util": "^26.1.0", "json5": "2.x", - "lodash.memoize": "4.x", + "lodash": "4.x", "make-error": "1.x", "mkdirp": "1.x", "semver": "7.x", @@ -6976,22 +12309,38 @@ }, "dependencies": { "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", "dev": true } } }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, "tsc-watch": { "version": "4.2.9", "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-4.2.9.tgz", @@ -7069,8 +12418,22 @@ "get-value": "^2.0.6", "is-extendable": "^0.1.1", "set-value": "^2.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + } } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -7140,9 +12503,9 @@ "optional": true }, "v8-to-istanbul": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz", - "integrity": "sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz", + "integrity": "sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.1", @@ -7228,12 +12591,12 @@ "dev": true }, "whatwg-url": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", - "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz", + "integrity": "sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==", "dev": true, "requires": { - "lodash.sortby": "^4.7.0", + "lodash": "^4.7.0", "tr46": "^2.0.2", "webidl-conversions": "^6.1.0" } @@ -7277,9 +12640,10 @@ } }, "ws": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", - "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==", + "requires": {} }, "xml-name-validator": { "version": "3.0.0", @@ -7304,6 +12668,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "optional": true, + "peer": true } } } diff --git a/src/commands/admin.ts b/src/commands/admin.ts index 0240dbd..e213204 100644 --- a/src/commands/admin.ts +++ b/src/commands/admin.ts @@ -1,9 +1,10 @@ import Command from "../core/command"; -import {CommonLibrary, logs, botHasPermission, clean} from "../core/lib"; +import {CommonLibrary, botHasPermission, clean} from "../core/lib"; import {Config, Storage} from "../core/structures"; import {PermissionNames, getPermissionLevel} from "../core/permissions"; import {Permissions} from "discord.js"; import * as discord from "discord.js"; +import {logs} from "../globals"; function getLogBuffer(type: string) { return { diff --git a/src/commands/fun/subcommands/eco-utils.ts b/src/commands/fun/subcommands/eco-utils.ts index 8ea5176..554b5bf 100644 --- a/src/commands/fun/subcommands/eco-utils.ts +++ b/src/commands/fun/subcommands/eco-utils.ts @@ -62,7 +62,7 @@ export function getSendEmbed(sender: User, receiver: User, amount: number): obje } export function isAuthorized(guild: Guild | null, channel: TextChannel | DMChannel | NewsChannel): boolean { - if (guild?.id === "637512823676600330" && channel?.id === "669464416420364288" || process.argv[2] === "dev") return true; + if ((guild?.id === "637512823676600330" && channel?.id === "669464416420364288") || IS_DEV_MODE) return true; else { channel.send("Sorry, this command can only be used in Monika's emote server. (#mon-stocks)"); return false; diff --git a/src/commands/help.ts b/src/commands/help.ts index 4a6ff2f..dd74ad4 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -19,7 +19,9 @@ export default new Command({ const command = commands.get(header); if (!command) - return $.warn(`Command "${header}" of category "${category}" unexpectedly doesn't exist!`); + return console.warn( + `Command "${header}" of category "${category}" unexpectedly doesn't exist!` + ); output += `\n- \`${header}\`: ${command.description}`; } @@ -37,7 +39,7 @@ export default new Command({ if (!command || header === "test") return $.channel.send(`No command found by the name \`${header}\`!`); if (command.originalCommandName) header = command.originalCommandName; - else $.warn(`originalCommandName isn't defined for ${header}?!`); + else console.warn(`originalCommandName isn't defined for ${header}?!`); let permLevel = command.permission ?? Command.PERMISSIONS.NONE; let usage = command.usage; @@ -48,7 +50,7 @@ export default new Command({ for (const [category, headers] of categories) { if (headers.includes(header)) { if (selectedCategory !== "Unknown") - $.warn( + console.warn( `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.` ); else selectedCategory = $(category).toTitleCase(); diff --git a/src/commands/scanemotes.ts b/src/commands/scanemotes.ts index 1ad5cd8..f93a949 100644 --- a/src/commands/scanemotes.ts +++ b/src/commands/scanemotes.ts @@ -131,7 +131,7 @@ export default new Command({ continueReactionLoop = false; if (reaction.count !== userReactions + botReactions) { - $.warn( + console.warn( `[Channel: ${channel.id}, Message: ${msg.id}] A reaction count of ${reaction.count} was expected but was given ${userReactions} user reactions and ${botReactions} bot reactions.` ); warnings++; @@ -161,7 +161,7 @@ export default new Command({ "y" )}.` ); - $.log(`Finished operation in ${finishTime - startTime} ms.`); + console.log(`Finished operation in ${finishTime - startTime} ms.`); $.channel.stopTyping(); // Display stats on emote usage. diff --git a/src/core/command.ts b/src/core/command.ts index fd4eb64..ae2e73c 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,4 +1,4 @@ -import $, {isType, parseVars, CommonLibrary} from "./lib"; +import {isType, parseVars, CommonLibrary} from "./lib"; import {Collection} from "discord.js"; import {PERMISSIONS} from "./permissions"; import {getPrefix} from "../core/structures"; @@ -68,11 +68,11 @@ export default class Command { for (const alias of aliases) { if (baseSubcommands.includes(alias)) - $.warn( + console.warn( `"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)` ); else if (this.subcommands.has(alias)) - $.warn( + console.warn( `Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)` ); else this.subcommands.set(alias, subcmd); @@ -81,17 +81,17 @@ export default class Command { } if (this.user && this.user.aliases.length > 0) - $.warn( + console.warn( `There are aliases defined for a "user"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` ); if (this.number && this.number.aliases.length > 0) - $.warn( + console.warn( `There are aliases defined for a "number"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` ); if (this.any && this.any.aliases.length > 0) - $.warn( + console.warn( `There are aliases defined for an "any"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` ); } @@ -179,7 +179,7 @@ export const loadableCommands = (async () => { command.originalCommandName = commandName; if (commands.has(commandName)) { - $.warn( + console.warn( `Command "${commandName}" already exists! Make sure to make each command uniquely identifiable across categories!` ); } else { @@ -188,7 +188,7 @@ export const loadableCommands = (async () => { for (const alias of command.aliases) { if (commands.has(alias)) { - $.warn( + console.warn( `Top-level alias "${alias}" from command "${commandID}" already exists either as a command or alias!` ); } else { @@ -199,9 +199,9 @@ export const loadableCommands = (async () => { if (!(category in lists)) lists[category] = []; lists[category].push(commandName); - $.log(`Loading Command: ${commandID}`); + console.log(`Loading Command: ${commandID}`); } else { - $.warn(`Command "${commandID}" has no default export which is a Command instance!`); + console.warn(`Command "${commandID}" has no default export which is a Command instance!`); } } } diff --git a/src/core/event.ts b/src/core/event.ts index c5b2979..d2b6f9a 100644 --- a/src/core/event.ts +++ b/src/core/event.ts @@ -1,6 +1,5 @@ import {Client, ClientEvents, Constants} from "discord.js"; import Storage from "./storage"; -import $ from "./lib"; interface EventOptions { readonly on?: (...args: ClientEvents[K]) => void; @@ -30,9 +29,9 @@ export async function loadEvents(client: Client) { if ((Object.values(Constants.Events) as string[]).includes(header)) { event.attach(client, header); - $.log(`Loading Event: ${header}`); + console.log(`Loading Event: ${header}`); } else - $.warn( + console.warn( `"${header}" is not a valid event type! Did you misspell it? (Note: If you fixed the issue, delete "dist" because the compiler won't automatically delete any extra files.)` ); } diff --git a/src/core/lib.ts b/src/core/lib.ts index 31d9b37..d2e7d5b 100644 --- a/src/core/lib.ts +++ b/src/core/lib.ts @@ -1,6 +1,5 @@ import {GenericWrapper, NumberWrapper, StringWrapper, ArrayWrapper} from "./wrappers"; import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember, Permissions} from "discord.js"; -import chalk from "chalk"; import {get} from "https"; import FileManager from "./storage"; import {eventListeners} from "../events/messageReactionRemove"; @@ -19,11 +18,6 @@ export interface CommonLibrary { // Common Library Functions // /** .catch($.handler.bind($)) or .catch(error => $.handler(error)) */ handler: (error: Error) => void; - log: (...args: any[]) => void; - warn: (...args: any[]) => void; - error: (...args: any[]) => void; - debug: (...args: any[]) => void; - ready: (...args: any[]) => void; paginate: ( message: Message, senderID: string, @@ -79,94 +73,13 @@ $.handler = function (this: CommonLibrary, error: Error) { `There was an error while trying to execute that command!\`\`\`${error.stack ?? error}\`\`\`` ); else - $.warn( + console.warn( "No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!" ); - $.error(error); + console.error(error); }; -// Logs with different levels of verbosity. -export const logs: {[type: string]: string} = { - error: "", - warn: "", - info: "", - verbose: "" -}; - -let enabled = true; - -export function setConsoleActivated(activated: boolean) { - enabled = activated; -} - -// The custom console. In order of verbosity, error, warn, log, and debug. Ready is a variation of log. -// General Purpose Logger -$.log = (...args: any[]) => { - if (enabled) console.log(chalk.white.bgGray(formatTimestamp()), chalk.black.bgWhite("INFO"), ...args); - - const text = `[${formatUTCTimestamp()}] [INFO] ${args.join(" ")}\n`; - logs.info += text; - logs.verbose += text; -}; -// "It'll still work, but you should really check up on this." -$.warn = (...args: any[]) => { - if (enabled) console.warn(chalk.white.bgGray(formatTimestamp()), chalk.black.bgYellow("WARN"), ...args); - - const text = `[${formatUTCTimestamp()}] [WARN] ${args.join(" ")}\n`; - logs.warn += text; - logs.info += text; - logs.verbose += text; -}; -// Used for anything which prevents the program from actually running. -$.error = (...args: any[]) => { - if (enabled) console.error(chalk.white.bgGray(formatTimestamp()), chalk.white.bgRed("ERROR"), ...args); - - const text = `[${formatUTCTimestamp()}] [ERROR] ${args.join(" ")}\n`; - logs.error += text; - logs.warn += text; - logs.info += text; - logs.verbose += text; -}; -// Be as verbose as possible. If anything might help when debugging an error, then include it. This only shows in your console if you run this with "dev", but you can still get it from "logs.verbose". -// $.debug(`core/lib::parseArgs("testing \"in progress\"") = ["testing", "in progress"]`) --> /::(.)() = -// Would probably be more suited for debugging program logic rather than function logic, which can be checked using unit tests. -$.debug = (...args: any[]) => { - if (process.argv[2] === "dev" && enabled) - console.debug(chalk.white.bgGray(formatTimestamp()), chalk.white.bgBlue("DEBUG"), ...args); - - const text = `[${formatUTCTimestamp()}] [DEBUG] ${args.join(" ")}\n`; - logs.verbose += text; -}; -// Used once at the start of the program when the bot loads. -$.ready = (...args: any[]) => { - if (enabled) console.log(chalk.white.bgGray(formatTimestamp()), chalk.black.bgGreen("READY"), ...args); - - const text = `[${formatUTCTimestamp()}] [READY] ${args.join(" ")}\n`; - logs.info += text; - logs.verbose += text; -}; - -export function formatTimestamp(now = new Date()) { - const year = now.getFullYear(); - const month = (now.getMonth() + 1).toString().padStart(2, "0"); - const day = now.getDate().toString().padStart(2, "0"); - const hour = now.getHours().toString().padStart(2, "0"); - const minute = now.getMinutes().toString().padStart(2, "0"); - const second = now.getSeconds().toString().padStart(2, "0"); - return `${year}-${month}-${day} ${hour}:${minute}:${second}`; -} - -export function formatUTCTimestamp(now = new Date()) { - const year = now.getUTCFullYear(); - const month = (now.getUTCMonth() + 1).toString().padStart(2, "0"); - const day = now.getUTCDate().toString().padStart(2, "0"); - const hour = now.getUTCHours().toString().padStart(2, "0"); - const minute = now.getUTCMinutes().toString().padStart(2, "0"); - const second = now.getUTCSeconds().toString().padStart(2, "0"); - return `${year}-${month}-${day} ${hour}:${minute}:${second}`; -} - export function botHasPermission(guild: Guild | null, permission: number): boolean { return !!guild?.me?.hasPermission(permission); } diff --git a/src/core/permissions.ts b/src/core/permissions.ts index 06f9a5b..336ffb5 100644 --- a/src/core/permissions.ts +++ b/src/core/permissions.ts @@ -1,6 +1,5 @@ import {GuildMember, Permissions} from "discord.js"; import {Config} from "./structures"; -import $ from "./lib"; export enum PERMISSIONS { NONE, @@ -70,7 +69,7 @@ export function getPermissionLevel(member: GuildMember): number { // By transitive property, lenNames and lenChecker have to be equal to each other as well. if (length !== lenNames || length !== lenChecker) - $.error( + console.error( `Permission object lengths aren't equal! Enum Length (${length}), Names Length (${lenNames}), and Functions Length (${lenChecker}). This WILL cause problems!` ); })(); diff --git a/src/core/storage.ts b/src/core/storage.ts index a28e665..8553fc6 100644 --- a/src/core/storage.ts +++ b/src/core/storage.ts @@ -1,5 +1,4 @@ import fs from "fs"; -import $ from "./lib"; const Storage = { read(header: string): object { @@ -14,7 +13,7 @@ const Storage = { data = JSON.parse(file); } catch (error) { if (process.argv[2] !== "dev") { - $.warn(`Malformed JSON data (header: ${header}), backing it up.`, file); + console.warn(`Malformed JSON data (header: ${header}), backing it up.`, file); fs.writeFile( `${path}.backup`, file, @@ -30,7 +29,7 @@ const Storage = { this.open("data"); const path = `data/${header}.json`; - if (process.argv[2] === "dev" || header === "config") { + if (IS_DEV_MODE || header === "config") { const result = JSON.stringify(data, null, "\t"); if (asynchronous) @@ -60,8 +59,8 @@ const Storage = { export function generateHandler(message: string) { return (error: Error | null) => { - if (error) $.error(error); - else $.debug(message); + if (error) console.error(error); + else console.debug(message); }; } diff --git a/src/core/structures.ts b/src/core/structures.ts index 37b2194..74e5adf 100644 --- a/src/core/structures.ts +++ b/src/core/structures.ts @@ -1,5 +1,5 @@ import FileManager from "./storage"; -import $, {select, GenericJSON, GenericStructure} from "./lib"; +import {select, GenericJSON, GenericStructure} from "./lib"; import {watch} from "fs"; import {Guild as DiscordGuild, Snowflake} from "discord.js"; @@ -63,7 +63,7 @@ class StorageStructure extends GenericStructure { /** Gets a user's profile if they exist and generate one if not. */ public getUser(id: string): User { if (!/\d{17,19}/g.test(id)) - $.warn(`"${id}" is not a valid user ID! It will be erased when the data loads again.`); + console.warn(`"${id}" is not a valid user ID! It will be erased when the data loads again.`); if (id in this.users) return this.users[id]; else { @@ -76,7 +76,7 @@ class StorageStructure extends GenericStructure { /** Gets a guild's settings if they exist and generate one if not. */ public getGuild(id: string): Guild { if (!/\d{17,19}/g.test(id)) - $.warn(`"${id}" is not a valid guild ID! It will be erased when the data loads again.`); + console.warn(`"${id}" is not a valid guild ID! It will be erased when the data loads again.`); if (id in this.guilds) return this.guilds[id]; else { @@ -93,9 +93,9 @@ export let Storage = new StorageStructure(FileManager.read("storage")); // This part will allow the user to manually edit any JSON files they want while the program is running which'll update the program's cache. // However, fs.watch is a buggy mess that should be avoided in production. While it helps test out stuff for development, it's not a good idea to have it running outside of development as it causes all sorts of issues. -if (process.argv[2] === "dev") { +if (IS_DEV_MODE) { watch("data", (event, filename) => { - $.debug("File Watcher:", event, filename); + console.debug("File Watcher:", event, filename); const header = filename.substring(0, filename.indexOf(".json")); switch (header) { diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts index 640cd51..47c2aa3 100644 --- a/src/events/channelCreate.ts +++ b/src/events/channelCreate.ts @@ -1,6 +1,5 @@ import Event from "../core/event"; import {client} from "../index"; -import $ from "../core/lib"; import * as discord from "discord.js"; export default new Event<"channelCreate">({ @@ -8,7 +7,7 @@ export default new Event<"channelCreate">({ const botGuilds = client.guilds; if (channel instanceof discord.GuildChannel) { const createdGuild = await botGuilds.fetch(channel.guild.id); - $.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`); + console.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`); } } }); diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts index 48fa2df..ac835ed 100644 --- a/src/events/channelDelete.ts +++ b/src/events/channelDelete.ts @@ -1,6 +1,5 @@ import Event from "../core/event"; import {client} from "../index"; -import $ from "../core/lib"; import * as discord from "discord.js"; export default new Event<"channelDelete">({ @@ -8,7 +7,7 @@ export default new Event<"channelDelete">({ const botGuilds = client.guilds; if (channel instanceof discord.GuildChannel) { const createdGuild = await botGuilds.fetch(channel.guild.id); - $.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`); + console.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`); } } }); diff --git a/src/events/emojiCreate.ts b/src/events/emojiCreate.ts index dffd9aa..2af3f82 100644 --- a/src/events/emojiCreate.ts +++ b/src/events/emojiCreate.ts @@ -1,10 +1,9 @@ import Event from "../core/event"; -import $ from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib"; export default new Event<"emojiCreate">({ on(emote) { - $.log(`Updated emote registry. ${emote.name}`); + console.log(`Updated emote registry. ${emote.name}`); updateGlobalEmoteRegistry(); } }); diff --git a/src/events/emojiDelete.ts b/src/events/emojiDelete.ts index a3d5d43..08687c9 100644 --- a/src/events/emojiDelete.ts +++ b/src/events/emojiDelete.ts @@ -1,10 +1,9 @@ import Event from "../core/event"; -import $ from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib"; export default new Event<"emojiDelete">({ on() { - $.log("Updated emote registry."); + console.log("Updated emote registry."); updateGlobalEmoteRegistry(); } }); diff --git a/src/events/emojiUpdate.ts b/src/events/emojiUpdate.ts index a7a8e11..7dbe125 100644 --- a/src/events/emojiUpdate.ts +++ b/src/events/emojiUpdate.ts @@ -1,10 +1,9 @@ import Event from "../core/event"; -import $ from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib"; export default new Event<"emojiUpdate">({ on() { - $.log("Updated emote registry."); + console.log("Updated emote registry."); updateGlobalEmoteRegistry(); } }); diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts index 2820db7..61fdec5 100644 --- a/src/events/guildCreate.ts +++ b/src/events/guildCreate.ts @@ -1,10 +1,9 @@ import Event from "../core/event"; -import $ from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib"; export default new Event<"guildCreate">({ on() { - $.log("Updated emote registry."); + console.log("Updated emote registry."); updateGlobalEmoteRegistry(); } }); diff --git a/src/events/guildDelete.ts b/src/events/guildDelete.ts index 8fed108..a8e7eaa 100644 --- a/src/events/guildDelete.ts +++ b/src/events/guildDelete.ts @@ -1,10 +1,9 @@ import Event from "../core/event"; -import $ from "../core/lib"; import {updateGlobalEmoteRegistry} from "../core/lib"; export default new Event<"guildDelete">({ on() { - $.log("Updated emote registry."); + console.log("Updated emote registry."); updateGlobalEmoteRegistry(); } }); diff --git a/src/events/message.ts b/src/events/message.ts index 85a54c0..701db08 100644 --- a/src/events/message.ts +++ b/src/events/message.ts @@ -82,13 +82,13 @@ export default new Event<"message">({ ); } - $.log( + console.log( `${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".` ); // Subcommand Recursion // let command = commands.get(header); - if (!command) return $.warn(`Command "${header}" was called but for some reason it's still undefined!`); + if (!command) return console.warn(`Command "${header}" was called but for some reason it's still undefined!`); const params: any[] = []; let isEndpoint = false; let permLevel = command.permission ?? Command.PERMISSIONS.NONE; @@ -96,7 +96,7 @@ export default new Event<"message">({ for (let param of args) { if (command.endpoint) { if (command.subcommands.size > 0 || command.user || command.number || command.any) - $.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`); + console.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`); isEndpoint = true; break; } @@ -117,7 +117,7 @@ export default new Event<"message">({ } if (!message.member) - return $.warn("This command was likely called from a DM channel meaning the member object is null."); + return console.warn("This command was likely called from a DM channel meaning the member object is null."); if (!hasPermission(message.member, permLevel)) { const userPermLevel = getPermissionLevel(message.member); diff --git a/src/events/ready.ts b/src/events/ready.ts index e3fe021..9e2ec12 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -1,13 +1,12 @@ import Event from "../core/event"; import {client} from "../index"; -import $ from "../core/lib"; import {Config} from "../core/structures"; import {updateGlobalEmoteRegistry} from "../core/lib"; export default new Event<"ready">({ once() { if (client.user) { - $.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`); + console.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`); client.user.setActivity({ type: "LISTENING", name: `${Config.prefix}help` diff --git a/src/globals.ts b/src/globals.ts new file mode 100644 index 0000000..3c8d4c7 --- /dev/null +++ b/src/globals.ts @@ -0,0 +1,86 @@ +import chalk from "chalk"; + +declare global { + var IS_DEV_MODE: boolean; + + interface Console { + ready: (...data: any[]) => void; + } +} + +global.IS_DEV_MODE = process.argv[2] === "dev"; + +const oldConsole = console; + +export const logs: {[type: string]: string} = { + error: "", + warn: "", + info: "", + verbose: "" +}; + +function formatTimestamp(now = new Date()) { + const year = now.getFullYear(); + const month = (now.getMonth() + 1).toString().padStart(2, "0"); + const day = now.getDate().toString().padStart(2, "0"); + const hour = now.getHours().toString().padStart(2, "0"); + const minute = now.getMinutes().toString().padStart(2, "0"); + const second = now.getSeconds().toString().padStart(2, "0"); + return `${year}-${month}-${day} ${hour}:${minute}:${second}`; +} + +function formatUTCTimestamp(now = new Date()) { + const year = now.getUTCFullYear(); + const month = (now.getUTCMonth() + 1).toString().padStart(2, "0"); + const day = now.getUTCDate().toString().padStart(2, "0"); + const hour = now.getUTCHours().toString().padStart(2, "0"); + const minute = now.getUTCMinutes().toString().padStart(2, "0"); + const second = now.getUTCSeconds().toString().padStart(2, "0"); + return `${year}-${month}-${day} ${hour}:${minute}:${second}`; +} + +// The custom console. In order of verbosity, error, warn, log, and debug. Ready is a variation of log. +console = { + ...oldConsole, + // General Purpose Logger + log(...args: any[]) { + oldConsole.log(chalk.white.bgGray(formatTimestamp()), chalk.black.bgWhite("INFO"), ...args); + const text = `[${formatUTCTimestamp()}] [INFO] ${args.join(" ")}\n`; + logs.info += text; + logs.verbose += text; + }, + // "It'll still work, but you should really check up on this." + warn(...args: any[]) { + oldConsole.warn(chalk.white.bgGray(formatTimestamp()), chalk.black.bgYellow("WARN"), ...args); + const text = `[${formatUTCTimestamp()}] [WARN] ${args.join(" ")}\n`; + logs.warn += text; + logs.info += text; + logs.verbose += text; + }, + // Used for anything which prevents the program from actually running. + error(...args: any[]) { + oldConsole.error(chalk.white.bgGray(formatTimestamp()), chalk.white.bgRed("ERROR"), ...args); + const text = `[${formatUTCTimestamp()}] [ERROR] ${args.join(" ")}\n`; + logs.error += text; + logs.warn += text; + logs.info += text; + logs.verbose += text; + }, + // Be as verbose as possible. If anything might help when debugging an error, then include it. This only shows in your console if you run this with "dev", but you can still get it from "logs.verbose". + // $.debug(`core/lib::parseArgs("testing \"in progress\"") = ["testing", "in progress"]`) --> /::(.)() = + // Would probably be more suited for debugging program logic rather than function logic, which can be checked using unit tests. + debug(...args: any[]) { + if (IS_DEV_MODE) oldConsole.debug(chalk.white.bgGray(formatTimestamp()), chalk.white.bgBlue("DEBUG"), ...args); + const text = `[${formatUTCTimestamp()}] [DEBUG] ${args.join(" ")}\n`; + logs.verbose += text; + }, + // Used once at the start of the program when the bot loads. + ready(...args: any[]) { + oldConsole.log(chalk.white.bgGray(formatTimestamp()), chalk.black.bgGreen("READY"), ...args); + const text = `[${formatUTCTimestamp()}] [READY] ${args.join(" ")}\n`; + logs.info += text; + logs.verbose += text; + } +}; + +console.log("Loading globals..."); diff --git a/src/index.ts b/src/index.ts index aa0d155..426046c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import "./globals"; import * as discord from "discord.js"; import setup from "./setup"; import {Config} from "./core/structures"; diff --git a/src/modules/message_embed.ts b/src/modules/message_embed.ts index b69e76e..e8bdb6d 100644 --- a/src/modules/message_embed.ts +++ b/src/modules/message_embed.ts @@ -1,60 +1,61 @@ -import { client } from '..' -import { Message, TextChannel, APIMessage, MessageEmbed } from 'discord.js' -import { getPrefix } from '../core/structures' -import { DiscordAPIError } from 'discord.js' +import {client} from ".."; +import {Message, TextChannel, APIMessage, MessageEmbed} from "discord.js"; +import {getPrefix} from "../core/structures"; +import {DiscordAPIError} from "discord.js"; export default async function quote(message: Message) { - if (message.author.bot) return + if (message.author.bot) return; // const message_link_regex = message.content.match(/(!)?https?:\/\/\w+\.com\/channels\/(\d+)\/(\d+)\/(\d+)/) - const message_link_regex = message.content.match(/([?)/) + const message_link_regex = message.content.match( + /([?)/ + ); - if (message_link_regex == null) return - const [, char, guildID, channelID, messageID] = message_link_regex + if (message_link_regex == null) return; + const [, char, guildID, channelID, messageID] = message_link_regex; - if (char || message.content.startsWith(getPrefix(message.guild))) return + if (char || message.content.startsWith(getPrefix(message.guild))) return; try { - const channel = client.guilds.cache.get(guildID)?.channels.cache.get(channelID) as TextChannel - const link_message = await channel.messages.fetch(messageID) + const channel = client.guilds.cache.get(guildID)?.channels.cache.get(channelID) as TextChannel; + const link_message = await channel.messages.fetch(messageID); - let rtmsg: string | APIMessage = '' + let rtmsg: string | APIMessage = ""; if (link_message.cleanContent) { rtmsg = new APIMessage(message.channel as TextChannel, { content: link_message.cleanContent, - disableMentions: 'all', + disableMentions: "all", files: link_message.attachments.array() - }) + }); } - const embeds = [ - ...link_message.embeds.filter(v => v.type == 'rich'), - ...link_message.attachments.values() - ] + const embeds = [...link_message.embeds.filter((v) => v.type == "rich"), ...link_message.attachments.values()]; /// @ts-ignore if (!link_message.cleanContent && embeds.empty) { - const Embed = new MessageEmbed() - .setDescription('🚫 The message is empty.') - return message.channel.send(Embed) + const Embed = new MessageEmbed().setDescription("🚫 The message is empty."); + return message.channel.send(Embed); } const infoEmbed = new MessageEmbed() .setAuthor( link_message.author.username, - link_message.author.displayAvatarURL({format: 'png', dynamic: true, size: 4096})) + link_message.author.displayAvatarURL({format: "png", dynamic: true, size: 4096}) + ) .setTimestamp(link_message.createdTimestamp) - .setDescription(`${link_message.cleanContent}\n\nSent in **${link_message.guild?.name}** | <#${link_message.channel.id}> ([link](https://discord.com/channels/${guildID}/${channelID}/${messageID}))`); - if (link_message.attachments.size !== 0) { - const image = link_message.attachments.first(); - /// @ts-ignore - infoEmbed.setImage(image.url); - } + .setDescription( + `${link_message.cleanContent}\n\nSent in **${link_message.guild?.name}** | <#${link_message.channel.id}> ([link](https://discord.com/channels/${guildID}/${channelID}/${messageID}))` + ); + if (link_message.attachments.size !== 0) { + const image = link_message.attachments.first(); + /// @ts-ignore + infoEmbed.setImage(image.url); + } - await message.channel.send(infoEmbed) + await message.channel.send(infoEmbed); } catch (error) { if (error instanceof DiscordAPIError) { - message.channel.send("I don't have access to this channel, or something else went wrong.") + message.channel.send("I don't have access to this channel, or something else went wrong."); } - return console.error(error) + return console.error(error); } -} \ No newline at end of file +} diff --git a/src/setup.ts b/src/setup.ts index 7edde85..97dc6e6 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -2,12 +2,11 @@ import {existsSync as exists, readFileSync as read, writeFile as write} from "fs import inquirer from "inquirer"; import Storage, {generateHandler} from "./core/storage"; import {Config} from "./core/structures"; -import $, {setConsoleActivated} from "./core/lib"; // The template should be built with a reductionist mentality. // Provide everything the user needs and then let them remove whatever they want. // That way, they aren't focusing on what's missing, but rather what they need for their command. -if (process.argv[2] === "dev" && !exists("src/commands/test.ts")) { +if (IS_DEV_MODE && !exists("src/commands/test.ts")) { write( "src/commands/test.ts", read("src/commands/template.ts"), @@ -64,8 +63,19 @@ export default { }, /** Prompt the user to set their token again. */ async again() { - $.error("It seems that the token you provided is invalid."); - setConsoleActivated(false); + console.error("It seems that the token you provided is invalid."); + + // Deactivate the console // + const oldConsole = console; + console = { + ...oldConsole, + log() {}, + warn() {}, + error() {}, + debug() {}, + ready() {} + }; + const answers = await inquirer.prompt(prompts.slice(0, 1)); Config.token = answers.token as string; Config.save(false); From 51fa9457b4a50ed1390d06b6203e102fae6917e8 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 30 Mar 2021 05:25:07 -0500 Subject: [PATCH 05/30] Fully separated utility functions from command menu --- src/commands/admin.ts | 48 +- src/commands/fun/8ball.ts | 6 +- src/commands/fun/cookie.ts | 12 +- src/commands/fun/eco.ts | 3 +- src/commands/fun/neko.ts | 6 +- src/commands/fun/ok.ts | 3 +- src/commands/fun/owoify.ts | 4 +- src/commands/fun/poll.ts | 3 +- src/commands/fun/subcommands/eco-core.ts | 10 +- .../fun/subcommands/eco-shop-items.ts | 4 +- src/commands/fun/subcommands/eco-shop.ts | 9 +- src/commands/fun/subcommands/eco-utils.ts | 10 +- src/commands/help.ts | 20 +- src/commands/info.ts | 20 +- src/commands/scanemotes.ts | 23 +- src/commands/template.ts | 11 +- src/commands/utilities/desc.ts | 19 +- src/commands/utilities/emote.ts | 2 +- src/commands/utilities/lsemotes.ts | 33 +- src/commands/utilities/react.ts | 5 +- src/commands/utilities/say.ts | 3 +- src/commands/utilities/shorten.ts | 3 +- src/commands/utilities/time.ts | 7 +- src/core/command.ts | 42 +- src/core/{wrappers.test.ts => lib.test.ts} | 26 +- src/core/lib.ts | 510 ++---------------- src/core/libd.ts | 418 ++++++++++++++ src/core/structures.ts | 2 +- src/core/wrappers.ts | 73 --- src/events/emojiCreate.ts | 2 +- src/events/emojiDelete.ts | 2 +- src/events/emojiUpdate.ts | 2 +- src/events/guildCreate.ts | 2 +- src/events/guildDelete.ts | 2 +- src/events/message.ts | 26 +- src/events/messageReactionRemove.ts | 2 +- src/events/ready.ts | 2 +- 37 files changed, 677 insertions(+), 698 deletions(-) rename src/core/{wrappers.test.ts => lib.test.ts} (58%) create mode 100644 src/core/libd.ts delete mode 100644 src/core/wrappers.ts diff --git a/src/commands/admin.ts b/src/commands/admin.ts index e213204..9545799 100644 --- a/src/commands/admin.ts +++ b/src/commands/admin.ts @@ -1,5 +1,5 @@ -import Command from "../core/command"; -import {CommonLibrary, botHasPermission, clean} from "../core/lib"; +import Command, {handler} from "../core/command"; +import {botHasPermission, clean} from "../core/libd"; import {Config, Storage} from "../core/structures"; import {PermissionNames, getPermissionLevel} from "../core/permissions"; import {Permissions} from "discord.js"; @@ -23,11 +23,11 @@ const statuses = ["online", "idle", "dnd", "invisible"]; export default new Command({ description: "An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.", - async run($: CommonLibrary): Promise { - if (!$.member) - return $.channel.send( - "Couldn't find a member object for you! Did you make sure you used this in a server?" - ); + async run($) { + if (!$.member) { + $.channel.send("Couldn't find a member object for you! Did you make sure you used this in a server?"); + return; + } const permLevel = getPermissionLevel($.member); $.channel.send( `${$.author.toString()}, your permission level is \`${PermissionNames[permLevel]}\` (${permLevel}).` @@ -42,7 +42,7 @@ export default new Command({ prefix: new Command({ description: "Set a custom prefix for your guild. Removes your custom prefix if none is provided.", usage: "()", - async run($: CommonLibrary): Promise { + async run($) { Storage.getGuild($.guild?.id || "N/A").prefix = null; Storage.save(); $.channel.send( @@ -50,7 +50,7 @@ export default new Command({ ); }, any: new Command({ - async run($: CommonLibrary): Promise { + async run($) { Storage.getGuild($.guild?.id || "N/A").prefix = $.args[0]; Storage.save(); $.channel.send(`The custom prefix for this guild is now \`${$.args[0]}\`.`); @@ -62,12 +62,12 @@ export default new Command({ diag: new Command({ description: 'Requests a debug log with the "info" verbosity level.', permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { + async run($) { $.channel.send(getLogBuffer("info")); }, any: new Command({ description: `Select a verbosity to listen to. Available levels: \`[${Object.keys(logs).join(", ")}]\``, - async run($: CommonLibrary): Promise { + async run($) { const type = $.args[0]; if (type in logs) $.channel.send(getLogBuffer(type)); @@ -83,14 +83,16 @@ export default new Command({ status: new Command({ description: "Changes the bot's status.", permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { + async run($) { $.channel.send("Setting status to `online`..."); }, any: new Command({ description: `Select a status to set to. Available statuses: \`[${statuses.join(", ")}]\`.`, - async run($: CommonLibrary): Promise { - if (!statuses.includes($.args[0])) return $.channel.send("That status doesn't exist!"); - else { + async run($) { + if (!statuses.includes($.args[0])) { + $.channel.send("That status doesn't exist!"); + return; + } else { $.client.user?.setStatus($.args[0]); $.channel.send(`Setting status to \`${$.args[0]}\`...`); } @@ -100,7 +102,7 @@ export default new Command({ purge: new Command({ description: "Purges bot messages.", permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { + async run($) { if ($.message.channel instanceof discord.DMChannel) { return; } @@ -124,7 +126,7 @@ export default new Command({ run: "A number was not provided.", number: new Command({ description: "Amount of messages to delete.", - async run($: CommonLibrary): Promise { + async run($) { if ($.channel.type === "dm") { await $.channel.send("Can't clear messages in the DMs!"); return; @@ -142,7 +144,7 @@ export default new Command({ usage: "", permission: Command.PERMISSIONS.BOT_OWNER, // You have to bring everything into scope to use them. AFAIK, there isn't a more maintainable way to do this, but at least TS will let you know if anything gets removed. - async run({args, author, channel, client, guild, member, message}): Promise { + async run({args, author, channel, client, guild, member, message}) { try { const code = args.join(" "); let evaled = eval(code); @@ -157,18 +159,18 @@ export default new Command({ nick: new Command({ description: "Change the bot's nickname.", permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { + async run($) { const nickName = $.args.join(" "); await $.guild?.me?.setNickname(nickName); if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES)) - $.message.delete({timeout: 5000}).catch($.handler.bind($)); + $.message.delete({timeout: 5000}).catch(handler.bind($)); $.channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000})); } }), guilds: new Command({ description: "Shows a list of all guilds the bot is a member of.", permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { + async run($) { const guildList = $.client.guilds.cache.array().map((e) => e.name); $.channel.send(guildList); } @@ -177,7 +179,7 @@ export default new Command({ description: "Set the activity of the bot.", permission: Command.PERMISSIONS.BOT_SUPPORT, usage: " ", - async run($: CommonLibrary): Promise { + async run($) { $.client.user?.setActivity(".help", { type: "LISTENING" }); @@ -185,7 +187,7 @@ export default new Command({ }, any: new Command({ description: `Select an activity type to set. Available levels: \`[${activities.join(", ")}]\``, - async run($: CommonLibrary): Promise { + async run($) { const type = $.args[0]; if (activities.includes(type)) { diff --git a/src/commands/fun/8ball.ts b/src/commands/fun/8ball.ts index c43ac31..84488a7 100644 --- a/src/commands/fun/8ball.ts +++ b/src/commands/fun/8ball.ts @@ -1,5 +1,5 @@ import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; +import {random} from "../../core/lib"; const responses = [ "Most likely,", @@ -31,9 +31,9 @@ export default new Command({ run: "Please provide a question.", any: new Command({ description: "Question to ask the 8-ball.", - async run($: CommonLibrary): Promise { + async run($) { const sender = $.message.author; - $.channel.send($(responses).random() + ` <@${sender.id}>`); + $.channel.send(`${random(responses)} <@${sender.id}>`); } }) }); diff --git a/src/commands/fun/cookie.ts b/src/commands/fun/cookie.ts index 5c545e9..99ace4b 100644 --- a/src/commands/fun/cookie.ts +++ b/src/commands/fun/cookie.ts @@ -1,18 +1,17 @@ import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; export default new Command({ description: "Gives specified user a cookie.", usage: "['all'/@user]", run: ":cookie: Here's a cookie!", any: new Command({ - async run($: CommonLibrary): Promise { - if ($.args[0] == "all") return $.channel.send(`${$.author} gave everybody a cookie!`); + async run($) { + if ($.args[0] == "all") $.channel.send(`${$.author} gave everybody a cookie!`); } }), user: new Command({ description: "User to give cookie to.", - async run($: CommonLibrary): Promise { + async run($) { const sender = $.author; const mention = $.message.mentions.users.first(); @@ -41,7 +40,10 @@ export default new Command({ `bakes <@${mention.id}> fresh cookies, it smells amazing.` ]; - if (mention.id == sender.id) return $.channel.send("You can't give yourself cookies!"); + if (mention.id == sender.id) { + $.channel.send("You can't give yourself cookies!"); + return; + } $.channel.send(`:cookie: <@${sender.id}> ` + cookies[Math.floor(Math.random() * cookies.length)]); } diff --git a/src/commands/fun/eco.ts b/src/commands/fun/eco.ts index 5be316f..6df36ae 100644 --- a/src/commands/fun/eco.ts +++ b/src/commands/fun/eco.ts @@ -3,6 +3,7 @@ import {isAuthorized, getMoneyEmbed} from "./subcommands/eco-utils"; import {DailyCommand, PayCommand, GuildCommand, LeaderboardCommand} from "./subcommands/eco-core"; import {BuyCommand, ShopCommand} from "./subcommands/eco-shop"; import {MondayCommand} from "./subcommands/eco-extras"; +import {callMemberByUsername} from "../../core/libd"; export default new Command({ description: "Economy command for Monika.", @@ -26,7 +27,7 @@ export default new Command({ }), any: new Command({ description: "See how much money someone else has by using their username.", - async run({guild, channel, args, callMemberByUsername, message}) { + async run({guild, channel, args, message}) { if (isAuthorized(guild, channel)) callMemberByUsername(message, args.join(" "), (member) => { channel.send(getMoneyEmbed(member.user)); diff --git a/src/commands/fun/neko.ts b/src/commands/fun/neko.ts index 63a8073..68ab1bf 100644 --- a/src/commands/fun/neko.ts +++ b/src/commands/fun/neko.ts @@ -2,13 +2,13 @@ import {URL} from "url"; import FileManager from "../../core/storage"; import Command from "../../core/command"; -import {CommonLibrary, getContent} from "../../core/lib"; +import {getContent} from "../../core/libd"; const endpoints = FileManager.read("endpoints"); export default new Command({ description: "Provides you with a random image with the selected argument.", - async run($: CommonLibrary): Promise { + async run($) { console.log(endpoints.sfw); $.channel.send( `Please provide an image type. Available arguments:\n\`[${Object.keys(endpoints.sfw).join(", ")}]\`.` @@ -16,7 +16,7 @@ export default new Command({ }, any: new Command({ description: "Image type to send.", - async run($: CommonLibrary): Promise { + async run($) { if (!($.args[0] in endpoints.sfw)) return $.channel.send("Couldn't find that endpoint!"); let baseURL = "https://nekos.life/api/v2"; diff --git a/src/commands/fun/ok.ts b/src/commands/fun/ok.ts index c7b3570..9c8d641 100644 --- a/src/commands/fun/ok.ts +++ b/src/commands/fun/ok.ts @@ -1,9 +1,8 @@ import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; export default new Command({ description: "Sends random ok message.", - async run($: CommonLibrary): Promise { + async run($) { const responses = [ "boomer", "zoomer", diff --git a/src/commands/fun/owoify.ts b/src/commands/fun/owoify.ts index ca5d18a..789d74a 100644 --- a/src/commands/fun/owoify.ts +++ b/src/commands/fun/owoify.ts @@ -1,10 +1,10 @@ /// @ts-nocheck import Command from "../../core/command"; -import {CommonLibrary, getContent} from "../../core/lib"; +import {getContent} from "../../core/libd"; export default new Command({ description: "OwO-ifies the input.", - async run($: CommonLibrary): Promise { + async run($) { let url = new URL(`https://nekos.life/api/v2/owoify?text=${$.args.join(" ")}`); const content = await getContent(url.toString()); $.channel.send(content.owo); diff --git a/src/commands/fun/poll.ts b/src/commands/fun/poll.ts index e104798..8b6d6e1 100644 --- a/src/commands/fun/poll.ts +++ b/src/commands/fun/poll.ts @@ -1,6 +1,5 @@ import {MessageEmbed} from "discord.js"; import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; export default new Command({ description: "Create a poll.", @@ -8,7 +7,7 @@ export default new Command({ run: "Please provide a question.", any: new Command({ description: "Question for the poll.", - async run($: CommonLibrary): Promise { + async run($) { const embed = new MessageEmbed() .setAuthor( `Poll created by ${$.message.author.username}`, diff --git a/src/commands/fun/subcommands/eco-core.ts b/src/commands/fun/subcommands/eco-core.ts index 5723137..ea174b1 100644 --- a/src/commands/fun/subcommands/eco-core.ts +++ b/src/commands/fun/subcommands/eco-core.ts @@ -1,5 +1,6 @@ import Command from "../../../core/command"; -import $ from "../../../core/lib"; +import {prompt} from "../../../core/libd"; +import {pluralise} from "../../../core/lib"; import {Storage} from "../../../core/structures"; import {isAuthorized, getMoneyEmbed, getSendEmbed, ECO_EMBED_COLOR} from "./eco-utils"; @@ -90,7 +91,7 @@ export const LeaderboardCommand = new Command({ fields.push({ name: `#${i + 1}. ${user.username}#${user.discriminator}`, - value: $(users[id].money).pluralise("Mon", "s") + value: pluralise(users[id].money, "Mon", "s") }); } @@ -141,7 +142,7 @@ export const PayCommand = new Command({ run: "You must use the format `eco pay `!" }), any: new Command({ - async run({args, author, channel, guild, prompt}) { + async run({args, author, channel, guild}) { if (isAuthorized(guild, channel)) { const last = args.pop(); @@ -177,7 +178,8 @@ export const PayCommand = new Command({ return prompt( await channel.send( - `Are you sure you want to send ${$(amount).pluralise( + `Are you sure you want to send ${pluralise( + amount, "Mon", "s" )} to this person?\n*(This message will automatically be deleted after 10 seconds.)*`, diff --git a/src/commands/fun/subcommands/eco-shop-items.ts b/src/commands/fun/subcommands/eco-shop-items.ts index cd0580c..0a90549 100644 --- a/src/commands/fun/subcommands/eco-shop-items.ts +++ b/src/commands/fun/subcommands/eco-shop-items.ts @@ -1,5 +1,5 @@ import {Message} from "discord.js"; -import $ from "../../../core/lib"; +import {random} from "../../../core/lib"; export interface ShopItem { cost: number; @@ -43,7 +43,7 @@ export const ShopItems: ShopItem[] = [ description: "Buys what is technically a laser bridge.", usage: "laser bridge", run(message) { - message.channel.send($(lines).random(), { + message.channel.send(random(lines), { files: [ { attachment: diff --git a/src/commands/fun/subcommands/eco-shop.ts b/src/commands/fun/subcommands/eco-shop.ts index 650cd8f..24ebcd7 100644 --- a/src/commands/fun/subcommands/eco-shop.ts +++ b/src/commands/fun/subcommands/eco-shop.ts @@ -1,5 +1,6 @@ import Command from "../../../core/command"; -import $ from "../../../core/lib"; +import {pluralise, split} from "../../../core/lib"; +import {paginate} from "../../../core/libd"; import {Storage, getPrefix} from "../../../core/structures"; import {isAuthorized, ECO_EMBED_COLOR} from "./eco-utils"; import {ShopItems, ShopItem} from "./eco-shop-items"; @@ -15,7 +16,7 @@ export const ShopCommand = new Command({ for (const item of selection) fields.push({ name: `**${item.title}** (${getPrefix(guild)}eco buy ${item.usage})`, - value: `${item.description} Costs ${$(item.cost).pluralise("Mon", "s")}.`, + value: `${item.description} Costs ${pluralise(item.cost, "Mon", "s")}.`, inline: false }); @@ -34,11 +35,11 @@ export const ShopCommand = new Command({ // In case there's just one page, omit unnecessary details. if (ShopItems.length <= 5) channel.send(getShopEmbed(ShopItems)); else { - const shopPages = $(ShopItems).split(5); + const shopPages = split(ShopItems, 5); const pageAmount = shopPages.length; const msg = await channel.send(getShopEmbed(shopPages[0], `Shop (Page 1 of ${pageAmount})`)); - $.paginate(msg, author.id, pageAmount, (page) => { + paginate(msg, author.id, pageAmount, (page) => { msg.edit(getShopEmbed(shopPages[page], `Shop (Page ${page + 1} of ${pageAmount})`)); }); } diff --git a/src/commands/fun/subcommands/eco-utils.ts b/src/commands/fun/subcommands/eco-utils.ts index 554b5bf..e4ea32f 100644 --- a/src/commands/fun/subcommands/eco-utils.ts +++ b/src/commands/fun/subcommands/eco-utils.ts @@ -1,4 +1,4 @@ -import $ from "../../../core/lib"; +import {pluralise} from "../../../core/lib"; import {Storage} from "../../../core/structures"; import {User, Guild, TextChannel, DMChannel, NewsChannel} from "discord.js"; @@ -20,7 +20,7 @@ export function getMoneyEmbed(user: User): object { fields: [ { name: "Balance", - value: $(profile.money).pluralise("Mon", "s") + value: pluralise(profile.money, "Mon", "s") } ] } @@ -39,15 +39,15 @@ export function getSendEmbed(sender: User, receiver: User, amount: number): obje }) }, title: "Transaction", - description: `${sender.toString()} has sent ${$(amount).pluralise("Mon", "s")} to ${receiver.toString()}!`, + description: `${sender.toString()} has sent ${pluralise(amount, "Mon", "s")} to ${receiver.toString()}!`, fields: [ { name: `Sender: ${sender.username}#${sender.discriminator}`, - value: $(Storage.getUser(sender.id).money).pluralise("Mon", "s") + value: pluralise(Storage.getUser(sender.id).money, "Mon", "s") }, { name: `Receiver: ${receiver.username}#${receiver.discriminator}`, - value: $(Storage.getUser(receiver.id).money).pluralise("Mon", "s") + value: pluralise(Storage.getUser(receiver.id).money, "Mon", "s") } ], footer: { diff --git a/src/commands/help.ts b/src/commands/help.ts index dd74ad4..02b8650 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,5 +1,5 @@ import Command from "../core/command"; -import {CommonLibrary} from "../core/lib"; +import {toTitleCase} from "../core/lib"; import {loadableCommands, categories} from "../core/command"; import {PermissionNames} from "../core/permissions"; @@ -7,12 +7,12 @@ export default new Command({ description: "Lists all commands. If a command is specified, their arguments are listed as well.", usage: "([command, [subcommand/type], ...])", aliases: ["h"], - async run($: CommonLibrary): Promise { + async run($) { const commands = await loadableCommands; let output = `Legend: \`\`, \`[list/of/stuff]\`, \`(optional)\`, \`()\`, \`([optional/list/...])\``; for (const [category, headers] of categories) { - output += `\n\n===[ ${$(category).toTitleCase()} ]===`; + output += `\n\n===[ ${toTitleCase(category)} ]===`; for (const header of headers) { if (header !== "test") { @@ -31,12 +31,15 @@ export default new Command({ $.channel.send(output, {split: true}); }, any: new Command({ - async run($: CommonLibrary): Promise { + async run($) { const commands = await loadableCommands; let header = $.args.shift() as string; let command = commands.get(header); - if (!command || header === "test") return $.channel.send(`No command found by the name \`${header}\`!`); + if (!command || header === "test") { + $.channel.send(`No command found by the name \`${header}\`!`); + return; + } if (command.originalCommandName) header = command.originalCommandName; else console.warn(`originalCommandName isn't defined for ${header}?!`); @@ -53,7 +56,7 @@ export default new Command({ console.warn( `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.` ); - else selectedCategory = $(category).toTitleCase(); + else selectedCategory = toTitleCase(category); } } @@ -86,7 +89,10 @@ export default new Command({ } } - if (invalid) return $.channel.send(`No command found by the name \`${header}\`!`); + if (invalid) { + $.channel.send(`No command found by the name \`${header}\`!`); + return; + } let append = ""; diff --git a/src/commands/info.ts b/src/commands/info.ts index 3ee4128..7b4b103 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -2,7 +2,7 @@ import {MessageEmbed, version as djsversion} from "discord.js"; import ms from "ms"; import os from "os"; import Command from "../core/command"; -import {CommonLibrary, formatBytes, trimArray} from "../core/lib"; +import {formatBytes, trimArray} from "../core/libd"; import {verificationLevels, filterLevels, regions, flags} from "../defs/info"; import moment from "moment"; import utc from "moment"; @@ -17,12 +17,12 @@ export default new Command({ avatar: new Command({ description: "Shows your own, or another user's avatar.", usage: "()", - async run($: CommonLibrary): Promise { + async run($) { $.channel.send($.author.displayAvatarURL({dynamic: true, size: 2048})); }, user: new Command({ description: "Shows your own, or another user's avatar.", - async run($: CommonLibrary): Promise { + async run($) { $.channel.send( $.args[0].displayAvatarURL({ dynamic: true, @@ -34,7 +34,7 @@ export default new Command({ }), bot: new Command({ description: "Displays info about the bot.", - async run($: CommonLibrary): Promise { + async run($) { const core = os.cpus()[0]; const embed = new MessageEmbed() .setColor($.guild?.me?.displayHexColor || "BLUE") @@ -76,7 +76,7 @@ export default new Command({ guild: new Command({ description: "Displays info about the current guild or another guild.", usage: "(/)", - async run($: CommonLibrary): Promise { + async run($) { if ($.guild) { $.channel.send(await getGuildInfo($.guild, $.guild)); } else { @@ -85,7 +85,7 @@ export default new Command({ }, any: new Command({ description: "Display info about a guild by finding its name or ID.", - async run($: CommonLibrary): Promise { + async run($) { // If a guild ID is provided (avoid the "number" subcommand because of inaccuracies), search for that guild if ($.args.length === 1 && /^\d{17,19}$/.test($.args[0])) { const id = $.args[0]; @@ -112,14 +112,16 @@ export default new Command({ }, user: new Command({ description: "Displays info about mentioned user.", - async run($: CommonLibrary): Promise { + async run($) { // Transforms the User object into a GuildMember object of the current guild. const member = await $.guild?.members.fetch($.args[0]); - if (!member) - return $.channel.send( + if (!member) { + $.channel.send( "No member object was found by that user! Are you sure you used this command in a server?" ); + return; + } const roles = member.roles.cache .sort((a: {position: number}, b: {position: number}) => b.position - a.position) diff --git a/src/commands/scanemotes.ts b/src/commands/scanemotes.ts index f93a949..9596f9f 100644 --- a/src/commands/scanemotes.ts +++ b/src/commands/scanemotes.ts @@ -1,5 +1,5 @@ -import Command from "../core/command"; -import {CommonLibrary} from "../core/lib"; +import Command, {handler} from "../core/command"; +import {pluralise} from "../core/lib"; import moment from "moment"; import {Collection, TextChannel} from "discord.js"; @@ -8,8 +8,11 @@ const lastUsedTimestamps: {[id: string]: number} = {}; export default new Command({ description: "Scans all text channels in the current guild and returns the number of times each emoji specific to the guild has been used. Has a cooldown of 24 hours per guild.", - async run($: CommonLibrary): Promise { - if (!$.guild) return $.channel.send(`You must use this command on a server!`); + async run($) { + if (!$.guild) { + $.channel.send(`You must use this command on a server!`); + return; + } // Test if the command is on cooldown. This isn't the strictest cooldown possible, because in the event that the bot crashes, the cooldown will be reset. But for all intends and purposes, it's a good enough cooldown. It's a per-server cooldown. const startTime = Date.now(); @@ -19,11 +22,12 @@ export default new Command({ const howLong = moment(startTime).to(lastUsedTimestamp + cooldown); // If it's been less than an hour since the command was last used, prevent it from executing. - if (difference < cooldown) - return $.channel.send( + if (difference < cooldown) { + $.channel.send( `This command requires a day to cooldown. You'll be able to activate this command ${howLong}.` ); - else lastUsedTimestamps[$.guild.id] = startTime; + return; + } else lastUsedTimestamps[$.guild.id] = startTime; const stats: { [id: string]: { @@ -155,7 +159,8 @@ export default new Command({ const finishTime = Date.now(); clearInterval(interval); statusMessage.edit( - `Finished operation in ${moment.duration(finishTime - startTime).humanize()} with ${$(warnings).pluralise( + `Finished operation in ${moment.duration(finishTime - startTime).humanize()} with ${pluralise( + warnings, "inconsistenc", "ies", "y" @@ -181,6 +186,6 @@ export default new Command({ ); } - $.channel.send(lines, {split: true}).catch($.handler.bind($)); + $.channel.send(lines, {split: true}).catch(handler.bind($)); } }); diff --git a/src/commands/template.ts b/src/commands/template.ts index 832fecd..686fe4a 100644 --- a/src/commands/template.ts +++ b/src/commands/template.ts @@ -1,5 +1,4 @@ import Command from "../core/command"; -import {CommonLibrary} from "../core/lib"; export default new Command({ description: @@ -8,7 +7,7 @@ export default new Command({ usage: "", permission: null, aliases: [], - async run($: CommonLibrary): Promise { + async run($) { // code }, subcommands: { @@ -19,7 +18,7 @@ export default new Command({ usage: "", permission: null, aliases: [], - async run($: CommonLibrary): Promise { + async run($) { // code } }) @@ -30,7 +29,7 @@ export default new Command({ endpoint: false, usage: "", permission: null, - async run($: CommonLibrary): Promise { + async run($) { // code } }), @@ -40,7 +39,7 @@ export default new Command({ endpoint: false, usage: "", permission: null, - async run($: CommonLibrary): Promise { + async run($) { // code } }), @@ -50,7 +49,7 @@ export default new Command({ endpoint: false, usage: "", permission: null, - async run($: CommonLibrary): Promise { + async run($) { // code } }) diff --git a/src/commands/utilities/desc.ts b/src/commands/utilities/desc.ts index c5e75f3..0ab3aaf 100644 --- a/src/commands/utilities/desc.ts +++ b/src/commands/utilities/desc.ts @@ -1,18 +1,25 @@ import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; export default new Command({ description: "Renames current voice channel.", usage: "", - async run($: CommonLibrary): Promise { + async run($) { const voiceChannel = $.message.member?.voice.channel; - if (!voiceChannel) return $.channel.send("You are not in a voice channel."); + if (!voiceChannel) { + $.channel.send("You are not in a voice channel."); + return; + } - if (!voiceChannel.guild.me?.hasPermission("MANAGE_CHANNELS")) - return $.channel.send("I am lacking the required permissions to perform this action."); + if (!voiceChannel.guild.me?.hasPermission("MANAGE_CHANNELS")) { + $.channel.send("I am lacking the required permissions to perform this action."); + return; + } - if ($.args.length === 0) return $.channel.send("Please provide a new voice channel name."); + if ($.args.length === 0) { + $.channel.send("Please provide a new voice channel name."); + return; + } const prevName = voiceChannel.name; const newName = $.args.join(" "); diff --git a/src/commands/utilities/emote.ts b/src/commands/utilities/emote.ts index 912774c..b84d9ef 100644 --- a/src/commands/utilities/emote.ts +++ b/src/commands/utilities/emote.ts @@ -1,6 +1,6 @@ import Command from "../../core/command"; import {queryClosestEmoteByName} from "./subcommands/emote-utils"; -import {botHasPermission} from "../../core/lib"; +import {botHasPermission} from "../../core/libd"; import {Permissions} from "discord.js"; export default new Command({ diff --git a/src/commands/utilities/lsemotes.ts b/src/commands/utilities/lsemotes.ts index 23311cb..6188439 100644 --- a/src/commands/utilities/lsemotes.ts +++ b/src/commands/utilities/lsemotes.ts @@ -1,26 +1,35 @@ import {GuildEmoji} from "discord.js"; import {MessageEmbed} from "discord.js"; import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; +import {split} from "../../core/lib"; +import {paginate} from "../../core/libd"; import vm from "vm"; +import {TextChannel} from "discord.js"; +import {DMChannel} from "discord.js"; +import {NewsChannel} from "discord.js"; +import {User} from "discord.js"; const REGEX_TIMEOUT_MS = 1000; export default new Command({ description: "Lists all emotes the bot has in it's registry,", usage: " (-flags)", - async run($: CommonLibrary): Promise { - displayEmoteList($, $.client.emojis.cache.array()); + async run($) { + displayEmoteList($.client.emojis.cache.array(), $.channel, $.author); }, any: new Command({ description: "Filters emotes by via a regular expression. Flags can be added by adding a dash at the end. For example, to do a case-insensitive search, do %prefix%lsemotes somepattern -i", - async run($: CommonLibrary): Promise { + async run($) { // If a guild ID is provided, filter all emotes by that guild (but only if there aren't any arguments afterward) if ($.args.length === 1 && /^\d{17,19}$/.test($.args[0])) { const guildID: string = $.args[0]; - displayEmoteList($, $.client.emojis.cache.filter((emote) => emote.guild.id === guildID).array()); + displayEmoteList( + $.client.emojis.cache.filter((emote) => emote.guild.id === guildID).array(), + $.channel, + $.author + ); } else { // Otherwise, search via a regex pattern let flags: string | undefined = undefined; @@ -55,7 +64,7 @@ export default new Command({ script.runInContext(context, {timeout: REGEX_TIMEOUT_MS}); emotes = sandbox.emotes; emoteCollection = emoteCollection.filter((emote) => emotes.has(emote.id)); // Only allow emotes that haven't been deleted. - displayEmoteList($, emoteCollection); + displayEmoteList(emoteCollection, $.channel, $.author); } catch (error) { if (error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") { $.channel.send( @@ -73,7 +82,7 @@ export default new Command({ }) }); -async function displayEmoteList($: CommonLibrary, emotes: GuildEmoji[]) { +async function displayEmoteList(emotes: GuildEmoji[], channel: TextChannel | DMChannel | NewsChannel, author: User) { emotes.sort((a, b) => { const first = a.name.toLowerCase(); const second = b.name.toLowerCase(); @@ -82,7 +91,7 @@ async function displayEmoteList($: CommonLibrary, emotes: GuildEmoji[]) { else if (first < second) return -1; else return 0; }); - const sections = $(emotes).split(20); + const sections = split(emotes, 20); const pages = sections.length; const embed = new MessageEmbed().setTitle("**Emotes**").setColor("AQUA"); let desc = ""; @@ -97,9 +106,9 @@ async function displayEmoteList($: CommonLibrary, emotes: GuildEmoji[]) { if (pages > 1) { embed.setTitle(`**Emotes** (Page 1 of ${pages})`); - const msg = await $.channel.send({embed}); + const msg = await channel.send({embed}); - $.paginate(msg, $.author.id, pages, (page) => { + paginate(msg, author.id, pages, (page) => { let desc = ""; for (const emote of sections[page]) { desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`; @@ -109,9 +118,9 @@ async function displayEmoteList($: CommonLibrary, emotes: GuildEmoji[]) { msg.edit(embed); }); } else { - await $.channel.send({embed}); + channel.send({embed}); } } else { - $.channel.send("No valid emotes found by that query."); + channel.send("No valid emotes found by that query."); } } diff --git a/src/commands/utilities/react.ts b/src/commands/utilities/react.ts index ceef800..cc7c031 100644 --- a/src/commands/utilities/react.ts +++ b/src/commands/utilities/react.ts @@ -1,5 +1,4 @@ import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; import {Message, Channel, TextChannel} from "discord.js"; import {queryClosestEmoteByName} from "./subcommands/emote-utils"; @@ -7,7 +6,7 @@ export default new Command({ description: "Reacts to the a previous message in your place. You have to react with the same emote before the bot removes that reaction.", usage: 'react ()', - async run($: CommonLibrary): Promise { + async run($) { let target: Message | undefined; let distance = 1; @@ -106,5 +105,7 @@ export default new Command({ reaction.users.remove($.client.user!); }, 5000); } + + return; } }); diff --git a/src/commands/utilities/say.ts b/src/commands/utilities/say.ts index f4d74f0..e58b90e 100644 --- a/src/commands/utilities/say.ts +++ b/src/commands/utilities/say.ts @@ -1,5 +1,4 @@ import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; export default new Command({ description: "Repeats your message.", @@ -7,7 +6,7 @@ export default new Command({ run: "Please provide a message for me to say!", any: new Command({ description: "Message to repeat.", - async run($: CommonLibrary): Promise { + async run($) { $.channel.send(`*${$.author} says:*\n${$.args.join(" ")}`); } }) diff --git a/src/commands/utilities/shorten.ts b/src/commands/utilities/shorten.ts index 5d0b854..79961f3 100644 --- a/src/commands/utilities/shorten.ts +++ b/src/commands/utilities/shorten.ts @@ -1,12 +1,11 @@ import Command from "../../core/command"; -import {CommonLibrary} from "../../core/lib"; import * as https from "https"; export default new Command({ description: "Shortens a given URL.", run: "Please provide a URL.", any: new Command({ - async run($: CommonLibrary): Promise { + async run($) { https.get("https://is.gd/create.php?format=simple&url=" + encodeURIComponent($.args[0]), function (res) { var body = ""; res.on("data", function (chunk) { diff --git a/src/commands/utilities/time.ts b/src/commands/utilities/time.ts index 8cdecc1..d520271 100644 --- a/src/commands/utilities/time.ts +++ b/src/commands/utilities/time.ts @@ -1,4 +1,5 @@ import Command from "../../core/command"; +import {ask, askYesOrNo, askMultipleChoice, prompt, callMemberByUsername} from "../../core/libd"; import {Storage} from "../../core/structures"; import {User} from "discord.js"; import moment from "moment"; @@ -176,7 +177,7 @@ export default new Command({ // Welcome to callback hell. We hope you enjoy your stay here! setup: new Command({ description: "Registers your timezone information for the bot.", - async run({author, channel, ask, askYesOrNo, askMultipleChoice}) { + async run({author, channel}) { const profile = Storage.getUser(author.id); profile.timezone = null; profile.daylightSavingsRegion = null; @@ -328,7 +329,7 @@ export default new Command({ }), delete: new Command({ description: "Delete your timezone information.", - async run({channel, author, prompt}) { + async run({channel, author}) { prompt( await channel.send( "Are you sure you want to delete your timezone information?\n*(This message will automatically be deleted after 10 seconds.)*" @@ -382,7 +383,7 @@ export default new Command({ }), any: new Command({ description: "See what time it is for someone else (by their username).", - async run({channel, args, message, callMemberByUsername}) { + async run({channel, args, message}) { callMemberByUsername(message, args.join(" "), (member) => { channel.send(getTimeEmbed(member.user)); }); diff --git a/src/core/command.ts b/src/core/command.ts index ae2e73c..5ee8a5b 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,16 +1,27 @@ -import {isType, parseVars, CommonLibrary} from "./lib"; +import {parseVars} from "./libd"; import {Collection} from "discord.js"; +import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember} from "discord.js"; import {PERMISSIONS} from "./permissions"; import {getPrefix} from "../core/structures"; import glob from "glob"; +interface CommandMenu { + args: any[]; + client: Client; + message: Message; + channel: TextChannel | DMChannel | NewsChannel; + guild: Guild | null; + author: User; + member: GuildMember | null; +} + interface CommandOptions { description?: string; endpoint?: boolean; usage?: string; permission?: PERMISSIONS | null; aliases?: string[]; - run?: (($: CommonLibrary) => Promise) | string; + run?: (($: CommandMenu) => Promise) | string; subcommands?: {[key: string]: Command}; user?: Command; number?: Command; @@ -32,7 +43,7 @@ export default class Command { public readonly permission: PERMISSIONS | null; public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. public originalCommandName: string | null; // If the command is an alias, what's the original name? - public run: (($: CommonLibrary) => Promise) | string; + public run: (($: CommandMenu) => Promise) | string; public readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. public user: Command | null; public number: Command | null; @@ -96,11 +107,11 @@ export default class Command { ); } - public execute($: CommonLibrary) { - if (isType(this.run, String)) { + public execute($: CommandMenu) { + if (typeof this.run === "string") { $.channel.send( parseVars( - this.run as string, + this.run, { author: $.author.toString(), prefix: getPrefix($.guild) @@ -108,7 +119,7 @@ export default class Command { "???" ) ); - } else (this.run as Function)($).catch($.handler.bind($)); + } else this.run($).catch(handler.bind($)); } public resolve(param: string): TYPES { @@ -224,3 +235,20 @@ function globP(path: string) { }); }); } + +// If you use promises, use this function to display the error in chat. +// Case #1: await $.channel.send(""); --> Automatically caught by Command.execute(). +// Case #2: $.channel.send("").catch(handler.bind($)); --> Manually caught by the user. +// TODO: Find a way to catch unhandled rejections automatically, forgoing the need for this. +export function handler(this: CommandMenu, error: Error) { + if (this) + this.channel.send( + `There was an error while trying to execute that command!\`\`\`${error.stack ?? error}\`\`\`` + ); + else + console.warn( + "No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!" + ); + + console.error(error); +} diff --git a/src/core/wrappers.test.ts b/src/core/lib.test.ts similarity index 58% rename from src/core/wrappers.test.ts rename to src/core/lib.test.ts index e540c63..0cd4cc9 100644 --- a/src/core/wrappers.test.ts +++ b/src/core/lib.test.ts @@ -1,46 +1,46 @@ import {strict as assert} from "assert"; -import {NumberWrapper, StringWrapper, ArrayWrapper} from "./wrappers"; +import {pluralise, pluraliseSigned, replaceAll, toTitleCase, split} from "./lib"; // I can't figure out a way to run the test suite while running the bot. describe("Wrappers", () => { describe("NumberWrapper", () => { describe("#pluralise()", () => { it('should return "5 credits"', () => { - assert.strictEqual(new NumberWrapper(5).pluralise("credit", "s"), "5 credits"); + assert.strictEqual(pluralise(5, "credit", "s"), "5 credits"); }); it('should return "1 credit"', () => { - assert.strictEqual(new NumberWrapper(1).pluralise("credit", "s"), "1 credit"); + assert.strictEqual(pluralise(1, "credit", "s"), "1 credit"); }); it('should return "-1 credits"', () => { - assert.strictEqual(new NumberWrapper(-1).pluralise("credit", "s"), "-1 credits"); + assert.strictEqual(pluralise(-1, "credit", "s"), "-1 credits"); }); it("should be able to work with a plural suffix", () => { - assert.strictEqual(new NumberWrapper(2).pluralise("part", "ies", "y"), "2 parties"); + assert.strictEqual(pluralise(2, "part", "ies", "y"), "2 parties"); }); it("should be able to work with a singular suffix", () => { - assert.strictEqual(new NumberWrapper(1).pluralise("part", "ies", "y"), "1 party"); + assert.strictEqual(pluralise(1, "part", "ies", "y"), "1 party"); }); it("should be able to exclude the number", () => { - assert.strictEqual(new NumberWrapper(1).pluralise("credit", "s", "", true), "credit"); + assert.strictEqual(pluralise(1, "credit", "s", "", true), "credit"); }); }); describe("#pluraliseSigned()", () => { it('should return "-1 credits"', () => { - assert.strictEqual(new NumberWrapper(-1).pluraliseSigned("credit", "s"), "-1 credits"); + assert.strictEqual(pluraliseSigned(-1, "credit", "s"), "-1 credits"); }); it('should return "+0 credits"', () => { - assert.strictEqual(new NumberWrapper(0).pluraliseSigned("credit", "s"), "+0 credits"); + assert.strictEqual(pluraliseSigned(0, "credit", "s"), "+0 credits"); }); it('should return "+1 credit"', () => { - assert.strictEqual(new NumberWrapper(1).pluraliseSigned("credit", "s"), "+1 credit"); + assert.strictEqual(pluraliseSigned(1, "credit", "s"), "+1 credit"); }); }); }); @@ -48,14 +48,14 @@ describe("Wrappers", () => { describe("StringWrapper", () => { describe("#replaceAll()", () => { it('should convert "test" to "zesz"', () => { - assert.strictEqual(new StringWrapper("test").replaceAll("t", "z"), "zesz"); + assert.strictEqual(replaceAll("test", "t", "z"), "zesz"); }); }); describe("#toTitleCase()", () => { it("should capitalize the first letter of each word", () => { assert.strictEqual( - new StringWrapper("yeetus deletus find salvation from jesus").toTitleCase(), + toTitleCase("yeetus deletus find salvation from jesus"), "Yeetus Deletus Find Salvation From Jesus" ); }); @@ -65,7 +65,7 @@ describe("Wrappers", () => { describe("ArrayWrapper", () => { describe("#split()", () => { it("should split [1,2,3,4,5,6,7,8,9,10] into [[1,2,3],[4,5,6],[7,8,9],[10]]", () => { - assert.deepStrictEqual(new ArrayWrapper([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).split(3), [ + assert.deepStrictEqual(split([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3), [ [1, 2, 3], [4, 5, 6], [7, 8, 9], diff --git a/src/core/lib.ts b/src/core/lib.ts index d2e7d5b..a0ebcb7 100644 --- a/src/core/lib.ts +++ b/src/core/lib.ts @@ -1,483 +1,61 @@ -import {GenericWrapper, NumberWrapper, StringWrapper, ArrayWrapper} from "./wrappers"; -import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember, Permissions} from "discord.js"; -import {get} from "https"; -import FileManager from "./storage"; -import {eventListeners} from "../events/messageReactionRemove"; -import {client} from "../index"; -import {EmoteRegistryDump, EmoteRegistryDumpEntry} from "./structures"; - -/** A type that describes what the library module does. */ -export interface CommonLibrary { - // Wrapper Object // - /** Wraps the value you enter with an object that provides extra functionality and provides common utility functions. */ - (value: number): NumberWrapper; - (value: string): StringWrapper; - (value: T[]): ArrayWrapper; - (value: T): GenericWrapper; - - // Common Library Functions // - /** .catch($.handler.bind($)) or .catch(error => $.handler(error)) */ - handler: (error: Error) => void; - paginate: ( - message: Message, - senderID: string, - total: number, - callback: (page: number) => void, - duration?: number - ) => void; - prompt: (message: Message, senderID: string, onConfirm: () => void, duration?: number) => void; - getMemberByUsername: (guild: Guild, username: string) => Promise; - callMemberByUsername: ( - message: Message, - username: string, - onSuccess: (member: GuildMember) => void - ) => Promise; - ask: ( - message: Message, - senderID: string, - condition: (reply: string) => boolean, - onSuccess: () => void, - onReject: () => string, - timeout?: number - ) => void; - askYesOrNo: (message: Message, senderID: string, timeout?: number) => Promise; - askMultipleChoice: (message: Message, senderID: string, callbackStack: (() => void)[], timeout?: number) => void; - - // Dynamic Properties // - args: any[]; - client: Client; - message: Message; - channel: TextChannel | DMChannel | NewsChannel; - guild: Guild | null; - author: User; - member: GuildMember | null; -} - -export default function $(value: number): NumberWrapper; -export default function $(value: string): StringWrapper; -export default function $(value: T[]): ArrayWrapper; -export default function $(value: T): GenericWrapper; -export default function $(value: any) { - if (isType(value, Number)) return new NumberWrapper(value); - else if (isType(value, String)) return new StringWrapper(value); - else if (isType(value, Array)) return new ArrayWrapper(value); - else return new GenericWrapper(value); -} - -// If you use promises, use this function to display the error in chat. -// Case #1: await $.channel.send(""); --> Automatically caught by Command.execute(). -// Case #2: $.channel.send("").catch($.handler.bind($)); --> Manually caught by the user. -$.handler = function (this: CommonLibrary, error: Error) { - if (this) - this.channel.send( - `There was an error while trying to execute that command!\`\`\`${error.stack ?? error}\`\`\`` - ); - else - console.warn( - "No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!" - ); - - console.error(error); -}; - -export function botHasPermission(guild: Guild | null, permission: number): boolean { - return !!guild?.me?.hasPermission(permission); -} - -export function updateGlobalEmoteRegistry(): void { - const data: EmoteRegistryDump = {version: 1, list: []}; - - for (const guild of client.guilds.cache.values()) { - for (const emote of guild.emojis.cache.values()) { - data.list.push({ - ref: emote.name, - id: emote.id, - name: emote.name, - requires_colons: emote.requiresColons || false, - animated: emote.animated, - url: emote.url, - guild_id: emote.guild.name, - guild_name: emote.guild.name - }); - } - } - - FileManager.write("emote-registry", data, true); -} - -// Maybe promisify this section to reduce the potential for creating callback hell? Especially if multiple questions in a row are being asked. - -// Pagination function that allows for customization via a callback. -// Define your own pages outside the function because this only manages the actual turning of pages. -$.paginate = async ( - message: Message, - senderID: string, - total: number, - callback: (page: number) => void, - duration = 60000 -) => { - let page = 0; - const turn = (amount: number) => { - page += amount; - - if (page < 0) page += total; - else if (page >= total) page -= total; - - callback(page); - }; - const BACKWARDS_EMOJI = "⬅️"; - const FORWARDS_EMOJI = "➡️"; - const handle = (emote: string, reacterID: string) => { - switch (emote) { - case BACKWARDS_EMOJI: - turn(-1); - break; - case FORWARDS_EMOJI: - turn(1); - break; - } - }; - - // Listen for reactions and call the handler. - let backwardsReaction = await message.react(BACKWARDS_EMOJI); - let forwardsReaction = await message.react(FORWARDS_EMOJI); - eventListeners.set(message.id, handle); - await message.awaitReactions( - (reaction, user) => { - if (user.id === senderID) { - // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. - // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. - const canDeleteEmotes = botHasPermission(message.guild, Permissions.FLAGS.MANAGE_MESSAGES); - handle(reaction.emoji.name, user.id); - - if (canDeleteEmotes) reaction.users.remove(user); - } - - return false; - }, - {time: duration} - ); - // When time's up, remove the bot's own reactions. - eventListeners.delete(message.id); - backwardsReaction.users.remove(message.author); - forwardsReaction.users.remove(message.author); -}; - -// Waits for the sender to either confirm an action or let it pass (and delete the message). -// This should probably be renamed to "confirm" now that I think of it, "prompt" is better used elsewhere. -// Append "\n*(This message will automatically be deleted after 10 seconds.)*" in the future? -$.prompt = async (message: Message, senderID: string, onConfirm: () => void, duration = 10000) => { - let isDeleted = false; - - message.react("✅"); - await message.awaitReactions( - (reaction, user) => { - if (user.id === senderID) { - if (reaction.emoji.name === "✅") { - onConfirm(); - isDeleted = true; - message.delete(); - } - } - - // CollectorFilter requires a boolean to be returned. - // My guess is that the return value of awaitReactions can be altered by making a boolean filter. - // However, because that's not my concern with this command, I don't have to worry about it. - // May as well just set it to false because I'm not concerned with collecting any reactions. - return false; - }, - {time: duration} - ); - - if (!isDeleted) message.delete(); -}; - -// A list of "channel-message" and callback pairs. Also, I imagine that the callback will be much more maintainable when discord.js v13 comes out with a dedicated message.referencedMessage property. -// Also, I'm defining it here instead of the message event because the load order screws up if you export it from there. Yeah... I'm starting to notice just how much technical debt has been built up. The command handler needs to be modularized and refactored sooner rather than later. Define all constants in one area then grab from there. -export const replyEventListeners = new Map void>(); - -// Asks the user for some input using the inline reply feature. The message here is a message you send beforehand. -// If the reply is rejected, reply with an error message (when stable support comes from discord.js). -// Append "\n*(Note: Make sure to use Discord's inline reply feature or this won't work!)*" in the future? And also the "you can now reply to this message" edit. -$.ask = async ( - message: Message, - senderID: string, - condition: (reply: string) => boolean, - onSuccess: () => void, - onReject: () => string, - timeout = 60000 -) => { - const referenceID = `${message.channel.id}-${message.id}`; - - replyEventListeners.set(referenceID, (reply) => { - if (reply.author.id === senderID) { - if (condition(reply.content)) { - onSuccess(); - replyEventListeners.delete(referenceID); - } else { - reply.reply(onReject()); - } - } - }); - - setTimeout(() => { - replyEventListeners.set(referenceID, (reply) => { - reply.reply("that action timed out, try using the command again"); - replyEventListeners.delete(referenceID); - }); - }, timeout); -}; - -$.askYesOrNo = (message: Message, senderID: string, timeout = 30000): Promise => { - return new Promise(async (resolve, reject) => { - let isDeleted = false; - - await message.react("✅"); - message.react("❌"); - await message.awaitReactions( - (reaction, user) => { - if (user.id === senderID) { - const isCheckReacted = reaction.emoji.name === "✅"; - - if (isCheckReacted || reaction.emoji.name === "❌") { - resolve(isCheckReacted); - isDeleted = true; - message.delete(); - } - } - - return false; - }, - {time: timeout} - ); - - if (!isDeleted) { - message.delete(); - reject("Prompt timed out."); - } - }); -}; - -// This MUST be split into an array. These emojis are made up of several characters each, adding up to 29 in length. -const multiNumbers = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"]; - -// This will bring up an option to let the user choose between one option out of many. -// This definitely needs a single callback alternative, because using the numerical version isn't actually that uncommon of a pattern. -$.askMultipleChoice = async (message: Message, senderID: string, callbackStack: (() => void)[], timeout = 90000) => { - if (callbackStack.length > multiNumbers.length) { - message.channel.send( - `\`ERROR: The amount of callbacks in "askMultipleChoice" must not exceed the total amount of allowed options (${multiNumbers.length})!\`` - ); - return; - } - - let isDeleted = false; - - for (let i = 0; i < callbackStack.length; i++) { - await message.react(multiNumbers[i]); - } - - await message.awaitReactions( - (reaction, user) => { - if (user.id === senderID) { - const index = multiNumbers.indexOf(reaction.emoji.name); - - if (index !== -1) { - callbackStack[index](); - isDeleted = true; - message.delete(); - } - } - - return false; - }, - {time: timeout} - ); - - if (!isDeleted) message.delete(); -}; - -$.getMemberByUsername = async (guild: Guild, username: string) => { - return ( - await guild.members.fetch({ - query: username, - limit: 1 - }) - ).first(); -}; - -/** Convenience function to handle false cases automatically. */ -$.callMemberByUsername = async (message: Message, username: string, onSuccess: (member: GuildMember) => void) => { - const guild = message.guild; - const send = message.channel.send; - - if (guild) { - const member = await $.getMemberByUsername(guild, username); - - if (member) onSuccess(member); - else send(`Couldn't find a user by the name of \`${username}\`!`); - } else send("You must execute this command in a server!"); -}; +// Library for pure functions /** - * Splits a command by spaces while accounting for quotes which capture string arguments. - * - `\"` = `"` - * - `\\` = `\` + * Pluralises a word and chooses a suffix attached to the root provided. + * - pluralise("credit", "s") = credit/credits + * - pluralise("part", "ies", "y") = party/parties + * - pluralise("sheep") = sheep */ -export function parseArgs(line: string): string[] { - let result = []; - let selection = ""; - let inString = false; - let isEscaped = false; +export function pluralise(value: number, word: string, plural = "", singular = "", excludeNumber = false): string { + let result = excludeNumber ? "" : `${value} `; - for (let c of line) { - if (isEscaped) { - if (['"', "\\"].includes(c)) selection += c; - else selection += "\\" + c; - - isEscaped = false; - } else if (c === "\\") isEscaped = true; - else if (c === '"') inString = !inString; - else if (c === " " && !inString) { - result.push(selection); - selection = ""; - } else selection += c; - } - - if (selection.length > 0) result.push(selection); + if (value === 1) result += word + singular; + else result += word + plural; return result; } /** - * Allows you to store a template string with variable markers and parse it later. - * - Use `%name%` for variables - * - `%%` = `%` - * - If the invalid token is null/undefined, nothing is changed. + * Pluralises a word for changes. + * - (-1).pluraliseSigned() = '-1 credits' + * - (0).pluraliseSigned() = '+0 credits' + * - (1).pluraliseSigned() = '+1 credit' */ -export function parseVars(line: string, definitions: {[key: string]: string}, invalid: string | null = ""): string { - let result = ""; - let inVariable = false; - let token = ""; - - for (const c of line) { - if (c === "%") { - if (inVariable) { - if (token === "") result += "%"; - else { - if (token in definitions) result += definitions[token]; - else if (invalid === null) result += `%${token}%`; - else result += invalid; - - token = ""; - } - } - - inVariable = !inVariable; - } else if (inVariable) token += c; - else result += c; - } - - return result; +export function pluraliseSigned( + value: number, + word: string, + plural = "", + singular = "", + excludeNumber = false +): string { + const sign = value >= 0 ? "+" : ""; + return `${sign}${pluralise(value, word, plural, singular, excludeNumber)}`; } -export function isType(value: any, type: any): boolean { - if (value === undefined && type === undefined) return true; - else if (value === null && type === null) return true; - else return value !== undefined && value !== null && value.constructor === type; +export function replaceAll(text: string, before: string, after: string): string { + while (text.indexOf(before) !== -1) text = text.replace(before, after); + return text; +} + +export function toTitleCase(text: string): string { + return text.replace(/([^\W_]+[^\s-]*) */g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()); +} + +/** Returns a random element from this array. */ +export function random(array: T[]): T { + return array[Math.floor(Math.random() * array.length)]; } /** - * Checks a value to see if it matches the fallback's type, otherwise returns the fallback. - * For the purposes of the templates system, this function will only check array types, objects should be checked under their own type (as you'd do anyway with something like a User object). - * If at any point the value doesn't match the data structure provided, the fallback is returned. - * Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this! + * Splits up this array into a specified length. + * `$([1,2,3,4,5,6,7,8,9,10]).split(3)` = `[[1,2,3],[4,5,6],[7,8,9],[10]]` */ -export function select(value: any, fallback: T, type: Function, isArray = false): T { - if (isArray && isType(value, Array)) { - for (let item of value) if (!isType(item, type)) return fallback; - return value; - } else { - if (isType(value, type)) return value; - else return fallback; - } +export function split(array: T[], lengthOfEachSection: number): T[][] { + const amountOfSections = Math.ceil(array.length / lengthOfEachSection); + const sections = new Array(amountOfSections); + + for (let index = 0; index < amountOfSections; index++) + sections[index] = array.slice(index * lengthOfEachSection, (index + 1) * lengthOfEachSection); + + return sections; } - -export function clean(text: any) { - if (typeof text === "string") - return text.replace(/`/g, "`" + String.fromCharCode(8203)).replace(/@/g, "@" + String.fromCharCode(8203)); - else return text; -} - -export function trimArray(arr: any, maxLen = 10) { - if (arr.length > maxLen) { - const len = arr.length - maxLen; - arr = arr.slice(0, maxLen); - arr.push(`${len} more...`); - } - return arr; -} - -export function formatBytes(bytes: any) { - if (bytes === 0) return "0 Bytes"; - const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; - const i = Math.floor(Math.log(bytes) / Math.log(1024)); - return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`; -} - -export function getContent(url: any) { - return new Promise((resolve, reject) => { - get(url, (res: {resume?: any; setEncoding?: any; on?: any; statusCode?: any}) => { - const {statusCode} = res; - if (statusCode !== 200) { - res.resume(); - reject(`Request failed. Status code: ${statusCode}`); - } - res.setEncoding("utf8"); - let rawData = ""; - res.on("data", (chunk: string) => { - rawData += chunk; - }); - res.on("end", () => { - try { - const parsedData = JSON.parse(rawData); - resolve(parsedData); - } catch (e) { - reject(`Error: ${e.message}`); - } - }); - }).on("error", (err: {message: any}) => { - reject(`Error: ${err.message}`); - }); - }); -} - -export interface GenericJSON { - [key: string]: any; -} - -export abstract class GenericStructure { - private __meta__ = "generic"; - - constructor(tag?: string) { - this.__meta__ = tag || this.__meta__; - } - - public save(asynchronous = true) { - const tag = this.__meta__; - /// @ts-ignore - delete this.__meta__; - FileManager.write(tag, this, asynchronous); - this.__meta__ = tag; - } -} - -// A 50% chance would be "Math.random() < 0.5" because Math.random() can be [0, 1), so to make two equal ranges, you'd need [0, 0.5)U[0.5, 1). -// Similar logic would follow for any other percentage. Math.random() < 1 is always true (100% chance) and Math.random() < 0 is always false (0% chance). -export const Random = { - num: (min: number, max: number) => Math.random() * (max - min) + min, - int: (min: number, max: number) => Math.floor(Random.num(min, max)), - chance: (decimal: number) => Math.random() < decimal, - sign: (number = 1) => number * (Random.chance(0.5) ? -1 : 1), - deviation: (base: number, deviation: number) => Random.num(base - deviation, base + deviation) -}; diff --git a/src/core/libd.ts b/src/core/libd.ts new file mode 100644 index 0000000..9c44fa4 --- /dev/null +++ b/src/core/libd.ts @@ -0,0 +1,418 @@ +// Library for Discord-specific functions +import {Message, Guild, GuildMember, Permissions} from "discord.js"; +import {get} from "https"; +import FileManager from "./storage"; +import {eventListeners} from "../events/messageReactionRemove"; +import {client} from "../index"; +import {EmoteRegistryDump} from "./structures"; + +export function botHasPermission(guild: Guild | null, permission: number): boolean { + return !!guild?.me?.hasPermission(permission); +} + +export function updateGlobalEmoteRegistry(): void { + const data: EmoteRegistryDump = {version: 1, list: []}; + + for (const guild of client.guilds.cache.values()) { + for (const emote of guild.emojis.cache.values()) { + data.list.push({ + ref: emote.name, + id: emote.id, + name: emote.name, + requires_colons: emote.requiresColons || false, + animated: emote.animated, + url: emote.url, + guild_id: emote.guild.name, + guild_name: emote.guild.name + }); + } + } + + FileManager.write("emote-registry", data, true); +} + +// Maybe promisify this section to reduce the potential for creating callback hell? Especially if multiple questions in a row are being asked. + +// Pagination function that allows for customization via a callback. +// Define your own pages outside the function because this only manages the actual turning of pages. +export async function paginate( + message: Message, + senderID: string, + total: number, + callback: (page: number) => void, + duration = 60000 +) { + let page = 0; + const turn = (amount: number) => { + page += amount; + + if (page < 0) page += total; + else if (page >= total) page -= total; + + callback(page); + }; + const BACKWARDS_EMOJI = "⬅️"; + const FORWARDS_EMOJI = "➡️"; + const handle = (emote: string, reacterID: string) => { + switch (emote) { + case BACKWARDS_EMOJI: + turn(-1); + break; + case FORWARDS_EMOJI: + turn(1); + break; + } + }; + + // Listen for reactions and call the handler. + let backwardsReaction = await message.react(BACKWARDS_EMOJI); + let forwardsReaction = await message.react(FORWARDS_EMOJI); + eventListeners.set(message.id, handle); + await message.awaitReactions( + (reaction, user) => { + if (user.id === senderID) { + // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. + // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. + const canDeleteEmotes = botHasPermission(message.guild, Permissions.FLAGS.MANAGE_MESSAGES); + handle(reaction.emoji.name, user.id); + + if (canDeleteEmotes) reaction.users.remove(user); + } + + return false; + }, + {time: duration} + ); + // When time's up, remove the bot's own reactions. + eventListeners.delete(message.id); + backwardsReaction.users.remove(message.author); + forwardsReaction.users.remove(message.author); +} + +// Waits for the sender to either confirm an action or let it pass (and delete the message). +// This should probably be renamed to "confirm" now that I think of it, "prompt" is better used elsewhere. +// Append "\n*(This message will automatically be deleted after 10 seconds.)*" in the future? +export async function prompt(message: Message, senderID: string, onConfirm: () => void, duration = 10000) { + let isDeleted = false; + + message.react("✅"); + await message.awaitReactions( + (reaction, user) => { + if (user.id === senderID) { + if (reaction.emoji.name === "✅") { + onConfirm(); + isDeleted = true; + message.delete(); + } + } + + // CollectorFilter requires a boolean to be returned. + // My guess is that the return value of awaitReactions can be altered by making a boolean filter. + // However, because that's not my concern with this command, I don't have to worry about it. + // May as well just set it to false because I'm not concerned with collecting any reactions. + return false; + }, + {time: duration} + ); + + if (!isDeleted) message.delete(); +} + +// A list of "channel-message" and callback pairs. Also, I imagine that the callback will be much more maintainable when discord.js v13 comes out with a dedicated message.referencedMessage property. +// Also, I'm defining it here instead of the message event because the load order screws up if you export it from there. Yeah... I'm starting to notice just how much technical debt has been built up. The command handler needs to be modularized and refactored sooner rather than later. Define all constants in one area then grab from there. +export const replyEventListeners = new Map void>(); + +// Asks the user for some input using the inline reply feature. The message here is a message you send beforehand. +// If the reply is rejected, reply with an error message (when stable support comes from discord.js). +// Append "\n*(Note: Make sure to use Discord's inline reply feature or this won't work!)*" in the future? And also the "you can now reply to this message" edit. +export function ask( + message: Message, + senderID: string, + condition: (reply: string) => boolean, + onSuccess: () => void, + onReject: () => string, + timeout = 60000 +) { + const referenceID = `${message.channel.id}-${message.id}`; + + replyEventListeners.set(referenceID, (reply) => { + if (reply.author.id === senderID) { + if (condition(reply.content)) { + onSuccess(); + replyEventListeners.delete(referenceID); + } else { + reply.reply(onReject()); + } + } + }); + + setTimeout(() => { + replyEventListeners.set(referenceID, (reply) => { + reply.reply("that action timed out, try using the command again"); + replyEventListeners.delete(referenceID); + }); + }, timeout); +} + +export function askYesOrNo(message: Message, senderID: string, timeout = 30000): Promise { + return new Promise(async (resolve, reject) => { + let isDeleted = false; + + await message.react("✅"); + message.react("❌"); + await message.awaitReactions( + (reaction, user) => { + if (user.id === senderID) { + const isCheckReacted = reaction.emoji.name === "✅"; + + if (isCheckReacted || reaction.emoji.name === "❌") { + resolve(isCheckReacted); + isDeleted = true; + message.delete(); + } + } + + return false; + }, + {time: timeout} + ); + + if (!isDeleted) { + message.delete(); + reject("Prompt timed out."); + } + }); +} + +// This MUST be split into an array. These emojis are made up of several characters each, adding up to 29 in length. +const multiNumbers = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"]; + +// This will bring up an option to let the user choose between one option out of many. +// This definitely needs a single callback alternative, because using the numerical version isn't actually that uncommon of a pattern. +export async function askMultipleChoice( + message: Message, + senderID: string, + callbackStack: (() => void)[], + timeout = 90000 +) { + if (callbackStack.length > multiNumbers.length) { + message.channel.send( + `\`ERROR: The amount of callbacks in "askMultipleChoice" must not exceed the total amount of allowed options (${multiNumbers.length})!\`` + ); + return; + } + + let isDeleted = false; + + for (let i = 0; i < callbackStack.length; i++) { + await message.react(multiNumbers[i]); + } + + await message.awaitReactions( + (reaction, user) => { + if (user.id === senderID) { + const index = multiNumbers.indexOf(reaction.emoji.name); + + if (index !== -1) { + callbackStack[index](); + isDeleted = true; + message.delete(); + } + } + + return false; + }, + {time: timeout} + ); + + if (!isDeleted) message.delete(); +} + +export async function getMemberByUsername(guild: Guild, username: string) { + return ( + await guild.members.fetch({ + query: username, + limit: 1 + }) + ).first(); +} + +/** Convenience function to handle false cases automatically. */ +export async function callMemberByUsername( + message: Message, + username: string, + onSuccess: (member: GuildMember) => void +) { + const guild = message.guild; + const send = message.channel.send; + + if (guild) { + const member = await getMemberByUsername(guild, username); + + if (member) onSuccess(member); + else send(`Couldn't find a user by the name of \`${username}\`!`); + } else send("You must execute this command in a server!"); +} + +/** + * Splits a command by spaces while accounting for quotes which capture string arguments. + * - `\"` = `"` + * - `\\` = `\` + */ +export function parseArgs(line: string): string[] { + let result = []; + let selection = ""; + let inString = false; + let isEscaped = false; + + for (let c of line) { + if (isEscaped) { + if (['"', "\\"].includes(c)) selection += c; + else selection += "\\" + c; + + isEscaped = false; + } else if (c === "\\") isEscaped = true; + else if (c === '"') inString = !inString; + else if (c === " " && !inString) { + result.push(selection); + selection = ""; + } else selection += c; + } + + if (selection.length > 0) result.push(selection); + + return result; +} + +/** + * Allows you to store a template string with variable markers and parse it later. + * - Use `%name%` for variables + * - `%%` = `%` + * - If the invalid token is null/undefined, nothing is changed. + */ +export function parseVars(line: string, definitions: {[key: string]: string}, invalid: string | null = ""): string { + let result = ""; + let inVariable = false; + let token = ""; + + for (const c of line) { + if (c === "%") { + if (inVariable) { + if (token === "") result += "%"; + else { + if (token in definitions) result += definitions[token]; + else if (invalid === null) result += `%${token}%`; + else result += invalid; + + token = ""; + } + } + + inVariable = !inVariable; + } else if (inVariable) token += c; + else result += c; + } + + return result; +} + +export function isType(value: any, type: any): boolean { + if (value === undefined && type === undefined) return true; + else if (value === null && type === null) return true; + else return value !== undefined && value !== null && value.constructor === type; +} + +/** + * Checks a value to see if it matches the fallback's type, otherwise returns the fallback. + * For the purposes of the templates system, this function will only check array types, objects should be checked under their own type (as you'd do anyway with something like a User object). + * If at any point the value doesn't match the data structure provided, the fallback is returned. + * Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this! + */ +export function select(value: any, fallback: T, type: Function, isArray = false): T { + if (isArray && isType(value, Array)) { + for (let item of value) if (!isType(item, type)) return fallback; + return value; + } else { + if (isType(value, type)) return value; + else return fallback; + } +} + +export function clean(text: any) { + if (typeof text === "string") + return text.replace(/`/g, "`" + String.fromCharCode(8203)).replace(/@/g, "@" + String.fromCharCode(8203)); + else return text; +} + +export function trimArray(arr: any, maxLen = 10) { + if (arr.length > maxLen) { + const len = arr.length - maxLen; + arr = arr.slice(0, maxLen); + arr.push(`${len} more...`); + } + return arr; +} + +export function formatBytes(bytes: any) { + if (bytes === 0) return "0 Bytes"; + const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`; +} + +export function getContent(url: any) { + return new Promise((resolve, reject) => { + get(url, (res: {resume?: any; setEncoding?: any; on?: any; statusCode?: any}) => { + const {statusCode} = res; + if (statusCode !== 200) { + res.resume(); + reject(`Request failed. Status code: ${statusCode}`); + } + res.setEncoding("utf8"); + let rawData = ""; + res.on("data", (chunk: string) => { + rawData += chunk; + }); + res.on("end", () => { + try { + const parsedData = JSON.parse(rawData); + resolve(parsedData); + } catch (e) { + reject(`Error: ${e.message}`); + } + }); + }).on("error", (err: {message: any}) => { + reject(`Error: ${err.message}`); + }); + }); +} + +export interface GenericJSON { + [key: string]: any; +} + +export abstract class GenericStructure { + private __meta__ = "generic"; + + constructor(tag?: string) { + this.__meta__ = tag || this.__meta__; + } + + public save(asynchronous = true) { + const tag = this.__meta__; + /// @ts-ignore + delete this.__meta__; + FileManager.write(tag, this, asynchronous); + this.__meta__ = tag; + } +} + +// A 50% chance would be "Math.random() < 0.5" because Math.random() can be [0, 1), so to make two equal ranges, you'd need [0, 0.5)U[0.5, 1). +// Similar logic would follow for any other percentage. Math.random() < 1 is always true (100% chance) and Math.random() < 0 is always false (0% chance). +export const Random = { + num: (min: number, max: number) => Math.random() * (max - min) + min, + int: (min: number, max: number) => Math.floor(Random.num(min, max)), + chance: (decimal: number) => Math.random() < decimal, + sign: (number = 1) => number * (Random.chance(0.5) ? -1 : 1), + deviation: (base: number, deviation: number) => Random.num(base - deviation, base + deviation) +}; diff --git a/src/core/structures.ts b/src/core/structures.ts index 74e5adf..07d8f5c 100644 --- a/src/core/structures.ts +++ b/src/core/structures.ts @@ -1,5 +1,5 @@ import FileManager from "./storage"; -import {select, GenericJSON, GenericStructure} from "./lib"; +import {select, GenericJSON, GenericStructure} from "./libd"; import {watch} from "fs"; import {Guild as DiscordGuild, Snowflake} from "discord.js"; diff --git a/src/core/wrappers.ts b/src/core/wrappers.ts deleted file mode 100644 index edf9b35..0000000 --- a/src/core/wrappers.ts +++ /dev/null @@ -1,73 +0,0 @@ -export class GenericWrapper { - protected readonly value: T; - - public constructor(value: T) { - this.value = value; - } -} - -export class NumberWrapper extends GenericWrapper { - /** - * Pluralises a word and chooses a suffix attached to the root provided. - * - pluralise("credit", "s") = credit/credits - * - pluralise("part", "ies", "y") = party/parties - * - pluralise("sheep") = sheep - */ - public pluralise(word: string, plural = "", singular = "", excludeNumber = false): string { - let result = excludeNumber ? "" : `${this.value} `; - - if (this.value === 1) result += word + singular; - else result += word + plural; - - return result; - } - - /** - * Pluralises a word for changes. - * - (-1).pluraliseSigned() = '-1 credits' - * - (0).pluraliseSigned() = '+0 credits' - * - (1).pluraliseSigned() = '+1 credit' - */ - public pluraliseSigned(word: string, plural = "", singular = "", excludeNumber = false): string { - const sign = this.value >= 0 ? "+" : ""; - return `${sign}${this.pluralise(word, plural, singular, excludeNumber)}`; - } -} - -export class StringWrapper extends GenericWrapper { - public replaceAll(before: string, after: string): string { - let result = this.value; - - while (result.indexOf(before) !== -1) result = result.replace(before, after); - - return result; - } - - public toTitleCase(): string { - return this.value.replace( - /([^\W_]+[^\s-]*) */g, - (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase() - ); - } -} - -export class ArrayWrapper extends GenericWrapper { - /** Returns a random element from this array. */ - public random(): T { - return this.value[Math.floor(Math.random() * this.value.length)]; - } - - /** - * Splits up this array into a specified length. - * `$([1,2,3,4,5,6,7,8,9,10]).split(3)` = `[[1,2,3],[4,5,6],[7,8,9],[10]]` - */ - public split(lengthOfEachSection: number): T[][] { - const amountOfSections = Math.ceil(this.value.length / lengthOfEachSection); - const sections: T[][] = new Array(amountOfSections); - - for (let index = 0; index < amountOfSections; index++) - sections[index] = this.value.slice(index * lengthOfEachSection, (index + 1) * lengthOfEachSection); - - return sections; - } -} diff --git a/src/events/emojiCreate.ts b/src/events/emojiCreate.ts index 2af3f82..92ef88e 100644 --- a/src/events/emojiCreate.ts +++ b/src/events/emojiCreate.ts @@ -1,5 +1,5 @@ import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/lib"; +import {updateGlobalEmoteRegistry} from "../core/libd"; export default new Event<"emojiCreate">({ on(emote) { diff --git a/src/events/emojiDelete.ts b/src/events/emojiDelete.ts index 08687c9..dc34190 100644 --- a/src/events/emojiDelete.ts +++ b/src/events/emojiDelete.ts @@ -1,5 +1,5 @@ import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/lib"; +import {updateGlobalEmoteRegistry} from "../core/libd"; export default new Event<"emojiDelete">({ on() { diff --git a/src/events/emojiUpdate.ts b/src/events/emojiUpdate.ts index 7dbe125..bd0b6b0 100644 --- a/src/events/emojiUpdate.ts +++ b/src/events/emojiUpdate.ts @@ -1,5 +1,5 @@ import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/lib"; +import {updateGlobalEmoteRegistry} from "../core/libd"; export default new Event<"emojiUpdate">({ on() { diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts index 61fdec5..480bd4d 100644 --- a/src/events/guildCreate.ts +++ b/src/events/guildCreate.ts @@ -1,5 +1,5 @@ import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/lib"; +import {updateGlobalEmoteRegistry} from "../core/libd"; export default new Event<"guildCreate">({ on() { diff --git a/src/events/guildDelete.ts b/src/events/guildDelete.ts index a8e7eaa..755a049 100644 --- a/src/events/guildDelete.ts +++ b/src/events/guildDelete.ts @@ -1,5 +1,5 @@ import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/lib"; +import {updateGlobalEmoteRegistry} from "../core/libd"; export default new Event<"guildDelete">({ on() { diff --git a/src/events/message.ts b/src/events/message.ts index 701db08..352bb57 100644 --- a/src/events/message.ts +++ b/src/events/message.ts @@ -3,7 +3,7 @@ import Command, {loadableCommands} from "../core/command"; import {hasPermission, getPermissionLevel, PermissionNames} from "../core/permissions"; import {Permissions} from "discord.js"; import {getPrefix} from "../core/structures"; -import $, {replyEventListeners} from "../core/lib"; +import {replyEventListeners} from "../core/libd"; import quote from "../modules/message_embed"; export default new Event<"message">({ @@ -132,20 +132,14 @@ export default new Event<"message">({ // The purpose of using $.bind($) is to clone the function so as to not modify the original $. // The cloned function doesn't copy the properties, so Object.assign() is used. // Object.assign() modifies the first element and returns that, the second element applies its properties and the third element applies its own overriding the second one. - command.execute( - Object.assign( - $.bind($), - { - args: params, - author: message.author, - channel: message.channel, - client: message.client, - guild: message.guild, - member: message.member, - message: message - }, - $ - ) - ); + command.execute({ + args: params, + author: message.author, + channel: message.channel, + client: message.client, + guild: message.guild, + member: message.member, + message: message + }); } }); diff --git a/src/events/messageReactionRemove.ts b/src/events/messageReactionRemove.ts index 10b1af2..4b3dbb2 100644 --- a/src/events/messageReactionRemove.ts +++ b/src/events/messageReactionRemove.ts @@ -1,6 +1,6 @@ import Event from "../core/event"; import {Permissions} from "discord.js"; -import {botHasPermission} from "../core/lib"; +import {botHasPermission} from "../core/libd"; // A list of message ID and callback pairs. You get the emote name and ID of the user reacting. export const eventListeners: Map void> = new Map(); diff --git a/src/events/ready.ts b/src/events/ready.ts index 9e2ec12..a4e1b9c 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -1,7 +1,7 @@ import Event from "../core/event"; import {client} from "../index"; import {Config} from "../core/structures"; -import {updateGlobalEmoteRegistry} from "../core/lib"; +import {updateGlobalEmoteRegistry} from "../core/libd"; export default new Event<"ready">({ once() { From 475ecb3d5dee5a92bfc9ae6e99ca66b6adb6464b Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 30 Mar 2021 05:54:52 -0500 Subject: [PATCH 06/30] Reworked permission handling --- src/commands/admin.ts | 20 +++---- src/commands/help.ts | 8 +-- src/commands/template.ts | 10 ++-- src/core/command.ts | 8 ++- src/core/permissions.ts | 113 ++++++++++++++++++--------------------- src/events/message.ts | 10 ++-- src/globals.ts | 12 +++++ 7 files changed, 95 insertions(+), 86 deletions(-) diff --git a/src/commands/admin.ts b/src/commands/admin.ts index 9545799..ff2d968 100644 --- a/src/commands/admin.ts +++ b/src/commands/admin.ts @@ -1,7 +1,7 @@ import Command, {handler} from "../core/command"; import {botHasPermission, clean} from "../core/libd"; import {Config, Storage} from "../core/structures"; -import {PermissionNames, getPermissionLevel} from "../core/permissions"; +import {getPermissionLevel, getPermissionName} from "../core/permissions"; import {Permissions} from "discord.js"; import * as discord from "discord.js"; import {logs} from "../globals"; @@ -30,14 +30,14 @@ export default new Command({ } const permLevel = getPermissionLevel($.member); $.channel.send( - `${$.author.toString()}, your permission level is \`${PermissionNames[permLevel]}\` (${permLevel}).` + `${$.author.toString()}, your permission level is \`${getPermissionName(permLevel)}\` (${permLevel}).` ); }, subcommands: { set: new Command({ description: "Set different per-guild settings for the bot.", run: "You have to specify the option you want to set.", - permission: Command.PERMISSIONS.ADMIN, + permission: PERMISSIONS.ADMIN, subcommands: { prefix: new Command({ description: "Set a custom prefix for your guild. Removes your custom prefix if none is provided.", @@ -61,7 +61,7 @@ export default new Command({ }), diag: new Command({ description: 'Requests a debug log with the "info" verbosity level.', - permission: Command.PERMISSIONS.BOT_SUPPORT, + permission: PERMISSIONS.BOT_SUPPORT, async run($) { $.channel.send(getLogBuffer("info")); }, @@ -82,7 +82,7 @@ export default new Command({ }), status: new Command({ description: "Changes the bot's status.", - permission: Command.PERMISSIONS.BOT_SUPPORT, + permission: PERMISSIONS.BOT_SUPPORT, async run($) { $.channel.send("Setting status to `online`..."); }, @@ -101,7 +101,7 @@ export default new Command({ }), purge: new Command({ description: "Purges bot messages.", - permission: Command.PERMISSIONS.BOT_SUPPORT, + permission: PERMISSIONS.BOT_SUPPORT, async run($) { if ($.message.channel instanceof discord.DMChannel) { return; @@ -142,7 +142,7 @@ export default new Command({ eval: new Command({ description: "Evaluate code.", usage: "", - permission: Command.PERMISSIONS.BOT_OWNER, + permission: PERMISSIONS.BOT_OWNER, // You have to bring everything into scope to use them. AFAIK, there isn't a more maintainable way to do this, but at least TS will let you know if anything gets removed. async run({args, author, channel, client, guild, member, message}) { try { @@ -158,7 +158,7 @@ export default new Command({ }), nick: new Command({ description: "Change the bot's nickname.", - permission: Command.PERMISSIONS.BOT_SUPPORT, + permission: PERMISSIONS.BOT_SUPPORT, async run($) { const nickName = $.args.join(" "); await $.guild?.me?.setNickname(nickName); @@ -169,7 +169,7 @@ export default new Command({ }), guilds: new Command({ description: "Shows a list of all guilds the bot is a member of.", - permission: Command.PERMISSIONS.BOT_SUPPORT, + permission: PERMISSIONS.BOT_SUPPORT, async run($) { const guildList = $.client.guilds.cache.array().map((e) => e.name); $.channel.send(guildList); @@ -177,7 +177,7 @@ export default new Command({ }), activity: new Command({ description: "Set the activity of the bot.", - permission: Command.PERMISSIONS.BOT_SUPPORT, + permission: PERMISSIONS.BOT_SUPPORT, usage: " ", async run($) { $.client.user?.setActivity(".help", { diff --git a/src/commands/help.ts b/src/commands/help.ts index 02b8650..71f2ae3 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,7 +1,7 @@ import Command from "../core/command"; import {toTitleCase} from "../core/lib"; import {loadableCommands, categories} from "../core/command"; -import {PermissionNames} from "../core/permissions"; +import {getPermissionName} from "../core/permissions"; export default new Command({ description: "Lists all commands. If a command is specified, their arguments are listed as well.", @@ -44,7 +44,7 @@ export default new Command({ if (command.originalCommandName) header = command.originalCommandName; else console.warn(`originalCommandName isn't defined for ${header}?!`); - let permLevel = command.permission ?? Command.PERMISSIONS.NONE; + let permLevel = command.permission ?? 0; let usage = command.usage; let invalid = false; @@ -135,7 +135,9 @@ export default new Command({ } $.channel.send( - `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${selectedCategory}\`\nPermission Required: \`${PermissionNames[permLevel]}\` (${permLevel})\nDescription: ${command.description}\n${append}`, + `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${selectedCategory}\`\nPermission Required: \`${getPermissionName( + permLevel + )}\` (${permLevel})\nDescription: ${command.description}\n${append}`, {split: true} ); } diff --git a/src/commands/template.ts b/src/commands/template.ts index 686fe4a..a97e8e2 100644 --- a/src/commands/template.ts +++ b/src/commands/template.ts @@ -5,7 +5,7 @@ export default new Command({ 'This is a template/testing command providing common functionality. Remove what you don\'t need, and rename/delete this file to generate a fresh command file here. This command should be automatically excluded from the help command. The "usage" parameter (string) overrides the default usage for the help command. The "endpoint" parameter (boolean) prevents further arguments from being passed. Also, as long as you keep the run function async, it\'ll return a promise allowing the program to automatically catch any synchronous errors. However, you\'ll have to do manual error handling if you go the then and catch route.', endpoint: false, usage: "", - permission: null, + permission: -1, aliases: [], async run($) { // code @@ -16,7 +16,7 @@ export default new Command({ 'This is a named subcommand, meaning that the key name is what determines the keyword to use. With default settings for example, "$test layer".', endpoint: false, usage: "", - permission: null, + permission: -1, aliases: [], async run($) { // code @@ -28,7 +28,7 @@ export default new Command({ 'This is the subcommand for getting users by pinging them or copying their ID. With default settings for example, "$test 237359961842253835". The argument will be a user object and won\'t run if no user is found by that ID.', endpoint: false, usage: "", - permission: null, + permission: -1, async run($) { // code } @@ -38,7 +38,7 @@ export default new Command({ 'This is a numeric subcommand, meaning that any type of number (excluding Infinity/NaN) will route to this command if present. With default settings for example, "$test -5.2". The argument with the number is already parsed so you can just use it without converting it.', endpoint: false, usage: "", - permission: null, + permission: -1, async run($) { // code } @@ -48,7 +48,7 @@ export default new Command({ "This is a generic subcommand, meaning that if there isn't a more specific subcommand that's called, it falls to this. With default settings for example, \"$test reeee\".", endpoint: false, usage: "", - permission: null, + permission: -1, async run($) { // code } diff --git a/src/core/command.ts b/src/core/command.ts index 5ee8a5b..c40d5a0 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,7 +1,6 @@ import {parseVars} from "./libd"; import {Collection} from "discord.js"; import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember} from "discord.js"; -import {PERMISSIONS} from "./permissions"; import {getPrefix} from "../core/structures"; import glob from "glob"; @@ -19,7 +18,7 @@ interface CommandOptions { description?: string; endpoint?: boolean; usage?: string; - permission?: PERMISSIONS | null; + permission?: number; aliases?: string[]; run?: (($: CommandMenu) => Promise) | string; subcommands?: {[key: string]: Command}; @@ -40,7 +39,7 @@ export default class Command { public readonly description: string; public readonly endpoint: boolean; public readonly usage: string; - public readonly permission: PERMISSIONS | null; + public readonly permission: number; // -1 (default) indicates to inherit, 0 is the lowest rank, 1 is second lowest rank, and so on. public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. public originalCommandName: string | null; // If the command is an alias, what's the original name? public run: (($: CommandMenu) => Promise) | string; @@ -49,13 +48,12 @@ export default class Command { public number: Command | null; public any: Command | null; public static readonly TYPES = TYPES; - public static readonly PERMISSIONS = PERMISSIONS; constructor(options?: CommandOptions) { this.description = options?.description || "No description."; this.endpoint = options?.endpoint || false; this.usage = options?.usage || ""; - this.permission = options?.permission ?? null; + this.permission = options?.permission ?? -1; this.aliases = options?.aliases ?? []; this.originalCommandName = null; this.run = options?.run || "No action was set on this command!"; diff --git a/src/core/permissions.ts b/src/core/permissions.ts index 336ffb5..c4c89ba 100644 --- a/src/core/permissions.ts +++ b/src/core/permissions.ts @@ -1,75 +1,68 @@ -import {GuildMember, Permissions} from "discord.js"; +import {User, GuildMember, Permissions} from "discord.js"; import {Config} from "./structures"; -export enum PERMISSIONS { - NONE, - MOD, - ADMIN, - OWNER, - BOT_SUPPORT, - BOT_ADMIN, - BOT_OWNER +interface PermissionLevel { + name: string; + check: (user: User, member: GuildMember | null) => boolean; } -export const PermissionNames = [ - "User", - "Moderator", - "Administrator", - "Server Owner", - "Bot Support", - "Bot Admin", - "Bot Owner" -]; - -// Here is where you enter in the functions that check for permissions. -const PermissionChecker: ((member: GuildMember) => boolean)[] = [ - // NONE // - () => true, - - // MOD // - (member) => - member.hasPermission(Permissions.FLAGS.MANAGE_ROLES) || - member.hasPermission(Permissions.FLAGS.MANAGE_MESSAGES) || - member.hasPermission(Permissions.FLAGS.KICK_MEMBERS) || - member.hasPermission(Permissions.FLAGS.BAN_MEMBERS), - - // ADMIN // - (member) => member.hasPermission(Permissions.FLAGS.ADMINISTRATOR), - - // OWNER // - (member) => member.guild.ownerID === member.id, - - // BOT_SUPPORT // - (member) => Config.support.includes(member.id), - - // BOT_ADMIN // - (member) => Config.admins.includes(member.id), - - // BOT_OWNER // - (member) => Config.owner === member.id +export const PermissionLevels: PermissionLevel[] = [ + { + // NONE // + name: "User", + check: () => true + }, + { + // MOD // + name: "Moderator", + check: (_, member) => + !!member && + (member.hasPermission(Permissions.FLAGS.MANAGE_ROLES) || + member.hasPermission(Permissions.FLAGS.MANAGE_MESSAGES) || + member.hasPermission(Permissions.FLAGS.KICK_MEMBERS) || + member.hasPermission(Permissions.FLAGS.BAN_MEMBERS)) + }, + { + // ADMIN // + name: "Administrator", + check: (_, member) => !!member && member.hasPermission(Permissions.FLAGS.ADMINISTRATOR) + }, + { + // OWNER // + name: "Server Owner", + check: (_, member) => !!member && member.guild.ownerID === member.id + }, + { + // BOT_SUPPORT // + name: "Bot Support", + check: (user) => Config.support.includes(user.id) + }, + { + // BOT_ADMIN // + name: "Bot Admin", + check: (user) => Config.admins.includes(user.id) + }, + { + // BOT_OWNER // + name: "Bot Owner", + check: (user) => Config.owner === user.id + } ]; // After checking the lengths of these three objects, use this as the length for consistency. -const length = Object.keys(PERMISSIONS).length / 2; +const length = PermissionLevels.length; -export function hasPermission(member: GuildMember, permission: PERMISSIONS): boolean { - for (let i = length - 1; i >= permission; i--) if (PermissionChecker[i](member)) return true; +export function hasPermission(member: GuildMember, permission: number): boolean { + for (let i = length - 1; i >= permission; i--) if (PermissionLevels[i].check(member.user, member)) return true; return false; } export function getPermissionLevel(member: GuildMember): number { - for (let i = length - 1; i >= 0; i--) if (PermissionChecker[i](member)) return i; + for (let i = length - 1; i >= 0; i--) if (PermissionLevels[i].check(member.user, member)) return i; return 0; } -// Length Checking -(() => { - const lenNames = PermissionNames.length; - const lenChecker = PermissionChecker.length; - - // By transitive property, lenNames and lenChecker have to be equal to each other as well. - if (length !== lenNames || length !== lenChecker) - console.error( - `Permission object lengths aren't equal! Enum Length (${length}), Names Length (${lenNames}), and Functions Length (${lenChecker}). This WILL cause problems!` - ); -})(); +export function getPermissionName(level: number) { + if (level > length || length < 0) return "N/A"; + else return PermissionLevels[level].name; +} diff --git a/src/events/message.ts b/src/events/message.ts index 352bb57..d805ed8 100644 --- a/src/events/message.ts +++ b/src/events/message.ts @@ -1,6 +1,6 @@ import Event from "../core/event"; import Command, {loadableCommands} from "../core/command"; -import {hasPermission, getPermissionLevel, PermissionNames} from "../core/permissions"; +import {hasPermission, getPermissionLevel, getPermissionName} from "../core/permissions"; import {Permissions} from "discord.js"; import {getPrefix} from "../core/structures"; import {replyEventListeners} from "../core/libd"; @@ -91,7 +91,7 @@ export default new Event<"message">({ if (!command) return console.warn(`Command "${header}" was called but for some reason it's still undefined!`); const params: any[] = []; let isEndpoint = false; - let permLevel = command.permission ?? Command.PERMISSIONS.NONE; + let permLevel = command.permission ?? 0; for (let param of args) { if (command.endpoint) { @@ -122,7 +122,11 @@ export default new Event<"message">({ if (!hasPermission(message.member, permLevel)) { const userPermLevel = getPermissionLevel(message.member); return message.channel.send( - `You don't have access to this command! Your permission level is \`${PermissionNames[userPermLevel]}\` (${userPermLevel}), but this command requires a permission level of \`${PermissionNames[permLevel]}\` (${permLevel}).` + `You don't have access to this command! Your permission level is \`${getPermissionName( + userPermLevel + )}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName( + permLevel + )}\` (${permLevel}).` ); } diff --git a/src/globals.ts b/src/globals.ts index 3c8d4c7..4f2e377 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -2,13 +2,25 @@ import chalk from "chalk"; declare global { var IS_DEV_MODE: boolean; + var PERMISSIONS: typeof PermissionsEnum; interface Console { ready: (...data: any[]) => void; } } +enum PermissionsEnum { + NONE, + MOD, + ADMIN, + OWNER, + BOT_SUPPORT, + BOT_ADMIN, + BOT_OWNER +} + global.IS_DEV_MODE = process.argv[2] === "dev"; +global.PERMISSIONS = PermissionsEnum; const oldConsole = console; From 02c18f57c74a6155e669155c3818e10b506116d0 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 30 Mar 2021 07:16:31 -0500 Subject: [PATCH 07/30] Reworked paginate function --- src/commands/fun/subcommands/eco-shop.ts | 20 ++--- src/commands/utilities/lsemotes.ts | 32 +++---- src/core/libd.ts | 107 ++++++++++++++--------- 3 files changed, 83 insertions(+), 76 deletions(-) diff --git a/src/commands/fun/subcommands/eco-shop.ts b/src/commands/fun/subcommands/eco-shop.ts index 24ebcd7..f5c167b 100644 --- a/src/commands/fun/subcommands/eco-shop.ts +++ b/src/commands/fun/subcommands/eco-shop.ts @@ -10,7 +10,7 @@ export const ShopCommand = new Command({ description: "Displays the list of items you can buy in the shop.", async run({guild, channel, author}) { if (isAuthorized(guild, channel)) { - function getShopEmbed(selection: ShopItem[], title = "Shop") { + function getShopEmbed(selection: ShopItem[], title: string) { const fields: EmbedField[] = []; for (const item of selection) @@ -32,17 +32,15 @@ export const ShopCommand = new Command({ }; } - // In case there's just one page, omit unnecessary details. - if (ShopItems.length <= 5) channel.send(getShopEmbed(ShopItems)); - else { - const shopPages = split(ShopItems, 5); - const pageAmount = shopPages.length; - const msg = await channel.send(getShopEmbed(shopPages[0], `Shop (Page 1 of ${pageAmount})`)); + const shopPages = split(ShopItems, 5); + const pageAmount = shopPages.length; - paginate(msg, author.id, pageAmount, (page) => { - msg.edit(getShopEmbed(shopPages[page], `Shop (Page ${page + 1} of ${pageAmount})`)); - }); - } + paginate(channel, author.id, pageAmount, (page, hasMultiplePages) => { + return getShopEmbed( + shopPages[page], + hasMultiplePages ? `Shop (Page ${page + 1} of ${pageAmount})` : "Shop" + ); + }); } } }); diff --git a/src/commands/utilities/lsemotes.ts b/src/commands/utilities/lsemotes.ts index 6188439..c369ab0 100644 --- a/src/commands/utilities/lsemotes.ts +++ b/src/commands/utilities/lsemotes.ts @@ -93,33 +93,21 @@ async function displayEmoteList(emotes: GuildEmoji[], channel: TextChannel | DMC }); const sections = split(emotes, 20); const pages = sections.length; - const embed = new MessageEmbed().setTitle("**Emotes**").setColor("AQUA"); - let desc = ""; + const embed = new MessageEmbed().setColor("AQUA"); // Gather the first page (if it even exists, which it might not if there no valid emotes appear) if (pages > 0) { - for (const emote of sections[0]) { - desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`; - } + paginate(channel, author.id, pages, (page, hasMultiplePages) => { + embed.setTitle(hasMultiplePages ? `**Emotes** (Page ${page + 1} of ${pages})` : "**Emotes**"); - embed.setDescription(desc); + let desc = ""; + for (const emote of sections[page]) { + desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`; + } + embed.setDescription(desc); - if (pages > 1) { - embed.setTitle(`**Emotes** (Page 1 of ${pages})`); - const msg = await channel.send({embed}); - - paginate(msg, author.id, pages, (page) => { - let desc = ""; - for (const emote of sections[page]) { - desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`; - } - embed.setTitle(`**Emotes** (Page ${page + 1} of ${pages})`); - embed.setDescription(desc); - msg.edit(embed); - }); - } else { - channel.send({embed}); - } + return embed; + }); } else { channel.send("No valid emotes found by that query."); } diff --git a/src/core/libd.ts b/src/core/libd.ts index 9c44fa4..3cc6b04 100644 --- a/src/core/libd.ts +++ b/src/core/libd.ts @@ -1,5 +1,14 @@ // Library for Discord-specific functions -import {Message, Guild, GuildMember, Permissions} from "discord.js"; +import { + Message, + Guild, + GuildMember, + Permissions, + TextChannel, + DMChannel, + NewsChannel, + MessageOptions +} from "discord.js"; import {get} from "https"; import FileManager from "./storage"; import {eventListeners} from "../events/messageReactionRemove"; @@ -36,57 +45,69 @@ export function updateGlobalEmoteRegistry(): void { // Pagination function that allows for customization via a callback. // Define your own pages outside the function because this only manages the actual turning of pages. export async function paginate( - message: Message, + channel: TextChannel | DMChannel | NewsChannel, senderID: string, total: number, - callback: (page: number) => void, + callback: (page: number, hasMultiplePages: boolean) => MessageOptions & {split?: false}, duration = 60000 ) { - let page = 0; - const turn = (amount: number) => { - page += amount; + const hasMultiplePages = total > 1; + const message = await channel.send(callback(0, hasMultiplePages)); - if (page < 0) page += total; - else if (page >= total) page -= total; + if (hasMultiplePages) { + let page = 0; + const turn = (amount: number) => { + page += amount; - callback(page); - }; - const BACKWARDS_EMOJI = "⬅️"; - const FORWARDS_EMOJI = "➡️"; - const handle = (emote: string, reacterID: string) => { - switch (emote) { - case BACKWARDS_EMOJI: - turn(-1); - break; - case FORWARDS_EMOJI: - turn(1); - break; - } - }; + if (page < 0) page += total; + else if (page >= total) page -= total; - // Listen for reactions and call the handler. - let backwardsReaction = await message.react(BACKWARDS_EMOJI); - let forwardsReaction = await message.react(FORWARDS_EMOJI); - eventListeners.set(message.id, handle); - await message.awaitReactions( - (reaction, user) => { - if (user.id === senderID) { - // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. - // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. - const canDeleteEmotes = botHasPermission(message.guild, Permissions.FLAGS.MANAGE_MESSAGES); - handle(reaction.emoji.name, user.id); - - if (canDeleteEmotes) reaction.users.remove(user); + message.edit(callback(page, true)); + }; + const BACKWARDS_EMOJI = "⬅️"; + const FORWARDS_EMOJI = "➡️"; + const handle = (emote: string, reacterID: string) => { + if (senderID === reacterID) { + switch (emote) { + case BACKWARDS_EMOJI: + turn(-1); + break; + case FORWARDS_EMOJI: + turn(1); + break; + } } + }; - return false; - }, - {time: duration} - ); - // When time's up, remove the bot's own reactions. - eventListeners.delete(message.id); - backwardsReaction.users.remove(message.author); - forwardsReaction.users.remove(message.author); + // Listen for reactions and call the handler. + let backwardsReaction = await message.react(BACKWARDS_EMOJI); + let forwardsReaction = await message.react(FORWARDS_EMOJI); + eventListeners.set(message.id, handle); + const collector = message.createReactionCollector( + (reaction, user) => { + if (user.id === senderID) { + // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. + // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. + const canDeleteEmotes = botHasPermission(message.guild, Permissions.FLAGS.MANAGE_MESSAGES); + handle(reaction.emoji.name, user.id); + if (canDeleteEmotes) reaction.users.remove(user); + collector.resetTimer(); + } + + return false; + }, + // Apparently, regardless of whether you put "time" or "idle", it won't matter to the collector. + // In order to actually reset the timer, you have to do it manually via collector.resetTimer(). + {time: duration} + ); + + // When time's up, remove the bot's own reactions. + collector.on("end", () => { + eventListeners.delete(message.id); + backwardsReaction.users.remove(message.author); + forwardsReaction.users.remove(message.author); + }); + } } // Waits for the sender to either confirm an action or let it pass (and delete the message). From 86ccb74ac2b519de114c641bd2a8eb70eea063ef Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 30 Mar 2021 18:14:15 -0500 Subject: [PATCH 08/30] Highly biased code review --- data/endpoints.json | 31 ---------- src/commands/admin.ts | 36 ++++++----- src/commands/fun/cookie.ts | 71 ++++++++++++---------- src/commands/fun/neko.ts | 45 +++++++++++--- src/commands/fun/ok.ts | 119 +++++++++++++++++++------------------ src/commands/info.ts | 26 ++++++-- src/core/libd.ts | 4 +- src/index.ts | 2 +- 8 files changed, 182 insertions(+), 152 deletions(-) delete mode 100644 data/endpoints.json diff --git a/data/endpoints.json b/data/endpoints.json deleted file mode 100644 index 69f15ac..0000000 --- a/data/endpoints.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "sfw": { - "tickle": "/img/tickle", - "slap": "/img/slap", - "poke": "/img/poke", - "pat": "/img/pat", - "neko": "/img/neko", - "meow": "/img/meow", - "lizard": "/img/lizard", - "kiss": "/img/kiss", - "hug": "/img/hug", - "foxGirl": "/img/fox_girl", - "feed": "/img/feed", - "cuddle": "/img/cuddle", - "why": "/why", - "catText": "/cat", - "fact": "/fact", - "nekoGif": "/img/ngif", - "kemonomimi": "/img/kemonomimi", - "holo": "/img/holo", - "smug": "/img/smug", - "baka": "/img/baka", - "woof": "/img/woof", - "spoiler": "/spoiler", - "wallpaper": "/img/wallpaper", - "goose": "/img/goose", - "gecg": "/img/gecg", - "avatar": "/img/avatar", - "waifu": "/img/waifu" - } -} \ No newline at end of file diff --git a/src/commands/admin.ts b/src/commands/admin.ts index ff2d968..e0c4362 100644 --- a/src/commands/admin.ts +++ b/src/commands/admin.ts @@ -100,24 +100,28 @@ export default new Command({ }) }), purge: new Command({ - description: "Purges bot messages.", + description: "Purges the bot's own messages.", permission: PERMISSIONS.BOT_SUPPORT, async run($) { - if ($.message.channel instanceof discord.DMChannel) { - return; - } - $.message.delete(); - const msgs = await $.channel.messages.fetch({ - limit: 100 - }); - const travMessages = msgs.filter((m) => m.author.id === $.client.user?.id); + // It's probably better to go through the bot's own messages instead of calling bulkDelete which requires MANAGE_MESSAGES. + if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES) && $.channel.type !== "dm") { + $.message.delete(); + const msgs = await $.channel.messages.fetch({ + limit: 100 + }); + const travMessages = msgs.filter((m) => m.author.id === $.client.user?.id); - await $.message.channel.send(`Found ${travMessages.size} messages to delete.`).then((m) => - m.delete({ - timeout: 5000 - }) - ); - await $.message.channel.bulkDelete(travMessages); + await $.channel.send(`Found ${travMessages.size} messages to delete.`).then((m) => + m.delete({ + timeout: 5000 + }) + ); + await $.channel.bulkDelete(travMessages); + } else { + $.channel.send( + "This command must be executed in a guild where I have the `MANAGE_MESSAGES` permission." + ); + } } }), clear: new Command({ @@ -172,7 +176,7 @@ export default new Command({ permission: PERMISSIONS.BOT_SUPPORT, async run($) { const guildList = $.client.guilds.cache.array().map((e) => e.name); - $.channel.send(guildList); + $.channel.send(guildList, {split: true}); } }), activity: new Command({ diff --git a/src/commands/fun/cookie.ts b/src/commands/fun/cookie.ts index 99ace4b..39a3948 100644 --- a/src/commands/fun/cookie.ts +++ b/src/commands/fun/cookie.ts @@ -1,51 +1,58 @@ +import {User} from "discord.js"; import Command from "../../core/command"; +import {random} from "../../core/lib"; +import {parseVars} from "../../core/libd"; + +const cookies = [ + `has given %target% a chocolate chip cookie!`, + `has given %target% a soft homemade oatmeal cookie!`, + `has given %target% a plain, dry, old cookie. It was the last one in the bag. Gross.`, + `gives %target% a sugar cookie. What, no frosting and sprinkles? 0/10 would not touch.`, + `gives %target% a chocolate chip cookie. Oh wait, those are raisins. Bleck!`, + `gives %target% an enormous cookie. Poking it gives you more cookies. Weird.`, + `gives %target% a fortune cookie. It reads "Why aren't you working on any projects?"`, + `gives %target% a fortune cookie. It reads "Give that special someone a compliment"`, + `gives %target% a fortune cookie. It reads "Take a risk!"`, + `gives %target% a fortune cookie. It reads "Go outside."`, + `gives %target% a fortune cookie. It reads "Don't forget to eat your veggies!"`, + `gives %target% a fortune cookie. It reads "Do you even lift?"`, + `gives %target% a fortune cookie. It reads "m808 pls"`, + `gives %target% a fortune cookie. It reads "If you move your hips, you'll get all the ladies."`, + `gives %target% a fortune cookie. It reads "I love you."`, + `gives %target% a Golden Cookie. You can't eat it because it is made of gold. Dammit.`, + `gives %target% an Oreo cookie with a glass of milk!`, + `gives %target% a rainbow cookie made with love :heart:`, + `gives %target% an old cookie that was left out in the rain, it's moldy.`, + `bakes %target% fresh cookies, it smells amazing.` +]; export default new Command({ description: "Gives specified user a cookie.", usage: "['all'/@user]", run: ":cookie: Here's a cookie!", - any: new Command({ - async run($) { - if ($.args[0] == "all") $.channel.send(`${$.author} gave everybody a cookie!`); - } - }), + subcommands: { + all: new Command({ + async run($) { + $.channel.send(`${$.author} gave everybody a cookie!`); + } + }) + }, user: new Command({ description: "User to give cookie to.", async run($) { const sender = $.author; - const mention = $.message.mentions.users.first(); - - if (!mention) return; - - const cookies = [ - `has given <@${mention.id}> a chocolate chip cookie!`, - `has given <@${mention.id}> a soft homemade oatmeal cookie!`, - `has given <@${mention.id}> a plain, dry, old cookie. It was the last one in the bag. Gross.`, - `gives <@${mention.id}> a sugar cookie. What, no frosting and sprinkles? 0/10 would not touch.`, - `gives <@${mention.id}> a chocolate chip cookie. Oh wait, those are raisins. Bleck!`, - `gives <@${mention.id}> an enormous cookie. Poking it gives you more cookies. Weird.`, - `gives <@${mention.id}> a fortune cookie. It reads "Why aren't you working on any projects?"`, - `gives <@${mention.id}> a fortune cookie. It reads "Give that special someone a compliment"`, - `gives <@${mention.id}> a fortune cookie. It reads "Take a risk!"`, - `gives <@${mention.id}> a fortune cookie. It reads "Go outside."`, - `gives <@${mention.id}> a fortune cookie. It reads "Don't forget to eat your veggies!"`, - `gives <@${mention.id}> a fortune cookie. It reads "Do you even lift?"`, - `gives <@${mention.id}> a fortune cookie. It reads "m808 pls"`, - `gives <@${mention.id}> a fortune cookie. It reads "If you move your hips, you'll get all the ladies."`, - `gives <@${mention.id}> a fortune cookie. It reads "I love you."`, - `gives <@${mention.id}> a Golden Cookie. You can't eat it because it is made of gold. Dammit.`, - `gives <@${mention.id}> an Oreo cookie with a glass of milk!`, - `gives <@${mention.id}> a rainbow cookie made with love :heart:`, - `gives <@${mention.id}> an old cookie that was left out in the rain, it's moldy.`, - `bakes <@${mention.id}> fresh cookies, it smells amazing.` - ]; + const mention: User = $.args[0]; if (mention.id == sender.id) { $.channel.send("You can't give yourself cookies!"); return; } - $.channel.send(`:cookie: <@${sender.id}> ` + cookies[Math.floor(Math.random() * cookies.length)]); + $.channel.send( + `:cookie: <@${sender.id}> ${parseVars(random(cookies), { + target: mention.toString() + })}` + ); } }) }); diff --git a/src/commands/fun/neko.ts b/src/commands/fun/neko.ts index 68ab1bf..1990bc2 100644 --- a/src/commands/fun/neko.ts +++ b/src/commands/fun/neko.ts @@ -1,15 +1,42 @@ -/// @ts-nocheck import {URL} from "url"; -import FileManager from "../../core/storage"; import Command from "../../core/command"; import {getContent} from "../../core/libd"; -const endpoints = FileManager.read("endpoints"); +const endpoints: {sfw: {[key: string]: string}} = { + sfw: { + tickle: "/img/tickle", + slap: "/img/slap", + poke: "/img/poke", + pat: "/img/pat", + neko: "/img/neko", + meow: "/img/meow", + lizard: "/img/lizard", + kiss: "/img/kiss", + hug: "/img/hug", + foxGirl: "/img/fox_girl", + feed: "/img/feed", + cuddle: "/img/cuddle", + why: "/why", + catText: "/cat", + fact: "/fact", + nekoGif: "/img/ngif", + kemonomimi: "/img/kemonomimi", + holo: "/img/holo", + smug: "/img/smug", + baka: "/img/baka", + woof: "/img/woof", + spoiler: "/spoiler", + wallpaper: "/img/wallpaper", + goose: "/img/goose", + gecg: "/img/gecg", + avatar: "/img/avatar", + waifu: "/img/waifu" + } +}; export default new Command({ description: "Provides you with a random image with the selected argument.", async run($) { - console.log(endpoints.sfw); $.channel.send( `Please provide an image type. Available arguments:\n\`[${Object.keys(endpoints.sfw).join(", ")}]\`.` ); @@ -17,10 +44,14 @@ export default new Command({ any: new Command({ description: "Image type to send.", async run($) { - if (!($.args[0] in endpoints.sfw)) return $.channel.send("Couldn't find that endpoint!"); + const arg = $.args[0]; - let baseURL = "https://nekos.life/api/v2"; - let url = new URL(`${baseURL}${endpoints.sfw[$.args[0]]}`); + if (!(arg in endpoints.sfw)) { + $.channel.send("Couldn't find that endpoint!"); + return; + } + + let url = new URL(`https://nekos.life/api/v2${endpoints.sfw[arg]}`); const content = await getContent(url.toString()); $.channel.send(content.url); } diff --git a/src/commands/fun/ok.ts b/src/commands/fun/ok.ts index 9c8d641..a4ea44a 100644 --- a/src/commands/fun/ok.ts +++ b/src/commands/fun/ok.ts @@ -1,66 +1,67 @@ import Command from "../../core/command"; +import {random} from "../../core/lib"; + +const responses = [ + "boomer", + "zoomer", + "the last generationer", + "the last airbender", + "fire nation", + "fire lord", + "guy fieri", + "guy from final fight", + "haggar", + "Max Thunder from Streets of Rage 2", + "police guy who fires bazookas", + "Mr. X", + "Leon Its Wrong If Its Not Ada Wong S. Kennedy.", + "Jill", + "JFK", + "george bush", + "obama", + "the world", + "copy of scott pilgrim vs the world", + "ok", + "ko", + "Hot Daddy Venomous", + "big daddy", + "John Cena", + "BubbleSpurJarJarBinks", + "T-Series", + "pewdiepie", + "markiplier", + "jacksepticeye", + "vanossgaming", + "miniladd", + "Traves", + "Wilbur Soot", + "sootrhianna", + "person with tiny ears", + "anti-rabbit", + "homo sapiens", + "homo", + "cute kitty", + "ugly kitty", + "sadness", + "doomer", + "gloomer", + "bloomer", + "edgelord", + "weeb", + "m'lady", + "Mr. Crabs", + "hand", + "lahoma", + "big man", + "fox", + "pear", + "cat", + "large man" +]; export default new Command({ description: "Sends random ok message.", async run($) { - const responses = [ - "boomer", - "zoomer", - "the last generationer", - "the last airbender", - "fire nation", - "fire lord", - "guy fieri", - "guy from final fight", - "haggar", - "Max Thunder from Streets of Rage 2", - "police guy who fires bazookas", - "Mr. X", - "Leon Its Wrong If Its Not Ada Wong S. Kennedy.", - "Jill", - "JFK", - "george bush", - "obama", - "the world", - "copy of scott pilgrim vs the world", - "ok", - "ko", - "Hot Daddy Venomous", - "big daddy", - "John Cena", - "BubbleSpurJarJarBinks", - "T-Series", - "pewdiepie", - "markiplier", - "jacksepticeye", - "vanossgaming", - "miniladd", - "Traves", - "Wilbur Soot", - "sootrhianna", - "person with tiny ears", - "anti-rabbit", - "homo sapiens", - "homo", - "cute kitty", - "ugly kitty", - "sadness", - "doomer", - "gloomer", - "bloomer", - "edgelord", - "weeb", - "m'lady", - "Mr. Crabs", - "hand", - "lahoma", - "big man", - "fox", - "pear", - "cat", - "large man" - ]; - - $.channel.send("ok " + responses[Math.floor(Math.random() * responses.length)]); + $.channel.send(`ok ${random(responses)}`); } }); diff --git a/src/commands/info.ts b/src/commands/info.ts index 7b4b103..7142ba5 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -2,14 +2,12 @@ import {MessageEmbed, version as djsversion} from "discord.js"; import ms from "ms"; import os from "os"; import Command from "../core/command"; -import {formatBytes, trimArray} from "../core/libd"; -import {verificationLevels, filterLevels, regions, flags} from "../defs/info"; +import {formatBytes, trimArray, getMemberByUsername} from "../core/libd"; +import {verificationLevels, filterLevels, regions} from "../defs/info"; import moment from "moment"; import utc from "moment"; import {Guild} from "discord.js"; -const {version} = require("../../package.json"); - export default new Command({ description: "Command to provide all sorts of info about the current server, a user, etc.", run: "Please provide an argument.\nFor help, run `%prefix%help info`.", @@ -30,6 +28,26 @@ export default new Command({ }) ); } + }), + any: new Command({ + description: "Shows another user's avatar by searching their name", + async run($) { + if ($.guild) { + const name = $.args.join(" "); + const member = await getMemberByUsername($.guild, name); + + if (member) { + $.channel.send( + member.user.displayAvatarURL({ + dynamic: true, + size: 2048 + }) + ); + } else { + $.channel.send(`No user found by the name \`${name}\`!`); + } + } + } }) }), bot: new Command({ diff --git a/src/core/libd.ts b/src/core/libd.ts index 3cc6b04..20a1ab1 100644 --- a/src/core/libd.ts +++ b/src/core/libd.ts @@ -381,9 +381,9 @@ export function formatBytes(bytes: any) { return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`; } -export function getContent(url: any) { +export function getContent(url: string): Promise<{url: string}> { return new Promise((resolve, reject) => { - get(url, (res: {resume?: any; setEncoding?: any; on?: any; statusCode?: any}) => { + get(url, (res) => { const {statusCode} = res; if (statusCode !== 200) { res.resume(); diff --git a/src/index.ts b/src/index.ts index 426046c..c6320a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,7 +53,7 @@ export const client = new discord.Client(); } ] }, - prefix: "!!", + prefix: Config.prefix, helpCmd: "mhelp", admins: ["717352467280691331"] }); From 3ef487c4a4d7eb1227091923c4399e004e409cdb Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 30 Mar 2021 21:19:04 -0500 Subject: [PATCH 09/30] Reorganized lib/libd functions and Lavalink --- src/commands/admin.ts | 4 +- src/commands/fun/cookie.ts | 2 +- src/commands/fun/neko.ts | 2 +- src/commands/info.ts | 3 +- src/core/command.ts | 2 +- src/core/lib.ts | 165 +++++++++++++++++++++++++++++++++++++ src/core/libd.ts | 164 ------------------------------------ src/core/structures.ts | 2 +- src/index.ts | 56 +------------ src/modules/lavalink.ts | 53 ++++++++++++ 10 files changed, 230 insertions(+), 223 deletions(-) create mode 100644 src/modules/lavalink.ts diff --git a/src/commands/admin.ts b/src/commands/admin.ts index e0c4362..72c5686 100644 --- a/src/commands/admin.ts +++ b/src/commands/admin.ts @@ -1,9 +1,9 @@ import Command, {handler} from "../core/command"; -import {botHasPermission, clean} from "../core/libd"; +import {clean} from "../core/lib"; +import {botHasPermission} from "../core/libd"; import {Config, Storage} from "../core/structures"; import {getPermissionLevel, getPermissionName} from "../core/permissions"; import {Permissions} from "discord.js"; -import * as discord from "discord.js"; import {logs} from "../globals"; function getLogBuffer(type: string) { diff --git a/src/commands/fun/cookie.ts b/src/commands/fun/cookie.ts index 39a3948..06c7086 100644 --- a/src/commands/fun/cookie.ts +++ b/src/commands/fun/cookie.ts @@ -1,7 +1,7 @@ import {User} from "discord.js"; import Command from "../../core/command"; import {random} from "../../core/lib"; -import {parseVars} from "../../core/libd"; +import {parseVars} from "../../core/lib"; const cookies = [ `has given %target% a chocolate chip cookie!`, diff --git a/src/commands/fun/neko.ts b/src/commands/fun/neko.ts index 1990bc2..113fb15 100644 --- a/src/commands/fun/neko.ts +++ b/src/commands/fun/neko.ts @@ -1,6 +1,6 @@ import {URL} from "url"; import Command from "../../core/command"; -import {getContent} from "../../core/libd"; +import {getContent} from "../../core/lib"; const endpoints: {sfw: {[key: string]: string}} = { sfw: { diff --git a/src/commands/info.ts b/src/commands/info.ts index 7142ba5..b95f19a 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -2,7 +2,8 @@ import {MessageEmbed, version as djsversion} from "discord.js"; import ms from "ms"; import os from "os"; import Command from "../core/command"; -import {formatBytes, trimArray, getMemberByUsername} from "../core/libd"; +import {formatBytes, trimArray} from "../core/lib"; +import {getMemberByUsername} from "../core/libd"; import {verificationLevels, filterLevels, regions} from "../defs/info"; import moment from "moment"; import utc from "moment"; diff --git a/src/core/command.ts b/src/core/command.ts index c40d5a0..c13b3a3 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,4 +1,4 @@ -import {parseVars} from "./libd"; +import {parseVars} from "./lib"; import {Collection} from "discord.js"; import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember} from "discord.js"; import {getPrefix} from "../core/structures"; diff --git a/src/core/lib.ts b/src/core/lib.ts index a0ebcb7..21b4a95 100644 --- a/src/core/lib.ts +++ b/src/core/lib.ts @@ -1,4 +1,169 @@ // Library for pure functions +import {get} from "https"; +import FileManager from "./storage"; + +/** + * Splits a command by spaces while accounting for quotes which capture string arguments. + * - `\"` = `"` + * - `\\` = `\` + */ +export function parseArgs(line: string): string[] { + let result = []; + let selection = ""; + let inString = false; + let isEscaped = false; + + for (let c of line) { + if (isEscaped) { + if (['"', "\\"].includes(c)) selection += c; + else selection += "\\" + c; + + isEscaped = false; + } else if (c === "\\") isEscaped = true; + else if (c === '"') inString = !inString; + else if (c === " " && !inString) { + result.push(selection); + selection = ""; + } else selection += c; + } + + if (selection.length > 0) result.push(selection); + + return result; +} + +/** + * Allows you to store a template string with variable markers and parse it later. + * - Use `%name%` for variables + * - `%%` = `%` + * - If the invalid token is null/undefined, nothing is changed. + */ +export function parseVars(line: string, definitions: {[key: string]: string}, invalid: string | null = ""): string { + let result = ""; + let inVariable = false; + let token = ""; + + for (const c of line) { + if (c === "%") { + if (inVariable) { + if (token === "") result += "%"; + else { + if (token in definitions) result += definitions[token]; + else if (invalid === null) result += `%${token}%`; + else result += invalid; + + token = ""; + } + } + + inVariable = !inVariable; + } else if (inVariable) token += c; + else result += c; + } + + return result; +} + +export function isType(value: any, type: any): boolean { + if (value === undefined && type === undefined) return true; + else if (value === null && type === null) return true; + else return value !== undefined && value !== null && value.constructor === type; +} + +/** + * Checks a value to see if it matches the fallback's type, otherwise returns the fallback. + * For the purposes of the templates system, this function will only check array types, objects should be checked under their own type (as you'd do anyway with something like a User object). + * If at any point the value doesn't match the data structure provided, the fallback is returned. + * Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this! + */ +export function select(value: any, fallback: T, type: Function, isArray = false): T { + if (isArray && isType(value, Array)) { + for (let item of value) if (!isType(item, type)) return fallback; + return value; + } else { + if (isType(value, type)) return value; + else return fallback; + } +} + +export function clean(text: any) { + if (typeof text === "string") + return text.replace(/`/g, "`" + String.fromCharCode(8203)).replace(/@/g, "@" + String.fromCharCode(8203)); + else return text; +} + +export function trimArray(arr: any, maxLen = 10) { + if (arr.length > maxLen) { + const len = arr.length - maxLen; + arr = arr.slice(0, maxLen); + arr.push(`${len} more...`); + } + return arr; +} + +export function formatBytes(bytes: any) { + if (bytes === 0) return "0 Bytes"; + const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`; +} + +export function getContent(url: string): Promise<{url: string}> { + return new Promise((resolve, reject) => { + get(url, (res) => { + const {statusCode} = res; + if (statusCode !== 200) { + res.resume(); + reject(`Request failed. Status code: ${statusCode}`); + } + res.setEncoding("utf8"); + let rawData = ""; + res.on("data", (chunk: string) => { + rawData += chunk; + }); + res.on("end", () => { + try { + const parsedData = JSON.parse(rawData); + resolve(parsedData); + } catch (e) { + reject(`Error: ${e.message}`); + } + }); + }).on("error", (err: {message: any}) => { + reject(`Error: ${err.message}`); + }); + }); +} + +export interface GenericJSON { + [key: string]: any; +} + +export abstract class GenericStructure { + private __meta__ = "generic"; + + constructor(tag?: string) { + this.__meta__ = tag || this.__meta__; + } + + public save(asynchronous = true) { + const tag = this.__meta__; + /// @ts-ignore + delete this.__meta__; + FileManager.write(tag, this, asynchronous); + this.__meta__ = tag; + } +} + +// A 50% chance would be "Math.random() < 0.5" because Math.random() can be [0, 1), so to make two equal ranges, you'd need [0, 0.5)U[0.5, 1). +// Similar logic would follow for any other percentage. Math.random() < 1 is always true (100% chance) and Math.random() < 0 is always false (0% chance). +export const Random = { + num: (min: number, max: number) => Math.random() * (max - min) + min, + int: (min: number, max: number) => Math.floor(Random.num(min, max)), + chance: (decimal: number) => Math.random() < decimal, + sign: (number = 1) => number * (Random.chance(0.5) ? -1 : 1), + deviation: (base: number, deviation: number) => Random.num(base - deviation, base + deviation) +}; /** * Pluralises a word and chooses a suffix attached to the root provided. diff --git a/src/core/libd.ts b/src/core/libd.ts index 20a1ab1..ab391cf 100644 --- a/src/core/libd.ts +++ b/src/core/libd.ts @@ -9,7 +9,6 @@ import { NewsChannel, MessageOptions } from "discord.js"; -import {get} from "https"; import FileManager from "./storage"; import {eventListeners} from "../events/messageReactionRemove"; import {client} from "../index"; @@ -274,166 +273,3 @@ export async function callMemberByUsername( else send(`Couldn't find a user by the name of \`${username}\`!`); } else send("You must execute this command in a server!"); } - -/** - * Splits a command by spaces while accounting for quotes which capture string arguments. - * - `\"` = `"` - * - `\\` = `\` - */ -export function parseArgs(line: string): string[] { - let result = []; - let selection = ""; - let inString = false; - let isEscaped = false; - - for (let c of line) { - if (isEscaped) { - if (['"', "\\"].includes(c)) selection += c; - else selection += "\\" + c; - - isEscaped = false; - } else if (c === "\\") isEscaped = true; - else if (c === '"') inString = !inString; - else if (c === " " && !inString) { - result.push(selection); - selection = ""; - } else selection += c; - } - - if (selection.length > 0) result.push(selection); - - return result; -} - -/** - * Allows you to store a template string with variable markers and parse it later. - * - Use `%name%` for variables - * - `%%` = `%` - * - If the invalid token is null/undefined, nothing is changed. - */ -export function parseVars(line: string, definitions: {[key: string]: string}, invalid: string | null = ""): string { - let result = ""; - let inVariable = false; - let token = ""; - - for (const c of line) { - if (c === "%") { - if (inVariable) { - if (token === "") result += "%"; - else { - if (token in definitions) result += definitions[token]; - else if (invalid === null) result += `%${token}%`; - else result += invalid; - - token = ""; - } - } - - inVariable = !inVariable; - } else if (inVariable) token += c; - else result += c; - } - - return result; -} - -export function isType(value: any, type: any): boolean { - if (value === undefined && type === undefined) return true; - else if (value === null && type === null) return true; - else return value !== undefined && value !== null && value.constructor === type; -} - -/** - * Checks a value to see if it matches the fallback's type, otherwise returns the fallback. - * For the purposes of the templates system, this function will only check array types, objects should be checked under their own type (as you'd do anyway with something like a User object). - * If at any point the value doesn't match the data structure provided, the fallback is returned. - * Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this! - */ -export function select(value: any, fallback: T, type: Function, isArray = false): T { - if (isArray && isType(value, Array)) { - for (let item of value) if (!isType(item, type)) return fallback; - return value; - } else { - if (isType(value, type)) return value; - else return fallback; - } -} - -export function clean(text: any) { - if (typeof text === "string") - return text.replace(/`/g, "`" + String.fromCharCode(8203)).replace(/@/g, "@" + String.fromCharCode(8203)); - else return text; -} - -export function trimArray(arr: any, maxLen = 10) { - if (arr.length > maxLen) { - const len = arr.length - maxLen; - arr = arr.slice(0, maxLen); - arr.push(`${len} more...`); - } - return arr; -} - -export function formatBytes(bytes: any) { - if (bytes === 0) return "0 Bytes"; - const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; - const i = Math.floor(Math.log(bytes) / Math.log(1024)); - return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`; -} - -export function getContent(url: string): Promise<{url: string}> { - return new Promise((resolve, reject) => { - get(url, (res) => { - const {statusCode} = res; - if (statusCode !== 200) { - res.resume(); - reject(`Request failed. Status code: ${statusCode}`); - } - res.setEncoding("utf8"); - let rawData = ""; - res.on("data", (chunk: string) => { - rawData += chunk; - }); - res.on("end", () => { - try { - const parsedData = JSON.parse(rawData); - resolve(parsedData); - } catch (e) { - reject(`Error: ${e.message}`); - } - }); - }).on("error", (err: {message: any}) => { - reject(`Error: ${err.message}`); - }); - }); -} - -export interface GenericJSON { - [key: string]: any; -} - -export abstract class GenericStructure { - private __meta__ = "generic"; - - constructor(tag?: string) { - this.__meta__ = tag || this.__meta__; - } - - public save(asynchronous = true) { - const tag = this.__meta__; - /// @ts-ignore - delete this.__meta__; - FileManager.write(tag, this, asynchronous); - this.__meta__ = tag; - } -} - -// A 50% chance would be "Math.random() < 0.5" because Math.random() can be [0, 1), so to make two equal ranges, you'd need [0, 0.5)U[0.5, 1). -// Similar logic would follow for any other percentage. Math.random() < 1 is always true (100% chance) and Math.random() < 0 is always false (0% chance). -export const Random = { - num: (min: number, max: number) => Math.random() * (max - min) + min, - int: (min: number, max: number) => Math.floor(Random.num(min, max)), - chance: (decimal: number) => Math.random() < decimal, - sign: (number = 1) => number * (Random.chance(0.5) ? -1 : 1), - deviation: (base: number, deviation: number) => Random.num(base - deviation, base + deviation) -}; diff --git a/src/core/structures.ts b/src/core/structures.ts index 07d8f5c..74e5adf 100644 --- a/src/core/structures.ts +++ b/src/core/structures.ts @@ -1,5 +1,5 @@ import FileManager from "./storage"; -import {select, GenericJSON, GenericStructure} from "./libd"; +import {select, GenericJSON, GenericStructure} from "./lib"; import {watch} from "fs"; import {Guild as DiscordGuild, Snowflake} from "discord.js"; diff --git a/src/index.ts b/src/index.ts index c6320a8..2ccf92c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,62 +1,14 @@ import "./globals"; -import * as discord from "discord.js"; +import {Client} from "discord.js"; import setup from "./setup"; import {Config} from "./core/structures"; import {loadEvents} from "./core/event"; -import "discord.js-lavalink-lib"; -import LavalinkMusic from "discord.js-lavalink-lib"; - -declare module "discord.js" { - interface Presence { - patch(data: any): void; - } -} - -// The terrible hacks were written by none other than The Noble Programmer On The White PC. - -// NOTE: Terrible hack ahead!!! In order to reduce the memory usage of the bot -// we only store the information from presences that we actually end up using, -// which currently is only the (online/idle/dnd/offline/...) status (see -// `src/commands/info.ts`). What data is retrieved from the `data` object -// (which contains the data received from the Gateway) and how can be seen -// here: -// . -const oldPresencePatch = discord.Presence.prototype.patch; -discord.Presence.prototype.patch = function patch(data: any) { - oldPresencePatch.call(this, {status: data.status}); -}; +import {attachToClient} from "./modules/lavalink"; // This is here in order to make it much less of a headache to access the client from other files. // This of course won't actually do anything until the setup process is complete and it logs in. -export const client = new discord.Client(); - -// NOTE: Terrible hack continued!!! Unfortunately we can't receive the presence -// data at all when the GUILD_PRESENCES intent is disabled, so while we do -// waste network bandwidth and the CPU time for decoding the incoming packets, -// the function which handles those packets is NOP-ed out, which, among other -// things, skips the code which caches the referenced users in the packet. See -// . -(client["actions"] as any)["PresenceUpdate"].handle = () => {}; - -(client as any).music = LavalinkMusic(client, { - lavalink: { - restnode: { - host: "localhost", - port: 2333, - password: "youshallnotpass" - }, - nodes: [ - { - host: "localhost", - port: 2333, - password: "youshallnotpass" - } - ] - }, - prefix: Config.prefix, - helpCmd: "mhelp", - admins: ["717352467280691331"] -}); +export const client = new Client(); +attachToClient(client); // Command loading will start as soon as an instance of "core/command" is loaded, which is loaded during "events/message". setup.init().then(() => { diff --git a/src/modules/lavalink.ts b/src/modules/lavalink.ts new file mode 100644 index 0000000..5e0ff90 --- /dev/null +++ b/src/modules/lavalink.ts @@ -0,0 +1,53 @@ +import {Presence, Client} from "discord.js"; +import LavalinkMusic from "discord.js-lavalink-lib"; +import {Config} from "../core/structures"; + +declare module "discord.js" { + interface Presence { + patch(data: any): void; + } +} + +// The terrible hacks were written by none other than The Noble Programmer On The White PC. + +// NOTE: Terrible hack ahead!!! In order to reduce the memory usage of the bot +// we only store the information from presences that we actually end up using, +// which currently is only the (online/idle/dnd/offline/...) status (see +// `src/commands/info.ts`). What data is retrieved from the `data` object +// (which contains the data received from the Gateway) and how can be seen +// here: +// . +const oldPresencePatch = Presence.prototype.patch; +Presence.prototype.patch = function patch(data: any) { + oldPresencePatch.call(this, {status: data.status}); +}; + +// NOTE: Terrible hack continued!!! Unfortunately we can't receive the presence +// data at all when the GUILD_PRESENCES intent is disabled, so while we do +// waste network bandwidth and the CPU time for decoding the incoming packets, +// the function which handles those packets is NOP-ed out, which, among other +// things, skips the code which caches the referenced users in the packet. See +// . +export function attachToClient(client: Client) { + (client["actions"] as any)["PresenceUpdate"].handle = () => {}; + + (client as any).music = LavalinkMusic(client, { + lavalink: { + restnode: { + host: "localhost", + port: 2333, + password: "youshallnotpass" + }, + nodes: [ + { + host: "localhost", + port: 2333, + password: "youshallnotpass" + } + ] + }, + prefix: Config.prefix, + helpCmd: "mhelp", + admins: ["717352467280691331"] + }); +} From 945102b7cf54ce67f1c5abaf82664884444aeb01 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 30 Mar 2021 21:56:25 -0500 Subject: [PATCH 10/30] Reworked event loading --- .gitignore | 3 +- src/core/event.ts | 38 ------- src/core/handler.ts | 158 ++++++++++++++++++++++++++++ src/core/libd.ts | 37 +++---- src/events/channelCreate.ts | 13 --- src/events/channelDelete.ts | 13 --- src/events/emojiCreate.ts | 9 -- src/events/emojiDelete.ts | 9 -- src/events/emojiUpdate.ts | 9 -- src/events/guildCreate.ts | 9 -- src/events/guildDelete.ts | 9 -- src/events/message.ts | 149 -------------------------- src/events/messageReactionRemove.ts | 18 ---- src/events/ready.ts | 17 --- src/index.ts | 13 ++- src/modules/channelListener.ts | 20 ++++ src/modules/emoteRegistry.ts | 53 ++++++++++ src/modules/lavalink.ts | 39 ++++--- 18 files changed, 272 insertions(+), 344 deletions(-) delete mode 100644 src/core/event.ts create mode 100644 src/core/handler.ts delete mode 100644 src/events/channelCreate.ts delete mode 100644 src/events/channelDelete.ts delete mode 100644 src/events/emojiCreate.ts delete mode 100644 src/events/emojiDelete.ts delete mode 100644 src/events/emojiUpdate.ts delete mode 100644 src/events/guildCreate.ts delete mode 100644 src/events/guildDelete.ts delete mode 100644 src/events/message.ts delete mode 100644 src/events/messageReactionRemove.ts delete mode 100644 src/events/ready.ts create mode 100644 src/modules/channelListener.ts create mode 100644 src/modules/emoteRegistry.ts diff --git a/.gitignore b/.gitignore index a3becd9..109f5cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Specific to this repository dist/ -data/* -!data/endpoints.json +data/ tmp/ test* !test/ diff --git a/src/core/event.ts b/src/core/event.ts deleted file mode 100644 index d2b6f9a..0000000 --- a/src/core/event.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {Client, ClientEvents, Constants} from "discord.js"; -import Storage from "./storage"; - -interface EventOptions { - readonly on?: (...args: ClientEvents[K]) => void; - readonly once?: (...args: ClientEvents[K]) => void; -} - -export default class Event { - private readonly on?: (...args: ClientEvents[K]) => void; - private readonly once?: (...args: ClientEvents[K]) => void; - - constructor(options: EventOptions) { - this.on = options.on; - this.once = options.once; - } - - // For this function, I'm going to assume that the event is used with the correct arguments and that the event tag is checked in "storage.ts". - public attach(client: Client, event: K) { - if (this.on) client.on(event, this.on); - if (this.once) client.once(event, this.once); - } -} - -export async function loadEvents(client: Client) { - for (const file of Storage.open("dist/events", (filename: string) => filename.endsWith(".js"))) { - const header = file.substring(0, file.indexOf(".js")); - const event = (await import(`../events/${header}`)).default; - - if ((Object.values(Constants.Events) as string[]).includes(header)) { - event.attach(client, header); - console.log(`Loading Event: ${header}`); - } else - console.warn( - `"${header}" is not a valid event type! Did you misspell it? (Note: If you fixed the issue, delete "dist" because the compiler won't automatically delete any extra files.)` - ); - } -} diff --git a/src/core/handler.ts b/src/core/handler.ts new file mode 100644 index 0000000..1de6c1e --- /dev/null +++ b/src/core/handler.ts @@ -0,0 +1,158 @@ +import {client} from "../index"; +import Command, {loadableCommands} from "../core/command"; +import {hasPermission, getPermissionLevel, getPermissionName} from "../core/permissions"; +import {Permissions} from "discord.js"; +import {getPrefix} from "../core/structures"; +import {replyEventListeners} from "../core/libd"; +import quote from "../modules/message_embed"; +import {Config} from "../core/structures"; + +client.on("message", async (message) => { + const commands = await loadableCommands; + + if (message.content.toLowerCase().includes("remember to drink water")) { + message.react("🚱"); + } + + // Message Setup // + if (message.author.bot) return; + + // If there's an inline reply, fire off that event listener (if it exists). + if (message.reference) { + const reference = message.reference; + replyEventListeners.get(`${reference.channelID}-${reference.messageID}`)?.(message); + } + + let prefix = getPrefix(message.guild); + const originalPrefix = prefix; + let exitEarly = !message.content.startsWith(prefix); + const clientUser = message.client.user; + let usesBotSpecificPrefix = false; + + if (!message.content.startsWith(prefix)) { + return quote(message); + } + + // If the client user exists, check if it starts with the bot-specific prefix. + if (clientUser) { + // If the prefix starts with the bot-specific prefix, go off that instead (these two options must mutually exclude each other). + // The pattern here has an optional space at the end to capture that and make it not mess with the header and args. + const matches = message.content.match(new RegExp(`^<@!?${clientUser.id}> ?`)); + + if (matches) { + prefix = matches[0]; + exitEarly = false; + usesBotSpecificPrefix = true; + } + } + + // If it doesn't start with the current normal prefix or the bot-specific unique prefix, exit the thread of execution early. + // Inline replies should still be captured here because if it doesn't exit early, two characters for a two-length prefix would still trigger commands. + if (exitEarly) return; + + const [header, ...args] = message.content.substring(prefix.length).split(/ +/); + + // If the message is just the prefix itself, move onto this block. + if (header === "" && args.length === 0) { + // I moved the bot-specific prefix to a separate conditional block to separate the logic. + // And because it listens for the mention as a prefix instead of a free-form mention, inline replies (probably) shouldn't ever trigger this unintentionally. + if (usesBotSpecificPrefix) { + message.channel.send(`${message.author.toString()}, my prefix on this guild is \`${originalPrefix}\`.`); + return; + } + } + + if (!commands.has(header)) return; + + if ( + message.channel.type === "text" && + !message.channel.permissionsFor(message.client.user || "")?.has(Permissions.FLAGS.SEND_MESSAGES) + ) { + let status; + + if (message.member?.hasPermission(Permissions.FLAGS.ADMINISTRATOR)) + status = + "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended."; + else + status = + "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong."; + + return message.author.send( + `I don't have permission to send messages in ${message.channel.toString()}. ${status}` + ); + } + + console.log( + `${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".` + ); + + // Subcommand Recursion // + let command = commands.get(header); + if (!command) return console.warn(`Command "${header}" was called but for some reason it's still undefined!`); + const params: any[] = []; + let isEndpoint = false; + let permLevel = command.permission ?? 0; + + for (let param of args) { + if (command.endpoint) { + if (command.subcommands.size > 0 || command.user || command.number || command.any) + console.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`); + isEndpoint = true; + break; + } + + const type = command.resolve(param); + command = command.get(param); + permLevel = command.permission ?? permLevel; + + if (type === Command.TYPES.USER) { + const id = param.match(/\d+/g)![0]; + try { + params.push(await message.client.users.fetch(id)); + } catch (error) { + return message.channel.send(`No user found by the ID \`${id}\`!`); + } + } else if (type === Command.TYPES.NUMBER) params.push(Number(param)); + else if (type !== Command.TYPES.SUBCOMMAND) params.push(param); + } + + if (!message.member) + return console.warn("This command was likely called from a DM channel meaning the member object is null."); + + if (!hasPermission(message.member, permLevel)) { + const userPermLevel = getPermissionLevel(message.member); + return message.channel.send( + `You don't have access to this command! Your permission level is \`${getPermissionName( + userPermLevel + )}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName( + permLevel + )}\` (${permLevel}).` + ); + } + + if (isEndpoint) return message.channel.send("Too many arguments!"); + + // Execute with dynamic library attached. // + // The purpose of using $.bind($) is to clone the function so as to not modify the original $. + // The cloned function doesn't copy the properties, so Object.assign() is used. + // Object.assign() modifies the first element and returns that, the second element applies its properties and the third element applies its own overriding the second one. + command.execute({ + args: params, + author: message.author, + channel: message.channel, + client: message.client, + guild: message.guild, + member: message.member, + message: message + }); +}); + +client.once("ready", () => { + if (client.user) { + console.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`); + client.user.setActivity({ + type: "LISTENING", + name: `${Config.prefix}help` + }); + } +}); diff --git a/src/core/libd.ts b/src/core/libd.ts index ab391cf..71ba70a 100644 --- a/src/core/libd.ts +++ b/src/core/libd.ts @@ -9,36 +9,25 @@ import { NewsChannel, MessageOptions } from "discord.js"; -import FileManager from "./storage"; -import {eventListeners} from "../events/messageReactionRemove"; import {client} from "../index"; -import {EmoteRegistryDump} from "./structures"; + +// A list of message ID and callback pairs. You get the emote name and ID of the user reacting. +const eventListeners: Map void> = new Map(); + +// Attached to the client, there can be one event listener attached to a message ID which is executed if present. +client.on("messageReactionRemove", (reaction, user) => { + const canDeleteEmotes = botHasPermission(reaction.message.guild, Permissions.FLAGS.MANAGE_MESSAGES); + + if (!canDeleteEmotes) { + const callback = eventListeners.get(reaction.message.id); + callback && callback(reaction.emoji.name, user.id); + } +}); export function botHasPermission(guild: Guild | null, permission: number): boolean { return !!guild?.me?.hasPermission(permission); } -export function updateGlobalEmoteRegistry(): void { - const data: EmoteRegistryDump = {version: 1, list: []}; - - for (const guild of client.guilds.cache.values()) { - for (const emote of guild.emojis.cache.values()) { - data.list.push({ - ref: emote.name, - id: emote.id, - name: emote.name, - requires_colons: emote.requiresColons || false, - animated: emote.animated, - url: emote.url, - guild_id: emote.guild.name, - guild_name: emote.guild.name - }); - } - } - - FileManager.write("emote-registry", data, true); -} - // Maybe promisify this section to reduce the potential for creating callback hell? Especially if multiple questions in a row are being asked. // Pagination function that allows for customization via a callback. diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts deleted file mode 100644 index 47c2aa3..0000000 --- a/src/events/channelCreate.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Event from "../core/event"; -import {client} from "../index"; -import * as discord from "discord.js"; - -export default new Event<"channelCreate">({ - async on(channel) { - const botGuilds = client.guilds; - if (channel instanceof discord.GuildChannel) { - const createdGuild = await botGuilds.fetch(channel.guild.id); - console.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`); - } - } -}); diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts deleted file mode 100644 index ac835ed..0000000 --- a/src/events/channelDelete.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Event from "../core/event"; -import {client} from "../index"; -import * as discord from "discord.js"; - -export default new Event<"channelDelete">({ - async on(channel) { - const botGuilds = client.guilds; - if (channel instanceof discord.GuildChannel) { - const createdGuild = await botGuilds.fetch(channel.guild.id); - console.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`); - } - } -}); diff --git a/src/events/emojiCreate.ts b/src/events/emojiCreate.ts deleted file mode 100644 index 92ef88e..0000000 --- a/src/events/emojiCreate.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/libd"; - -export default new Event<"emojiCreate">({ - on(emote) { - console.log(`Updated emote registry. ${emote.name}`); - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/emojiDelete.ts b/src/events/emojiDelete.ts deleted file mode 100644 index dc34190..0000000 --- a/src/events/emojiDelete.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/libd"; - -export default new Event<"emojiDelete">({ - on() { - console.log("Updated emote registry."); - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/emojiUpdate.ts b/src/events/emojiUpdate.ts deleted file mode 100644 index bd0b6b0..0000000 --- a/src/events/emojiUpdate.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/libd"; - -export default new Event<"emojiUpdate">({ - on() { - console.log("Updated emote registry."); - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts deleted file mode 100644 index 480bd4d..0000000 --- a/src/events/guildCreate.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/libd"; - -export default new Event<"guildCreate">({ - on() { - console.log("Updated emote registry."); - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/guildDelete.ts b/src/events/guildDelete.ts deleted file mode 100644 index 755a049..0000000 --- a/src/events/guildDelete.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Event from "../core/event"; -import {updateGlobalEmoteRegistry} from "../core/libd"; - -export default new Event<"guildDelete">({ - on() { - console.log("Updated emote registry."); - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/message.ts b/src/events/message.ts deleted file mode 100644 index d805ed8..0000000 --- a/src/events/message.ts +++ /dev/null @@ -1,149 +0,0 @@ -import Event from "../core/event"; -import Command, {loadableCommands} from "../core/command"; -import {hasPermission, getPermissionLevel, getPermissionName} from "../core/permissions"; -import {Permissions} from "discord.js"; -import {getPrefix} from "../core/structures"; -import {replyEventListeners} from "../core/libd"; -import quote from "../modules/message_embed"; - -export default new Event<"message">({ - async on(message) { - const commands = await loadableCommands; - - if (message.content.toLowerCase().includes("remember to drink water")) { - message.react("🚱"); - } - - // Message Setup // - if (message.author.bot) return; - - // If there's an inline reply, fire off that event listener (if it exists). - if (message.reference) { - const reference = message.reference; - replyEventListeners.get(`${reference.channelID}-${reference.messageID}`)?.(message); - } - - let prefix = getPrefix(message.guild); - const originalPrefix = prefix; - let exitEarly = !message.content.startsWith(prefix); - const clientUser = message.client.user; - let usesBotSpecificPrefix = false; - - if (!message.content.startsWith(prefix)) { - return quote(message); - } - - // If the client user exists, check if it starts with the bot-specific prefix. - if (clientUser) { - // If the prefix starts with the bot-specific prefix, go off that instead (these two options must mutually exclude each other). - // The pattern here has an optional space at the end to capture that and make it not mess with the header and args. - const matches = message.content.match(new RegExp(`^<@!?${clientUser.id}> ?`)); - - if (matches) { - prefix = matches[0]; - exitEarly = false; - usesBotSpecificPrefix = true; - } - } - - // If it doesn't start with the current normal prefix or the bot-specific unique prefix, exit the thread of execution early. - // Inline replies should still be captured here because if it doesn't exit early, two characters for a two-length prefix would still trigger commands. - if (exitEarly) return; - - const [header, ...args] = message.content.substring(prefix.length).split(/ +/); - - // If the message is just the prefix itself, move onto this block. - if (header === "" && args.length === 0) { - // I moved the bot-specific prefix to a separate conditional block to separate the logic. - // And because it listens for the mention as a prefix instead of a free-form mention, inline replies (probably) shouldn't ever trigger this unintentionally. - if (usesBotSpecificPrefix) { - message.channel.send(`${message.author.toString()}, my prefix on this guild is \`${originalPrefix}\`.`); - return; - } - } - - if (!commands.has(header)) return; - - if ( - message.channel.type === "text" && - !message.channel.permissionsFor(message.client.user || "")?.has(Permissions.FLAGS.SEND_MESSAGES) - ) { - let status; - - if (message.member?.hasPermission(Permissions.FLAGS.ADMINISTRATOR)) - status = - "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended."; - else - status = - "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong."; - - return message.author.send( - `I don't have permission to send messages in ${message.channel.toString()}. ${status}` - ); - } - - console.log( - `${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".` - ); - - // Subcommand Recursion // - let command = commands.get(header); - if (!command) return console.warn(`Command "${header}" was called but for some reason it's still undefined!`); - const params: any[] = []; - let isEndpoint = false; - let permLevel = command.permission ?? 0; - - for (let param of args) { - if (command.endpoint) { - if (command.subcommands.size > 0 || command.user || command.number || command.any) - console.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`); - isEndpoint = true; - break; - } - - const type = command.resolve(param); - command = command.get(param); - permLevel = command.permission ?? permLevel; - - if (type === Command.TYPES.USER) { - const id = param.match(/\d+/g)![0]; - try { - params.push(await message.client.users.fetch(id)); - } catch (error) { - return message.channel.send(`No user found by the ID \`${id}\`!`); - } - } else if (type === Command.TYPES.NUMBER) params.push(Number(param)); - else if (type !== Command.TYPES.SUBCOMMAND) params.push(param); - } - - if (!message.member) - return console.warn("This command was likely called from a DM channel meaning the member object is null."); - - if (!hasPermission(message.member, permLevel)) { - const userPermLevel = getPermissionLevel(message.member); - return message.channel.send( - `You don't have access to this command! Your permission level is \`${getPermissionName( - userPermLevel - )}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName( - permLevel - )}\` (${permLevel}).` - ); - } - - if (isEndpoint) return message.channel.send("Too many arguments!"); - - // Execute with dynamic library attached. // - // The purpose of using $.bind($) is to clone the function so as to not modify the original $. - // The cloned function doesn't copy the properties, so Object.assign() is used. - // Object.assign() modifies the first element and returns that, the second element applies its properties and the third element applies its own overriding the second one. - command.execute({ - args: params, - author: message.author, - channel: message.channel, - client: message.client, - guild: message.guild, - member: message.member, - message: message - }); - } -}); diff --git a/src/events/messageReactionRemove.ts b/src/events/messageReactionRemove.ts deleted file mode 100644 index 4b3dbb2..0000000 --- a/src/events/messageReactionRemove.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Event from "../core/event"; -import {Permissions} from "discord.js"; -import {botHasPermission} from "../core/libd"; - -// A list of message ID and callback pairs. You get the emote name and ID of the user reacting. -export const eventListeners: Map void> = new Map(); - -// Attached to the client, there can be one event listener attached to a message ID which is executed if present. -export default new Event<"messageReactionRemove">({ - on(reaction, user) { - const canDeleteEmotes = botHasPermission(reaction.message.guild, Permissions.FLAGS.MANAGE_MESSAGES); - - if (!canDeleteEmotes) { - const callback = eventListeners.get(reaction.message.id); - callback && callback(reaction.emoji.name, user.id); - } - } -}); diff --git a/src/events/ready.ts b/src/events/ready.ts deleted file mode 100644 index a4e1b9c..0000000 --- a/src/events/ready.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Event from "../core/event"; -import {client} from "../index"; -import {Config} from "../core/structures"; -import {updateGlobalEmoteRegistry} from "../core/libd"; - -export default new Event<"ready">({ - once() { - if (client.user) { - console.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`); - client.user.setActivity({ - type: "LISTENING", - name: `${Config.prefix}help` - }); - } - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/index.ts b/src/index.ts index 2ccf92c..9f709e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,20 @@ +// Bootstrapping Section // import "./globals"; import {Client} from "discord.js"; import setup from "./setup"; import {Config} from "./core/structures"; -import {loadEvents} from "./core/event"; -import {attachToClient} from "./modules/lavalink"; // This is here in order to make it much less of a headache to access the client from other files. // This of course won't actually do anything until the setup process is complete and it logs in. export const client = new Client(); -attachToClient(client); -// Command loading will start as soon as an instance of "core/command" is loaded, which is loaded during "events/message". +// Send the login request to Discord's API and then load modules while waiting for it. setup.init().then(() => { - loadEvents(client); client.login(Config.token).catch(setup.again); }); + +// Initialize Modules // +import "./core/handler"; // Command loading will start as soon as an instance of "core/command" is loaded, which is loaded in "core/handler". +import "./modules/lavalink"; +import "./modules/emoteRegistry"; +import "./modules/channelListener"; diff --git a/src/modules/channelListener.ts b/src/modules/channelListener.ts new file mode 100644 index 0000000..910d1ab --- /dev/null +++ b/src/modules/channelListener.ts @@ -0,0 +1,20 @@ +import {client} from "../index"; +import {GuildChannel} from "discord.js"; + +client.on("channelCreate", async (channel) => { + const botGuilds = client.guilds; + + if (channel instanceof GuildChannel) { + const createdGuild = await botGuilds.fetch(channel.guild.id); + console.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`); + } +}); + +client.on("channelDelete", async (channel) => { + const botGuilds = client.guilds; + + if (channel instanceof GuildChannel) { + const createdGuild = await botGuilds.fetch(channel.guild.id); + console.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`); + } +}); diff --git a/src/modules/emoteRegistry.ts b/src/modules/emoteRegistry.ts new file mode 100644 index 0000000..bcc7ed0 --- /dev/null +++ b/src/modules/emoteRegistry.ts @@ -0,0 +1,53 @@ +import {client} from "../index"; +import FileManager from "../core/storage"; +import {EmoteRegistryDump} from "../core/structures"; + +function updateGlobalEmoteRegistry(): void { + const data: EmoteRegistryDump = {version: 1, list: []}; + + for (const guild of client.guilds.cache.values()) { + for (const emote of guild.emojis.cache.values()) { + data.list.push({ + ref: emote.name, + id: emote.id, + name: emote.name, + requires_colons: emote.requiresColons || false, + animated: emote.animated, + url: emote.url, + guild_id: emote.guild.name, + guild_name: emote.guild.name + }); + } + } + + FileManager.write("emote-registry", data, true); +} + +client.on("emojiCreate", (emote) => { + console.log(`Updated emote registry. ${emote.name}`); + updateGlobalEmoteRegistry(); +}); + +client.on("emojiDelete", () => { + console.log("Updated emote registry."); + updateGlobalEmoteRegistry(); +}); + +client.on("emojiUpdate", () => { + console.log("Updated emote registry."); + updateGlobalEmoteRegistry(); +}); + +client.on("guildCreate", () => { + console.log("Updated emote registry."); + updateGlobalEmoteRegistry(); +}); + +client.on("guildDelete", () => { + console.log("Updated emote registry."); + updateGlobalEmoteRegistry(); +}); + +client.on("ready", () => { + updateGlobalEmoteRegistry(); +}); diff --git a/src/modules/lavalink.ts b/src/modules/lavalink.ts index 5e0ff90..39dc5c8 100644 --- a/src/modules/lavalink.ts +++ b/src/modules/lavalink.ts @@ -1,6 +1,7 @@ -import {Presence, Client} from "discord.js"; +import {Presence} from "discord.js"; import LavalinkMusic from "discord.js-lavalink-lib"; import {Config} from "../core/structures"; +import {client} from "../index"; declare module "discord.js" { interface Presence { @@ -28,26 +29,24 @@ Presence.prototype.patch = function patch(data: any) { // the function which handles those packets is NOP-ed out, which, among other // things, skips the code which caches the referenced users in the packet. See // . -export function attachToClient(client: Client) { - (client["actions"] as any)["PresenceUpdate"].handle = () => {}; +(client["actions"] as any)["PresenceUpdate"].handle = () => {}; - (client as any).music = LavalinkMusic(client, { - lavalink: { - restnode: { +(client as any).music = LavalinkMusic(client, { + lavalink: { + restnode: { + host: "localhost", + port: 2333, + password: "youshallnotpass" + }, + nodes: [ + { host: "localhost", port: 2333, password: "youshallnotpass" - }, - nodes: [ - { - host: "localhost", - port: 2333, - password: "youshallnotpass" - } - ] - }, - prefix: Config.prefix, - helpCmd: "mhelp", - admins: ["717352467280691331"] - }); -} + } + ] + }, + prefix: Config.prefix, + helpCmd: "mhelp", + admins: ["717352467280691331"] +}); From 974985586d133fe0a99ef7e6c8f3d9f327ec0573 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 30 Mar 2021 22:22:25 -0500 Subject: [PATCH 11/30] Rearranged command categories --- src/commands/fun/eco.ts | 8 ++++---- .../fun/{subcommands => modules}/eco-core.ts | 0 .../fun/{subcommands => modules}/eco-extras.ts | 0 .../fun/{subcommands => modules}/eco-shop-items.ts | 0 .../fun/{subcommands => modules}/eco-shop.ts | 0 .../fun/{subcommands => modules}/eco-utils.ts | 0 src/commands/fun/owoify.ts | 6 +++--- src/commands/{ => system}/admin.ts | 12 ++++++------ src/commands/{ => system}/help.ts | 8 ++++---- src/commands/{utilities => utility}/desc.ts | 0 src/commands/{utilities => utility}/emote.ts | 2 +- src/commands/{ => utility}/info.ts | 8 ++++---- src/commands/{utilities => utility}/lsemotes.ts | 0 .../subcommands => utility/modules}/emote-utils.ts | 0 src/commands/{utilities => utility}/react.ts | 2 +- src/commands/{utilities => utility}/say.ts | 0 src/commands/{ => utility}/scanemotes.ts | 4 ++-- src/commands/{utilities => utility}/shorten.ts | 0 src/commands/{utilities => utility}/time.ts | 0 src/core/permissions.ts | 2 +- 20 files changed, 26 insertions(+), 26 deletions(-) rename src/commands/fun/{subcommands => modules}/eco-core.ts (100%) rename src/commands/fun/{subcommands => modules}/eco-extras.ts (100%) rename src/commands/fun/{subcommands => modules}/eco-shop-items.ts (100%) rename src/commands/fun/{subcommands => modules}/eco-shop.ts (100%) rename src/commands/fun/{subcommands => modules}/eco-utils.ts (100%) rename src/commands/{ => system}/admin.ts (96%) rename src/commands/{ => system}/help.ts (96%) rename src/commands/{utilities => utility}/desc.ts (100%) rename src/commands/{utilities => utility}/emote.ts (89%) rename src/commands/{ => utility}/info.ts (98%) rename src/commands/{utilities => utility}/lsemotes.ts (100%) rename src/commands/{utilities/subcommands => utility/modules}/emote-utils.ts (100%) rename src/commands/{utilities => utility}/react.ts (98%) rename src/commands/{utilities => utility}/say.ts (100%) rename src/commands/{ => utility}/scanemotes.ts (99%) rename src/commands/{utilities => utility}/shorten.ts (100%) rename src/commands/{utilities => utility}/time.ts (100%) diff --git a/src/commands/fun/eco.ts b/src/commands/fun/eco.ts index 6df36ae..380cc33 100644 --- a/src/commands/fun/eco.ts +++ b/src/commands/fun/eco.ts @@ -1,8 +1,8 @@ import Command from "../../core/command"; -import {isAuthorized, getMoneyEmbed} from "./subcommands/eco-utils"; -import {DailyCommand, PayCommand, GuildCommand, LeaderboardCommand} from "./subcommands/eco-core"; -import {BuyCommand, ShopCommand} from "./subcommands/eco-shop"; -import {MondayCommand} from "./subcommands/eco-extras"; +import {isAuthorized, getMoneyEmbed} from "./modules/eco-utils"; +import {DailyCommand, PayCommand, GuildCommand, LeaderboardCommand} from "./modules/eco-core"; +import {BuyCommand, ShopCommand} from "./modules/eco-shop"; +import {MondayCommand} from "./modules/eco-extras"; import {callMemberByUsername} from "../../core/libd"; export default new Command({ diff --git a/src/commands/fun/subcommands/eco-core.ts b/src/commands/fun/modules/eco-core.ts similarity index 100% rename from src/commands/fun/subcommands/eco-core.ts rename to src/commands/fun/modules/eco-core.ts diff --git a/src/commands/fun/subcommands/eco-extras.ts b/src/commands/fun/modules/eco-extras.ts similarity index 100% rename from src/commands/fun/subcommands/eco-extras.ts rename to src/commands/fun/modules/eco-extras.ts diff --git a/src/commands/fun/subcommands/eco-shop-items.ts b/src/commands/fun/modules/eco-shop-items.ts similarity index 100% rename from src/commands/fun/subcommands/eco-shop-items.ts rename to src/commands/fun/modules/eco-shop-items.ts diff --git a/src/commands/fun/subcommands/eco-shop.ts b/src/commands/fun/modules/eco-shop.ts similarity index 100% rename from src/commands/fun/subcommands/eco-shop.ts rename to src/commands/fun/modules/eco-shop.ts diff --git a/src/commands/fun/subcommands/eco-utils.ts b/src/commands/fun/modules/eco-utils.ts similarity index 100% rename from src/commands/fun/subcommands/eco-utils.ts rename to src/commands/fun/modules/eco-utils.ts diff --git a/src/commands/fun/owoify.ts b/src/commands/fun/owoify.ts index 789d74a..af041df 100644 --- a/src/commands/fun/owoify.ts +++ b/src/commands/fun/owoify.ts @@ -1,12 +1,12 @@ -/// @ts-nocheck import Command from "../../core/command"; -import {getContent} from "../../core/libd"; +import {getContent} from "../../core/lib"; +import {URL} from "url"; export default new Command({ description: "OwO-ifies the input.", async run($) { let url = new URL(`https://nekos.life/api/v2/owoify?text=${$.args.join(" ")}`); - const content = await getContent(url.toString()); + const content = (await getContent(url.toString())) as any; // Apparently, the object in question is {owo: string}. $.channel.send(content.owo); } }); diff --git a/src/commands/admin.ts b/src/commands/system/admin.ts similarity index 96% rename from src/commands/admin.ts rename to src/commands/system/admin.ts index 72c5686..15ab73d 100644 --- a/src/commands/admin.ts +++ b/src/commands/system/admin.ts @@ -1,10 +1,10 @@ -import Command, {handler} from "../core/command"; -import {clean} from "../core/lib"; -import {botHasPermission} from "../core/libd"; -import {Config, Storage} from "../core/structures"; -import {getPermissionLevel, getPermissionName} from "../core/permissions"; +import Command, {handler} from "../../core/command"; +import {clean} from "../../core/lib"; +import {botHasPermission} from "../../core/libd"; +import {Config, Storage} from "../../core/structures"; +import {getPermissionLevel, getPermissionName} from "../../core/permissions"; import {Permissions} from "discord.js"; -import {logs} from "../globals"; +import {logs} from "../../globals"; function getLogBuffer(type: string) { return { diff --git a/src/commands/help.ts b/src/commands/system/help.ts similarity index 96% rename from src/commands/help.ts rename to src/commands/system/help.ts index 71f2ae3..fdf1249 100644 --- a/src/commands/help.ts +++ b/src/commands/system/help.ts @@ -1,7 +1,7 @@ -import Command from "../core/command"; -import {toTitleCase} from "../core/lib"; -import {loadableCommands, categories} from "../core/command"; -import {getPermissionName} from "../core/permissions"; +import Command from "../../core/command"; +import {toTitleCase} from "../../core/lib"; +import {loadableCommands, categories} from "../../core/command"; +import {getPermissionName} from "../../core/permissions"; export default new Command({ description: "Lists all commands. If a command is specified, their arguments are listed as well.", diff --git a/src/commands/utilities/desc.ts b/src/commands/utility/desc.ts similarity index 100% rename from src/commands/utilities/desc.ts rename to src/commands/utility/desc.ts diff --git a/src/commands/utilities/emote.ts b/src/commands/utility/emote.ts similarity index 89% rename from src/commands/utilities/emote.ts rename to src/commands/utility/emote.ts index b84d9ef..addaec0 100644 --- a/src/commands/utilities/emote.ts +++ b/src/commands/utility/emote.ts @@ -1,5 +1,5 @@ import Command from "../../core/command"; -import {queryClosestEmoteByName} from "./subcommands/emote-utils"; +import {queryClosestEmoteByName} from "./modules/emote-utils"; import {botHasPermission} from "../../core/libd"; import {Permissions} from "discord.js"; diff --git a/src/commands/info.ts b/src/commands/utility/info.ts similarity index 98% rename from src/commands/info.ts rename to src/commands/utility/info.ts index b95f19a..e2e6025 100644 --- a/src/commands/info.ts +++ b/src/commands/utility/info.ts @@ -1,10 +1,10 @@ import {MessageEmbed, version as djsversion} from "discord.js"; import ms from "ms"; import os from "os"; -import Command from "../core/command"; -import {formatBytes, trimArray} from "../core/lib"; -import {getMemberByUsername} from "../core/libd"; -import {verificationLevels, filterLevels, regions} from "../defs/info"; +import Command from "../../core/command"; +import {formatBytes, trimArray} from "../../core/lib"; +import {getMemberByUsername} from "../../core/libd"; +import {verificationLevels, filterLevels, regions} from "../../defs/info"; import moment from "moment"; import utc from "moment"; import {Guild} from "discord.js"; diff --git a/src/commands/utilities/lsemotes.ts b/src/commands/utility/lsemotes.ts similarity index 100% rename from src/commands/utilities/lsemotes.ts rename to src/commands/utility/lsemotes.ts diff --git a/src/commands/utilities/subcommands/emote-utils.ts b/src/commands/utility/modules/emote-utils.ts similarity index 100% rename from src/commands/utilities/subcommands/emote-utils.ts rename to src/commands/utility/modules/emote-utils.ts diff --git a/src/commands/utilities/react.ts b/src/commands/utility/react.ts similarity index 98% rename from src/commands/utilities/react.ts rename to src/commands/utility/react.ts index cc7c031..152e98d 100644 --- a/src/commands/utilities/react.ts +++ b/src/commands/utility/react.ts @@ -1,6 +1,6 @@ import Command from "../../core/command"; import {Message, Channel, TextChannel} from "discord.js"; -import {queryClosestEmoteByName} from "./subcommands/emote-utils"; +import {queryClosestEmoteByName} from "./modules/emote-utils"; export default new Command({ description: diff --git a/src/commands/utilities/say.ts b/src/commands/utility/say.ts similarity index 100% rename from src/commands/utilities/say.ts rename to src/commands/utility/say.ts diff --git a/src/commands/scanemotes.ts b/src/commands/utility/scanemotes.ts similarity index 99% rename from src/commands/scanemotes.ts rename to src/commands/utility/scanemotes.ts index 9596f9f..b087793 100644 --- a/src/commands/scanemotes.ts +++ b/src/commands/utility/scanemotes.ts @@ -1,5 +1,5 @@ -import Command, {handler} from "../core/command"; -import {pluralise} from "../core/lib"; +import Command, {handler} from "../../core/command"; +import {pluralise} from "../../core/lib"; import moment from "moment"; import {Collection, TextChannel} from "discord.js"; diff --git a/src/commands/utilities/shorten.ts b/src/commands/utility/shorten.ts similarity index 100% rename from src/commands/utilities/shorten.ts rename to src/commands/utility/shorten.ts diff --git a/src/commands/utilities/time.ts b/src/commands/utility/time.ts similarity index 100% rename from src/commands/utilities/time.ts rename to src/commands/utility/time.ts diff --git a/src/core/permissions.ts b/src/core/permissions.ts index c4c89ba..37b0000 100644 --- a/src/core/permissions.ts +++ b/src/core/permissions.ts @@ -63,6 +63,6 @@ export function getPermissionLevel(member: GuildMember): number { } export function getPermissionName(level: number) { - if (level > length || length < 0) return "N/A"; + if (level > length || level < 0) return "N/A"; else return PermissionLevels[level].name; } From df3e4e8e6eaad6d0d1aeae2c05af8fb74277d0af Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Wed, 31 Mar 2021 02:00:03 -0500 Subject: [PATCH 12/30] Made some minor changes to modules --- src/commands/system/admin.ts | 2 +- src/commands/system/help.ts | 2 ++ src/index.ts | 5 +++-- src/{ => modules}/globals.ts | 0 src/modules/lavalink.ts | 34 +++------------------------------- src/modules/presence.ts | 30 ++++++++++++++++++++++++++++++ src/{ => modules}/setup.ts | 4 ++-- 7 files changed, 41 insertions(+), 36 deletions(-) rename src/{ => modules}/globals.ts (100%) create mode 100644 src/modules/presence.ts rename src/{ => modules}/setup.ts (96%) diff --git a/src/commands/system/admin.ts b/src/commands/system/admin.ts index 15ab73d..8dae940 100644 --- a/src/commands/system/admin.ts +++ b/src/commands/system/admin.ts @@ -4,7 +4,7 @@ import {botHasPermission} from "../../core/libd"; import {Config, Storage} from "../../core/structures"; import {getPermissionLevel, getPermissionName} from "../../core/permissions"; import {Permissions} from "discord.js"; -import {logs} from "../../globals"; +import {logs} from "../../modules/globals"; function getLogBuffer(type: string) { return { diff --git a/src/commands/system/help.ts b/src/commands/system/help.ts index fdf1249..8ef9fe2 100644 --- a/src/commands/system/help.ts +++ b/src/commands/system/help.ts @@ -65,6 +65,8 @@ export default new Command({ command = command.get(param); permLevel = command.permission ?? permLevel; + if (permLevel === -1) permLevel = command.permission; + switch (type) { case Command.TYPES.SUBCOMMAND: header += ` ${command.originalCommandName}`; diff --git a/src/index.ts b/src/index.ts index 9f709e0..497c4f6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ // Bootstrapping Section // -import "./globals"; +import "./modules/globals"; import {Client} from "discord.js"; -import setup from "./setup"; +import setup from "./modules/setup"; import {Config} from "./core/structures"; // This is here in order to make it much less of a headache to access the client from other files. @@ -15,6 +15,7 @@ setup.init().then(() => { // Initialize Modules // import "./core/handler"; // Command loading will start as soon as an instance of "core/command" is loaded, which is loaded in "core/handler". +import "./modules/presence"; import "./modules/lavalink"; import "./modules/emoteRegistry"; import "./modules/channelListener"; diff --git a/src/globals.ts b/src/modules/globals.ts similarity index 100% rename from src/globals.ts rename to src/modules/globals.ts diff --git a/src/modules/lavalink.ts b/src/modules/lavalink.ts index 39dc5c8..92d6ee9 100644 --- a/src/modules/lavalink.ts +++ b/src/modules/lavalink.ts @@ -1,37 +1,9 @@ -import {Presence} from "discord.js"; -import LavalinkMusic from "discord.js-lavalink-lib"; +import attachClientToLavalink from "discord.js-lavalink-lib"; import {Config} from "../core/structures"; import {client} from "../index"; -declare module "discord.js" { - interface Presence { - patch(data: any): void; - } -} - -// The terrible hacks were written by none other than The Noble Programmer On The White PC. - -// NOTE: Terrible hack ahead!!! In order to reduce the memory usage of the bot -// we only store the information from presences that we actually end up using, -// which currently is only the (online/idle/dnd/offline/...) status (see -// `src/commands/info.ts`). What data is retrieved from the `data` object -// (which contains the data received from the Gateway) and how can be seen -// here: -// . -const oldPresencePatch = Presence.prototype.patch; -Presence.prototype.patch = function patch(data: any) { - oldPresencePatch.call(this, {status: data.status}); -}; - -// NOTE: Terrible hack continued!!! Unfortunately we can't receive the presence -// data at all when the GUILD_PRESENCES intent is disabled, so while we do -// waste network bandwidth and the CPU time for decoding the incoming packets, -// the function which handles those packets is NOP-ed out, which, among other -// things, skips the code which caches the referenced users in the packet. See -// . -(client["actions"] as any)["PresenceUpdate"].handle = () => {}; - -(client as any).music = LavalinkMusic(client, { +// Although the example showed to do "client.music = LavaLink(...)" and "(client as any).music = Lavalink(...)" was done to match that, nowhere in the library is client.music ever actually used nor does the function return anything. In other words, client.music is undefined and is never used. +attachClientToLavalink(client, { lavalink: { restnode: { host: "localhost", diff --git a/src/modules/presence.ts b/src/modules/presence.ts new file mode 100644 index 0000000..50c5f64 --- /dev/null +++ b/src/modules/presence.ts @@ -0,0 +1,30 @@ +import {Presence} from "discord.js"; +import {client} from "../index"; + +declare module "discord.js" { + interface Presence { + patch(data: any): void; + } +} + +// The terrible hacks were written by none other than The Noble Programmer On The White PC. + +// NOTE: Terrible hack ahead!!! In order to reduce the memory usage of the bot +// we only store the information from presences that we actually end up using, +// which currently is only the (online/idle/dnd/offline/...) status (see +// `src/commands/info.ts`). What data is retrieved from the `data` object +// (which contains the data received from the Gateway) and how can be seen +// here: +// . +const oldPresencePatch = Presence.prototype.patch; +Presence.prototype.patch = function patch(data: any) { + oldPresencePatch.call(this, {status: data.status}); +}; + +// NOTE: Terrible hack continued!!! Unfortunately we can't receive the presence +// data at all when the GUILD_PRESENCES intent is disabled, so while we do +// waste network bandwidth and the CPU time for decoding the incoming packets, +// the function which handles those packets is NOP-ed out, which, among other +// things, skips the code which caches the referenced users in the packet. See +// . +(client["actions"] as any)["PresenceUpdate"].handle = () => {}; diff --git a/src/setup.ts b/src/modules/setup.ts similarity index 96% rename from src/setup.ts rename to src/modules/setup.ts index 97dc6e6..99ca135 100644 --- a/src/setup.ts +++ b/src/modules/setup.ts @@ -1,7 +1,7 @@ import {existsSync as exists, readFileSync as read, writeFile as write} from "fs"; import inquirer from "inquirer"; -import Storage, {generateHandler} from "./core/storage"; -import {Config} from "./core/structures"; +import Storage, {generateHandler} from "../core/storage"; +import {Config} from "../core/structures"; // The template should be built with a reductionist mentality. // Provide everything the user needs and then let them remove whatever they want. From 9adc5eea6e86ee7c89c03df17ff35f8acf165443 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Thu, 1 Apr 2021 05:44:44 -0500 Subject: [PATCH 13/30] Started attempting to split up core handler --- jest.config.js | 5 +- src/core/eventListeners.ts | 28 +++++ src/core/handler.ts | 117 ++++++++++-------- src/core/libd.ts | 25 +--- src/index.ts | 3 + src/modules/intercept.ts | 7 ++ src/modules/messageEmbed.test.ts | 55 ++++++++ .../{message_embed.ts => messageEmbed.ts} | 32 +++-- 8 files changed, 186 insertions(+), 86 deletions(-) create mode 100644 src/core/eventListeners.ts create mode 100644 src/modules/intercept.ts create mode 100644 src/modules/messageEmbed.test.ts rename src/modules/{message_embed.ts => messageEmbed.ts} (65%) diff --git a/jest.config.js b/jest.config.js index 09097b3..7917d47 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,5 +3,8 @@ module.exports = { testMatch: ["**/*.test.+(ts|tsx)"], transform: { "^.+\\.(ts|tsx)$": "ts-jest" - } + }, + // The environment is the DOM by default, so discord.js fails to load because it's calling a Node-specific function. + // https://github.com/discordjs/discord.js/issues/3971#issuecomment-602010271 + testEnvironment: "node" }; diff --git a/src/core/eventListeners.ts b/src/core/eventListeners.ts new file mode 100644 index 0000000..e32db03 --- /dev/null +++ b/src/core/eventListeners.ts @@ -0,0 +1,28 @@ +import {client} from "../index"; +import {botHasPermission} from "./libd"; +import {Permissions, Message} from "discord.js"; + +// A list of message ID and callback pairs. You get the emote name and ID of the user reacting. +export const unreactEventListeners: Map void> = new Map(); + +// Attached to the client, there can be one event listener attached to a message ID which is executed if present. +client.on("messageReactionRemove", (reaction, user) => { + const canDeleteEmotes = botHasPermission(reaction.message.guild, Permissions.FLAGS.MANAGE_MESSAGES); + + if (!canDeleteEmotes) { + const callback = unreactEventListeners.get(reaction.message.id); + callback && callback(reaction.emoji.name, user.id); + } +}); + +// A list of "channel-message" and callback pairs. Also, I imagine that the callback will be much more maintainable when discord.js v13 comes out with a dedicated message.referencedMessage property. +// Also, I'm defining it here instead of the message event because the load order screws up if you export it from there. Yeah... I'm starting to notice just how much technical debt has been built up. The command handler needs to be modularized and refactored sooner rather than later. Define all constants in one area then grab from there. +export const replyEventListeners = new Map void>(); + +client.on("message", (message) => { + // If there's an inline reply, fire off that event listener (if it exists). + if (message.reference) { + const reference = message.reference; + replyEventListeners.get(`${reference.channelID}-${reference.messageID}`)?.(message); + } +}); diff --git a/src/core/handler.ts b/src/core/handler.ts index 1de6c1e..2a9fe42 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -1,38 +1,43 @@ import {client} from "../index"; -import Command, {loadableCommands} from "../core/command"; -import {hasPermission, getPermissionLevel, getPermissionName} from "../core/permissions"; -import {Permissions} from "discord.js"; -import {getPrefix} from "../core/structures"; -import {replyEventListeners} from "../core/libd"; -import quote from "../modules/message_embed"; -import {Config} from "../core/structures"; +import Command, {loadableCommands} from "./command"; +import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; +import {Permissions, Message} from "discord.js"; +import {getPrefix} from "./structures"; +import {Config} from "./structures"; + +/////////// +// Steps // +/////////// +// 1. Someone sends a message in chat. +// 2. Check if bot, then load commands. +// 3. Check if "...". If not, check if "@...". Resolve prefix and cropped message (if possible). +// 4. Test if bot has permission to send messages. +// 5. Once confirmed as a command, resolve the subcommand. +// 6. Check permission level and whether or not it's an endpoint. +// 7. Execute command if all successful. + +// For custom message events that want to cancel this one on certain conditions. +const interceptRules: ((message: Message) => boolean)[] = [(message) => message.author.bot]; + +export function addInterceptRule(handler: (message: Message) => boolean) { + interceptRules.push(handler); +} client.on("message", async (message) => { + for (const shouldIntercept of interceptRules) { + if (shouldIntercept(message)) { + return; + } + } + const commands = await loadableCommands; - if (message.content.toLowerCase().includes("remember to drink water")) { - message.react("🚱"); - } - - // Message Setup // - if (message.author.bot) return; - - // If there's an inline reply, fire off that event listener (if it exists). - if (message.reference) { - const reference = message.reference; - replyEventListeners.get(`${reference.channelID}-${reference.messageID}`)?.(message); - } - let prefix = getPrefix(message.guild); const originalPrefix = prefix; let exitEarly = !message.content.startsWith(prefix); const clientUser = message.client.user; let usesBotSpecificPrefix = false; - if (!message.content.startsWith(prefix)) { - return quote(message); - } - // If the client user exists, check if it starts with the bot-specific prefix. if (clientUser) { // If the prefix starts with the bot-specific prefix, go off that instead (these two options must mutually exclude each other). @@ -87,34 +92,8 @@ client.on("message", async (message) => { ); // Subcommand Recursion // - let command = commands.get(header); - if (!command) return console.warn(`Command "${header}" was called but for some reason it's still undefined!`); - const params: any[] = []; - let isEndpoint = false; - let permLevel = command.permission ?? 0; - - for (let param of args) { - if (command.endpoint) { - if (command.subcommands.size > 0 || command.user || command.number || command.any) - console.warn(`An endpoint cannot have subcommands! Check ${originalPrefix}${header} again.`); - isEndpoint = true; - break; - } - - const type = command.resolve(param); - command = command.get(param); - permLevel = command.permission ?? permLevel; - - if (type === Command.TYPES.USER) { - const id = param.match(/\d+/g)![0]; - try { - params.push(await message.client.users.fetch(id)); - } catch (error) { - return message.channel.send(`No user found by the ID \`${id}\`!`); - } - } else if (type === Command.TYPES.NUMBER) params.push(Number(param)); - else if (type !== Command.TYPES.SUBCOMMAND) params.push(param); - } + let command = commands.get(header)!; + //resolveSubcommand() if (!message.member) return console.warn("This command was likely called from a DM channel meaning the member object is null."); @@ -147,6 +126,40 @@ client.on("message", async (message) => { }); }); +// Takes a base command and a list of string parameters and returns: +// - The resolved subcommand +// - The resolved parameters +// - Whether or not an endpoint has been broken +// - The permission level required +async function resolveSubcommand(command: Command, args: string[]): [Command, any[], boolean, number] { + const params: any[] = []; + let isEndpoint = false; + let permLevel = command.permission ?? 0; + + for (const param of args) { + if (command.endpoint) { + if (command.subcommands.size > 0 || command.user || command.number || command.any) + console.warn("An endpoint cannot have subcommands!"); + isEndpoint = true; + break; + } + + const type = command.resolve(param); + command = command.get(param); + permLevel = command.permission ?? permLevel; + + if (type === Command.TYPES.USER) { + const id = param.match(/\d+/g)![0]; + try { + params.push(await message.client.users.fetch(id)); + } catch (error) { + return message.channel.send(`No user found by the ID \`${id}\`!`); + } + } else if (type === Command.TYPES.NUMBER) params.push(Number(param)); + else if (type !== Command.TYPES.SUBCOMMAND) params.push(param); + } +} + client.once("ready", () => { if (client.user) { console.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`); diff --git a/src/core/libd.ts b/src/core/libd.ts index 71ba70a..3746b6a 100644 --- a/src/core/libd.ts +++ b/src/core/libd.ts @@ -9,20 +9,9 @@ import { NewsChannel, MessageOptions } from "discord.js"; -import {client} from "../index"; +import {unreactEventListeners, replyEventListeners} from "./eventListeners"; -// A list of message ID and callback pairs. You get the emote name and ID of the user reacting. -const eventListeners: Map void> = new Map(); - -// Attached to the client, there can be one event listener attached to a message ID which is executed if present. -client.on("messageReactionRemove", (reaction, user) => { - const canDeleteEmotes = botHasPermission(reaction.message.guild, Permissions.FLAGS.MANAGE_MESSAGES); - - if (!canDeleteEmotes) { - const callback = eventListeners.get(reaction.message.id); - callback && callback(reaction.emoji.name, user.id); - } -}); +export type SingleMessageOptions = MessageOptions & {split?: false}; export function botHasPermission(guild: Guild | null, permission: number): boolean { return !!guild?.me?.hasPermission(permission); @@ -36,7 +25,7 @@ export async function paginate( channel: TextChannel | DMChannel | NewsChannel, senderID: string, total: number, - callback: (page: number, hasMultiplePages: boolean) => MessageOptions & {split?: false}, + callback: (page: number, hasMultiplePages: boolean) => SingleMessageOptions, duration = 60000 ) { const hasMultiplePages = total > 1; @@ -70,7 +59,7 @@ export async function paginate( // Listen for reactions and call the handler. let backwardsReaction = await message.react(BACKWARDS_EMOJI); let forwardsReaction = await message.react(FORWARDS_EMOJI); - eventListeners.set(message.id, handle); + unreactEventListeners.set(message.id, handle); const collector = message.createReactionCollector( (reaction, user) => { if (user.id === senderID) { @@ -91,7 +80,7 @@ export async function paginate( // When time's up, remove the bot's own reactions. collector.on("end", () => { - eventListeners.delete(message.id); + unreactEventListeners.delete(message.id); backwardsReaction.users.remove(message.author); forwardsReaction.users.remove(message.author); }); @@ -127,10 +116,6 @@ export async function prompt(message: Message, senderID: string, onConfirm: () = if (!isDeleted) message.delete(); } -// A list of "channel-message" and callback pairs. Also, I imagine that the callback will be much more maintainable when discord.js v13 comes out with a dedicated message.referencedMessage property. -// Also, I'm defining it here instead of the message event because the load order screws up if you export it from there. Yeah... I'm starting to notice just how much technical debt has been built up. The command handler needs to be modularized and refactored sooner rather than later. Define all constants in one area then grab from there. -export const replyEventListeners = new Map void>(); - // Asks the user for some input using the inline reply feature. The message here is a message you send beforehand. // If the reply is rejected, reply with an error message (when stable support comes from discord.js). // Append "\n*(Note: Make sure to use Discord's inline reply feature or this won't work!)*" in the future? And also the "you can now reply to this message" edit. diff --git a/src/index.ts b/src/index.ts index 497c4f6..5d1f52c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,7 +15,10 @@ setup.init().then(() => { // Initialize Modules // import "./core/handler"; // Command loading will start as soon as an instance of "core/command" is loaded, which is loaded in "core/handler". +import "./core/eventListeners"; import "./modules/presence"; import "./modules/lavalink"; import "./modules/emoteRegistry"; import "./modules/channelListener"; +import "./modules/intercept"; +import "./modules/messageEmbed"; diff --git a/src/modules/intercept.ts b/src/modules/intercept.ts new file mode 100644 index 0000000..c9a517d --- /dev/null +++ b/src/modules/intercept.ts @@ -0,0 +1,7 @@ +import {client} from "../index"; + +client.on("message", (message) => { + if (message.content.toLowerCase().includes("remember to drink water")) { + message.react("🚱"); + } +}); diff --git a/src/modules/messageEmbed.test.ts b/src/modules/messageEmbed.test.ts new file mode 100644 index 0000000..3508de2 --- /dev/null +++ b/src/modules/messageEmbed.test.ts @@ -0,0 +1,55 @@ +jest.useFakeTimers(); +import {strict as assert} from "assert"; +//import {extractFirstMessageLink} from "./messageEmbed"; + +/*describe("modules/messageEmbed", () => { + describe("extractFirstMessageLink()", () => { + const guildID = "802906483866631183"; + const channelID = "681747101169682147" + const messageID = "996363055050949479"; + const post = `channels/${guildID}/${channelID}/${messageID}`; + const commonUrl = `https://discord.com/channels/${post}`; + const combined = [guildID, channelID, messageID]; + + it('should return work and extract correctly on an isolated link', () => { + const result = extractFirstMessageLink(commonUrl); + assert.deepStrictEqual(result, combined); + }) + + it('should return work and extract correctly on a link within a message', () => { + const result = extractFirstMessageLink(`sample text${commonUrl}, more sample text`); + assert.deepStrictEqual(result, combined); + }) + + it('should return null on "!link"', () => { + const result = extractFirstMessageLink(`just some !${commonUrl} text`); + assert.strictEqual(result, null); + }) + + it('should return null on ""', () => { + const result = extractFirstMessageLink(`just some <${commonUrl}> text`); + assert.strictEqual(result, null); + }) + + it('should return work and extract correctly on " { + const result = extractFirstMessageLink(`just some <${commonUrl} text`); + assert.deepStrictEqual(result, combined); + }) + + it('should return work and extract correctly on "link>"', () => { + const result = extractFirstMessageLink(`just some ${commonUrl}> text`); + assert.deepStrictEqual(result, combined); + }) + + it('should return work and extract correctly on a canary link', () => { + const result = extractFirstMessageLink(`https://canary.discord.com/${post}`); + assert.deepStrictEqual(result, combined); + }) + }) +});*/ + +describe("placeholder", () => { + it("placeholder", async () => { + assert.strictEqual(1, 1); + }); +}); diff --git a/src/modules/message_embed.ts b/src/modules/messageEmbed.ts similarity index 65% rename from src/modules/message_embed.ts rename to src/modules/messageEmbed.ts index e8bdb6d..1d5c1c5 100644 --- a/src/modules/message_embed.ts +++ b/src/modules/messageEmbed.ts @@ -1,19 +1,14 @@ -import {client} from ".."; -import {Message, TextChannel, APIMessage, MessageEmbed} from "discord.js"; +import {client} from "../index"; +import {TextChannel, APIMessage, MessageEmbed} from "discord.js"; import {getPrefix} from "../core/structures"; import {DiscordAPIError} from "discord.js"; -export default async function quote(message: Message) { - if (message.author.bot) return; - // const message_link_regex = message.content.match(/(!)?https?:\/\/\w+\.com\/channels\/(\d+)\/(\d+)\/(\d+)/) - const message_link_regex = message.content.match( - /([?)/ - ); - - if (message_link_regex == null) return; - const [, char, guildID, channelID, messageID] = message_link_regex; - - if (char || message.content.startsWith(getPrefix(message.guild))) return; +client.on("message", async (message) => { + // Only execute if the message is from a user and isn't a command. + if (message.content.startsWith(getPrefix(message.guild)) || message.author.bot) return; + const messageLink = extractFirstMessageLink(message.content); + if (!messageLink) return; + const [guildID, channelID, messageID] = messageLink; try { const channel = client.guilds.cache.get(guildID)?.channels.cache.get(channelID) as TextChannel; @@ -58,4 +53,15 @@ export default async function quote(message: Message) { } return console.error(error); } +}); + +export function extractFirstMessageLink(message: string): [string, string, string] | null { + const messageLinkMatch = message.match( + /([!<])?https?:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/channels\/(\d{17,19})\/(\d{17,19})\/(\d{17,19})(>)?/ + ); + if (messageLinkMatch === null) return null; + const [, leftToken, guildID, channelID, messageID, rightToken] = messageLinkMatch; + // "!link" and "" will cancel the embed request. + if (leftToken === "!" || (leftToken === "<" && rightToken === ">")) return null; + else return [guildID, channelID, messageID]; } From f650faee8985eb0277e19bb0b1a86115b35ae0b0 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Sat, 3 Apr 2021 04:58:20 -0500 Subject: [PATCH 14/30] Reorganized code dealing with the command class --- src/commands/system/help.ts | 82 ++-------------- src/core/command.ts | 184 +++++++++++++++++++++++------------- src/core/handler.ts | 179 +++++++++-------------------------- src/core/loader.ts | 103 ++++++++++++++++++++ 4 files changed, 269 insertions(+), 279 deletions(-) create mode 100644 src/core/loader.ts diff --git a/src/commands/system/help.ts b/src/commands/system/help.ts index 8ef9fe2..6277fde 100644 --- a/src/commands/system/help.ts +++ b/src/commands/system/help.ts @@ -1,6 +1,6 @@ import Command from "../../core/command"; import {toTitleCase} from "../../core/lib"; -import {loadableCommands, categories} from "../../core/command"; +import {loadableCommands, categories} from "../../core/loader"; import {getPermissionName} from "../../core/permissions"; export default new Command({ @@ -32,69 +32,7 @@ export default new Command({ }, any: new Command({ async run($) { - const commands = await loadableCommands; - let header = $.args.shift() as string; - let command = commands.get(header); - - if (!command || header === "test") { - $.channel.send(`No command found by the name \`${header}\`!`); - return; - } - - if (command.originalCommandName) header = command.originalCommandName; - else console.warn(`originalCommandName isn't defined for ${header}?!`); - - let permLevel = command.permission ?? 0; - let usage = command.usage; - let invalid = false; - - let selectedCategory = "Unknown"; - - for (const [category, headers] of categories) { - if (headers.includes(header)) { - if (selectedCategory !== "Unknown") - console.warn( - `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.` - ); - else selectedCategory = toTitleCase(category); - } - } - - for (const param of $.args) { - const type = command.resolve(param); - command = command.get(param); - permLevel = command.permission ?? permLevel; - - if (permLevel === -1) permLevel = command.permission; - - switch (type) { - case Command.TYPES.SUBCOMMAND: - header += ` ${command.originalCommandName}`; - break; - case Command.TYPES.USER: - header += " "; - break; - case Command.TYPES.NUMBER: - header += " "; - break; - case Command.TYPES.ANY: - header += " "; - break; - default: - header += ` ${param}`; - break; - } - - if (type === Command.TYPES.NONE) { - invalid = true; - break; - } - } - - if (invalid) { - $.channel.send(`No command found by the name \`${header}\`!`); - return; - } + // [category, commandName, command, subcommandInfo] = resolveCommandInfo(); let append = ""; @@ -123,18 +61,10 @@ export default new Command({ append = "Usages:" + (list.length > 0 ? `\n${list.join("\n")}` : " None."); } else append = `Usage: \`${header} ${usage}\``; - let aliases = "None"; - - if (command.aliases.length > 0) { - aliases = ""; - - for (let i = 0; i < command.aliases.length; i++) { - const alias = command.aliases[i]; - aliases += `\`${alias}\``; - - if (i !== command.aliases.length - 1) aliases += ", "; - } - } + const formattedAliases: string[] = []; + for (const alias of command.aliases) formattedAliases.push(`\`${alias}\``); + // Short circuit an empty string, in this case, if there are no aliases. + const aliases = formattedAliases.join(", ") || "None"; $.channel.send( `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${selectedCategory}\`\nPermission Required: \`${getPermissionName( diff --git a/src/core/command.ts b/src/core/command.ts index c13b3a3..4cfb777 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -2,7 +2,8 @@ import {parseVars} from "./lib"; import {Collection} from "discord.js"; import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember} from "discord.js"; import {getPrefix} from "../core/structures"; -import glob from "glob"; +import {SingleMessageOptions} from "./libd"; +import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; interface CommandMenu { args: any[]; @@ -27,7 +28,7 @@ interface CommandOptions { any?: Command; } -export enum TYPES { +enum TYPES { SUBCOMMAND, USER, NUMBER, @@ -47,7 +48,6 @@ export default class Command { public user: Command | null; public number: Command | null; public any: Command | null; - public static readonly TYPES = TYPES; constructor(options?: CommandOptions) { this.description = options?.description || "No description."; @@ -120,6 +120,67 @@ export default class Command { } else this.run($).catch(handler.bind($)); } + // Will return null if it successfully executes, SingleMessageOptions if there's an error (to let the user know what it is). + public async actualExecute(args: string[], tmp: any): Promise { + // Subcommand Recursion // + let command = commands.get(header)!; + //resolveSubcommand() + const params: any[] = []; + let isEndpoint = false; + let permLevel = command.permission ?? 0; + + for (const param of args) { + if (command.endpoint) { + if (command.subcommands.size > 0 || command.user || command.number || command.any) + console.warn("An endpoint cannot have subcommands!"); + isEndpoint = true; + break; + } + + const type = command.resolve(param); + command = command.get(param); + permLevel = command.permission ?? permLevel; + + if (type === TYPES.USER) { + const id = param.match(/\d+/g)![0]; + try { + params.push(await message.client.users.fetch(id)); + } catch (error) { + return message.channel.send(`No user found by the ID \`${id}\`!`); + } + } else if (type === TYPES.NUMBER) params.push(Number(param)); + else if (type !== TYPES.SUBCOMMAND) params.push(param); + } + + if (!message.member) + return console.warn("This command was likely called from a DM channel meaning the member object is null."); + + if (!hasPermission(message.member, permLevel)) { + const userPermLevel = getPermissionLevel(message.member); + return message.channel.send( + `You don't have access to this command! Your permission level is \`${getPermissionName( + userPermLevel + )}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName( + permLevel + )}\` (${permLevel}).` + ); + } + + if (isEndpoint) return message.channel.send("Too many arguments!"); + + command.execute({ + args: params, + author: message.author, + channel: message.channel, + client: message.client, + guild: message.guild, + member: message.member, + message: message + }); + + return null; + } + public resolve(param: string): TYPES { if (this.subcommands.has(param)) return TYPES.SUBCOMMAND; // Any Discord ID format will automatically format to a user ID. @@ -154,84 +215,73 @@ export default class Command { return command; } -} -// Internally, it'll keep its original capitalization. It's up to you to convert it to title case when you make a help command. -export const categories = new Collection(); + // Returns: [category, command name, command, available subcommands: [type, subcommand]] + public resolveCommandInfo(args: string[]): [string, string, Command, Collection] { + const commands = await loadableCommands; + let header = args.shift(); + let command = commands.get(header); -/** Returns the cache of the commands if it exists and searches the directory if not. */ -export const loadableCommands = (async () => { - const commands = new Collection(); - // Include all .ts files recursively in "src/commands/". - const files = await globP("src/commands/**/*.ts"); - // Extract the usable parts from "src/commands/" if: - // - The path is 1 to 2 subdirectories (a or a/b, not a/b/c) - // - Any leading directory isn't "modules" - // - The filename doesn't end in .test.ts (for jest testing) - // - The filename cannot be the hardcoded top-level "template.ts", reserved for generating templates - const pattern = /src\/commands\/(?!template\.ts)(?!modules\/)(\w+(?:\/\w+)?)(?:test\.)?\.ts/; - const lists: {[category: string]: string[]} = {}; + if (!command || header === "test") { + $.channel.send(`No command found by the name \`${header}\`!`); + return; + } - for (const path of files) { - const match = pattern.exec(path); + if (command.originalCommandName) header = command.originalCommandName; + else console.warn(`originalCommandName isn't defined for ${header}?!`); - if (match) { - const commandID = match[1]; // e.g. "utilities/info" - const slashIndex = commandID.indexOf("/"); - const isMiscCommand = slashIndex !== -1; - const category = isMiscCommand ? commandID.substring(0, slashIndex) : "miscellaneous"; - const commandName = isMiscCommand ? commandID.substring(slashIndex + 1) : commandID; // e.g. "info" - // If the dynamic import works, it must be an object at the very least. Then, just test to see if it's a proper instance. - const command = (await import(`../commands/${commandID}`)).default as unknown; + let permLevel = command.permission ?? 0; + let usage = command.usage; + let invalid = false; - if (command instanceof Command) { - command.originalCommandName = commandName; + let selectedCategory = "Unknown"; - if (commands.has(commandName)) { + for (const [category, headers] of categories) { + if (headers.includes(header)) { + if (selectedCategory !== "Unknown") console.warn( - `Command "${commandName}" already exists! Make sure to make each command uniquely identifiable across categories!` + `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.` ); - } else { - commands.set(commandName, command); - } - - for (const alias of command.aliases) { - if (commands.has(alias)) { - console.warn( - `Top-level alias "${alias}" from command "${commandID}" already exists either as a command or alias!` - ); - } else { - commands.set(alias, command); - } - } - - if (!(category in lists)) lists[category] = []; - lists[category].push(commandName); - - console.log(`Loading Command: ${commandID}`); - } else { - console.warn(`Command "${commandID}" has no default export which is a Command instance!`); + else selectedCategory = toTitleCase(category); } } - } - for (const category in lists) { - categories.set(category, lists[category]); - } + for (const param of args) { + const type = command.resolve(param); + command = command.get(param); + permLevel = command.permission ?? permLevel; - return commands; -})(); + if (permLevel === -1) permLevel = command.permission; -function globP(path: string) { - return new Promise((resolve, reject) => { - glob(path, (error, files) => { - if (error) { - reject(error); - } else { - resolve(files); + switch (type) { + case TYPES.SUBCOMMAND: + header += ` ${command.originalCommandName}`; + break; + case TYPES.USER: + header += " "; + break; + case TYPES.NUMBER: + header += " "; + break; + case TYPES.ANY: + header += " "; + break; + default: + header += ` ${param}`; + break; } - }); - }); + + if (type === TYPES.NONE) { + invalid = true; + break; + } + } + + if (invalid) { + $.channel.send(`No command found by the name \`${header}\`!`); + return; + } + } } // If you use promises, use this function to display the error in chat. diff --git a/src/core/handler.ts b/src/core/handler.ts index 2a9fe42..d0cba3c 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -1,28 +1,17 @@ import {client} from "../index"; -import Command, {loadableCommands} from "./command"; -import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; +import {loadableCommands} from "./loader"; import {Permissions, Message} from "discord.js"; import {getPrefix} from "./structures"; import {Config} from "./structures"; -/////////// -// Steps // -/////////// -// 1. Someone sends a message in chat. -// 2. Check if bot, then load commands. -// 3. Check if "...". If not, check if "@...". Resolve prefix and cropped message (if possible). -// 4. Test if bot has permission to send messages. -// 5. Once confirmed as a command, resolve the subcommand. -// 6. Check permission level and whether or not it's an endpoint. -// 7. Execute command if all successful. - -// For custom message events that want to cancel this one on certain conditions. +// For custom message events that want to cancel the command handler on certain conditions. const interceptRules: ((message: Message) => boolean)[] = [(message) => message.author.bot]; export function addInterceptRule(handler: (message: Message) => boolean) { interceptRules.push(handler); } +// Note: client.user is only undefined before the bot logs in, so by this point, client.user cannot be undefined. client.on("message", async (message) => { for (const shouldIntercept of interceptRules) { if (shouldIntercept(message)) { @@ -30,139 +19,57 @@ client.on("message", async (message) => { } } - const commands = await loadableCommands; - - let prefix = getPrefix(message.guild); - const originalPrefix = prefix; - let exitEarly = !message.content.startsWith(prefix); - const clientUser = message.client.user; - let usesBotSpecificPrefix = false; - - // If the client user exists, check if it starts with the bot-specific prefix. - if (clientUser) { - // If the prefix starts with the bot-specific prefix, go off that instead (these two options must mutually exclude each other). - // The pattern here has an optional space at the end to capture that and make it not mess with the header and args. - const matches = message.content.match(new RegExp(`^<@!?${clientUser.id}> ?`)); - - if (matches) { - prefix = matches[0]; - exitEarly = false; - usesBotSpecificPrefix = true; - } - } - - // If it doesn't start with the current normal prefix or the bot-specific unique prefix, exit the thread of execution early. - // Inline replies should still be captured here because if it doesn't exit early, two characters for a two-length prefix would still trigger commands. - if (exitEarly) return; - - const [header, ...args] = message.content.substring(prefix.length).split(/ +/); - - // If the message is just the prefix itself, move onto this block. - if (header === "" && args.length === 0) { - // I moved the bot-specific prefix to a separate conditional block to separate the logic. - // And because it listens for the mention as a prefix instead of a free-form mention, inline replies (probably) shouldn't ever trigger this unintentionally. - if (usesBotSpecificPrefix) { - message.channel.send(`${message.author.toString()}, my prefix on this guild is \`${originalPrefix}\`.`); - return; - } - } - - if (!commands.has(header)) return; - + // Continue if the bot has permission to send messages in this channel. if ( - message.channel.type === "text" && - !message.channel.permissionsFor(message.client.user || "")?.has(Permissions.FLAGS.SEND_MESSAGES) + message.channel.type === "dm" || + message.channel.permissionsFor(client.user!)!.has(Permissions.FLAGS.SEND_MESSAGES) ) { - let status; + const text = message.content; + const prefix = getPrefix(message.guild); - if (message.member?.hasPermission(Permissions.FLAGS.ADMINISTRATOR)) - status = - "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended."; - else - status = - "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong."; - - return message.author.send( - `I don't have permission to send messages in ${message.channel.toString()}. ${status}` - ); - } - - console.log( - `${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".` - ); - - // Subcommand Recursion // - let command = commands.get(header)!; - //resolveSubcommand() - - if (!message.member) - return console.warn("This command was likely called from a DM channel meaning the member object is null."); - - if (!hasPermission(message.member, permLevel)) { - const userPermLevel = getPermissionLevel(message.member); - return message.channel.send( - `You don't have access to this command! Your permission level is \`${getPermissionName( - userPermLevel - )}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName( - permLevel - )}\` (${permLevel}).` - ); - } - - if (isEndpoint) return message.channel.send("Too many arguments!"); - - // Execute with dynamic library attached. // - // The purpose of using $.bind($) is to clone the function so as to not modify the original $. - // The cloned function doesn't copy the properties, so Object.assign() is used. - // Object.assign() modifies the first element and returns that, the second element applies its properties and the third element applies its own overriding the second one. - command.execute({ - args: params, - author: message.author, - channel: message.channel, - client: message.client, - guild: message.guild, - member: message.member, - message: message - }); -}); - -// Takes a base command and a list of string parameters and returns: -// - The resolved subcommand -// - The resolved parameters -// - Whether or not an endpoint has been broken -// - The permission level required -async function resolveSubcommand(command: Command, args: string[]): [Command, any[], boolean, number] { - const params: any[] = []; - let isEndpoint = false; - let permLevel = command.permission ?? 0; - - for (const param of args) { - if (command.endpoint) { - if (command.subcommands.size > 0 || command.user || command.number || command.any) - console.warn("An endpoint cannot have subcommands!"); - isEndpoint = true; - break; + // First, test if the message is just a ping to the bot. + if (new RegExp(`^<@!?${client.user!.id}>$`).test(text)) { + message.channel.send(`${message.author}, my prefix on this guild is \`${prefix}\`.`); } + // Then check if it's a normal command. + else if (text.startsWith(prefix)) { + const [header, ...args] = text.substring(prefix.length).split(/ +/); + const commands = await loadableCommands; - const type = command.resolve(param); - command = command.get(param); - permLevel = command.permission ?? permLevel; + if (commands.has(header)) { + const command = commands.get(header)!; - if (type === Command.TYPES.USER) { - const id = param.match(/\d+/g)![0]; - try { - params.push(await message.client.users.fetch(id)); - } catch (error) { - return message.channel.send(`No user found by the ID \`${id}\`!`); + // Send the arguments to the command to resolve and execute. + // TMP[MAKE SURE TO REPLACE WITH command.execute WHEN FINISHED] + const result = await command.actualExecute(args, { + author: message.author, + channel: message.channel, + client: message.client, + guild: message.guild, + member: message.member, + message: message + }); + + // If something went wrong, let the user know (like if they don't have permission to use a command). + if (result) { + message.channel.send(result); + } } - } else if (type === Command.TYPES.NUMBER) params.push(Number(param)); - else if (type !== Command.TYPES.SUBCOMMAND) params.push(param); + } + } else { + message.author.send( + `I don't have permission to send messages in ${message.channel}. ${ + message.member!.hasPermission(Permissions.FLAGS.ADMINISTRATOR) + ? "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended." + : "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong." + }` + ); } -} +}); client.once("ready", () => { if (client.user) { - console.ready(`Logged in as ${client.user.username}#${client.user.discriminator}.`); + console.ready(`Logged in as ${client.user.tag}.`); client.user.setActivity({ type: "LISTENING", name: `${Config.prefix}help` diff --git a/src/core/loader.ts b/src/core/loader.ts new file mode 100644 index 0000000..a7f7d7d --- /dev/null +++ b/src/core/loader.ts @@ -0,0 +1,103 @@ +import {Collection} from "discord.js"; +import glob from "glob"; +import Command from "./command"; + +// Internally, it'll keep its original capitalization. It's up to you to convert it to title case when you make a help command. +export const categories = new Collection(); + +/** Returns the cache of the commands if it exists and searches the directory if not. */ +export const loadableCommands = (async () => { + const commands = new Collection(); + // Include all .ts files recursively in "src/commands/". + const files = await globP("src/commands/**/*.ts"); + // Extract the usable parts from "src/commands/" if: + // - The path is 1 to 2 subdirectories (a or a/b, not a/b/c) + // - Any leading directory isn't "modules" + // - The filename doesn't end in .test.ts (for jest testing) + // - The filename cannot be the hardcoded top-level "template.ts", reserved for generating templates + const pattern = /src\/commands\/(?!template\.ts)(?!modules\/)(\w+(?:\/\w+)?)(?:test\.)?\.ts/; + const lists: {[category: string]: string[]} = {}; + + for (const path of files) { + const match = pattern.exec(path); + + if (match) { + const commandID = match[1]; // e.g. "utilities/info" + const slashIndex = commandID.indexOf("/"); + const isMiscCommand = slashIndex !== -1; + const category = isMiscCommand ? commandID.substring(0, slashIndex) : "miscellaneous"; + const commandName = isMiscCommand ? commandID.substring(slashIndex + 1) : commandID; // e.g. "info" + // If the dynamic import works, it must be an object at the very least. Then, just test to see if it's a proper instance. + const command = (await import(`../commands/${commandID}`)).default as unknown; + + if (command instanceof Command) { + command.originalCommandName = commandName; + + if (commands.has(commandName)) { + console.warn( + `Command "${commandName}" already exists! Make sure to make each command uniquely identifiable across categories!` + ); + } else { + commands.set(commandName, command); + } + + for (const alias of command.aliases) { + if (commands.has(alias)) { + console.warn( + `Top-level alias "${alias}" from command "${commandID}" already exists either as a command or alias!` + ); + } else { + commands.set(alias, command); + } + } + + if (!(category in lists)) lists[category] = []; + lists[category].push(commandName); + + console.log(`Loading Command: ${commandID}`); + } else { + console.warn(`Command "${commandID}" has no default export which is a Command instance!`); + } + } + } + + for (const category in lists) { + categories.set(category, lists[category]); + } + + return commands; +})(); + +function globP(path: string) { + return new Promise((resolve, reject) => { + glob(path, (error, files) => { + if (error) { + reject(error); + } else { + resolve(files); + } + }); + }); +} + +// Gathers a list of categories and top-level commands. +// Returns: new Collection() +/*export async function getCommandList(): Promise> { + const categorizedCommands = new Collection(); + const commands = await loadableCommands; + + for (const [category, headers] of categories) { + const commandList: Command[] = []; + + for (const header of headers) { + if (header !== "test") { + // If this is somehow undefined, it'll show up as an error when implementing a help command. + commandList.push(commands.get(header)!); + } + } + + categorizedCommands.set(toTitleCase(category), commandList); + } + + return categorizedCommands; +}*/ From 63441b4aca8582e3e841aee202aedc12b60b73ec Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Sun, 4 Apr 2021 17:28:32 -0500 Subject: [PATCH 15/30] Added more type guards/properties to Command class --- src/core/command.ts | 191 ++++++++++++++++++++++++++------------------ src/core/lib.ts | 10 +++ src/core/loader.ts | 6 +- 3 files changed, 126 insertions(+), 81 deletions(-) diff --git a/src/core/command.ts b/src/core/command.ts index 4cfb777..d25a647 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,10 +1,30 @@ -import {parseVars} from "./lib"; +import {parseVars, requireAllCasesHandledFor} from "./lib"; import {Collection} from "discord.js"; import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember} from "discord.js"; import {getPrefix} from "../core/structures"; import {SingleMessageOptions} from "./libd"; import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; +export enum TYPES { + SUBCOMMAND, + USER, + NUMBER, + ANY, + NONE +} + +// Callbacks don't work with discriminated unions: +// - https://github.com/microsoft/TypeScript/issues/41759 +// - https://github.com/microsoft/TypeScript/issues/35769 +// Therefore, there won't by any type narrowing on channel or guild of CommandMenu until this is fixed. +// Otherwise, you'd have to define channelType for every single subcommand, which would get very tedious. +// Just use type assertions when you specify a channel type. +export enum CHANNEL_TYPE { + ANY, + GUILD, + DM +} + interface CommandMenu { args: any[]; client: Client; @@ -12,97 +32,101 @@ interface CommandMenu { channel: TextChannel | DMChannel | NewsChannel; guild: Guild | null; author: User; + // According to the documentation, a message can be part of a guild while also not having a + // member object for the author. This will happen if the author of a message left the guild. member: GuildMember | null; } -interface CommandOptions { +interface CommandOptionsBase { description?: string; endpoint?: boolean; usage?: string; permission?: number; - aliases?: string[]; + nsfw?: boolean; + channelType?: CHANNEL_TYPE; run?: (($: CommandMenu) => Promise) | string; - subcommands?: {[key: string]: Command}; +} + +interface CommandOptionsEndpoint { + endpoint: true; +} + +// Prevents subcommands from being added by compile-time. +interface CommandOptionsNonEndpoint { + endpoint?: false; + subcommands?: {[key: string]: NamedCommand}; user?: Command; number?: Command; any?: Command; } -enum TYPES { - SUBCOMMAND, - USER, - NUMBER, - ANY, - NONE -} +type CommandOptions = CommandOptionsBase & (CommandOptionsEndpoint | CommandOptionsNonEndpoint); +type NamedCommandOptions = CommandOptions & {aliases?: string[]}; -export default class Command { +// RegEx patterns used for identifying/extracting each type from a string argument. +const patterns = { + // +}; + +export class Command { public readonly description: string; public readonly endpoint: boolean; public readonly usage: string; public readonly permission: number; // -1 (default) indicates to inherit, 0 is the lowest rank, 1 is second lowest rank, and so on. - public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. - public originalCommandName: string | null; // If the command is an alias, what's the original name? - public run: (($: CommandMenu) => Promise) | string; - public readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. - public user: Command | null; - public number: Command | null; - public any: Command | null; + public readonly nsfw: boolean; + public readonly channelType: CHANNEL_TYPE; + protected run: (($: CommandMenu) => Promise) | string; + protected readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. + protected user: Command | null; + protected number: Command | null; + protected any: Command | null; + public static readonly CHANNEL_TYPE = CHANNEL_TYPE; constructor(options?: CommandOptions) { this.description = options?.description || "No description."; - this.endpoint = options?.endpoint || false; - this.usage = options?.usage || ""; + this.endpoint = !!options?.endpoint; + this.usage = options?.usage ?? ""; this.permission = options?.permission ?? -1; - this.aliases = options?.aliases ?? []; - this.originalCommandName = null; + this.nsfw = !!options?.nsfw; + this.channelType = options?.channelType ?? CHANNEL_TYPE.ANY; this.run = options?.run || "No action was set on this command!"; this.subcommands = new Collection(); // Populate this collection after setting subcommands. - this.user = options?.user || null; - this.number = options?.number || null; - this.any = options?.any || null; + this.user = null; + this.number = null; + this.any = null; - if (options?.subcommands) { - const baseSubcommands = Object.keys(options.subcommands); + if (options && !options.endpoint) { + this.user = options?.user || null; + this.number = options?.number || null; + this.any = options?.any || null; - // Loop once to set the base subcommands. - for (const name in options.subcommands) this.subcommands.set(name, options.subcommands[name]); + if (options?.subcommands) { + const baseSubcommands = Object.keys(options.subcommands); - // Then loop again to make aliases point to the base subcommands and warn if something's not right. - // This shouldn't be a problem because I'm hoping that JS stores these as references that point to the same object. - for (const name in options.subcommands) { - const subcmd = options.subcommands[name]; - subcmd.originalCommandName = name; - const aliases = subcmd.aliases; + // Loop once to set the base subcommands. + for (const name in options.subcommands) this.subcommands.set(name, options.subcommands[name]); - for (const alias of aliases) { - if (baseSubcommands.includes(alias)) - console.warn( - `"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)` - ); - else if (this.subcommands.has(alias)) - console.warn( - `Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)` - ); - else this.subcommands.set(alias, subcmd); + // Then loop again to make aliases point to the base subcommands and warn if something's not right. + // This shouldn't be a problem because I'm hoping that JS stores these as references that point to the same object. + for (const name in options.subcommands) { + const subcmd = options.subcommands[name]; + subcmd.originalCommandName = name; + const aliases = subcmd.aliases; + + for (const alias of aliases) { + if (baseSubcommands.includes(alias)) + console.warn( + `"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)` + ); + else if (this.subcommands.has(alias)) + console.warn( + `Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)` + ); + else this.subcommands.set(alias, subcmd); + } } } } - - if (this.user && this.user.aliases.length > 0) - console.warn( - `There are aliases defined for a "user"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` - ); - - if (this.number && this.number.aliases.length > 0) - console.warn( - `There are aliases defined for a "number"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` - ); - - if (this.any && this.any.aliases.length > 0) - console.warn( - `There are aliases defined for an "any"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` - ); } public execute($: CommandMenu) { @@ -120,23 +144,18 @@ export default class Command { } else this.run($).catch(handler.bind($)); } + // Go through the arguments provided and find the right subcommand, then execute with the given arguments. // Will return null if it successfully executes, SingleMessageOptions if there's an error (to let the user know what it is). public async actualExecute(args: string[], tmp: any): Promise { + // For debug info, use this.originalCommandName? // Subcommand Recursion // - let command = commands.get(header)!; + let command: Command = new Command(); // = commands.get(header)!; //resolveSubcommand() const params: any[] = []; let isEndpoint = false; let permLevel = command.permission ?? 0; for (const param of args) { - if (command.endpoint) { - if (command.subcommands.size > 0 || command.user || command.number || command.any) - console.warn("An endpoint cannot have subcommands!"); - isEndpoint = true; - break; - } - const type = command.resolve(param); command = command.get(param); permLevel = command.permission ?? permLevel; @@ -181,7 +200,7 @@ export default class Command { return null; } - public resolve(param: string): TYPES { + private resolve(param: string): TYPES { if (this.subcommands.has(param)) return TYPES.SUBCOMMAND; // Any Discord ID format will automatically format to a user ID. else if (this.user && /\d{17,19}/.test(param)) return TYPES.USER; @@ -191,33 +210,35 @@ export default class Command { else return TYPES.NONE; } - public get(param: string): Command { + private get(param: string): Command { const type = this.resolve(param); let command: Command; switch (type) { case TYPES.SUBCOMMAND: - command = this.subcommands.get(param) as Command; + command = this.subcommands.get(param)!; break; case TYPES.USER: - command = this.user as Command; + command = this.user!; break; case TYPES.NUMBER: - command = this.number as Command; + command = this.number!; break; case TYPES.ANY: - command = this.any as Command; + command = this.any!; break; - default: + case TYPES.NONE: command = this; break; + default: + requireAllCasesHandledFor(type); } return command; } // Returns: [category, command name, command, available subcommands: [type, subcommand]] - public resolveCommandInfo(args: string[]): [string, string, Command, Collection] { + public async resolveInfo(args: string[]): [string, string, Command, Collection] | null { const commands = await loadableCommands; let header = args.shift(); let command = commands.get(header); @@ -253,6 +274,7 @@ export default class Command { if (permLevel === -1) permLevel = command.permission; + // Switch over to doing `$help info ` switch (type) { case TYPES.SUBCOMMAND: header += ` ${command.originalCommandName}`; @@ -266,9 +288,11 @@ export default class Command { case TYPES.ANY: header += " "; break; - default: + case TYPES.NONE: header += ` ${param}`; break; + default: + requireAllCasesHandledFor(type); } if (type === TYPES.NONE) { @@ -284,6 +308,17 @@ export default class Command { } } +export class NamedCommand extends Command { + public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. + public originalCommandName: string | null; // If the command is an alias, what's the original name? + + constructor(options?: NamedCommandOptions) { + super(options); + this.aliases = options?.aliases || []; + this.originalCommandName = null; + } +} + // If you use promises, use this function to display the error in chat. // Case #1: await $.channel.send(""); --> Automatically caught by Command.execute(). // Case #2: $.channel.send("").catch(handler.bind($)); --> Manually caught by the user. diff --git a/src/core/lib.ts b/src/core/lib.ts index 21b4a95..477089f 100644 --- a/src/core/lib.ts +++ b/src/core/lib.ts @@ -224,3 +224,13 @@ export function split(array: T[], lengthOfEachSection: number): T[][] { return sections; } + +/** + * Utility function to require all possible cases to be handled at compile time. + * + * To use this function, place it in the "default" case of a switch statement or the "else" statement of an if-else branch. + * If all cases are handled, the variable being tested for should be of type "never", and if it isn't, that means not all cases are handled yet. + */ +export function requireAllCasesHandledFor(variable: never): never { + throw new Error(`This function should never be called but got the value: ${variable}`); +} diff --git a/src/core/loader.ts b/src/core/loader.ts index a7f7d7d..8333c1b 100644 --- a/src/core/loader.ts +++ b/src/core/loader.ts @@ -1,6 +1,6 @@ import {Collection} from "discord.js"; import glob from "glob"; -import Command from "./command"; +import {Command, NamedCommand} from "./command"; // Internally, it'll keep its original capitalization. It's up to you to convert it to title case when you make a help command. export const categories = new Collection(); @@ -30,7 +30,7 @@ export const loadableCommands = (async () => { // If the dynamic import works, it must be an object at the very least. Then, just test to see if it's a proper instance. const command = (await import(`../commands/${commandID}`)).default as unknown; - if (command instanceof Command) { + if (command instanceof NamedCommand) { command.originalCommandName = commandName; if (commands.has(commandName)) { @@ -56,7 +56,7 @@ export const loadableCommands = (async () => { console.log(`Loading Command: ${commandID}`); } else { - console.warn(`Command "${commandID}" has no default export which is a Command instance!`); + console.warn(`Command "${commandID}" has no default export which is a NamedCommand instance!`); } } } From 6eea0689099fbfbb83b162b99ef824e3225d36f8 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Sun, 4 Apr 2021 19:35:10 -0500 Subject: [PATCH 16/30] Reworked Command.execute and subcommand recursion --- src/core/command.ts | 305 ++++++++++++++++++++-------------------- src/core/handler.ts | 49 ++++--- src/core/permissions.ts | 14 +- 3 files changed, 192 insertions(+), 176 deletions(-) diff --git a/src/core/command.ts b/src/core/command.ts index d25a647..20b7f90 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,9 +1,10 @@ import {parseVars, requireAllCasesHandledFor} from "./lib"; import {Collection} from "discord.js"; -import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember} from "discord.js"; +import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember, GuildChannel} from "discord.js"; import {getPrefix} from "../core/structures"; import {SingleMessageOptions} from "./libd"; import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; +import {client} from "../index"; export enum TYPES { SUBCOMMAND, @@ -13,6 +14,16 @@ export enum TYPES { NONE } +// RegEx patterns used for identifying/extracting each type from a string argument. +const patterns = { + channel: /^<#(\d{17,19})>$/, + role: /^<@&(\d{17,19})>$/, + emote: /^$/, + message: /(?:\d{17,19}\/(\d{17,19})\/(\d{17,19})$)|(?:^(\d{17,19})-(\d{17,19})$)/, + user: /^<@!?(\d{17,19})>$/, + id: /^(\d{17,19})$/ +}; + // Callbacks don't work with discriminated unions: // - https://github.com/microsoft/TypeScript/issues/41759 // - https://github.com/microsoft/TypeScript/issues/35769 @@ -26,55 +37,58 @@ export enum CHANNEL_TYPE { } interface CommandMenu { - args: any[]; - client: Client; - message: Message; - channel: TextChannel | DMChannel | NewsChannel; - guild: Guild | null; - author: User; + readonly args: any[]; + readonly client: Client; + readonly message: Message; + readonly channel: TextChannel | DMChannel | NewsChannel; + readonly guild: Guild | null; + readonly author: User; // According to the documentation, a message can be part of a guild while also not having a // member object for the author. This will happen if the author of a message left the guild. - member: GuildMember | null; + readonly member: GuildMember | null; } interface CommandOptionsBase { - description?: string; - endpoint?: boolean; - usage?: string; - permission?: number; - nsfw?: boolean; - channelType?: CHANNEL_TYPE; - run?: (($: CommandMenu) => Promise) | string; + readonly description?: string; + readonly endpoint?: boolean; + readonly usage?: string; + readonly permission?: number; + readonly nsfw?: boolean; + readonly channelType?: CHANNEL_TYPE; + readonly run?: (($: CommandMenu) => Promise) | string; } interface CommandOptionsEndpoint { - endpoint: true; + readonly endpoint: true; } // Prevents subcommands from being added by compile-time. interface CommandOptionsNonEndpoint { - endpoint?: false; - subcommands?: {[key: string]: NamedCommand}; - user?: Command; - number?: Command; - any?: Command; + readonly endpoint?: false; + readonly subcommands?: {[key: string]: NamedCommand}; + readonly user?: Command; + readonly number?: Command; + readonly any?: Command; } type CommandOptions = CommandOptionsBase & (CommandOptionsEndpoint | CommandOptionsNonEndpoint); type NamedCommandOptions = CommandOptions & {aliases?: string[]}; -// RegEx patterns used for identifying/extracting each type from a string argument. -const patterns = { - // -}; +interface ExecuteCommandMetadata { + readonly header: string; + readonly args: string[]; + permission: number; + nsfw: boolean; + channelType: CHANNEL_TYPE; +} export class Command { public readonly description: string; public readonly endpoint: boolean; public readonly usage: string; public readonly permission: number; // -1 (default) indicates to inherit, 0 is the lowest rank, 1 is second lowest rank, and so on. - public readonly nsfw: boolean; - public readonly channelType: CHANNEL_TYPE; + public readonly nsfw: boolean | null; // null (default) indicates to inherit + public readonly channelType: CHANNEL_TYPE | null; // null (default) indicates to inherit protected run: (($: CommandMenu) => Promise) | string; protected readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. protected user: Command | null; @@ -87,8 +101,8 @@ export class Command { this.endpoint = !!options?.endpoint; this.usage = options?.usage ?? ""; this.permission = options?.permission ?? -1; - this.nsfw = !!options?.nsfw; - this.channelType = options?.channelType ?? CHANNEL_TYPE.ANY; + this.nsfw = options?.nsfw ?? null; + this.channelType = options?.channelType ?? null; this.run = options?.run || "No action was set on this command!"; this.subcommands = new Collection(); // Populate this collection after setting subcommands. this.user = null; @@ -129,116 +143,135 @@ export class Command { } } - public execute($: CommandMenu) { - if (typeof this.run === "string") { - $.channel.send( - parseVars( - this.run, - { - author: $.author.toString(), - prefix: getPrefix($.guild) - }, - "???" - ) - ); - } else this.run($).catch(handler.bind($)); - } - // Go through the arguments provided and find the right subcommand, then execute with the given arguments. // Will return null if it successfully executes, SingleMessageOptions if there's an error (to let the user know what it is). - public async actualExecute(args: string[], tmp: any): Promise { - // For debug info, use this.originalCommandName? - // Subcommand Recursion // - let command: Command = new Command(); // = commands.get(header)!; - //resolveSubcommand() - const params: any[] = []; - let isEndpoint = false; - let permLevel = command.permission ?? 0; + public async execute( + args: string[], + menu: CommandMenu, + metadata: ExecuteCommandMetadata + ): Promise { + const param = args.shift(); - for (const param of args) { - const type = command.resolve(param); - command = command.get(param); - permLevel = command.permission ?? permLevel; + // If there are no arguments left, execute the current command. Otherwise, continue on. + if (!param) { + // See if there is anything that'll prevent the user from executing the command. - if (type === TYPES.USER) { - const id = param.match(/\d+/g)![0]; - try { - params.push(await message.client.users.fetch(id)); - } catch (error) { - return message.channel.send(`No user found by the ID \`${id}\`!`); + // 1. Does this command specify a required channel type? If so, does the channel type match? + if ( + metadata.channelType === CHANNEL_TYPE.GUILD && + (!(menu.channel instanceof GuildChannel) || menu.guild === null) + ) { + return {content: "This command must be executed in a server."}; + } else if ( + metadata.channelType === CHANNEL_TYPE.DM && + (menu.channel.type !== "dm" || menu.guild !== null) + ) { + return {content: "This command must be executed as a direct message."}; + } + + // 2. Is this an NSFW command where the channel prevents such use? (DM channels bypass this requirement.) + if (metadata.nsfw && menu.channel.type !== "dm" && !menu.channel.nsfw) { + return {content: "This command must be executed in either an NSFW channel or as a direct message."}; + } + + // 3. Does the user have permission to execute the command? + if (!hasPermission(menu.author, menu.member, metadata.permission)) { + const userPermLevel = getPermissionLevel(menu.author, menu.member); + + return { + content: `You don't have access to this command! Your permission level is \`${getPermissionName( + userPermLevel + )}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName( + metadata.permission + )}\` (${metadata.permission}).` + }; + } + + // Then capture any potential errors. + try { + if (typeof this.run === "string") { + await menu.channel.send( + parseVars( + this.run, + { + author: menu.author.toString(), + prefix: getPrefix(menu.guild) + }, + "???" + ) + ); + } else { + await this.run(menu); } - } else if (type === TYPES.NUMBER) params.push(Number(param)); - else if (type !== TYPES.SUBCOMMAND) params.push(param); + + return null; + } catch (error) { + const errorMessage = error.stack ?? error; + console.error(`Command Error: ${metadata.header} (${metadata.args})\n${errorMessage}`); + + return { + content: `There was an error while trying to execute that command!\`\`\`${errorMessage}\`\`\`` + }; + } } - if (!message.member) - return console.warn("This command was likely called from a DM channel meaning the member object is null."); + // If the current command is an endpoint but there are still some arguments left, don't continue. + if (this.endpoint) return {content: "Too many arguments!"}; - if (!hasPermission(message.member, permLevel)) { - const userPermLevel = getPermissionLevel(message.member); - return message.channel.send( - `You don't have access to this command! Your permission level is \`${getPermissionName( - userPermLevel - )}\` (${userPermLevel}), but this command requires a permission level of \`${getPermissionName( - permLevel - )}\` (${permLevel}).` - ); + // If the current command's permission level isn't -1 (inherit), then set the permission metadata equal to that. + if (this.permission !== -1) metadata.permission = this.permission; + + // If the current command has an NSFW setting specified, set it. + if (this.nsfw !== null) metadata.nsfw = this.nsfw; + + // If the current command doesn't inherit its channel type, set it. + if (this.channelType !== null) metadata.channelType = this.channelType; + + // Resolve the value of the current command's argument (adding it to the resolved args), + // then pass the thread of execution to whichever subcommand is valid (if any). + if (this.subcommands.has(param)) { + return this.subcommands.get(param)!.execute(args, menu, metadata); + } else if (this.user && patterns.user.test(param)) { + const id = patterns.user.exec(param)![1]; + + try { + menu.args.push(await client.users.fetch(id)); + return this.user.execute(args, menu, metadata); + } catch { + return { + content: `No user found by the ID \`${id}\`!` + }; + } + } else if (this.number && !Number.isNaN(Number(param)) && param !== "Infinity" && param !== "-Infinity") { + menu.args.push(Number(param)); + return this.number.execute(args, menu, metadata); + } else if (this.any) { + menu.args.push(param); + return this.any.execute(args, menu, metadata); + } else { + // Continue adding on the rest of the arguments if there's no valid subcommand. + menu.args.push(param); + return this.execute(args, menu, metadata); } - if (isEndpoint) return message.channel.send("Too many arguments!"); - - command.execute({ - args: params, - author: message.author, - channel: message.channel, - client: message.client, - guild: message.guild, - member: message.member, - message: message - }); - - return null; + // Note: Do NOT add a return statement here. In case one of the other sections is missing + // a return statement, there'll be a compile error to catch that. } +} - private resolve(param: string): TYPES { - if (this.subcommands.has(param)) return TYPES.SUBCOMMAND; - // Any Discord ID format will automatically format to a user ID. - else if (this.user && /\d{17,19}/.test(param)) return TYPES.USER; - // Disallow infinity and allow for 0. - else if (this.number && (Number(param) || param === "0") && !param.includes("Infinity")) return TYPES.NUMBER; - else if (this.any) return TYPES.ANY; - else return TYPES.NONE; - } +export class NamedCommand extends Command { + public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. + public originalCommandName: string | null; // If the command is an alias, what's the original name? - private get(param: string): Command { - const type = this.resolve(param); - let command: Command; - - switch (type) { - case TYPES.SUBCOMMAND: - command = this.subcommands.get(param)!; - break; - case TYPES.USER: - command = this.user!; - break; - case TYPES.NUMBER: - command = this.number!; - break; - case TYPES.ANY: - command = this.any!; - break; - case TYPES.NONE: - command = this; - break; - default: - requireAllCasesHandledFor(type); - } - - return command; + constructor(options?: NamedCommandOptions) { + super(options); + this.aliases = options?.aliases || []; + this.originalCommandName = null; } // Returns: [category, command name, command, available subcommands: [type, subcommand]] public async resolveInfo(args: string[]): [string, string, Command, Collection] | null { + // For debug info, use this.originalCommandName? (if it exists?) const commands = await loadableCommands; let header = args.shift(); let command = commands.get(header); @@ -307,31 +340,3 @@ export class Command { } } } - -export class NamedCommand extends Command { - public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. - public originalCommandName: string | null; // If the command is an alias, what's the original name? - - constructor(options?: NamedCommandOptions) { - super(options); - this.aliases = options?.aliases || []; - this.originalCommandName = null; - } -} - -// If you use promises, use this function to display the error in chat. -// Case #1: await $.channel.send(""); --> Automatically caught by Command.execute(). -// Case #2: $.channel.send("").catch(handler.bind($)); --> Manually caught by the user. -// TODO: Find a way to catch unhandled rejections automatically, forgoing the need for this. -export function handler(this: CommandMenu, error: Error) { - if (this) - this.channel.send( - `There was an error while trying to execute that command!\`\`\`${error.stack ?? error}\`\`\`` - ); - else - console.warn( - "No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!" - ); - - console.error(error); -} diff --git a/src/core/handler.ts b/src/core/handler.ts index d0cba3c..3566a77 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -3,6 +3,7 @@ import {loadableCommands} from "./loader"; import {Permissions, Message} from "discord.js"; import {getPrefix} from "./structures"; import {Config} from "./structures"; +import {CHANNEL_TYPE} from "./command"; // For custom message events that want to cancel the command handler on certain conditions. const interceptRules: ((message: Message) => boolean)[] = [(message) => message.author.bot]; @@ -19,17 +20,16 @@ client.on("message", async (message) => { } } + const {author, channel, content, guild, member} = message; + // Continue if the bot has permission to send messages in this channel. - if ( - message.channel.type === "dm" || - message.channel.permissionsFor(client.user!)!.has(Permissions.FLAGS.SEND_MESSAGES) - ) { - const text = message.content; - const prefix = getPrefix(message.guild); + if (channel.type === "dm" || channel.permissionsFor(client.user!)!.has(Permissions.FLAGS.SEND_MESSAGES)) { + const text = content; + const prefix = getPrefix(guild); // First, test if the message is just a ping to the bot. if (new RegExp(`^<@!?${client.user!.id}>$`).test(text)) { - message.channel.send(`${message.author}, my prefix on this guild is \`${prefix}\`.`); + channel.send(`${author}, my prefix on this guild is \`${prefix}\`.`); } // Then check if it's a normal command. else if (text.startsWith(prefix)) { @@ -41,25 +41,36 @@ client.on("message", async (message) => { // Send the arguments to the command to resolve and execute. // TMP[MAKE SURE TO REPLACE WITH command.execute WHEN FINISHED] - const result = await command.actualExecute(args, { - author: message.author, - channel: message.channel, - client: message.client, - guild: message.guild, - member: message.member, - message: message - }); + const result = await command.execute( + args, + { + author, + channel, + client, + guild, + member, + message, + args: [] + }, + { + header, + args, + permission: 0, + nsfw: false, + channelType: CHANNEL_TYPE.ANY + } + ); // If something went wrong, let the user know (like if they don't have permission to use a command). if (result) { - message.channel.send(result); + channel.send(result); } } } } else { - message.author.send( - `I don't have permission to send messages in ${message.channel}. ${ - message.member!.hasPermission(Permissions.FLAGS.ADMINISTRATOR) + author.send( + `I don't have permission to send messages in ${channel}. ${ + member!.hasPermission(Permissions.FLAGS.ADMINISTRATOR) ? "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended." : "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong." }` diff --git a/src/core/permissions.ts b/src/core/permissions.ts index 37b0000..9798a2a 100644 --- a/src/core/permissions.ts +++ b/src/core/permissions.ts @@ -15,7 +15,7 @@ export const PermissionLevels: PermissionLevel[] = [ { // MOD // name: "Moderator", - check: (_, member) => + check: (_user, member) => !!member && (member.hasPermission(Permissions.FLAGS.MANAGE_ROLES) || member.hasPermission(Permissions.FLAGS.MANAGE_MESSAGES) || @@ -25,12 +25,12 @@ export const PermissionLevels: PermissionLevel[] = [ { // ADMIN // name: "Administrator", - check: (_, member) => !!member && member.hasPermission(Permissions.FLAGS.ADMINISTRATOR) + check: (_user, member) => !!member && member.hasPermission(Permissions.FLAGS.ADMINISTRATOR) }, { // OWNER // name: "Server Owner", - check: (_, member) => !!member && member.guild.ownerID === member.id + check: (_user, member) => !!member && member.guild.ownerID === member.id }, { // BOT_SUPPORT // @@ -52,13 +52,13 @@ export const PermissionLevels: PermissionLevel[] = [ // After checking the lengths of these three objects, use this as the length for consistency. const length = PermissionLevels.length; -export function hasPermission(member: GuildMember, permission: number): boolean { - for (let i = length - 1; i >= permission; i--) if (PermissionLevels[i].check(member.user, member)) return true; +export function hasPermission(user: User, member: GuildMember | null, permission: number): boolean { + for (let i = length - 1; i >= permission; i--) if (PermissionLevels[i].check(user, member)) return true; return false; } -export function getPermissionLevel(member: GuildMember): number { - for (let i = length - 1; i >= 0; i--) if (PermissionLevels[i].check(member.user, member)) return i; +export function getPermissionLevel(user: User, member: GuildMember | null): number { + for (let i = length - 1; i >= 0; i--) if (PermissionLevels[i].check(user, member)) return i; return 0; } From 2a4d08d0bca51c56c7fea71dc5fd23d514370984 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Sun, 4 Apr 2021 20:01:29 -0500 Subject: [PATCH 17/30] Added special case for bot DMs --- src/core/handler.ts | 76 +++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/src/core/handler.ts b/src/core/handler.ts index 3566a77..cff1c41 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -12,7 +12,14 @@ export function addInterceptRule(handler: (message: Message) => boolean) { interceptRules.push(handler); } +const defaultMetadata = { + permission: 0, + nsfw: false, + channelType: CHANNEL_TYPE.ANY +}; + // Note: client.user is only undefined before the bot logs in, so by this point, client.user cannot be undefined. +// Note: guild.available will never need to be checked because the message starts in either a DM channel or an already-available guild. client.on("message", async (message) => { for (const shouldIntercept of interceptRules) { if (shouldIntercept(message)) { @@ -20,46 +27,65 @@ client.on("message", async (message) => { } } + const commands = await loadableCommands; const {author, channel, content, guild, member} = message; + const text = content; + const menu = { + author, + channel, + client, + guild, + member, + message, + args: [] + }; + // Execute a dedicated block for messages in DM channels. + if (channel.type === "dm") { + // In a DM channel, simply forget about the prefix and execute any message as a command. + const [header, ...args] = text.split(/ +/); + + if (commands.has(header)) { + const command = commands.get(header)!; + + // Send the arguments to the command to resolve and execute. + const result = await command.execute(args, menu, { + header, + args, + ...defaultMetadata + }); + + // If something went wrong, let the user know (like if they don't have permission to use a command). + if (result) { + channel.send(result); + } + } else { + channel.send( + `I couldn't find the command or alias that starts with \`${header}\`. To see the list of commands, type \`help\`` + ); + } + } // Continue if the bot has permission to send messages in this channel. - if (channel.type === "dm" || channel.permissionsFor(client.user!)!.has(Permissions.FLAGS.SEND_MESSAGES)) { - const text = content; + else if (channel.permissionsFor(client.user!)!.has(Permissions.FLAGS.SEND_MESSAGES)) { const prefix = getPrefix(guild); // First, test if the message is just a ping to the bot. if (new RegExp(`^<@!?${client.user!.id}>$`).test(text)) { - channel.send(`${author}, my prefix on this guild is \`${prefix}\`.`); + channel.send(`${author}, my prefix on this server is \`${prefix}\`.`); } // Then check if it's a normal command. else if (text.startsWith(prefix)) { const [header, ...args] = text.substring(prefix.length).split(/ +/); - const commands = await loadableCommands; if (commands.has(header)) { const command = commands.get(header)!; // Send the arguments to the command to resolve and execute. - // TMP[MAKE SURE TO REPLACE WITH command.execute WHEN FINISHED] - const result = await command.execute( + const result = await command.execute(args, menu, { + header, args, - { - author, - channel, - client, - guild, - member, - message, - args: [] - }, - { - header, - args, - permission: 0, - nsfw: false, - channelType: CHANNEL_TYPE.ANY - } - ); + ...defaultMetadata + }); // If something went wrong, let the user know (like if they don't have permission to use a command). if (result) { @@ -67,7 +93,9 @@ client.on("message", async (message) => { } } } - } else { + } + // Otherwise, let the sender know that the bot doesn't have permission to send messages. + else { author.send( `I don't have permission to send messages in ${channel}. ${ member!.hasPermission(Permissions.FLAGS.ADMINISTRATOR) From 6ed4c0988f287a62e616d6fa69194d8a53281ad7 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Sun, 4 Apr 2021 22:40:31 -0500 Subject: [PATCH 18/30] Implemented rough draft of info resolver method --- src/commands/system/help.ts | 87 ++++++++------- src/core/command.ts | 208 ++++++++++++++++++++++-------------- src/core/handler.ts | 12 +-- src/core/loader.ts | 2 +- 4 files changed, 184 insertions(+), 125 deletions(-) diff --git a/src/commands/system/help.ts b/src/commands/system/help.ts index 6277fde..c772eef 100644 --- a/src/commands/system/help.ts +++ b/src/commands/system/help.ts @@ -1,9 +1,9 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core/command"; import {toTitleCase} from "../../core/lib"; import {loadableCommands, categories} from "../../core/loader"; import {getPermissionName} from "../../core/permissions"; -export default new Command({ +export default new NamedCommand({ description: "Lists all commands. If a command is specified, their arguments are listed as well.", usage: "([command, [subcommand/type], ...])", aliases: ["h"], @@ -16,13 +16,7 @@ export default new Command({ for (const header of headers) { if (header !== "test") { - const command = commands.get(header); - - if (!command) - return console.warn( - `Command "${header}" of category "${category}" unexpectedly doesn't exist!` - ); - + const command = commands.get(header)!; output += `\n- \`${header}\`: ${command.description}`; } } @@ -32,44 +26,63 @@ export default new Command({ }, any: new Command({ async run($) { - // [category, commandName, command, subcommandInfo] = resolveCommandInfo(); + // Setup the root command + const commands = await loadableCommands; + let header = $.args.shift() as string; + let command = commands.get(header); + if (!command || header === "test") return $.channel.send(`No command found by the name \`${header}\`.`); + if (!(command instanceof NamedCommand)) + return $.channel.send(`Command is not a proper instance of NamedCommand.`); + if (command.name) header = command.name; + + // Search categories + let category = "Unknown"; + for (const [referenceCategory, headers] of categories) { + if (headers.includes(header)) { + category = toTitleCase(referenceCategory); + break; + } + } + + // Gather info + const result = await command.resolveInfo($.args); + + if (result.type === "error") return $.channel.send(result.message); let append = ""; + command = result.command; - if (usage === "") { + if (command.usage === "") { const list: string[] = []; - command.subcommands.forEach((subcmd, subtag) => { - // Don't capture duplicates generated from aliases. - if (subcmd.originalCommandName === subtag) { - const customUsage = subcmd.usage ? ` ${subcmd.usage}` : ""; - list.push(`- \`${header} ${subtag}${customUsage}\` - ${subcmd.description}`); - } - }); + for (const [tag, subcommand] of result.keyedSubcommandInfo) { + const customUsage = subcommand.usage ? ` ${subcommand.usage}` : ""; + list.push(`- \`${header} ${tag}${customUsage}\` - ${subcommand.description}`); + } - const addDynamicType = (cmd: Command | null, type: string) => { - if (cmd) { - const customUsage = cmd.usage ? ` ${cmd.usage}` : ""; - list.push(`- \`${header} <${type}>${customUsage}\` - ${cmd.description}`); - } - }; - - addDynamicType(command.user, "user"); - addDynamicType(command.number, "number"); - addDynamicType(command.any, "any"); + for (const [type, subcommand] of result.subcommandInfo) { + const customUsage = subcommand.usage ? ` ${subcommand.usage}` : ""; + list.push(`- \`${header} ${type}${customUsage}\` - ${subcommand.description}`); + } append = "Usages:" + (list.length > 0 ? `\n${list.join("\n")}` : " None."); - } else append = `Usage: \`${header} ${usage}\``; + } else { + append = `Usage: \`${header} ${command.usage}\``; + } - const formattedAliases: string[] = []; - for (const alias of command.aliases) formattedAliases.push(`\`${alias}\``); - // Short circuit an empty string, in this case, if there are no aliases. - const aliases = formattedAliases.join(", ") || "None"; + let aliases = "N/A"; - $.channel.send( - `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${selectedCategory}\`\nPermission Required: \`${getPermissionName( - permLevel - )}\` (${permLevel})\nDescription: ${command.description}\n${append}`, + if (command instanceof NamedCommand) { + const formattedAliases: string[] = []; + for (const alias of command.aliases) formattedAliases.push(`\`${alias}\``); + // Short circuit an empty string, in this case, if there are no aliases. + aliases = formattedAliases.join(", ") || "None"; + } + + return $.channel.send( + `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${category}\`\nPermission Required: \`${getPermissionName( + result.permission + )}\` (${result.permission})\nDescription: ${command.description}\n${append}`, {split: true} ); } diff --git a/src/core/command.ts b/src/core/command.ts index 20b7f90..79b1693 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,4 +1,4 @@ -import {parseVars, requireAllCasesHandledFor} from "./lib"; +import {parseVars} from "./lib"; import {Collection} from "discord.js"; import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember, GuildChannel} from "discord.js"; import {getPrefix} from "../core/structures"; @@ -82,6 +82,36 @@ interface ExecuteCommandMetadata { channelType: CHANNEL_TYPE; } +interface CommandInfo { + readonly type: "info"; + readonly command: Command; + readonly subcommandInfo: Collection; + readonly keyedSubcommandInfo: Collection; + readonly permission: number; + readonly nsfw: boolean; + readonly channelType: CHANNEL_TYPE; + readonly args: string[]; +} + +interface CommandInfoError { + readonly type: "error"; + readonly message: string; +} + +interface CommandInfoMetadata { + permission: number; + nsfw: boolean; + channelType: CHANNEL_TYPE; + args: string[]; + usage: string; +} + +export const defaultMetadata = { + permission: 0, + nsfw: false, + channelType: CHANNEL_TYPE.ANY +}; + export class Command { public readonly description: string; public readonly endpoint: boolean; @@ -90,7 +120,7 @@ export class Command { public readonly nsfw: boolean | null; // null (default) indicates to inherit public readonly channelType: CHANNEL_TYPE | null; // null (default) indicates to inherit protected run: (($: CommandMenu) => Promise) | string; - protected readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. + protected readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. protected user: Command | null; protected number: Command | null; protected any: Command | null; @@ -124,7 +154,7 @@ export class Command { // This shouldn't be a problem because I'm hoping that JS stores these as references that point to the same object. for (const name in options.subcommands) { const subcmd = options.subcommands[name]; - subcmd.originalCommandName = name; + subcmd.name = name; const aliases = subcmd.aliases; for (const alias of aliases) { @@ -153,7 +183,7 @@ export class Command { const param = args.shift(); // If there are no arguments left, execute the current command. Otherwise, continue on. - if (!param) { + if (param === undefined) { // See if there is anything that'll prevent the user from executing the command. // 1. Does this command specify a required channel type? If so, does the channel type match? @@ -218,13 +248,9 @@ export class Command { // If the current command is an endpoint but there are still some arguments left, don't continue. if (this.endpoint) return {content: "Too many arguments!"}; - // If the current command's permission level isn't -1 (inherit), then set the permission metadata equal to that. + // Update inherited properties if the current command specifies a property. if (this.permission !== -1) metadata.permission = this.permission; - - // If the current command has an NSFW setting specified, set it. if (this.nsfw !== null) metadata.nsfw = this.nsfw; - - // If the current command doesn't inherit its channel type, set it. if (this.channelType !== null) metadata.channelType = this.channelType; // Resolve the value of the current command's argument (adding it to the resolved args), @@ -257,11 +283,97 @@ export class Command { // Note: Do NOT add a return statement here. In case one of the other sections is missing // a return statement, there'll be a compile error to catch that. } + + // What this does is resolve the resulting subcommand as well as the inherited properties and the available subcommands. + public async resolveInfo(args: string[]): Promise { + return this.resolveInfoInternal(args, {...defaultMetadata, args: [], usage: ""}); + } + + private async resolveInfoInternal( + args: string[], + metadata: CommandInfoMetadata + ): Promise { + const param = args.shift(); + + // If there are no arguments left, return the data or an error message. + if (param === undefined) { + const keyedSubcommandInfo = new Collection(); + const subcommandInfo = new Collection(); + + // Get all the subcommands of the current command but without aliases. + for (const [tag, command] of this.subcommands.entries()) { + // Don't capture duplicates generated from aliases. + if (tag === command.name) { + keyedSubcommandInfo.set(tag, command); + } + } + + // Then get all the generic subcommands. + if (this.user) subcommandInfo.set("", this.user); + if (this.number) subcommandInfo.set("", this.number); + if (this.any) subcommandInfo.set("", this.any); + + return { + type: "info", + command: this, + keyedSubcommandInfo, + subcommandInfo, + ...metadata + }; + } + + // Update inherited properties if the current command specifies a property. + if (this.permission !== -1) metadata.permission = this.permission; + if (this.nsfw !== null) metadata.nsfw = this.nsfw; + if (this.channelType !== null) metadata.channelType = this.channelType; + if (this.usage !== "") metadata.usage = this.usage; + + // Then test if anything fits any hardcoded values, otherwise check if it's a valid keyed subcommand. + if (param === "") { + if (this.user) { + metadata.args.push(""); + return this.user.resolveInfoInternal(args, metadata); + } else { + return { + type: "error", + message: `No subcommand found by the argument list: \`${metadata.args.join(" ")}\`` + }; + } + } else if (param === "") { + if (this.number) { + metadata.args.push(""); + return this.number.resolveInfoInternal(args, metadata); + } else { + return { + type: "error", + message: `No subcommand found by the argument list: \`${metadata.args.join(" ")}\`` + }; + } + } else if (param === "") { + if (this.any) { + metadata.args.push(""); + return this.any.resolveInfoInternal(args, metadata); + } else { + return { + type: "error", + message: `No subcommand found by the argument list: \`${metadata.args.join(" ")}\`` + }; + } + } else if (this.subcommands?.has(param)) { + metadata.args.push(param); + return this.subcommands.get(param)!.resolveInfoInternal(args, metadata); + } else { + return { + type: "error", + message: `No subcommand found by the argument list: \`${metadata.args.join(" ")}\`` + }; + } + } } export class NamedCommand extends Command { public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. - public originalCommandName: string | null; // If the command is an alias, what's the original name? + private originalCommandName: string | null; // If the command is an alias, what's the original name? constructor(options?: NamedCommandOptions) { super(options); @@ -269,74 +381,14 @@ export class NamedCommand extends Command { this.originalCommandName = null; } - // Returns: [category, command name, command, available subcommands: [type, subcommand]] - public async resolveInfo(args: string[]): [string, string, Command, Collection] | null { - // For debug info, use this.originalCommandName? (if it exists?) - const commands = await loadableCommands; - let header = args.shift(); - let command = commands.get(header); + public get name(): string { + if (this.originalCommandName === null) throw new Error("originalCommandName must be set before accessing it!"); + else return this.originalCommandName; + } - if (!command || header === "test") { - $.channel.send(`No command found by the name \`${header}\`!`); - return; - } - - if (command.originalCommandName) header = command.originalCommandName; - else console.warn(`originalCommandName isn't defined for ${header}?!`); - - let permLevel = command.permission ?? 0; - let usage = command.usage; - let invalid = false; - - let selectedCategory = "Unknown"; - - for (const [category, headers] of categories) { - if (headers.includes(header)) { - if (selectedCategory !== "Unknown") - console.warn( - `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.` - ); - else selectedCategory = toTitleCase(category); - } - } - - for (const param of args) { - const type = command.resolve(param); - command = command.get(param); - permLevel = command.permission ?? permLevel; - - if (permLevel === -1) permLevel = command.permission; - - // Switch over to doing `$help info ` - switch (type) { - case TYPES.SUBCOMMAND: - header += ` ${command.originalCommandName}`; - break; - case TYPES.USER: - header += " "; - break; - case TYPES.NUMBER: - header += " "; - break; - case TYPES.ANY: - header += " "; - break; - case TYPES.NONE: - header += ` ${param}`; - break; - default: - requireAllCasesHandledFor(type); - } - - if (type === TYPES.NONE) { - invalid = true; - break; - } - } - - if (invalid) { - $.channel.send(`No command found by the name \`${header}\`!`); - return; - } + public set name(value: string) { + if (this.originalCommandName !== null) + throw new Error(`originalCommandName cannot be set twice! Attempted to set the value to "${value}".`); + else this.originalCommandName = value; } } diff --git a/src/core/handler.ts b/src/core/handler.ts index cff1c41..9a7582d 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -3,7 +3,7 @@ import {loadableCommands} from "./loader"; import {Permissions, Message} from "discord.js"; import {getPrefix} from "./structures"; import {Config} from "./structures"; -import {CHANNEL_TYPE} from "./command"; +import {defaultMetadata} from "./command"; // For custom message events that want to cancel the command handler on certain conditions. const interceptRules: ((message: Message) => boolean)[] = [(message) => message.author.bot]; @@ -12,12 +12,6 @@ export function addInterceptRule(handler: (message: Message) => boolean) { interceptRules.push(handler); } -const defaultMetadata = { - permission: 0, - nsfw: false, - channelType: CHANNEL_TYPE.ANY -}; - // Note: client.user is only undefined before the bot logs in, so by this point, client.user cannot be undefined. // Note: guild.available will never need to be checked because the message starts in either a DM channel or an already-available guild. client.on("message", async (message) => { @@ -51,7 +45,7 @@ client.on("message", async (message) => { // Send the arguments to the command to resolve and execute. const result = await command.execute(args, menu, { header, - args, + args: [...args], ...defaultMetadata }); @@ -83,7 +77,7 @@ client.on("message", async (message) => { // Send the arguments to the command to resolve and execute. const result = await command.execute(args, menu, { header, - args, + args: [...args], ...defaultMetadata }); diff --git a/src/core/loader.ts b/src/core/loader.ts index 8333c1b..6c6fa7f 100644 --- a/src/core/loader.ts +++ b/src/core/loader.ts @@ -31,7 +31,7 @@ export const loadableCommands = (async () => { const command = (await import(`../commands/${commandID}`)).default as unknown; if (command instanceof NamedCommand) { - command.originalCommandName = commandName; + command.name = commandName; if (commands.has(commandName)) { console.warn( From 5c3896c2dbcd15ed8deb3f47c42d1a51f27f17ac Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Sun, 4 Apr 2021 23:35:12 -0500 Subject: [PATCH 19/30] Separated command handler from utility modules and fixed lingering errors in commands --- src/commands/fun/8ball.ts | 12 +- src/commands/fun/cookie.ts | 23 ++- src/commands/fun/eco.ts | 5 +- src/commands/fun/modules/eco-core.ts | 15 +- src/commands/fun/modules/eco-extras.ts | 6 +- src/commands/fun/modules/eco-shop-items.ts | 2 +- src/commands/fun/modules/eco-shop.ts | 11 +- src/commands/fun/modules/eco-utils.ts | 4 +- src/commands/fun/neko.ts | 18 +-- src/commands/fun/ok.ts | 10 +- src/commands/fun/owoify.ts | 12 +- src/commands/fun/poll.ts | 16 +-- src/commands/system/admin.ts | 154 ++++++++++----------- src/commands/system/help.ts | 24 ++-- src/commands/template.ts | 57 +------- src/commands/utility/desc.ts | 20 +-- src/commands/utility/emote.ts | 6 +- src/commands/utility/info.ts | 99 +++++++------ src/commands/utility/lsemotes.ts | 44 +++--- src/commands/utility/react.ts | 59 ++++---- src/commands/utility/say.ts | 8 +- src/commands/utility/scanemotes.ts | 32 ++--- src/commands/utility/shorten.ts | 10 +- src/commands/utility/time.ts | 15 +- src/core/command.ts | 28 ++-- src/core/handler.ts | 13 +- src/core/index.ts | 15 ++ src/core/permissions.ts | 2 +- src/index.ts | 3 +- src/{core => }/lib.test.ts | 0 src/{core => }/lib.ts | 2 +- src/modules/emoteRegistry.ts | 4 +- src/modules/lavalink.ts | 2 +- src/modules/messageEmbed.ts | 2 +- src/modules/ready.ts | 12 ++ src/modules/setup.ts | 4 +- src/{core => modules}/storage.ts | 0 src/{core => }/structures.ts | 2 +- 38 files changed, 344 insertions(+), 407 deletions(-) create mode 100644 src/core/index.ts rename src/{core => }/lib.test.ts (100%) rename src/{core => }/lib.ts (99%) create mode 100644 src/modules/ready.ts rename src/{core => modules}/storage.ts (100%) rename src/{core => }/structures.ts (99%) diff --git a/src/commands/fun/8ball.ts b/src/commands/fun/8ball.ts index 84488a7..2c45770 100644 --- a/src/commands/fun/8ball.ts +++ b/src/commands/fun/8ball.ts @@ -1,5 +1,5 @@ -import Command from "../../core/command"; -import {random} from "../../core/lib"; +import {Command, NamedCommand} from "../../core"; +import {random} from "../../lib"; const responses = [ "Most likely,", @@ -24,16 +24,16 @@ const responses = [ "Very doubtful," ]; -export default new Command({ +export default new NamedCommand({ description: "Answers your question in an 8-ball manner.", endpoint: false, usage: "", run: "Please provide a question.", any: new Command({ description: "Question to ask the 8-ball.", - async run($) { - const sender = $.message.author; - $.channel.send(`${random(responses)} <@${sender.id}>`); + async run({message, channel, guild, author, member, client, args}) { + const sender = message.author; + channel.send(`${random(responses)} <@${sender.id}>`); } }) }); diff --git a/src/commands/fun/cookie.ts b/src/commands/fun/cookie.ts index 06c7086..50b1b3d 100644 --- a/src/commands/fun/cookie.ts +++ b/src/commands/fun/cookie.ts @@ -1,7 +1,6 @@ import {User} from "discord.js"; -import Command from "../../core/command"; -import {random} from "../../core/lib"; -import {parseVars} from "../../core/lib"; +import {Command, NamedCommand} from "../../core"; +import {random, parseVars} from "../../lib"; const cookies = [ `has given %target% a chocolate chip cookie!`, @@ -26,29 +25,29 @@ const cookies = [ `bakes %target% fresh cookies, it smells amazing.` ]; -export default new Command({ +export default new NamedCommand({ description: "Gives specified user a cookie.", usage: "['all'/@user]", run: ":cookie: Here's a cookie!", subcommands: { - all: new Command({ - async run($) { - $.channel.send(`${$.author} gave everybody a cookie!`); + all: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + channel.send(`${author} gave everybody a cookie!`); } }) }, user: new Command({ description: "User to give cookie to.", - async run($) { - const sender = $.author; - const mention: User = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + const sender = author; + const mention: User = args[0]; if (mention.id == sender.id) { - $.channel.send("You can't give yourself cookies!"); + channel.send("You can't give yourself cookies!"); return; } - $.channel.send( + channel.send( `:cookie: <@${sender.id}> ${parseVars(random(cookies), { target: mention.toString() })}` diff --git a/src/commands/fun/eco.ts b/src/commands/fun/eco.ts index 380cc33..37c288e 100644 --- a/src/commands/fun/eco.ts +++ b/src/commands/fun/eco.ts @@ -1,11 +1,10 @@ -import Command from "../../core/command"; +import {Command, NamedCommand, callMemberByUsername} from "../../core"; import {isAuthorized, getMoneyEmbed} from "./modules/eco-utils"; import {DailyCommand, PayCommand, GuildCommand, LeaderboardCommand} from "./modules/eco-core"; import {BuyCommand, ShopCommand} from "./modules/eco-shop"; import {MondayCommand} from "./modules/eco-extras"; -import {callMemberByUsername} from "../../core/libd"; -export default new Command({ +export default new NamedCommand({ description: "Economy command for Monika.", async run({guild, channel, author}) { if (isAuthorized(guild, channel)) channel.send(getMoneyEmbed(author)); diff --git a/src/commands/fun/modules/eco-core.ts b/src/commands/fun/modules/eco-core.ts index ea174b1..b1966f2 100644 --- a/src/commands/fun/modules/eco-core.ts +++ b/src/commands/fun/modules/eco-core.ts @@ -1,10 +1,9 @@ -import Command from "../../../core/command"; -import {prompt} from "../../../core/libd"; -import {pluralise} from "../../../core/lib"; -import {Storage} from "../../../core/structures"; +import {Command, NamedCommand, prompt} from "../../../core"; +import {pluralise} from "../../../lib"; +import {Storage} from "../../../structures"; import {isAuthorized, getMoneyEmbed, getSendEmbed, ECO_EMBED_COLOR} from "./eco-utils"; -export const DailyCommand = new Command({ +export const DailyCommand = new NamedCommand({ description: "Pick up your daily Mons. The cooldown is per user and every 22 hours to allow for some leeway.", aliases: ["get"], async run({author, channel, guild}) { @@ -38,7 +37,7 @@ export const DailyCommand = new Command({ } }); -export const GuildCommand = new Command({ +export const GuildCommand = new NamedCommand({ description: "Get info on the guild's economy as a whole.", async run({guild, channel}) { if (isAuthorized(guild, channel)) { @@ -75,7 +74,7 @@ export const GuildCommand = new Command({ } }); -export const LeaderboardCommand = new Command({ +export const LeaderboardCommand = new NamedCommand({ description: "See the richest players.", aliases: ["top"], async run({guild, channel, client}) { @@ -109,7 +108,7 @@ export const LeaderboardCommand = new Command({ } }); -export const PayCommand = new Command({ +export const PayCommand = new NamedCommand({ description: "Send money to someone.", usage: " ", run: "Who are you sending this money to?", diff --git a/src/commands/fun/modules/eco-extras.ts b/src/commands/fun/modules/eco-extras.ts index 274bb16..f8de393 100644 --- a/src/commands/fun/modules/eco-extras.ts +++ b/src/commands/fun/modules/eco-extras.ts @@ -1,10 +1,10 @@ -import Command from "../../../core/command"; -import {Storage} from "../../../core/structures"; +import {Command, NamedCommand} from "../../../core/command"; +import {Storage} from "../../../structures"; import {isAuthorized, getMoneyEmbed} from "./eco-utils"; const WEEKDAY = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; -export const MondayCommand = new Command({ +export const MondayCommand = new NamedCommand({ description: "Use this on a UTC Monday to get an extra Mon. Does not affect your 22 hour timer for `eco daily`.", async run({guild, channel, author}) { if (isAuthorized(guild, channel)) { diff --git a/src/commands/fun/modules/eco-shop-items.ts b/src/commands/fun/modules/eco-shop-items.ts index 0a90549..d50a779 100644 --- a/src/commands/fun/modules/eco-shop-items.ts +++ b/src/commands/fun/modules/eco-shop-items.ts @@ -1,5 +1,5 @@ import {Message} from "discord.js"; -import {random} from "../../../core/lib"; +import {random} from "../../../lib"; export interface ShopItem { cost: number; diff --git a/src/commands/fun/modules/eco-shop.ts b/src/commands/fun/modules/eco-shop.ts index f5c167b..47d0178 100644 --- a/src/commands/fun/modules/eco-shop.ts +++ b/src/commands/fun/modules/eco-shop.ts @@ -1,12 +1,11 @@ -import Command from "../../../core/command"; -import {pluralise, split} from "../../../core/lib"; -import {paginate} from "../../../core/libd"; -import {Storage, getPrefix} from "../../../core/structures"; +import {Command, NamedCommand, paginate} from "../../../core"; +import {pluralise, split} from "../../../lib"; +import {Storage, getPrefix} from "../../../structures"; import {isAuthorized, ECO_EMBED_COLOR} from "./eco-utils"; import {ShopItems, ShopItem} from "./eco-shop-items"; import {EmbedField} from "discord.js"; -export const ShopCommand = new Command({ +export const ShopCommand = new NamedCommand({ description: "Displays the list of items you can buy in the shop.", async run({guild, channel, author}) { if (isAuthorized(guild, channel)) { @@ -45,7 +44,7 @@ export const ShopCommand = new Command({ } }); -export const BuyCommand = new Command({ +export const BuyCommand = new NamedCommand({ description: "Buys an item from the shop.", usage: "", async run({guild, channel, args, message, author}) { diff --git a/src/commands/fun/modules/eco-utils.ts b/src/commands/fun/modules/eco-utils.ts index e4ea32f..327abdb 100644 --- a/src/commands/fun/modules/eco-utils.ts +++ b/src/commands/fun/modules/eco-utils.ts @@ -1,5 +1,5 @@ -import {pluralise} from "../../../core/lib"; -import {Storage} from "../../../core/structures"; +import {pluralise} from "../../../lib"; +import {Storage} from "../../../structures"; import {User, Guild, TextChannel, DMChannel, NewsChannel} from "discord.js"; export const ECO_EMBED_COLOR = 0xf1c40f; diff --git a/src/commands/fun/neko.ts b/src/commands/fun/neko.ts index 113fb15..46d0bfc 100644 --- a/src/commands/fun/neko.ts +++ b/src/commands/fun/neko.ts @@ -1,6 +1,6 @@ import {URL} from "url"; -import Command from "../../core/command"; -import {getContent} from "../../core/lib"; +import {Command, NamedCommand} from "../../core"; +import {getContent} from "../../lib"; const endpoints: {sfw: {[key: string]: string}} = { sfw: { @@ -34,26 +34,26 @@ const endpoints: {sfw: {[key: string]: string}} = { } }; -export default new Command({ +export default new NamedCommand({ description: "Provides you with a random image with the selected argument.", - async run($) { - $.channel.send( + async run({message, channel, guild, author, member, client, args}) { + channel.send( `Please provide an image type. Available arguments:\n\`[${Object.keys(endpoints.sfw).join(", ")}]\`.` ); }, any: new Command({ description: "Image type to send.", - async run($) { - const arg = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + const arg = args[0]; if (!(arg in endpoints.sfw)) { - $.channel.send("Couldn't find that endpoint!"); + channel.send("Couldn't find that endpoint!"); return; } let url = new URL(`https://nekos.life/api/v2${endpoints.sfw[arg]}`); const content = await getContent(url.toString()); - $.channel.send(content.url); + channel.send(content.url); } }) }); diff --git a/src/commands/fun/ok.ts b/src/commands/fun/ok.ts index a4ea44a..b2998ab 100644 --- a/src/commands/fun/ok.ts +++ b/src/commands/fun/ok.ts @@ -1,5 +1,5 @@ -import Command from "../../core/command"; -import {random} from "../../core/lib"; +import {Command, NamedCommand} from "../../core"; +import {random} from "../../lib"; const responses = [ "boomer", @@ -59,9 +59,9 @@ const responses = [ "large man" ]; -export default new Command({ +export default new NamedCommand({ description: "Sends random ok message.", - async run($) { - $.channel.send(`ok ${random(responses)}`); + async run({message, channel, guild, author, member, client, args}) { + channel.send(`ok ${random(responses)}`); } }); diff --git a/src/commands/fun/owoify.ts b/src/commands/fun/owoify.ts index af041df..2b4cdd7 100644 --- a/src/commands/fun/owoify.ts +++ b/src/commands/fun/owoify.ts @@ -1,12 +1,12 @@ -import Command from "../../core/command"; -import {getContent} from "../../core/lib"; +import {Command, NamedCommand} from "../../core"; +import {getContent} from "../../lib"; import {URL} from "url"; -export default new Command({ +export default new NamedCommand({ description: "OwO-ifies the input.", - async run($) { - let url = new URL(`https://nekos.life/api/v2/owoify?text=${$.args.join(" ")}`); + async run({message, channel, guild, author, member, client, args}) { + let url = new URL(`https://nekos.life/api/v2/owoify?text=${args.join(" ")}`); const content = (await getContent(url.toString())) as any; // Apparently, the object in question is {owo: string}. - $.channel.send(content.owo); + channel.send(content.owo); } }); diff --git a/src/commands/fun/poll.ts b/src/commands/fun/poll.ts index 8b6d6e1..38efb68 100644 --- a/src/commands/fun/poll.ts +++ b/src/commands/fun/poll.ts @@ -1,25 +1,25 @@ import {MessageEmbed} from "discord.js"; -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Create a poll.", usage: "", run: "Please provide a question.", any: new Command({ description: "Question for the poll.", - async run($) { + async run({message, channel, guild, author, member, client, args}) { const embed = new MessageEmbed() .setAuthor( - `Poll created by ${$.message.author.username}`, - $.message.guild?.iconURL({dynamic: true}) ?? undefined + `Poll created by ${message.author.username}`, + message.guild?.iconURL({dynamic: true}) ?? undefined ) .setColor(0xffffff) .setFooter("React to vote.") - .setDescription($.args.join(" ")); - const msg = await $.channel.send(embed); + .setDescription(args.join(" ")); + const msg = await channel.send(embed); await msg.react("✅"); await msg.react("⛔"); - $.message.delete({ + message.delete({ timeout: 1000 }); } diff --git a/src/commands/system/admin.ts b/src/commands/system/admin.ts index 8dae940..b2da0bb 100644 --- a/src/commands/system/admin.ts +++ b/src/commands/system/admin.ts @@ -1,8 +1,6 @@ -import Command, {handler} from "../../core/command"; -import {clean} from "../../core/lib"; -import {botHasPermission} from "../../core/libd"; -import {Config, Storage} from "../../core/structures"; -import {getPermissionLevel, getPermissionName} from "../../core/permissions"; +import {Command, NamedCommand, botHasPermission, getPermissionLevel, getPermissionName} from "../../core"; +import {clean} from "../../lib"; +import {Config, Storage} from "../../structures"; import {Permissions} from "discord.js"; import {logs} from "../../modules/globals"; @@ -20,59 +18,55 @@ function getLogBuffer(type: string) { const activities = ["playing", "listening", "streaming", "watching"]; const statuses = ["online", "idle", "dnd", "invisible"]; -export default new Command({ +export default new NamedCommand({ description: "An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.", - async run($) { - if (!$.member) { - $.channel.send("Couldn't find a member object for you! Did you make sure you used this in a server?"); - return; - } - const permLevel = getPermissionLevel($.member); - $.channel.send( - `${$.author.toString()}, your permission level is \`${getPermissionName(permLevel)}\` (${permLevel}).` - ); + async run({message, channel, guild, author, member, client, args}) { + if (!member) + return channel.send("Couldn't find a member object for you! Did you make sure you used this in a server?"); + const permLevel = getPermissionLevel(author, member); + return channel.send(`${author}, your permission level is \`${getPermissionName(permLevel)}\` (${permLevel}).`); }, subcommands: { - set: new Command({ + set: new NamedCommand({ description: "Set different per-guild settings for the bot.", run: "You have to specify the option you want to set.", permission: PERMISSIONS.ADMIN, subcommands: { - prefix: new Command({ + prefix: new NamedCommand({ description: "Set a custom prefix for your guild. Removes your custom prefix if none is provided.", usage: "()", - async run($) { - Storage.getGuild($.guild?.id || "N/A").prefix = null; + async run({message, channel, guild, author, member, client, args}) { + Storage.getGuild(guild?.id || "N/A").prefix = null; Storage.save(); - $.channel.send( + channel.send( `The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.` ); }, any: new Command({ - async run($) { - Storage.getGuild($.guild?.id || "N/A").prefix = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + Storage.getGuild(guild?.id || "N/A").prefix = args[0]; Storage.save(); - $.channel.send(`The custom prefix for this guild is now \`${$.args[0]}\`.`); + channel.send(`The custom prefix for this guild is now \`${args[0]}\`.`); } }) }) } }), - diag: new Command({ + diag: new NamedCommand({ description: 'Requests a debug log with the "info" verbosity level.', permission: PERMISSIONS.BOT_SUPPORT, - async run($) { - $.channel.send(getLogBuffer("info")); + async run({message, channel, guild, author, member, client, args}) { + channel.send(getLogBuffer("info")); }, any: new Command({ description: `Select a verbosity to listen to. Available levels: \`[${Object.keys(logs).join(", ")}]\``, - async run($) { - const type = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + const type = args[0]; - if (type in logs) $.channel.send(getLogBuffer(type)); + if (type in logs) channel.send(getLogBuffer(type)); else - $.channel.send( + channel.send( `Couldn't find a verbosity level named \`${type}\`! The available types are \`[${Object.keys( logs ).join(", ")}]\`.` @@ -80,75 +74,72 @@ export default new Command({ } }) }), - status: new Command({ + status: new NamedCommand({ description: "Changes the bot's status.", permission: PERMISSIONS.BOT_SUPPORT, - async run($) { - $.channel.send("Setting status to `online`..."); + async run({message, channel, guild, author, member, client, args}) { + channel.send("Setting status to `online`..."); }, any: new Command({ description: `Select a status to set to. Available statuses: \`[${statuses.join(", ")}]\`.`, - async run($) { - if (!statuses.includes($.args[0])) { - $.channel.send("That status doesn't exist!"); - return; + async run({message, channel, guild, author, member, client, args}) { + if (!statuses.includes(args[0])) { + return channel.send("That status doesn't exist!"); } else { - $.client.user?.setStatus($.args[0]); - $.channel.send(`Setting status to \`${$.args[0]}\`...`); + client.user?.setStatus(args[0]); + return channel.send(`Setting status to \`${args[0]}\`...`); } } }) }), - purge: new Command({ + purge: new NamedCommand({ description: "Purges the bot's own messages.", permission: PERMISSIONS.BOT_SUPPORT, - async run($) { + async run({message, channel, guild, author, member, client, args}) { // It's probably better to go through the bot's own messages instead of calling bulkDelete which requires MANAGE_MESSAGES. - if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES) && $.channel.type !== "dm") { - $.message.delete(); - const msgs = await $.channel.messages.fetch({ + if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES) && channel.type !== "dm") { + message.delete(); + const msgs = await channel.messages.fetch({ limit: 100 }); - const travMessages = msgs.filter((m) => m.author.id === $.client.user?.id); + const travMessages = msgs.filter((m) => m.author.id === client.user?.id); - await $.channel.send(`Found ${travMessages.size} messages to delete.`).then((m) => + await channel.send(`Found ${travMessages.size} messages to delete.`).then((m) => m.delete({ timeout: 5000 }) ); - await $.channel.bulkDelete(travMessages); + await channel.bulkDelete(travMessages); } else { - $.channel.send( + channel.send( "This command must be executed in a guild where I have the `MANAGE_MESSAGES` permission." ); } } }), - clear: new Command({ + clear: new NamedCommand({ description: "Clears a given amount of messages.", usage: "", run: "A number was not provided.", number: new Command({ description: "Amount of messages to delete.", - async run($) { - if ($.channel.type === "dm") { - await $.channel.send("Can't clear messages in the DMs!"); - return; - } - $.message.delete(); - const fetched = await $.channel.messages.fetch({ - limit: $.args[0] + async run({message, channel, guild, author, member, client, args}) { + if (channel.type === "dm") return channel.send("Can't clear messages in the DMs!"); + message.delete(); + const fetched = await channel.messages.fetch({ + limit: args[0] }); - await $.channel.bulkDelete(fetched); + await channel.bulkDelete(fetched); + return; } }) }), - eval: new Command({ + eval: new NamedCommand({ description: "Evaluate code.", usage: "", permission: PERMISSIONS.BOT_OWNER, // You have to bring everything into scope to use them. AFAIK, there isn't a more maintainable way to do this, but at least TS will let you know if anything gets removed. - async run({args, author, channel, client, guild, member, message}) { + async run({message, channel, guild, author, member, client, args}) { try { const code = args.join(" "); let evaled = eval(code); @@ -160,49 +151,46 @@ export default new Command({ } } }), - nick: new Command({ + nick: new NamedCommand({ description: "Change the bot's nickname.", permission: PERMISSIONS.BOT_SUPPORT, - async run($) { - const nickName = $.args.join(" "); - await $.guild?.me?.setNickname(nickName); - if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES)) - $.message.delete({timeout: 5000}).catch(handler.bind($)); - $.channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000})); + async run({message, channel, guild, author, member, client, args}) { + const nickName = args.join(" "); + await guild?.me?.setNickname(nickName); + if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES)) message.delete({timeout: 5000}); + channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000})); } }), - guilds: new Command({ + guilds: new NamedCommand({ description: "Shows a list of all guilds the bot is a member of.", permission: PERMISSIONS.BOT_SUPPORT, - async run($) { - const guildList = $.client.guilds.cache.array().map((e) => e.name); - $.channel.send(guildList, {split: true}); + async run({message, channel, guild, author, member, client, args}) { + const guildList = client.guilds.cache.array().map((e) => e.name); + channel.send(guildList, {split: true}); } }), - activity: new Command({ + activity: new NamedCommand({ description: "Set the activity of the bot.", permission: PERMISSIONS.BOT_SUPPORT, usage: " ", - async run($) { - $.client.user?.setActivity(".help", { + async run({message, channel, guild, author, member, client, args}) { + client.user?.setActivity(".help", { type: "LISTENING" }); - $.channel.send("Activity set to default."); + channel.send("Activity set to default."); }, any: new Command({ description: `Select an activity type to set. Available levels: \`[${activities.join(", ")}]\``, - async run($) { - const type = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + const type = args[0]; if (activities.includes(type)) { - $.client.user?.setActivity($.args.slice(1).join(" "), { - type: $.args[0].toUpperCase() + client.user?.setActivity(args.slice(1).join(" "), { + type: args[0].toUpperCase() }); - $.channel.send( - `Set activity to \`${$.args[0].toUpperCase()}\` \`${$.args.slice(1).join(" ")}\`.` - ); + channel.send(`Set activity to \`${args[0].toUpperCase()}\` \`${args.slice(1).join(" ")}\`.`); } else - $.channel.send( + channel.send( `Couldn't find an activity type named \`${type}\`! The available types are \`[${activities.join( ", " )}]\`.` diff --git a/src/commands/system/help.ts b/src/commands/system/help.ts index c772eef..e3b160c 100644 --- a/src/commands/system/help.ts +++ b/src/commands/system/help.ts @@ -1,13 +1,11 @@ -import {Command, NamedCommand} from "../../core/command"; -import {toTitleCase} from "../../core/lib"; -import {loadableCommands, categories} from "../../core/loader"; -import {getPermissionName} from "../../core/permissions"; +import {Command, NamedCommand, loadableCommands, categories, getPermissionName} from "../../core"; +import {toTitleCase} from "../../lib"; export default new NamedCommand({ description: "Lists all commands. If a command is specified, their arguments are listed as well.", usage: "([command, [subcommand/type], ...])", aliases: ["h"], - async run($) { + async run({message, channel, guild, author, member, client, args}) { const commands = await loadableCommands; let output = `Legend: \`\`, \`[list/of/stuff]\`, \`(optional)\`, \`()\`, \`([optional/list/...])\``; @@ -22,17 +20,17 @@ export default new NamedCommand({ } } - $.channel.send(output, {split: true}); + channel.send(output, {split: true}); }, any: new Command({ - async run($) { + async run({message, channel, guild, author, member, client, args}) { // Setup the root command const commands = await loadableCommands; - let header = $.args.shift() as string; + let header = args.shift() as string; let command = commands.get(header); - if (!command || header === "test") return $.channel.send(`No command found by the name \`${header}\`.`); + if (!command || header === "test") return channel.send(`No command found by the name \`${header}\`.`); if (!(command instanceof NamedCommand)) - return $.channel.send(`Command is not a proper instance of NamedCommand.`); + return channel.send(`Command is not a proper instance of NamedCommand.`); if (command.name) header = command.name; // Search categories @@ -45,9 +43,9 @@ export default new NamedCommand({ } // Gather info - const result = await command.resolveInfo($.args); + const result = await command.resolveInfo(args); - if (result.type === "error") return $.channel.send(result.message); + if (result.type === "error") return channel.send(result.message); let append = ""; command = result.command; @@ -79,7 +77,7 @@ export default new NamedCommand({ aliases = formattedAliases.join(", ") || "None"; } - return $.channel.send( + return channel.send( `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${category}\`\nPermission Required: \`${getPermissionName( result.permission )}\` (${result.permission})\nDescription: ${command.description}\n${append}`, diff --git a/src/commands/template.ts b/src/commands/template.ts index a97e8e2..70d0e65 100644 --- a/src/commands/template.ts +++ b/src/commands/template.ts @@ -1,56 +1,7 @@ -import Command from "../core/command"; +import {Command, NamedCommand} from "../core"; -export default new Command({ - description: - 'This is a template/testing command providing common functionality. Remove what you don\'t need, and rename/delete this file to generate a fresh command file here. This command should be automatically excluded from the help command. The "usage" parameter (string) overrides the default usage for the help command. The "endpoint" parameter (boolean) prevents further arguments from being passed. Also, as long as you keep the run function async, it\'ll return a promise allowing the program to automatically catch any synchronous errors. However, you\'ll have to do manual error handling if you go the then and catch route.', - endpoint: false, - usage: "", - permission: -1, - aliases: [], - async run($) { +export default new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { // code - }, - subcommands: { - layer: new Command({ - description: - 'This is a named subcommand, meaning that the key name is what determines the keyword to use. With default settings for example, "$test layer".', - endpoint: false, - usage: "", - permission: -1, - aliases: [], - async run($) { - // code - } - }) - }, - user: new Command({ - description: - 'This is the subcommand for getting users by pinging them or copying their ID. With default settings for example, "$test 237359961842253835". The argument will be a user object and won\'t run if no user is found by that ID.', - endpoint: false, - usage: "", - permission: -1, - async run($) { - // code - } - }), - number: new Command({ - description: - 'This is a numeric subcommand, meaning that any type of number (excluding Infinity/NaN) will route to this command if present. With default settings for example, "$test -5.2". The argument with the number is already parsed so you can just use it without converting it.', - endpoint: false, - usage: "", - permission: -1, - async run($) { - // code - } - }), - any: new Command({ - description: - "This is a generic subcommand, meaning that if there isn't a more specific subcommand that's called, it falls to this. With default settings for example, \"$test reeee\".", - endpoint: false, - usage: "", - permission: -1, - async run($) { - // code - } - }) + } }); diff --git a/src/commands/utility/desc.ts b/src/commands/utility/desc.ts index 0ab3aaf..ed0b87c 100644 --- a/src/commands/utility/desc.ts +++ b/src/commands/utility/desc.ts @@ -1,29 +1,29 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Renames current voice channel.", usage: "", - async run($) { - const voiceChannel = $.message.member?.voice.channel; + async run({message, channel, guild, author, member, client, args}) { + const voiceChannel = message.member?.voice.channel; if (!voiceChannel) { - $.channel.send("You are not in a voice channel."); + channel.send("You are not in a voice channel."); return; } if (!voiceChannel.guild.me?.hasPermission("MANAGE_CHANNELS")) { - $.channel.send("I am lacking the required permissions to perform this action."); + channel.send("I am lacking the required permissions to perform this action."); return; } - if ($.args.length === 0) { - $.channel.send("Please provide a new voice channel name."); + if (args.length === 0) { + channel.send("Please provide a new voice channel name."); return; } const prevName = voiceChannel.name; - const newName = $.args.join(" "); + const newName = args.join(" "); await voiceChannel.setName(newName); - await $.channel.send(`Changed channel name from "${prevName}" to "${newName}".`); + await channel.send(`Changed channel name from "${prevName}" to "${newName}".`); } }); diff --git a/src/commands/utility/emote.ts b/src/commands/utility/emote.ts index addaec0..23a6563 100644 --- a/src/commands/utility/emote.ts +++ b/src/commands/utility/emote.ts @@ -1,9 +1,7 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import {queryClosestEmoteByName} from "./modules/emote-utils"; -import {botHasPermission} from "../../core/libd"; -import {Permissions} from "discord.js"; -export default new Command({ +export default new NamedCommand({ description: "Send the specified emote.", run: "Please provide a command name.", any: new Command({ diff --git a/src/commands/utility/info.ts b/src/commands/utility/info.ts index e2e6025..68b4d35 100644 --- a/src/commands/utility/info.ts +++ b/src/commands/utility/info.ts @@ -1,29 +1,26 @@ -import {MessageEmbed, version as djsversion} from "discord.js"; +import {MessageEmbed, version as djsversion, Guild} from "discord.js"; import ms from "ms"; import os from "os"; -import Command from "../../core/command"; -import {formatBytes, trimArray} from "../../core/lib"; -import {getMemberByUsername} from "../../core/libd"; +import {Command, NamedCommand, getMemberByUsername} from "../../core"; +import {formatBytes, trimArray} from "../../lib"; import {verificationLevels, filterLevels, regions} from "../../defs/info"; -import moment from "moment"; -import utc from "moment"; -import {Guild} from "discord.js"; +import moment, {utc} from "moment"; -export default new Command({ +export default new NamedCommand({ description: "Command to provide all sorts of info about the current server, a user, etc.", run: "Please provide an argument.\nFor help, run `%prefix%help info`.", subcommands: { - avatar: new Command({ + avatar: new NamedCommand({ description: "Shows your own, or another user's avatar.", usage: "()", - async run($) { - $.channel.send($.author.displayAvatarURL({dynamic: true, size: 2048})); + async run({message, channel, guild, author, member, client, args}) { + channel.send(author.displayAvatarURL({dynamic: true, size: 2048})); }, user: new Command({ description: "Shows your own, or another user's avatar.", - async run($) { - $.channel.send( - $.args[0].displayAvatarURL({ + async run({message, channel, guild, author, member, client, args}) { + channel.send( + args[0].displayAvatarURL({ dynamic: true, size: 2048 }) @@ -32,39 +29,39 @@ export default new Command({ }), any: new Command({ description: "Shows another user's avatar by searching their name", - async run($) { - if ($.guild) { - const name = $.args.join(" "); - const member = await getMemberByUsername($.guild, name); + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const name = args.join(" "); + const member = await getMemberByUsername(guild, name); if (member) { - $.channel.send( + channel.send( member.user.displayAvatarURL({ dynamic: true, size: 2048 }) ); } else { - $.channel.send(`No user found by the name \`${name}\`!`); + channel.send(`No user found by the name \`${name}\`!`); } } } }) }), - bot: new Command({ + bot: new NamedCommand({ description: "Displays info about the bot.", - async run($) { + async run({message, channel, guild, author, member, client, args}) { const core = os.cpus()[0]; const embed = new MessageEmbed() - .setColor($.guild?.me?.displayHexColor || "BLUE") + .setColor(guild?.me?.displayHexColor || "BLUE") .addField("General", [ - `**❯ Client:** ${$.client.user?.tag} (${$.client.user?.id})`, - `**❯ Servers:** ${$.client.guilds.cache.size.toLocaleString()}`, - `**❯ Users:** ${$.client.guilds.cache + `**❯ Client:** ${client.user?.tag} (${client.user?.id})`, + `**❯ Servers:** ${client.guilds.cache.size.toLocaleString()}`, + `**❯ Users:** ${client.guilds.cache .reduce((a: any, b: {memberCount: any}) => a + b.memberCount, 0) .toLocaleString()}`, - `**❯ Channels:** ${$.client.channels.cache.size.toLocaleString()}`, - `**❯ Creation Date:** ${utc($.client.user?.createdTimestamp).format("Do MMMM YYYY HH:mm:ss")}`, + `**❯ Channels:** ${client.channels.cache.size.toLocaleString()}`, + `**❯ Creation Date:** ${utc(client.user?.createdTimestamp).format("Do MMMM YYYY HH:mm:ss")}`, `**❯ Node.JS:** ${process.version}`, `**❯ Version:** v${process.env.npm_package_version}`, `**❯ Discord.JS:** ${djsversion}`, @@ -84,45 +81,45 @@ export default new Command({ `\u3000 • Used: ${formatBytes(process.memoryUsage().heapUsed)}` ]) .setTimestamp(); - const avatarURL = $.client.user?.displayAvatarURL({ + const avatarURL = client.user?.displayAvatarURL({ dynamic: true, size: 2048 }); if (avatarURL) embed.setThumbnail(avatarURL); - $.channel.send(embed); + channel.send(embed); } }), - guild: new Command({ + guild: new NamedCommand({ description: "Displays info about the current guild or another guild.", usage: "(/)", - async run($) { - if ($.guild) { - $.channel.send(await getGuildInfo($.guild, $.guild)); + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + channel.send(await getGuildInfo(guild, guild)); } else { - $.channel.send("Please execute this command in a guild."); + channel.send("Please execute this command in a guild."); } }, any: new Command({ description: "Display info about a guild by finding its name or ID.", - async run($) { + async run({message, channel, guild, author, member, client, args}) { // If a guild ID is provided (avoid the "number" subcommand because of inaccuracies), search for that guild - if ($.args.length === 1 && /^\d{17,19}$/.test($.args[0])) { - const id = $.args[0]; - const guild = $.client.guilds.cache.get(id); + if (args.length === 1 && /^\d{17,19}$/.test(args[0])) { + const id = args[0]; + const guild = client.guilds.cache.get(id); if (guild) { - $.channel.send(await getGuildInfo(guild, $.guild)); + channel.send(await getGuildInfo(guild, guild)); } else { - $.channel.send(`None of the servers I'm in matches the guild ID \`${id}\`!`); + channel.send(`None of the servers I'm in matches the guild ID \`${id}\`!`); } } else { - const query: string = $.args.join(" ").toLowerCase(); - const guild = $.client.guilds.cache.find((guild) => guild.name.toLowerCase().includes(query)); + const query: string = args.join(" ").toLowerCase(); + const guild = client.guilds.cache.find((guild) => guild.name.toLowerCase().includes(query)); if (guild) { - $.channel.send(await getGuildInfo(guild, $.guild)); + channel.send(await getGuildInfo(guild, guild)); } else { - $.channel.send(`None of the servers I'm in matches the query \`${query}\`!`); + channel.send(`None of the servers I'm in matches the query \`${query}\`!`); } } } @@ -131,12 +128,12 @@ export default new Command({ }, user: new Command({ description: "Displays info about mentioned user.", - async run($) { + async run({message, channel, guild, author, client, args}) { // Transforms the User object into a GuildMember object of the current guild. - const member = await $.guild?.members.fetch($.args[0]); + const member = await guild?.members.fetch(args[0]); if (!member) { - $.channel.send( + channel.send( "No member object was found by that user! Are you sure you used this command in a server?" ); return; @@ -166,16 +163,14 @@ export default new Command({ `**❯ Game:** ${member.user.presence.activities || "Not playing a game."}` ]) .addField("Member", [ - `**❯ Highest Role:** ${ - member.roles.highest.id === $.guild?.id ? "None" : member.roles.highest.name - }`, + `**❯ Highest Role:** ${member.roles.highest.id === guild?.id ? "None" : member.roles.highest.name}`, `**❯ Server Join Date:** ${moment(member.joinedAt).format("LL LTS")}`, `**❯ Hoist Role:** ${member.roles.hoist ? member.roles.hoist.name : "None"}`, `**❯ Roles:** [${roles.length}]: ${ roles.length == 0 ? "None" : roles.length <= 10 ? roles.join(", ") : trimArray(roles).join(", ") }` ]); - $.channel.send(embed); + channel.send(embed); } }) }); diff --git a/src/commands/utility/lsemotes.ts b/src/commands/utility/lsemotes.ts index c369ab0..a04f044 100644 --- a/src/commands/utility/lsemotes.ts +++ b/src/commands/utility/lsemotes.ts @@ -1,44 +1,38 @@ -import {GuildEmoji} from "discord.js"; -import {MessageEmbed} from "discord.js"; -import Command from "../../core/command"; -import {split} from "../../core/lib"; -import {paginate} from "../../core/libd"; +import {GuildEmoji, MessageEmbed, TextChannel, DMChannel, NewsChannel, User} from "discord.js"; +import {Command, NamedCommand, paginate} from "../../core"; +import {split} from "../../lib"; import vm from "vm"; -import {TextChannel} from "discord.js"; -import {DMChannel} from "discord.js"; -import {NewsChannel} from "discord.js"; -import {User} from "discord.js"; const REGEX_TIMEOUT_MS = 1000; -export default new Command({ +export default new NamedCommand({ description: "Lists all emotes the bot has in it's registry,", usage: " (-flags)", - async run($) { - displayEmoteList($.client.emojis.cache.array(), $.channel, $.author); + async run({message, channel, guild, author, member, client, args}) { + displayEmoteList(client.emojis.cache.array(), channel, author); }, any: new Command({ description: "Filters emotes by via a regular expression. Flags can be added by adding a dash at the end. For example, to do a case-insensitive search, do %prefix%lsemotes somepattern -i", - async run($) { + async run({message, channel, guild, author, member, client, args}) { // If a guild ID is provided, filter all emotes by that guild (but only if there aren't any arguments afterward) - if ($.args.length === 1 && /^\d{17,19}$/.test($.args[0])) { - const guildID: string = $.args[0]; + if (args.length === 1 && /^\d{17,19}$/.test(args[0])) { + const guildID: string = args[0]; displayEmoteList( - $.client.emojis.cache.filter((emote) => emote.guild.id === guildID).array(), - $.channel, - $.author + client.emojis.cache.filter((emote) => emote.guild.id === guildID).array(), + channel, + author ); } else { // Otherwise, search via a regex pattern let flags: string | undefined = undefined; - if (/^-[dgimsuy]{1,7}$/.test($.args[$.args.length - 1])) { - flags = $.args.pop().substring(1); + if (/^-[dgimsuy]{1,7}$/.test(args[args.length - 1])) { + flags = args.pop().substring(1); } - let emoteCollection = $.client.emojis.cache.array(); + let emoteCollection = client.emojis.cache.array(); // Creates a sandbox to stop a regular expression if it takes too much time to search. // To avoid passing in a giant data structure, I'll just pass in the structure {[id: string]: [name: string]}. //let emotes: {[id: string]: string} = {}; @@ -50,7 +44,7 @@ export default new Command({ // The result will be sandbox.emotes because it'll be modified in-place. const sandbox = { - regex: new RegExp($.args.join(" "), flags), + regex: new RegExp(args.join(" "), flags), emotes }; const context = vm.createContext(sandbox); @@ -64,10 +58,10 @@ export default new Command({ script.runInContext(context, {timeout: REGEX_TIMEOUT_MS}); emotes = sandbox.emotes; emoteCollection = emoteCollection.filter((emote) => emotes.has(emote.id)); // Only allow emotes that haven't been deleted. - displayEmoteList(emoteCollection, $.channel, $.author); + displayEmoteList(emoteCollection, channel, author); } catch (error) { if (error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") { - $.channel.send( + channel.send( `The regular expression you entered exceeded the time limit of ${REGEX_TIMEOUT_MS} milliseconds.` ); } else { @@ -75,7 +69,7 @@ export default new Command({ } } } else { - $.channel.send("Failed to initialize sandbox."); + channel.send("Failed to initialize sandbox."); } } } diff --git a/src/commands/utility/react.ts b/src/commands/utility/react.ts index 152e98d..43f3394 100644 --- a/src/commands/utility/react.ts +++ b/src/commands/utility/react.ts @@ -1,17 +1,17 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import {Message, Channel, TextChannel} from "discord.js"; import {queryClosestEmoteByName} from "./modules/emote-utils"; -export default new Command({ +export default new NamedCommand({ description: "Reacts to the a previous message in your place. You have to react with the same emote before the bot removes that reaction.", usage: 'react ()', - async run($) { + async run({message, channel, guild, author, member, client, args}) { let target: Message | undefined; let distance = 1; - if ($.args.length >= 2) { - const last = $.args[$.args.length - 1]; // Because this is optional, do not .pop() unless you're sure it's a message link indicator. + if (args.length >= 2) { + const last = args[args.length - 1]; // Because this is optional, do not .pop() unless you're sure it's a message link indicator. const URLPattern = /^(?:https:\/\/discord.com\/channels\/(\d{17,19})\/(\d{17,19})\/(\d{17,19}))$/; const copyIDPattern = /^(?:(\d{17,19})-(\d{17,19}))$/; @@ -21,66 +21,65 @@ export default new Command({ const guildID = match[1]; const channelID = match[2]; const messageID = match[3]; - let guild = $.guild; - let channel: Channel | undefined = $.channel; + let tmpChannel: Channel | undefined = channel; if (guild?.id !== guildID) { try { - guild = await $.client.guilds.fetch(guildID); + guild = await client.guilds.fetch(guildID); } catch { - return $.channel.send(`\`${guildID}\` is an invalid guild ID!`); + return channel.send(`\`${guildID}\` is an invalid guild ID!`); } } - if (channel.id !== channelID) channel = guild.channels.cache.get(channelID); - if (!channel) return $.channel.send(`\`${channelID}\` is an invalid channel ID!`); + if (tmpChannel.id !== channelID) tmpChannel = guild.channels.cache.get(channelID); + if (!tmpChannel) return channel.send(`\`${channelID}\` is an invalid channel ID!`); - if ($.message.id !== messageID) { + if (message.id !== messageID) { try { - target = await (channel as TextChannel).messages.fetch(messageID); + target = await (tmpChannel as TextChannel).messages.fetch(messageID); } catch { - return $.channel.send(`\`${messageID}\` is an invalid message ID!`); + return channel.send(`\`${messageID}\` is an invalid message ID!`); } } - $.args.pop(); + args.pop(); } // - ("Copy ID" Button) else if (copyIDPattern.test(last)) { const match = copyIDPattern.exec(last)!; const channelID = match[1]; const messageID = match[2]; - let channel: Channel | undefined = $.channel; + let tmpChannel: Channel | undefined = channel; - if (channel.id !== channelID) channel = $.guild?.channels.cache.get(channelID); - if (!channel) return $.channel.send(`\`${channelID}\` is an invalid channel ID!`); + if (tmpChannel.id !== channelID) tmpChannel = guild?.channels.cache.get(channelID); + if (!tmpChannel) return channel.send(`\`${channelID}\` is an invalid channel ID!`); - if ($.message.id !== messageID) { + if (message.id !== messageID) { try { - target = await (channel as TextChannel).messages.fetch(messageID); + target = await (tmpChannel as TextChannel).messages.fetch(messageID); } catch { - return $.channel.send(`\`${messageID}\` is an invalid message ID!`); + return channel.send(`\`${messageID}\` is an invalid message ID!`); } } - $.args.pop(); + args.pop(); } // else if (/^\d{17,19}$/.test(last)) { try { - target = await $.channel.messages.fetch(last); + target = await channel.messages.fetch(last); } catch { - return $.channel.send(`No valid message found by the ID \`${last}\`!`); + return channel.send(`No valid message found by the ID \`${last}\`!`); } - $.args.pop(); + args.pop(); } // The entire string has to be a number for this to match. Prevents leaCheeseAmerican1 from triggering this. else if (/^\d+$/.test(last)) { distance = parseInt(last); - if (distance >= 0 && distance <= 99) $.args.pop(); - else return $.channel.send("Your distance must be between 0 and 99!"); + if (distance >= 0 && distance <= 99) args.pop(); + else return channel.send("Your distance must be between 0 and 99!"); } } @@ -88,13 +87,13 @@ export default new Command({ // Messages are ordered from latest to earliest. // You also have to add 1 as well because fetchMessages includes your own message. target = ( - await $.message.channel.messages.fetch({ + await message.channel.messages.fetch({ limit: distance + 1 }) ).last(); } - for (const search of $.args) { + for (const search of args) { // Even though the bot will always grab *some* emote, the user can choose not to keep that emote there if it isn't what they want const emote = queryClosestEmoteByName(search); const reaction = await target!.react(emote); @@ -102,7 +101,7 @@ export default new Command({ // This part is called with a promise because you don't want to wait 5 seconds between each reaction. setTimeout(() => { // This reason for this null assertion is that by the time you use this command, the client is going to be loaded. - reaction.users.remove($.client.user!); + reaction.users.remove(client.user!); }, 5000); } diff --git a/src/commands/utility/say.ts b/src/commands/utility/say.ts index e58b90e..6c70dfb 100644 --- a/src/commands/utility/say.ts +++ b/src/commands/utility/say.ts @@ -1,13 +1,13 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Repeats your message.", usage: "", run: "Please provide a message for me to say!", any: new Command({ description: "Message to repeat.", - async run($) { - $.channel.send(`*${$.author} says:*\n${$.args.join(" ")}`); + async run({message, channel, guild, author, member, client, args}) { + channel.send(`*${author} says:*\n${args.join(" ")}`); } }) }); diff --git a/src/commands/utility/scanemotes.ts b/src/commands/utility/scanemotes.ts index b087793..41d5115 100644 --- a/src/commands/utility/scanemotes.ts +++ b/src/commands/utility/scanemotes.ts @@ -1,33 +1,33 @@ -import Command, {handler} from "../../core/command"; -import {pluralise} from "../../core/lib"; +import {Command, NamedCommand} from "../../core"; +import {pluralise} from "../../lib"; import moment from "moment"; import {Collection, TextChannel} from "discord.js"; const lastUsedTimestamps: {[id: string]: number} = {}; -export default new Command({ +export default new NamedCommand({ description: "Scans all text channels in the current guild and returns the number of times each emoji specific to the guild has been used. Has a cooldown of 24 hours per guild.", - async run($) { - if (!$.guild) { - $.channel.send(`You must use this command on a server!`); + async run({message, channel, guild, author, member, client, args}) { + if (!guild) { + channel.send(`You must use this command on a server!`); return; } // Test if the command is on cooldown. This isn't the strictest cooldown possible, because in the event that the bot crashes, the cooldown will be reset. But for all intends and purposes, it's a good enough cooldown. It's a per-server cooldown. const startTime = Date.now(); const cooldown = 86400000; // 24 hours - const lastUsedTimestamp = lastUsedTimestamps[$.guild.id] ?? 0; + const lastUsedTimestamp = lastUsedTimestamps[guild.id] ?? 0; const difference = startTime - lastUsedTimestamp; const howLong = moment(startTime).to(lastUsedTimestamp + cooldown); // If it's been less than an hour since the command was last used, prevent it from executing. if (difference < cooldown) { - $.channel.send( + channel.send( `This command requires a day to cooldown. You'll be able to activate this command ${howLong}.` ); return; - } else lastUsedTimestamps[$.guild.id] = startTime; + } else lastUsedTimestamps[guild.id] = startTime; const stats: { [id: string]: { @@ -39,20 +39,20 @@ export default new Command({ } = {}; let totalUserEmoteUsage = 0; // IMPORTANT: You MUST check if the bot actually has access to the channel in the first place. It will get the list of all channels, but that doesn't mean it has access to every channel. Without this, it'll require admin access and throw an annoying unhelpful DiscordAPIError: Missing Access otherwise. - const allTextChannelsInCurrentGuild = $.guild.channels.cache.filter( + const allTextChannelsInCurrentGuild = guild.channels.cache.filter( (channel) => channel.type === "text" && channel.viewable ) as Collection; let messagesSearched = 0; let channelsSearched = 0; let currentChannelName = ""; const totalChannels = allTextChannelsInCurrentGuild.size; - const statusMessage = await $.channel.send("Gathering emotes..."); + const statusMessage = await channel.send("Gathering emotes..."); let warnings = 0; - $.channel.startTyping(); + channel.startTyping(); // Initialize the emote stats object with every emote in the current guild. // The goal here is to cut the need to access guild.emojis.get() which'll make it faster and easier to work with. - for (let emote of $.guild.emojis.cache.values()) { + for (let emote of guild.emojis.cache.values()) { // If you don't include the "a" for animated emotes, it'll not only not show up, but also cause all other emotes in the same message to not show up. The emote name is self-correcting but it's better to keep the right value since it'll be used to calculate message lengths that fit. stats[emote.id] = { name: emote.name, @@ -70,7 +70,7 @@ export default new Command({ for (const channel of allTextChannelsInCurrentGuild.values()) { currentChannelName = channel.name; - let selected = channel.lastMessageID ?? $.message.id; + let selected = channel.lastMessageID ?? message.id; let continueLoop = true; while (continueLoop) { @@ -167,7 +167,7 @@ export default new Command({ )}.` ); console.log(`Finished operation in ${finishTime - startTime} ms.`); - $.channel.stopTyping(); + channel.stopTyping(); // Display stats on emote usage. // This can work outside the loop now that it's synchronous, and now it's clearer what code is meant to execute at the end. @@ -186,6 +186,6 @@ export default new Command({ ); } - $.channel.send(lines, {split: true}).catch(handler.bind($)); + await channel.send(lines, {split: true}); } }); diff --git a/src/commands/utility/shorten.ts b/src/commands/utility/shorten.ts index 79961f3..a14c460 100644 --- a/src/commands/utility/shorten.ts +++ b/src/commands/utility/shorten.ts @@ -1,18 +1,18 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import * as https from "https"; -export default new Command({ +export default new NamedCommand({ description: "Shortens a given URL.", run: "Please provide a URL.", any: new Command({ - async run($) { - https.get("https://is.gd/create.php?format=simple&url=" + encodeURIComponent($.args[0]), function (res) { + async run({message, channel, guild, author, member, client, args}) { + https.get("https://is.gd/create.php?format=simple&url=" + encodeURIComponent(args[0]), function (res) { var body = ""; res.on("data", function (chunk) { body += chunk; }); res.on("end", function () { - $.channel.send(`<${body}>`); + channel.send(`<${body}>`); }); }); } diff --git a/src/commands/utility/time.ts b/src/commands/utility/time.ts index d520271..c51552a 100644 --- a/src/commands/utility/time.ts +++ b/src/commands/utility/time.ts @@ -1,6 +1,5 @@ -import Command from "../../core/command"; -import {ask, askYesOrNo, askMultipleChoice, prompt, callMemberByUsername} from "../../core/libd"; -import {Storage} from "../../core/structures"; +import {Command, NamedCommand, ask, askYesOrNo, askMultipleChoice, prompt, callMemberByUsername} from "../../core"; +import {Storage} from "../../structures"; import {User} from "discord.js"; import moment from "moment"; @@ -167,7 +166,7 @@ function getTimeEmbed(user: User) { return embed; } -export default new Command({ +export default new NamedCommand({ description: "Show others what time it is for you.", aliases: ["tz"], async run({channel, author}) { @@ -175,7 +174,7 @@ export default new Command({ }, subcommands: { // Welcome to callback hell. We hope you enjoy your stay here! - setup: new Command({ + setup: new NamedCommand({ description: "Registers your timezone information for the bot.", async run({author, channel}) { const profile = Storage.getUser(author.id); @@ -327,7 +326,7 @@ export default new Command({ ); } }), - delete: new Command({ + delete: new NamedCommand({ description: "Delete your timezone information.", async run({channel, author}) { prompt( @@ -344,7 +343,7 @@ export default new Command({ ); } }), - utc: new Command({ + utc: new NamedCommand({ description: "Displays UTC time.", async run({channel}) { const time = moment().utc(); @@ -370,7 +369,7 @@ export default new Command({ }); } }), - daylight: new Command({ + daylight: new NamedCommand({ description: "Provides information on the daylight savings region", run: DST_NOTE_INFO }) diff --git a/src/core/command.ts b/src/core/command.ts index 79b1693..becc5f6 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,19 +1,21 @@ -import {parseVars} from "./lib"; -import {Collection} from "discord.js"; -import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember, GuildChannel} from "discord.js"; -import {getPrefix} from "../core/structures"; +import {parseVars} from "../lib"; +import { + Collection, + Client, + Message, + TextChannel, + DMChannel, + NewsChannel, + Guild, + User, + GuildMember, + GuildChannel +} from "discord.js"; +import {getPrefix} from "../structures"; import {SingleMessageOptions} from "./libd"; import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; import {client} from "../index"; -export enum TYPES { - SUBCOMMAND, - USER, - NUMBER, - ANY, - NONE -} - // RegEx patterns used for identifying/extracting each type from a string argument. const patterns = { channel: /^<#(\d{17,19})>$/, @@ -30,7 +32,7 @@ const patterns = { // Therefore, there won't by any type narrowing on channel or guild of CommandMenu until this is fixed. // Otherwise, you'd have to define channelType for every single subcommand, which would get very tedious. // Just use type assertions when you specify a channel type. -export enum CHANNEL_TYPE { +enum CHANNEL_TYPE { ANY, GUILD, DM diff --git a/src/core/handler.ts b/src/core/handler.ts index 9a7582d..0646394 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -1,8 +1,7 @@ import {client} from "../index"; import {loadableCommands} from "./loader"; import {Permissions, Message} from "discord.js"; -import {getPrefix} from "./structures"; -import {Config} from "./structures"; +import {getPrefix} from "../structures"; import {defaultMetadata} from "./command"; // For custom message events that want to cancel the command handler on certain conditions. @@ -99,13 +98,3 @@ client.on("message", async (message) => { ); } }); - -client.once("ready", () => { - if (client.user) { - console.ready(`Logged in as ${client.user.tag}.`); - client.user.setActivity({ - type: "LISTENING", - name: `${Config.prefix}help` - }); - } -}); diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..16b06e0 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,15 @@ +export {Command, NamedCommand} from "./command"; +export {addInterceptRule} from "./handler"; +export { + SingleMessageOptions, + botHasPermission, + paginate, + prompt, + ask, + askYesOrNo, + askMultipleChoice, + getMemberByUsername, + callMemberByUsername +} from "./libd"; +export {loadableCommands, categories} from "./loader"; +export {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; diff --git a/src/core/permissions.ts b/src/core/permissions.ts index 9798a2a..a251a64 100644 --- a/src/core/permissions.ts +++ b/src/core/permissions.ts @@ -1,5 +1,5 @@ import {User, GuildMember, Permissions} from "discord.js"; -import {Config} from "./structures"; +import {Config} from "../structures"; interface PermissionLevel { name: string; diff --git a/src/index.ts b/src/index.ts index 5d1f52c..7280c76 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ import "./modules/globals"; import {Client} from "discord.js"; import setup from "./modules/setup"; -import {Config} from "./core/structures"; +import {Config} from "./structures"; // This is here in order to make it much less of a headache to access the client from other files. // This of course won't actually do anything until the setup process is complete and it logs in. @@ -16,6 +16,7 @@ setup.init().then(() => { // Initialize Modules // import "./core/handler"; // Command loading will start as soon as an instance of "core/command" is loaded, which is loaded in "core/handler". import "./core/eventListeners"; +import "./modules/ready"; import "./modules/presence"; import "./modules/lavalink"; import "./modules/emoteRegistry"; diff --git a/src/core/lib.test.ts b/src/lib.test.ts similarity index 100% rename from src/core/lib.test.ts rename to src/lib.test.ts diff --git a/src/core/lib.ts b/src/lib.ts similarity index 99% rename from src/core/lib.ts rename to src/lib.ts index 477089f..8036a96 100644 --- a/src/core/lib.ts +++ b/src/lib.ts @@ -1,6 +1,6 @@ // Library for pure functions import {get} from "https"; -import FileManager from "./storage"; +import FileManager from "./modules/storage"; /** * Splits a command by spaces while accounting for quotes which capture string arguments. diff --git a/src/modules/emoteRegistry.ts b/src/modules/emoteRegistry.ts index bcc7ed0..a3de8bc 100644 --- a/src/modules/emoteRegistry.ts +++ b/src/modules/emoteRegistry.ts @@ -1,6 +1,6 @@ import {client} from "../index"; -import FileManager from "../core/storage"; -import {EmoteRegistryDump} from "../core/structures"; +import FileManager from "./storage"; +import {EmoteRegistryDump} from "../structures"; function updateGlobalEmoteRegistry(): void { const data: EmoteRegistryDump = {version: 1, list: []}; diff --git a/src/modules/lavalink.ts b/src/modules/lavalink.ts index 92d6ee9..41a24fb 100644 --- a/src/modules/lavalink.ts +++ b/src/modules/lavalink.ts @@ -1,5 +1,5 @@ import attachClientToLavalink from "discord.js-lavalink-lib"; -import {Config} from "../core/structures"; +import {Config} from "../structures"; import {client} from "../index"; // Although the example showed to do "client.music = LavaLink(...)" and "(client as any).music = Lavalink(...)" was done to match that, nowhere in the library is client.music ever actually used nor does the function return anything. In other words, client.music is undefined and is never used. diff --git a/src/modules/messageEmbed.ts b/src/modules/messageEmbed.ts index 1d5c1c5..5018ef2 100644 --- a/src/modules/messageEmbed.ts +++ b/src/modules/messageEmbed.ts @@ -1,6 +1,6 @@ import {client} from "../index"; import {TextChannel, APIMessage, MessageEmbed} from "discord.js"; -import {getPrefix} from "../core/structures"; +import {getPrefix} from "../structures"; import {DiscordAPIError} from "discord.js"; client.on("message", async (message) => { diff --git a/src/modules/ready.ts b/src/modules/ready.ts new file mode 100644 index 0000000..9c3c86b --- /dev/null +++ b/src/modules/ready.ts @@ -0,0 +1,12 @@ +import {client} from "../index"; +import {Config} from "../structures"; + +client.once("ready", () => { + if (client.user) { + console.ready(`Logged in as ${client.user.tag}.`); + client.user.setActivity({ + type: "LISTENING", + name: `${Config.prefix}help` + }); + } +}); diff --git a/src/modules/setup.ts b/src/modules/setup.ts index 99ca135..f146dad 100644 --- a/src/modules/setup.ts +++ b/src/modules/setup.ts @@ -1,7 +1,7 @@ import {existsSync as exists, readFileSync as read, writeFile as write} from "fs"; import inquirer from "inquirer"; -import Storage, {generateHandler} from "../core/storage"; -import {Config} from "../core/structures"; +import Storage, {generateHandler} from "./storage"; +import {Config} from "../structures"; // The template should be built with a reductionist mentality. // Provide everything the user needs and then let them remove whatever they want. diff --git a/src/core/storage.ts b/src/modules/storage.ts similarity index 100% rename from src/core/storage.ts rename to src/modules/storage.ts diff --git a/src/core/structures.ts b/src/structures.ts similarity index 99% rename from src/core/structures.ts rename to src/structures.ts index 74e5adf..401c4d6 100644 --- a/src/core/structures.ts +++ b/src/structures.ts @@ -1,4 +1,4 @@ -import FileManager from "./storage"; +import FileManager from "./modules/storage"; import {select, GenericJSON, GenericStructure} from "./lib"; import {watch} from "fs"; import {Guild as DiscordGuild, Snowflake} from "discord.js"; From 44cae5c0cba0be0209ff8700d56b4410af38a8ce Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Mon, 5 Apr 2021 03:46:50 -0500 Subject: [PATCH 20/30] Fixed some bugs and added proper event handler --- src/commands/system/help.ts | 32 +++++++++++++++++++++++++++----- src/core/command.ts | 31 +++++++++++++++++-------------- src/core/handler.ts | 31 ++++++++++++++++++++++++++++++- src/core/index.ts | 2 +- src/modules/lavalink.ts | 24 ++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 21 deletions(-) diff --git a/src/commands/system/help.ts b/src/commands/system/help.ts index e3b160c..d205a10 100644 --- a/src/commands/system/help.ts +++ b/src/commands/system/help.ts @@ -1,5 +1,5 @@ -import {Command, NamedCommand, loadableCommands, categories, getPermissionName} from "../../core"; -import {toTitleCase} from "../../lib"; +import {Command, NamedCommand, loadableCommands, categories, getPermissionName, CHANNEL_TYPE} from "../../core"; +import {toTitleCase, requireAllCasesHandledFor} from "../../lib"; export default new NamedCommand({ description: "Lists all commands. If a command is specified, their arguments are listed as well.", @@ -10,14 +10,19 @@ export default new NamedCommand({ let output = `Legend: \`\`, \`[list/of/stuff]\`, \`(optional)\`, \`()\`, \`([optional/list/...])\``; for (const [category, headers] of categories) { - output += `\n\n===[ ${toTitleCase(category)} ]===`; + let tmp = `\n\n===[ ${toTitleCase(category)} ]===`; + // Ignore empty categories, including ["test"]. + let hasActualCommands = false; for (const header of headers) { if (header !== "test") { const command = commands.get(header)!; - output += `\n- \`${header}\`: ${command.description}`; + tmp += `\n- \`${header}\`: ${command.description}`; + hasActualCommands = true; } } + + if (hasActualCommands) output += tmp; } channel.send(output, {split: true}); @@ -50,6 +55,8 @@ export default new NamedCommand({ let append = ""; command = result.command; + if (result.args.length > 0) header += " " + result.args.join(" "); + if (command.usage === "") { const list: string[] = []; @@ -80,9 +87,24 @@ export default new NamedCommand({ return channel.send( `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${category}\`\nPermission Required: \`${getPermissionName( result.permission - )}\` (${result.permission})\nDescription: ${command.description}\n${append}`, + )}\` (${result.permission})\nChannel Type: ${getChannelTypeName(result.channelType)}\nNSFW Only: ${ + result.nsfw ? "Yes" : "No" + }\nDescription: ${command.description}\n${append}`, {split: true} ); } }) }); + +function getChannelTypeName(type: CHANNEL_TYPE): string { + switch (type) { + case CHANNEL_TYPE.ANY: + return "Any"; + case CHANNEL_TYPE.GUILD: + return "Guild Only"; + case CHANNEL_TYPE.DM: + return "DM Only"; + default: + requireAllCasesHandledFor(type); + } +} diff --git a/src/core/command.ts b/src/core/command.ts index becc5f6..d389619 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -32,7 +32,7 @@ const patterns = { // Therefore, there won't by any type narrowing on channel or guild of CommandMenu until this is fixed. // Otherwise, you'd have to define channelType for every single subcommand, which would get very tedious. // Just use type assertions when you specify a channel type. -enum CHANNEL_TYPE { +export enum CHANNEL_TYPE { ANY, GUILD, DM @@ -126,7 +126,6 @@ export class Command { protected user: Command | null; protected number: Command | null; protected any: Command | null; - public static readonly CHANNEL_TYPE = CHANNEL_TYPE; constructor(options?: CommandOptions) { this.description = options?.description || "No description."; @@ -182,6 +181,13 @@ export class Command { menu: CommandMenu, metadata: ExecuteCommandMetadata ): Promise { + // Update inherited properties if the current command specifies a property. + // In case there are no initial arguments, these should go first so that it can register. + if (this.permission !== -1) metadata.permission = this.permission; + if (this.nsfw !== null) metadata.nsfw = this.nsfw; + if (this.channelType !== null) metadata.channelType = this.channelType; + + // Take off the leftmost argument from the list. const param = args.shift(); // If there are no arguments left, execute the current command. Otherwise, continue on. @@ -239,7 +245,7 @@ export class Command { return null; } catch (error) { const errorMessage = error.stack ?? error; - console.error(`Command Error: ${metadata.header} (${metadata.args})\n${errorMessage}`); + console.error(`Command Error: ${metadata.header} (${metadata.args.join(", ")})\n${errorMessage}`); return { content: `There was an error while trying to execute that command!\`\`\`${errorMessage}\`\`\`` @@ -250,11 +256,6 @@ export class Command { // If the current command is an endpoint but there are still some arguments left, don't continue. if (this.endpoint) return {content: "Too many arguments!"}; - // Update inherited properties if the current command specifies a property. - if (this.permission !== -1) metadata.permission = this.permission; - if (this.nsfw !== null) metadata.nsfw = this.nsfw; - if (this.channelType !== null) metadata.channelType = this.channelType; - // Resolve the value of the current command's argument (adding it to the resolved args), // then pass the thread of execution to whichever subcommand is valid (if any). if (this.subcommands.has(param)) { @@ -295,6 +296,14 @@ export class Command { args: string[], metadata: CommandInfoMetadata ): Promise { + // Update inherited properties if the current command specifies a property. + // In case there are no initial arguments, these should go first so that it can register. + if (this.permission !== -1) metadata.permission = this.permission; + if (this.nsfw !== null) metadata.nsfw = this.nsfw; + if (this.channelType !== null) metadata.channelType = this.channelType; + if (this.usage !== "") metadata.usage = this.usage; + + // Take off the leftmost argument from the list. const param = args.shift(); // If there are no arguments left, return the data or an error message. @@ -324,12 +333,6 @@ export class Command { }; } - // Update inherited properties if the current command specifies a property. - if (this.permission !== -1) metadata.permission = this.permission; - if (this.nsfw !== null) metadata.nsfw = this.nsfw; - if (this.channelType !== null) metadata.channelType = this.channelType; - if (this.usage !== "") metadata.usage = this.usage; - // Then test if anything fits any hardcoded values, otherwise check if it's a valid keyed subcommand. if (param === "") { if (this.user) { diff --git a/src/core/handler.ts b/src/core/handler.ts index 0646394..10bea9e 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -1,6 +1,6 @@ import {client} from "../index"; import {loadableCommands} from "./loader"; -import {Permissions, Message} from "discord.js"; +import {Permissions, Message, TextChannel, DMChannel, NewsChannel} from "discord.js"; import {getPrefix} from "../structures"; import {defaultMetadata} from "./command"; @@ -11,6 +11,16 @@ export function addInterceptRule(handler: (message: Message) => boolean) { interceptRules.push(handler); } +const lastCommandInfo: { + header: string; + args: string[]; + channel: TextChannel | DMChannel | NewsChannel | null; +} = { + header: "N/A", + args: [], + channel: null +}; + // Note: client.user is only undefined before the bot logs in, so by this point, client.user cannot be undefined. // Note: guild.available will never need to be checked because the message starts in either a DM channel or an already-available guild. client.on("message", async (message) => { @@ -41,6 +51,11 @@ client.on("message", async (message) => { if (commands.has(header)) { const command = commands.get(header)!; + // Set last command info in case of unhandled rejections. + lastCommandInfo.header = header; + lastCommandInfo.args = [...args]; + lastCommandInfo.channel = channel; + // Send the arguments to the command to resolve and execute. const result = await command.execute(args, menu, { header, @@ -73,6 +88,11 @@ client.on("message", async (message) => { if (commands.has(header)) { const command = commands.get(header)!; + // Set last command info in case of unhandled rejections. + lastCommandInfo.header = header; + lastCommandInfo.args = [...args]; + lastCommandInfo.channel = channel; + // Send the arguments to the command to resolve and execute. const result = await command.execute(args, menu, { header, @@ -98,3 +118,12 @@ client.on("message", async (message) => { ); } }); + +process.on("unhandledRejection", (reason: any) => { + if (reason?.name === "DiscordAPIError") { + console.error(`Command Error: ${lastCommandInfo.header} (${lastCommandInfo.args.join(", ")})\n${reason.stack}`); + lastCommandInfo.channel?.send( + `There was an error while trying to execute that command!\`\`\`${reason.stack}\`\`\`` + ); + } +}); diff --git a/src/core/index.ts b/src/core/index.ts index 16b06e0..047e6f5 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,4 +1,4 @@ -export {Command, NamedCommand} from "./command"; +export {Command, NamedCommand, CHANNEL_TYPE} from "./command"; export {addInterceptRule} from "./handler"; export { SingleMessageOptions, diff --git a/src/modules/lavalink.ts b/src/modules/lavalink.ts index 41a24fb..8b18a7a 100644 --- a/src/modules/lavalink.ts +++ b/src/modules/lavalink.ts @@ -22,3 +22,27 @@ attachClientToLavalink(client, { helpCmd: "mhelp", admins: ["717352467280691331"] }); + +// Disable the unhandledRejection listener by Lavalink because it captures every single unhandled +// rejection and adds its message with it. Then replace it with a better, more selective error handler. +for (const listener of process.listeners("unhandledRejection")) { + if (listener.toString().includes("discord.js-lavalink-musicbot")) { + process.off("unhandledRejection", listener); + } +} + +process.on("unhandledRejection", (reason: any) => { + if (reason?.code === "ECONNREFUSED") { + console.error( + `[discord.js-lavalink-musicbot] Caught unhandled rejection: ${reason.stack}\nIf this is causing issues, head to the support server at https://discord.gg/dNN4azK` + ); + } +}); + +// It's unsafe to process uncaughtException because after an uncaught exception, the system +// becomes corrupted. So disable Lavalink from adding a hook to it. +for (const listener of process.listeners("uncaughtException")) { + if (listener.toString().includes("discord.js-lavalink-musicbot")) { + process.off("uncaughtException", listener); + } +} From 03f37680e7e1cb1e8bb3965aa9168e32d65df6a6 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Mon, 5 Apr 2021 04:26:33 -0500 Subject: [PATCH 21/30] Fully isolated command handler from rest of code --- src/commands/fun/modules/eco-extras.ts | 2 +- src/core/command.ts | 21 +++- src/core/eventListeners.ts | 39 +++---- src/core/handler.ts | 147 +++++++++++++------------ src/core/index.ts | 1 + src/core/interface.ts | 23 ++++ src/core/loader.ts | 22 ---- src/core/permissions.ts | 64 ++--------- src/index.ts | 54 ++++++++- 9 files changed, 193 insertions(+), 180 deletions(-) create mode 100644 src/core/interface.ts diff --git a/src/commands/fun/modules/eco-extras.ts b/src/commands/fun/modules/eco-extras.ts index f8de393..f2760dd 100644 --- a/src/commands/fun/modules/eco-extras.ts +++ b/src/commands/fun/modules/eco-extras.ts @@ -1,4 +1,4 @@ -import {Command, NamedCommand} from "../../../core/command"; +import {Command, NamedCommand} from "../../../core"; import {Storage} from "../../../structures"; import {isAuthorized, getMoneyEmbed} from "./eco-utils"; diff --git a/src/core/command.ts b/src/core/command.ts index d389619..f1a06ce 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,4 +1,3 @@ -import {parseVars} from "../lib"; import { Collection, Client, @@ -11,10 +10,24 @@ import { GuildMember, GuildChannel } from "discord.js"; -import {getPrefix} from "../structures"; import {SingleMessageOptions} from "./libd"; import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; -import {client} from "../index"; +import {getPrefix} from "./interface"; +import {parseVars} from "../lib"; + +/** + * ===[ Command Types ]=== + * SUBCOMMAND - Any specifically-defined keywords / string literals. + * CHANNEL - <#...> + * ROLE - <@&...> + * EMOTE - <::ID> (The previous two values, animated and emote name respectively, do not matter at all for finding the emote.) + * MESSAGE - Available by using the built-in "Copy Message Link" or "Copy ID" buttons. https://discordapp.com/channels/// or - (automatically searches all guilds for the channel ID). + * USER - <@...> and <@!...> + * ID - Any number with 17-19 digits. Only used as a redirect to another subcommand type. + * NUMBER - Any valid number via the Number() function, except for NaN and Infinity (because those can really mess with the program). + * ANY - Generic argument case. + * NONE - No subcommands exist. + */ // RegEx patterns used for identifying/extracting each type from a string argument. const patterns = { @@ -264,7 +277,7 @@ export class Command { const id = patterns.user.exec(param)![1]; try { - menu.args.push(await client.users.fetch(id)); + menu.args.push(await menu.client.users.fetch(id)); return this.user.execute(args, menu, metadata); } catch { return { diff --git a/src/core/eventListeners.ts b/src/core/eventListeners.ts index e32db03..705647e 100644 --- a/src/core/eventListeners.ts +++ b/src/core/eventListeners.ts @@ -1,28 +1,29 @@ -import {client} from "../index"; +import {Client, Permissions, Message} from "discord.js"; import {botHasPermission} from "./libd"; -import {Permissions, Message} from "discord.js"; // A list of message ID and callback pairs. You get the emote name and ID of the user reacting. export const unreactEventListeners: Map void> = new Map(); -// Attached to the client, there can be one event listener attached to a message ID which is executed if present. -client.on("messageReactionRemove", (reaction, user) => { - const canDeleteEmotes = botHasPermission(reaction.message.guild, Permissions.FLAGS.MANAGE_MESSAGES); - - if (!canDeleteEmotes) { - const callback = unreactEventListeners.get(reaction.message.id); - callback && callback(reaction.emoji.name, user.id); - } -}); - // A list of "channel-message" and callback pairs. Also, I imagine that the callback will be much more maintainable when discord.js v13 comes out with a dedicated message.referencedMessage property. // Also, I'm defining it here instead of the message event because the load order screws up if you export it from there. Yeah... I'm starting to notice just how much technical debt has been built up. The command handler needs to be modularized and refactored sooner rather than later. Define all constants in one area then grab from there. export const replyEventListeners = new Map void>(); -client.on("message", (message) => { - // If there's an inline reply, fire off that event listener (if it exists). - if (message.reference) { - const reference = message.reference; - replyEventListeners.get(`${reference.channelID}-${reference.messageID}`)?.(message); - } -}); +export function attachEventListenersToClient(client: Client) { + // Attached to the client, there can be one event listener attached to a message ID which is executed if present. + client.on("messageReactionRemove", (reaction, user) => { + const canDeleteEmotes = botHasPermission(reaction.message.guild, Permissions.FLAGS.MANAGE_MESSAGES); + + if (!canDeleteEmotes) { + const callback = unreactEventListeners.get(reaction.message.id); + callback && callback(reaction.emoji.name, user.id); + } + }); + + client.on("message", (message) => { + // If there's an inline reply, fire off that event listener (if it exists). + if (message.reference) { + const reference = message.reference; + replyEventListeners.get(`${reference.channelID}-${reference.messageID}`)?.(message); + } + }); +} diff --git a/src/core/handler.ts b/src/core/handler.ts index 10bea9e..54f8f1b 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -1,8 +1,7 @@ -import {client} from "../index"; +import {Client, Permissions, Message, TextChannel, DMChannel, NewsChannel} from "discord.js"; import {loadableCommands} from "./loader"; -import {Permissions, Message, TextChannel, DMChannel, NewsChannel} from "discord.js"; -import {getPrefix} from "../structures"; import {defaultMetadata} from "./command"; +import {getPrefix} from "./interface"; // For custom message events that want to cancel the command handler on certain conditions. const interceptRules: ((message: Message) => boolean)[] = [(message) => message.author.bot]; @@ -23,67 +22,31 @@ const lastCommandInfo: { // Note: client.user is only undefined before the bot logs in, so by this point, client.user cannot be undefined. // Note: guild.available will never need to be checked because the message starts in either a DM channel or an already-available guild. -client.on("message", async (message) => { - for (const shouldIntercept of interceptRules) { - if (shouldIntercept(message)) { - return; - } - } - - const commands = await loadableCommands; - const {author, channel, content, guild, member} = message; - const text = content; - const menu = { - author, - channel, - client, - guild, - member, - message, - args: [] - }; - - // Execute a dedicated block for messages in DM channels. - if (channel.type === "dm") { - // In a DM channel, simply forget about the prefix and execute any message as a command. - const [header, ...args] = text.split(/ +/); - - if (commands.has(header)) { - const command = commands.get(header)!; - - // Set last command info in case of unhandled rejections. - lastCommandInfo.header = header; - lastCommandInfo.args = [...args]; - lastCommandInfo.channel = channel; - - // Send the arguments to the command to resolve and execute. - const result = await command.execute(args, menu, { - header, - args: [...args], - ...defaultMetadata - }); - - // If something went wrong, let the user know (like if they don't have permission to use a command). - if (result) { - channel.send(result); +export function attachMessageHandlerToClient(client: Client) { + client.on("message", async (message) => { + for (const shouldIntercept of interceptRules) { + if (shouldIntercept(message)) { + return; } - } else { - channel.send( - `I couldn't find the command or alias that starts with \`${header}\`. To see the list of commands, type \`help\`` - ); } - } - // Continue if the bot has permission to send messages in this channel. - else if (channel.permissionsFor(client.user!)!.has(Permissions.FLAGS.SEND_MESSAGES)) { - const prefix = getPrefix(guild); - // First, test if the message is just a ping to the bot. - if (new RegExp(`^<@!?${client.user!.id}>$`).test(text)) { - channel.send(`${author}, my prefix on this server is \`${prefix}\`.`); - } - // Then check if it's a normal command. - else if (text.startsWith(prefix)) { - const [header, ...args] = text.substring(prefix.length).split(/ +/); + const commands = await loadableCommands; + const {author, channel, content, guild, member} = message; + const text = content; + const menu = { + author, + channel, + client, + guild, + member, + message, + args: [] + }; + + // Execute a dedicated block for messages in DM channels. + if (channel.type === "dm") { + // In a DM channel, simply forget about the prefix and execute any message as a command. + const [header, ...args] = text.split(/ +/); if (commands.has(header)) { const command = commands.get(header)!; @@ -104,20 +67,58 @@ client.on("message", async (message) => { if (result) { channel.send(result); } + } else { + channel.send( + `I couldn't find the command or alias that starts with \`${header}\`. To see the list of commands, type \`help\`` + ); } } - } - // Otherwise, let the sender know that the bot doesn't have permission to send messages. - else { - author.send( - `I don't have permission to send messages in ${channel}. ${ - member!.hasPermission(Permissions.FLAGS.ADMINISTRATOR) - ? "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended." - : "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong." - }` - ); - } -}); + // Continue if the bot has permission to send messages in this channel. + else if (channel.permissionsFor(client.user!)!.has(Permissions.FLAGS.SEND_MESSAGES)) { + const prefix = getPrefix(guild); + + // First, test if the message is just a ping to the bot. + if (new RegExp(`^<@!?${client.user!.id}>$`).test(text)) { + channel.send(`${author}, my prefix on this server is \`${prefix}\`.`); + } + // Then check if it's a normal command. + else if (text.startsWith(prefix)) { + const [header, ...args] = text.substring(prefix.length).split(/ +/); + + if (commands.has(header)) { + const command = commands.get(header)!; + + // Set last command info in case of unhandled rejections. + lastCommandInfo.header = header; + lastCommandInfo.args = [...args]; + lastCommandInfo.channel = channel; + + // Send the arguments to the command to resolve and execute. + const result = await command.execute(args, menu, { + header, + args: [...args], + ...defaultMetadata + }); + + // If something went wrong, let the user know (like if they don't have permission to use a command). + if (result) { + channel.send(result); + } + } + } + } + // Otherwise, let the sender know that the bot doesn't have permission to send messages. + else { + author.send( + `I don't have permission to send messages in ${channel}. ${ + member!.hasPermission(Permissions.FLAGS.ADMINISTRATOR) + ? "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended." + : "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong." + }` + ); + } + }); +} process.on("unhandledRejection", (reason: any) => { if (reason?.name === "DiscordAPIError") { diff --git a/src/core/index.ts b/src/core/index.ts index 047e6f5..0b1d56f 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,5 +1,6 @@ export {Command, NamedCommand, CHANNEL_TYPE} from "./command"; export {addInterceptRule} from "./handler"; +export {launch} from "./interface"; export { SingleMessageOptions, botHasPermission, diff --git a/src/core/interface.ts b/src/core/interface.ts new file mode 100644 index 0000000..b04d2af --- /dev/null +++ b/src/core/interface.ts @@ -0,0 +1,23 @@ +import {Client, User, GuildMember, Guild} from "discord.js"; +import {attachMessageHandlerToClient} from "./handler"; +import {attachEventListenersToClient} from "./eventListeners"; + +interface LaunchSettings { + permissionLevels: PermissionLevel[]; + getPrefix: (guild: Guild | null) => string; +} + +export async function launch(client: Client, settings: LaunchSettings) { + attachMessageHandlerToClient(client); + attachEventListenersToClient(client); + permissionLevels = settings.permissionLevels; + getPrefix = settings.getPrefix; +} + +interface PermissionLevel { + name: string; + check: (user: User, member: GuildMember | null) => boolean; +} + +export let permissionLevels: PermissionLevel[] = []; +export let getPrefix: (guild: Guild | null) => string = () => "."; diff --git a/src/core/loader.ts b/src/core/loader.ts index 6c6fa7f..d5ba5c1 100644 --- a/src/core/loader.ts +++ b/src/core/loader.ts @@ -79,25 +79,3 @@ function globP(path: string) { }); }); } - -// Gathers a list of categories and top-level commands. -// Returns: new Collection() -/*export async function getCommandList(): Promise> { - const categorizedCommands = new Collection(); - const commands = await loadableCommands; - - for (const [category, headers] of categories) { - const commandList: Command[] = []; - - for (const header of headers) { - if (header !== "test") { - // If this is somehow undefined, it'll show up as an error when implementing a help command. - commandList.push(commands.get(header)!); - } - } - - categorizedCommands.set(toTitleCase(category), commandList); - } - - return categorizedCommands; -}*/ diff --git a/src/core/permissions.ts b/src/core/permissions.ts index a251a64..fb430c8 100644 --- a/src/core/permissions.ts +++ b/src/core/permissions.ts @@ -1,68 +1,18 @@ -import {User, GuildMember, Permissions} from "discord.js"; -import {Config} from "../structures"; - -interface PermissionLevel { - name: string; - check: (user: User, member: GuildMember | null) => boolean; -} - -export const PermissionLevels: PermissionLevel[] = [ - { - // NONE // - name: "User", - check: () => true - }, - { - // MOD // - name: "Moderator", - check: (_user, member) => - !!member && - (member.hasPermission(Permissions.FLAGS.MANAGE_ROLES) || - member.hasPermission(Permissions.FLAGS.MANAGE_MESSAGES) || - member.hasPermission(Permissions.FLAGS.KICK_MEMBERS) || - member.hasPermission(Permissions.FLAGS.BAN_MEMBERS)) - }, - { - // ADMIN // - name: "Administrator", - check: (_user, member) => !!member && member.hasPermission(Permissions.FLAGS.ADMINISTRATOR) - }, - { - // OWNER // - name: "Server Owner", - check: (_user, member) => !!member && member.guild.ownerID === member.id - }, - { - // BOT_SUPPORT // - name: "Bot Support", - check: (user) => Config.support.includes(user.id) - }, - { - // BOT_ADMIN // - name: "Bot Admin", - check: (user) => Config.admins.includes(user.id) - }, - { - // BOT_OWNER // - name: "Bot Owner", - check: (user) => Config.owner === user.id - } -]; - -// After checking the lengths of these three objects, use this as the length for consistency. -const length = PermissionLevels.length; +import {User, GuildMember} from "discord.js"; +import {permissionLevels} from "./interface"; export function hasPermission(user: User, member: GuildMember | null, permission: number): boolean { - for (let i = length - 1; i >= permission; i--) if (PermissionLevels[i].check(user, member)) return true; + for (let i = permissionLevels.length - 1; i >= permission; i--) + if (permissionLevels[i].check(user, member)) return true; return false; } export function getPermissionLevel(user: User, member: GuildMember | null): number { - for (let i = length - 1; i >= 0; i--) if (PermissionLevels[i].check(user, member)) return i; + for (let i = permissionLevels.length - 1; i >= 0; i--) if (permissionLevels[i].check(user, member)) return i; return 0; } export function getPermissionName(level: number) { - if (level > length || level < 0) return "N/A"; - else return PermissionLevels[level].name; + if (level > permissionLevels.length || level < 0) return "N/A"; + else return permissionLevels[level].name; } diff --git a/src/index.ts b/src/index.ts index 7280c76..21633ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,9 @@ // Bootstrapping Section // import "./modules/globals"; -import {Client} from "discord.js"; +import {Client, Permissions} from "discord.js"; +import {launch} from "./core"; import setup from "./modules/setup"; -import {Config} from "./structures"; +import {Config, getPrefix} from "./structures"; // This is here in order to make it much less of a headache to access the client from other files. // This of course won't actually do anything until the setup process is complete and it logs in. @@ -13,9 +14,54 @@ setup.init().then(() => { client.login(Config.token).catch(setup.again); }); +// Setup the command handler. +launch(client, { + permissionLevels: [ + { + // NONE // + name: "User", + check: () => true + }, + { + // MOD // + name: "Moderator", + check: (_user, member) => + !!member && + (member.hasPermission(Permissions.FLAGS.MANAGE_ROLES) || + member.hasPermission(Permissions.FLAGS.MANAGE_MESSAGES) || + member.hasPermission(Permissions.FLAGS.KICK_MEMBERS) || + member.hasPermission(Permissions.FLAGS.BAN_MEMBERS)) + }, + { + // ADMIN // + name: "Administrator", + check: (_user, member) => !!member && member.hasPermission(Permissions.FLAGS.ADMINISTRATOR) + }, + { + // OWNER // + name: "Server Owner", + check: (_user, member) => !!member && member.guild.ownerID === member.id + }, + { + // BOT_SUPPORT // + name: "Bot Support", + check: (user) => Config.support.includes(user.id) + }, + { + // BOT_ADMIN // + name: "Bot Admin", + check: (user) => Config.admins.includes(user.id) + }, + { + // BOT_OWNER // + name: "Bot Owner", + check: (user) => Config.owner === user.id + } + ], + getPrefix: getPrefix +}); + // Initialize Modules // -import "./core/handler"; // Command loading will start as soon as an instance of "core/command" is loaded, which is loaded in "core/handler". -import "./core/eventListeners"; import "./modules/ready"; import "./modules/presence"; import "./modules/lavalink"; From 4a78ce808becae3f0c4ff60208d65e219549ec3d Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Mon, 5 Apr 2021 06:45:28 -0500 Subject: [PATCH 22/30] Added more subcommand types --- src/core/command.ts | 274 +++++++++++++++++++++++++++++++++++++++---- src/modules/setup.ts | 10 ++ 2 files changed, 261 insertions(+), 23 deletions(-) diff --git a/src/core/command.ts b/src/core/command.ts index f1a06ce..ba89c71 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -13,7 +13,7 @@ import { import {SingleMessageOptions} from "./libd"; import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions"; import {getPrefix} from "./interface"; -import {parseVars} from "../lib"; +import {parseVars, requireAllCasesHandledFor} from "../lib"; /** * ===[ Command Types ]=== @@ -34,11 +34,15 @@ const patterns = { channel: /^<#(\d{17,19})>$/, role: /^<@&(\d{17,19})>$/, emote: /^$/, - message: /(?:\d{17,19}\/(\d{17,19})\/(\d{17,19})$)|(?:^(\d{17,19})-(\d{17,19})$)/, + messageLink: /^https?:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/channels\/(?:\d{17,19}|@me)\/(\d{17,19})\/(\d{17,19})$/, + messagePair: /^(\d{17,19})-(\d{17,19})$/, user: /^<@!?(\d{17,19})>$/, id: /^(\d{17,19})$/ }; +// Maybe add a guild redirect... somehow? +type ID = "channel" | "role" | "emote" | "message" | "user"; + // Callbacks don't work with discriminated unions: // - https://github.com/microsoft/TypeScript/issues/41759 // - https://github.com/microsoft/TypeScript/issues/35769 @@ -78,10 +82,17 @@ interface CommandOptionsEndpoint { } // Prevents subcommands from being added by compile-time. +// Also, contrary to what you might think, channel pings do still work in DM channels. +// Role pings, maybe not, but it's not a big deal. interface CommandOptionsNonEndpoint { readonly endpoint?: false; readonly subcommands?: {[key: string]: NamedCommand}; + readonly channel?: Command; + readonly role?: Command; + readonly emote?: Command; + readonly message?: Command; readonly user?: Command; + readonly id?: ID; readonly number?: Command; readonly any?: Command; } @@ -119,6 +130,7 @@ interface CommandInfoMetadata { channelType: CHANNEL_TYPE; args: string[]; usage: string; + readonly originalArgs: string[]; } export const defaultMetadata = { @@ -136,7 +148,13 @@ export class Command { public readonly channelType: CHANNEL_TYPE | null; // null (default) indicates to inherit protected run: (($: CommandMenu) => Promise) | string; protected readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. + protected channel: Command | null; + protected role: Command | null; + protected emote: Command | null; + protected message: Command | null; protected user: Command | null; + protected id: Command | null; + protected idType: ID | null; protected number: Command | null; protected any: Command | null; @@ -149,14 +167,47 @@ export class Command { this.channelType = options?.channelType ?? null; this.run = options?.run || "No action was set on this command!"; this.subcommands = new Collection(); // Populate this collection after setting subcommands. + this.channel = null; + this.role = null; + this.emote = null; + this.message = null; this.user = null; + this.id = null; + this.idType = null; this.number = null; this.any = null; if (options && !options.endpoint) { - this.user = options?.user || null; - this.number = options?.number || null; - this.any = options?.any || null; + if (options?.channel) this.channel = options.channel; + if (options?.role) this.role = options.role; + if (options?.emote) this.emote = options.emote; + if (options?.message) this.message = options.message; + if (options?.user) this.user = options.user; + if (options?.number) this.number = options.number; + if (options?.any) this.any = options.any; + if (options?.id) this.idType = options.id; + + if (options?.id) { + switch (options.id) { + case "channel": + this.id = this.channel; + break; + case "role": + this.id = this.role; + break; + case "emote": + this.id = this.emote; + break; + case "message": + this.id = this.message; + break; + case "user": + this.id = this.user; + break; + default: + requireAllCasesHandledFor(options.id); + } + } if (options?.subcommands) { const baseSubcommands = Object.keys(options.subcommands); @@ -271,8 +322,85 @@ export class Command { // Resolve the value of the current command's argument (adding it to the resolved args), // then pass the thread of execution to whichever subcommand is valid (if any). + const isMessageLink = patterns.messageLink.test(param); + const isMessagePair = patterns.messagePair.test(param); + if (this.subcommands.has(param)) { return this.subcommands.get(param)!.execute(args, menu, metadata); + } else if (this.channel && patterns.channel.test(param)) { + const id = patterns.channel.exec(param)![1]; + const channel = menu.client.channels.cache.get(id); + + // Users can only enter in this format for text channels, so this restricts it to that. + if (channel instanceof TextChannel) { + menu.args.push(channel); + return this.channel.execute(args, menu, metadata); + } else { + return { + content: `\`${id}\` is not a valid text channel!` + }; + } + } else if (this.role && patterns.role.test(param)) { + const id = patterns.role.exec(param)![1]; + + if (!menu.guild) { + return { + content: "You can't use role parameters in DM channels!" + }; + } + + const role = menu.guild.roles.cache.get(id); + + if (role) { + menu.args.push(role); + return this.role.execute(args, menu, metadata); + } else { + return { + content: `\`${id}\` is not a valid role in this server!` + }; + } + } else if (this.emote && patterns.emote.test(param)) { + const id = patterns.emote.exec(param)![1]; + const emote = menu.client.emojis.cache.get(id); + + if (emote) { + menu.args.push(emote); + return this.emote.execute(args, menu, metadata); + } else { + return { + content: `\`${id}\` isn't a valid emote!` + }; + } + } else if (this.message && (isMessageLink || isMessagePair)) { + let channelID = ""; + let messageID = ""; + + if (isMessageLink) { + const result = patterns.messageLink.exec(param)!; + channelID = result[1]; + messageID = result[2]; + } else if (isMessagePair) { + const result = patterns.messagePair.exec(param)!; + channelID = result[1]; + messageID = result[2]; + } + + const channel = menu.client.channels.cache.get(channelID); + + if (channel instanceof TextChannel || channel instanceof DMChannel) { + try { + menu.args.push(await channel.messages.fetch(messageID)); + return this.message.execute(args, menu, metadata); + } catch { + return { + content: `\`${messageID}\` isn't a valid message of channel ${channel}!` + }; + } + } else { + return { + content: `\`${channelID}\` is not a valid text channel!` + }; + } } else if (this.user && patterns.user.test(param)) { const id = patterns.user.exec(param)![1]; @@ -284,6 +412,73 @@ export class Command { content: `No user found by the ID \`${id}\`!` }; } + } else if (this.id && this.idType && patterns.id.test(param)) { + const id = patterns.id.exec(param)![1]; + + // Probably modularize the findXByY code in general in libd. + // Because this part is pretty much a whole bunch of copy pastes. + switch (this.idType) { + case "channel": + const channel = menu.client.channels.cache.get(id); + + // Users can only enter in this format for text channels, so this restricts it to that. + if (channel instanceof TextChannel) { + menu.args.push(channel); + return this.id.execute(args, menu, metadata); + } else { + return { + content: `\`${id}\` isn't a valid text channel!` + }; + } + case "role": + if (!menu.guild) { + return { + content: "You can't use role parameters in DM channels!" + }; + } + + const role = menu.guild.roles.cache.get(id); + + if (role) { + menu.args.push(role); + return this.id.execute(args, menu, metadata); + } else { + return { + content: `\`${id}\` isn't a valid role in this server!` + }; + } + case "emote": + const emote = menu.client.emojis.cache.get(id); + + if (emote) { + menu.args.push(emote); + return this.id.execute(args, menu, metadata); + } else { + return { + content: `\`${id}\` isn't a valid emote!` + }; + } + case "message": + try { + menu.args.push(await menu.channel.messages.fetch(id)); + return this.id.execute(args, menu, metadata); + } catch { + return { + content: `\`${id}\` isn't a valid message of channel ${menu.channel}!` + }; + } + case "user": + try { + menu.args.push(await menu.client.users.fetch(id)); + return this.id.execute(args, menu, metadata); + } catch { + return { + content: `No user found by the ID \`${id}\`!` + }; + } + default: + requireAllCasesHandledFor(this.idType); + } } else if (this.number && !Number.isNaN(Number(param)) && param !== "Infinity" && param !== "-Infinity") { menu.args.push(Number(param)); return this.number.execute(args, menu, metadata); @@ -302,7 +497,7 @@ export class Command { // What this does is resolve the resulting subcommand as well as the inherited properties and the available subcommands. public async resolveInfo(args: string[]): Promise { - return this.resolveInfoInternal(args, {...defaultMetadata, args: [], usage: ""}); + return this.resolveInfoInternal(args, {...defaultMetadata, args: [], usage: "", originalArgs: [...args]}); } private async resolveInfoInternal( @@ -333,7 +528,12 @@ export class Command { } // Then get all the generic subcommands. + if (this.channel) subcommandInfo.set("", this.channel); + if (this.role) subcommandInfo.set("", this.role); + if (this.emote) subcommandInfo.set("", this.emote); + if (this.message) subcommandInfo.set("", this.message); if (this.user) subcommandInfo.set("", this.user); + if (this.id) subcommandInfo.set(`>`, this.id); if (this.number) subcommandInfo.set("", this.number); if (this.any) subcommandInfo.set("", this.any); @@ -346,45 +546,73 @@ export class Command { }; } + const invalidSubcommandGenerator: () => CommandInfoError = () => ({ + type: "error", + message: `No subcommand found by the argument list: \`${metadata.originalArgs.join(" ")}\`` + }); + // Then test if anything fits any hardcoded values, otherwise check if it's a valid keyed subcommand. - if (param === "") { + if (param === "") { + if (this.channel) { + metadata.args.push(""); + return this.channel.resolveInfoInternal(args, metadata); + } else { + return invalidSubcommandGenerator(); + } + } else if (param === "") { + if (this.role) { + metadata.args.push(""); + return this.role.resolveInfoInternal(args, metadata); + } else { + return invalidSubcommandGenerator(); + } + } else if (param === "") { + if (this.emote) { + metadata.args.push(""); + return this.emote.resolveInfoInternal(args, metadata); + } else { + return invalidSubcommandGenerator(); + } + } else if (param === "") { + if (this.message) { + metadata.args.push(""); + return this.message.resolveInfoInternal(args, metadata); + } else { + return invalidSubcommandGenerator(); + } + } else if (param === "") { if (this.user) { metadata.args.push(""); return this.user.resolveInfoInternal(args, metadata); } else { - return { - type: "error", - message: `No subcommand found by the argument list: \`${metadata.args.join(" ")}\`` - }; + return invalidSubcommandGenerator(); + } + } else if (param === "") { + if (this.id) { + metadata.args.push(`>`); + return this.id.resolveInfoInternal(args, metadata); + } else { + return invalidSubcommandGenerator(); } } else if (param === "") { if (this.number) { metadata.args.push(""); return this.number.resolveInfoInternal(args, metadata); } else { - return { - type: "error", - message: `No subcommand found by the argument list: \`${metadata.args.join(" ")}\`` - }; + return invalidSubcommandGenerator(); } } else if (param === "") { if (this.any) { metadata.args.push(""); return this.any.resolveInfoInternal(args, metadata); } else { - return { - type: "error", - message: `No subcommand found by the argument list: \`${metadata.args.join(" ")}\`` - }; + return invalidSubcommandGenerator(); } } else if (this.subcommands?.has(param)) { metadata.args.push(param); return this.subcommands.get(param)!.resolveInfoInternal(args, metadata); } else { - return { - type: "error", - message: `No subcommand found by the argument list: \`${metadata.args.join(" ")}\`` - }; + return invalidSubcommandGenerator(); } } } diff --git a/src/modules/setup.ts b/src/modules/setup.ts index f146dad..9b44eda 100644 --- a/src/modules/setup.ts +++ b/src/modules/setup.ts @@ -14,6 +14,16 @@ if (IS_DEV_MODE && !exists("src/commands/test.ts")) { ); } +// A generic process handler is set to catch unhandled rejections other than the ones from Lavalink and Discord. +process.on("unhandledRejection", (reason: any) => { + const isLavalinkError = reason?.code === "ECONNREFUSED"; + const isDiscordError = reason?.name === "DiscordAPIError"; + + if (!isLavalinkError && !isDiscordError) { + console.error(reason.stack); + } +}); + // This file is called (or at least should be called) automatically as long as a config file doesn't exist yet. // And that file won't be written until the data is successfully initialized. const prompts = [ From 5402883a2f4c245cbc602b7dc92e87164ab96753 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Mon, 5 Apr 2021 07:21:27 -0500 Subject: [PATCH 23/30] Resolved all lingering post-merge errors --- package-lock.json | 13 -- package.json | 1 - src/commands/admin.ts | 329 ------------------------------ src/commands/fun/figlet.ts | 14 +- src/commands/fun/insult.ts | 14 +- src/commands/fun/love.ts | 14 +- src/commands/fun/ravi.ts | 18 +- src/commands/fun/thonk.ts | 10 +- src/commands/fun/urban.ts | 16 +- src/commands/fun/weather.ts | 16 +- src/commands/fun/whois.ts | 36 ++-- src/commands/system/admin.ts | 120 +++++++++++ src/commands/utility/calc.ts | 18 +- src/commands/utility/code.ts | 4 +- src/commands/utility/invite.ts | 12 +- src/commands/utility/todo.ts | 44 ++-- src/commands/utility/translate.ts | 14 +- src/events/guildCreate.ts | 32 --- src/events/guildDelete.ts | 24 --- src/events/guildMemberAdd.ts | 77 ------- src/events/ready.ts | 20 -- src/index.ts | 1 + src/modules/emoteRegistry.ts | 41 +++- src/modules/guildMemberAdd.ts | 74 +++++++ src/modules/ready.ts | 4 +- 25 files changed, 349 insertions(+), 617 deletions(-) delete mode 100644 src/commands/admin.ts delete mode 100644 src/events/guildCreate.ts delete mode 100644 src/events/guildDelete.ts delete mode 100644 src/events/guildMemberAdd.ts delete mode 100644 src/events/ready.ts create mode 100644 src/modules/guildMemberAdd.ts diff --git a/package-lock.json b/package-lock.json index 86853ab..80b5169 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "@types/inquirer": "^6.5.0", "@types/jest": "^26.0.20", "@types/mathjs": "^6.0.11", - "@types/mocha": "^8.2.0", "@types/ms": "^0.7.31", "@types/node": "^14.14.20", "@types/ws": "^7.4.0", @@ -1083,12 +1082,6 @@ "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, - "node_modules/@types/mocha": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", - "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", - "dev": true - }, "node_modules/@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", @@ -8966,12 +8959,6 @@ "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, - "@types/mocha": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", - "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", - "dev": true - }, "@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", diff --git a/package.json b/package.json index e38dcab..a48dcd5 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "@types/inquirer": "^6.5.0", "@types/jest": "^26.0.20", "@types/mathjs": "^6.0.11", - "@types/mocha": "^8.2.0", "@types/ms": "^0.7.31", "@types/node": "^14.14.20", "@types/ws": "^7.4.0", diff --git a/src/commands/admin.ts b/src/commands/admin.ts deleted file mode 100644 index 2622087..0000000 --- a/src/commands/admin.ts +++ /dev/null @@ -1,329 +0,0 @@ -import Command from "../core/command"; -import {CommonLibrary, logs, botHasPermission, clean} from "../core/lib"; -import {Config, Storage} from "../core/structures"; -import {PermissionNames, getPermissionLevel} from "../core/permissions"; -import {Permissions} from "discord.js"; -import * as discord from "discord.js"; - -function getLogBuffer(type: string) { - return { - files: [ - { - attachment: Buffer.alloc(logs[type].length, logs[type]), - name: `${Date.now()}.${type}.log` - } - ] - }; -} - -const activities = ["playing", "listening", "streaming", "watching"]; -const statuses = ["online", "idle", "dnd", "invisible"]; - -export default new Command({ - description: - "An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.", - async run($: CommonLibrary): Promise { - if (!$.member) - return $.channel.send( - "Couldn't find a member object for you! Did you make sure you used this in a server?" - ); - const permLevel = getPermissionLevel($.member); - $.channel.send( - `${$.author.toString()}, your permission level is \`${PermissionNames[permLevel]}\` (${permLevel}).` - ); - }, - subcommands: { - set: new Command({ - description: "Set different per-guild settings for the bot.", - run: "You have to specify the option you want to set.", - permission: Command.PERMISSIONS.ADMIN, - subcommands: { - prefix: new Command({ - description: "Set a custom prefix for your guild. Removes your custom prefix if none is provided.", - usage: "()", - async run($: CommonLibrary): Promise { - Storage.getGuild($.guild?.id || "N/A").prefix = null; - Storage.save(); - $.channel.send( - `The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.` - ); - }, - any: new Command({ - async run($: CommonLibrary): Promise { - Storage.getGuild($.guild?.id || "N/A").prefix = $.args[0]; - Storage.save(); - $.channel.send(`The custom prefix for this guild is now \`${$.args[0]}\`.`); - } - }) - }), - welcome: new Command({ - description: "Configure your server's welcome settings for the bot.", - usage: "type/channel <...>", - run: "You need to specify which part to modify, `type`/`channel`.", - subcommands: { - type: new Command({ - description: - "Sets how welcome messages are displayed for your server. Removes welcome messages if unspecified.", - usage: "`none`/`text`/`graphical`", - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeType = "none"; - Storage.save(); - $.channel.send("Set this server's welcome type to `none`."); - } else { - $.channel.send("You must use this command in a server."); - } - }, - // I should probably make this a bit more dynamic... Oh well. - subcommands: { - text: new Command({ - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeType = "text"; - Storage.save(); - $.channel.send("Set this server's welcome type to `text`."); - } else { - $.channel.send("You must use this command in a server."); - } - } - }), - graphical: new Command({ - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeType = "graphical"; - Storage.save(); - $.channel.send("Set this server's welcome type to `graphical`."); - } else { - $.channel.send("You must use this command in a server."); - } - } - }) - } - }), - channel: new Command({ - description: "Sets the welcome channel for your server. Type `#` to reference the channel.", - usage: "()", - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeChannel = $.channel.id; - Storage.save(); - $.channel.send( - `Successfully set ${$.channel} as the welcome channel for this server.` - ); - } else { - $.channel.send("You must use this command in a server."); - } - }, - // If/when channel types come out, this will be the perfect candidate to test it. - any: new Command({ - async run($) { - if ($.guild) { - const match = $.args[0].match(/^<#(\d{17,19})>$/); - - if (match) { - Storage.getGuild($.guild.id).welcomeChannel = match[1]; - Storage.save(); - $.channel.send( - `Successfully set this server's welcome channel to ${match[0]}.` - ); - } else { - $.channel.send( - "You must provide a reference channel. You can do this by typing `#` then searching for the proper channel." - ); - } - } else { - $.channel.send("You must use this command in a server."); - } - } - }) - }), - message: new Command({ - description: - "Sets a custom welcome message for your server. Use `%user%` as the placeholder for the user.", - usage: "()", - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeMessage = null; - Storage.save(); - $.channel.send("Reset your server's welcome message to the default."); - } else { - $.channel.send("You must use this command in a server."); - } - }, - any: new Command({ - async run($) { - if ($.guild) { - const message = $.args.join(" "); - Storage.getGuild($.guild.id).welcomeMessage = message; - Storage.save(); - $.channel.send(`Set your server's welcome message to \`${message}\`.`); - } else { - $.channel.send("You must use this command in a server."); - } - } - }) - }) - } - }) - } - }), - diag: new Command({ - description: 'Requests a debug log with the "info" verbosity level.', - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - $.channel.send(getLogBuffer("info")); - }, - any: new Command({ - description: `Select a verbosity to listen to. Available levels: \`[${Object.keys(logs).join(", ")}]\``, - async run($: CommonLibrary): Promise { - const type = $.args[0]; - - if (type in logs) $.channel.send(getLogBuffer(type)); - else - $.channel.send( - `Couldn't find a verbosity level named \`${type}\`! The available types are \`[${Object.keys( - logs - ).join(", ")}]\`.` - ); - } - }) - }), - status: new Command({ - description: "Changes the bot's status.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - $.channel.send("Setting status to `online`..."); - }, - any: new Command({ - description: `Select a status to set to. Available statuses: \`[${statuses.join(", ")}]\`.`, - async run($: CommonLibrary): Promise { - if (!statuses.includes($.args[0])) return $.channel.send("That status doesn't exist!"); - else { - $.client.user?.setStatus($.args[0]); - $.channel.send(`Setting status to \`${$.args[0]}\`...`); - } - } - }) - }), - purge: new Command({ - description: "Purges bot messages.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - if ($.message.channel instanceof discord.DMChannel) { - return; - } - $.message.delete(); - const msgs = await $.channel.messages.fetch({ - limit: 100 - }); - const travMessages = msgs.filter((m) => m.author.id === $.client.user?.id); - - await $.message.channel.send(`Found ${travMessages.size} messages to delete.`).then((m) => - m.delete({ - timeout: 5000 - }) - ); - await $.message.channel.bulkDelete(travMessages); - } - }), - clear: new Command({ - description: "Clears a given amount of messages.", - usage: "", - run: "A number was not provided.", - number: new Command({ - description: "Amount of messages to delete.", - async run($: CommonLibrary): Promise { - if ($.channel.type === "dm") { - await $.channel.send("Can't clear messages in the DMs!"); - return; - } - $.message.delete(); - const fetched = await $.channel.messages.fetch({ - limit: $.args[0] - }); - await $.channel.bulkDelete(fetched); - } - }) - }), - eval: new Command({ - description: "Evaluate code.", - usage: "", - permission: Command.PERMISSIONS.BOT_OWNER, - // You have to bring everything into scope to use them. AFAIK, there isn't a more maintainable way to do this, but at least TS will let you know if anything gets removed. - async run({args, author, channel, client, guild, member, message}): Promise { - try { - const code = args.join(" "); - let evaled = eval(code); - - if (typeof evaled !== "string") evaled = require("util").inspect(evaled); - channel.send(clean(evaled), {code: "js", split: true}); - } catch (err) { - channel.send(`\`ERROR\` \`\`\`js\n${clean(err)}\n\`\`\``); - } - } - }), - nick: new Command({ - description: "Change the bot's nickname.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - const nickName = $.args.join(" "); - await $.guild?.me?.setNickname(nickName); - if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES)) - $.message.delete({timeout: 5000}).catch($.handler.bind($)); - $.channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000})); - } - }), - guilds: new Command({ - description: "Shows a list of all guilds the bot is a member of.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - const guildList = $.client.guilds.cache.array().map((e) => e.name); - $.channel.send(guildList); - } - }), - activity: new Command({ - description: "Set the activity of the bot.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - usage: " ", - async run($: CommonLibrary): Promise { - $.client.user?.setActivity(".help", { - type: "LISTENING" - }); - $.channel.send("Activity set to default."); - }, - any: new Command({ - description: `Select an activity type to set. Available levels: \`[${activities.join(", ")}]\``, - async run($: CommonLibrary): Promise { - const type = $.args[0]; - - if (activities.includes(type)) { - $.client.user?.setActivity($.args.slice(1).join(" "), { - type: $.args[0].toUpperCase() - }); - $.channel.send( - `Set activity to \`${$.args[0].toUpperCase()}\` \`${$.args.slice(1).join(" ")}\`.` - ); - } else - $.channel.send( - `Couldn't find an activity type named \`${type}\`! The available types are \`[${activities.join( - ", " - )}]\`.` - ); - } - }) - }), - syslog: new Command({ - description: "Sets up the current channel to receive system logs.", - permission: Command.PERMISSIONS.BOT_ADMIN, - async run($) { - if ($.guild) { - Config.systemLogsChannel = $.channel.id; - Config.save(); - $.channel.send(`Successfully set ${$.channel} as the system logs channel.`); - } else { - $.channel.send("DM system log channels aren't supported."); - } - } - }) - } -}); diff --git a/src/commands/fun/figlet.ts b/src/commands/fun/figlet.ts index ded3d94..b78ba89 100644 --- a/src/commands/fun/figlet.ts +++ b/src/commands/fun/figlet.ts @@ -1,15 +1,15 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import figlet from "figlet"; -export default new Command({ +export default new NamedCommand({ description: "Generates a figlet of your input.", - async run($) { - const input = $.args.join(" "); - if (!$.args[0]) { - $.channel.send("You have to provide input for me to create a figlet!"); + async run({message, channel, guild, author, member, client, args}) { + const input = args.join(" "); + if (!args[0]) { + channel.send("You have to provide input for me to create a figlet!"); return; } - $.channel.send( + channel.send( "```" + figlet.textSync(`${input}`, { horizontalLayout: "full" diff --git a/src/commands/fun/insult.ts b/src/commands/fun/insult.ts index c2e0260..63847fd 100644 --- a/src/commands/fun/insult.ts +++ b/src/commands/fun/insult.ts @@ -1,14 +1,14 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Insult TravBot! >:D", - async run($) { - $.channel.startTyping(); + async run({message, channel, guild, author, member, client, args}) { + channel.startTyping(); setTimeout(() => { - $.channel.send( - `${$.author.toString()} What the fuck did you just fucking say about me, you little bitch? I'll have you know I graduated top of my class in the Navy Seals, and I've been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I'm the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You're fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that's just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little "clever" comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn't, you didn't, and now you're paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You're fucking dead, kiddo.` + channel.send( + `${author} What the fuck did you just fucking say about me, you little bitch? I'll have you know I graduated top of my class in the Navy Seals, and I've been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I'm the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You're fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that's just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little "clever" comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn't, you didn't, and now you're paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You're fucking dead, kiddo.` ); - $.channel.stopTyping(); + channel.stopTyping(); }, 60000); } }); diff --git a/src/commands/fun/love.ts b/src/commands/fun/love.ts index d0f9a5a..94467c5 100644 --- a/src/commands/fun/love.ts +++ b/src/commands/fun/love.ts @@ -1,13 +1,13 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Chooses someone to love.", - async run($) { - if ($.guild) { - const member = $.guild.members.cache.random(); - $.channel.send(`I love ${member.user.username}!`); + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const member = guild.members.cache.random(); + channel.send(`I love ${member.user.username}!`); } else { - $.channel.send("You must use this command in a guild!"); + channel.send("You must use this command in a guild!"); } } }); diff --git a/src/commands/fun/ravi.ts b/src/commands/fun/ravi.ts index fbcf14e..7b408f6 100644 --- a/src/commands/fun/ravi.ts +++ b/src/commands/fun/ravi.ts @@ -1,11 +1,11 @@ -import Command from "../../core/command"; -import {Random} from "../../core/lib"; +import {Command, NamedCommand} from "../../core"; +import {Random} from "../../lib"; -export default new Command({ +export default new NamedCommand({ description: "Ravioli ravioli...", usage: "[number from 1 to 9]", - async run($) { - $.channel.send({ + async run({message, channel, guild, author, member, client, args}) { + channel.send({ embed: { title: "Ravioli ravioli...", image: { @@ -18,11 +18,11 @@ export default new Command({ }); }, number: new Command({ - async run($) { - const arg: number = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + const arg: number = args[0]; if (arg >= 1 && arg <= 9) { - $.channel.send({ + channel.send({ embed: { title: "Ravioli ravioli...", image: { @@ -31,7 +31,7 @@ export default new Command({ } }); } else { - $.channel.send("Please provide a number between 1 and 9."); + channel.send("Please provide a number between 1 and 9."); } } }) diff --git a/src/commands/fun/thonk.ts b/src/commands/fun/thonk.ts index a09c6e4..762549e 100644 --- a/src/commands/fun/thonk.ts +++ b/src/commands/fun/thonk.ts @@ -1,4 +1,4 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; const letters: {[letter: string]: string[]} = { a: "aáàảãạâấầẩẫậăắằẳẵặ".split(""), @@ -31,11 +31,11 @@ function transform(str: string) { let phrase = "I have no currently set phrase!"; -export default new Command({ +export default new NamedCommand({ description: "Transforms your text into vietnamese.", usage: "thonk ([text])", - async run($) { - if ($.args.length > 0) phrase = $.args.join(" "); - $.channel.send(transform(phrase)); + async run({message, channel, guild, author, member, client, args}) { + if (args.length > 0) phrase = args.join(" "); + channel.send(transform(phrase)); } }); diff --git a/src/commands/fun/urban.ts b/src/commands/fun/urban.ts index 1f21c2e..2901433 100644 --- a/src/commands/fun/urban.ts +++ b/src/commands/fun/urban.ts @@ -1,16 +1,16 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import {MessageEmbed} from "discord.js"; // Anycasting Alert const urban = require("relevant-urban"); -export default new Command({ +export default new NamedCommand({ description: "Gives you a definition of the inputted word.", - async run($) { - if (!$.args[0]) { - $.channel.send("Please input a word."); + async run({message, channel, guild, author, member, client, args}) { + if (!args[0]) { + channel.send("Please input a word."); } - const res = await urban($.args.join(" ")).catch((e: Error) => { - return $.channel.send("Sorry, that word was not found."); + const res = await urban(args.join(" ")).catch((e: Error) => { + return channel.send("Sorry, that word was not found."); }); const embed = new MessageEmbed() .setColor(0x1d2439) @@ -22,6 +22,6 @@ export default new Command({ if (res.tags.length > 0 && res.tags.join(" ").length < 1024) { embed.addField("Tags", res.tags.join(", "), true); } - $.channel.send(embed); + channel.send(embed); } }); diff --git a/src/commands/fun/weather.ts b/src/commands/fun/weather.ts index aaac509..d232ea3 100644 --- a/src/commands/fun/weather.ts +++ b/src/commands/fun/weather.ts @@ -1,22 +1,22 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import {MessageEmbed} from "discord.js"; // Anycasting Alert const weather = require("weather-js"); -export default new Command({ +export default new NamedCommand({ description: "Shows weather info of specified location.", - async run($) { - if ($.args.length == 0) { - $.channel.send("You need to provide a city."); + async run({message, channel, guild, author, member, client, args}) { + if (args.length == 0) { + channel.send("You need to provide a city."); return; } weather.find( { - search: $.args.join(" "), + search: args.join(" "), degreeType: "C" }, function (err: any, result: any) { - if (err) $.channel.send(err); + if (err) channel.send(err); var current = result[0].current; var location = result[0].location; const embed = new MessageEmbed() @@ -30,7 +30,7 @@ export default new Command({ .addField("Feels like", `${current.feelslike} Degrees`, true) .addField("Winds", current.winddisplay, true) .addField("Humidity", `${current.humidity}%`, true); - $.channel.send({ + channel.send({ embed }); } diff --git a/src/commands/fun/whois.ts b/src/commands/fun/whois.ts index ed7e07e..c8daff2 100644 --- a/src/commands/fun/whois.ts +++ b/src/commands/fun/whois.ts @@ -1,5 +1,5 @@ import {User} from "discord.js"; -import Command from "../../core/command"; +import {Command, NamedCommand, getMemberByUsername} from "../../core"; // Quotes must be used here or the numbers will change const registry: {[id: string]: string} = { @@ -34,49 +34,49 @@ const registry: {[id: string]: string} = { "Some random conlanger, worldbuilder and programmer doofus. ~~May also secretly be a nyan. :3~~" }; -export default new Command({ +export default new NamedCommand({ description: "Tells you who you or the specified user is.", aliases: ["whoami"], - async run($) { - const id = $.author.id; + async run({message, channel, guild, author, member, client, args}) { + const id = author.id; if (id in registry) { - $.channel.send(registry[id]); + channel.send(registry[id]); } else { - $.channel.send("You haven't been added to the registry yet!"); + channel.send("You haven't been added to the registry yet!"); } }, user: new Command({ - async run($) { - const user: User = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + const user: User = args[0]; const id = user.id; if (id in registry) { - $.channel.send(`\`${user.username}\` - ${registry[id]}`); + channel.send(`\`${user.username}\` - ${registry[id]}`); } else { - $.channel.send(`\`${user.username}#${user.discriminator}\` hasn't been added to the registry yet!`); + channel.send(`\`${user.username}#${user.discriminator}\` hasn't been added to the registry yet!`); } } }), any: new Command({ - async run($) { - if ($.guild) { - const query: string = $.args.join(" "); - const member = await $.getMemberByUsername($.guild, query); + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const query: string = args.join(" "); + const member = await getMemberByUsername(guild, query); if (member && member.id in registry) { const id = member.id; if (id in registry) { - $.channel.send(`\`${member.user.username}\` - ${registry[member.id]}`); + channel.send(`\`${member.user.username}\` - ${registry[member.id]}`); } else { - $.channel.send(`\`${member.user.username}\` hasn't been added to the registry yet!`); + channel.send(`\`${member.user.username}\` hasn't been added to the registry yet!`); } } else { - $.channel.send(`Couldn't find a user by the name of \`${query}\`!`); + channel.send(`Couldn't find a user by the name of \`${query}\`!`); } } else { - $.channel.send( + channel.send( "You must run this in a guild! (*If you have the user's ID, you don't have to be in a guild.*)" ); } diff --git a/src/commands/system/admin.ts b/src/commands/system/admin.ts index b2da0bb..7aeebc2 100644 --- a/src/commands/system/admin.ts +++ b/src/commands/system/admin.ts @@ -50,6 +50,113 @@ export default new NamedCommand({ channel.send(`The custom prefix for this guild is now \`${args[0]}\`.`); } }) + }), + welcome: new NamedCommand({ + description: "Configure your server's welcome settings for the bot.", + usage: "type/channel <...>", + run: "You need to specify which part to modify, `type`/`channel`.", + subcommands: { + type: new NamedCommand({ + description: + "Sets how welcome messages are displayed for your server. Removes welcome messages if unspecified.", + usage: "`none`/`text`/`graphical`", + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeType = "none"; + Storage.save(); + channel.send("Set this server's welcome type to `none`."); + } else { + channel.send("You must use this command in a server."); + } + }, + // I should probably make this a bit more dynamic... Oh well. + subcommands: { + text: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeType = "text"; + Storage.save(); + channel.send("Set this server's welcome type to `text`."); + } else { + channel.send("You must use this command in a server."); + } + } + }), + graphical: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeType = "graphical"; + Storage.save(); + channel.send("Set this server's welcome type to `graphical`."); + } else { + channel.send("You must use this command in a server."); + } + } + }) + } + }), + channel: new NamedCommand({ + description: "Sets the welcome channel for your server. Type `#` to reference the channel.", + usage: "()", + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeChannel = channel.id; + Storage.save(); + channel.send(`Successfully set ${channel} as the welcome channel for this server.`); + } else { + channel.send("You must use this command in a server."); + } + }, + // If/when channel types come out, this will be the perfect candidate to test it. + any: new Command({ + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const match = args[0].match(/^<#(\d{17,19})>$/); + + if (match) { + Storage.getGuild(guild.id).welcomeChannel = match[1]; + Storage.save(); + channel.send( + `Successfully set this server's welcome channel to ${match[0]}.` + ); + } else { + channel.send( + "You must provide a reference channel. You can do this by typing `#` then searching for the proper channel." + ); + } + } else { + channel.send("You must use this command in a server."); + } + } + }) + }), + message: new NamedCommand({ + description: + "Sets a custom welcome message for your server. Use `%user%` as the placeholder for the user.", + usage: "()", + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeMessage = null; + Storage.save(); + channel.send("Reset your server's welcome message to the default."); + } else { + channel.send("You must use this command in a server."); + } + }, + any: new Command({ + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const message = args.join(" "); + Storage.getGuild(guild.id).welcomeMessage = message; + Storage.save(); + channel.send(`Set your server's welcome message to \`${message}\`.`); + } else { + channel.send("You must use this command in a server."); + } + } + }) + }) + } }) } }), @@ -197,6 +304,19 @@ export default new NamedCommand({ ); } }) + }), + syslog: new NamedCommand({ + description: "Sets up the current channel to receive system logs.", + permission: PERMISSIONS.BOT_ADMIN, + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Config.systemLogsChannel = channel.id; + Config.save(); + channel.send(`Successfully set ${channel} as the system logs channel.`); + } else { + channel.send("DM system log channels aren't supported."); + } + } }) } }); diff --git a/src/commands/utility/calc.ts b/src/commands/utility/calc.ts index a3fe45e..2303351 100644 --- a/src/commands/utility/calc.ts +++ b/src/commands/utility/calc.ts @@ -1,26 +1,26 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import * as math from "mathjs"; import {MessageEmbed} from "discord.js"; -export default new Command({ +export default new NamedCommand({ description: "Calculates a specified math expression.", - async run($) { - if (!$.args[0]) { - $.channel.send("Please provide a calculation."); + async run({message, channel, guild, author, member, client, args}) { + if (!args[0]) { + channel.send("Please provide a calculation."); return; } let resp; try { - resp = math.evaluate($.args.join(" ")); + resp = math.evaluate(args.join(" ")); } catch (e) { - $.channel.send("Please provide a *valid* calculation."); + channel.send("Please provide a *valid* calculation."); return; } const embed = new MessageEmbed() .setColor(0xffffff) .setTitle("Math Calculation") - .addField("Input", `\`\`\`js\n${$.args.join("")}\`\`\``) + .addField("Input", `\`\`\`js\n${args.join("")}\`\`\``) .addField("Output", `\`\`\`js\n${resp}\`\`\``); - $.channel.send(embed); + channel.send(embed); } }); diff --git a/src/commands/utility/code.ts b/src/commands/utility/code.ts index b473b57..39e41b7 100644 --- a/src/commands/utility/code.ts +++ b/src/commands/utility/code.ts @@ -1,6 +1,6 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Gives you the Github link.", run: "https://github.com/keanuplayz/TravBot-v3" }); diff --git a/src/commands/utility/invite.ts b/src/commands/utility/invite.ts index 7f1fc94..5d5657c 100644 --- a/src/commands/utility/invite.ts +++ b/src/commands/utility/invite.ts @@ -1,11 +1,11 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Gives you the invite link.", - async run($) { - $.channel.send( - `https://discordapp.com/api/oauth2/authorize?client_id=${$.client.user!.id}&permissions=${ - $.args[0] || 8 + async run({message, channel, guild, author, member, client, args}) { + channel.send( + `https://discordapp.com/api/oauth2/authorize?client_id=${client.user!.id}&permissions=${ + args[0] || 8 }&scope=bot` ); } diff --git a/src/commands/utility/todo.ts b/src/commands/utility/todo.ts index 1f6c639..594610b 100644 --- a/src/commands/utility/todo.ts +++ b/src/commands/utility/todo.ts @@ -1,13 +1,13 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import moment from "moment"; -import {Storage} from "../../core/structures"; +import {Storage} from "../../structures"; import {MessageEmbed} from "discord.js"; -export default new Command({ +export default new NamedCommand({ description: "Keep and edit your personal todo list.", - async run($) { - const user = Storage.getUser($.author.id); - const embed = new MessageEmbed().setTitle(`Todo list for ${$.author.tag}`).setColor("BLUE"); + async run({message, channel, guild, author, member, client, args}) { + const user = Storage.getUser(author.id); + const embed = new MessageEmbed().setTitle(`Todo list for ${author.tag}`).setColor("BLUE"); for (const timestamp in user.todoList) { const date = new Date(Number(timestamp)); @@ -17,23 +17,23 @@ export default new Command({ ); } - $.channel.send(embed); + channel.send(embed); }, subcommands: { - add: new Command({ - async run($) { - const user = Storage.getUser($.author.id); - const note = $.args.join(" "); + add: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + const user = Storage.getUser(author.id); + const note = args.join(" "); user.todoList[Date.now().toString()] = note; console.debug(user.todoList); Storage.save(); - $.channel.send(`Successfully added \`${note}\` to your todo list.`); + channel.send(`Successfully added \`${note}\` to your todo list.`); } }), - remove: new Command({ - async run($) { - const user = Storage.getUser($.author.id); - const note = $.args.join(" "); + remove: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + const user = Storage.getUser(author.id); + const note = args.join(" "); let isFound = false; for (const timestamp in user.todoList) { @@ -43,19 +43,19 @@ export default new Command({ delete user.todoList[timestamp]; Storage.save(); isFound = true; - $.channel.send(`Removed \`${note}\` from your todo list.`); + channel.send(`Removed \`${note}\` from your todo list.`); } } - if (!isFound) $.channel.send("That item couldn't be found."); + if (!isFound) channel.send("That item couldn't be found."); } }), - clear: new Command({ - async run($) { - const user = Storage.getUser($.author.id); + clear: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + const user = Storage.getUser(author.id); user.todoList = {}; Storage.save(); - $.channel.send("Cleared todo list."); + channel.send("Cleared todo list."); } }) } diff --git a/src/commands/utility/translate.ts b/src/commands/utility/translate.ts index 2e56087..b697855 100644 --- a/src/commands/utility/translate.ts +++ b/src/commands/utility/translate.ts @@ -1,18 +1,18 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; // Anycasting Alert const translate = require("translate-google"); -export default new Command({ +export default new NamedCommand({ description: "Translates your input.", usage: " ", - async run($) { - const lang = $.args[0]; - const input = $.args.slice(1).join(" "); + async run({message, channel, guild, author, member, client, args}) { + const lang = args[0]; + const input = args.slice(1).join(" "); translate(input, { to: lang }) .then((res: any) => { - $.channel.send({ + channel.send({ embed: { title: "Translation", fields: [ @@ -30,7 +30,7 @@ export default new Command({ }) .catch((err: any) => { console.error(err); - $.channel.send( + channel.send( `${err}\nPlease use the following list: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes` ); }); diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts deleted file mode 100644 index 02189b3..0000000 --- a/src/events/guildCreate.ts +++ /dev/null @@ -1,32 +0,0 @@ -import Event from "../core/event"; -import $ from "../core/lib"; -import {updateGlobalEmoteRegistry} from "../core/lib"; -import {client} from "../index"; -import {Config} from "../core/structures"; -import {TextChannel} from "discord.js"; - -export default new Event<"guildCreate">({ - on(guild) { - $.log( - `[GUILD JOIN] ${guild.name} (${guild.id}) added the bot. Owner: ${guild.owner!.user.tag} (${ - guild.owner!.user.id - }). Updated emote registry.` - ); - - if (Config.systemLogsChannel) { - const channel = client.channels.cache.get(Config.systemLogsChannel); - - if (channel && channel.type === "text") { - (channel as TextChannel).send( - `TravBot joined: \`${guild.name}\`. The owner of this guild is: \`${guild.owner!.user.tag}\` (\`${ - guild.owner!.user.id - }\`)` - ); - } else { - console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); - } - } - - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/guildDelete.ts b/src/events/guildDelete.ts deleted file mode 100644 index 41c656f..0000000 --- a/src/events/guildDelete.ts +++ /dev/null @@ -1,24 +0,0 @@ -import Event from "../core/event"; -import $ from "../core/lib"; -import {updateGlobalEmoteRegistry} from "../core/lib"; -import {client} from "../index"; -import {Config} from "../core/structures"; -import {TextChannel} from "discord.js"; - -export default new Event<"guildDelete">({ - on(guild) { - $.log(`[GUILD LEAVE] ${guild.name} (${guild.id}) removed the bot. Updated emote registry.`); - - if (Config.systemLogsChannel) { - const channel = client.channels.cache.get(Config.systemLogsChannel); - - if (channel && channel.type === "text") { - (channel as TextChannel).send(`\`${guild.name}\` (\`${guild.id}\`) removed the bot.`); - } else { - console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); - } - } - - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/guildMemberAdd.ts b/src/events/guildMemberAdd.ts deleted file mode 100644 index 1855094..0000000 --- a/src/events/guildMemberAdd.ts +++ /dev/null @@ -1,77 +0,0 @@ -import Event from "../core/event"; -import $, {parseVars} from "../core/lib"; -import {createCanvas, loadImage, Canvas} from "canvas"; -import {Storage} from "../core/structures"; -import {TextChannel, MessageAttachment} from "discord.js"; - -function applyText(canvas: Canvas, text: string) { - const ctx = canvas.getContext("2d"); - let fontSize = 70; - - do { - ctx.font = `${(fontSize -= 10)}px sans-serif`; - } while (ctx.measureText(text).width > canvas.width - 300); - - return ctx.font; -} - -export default new Event<"guildMemberAdd">({ - async on(member) { - const {welcomeType, welcomeChannel, welcomeMessage} = Storage.getGuild(member.guild.id); - - if (welcomeChannel) { - const channel = member.guild.channels.cache.get(welcomeChannel); - - if (channel && channel.type === "text") { - if (welcomeType === "graphical") { - const canvas = createCanvas(700, 250); - const ctx = canvas.getContext("2d"); - const background = await loadImage( - "https://raw.githubusercontent.com/keanuplayz/TravBot/dev/assets/wallpaper.png" - ); - ctx.drawImage(background, 0, 0, canvas.width, canvas.height); - - ctx.strokeStyle = "#74037b"; - ctx.strokeRect(0, 0, canvas.width, canvas.height); - - ctx.font = "28px sans-serif"; - ctx.fillStyle = "#ffffff"; - ctx.fillText("Welcome to the server,", canvas.width / 2.5, canvas.height / 3.5); - - ctx.font = applyText(canvas, member.displayName); - ctx.fillStyle = "#ffffff"; - ctx.fillText(`${member.displayName}!`, canvas.width / 2.5, canvas.height / 1.5); - - ctx.beginPath(); - ctx.arc(125, 125, 100, 0, Math.PI * 2, true); - ctx.closePath(); - ctx.clip(); - - const avatarURL = - member.user.avatarURL({ - dynamic: true, - size: 2048, - format: "png" - }) ?? member.user.defaultAvatarURL; - const avatar = await loadImage(avatarURL); - ctx.drawImage(avatar, 25, 25, 200, 200); - - const attachment = new MessageAttachment(canvas.toBuffer("image/png"), "welcome-image.png"); - (channel as TextChannel).send(`Welcome \`${member.user.tag}\`!`, attachment); - } else if (welcomeType === "text") { - (channel as TextChannel).send( - parseVars( - welcomeMessage || - "Say hello to `%user%`, everyone! We all need a warm welcome sometimes :D", - { - user: member.user.tag - } - ) - ); - } - } else { - $.error(`"${welcomeChannel}" is not a valid text channel ID!`); - } - } - } -}); diff --git a/src/events/ready.ts b/src/events/ready.ts deleted file mode 100644 index b8467aa..0000000 --- a/src/events/ready.ts +++ /dev/null @@ -1,20 +0,0 @@ -import Event from "../core/event"; -import {client} from "../index"; -import $ from "../core/lib"; -import {Config} from "../core/structures"; -import {updateGlobalEmoteRegistry} from "../core/lib"; - -export default new Event<"ready">({ - once() { - if (client.user) { - $.ready( - `Logged in as ${client.user.tag}, ready to serve ${client.users.cache.size} users in ${client.guilds.cache.size} servers..` - ); - client.user.setActivity({ - type: "LISTENING", - name: `${Config.prefix}help` - }); - } - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/index.ts b/src/index.ts index 21633ce..67cacb5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -69,3 +69,4 @@ import "./modules/emoteRegistry"; import "./modules/channelListener"; import "./modules/intercept"; import "./modules/messageEmbed"; +import "./modules/guildMemberAdd"; diff --git a/src/modules/emoteRegistry.ts b/src/modules/emoteRegistry.ts index a3de8bc..88ab099 100644 --- a/src/modules/emoteRegistry.ts +++ b/src/modules/emoteRegistry.ts @@ -1,6 +1,7 @@ import {client} from "../index"; import FileManager from "./storage"; -import {EmoteRegistryDump} from "../structures"; +import {EmoteRegistryDump, Config} from "../structures"; +import {TextChannel} from "discord.js"; function updateGlobalEmoteRegistry(): void { const data: EmoteRegistryDump = {version: 1, list: []}; @@ -38,13 +39,43 @@ client.on("emojiUpdate", () => { updateGlobalEmoteRegistry(); }); -client.on("guildCreate", () => { - console.log("Updated emote registry."); +client.on("guildCreate", (guild) => { + console.log( + `[GUILD JOIN] ${guild.name} (${guild.id}) added the bot. Owner: ${guild.owner!.user.tag} (${ + guild.owner!.user.id + }). Updated emote registry.` + ); + + if (Config.systemLogsChannel) { + const channel = client.channels.cache.get(Config.systemLogsChannel); + + if (channel && channel.type === "text") { + (channel as TextChannel).send( + `TravBot joined: \`${guild.name}\`. The owner of this guild is: \`${guild.owner!.user.tag}\` (\`${ + guild.owner!.user.id + }\`)` + ); + } else { + console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); + } + } + updateGlobalEmoteRegistry(); }); -client.on("guildDelete", () => { - console.log("Updated emote registry."); +client.on("guildDelete", (guild) => { + console.log(`[GUILD LEAVE] ${guild.name} (${guild.id}) removed the bot. Updated emote registry.`); + + if (Config.systemLogsChannel) { + const channel = client.channels.cache.get(Config.systemLogsChannel); + + if (channel && channel.type === "text") { + (channel as TextChannel).send(`\`${guild.name}\` (\`${guild.id}\`) removed the bot.`); + } else { + console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); + } + } + updateGlobalEmoteRegistry(); }); diff --git a/src/modules/guildMemberAdd.ts b/src/modules/guildMemberAdd.ts new file mode 100644 index 0000000..9a26ffd --- /dev/null +++ b/src/modules/guildMemberAdd.ts @@ -0,0 +1,74 @@ +import {createCanvas, loadImage, Canvas} from "canvas"; +import {TextChannel, MessageAttachment} from "discord.js"; +import {parseVars} from "../lib"; +import {Storage} from "../structures"; +import {client} from "../index"; + +function applyText(canvas: Canvas, text: string) { + const ctx = canvas.getContext("2d"); + let fontSize = 70; + + do { + ctx.font = `${(fontSize -= 10)}px sans-serif`; + } while (ctx.measureText(text).width > canvas.width - 300); + + return ctx.font; +} + +client.on("guildMemberAdd", async (member) => { + const {welcomeType, welcomeChannel, welcomeMessage} = Storage.getGuild(member.guild.id); + + if (welcomeChannel) { + const channel = member.guild.channels.cache.get(welcomeChannel); + + if (channel && channel.type === "text") { + if (welcomeType === "graphical") { + const canvas = createCanvas(700, 250); + const ctx = canvas.getContext("2d"); + const background = await loadImage( + "https://raw.githubusercontent.com/keanuplayz/TravBot/dev/assets/wallpaper.png" + ); + ctx.drawImage(background, 0, 0, canvas.width, canvas.height); + + ctx.strokeStyle = "#74037b"; + ctx.strokeRect(0, 0, canvas.width, canvas.height); + + ctx.font = "28px sans-serif"; + ctx.fillStyle = "#ffffff"; + ctx.fillText("Welcome to the server,", canvas.width / 2.5, canvas.height / 3.5); + + ctx.font = applyText(canvas, member.displayName); + ctx.fillStyle = "#ffffff"; + ctx.fillText(`${member.displayName}!`, canvas.width / 2.5, canvas.height / 1.5); + + ctx.beginPath(); + ctx.arc(125, 125, 100, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.clip(); + + const avatarURL = + member.user.avatarURL({ + dynamic: true, + size: 2048, + format: "png" + }) ?? member.user.defaultAvatarURL; + const avatar = await loadImage(avatarURL); + ctx.drawImage(avatar, 25, 25, 200, 200); + + const attachment = new MessageAttachment(canvas.toBuffer("image/png"), "welcome-image.png"); + (channel as TextChannel).send(`Welcome \`${member.user.tag}\`!`, attachment); + } else if (welcomeType === "text") { + (channel as TextChannel).send( + parseVars( + welcomeMessage || "Say hello to `%user%`, everyone! We all need a warm welcome sometimes :D", + { + user: member.user.tag + } + ) + ); + } + } else { + console.error(`"${welcomeChannel}" is not a valid text channel ID!`); + } + } +}); diff --git a/src/modules/ready.ts b/src/modules/ready.ts index 9c3c86b..eac1fa0 100644 --- a/src/modules/ready.ts +++ b/src/modules/ready.ts @@ -3,7 +3,9 @@ import {Config} from "../structures"; client.once("ready", () => { if (client.user) { - console.ready(`Logged in as ${client.user.tag}.`); + console.ready( + `Logged in as ${client.user.tag}, ready to serve ${client.users.cache.size} users in ${client.guilds.cache.size} servers..` + ); client.user.setActivity({ type: "LISTENING", name: `${Config.prefix}help` From db3320365711808c3bfb6427ea0f14db50e38316 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:15:17 -0500 Subject: [PATCH 24/30] Cleaned up guild checks and return statements --- src/commands/fun/cookie.ts | 8 +- src/commands/fun/eco.ts | 1 + src/commands/fun/figlet.ts | 7 +- src/commands/fun/love.ts | 13 +-- src/commands/fun/modules/eco-core.ts | 1 + src/commands/fun/neko.ts | 9 +- src/commands/fun/weather.ts | 7 +- src/commands/fun/whois.ts | 32 +++--- src/commands/system/admin.ts | 128 +++++++++-------------- src/commands/utility/calc.ts | 10 +- src/commands/utility/desc.ts | 20 +--- src/commands/utility/info.ts | 146 ++++++++++++++------------- src/commands/utility/scanemotes.ts | 23 ++--- src/commands/utility/time.ts | 1 + 14 files changed, 174 insertions(+), 232 deletions(-) diff --git a/src/commands/fun/cookie.ts b/src/commands/fun/cookie.ts index 50b1b3d..69a624a 100644 --- a/src/commands/fun/cookie.ts +++ b/src/commands/fun/cookie.ts @@ -36,18 +36,16 @@ export default new NamedCommand({ } }) }, + id: "user", user: new Command({ description: "User to give cookie to.", async run({message, channel, guild, author, member, client, args}) { const sender = author; const mention: User = args[0]; - if (mention.id == sender.id) { - channel.send("You can't give yourself cookies!"); - return; - } + if (mention.id == sender.id) return channel.send("You can't give yourself cookies!"); - channel.send( + return channel.send( `:cookie: <@${sender.id}> ${parseVars(random(cookies), { target: mention.toString() })}` diff --git a/src/commands/fun/eco.ts b/src/commands/fun/eco.ts index 37c288e..da7d97a 100644 --- a/src/commands/fun/eco.ts +++ b/src/commands/fun/eco.ts @@ -18,6 +18,7 @@ export default new NamedCommand({ shop: ShopCommand, monday: MondayCommand }, + id: "user", user: new Command({ description: "See how much money someone else has by using their user ID or pinging them.", async run({guild, channel, args}) { diff --git a/src/commands/fun/figlet.ts b/src/commands/fun/figlet.ts index b78ba89..439ce00 100644 --- a/src/commands/fun/figlet.ts +++ b/src/commands/fun/figlet.ts @@ -5,11 +5,8 @@ export default new NamedCommand({ description: "Generates a figlet of your input.", async run({message, channel, guild, author, member, client, args}) { const input = args.join(" "); - if (!args[0]) { - channel.send("You have to provide input for me to create a figlet!"); - return; - } - channel.send( + if (!args[0]) return channel.send("You have to provide input for me to create a figlet!"); + return channel.send( "```" + figlet.textSync(`${input}`, { horizontalLayout: "full" diff --git a/src/commands/fun/love.ts b/src/commands/fun/love.ts index 94467c5..bf42d0d 100644 --- a/src/commands/fun/love.ts +++ b/src/commands/fun/love.ts @@ -1,13 +1,10 @@ -import {Command, NamedCommand} from "../../core"; +import {Command, NamedCommand, CHANNEL_TYPE} from "../../core"; export default new NamedCommand({ description: "Chooses someone to love.", - async run({message, channel, guild, author, member, client, args}) { - if (guild) { - const member = guild.members.cache.random(); - channel.send(`I love ${member.user.username}!`); - } else { - channel.send("You must use this command in a guild!"); - } + channelType: CHANNEL_TYPE.GUILD, + async run({message, channel, guild, author, client, args}) { + const member = guild!.members.cache.random(); + channel.send(`I love ${member.nickname ?? member.user.username}!`); } }); diff --git a/src/commands/fun/modules/eco-core.ts b/src/commands/fun/modules/eco-core.ts index b1966f2..6ed9628 100644 --- a/src/commands/fun/modules/eco-core.ts +++ b/src/commands/fun/modules/eco-core.ts @@ -112,6 +112,7 @@ export const PayCommand = new NamedCommand({ description: "Send money to someone.", usage: " ", run: "Who are you sending this money to?", + id: "user", user: new Command({ run: "You need to enter an amount you're sending!", number: new Command({ diff --git a/src/commands/fun/neko.ts b/src/commands/fun/neko.ts index 46d0bfc..18cfe76 100644 --- a/src/commands/fun/neko.ts +++ b/src/commands/fun/neko.ts @@ -45,15 +45,10 @@ export default new NamedCommand({ description: "Image type to send.", async run({message, channel, guild, author, member, client, args}) { const arg = args[0]; - - if (!(arg in endpoints.sfw)) { - channel.send("Couldn't find that endpoint!"); - return; - } - + if (!(arg in endpoints.sfw)) return channel.send("Couldn't find that endpoint!"); let url = new URL(`https://nekos.life/api/v2${endpoints.sfw[arg]}`); const content = await getContent(url.toString()); - channel.send(content.url); + return channel.send(content.url); } }) }); diff --git a/src/commands/fun/weather.ts b/src/commands/fun/weather.ts index d232ea3..590755f 100644 --- a/src/commands/fun/weather.ts +++ b/src/commands/fun/weather.ts @@ -6,11 +6,8 @@ const weather = require("weather-js"); export default new NamedCommand({ description: "Shows weather info of specified location.", async run({message, channel, guild, author, member, client, args}) { - if (args.length == 0) { - channel.send("You need to provide a city."); - return; - } - weather.find( + if (args.length == 0) return channel.send("You need to provide a city."); + return weather.find( { search: args.join(" "), degreeType: "C" diff --git a/src/commands/fun/whois.ts b/src/commands/fun/whois.ts index c8daff2..a65a064 100644 --- a/src/commands/fun/whois.ts +++ b/src/commands/fun/whois.ts @@ -1,5 +1,5 @@ import {User} from "discord.js"; -import {Command, NamedCommand, getMemberByUsername} from "../../core"; +import {Command, NamedCommand, getMemberByUsername, CHANNEL_TYPE} from "../../core"; // Quotes must be used here or the numbers will change const registry: {[id: string]: string} = { @@ -46,6 +46,7 @@ export default new NamedCommand({ channel.send("You haven't been added to the registry yet!"); } }, + id: "user", user: new Command({ async run({message, channel, guild, author, member, client, args}) { const user: User = args[0]; @@ -54,31 +55,28 @@ export default new NamedCommand({ if (id in registry) { channel.send(`\`${user.username}\` - ${registry[id]}`); } else { - channel.send(`\`${user.username}#${user.discriminator}\` hasn't been added to the registry yet!`); + channel.send(`\`${user.tag}\` hasn't been added to the registry yet!`); } } }), any: new Command({ - async run({message, channel, guild, author, member, client, args}) { - if (guild) { - const query: string = args.join(" "); - const member = await getMemberByUsername(guild, query); + channelType: CHANNEL_TYPE.GUILD, + async run({message, channel, guild, author, client, args}) { + const query = args.join(" ") as string; + const member = await getMemberByUsername(guild!, query); - if (member && member.id in registry) { - const id = member.id; + if (member && member.id in registry) { + const id = member.id; - if (id in registry) { - channel.send(`\`${member.user.username}\` - ${registry[member.id]}`); - } else { - channel.send(`\`${member.user.username}\` hasn't been added to the registry yet!`); - } + if (id in registry) { + channel.send(`\`${member.nickname ?? member.user.username}\` - ${registry[member.id]}`); } else { - channel.send(`Couldn't find a user by the name of \`${query}\`!`); + channel.send( + `\`${member.nickname ?? member.user.username}\` hasn't been added to the registry yet!` + ); } } else { - channel.send( - "You must run this in a guild! (*If you have the user's ID, you don't have to be in a guild.*)" - ); + channel.send(`Couldn't find a user by the name of \`${query}\`!`); } } }) diff --git a/src/commands/system/admin.ts b/src/commands/system/admin.ts index 7aeebc2..ac0e9e0 100644 --- a/src/commands/system/admin.ts +++ b/src/commands/system/admin.ts @@ -1,7 +1,7 @@ -import {Command, NamedCommand, botHasPermission, getPermissionLevel, getPermissionName} from "../../core"; +import {Command, NamedCommand, botHasPermission, getPermissionLevel, getPermissionName, CHANNEL_TYPE} from "../../core"; import {clean} from "../../lib"; import {Config, Storage} from "../../structures"; -import {Permissions} from "discord.js"; +import {Permissions, TextChannel} from "discord.js"; import {logs} from "../../modules/globals"; function getLogBuffer(type: string) { @@ -22,8 +22,6 @@ export default new NamedCommand({ description: "An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.", async run({message, channel, guild, author, member, client, args}) { - if (!member) - return channel.send("Couldn't find a member object for you! Did you make sure you used this in a server?"); const permLevel = getPermissionLevel(author, member); return channel.send(`${author}, your permission level is \`${getPermissionName(permLevel)}\` (${permLevel}).`); }, @@ -32,12 +30,13 @@ export default new NamedCommand({ description: "Set different per-guild settings for the bot.", run: "You have to specify the option you want to set.", permission: PERMISSIONS.ADMIN, + channelType: CHANNEL_TYPE.GUILD, subcommands: { prefix: new NamedCommand({ description: "Set a custom prefix for your guild. Removes your custom prefix if none is provided.", usage: "()", async run({message, channel, guild, author, member, client, args}) { - Storage.getGuild(guild?.id || "N/A").prefix = null; + Storage.getGuild(guild!.id).prefix = null; Storage.save(); channel.send( `The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.` @@ -45,7 +44,7 @@ export default new NamedCommand({ }, any: new Command({ async run({message, channel, guild, author, member, client, args}) { - Storage.getGuild(guild?.id || "N/A").prefix = args[0]; + Storage.getGuild(guild!.id).prefix = args[0]; Storage.save(); channel.send(`The custom prefix for this guild is now \`${args[0]}\`.`); } @@ -61,36 +60,24 @@ export default new NamedCommand({ "Sets how welcome messages are displayed for your server. Removes welcome messages if unspecified.", usage: "`none`/`text`/`graphical`", async run({message, channel, guild, author, member, client, args}) { - if (guild) { - Storage.getGuild(guild.id).welcomeType = "none"; - Storage.save(); - channel.send("Set this server's welcome type to `none`."); - } else { - channel.send("You must use this command in a server."); - } + Storage.getGuild(guild!.id).welcomeType = "none"; + Storage.save(); + channel.send("Set this server's welcome type to `none`."); }, // I should probably make this a bit more dynamic... Oh well. subcommands: { text: new NamedCommand({ async run({message, channel, guild, author, member, client, args}) { - if (guild) { - Storage.getGuild(guild.id).welcomeType = "text"; - Storage.save(); - channel.send("Set this server's welcome type to `text`."); - } else { - channel.send("You must use this command in a server."); - } + Storage.getGuild(guild!.id).welcomeType = "text"; + Storage.save(); + channel.send("Set this server's welcome type to `text`."); } }), graphical: new NamedCommand({ async run({message, channel, guild, author, member, client, args}) { - if (guild) { - Storage.getGuild(guild.id).welcomeType = "graphical"; - Storage.save(); - channel.send("Set this server's welcome type to `graphical`."); - } else { - channel.send("You must use this command in a server."); - } + Storage.getGuild(guild!.id).welcomeType = "graphical"; + Storage.save(); + channel.send("Set this server's welcome type to `graphical`."); } }) } @@ -99,34 +86,17 @@ export default new NamedCommand({ description: "Sets the welcome channel for your server. Type `#` to reference the channel.", usage: "()", async run({message, channel, guild, author, member, client, args}) { - if (guild) { - Storage.getGuild(guild.id).welcomeChannel = channel.id; - Storage.save(); - channel.send(`Successfully set ${channel} as the welcome channel for this server.`); - } else { - channel.send("You must use this command in a server."); - } + Storage.getGuild(guild!.id).welcomeChannel = channel.id; + Storage.save(); + channel.send(`Successfully set ${channel} as the welcome channel for this server.`); }, - // If/when channel types come out, this will be the perfect candidate to test it. - any: new Command({ + id: "channel", + channel: new Command({ async run({message, channel, guild, author, member, client, args}) { - if (guild) { - const match = args[0].match(/^<#(\d{17,19})>$/); - - if (match) { - Storage.getGuild(guild.id).welcomeChannel = match[1]; - Storage.save(); - channel.send( - `Successfully set this server's welcome channel to ${match[0]}.` - ); - } else { - channel.send( - "You must provide a reference channel. You can do this by typing `#` then searching for the proper channel." - ); - } - } else { - channel.send("You must use this command in a server."); - } + const result = args[0] as TextChannel; + Storage.getGuild(guild!.id).welcomeChannel = result.id; + Storage.save(); + channel.send(`Successfully set this server's welcome channel to ${result}.`); } }) }), @@ -135,24 +105,16 @@ export default new NamedCommand({ "Sets a custom welcome message for your server. Use `%user%` as the placeholder for the user.", usage: "()", async run({message, channel, guild, author, member, client, args}) { - if (guild) { - Storage.getGuild(guild.id).welcomeMessage = null; - Storage.save(); - channel.send("Reset your server's welcome message to the default."); - } else { - channel.send("You must use this command in a server."); - } + Storage.getGuild(guild!.id).welcomeMessage = null; + Storage.save(); + channel.send("Reset your server's welcome message to the default."); }, any: new Command({ async run({message, channel, guild, author, member, client, args}) { - if (guild) { - const message = args.join(" "); - Storage.getGuild(guild.id).welcomeMessage = message; - Storage.save(); - channel.send(`Set your server's welcome message to \`${message}\`.`); - } else { - channel.send("You must use this command in a server."); - } + const newMessage = args.join(" "); + Storage.getGuild(guild!.id).welcomeMessage = newMessage; + Storage.save(); + channel.send(`Set your server's welcome message to \`${newMessage}\`.`); } }) }) @@ -202,9 +164,10 @@ export default new NamedCommand({ purge: new NamedCommand({ description: "Purges the bot's own messages.", permission: PERMISSIONS.BOT_SUPPORT, + channelType: CHANNEL_TYPE.GUILD, async run({message, channel, guild, author, member, client, args}) { // It's probably better to go through the bot's own messages instead of calling bulkDelete which requires MANAGE_MESSAGES. - if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES) && channel.type !== "dm") { + if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES)) { message.delete(); const msgs = await channel.messages.fetch({ limit: 100 @@ -216,7 +179,7 @@ export default new NamedCommand({ timeout: 5000 }) ); - await channel.bulkDelete(travMessages); + await (channel as TextChannel).bulkDelete(travMessages); } else { channel.send( "This command must be executed in a guild where I have the `MANAGE_MESSAGES` permission." @@ -227,17 +190,16 @@ export default new NamedCommand({ clear: new NamedCommand({ description: "Clears a given amount of messages.", usage: "", + channelType: CHANNEL_TYPE.GUILD, run: "A number was not provided.", number: new Command({ description: "Amount of messages to delete.", async run({message, channel, guild, author, member, client, args}) { - if (channel.type === "dm") return channel.send("Can't clear messages in the DMs!"); message.delete(); const fetched = await channel.messages.fetch({ limit: args[0] }); - await channel.bulkDelete(fetched); - return; + return await (channel as TextChannel).bulkDelete(fetched); } }) }), @@ -261,9 +223,10 @@ export default new NamedCommand({ nick: new NamedCommand({ description: "Change the bot's nickname.", permission: PERMISSIONS.BOT_SUPPORT, + channelType: CHANNEL_TYPE.GUILD, async run({message, channel, guild, author, member, client, args}) { const nickName = args.join(" "); - await guild?.me?.setNickname(nickName); + await guild!.me?.setNickname(nickName); if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES)) message.delete({timeout: 5000}); channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000})); } @@ -308,15 +271,20 @@ export default new NamedCommand({ syslog: new NamedCommand({ description: "Sets up the current channel to receive system logs.", permission: PERMISSIONS.BOT_ADMIN, + channelType: CHANNEL_TYPE.GUILD, async run({message, channel, guild, author, member, client, args}) { - if (guild) { - Config.systemLogsChannel = channel.id; + Config.systemLogsChannel = channel.id; + Config.save(); + channel.send(`Successfully set ${channel} as the system logs channel.`); + }, + channel: new Command({ + async run({message, channel, guild, author, member, client, args}) { + const targetChannel = args[0] as TextChannel; + Config.systemLogsChannel = targetChannel.id; Config.save(); - channel.send(`Successfully set ${channel} as the system logs channel.`); - } else { - channel.send("DM system log channels aren't supported."); + channel.send(`Successfully set ${targetChannel} as the system logs channel.`); } - } + }) }) } }); diff --git a/src/commands/utility/calc.ts b/src/commands/utility/calc.ts index 2303351..dad3e88 100644 --- a/src/commands/utility/calc.ts +++ b/src/commands/utility/calc.ts @@ -5,22 +5,18 @@ import {MessageEmbed} from "discord.js"; export default new NamedCommand({ description: "Calculates a specified math expression.", async run({message, channel, guild, author, member, client, args}) { - if (!args[0]) { - channel.send("Please provide a calculation."); - return; - } + if (!args[0]) return channel.send("Please provide a calculation."); let resp; try { resp = math.evaluate(args.join(" ")); } catch (e) { - channel.send("Please provide a *valid* calculation."); - return; + return channel.send("Please provide a *valid* calculation."); } const embed = new MessageEmbed() .setColor(0xffffff) .setTitle("Math Calculation") .addField("Input", `\`\`\`js\n${args.join("")}\`\`\``) .addField("Output", `\`\`\`js\n${resp}\`\`\``); - channel.send(embed); + return channel.send(embed); } }); diff --git a/src/commands/utility/desc.ts b/src/commands/utility/desc.ts index ed0b87c..b3c3d57 100644 --- a/src/commands/utility/desc.ts +++ b/src/commands/utility/desc.ts @@ -6,24 +6,14 @@ export default new NamedCommand({ async run({message, channel, guild, author, member, client, args}) { const voiceChannel = message.member?.voice.channel; - if (!voiceChannel) { - channel.send("You are not in a voice channel."); - return; - } - - if (!voiceChannel.guild.me?.hasPermission("MANAGE_CHANNELS")) { - channel.send("I am lacking the required permissions to perform this action."); - return; - } - - if (args.length === 0) { - channel.send("Please provide a new voice channel name."); - return; - } + if (!voiceChannel) return channel.send("You are not in a voice channel."); + if (!voiceChannel.guild.me?.hasPermission("MANAGE_CHANNELS")) + return channel.send("I am lacking the required permissions to perform this action."); + if (args.length === 0) return channel.send("Please provide a new voice channel name."); const prevName = voiceChannel.name; const newName = args.join(" "); await voiceChannel.setName(newName); - await channel.send(`Changed channel name from "${prevName}" to "${newName}".`); + return await channel.send(`Changed channel name from "${prevName}" to "${newName}".`); } }); diff --git a/src/commands/utility/info.ts b/src/commands/utility/info.ts index 68b4d35..7006a0e 100644 --- a/src/commands/utility/info.ts +++ b/src/commands/utility/info.ts @@ -1,14 +1,16 @@ -import {MessageEmbed, version as djsversion, Guild} from "discord.js"; +import {MessageEmbed, version as djsversion, Guild, User, GuildMember} from "discord.js"; import ms from "ms"; import os from "os"; -import {Command, NamedCommand, getMemberByUsername} from "../../core"; +import {Command, NamedCommand, getMemberByUsername, CHANNEL_TYPE} from "../../core"; import {formatBytes, trimArray} from "../../lib"; import {verificationLevels, filterLevels, regions} from "../../defs/info"; import moment, {utc} from "moment"; export default new NamedCommand({ description: "Command to provide all sorts of info about the current server, a user, etc.", - run: "Please provide an argument.\nFor help, run `%prefix%help info`.", + async run({message, channel, guild, author, member, client, args}) { + channel.send(await getUserInfo(author, member)); + }, subcommands: { avatar: new NamedCommand({ description: "Shows your own, or another user's avatar.", @@ -16,6 +18,7 @@ export default new NamedCommand({ async run({message, channel, guild, author, member, client, args}) { channel.send(author.displayAvatarURL({dynamic: true, size: 2048})); }, + id: "user", user: new Command({ description: "Shows your own, or another user's avatar.", async run({message, channel, guild, author, member, client, args}) { @@ -29,21 +32,20 @@ export default new NamedCommand({ }), any: new Command({ description: "Shows another user's avatar by searching their name", - async run({message, channel, guild, author, member, client, args}) { - if (guild) { - const name = args.join(" "); - const member = await getMemberByUsername(guild, name); + channelType: CHANNEL_TYPE.GUILD, + async run({message, channel, guild, author, client, args}) { + const name = args.join(" "); + const member = await getMemberByUsername(guild!, name); - if (member) { - channel.send( - member.user.displayAvatarURL({ - dynamic: true, - size: 2048 - }) - ); - } else { - channel.send(`No user found by the name \`${name}\`!`); - } + if (member) { + channel.send( + member.user.displayAvatarURL({ + dynamic: true, + size: 2048 + }) + ); + } else { + channel.send(`No user found by the name \`${name}\`!`); } } }) @@ -92,12 +94,9 @@ export default new NamedCommand({ guild: new NamedCommand({ description: "Displays info about the current guild or another guild.", usage: "(/)", + channelType: CHANNEL_TYPE.GUILD, async run({message, channel, guild, author, member, client, args}) { - if (guild) { - channel.send(await getGuildInfo(guild, guild)); - } else { - channel.send("Please execute this command in a guild."); - } + channel.send(await getGuildInfo(guild!, guild)); }, any: new Command({ description: "Display info about a guild by finding its name or ID.", @@ -105,19 +104,21 @@ export default new NamedCommand({ // If a guild ID is provided (avoid the "number" subcommand because of inaccuracies), search for that guild if (args.length === 1 && /^\d{17,19}$/.test(args[0])) { const id = args[0]; - const guild = client.guilds.cache.get(id); + const targetGuild = client.guilds.cache.get(id); - if (guild) { - channel.send(await getGuildInfo(guild, guild)); + if (targetGuild) { + channel.send(await getGuildInfo(targetGuild, guild)); } else { channel.send(`None of the servers I'm in matches the guild ID \`${id}\`!`); } } else { const query: string = args.join(" ").toLowerCase(); - const guild = client.guilds.cache.find((guild) => guild.name.toLowerCase().includes(query)); + const targetGuild = client.guilds.cache.find((guild) => + guild.name.toLowerCase().includes(query) + ); - if (guild) { - channel.send(await getGuildInfo(guild, guild)); + if (targetGuild) { + channel.send(await getGuildInfo(targetGuild, guild)); } else { channel.send(`None of the servers I'm in matches the query \`${query}\`!`); } @@ -126,55 +127,62 @@ export default new NamedCommand({ }) }) }, + id: "user", user: new Command({ description: "Displays info about mentioned user.", async run({message, channel, guild, author, client, args}) { + const user = args[0] as User; // Transforms the User object into a GuildMember object of the current guild. - const member = await guild?.members.fetch(args[0]); - - if (!member) { - channel.send( - "No member object was found by that user! Are you sure you used this command in a server?" - ); - return; - } - - const roles = member.roles.cache - .sort((a: {position: number}, b: {position: number}) => b.position - a.position) - .map((role: {toString: () => any}) => role.toString()) - .slice(0, -1); - const userFlags = (await member.user.fetchFlags()).toArray(); - - const embed = new MessageEmbed() - .setThumbnail(member.user.displayAvatarURL({dynamic: true, size: 512})) - .setColor(member.displayHexColor || "BLUE") - .addField("User", [ - `**❯ Username:** ${member.user.username}`, - `**❯ Discriminator:** ${member.user.discriminator}`, - `**❯ ID:** ${member.id}`, - `**❯ Flags:** ${userFlags.length ? userFlags.join(", ") : "None"}`, - `**❯ Avatar:** [Link to avatar](${member.user.displayAvatarURL({ - dynamic: true - })})`, - `**❯ Time Created:** ${moment(member.user.createdTimestamp).format("LT")} ${moment( - member.user.createdTimestamp - ).format("LL")} ${moment(member.user.createdTimestamp).fromNow()}`, - `**❯ Status:** ${member.user.presence.status}`, - `**❯ Game:** ${member.user.presence.activities || "Not playing a game."}` - ]) - .addField("Member", [ - `**❯ Highest Role:** ${member.roles.highest.id === guild?.id ? "None" : member.roles.highest.name}`, - `**❯ Server Join Date:** ${moment(member.joinedAt).format("LL LTS")}`, - `**❯ Hoist Role:** ${member.roles.hoist ? member.roles.hoist.name : "None"}`, - `**❯ Roles:** [${roles.length}]: ${ - roles.length == 0 ? "None" : roles.length <= 10 ? roles.join(", ") : trimArray(roles).join(", ") - }` - ]); - channel.send(embed); + const member = guild?.members.resolve(args[0]); + channel.send(await getUserInfo(user, member)); } }) }); +async function getUserInfo(user: User, member: GuildMember | null | undefined): Promise { + const userFlags = (await user.fetchFlags()).toArray(); + + const embed = new MessageEmbed() + .setThumbnail(user.displayAvatarURL({dynamic: true, size: 512})) + .setColor("BLUE") + .addField("User", [ + `**❯ Username:** ${user.username}`, + `**❯ Discriminator:** ${user.discriminator}`, + `**❯ ID:** ${user.id}`, + `**❯ Flags:** ${userFlags.length ? userFlags.join(", ") : "None"}`, + `**❯ Avatar:** [Link to avatar](${user.displayAvatarURL({ + dynamic: true + })})`, + `**❯ Time Created:** ${moment(user.createdTimestamp).format("LT")} ${moment(user.createdTimestamp).format( + "LL" + )} ${moment(user.createdTimestamp).fromNow()}`, + `**❯ Status:** ${user.presence.status}`, + `**❯ Game:** ${user.presence.activities || "Not playing a game."}` + ]); + + if (member) { + const roles = member.roles.cache + .sort((a: {position: number}, b: {position: number}) => b.position - a.position) + .map((role: {toString: () => any}) => role.toString()) + .slice(0, -1); + + embed + .setColor(member.displayHexColor) + .addField("Member", [ + `**❯ Highest Role:** ${ + member.roles.highest.id === member.guild.id ? "None" : member.roles.highest.name + }`, + `**❯ Server Join Date:** ${moment(member.joinedAt).format("LL LTS")}`, + `**❯ Hoist Role:** ${member.roles.hoist ? member.roles.hoist.name : "None"}`, + `**❯ Roles:** [${roles.length}]: ${ + roles.length == 0 ? "None" : roles.length <= 10 ? roles.join(", ") : trimArray(roles).join(", ") + }` + ]); + } + + return embed; +} + async function getGuildInfo(guild: Guild, currentGuild: Guild | null) { const members = await guild.members.fetch({ withPresences: true, diff --git a/src/commands/utility/scanemotes.ts b/src/commands/utility/scanemotes.ts index 41d5115..a60d861 100644 --- a/src/commands/utility/scanemotes.ts +++ b/src/commands/utility/scanemotes.ts @@ -1,4 +1,4 @@ -import {Command, NamedCommand} from "../../core"; +import {Command, NamedCommand, CHANNEL_TYPE} from "../../core"; import {pluralise} from "../../lib"; import moment from "moment"; import {Collection, TextChannel} from "discord.js"; @@ -8,26 +8,21 @@ const lastUsedTimestamps: {[id: string]: number} = {}; export default new NamedCommand({ description: "Scans all text channels in the current guild and returns the number of times each emoji specific to the guild has been used. Has a cooldown of 24 hours per guild.", + channelType: CHANNEL_TYPE.GUILD, async run({message, channel, guild, author, member, client, args}) { - if (!guild) { - channel.send(`You must use this command on a server!`); - return; - } - // Test if the command is on cooldown. This isn't the strictest cooldown possible, because in the event that the bot crashes, the cooldown will be reset. But for all intends and purposes, it's a good enough cooldown. It's a per-server cooldown. const startTime = Date.now(); const cooldown = 86400000; // 24 hours - const lastUsedTimestamp = lastUsedTimestamps[guild.id] ?? 0; + const lastUsedTimestamp = lastUsedTimestamps[guild!.id] ?? 0; const difference = startTime - lastUsedTimestamp; const howLong = moment(startTime).to(lastUsedTimestamp + cooldown); // If it's been less than an hour since the command was last used, prevent it from executing. - if (difference < cooldown) { - channel.send( + if (difference < cooldown) + return channel.send( `This command requires a day to cooldown. You'll be able to activate this command ${howLong}.` ); - return; - } else lastUsedTimestamps[guild.id] = startTime; + else lastUsedTimestamps[guild!.id] = startTime; const stats: { [id: string]: { @@ -39,7 +34,7 @@ export default new NamedCommand({ } = {}; let totalUserEmoteUsage = 0; // IMPORTANT: You MUST check if the bot actually has access to the channel in the first place. It will get the list of all channels, but that doesn't mean it has access to every channel. Without this, it'll require admin access and throw an annoying unhelpful DiscordAPIError: Missing Access otherwise. - const allTextChannelsInCurrentGuild = guild.channels.cache.filter( + const allTextChannelsInCurrentGuild = guild!.channels.cache.filter( (channel) => channel.type === "text" && channel.viewable ) as Collection; let messagesSearched = 0; @@ -52,7 +47,7 @@ export default new NamedCommand({ // Initialize the emote stats object with every emote in the current guild. // The goal here is to cut the need to access guild.emojis.get() which'll make it faster and easier to work with. - for (let emote of guild.emojis.cache.values()) { + for (let emote of guild!.emojis.cache.values()) { // If you don't include the "a" for animated emotes, it'll not only not show up, but also cause all other emotes in the same message to not show up. The emote name is self-correcting but it's better to keep the right value since it'll be used to calculate message lengths that fit. stats[emote.id] = { name: emote.name, @@ -186,6 +181,6 @@ export default new NamedCommand({ ); } - await channel.send(lines, {split: true}); + return await channel.send(lines, {split: true}); } }); diff --git a/src/commands/utility/time.ts b/src/commands/utility/time.ts index c51552a..23250ba 100644 --- a/src/commands/utility/time.ts +++ b/src/commands/utility/time.ts @@ -374,6 +374,7 @@ export default new NamedCommand({ run: DST_NOTE_INFO }) }, + id: "user", user: new Command({ description: "See what time it is for someone else.", async run({channel, args}) { From cd9aaa5f4b2cc331cb22b8f0ed6efb143e642911 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:48:17 -0500 Subject: [PATCH 25/30] Added comma --- src/commands/fun/whois.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/fun/whois.ts b/src/commands/fun/whois.ts index 86ba4a0..cee90c0 100644 --- a/src/commands/fun/whois.ts +++ b/src/commands/fun/whois.ts @@ -31,7 +31,7 @@ const registry: {[id: string]: string} = { "440399719076855818": "You are, uhh, Stay Put, Soft Puppy, Es-Pee, Swift Pacemaker, Smug Poyo, and many more.\n...Seriously, this woman has too many names.", "243061915281129472": - "Some random conlanger, worldbuilder and programmer doofus. ~~May also secretly be a nyan. :3~~" + "Some random conlanger, worldbuilder and programmer doofus. ~~May also secretly be a nyan. :3~~", "367439475153829892": "A weeb.", "760375501775700038": "˙qǝǝʍ ∀", "389178357302034442": "In his dreams, he is the star. its him. <:itsMe:808174425253871657>", From 728434251448b10b363b57654c2c0432383184e6 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Tue, 6 Apr 2021 08:19:41 -0500 Subject: [PATCH 26/30] Began revising documentation and copied changelog --- .prettierignore | 1 + CHANGELOG.md | 109 +++++++++++++++++++ README.md | 41 +++++-- docs/CHANGELOG.md | 0 docs/DesignDecisions.md | 1 + docs/Documentation.md | 202 ++--------------------------------- docs/GettingStarted.md | 20 ---- docs/Overview.md | 230 ++++++++++++++++++++++++++++++++++++++++ docs/Specifications.md | 48 --------- package-lock.json | 4 +- package.json | 28 ++--- 11 files changed, 400 insertions(+), 284 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 docs/CHANGELOG.md create mode 100644 docs/DesignDecisions.md delete mode 100644 docs/GettingStarted.md create mode 100644 docs/Overview.md delete mode 100644 docs/Specifications.md diff --git a/.prettierignore b/.prettierignore index 40343cc..28e6c5e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,6 +10,7 @@ LICENSE dist/ data/ docs/ +*.md tmp/ test* !test/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f6bdb65 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,109 @@ +# 2.8.4 - Reworked the react command +- `react` is now a fully versatile command for helping you react to other messages with non-server emotes. + - Now properly reacts to the previous message (bug fix). + - Provides you the option to react to any number of messages before your message (3 messages above yours for example). + - Renamed guild ID to message ID for clarity's sake. + - Now removes the bot's own reaction after a few seconds to make the reaction count more accurate. + - Now lets you react with multiple messages in a row. + - Now reacts with ❓ if no reactions were found at all (see below). +- `emote`: + - Is now case-sensitive again (because there are too many name conflicts). + - Accepts multiple emotes for tiled emotes. + - Now reacts to your message with ❓ instead of `None of those emote names were valid!` so that the bot doesn't spam the chat if you can't find the right emote (because you'll still be able to delete your messages). +- `thonk` now stores the last specified phrase so you can repeat a phrase with different diacritics. + +# 2.8.3 - The ultimate meme + +# 2.8.2 +- Added a changelog. +- Added an extra instruction to the readme's installation. +- Made commands utilize the existing `Array.random()` function. +- Removed concatenation when using template strings. +- Added `Number.pluralise()` for convenient pluralization. +- Reworked the `neko` command. +- Made `whoami` sync up with `whois` by using the same config. +- Fixed a bug with `emote` where it wouldn't find any upper case emotes and made it more lenient to just include any emote (so you don't have to remember the exact emote name). +- Moved lists and gathering shop items outside of `exports.run()` so that it initializes once during the bot's initialization (or when reloaded) rather than every time the command is called. + +# 2.8.1 - Modularized eco shop and eco buy +- Fixed scanemotes sometimes not displaying all emotes. This was an issue of not accounting for whether an emote was animated or not. +- Modularized `eco shop` and `eco buy`. Shop items are now in the `shop` subfolder and `eco shop` now separates every 5 shop items into separate pages automatically. + +# 2.8.0 - Added graphical welcome setting +- Adds a new option to the `set` and `conf` commands, allowing you to enable an image being sent as a welcome. + +# 2.7.1 - Added eco buy laser bridge and reworked scanemotes +- `eco buy laser bridge` - Added a shop item. Buy what is technically a laser bridge. Costs 3 Mons. +- `insult` - Now pings the user who activated it. +- `scanemotes` - Reworked the command after a test run in a big server. + - Merged the unsorted and sorted emote listings into one section. The unsorted emotes pretty much had a random order as it was pretty much which emote was added first as the search went on, so nothing's gone there. `#1 :emote: x 20 - 30.532% (Bots: 132)` + - Bumped the cooldown from 1 hour to 1 day. + - An updated progress meter which now stays on a single channel at a time because it's no longer asynchronous. This progress bar also works with Discord's rate limits. `Searching channel ___... (___ messages scanned, x/y channels scanned)` + - Now includes all emotes in a server, even if they haven't been used. + - Now properly counts emote usage for reactions (whether or not a bot reacted to a message) + +# 2.7.0 - Added percentages to scanemotes +## Major Changes +- Added an hour long cooldown to `scanemotes` per server because it's a very memory-intensive task to search through every single message. +- Added a second list of emotes to `scanemotes`, sorting by percentage of users-only usage. +- Added a function to the client's common functions to generate a page users can turn. +## Minor Changes +- `avatar` + - Now has proper error handling when searching by mention and ID. + - No longer pings the user, it just sends the image link by itself. +- `eco` + - Merges `sender.id + message.guild.id` into `compositeID` since it's so frequently used. + - Bug Fix: If you have exactly 1 Mon and you pay someone 1 Mon, they'll get 1 more Mon and you'll still have 1 Mon because the 0 coerces to false resetting your money, because JS soft comparison. Fixed by using the "in" operator instead. + - Uses else ifs to make the command marginally faster. + - Now properly handles mentions and extracting the user ID from them in the `pay` subcommand. + - Added a message that occurs when the user tries to buy an item that doesn't exist. +- Added an `insult` command which will have the bot type out the navy seals copypasta for a minute. +- Modified the `invite` command to auto-generate a link based on the current bot client ID rather than having it be hardcoded to TravBot specifically. +- Added error checking to `scanemotes` so users aren't left in the dark if something happens. +- On big servers, `scanemotes` should now have emotes actually show up. + +# 2.6.1 - Hotfix: Scan emotes no longer requires admin +- Fixed the `scanemotes` command to no longer require admin permissions. This was due to an oversight: There can be channels which the bot doesn't have access to, ie private channels. You have to check if the bot has access to a channel because the filter will gather all text-based channels regardless. Admin permissions overrides all restrictions, which is why it only worked with admin permissions. +- Entering a username in the `avatar` command unsuccessfully will now send a message in chat. + +# 2.6.0 - Added the ability to get other users' avatars and see emote usage +- You can now scan the current guild for emote usage, collecting all emotes used in messages and reactions. (example below) +![2020-06-19 04_08_22-Window](https://user-images.githubusercontent.com/44940783/85116219-98a69280-b1e2-11ea-9246-b8f5ff2537ea.png) +- You can now get other avatars by providing an ID (works even when the bot doesn't share the same server as that user), username, or by pinging them. +- Included the fix for `serverinfo`. + +# 2.5.3 +- Changed default prefix for setup. +- Enhanced `react` command. New optional guildID arg. +- Added message logger. +- Fixed calc permission error. +- Removed `delete` command. +- Added ignored and notified channels to logger. +- Added images to logger. Added author to logger. +- Emote command is now not case-sensitive. + +# 2.5.2 - Bug fixes to the "eco" command +- Now prevents users from sending negative amounts of money to others (minimum of 1 Mon). +- Also prevents users from sending decimal amounts. +- Fixes a potentially wrong substring for user IDs. +- Now requires an argument when using the "desc" command. + +# 2.5.1 +- Added shop functionality to eco. +- Fixed faulty guild check. +- Attempt at fixing emote for eco cute. +- Pluralised "mon" for eco handhold. +- Added `translate` command. + +# 2.5.0 - Added the "pay" sub-command to "eco" + +# 2.4.1 +- Added Procfile. +- Updated whoami's keys. +- Rewrote `desc` command. + +# 2.4.0 - Implemented music system +- VC Rename command +- Travis CI configuration +- Music system +- Dependency updates diff --git a/README.md b/README.md index b099c29..094666e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,24 @@ # TravBot-v3 -Fourth revision of TravBot, version number 3.0.0. +

+ +

+

+ + License + + + Version + + + Version (Dev) + + + Discord.js Version + +

+ +Fourth revision of [TravBot](https://github.com/keanuplayz/TravBot), version number 3.0.0. This is the repo belonging to the code of TravBot v3. @@ -8,14 +26,19 @@ This version will be the final revision of TravBot, this being the final structu Thank you for coming on this journey with me, but it is time to put the big changes to an end. +## Installation + +1. `npm install` +2. `npm run build` +3. `npm start` + +## Contributing + +To get information on how to contribute to this project, see the [overview](docs/Overview.md) as well as other files in the `docs` folder meant for developers. + ## Special Thanks Special thanks to: - -- Lexi Sother (TravBot v2, structural overhaul. Reviewing PRs.) -- WatDuhHekBro (a _lot_ of contributions to TravBot v2) -- Zeehondie (Ideas for various commands.) - -### License - -Refer to the [LICENSE](https://github.com/keanuplayz/TravBot-v3/tree/master/LICENSE) file. +- Lexi Sother (TravBot v2, structural overhaul. Reviewing PRs.) +- WatDuhHekBro (a *lot* of contributions to TravBot v2) +- Zeehondie (Ideas for various commands.) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/DesignDecisions.md b/docs/DesignDecisions.md new file mode 100644 index 0000000..27306bf --- /dev/null +++ b/docs/DesignDecisions.md @@ -0,0 +1 @@ +Coming Soon™ diff --git a/docs/Documentation.md b/docs/Documentation.md index c720942..cf91fb2 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -1,199 +1,19 @@ -# What this is +# Table of Contents -This is a user-friendly version of the project's structure (compared to the amalgamation that has become [specifications](Specifications.md) which is just a list of design decisions and isn't actually helpful at all for understanding the code). This will follow the line of logic that the program would run through. - -# Building/Setup - -- `npm run dev` runs the TypeScript compiler in watch mode, meaning that any changes you make to the code will automatically reload the bot. -- This will take all the files in `src` (where all the code is) and compile it into `dist` which is what the program actually uses. - - If there's a runtime error, `dist\commands\test.js:25:30` for example, then you have to into `dist` instead of `src`, then find the line that corresponds. - -# Launching - -When you start the program, it'll run the code in `index` (meaning both `src/index.ts` and `dist/index.js`, they're the same except that `dist/<...>.js` is compiled). The code in `index` will call `setup` and check if `data/config.json` exists, prompting you if it doesn't. It'll then run initialization code. +... # Structure -- `commands` contains all the commands. -- `defs` contains static definitions. -- `core` contains the foundation of the program. You won't need to worry about this unless you're modifying pre-existing behavior of the `Command` class for example or add a function to the library. -- `events` contains all the events. Again, you generally won't need to touch this unless you're listening for a new Discord event. +- `src`: Contains all the code for the bot itself. Code directly in this folder is for the starting index file as well as commonly accessed utility files. + - `core`: This is currently where the command handler is. Try to keep it as isolated as possible, it might split off to become its own module. + - `commands`: Where all the dynamically loaded commands are stored. You can use a subfolder to specify the command category. Specify a `modules` folder to create files that are ignored by the command loader. + - `modules`: This is where mostly single-purpose blocks of code go. (This is **not** the same as a `modules` folder under `commands`.) + - `defs`: Contains static definitions. +- `dist`: This is where the runnable code in `src` compiles to. (The directory structure mirrors `src`.) +- `data`: Holds all the dynamic/private data used by the bot. This folder is not meant to hold definitions. +- `docs`: Information for developers who want to contribute. -# The Command Class - -A valid command file must be located in `commands` and export a default `Command` instance. Assume that we're working with `commands/money.ts`. - -```js -import Command from '../core/command'; - -export default new Command({ - //... -}); -``` - -The `run` property can be either a function or a string. If it's a function, you get one parameter, `$` which represents the common library (see below). If it's a string, it's a variable string. - -- `%author%` pings the person who sent the message. -- `%prefix%` gets the bot's current prefix in the selected server. - -```js -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; - -export default new Command({ - run: '%author%, make sure to use the prefix! (%prefix)', -}); -``` - -...is equal to... - -```js -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; -import { getPrefix } from '../core/structures'; - -export default new Command({ - async run($: CommonLibrary): Promise { - $.channel.send( - `${$.author.toString()}, make sure to use the prefix! (${getPrefix( - $.guild, - )})`, - ); - }, -}); -``` - -Now here's where it gets fun. The `Command` class is a recursive structure, containing other `Command` instances as properties. - -- `subcommands` is used for specific keywords for accessing a certain command. For example, `$eco pay` has a subcommand of `pay`. - -```js -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; - -export default new Command({ - subcommands: { - pay: new Command({ - //... - }), - }, -}); -``` - -There's also `user` which listens for a ping or a Discord ID, `<@237359961842253835>` and `237359961842253835` respectively. The argument will be a `User` object. - -```js -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; - -export default new Command({ - user: new Command({ - async run($: CommonLibrary): Promise { - $.debug($.args[0].username); // "WatDuhHekBro" - }, - }), -}); -``` - -There's also `number` which checks for any number type except `Infinity`, converting the argument to a `number` type. - -```js -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; - -export default new Command({ - number: new Command({ - async run($: CommonLibrary): Promise { - $.debug($.args[0] + 5); - }, - }), -}); -``` - -And then there's `any` which catches everything else that doesn't fall into the above categories. The argument will be a `string`. - -```js -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; - -export default new Command({ - any: new Command({ - async run($: CommonLibrary): Promise { - $.debug($.args[0].toUpperCase()); - }, - }), -}); -``` - -Of course, maybe you just want to get string arguments regardless, and since everything is an optional property, so you'd then just include `any` and not `subcommands`, `user`, or `number`. - -## Other Properties - -- `description`: The description for that specific command. -- `endpoint`: A `boolean` determining whether or not to prevent any further arguments. For example, you could prevent `$money daily too many arguments`. -- `usage`: Provide a custom usage for the help menu. Do note that this is relative to the subcommand, so the below will result in `$money pay `. - -```js -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; - -export default new Command({ - subcommands: { - pay: new Command({ - usage: ' ', - }), - }, -}); -``` - -- `permission`: The permission to restrict the current command to. You can specify it for certain subcommands, useful for having `$money` be open to anyone but not `$money admin`. If it's `null` (default), the permission will inherit whatever was declared before (if any). The default value is NOT the same as `Command.PERMISSIONS.NONE`. -- `aliases`: A list of aliases (if any). - -## Alternatives to Nesting - -For a lot of the metadata properties like `description`, you must provide them when creating a new `Command` instance. However, you can freely modify and attach subcommands, useful for splitting a command into multiple files. - -```js -import pay from "./subcommands/pay"; - -const cmd = new Command({ - description: "Handle your money." -}); -cmd.subcommands.set("pay", pay); -cmd.run = async($: CommonLibrary): Promise { - $.debug($.args); -}; -cmd.any = new Command({ - //... -}); - -export default cmd; -``` - -## Error Handling - -Any errors caused when using `await` or just regular synchronous functions will be automatically caught, you don't need to worry about those. However, promises must be caught manually. For example, `$.channel.send("")` will throw an error because you can't send empty messages to Discord, but since it's a promise, it'll just fade without throwing an error. There are two ways to do this: - -- `$.channel.send("").catch($.handler.bind($))` -- `$.channel.send("").catch(error => $.handler(error))` - -# The Common Library - -This is the container of functions available without having to import `core/lib`, usually as `$`. When accessing this from a command's `run` function, it'll also come with shortcuts to other properties. - -## Custom Wrappers - -- `$(5)` = `new NumberWrapper(5)` -- `$("text")` = `new StringWrapper("text")` -- `$([1,2,3])` = `new ArrayWrapper([1,2,3])` - -## Custom Logger - -- `$.log(...)` -- `$.warn(...)` -- `$.error(...)` -- `$.debug(...)` -- `$.ready(...)` (only meant to be used once at the start of the program) +# Cleanup is Soon™ ## Convenience Functions diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md deleted file mode 100644 index a003a81..0000000 --- a/docs/GettingStarted.md +++ /dev/null @@ -1,20 +0,0 @@ -# Getting Started - -1. `npm install` -2. `npm run build` -3. `npm start` - -# Getting Started (Developers) - -1. `npm install` -2. `npm run dev` -3. Familiarize yourself with the [project's structure](Documentation.md). -4. Make sure to avoid using `npm run build`! This will remove all your dev dependencies (in order to reduce space used). Instead, use `npm run once` to compile and build in non-dev mode. -5. Begin developing. - -## Don't forget to... - -- ...update the [changelog](CHANGELOG.md) and any other necessary docs. -- ...update the version numbers in `package.json` and `package-lock.json`. -- ...make sure the test suite passes by running `npm test`. -- ...format the code by running `npm run format`. diff --git a/docs/Overview.md b/docs/Overview.md new file mode 100644 index 0000000..19e0981 --- /dev/null +++ b/docs/Overview.md @@ -0,0 +1,230 @@ +# Table of Contents + +- [Introduction](#introduction) +- [Setting up the development environment](#setting-up-the-development-environment) +- [Adding a new command](#adding-a-new-command) +- [Adding a new non-command feature](#adding-a-new-non-command-feature) + +# Introduction + +This is a brief overview that'll describe where and how to add new features to TravBot. For more details on specific functions, head over to the [documentation](Documentation.md). Assume the prefix for all of these examples is `$`. + +# Setting up the development environment + +1. `npm install` +2. `npm run dev` *(runs the TypeScript compiler in watch mode, meaning any changes you make to the code will automatically reload the bot)* + +*Note: Make sure to avoid using `npm run build`! This will remove all your dev dependencies (in order to reduce space used). Instead, use `npm run once` to compile and build in non-dev mode.* + +## Don't forget to... + +- ...update the [changelog](../CHANGELOG.md) and any other necessary docs. +- ...update the version numbers in [package.json](../package.json) and [package-lock.json](../package-lock.json). + +# Adding a new command + +To add a new command, go to `src/commands` and either copy the [template](../src/commands/template.ts) or rename the auto-generated test file (`../src/commands/test.ts`). For reference, this is the barebones requirement for a command file. + +## The very basics of a command + +```ts +import {NamedCommand} from "../core"; + +export default new NamedCommand(); +``` + +To make something actually happen when the command is run however, you implement the `run` property. + +```ts +import {Command, NamedCommand} from "../core"; + +export default new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + channel.send("test"); + } +}); +``` + +### Quick note on the run property + +You can also enter a string for the `run` property which will send a message with that string specified ([you can also specify some variables in that string](Documentation.md#)). The above is functionally equivalent to the below. + +```ts +import {Command, NamedCommand} from "../core"; + +export default new NamedCommand({ + run: "test" +}); +``` + +## Introducing subcommands + +Where this command handler really shines though is from its subcommands feature. You can filter and parse argument lists in a declarative manner. + +```ts +import {Command, NamedCommand} from "../core"; + +export default new NamedCommand({ + user: new Command({ + async run({message, channel, guild, author, member, client, args}) { + const user = args[0]; + } + }) +}); +``` + +Here, . For example, if this file was named `test.ts`, `$test <@237359961842253835>` would get the user by the ID `237359961842253835` into `args[0]` as a [User](https://discord.js.org/#/docs/main/stable/class/User) object. `$test experiment` would run as if you just called `$test` *(given that [endpoint](Documentation.md#) isn't set to `true`)*. + +If you want, you can typecast the argument to be more strongly typed, because the type of `args` is `any[]`. *([See why if you're curious.](DesignDecisions.md#))* + +```ts +import {Command, NamedCommand} from "../core"; +import {User} from "discord.js"; + +export default new NamedCommand({ + user: new Command({ + async run({message, channel, guild, author, member, client, args}) { + const user = args[0] as User; + } + }) +}); +``` + +## Keyed subcommands + +For keyed subcommands, you would instead use a `NamedCommand`. + +```ts +import {Command, NamedCommand} from "../core"; + +export default new NamedCommand({ + run: "one", + subcommands: { + bread: new NamedCommand({ + run: "two" + }) + } +}); +``` + +If the file was named `cat.ts`: +- `$cat` would output `one` +- `$cat bread` would output `two` + +Only `bread` in this case would lead to `two` being the output, which is different from the generic subcommand types in previous examples. + +You get an additional property with `NamedCommand`s: `aliases`. That means you can define aliases not only for top-level commands, but also every layer of subcommands. + +```ts +import {Command, NamedCommand} from "../core"; + +export default new NamedCommand({ + aliases: ["potato"], + subcommands: { + slice: new NamedCommand({ + aliases: ["pear"] + }) + } +}); +``` + +For example, if this file was named `plant.ts`, the following would work: +- `$plant` +- `$potato` +- `$plant slice` +- `$plant pear` +- `$potato slice` +- `$potato pear` + +## Metadata / Command Properties + +You can also specify metadata for commands by adding additional properties. Some of these properties are per-command while others are inherited. + +```ts +import {Command, NamedCommand} from "../core"; + +export default new NamedCommand({ + description: "desc one", + subcommands: { + pineapple: new NamedCommand({ + //... + }) + } +}); +``` + +`description` is an example of a per-command property (which is used in a help command). If the file was named `siege.ts`: +- The description of `$siege` would be `desc one`. +- There wouldn't be a description for `$siege pineapple`. + +This is in contrast to inherited properties. + +```ts +import {Command, NamedCommand, CHANNEL_TYPE} from "../core"; + +export default new NamedCommand({ + channelType: CHANNEL_TYPE.GUILD, + subcommands: { + pineapple: new NamedCommand({ + //... + }) + } +}); +``` + +Here, the property `channelType` would spread to all subcommands unless a subcommand defines it. Using the above example, the `channelType` for both `$siege` and `$siege pineapple` would be `CHANNEL_TYPE.GUILD`. + +*To get a full list of metadata properties, see the [documentation](Documentation.md#).* + +## Utility Functions + +You'll have to import these manually, however it's in the same import list as `Command` and `NamedCommand`. + +```ts +import {Command, NamedCommand, paginate} from "../core"; + +export default new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + paginate(/* enter your code here */); + } +}); +``` + +*To get a full list of utility functions, see the [documentation](Documentation.md#).* + +# Adding a new non-command feature + +If the feature you want to add isn't specifically a command, or the change you're making involves adding event listeners, go to `src/modules` and create a file. Code written here won't be automatically loaded, so make sure to open [src/index.ts](../src/index.ts) and add an import statement at the bottom. + +```ts +import "./modules/myModule"; +``` + +This will just run whatever code is in there. + +## Listening for events + +Rather than have an `events` folder which contains dynamically loaded events, you add an event listener directly via `client.on("...", () => {})`. *([See why if you're curious.](DesignDecisions.md#))* The client can be imported from the index file. + +```ts +import {client} from ".."; + +client.on("message", (message) => { + //... +}); +``` + +As long as you make sure to add that import statement in the index file itself, the event(s) will load. + +**Just make sure you instantiate the client *before* you import a module or you'll get a runtime error.** + +`index.ts` +```ts +import {Client} from "discord.js"; + +export const client = new Client(); + +//... + +import "./modules/myModule"; +``` diff --git a/docs/Specifications.md b/docs/Specifications.md deleted file mode 100644 index 49bef9d..0000000 --- a/docs/Specifications.md +++ /dev/null @@ -1,48 +0,0 @@ -# Structure - -The top-level directory is reserved for files that have to be there for it to work as well as configuration files. - -- `src`: Contains all the code for the bot itself. Code in this directory is for independent tasks keeping the initialization out of the subdirectories. - - `core`: This is where core structures and critical functions for the bot go. - - `modules`: This is where modules go that accomplish one specific purpose but isn't so necessary for the bot to function. The goal is to be able to safely remove these without too much trouble. - - `commands`: Here's the place to store commands. The file name determines the command name. - - `subcommands/`: All commands here are ignored by the category loader. Here is where you can split commands into different files. Also works per directory, for example, `utility/subcommands/` is ignored. - - `/`: Specify a directory which'll group commands into a category. For example, a `utility` folder would make all commands inside have the `Utility` category. - - `.ts`: All commands at this level will have the `Miscellaneous` category. - - `events`: Here's the place to store events. The file name determines the event type. -- `dist`: This is where the runnable code in `src` compiles to. (The directory structure mirrors `src`.) -- `test`: Used for all the unit tests. -- `data`: Holds all the dynamic data used by the bot. This is what you modify if you want to change stuff for just your instance of the bot. -- `standard`: Contains all the standard data to be used with the project itself. It's part of the code and will not be checked for inaccuracies because it's not meant to be easily modified. -- `docs`: Used for information about the design of the project. - -# Specific Files - -This list starts from `src`/`dist`. - -- `index`: This is the entry point of the bot. Here is where all the initialization is done, because the idea is to keep repeatable code in separate modules while having code that runs only once here designating this is **the** starting point. -- `setup`: Used for the first time the bot is loaded, walking the user through setting up the bot. -- `core/lib`: Exports a function object which lets you wrap values letting you call special functions as well as calling utility functions common to all commands. -- `core/structures`: Contains all the structures that the dynamic data read from JSON files should follow. This exports instances of these classes. -- `core/command`: Contains the class used to instantiate commands. -- `core/event`: Contains the class used to instantiate events. -- `core/storage`: Exports an object which handles everything related to files. -- `core/wrappers`: Contains classes that wrap around values and provide extra functionality. -- `core/permissions`: The file containing everything related to permissions. - -# Design Decisions - -- All top-level files (relative to `src`/`dist`) should ideally be independent, one-time use scripts. This helps separate code that just initializes once and reusable code that forms the bulk of the main program itself. That's why all the file searching and loading commands/events will be done in `index`. -- Wrapper objects were designed with the idea of letting you assign functions directly to native objects [without the baggage of actually doing so](https://developer.mozilla.org/en-US/docs/Web/JavaScript/The_performance_hazards_of__%5B%5BPrototype%5D%5D_mutation). -- `test` should be a keyword for any file not tracked by git and generally be more flexible to play around with. It should also be automatically generated during initialization in `commands` so you can have templates ready for new commands. -- The storage module should not provide an auto-write feature. This would actually end up overcomplicating things especially when code isn't fully blocking. -- I think it's much easier to make a template system within the code itself. After all, the templates only change when the code changes to use new keys or remove old ones. You'll also be able to dedicate specific classes for the task rather than attaching meta tags to arrays and objects. -- I decided to forget about implementing dynamic events. I don't think it'll work with this setup. After all, there are only so many events you can use, whereas commands can have any number of ones, more suitable for dynamic loading. The main reasons were unsecure types and no easy way to access variables like the config or client. -- I want to make attaching subcommands more flexible, so you can either add subcommands in the constructor or by using a method. However, you have to add all other properties when instantiating a command. -- All commands should have only one parameter. This parameter is meant to be flexible so you can add properties without making a laundry list of parameters. It also has convenience functions too so you don't have to import the library for each command. -- The objects in `core/structures` are initialized into a special object once and then cached into memory automatically due to an import system. This means you don't have to keep loading JSON files over and over again even without the stack storage system. Because a JSON file resolves into an object, any extra keys are removed automatically (as it isn't initialized into the data) and any property that doesn't yet exist on the JSON object will be initialized into something. You can then have specific functions like `addUser` onto objects with a specific structure. -- There were several possible ways to go about implementing aliases and subaliases. - - Two properties on the `Command` class, `aliases: string[]` and `subaliases: {[key: string]: string[]}`. - - Exporting a const named `aliases` which would handle top-level aliases. - - For `subaliases`, either making subaliases work like redirects (Instead of doing `new Command(...)`, you'd do `"nameOfSubcommand"`), or define properties on `Command`. -- What I ended up doing for aliases is making an `aliases` property on `Command` and then converting those string arrays to a more usable structure with strings pointing to the original commands. `aliases` at the very top will determine global aliases and is pretty much the exception in the program's logic. `aliases` elsewhere will provide aliases per subcommand. For anything other than the top-level or `subcommands`, `aliases` does nothing (plus you'll get a warning about it). diff --git a/package-lock.json b/package-lock.json index 80b5169..c4d3d3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "d.js-v12-bot", + "name": "travebot", "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "d.js-v12-bot", + "name": "travebot", "version": "0.0.1", "hasInstallScript": true, "license": "MIT", diff --git a/package.json b/package.json index a48dcd5..ab53c23 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,17 @@ { - "name": "d.js-v12-bot", + "name": "travebot", "version": "0.0.1", - "description": "A Discord bot built on Discord.JS v12", + "description": "TravBot Discord bot.", "main": "dist/index.js", + "scripts": { + "build": "tsc --project tsconfig.prod.json && npm prune --production", + "start": "node .", + "once": "tsc && npm start", + "dev": "tsc-watch --onSuccess \"node . dev\"", + "test": "jest", + "format": "prettier --write **/*", + "postinstall": "husky install" + }, "dependencies": { "canvas": "^2.7.0", "chalk": "^4.1.0", @@ -34,19 +43,10 @@ "tsc-watch": "^4.2.9", "typescript": "^3.9.7" }, - "scripts": { - "build": "tsc --project tsconfig.prod.json && npm prune --production", - "start": "node .", - "once": "tsc && npm start", - "dev": "tsc-watch --onSuccess \"node . dev\"", - "test": "jest", - "format": "prettier --write **/*", - "postinstall": "husky install" - }, + "author": "Keanu Timmermans", + "license": "MIT", "keywords": [ "discord.js", "bot" - ], - "author": "Keanu Timmermans", - "license": "MIT" + ] } From 397287ec3c148478dfe50ae41dfece9e0b00f0ac Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Wed, 7 Apr 2021 01:00:57 -0500 Subject: [PATCH 27/30] Retroactively added version history --- CHANGELOG.md | 159 ++++++++++++++++++++++++++++++++++++++---- docs/Documentation.md | 18 +++++ docs/Overview.md | 5 +- package-lock.json | 4 +- package.json | 2 +- 5 files changed, 166 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6bdb65..fa8c546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,133 @@ -# 2.8.4 - Reworked the react command +# 3.2.0 - Internal refactor, more subcommand types, and more command type guards (2021-??-??) +- The custom logger changed: `$.log` no longer exists, it's just `console.log`. Now you don't have to do `import $ from "../core/lib"` at the top of every file that uses the custom logger. +- Utility functions are no longer attached to the command menu. Stuff like `$.paginate()` and `$(5).pluralise()` instead need to be imported and used as regular functions. +- The `paginate` function was reworked to reduce the amount of repetition you had to do. +- Events are no longer loaded dynamically. What you do is `import "./some-file"` which will run the code in there, attaching the event to the client. Since events are no longer bound to certain files, you can keep them more organized: + - Since there can be multiple listeners per event, large event files can be split up into more organized blocks. + - You can also group together related events like `channelCreate` and `channelDelete` and show the relation in one single file rather than splitting them up just because they're two different events. +- Lots of files were moved around: + - The `core` folder represents the command handler and is pretty much treated as if it was an external module. That means that instead of importing different items from each file, you'd import it from its index file (which is shortened to `import {} from ../core`). My hope is to move this section to its own module eventually™. + - Other `core` files that were more or less specific to the bot were moved outside, either at the top-level or into `modules`. This includes stuff like the library file containing utility functions as well as structures for storing/loading data. Since they're at the top now, there's less typing involved in importing them (`../lib` instead of `../core/lib` and so on). + - Commands are still dynamically loaded. This won't change. +- Added more type guards to the `Command` class, reducing the amount of unused properties there are. + - If a command has `endpoint: true` specified, it'll now prevent adding subcommands at compile-time rather than relying on runtime warnings. + - Added a `NamedCommand` subclass on top-level commands (default exports) as well as keyed subcommands (basically the ones with a hardcoded value). `NamedCommand`s have access to `aliases`. Having `aliases` on something like a numeric subcommand (ie `$test 5`) doesn't really make sense. +- Added more features to the `Command` class as well: + - You can now restrict certain commands to Guild-only channels or DM-only channels. Unfortunately, there's a bug in TypeScript where callbacks don't get affected by discriminated unions. So for now, if you set a command's channel type, just do a non-null assertion on `guild` and a `TextChannel` assertion for `channel` (and vice versa). + - A command can now be designated as NSFW-only. + - Added more subcommand types: + - Channel: `<#...>` + - Role: `<@&...>` + - Emote: `` + - Message: `https://discordapp.com/channels///` or `-` from the "Copy Message Link" and "Copy ID" (shift) buttons. + - ID: Any Discord ID. In order to use this, you have to specify which subcommand type you want to redirect it to. For example, to replicate the old behavior with plain IDs being converted to user IDs, you first implement user `user: new Command(...)` then do `id: "user"`. + - Some changes to subcommands: + - User: `<@...>` and `<@!...>` - Its default state is more restricted. It no longer accepts standalone IDs by default. +- You'll notice in a lot of commands as well as the template that properties are destructured. While using `$` will work just fine, having `{message, channel, guild}` will let you access properties using `channel` instead of `$.channel` and so on. +- Direct messaging the bot now listens for commands. You don't need a prefix when doing this, it's assumed you're running a command. +- Command invocations are no longer logged every single time. Now the catch block shows the command used and the arguments, and unhandled rejections related to Discord are captured too, showing the same information. +- I added Husky and I think I've got its pre-commit hook to work. If this goes as expected, the formatter should be called every commit so there aren't any more formatting commits. +- Internally, the core message handler and the `Command` class(es) are very de-spaghettified compared to before. Its methods are a lot more modular now. +- Retroactively added version history for TravBot-v3. +- Revised documentation. + +# 3.1.10 (2021-04-06) +- Ported the rest of features from TravBot-v2 +- Prototyped stream notifications +- Added eco bet command +- Added several entries to the `whois` list +- Added functionality for reacting to in-line replies + +# 3.1.9 (2021-03-28) +- Stops deleting the `emote` invocation +- Added channel lock for `eco` +- Listens for CheeseBot's "Remember to drink water!" message and reacts with 🚱 +- Added message quoting +- Added sandboxed regex query to `lsemotes` with timeout +- Added `info guild` for other guilds + +# 3.1.8 - Introduce a terrible hack to reduce memory usage and a few other less significant changes (2021-02-16) +- Add the titular hack™️ aka "pulling CC modding on discord.js". +- Reduce the usage of caches where possible (don't remember whether I eliminated all of them or not; note that guild members, roles and emojis can be assumed to be always cached if the guild object is available), especially in the info command (because I have effectively broken the automatic members cache with the titular hack). Also get rid of calls to `BaseManager#resolve` to make cache lookups explicit. +- Get rid of usages of `@ts-ignore` (never do this, or I'll kill you!!!). +- Enable sourcemaps for seeing the source code lines in the error stack traces. +- Raise the target JS edition to ES2019 since Node.js installed on the deployment machine is version 15.x anyway. + +# 3.1.7 - Added time command for user-submitted timezones (2021-01-25) + +# 3.1.6 - Added emote dumper (2021-01-03) + +# 3.1.5 - Attempting to fix the emote name resolution (2020-12-20) +This is an attempt at fixing some notable problems with the `emote` and `react` commands, including the following: +- `leaSMUG` would resolve to `leaSmug` (should be fixed by taking capitalization into account and making an offset number from each difference) +- `leaCheese` would resolve to `leaCheeseAngry` (should be fixed by taking length into account in its heuristics) + +# 3.1.4 - New formatter settings, hotfix, and feature (2020-12-16) +- Added `eco monday` +- Fixed `eval` command +- Ported `eco guild` +- Added more message linking options to `react` +- Added formatter +- Fixed error prevention in `neko` command + +# 3.1.3 - Ported the eco command (2020-12-15) +- Ported the `eco` command, as well as `eco shop` and `eco buy` +- Public Rollout + +# 3.1.2 - Added music functionality and ported more commands (2020-10-24) +- Added music functionality via Lavalink +- Ported the following commands: + - `lsemotes` + - `shorten` + - `eval` + - `info bot` + - `admin clear` + - `cookie` + - `neko` + - `ok` + - `owoify` + - `desc` + - `react` + - `say` +- Bug fixes to `info guild` + +# 3.1.1 - Began the porting process (2020-09-11) +- Ported the following commands: + - `info` + - `8ball` + - `poll` + - `lsemotes` + - `scanemotes` +- Ported the following commands to `admin`: + - `status` + - `purge` + - `nick` + - `guilds` + - `activity` +- Added pinging bot for prefix and var string prefix +- Added removing emotes in paginate if possible +- Added command aliases +- Added CodeQL +- Modularized finding members by their username +- Added documentation +- Added Docker support + +# 3.1.0 - Restructured the project according to CrossExchange (2020-07-26) +Ported over CrossExchange v1.0.1 and added several additional features: +- Command Categories: This follows suit of [the pre-rework command structure](https://github.com/keanuplayz/TravBot-v3/tree/pre-typescript/src/Commands), where you have categories (now `utility` which gets capitalized to `Utility` for example) and miscellaneous commands. + - `subcommands` will be a reserved directory name to allow you to split up big command files into smaller ones. `commands/subcommands` is ignored as does `commands/utility/subcommands`. + - The way you'd work with splitting up these commands is that instead of doing `export default new Command(...)`, you would instead do: + - `subcommands/part1.ts`: `export default new Command(...)` + - `main.ts`: `import sub from "./subcommands/part1"; const a = new Command(...); a.attach("layer", sub); export default a;` +- Command Permissions: These permissions will work with the recursive structure as well because it'd be useful to section off different subcommands into different permissions. For example, everyone has access to `.money` but if you want to add `.money set ` (better for organization), you'd simply assign a property to `.money set` and it'd affect everything below it unless overridden. See `admin.ts` for an example on how this works. +- Dynamically-Loaded Events: All events now read from the `events` folder. If you want to access the client, you can do so by importing it from the index. (`import {client} from "../index";`) + +# 3.0.0 - Brainstormed first structure (2020-07-08) +- Adds folder-separated command categories. +- Adding commands now involves instantiating classes rather than exporting a function with some settings. +- Adds structures for better organization of commonly used classes like `Command` and `Event`. + +# 2.8.4 - Reworked the react command (2020-09-05) - `react` is now a fully versatile command for helping you react to other messages with non-server emotes. - Now properly reacts to the previous message (bug fix). - Provides you the option to react to any number of messages before your message (3 messages above yours for example). @@ -12,9 +141,9 @@ - Now reacts to your message with ❓ instead of `None of those emote names were valid!` so that the bot doesn't spam the chat if you can't find the right emote (because you'll still be able to delete your messages). - `thonk` now stores the last specified phrase so you can repeat a phrase with different diacritics. -# 2.8.3 - The ultimate meme +# 2.8.3 - The ultimate meme (2020-08-08) -# 2.8.2 +# 2.8.2 (2020-07-01) - Added a changelog. - Added an extra instruction to the readme's installation. - Made commands utilize the existing `Array.random()` function. @@ -25,14 +154,14 @@ - Fixed a bug with `emote` where it wouldn't find any upper case emotes and made it more lenient to just include any emote (so you don't have to remember the exact emote name). - Moved lists and gathering shop items outside of `exports.run()` so that it initializes once during the bot's initialization (or when reloaded) rather than every time the command is called. -# 2.8.1 - Modularized eco shop and eco buy +# 2.8.1 - Modularized eco shop and eco buy (2020-06-30) - Fixed scanemotes sometimes not displaying all emotes. This was an issue of not accounting for whether an emote was animated or not. - Modularized `eco shop` and `eco buy`. Shop items are now in the `shop` subfolder and `eco shop` now separates every 5 shop items into separate pages automatically. -# 2.8.0 - Added graphical welcome setting +# 2.8.0 - Added graphical welcome setting (2020-06-29) - Adds a new option to the `set` and `conf` commands, allowing you to enable an image being sent as a welcome. -# 2.7.1 - Added eco buy laser bridge and reworked scanemotes +# 2.7.1 - Added eco buy laser bridge and reworked scanemotes (2020-06-28) - `eco buy laser bridge` - Added a shop item. Buy what is technically a laser bridge. Costs 3 Mons. - `insult` - Now pings the user who activated it. - `scanemotes` - Reworked the command after a test run in a big server. @@ -42,7 +171,7 @@ - Now includes all emotes in a server, even if they haven't been used. - Now properly counts emote usage for reactions (whether or not a bot reacted to a message) -# 2.7.0 - Added percentages to scanemotes +# 2.7.0 - Added percentages to scanemotes (2020-06-26) ## Major Changes - Added an hour long cooldown to `scanemotes` per server because it's a very memory-intensive task to search through every single message. - Added a second list of emotes to `scanemotes`, sorting by percentage of users-only usage. @@ -62,17 +191,17 @@ - Added error checking to `scanemotes` so users aren't left in the dark if something happens. - On big servers, `scanemotes` should now have emotes actually show up. -# 2.6.1 - Hotfix: Scan emotes no longer requires admin +# 2.6.1 - Hotfix: Scan emotes no longer requires admin (2020-06-22) - Fixed the `scanemotes` command to no longer require admin permissions. This was due to an oversight: There can be channels which the bot doesn't have access to, ie private channels. You have to check if the bot has access to a channel because the filter will gather all text-based channels regardless. Admin permissions overrides all restrictions, which is why it only worked with admin permissions. - Entering a username in the `avatar` command unsuccessfully will now send a message in chat. -# 2.6.0 - Added the ability to get other users' avatars and see emote usage +# 2.6.0 - Added the ability to get other users' avatars and see emote usage (2020-06-19) - You can now scan the current guild for emote usage, collecting all emotes used in messages and reactions. (example below) ![2020-06-19 04_08_22-Window](https://user-images.githubusercontent.com/44940783/85116219-98a69280-b1e2-11ea-9246-b8f5ff2537ea.png) - You can now get other avatars by providing an ID (works even when the bot doesn't share the same server as that user), username, or by pinging them. - Included the fix for `serverinfo`. -# 2.5.3 +# 2.5.3 (2020-06-16) - Changed default prefix for setup. - Enhanced `react` command. New optional guildID arg. - Added message logger. @@ -82,27 +211,27 @@ - Added images to logger. Added author to logger. - Emote command is now not case-sensitive. -# 2.5.2 - Bug fixes to the "eco" command +# 2.5.2 - Bug fixes to the "eco" command (2020-06-01) - Now prevents users from sending negative amounts of money to others (minimum of 1 Mon). - Also prevents users from sending decimal amounts. - Fixes a potentially wrong substring for user IDs. - Now requires an argument when using the "desc" command. -# 2.5.1 +# 2.5.1 (2020-05-18) - Added shop functionality to eco. - Fixed faulty guild check. - Attempt at fixing emote for eco cute. - Pluralised "mon" for eco handhold. - Added `translate` command. -# 2.5.0 - Added the "pay" sub-command to "eco" +# 2.5.0 - Added the "pay" sub-command to "eco" (2020-05-09) -# 2.4.1 +# 2.4.1 (2020-04-18) - Added Procfile. - Updated whoami's keys. - Rewrote `desc` command. -# 2.4.0 - Implemented music system +# 2.4.0 - Implemented music system (2020-04-11) - VC Rename command - Travis CI configuration - Music system diff --git a/docs/Documentation.md b/docs/Documentation.md index cf91fb2..8f45a7a 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -13,6 +13,24 @@ - `data`: Holds all the dynamic/private data used by the bot. This folder is not meant to hold definitions. - `docs`: Information for developers who want to contribute. +# Version Numbers + +When a new version is ready to be declared... +- ...update the [changelog](../CHANGELOG.md). +- ...update the version numbers in [package.json](../package.json) and [package-lock.json](../package-lock.json). + +## Naming Versions + +Because versions are assigned to batches of changes rather than single changes (or even single commits), versioning is used a bit differently in order to avoid wasting version numbers. + +`..-` +- `` is a defined as the overarching version group of TravBot. TravBot-v2 went by `2.x.x` and all versions of TravBot-v3 will go by `3.x.x`. +- `` includes any big overhauls or revisions of the entire codebase. +- `` includes any feature additions in a specific area of the codebase. +- `` will be pretty much for any very small changes like a quick bug fix or typos. *Note: Normally, these would probably get grouped up, but if there hasn't been a proper version in a while, this will get pushed as a patch.* + +*Note: This system doesn't retroactively apply to TravBot-v2, which is why this version naming system won't make sense for v2's changelog.* + # Cleanup is Soon™ ## Convenience Functions diff --git a/docs/Overview.md b/docs/Overview.md index 19e0981..f6854c4 100644 --- a/docs/Overview.md +++ b/docs/Overview.md @@ -16,10 +16,7 @@ This is a brief overview that'll describe where and how to add new features to T *Note: Make sure to avoid using `npm run build`! This will remove all your dev dependencies (in order to reduce space used). Instead, use `npm run once` to compile and build in non-dev mode.* -## Don't forget to... - -- ...update the [changelog](../CHANGELOG.md) and any other necessary docs. -- ...update the version numbers in [package.json](../package.json) and [package-lock.json](../package-lock.json). +*Note: If you update one of the APIs or utility functions, make sure to update the [documentation](Documentation.md).* # Adding a new command diff --git a/package-lock.json b/package-lock.json index c4d3d3a..a2b61d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "travebot", - "version": "0.0.1", + "version": "3.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "travebot", - "version": "0.0.1", + "version": "3.2.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index ab53c23..4903443 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "travebot", - "version": "0.0.1", + "version": "3.2.0", "description": "TravBot Discord bot.", "main": "dist/index.js", "scripts": { From 5a64aed45d2c76bb559179ffbc7d3f975291d760 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Wed, 7 Apr 2021 04:58:13 -0500 Subject: [PATCH 28/30] Filled out design decisions doc --- docs/DesignDecisions.md | 126 +++++++++++++++++++++++++++++++++++++++- docs/Documentation.md | 7 +++ docs/Overview.md | 4 +- src/core/command.ts | 32 +++++----- src/core/libd.ts | 11 ++++ 5 files changed, 164 insertions(+), 16 deletions(-) diff --git a/docs/DesignDecisions.md b/docs/DesignDecisions.md index 27306bf..4a2bbd5 100644 --- a/docs/DesignDecisions.md +++ b/docs/DesignDecisions.md @@ -1 +1,125 @@ -Coming Soon™ +# Using the Command Class + +## any[] Parameters For Subcommand Run + +Unless there's some sort of TypeScript wizardry to solve this, the `args` parameter in the subcommand type will have to be `any[]` because it's simply too context-dependent to statically figure it out. +- Each subcommand is its own layer which doesn't know about parent commands at compile-time. +- Subcommands can be split into different files for code maintainability. +- Even though the last argument is able to be strongly-typed, if you have multiple parameters, you'd essentially only get static benefits for one of the arguments, and you wouldn't even know the location of that one argument. +- Overall, it's just easier to use your best judgement then use type assertions. + +## Channel Type Type Guards + +As of right now, it's currently not feasible to implement type guards for channel types. [Discriminated unions with a default parameter don't work with callbacks.](https://github.com/microsoft/TypeScript/issues/41759) In order to implement type guards, the `channelType` parameter would have to be required, making each command layer quite tedious. + +So instead, use non-null assertions when setting the `channelType`. For example: + +```ts +import {Command, NamedCommand, CHANNEL_TYPE} from "../core"; +import {TextChannel} from "discord.js"; + +export default new NamedCommand({ + channelType: CHANNEL_TYPE.GUILD, + async run({message, channel, guild, author, member, client, args}) { + console.log(guild!.name); + console.log(member!.nickname); + console.log((channel as TextChannel).name !== "dm"); + } +}); +``` + +```ts +import {Command, NamedCommand, CHANNEL_TYPE} from "../core"; +import {DMChannel} from "discord.js"; + +export default new NamedCommand({ + channelType: CHANNEL_TYPE.DM, + async run({message, channel, guild, author, member, client, args}) { + console.log(guild === null); + console.log(member === null); + console.log((channel as DMChannel).type === "dm"); + } +}); +``` + +The three guarantees are whether or not `guild` will be `null`, whether or not `member` will be `null`, and the type of `channel`. + +*Take note that `member` can still be `null` even in a guild (for example, if you target a message by someone who left), `member` cannot be `null` here because the `message` being sent must be by someone who is in the guild by this point.* + +## Uneven Return Paths + +`Command.run` doesn't use the return values for anything, so it's safe to do `return channel.send(...)` to merge those two statements. However, you'll come across an error: `Not all code paths return a value.` + +There are several ways to resolve this issue: +- Split all `return channel.send(...)` statements to `{channel.send(...); return;}` +- Set an explicit any return type in the function header: `async run(...): Promise {` +- Add an extra `return` statement at the end of each path + +## Type Guards + +The `Command` class is implemented in a certain way to provide type guards which reduce unnecessary properties at compile-time rather than warning the user at runtime. +- The reason `NamedCommand` (which extends `Command`) exists is to provide a type guard for `aliases`. After all, `aliases` doesn't really make sense for generic subcommand types - how would you handle an alias for a type that accepts a number for example? +- The `endpoint` property changes what other properties are available via a discriminated union. If `endpoint` is `true`, no subcommands of any type can be defined. After all, it wouldn't make sense logically. + +## Boolean Types + +Boolean subcommand types won't be implemented: +- Since there are only two values, why not just put it under `subcommands`? +- If boolean types were to be implemented, how many different types of input would have to be considered? `yes`/`no`, `y`/`n`, `true`/`false`, `1`/`0`, etc. + +## Hex and Octal Number Types + +For common use cases, there wouldn't be a need to go accept numbers of different bases. The only time it would be applicable is if there was some sort of base converter command, and even then, it'd be better to just implement custom logic. + +# The Command Handler + +## The Scope of the Command Handler + +What this does: +- Provides the `Command`/`NamedCommand` classes. +- Dynamically loads commands and attaches runtime metadata. +- Provides utility functions specific to Discord to make certain patterns of commands less tedious to implement. + +What this doesn't do: +- Manage the general file system or serialization/deserialization of data. +- Provide general utility functions. +- Provide any Discord-related functionality besides strictly command handling. + +## Client Creation + +Creating the client is beyond the scope of the command handler and will not be abstracted away. Instead, the user will simply attach the command handler to the client to initialize it. +- This makes it so if a user wants to specify their own `ClientOptions` when instantiating the client, it's less troublesome to implement. +- The user can export the client and use it throughout different parts of their code. + +## Bot-Specific Mentions + +Pinging the bot will display the current guild prefix. The bot mention will not serve as an alternate prefix. +- When talking about a bot, the bot might be pinged to show who it is. It could be in the middle (so don't listen for a prefix anywhere) or it could be at the start (so only listen to a standalone ping). +- It likely isn't a common use case to ping the bot. The only time it would really shine is in the event two bots have a prefix conflict, but the command that changes prefixes can simply add a parameter to deal with that case. For example, instead of `@bot set prefix `, you'd use `set prefix @bot`. + +## Direct Messages + +When direct messaging a bot, no prefixes will be used at all because it's assumed that you're executing a command. Because the only people allowed is the user and the bot, NSFW-only commands can also be executed here. + +## Permission Setup + +Because the command handler provides no specific permission set, it's up to the user to come up with functions to add permissions as well as create the enum that assigns permissions. +- The `permission` property of a `Command` instance is `-1` by default, which means to inherit the permission level from the parent command. If you want, you can create your enum like this: `enum Permissions {INHERIT = -1, USER, ADMIN}`, where `Permissions.USER = 0` and `Permissions.ADMIN = 1`. + +# Miscellaneous + +## Static Event Loading + +While dynamic loading fits very well with commands, it was more or less clunky design to try and make events fit the same model: +- There are no restrictions when it comes to command names, and the name of the file will determine the name of the command, which avoids repetition. Events on the other hand involved lots of boilerplate to get static types back. +- Since there can be multiple listeners per event, large event files can be split up into more organized blocks. +- Likewise, small event listeners which span multiple events can be grouped together like `channelCreate` and `channelDelete`, showing the relation in one single file rather than splitting them up just because they're two different events. + +## Testing + +For TravBot, there'll be two types of tests: standard unit tests and manual integration tests. +- Standard unit tests are executed only on isolated functions and are part of the pre-commit hook. +- Somehow, including the bot in an import chain will cause the system to crash (same error message as [this](https://stackoverflow.com/questions/66102858/discord-clientuser-is-not-a-constructor)). That's why the integration tests are manually done. There would be a list of inputs and outputs to check of each command for tests while simultaneously serving as a help menu with examples of all possible inputs/outputs for others to see. +- An idea which will not be implemented is prompting the user for inputs during the tests. This is no better than manual tests, worse actually, because if this had to run before each commit, it'd quickly become a nightmare. +- Maybe take some ideas from something like [this](https://github.com/stuyy/jest-unit-tests-demo) in the future to get tests to properly work. +- Another possibility is to use `client.emit(...)` then mock the `message.channel.send(...)` function which would listen if the input is correct. diff --git a/docs/Documentation.md b/docs/Documentation.md index 8f45a7a..b61381c 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -31,6 +31,13 @@ Because versions are assigned to batches of changes rather than single changes ( *Note: This system doesn't retroactively apply to TravBot-v2, which is why this version naming system won't make sense for v2's changelog.* +# Message Subcommand Type + +- `https://discord.com/channels///` comes from the `Copy Message Link` button. +- `-` comes from holding `Shift` on the desktop application and clicking on the `Copy ID` button. + + + # Cleanup is Soon™ ## Convenience Functions diff --git a/docs/Overview.md b/docs/Overview.md index f6854c4..5b57397 100644 --- a/docs/Overview.md +++ b/docs/Overview.md @@ -72,7 +72,7 @@ export default new NamedCommand({ Here, . For example, if this file was named `test.ts`, `$test <@237359961842253835>` would get the user by the ID `237359961842253835` into `args[0]` as a [User](https://discord.js.org/#/docs/main/stable/class/User) object. `$test experiment` would run as if you just called `$test` *(given that [endpoint](Documentation.md#) isn't set to `true`)*. -If you want, you can typecast the argument to be more strongly typed, because the type of `args` is `any[]`. *([See why if you're curious.](DesignDecisions.md#))* +If you want, you can typecast the argument to be more strongly typed, because the type of `args` is `any[]`. *([See why if you're curious.](DesignDecisions.md#any[]-parameters-for-subcommand-run))* ```ts import {Command, NamedCommand} from "../core"; @@ -201,7 +201,7 @@ This will just run whatever code is in there. ## Listening for events -Rather than have an `events` folder which contains dynamically loaded events, you add an event listener directly via `client.on("...", () => {})`. *([See why if you're curious.](DesignDecisions.md#))* The client can be imported from the index file. +Rather than have an `events` folder which contains dynamically loaded events, you add an event listener directly via `client.on("...", () => {})`. *([See why if you're curious.](DesignDecisions.md#static-event-loading))* The client can be imported from the index file. ```ts import {client} from ".."; diff --git a/src/core/command.ts b/src/core/command.ts index ba89c71..0835da9 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -139,6 +139,7 @@ export const defaultMetadata = { channelType: CHANNEL_TYPE.ANY }; +// Each Command instance represents a block that links other Command instances under it. export class Command { public readonly description: string; public readonly endpoint: boolean; @@ -146,17 +147,19 @@ export class Command { public readonly permission: number; // -1 (default) indicates to inherit, 0 is the lowest rank, 1 is second lowest rank, and so on. public readonly nsfw: boolean | null; // null (default) indicates to inherit public readonly channelType: CHANNEL_TYPE | null; // null (default) indicates to inherit - protected run: (($: CommandMenu) => Promise) | string; - protected readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. - protected channel: Command | null; - protected role: Command | null; - protected emote: Command | null; - protected message: Command | null; - protected user: Command | null; - protected id: Command | null; - protected idType: ID | null; - protected number: Command | null; - protected any: Command | null; + // The execute and subcommand properties are restricted to the class because subcommand recursion could easily break when manually handled. + // The class will handle checking for null fields. + private run: (($: CommandMenu) => Promise) | string; + private readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. + private channel: Command | null; + private role: Command | null; + private emote: Command | null; + private message: Command | null; + private user: Command | null; + private id: Command | null; + private idType: ID | null; + private number: Command | null; + private any: Command | null; constructor(options?: CommandOptions) { this.description = options?.description || "No description."; @@ -240,6 +243,9 @@ export class Command { // Go through the arguments provided and find the right subcommand, then execute with the given arguments. // Will return null if it successfully executes, SingleMessageOptions if there's an error (to let the user know what it is). + // + // Calls the resulting subcommand's execute method in order to make more modular code, basically pushing the chain of execution to the subcommand. + // For example, a numeric subcommand would accept args of [4] then execute on it. public async execute( args: string[], menu: CommandMenu, @@ -261,12 +267,12 @@ export class Command { // 1. Does this command specify a required channel type? If so, does the channel type match? if ( metadata.channelType === CHANNEL_TYPE.GUILD && - (!(menu.channel instanceof GuildChannel) || menu.guild === null) + (!(menu.channel instanceof GuildChannel) || menu.guild === null || menu.member === null) ) { return {content: "This command must be executed in a server."}; } else if ( metadata.channelType === CHANNEL_TYPE.DM && - (menu.channel.type !== "dm" || menu.guild !== null) + (menu.channel.type !== "dm" || menu.guild !== null || menu.member !== null) ) { return {content: "This command must be executed as a direct message."}; } diff --git a/src/core/libd.ts b/src/core/libd.ts index 3746b6a..a8df915 100644 --- a/src/core/libd.ts +++ b/src/core/libd.ts @@ -247,3 +247,14 @@ export async function callMemberByUsername( else send(`Couldn't find a user by the name of \`${username}\`!`); } else send("You must execute this command in a server!"); } + +// TO DO Section // + +// getGuildByID() - checks for guild.available (boolean) +// getGuildByName() +// findMemberByNickname() - gets a member by their nickname or their username +// findUserByUsername() + +// For "get x by y" methods: +// Caching: All guilds, channels, and roles are fully cached, while the caches for messages, users, and members aren't complete. +// It's more reliable to get users/members by fetching their IDs. fetch() will searching through the cache anyway. From e7dfab75927bc6599207b7c4e49162c20edf7007 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Wed, 7 Apr 2021 05:58:09 -0500 Subject: [PATCH 29/30] Updated the documentation --- docs/Documentation.md | 120 +++++++++++++++++----------------------- docs/Overview.md | 8 +-- src/core/libd.ts | 17 +++++- src/core/permissions.ts | 7 +++ src/modules/storage.ts | 1 + src/structures.ts | 4 ++ 6 files changed, 82 insertions(+), 75 deletions(-) diff --git a/docs/Documentation.md b/docs/Documentation.md index b61381c..d035587 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -1,6 +1,12 @@ # Table of Contents -... +- [Structure](#structure) +- [Version Numbers](#version-numbers) +- [Message Subcommand Type](#message-subcommand-type) +- [Command Menu](#command-menu) +- [Command Metadata](#command-metadata) +- [Command Var String](#command-var-string) +- [Utility Functions](#utility-functions) # Structure @@ -36,96 +42,70 @@ Because versions are assigned to batches of changes rather than single changes ( - `https://discord.com/channels///` comes from the `Copy Message Link` button. - `-` comes from holding `Shift` on the desktop application and clicking on the `Copy ID` button. +# Command Menu +- `args`: A list of arguments in the command. It's relative to the subcommand, so if you do `$test this 5`, `5` becomes `$.args[0]` if `this` is a subcommand. Args are already converted, so a `number` subcommand would return a number rather than a string. +- `client`: `message.client` +- `message`: `message` +- `channel`: `message.channel` +- `guild`: `message.guild` +- `author`: `message.author` +- `member`: `message.member` -# Cleanup is Soon™ +# Command Metadata -## Convenience Functions +- `description`: The command description that'll appear in the help menu. +- `endpoint`: Whether or not any arguments are allowed after the command. +- `usage`: Defines a custom usage when showing the command in the help menu. +- `permission`: *(Inherits)* -1 (default) indicates to inherit, 0 is the lowest rank, 1 is second lowest rank, and so on. +- `nsfw`: *(Inherits)* Whether or not the command is restricted to NSFW channels and DM channels. +- `channelType`: *(Inherits)* Whether the command is restricted to guild channels, DM channels, or has no restriction. Uses the `CHANNEL_TYPE` enum provided by the command handler. -This modularizes certain patterns of code to make things easier. +# Command Var String -- `$.paginate()`: Takes a message and some additional parameters and makes a reaction page with it. All the pagination logic is taken care of but nothing more, the page index is returned and you have to send a callback to do something with it. +- `%author%` - A user mention of the person who called the command. +- `%prefix%` - The prefix of the current guild. -```js +# Utility Functions + +## [src/core (libd)](../src/core/libd.ts) - Utility functions specific for working with Discord + +`paginate()` +```ts const pages = ['one', 'two', 'three']; -const msg = await $.channel.send(pages[0]); +const msg = await channel.send(pages[0]); -$.paginate(msg, $.author.id, pages.length, (page) => { - msg.edit(pages[page]); +paginate(msg, author.id, pages.length, (page) => { + msg.edit(pages[page]); }); ``` -- `$.prompt()`: Prompts the user about a decision before following through. +`prompt()` +```ts +const msg = await channel.send('Are you sure you want to delete this?'); -```js -const msg = await $.channel.send('Are you sure you want to delete this?'); - -$.prompt(msg, $.author.id, () => { - delete this; // Replace this with actual code. +prompt(msg, author.id, () => { + //... }); ``` -- `$.getMemberByUsername()`: Gets a user by their username. Gets the first one then rolls with it. -- `$.callMemberByUsername()`: Convenience function to handle cases where someone isn't found by a username automatically. - -```js -$.callMemberByUsername($.message, $.args.join(' '), (member) => { - $.channel.send(`Your nickname is ${member.nickname}.`); +`callMemberByUsername()` +```ts +callMemberByUsername(message, args.join(" "), (member) => { + channel.send(`Your nickname is ${member.nickname}.`); }); ``` -## Dynamic Properties +## [src/lib](../src/lib.ts) - General utility functions -These will be accessible only inside a `Command` and will change per message. - -- `$.args`: A list of arguments in the command. It's relative to the subcommand, so if you do `$test this 5`, `5` becomes `$.args[0]` if `this` is a subcommand. Args are already converted, so a `number` subcommand would return a number rather than a string. -- `$.client`: `message.client` -- `$.message`: `message` -- `$.channel`: `message.channel` -- `$.guild`: `message.guild` -- `$.author`: `message.author` -- `$.member`: `message.member` - -# Wrappers - -This is similar to modifying a primitive object's `prototype` without actually doing so. - -## NumberWrapper - -- `.pluralise()`: A substitute for not having to do `amount === 1 ? "singular" : "plural"`. For example, `$(x).pluralise("credit", "s")` will return `"1 credit"` and/or `"5 credits"` respectively. -- `.pluraliseSigned()`: This builds on `.pluralise()` and adds a sign at the beginning for marking changes/differences. `$(0).pluraliseSigned("credit", "s")` will return `"+0 credits"`. - -## StringWrapper - -- `.replaceAll()`: A non-regex alternative to replacing everything in a string. `$("test").replaceAll('t', 'z')` = `"zesz"`. -- `.toTitleCase()`: Capitalizes the first letter of each word. `$("this is some text").toTitleCase()` = `"This Is Some Text"`. - -## ArrayWrapper - -- `.random()`: Returns a random element from an array. `$([1,2,3]).random()` could be any one of those elements. -- `.split()`: Splits an array into different arrays by a specified length. `$([1,2,3,4,5,6,7,8,9,10]).split(3)` = `[[1,2,3],[4,5,6],[7,8,9],[10]]`. - -# Other Library Functions - -These do have to be manually imported, which are used more on a case-by-case basis. - -- `formatTimestamp()`: Formats a `Date` object into your system's time. `YYYY-MM-DD HH:MM:SS` -- `formatUTCTimestamp()`: Formats a `Date` object into UTC time. `YYYY-MM-DD HH:MM:SS` -- `botHasPermission()`: Tests if a bot has a certain permission in a specified guild. - `parseArgs()`: Turns `call test "args with spaces" "even more spaces"` into `["call", "test", "args with spaces", "even more spaces"]`, inspired by the command line. - `parseVars()`: Replaces all `%` args in a string with stuff you specify. For example, you can replace all `nop` with `asm`, and `register %nop%` will turn into `register asm`. Useful for storing strings with variables in one place them accessing them in another place. - `isType()`: Used for type-checking. Useful for testing `any` types. - `select()`: Checks if a variable matches a certain type and uses the fallback value if not. (Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this!) - `Random`: An object of functions containing stuff related to randomness. `Random.num` is a random decimal, `Random.int` is a random integer, `Random.chance` takes a number ranging from `0` to `1` as a percentage. `Random.sign` takes a number and has a 50-50 chance to be negative or positive. `Random.deviation` takes a number and a magnitude and produces a random number within those confines. `(5, 2)` would produce any number between `3` and `7`. - -# Other Core Functions - -- `permissions::hasPermission()`: Checks if a `Member` has a certain permission. -- `permissions::getPermissionLevel()`: Gets a `Member`'s permission level according to the permissions enum defined in the file. -- `structures::getPrefix()`: Get the current prefix of the guild or the bot's prefix if none is found. - -# The other core files - -- `core/permissions`: Contains all the permission roles and checking functions. -- `core/structures`: Contains all the code handling dynamic JSON data. Has a one-to-one connection with each file generated, for example, `Config` which calls `super("config")` meaning it writes to `data/config.json`. -- `core/storage`: Handles most of the file system operations, all of the ones related to `data` at least. +- `pluralise()`: A substitute for not having to do `amount === 1 ? "singular" : "plural"`. For example, `pluralise(x, "credit", "s")` will return `"1 credit"` and/or `"5 credits"` respectively. +- `pluraliseSigned()`: This builds on `pluralise()` and adds a sign at the beginning for marking changes/differences. `pluraliseSigned(0, "credit", "s")` will return `"+0 credits"`. +- `replaceAll()`: A non-regex alternative to replacing everything in a string. `replaceAll("test", "t", "z")` = `"zesz"`. +- `toTitleCase()`: Capitalizes the first letter of each word. `toTitleCase("this is some text")` = `"This Is Some Text"`. +- `random()`: Returns a random element from an array. `random([1,2,3])` could be any one of those elements. +- `split()`: Splits an array into different arrays by a specified length. `split([1,2,3,4,5,6,7,8,9,10], 3)` = `[[1,2,3],[4,5,6],[7,8,9],[10]]`. diff --git a/docs/Overview.md b/docs/Overview.md index 5b57397..31935f1 100644 --- a/docs/Overview.md +++ b/docs/Overview.md @@ -44,7 +44,7 @@ export default new NamedCommand({ ### Quick note on the run property -You can also enter a string for the `run` property which will send a message with that string specified ([you can also specify some variables in that string](Documentation.md#)). The above is functionally equivalent to the below. +You can also enter a string for the `run` property which will send a message with that string specified ([you can also specify some variables in that string](Documentation.md#command-var-string)). The above is functionally equivalent to the below. ```ts import {Command, NamedCommand} from "../core"; @@ -70,7 +70,7 @@ export default new NamedCommand({ }); ``` -Here, . For example, if this file was named `test.ts`, `$test <@237359961842253835>` would get the user by the ID `237359961842253835` into `args[0]` as a [User](https://discord.js.org/#/docs/main/stable/class/User) object. `$test experiment` would run as if you just called `$test` *(given that [endpoint](Documentation.md#) isn't set to `true`)*. +Here, . For example, if this file was named `test.ts`, `$test <@237359961842253835>` would get the user by the ID `237359961842253835` into `args[0]` as a [User](https://discord.js.org/#/docs/main/stable/class/User) object. `$test experiment` would run as if you just called `$test` *(given that [endpoint](Documentation.md#command-metadata) isn't set to `true`)*. If you want, you can typecast the argument to be more strongly typed, because the type of `args` is `any[]`. *([See why if you're curious.](DesignDecisions.md#any[]-parameters-for-subcommand-run))* @@ -171,7 +171,7 @@ export default new NamedCommand({ Here, the property `channelType` would spread to all subcommands unless a subcommand defines it. Using the above example, the `channelType` for both `$siege` and `$siege pineapple` would be `CHANNEL_TYPE.GUILD`. -*To get a full list of metadata properties, see the [documentation](Documentation.md#).* +*To get a full list of metadata properties, see the [documentation](Documentation.md#command-menu).* ## Utility Functions @@ -187,7 +187,7 @@ export default new NamedCommand({ }); ``` -*To get a full list of utility functions, see the [documentation](Documentation.md#).* +*To get a full list of utility functions, see the [documentation](Documentation.md#utility-functions).* # Adding a new non-command feature diff --git a/src/core/libd.ts b/src/core/libd.ts index a8df915..84d6ea2 100644 --- a/src/core/libd.ts +++ b/src/core/libd.ts @@ -13,6 +13,9 @@ import {unreactEventListeners, replyEventListeners} from "./eventListeners"; export type SingleMessageOptions = MessageOptions & {split?: false}; +/** + * Tests if a bot has a certain permission in a specified guild. + */ export function botHasPermission(guild: Guild | null, permission: number): boolean { return !!guild?.me?.hasPermission(permission); } @@ -21,6 +24,10 @@ export function botHasPermission(guild: Guild | null, permission: number): boole // Pagination function that allows for customization via a callback. // Define your own pages outside the function because this only manages the actual turning of pages. + +/** + * Takes a message and some additional parameters and makes a reaction page with it. All the pagination logic is taken care of but nothing more, the page index is returned and you have to send a callback to do something with it. + */ export async function paginate( channel: TextChannel | DMChannel | NewsChannel, senderID: string, @@ -90,6 +97,9 @@ export async function paginate( // Waits for the sender to either confirm an action or let it pass (and delete the message). // This should probably be renamed to "confirm" now that I think of it, "prompt" is better used elsewhere. // Append "\n*(This message will automatically be deleted after 10 seconds.)*" in the future? +/** + * Prompts the user about a decision before following through. + */ export async function prompt(message: Message, senderID: string, onConfirm: () => void, duration = 10000) { let isDeleted = false; @@ -222,6 +232,9 @@ export async function askMultipleChoice( if (!isDeleted) message.delete(); } +/** + * Gets a user by their username. Gets the first one then rolls with it. + */ export async function getMemberByUsername(guild: Guild, username: string) { return ( await guild.members.fetch({ @@ -231,7 +244,9 @@ export async function getMemberByUsername(guild: Guild, username: string) { ).first(); } -/** Convenience function to handle false cases automatically. */ +/** + * Convenience function to handle cases where someone isn't found by a username automatically. + */ export async function callMemberByUsername( message: Message, username: string, diff --git a/src/core/permissions.ts b/src/core/permissions.ts index fb430c8..ba3be33 100644 --- a/src/core/permissions.ts +++ b/src/core/permissions.ts @@ -1,12 +1,19 @@ +// Contains all the permission roles and checking functions. import {User, GuildMember} from "discord.js"; import {permissionLevels} from "./interface"; +/** + * Checks if a `Member` has a certain permission. + */ export function hasPermission(user: User, member: GuildMember | null, permission: number): boolean { for (let i = permissionLevels.length - 1; i >= permission; i--) if (permissionLevels[i].check(user, member)) return true; return false; } +/** + * Gets a `Member`'s permission level according to the permissions enum defined in the file. + */ export function getPermissionLevel(user: User, member: GuildMember | null): number { for (let i = permissionLevels.length - 1; i >= 0; i--) if (permissionLevels[i].check(user, member)) return i; return 0; diff --git a/src/modules/storage.ts b/src/modules/storage.ts index 8553fc6..c38ddc6 100644 --- a/src/modules/storage.ts +++ b/src/modules/storage.ts @@ -1,3 +1,4 @@ +// Handles most of the file system operations, all of the ones related to `data` at least. import fs from "fs"; const Storage = { diff --git a/src/structures.ts b/src/structures.ts index f764d5a..351bbb6 100644 --- a/src/structures.ts +++ b/src/structures.ts @@ -1,3 +1,4 @@ +// Contains all the code handling dynamic JSON data. Has a one-to-one connection with each file generated, for example, `Config` which calls `super("config")` meaning it writes to `data/config.json`. import FileManager from "./modules/storage"; import {select, GenericJSON, GenericStructure} from "./lib"; import {watch} from "fs"; @@ -145,6 +146,9 @@ if (IS_DEV_MODE) { }); } +/** + * Get the current prefix of the guild or the bot's prefix if none is found. + */ export function getPrefix(guild: DiscordGuild | null): string { let prefix = Config.prefix; From a7aea6a28e49697c6a97f64865626ab430495fff Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Thu, 8 Apr 2021 16:43:58 -0500 Subject: [PATCH 30/30] Added streaminfo message and system info module --- src/commands/utility/streaminfo.ts | 9 ++++- src/index.ts | 2 +- src/modules/channelListener.ts | 20 ----------- src/modules/emoteRegistry.ts | 41 +++------------------ src/modules/systemInfo.ts | 57 ++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 58 deletions(-) delete mode 100644 src/modules/channelListener.ts create mode 100644 src/modules/systemInfo.ts diff --git a/src/commands/utility/streaminfo.ts b/src/commands/utility/streaminfo.ts index 969e668..c1a0c9d 100644 --- a/src/commands/utility/streaminfo.ts +++ b/src/commands/utility/streaminfo.ts @@ -8,8 +8,15 @@ export default new NamedCommand({ if (streamList.has(userID)) { const stream = streamList.get(userID)!; - stream.description = args.join(" ") || "No description set."; + const description = args.join(" ") || "No description set."; + stream.description = description; stream.update(); + channel.send(`Successfully set the stream description to:`, { + embed: { + description, + color: member!.displayColor + } + }); } else { // Alternatively, I could make descriptions last outside of just one stream. channel.send("You can only use this command when streaming."); diff --git a/src/index.ts b/src/index.ts index 6c917d7..d1c4736 100644 --- a/src/index.ts +++ b/src/index.ts @@ -66,7 +66,7 @@ import "./modules/ready"; import "./modules/presence"; import "./modules/lavalink"; import "./modules/emoteRegistry"; -import "./modules/channelListener"; +import "./modules/systemInfo"; import "./modules/intercept"; import "./modules/messageEmbed"; import "./modules/guildMemberAdd"; diff --git a/src/modules/channelListener.ts b/src/modules/channelListener.ts deleted file mode 100644 index 910d1ab..0000000 --- a/src/modules/channelListener.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {client} from "../index"; -import {GuildChannel} from "discord.js"; - -client.on("channelCreate", async (channel) => { - const botGuilds = client.guilds; - - if (channel instanceof GuildChannel) { - const createdGuild = await botGuilds.fetch(channel.guild.id); - console.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`); - } -}); - -client.on("channelDelete", async (channel) => { - const botGuilds = client.guilds; - - if (channel instanceof GuildChannel) { - const createdGuild = await botGuilds.fetch(channel.guild.id); - console.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`); - } -}); diff --git a/src/modules/emoteRegistry.ts b/src/modules/emoteRegistry.ts index 88ab099..a3de8bc 100644 --- a/src/modules/emoteRegistry.ts +++ b/src/modules/emoteRegistry.ts @@ -1,7 +1,6 @@ import {client} from "../index"; import FileManager from "./storage"; -import {EmoteRegistryDump, Config} from "../structures"; -import {TextChannel} from "discord.js"; +import {EmoteRegistryDump} from "../structures"; function updateGlobalEmoteRegistry(): void { const data: EmoteRegistryDump = {version: 1, list: []}; @@ -39,43 +38,13 @@ client.on("emojiUpdate", () => { updateGlobalEmoteRegistry(); }); -client.on("guildCreate", (guild) => { - console.log( - `[GUILD JOIN] ${guild.name} (${guild.id}) added the bot. Owner: ${guild.owner!.user.tag} (${ - guild.owner!.user.id - }). Updated emote registry.` - ); - - if (Config.systemLogsChannel) { - const channel = client.channels.cache.get(Config.systemLogsChannel); - - if (channel && channel.type === "text") { - (channel as TextChannel).send( - `TravBot joined: \`${guild.name}\`. The owner of this guild is: \`${guild.owner!.user.tag}\` (\`${ - guild.owner!.user.id - }\`)` - ); - } else { - console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); - } - } - +client.on("guildCreate", () => { + console.log("Updated emote registry."); updateGlobalEmoteRegistry(); }); -client.on("guildDelete", (guild) => { - console.log(`[GUILD LEAVE] ${guild.name} (${guild.id}) removed the bot. Updated emote registry.`); - - if (Config.systemLogsChannel) { - const channel = client.channels.cache.get(Config.systemLogsChannel); - - if (channel && channel.type === "text") { - (channel as TextChannel).send(`\`${guild.name}\` (\`${guild.id}\`) removed the bot.`); - } else { - console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); - } - } - +client.on("guildDelete", () => { + console.log("Updated emote registry."); updateGlobalEmoteRegistry(); }); diff --git a/src/modules/systemInfo.ts b/src/modules/systemInfo.ts new file mode 100644 index 0000000..f9a0b64 --- /dev/null +++ b/src/modules/systemInfo.ts @@ -0,0 +1,57 @@ +import {client} from "../index"; +import {GuildChannel, TextChannel} from "discord.js"; +import {Config} from "../structures"; + +client.on("channelCreate", async (channel) => { + const botGuilds = client.guilds; + + if (channel instanceof GuildChannel) { + const createdGuild = await botGuilds.fetch(channel.guild.id); + console.log(`Channel created in '${createdGuild.name}' called '#${channel.name}'`); + } +}); + +client.on("channelDelete", async (channel) => { + const botGuilds = client.guilds; + + if (channel instanceof GuildChannel) { + const createdGuild = await botGuilds.fetch(channel.guild.id); + console.log(`Channel deleted in '${createdGuild.name}' called '#${channel.name}'`); + } +}); + +client.on("guildCreate", (guild) => { + console.log( + `[GUILD JOIN] ${guild.name} (${guild.id}) added the bot. Owner: ${guild.owner!.user.tag} (${ + guild.owner!.user.id + }).` + ); + + if (Config.systemLogsChannel) { + const channel = client.channels.cache.get(Config.systemLogsChannel); + + if (channel && channel.type === "text") { + (channel as TextChannel).send( + `TravBot joined: \`${guild.name}\`. The owner of this guild is: \`${guild.owner!.user.tag}\` (\`${ + guild.owner!.user.id + }\`)` + ); + } else { + console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); + } + } +}); + +client.on("guildDelete", (guild) => { + console.log(`[GUILD LEAVE] ${guild.name} (${guild.id}) removed the bot.`); + + if (Config.systemLogsChannel) { + const channel = client.channels.cache.get(Config.systemLogsChannel); + + if (channel && channel.type === "text") { + (channel as TextChannel).send(`\`${guild.name}\` (\`${guild.id}\`) removed the bot.`); + } else { + console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); + } + } +});