diff --git a/knexfile.js b/knexfile.js deleted file mode 100644 index 0a4f0a43..00000000 --- a/knexfile.js +++ /dev/null @@ -1,34 +0,0 @@ -require('dotenv').config(); - -const moment = require('moment-timezone'); -moment.tz.setDefault('UTC'); - -module.exports = { - client: 'mysql2', - connection: { - host: process.env.DB_HOST, - user: process.env.DB_USER, - password: process.env.DB_PASSWORD, - database: process.env.DB_DATABASE, - charset: 'utf8mb4', - timezone: 'UTC', - supportBigNumbers: true, - bigNumberStrings: true, - dateStrings: true, - typeCast(field, next) { - if (field.type === 'DATETIME') { - const val = field.string(); - return val != null ? moment(val).format('YYYY-MM-DD HH:mm:ss') : null; - } - - return next(); - } - }, - pool: { - afterCreate(conn, cb) { - conn.query('SET time_zone = "+00:00";', err => { - cb(err, conn); - }); - } - } -}; diff --git a/migrations/1540519249973-CreatePreTypeORMTables.ts b/migrations/1540519249973-CreatePreTypeORMTables.ts new file mode 100644 index 00000000..9b0044ad --- /dev/null +++ b/migrations/1540519249973-CreatePreTypeORMTables.ts @@ -0,0 +1,112 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class CreatePreTypeORMTables1540519249973 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS \`archives\` ( + \`id\` VARCHAR(36) NOT NULL, + \`guild_id\` VARCHAR(20) NOT NULL, + \`body\` MEDIUMTEXT NOT NULL, + \`created_at\` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + \`expires_at\` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (\`id\`) + ) + COLLATE='utf8mb4_general_ci' + `); + + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS \`cases\` ( + \`id\` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + \`guild_id\` BIGINT(20) UNSIGNED NOT NULL, + \`case_number\` INT(10) UNSIGNED NOT NULL, + \`user_id\` BIGINT(20) UNSIGNED NOT NULL, + \`user_name\` VARCHAR(128) NOT NULL, + \`mod_id\` BIGINT(20) UNSIGNED NULL DEFAULT NULL, + \`mod_name\` VARCHAR(128) NULL DEFAULT NULL, + \`type\` INT(10) UNSIGNED NOT NULL, + \`audit_log_id\` BIGINT(20) NULL DEFAULT NULL, + \`created_at\` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (\`id\`), + UNIQUE INDEX \`mod_actions_guild_id_case_number_unique\` (\`guild_id\`, \`case_number\`), + UNIQUE INDEX \`mod_actions_audit_log_id_unique\` (\`audit_log_id\`), + INDEX \`mod_actions_user_id_index\` (\`user_id\`), + INDEX \`mod_actions_mod_id_index\` (\`mod_id\`), + INDEX \`mod_actions_created_at_index\` (\`created_at\`) + ) + COLLATE = 'utf8mb4_general_ci' + `); + + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS \`case_notes\` ( + \`id\` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + \`case_id\` INT(10) UNSIGNED NOT NULL, + \`mod_id\` BIGINT(20) UNSIGNED NULL DEFAULT NULL, + \`mod_name\` VARCHAR(128) NULL DEFAULT NULL, + \`body\` TEXT NOT NULL, + \`created_at\` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (\`id\`), + INDEX \`mod_action_notes_mod_action_id_index\` (\`case_id\`), + INDEX \`mod_action_notes_mod_id_index\` (\`mod_id\`), + INDEX \`mod_action_notes_created_at_index\` (\`created_at\`) + ) + COLLATE = 'utf8mb4_general_ci' + `); + + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS \`mutes\` ( + \`guild_id\` BIGINT(20) UNSIGNED NOT NULL, + \`user_id\` BIGINT(20) UNSIGNED NOT NULL, + \`created_at\` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + \`expires_at\` DATETIME NULL DEFAULT NULL, + \`case_id\` INT(10) UNSIGNED NULL DEFAULT NULL, + PRIMARY KEY (\`guild_id\`, \`user_id\`), + INDEX \`mutes_expires_at_index\` (\`expires_at\`), + INDEX \`mutes_case_id_foreign\` (\`case_id\`), + CONSTRAINT \`mutes_case_id_foreign\` FOREIGN KEY (\`case_id\`) REFERENCES \`cases\` (\`id\`) + ON DELETE SET NULL + ) + COLLATE = 'utf8mb4_general_ci' + `); + + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS \`persisted_data\` ( + \`guild_id\` VARCHAR(20) NOT NULL, + \`user_id\` VARCHAR(20) NOT NULL, + \`roles\` VARCHAR(1024) NULL DEFAULT NULL, + \`nickname\` VARCHAR(255) NULL DEFAULT NULL, + \`is_voice_muted\` INT(11) NOT NULL DEFAULT '0', + PRIMARY KEY (\`guild_id\`, \`user_id\`) + ) + COLLATE = 'utf8mb4_general_ci' + `); + + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS \`reaction_roles\` ( + \`guild_id\` VARCHAR(20) NOT NULL, + \`channel_id\` VARCHAR(20) NOT NULL, + \`message_id\` VARCHAR(20) NOT NULL, + \`emoji\` VARCHAR(20) NOT NULL, + \`role_id\` VARCHAR(20) NOT NULL, + PRIMARY KEY (\`guild_id\`, \`channel_id\`, \`message_id\`, \`emoji\`), + INDEX \`reaction_roles_message_id_emoji_index\` (\`message_id\`, \`emoji\`) + ) + COLLATE = 'utf8mb4_general_ci' + `); + + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS \`tags\` ( + \`guild_id\` BIGINT(20) UNSIGNED NOT NULL, + \`tag\` VARCHAR(64) NOT NULL, + \`user_id\` BIGINT(20) UNSIGNED NOT NULL, + \`body\` TEXT NOT NULL, + \`created_at\` DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (\`guild_id\`, \`tag\`) + ) + COLLATE = 'utf8mb4_general_ci' + `); + } + + public async down(queryRunner: QueryRunner): Promise { + // No down function since we're migrating (hehe) from another migration system (knex) + } +} diff --git a/migrations/20180701120000_create_mod_action_tables.js b/migrations/20180701120000_create_mod_action_tables.js deleted file mode 100644 index dc5ef51b..00000000 --- a/migrations/20180701120000_create_mod_action_tables.js +++ /dev/null @@ -1,34 +0,0 @@ -exports.up = async function(knex) { - if (! await knex.schema.hasTable('mod_actions')) { - await knex.schema.createTable('mod_actions', table => { - table.increments('id'); - table.bigInteger('guild_id').unsigned().notNullable(); - table.integer('case_number').unsigned().notNullable(); - table.bigInteger('user_id').index().unsigned().notNullable(); - table.string('user_name', 128).notNullable(); - table.bigInteger('mod_id').index().unsigned().nullable().defaultTo(null); - table.string('mod_name', 128).nullable().defaultTo(null); - table.integer('action_type').unsigned().notNullable(); - table.bigInteger('audit_log_id').unique().nullable().defaultTo(null); - table.dateTime('created_at').index().defaultTo(knex.raw('NOW()')).notNullable(); - - table.unique(['guild_id', 'case_number']); - }); - } - - if (! await knex.schema.hasTable('mod_action_notes')) { - await knex.schema.createTable('mod_action_notes', table => { - table.increments('id'); - table.integer('mod_action_id').unsigned().notNullable().index().references('id').inTable('mod_actions').onDelete('CASCADE'); - table.bigInteger('mod_id').index().unsigned().nullable().defaultTo(null); - table.string('mod_name', 128).nullable().defaultTo(null); - table.text('body').notNullable(); - table.dateTime('created_at').index().defaultTo(knex.raw('NOW()')).notNullable(); - }); - } -}; - -exports.down = async function(knex) { - await knex.schema.dropTableIfExists('mod_action_notes'); - await knex.schema.dropTableIfExists('mod_actions'); -}; diff --git a/migrations/20180707152000_create_mutes_table.js b/migrations/20180707152000_create_mutes_table.js deleted file mode 100644 index 699781a3..00000000 --- a/migrations/20180707152000_create_mutes_table.js +++ /dev/null @@ -1,17 +0,0 @@ -exports.up = async function(knex) { - if (! await knex.schema.hasTable('mutes')) { - await knex.schema.createTable('mutes', table => { - table.bigInteger('guild_id').unsigned().notNullable(); - table.bigInteger('user_id').unsigned().notNullable(); - table.dateTime('created_at').defaultTo(knex.raw('NOW()')); - table.dateTime('expires_at').nullable().defaultTo(null); - - table.primary(['guild_id', 'user_id']); - table.index(['expires_at']); - }); - } -}; - -exports.down = async function(knex) { - await knex.schema.dropTableIfExists('mutes'); -}; diff --git a/migrations/20180712020400_rename_mod_actions_to_cases.js b/migrations/20180712020400_rename_mod_actions_to_cases.js deleted file mode 100644 index 07d05d01..00000000 --- a/migrations/20180712020400_rename_mod_actions_to_cases.js +++ /dev/null @@ -1,21 +0,0 @@ -exports.up = async function(knex) { - await knex.schema.renameTable('mod_actions', 'cases'); - await knex.schema.renameTable('mod_action_notes', 'case_notes'); - await knex.schema.table('cases', table => { - table.renameColumn('action_type', 'type'); - }); - await knex.schema.table('case_notes', table => { - table.renameColumn('mod_action_id', 'case_id'); - }); -}; - -exports.down = async function(knex) { - await knex.schema.table('cases', table => { - table.renameColumn('type', 'action_type'); - }); - await knex.schema.table('case_notes', table => { - table.renameColumn('case_id', 'mod_action_id'); - }); - await knex.schema.renameTable('cases', 'mod_actions'); - await knex.schema.renameTable('case_notes', 'mod_action_notes'); -}; diff --git a/migrations/20180729142200_create_reaction_roles_table.js b/migrations/20180729142200_create_reaction_roles_table.js deleted file mode 100644 index 78880a10..00000000 --- a/migrations/20180729142200_create_reaction_roles_table.js +++ /dev/null @@ -1,18 +0,0 @@ -exports.up = async function(knex, Promise) { - if (! await knex.schema.hasTable('reaction_roles')) { - await knex.schema.createTable('reaction_roles', table => { - table.string('guild_id', 20).notNullable(); - table.string('channel_id', 20).notNullable(); - table.string('message_id', 20).notNullable(); - table.string('emoji', 20).notNullable(); - table.string('role_id', 20).notNullable(); - - table.primary(['guild_id', 'channel_id', 'message_id', 'emoji']); - table.index(['message_id', 'emoji']); - }); - } -}; - -exports.down = async function(knex, Promise) { - await knex.schema.dropTableIfExists('reaction_roles'); -}; diff --git a/migrations/20180730230000_create_persisted_data_table.js b/migrations/20180730230000_create_persisted_data_table.js deleted file mode 100644 index 2da9737e..00000000 --- a/migrations/20180730230000_create_persisted_data_table.js +++ /dev/null @@ -1,17 +0,0 @@ -exports.up = async function(knex, Promise) { - if (! await knex.schema.hasTable('persisted_data')) { - await knex.schema.createTable('persisted_data', table => { - table.string('guild_id', 20).notNullable(); - table.string('user_id', 20).notNullable(); - table.string('roles', 1024).nullable().defaultTo(null); - table.string('nickname', 255).nullable().defaultTo(null); - table.integer('is_voice_muted').notNullable().defaultTo(0); - - table.primary(['guild_id', 'user_id']); - }); - } -}; - -exports.down = async function(knex, Promise) { - await knex.schema.dropTableIfExists('persisted_data'); -}; diff --git a/migrations/20180801185500_create_spam_logs_table.js b/migrations/20180801185500_create_spam_logs_table.js deleted file mode 100644 index f40b01fa..00000000 --- a/migrations/20180801185500_create_spam_logs_table.js +++ /dev/null @@ -1,15 +0,0 @@ -exports.up = async function(knex, Promise) { - if (! await knex.schema.hasTable('spam_logs')) { - await knex.schema.createTable('spam_logs', table => { - table.string('id', 36).notNullable().primary(); - table.string('guild_id', 20).notNullable(); - table.text('body', 'mediumtext').notNullable(); - table.dateTime('created_at').defaultTo(knex.raw('NOW()')).notNullable(); - table.dateTime('expires_at').nullable(); - }); - } -}; - -exports.down = async function(knex, Promise) { - await knex.schema.dropTableIfExists('spam_logs'); -}; diff --git a/migrations/20180804235500_add_case_id_to_mutes.js b/migrations/20180804235500_add_case_id_to_mutes.js deleted file mode 100644 index e89e2952..00000000 --- a/migrations/20180804235500_add_case_id_to_mutes.js +++ /dev/null @@ -1,12 +0,0 @@ -exports.up = async function(knex, Promise) { - await knex.schema.table('mutes', table => { - table.integer('case_id').unsigned().nullable().defaultTo(null).after('user_id').references('id').inTable('cases').onDelete('SET NULL'); - }); -}; - -exports.down = async function(knex, Promise) { - await knex.schema.table('mutes', table => { - table.dropForeign('case_id'); - table.dropColumn('case_id'); - }); -}; diff --git a/migrations/20180805011100_create_tags_table.js b/migrations/20180805011100_create_tags_table.js deleted file mode 100644 index 2cbab3c2..00000000 --- a/migrations/20180805011100_create_tags_table.js +++ /dev/null @@ -1,17 +0,0 @@ -exports.up = async function(knex) { - if (! await knex.schema.hasTable('tags')) { - await knex.schema.createTable('tags', table => { - table.bigInteger('guild_id').unsigned().notNullable(); - table.string('tag', 64).notNullable(); - table.bigInteger('user_id').unsigned().notNullable(); - table.text('body').notNullable(); - table.dateTime('created_at').defaultTo(knex.raw('NOW()')); - - table.primary(['guild_id', 'tag']); - }); - } -}; - -exports.down = async function(knex) { - await knex.schema.dropTableIfExists('tags'); -}; diff --git a/migrations/20180818192600_rename_spam_logs_to_archives.js b/migrations/20180818192600_rename_spam_logs_to_archives.js deleted file mode 100644 index 39933ae9..00000000 --- a/migrations/20180818192600_rename_spam_logs_to_archives.js +++ /dev/null @@ -1,7 +0,0 @@ -exports.up = async function(knex) { - await knex.schema.renameTable('spam_logs', 'archives'); -}; - -exports.down = async function(knex) { - await knex.schema.renameTable('archives', 'spam_logs'); -}; diff --git a/ormconfig.js b/ormconfig.js new file mode 100644 index 00000000..8a59bb25 --- /dev/null +++ b/ormconfig.js @@ -0,0 +1,38 @@ +require('dotenv').config(); + +const moment = require('moment-timezone'); +moment.tz.setDefault('UTC'); + +module.exports = { + type: "mysql", + host: process.env.DB_HOST, + username: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + charset: 'utf8mb4', + supportBigNumbers: true, + bigNumberStrings: true, + dateStrings: true, + synchronize: false, + + // Entities + entities: [`${__dirname}/src/data/entities/*.ts`], + + // Pool options + extra: { + typeCast(field, next) { + if (field.type === 'DATETIME') { + const val = field.string(); + return val != null ? moment(val).format('YYYY-MM-DD HH:mm:ss') : null; + } + + return next(); + } + }, + + // Migrations + migrations: ["migrations/*.ts"], + cli: { + migrationsDir: "migrations" + }, +}; diff --git a/package-lock.json b/package-lock.json index 3557ba9c..7ea0c093 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,20 +13,6 @@ "any-observable": "^0.3.0" } }, - "@types/bluebird": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.21.tgz", - "integrity": "sha512-6UNEwyw+6SGMC/WMI0ld0PS4st7Qq51qgguFrFizOSpGvZiqe9iswztFSdZvwJBEhLOy2JaxNE6VC7yMAlbfyQ==" - }, - "@types/knex": { - "version": "0.0.64", - "resolved": "https://registry.npmjs.org/@types/knex/-/knex-0.0.64.tgz", - "integrity": "sha512-ClgCcVWKg2SNzeLkkIXfvb8R92laXaUzsjA3QcH2CxG0OhPJAoeF0e8vkPk3kaLW3gnpAfdOGdrdJKCssIkCRw==", - "requires": { - "@types/bluebird": "*", - "@types/node": "*" - } - }, "@types/lodash": { "version": "4.14.110", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.110.tgz", @@ -49,9 +35,9 @@ } }, "@types/node": { - "version": "8.10.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.20.tgz", - "integrity": "sha512-M7x8+5D1k/CuA6jhiwuSCmE8sbUWJF0wYsjcig9WrXvwUI5ArEoUBdOXpV4JcEMrLp02/QbDjw+kI+vQeKyQgg==" + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.0.tgz", + "integrity": "sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ==" }, "abbrev": { "version": "1.1.1", @@ -77,8 +63,7 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "ansi-styles": { "version": "3.2.1", @@ -88,17 +73,17 @@ "color-convert": "^1.9.0" } }, - "ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" - }, "any-observable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -123,8 +108,7 @@ "app-root-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", - "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", - "dev": true + "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=" }, "argparse": { "version": "1.0.10", @@ -143,7 +127,8 @@ "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==" + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true }, "arr-union": { "version": "3.1.0", @@ -168,11 +153,6 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, - "async": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" - }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -243,20 +223,10 @@ } } }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, "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=" }, "base": { "version": "0.11.2", @@ -313,17 +283,22 @@ } } }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "bignumber.js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==" + }, "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -343,7 +318,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" @@ -378,6 +352,15 @@ } } }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -404,8 +387,7 @@ "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" }, "capture-stack-trace": { "version": "1.0.0", @@ -413,15 +395,6 @@ "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", "dev": true }, - "cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", - "requires": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - } - }, "chalk": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", @@ -517,6 +490,39 @@ "restore-cursor": "^1.0.1" } }, + "cli-highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-1.2.3.tgz", + "integrity": "sha512-cmc4Y2kJuEpT2KZd9pgWWskpDMMfJu2roIcY1Ya/aIItufF5FKsV/NtA6vvdhSUllR8KJfvQDNmIcskU+MKLDg==", + "requires": { + "chalk": "^2.3.0", + "highlight.js": "^9.6.0", + "mz": "^2.4.0", + "parse5": "^3.0.3", + "yargs": "^10.0.3" + }, + "dependencies": { + "yargs": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", + "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^8.1.0" + } + } + } + }, "cli-spinners": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", @@ -570,11 +576,20 @@ } } }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "collection-visit": { "version": "1.0.0", @@ -599,15 +614,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" - }, "commander": { "version": "2.16.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", - "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==" + "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==", + "dev": true }, "component-emitter": { "version": "1.2.1", @@ -618,8 +629,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=" }, "configstore": { "version": "3.1.2", @@ -641,11 +651,6 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -675,7 +680,6 @@ "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", @@ -688,11 +692,6 @@ "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", "dev": true }, - "cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" - }, "date-fns": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", @@ -703,11 +702,15 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, "requires": { "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -767,19 +770,6 @@ } } }, - "denque": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.3.0.tgz", - "integrity": "sha512-4SRaSj+PqmrS1soW5/Avd7eJIM2JJIqLLmwhRqIGleZM/8KwZq80njbSS2Iqas+6oARkSkLDHEk4mm78q3JlIg==" - }, - "detect-file": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", - "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", - "requires": { - "fs-exists-sync": "^0.1.0" - } - }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -847,9 +837,9 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esutils": { "version": "2.0.2", @@ -937,65 +927,6 @@ } } }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "requires": { - "fill-range": "^2.1.0" - }, - "dependencies": { - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", - "requires": { - "os-homedir": "^1.0.1" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -1082,10 +1013,10 @@ } } }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" + "figlet": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.2.1.tgz", + "integrity": "sha512-qc8gycfnnfOmfvPl7Fi3JeTbcvdmbZkckyUVGGAM02je7Ookvu+bBfKy1I4FKqTsQHCs3ARJ76ip/k98r+OQuQ==" }, "figures": { "version": "1.7.0", @@ -1097,11 +1028,6 @@ "object-assign": "^4.1.0" } }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1131,124 +1057,19 @@ "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", "dev": true }, - "findup-sync": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", - "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "requires": { - "detect-file": "^0.1.0", - "is-glob": "^2.0.1", - "micromatch": "^2.3.7", - "resolve-dir": "^0.1.0" - }, - "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + "locate-path": "^2.0.0" } }, - "flagged-respawn": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz", - "integrity": "sha1-/xke3c1wiKZ1smEP/8l2vpuAdLU=" - }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "requires": { - "for-in": "^1.0.1" - } + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true }, "fragment-cache": { "version": "0.2.1", @@ -1265,16 +1086,10 @@ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=" - }, "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": "1.2.4", @@ -1805,15 +1620,10 @@ } } }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" - }, - "generic-pool": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.5.4.tgz", - "integrity": "sha1-OMYYhRPhQDCUjsblz2VSPZd5KZs=" + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, "get-own-enumerable-property-symbols": { "version": "2.0.1", @@ -1824,8 +1634,7 @@ "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "get-value": { "version": "2.0.6", @@ -1837,7 +1646,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1847,38 +1655,6 @@ "path-is-absolute": "^1.0.0" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -1898,40 +1674,6 @@ "ini": "^1.3.4" } }, - "global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", - "requires": { - "global-prefix": "^0.1.4", - "is-windows": "^0.2.0" - }, - "dependencies": { - "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=" - } - } - }, - "global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", - "requires": { - "homedir-polyfill": "^1.0.0", - "ini": "^1.3.4", - "is-windows": "^0.2.0", - "which": "^1.2.12" - }, - "dependencies": { - "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=" - } - } - }, "got": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", @@ -2009,6 +1751,11 @@ } } }, + "highlight.js": { + "version": "9.13.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz", + "integrity": "sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==" + }, "homedir-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", @@ -2033,13 +1780,10 @@ "strip-indent": "^2.0.0" } }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" }, "ignore-by-default": { "version": "1.0.1", @@ -2072,7 +1816,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" @@ -2086,12 +1829,13 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true }, - "interpret": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.6.6.tgz", - "integrity": "sha1-/s16GOfOXKar+5U+H4YhOknxYls=" + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -2131,7 +1875,8 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, "is-ci": { "version": "1.1.0", @@ -2187,23 +1932,11 @@ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", "dev": true }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "requires": { - "is-primitive": "^2.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=" + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true }, "is-extglob": { "version": "2.1.1", @@ -2223,8 +1956,7 @@ "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 + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-glob": { "version": "3.1.0", @@ -2304,16 +2036,6 @@ "isobject": "^3.0.1" } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" - }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -2341,8 +2063,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-windows": { "version": "1.0.2", @@ -2366,11 +2087,6 @@ "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=" - }, "jest-get-type": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", @@ -2413,128 +2129,19 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "knex": { - "version": "0.12.6", - "resolved": "https://registry.npmjs.org/knex/-/knex-0.12.6.tgz", - "integrity": "sha1-olXw6gOvLCyUaHpiLAiswalGPA4=", - "requires": { - "babel-runtime": "^6.11.6", - "bluebird": "^3.4.6", - "chalk": "^1.0.0", - "commander": "^2.2.0", - "debug": "^2.1.3", - "generic-pool": "^2.4.2", - "inherits": "~2.0.1", - "interpret": "^0.6.5", - "liftoff": "~2.2.0", - "lodash": "^4.6.0", - "minimist": "~1.1.0", - "mkdirp": "^0.5.0", - "node-uuid": "^1.4.7", - "pg-connection-string": "^0.1.3", - "readable-stream": "^1.1.12", - "tildify": "~1.0.0", - "v8flags": "^2.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "minimist": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", - "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=" - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "v8flags": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", - "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", - "requires": { - "user-home": "^1.1.1" - } - } - } + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true }, "knub": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/knub/-/knub-10.1.0.tgz", - "integrity": "sha512-W14IbTSz4iGvgKQ4QutY8s+5rZcqYmB8ll3aUQ8nYDp23VDCdm/6Do0f8j0zip5h05cmfVmVRIXabfvfFQkxmA==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/knub/-/knub-12.1.1.tgz", + "integrity": "sha512-K6a/F/wQMAkTIKPHQk5mWH8l5zkbFxqMXQ0ptjbIfxPJUvCvm8NxVju+rlD4h3fLloALKNOHrKKpBR506QJ+ag==", "requires": { "escape-string-regexp": "^1.0.5", - "js-yaml": "^3.9.1", "lodash.at": "^4.6.0", "lodash.difference": "^4.5.0", "lodash.merge": "^4.6.1", - "reflect-metadata": "^0.1.10", - "winston": "^2.3.1" + "reflect-metadata": "^0.1.10" } }, "latest-version": { @@ -2546,24 +2153,20 @@ "package-json": "^4.0.0" } }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", "dev": true }, - "liftoff": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.2.5.tgz", - "integrity": "sha1-mYwods/0hLED5EI7k9NW2kRzTJE=", - "requires": { - "extend": "^3.0.0", - "findup-sync": "^0.4.2", - "flagged-respawn": "^0.3.2", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" - } - }, "lint-staged": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-7.2.0.tgz", @@ -2814,10 +2417,20 @@ } } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true }, "lodash.at": { "version": "4.6.0", @@ -2874,11 +2487,6 @@ "cli-cursor": "^1.0.2" } }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -2889,7 +2497,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -2930,10 +2537,13 @@ "object-visit": "^1.0.0" } }, - "math-random": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=" + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "^1.0.0" + } }, "micromatch": { "version": "3.1.10", @@ -2956,11 +2566,15 @@ "to-regex": "^3.0.2" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, "minimatch": { "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" } @@ -3024,47 +2638,25 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mysql2": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-1.6.0.tgz", - "integrity": "sha512-L5OFVrJ0jxwoZvxKES9V5szw7Uq0VmN1jLT9ISQVsixb9JF11RExmzkjy+0ErVjXZrEjlms1TKSlrRGEGGA1bw==", + "mysql": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.16.0.tgz", + "integrity": "sha512-dPbN2LHonQp7D5ja5DJXNbCLe/HRdu+f3v61aguzNRQIrmZLOeRoymBYyeThrR6ug+FqzDL95Gc9maqZUJS+Gw==", "requires": { - "cardinal": "2.1.1", - "denque": "1.3.0", - "generate-function": "^2.0.0", - "iconv-lite": "^0.4.18", - "long": "^4.0.0", - "lru-cache": "4.1.1", - "named-placeholders": "1.1.1", - "object-assign": "^4.1.1", - "seq-queue": "0.0.5", + "bignumber.js": "4.1.0", + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2", "sqlstring": "2.3.1" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - } } }, - "named-placeholders": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.1.tgz", - "integrity": "sha1-O3oNJiA910s6nfTJz7gnsvuQfmQ=", + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "requires": { - "lru-cache": "2.5.0" - }, - "dependencies": { - "lru-cache": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz", - "integrity": "sha1-2COIrpyWC+y+oMc7uet5tsbOmus=" - } + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, "nan": { @@ -3139,7 +2731,6 @@ "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" } @@ -3158,8 +2749,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", @@ -3206,15 +2796,6 @@ "isobject": "^3.0.0" } }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -3228,7 +2809,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -3299,16 +2879,52 @@ } } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + }, + "dependencies": { + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.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" + } + } + } }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } }, "p-map": { "version": "1.2.0", @@ -3316,6 +2932,11 @@ "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", "dev": true }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, "package-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", @@ -3328,31 +2949,10 @@ "semver": "^5.1.0" } }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - } - } + "parent-require": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", + "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=" }, "parse-json": { "version": "4.0.0", @@ -3369,6 +2969,14 @@ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "requires": { + "@types/node": "*" + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -3381,11 +2989,15 @@ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, "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-is-inside": { "version": "1.0.2", @@ -3396,13 +3008,13 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true }, "pause-stream": { "version": "0.0.11", @@ -3413,11 +3025,6 @@ "through": "~2.3" } }, - "pg-connection-string": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", - "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=" - }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -3445,11 +3052,6 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" - }, "prettier": { "version": "1.13.7", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.13.7.tgz", @@ -3469,8 +3071,7 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "ps-tree": { "version": "1.1.0", @@ -3495,23 +3096,6 @@ "ps-tree": "^1.1.0" } }, - "randomatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", - "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - } - } - }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -3528,7 +3112,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3551,40 +3134,11 @@ "set-immediate-shim": "^1.0.1" } }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "requires": { - "resolve": "^1.1.6" - } - }, - "redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", - "requires": { - "esprima": "~4.0.0" - } - }, "reflect-metadata": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz", "integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==" }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -3617,17 +3171,20 @@ "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=" + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true }, "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "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=" + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true }, "repeating": { "version": "2.0.1", @@ -3638,23 +3195,25 @@ "is-finite": "^1.0.0" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, "resolve": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dev": true, "requires": { "path-parse": "^1.0.5" } }, - "resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", - "requires": { - "expand-tilde": "^1.2.2", - "global-modules": "^0.2.3" - } - }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -3689,8 +3248,7 @@ "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 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -3701,10 +3259,10 @@ "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==" + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { "version": "5.5.0", @@ -3727,10 +3285,10 @@ "semver": "^5.0.3" } }, - "seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-immediate-shim": { "version": "1.0.1", @@ -3765,7 +3323,6 @@ "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" } @@ -3773,14 +3330,12 @@ "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 + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "slice-ansi": { "version": "0.0.4", @@ -3964,11 +3519,6 @@ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, "staged-git-files": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-1.1.1.tgz", @@ -4015,7 +3565,6 @@ "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" @@ -4025,7 +3574,6 @@ "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" } @@ -4045,7 +3593,6 @@ "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" } @@ -4058,8 +3605,7 @@ "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 + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-indent": { "version": "2.0.0", @@ -4112,20 +3658,28 @@ } } }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, - "tildify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.0.0.tgz", - "integrity": "sha1-KgIdtej73gqPi03zetqo+x05190=", - "requires": { - "user-home": "^1.0.0" - } - }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -4261,10 +3815,37 @@ "integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins=", "optional": true }, + "typeorm": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.8.tgz", + "integrity": "sha512-MvMwi3Qd1nTBMZsohOy9Pb7eHFXY+YRtPdG90ayfPeC7z2DJXE0gOF8zAKUlxLy1+Dyyq/tOQS+LY/4/6yI/3A==", + "requires": { + "app-root-path": "^2.0.1", + "buffer": "^5.1.0", + "chalk": "^2.3.2", + "cli-highlight": "^1.2.3", + "debug": "^3.1.0", + "dotenv": "^5.0.1", + "glob": "^7.1.2", + "js-yaml": "^3.11.0", + "mkdirp": "^0.5.1", + "reflect-metadata": "^0.1.12", + "xml2js": "^0.4.17", + "yargonaut": "^1.1.2", + "yargs": "^11.1.0" + }, + "dependencies": { + "dotenv": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", + "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==" + } + } + }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.3.tgz", + "integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA==" }, "undefsafe": { "version": "2.0.2", @@ -4424,16 +4005,10 @@ "kind-of": "^6.0.2" } }, - "user-home": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", - "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=" - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { "version": "3.3.2", @@ -4456,6 +4031,11 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, "widest-line": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", @@ -4465,24 +4045,52 @@ "string-width": "^2.1.1" } }, - "winston": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.3.tgz", - "integrity": "sha512-GYKuysPz2pxYAVJD2NPsDLP5Z79SDEzPm9/j4tCjkF/n89iBNGBMJcR+dMUqxgPNgoSs6fVygPi+Vl2oxIpBuw==", + "wrap-ansi": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "async": "~1.0.0", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "stack-trace": "0.0.x" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.3.0", @@ -4509,11 +4117,114 @@ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, + "yargonaut": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz", + "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", + "requires": { + "chalk": "^1.1.1", + "figlet": "^1.1.1", + "parent-require": "^1.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "yargs": { + "version": "11.1.0", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + }, + "dependencies": { + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", + "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", + "requires": { + "camelcase": "^4.1.0" + } + }, "yn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", diff --git a/package.json b/package.json index 7bd68e9d..edd0af66 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,7 @@ "precommit": "lint-staged", "postcommit": "git update-index --again", "format": "prettier --write \"./**/*.ts\"", - "db-migrate": "knex migrate:latest", - "db-rollback": "knex migrate:rollback" + "typeorm": "ts-node ./node_modules/typeorm/cli.js" }, "lint-staged": { "*.ts": [ @@ -23,34 +22,35 @@ "author": "", "license": "ISC", "dependencies": { - "@types/knex": "0.0.64", "@types/lodash.at": "^4.6.3", "@types/moment-timezone": "^0.5.6", - "@types/node": "^8.0.50", "dotenv": "^4.0.0", "emoji-regex": "^7.0.0", "eris": "^0.8.6", "escape-string-regexp": "^1.0.5", "humanize-duration": "^3.15.0", - "knex": "0.12.6", - "knub": "^10.1.0", + "js-yaml": "^3.12.0", + "knub": "^12.1.1", "lodash.at": "^4.6.0", "lodash.chunk": "^4.2.0", "lodash.difference": "^4.5.0", "lodash.intersection": "^4.4.0", "lodash.isequal": "^4.5.0", "moment-timezone": "^0.5.21", - "mysql2": "^1.6.0", + "mysql": "^2.16.0", + "reflect-metadata": "^0.1.12", "tlds": "^1.203.1", "ts-node": "^3.3.0", - "typescript": "^2.9.2", + "typeorm": "^0.2.8", + "typescript": "^3.1.3", "uuid": "^3.3.2" }, "devDependencies": { - "nodemon": "^1.17.5", - "lint-staged": "^7.2.0", - "prettier": "^1.8.2", + "@types/node": "^10.12.0", "husky": "^0.14.3", + "lint-staged": "^7.2.0", + "nodemon": "^1.17.5", + "prettier": "^1.8.2", "tslint": "^5.8.0", "tslint-config-prettier": "^1.6.0" } diff --git a/src/SimpleError.ts b/src/SimpleError.ts new file mode 100644 index 00000000..ad5a6833 --- /dev/null +++ b/src/SimpleError.ts @@ -0,0 +1,13 @@ +import util from "util"; + +export class SimpleError { + public message: string; + + constructor(message: string) { + this.message = message; + } + + [util.inspect.custom](depth, options) { + return `Error: ${this.message}`; + } +} diff --git a/src/data/BaseRepository.ts b/src/data/BaseRepository.ts new file mode 100644 index 00000000..3a0d7ae4 --- /dev/null +++ b/src/data/BaseRepository.ts @@ -0,0 +1,50 @@ +export class BaseRepository { + private static guildInstances: Map; + private nextRelations: string[]; + + protected guildId: string; + + constructor(guildId: string) { + this.guildId = guildId; + this.nextRelations = []; + } + + /** + * Returns a cached instance of the inheriting class for the specified guildId, + * or creates a new instance if one doesn't exist yet + */ + public static getInstance(this: T, guildId: string): InstanceType { + if (!this.guildInstances) { + this.guildInstances = new Map(); + } + + if (!this.guildInstances.has(guildId)) { + this.guildInstances.set(guildId, new this(guildId)); + } + + return this.guildInstances.get(guildId) as InstanceType; + } + + /** + * Primes the specified relation(s) to be used in the next database operation. + * Can be chained. + */ + public with(relations: string | string[]): this { + if (Array.isArray(relations)) { + this.nextRelations.push(...relations); + } else { + this.nextRelations.push(relations); + } + + return this; + } + + /** + * Gets and resets the relations primed using with() + */ + protected getRelations(): string[] { + const relations = this.nextRelations || []; + this.nextRelations = []; + return relations; + } +} diff --git a/src/data/CaseTypeColors.ts b/src/data/CaseTypeColors.ts index 9e8ef259..b879acc2 100644 --- a/src/data/CaseTypeColors.ts +++ b/src/data/CaseTypeColors.ts @@ -1,12 +1,12 @@ -import { CaseType } from "./CaseType"; +import { CaseTypes } from "./CaseTypes"; export const CaseTypeColors = { - [CaseType.Note]: 0x3498db, - [CaseType.Warn]: 0xdae622, - [CaseType.Mute]: 0xe6b122, - [CaseType.Unmute]: 0xa175b3, - [CaseType.Kick]: 0xe67e22, - [CaseType.Softban]: 0xe67e22, - [CaseType.Ban]: 0xcb4314, - [CaseType.Unban]: 0x9b59b6 + [CaseTypes.Note]: 0x3498db, + [CaseTypes.Warn]: 0xdae622, + [CaseTypes.Mute]: 0xe6b122, + [CaseTypes.Unmute]: 0xa175b3, + [CaseTypes.Kick]: 0xe67e22, + [CaseTypes.Softban]: 0xe67e22, + [CaseTypes.Ban]: 0xcb4314, + [CaseTypes.Unban]: 0x9b59b6 }; diff --git a/src/data/CaseType.ts b/src/data/CaseTypes.ts similarity index 78% rename from src/data/CaseType.ts rename to src/data/CaseTypes.ts index 583916a8..3c1ac8fb 100644 --- a/src/data/CaseType.ts +++ b/src/data/CaseTypes.ts @@ -1,4 +1,4 @@ -export enum CaseType { +export enum CaseTypes { Ban = 1, Unban, Note, diff --git a/src/data/GuildArchives.ts b/src/data/GuildArchives.ts index 65a220ee..6ac2544a 100644 --- a/src/data/GuildArchives.ts +++ b/src/data/GuildArchives.ts @@ -1,51 +1,52 @@ import uuid from "uuid/v4"; // tslint:disable-line import moment from "moment-timezone"; -import knex from "../knex"; -import SpamLog from "../models/SpamLog"; +import { ArchiveEntry } from "./entities/ArchiveEntry"; +import { getRepository, Repository } from "typeorm"; +import { BaseRepository } from "./BaseRepository"; const DEFAULT_EXPIRY_DAYS = 30; -function deleteExpiredArchives() { - knex("archives") - .where("expires_at", "<=", knex.raw("NOW()")) - .delete(); -} - -deleteExpiredArchives(); -setInterval(deleteExpiredArchives, 1000 * 60 * 60); // Clean expired archives every hour - -export class GuildArchives { - protected guildId: string; +export class GuildArchives extends BaseRepository { + protected archives: Repository; constructor(guildId) { - this.guildId = guildId; + super(guildId); + this.archives = getRepository(ArchiveEntry); + + // Clean expired archives at start and then every hour + this.deleteExpiredArchives(); + setInterval(() => this.deleteExpiredArchives(), 1000 * 60 * 60); } - generateNewLogId() { - return uuid(); + private deleteExpiredArchives() { + this.archives + .createQueryBuilder() + .where("expires_at <= NOW()") + .delete() + .execute(); } - async find(id: string): Promise { - const result = await knex("archives") - .where("id", id) - .first(); - - return result ? new SpamLog(result) : null; + async find(id: string): Promise { + return this.archives.findOne({ + where: { id }, + relations: this.getRelations() + }); } - async create(body: string, expiresAt: moment.Moment = null) { - const id = this.generateNewLogId(); + /** + * @returns ID of the created entry + */ + async create(body: string, expiresAt: moment.Moment = null): Promise { if (!expiresAt) { expiresAt = moment().add(DEFAULT_EXPIRY_DAYS, "days"); } - await knex("archives").insert({ - id, + const result = await this.archives.insert({ guild_id: this.guildId, body, expires_at: expiresAt.format("YYYY-MM-DD HH:mm:ss") }); - return id; + return result.identifiers[0].id; } } diff --git a/src/data/GuildCases.ts b/src/data/GuildCases.ts index e9075874..9d7cc93c 100644 --- a/src/data/GuildCases.ts +++ b/src/data/GuildCases.ts @@ -1,93 +1,78 @@ -import knex from "../knex"; -import Case from "../models/Case"; -import CaseNote from "../models/CaseNote"; +import { Case } from "./entities/Case"; +import { CaseNote } from "./entities/CaseNote"; +import { BaseRepository } from "./BaseRepository"; +import { getRepository, In, Repository } from "typeorm"; -export class GuildCases { - protected guildId: string; +export class GuildCases extends BaseRepository { + private cases: Repository; + private caseNotes: Repository; constructor(guildId) { - this.guildId = guildId; + super(guildId); + this.cases = getRepository(Case); + this.caseNotes = getRepository(CaseNote); } async get(ids: number[]): Promise { - const result = await knex("cases") - .whereIn("id", ids) - .select(); - - return result.map(r => new Case(r)); + return this.cases.find({ + relations: this.getRelations(), + where: { + guild_id: this.guildId, + id: In(ids) + } + }); } async find(id: number): Promise { - const result = await knex("cases") - .where("guild_id", this.guildId) - .where("id", id) - .first(); - - return result ? new Case(result) : null; + return this.cases.findOne({ + relations: this.getRelations(), + where: { + guild_id: this.guildId, + id + } + }); } async findByCaseNumber(caseNumber: number): Promise { - const result = await knex("cases") - .where("guild_id", this.guildId) - .where("case_number", caseNumber) - .first(); - - return result ? new Case(result) : null; - } - - async getCaseNotes(caseId: number): Promise { - const results = await knex("case_notes") - .where("case_id", caseId) - .select(); - - return results.map(r => new CaseNote(r)); + return this.cases.findOne({ + relations: this.getRelations(), + where: { + guild_id: this.guildId, + case_number: caseNumber + } + }); } async getByUserId(userId: string): Promise { - const results = await knex("cases") - .where("guild_id", this.guildId) - .where("user_id", userId) - .select(); - - return results.map(r => new Case(r)); - } - - async findFirstCaseNote(caseId: number): Promise { - const result = await knex("case_notes") - .where("case_id", caseId) - .first(); - - return result ? new CaseNote(result) : null; + return this.cases.find({ + relations: this.getRelations(), + where: { + guild_id: this.guildId, + user_id: userId + } + }); } async create(data): Promise { - return knex - .insert({ - ...data, - guild_id: this.guildId, - case_number: knex.raw( - "(SELECT IFNULL(MAX(case_number)+1, 1) FROM cases AS ma2 WHERE guild_id = ?)", - this.guildId - ) - }) - .returning("id") - .into("cases") - .then(ids => Number(ids[0])); + const result = await this.cases.insert({ + ...data, + guild_id: this.guildId, + case_number: () => `(SELECT IFNULL(MAX(case_number)+1, 1) FROM cases AS ma2 WHERE guild_id = ${this.guildId})` + }); + + return result.identifiers[0].id; } update(id, data) { - return knex("cases") - .where("id", id) - .update(data); + return this.cases.update(id, data); } - createNote(caseId: number, data: any) { - return knex - .insert({ - ...data, - case_id: caseId - }) - .into("case_notes") - .return(); + async createNote(caseId: number, data: any): Promise { + const result = await this.caseNotes.insert({ + ...data, + case_id: caseId + }); + + return result.identifiers[0].id; } } diff --git a/src/data/GuildMutes.ts b/src/data/GuildMutes.ts index 59e35d01..0e80ab09 100644 --- a/src/data/GuildMutes.ts +++ b/src/data/GuildMutes.ts @@ -1,31 +1,32 @@ -import knex from "../knex"; import moment from "moment-timezone"; -import Mute from "../models/Mute"; +import { Mute } from "./entities/Mute"; +import { BaseRepository } from "./BaseRepository"; +import { getRepository, Repository, Brackets } from "typeorm"; -export class GuildMutes { - protected guildId: string; +export class GuildMutes extends BaseRepository { + private mutes: Repository; constructor(guildId) { - this.guildId = guildId; + super(guildId); + this.mutes = getRepository(Mute); } async getExpiredMutes(): Promise { - const result = await knex("mutes") - .where("guild_id", this.guildId) - .whereNotNull("expires_at") - .whereRaw("expires_at <= NOW()") - .select(); - - return result.map(r => new Mute(r)); + return this.mutes + .createQueryBuilder("mutes") + .where("guild_id = :guild_id", { guild_id: this.guildId }) + .where("expires_at IS NOT NULL") + .where("expires_at <= NOW()") + .getMany(); } async findExistingMuteForUserId(userId: string): Promise { - const result = await knex("mutes") - .where("guild_id", this.guildId) - .where("user_id", userId) - .first(); - - return result ? new Mute(result) : null; + return this.mutes.findOne({ + where: { + guild_id: this.guildId, + user_id: userId + } + }); } async addMute(userId, expiryTime) { @@ -35,13 +36,11 @@ export class GuildMutes { .format("YYYY-MM-DD HH:mm:ss") : null; - return knex - .insert({ - guild_id: this.guildId, - user_id: userId, - expires_at: expiresAt - }) - .into("mutes"); + return this.mutes.insert({ + guild_id: this.guildId, + user_id: userId, + expires_at: expiresAt + }); } async updateExpiryTime(userId, newExpiryTime) { @@ -51,12 +50,15 @@ export class GuildMutes { .format("YYYY-MM-DD HH:mm:ss") : null; - return knex("mutes") - .where("guild_id", this.guildId) - .where("user_id", userId) - .update({ + return this.mutes.update( + { + guild_id: this.guildId, + user_id: userId + }, + { expires_at: expiresAt - }); + } + ); } async addOrUpdateMute(userId, expiryTime) { @@ -70,25 +72,33 @@ export class GuildMutes { } async getActiveMutes(): Promise { - const result = await knex("mutes") - .where("guild_id", this.guildId) - .where(q => q.whereRaw("expires_at > NOW()").orWhereNull("expires_at")) - .select(); - - return result.map(r => new Mute(r)); + return this.mutes + .createQueryBuilder("mutes") + .where("guild_id = :guild_id", { guild_id: this.guildId }) + .andWhere( + new Brackets(qb => { + qb.where("expires_at > NOW()").orWhere("expires_at IS NULL"); + }) + ) + .getMany(); } async setCaseId(userId, caseId) { - await knex("mutes") - .where("guild_id", this.guildId) - .where("user_id", userId) - .update({ case_id: caseId }); + await this.mutes.update( + { + guild_id: this.guildId, + user_id: userId + }, + { + case_id: caseId + } + ); } async clear(userId) { - return knex("mutes") - .where("guild_id", this.guildId) - .where("user_id", userId) - .delete(); + await this.mutes.delete({ + guild_id: this.guildId, + user_id: userId + }); } } diff --git a/src/data/GuildPersistedData.ts b/src/data/GuildPersistedData.ts index 2126847c..85c5dcb0 100644 --- a/src/data/GuildPersistedData.ts +++ b/src/data/GuildPersistedData.ts @@ -1,5 +1,6 @@ -import knex from "../knex"; -import PersistedData from "../models/PersistedData"; +import { PersistedData } from "./entities/PersistedData"; +import { BaseRepository } from "./BaseRepository"; +import { getRepository, Repository } from "typeorm"; export interface IPartialPersistData { roles?: string[]; @@ -7,20 +8,21 @@ export interface IPartialPersistData { is_voice_muted?: boolean; } -export class GuildPersistedData { - protected guildId: string; +export class GuildPersistedData extends BaseRepository { + private persistedData: Repository; constructor(guildId) { - this.guildId = guildId; + super(guildId); + this.persistedData = getRepository(PersistedData); } async find(userId: string) { - const result = await knex("persisted_data") - .where("guild_id", this.guildId) - .where("user_id", userId) - .first(); - - return result ? new PersistedData(result) : null; + return this.persistedData.findOne({ + where: { + guild_id: this.guildId, + user_id: userId + } + }); } async set(userId: string, data: IPartialPersistData = {}) { @@ -31,12 +33,15 @@ export class GuildPersistedData { const existing = await this.find(userId); if (existing) { - await knex("persisted_data") - .where("guild_id", this.guildId) - .where("user_id", userId) - .update(finalData); + await this.persistedData.update( + { + guild_id: this.guildId, + user_id: userId + }, + finalData + ); } else { - await knex("persisted_data").insert({ + await this.persistedData.insert({ ...finalData, guild_id: this.guildId, user_id: userId @@ -45,9 +50,9 @@ export class GuildPersistedData { } async clear(userId: string) { - await knex("persisted_data") - .where("guild_id", this.guildId) - .where("user_id", userId) - .delete(); + await this.persistedData.delete({ + guild_id: this.guildId, + user_id: userId + }); } } diff --git a/src/data/GuildReactionRoles.ts b/src/data/GuildReactionRoles.ts index f282f783..1f7ad422 100644 --- a/src/data/GuildReactionRoles.ts +++ b/src/data/GuildReactionRoles.ts @@ -1,54 +1,57 @@ -import knex from "../knex"; -import ReactionRole from "../models/ReactionRole"; +import { ReactionRole } from "./entities/ReactionRole"; +import { BaseRepository } from "./BaseRepository"; +import { getRepository, Repository } from "typeorm"; -export class GuildReactionRoles { - protected guildId: string; +export class GuildReactionRoles extends BaseRepository { + private reactionRoles: Repository; constructor(guildId) { - this.guildId = guildId; + super(guildId); + this.reactionRoles = getRepository(ReactionRole); } async all(): Promise { - const results = await knex("reaction_roles") - .where("guild_id", this.guildId) - .select(); - - return results.map(r => new ReactionRole(r)); + return this.reactionRoles.find({ + where: { + guild_id: this.guildId + } + }); } async getForMessage(messageId: string): Promise { - const results = await knex("reaction_roles") - .where("guild_id", this.guildId) - .where("message_id", messageId) - .select(); - - return results.map(r => new ReactionRole(r)); + return this.reactionRoles.find({ + where: { + guild_id: this.guildId, + message_id: messageId + } + }); } async getByMessageAndEmoji(messageId: string, emoji: string): Promise { - const result = await knex("reaction_roles") - .where("guild_id", this.guildId) - .where("message_id", messageId) - .where("emoji", emoji) - .first(); - - return result ? new ReactionRole(result) : null; + return this.reactionRoles.findOne({ + where: { + guild_id: this.guildId, + message_id: messageId, + emoji + } + }); } async removeFromMessage(messageId: string, emoji: string = null) { - let query = knex("reaction_roles") - .where("guild_id", this.guildId) - .where("message_id", messageId); + const criteria: any = { + guild_id: this.guildId, + message_id: messageId + }; if (emoji) { - query = query.where("emoji", emoji); + criteria.emoji = emoji; } - await query.delete(); + await this.reactionRoles.delete(criteria); } async add(channelId: string, messageId: string, emoji: string, roleId: string) { - await knex("reaction_roles").insert({ + await this.reactionRoles.insert({ guild_id: this.guildId, channel_id: channelId, message_id: messageId, diff --git a/src/data/GuildTags.ts b/src/data/GuildTags.ts index f432642b..5e4b5287 100644 --- a/src/data/GuildTags.ts +++ b/src/data/GuildTags.ts @@ -1,36 +1,40 @@ -import knex from "../knex"; -import moment from "moment-timezone"; -import Tag from "../models/Tag"; +import { Tag } from "./entities/Tag"; +import { getRepository, Repository } from "typeorm"; +import { BaseRepository } from "./BaseRepository"; -export class GuildTags { - protected guildId: string; +export class GuildTags extends BaseRepository { + private tags: Repository; constructor(guildId) { - this.guildId = guildId; + super(guildId); + this.tags = getRepository(Tag); } async find(tag): Promise { - const result = await knex("tags") - .where("guild_id", this.guildId) - .where("tag", tag) - .first(); - - return result ? new Tag(result) : null; + return this.tags.findOne({ + where: { + guild_id: this.guildId, + tag + } + }); } async createOrUpdate(tag, body, userId) { const existingTag = await this.find(tag); if (existingTag) { - await knex("tags") - .where("guild_id", this.guildId) - .where("tag", tag) - .update({ + await this.tags + .createQueryBuilder() + .update() + .set({ body, user_id: userId, - created_at: knex.raw("NOW()") - }); + created_at: () => "NOW()" + }) + .where("guild_id = :guildId", { guildId: this.guildId }) + .where("tag = :tag", { tag }) + .execute(); } else { - await knex("tags").insert({ + await this.tags.insert({ guild_id: this.guildId, user_id: userId, tag, @@ -40,9 +44,9 @@ export class GuildTags { } async delete(tag) { - await knex("tags") - .where("guild_id", this.guildId) - .where("tag", tag) - .delete(); + await this.tags.delete({ + guild_id: this.guildId, + tag + }); } } diff --git a/src/data/db.ts b/src/data/db.ts new file mode 100644 index 00000000..7e108463 --- /dev/null +++ b/src/data/db.ts @@ -0,0 +1,23 @@ +import { SimpleError } from "../SimpleError"; +import { Connection, createConnection } from "typeorm"; + +let connectionPromise: Promise; + +export let connection: Connection; + +export function connect() { + if (!connectionPromise) { + connectionPromise = createConnection().then(newConnection => { + return newConnection.query("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP) AS tz").then(r => { + if (r[0].tz !== "00:00:00") { + throw new SimpleError(`Database timezone must be UTC (detected ${r[0].tz})`); + } + + connection = newConnection; + return newConnection; + }); + }); + } + + return connectionPromise; +} diff --git a/src/data/entities/ArchiveEntry.ts b/src/data/entities/ArchiveEntry.ts new file mode 100644 index 00000000..e3250665 --- /dev/null +++ b/src/data/entities/ArchiveEntry.ts @@ -0,0 +1,16 @@ +import { Entity, Column, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm"; + +@Entity("archives") +export class ArchiveEntry { + @Column() + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column() guild_id: string; + + @Column() body: string; + + @Column() created_at: string; + + @Column() expires_at: string; +} diff --git a/src/data/entities/Case.ts b/src/data/entities/Case.ts new file mode 100644 index 00000000..6cb779b5 --- /dev/null +++ b/src/data/entities/Case.ts @@ -0,0 +1,28 @@ +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; +import { CaseNote } from "./CaseNote"; + +@Entity("cases") +export class Case { + @PrimaryGeneratedColumn() id: number; + + @Column() guild_id: string; + + @Column() case_number: number; + + @Column() user_id: string; + + @Column() user_name: string; + + @Column() mod_id: string; + + @Column() mod_name: string; + + @Column() type: number; + + @Column() audit_log_id: string; + + @Column() created_at: string; + + @OneToMany(type => CaseNote, note => note.case) + notes: CaseNote[]; +} diff --git a/src/data/entities/CaseNote.ts b/src/data/entities/CaseNote.ts new file mode 100644 index 00000000..109f72de --- /dev/null +++ b/src/data/entities/CaseNote.ts @@ -0,0 +1,21 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from "typeorm"; +import { Case } from "./Case"; + +@Entity("case_notes") +export class CaseNote { + @PrimaryGeneratedColumn() id: number; + + @Column() case_id: number; + + @Column() mod_id: string; + + @Column() mod_name: string; + + @Column() body: string; + + @Column() created_at: string; + + @ManyToOne(type => Case, theCase => theCase.notes) + @JoinColumn({ name: "case_id" }) + case: Case; +} diff --git a/src/data/entities/Mute.ts b/src/data/entities/Mute.ts new file mode 100644 index 00000000..6544b441 --- /dev/null +++ b/src/data/entities/Mute.ts @@ -0,0 +1,18 @@ +import { Entity, Column, PrimaryColumn } from "typeorm"; + +@Entity("mutes") +export class Mute { + @Column() + @PrimaryColumn() + guild_id: string; + + @Column() + @PrimaryColumn() + user_id: string; + + @Column() created_at: string; + + @Column() expires_at: string; + + @Column() case_id: number; +} diff --git a/src/data/entities/PersistedData.ts b/src/data/entities/PersistedData.ts new file mode 100644 index 00000000..d7acff2a --- /dev/null +++ b/src/data/entities/PersistedData.ts @@ -0,0 +1,18 @@ +import { Entity, Column, PrimaryColumn } from "typeorm"; + +@Entity("persisted_data") +export class PersistedData { + @Column() + @PrimaryColumn() + guild_id: string; + + @Column() + @PrimaryColumn() + user_id: string; + + @Column() roles: string; + + @Column() nickname: string; + + @Column() is_voice_muted: number; +} diff --git a/src/data/entities/ReactionRole.ts b/src/data/entities/ReactionRole.ts new file mode 100644 index 00000000..ebefd1a0 --- /dev/null +++ b/src/data/entities/ReactionRole.ts @@ -0,0 +1,22 @@ +import { Entity, Column, PrimaryColumn } from "typeorm"; + +@Entity("reaction_roles") +export class ReactionRole { + @Column() + @PrimaryColumn() + guild_id: string; + + @Column() + @PrimaryColumn() + channel_id: string; + + @Column() + @PrimaryColumn() + message_id: string; + + @Column() + @PrimaryColumn() + emoji: string; + + @Column() role_id: string; +} diff --git a/src/data/entities/Tag.ts b/src/data/entities/Tag.ts new file mode 100644 index 00000000..948e0f14 --- /dev/null +++ b/src/data/entities/Tag.ts @@ -0,0 +1,18 @@ +import { Entity, Column, PrimaryColumn, CreateDateColumn } from "typeorm"; + +@Entity("tags") +export class Tag { + @Column() + @PrimaryColumn() + guild_id: string; + + @Column() + @PrimaryColumn() + tag: string; + + @Column() user_id: string; + + @Column() body: string; + + @Column() created_at: string; +} diff --git a/src/index.ts b/src/index.ts index a5bc560e..3c5e5320 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,9 @@ +import path from "path"; +import yaml from "js-yaml"; + +import _fs from "fs"; +const fs = _fs.promises; + require("dotenv").config(); process.on("unhandledRejection", (reason, p) => { @@ -17,13 +23,12 @@ process.on("uncaughtException", err => { }); // Always use UTC -// This is also set for the database in knexfile import moment from "moment-timezone"; moment.tz.setDefault("UTC"); import { Client } from "eris"; import { Knub, logger } from "knub"; -import knex from "./knex"; +import { connect } from "./data/db"; // Global plugins import { BotControlPlugin } from "./plugins/BotControl"; @@ -42,7 +47,9 @@ import { TagsPlugin } from "./plugins/Tags"; // Run latest database migrations logger.info("Running database migrations"); -knex.migrate.latest().then(() => { +connect().then(async conn => { + await conn.runMigrations(); + const client = new Client(process.env.TOKEN, { getAllUsers: true }); @@ -72,6 +79,25 @@ knex.migrate.latest().then(() => { return keys.filter(pluginName => { return plugins[pluginName] && plugins[pluginName].enabled !== false; }); + }, + + async getConfig(id) { + const configFile = id ? `${id}.yml` : "global.yml"; + const configPath = path.join("config", configFile); + + try { + await fs.access(configPath); + } catch (e) { + return {}; + } + + const yamlString = await fs.readFile(configPath, { encoding: "utf8" }); + return yaml.safeLoad(yamlString); + }, + + logFn: (level, msg) => { + if (level === "debug") return; + console.log(`[${level.toUpperCase()}] ${msg}`); } } }); diff --git a/src/knex.ts b/src/knex.ts deleted file mode 100644 index 41a2e820..00000000 --- a/src/knex.ts +++ /dev/null @@ -1,6 +0,0 @@ -const knexfile = require("../knexfile"); -import knex from "knex"; - -const db = knex(knexfile); - -export default db; diff --git a/src/models/Case.ts b/src/models/Case.ts deleted file mode 100644 index 5b0cd5a5..00000000 --- a/src/models/Case.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Model from "./Model"; - -export default class Case extends Model { - public id: number; - public guild_id: string; - public case_number: number; - public user_id: string; - public user_name: string; - public mod_id: string; - public mod_name: string; - public type: number; - public audit_log_id: string; - public created_at: string; -} diff --git a/src/models/CaseNote.ts b/src/models/CaseNote.ts deleted file mode 100644 index 1b197c4c..00000000 --- a/src/models/CaseNote.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Model from "./Model"; - -export default class CaseNote extends Model { - public id: number; - public case_id: number; - public mod_id: string; - public mod_name: string; - public body: string; - public created_at: string; -} diff --git a/src/models/Model.ts b/src/models/Model.ts deleted file mode 100644 index 28643e75..00000000 --- a/src/models/Model.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default class Model { - constructor(props) { - for (const key in props) { - this[key] = props[key]; - } - } -} diff --git a/src/models/Mute.ts b/src/models/Mute.ts deleted file mode 100644 index 2e58bc4b..00000000 --- a/src/models/Mute.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Model from "./Model"; - -export default class Mute extends Model { - public guild_id: string; - public user_id: string; - public case_id: number; - public created_at: string; - public expires_at: string; -} diff --git a/src/models/PersistedData.ts b/src/models/PersistedData.ts deleted file mode 100644 index b4b1e2e0..00000000 --- a/src/models/PersistedData.ts +++ /dev/null @@ -1,26 +0,0 @@ -import Model from "./Model"; - -export default class PersistedData extends Model { - private _roles; - private _isVoiceMuted; - - public guild_id: string; - public user_id: string; - public nickname: string; - - set roles(v) { - this._roles = v ? v.split(",") : []; - } - - get roles() { - return this._roles; - } - - set is_voice_muted(v) { - this._isVoiceMuted = v === 1; - } - - get is_voice_muted() { - return this._isVoiceMuted; - } -} diff --git a/src/models/ReactionRole.ts b/src/models/ReactionRole.ts deleted file mode 100644 index f24a47e7..00000000 --- a/src/models/ReactionRole.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Model from "./Model"; - -export default class ReactionRole extends Model { - public guild_id: string; - public channel_id: string; - public message_id: string; - public emoji: string; - public role_id: string; -} diff --git a/src/models/SpamLog.ts b/src/models/SpamLog.ts deleted file mode 100644 index 6b92b0e8..00000000 --- a/src/models/SpamLog.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Model from "./Model"; - -export default class SpamLog extends Model { - public id: string; - public guild_id: string; - public body: string; - public created_at: string; - public expires_at: string; -} diff --git a/src/models/Tag.ts b/src/models/Tag.ts deleted file mode 100644 index 343de491..00000000 --- a/src/models/Tag.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Model from "./Model"; - -export default class Tag extends Model { - public guild_id: string; - public tag: string; - public user_id: string; - public body: string; - public created_at: string; -} diff --git a/src/plugins/ModActions.ts b/src/plugins/ModActions.ts index 94a7577a..9e969e1f 100644 --- a/src/plugins/ModActions.ts +++ b/src/plugins/ModActions.ts @@ -11,14 +11,13 @@ import { errorMessage, findRelevantAuditLogEntry, formatTemplateString, - sleep, stripObjectToScalars, successMessage, trimLines } from "../utils"; import { GuildMutes } from "../data/GuildMutes"; -import Case from "../models/Case"; -import { CaseType } from "../data/CaseType"; +import { Case } from "../data/entities/Case"; +import { CaseTypes } from "../data/CaseTypes"; import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; import Timer = NodeJS.Timer; @@ -47,8 +46,8 @@ export class ModActionsPlugin extends Plugin { protected ignoredEvents: IIgnoredEvent[]; async onLoad() { - this.cases = new GuildCases(this.guildId); - this.mutes = new GuildMutes(this.guildId); + this.cases = GuildCases.getInstance(this.guildId); + this.mutes = GuildMutes.getInstance(this.guildId); this.serverLogs = new GuildLogs(this.guildId); this.ignoredEvents = []; @@ -156,9 +155,9 @@ export class ModActionsPlugin extends Plugin { const modId = relevantAuditLogEntry.user.id; const auditLogId = relevantAuditLogEntry.id; - await this.createCase(user.id, modId, CaseType.Ban, auditLogId, relevantAuditLogEntry.reason, true); + await this.createCase(user.id, modId, CaseTypes.Ban, auditLogId, relevantAuditLogEntry.reason, true); } else { - await this.createCase(user.id, null, CaseType.Ban); + await this.createCase(user.id, null, CaseTypes.Ban); } } @@ -183,9 +182,9 @@ export class ModActionsPlugin extends Plugin { const modId = relevantAuditLogEntry.user.id; const auditLogId = relevantAuditLogEntry.id; - await this.createCase(user.id, modId, CaseType.Unban, auditLogId, null, true); + await this.createCase(user.id, modId, CaseTypes.Unban, auditLogId, null, true); } else { - await this.createCase(user.id, null, CaseType.Unban); + await this.createCase(user.id, null, CaseTypes.Unban); } } @@ -228,7 +227,7 @@ export class ModActionsPlugin extends Plugin { this.createCase( member.id, kickAuditLogEntry.user.id, - CaseType.Kick, + CaseTypes.Kick, kickAuditLogEntry.id, kickAuditLogEntry.reason, true @@ -274,12 +273,13 @@ export class ModActionsPlugin extends Plugin { const user = await this.bot.users.get(args.userId); const userName = user ? `${user.username}#${user.discriminator}` : "member"; - await this.createCase(args.userId, msg.author.id, CaseType.Note, null, args.note); + await this.createCase(args.userId, msg.author.id, CaseTypes.Note, null, args.note); msg.channel.createMessage(successMessage(`Note added on ${userName}`)); } @d.command("warn", " ") @d.permission("warn") + @d.nonBlocking() async warnCmd(msg: Message, args: any) { // Make sure we're allowed to warn this member if (!this.canActOn(msg.member, args.member)) { @@ -307,7 +307,7 @@ export class ModActionsPlugin extends Plugin { } } - await this.createCase(args.member.id, msg.author.id, CaseType.Warn, null, args.reason); + await this.createCase(args.member.id, msg.author.id, CaseTypes.Warn, null, args.reason); msg.channel.createMessage( successMessage(`Warned **${args.member.user.username}#${args.member.user.discriminator}**`) @@ -363,7 +363,7 @@ export class ModActionsPlugin extends Plugin { } } else { // Create a case - const caseId = await this.createCase(args.member.id, msg.author.id, CaseType.Mute, null, args.reason); + const caseId = await this.createCase(args.member.id, msg.author.id, CaseTypes.Mute, null, args.reason); await this.mutes.setCaseId(args.member.id, caseId); } @@ -458,7 +458,7 @@ export class ModActionsPlugin extends Plugin { } // Create a case - await this.createCase(args.member.id, msg.author.id, CaseType.Unmute, null, args.reason); + await this.createCase(args.member.id, msg.author.id, CaseTypes.Unmute, null, args.reason); // Log the action this.serverLogs.log(LogType.MEMBER_UNMUTE, { @@ -568,7 +568,7 @@ export class ModActionsPlugin extends Plugin { args.member.kick(args.reason); // Create a case for this action - await this.createCase(args.member.id, msg.author.id, CaseType.Kick, null, args.reason); + await this.createCase(args.member.id, msg.author.id, CaseTypes.Kick, null, args.reason); // Confirm the action to the moderator let response = `Kicked **${args.member.user.username}#${args.member.user.discriminator}**`; @@ -613,7 +613,7 @@ export class ModActionsPlugin extends Plugin { args.member.ban(1, args.reason); // Create a case for this action - await this.createCase(args.member.id, msg.author.id, CaseType.Ban, null, args.reason); + await this.createCase(args.member.id, msg.author.id, CaseTypes.Ban, null, args.reason); // Confirm the action to the moderator let response = `Banned **${args.member.user.username}#${args.member.user.discriminator}**`; @@ -646,7 +646,7 @@ export class ModActionsPlugin extends Plugin { await this.guild.unbanMember(args.member.id); // Create a case for this action - await this.createCase(args.member.id, msg.author.id, CaseType.Softban, null, args.reason); + await this.createCase(args.member.id, msg.author.id, CaseTypes.Softban, null, args.reason); // Confirm the action to the moderator msg.channel.createMessage( @@ -677,7 +677,7 @@ export class ModActionsPlugin extends Plugin { msg.channel.createMessage(successMessage("Member unbanned!")); // Create a case - this.createCase(args.userId, msg.author.id, CaseType.Unban, null, args.reason); + this.createCase(args.userId, msg.author.id, CaseTypes.Unban, null, args.reason); // Log the action this.serverLogs.log(LogType.MEMBER_UNBAN, { @@ -710,7 +710,7 @@ export class ModActionsPlugin extends Plugin { msg.channel.createMessage(successMessage("Member forcebanned!")); // Create a case - this.createCase(args.userId, msg.author.id, CaseType.Ban, null, args.reason); + this.createCase(args.userId, msg.author.id, CaseTypes.Ban, null, args.reason); // Log the action this.serverLogs.log(LogType.MEMBER_FORCEBAN, { @@ -721,6 +721,7 @@ export class ModActionsPlugin extends Plugin { @d.command("massban", "") @d.permission("massban") + @d.nonBlocking() async massbanCmd(msg: Message, args: { userIds: string[] }) { // Limit to 100 users at once (arbitrary?) if (args.userIds.length > 100) { @@ -763,7 +764,7 @@ export class ModActionsPlugin extends Plugin { for (const userId of args.userIds) { try { await this.guild.banMember(userId); - await this.createCase(userId, msg.author.id, CaseType.Ban, null, `Mass ban: ${banReason}`, false, false); + await this.createCase(userId, msg.author.id, CaseTypes.Ban, null, `Mass ban: ${banReason}`, false, false); } catch (e) { failedBans.push(userId); } @@ -811,13 +812,13 @@ export class ModActionsPlugin extends Plugin { // Verify the case type is valid const type: string = args.type[0].toUpperCase() + args.type.slice(1).toLowerCase(); - if (!CaseType[type]) { + if (!CaseTypes[type]) { msg.channel.createMessage(errorMessage("Cannot add case: invalid case type")); return; } // Create the case - const caseId = await this.createCase(args.target, msg.author.id, CaseType[type], null, args.reason); + const caseId = await this.createCase(args.target, msg.author.id, CaseTypes[type], null, args.reason); const theCase = await this.cases.find(caseId); // Log the action @@ -852,7 +853,7 @@ export class ModActionsPlugin extends Plugin { @d.command(/cases|usercases/, " [expanded:string]") @d.permission("view") async usercasesCmd(msg: Message, args: { userId: string; expanded?: string }) { - const cases = await this.cases.getByUserId(args.userId); + const cases = await this.cases.with("notes").getByUserId(args.userId); const user = this.bot.users.get(args.userId); const userName = user ? `${user.username}#${user.discriminator}` : "Unknown#0000"; const prefix = this.knub.getGuildData(this.guildId).config.prefix; @@ -869,7 +870,8 @@ export class ModActionsPlugin extends Plugin { // Compact view (= regular message with a preview of each case) const lines = []; for (const theCase of cases) { - const firstNote = await this.cases.findFirstCaseNote(theCase.id); + theCase.notes.sort((a, b) => (a.created_at > b.created_at ? 1 : -1)); + const firstNote = theCase.notes[0]; let reason = firstNote ? firstNote.body : ""; if (reason.length > CASE_LIST_REASON_MAX_LENGTH) { @@ -882,7 +884,7 @@ export class ModActionsPlugin extends Plugin { reason = disableLinkPreviews(reason); - lines.push(`Case \`#${theCase.case_number}\` __${CaseType[theCase.type]}__ ${reason}`); + lines.push(`Case \`#${theCase.case_number}\` __${CaseTypes[theCase.type]}__ ${reason}`); } const finalMessage = trimLines(` @@ -945,7 +947,7 @@ export class ModActionsPlugin extends Plugin { protected async displayCase(caseOrCaseId: Case | number, channelId: string) { let theCase: Case; if (typeof caseOrCaseId === "number") { - theCase = await this.cases.find(caseOrCaseId); + theCase = await this.cases.with("notes").find(caseOrCaseId); } else { theCase = caseOrCaseId; } @@ -953,10 +955,8 @@ export class ModActionsPlugin extends Plugin { if (!theCase) return; if (!this.guild.channels.get(channelId)) return; - const notes = await this.cases.getCaseNotes(theCase.id); - const createdAt = moment(theCase.created_at); - const actionTypeStr = CaseType[theCase.type].toUpperCase(); + const actionTypeStr = CaseTypes[theCase.type].toUpperCase(); const embed: any = { title: `${actionTypeStr} - Case #${theCase.case_number}`, @@ -981,8 +981,8 @@ export class ModActionsPlugin extends Plugin { embed.color = CaseTypeColors[theCase.type]; } - if (notes.length) { - notes.forEach((note: any) => { + if (theCase.notes.length) { + theCase.notes.forEach((note: any) => { const noteDate = moment(note.created_at); embed.fields.push({ name: `${note.mod_name} at ${noteDate.format("YYYY-MM-DD [at] HH:mm")}:`, @@ -1014,7 +1014,7 @@ export class ModActionsPlugin extends Plugin { public async createCase( userId: string, modId: string, - caseType: CaseType, + caseType: CaseTypes, auditLogId: string = null, reason: string = null, automatic = false, diff --git a/src/plugins/Persist.ts b/src/plugins/Persist.ts index 00903270..1d9bab93 100644 --- a/src/plugins/Persist.ts +++ b/src/plugins/Persist.ts @@ -21,7 +21,7 @@ export class PersistPlugin extends Plugin { } onLoad() { - this.persistedData = new GuildPersistedData(this.guildId); + this.persistedData = GuildPersistedData.getInstance(this.guildId); this.logs = new GuildLogs(this.guildId); } diff --git a/src/plugins/ReactionRoles.ts b/src/plugins/ReactionRoles.ts index d5dce3f5..74c4a483 100644 --- a/src/plugins/ReactionRoles.ts +++ b/src/plugins/ReactionRoles.ts @@ -30,20 +30,17 @@ export class ReactionRolesPlugin extends Plugin { } async onLoad() { - this.reactionRoles = new GuildReactionRoles(this.guildId); + this.reactionRoles = GuildReactionRoles.getInstance(this.guildId); return; // Pre-fetch all messages with reaction roles so we get their events const reactionRoles = await this.reactionRoles.all(); - const channelMessages: Map> = reactionRoles.reduce( - (map: Map>, row) => { - if (!map.has(row.channel_id)) map.set(row.channel_id, new Set()); - map.get(row.channel_id).add(row.message_id); - return map; - }, - new Map() - ); + const channelMessages: Map> = reactionRoles.reduce((map: Map>, row) => { + if (!map.has(row.channel_id)) map.set(row.channel_id, new Set()); + map.get(row.channel_id).add(row.message_id); + return map; + }, new Map()); const msgLoadPromises = []; @@ -62,10 +59,7 @@ export class ReactionRolesPlugin extends Plugin { @d.command("reaction_roles", " ") @d.permission("manage") - async reactionRolesCmd( - msg: Message, - args: { channel: Channel; messageId: string; reactionRolePairs: string } - ) { + async reactionRolesCmd(msg: Message, args: { channel: Channel; messageId: string; reactionRolePairs: string }) { if (!(args.channel instanceof TextChannel)) { msg.channel.createMessage(errorMessage("Channel must be a text channel!")); return; @@ -100,9 +94,7 @@ export class ReactionRolesPlugin extends Plugin { // Verify the specified emojis and roles are valid for (const pair of newRolePairs) { if (isSnowflake(pair[0]) && !guildEmojiIds.includes(pair[0])) { - msg.channel.createMessage( - errorMessage("I can only use regular emojis and custom emojis from this server") - ); + msg.channel.createMessage(errorMessage("I can only use regular emojis and custom emojis from this server")); return; } @@ -113,9 +105,7 @@ export class ReactionRolesPlugin extends Plugin { } const oldReactionRoles = await this.reactionRoles.getForMessage(targetMessage.id); - const oldRolePairs: ReactionRolePair[] = oldReactionRoles.map( - r => [r.emoji, r.role_id] as ReactionRolePair - ); + const oldRolePairs: ReactionRolePair[] = oldReactionRoles.map(r => [r.emoji, r.role_id] as ReactionRolePair); // Remove old reaction/role pairs that weren't included in the new pairs or were changed in some way const toRemove = oldRolePairs.filter( @@ -154,10 +144,7 @@ export class ReactionRolesPlugin extends Plugin { @d.event("messageReactionAdd") async onAddReaction(msg: Message, emoji: CustomEmoji, userId: string) { - const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji( - msg.id, - emoji.id || emoji.name - ); + const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(msg.id, emoji.id || emoji.name); if (!matchingReactionRole) return; const member = this.guild.members.get(userId); @@ -168,10 +155,7 @@ export class ReactionRolesPlugin extends Plugin { @d.event("messageReactionRemove") async onRemoveReaction(msg: Message, emoji: CustomEmoji, userId: string) { - const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji( - msg.id, - emoji.id || emoji.name - ); + const matchingReactionRole = await this.reactionRoles.getByMessageAndEmoji(msg.id, emoji.id || emoji.name); if (!matchingReactionRole) return; const member = this.guild.members.get(userId); diff --git a/src/plugins/Spam.ts b/src/plugins/Spam.ts index 8c171efe..dcdee4fe 100644 --- a/src/plugins/Spam.ts +++ b/src/plugins/Spam.ts @@ -1,19 +1,18 @@ import { decorators as d, Plugin } from "knub"; -import { Channel, Message, TextChannel, User } from "eris"; +import { Channel, Message, User } from "eris"; import { formatTemplateString, getEmojiInString, getRoleMentions, getUrlsInString, getUserMentions, - sleep, stripObjectToScalars, trimLines } from "../utils"; import { LogType } from "../data/LogType"; import { GuildLogs } from "../data/GuildLogs"; import { ModActionsPlugin } from "./ModActions"; -import { CaseType } from "../data/CaseType"; +import { CaseTypes } from "../data/CaseTypes"; import { GuildArchives } from "../data/GuildArchives"; import moment from "moment-timezone"; @@ -23,7 +22,8 @@ enum RecentActionType { Link, Attachment, Emoji, - Newline + Newline, + Censor } interface IRecentAction { @@ -96,7 +96,7 @@ export class SpamPlugin extends Plugin { onLoad() { this.logs = new GuildLogs(this.guildId); - this.archives = new GuildArchives(this.guildId); + this.archives = GuildArchives.getInstance(this.guildId); this.recentActions = []; this.expiryInterval = setInterval(() => this.clearOldRecentActions(), 1000 * 60); @@ -260,7 +260,7 @@ export class SpamPlugin extends Plugin { const logUrl = await this.saveSpamArchives(uniqueMessages, msg.channel, msg.author); // Create a case and log the actions taken above - const caseType = spamConfig.mute ? CaseType.Mute : CaseType.Note; + const caseType = spamConfig.mute ? CaseTypes.Mute : CaseTypes.Note; const caseText = trimLines(` Automatic spam detection: ${description} (over ${spamConfig.count} in ${spamConfig.interval}s) ${logUrl} @@ -297,6 +297,14 @@ export class SpamPlugin extends Plugin { ); } + // For interoperability with the Censor plugin + async logCensor(msg: Message) { + const spamConfig = this.configValueForMsg(msg, "max_censor"); + if (spamConfig) { + this.logAndDetectSpam(msg, RecentActionType.Censor, spamConfig, 1, "too many censored messages"); + } + } + @d.event("messageCreate") async onMessageCreate(msg: Message) { if (msg.author.bot) return; diff --git a/src/plugins/Tags.ts b/src/plugins/Tags.ts index 948afc66..929c1439 100644 --- a/src/plugins/Tags.ts +++ b/src/plugins/Tags.ts @@ -1,5 +1,5 @@ import { Plugin, decorators as d } from "knub"; -import { Channel, Message, TextChannel } from "eris"; +import { Message } from "eris"; import { errorMessage, successMessage } from "../utils"; import { GuildTags } from "../data/GuildTags"; @@ -29,7 +29,7 @@ export class TagsPlugin extends Plugin { } onLoad() { - this.tags = new GuildTags(this.guildId); + this.tags = GuildTags.getInstance(this.guildId); } @d.command("tag", " ") diff --git a/src/plugins/Utility.ts b/src/plugins/Utility.ts index f9b2238e..aac9ff36 100644 --- a/src/plugins/Utility.ts +++ b/src/plugins/Utility.ts @@ -1,19 +1,12 @@ import { Plugin, decorators as d, reply } from "knub"; import { Channel, EmbedOptions, Message, TextChannel, User, VoiceChannel } from "eris"; -import { - embedPadding, - errorMessage, - getMessages, - stripObjectToScalars, - successMessage, - trimLines -} from "../utils"; +import { embedPadding, errorMessage, getMessages, stripObjectToScalars, successMessage, trimLines } from "../utils"; import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; import moment from "moment-timezone"; import humanizeDuration from "humanize-duration"; import { GuildCases } from "../data/GuildCases"; -import { CaseType } from "../data/CaseType"; +import { CaseTypes } from "../data/CaseTypes"; const MAX_SEARCH_RESULTS = 15; const MAX_CLEAN_COUNT = 50; @@ -54,7 +47,7 @@ export class UtilityPlugin extends Plugin { onLoad() { this.logs = new GuildLogs(this.guildId); - this.cases = new GuildCases(this.guildId); + this.cases = GuildCases.getInstance(this.guildId); if (activeReloads && activeReloads.has(this.guildId)) { activeReloads.get(this.guildId).createMessage(successMessage("Reloaded!")); @@ -80,9 +73,7 @@ export class UtilityPlugin extends Plugin { } const level = this.getMemberLevel(member); - msg.channel.createMessage( - `The permission level of ${member.username}#${member.discriminator} is **${level}**` - ); + msg.channel.createMessage(`The permission level of ${member.username}#${member.discriminator} is **${level}**`); } @d.command("search", "") @@ -126,9 +117,7 @@ export class UtilityPlugin extends Plugin { }); lines = lines.slice(from, to); - const footer = paginated - ? "Add a page number to the end of the command to browse results" - : ""; + const footer = paginated ? "Add a page number to the end of the command to browse results" : ""; msg.channel.createMessage(`${header}\n\`\`\`${lines.join("\n")}\`\`\`${footer}`); } else { @@ -152,25 +141,17 @@ export class UtilityPlugin extends Plugin { @d.permission("clean") async cleanAllCmd(msg: Message, args: { count: number }) { if (args.count > MAX_CLEAN_COUNT || args.count <= 0) { - msg.channel.createMessage( - errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`) - ); + msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)); return; } - const messagesToClean = await getMessages( - msg.channel as TextChannel, - m => m.id !== msg.id, - args.count - ); + const messagesToClean = await getMessages(msg.channel as TextChannel, m => m.id !== msg.id, args.count); if (messagesToClean.length > 0) { await this.cleanMessages(msg.channel, messagesToClean.map(m => m.id), msg.author); } msg.channel.createMessage( - successMessage( - `Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}` - ) + successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`) ); } @@ -178,9 +159,7 @@ export class UtilityPlugin extends Plugin { @d.permission("clean") async cleanUserCmd(msg: Message, args: { userId: string; count: number }) { if (args.count > MAX_CLEAN_COUNT || args.count <= 0) { - msg.channel.createMessage( - errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`) - ); + msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)); return; } @@ -194,9 +173,7 @@ export class UtilityPlugin extends Plugin { } msg.channel.createMessage( - successMessage( - `Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}` - ) + successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`) ); } @@ -204,9 +181,7 @@ export class UtilityPlugin extends Plugin { @d.permission("clean") async cleanBotCmd(msg: Message, args: { count: number }) { if (args.count > MAX_CLEAN_COUNT || args.count <= 0) { - msg.channel.createMessage( - errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`) - ); + msg.channel.createMessage(errorMessage(`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`)); return; } @@ -220,9 +195,7 @@ export class UtilityPlugin extends Plugin { } msg.channel.createMessage( - successMessage( - `Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}` - ) + successMessage(`Cleaned ${messagesToClean.length} ${messagesToClean.length === 1 ? "message" : "messages"}`) ); } @@ -283,7 +256,7 @@ export class UtilityPlugin extends Plugin { }); const caseSummaries = cases.map(c => { - return `${CaseType[c.type]} (#${c.case_number})`; + return `${CaseTypes[c.type]} (#${c.case_number})`; }); embed.fields.push({ diff --git a/tsconfig.json b/tsconfig.json index 4449f0a2..b6fa8ed1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,8 @@ "noImplicitAny": false, "allowSyntheticDefaultImports": true, "experimentalDecorators": true, - "target": "ES2017", + "emitDecoratorMetadata": true, + "target": "ES6", "lib": [ "es6", "es7",