mirror of
https://github.com/BluemediaGER/muse.git
synced 2024-11-22 16:55:30 +01:00
Setup and migrate to Prisma (#456)
This commit is contained in:
parent
129d121364
commit
51d378e4cb
|
@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Changed
|
||||
- Migrated from Sequelize to Prisma. (#456)
|
||||
|
||||
## [0.2.1] - 2021-12-18
|
||||
### Added
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE `FileCaches` (`hash` VARCHAR(255) UNIQUE PRIMARY KEY, `bytes` INTEGER, `accessedAt` DATETIME, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `KeyValueCaches` (`key` VARCHAR(255) UNIQUE PRIMARY KEY, `value` TEXT, `expiresAt` DATETIME, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Settings` (`guildId` VARCHAR(255) UNIQUE PRIMARY KEY, `prefix` VARCHAR(255), `channel` VARCHAR(255), `finishedSetup` TINYINT(1) DEFAULT 0, `playlistLimit` INTEGER DEFAULT '50', `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Shortcuts` (`id` INTEGER PRIMARY KEY, `guildId` VARCHAR(255), `authorId` VARCHAR(255), `shortcut` VARCHAR(255), `command` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "shortcuts_shortcut" ON "Shortcuts"("shortcut");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "shortcuts_guild_id" ON "Shortcuts"("guildId");
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to alter the column `finishedSetup` on the `Settings` table. The data in that column could be lost. The data in that column will be cast from `Unsupported("tinyint(1)")` to `Boolean`.
|
||||
- Made the column `expiresAt` on table `KeyValueCaches` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `key` on table `KeyValueCaches` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `value` on table `KeyValueCaches` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `authorId` on table `Shortcuts` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `command` on table `Shortcuts` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `guildId` on table `Shortcuts` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `id` on table `Shortcuts` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `shortcut` on table `Shortcuts` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `accessedAt` on table `FileCaches` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `bytes` on table `FileCaches` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `hash` on table `FileCaches` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `guildId` on table `Settings` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `prefix` on table `Settings` required. This step will fail if there are existing NULL values in that column.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_KeyValueCaches" (
|
||||
"key" TEXT NOT NULL PRIMARY KEY,
|
||||
"value" TEXT NOT NULL,
|
||||
"expiresAt" DATETIME NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_KeyValueCaches" ("createdAt", "expiresAt", "key", "updatedAt", "value") SELECT "createdAt", "expiresAt", "key", "updatedAt", "value" FROM "KeyValueCaches";
|
||||
DROP TABLE "KeyValueCaches";
|
||||
ALTER TABLE "new_KeyValueCaches" RENAME TO "KeyValueCache";
|
||||
CREATE TABLE "new_Shortcuts" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"guildId" TEXT NOT NULL,
|
||||
"authorId" TEXT NOT NULL,
|
||||
"shortcut" TEXT NOT NULL,
|
||||
"command" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Shortcuts" ("authorId", "command", "createdAt", "guildId", "id", "shortcut", "updatedAt") SELECT "authorId", "command", "createdAt", "guildId", "id", "shortcut", "updatedAt" FROM "Shortcuts";
|
||||
DROP TABLE "Shortcuts";
|
||||
ALTER TABLE "new_Shortcuts" RENAME TO "Shortcut";
|
||||
CREATE INDEX "shortcuts_shortcut" ON "Shortcut"("shortcut");
|
||||
CREATE INDEX "shortcuts_guild_id" ON "Shortcut"("guildId");
|
||||
CREATE TABLE "new_FileCaches" (
|
||||
"hash" TEXT NOT NULL PRIMARY KEY,
|
||||
"bytes" INTEGER NOT NULL,
|
||||
"accessedAt" DATETIME NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_FileCaches" ("accessedAt", "bytes", "createdAt", "hash", "updatedAt") SELECT "accessedAt", "bytes", "createdAt", "hash", "updatedAt" FROM "FileCaches";
|
||||
DROP TABLE "FileCaches";
|
||||
ALTER TABLE "new_FileCaches" RENAME TO "FileCache";
|
||||
CREATE TABLE "new_Settings" (
|
||||
"guildId" TEXT NOT NULL PRIMARY KEY,
|
||||
"prefix" TEXT NOT NULL,
|
||||
"channel" TEXT,
|
||||
"finishedSetup" BOOLEAN NOT NULL DEFAULT false,
|
||||
"playlistLimit" INTEGER NOT NULL DEFAULT 50,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Settings" ("channel", "createdAt", "finishedSetup", "guildId", "playlistLimit", "prefix", "updatedAt") SELECT "channel", "createdAt", coalesce("finishedSetup", false) AS "finishedSetup", "guildId", coalesce("playlistLimit", 50) AS "playlistLimit", "prefix", "updatedAt" FROM "Settings";
|
||||
DROP TABLE "Settings";
|
||||
ALTER TABLE "new_Settings" RENAME TO "Setting";
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Shortcut_guildId_shortcut_idx" ON "Shortcut"("guildId", "shortcut");
|
6
migrations/20220102176527_datetime_casting/migration.sql
Normal file
6
migrations/20220102176527_datetime_casting/migration.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
-- Manual migration to cast DateTime Column from "2021-12-29 20:16:54.221 +00:00" format to timestamp
|
||||
|
||||
UPDATE FileCache SET createdAt = CAST(strftime('%s', createdAt) AS INT) ,updatedAt = CAST(strftime('%s', updatedAt) AS INT), accessedAt = CAST(strftime('%s', accessedAt) AS INT);
|
||||
UPDATE KeyValueCache SET createdAt = CAST(strftime('%s', createdAt) AS INT) ,updatedAt = CAST(strftime('%s', updatedAt) AS INT), expiresAt = CAST(strftime('%s', expiresAt) AS INT);
|
||||
UPDATE Setting SET createdAt = CAST(strftime('%s', createdAt) AS INT) ,updatedAt = CAST(strftime('%s', updatedAt) AS INT);
|
||||
UPDATE Shortcut SET createdAt = CAST(strftime('%s', createdAt) AS INT) ,updatedAt = CAST(strftime('%s', updatedAt) AS INT);
|
3
migrations/migration_lock.toml
Normal file
3
migrations/migration_lock.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"ignore": ["**/*.test.ts", "**/*.spec.ts", ".git", "node_modules"],
|
||||
"watch": ["dist"],
|
||||
"exec": "npm start",
|
||||
"exec": "npm run env:set-database-url -- node --experimental-json-modules dist/src/scripts/start.js",
|
||||
"ext": "js"
|
||||
}
|
||||
|
|
11
package.json
11
package.json
|
@ -24,8 +24,12 @@
|
|||
"build": "tsc",
|
||||
"build:watch": "tsc --watch",
|
||||
"prepack": "npm run clean && npm run build",
|
||||
"start": "node --experimental-json-modules dist/src/index.js",
|
||||
"start": "npm run env:set-database-url -- node --experimental-json-modules dist/src/scripts/migrate-and-start.js",
|
||||
"dev": "concurrently nodemon 'tsc --watch'",
|
||||
"migrations:generate": "npm run prisma migrate dev",
|
||||
"migrations:run": "npm run prisma migrate deploy",
|
||||
"prisma": "npm run env:set-database-url prisma",
|
||||
"env:set-database-url": "node dist/src/scripts/run-with-database-url.js",
|
||||
"release": "release-it"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -47,6 +51,7 @@
|
|||
"eslint-config-xo-typescript": "^0.44.0",
|
||||
"husky": "^4.3.8",
|
||||
"nodemon": "^2.0.7",
|
||||
"prisma": "^3.7.0",
|
||||
"release-it": "^14.11.8",
|
||||
"ts-node": "^10.4.0",
|
||||
"type-fest": "^2.8.0",
|
||||
|
@ -77,12 +82,14 @@
|
|||
"dependencies": {
|
||||
"@discordjs/opus": "^0.7.0",
|
||||
"@discordjs/voice": "^0.7.5",
|
||||
"@prisma/client": "^3.7.0",
|
||||
"@types/libsodium-wrappers": "^0.7.9",
|
||||
"array-shuffle": "^3.0.0",
|
||||
"debug": "^4.3.3",
|
||||
"delay": "^5.0.0",
|
||||
"discord.js": "^13.3.0",
|
||||
"dotenv": "^8.5.1",
|
||||
"execa": "^6.0.0",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"fs-capacitor": "^7.0.1",
|
||||
"get-youtube-id": "^1.0.1",
|
||||
|
@ -99,8 +106,6 @@
|
|||
"p-limit": "^4.0.0",
|
||||
"p-queue": "^7.1.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sequelize": "^6.11.0",
|
||||
"sequelize-typescript": "^2.1.1",
|
||||
"spotify-uri": "^2.2.0",
|
||||
"spotify-web-api-node": "^5.0.2",
|
||||
"sqlite3": "^5.0.2",
|
||||
|
|
49
schema.prisma
Normal file
49
schema.prisma
Normal file
|
@ -0,0 +1,49 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model FileCache {
|
||||
hash String @id
|
||||
bytes Int
|
||||
accessedAt DateTime
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model KeyValueCache {
|
||||
key String @id
|
||||
value String
|
||||
expiresAt DateTime
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Setting {
|
||||
guildId String @id
|
||||
prefix String
|
||||
channel String?
|
||||
finishedSetup Boolean @default(false)
|
||||
playlistLimit Int @default(50)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Shortcut {
|
||||
id Int @id @default(autoincrement())
|
||||
guildId String
|
||||
authorId String
|
||||
shortcut String
|
||||
command String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
|
||||
@@index([shortcut], map: "shortcuts_shortcut")
|
||||
@@index([guildId], map: "shortcuts_guild_id")
|
||||
@@index([guildId, shortcut])
|
||||
}
|
52
src/bot.ts
52
src/bot.ts
|
@ -2,7 +2,7 @@ import {Client, Message, Collection} from 'discord.js';
|
|||
import {inject, injectable} from 'inversify';
|
||||
import ora from 'ora';
|
||||
import {TYPES} from './types.js';
|
||||
import {Settings, Shortcut} from './models/index.js';
|
||||
import {prisma} from './utils/db.js';
|
||||
import container from './inversify.config.js';
|
||||
import Command from './commands/index.js';
|
||||
import debug from './utils/debug.js';
|
||||
|
@ -21,7 +21,11 @@ export default class {
|
|||
private readonly token: string;
|
||||
private readonly commands!: Collection<string, Command>;
|
||||
|
||||
constructor(@inject(TYPES.Client) client: Client, @inject(TYPES.Services.NaturalLanguage) naturalLanguage: NaturalLanguage, @inject(TYPES.Config) config: Config) {
|
||||
constructor(
|
||||
@inject(TYPES.Client) client: Client,
|
||||
@inject(TYPES.Services.NaturalLanguage) naturalLanguage: NaturalLanguage,
|
||||
@inject(TYPES.Config) config: Config,
|
||||
) {
|
||||
this.client = client;
|
||||
this.naturalLanguage = naturalLanguage;
|
||||
this.token = config.DISCORD_TOKEN;
|
||||
|
@ -33,7 +37,9 @@ export default class {
|
|||
container.getAll<Command>(TYPES.Command).forEach(command => {
|
||||
const commandNames = [command.name, ...command.aliases];
|
||||
|
||||
commandNames.forEach(commandName => this.commands.set(commandName, command));
|
||||
commandNames.forEach(commandName =>
|
||||
this.commands.set(commandName, command),
|
||||
);
|
||||
});
|
||||
|
||||
this.client.on('messageCreate', async (msg: Message) => {
|
||||
|
@ -42,7 +48,11 @@ export default class {
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = await Settings.findByPk(msg.guild.id);
|
||||
const settings = await prisma.setting.findUnique({
|
||||
where: {
|
||||
guildId: msg.guild.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!settings) {
|
||||
// Got into a bad state, send owner welcome message
|
||||
|
@ -52,27 +62,42 @@ export default class {
|
|||
|
||||
const {prefix, channel} = settings;
|
||||
|
||||
if (!msg.content.startsWith(prefix) && !msg.author.bot && msg.channel.id === channel && await this.naturalLanguage.execute(msg)) {
|
||||
if (
|
||||
!msg.content.startsWith(prefix)
|
||||
&& !msg.author.bot
|
||||
&& msg.channel.id === channel
|
||||
&& (await this.naturalLanguage.execute(msg))
|
||||
) {
|
||||
// Natural language command handled message
|
||||
return;
|
||||
}
|
||||
|
||||
if (!msg.content.startsWith(prefix) || msg.author.bot || msg.channel.id !== channel) {
|
||||
if (
|
||||
!msg.content.startsWith(prefix)
|
||||
|| msg.author.bot
|
||||
|| msg.channel.id !== channel
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let args = msg.content.slice(prefix.length).split(/ +/);
|
||||
const command = args.shift()!.toLowerCase();
|
||||
|
||||
// Get possible shortcut
|
||||
const shortcut = await Shortcut.findOne({where: {guildId: msg.guild.id, shortcut: command}});
|
||||
const shortcut = await prisma.shortcut.findFirst({
|
||||
where: {
|
||||
guildId: msg.guild.id,
|
||||
shortcut: command,
|
||||
},
|
||||
});
|
||||
|
||||
let handler: Command;
|
||||
|
||||
if (this.commands.has(command)) {
|
||||
handler = this.commands.get(command)!;
|
||||
} else if (shortcut) {
|
||||
const possibleHandler = this.commands.get(shortcut.command.split(' ')[0]);
|
||||
const possibleHandler = this.commands.get(
|
||||
shortcut.command.split(' ')[0],
|
||||
);
|
||||
|
||||
if (possibleHandler) {
|
||||
handler = possibleHandler;
|
||||
|
@ -93,7 +118,9 @@ export default class {
|
|||
await handler.execute(msg, args);
|
||||
} catch (error: unknown) {
|
||||
debug(error);
|
||||
await msg.channel.send(errorMsg((error as Error).message.toLowerCase()));
|
||||
await msg.channel.send(
|
||||
errorMsg((error as Error).message.toLowerCase()),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -102,7 +129,10 @@ export default class {
|
|||
this.client.on('ready', () => {
|
||||
debug(generateDependencyReport());
|
||||
|
||||
spinner.succeed(`Ready! Invite the bot with https://discordapp.com/oauth2/authorize?client_id=${this.client.user?.id ?? ''}&scope=bot&permissions=36752448`);
|
||||
spinner.succeed(
|
||||
`Ready! Invite the bot with https://discordapp.com/oauth2/authorize?client_id=${this.client.user?.id ?? ''
|
||||
}&scope=bot&permissions=36752448`,
|
||||
);
|
||||
});
|
||||
|
||||
this.client.on('error', console.error);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {TextChannel, Message, GuildChannel, ThreadChannel} from 'discord.js';
|
||||
import {injectable} from 'inversify';
|
||||
import {Settings} from '../models/index.js';
|
||||
import errorMsg from '../utils/error-msg.js';
|
||||
import Command from '.';
|
||||
import {prisma} from '../utils/db.js';
|
||||
|
||||
@injectable()
|
||||
export default class implements Command {
|
||||
|
@ -17,9 +17,13 @@ export default class implements Command {
|
|||
public async execute(msg: Message, args: string []): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
// Show current settings
|
||||
const settings = await Settings.findByPk(msg.guild!.id);
|
||||
const settings = await prisma.setting.findUnique({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (settings) {
|
||||
if (settings?.channel) {
|
||||
let response = `prefix: \`${settings.prefix}\`\n`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
response += `channel: ${msg.guild!.channels.cache.get(settings.channel)!.toString()}\n`;
|
||||
|
@ -47,7 +51,14 @@ export default class implements Command {
|
|||
case 'prefix': {
|
||||
const newPrefix = args[1];
|
||||
|
||||
await Settings.update({prefix: newPrefix}, {where: {guildId: msg.guild!.id}});
|
||||
await prisma.setting.update({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
},
|
||||
data: {
|
||||
prefix: newPrefix,
|
||||
},
|
||||
});
|
||||
|
||||
await msg.channel.send(`👍 prefix updated to \`${newPrefix}\``);
|
||||
break;
|
||||
|
@ -63,7 +74,14 @@ export default class implements Command {
|
|||
}
|
||||
|
||||
if (channel && channel.type === 'GUILD_TEXT') {
|
||||
await Settings.update({channel: channel.id}, {where: {guildId: msg.guild!.id}});
|
||||
await prisma.setting.update({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
},
|
||||
data: {
|
||||
channel: channel.id,
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
(channel as TextChannel).send('hey apparently I\'m bound to this channel now'),
|
||||
|
@ -83,7 +101,15 @@ export default class implements Command {
|
|||
return;
|
||||
}
|
||||
|
||||
await Settings.update({playlistLimit}, {where: {guildId: msg.guild!.id}});
|
||||
await prisma.setting.update({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
},
|
||||
data: {
|
||||
playlistLimit,
|
||||
},
|
||||
});
|
||||
|
||||
await msg.channel.send(`👍 playlist-limit updated to ${playlistLimit}`);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ import {Message, Util} from 'discord.js';
|
|||
import {injectable} from 'inversify';
|
||||
import Command from '.';
|
||||
import {TYPES} from '../types.js';
|
||||
import {Settings} from '../models/index.js';
|
||||
import container from '../inversify.config.js';
|
||||
import {prisma} from '../utils/db.js';
|
||||
|
||||
@injectable()
|
||||
export default class implements Command {
|
||||
|
@ -21,7 +21,11 @@ export default class implements Command {
|
|||
this.commands = container.getAll<Command>(TYPES.Command);
|
||||
}
|
||||
|
||||
const settings = await Settings.findOne({where: {guildId: msg.guild!.id}});
|
||||
const settings = await prisma.setting.findUnique({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!settings) {
|
||||
return;
|
||||
|
|
|
@ -10,7 +10,7 @@ import LoadingMessage from '../utils/loading-message.js';
|
|||
import errorMsg from '../utils/error-msg.js';
|
||||
import Command from '.';
|
||||
import GetSongs from '../services/get-songs.js';
|
||||
import Settings from '../models/settings.js';
|
||||
import {prisma} from '../utils/db.js';
|
||||
|
||||
@injectable()
|
||||
export default class implements Command {
|
||||
|
@ -41,8 +41,15 @@ export default class implements Command {
|
|||
// eslint-disable-next-line complexity
|
||||
public async execute(msg: Message, args: string[]): Promise<void> {
|
||||
const [targetVoiceChannel] = getMemberVoiceChannel(msg.member!) ?? getMostPopularVoiceChannel(msg.guild!);
|
||||
const settings = await Settings.findByPk(msg.guild!.id);
|
||||
const {playlistLimit} = settings!;
|
||||
const setting = await prisma.setting.findUnique({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
}});
|
||||
if (!setting) {
|
||||
throw new Error(`Couldn't find settings for guild ${msg.guild!.id}`);
|
||||
}
|
||||
|
||||
const {playlistLimit} = setting;
|
||||
|
||||
const res = new LoadingMessage(msg.channel as TextChannel);
|
||||
await res.start();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {Message} from 'discord.js';
|
||||
import {injectable} from 'inversify';
|
||||
import {Shortcut, Settings} from '../models/index.js';
|
||||
import errorMsg from '../utils/error-msg.js';
|
||||
import Command from '.';
|
||||
import {prisma} from '../utils/db.js';
|
||||
|
||||
@injectable()
|
||||
export default class implements Command {
|
||||
|
@ -18,7 +18,11 @@ export default class implements Command {
|
|||
public async execute(msg: Message, args: string []): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
// Get shortcuts for guild
|
||||
const shortcuts = await Shortcut.findAll({where: {guildId: msg.guild!.id}});
|
||||
const shortcuts = await prisma.shortcut.findMany({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (shortcuts.length === 0) {
|
||||
await msg.channel.send('no shortcuts exist');
|
||||
|
@ -26,7 +30,11 @@ export default class implements Command {
|
|||
}
|
||||
|
||||
// Get prefix for guild
|
||||
const settings = await Settings.findOne({where: {guildId: msg.guild!.id}});
|
||||
const settings = await prisma.setting.findUnique({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!settings) {
|
||||
return;
|
||||
|
@ -48,7 +56,12 @@ export default class implements Command {
|
|||
|
||||
switch (action) {
|
||||
case 'set': {
|
||||
const shortcut = await Shortcut.findOne({where: {guildId: msg.guild!.id, shortcut: shortcutName}});
|
||||
const shortcut = await prisma.shortcut.findFirst({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
shortcut: shortcutName,
|
||||
},
|
||||
});
|
||||
|
||||
const command = args.slice(2).join(' ');
|
||||
|
||||
|
@ -60,10 +73,15 @@ export default class implements Command {
|
|||
return;
|
||||
}
|
||||
|
||||
await shortcut.update(newShortcut);
|
||||
await prisma.shortcut.update({
|
||||
where: {
|
||||
id: shortcut.id,
|
||||
},
|
||||
data: newShortcut,
|
||||
});
|
||||
await msg.channel.send('shortcut updated');
|
||||
} else {
|
||||
await Shortcut.create(newShortcut);
|
||||
await prisma.shortcut.create({data: newShortcut});
|
||||
await msg.channel.send('shortcut created');
|
||||
}
|
||||
|
||||
|
@ -72,7 +90,12 @@ export default class implements Command {
|
|||
|
||||
case 'delete': {
|
||||
// Check if shortcut exists
|
||||
const shortcut = await Shortcut.findOne({where: {guildId: msg.guild!.id, shortcut: shortcutName}});
|
||||
const shortcut = await prisma.shortcut.findFirst({
|
||||
where: {
|
||||
guildId: msg.guild!.id,
|
||||
shortcut: shortcutName,
|
||||
},
|
||||
});
|
||||
|
||||
if (!shortcut) {
|
||||
await msg.channel.send(errorMsg('shortcut doesn\'t exist'));
|
||||
|
@ -85,7 +108,11 @@ export default class implements Command {
|
|||
return;
|
||||
}
|
||||
|
||||
await shortcut.destroy();
|
||||
await prisma.shortcut.delete({
|
||||
where: {
|
||||
id: shortcut.id,
|
||||
},
|
||||
});
|
||||
|
||||
await msg.channel.send('shortcut deleted');
|
||||
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
import {Guild, TextChannel, Message, MessageReaction, User} from 'discord.js';
|
||||
import emoji from 'node-emoji';
|
||||
import pEvent from 'p-event';
|
||||
import {Settings} from '../models/index.js';
|
||||
import {chunk} from '../utils/arrays.js';
|
||||
import {prisma} from '../utils/db.js';
|
||||
|
||||
const DEFAULT_PREFIX = '!';
|
||||
|
||||
export default async (guild: Guild): Promise<void> => {
|
||||
await Settings.upsert({guildId: guild.id, prefix: DEFAULT_PREFIX});
|
||||
await prisma.setting.upsert({
|
||||
where: {
|
||||
guildId: guild.id,
|
||||
},
|
||||
create: {
|
||||
guildId: guild.id,
|
||||
prefix: DEFAULT_PREFIX,
|
||||
},
|
||||
update: {
|
||||
prefix: DEFAULT_PREFIX,
|
||||
},
|
||||
});
|
||||
|
||||
const owner = await guild.client.users.fetch(guild.ownerId);
|
||||
|
||||
|
@ -70,7 +81,15 @@ export default async (guild: Guild): Promise<void> => {
|
|||
const prefixCharacter = prefixResponses.first()!.content;
|
||||
|
||||
// Save settings
|
||||
await Settings.update({prefix: prefixCharacter, channel: chosenChannel.id}, {where: {guildId: guild.id}});
|
||||
await prisma.setting.update({
|
||||
where: {
|
||||
guildId: guild.id,
|
||||
},
|
||||
data: {
|
||||
channel: chosenChannel.id,
|
||||
prefix: prefixCharacter,
|
||||
},
|
||||
});
|
||||
|
||||
// Send welcome
|
||||
const boundChannel = guild.client.channels.cache.get(chosenChannel.id) as TextChannel;
|
||||
|
|
22
src/index.ts
22
src/index.ts
|
@ -1,28 +1,14 @@
|
|||
import makeDir from 'make-dir';
|
||||
import path from 'path';
|
||||
import {makeLines} from 'nodesplash';
|
||||
import container from './inversify.config.js';
|
||||
import {TYPES} from './types.js';
|
||||
import Bot from './bot.js';
|
||||
import {sequelize} from './utils/db.js';
|
||||
import Config from './services/config.js';
|
||||
import FileCacheProvider from './services/file-cache.js';
|
||||
import metadata from '../package.json';
|
||||
|
||||
const bot = container.get<Bot>(TYPES.Bot);
|
||||
|
||||
(async () => {
|
||||
// Banner
|
||||
console.log(makeLines({
|
||||
user: 'codetheweb',
|
||||
repository: 'muse',
|
||||
version: metadata.version,
|
||||
paypalUser: 'codetheweb',
|
||||
githubSponsor: 'codetheweb',
|
||||
madeByPrefix: 'Made with 🎶 by ',
|
||||
}).join('\n'));
|
||||
console.log('\n');
|
||||
|
||||
const startBot = async () => {
|
||||
// Create data directories if necessary
|
||||
const config = container.get<Config>(TYPES.Config);
|
||||
|
||||
|
@ -30,9 +16,9 @@ const bot = container.get<Bot>(TYPES.Bot);
|
|||
await makeDir(config.CACHE_DIR);
|
||||
await makeDir(path.join(config.CACHE_DIR, 'tmp'));
|
||||
|
||||
await sequelize.sync({alter: true});
|
||||
|
||||
await container.get<FileCacheProvider>(TYPES.FileCache).cleanup();
|
||||
|
||||
await bot.listen();
|
||||
})();
|
||||
};
|
||||
|
||||
export {startBot};
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import {Table, Column, PrimaryKey, Model} from 'sequelize-typescript';
|
||||
|
||||
@Table
|
||||
export default class FileCache extends Model {
|
||||
@PrimaryKey
|
||||
@Column
|
||||
hash!: string;
|
||||
|
||||
@Column
|
||||
bytes!: number;
|
||||
|
||||
@Column
|
||||
accessedAt!: Date;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import FileCache from './file-cache.js';
|
||||
import KeyValueCache from './key-value-cache.js';
|
||||
import Settings from './settings.js';
|
||||
import Shortcut from './shortcut.js';
|
||||
|
||||
export {
|
||||
FileCache,
|
||||
KeyValueCache,
|
||||
Settings,
|
||||
Shortcut,
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
import {Table, Column, PrimaryKey, Model} from 'sequelize-typescript';
|
||||
import sequelize from 'sequelize';
|
||||
|
||||
@Table
|
||||
export default class KeyValueCache extends Model {
|
||||
@PrimaryKey
|
||||
@Column
|
||||
key!: string;
|
||||
|
||||
@Column(sequelize.TEXT)
|
||||
value!: string;
|
||||
|
||||
@Column
|
||||
expiresAt!: Date;
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import {Table, Column, PrimaryKey, Model, Default} from 'sequelize-typescript';
|
||||
|
||||
@Table
|
||||
export default class Settings extends Model {
|
||||
@PrimaryKey
|
||||
@Column
|
||||
guildId!: string;
|
||||
|
||||
@Column
|
||||
prefix!: string;
|
||||
|
||||
@Column
|
||||
channel!: string;
|
||||
|
||||
@Default(false)
|
||||
@Column
|
||||
finishedSetup!: boolean;
|
||||
|
||||
@Default(50)
|
||||
@Column
|
||||
playlistLimit!: number;
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import {Table, Column, PrimaryKey, Model, AutoIncrement, Index} from 'sequelize-typescript';
|
||||
|
||||
@Table
|
||||
export default class Shortcut extends Model {
|
||||
@PrimaryKey
|
||||
@AutoIncrement
|
||||
@Column
|
||||
id!: number;
|
||||
|
||||
@Column
|
||||
@Index
|
||||
guildId!: string;
|
||||
|
||||
@Column
|
||||
authorId!: string;
|
||||
|
||||
@Column
|
||||
@Index
|
||||
shortcut!: string;
|
||||
|
||||
@Column
|
||||
command!: string;
|
||||
}
|
83
src/scripts/migrate-and-start.ts
Normal file
83
src/scripts/migrate-and-start.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
// This script applies Prisma migrations
|
||||
// and then starts Muse.
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
import {execa, ExecaError} from 'execa';
|
||||
import {promises as fs} from 'fs';
|
||||
import Prisma from '@prisma/client';
|
||||
import ora from 'ora';
|
||||
import {startBot} from '../index.js';
|
||||
import logBanner from '../utils/log-banner.js';
|
||||
import {createDatabasePath} from '../utils/create-database-url.js';
|
||||
import {DATA_DIR} from '../services/config.js';
|
||||
|
||||
const client = new Prisma.PrismaClient();
|
||||
|
||||
const migrateFromSequelizeToPrisma = async () => {
|
||||
await execa('prisma', ['migrate', 'resolve', '--applied', '20220101155430_migrate_from_sequelize'], {preferLocal: true});
|
||||
};
|
||||
|
||||
const doesUserHaveExistingDatabase = async () => {
|
||||
try {
|
||||
await fs.access(createDatabasePath(DATA_DIR));
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const hasDatabaseBeenMigratedToPrisma = async () => {
|
||||
try {
|
||||
await client.$queryRaw`SELECT COUNT(id) FROM _prisma_migrations`;
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Prisma.Prisma.PrismaClientKnownRequestError && error.code === 'P2010') {
|
||||
// Table doesn't exist
|
||||
return false;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
// Banner
|
||||
logBanner();
|
||||
|
||||
const spinner = ora('Applying database migrations...').start();
|
||||
|
||||
if (await doesUserHaveExistingDatabase()) {
|
||||
if (!(await hasDatabaseBeenMigratedToPrisma())) {
|
||||
try {
|
||||
await migrateFromSequelizeToPrisma();
|
||||
} catch (error) {
|
||||
if ((error as ExecaError).stderr) {
|
||||
spinner.fail('Failed to apply database migrations (going from Sequelize to Prisma):');
|
||||
console.error((error as ExecaError).stderr);
|
||||
process.exit(1);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await execa('prisma', ['migrate', 'deploy'], {preferLocal: true});
|
||||
} catch (error: unknown) {
|
||||
if ((error as ExecaError).stderr) {
|
||||
spinner.fail('Failed to apply database migrations:');
|
||||
console.error((error as ExecaError).stderr);
|
||||
process.exit(1);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
spinner.succeed('Database migrations applied.');
|
||||
|
||||
await startBot();
|
||||
})();
|
13
src/scripts/run-with-database-url.ts
Normal file
13
src/scripts/run-with-database-url.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import {DATA_DIR} from '../services/config.js';
|
||||
import createDatabaseUrl from '../utils/create-database-url.js';
|
||||
import {execa} from 'execa';
|
||||
|
||||
process.env.DATABASE_URL = createDatabaseUrl(DATA_DIR);
|
||||
|
||||
(async () => {
|
||||
await execa(process.argv[2], process.argv.slice(3), {
|
||||
preferLocal: true,
|
||||
stderr: process.stderr,
|
||||
stdout: process.stdout,
|
||||
});
|
||||
})();
|
9
src/scripts/start.ts
Normal file
9
src/scripts/start.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
// This script is mainly used during development.
|
||||
// Starts Muse without applying database migrations.
|
||||
import {startBot} from '../index.js';
|
||||
import logBanner from '../utils/log-banner.js';
|
||||
|
||||
(async () => {
|
||||
logBanner();
|
||||
await startBot();
|
||||
})();
|
|
@ -1,4 +1,5 @@
|
|||
import dotenv from 'dotenv';
|
||||
import 'reflect-metadata';
|
||||
import {injectable} from 'inversify';
|
||||
import path from 'path';
|
||||
import xbytes from 'xbytes';
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import {promises as fs, createWriteStream} from 'fs';
|
||||
import path from 'path';
|
||||
import {inject, injectable} from 'inversify';
|
||||
import sequelize from 'sequelize';
|
||||
import {FileCache} from '../models/index.js';
|
||||
import {TYPES} from '../types.js';
|
||||
import Config from './config.js';
|
||||
import PQueue from 'p-queue';
|
||||
import debug from '../utils/debug.js';
|
||||
import {prisma} from '../utils/db.js';
|
||||
import {FileCache} from '@prisma/client';
|
||||
|
||||
@injectable()
|
||||
export default class FileCacheProvider {
|
||||
|
@ -23,7 +23,11 @@ export default class FileCacheProvider {
|
|||
* @param hash lookup key
|
||||
*/
|
||||
async getPathFor(hash: string): Promise<string> {
|
||||
const model = await FileCache.findByPk(hash);
|
||||
const model = await prisma.fileCache.findUnique({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
|
||||
if (!model) {
|
||||
throw new Error('File is not cached');
|
||||
|
@ -34,12 +38,23 @@ export default class FileCacheProvider {
|
|||
try {
|
||||
await fs.access(resolvedPath);
|
||||
} catch (_: unknown) {
|
||||
await FileCache.destroy({where: {hash}});
|
||||
await prisma.fileCache.delete({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
|
||||
throw new Error('File is not cached');
|
||||
}
|
||||
|
||||
await model.update({accessedAt: new Date()});
|
||||
await prisma.fileCache.update({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
data: {
|
||||
accessedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return resolvedPath;
|
||||
}
|
||||
|
@ -64,7 +79,13 @@ export default class FileCacheProvider {
|
|||
try {
|
||||
await fs.rename(tmpPath, finalPath);
|
||||
|
||||
await FileCache.create({hash, bytes: stats.size, accessedAt: new Date()});
|
||||
await prisma.fileCache.create({
|
||||
data: {
|
||||
hash,
|
||||
accessedAt: new Date(),
|
||||
bytes: stats.size,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
debug('Errored when moving a finished cache file:', error);
|
||||
}
|
||||
|
@ -100,14 +121,19 @@ export default class FileCacheProvider {
|
|||
// Continue to evict until we're under the limit
|
||||
/* eslint-disable no-await-in-loop */
|
||||
while (totalSizeBytes > this.config.CACHE_LIMIT_IN_BYTES) {
|
||||
const oldest = await FileCache.findOne({
|
||||
order: [
|
||||
['accessedAt', 'ASC'],
|
||||
],
|
||||
const oldest = await prisma.fileCache.findFirst({
|
||||
orderBy: {
|
||||
accessedAt: 'asc',
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
if (oldest) {
|
||||
await oldest.destroy();
|
||||
await prisma.fileCache.delete({
|
||||
where: {
|
||||
hash: oldest.hash,
|
||||
},
|
||||
});
|
||||
await fs.unlink(path.join(this.config.CACHE_DIR, oldest.hash));
|
||||
debug(`${oldest.hash} has been evicted`);
|
||||
numOfEvictedFiles++;
|
||||
|
@ -128,7 +154,11 @@ export default class FileCacheProvider {
|
|||
// Check filesystem direction (do files exist on the disk but not in the database?)
|
||||
for await (const dirent of await fs.opendir(this.config.CACHE_DIR)) {
|
||||
if (dirent.isFile()) {
|
||||
const model = await FileCache.findByPk(dirent.name);
|
||||
const model = await prisma.fileCache.findUnique({
|
||||
where: {
|
||||
hash: dirent.name,
|
||||
},
|
||||
});
|
||||
|
||||
if (!model) {
|
||||
debug(`${dirent.name} was present on disk but was not in the database. Removing from disk.`);
|
||||
|
@ -145,7 +175,11 @@ export default class FileCacheProvider {
|
|||
await fs.access(filePath);
|
||||
} catch {
|
||||
debug(`${model.hash} was present in database but was not on disk. Removing from database.`);
|
||||
await model.destroy();
|
||||
await prisma.fileCache.delete({
|
||||
where: {
|
||||
hash: model.hash,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,11 +190,12 @@ export default class FileCacheProvider {
|
|||
* @returns the total size of the cache in bytes
|
||||
*/
|
||||
private async getDiskUsageInBytes() {
|
||||
const [{dataValues: {totalSizeBytes}}] = await FileCache.findAll({
|
||||
attributes: [
|
||||
[sequelize.fn('sum', sequelize.col('bytes')), 'totalSizeBytes'],
|
||||
],
|
||||
}) as unknown as [{dataValues: {totalSizeBytes: number}}];
|
||||
const data = await prisma.fileCache.aggregate({
|
||||
_sum: {
|
||||
bytes: true,
|
||||
},
|
||||
});
|
||||
const totalSizeBytes = data._sum.bytes ?? 0;
|
||||
|
||||
return totalSizeBytes;
|
||||
}
|
||||
|
@ -176,24 +211,26 @@ export default class FileCacheProvider {
|
|||
let models: FileCache[] = [];
|
||||
|
||||
const fetchNextBatch = async () => {
|
||||
let where = {};
|
||||
let where;
|
||||
|
||||
if (previousCreatedAt) {
|
||||
where = {
|
||||
createdAt: {
|
||||
[sequelize.Op.gt]: previousCreatedAt,
|
||||
gt: previousCreatedAt,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
models = await FileCache.findAll({
|
||||
models = await prisma.fileCache.findMany({
|
||||
where,
|
||||
limit,
|
||||
order: ['createdAt'],
|
||||
orderBy: {
|
||||
createdAt: 'asc',
|
||||
},
|
||||
take: limit,
|
||||
});
|
||||
|
||||
if (models.length > 0) {
|
||||
previousCreatedAt = models[models.length - 1].createdAt as Date;
|
||||
previousCreatedAt = models[models.length - 1].createdAt;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {injectable} from 'inversify';
|
||||
import {KeyValueCache} from '../models/index.js';
|
||||
import {prisma} from '../utils/db.js';
|
||||
import debug from '../utils/debug.js';
|
||||
|
||||
type Seconds = number;
|
||||
|
@ -29,7 +29,11 @@ export default class KeyValueCacheProvider {
|
|||
throw new Error(`Cache key ${key} is too short.`);
|
||||
}
|
||||
|
||||
const cachedResult = await KeyValueCache.findByPk(key);
|
||||
const cachedResult = await prisma.keyValueCache.findUnique({
|
||||
where: {
|
||||
key,
|
||||
},
|
||||
});
|
||||
|
||||
if (cachedResult) {
|
||||
if (new Date() < cachedResult.expiresAt) {
|
||||
|
@ -37,7 +41,11 @@ export default class KeyValueCacheProvider {
|
|||
return JSON.parse(cachedResult.value) as F;
|
||||
}
|
||||
|
||||
await cachedResult.destroy();
|
||||
await prisma.keyValueCache.delete({
|
||||
where: {
|
||||
key,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
debug(`Cache miss: ${key}`);
|
||||
|
@ -45,10 +53,21 @@ export default class KeyValueCacheProvider {
|
|||
const result = await func(...options as any[]);
|
||||
|
||||
// Save result
|
||||
await KeyValueCache.upsert({
|
||||
key,
|
||||
value: JSON.stringify(result),
|
||||
expiresAt: futureTimeToDate(expiresIn),
|
||||
const value = JSON.stringify(result);
|
||||
const expiresAt = futureTimeToDate(expiresIn);
|
||||
await prisma.keyValueCache.upsert({
|
||||
where: {
|
||||
key,
|
||||
},
|
||||
update: {
|
||||
value,
|
||||
expiresAt,
|
||||
},
|
||||
create: {
|
||||
key,
|
||||
value,
|
||||
expiresAt,
|
||||
},
|
||||
});
|
||||
|
||||
return result;
|
||||
|
|
5
src/utils/create-database-url.ts
Normal file
5
src/utils/create-database-url.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export const createDatabasePath = (directory: string) => `${directory}/db.sqlite`;
|
||||
|
||||
const createDatabaseUrl = (directory: string) => `file:${createDatabasePath(directory)}`;
|
||||
|
||||
export default createDatabaseUrl;
|
|
@ -1,12 +1,3 @@
|
|||
import {Sequelize} from 'sequelize-typescript';
|
||||
import path from 'path';
|
||||
import {DATA_DIR} from '../services/config.js';
|
||||
import {FileCache, KeyValueCache, Settings, Shortcut} from '../models/index.js';
|
||||
import Prisma from '@prisma/client';
|
||||
|
||||
export const sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
database: 'muse',
|
||||
storage: path.join(DATA_DIR, 'db.sqlite'),
|
||||
models: [FileCache, KeyValueCache, Settings, Shortcut],
|
||||
logging: false,
|
||||
});
|
||||
export const prisma = new Prisma.PrismaClient();
|
||||
|
|
16
src/utils/log-banner.ts
Normal file
16
src/utils/log-banner.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import {makeLines} from 'nodesplash';
|
||||
import metadata from '../../package.json';
|
||||
|
||||
const logBanner = () => {
|
||||
console.log(makeLines({
|
||||
user: 'codetheweb',
|
||||
repository: 'muse',
|
||||
version: metadata.version,
|
||||
paypalUser: 'codetheweb',
|
||||
githubSponsor: 'codetheweb',
|
||||
madeByPrefix: 'Made with 🎶 by ',
|
||||
}).join('\n'));
|
||||
console.log('\n');
|
||||
};
|
||||
|
||||
export default logBanner;
|
172
yarn.lock
172
yarn.lock
|
@ -258,6 +258,23 @@
|
|||
dependencies:
|
||||
"@octokit/openapi-types" "^11.2.0"
|
||||
|
||||
"@prisma/client@^3.7.0":
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.7.0.tgz#9cafc105f12635c95e9b7e7b18e8fbf52cf3f18a"
|
||||
integrity sha512-fUJMvBOX5C7JPc0e3CJD6Gbelbu4dMJB4ScYpiht8HMUnRShw20ULOipTopjNtl6ekHQJ4muI7pXlQxWS9nMbw==
|
||||
dependencies:
|
||||
"@prisma/engines-version" "3.7.0-31.8746e055198f517658c08a0c426c7eec87f5a85f"
|
||||
|
||||
"@prisma/engines-version@3.7.0-31.8746e055198f517658c08a0c426c7eec87f5a85f":
|
||||
version "3.7.0-31.8746e055198f517658c08a0c426c7eec87f5a85f"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.7.0-31.8746e055198f517658c08a0c426c7eec87f5a85f.tgz#055f36ac8b06c301332c14963cd0d6c795942c90"
|
||||
integrity sha512-+qx2b+HK7BKF4VCa0LZ/t1QCXsu6SmvhUQyJkOD2aPpmOzket4fEnSKQZSB0i5tl7rwCDsvAiSeK8o7rf+yvwg==
|
||||
|
||||
"@prisma/engines@3.7.0-31.8746e055198f517658c08a0c426c7eec87f5a85f":
|
||||
version "3.7.0-31.8746e055198f517658c08a0c426c7eec87f5a85f"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.7.0-31.8746e055198f517658c08a0c426c7eec87f5a85f.tgz#12f28d5b78519fbd84c89a5bdff457ff5095e7a2"
|
||||
integrity sha512-W549ub5NlgexNhR8EFstA/UwAWq3Zq0w9aNkraqsozVCt2CsX+lK4TK7IW5OZVSnxHwRjrgEAt3r9yPy8nZQRg==
|
||||
|
||||
"@release-it/keep-a-changelog@^2.3.0":
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@release-it/keep-a-changelog/-/keep-a-changelog-2.3.0.tgz#dfe888f97604a13c161e78f67509b296fb91ebc5"
|
||||
|
@ -613,11 +630,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
|||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
any-promise@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||
|
@ -1312,11 +1324,6 @@ dotenv@^8.5.1:
|
|||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
|
||||
integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
|
||||
|
||||
dottie@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/dottie/-/dottie-2.0.2.tgz#cc91c0726ce3a054ebf11c55fbc92a7f266dd154"
|
||||
integrity sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==
|
||||
|
||||
duplexer3@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||
|
@ -1546,6 +1553,21 @@ execa@^4.0.2:
|
|||
signal-exit "^3.0.2"
|
||||
strip-final-newline "^2.0.0"
|
||||
|
||||
execa@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-6.0.0.tgz#598b46f09ae44f5d8097a30cfb1681d0f0371503"
|
||||
integrity sha512-m4wU9j4Z9nXXoqT8RSfl28JSwmMNLFF69OON8H/lL3NeU0tNpGz313bcOfYoBBHokB0dC2tMl3VUcKgHELhL2Q==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
get-stream "^6.0.1"
|
||||
human-signals "^3.0.1"
|
||||
is-stream "^3.0.0"
|
||||
merge-stream "^2.0.0"
|
||||
npm-run-path "^5.0.1"
|
||||
onetime "^6.0.0"
|
||||
signal-exit "^3.0.5"
|
||||
strip-final-newline "^3.0.0"
|
||||
|
||||
extend@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||
|
@ -1869,7 +1891,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
|
|||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob@7.2.0, glob@^7.0.0, glob@^7.0.3, glob@^7.1.3:
|
||||
glob@^7.0.0, glob@^7.0.3, glob@^7.1.3:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
|
@ -2071,6 +2093,11 @@ human-signals@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
|
||||
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
|
||||
|
||||
human-signals@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5"
|
||||
integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==
|
||||
|
||||
husky@^4.3.8:
|
||||
version "4.3.8"
|
||||
resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.8.tgz#31144060be963fd6850e5cc8f019a1dfe194296d"
|
||||
|
@ -2153,11 +2180,6 @@ imurmurhash@^0.1.4:
|
|||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||
|
||||
inflection@1.13.1:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.13.1.tgz#c5cadd80888a90cf84c2e96e340d7edc85d5f0cb"
|
||||
integrity sha512-dldYtl2WlN0QDkIDtg8+xFwOS2Tbmp12t1cHa5/YClU6ZQjTFm7B66UcVbh9NQB+HvT5BAd2t5+yKsBkw5pcqA==
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
|
@ -2333,6 +2355,11 @@ is-stream@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
|
||||
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
||||
|
||||
is-stream@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
|
||||
integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
|
||||
|
||||
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
|
@ -2629,6 +2656,11 @@ mimic-fn@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
mimic-fn@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
|
||||
integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
|
||||
|
||||
mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
|
@ -2698,18 +2730,6 @@ mkdirp@^1.0.3:
|
|||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
moment-timezone@^0.5.31:
|
||||
version "0.5.34"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
|
||||
integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==
|
||||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
"moment@>= 2.9.0", moment@^2.26.0:
|
||||
version "2.29.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
@ -2904,6 +2924,13 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1:
|
|||
dependencies:
|
||||
path-key "^3.0.0"
|
||||
|
||||
npm-run-path@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.0.1.tgz#748dd68ed7de377bb1f7132c7dafe657be5ab400"
|
||||
integrity sha512-ybBJQUSyFwEEhqO2lXmyKOl9ucHtyZBWVM0h0FiMfT/+WKxCUZFa95qAR2X3w/w6oigN3B0b2UNHZbD+kdfD5w==
|
||||
dependencies:
|
||||
path-key "^4.0.0"
|
||||
|
||||
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
|
||||
|
@ -2958,6 +2985,13 @@ onetime@^5.1.0, onetime@^5.1.2:
|
|||
dependencies:
|
||||
mimic-fn "^2.1.0"
|
||||
|
||||
onetime@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
|
||||
integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
|
||||
dependencies:
|
||||
mimic-fn "^4.0.0"
|
||||
|
||||
open@7.4.2:
|
||||
version "7.4.2"
|
||||
resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
|
||||
|
@ -3181,6 +3215,11 @@ path-key@^3.0.0, path-key@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
path-key@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
|
||||
integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
|
||||
|
||||
path-parse@^1.0.6:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
|
@ -3196,11 +3235,6 @@ performance-now@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
pg-connection-string@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
|
||||
integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
||||
|
@ -3235,6 +3269,13 @@ prism-media@^1.3.2:
|
|||
resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.3.2.tgz#a1f04423ec15d22f3d62b1987b6a25dc49aad13b"
|
||||
integrity sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g==
|
||||
|
||||
prisma@^3.7.0:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.7.0.tgz#9c73eeb2f16f767fdf523d0f4cc4c749734d62e2"
|
||||
integrity sha512-pzgc95msPLcCHqOli7Hnabu/GRfSGSUWl5s2P6N13T/rgMB+NNeKbxCmzQiZT2yLOeLEPivV6YrW1oeQIwJxcg==
|
||||
dependencies:
|
||||
"@prisma/engines" "3.7.0-31.8746e055198f517658c08a0c426c7eec87f5a85f"
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
|
@ -3506,13 +3547,6 @@ restore-cursor@^4.0.0:
|
|||
onetime "^5.1.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
retry-as-promised@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-3.2.0.tgz#769f63d536bec4783549db0777cb56dadd9d8543"
|
||||
integrity sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==
|
||||
dependencies:
|
||||
any-promise "^1.3.0"
|
||||
|
||||
retry@0.13.1:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
|
||||
|
@ -3622,38 +3656,6 @@ semver@~5.3.0:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
|
||||
integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
|
||||
|
||||
sequelize-pool@^6.0.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-6.1.0.tgz#caaa0c1e324d3c2c3a399fed2c7998970925d668"
|
||||
integrity sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg==
|
||||
|
||||
sequelize-typescript@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/sequelize-typescript/-/sequelize-typescript-2.1.1.tgz#92445632062db868b760cd20215406403da737a2"
|
||||
integrity sha512-4am/5O6dlAvtR/akH2KizcECm4rRAjWr+oc5mo9vFVMez8hrbOhQlDNzk0H6VMOQASCN7yBF+qOnSEN60V6/vA==
|
||||
dependencies:
|
||||
glob "7.2.0"
|
||||
|
||||
sequelize@^6.11.0:
|
||||
version "6.11.0"
|
||||
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-6.11.0.tgz#11620b02ab2ca02af59ec652dd22bef7ccd348af"
|
||||
integrity sha512-+j3N5lr+FR1eicMRGR3bRsGOl9HMY0UGb2PyB2i1yZ64XBgsz3xejMH0UD45LcUitj40soDGIa9CyvZG0dfzKg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
dottie "^2.0.0"
|
||||
inflection "1.13.1"
|
||||
lodash "^4.17.20"
|
||||
moment "^2.26.0"
|
||||
moment-timezone "^0.5.31"
|
||||
pg-connection-string "^2.5.0"
|
||||
retry-as-promised "^3.2.0"
|
||||
semver "^7.3.2"
|
||||
sequelize-pool "^6.0.0"
|
||||
toposort-class "^1.0.1"
|
||||
uuid "^8.1.0"
|
||||
validator "^13.7.0"
|
||||
wkx "^0.5.0"
|
||||
|
||||
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
|
@ -3694,7 +3696,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f"
|
||||
integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==
|
||||
|
||||
signal-exit@^3.0.3:
|
||||
signal-exit@^3.0.3, signal-exit@^3.0.5:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af"
|
||||
integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==
|
||||
|
@ -3843,6 +3845,11 @@ strip-final-newline@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||
|
||||
strip-final-newline@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
|
||||
integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
|
||||
|
||||
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
|
@ -3970,11 +3977,6 @@ to-regex-range@^5.0.1:
|
|||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
toposort-class@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988"
|
||||
integrity sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=
|
||||
|
||||
touch@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
|
||||
|
@ -4167,7 +4169,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
uuid@8.3.2, uuid@^8.1.0:
|
||||
uuid@8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
@ -4187,11 +4189,6 @@ vali-date@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6"
|
||||
integrity sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=
|
||||
|
||||
validator@^13.7.0:
|
||||
version "13.7.0"
|
||||
resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857"
|
||||
integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==
|
||||
|
||||
verror@1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
|
||||
|
@ -4261,13 +4258,6 @@ windows-release@^4.0.0:
|
|||
dependencies:
|
||||
execa "^4.0.2"
|
||||
|
||||
wkx@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c"
|
||||
integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
word-wrap@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
|
|
Loading…
Reference in a new issue