├── .env.example
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .husky
└── pre-commit
├── .prettierrc.js
├── .vscode
└── settings.json
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── README.md
├── docker-compose.yml
├── heroku.yml
├── meta
└── logo.png
├── package.json
├── src
├── bot.ts
├── db.ts
├── entities
│ ├── HelpThread.ts
│ ├── Rep.ts
│ └── Snippet.ts
├── env.ts
├── index.ts
├── log.ts
├── modules
│ ├── autorole.ts
│ ├── etc.ts
│ ├── handbook.ts
│ ├── help.ts
│ ├── helpForum.ts
│ ├── mod.ts
│ ├── playground.ts
│ ├── rep.ts
│ ├── snippet.ts
│ └── twoslash.ts
└── util
│ ├── codeBlocks.ts
│ ├── customCommand.ts
│ ├── getReferencedMessage.ts
│ ├── getTypeScriptModule.ts
│ ├── limitedSizeMap.ts
│ ├── send.ts
│ └── sendPaginatedMessage.ts
├── tsconfig.json
└── yarn.lock
/.env.example:
--------------------------------------------------------------------------------
1 | TOKEN=YOUR_DISCORD_BOT_TOKEN
2 | BOT_ADMINS=123,456
3 |
4 | # can have multiple seperated by ,
5 | AUTOROLE=MSG_ID:ROLE_ID:EMOJI:AUTOREMOVE
6 | # Another example: AUTOROLE=738932146978160661:728202487672078368:❌:false,738932146978160661:738936540465725520:✅:true
7 |
8 | DATABASE_URL="postgres://tscbot:tscbot@localhost:5432/tscbot"
9 |
10 | # Role given to trusted members, not full moderators, but can use some commands which
11 | # are not given to all server members.
12 | TRUSTED_ROLE_ID=
13 |
14 | RULES_CHANNEL=
15 |
16 | ROLES_CHANNEL=
17 |
18 | HOW_TO_GET_HELP_CHANNEL=
19 | HOW_TO_GIVE_HELP_CHANNEL=
20 |
21 | HELP_FORUM_CHANNEL=
22 | HELP_REQUESTS_CHANNEL=
23 | HELP_FORUM_OPEN_TAG=Open
24 | HELP_FORUM_RESOLVED_TAG=Resolved
25 |
26 | # Time in milliseconds before !helper can be run
27 | TIME_BEFORE_HELPER_PING=
28 |
29 | SUGGESTIONS_CHANNEL=
30 |
31 | LOG_CHANNEL=
32 |
33 | # PREFIXES=!,t!
34 |
35 | # REP_EMOJI=⭐
36 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Main
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | env:
8 | IMAGE_NAME: bot
9 |
10 | jobs:
11 | main:
12 | name: main
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v1
16 | - name: install node v16
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: 16
20 |
21 | - name: yarn install
22 | run: yarn install
23 | - name: build
24 | run: yarn run build
25 | - name: lint
26 | run: yarn run lint
27 |
28 | - name: Set up Docker Buildx
29 | if: contains(github.ref, 'master')
30 | uses: docker/setup-buildx-action@v1
31 |
32 | - name: Login to Github Container Registry
33 | if: contains(github.ref, 'master')
34 | uses: docker/login-action@v1
35 | with:
36 | registry: ghcr.io
37 | username: ${{ github.repository_owner }}
38 | password: ${{ secrets.GHCR_TOKEN }}
39 |
40 | - name: Build and push
41 | if: contains(github.ref, 'master')
42 | uses: docker/build-push-action@v2
43 | with:
44 | context: .
45 | file: ./Dockerfile
46 | push: true
47 | cache-from: type=registry,ref=ghcr.io/typescript-community/bot:latest
48 | cache-to: type=inline
49 | tags: |
50 | ghcr.io/typescript-community/bot:latest
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
63 | dist/
64 |
65 | .idea
66 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx pretty-quick --staged
5 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | /**
3 | * Include parentheses around a sole arrow function parameter.
4 | *
5 | * avoid - Omit parens when possible. Example: `x => x`
6 | * always - Always include parens. Example: `(x) => x`
7 | */
8 | arrowParens: 'avoid',
9 |
10 | /**
11 | * Print spaces between brackets.
12 | *
13 | * Type: boolean
14 | */
15 | bracketSpacing: true,
16 |
17 | /**
18 | * Print (to stderr) where a cursor at the given position would move to after formatting.
19 | * This option cannot be used with --range-start and --range-end.
20 | *
21 | * Type: integer
22 | */
23 | cursorOffset: -1,
24 |
25 | /**
26 | * Which end of line characters to apply.
27 | *
28 | * auto - Maintain existing (mixed values within one file are normalised by looking at what's used after the first line)
29 | * lf - Line Feed only (\n), common on Linux and macOS as well as inside git repos
30 | * crlf - Carriage Return + Line Feed characters (\r\n), common on Windows
31 | * cr - Carriage Return character only (\r), used very rarely
32 | */
33 | endOfLine: 'auto',
34 |
35 | /**
36 | * How to wrap prose.
37 | *
38 | * always - Wrap prose if it exceeds the print width.
39 | * never - Do not wrap prose.
40 | * preserve - Wrap prose as-is.
41 | */
42 | proseWrap: 'preserve',
43 |
44 | /**
45 | * Change when properties in objects are quoted.
46 | *
47 | * as-needed - Only add quotes around object properties where required.
48 | * consistent - If at least one property in an object requires quotes, quote all properties.
49 | * preserve - Respect the input use of quotes in object properties.
50 | */
51 | quoteProps: 'as-needed',
52 |
53 | /**
54 | * Print semicolons.
55 | *
56 | * Type: boolean
57 | */
58 | semi: true,
59 |
60 | /**
61 | * Use single quotes instead of double quotes.
62 | *
63 | * Type: boolean
64 | */
65 | singleQuote: true,
66 |
67 | /**
68 | * Number of spaces per indentation level.
69 | *
70 | * Type: integer
71 | */
72 | tabWidth: 4,
73 |
74 | /**
75 | * Print trailing commas wherever possible when multi-line.
76 | *
77 | * none - No trailing commas.
78 | * es5 - Trailing commas where valid in ES5 (objects, arrays, etc.)
79 | * all - Trailing commas wherever possible (including function arguments).
80 | */
81 | trailingComma: 'all',
82 |
83 | /**
84 | * Indent with tabs instead of spaces.
85 | *
86 | * Type: boolean
87 | */
88 | useTabs: true,
89 | };
90 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib",
3 | "cSpell.words": [
4 | "algoliasearch",
5 | "autorole",
6 | "Cooldown",
7 | "leaderboard",
8 | "twoslash",
9 | "twoslasher"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 2022-12-16
2 |
3 | - Remove `!close`, update `!helper` to include thread tags.
4 |
5 | # 2022-11-19
6 |
7 | - Removed `HELP_CATEGORY`, `GENERAL_HELP_CHANNEL` environment variables.
8 | - Added `HELP_FORUM_CHANNEL`, `HELP_REQUESTS_CHANNEL` environment variables.
9 | - Updated how to get help and how to give help channel content to not use embeds.
10 | - Updated to Discord.js 14, removed Cookiecord to prevent future delays in updating versions.
11 | - The bot will now react on the configured autorole messages to indicate available roles.
12 | - Unhandled rejections will now only be ignored if `NODE_ENV` is set to `production`.
13 | - Removed admin `checkThreads` command as using it would result in the bot checking for closed threads twice as often until restarted.
14 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:16.18.1-alpine
2 | WORKDIR /usr/src/app
3 |
4 | COPY yarn.lock ./
5 | COPY package.json ./
6 |
7 | RUN yarn
8 |
9 | COPY . .
10 |
11 | RUN yarn build
12 |
13 | CMD [ "node", "dist/index.js" ]
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 TypeScript Community
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # TypeScript Community Bot
4 |
5 | _A utility bot built for the [TypeScript Community Discord Server](https://discord.gg/typescript)._
6 |
7 |
8 |
9 | # Contributing
10 |
11 | See the [documentation](https://cookiecord.js.org/) for the framework we use.
12 |
13 | We also have a docker-compose.yml for development, along with a .env.example.
14 |
15 | **A quick note about the help channel system:** Please only use it if you have a large server (10k+ members) as it will likely inconvenience your members rather than benefit them. We used a static channel system (#help-1 and #help-2) up until around 9,000 members, when we started to see issues arising (many questions being asked on top of each other without answers).
16 |
17 | ## Thanks!
18 |
19 | - [ckie](https://github.com/ckiee) for writing the base for the bot!
20 | - [Python Discord](https://github.com/python-discord) for heavily influencing our help channel system.
21 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | # Development compose file
2 | version: '3'
3 |
4 | services:
5 | bot:
6 | image: node:16
7 | command: yarn start
8 | depends_on:
9 | - postgres
10 | volumes:
11 | - .:/srv/bot:rw
12 | working_dir: /srv/bot
13 | environment:
14 | - NODE_ENV=development
15 | - DATABASE_URL=postgres://tscbot:tscbot@postgres:5432/tscbot
16 | env_file:
17 | - .env
18 |
19 | postgres:
20 | image: postgres
21 | restart: always
22 | environment:
23 | POSTGRES_USER: 'tscbot'
24 | POSTGRES_DB: 'tscbot'
25 | POSTGRES_PASSWORD: tscbot
26 | PGPORT: 5432
27 | volumes:
28 | - 'postgres_data:/postgres/data'
29 | ports:
30 | - 5432:5432
31 |
32 | volumes:
33 | postgres_data:
34 |
--------------------------------------------------------------------------------
/heroku.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | worker: Dockerfile
4 |
--------------------------------------------------------------------------------
/meta/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/typescript-community/community-bot/02b8dc2d422298f13800763b55c28a7091d93cd3/meta/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tsc-bot",
3 | "version": "0.0.1",
4 | "description": "Typescript Community Bot",
5 | "main": "dist/index.js",
6 | "dependencies": {
7 | "@typescript/twoslash": "^3.2.1",
8 | "algoliasearch": "^4.14.2",
9 | "discord.js": "^14.6.0",
10 | "dotenv-safe": "^8.2.0",
11 | "html-entities": "^2.3.3",
12 | "lz-string": "^1.4.4",
13 | "npm-registry-fetch": "^14.0.2",
14 | "parse-duration": "^1.0.2",
15 | "pg": "^8.8.0",
16 | "prettier": "^2.7.1",
17 | "pretty-ms": "^8.0.0",
18 | "tar": "^6.1.12",
19 | "typeorm": "^0.3.10",
20 | "undici": "^5.12.0"
21 | },
22 | "devDependencies": {
23 | "@types/dotenv-safe": "8.1.2",
24 | "@types/lz-string": "1.3.34",
25 | "@types/node": "16.18.3",
26 | "@types/npm-registry-fetch": "8.0.4",
27 | "@types/prettier": "2.7.1",
28 | "@types/tar": "6.1.3",
29 | "@types/ws": "8.5.3",
30 | "husky": "^8.0.2",
31 | "pretty-quick": "^3.1.3",
32 | "ts-node-dev": "^2.0.0",
33 | "typescript": "^4.9.3"
34 | },
35 | "scripts": {
36 | "start": "ts-node-dev --respawn src",
37 | "build": "tsc",
38 | "lint": "prettier --check \"src/**/*.ts\"",
39 | "lint:fix": "prettier \"src/**/*.ts\" --write ",
40 | "prepare": "husky install"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/bot.ts:
--------------------------------------------------------------------------------
1 | import { Message, Client, User, GuildMember } from 'discord.js';
2 | import { botAdmins, prefixes, trustedRoleId } from './env';
3 |
4 | export interface CommandRegistration {
5 | aliases: string[];
6 | description?: string;
7 | listener: (msg: Message, content: string) => Promise;
8 | }
9 |
10 | interface Command {
11 | admin: boolean;
12 | aliases: string[];
13 | description?: string;
14 | listener: (msg: Message, content: string) => Promise;
15 | }
16 |
17 | export class Bot {
18 | commands = new Map();
19 |
20 | constructor(public client: Client) {
21 | client.on('messageCreate', msg => {
22 | const triggerWithPrefix = msg.content.split(/\s/)[0];
23 | const matchingPrefix = prefixes.find(p =>
24 | triggerWithPrefix.startsWith(p),
25 | );
26 | if (matchingPrefix) {
27 | const content = msg.content
28 | .substring(triggerWithPrefix.length + 1)
29 | .trim();
30 |
31 | const command = this.getByTrigger(
32 | triggerWithPrefix.substring(matchingPrefix.length),
33 | );
34 |
35 | if (!command || (command.admin && !this.isAdmin(msg.author))) {
36 | return;
37 | }
38 | command.listener(msg, content).catch(err => {
39 | this.client.emit('error', err);
40 | });
41 | }
42 | });
43 | }
44 |
45 | registerCommand(registration: CommandRegistration) {
46 | const command: Command = {
47 | ...registration,
48 | admin: false,
49 | };
50 | for (const a of command.aliases) {
51 | this.commands.set(a, command);
52 | }
53 | }
54 |
55 | registerAdminCommand(registration: CommandRegistration) {
56 | const command: Command = {
57 | ...registration,
58 | admin: true,
59 | };
60 | for (const a of command.aliases) {
61 | this.commands.set(a, command);
62 | }
63 | }
64 |
65 | getByTrigger(trigger: string): Command | undefined {
66 | return this.commands.get(trigger);
67 | }
68 |
69 | isMod(member: GuildMember | null) {
70 | return member?.permissions.has('ManageMessages') ?? false;
71 | }
72 |
73 | isAdmin(user: User) {
74 | return botAdmins.includes(user.id);
75 | }
76 |
77 | isTrusted(msg: Message) {
78 | if (!msg.guild || !msg.member || !msg.channel.isTextBased()) {
79 | return false;
80 | }
81 |
82 | if (
83 | !msg.member.roles.cache.has(trustedRoleId) &&
84 | !msg.member.permissions.has('ManageMessages')
85 | ) {
86 | return false;
87 | }
88 |
89 | return true;
90 | }
91 |
92 | async getTargetUser(msg: Message): Promise {
93 | const query = msg.content.split(/\s/)[1];
94 |
95 | const mentioned = msg.mentions.members?.first()?.user;
96 | if (mentioned) return mentioned;
97 |
98 | if (!query) return;
99 |
100 | // Search by ID
101 | const queriedUser = await this.client.users
102 | .fetch(query)
103 | .catch(() => undefined);
104 | if (queriedUser) return queriedUser;
105 |
106 | // Search by name, likely a better way to do this...
107 | for (const user of this.client.users.cache.values()) {
108 | if (user.tag === query || user.username === query) {
109 | return user;
110 | }
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/db.ts:
--------------------------------------------------------------------------------
1 | import { DataSource } from 'typeorm';
2 | import { dbUrl } from './env';
3 | import { Rep } from './entities/Rep';
4 | import { HelpThread } from './entities/HelpThread';
5 | import { Snippet } from './entities/Snippet';
6 |
7 | let db: DataSource | undefined;
8 | export async function getDB() {
9 | if (db) return db;
10 |
11 | // Require ssl in production
12 | const extraOpts =
13 | process.env.NODE_ENV === 'production'
14 | ? {
15 | ssl: true,
16 | extra: {
17 | ssl: {
18 | rejectUnauthorized: false,
19 | },
20 | },
21 | }
22 | : {};
23 |
24 | db = new DataSource({
25 | type: 'postgres',
26 | url: dbUrl,
27 | synchronize: true,
28 | logging: false,
29 | entities: [Rep, HelpThread, Snippet],
30 | ...extraOpts,
31 | });
32 | await db.initialize();
33 | console.log('Connected to DB');
34 | return db;
35 | }
36 |
--------------------------------------------------------------------------------
/src/entities/HelpThread.ts:
--------------------------------------------------------------------------------
1 | import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm';
2 |
3 | @Entity()
4 | export class HelpThread extends BaseEntity {
5 | @PrimaryColumn()
6 | threadId!: string;
7 |
8 | @Column()
9 | ownerId!: string;
10 |
11 | // When @helper was last pinged
12 | @Column({ nullable: true })
13 | helperTimestamp?: string;
14 |
15 | /**
16 | * When the title was last set, exists only for backwards compat
17 | * @deprecated
18 | */
19 | @Column({ nullable: true })
20 | titleSetTimestamp?: string;
21 |
22 | /**
23 | * The id of the original message, exists only for backwards compat
24 | * @deprecated
25 | */
26 | @Column({ nullable: true })
27 | origMessageId?: string;
28 | }
29 |
--------------------------------------------------------------------------------
/src/entities/Rep.ts:
--------------------------------------------------------------------------------
1 | import { Entity, BaseEntity, PrimaryColumn, Column } from 'typeorm';
2 |
3 | @Entity()
4 | export class Rep extends BaseEntity {
5 | @PrimaryColumn()
6 | messageId!: string;
7 |
8 | @Column()
9 | date!: string;
10 |
11 | @Column()
12 | channel!: string;
13 |
14 | @Column()
15 | amount!: number;
16 |
17 | @Column()
18 | recipient!: string;
19 |
20 | @Column()
21 | initialGiver!: string;
22 | }
23 |
--------------------------------------------------------------------------------
/src/entities/Snippet.ts:
--------------------------------------------------------------------------------
1 | import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm';
2 |
3 | @Entity()
4 | export class Snippet extends BaseEntity {
5 | @PrimaryColumn()
6 | id!: string;
7 |
8 | @Column()
9 | owner!: string;
10 |
11 | @Column()
12 | uses!: number;
13 |
14 | @Column({ nullable: true })
15 | content?: string;
16 |
17 | @Column({ nullable: true })
18 | title?: string;
19 |
20 | @Column({ nullable: true })
21 | description?: string;
22 |
23 | @Column({ nullable: true })
24 | color?: number;
25 |
26 | @Column({ nullable: true })
27 | image?: string;
28 |
29 | @Column({ nullable: true })
30 | url?: string;
31 | }
32 |
--------------------------------------------------------------------------------
/src/env.ts:
--------------------------------------------------------------------------------
1 | import dotenv from 'dotenv-safe';
2 | dotenv.config();
3 |
4 | export const token = process.env.TOKEN!;
5 | export const botAdmins = process.env.BOT_ADMINS!.split(',');
6 |
7 | export const autorole = process.env.AUTOROLE!.split(',').map(x => {
8 | const [msgID, roleID, emoji, autoRemove] = x.split(':');
9 | return {
10 | msgID,
11 | roleID,
12 | emoji,
13 | autoRemove: autoRemove == 'true',
14 | };
15 | });
16 |
17 | export const dbUrl = process.env.DATABASE_URL!;
18 |
19 | export const howToGetHelpChannel = process.env.HOW_TO_GET_HELP_CHANNEL!;
20 | export const howToGiveHelpChannel = process.env.HOW_TO_GIVE_HELP_CHANNEL!;
21 | export const helpForumChannel = process.env.HELP_FORUM_CHANNEL!;
22 | export const helpRequestsChannel = process.env.HELP_REQUESTS_CHANNEL!;
23 |
24 | export const helpForumOpenTagName = process.env.HELP_FORUM_OPEN_TAG!;
25 | export const helpForumResolvedTagName = process.env.HELP_FORUM_RESOLVED_TAG!;
26 |
27 | export const trustedRoleId = process.env.TRUSTED_ROLE_ID!;
28 |
29 | export const rulesChannelId = process.env.RULES_CHANNEL!;
30 |
31 | export const rolesChannelId = process.env.ROLES_CHANNEL!;
32 |
33 | export const TS_BLUE = '#007ACC';
34 | export const GREEN = '#3ba55d';
35 | // Picked from Discord's blockquote line
36 | export const BLOCKQUOTE_GREY = '#4f545c';
37 |
38 | export const timeBeforeHelperPing = parseInt(
39 | process.env.TIME_BEFORE_HELPER_PING!,
40 | );
41 |
42 | export const suggestionsChannelId = process.env.SUGGESTIONS_CHANNEL!;
43 |
44 | export const logChannelId = process.env.LOG_CHANNEL!;
45 |
46 | export const prefixes = (process.env.PREFIXES ?? '!').split(',');
47 |
48 | export const repEmoji = process.env.REP_EMOJI ?? '⭐';
49 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Client, GatewayIntentBits, Partials } from 'discord.js';
2 | import { Bot } from './bot';
3 | import { getDB } from './db';
4 | import { token } from './env';
5 | import { hookLog } from './log';
6 |
7 | import { autoroleModule } from './modules/autorole';
8 | import { etcModule } from './modules/etc';
9 | import { handbookModule } from './modules/handbook';
10 | import { helpModule } from './modules/help';
11 | import { modModule } from './modules/mod';
12 | import { playgroundModule } from './modules/playground';
13 | import { repModule } from './modules/rep';
14 | import { twoslashModule } from './modules/twoslash';
15 | import { snippetModule } from './modules/snippet';
16 | import { helpForumModule } from './modules/helpForum';
17 |
18 | const client = new Client({
19 | partials: [
20 | Partials.Reaction,
21 | Partials.Message,
22 | Partials.User,
23 | Partials.Channel,
24 | ],
25 | allowedMentions: {
26 | parse: ['users', 'roles'],
27 | },
28 | intents: [
29 | GatewayIntentBits.Guilds,
30 | GatewayIntentBits.GuildMessages,
31 | GatewayIntentBits.GuildMembers,
32 | GatewayIntentBits.GuildMessageReactions,
33 | GatewayIntentBits.DirectMessages,
34 | GatewayIntentBits.MessageContent,
35 | ],
36 | }).setMaxListeners(Infinity);
37 |
38 | getDB().then(() => client.login(token));
39 |
40 | client.on('ready', async () => {
41 | const bot = new Bot(client);
42 | console.log(`Logged in as ${client.user?.tag}`);
43 | await hookLog(client);
44 |
45 | for (const mod of [
46 | autoroleModule,
47 | etcModule,
48 | helpForumModule,
49 | playgroundModule,
50 | repModule,
51 | twoslashModule,
52 | helpModule,
53 | snippetModule,
54 | handbookModule,
55 | modModule,
56 | ]) {
57 | await mod(bot);
58 | }
59 | });
60 |
61 | client.on('error', error => {
62 | console.error(error);
63 | });
64 |
65 | if (process.env.NODE_ENV === 'production') {
66 | process.on('unhandledRejection', e => {
67 | console.error('Unhandled rejection', e);
68 | });
69 | }
70 |
--------------------------------------------------------------------------------
/src/log.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BaseGuildTextChannel,
3 | Client,
4 | GuildMember,
5 | TextChannel,
6 | ThreadChannel,
7 | User,
8 | } from 'discord.js';
9 | import { inspect } from 'util';
10 | import { logChannelId } from './env';
11 |
12 | const logDebounceTime = 5000;
13 | const logMaxLength = 2000;
14 |
15 | export async function hookLog(client: Client) {
16 | const guild = client.guilds.cache.get(
17 | (await client.guilds.fetch()).first()!.id,
18 | )!;
19 | const channel = (await guild.channels.fetch(logChannelId)) as TextChannel;
20 | let curLogText = '';
21 | let timeout: NodeJS.Timeout | null = null;
22 | const origLog = console.log;
23 | console.log = (...args) => {
24 | origLog(...args);
25 | postLog(args);
26 | };
27 | const origError = console.error;
28 | console.error = (...args) => {
29 | origError(...args);
30 | postLog(['[ERROR]', ...args]);
31 | };
32 | console.log('Writing logs to', channel);
33 | function argToString(arg: unknown) {
34 | if (typeof arg === 'string') return arg;
35 | return inspect(arg);
36 | }
37 | function postLog(args: unknown[]) {
38 | curLogText += `[${new Date().toISOString()}] ${args
39 | .map(argToString)
40 | .join(' ')}\n`;
41 | if (timeout) clearTimeout(timeout);
42 | while (curLogText.length > logMaxLength) {
43 | const initial =
44 | curLogText.match(/^[^]{0,2000}\n/g)?.[0] ??
45 | curLogText.slice(0, 2000);
46 | curLogText = curLogText.slice(initial.length);
47 | postCodeblock(initial);
48 | }
49 | if (curLogText.trim().length)
50 | timeout = setTimeout(() => {
51 | postCodeblock(curLogText);
52 | curLogText = '';
53 | }, logDebounceTime);
54 | }
55 | async function postCodeblock(content: string) {
56 | channel.send(`\`\`\`accesslog\n${content}\n\`\`\``);
57 | }
58 | }
59 |
60 | function defineCustomUtilInspect(
61 | Cls: { prototype: T },
62 | cb: (value: T) => string,
63 | ) {
64 | // @ts-ignore
65 | Cls.prototype[inspect.custom] = function () {
66 | return cb(this);
67 | };
68 | }
69 |
70 | const inspectUser = (user: User) =>
71 | `@${user.username}#${user.discriminator}/${user.id}`;
72 | defineCustomUtilInspect(User, inspectUser);
73 | defineCustomUtilInspect(GuildMember, member => inspectUser(member.user));
74 |
75 | const channels: Array<{ prototype: { name: string; id: string } }> = [
76 | BaseGuildTextChannel,
77 | ThreadChannel,
78 | ];
79 |
80 | for (const ctor of channels) {
81 | defineCustomUtilInspect(ctor, channel => `#${channel.name}/${channel.id}`);
82 | }
83 |
--------------------------------------------------------------------------------
/src/modules/autorole.ts:
--------------------------------------------------------------------------------
1 | import { Bot } from '../bot';
2 | import { autorole, rolesChannelId } from '../env';
3 |
4 | export async function autoroleModule({ client }: Bot) {
5 | const channel = await client.channels.fetch(rolesChannelId);
6 | if (!channel?.isTextBased()) {
7 | console.error(
8 | `Roles channel (${rolesChannelId}) does not exist or is not text based.`,
9 | );
10 | return;
11 | }
12 |
13 | for (const ar of autorole) {
14 | const msg = await channel.messages.fetch(ar.msgID);
15 | if (!msg) {
16 | console.error(`Role message does not exist for ${ar.msgID}`);
17 | }
18 | await msg?.react(ar.emoji);
19 | }
20 |
21 | client.on('messageReactionAdd', async (reaction, user) => {
22 | if (user.id == client.user.id) return;
23 | if (reaction.partial) await reaction.fetch();
24 | for (const ar of autorole) {
25 | const msg = reaction.message;
26 | if (
27 | ar.emoji !== reaction.emoji.toString() ||
28 | ar.msgID !== msg.id ||
29 | !msg.guild
30 | )
31 | continue;
32 | if (ar.autoRemove) await reaction.users.remove(user.id);
33 | const member = await msg.guild.members.fetch(user.id);
34 | await member.roles.add(ar.roleID);
35 | console.log('Gave role', ar.roleID, 'to', member);
36 | if (!reaction.users.cache.has(client.user.id)) {
37 | await msg.react(reaction.emoji);
38 | }
39 | }
40 | });
41 |
42 | client.on('messageReactionRemove', async (reaction, user) => {
43 | if (user.id == client.user.id) return;
44 | if (reaction.partial) await reaction.fetch();
45 | for (const ar of autorole) {
46 | const msg = reaction.message;
47 | if (
48 | ar.emoji !== reaction.emoji.toString() ||
49 | ar.msgID !== msg.id ||
50 | ar.autoRemove ||
51 | !msg.guild
52 | )
53 | continue;
54 | const member = await msg.guild.members.fetch(user.id);
55 | await member.roles.remove(ar.roleID);
56 | console.log('Removed role', ar.roleID, 'from', member);
57 | }
58 | });
59 | }
60 |
--------------------------------------------------------------------------------
/src/modules/etc.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ThreadAutoArchiveDuration,
3 | Message,
4 | MessageReaction,
5 | User,
6 | ThreadChannel,
7 | } from 'discord.js';
8 | import { Bot } from '../bot';
9 | import { suggestionsChannelId } from '../env';
10 | import {
11 | clearMessageOwnership,
12 | DELETE_EMOJI,
13 | ownsBotMessage,
14 | } from '../util/send';
15 |
16 | const emojiRegex = /<:\w+?:(\d+?)>|(\p{Emoji_Presentation})/gu;
17 |
18 | const defaultPollEmojis = ['✅', '❌', '🤷'];
19 |
20 | export function etcModule(bot: Bot) {
21 | bot.registerCommand({
22 | aliases: ['ping'],
23 | description: 'See if the bot is alive',
24 | async listener(msg) {
25 | await msg.channel.send('pong. :ping_pong:');
26 | },
27 | });
28 |
29 | bot.client.on('messageCreate', async msg => {
30 | if (msg.author.bot || !msg.content.toLowerCase().startsWith('poll:'))
31 | return;
32 | let emojis = [
33 | ...new Set(
34 | [...msg.content.matchAll(emojiRegex)].map(x => x[1] ?? x[2]),
35 | ),
36 | ];
37 | if (!emojis.length) emojis = defaultPollEmojis;
38 | for (const emoji of emojis) await msg.react(emoji);
39 | });
40 |
41 | bot.client.on('messageCreate', async msg => {
42 | if (msg.author.bot || msg.channelId !== suggestionsChannelId) return;
43 | // First 50 characters of the first line of the content (without cutting off a word)
44 | const title =
45 | msg.content
46 | .split('\n')[0]
47 | .split(/(^.{0,50}\b)/)
48 | .find(x => x) ?? 'Suggestion';
49 | await msg.startThread({
50 | name: title,
51 | autoArchiveDuration: ThreadAutoArchiveDuration.OneDay,
52 | });
53 | for (let emoji of defaultPollEmojis) await msg.react(emoji);
54 | });
55 |
56 | bot.client.on('threadUpdate', async thread => {
57 | if (
58 | thread.parentId !== suggestionsChannelId ||
59 | !((await thread.fetch()) as ThreadChannel).archived
60 | )
61 | return;
62 | const channel = thread.parent!;
63 | let lastMessage = null;
64 | let suggestion: Message;
65 | while (!suggestion!) {
66 | const msgs = await channel.messages.fetch({
67 | before: lastMessage ?? undefined,
68 | limit: 5,
69 | });
70 | suggestion = msgs.find(msg => msg.thread?.id === thread.id)!;
71 | lastMessage = msgs.last()!.id as string;
72 | }
73 | const pollingResults = defaultPollEmojis.map(emoji => {
74 | const reactions = suggestion.reactions.resolve(emoji);
75 | // Subtract the bot's vote
76 | const count = (reactions?.count ?? 0) - 1;
77 | return [emoji, count] as const;
78 | });
79 | const pollingResultStr = pollingResults
80 | .sort((a, b) => b[1] - a[1])
81 | .map(([emoji, count]) => `${count} ${emoji}`)
82 | .join(' ');
83 | await suggestion.reply({
84 | content: `Polling finished; result: ${pollingResultStr}`,
85 | });
86 | });
87 |
88 | bot.client.on('messageReactionAdd', async (reaction, member) => {
89 | if (reaction.partial) return;
90 |
91 | if ((await reaction.message.fetch()).author.id !== bot.client.user.id)
92 | return;
93 | if (reaction.emoji.name !== DELETE_EMOJI) return;
94 | if (member.id === bot.client.user.id) return;
95 |
96 | if (ownsBotMessage(reaction.message, member.id)) {
97 | clearMessageOwnership(reaction.message);
98 | await reaction.message.delete();
99 | } else {
100 | await reaction.users.remove(member.id);
101 | }
102 | });
103 |
104 | bot.registerAdminCommand({
105 | aliases: ['kill'],
106 | async listener(msg) {
107 | const confirm = '✅';
108 | const confirmationMessage = await msg.channel.send('Confirm?');
109 | confirmationMessage.react(confirm);
110 | const reactionFilter = (reaction: MessageReaction, user: User) =>
111 | reaction.emoji.name === confirm && user.id === msg.author.id;
112 | const proceed = await confirmationMessage
113 | .awaitReactions({
114 | filter: reactionFilter,
115 | max: 1,
116 | time: 10 * 1000,
117 | errors: ['time'],
118 | })
119 | .then(() => true)
120 | .catch(() => false);
121 | await confirmationMessage.delete();
122 | if (!proceed) return;
123 | await msg.react('☠️');
124 | process.stdout.write(`
125 | ,--.
126 | { }
127 | K, }
128 | / ~Y\`
129 | , / /
130 | {_'-K.__/
131 | \`/-.__L._
132 | / ' /\`\\_}
133 | / ' /
134 | ____ / ' /
135 | ,-'~~~~ ~~/ ' /_
136 | ,' \`\`~~~ ',
137 | ( Y
138 | { I
139 | { - \`,
140 | | ', )
141 | | | ,..__ __. Y
142 | | .,_./ Y ' / ^Y J )|
143 | \ |' / | | || Killed by @${msg.author.tag}/${msg.author.id}
144 | \ L_/ . _ (_,.'(
145 | \, , ^^""' / | )
146 | \_ \ /,L] /
147 | '-_~-, \` \` ./\`
148 | \`'{_ )
149 | ^^\..___,.--\`
150 | `);
151 | process.exit(1);
152 | },
153 | });
154 | }
155 |
--------------------------------------------------------------------------------
/src/modules/handbook.ts:
--------------------------------------------------------------------------------
1 | import { EmbedBuilder } from 'discord.js';
2 | import algoliasearch from 'algoliasearch/lite';
3 | import { sendWithMessageOwnership } from '../util/send';
4 | import { TS_BLUE } from '../env';
5 | import { decode } from 'html-entities';
6 | import { Bot } from '../bot';
7 |
8 | const ALGOLIA_APP_ID = 'BGCDYOIYZ5';
9 | const ALGOLIA_API_KEY = '37ee06fa68db6aef451a490df6df7c60';
10 | const ALGOLIA_INDEX_NAME = 'typescriptlang';
11 |
12 | const algolia = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_API_KEY, {});
13 |
14 | type AlgoliaResult = {
15 | hierarchy: Record;
16 | url: string;
17 | };
18 |
19 | const HANDBOOK_EMBED = new EmbedBuilder()
20 | .setColor(TS_BLUE)
21 | .setTitle('The TypeScript Handbook')
22 | .setURL('https://www.typescriptlang.org/docs/handbook/intro.html')
23 | .setFooter({ text: 'You can search with `!handbook `' });
24 |
25 | export async function handbookModule(bot: Bot) {
26 | bot.registerCommand({
27 | aliases: ['handbook', 'hb'],
28 | description: 'Search the TypeScript Handbook',
29 | async listener(msg, content) {
30 | if (!content) {
31 | return await sendWithMessageOwnership(msg, {
32 | embeds: [HANDBOOK_EMBED],
33 | });
34 | }
35 |
36 | console.log('Searching algolia for', [content]);
37 | const data = await algolia.search([
38 | {
39 | indexName: ALGOLIA_INDEX_NAME,
40 | query: content,
41 | params: {
42 | offset: 0,
43 | length: 1,
44 | },
45 | },
46 | ]);
47 | console.log('Algolia response:', data);
48 | const hit = data.results[0].hits[0];
49 | if (!hit)
50 | return await sendWithMessageOwnership(
51 | msg,
52 | ':x: No results found for that query',
53 | );
54 | const hierarchyParts = [0, 1, 2, 3, 4, 5, 6]
55 | .map(i => hit.hierarchy[`lvl${i}`])
56 | .filter(x => x);
57 | const embed = new EmbedBuilder()
58 | .setColor(TS_BLUE)
59 | .setTitle(decode(hierarchyParts[hierarchyParts.length - 1]))
60 | .setAuthor({
61 | name: decode(hierarchyParts.slice(0, -1).join(' / ')),
62 | })
63 | .setURL(hit.url);
64 | await sendWithMessageOwnership(msg, { embeds: [embed] });
65 | },
66 | });
67 | }
68 |
--------------------------------------------------------------------------------
/src/modules/help.ts:
--------------------------------------------------------------------------------
1 | import { EmbedBuilder } from 'discord.js';
2 | import { Bot, CommandRegistration } from '../bot';
3 | import { Snippet } from '../entities/Snippet';
4 | import { sendWithMessageOwnership } from '../util/send';
5 |
6 | function getCategoryHelp(cat: string, commands: Iterable) {
7 | const out: string[] = [];
8 |
9 | for (const cmd of new Set(commands)) {
10 | if (!cmd.description) continue;
11 | const [cat2, description] = splitCategoryDescription(cmd.description);
12 | if (cat !== cat2) continue;
13 | out.push(`\`${cmd.aliases[0]}\` ► ${description}`);
14 | }
15 |
16 | return out.join('\n');
17 | }
18 |
19 | function splitCategoryDescription(description: string): [string, string] {
20 | const split = description.split(': ', 2);
21 | if (split.length !== 2) {
22 | return ['Misc', description];
23 | }
24 | return split as [string, string];
25 | }
26 |
27 | function getCommandCategories(commands: Iterable) {
28 | const categories = new Set();
29 |
30 | for (const cmd of commands) {
31 | categories.add(splitCategoryDescription(cmd.description ?? '')[0]);
32 | }
33 |
34 | return [...categories].sort((a, b) => a.localeCompare(b));
35 | }
36 |
37 | export function helpModule(bot: Bot) {
38 | bot.registerCommand({
39 | aliases: ['help', 'commands', 'h'],
40 | description: "Sends what you're looking at right now",
41 | async listener(msg) {
42 | const cmdTrigger = msg.content.split(/\s/)[1];
43 |
44 | if (!msg.guild) return;
45 |
46 | if (!cmdTrigger) {
47 | const embed = new EmbedBuilder()
48 | .setAuthor({
49 | name: msg.guild.name,
50 | iconURL: msg.guild.iconURL() || undefined,
51 | })
52 | .setTitle('Bot Usage')
53 | .setDescription(
54 | `Hello ${msg.author.username}! Here is a list of all commands in me! To get detailed description on any specific command, do \`help \``,
55 | );
56 |
57 | for (const cat of getCommandCategories(bot.commands.values())) {
58 | embed.addFields({
59 | name: `**${cat} Commands:**`,
60 | value: getCategoryHelp(cat, bot.commands.values()),
61 | });
62 | }
63 |
64 | embed
65 | .setFooter({
66 | text: bot.client.user.username,
67 | iconURL: bot.client.user.displayAvatarURL(),
68 | })
69 | .setTimestamp();
70 |
71 | return await sendWithMessageOwnership(msg, { embeds: [embed] });
72 | }
73 |
74 | let cmd: { description?: string; aliases?: string[] } =
75 | bot.getByTrigger(cmdTrigger) || {};
76 |
77 | if (!cmd.description && cmdTrigger.includes(':')) {
78 | const snippet = await Snippet.findOne({
79 | where: { title: cmdTrigger },
80 | });
81 | if (snippet)
82 | cmd = {
83 | description: `A custom snippet created by <@${snippet.owner}>`,
84 | };
85 | else
86 | cmd = {
87 | description:
88 | 'Run the first snippet that matches that pattern',
89 | };
90 | }
91 |
92 | if (!cmd.description)
93 | return await sendWithMessageOwnership(
94 | msg,
95 | `:x: Command not found`,
96 | );
97 |
98 | const embed = new EmbedBuilder().setTitle(
99 | `\`${cmdTrigger}\` Usage`,
100 | );
101 | // Get rid of duplicates, this can happen if someone adds the method name as an alias
102 | const triggers = new Set(cmd.aliases ?? [cmdTrigger]);
103 | if (triggers.size > 1) {
104 | embed.addFields({
105 | name: 'Aliases',
106 | value: Array.from(triggers, t => `\`${t}\``).join(', '),
107 | });
108 | }
109 | embed.addFields({
110 | name: 'Description',
111 | value: `*${
112 | splitCategoryDescription(cmd.description ?? '')[1]
113 | }*`,
114 | });
115 |
116 | await sendWithMessageOwnership(msg, { embeds: [embed] });
117 | },
118 | });
119 | }
120 |
--------------------------------------------------------------------------------
/src/modules/helpForum.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ChannelType,
3 | ThreadChannel,
4 | TextChannel,
5 | Channel,
6 | ForumChannel,
7 | Message,
8 | } from 'discord.js';
9 | import { Bot } from '../bot';
10 | import { HelpThread } from '../entities/HelpThread';
11 | import {
12 | helpForumChannel,
13 | helpForumOpenTagName,
14 | helpForumResolvedTagName,
15 | helpRequestsChannel,
16 | howToGetHelpChannel,
17 | howToGiveHelpChannel,
18 | rolesChannelId,
19 | timeBeforeHelperPing,
20 | trustedRoleId,
21 | } from '../env';
22 | import { sendWithMessageOwnership } from '../util/send';
23 |
24 | const MAX_TAG_COUNT = 5;
25 |
26 | // Use a non-breaking space to force Discord to leave empty lines alone
27 | const postGuidelines = (here = true) =>
28 | listify(`
29 | **How To Get Help**
30 | - Create a new post ${
31 | here ? 'here' : `in <#${helpForumChannel}>`
32 | } with your question.
33 | - It's always ok to just ask your question; you don't need permission.
34 | - Someone will (hopefully!) come along and help you.
35 | - When your question is resolved, type \`!resolved\`.
36 | \u200b
37 | **How To Get Better Help**
38 | - Explain what you want to happen and why…
39 | - …and what actually happens, and your best guess at why.
40 | - Include a short code sample and any error messages you got.
41 | - Text is better than screenshots. Start code blocks with \`\`\`ts.
42 | - If possible, create a minimal reproduction in the TypeScript Playground: .
43 | - Send the full link in its own message; do not use a link shortener.
44 | - For more tips, check out StackOverflow's guide on asking good questions:
45 | \u200b
46 | **If You Haven't Gotten Help**
47 | Usually someone will try to answer and help solve the issue within a few hours. If not, and if you have followed the bullets above, you can ping helpers by running !helper.
48 | `);
49 |
50 | const howToGiveHelp = listify(`
51 | **How To Give Help**
52 | - The channel sidebar on the left will list posts you have joined.
53 | - You can scroll through the channel to see all recent questions.
54 |
55 | **How To Give *Better* Help**
56 | - Get yourself the <@&${trustedRoleId}> role at <#${rolesChannelId}>
57 | - (If you don't like the pings, you can disable role mentions for the server.)
58 | - As a <@&${trustedRoleId}>, you can:
59 | - React to a help post to add tags.
60 | - If a post appears to be resolved, run \`!resolved\` to mark it as such.
61 | - *Only do this if the asker has indicated that their question has been resolved.*
62 | - Conversely, you can run \`!reopen\` if the asker has follow-up questions.
63 |
64 | **Useful Snippets**
65 | - \`!screenshot\` — for if an asker posts a screenshot of code
66 | - \`!ask\` — for if an asker only posts "can I get help?"
67 | `);
68 |
69 | const helperResolve = (owner: string, helper: string) => `
70 | <@${owner}>
71 | Because your issue seemed to be resolved, this post was marked as resolved by <@${helper}>.
72 | If your issue is not resolved, **you can reopen this post by running \`!reopen\`**.
73 | *If you have a different question, make a new post in <#${helpForumChannel}>.*
74 | `;
75 |
76 | export async function helpForumModule(bot: Bot) {
77 | const channel = await bot.client.guilds.cache
78 | .first()
79 | ?.channels.fetch(helpForumChannel)!;
80 | if (channel?.type !== ChannelType.GuildForum) {
81 | console.error(`Expected ${helpForumChannel} to be a forum channel.`);
82 | return;
83 | }
84 | const forumChannel = channel;
85 | const openTag = getTag(forumChannel, helpForumOpenTagName);
86 | const resolvedTag = getTag(forumChannel, helpForumResolvedTagName);
87 |
88 | const helpRequestChannel = await bot.client.guilds.cache
89 | .first()
90 | ?.channels.fetch(helpRequestsChannel)!;
91 | if (!helpRequestChannel?.isTextBased()) {
92 | console.error(`Expected ${helpRequestChannel} to be a text channel.`);
93 | return;
94 | }
95 |
96 | await forumChannel.setTopic(postGuidelines());
97 |
98 | bot.client.on('threadCreate', async thread => {
99 | const owner = await thread.fetchOwner();
100 | if (!owner?.user || !isHelpThread(thread)) return;
101 | console.log(
102 | 'Received new question from',
103 | owner.user.tag,
104 | 'in thread',
105 | thread.id,
106 | );
107 |
108 | await HelpThread.create({
109 | threadId: thread.id,
110 | ownerId: owner.user.id,
111 | }).save();
112 |
113 | await setStatus(thread, openTag);
114 | });
115 |
116 | bot.client.on('threadDelete', async thread => {
117 | if (!isHelpThread(thread)) return;
118 | await HelpThread.delete({
119 | threadId: thread.id,
120 | });
121 | });
122 |
123 | bot.registerCommand({
124 | aliases: ['helper', 'helpers'],
125 | description: 'Help System: Ping the @Helper role from a help post',
126 | async listener(msg, comment) {
127 | if (!isHelpThread(msg.channel)) {
128 | return sendWithMessageOwnership(
129 | msg,
130 | ':warning: You may only ping helpers from a help post',
131 | );
132 | }
133 |
134 | const thread = msg.channel;
135 | const threadData = await getHelpThread(thread.id);
136 |
137 | // Ensure the user has permission to ping helpers
138 | const isAsker = msg.author.id === threadData.ownerId;
139 | const isTrusted = bot.isTrusted(msg);
140 |
141 | if (!isAsker && !isTrusted) {
142 | return sendWithMessageOwnership(
143 | msg,
144 | ':warning: Only the asker can ping helpers',
145 | );
146 | }
147 |
148 | const askTime = thread.createdTimestamp;
149 | const pingAllowedAfter =
150 | +(threadData.helperTimestamp ?? askTime ?? Date.now()) +
151 | timeBeforeHelperPing;
152 |
153 | // Ensure they've waited long enough
154 | // Trusted members (who aren't the asker) are allowed to disregard the timeout
155 | if (isAsker && Date.now() < pingAllowedAfter) {
156 | return sendWithMessageOwnership(
157 | msg,
158 | `:warning: Please wait a bit longer. You can ping helpers .`,
161 | );
162 | }
163 |
164 | const tagStrings = thread.appliedTags.flatMap(t => {
165 | const tag = forumChannel.availableTags.find(at => at.id === t);
166 | if (!tag) return [];
167 | if (!tag.emoji) return tag.name;
168 |
169 | const emoji = tag.emoji.id
170 | ? `<:${tag.emoji.name}:${tag.emoji.id}>`
171 | : tag.emoji.name;
172 | return `${emoji} ${tag.name}`;
173 | });
174 | const tags = tagStrings ? `(${tagStrings.join(', ')})` : '';
175 |
176 | // The beacons are lit, Gondor calls for aid
177 | await Promise.all([
178 | helpRequestChannel.send(
179 | `<@&${trustedRoleId}> ${msg.channel} ${tags} ${
180 | isTrusted ? comment : ''
181 | }`,
182 | ),
183 | msg.react('✅'),
184 | HelpThread.update(thread.id, {
185 | helperTimestamp: Date.now().toString(),
186 | }),
187 | ]);
188 | },
189 | });
190 |
191 | bot.registerCommand({
192 | aliases: ['resolved', 'resolve', 'close', 'closed', 'done', 'solved'],
193 | description: 'Help System: Mark a post as resolved',
194 | async listener(msg) {
195 | changeStatus(msg, true);
196 | },
197 | });
198 |
199 | bot.registerCommand({
200 | aliases: ['reopen', 'open', 'unresolved', 'unresolve'],
201 | description: 'Help System: Reopen a resolved post',
202 | async listener(msg) {
203 | changeStatus(msg, false);
204 | },
205 | });
206 |
207 | bot.client.on('messageReactionAdd', async reaction => {
208 | const message = reaction.message;
209 | const thread = await message.channel.fetch();
210 | if (!isHelpThread(thread)) {
211 | return;
212 | }
213 | const initial = await thread.fetchStarterMessage();
214 | if (initial?.id !== message.id) return;
215 | const tag = forumChannel.availableTags.find(
216 | t =>
217 | t.emoji &&
218 | !t.moderated &&
219 | t.emoji.id === reaction.emoji.id &&
220 | t.emoji.name === reaction.emoji.name,
221 | );
222 | if (!tag) return;
223 | if (thread.appliedTags.length < MAX_TAG_COUNT) {
224 | await thread.setAppliedTags([...thread.appliedTags, tag.id]);
225 | }
226 | await reaction.remove();
227 | });
228 |
229 | async function changeStatus(msg: Message, resolved: boolean) {
230 | const thread = msg.channel;
231 | if (!isHelpThread(thread)) {
232 | return sendWithMessageOwnership(
233 | msg,
234 | ':warning: Can only be run in a help post',
235 | );
236 | }
237 |
238 | const threadData = await getHelpThread(thread.id);
239 | const isAsker = msg.author.id === threadData.ownerId;
240 | const isTrusted = bot.isTrusted(msg);
241 |
242 | if (!isAsker && !isTrusted) {
243 | return sendWithMessageOwnership(
244 | msg,
245 | ':warning: Only the asker can change the status of a help post',
246 | );
247 | }
248 |
249 | await setStatus(thread, resolved ? resolvedTag : openTag);
250 | await msg.react('✅');
251 |
252 | if (resolved && !isAsker) {
253 | await thread.send(helperResolve(thread.ownerId!, msg.author.id));
254 | }
255 | }
256 |
257 | bot.registerAdminCommand({
258 | aliases: ['htgh'],
259 | async listener(msg) {
260 | if (
261 | msg.channel.id !== howToGetHelpChannel &&
262 | msg.channel.id !== howToGiveHelpChannel
263 | ) {
264 | return;
265 | }
266 | (await msg.channel.messages.fetch()).forEach(x => x.delete());
267 | const message =
268 | msg.channel.id === howToGetHelpChannel
269 | ? postGuidelines(false)
270 | : howToGiveHelp;
271 | // Force a blank line at the beginning of the message for compact-mode users
272 | msg.channel.send(`** **\n` + message.trim());
273 | },
274 | });
275 |
276 | async function getHelpThread(threadId: string) {
277 | const threadData = await HelpThread.findOneBy({ threadId });
278 |
279 | if (!threadData) {
280 | // Thread was created when the bot was down.
281 | const thread = await forumChannel.threads.fetch(threadId);
282 | if (!thread) {
283 | throw new Error('Not a forum thread ID');
284 | }
285 | return await HelpThread.create({
286 | threadId,
287 | ownerId: thread.ownerId!,
288 | }).save();
289 | }
290 |
291 | return threadData;
292 | }
293 |
294 | function isHelpThread(
295 | channel: ThreadChannel | Channel,
296 | ): channel is ThreadChannel & { parent: TextChannel } {
297 | return (
298 | channel instanceof ThreadChannel &&
299 | channel.parent?.id === forumChannel.id
300 | );
301 | }
302 |
303 | function getTag(channel: ForumChannel, name: string) {
304 | const tag = channel.availableTags.find(x => x.name === name);
305 | if (!tag) throw new Error(`Could not find tag ${name}`);
306 | return tag.id;
307 | }
308 |
309 | async function setStatus(thread: ThreadChannel, tag: string) {
310 | let tags = thread.appliedTags.filter(
311 | x => x !== openTag && x !== resolvedTag,
312 | );
313 | if (tags.length === MAX_TAG_COUNT) {
314 | tags = tags.slice(0, -1);
315 | }
316 | await thread.setAppliedTags([tag, ...tags]);
317 | }
318 | }
319 |
320 | function listify(text: string) {
321 | // A zero-width space (necessary to prevent discord from trimming the leading whitespace), followed by a three non-breaking spaces.
322 | const indent = '\u200b\u00a0\u00a0\u00a0';
323 | const bullet = '•';
324 | return text.replace(/^(\s*)-/gm, `$1${bullet}`).replace(/\t/g, indent);
325 | }
326 |
--------------------------------------------------------------------------------
/src/modules/mod.ts:
--------------------------------------------------------------------------------
1 | import { Message, Snowflake, User } from 'discord.js';
2 | import { Bot } from '../bot';
3 | import { rulesChannelId } from '../env';
4 |
5 | // Most job posts are in this format:
6 | // > [FOR HIRE][REMOTE][SOMETHING ELSE]
7 | // > Hi, I'm ponyman6000. Hire me!
8 | const jobPostRegex = /^(?:\[[A-Z /-]+\]\s*){2,}\n/i;
9 |
10 | const SPAM_CHANNEL_THRESHOLD = 3;
11 | const SPAM_MAX_TIME = 5000;
12 |
13 | interface RecentMessageInfo {
14 | author: User;
15 | firstPost: Date;
16 | channels: Set;
17 | messages: Message[];
18 | }
19 | const recentMessages = new Map();
20 | const CLEANUP_RECENT_MESSAGES_MAP_INTERVAL = 10000;
21 |
22 | export function modModule({ client }: Bot) {
23 | client.on('messageCreate', async msg => {
24 | if (msg.author.bot || !jobPostRegex.test(msg.content)) return;
25 | await msg.delete();
26 | await msg.channel.send(
27 | `${msg.author} We don't do job posts here; see <#${rulesChannelId}>`,
28 | );
29 | console.log('Deleted job post message from', msg.author);
30 | });
31 |
32 | client.on('messageCreate', async msg => {
33 | if (!msg.guild || msg.author.id === client.user.id) return;
34 | const messageIdentifier = msg.content.trim().toLowerCase();
35 | if (!messageIdentifier) return;
36 | let recentMessageInfo = recentMessages.get(messageIdentifier);
37 | if (
38 | recentMessageInfo &&
39 | recentMessageInfo.author.id === msg.author.id &&
40 | +recentMessageInfo.firstPost + SPAM_MAX_TIME > Date.now()
41 | ) {
42 | recentMessageInfo.channels.add(msg.channel.id);
43 | recentMessageInfo.messages.push(msg);
44 | if (recentMessageInfo.channels.size >= SPAM_CHANNEL_THRESHOLD) {
45 | await Promise.all([
46 | msg.guild.members
47 | .fetch(msg.author)
48 | .then(member => void member.kick('Spam')),
49 | ...recentMessageInfo.messages.map(msg => void msg.delete()),
50 | ]);
51 | console.log(
52 | 'Kicked',
53 | msg.author,
54 | 'for spam and deleted',
55 | recentMessageInfo.messages.length,
56 | 'identical messages',
57 | );
58 | }
59 | } else {
60 | recentMessages.set(messageIdentifier, {
61 | author: msg.author,
62 | firstPost: new Date(),
63 | channels: new Set([msg.channel.id]),
64 | messages: [msg],
65 | });
66 | }
67 | });
68 | }
69 |
70 | setInterval(() => {
71 | for (const [messageText, recentMessageInfo] of recentMessages) {
72 | if (+recentMessageInfo.firstPost + SPAM_MAX_TIME < Date.now()) {
73 | recentMessages.delete(messageText);
74 | }
75 | }
76 | }, CLEANUP_RECENT_MESSAGES_MAP_INTERVAL);
77 |
--------------------------------------------------------------------------------
/src/modules/playground.ts:
--------------------------------------------------------------------------------
1 | import { EmbedBuilder, Message, User } from 'discord.js';
2 | import {
3 | compressToEncodedURIComponent,
4 | decompressFromEncodedURIComponent,
5 | } from 'lz-string';
6 | import { format } from 'prettier';
7 | import { URLSearchParams } from 'url';
8 | import { TS_BLUE } from '../env';
9 | import {
10 | makeCodeBlock,
11 | findCode,
12 | matchPlaygroundLink,
13 | PlaygroundLinkMatch,
14 | } from '../util/codeBlocks';
15 | import { LimitedSizeMap } from '../util/limitedSizeMap';
16 | import { addMessageOwnership, sendWithMessageOwnership } from '../util/send';
17 | import { fetch } from 'undici';
18 | import { Bot } from '../bot';
19 |
20 | const PLAYGROUND_BASE = 'https://www.typescriptlang.org/play/#code/';
21 | const LINK_SHORTENER_ENDPOINT = 'https://tsplay.dev/api/short';
22 | const MAX_EMBED_LENGTH = 512;
23 | const DEFAULT_EMBED_LENGTH = 256;
24 |
25 | export async function playgroundModule(bot: Bot) {
26 | const editedLongLink = new LimitedSizeMap(1000);
27 |
28 | bot.registerCommand({
29 | aliases: ['playground', 'pg', 'playg'],
30 | description: 'Shorten a TypeScript playground link',
31 | async listener(msg, content) {
32 | console.log('Playground', msg.content);
33 |
34 | let code: string | undefined = content;
35 |
36 | if (!code) {
37 | code = await findCode(msg, true);
38 | if (!code)
39 | return sendWithMessageOwnership(
40 | msg,
41 | ":warning: couldn't find a codeblock!",
42 | );
43 | }
44 | const embed = new EmbedBuilder()
45 | .setURL(PLAYGROUND_BASE + compressToEncodedURIComponent(code))
46 | .setTitle('View in Playground')
47 | .setColor(TS_BLUE);
48 | await sendWithMessageOwnership(msg, { embeds: [embed] });
49 | },
50 | });
51 |
52 | bot.client.on('messageCreate', async msg => {
53 | if (msg.author.bot) return;
54 | if (msg.content[0] === '!') return;
55 | const exec = matchPlaygroundLink(msg.content);
56 | if (!exec) return;
57 | const embed = createPlaygroundEmbed(msg.author, exec);
58 | if (exec.isWholeMatch) {
59 | // Message only contained the link
60 | await sendWithMessageOwnership(msg, {
61 | embeds: [embed],
62 | });
63 | await msg.delete();
64 | } else {
65 | // Message also contained other characters
66 | const botMsg = await msg.channel.send({
67 | embeds: [embed],
68 | content: `${msg.author} Here's a shortened URL of your playground link! You can remove the full link from your message.`,
69 | });
70 | editedLongLink.set(msg.id, botMsg);
71 | await addMessageOwnership(botMsg, msg.author);
72 | }
73 | });
74 |
75 | bot.client.on('messageCreate', async msg => {
76 | const attachment = msg.attachments.find(a => a.name === 'message.txt');
77 | if (msg.author.bot || !attachment) return;
78 | const content = await fetch(attachment.url).then(r => r.text());
79 | const exec = matchPlaygroundLink(content);
80 | // By default, if you write a message in the box and then paste a long
81 | // playground link, it will only put the paste in message.txt and will
82 | // put the rest of the message in msg.content
83 | if (!exec?.isWholeMatch) return;
84 | const shortenedUrl = await shortenPlaygroundLink(exec.url);
85 | const embed = createPlaygroundEmbed(msg.author, exec, shortenedUrl);
86 | await sendWithMessageOwnership(msg, {
87 | embeds: [embed],
88 | });
89 | if (!msg.content) await msg.delete();
90 | });
91 |
92 | bot.client.on('messageUpdate', async (_oldMsg, msg) => {
93 | if (msg.partial) msg = await msg.fetch();
94 | const exec = matchPlaygroundLink(msg.content);
95 | if (msg.author.bot || !editedLongLink.has(msg.id) || exec) return;
96 | const botMsg = editedLongLink.get(msg.id);
97 | // Edit the message to only have the embed and not the "please edit your message" message
98 | await botMsg?.edit({
99 | content: '',
100 | embeds: [botMsg.embeds[0]],
101 | });
102 | editedLongLink.delete(msg.id);
103 | });
104 | }
105 |
106 | // Take care when messing with the truncation, it's extremely finnicky
107 | function createPlaygroundEmbed(
108 | author: User,
109 | { url: _url, query, code, isEscaped }: PlaygroundLinkMatch,
110 | url: string = _url,
111 | ) {
112 | const embed = new EmbedBuilder()
113 | .setColor(TS_BLUE)
114 | .setTitle('Playground Link')
115 | .setAuthor({ name: author.tag, iconURL: author.displayAvatarURL() })
116 | .setURL(url);
117 |
118 | const unzipped = decompressFromEncodedURIComponent(code);
119 | if (!unzipped) return embed;
120 |
121 | // Without 'normalized' you can't get consistent lengths across platforms
122 | // Matters because the playground uses the line breaks of whoever created it
123 | const lines = unzipped.split(/\r\n|\r|\n/);
124 | const normalized = lines.join('\n');
125 |
126 | const lengths = lines.map(l => l.length);
127 | const cum = lengths.slice(1).reduce((acc, len, i) => {
128 | acc.push(len + acc[i] + '\n'.length);
129 | return acc;
130 | }, lengths.slice(0, 1));
131 | const lineIndices = [0].concat(cum);
132 |
133 | // Note: lines are 1-indexed
134 | let { startLine, endLine } = getSelectionQueryParams(query, lines.length);
135 |
136 | const startChar = startLine ? lineIndices[startLine - 1] : 0;
137 | const cutoff = endLine
138 | ? Math.min(lineIndices[endLine], startChar + MAX_EMBED_LENGTH)
139 | : startChar + DEFAULT_EMBED_LENGTH;
140 | // End of the line containing the cutoff
141 | const endChar = lineIndices.find(len => len >= cutoff) ?? normalized.length;
142 |
143 | let pretty;
144 | try {
145 | // Make lines as short as reasonably possible, so they fit in the embed.
146 | // We pass prettier the full string, but only format part of it, so we can
147 | // calculate where the endChar is post-formatting.
148 | pretty = format(normalized, {
149 | parser: 'typescript',
150 | printWidth: 55,
151 | tabWidth: 2,
152 | semi: false,
153 | bracketSpacing: false,
154 | arrowParens: 'avoid',
155 | rangeStart: startChar,
156 | rangeEnd: endChar,
157 | });
158 | } catch (e) {
159 | // Likely a syntax error
160 | pretty = normalized;
161 | }
162 | const prettyEndChar = pretty.length - (normalized.length - endChar);
163 | const formattedSection = pretty.slice(startChar, prettyEndChar);
164 | const content =
165 | (startChar === 0 ? '' : '...\n') +
166 | formattedSection.replace(/^\s*\n|\n\s*$/g, '') +
167 | (prettyEndChar === pretty.length ? '' : '\n...');
168 |
169 | if (!isEscaped) {
170 | embed.setDescription('**Preview:**' + makeCodeBlock(content));
171 | if (!startLine && !endLine) {
172 | embed.setFooter({
173 | text: 'You can choose specific lines to embed by selecting them before copying the link.',
174 | });
175 | }
176 | }
177 |
178 | return embed;
179 | }
180 |
181 | async function shortenPlaygroundLink(url: string) {
182 | const response = await fetch(LINK_SHORTENER_ENDPOINT, {
183 | method: 'post',
184 | body: JSON.stringify({ url, createdOn: 'api', expires: false }),
185 | headers: {
186 | 'Content-Type': 'application/json',
187 | },
188 | });
189 | const { shortened } = (await response.json()) as any;
190 | if (typeof shortened !== 'string')
191 | throw new Error('Received invalid api response from link shortener');
192 | return shortened;
193 | }
194 |
195 | // Sometimes the cursor is at the start of the selection, and other times
196 | // it's at the end of the selection; we don't care which, only that the
197 | // lower one always comes first. Also, parameter validation happens here
198 | function getSelectionQueryParams(query: string, numLines: number) {
199 | const params = new URLSearchParams(query);
200 |
201 | const [startLine, endLine] = ['pln', 'ssl']
202 | // @ts-expect-error parseInt(null) is okay here since we check for NaN
203 | .map(name => parseInt(params.get(name)))
204 | .map(n => (!Number.isNaN(n) && 1 <= n && n <= numLines ? n : undefined))
205 | .sort((a, b) => (a ?? Infinity) - (b ?? Infinity));
206 |
207 | return { startLine, endLine };
208 | }
209 |
--------------------------------------------------------------------------------
/src/modules/rep.ts:
--------------------------------------------------------------------------------
1 | import { Message, EmbedBuilder } from 'discord.js';
2 | import { repEmoji, TS_BLUE } from '../env';
3 |
4 | import { Rep } from '../entities/Rep';
5 | import { sendPaginatedMessage } from '../util/sendPaginatedMessage';
6 | import { getMessageOwner, sendWithMessageOwnership } from '../util/send';
7 | import { Bot } from '../bot';
8 |
9 | // The Chinese is outside the group on purpose, because CJK languages don't have word bounds. Therefore we only look for key characters
10 |
11 | const thanksRegex =
12 | /* cspell:disable-next-line */
13 | /\b(?:thank|thanks|thx|cheers|thanx|thnks|ty|tysm|tks|tkx|danke|merci|gracias|grazie|xiexie)\b|谢/i;
14 |
15 | const removedReactions = new Set();
16 |
17 | export function repModule(bot: Bot) {
18 | const { client } = bot;
19 |
20 | function giveRep(
21 | msg: Pick,
22 | { recipient, initialGiver }: Pick,
23 | ) {
24 | console.log('Creating a Rep with recipient', recipient);
25 |
26 | return Rep.create({
27 | messageId: msg.id,
28 | channel: msg.channelId,
29 | amount: 1,
30 | recipient,
31 | initialGiver,
32 | date: new Date().toISOString(),
33 | }).save();
34 | }
35 |
36 | async function onThank(msg: Message, force = false) {
37 | // Check for thanks messages
38 | const isThanks = thanksRegex.test(msg.content);
39 | if (msg.author.bot || (!isThanks && !force) || !msg.guild) return;
40 |
41 | const mentionUsers = msg.mentions.users.filter(
42 | user => user.id !== msg.member?.id && user.id !== client.user.id,
43 | );
44 | if (mentionUsers.size !== 1) return;
45 |
46 | const recipient = mentionUsers.first()!;
47 |
48 | await giveRep(msg, {
49 | recipient: recipient.id,
50 | initialGiver: msg.author.id,
51 | });
52 |
53 | await msg.react(repEmoji);
54 | }
55 |
56 | client.on('messageCreate', msg => onThank(msg));
57 |
58 | client.on('messageReactionAdd', async (reaction, user) => {
59 | if (
60 | !reaction.message.guild ||
61 | user.id === client.user.id ||
62 | (reaction.emoji.id ?? reaction.emoji.name) !== repEmoji
63 | ) {
64 | return;
65 | }
66 |
67 | const msg = reaction.message;
68 | const author = (await msg.fetch()).author;
69 |
70 | console.log('Received rep reaction on', msg.id);
71 |
72 | if (user.id === author.id) {
73 | return removeReaction();
74 | }
75 |
76 | console.log('Querying database for existing Rep');
77 |
78 | let existingRep = await Rep.findOne({ where: { messageId: msg.id } });
79 |
80 | if (existingRep) {
81 | console.log('Found existing Rep', existingRep);
82 | if (user.id === existingRep.recipient) {
83 | console.log('User is recipient; removing reaction');
84 | return removeReaction();
85 | }
86 | console.log('Existing amount is', existingRep.amount);
87 | existingRep.amount++;
88 | existingRep.save();
89 | console.log('Incremented amount to', existingRep.amount);
90 | return;
91 | }
92 |
93 | let recipient = author.id;
94 |
95 | if (recipient == client.user.id) {
96 | console.log('Recipient is bot; checking for message ownership');
97 | let altRecipient = getMessageOwner(msg);
98 | if (!altRecipient) {
99 | console.log('No message owner recorded; removing reaction');
100 | return removeReaction();
101 | }
102 | console.log('Message owner is', altRecipient);
103 | recipient = altRecipient;
104 | }
105 |
106 | await giveRep(msg, {
107 | recipient,
108 | initialGiver: user.id,
109 | });
110 |
111 | async function removeReaction() {
112 | removedReactions.add([msg.id, user.id].toString());
113 | await reaction.users.remove(user.id);
114 | }
115 | });
116 |
117 | client.on('messageReactionRemove', async (reaction, user) => {
118 | if (
119 | !reaction.message.guild ||
120 | (reaction.emoji.id ?? reaction.emoji.name) !== repEmoji ||
121 | removedReactions.delete([reaction.message.id, user.id].toString())
122 | )
123 | return;
124 |
125 | let rep = await Rep.findOne({
126 | where: {
127 | messageId: reaction.message.id,
128 | },
129 | });
130 |
131 | if (!rep) return;
132 |
133 | rep.amount -= 1;
134 | await rep.save();
135 |
136 | console.log(
137 | 'Decremented rep amount to',
138 | rep.amount,
139 | 'for message',
140 | rep.messageId,
141 | );
142 | });
143 |
144 | client.on('messageDelete', async msg => {
145 | await Rep.delete(msg.id);
146 | });
147 |
148 | bot.registerCommand({
149 | aliases: ['rep'],
150 | description: 'Reputation: Give a different user some reputation points',
151 | async listener(msg) {
152 | const targetMember = msg.content.split(/\s/)[1];
153 |
154 | if (targetMember.match(/\d+/)) {
155 | const user = await client.users
156 | .fetch(targetMember)
157 | .catch(() => null);
158 |
159 | if (user) {
160 | if (user.id === msg.author.id) {
161 | await msg.react('🤡');
162 | return;
163 | }
164 | await giveRep(msg, {
165 | recipient: targetMember,
166 | initialGiver: msg.author.id,
167 | });
168 | await msg.react(repEmoji);
169 | return;
170 | }
171 | }
172 |
173 | await onThank(msg, true);
174 | },
175 | });
176 |
177 | bot.registerCommand({
178 | aliases: ['history'],
179 | description: "Reputation: View a user's reputation history",
180 | async listener(msg) {
181 | if (!msg.member) return;
182 | let user = await bot.getTargetUser(msg);
183 |
184 | if (!user) {
185 | await sendWithMessageOwnership(
186 | msg,
187 | 'Unable to find user to give rep',
188 | );
189 | return;
190 | }
191 |
192 | const records = (await Rep.find({ where: { recipient: user.id } }))
193 | .reverse()
194 | .filter(x => x.amount > 0)
195 | .map(rg => {
196 | const emoji =
197 | msg.guild!.emojis.resolve(repEmoji) ?? repEmoji;
198 | const messageLink = `https://discord.com/channels/${
199 | msg.guild!.id
200 | }/${rg.channel}/${rg.messageId}`;
201 | return `**${
202 | rg.amount
203 | } ${emoji}** on [message](${messageLink}) (<@${
204 | rg.initialGiver
205 | }>${rg.amount > 1 ? ' et al.' : ''}) at `;
208 | });
209 | if (!records.length) records.push('[no reputation history]');
210 | const recordsPerPage = 10;
211 | const pages = records
212 | .reduce((acc, cur, index) => {
213 | const curChunk = Math.floor(index / recordsPerPage);
214 | acc[curChunk] ??= [];
215 | acc[curChunk].push(cur);
216 | return acc;
217 | }, [])
218 | .map(page => page.join('\n'));
219 | const embed = new EmbedBuilder().setColor(TS_BLUE).setAuthor({
220 | name: user.tag,
221 | iconURL: user.displayAvatarURL(),
222 | });
223 | await sendPaginatedMessage(
224 | embed,
225 | pages,
226 | msg.member,
227 | msg.channel,
228 | 300000,
229 | );
230 | },
231 | });
232 |
233 | bot.registerCommand({
234 | aliases: ['leaderboard', 'lb'],
235 | description: 'Reputation: See who has the most reputation',
236 | async listener(msg) {
237 | const periods = {
238 | 'rolling-hour': ['(past hour)', Date.now() - 60 * 60 * 1000],
239 | 'rolling-day': ['(past day)', Date.now() - 24 * 60 * 60 * 1000],
240 | 'rolling-month': [
241 | '(past 30 days)',
242 | Date.now() - 30 * 24 * 60 * 60 * 1000,
243 | ],
244 | 'rolling-year': [
245 | '(past year)',
246 | Date.now() - 365 * 24 * 60 * 60 * 1000,
247 | ],
248 | day: [
249 | '(today)',
250 | +new Date(
251 | new Date().getFullYear(),
252 | new Date().getMonth(),
253 | new Date().getDate(),
254 | ),
255 | ],
256 | month: [
257 | '(this month)',
258 | +new Date(new Date().getFullYear(), new Date().getMonth()),
259 | ],
260 | year: ['(this year)', +new Date(new Date().getFullYear())],
261 | all: ['(all time)', 0],
262 | } as const;
263 |
264 | const period = msg.content.split(/\s/)[1] || 'rolling-month';
265 |
266 | if (!(period in periods))
267 | return await sendWithMessageOwnership(
268 | msg,
269 | `:x: Invalid period (expected one of ${Object.keys(periods)
270 | .map(x => `\`${x}\``)
271 | .join(', ')})`,
272 | );
273 | const [text, dateMin] = periods[period as keyof typeof periods];
274 | const topEmojis = [
275 | ':first_place:',
276 | ':second_place:',
277 | ':third_place:',
278 | ];
279 | const query = Rep.createQueryBuilder()
280 | .where(`date > '${new Date(dateMin).toISOString()}'`)
281 | .select(['recipient', 'SUM(amount)', 'MAX(date)'])
282 | .groupBy('recipient')
283 | .orderBy('SUM(amount)', 'DESC')
284 | .limit(10);
285 | const data = (await query.getRawMany()) as {
286 | recipient: string;
287 | sum: number;
288 | }[];
289 | const embed = new EmbedBuilder()
290 | .setColor(TS_BLUE)
291 | .setTitle(`Top 10 Reputation ${text}`)
292 | .setDescription(
293 | data
294 | .map(
295 | (x, index) =>
296 | `${
297 | topEmojis[index] || ':white_small_square:'
298 | } **<@${x.recipient}>** with **${
299 | x.sum
300 | }** points.`,
301 | )
302 | .join('\n'),
303 | );
304 | await msg.channel.send({ embeds: [embed] });
305 | },
306 | });
307 | }
308 |
--------------------------------------------------------------------------------
/src/modules/snippet.ts:
--------------------------------------------------------------------------------
1 | import { EmbedBuilder, TextChannel, User } from 'discord.js';
2 | import { Snippet } from '../entities/Snippet';
3 | import { BLOCKQUOTE_GREY } from '../env';
4 | import { sendWithMessageOwnership } from '../util/send';
5 | import { getReferencedMessage } from '../util/getReferencedMessage';
6 | import { splitCustomCommand } from '../util/customCommand';
7 | import { Bot } from '../bot';
8 |
9 | // https://stackoverflow.com/a/3809435
10 | const LINK_REGEX =
11 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
12 |
13 | const DISCORD_MESSAGE_LINK_REGEX_ANCHORED =
14 | /^https:\/\/discord.com\/channels\/(\d+)\/(\d+)\/(\d+)$/;
15 |
16 | export function snippetModule(bot: Bot) {
17 | bot.client.on('messageCreate', async msg => {
18 | const commandData = await splitCustomCommand(bot, msg);
19 | if (!commandData) return;
20 | const { command } = commandData;
21 |
22 | if (command.includes('*') && !command.includes(':')) return;
23 |
24 | const [match] = await interpretSpecifier(msg.author, command, 1);
25 |
26 | if (!match) return;
27 |
28 | // We already know there's a snippet under this id from the search
29 | const snippet = (await Snippet.findOneBy({ id: match.id }))!;
30 |
31 | await addSnippetUses(match.id);
32 | const onDelete = () => addSnippetUses(match.id, -1);
33 |
34 | if (snippet.content) {
35 | await sendWithMessageOwnership(msg, snippet.content, onDelete);
36 | return;
37 | }
38 |
39 | const owner = await bot.client.users.fetch(snippet.owner);
40 | const embed = new EmbedBuilder({
41 | ...snippet,
42 | // image is in an incompatible format, so we have to set it later
43 | image: undefined,
44 | });
45 | if (match.id.includes(':'))
46 | embed.setAuthor({
47 | name: owner.tag,
48 | iconURL: owner.displayAvatarURL(),
49 | });
50 | if (snippet.image) embed.setImage(snippet.image);
51 | await sendWithMessageOwnership(msg, { embeds: [embed] }, onDelete);
52 | });
53 |
54 | bot.registerCommand({
55 | description: 'Snippet: List snippets matching an optional filter',
56 | aliases: ['listSnippets', 'snippets', 'snips'],
57 | async listener(msg, specifier) {
58 | const limit = 20;
59 | const matches = await interpretSpecifier(
60 | msg.author,
61 | specifier || '*',
62 | limit + 1,
63 | );
64 | await sendWithMessageOwnership(msg, {
65 | embeds: [
66 | new EmbedBuilder()
67 | .setColor(BLOCKQUOTE_GREY)
68 | .setTitle(
69 | `${
70 | matches.length > limit
71 | ? `${limit}+`
72 | : matches.length
73 | } Matches Found`,
74 | )
75 | .setDescription(
76 | matches
77 | .slice(0, limit)
78 | .map(
79 | s =>
80 | `- \`${s.id}\` with **${s.uses}** uses`,
81 | )
82 | .join('\n'),
83 | ),
84 | ],
85 | });
86 | },
87 | });
88 |
89 | bot.registerCommand({
90 | description: 'Snippet: Create or edit a snippet',
91 | aliases: ['snip', 'snippet', 'createSnippet'],
92 | async listener(msg, content) {
93 | if (!msg.member) return;
94 |
95 | const [name, ...parts] = content.split(' ');
96 | const source = parts.join(' ');
97 |
98 | const linkedMessage = await getMessageFromLink(source);
99 |
100 | if (!name) {
101 | return await sendWithMessageOwnership(
102 | msg,
103 | ':x: You have to supply a name for the command',
104 | );
105 | }
106 |
107 | const id = name.startsWith('!')
108 | ? `${sanitizeIdPart(name.slice(1))}`
109 | : `${sanitizeIdPart(msg.author.username)}:${sanitizeIdPart(
110 | name,
111 | )}`;
112 | const existingSnippet = await Snippet.findOneBy({ id });
113 |
114 | if (!id.includes(':') && !bot.isMod(msg.member))
115 | return await sendWithMessageOwnership(
116 | msg,
117 | ":x: You don't have permission to create a global snippet",
118 | );
119 |
120 | if (
121 | !bot.isMod(msg.member) &&
122 | existingSnippet &&
123 | existingSnippet.owner !== msg.author.id
124 | )
125 | return await sendWithMessageOwnership(
126 | msg,
127 | ":x: Cannot edit another user's snippet",
128 | );
129 |
130 | const title = `\`!${id}\`: `;
131 | const base = {
132 | id,
133 | uses: existingSnippet?.uses ?? 0,
134 | owner: msg.author.id,
135 | title,
136 | };
137 | let data: Partial | undefined;
138 |
139 | if (source && !linkedMessage) {
140 | const referencedSnippet = await Snippet.findOneBy({
141 | id: source,
142 | });
143 | if (!referencedSnippet)
144 | return await sendWithMessageOwnership(
145 | msg,
146 | ':x: Second argument must be a valid discord message link or snippet id',
147 | );
148 | data = {
149 | ...referencedSnippet,
150 | ...base,
151 | title:
152 | base.title +
153 | referencedSnippet.title
154 | ?.split(': ')
155 | .slice(1)
156 | .join(': '),
157 | };
158 | } else {
159 | const sourceMessage =
160 | linkedMessage ?? (await getReferencedMessage(msg));
161 | if (!sourceMessage)
162 | return await sendWithMessageOwnership(
163 | msg,
164 | ':x: You have to reply or link to a comment to make it a snippet',
165 | );
166 |
167 | const description = sourceMessage.content;
168 | const referencedEmbed = sourceMessage.embeds[0];
169 |
170 | if (LINK_REGEX.exec(description)?.[0] === description)
171 | data = {
172 | ...base,
173 | content: description,
174 | };
175 | else if (description)
176 | data = {
177 | ...base,
178 | description,
179 | color: parseInt(BLOCKQUOTE_GREY.slice(1), 16),
180 | };
181 | else if (referencedEmbed)
182 | data = {
183 | ...base,
184 | title: title + (referencedEmbed.title || ''),
185 | description: referencedEmbed.description!,
186 | color: referencedEmbed.color!,
187 | image: referencedEmbed.image?.url,
188 | url: referencedEmbed.url!,
189 | };
190 | }
191 |
192 | if (!data) {
193 | return await sendWithMessageOwnership(
194 | msg,
195 | ':x: Cannot generate a snippet from that message',
196 | );
197 | }
198 |
199 | await existingSnippet?.remove();
200 | await Snippet.create(data).save();
201 | const verb = existingSnippet ? 'Edited' : 'Created';
202 | await sendWithMessageOwnership(
203 | msg,
204 | `:white_check_mark: ${verb} snippet \`${id}\``,
205 | );
206 | console.log(`${verb} snippet ${id} for`, msg.author);
207 | },
208 | });
209 |
210 | bot.registerCommand({
211 | description: 'Snippet: Delete a snippet you own',
212 | aliases: ['deleteSnip'],
213 | async listener(msg, id) {
214 | if (!msg.member) return;
215 | const snippet = await Snippet.findOneBy({ id });
216 | if (!snippet)
217 | return await sendWithMessageOwnership(
218 | msg,
219 | ':x: No snippet found with that id',
220 | );
221 | if (!bot.isMod(msg.member) && snippet.owner !== msg.author.id)
222 | return await sendWithMessageOwnership(
223 | msg,
224 | ":x: Cannot delete another user's snippet",
225 | );
226 | await snippet.remove();
227 | console.log(`Deleted snippet ${id} for`, msg.author);
228 | sendWithMessageOwnership(msg, ':white_check_mark: Deleted snippet');
229 | },
230 | });
231 |
232 | async function getMessageFromLink(messageLink: string | undefined) {
233 | const messageLinkExec = DISCORD_MESSAGE_LINK_REGEX_ANCHORED.exec(
234 | messageLink ?? '',
235 | );
236 | if (!messageLinkExec) return;
237 | const guild = bot.client.guilds.cache.get(messageLinkExec[1]);
238 | const channel = guild?.channels.cache.get(messageLinkExec[2]);
239 | if (!channel || !(channel instanceof TextChannel)) return;
240 | const message = await channel.messages.fetch(messageLinkExec[3]);
241 | return message;
242 | }
243 | }
244 |
245 | const sanitizeIdPart = (part: string) =>
246 | part.toLowerCase().replace(/[^\w-]/g, '');
247 |
248 | const interpretSpecifier = async (
249 | sender: User,
250 | specifier: string,
251 | limit: number,
252 | ): Promise => {
253 | specifier = specifier.replace(/\\/g, '');
254 | if (/[^\w:*%-]/.test(specifier)) return [];
255 | // `%` is SQL's /.*/g for LIKE
256 | specifier = specifier.replace(/\*/g, '%');
257 | const baseQuery = () =>
258 | Snippet.createQueryBuilder()
259 | .select(['id', 'owner', 'uses'])
260 | .where('id like :specifier')
261 | .orderBy('uses', 'DESC')
262 | .setParameters({ specifier: specifier, owner: sender.id })
263 | .limit(limit);
264 | if (specifier.startsWith(':')) {
265 | specifier = '%' + specifier;
266 | const matches = await baseQuery()
267 | .andWhere('owner = :owner')
268 | .getRawMany();
269 | if (matches.length) return matches;
270 | }
271 | return await baseQuery().getRawMany();
272 | };
273 |
274 | const addSnippetUses = async (id: string, amount = 1) => {
275 | await Snippet.createQueryBuilder()
276 | .update()
277 | .where('id = :id')
278 | .set({ uses: () => 'uses + :amount' })
279 | .setParameters({ id, amount })
280 | .execute();
281 | };
282 |
283 | type SnippetInfo = Pick;
284 |
--------------------------------------------------------------------------------
/src/modules/twoslash.ts:
--------------------------------------------------------------------------------
1 | import { Message } from 'discord.js';
2 | import { twoslasher, TwoSlashReturn } from '@typescript/twoslash';
3 | import { ScriptTarget, type CompilerOptions } from 'typescript';
4 | import { makeCodeBlock, findCode } from '../util/codeBlocks';
5 | import { sendWithMessageOwnership } from '../util/send';
6 | import { getTypeScriptModule, TypeScript } from '../util/getTypeScriptModule';
7 | import { splitCustomCommand } from '../util/customCommand';
8 | import { Bot } from '../bot';
9 |
10 | const defaultCompilerOptions: CompilerOptions = {
11 | target: ScriptTarget.ESNext,
12 | };
13 |
14 | // Preload typescript@latest
15 | getTypeScriptModule('latest');
16 |
17 | // Remove `@noErrorTruncation` from the source; this can cause lag/crashes for large errors
18 | function redactNoErrorTruncation(code: string) {
19 | return code.replace(/@noErrorTruncation/g, '');
20 | }
21 |
22 | export function twoslashModule(bot: Bot) {
23 | bot.registerCommand({
24 | description:
25 | 'Twoslash: Run twoslash on the latest codeblock, optionally returning the quick infos of specified symbols. You can use ts@4.8.3 or ts@next to run a specific version.',
26 | aliases: ['twoslash', 'ts'],
27 | async listener(msg, content) {
28 | await twoslash(msg, 'latest', content);
29 | },
30 | });
31 |
32 | bot.client.on('messageCreate', async msg => {
33 | const commandData = await splitCustomCommand(bot, msg);
34 | if (!commandData) return;
35 | const { command, args } = commandData;
36 | if (!command.startsWith('ts@') && !command.startsWith('twoslash@'))
37 | return;
38 | const version = command.split('@').slice(1).join('@');
39 | await twoslash(msg, version, args);
40 | });
41 |
42 | bot.client.on('messageCreate', async msg => {
43 | const match = msg.content.match(
44 | /^```(?:ts |typescript )?twoslash\n([\s\S]+)```$/im,
45 | );
46 | if (!msg.author.bot && match) {
47 | await twoslashBlock(
48 | msg,
49 | match[1],
50 | (await getTypeScriptModule('latest'))!,
51 | );
52 | await msg.delete();
53 | }
54 | });
55 | }
56 |
57 | async function twoslash(msg: Message, version: string, content: string) {
58 | const tsModule = await getTypeScriptModule(version);
59 |
60 | if (!tsModule)
61 | return await sendWithMessageOwnership(
62 | msg,
63 | ':x: Could not find that version of TypeScript',
64 | );
65 |
66 | const code = await findCode(msg);
67 |
68 | if (!code)
69 | return await sendWithMessageOwnership(
70 | msg,
71 | `:warning: could not find any TypeScript codeblocks in the past 10 messages`,
72 | );
73 |
74 | if (!content) return await twoslashBlock(msg, code, tsModule);
75 |
76 | if (!/^\s*([_$a-zA-Z][_$0-9a-zA-Z]*\b\s*)+/.test(content)) {
77 | return sendWithMessageOwnership(
78 | msg,
79 | 'You need to give me a valid symbol name to look for!',
80 | );
81 | }
82 |
83 | const symbols = [...new Set(content.trim().split(/\s+/g))];
84 |
85 | let ret: TwoSlashReturn;
86 | try {
87 | ret = twoslasher(redactNoErrorTruncation(code), 'ts', {
88 | tsModule,
89 | defaultCompilerOptions,
90 | defaultOptions: { noErrorValidation: true },
91 | });
92 | } catch (e) {
93 | if (!(e instanceof Error)) throw e;
94 | return await sendWithMessageOwnership(msg, `:x: ${e.message}`);
95 | }
96 |
97 | const blocks = [];
98 |
99 | for (const symbol of symbols) {
100 | const block = [];
101 | const matches: Record> = {};
102 | for (const quickInfo of ret.staticQuickInfos) {
103 | if (quickInfo.targetString !== symbol) continue;
104 | (matches[quickInfo.text] =
105 | matches[quickInfo.text] ?? new Set()).add(
106 | `${quickInfo.line + 1}:${quickInfo.character + 1}`,
107 | );
108 | }
109 | if (!Object.entries(matches).length)
110 | block.push(`/* No symbol named \`${symbol}\` found */`);
111 | for (const [info, locSet] of Object.entries(matches)) {
112 | block.push(`${info} /* ${[...locSet].join(', ')} */`);
113 | }
114 | blocks.push(block);
115 | }
116 |
117 | await sendWithMessageOwnership(
118 | msg,
119 | blocks.map(block => makeCodeBlock(block.join('\n'))).join(''),
120 | );
121 | }
122 |
123 | async function twoslashBlock(msg: Message, code: string, tsModule: TypeScript) {
124 | let ret: TwoSlashReturn;
125 | try {
126 | ret = twoslasher(redactNoErrorTruncation(code), 'ts', {
127 | tsModule,
128 | defaultCompilerOptions,
129 | defaultOptions: {
130 | noErrorValidation: true,
131 | noStaticSemanticInfo: false,
132 | },
133 | });
134 | } catch (e) {
135 | if (!(e instanceof Error)) throw e;
136 | return await sendWithMessageOwnership(msg, `:x: ${e.message}`);
137 | }
138 |
139 | const resultLines: string[] = [];
140 | const twoslashLines = ret.code.split('\n');
141 |
142 | twoslashLines.forEach((line, index) => {
143 | resultLines.push(line);
144 |
145 | const lineErrors = ret.errors.filter(e => e.line === index);
146 | const lineQueries = ret.queries.filter(e => e.line === index + 1);
147 |
148 | if (lineErrors.length + lineQueries.length === 0) return;
149 |
150 | if (lineErrors.length) {
151 | // Make sure all lines of errors start with a comment
152 | const errors = lineErrors.map(
153 | e => '// ' + e.renderedMessage.split('\n').join('\n// '),
154 | );
155 |
156 | // Points to errors, e.g. ' ^^^^ ^^^^^'
157 | const hats = lineErrors
158 | // only those with a valid span
159 | .filter(x => x.character != null && x.length != null)
160 | // map to [start, end (non-inclusive)]
161 | .map(
162 | error =>
163 | [
164 | error.character!,
165 | error.character! + error.length!,
166 | ] as const,
167 | )
168 | // sort by start, ascending
169 | .sort((a, b) => a[0] - b[0])
170 | // fix overlapping ranges
171 | .map((cur, i, a) => {
172 | let prev = a[i - 1];
173 | if (!prev) return cur;
174 | return [
175 | Math.max(prev[1], cur[0]),
176 | Math.max(prev[1], cur[1]),
177 | ] as const;
178 | })
179 | // remove empty ranges
180 | .filter(([start, end]) => start < end)
181 | // map each to hats
182 | .map(([start, end], i, a) => {
183 | let prevEnd = a[i - 1]?.[1] ?? 0;
184 | return (
185 | ' '.repeat(start - prevEnd) + '^'.repeat(end - start)
186 | );
187 | })
188 | // join the resulting strings
189 | .join('');
190 |
191 | if (hats.length > 0) {
192 | resultLines.push('//' + hats.slice(2));
193 | }
194 |
195 | resultLines.push(...errors);
196 | }
197 |
198 | // Inline queries for showing the LSP lookup for a token
199 | if (lineQueries.length) {
200 | let queryComment = '//';
201 | lineQueries.forEach(q => {
202 | const spaceBefore = q.offset - queryComment.length;
203 | queryComment += ' '.repeat(spaceBefore);
204 | queryComment += '^? - ';
205 | queryComment +=
206 | q.text?.replace(/\n/g, '\n//' + ' '.repeat(spaceBefore)) ||
207 | '';
208 | });
209 | resultLines.push(queryComment);
210 | }
211 | });
212 |
213 | const output = resultLines.join('\n');
214 | return sendWithMessageOwnership(msg, makeCodeBlock(output) + '\n');
215 | }
216 |
--------------------------------------------------------------------------------
/src/util/codeBlocks.ts:
--------------------------------------------------------------------------------
1 | import { Message, MessageType } from 'discord.js';
2 | import { decompressFromEncodedURIComponent } from 'lz-string';
3 | import { getReferencedMessage } from './getReferencedMessage';
4 |
5 | const CODEBLOCK_REGEX = /```(?:ts|typescript|js|javascript)?\n([\s\S]+)```/;
6 |
7 | const PLAYGROUND_REGEX =
8 | /(https?:\/\/(?:www\.)?(?:typescriptlang|staging-typescript)\.org\/(?:[a-z]{2,3}\/)?(?:play|dev\/bug-workbench)(?:\/index\.html)?\/?(\??(?:\w+=[^\s#&]*)?(?:\&\w+=[^\s#&]*)*)#code\/([\w\-%+_]+={0,4}))>?/;
9 |
10 | export type PlaygroundLinkMatch = {
11 | url: string;
12 | query: string;
13 | code: string;
14 | isWholeMatch: boolean;
15 | /* Is the url wrapped in < > ? */
16 | isEscaped: boolean;
17 | };
18 | export function matchPlaygroundLink(
19 | msg: string,
20 | ): PlaygroundLinkMatch | undefined {
21 | const match = msg.match(PLAYGROUND_REGEX);
22 | if (!match) return;
23 | const [possiblyEscapedUrl, url, query, code] = match;
24 | const isWholeMatch = msg === possiblyEscapedUrl;
25 | const isEscaped = possiblyEscapedUrl.length === url.length + 2;
26 | return { url, query, code, isWholeMatch, isEscaped };
27 | }
28 |
29 | export async function findCode(message: Message, ignoreLinks = false) {
30 | const codeInMessage = await findCodeInMessage(message, ignoreLinks);
31 | if (codeInMessage) return codeInMessage;
32 | const referencedMessage = await getReferencedMessage(message);
33 | if (referencedMessage) {
34 | const codeInReferencedMessage = await findCodeInMessage(
35 | referencedMessage,
36 | ignoreLinks,
37 | );
38 | if (codeInReferencedMessage) return codeInReferencedMessage;
39 | }
40 | const msgs = await message.channel.messages.fetch({ limit: 10 });
41 |
42 | for (const msg of msgs.values()) {
43 | const code = await findCodeInMessage(msg, ignoreLinks);
44 | if (code) return code;
45 | }
46 | }
47 |
48 | // Two possibilities:
49 | // 1: Normal code block annotated with ts from a non-bot
50 | // 2: Link to TS playground. This can be either from a bot or a normal user
51 | // since we shorten playground links on their own and delete the message.
52 | async function findCodeInMessage(msg: Message, ignoreLinks = false) {
53 | if (msg.type === MessageType.ThreadStarterMessage) {
54 | msg = await msg.fetchReference();
55 | }
56 | const { author, content, embeds } = msg;
57 | if (!author.bot) {
58 | const match = content.match(CODEBLOCK_REGEX);
59 | if (match && match[1].length) {
60 | return match[1];
61 | }
62 | }
63 |
64 | if (ignoreLinks) return;
65 |
66 | const codeSources = [content, ...embeds.map(({ url }) => url)];
67 |
68 | for (const code of codeSources) {
69 | const match = code && matchPlaygroundLink(code);
70 | if (match) {
71 | return decompressFromEncodedURIComponent(match.code);
72 | }
73 | }
74 | }
75 |
76 | const CODEBLOCK = '```';
77 | // 2048 is the most characters Discord allows in a message/embed
78 | const MAX_CODE_LENGTH = 2048 - `${CODEBLOCK}ts\n${CODEBLOCK}`.length;
79 |
80 | export function makeCodeBlock(code: string) {
81 | return `${CODEBLOCK}ts\n${truncate(
82 | escapeCode(code),
83 | MAX_CODE_LENGTH,
84 | )}${CODEBLOCK}`;
85 | }
86 |
87 | // Note: If str.length === cutoff, the string fits! No need to truncate.
88 | // (This is an easy off-by-one error to make)
89 | export function truncate(str: string, max: number) {
90 | return str.length <= max ? str : str.slice(0, max - 1) + '…';
91 | }
92 |
93 | // Custom escape function instead of using discord.js Util.escapeCodeBlock because this
94 | // produces better results with template literal types. Discord's markdown handling is pretty
95 | // bad. It doesn't properly handle escaping back ticks, so we instead insert zero width spaces
96 | // so that users cannot escape our code block.
97 | function escapeCode(code: string) {
98 | return code.replace(/`(?=`)/g, '`\u200B');
99 | }
100 |
--------------------------------------------------------------------------------
/src/util/customCommand.ts:
--------------------------------------------------------------------------------
1 | import { Message } from 'discord.js';
2 | import { Bot } from '../bot';
3 | import { prefixes } from '../env';
4 |
5 | export async function splitCustomCommand(bot: Bot, msg: Message) {
6 | const [commandPart, ...argParts] = msg.content.split(' ');
7 | const matchingPrefix = prefixes.find(x => msg.content.startsWith(x));
8 | if (!matchingPrefix) return;
9 | let command = commandPart.slice(matchingPrefix.length);
10 | if (bot.getByTrigger(command)) return;
11 | if (!command) return;
12 | return {
13 | command,
14 | args: argParts.join(' '),
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/src/util/getReferencedMessage.ts:
--------------------------------------------------------------------------------
1 | import { Message } from 'discord.js';
2 |
3 | export async function getReferencedMessage(msg: Message) {
4 | if (!msg.reference?.messageId) return null;
5 | const message = await msg.channel.messages.fetch(msg.reference.messageId);
6 | return message;
7 | }
8 |
--------------------------------------------------------------------------------
/src/util/getTypeScriptModule.ts:
--------------------------------------------------------------------------------
1 | import fetch from 'npm-registry-fetch';
2 | import { promises as fs } from 'fs';
3 | import tar from 'tar';
4 | import path from 'path';
5 | import os from 'os';
6 | import { once } from 'events';
7 |
8 | export type TypeScript = typeof import('typescript');
9 |
10 | const moduleMemo = new Map>();
11 |
12 | // Refresh package data after 1 hour
13 | const tsPackageDataTimeout = 1000 * 60 * 60;
14 |
15 | let tsPackageData:
16 | | {
17 | 'dist-tags': Record;
18 | versions: Record<
19 | string,
20 | {
21 | dist: {
22 | tarball: string;
23 | };
24 | }
25 | >;
26 | }
27 | | undefined;
28 |
29 | export async function getTypeScriptModule(
30 | version: string | null,
31 | ): Promise {
32 | console.log(`Downloading typescript@${version}`);
33 |
34 | version = await resolveVersion(version);
35 |
36 | if (!version) {
37 | console.log(`typescript@${version} does not exist`);
38 | return null;
39 | }
40 |
41 | const memoModule = moduleMemo.get(version);
42 | if (memoModule) return memoModule;
43 |
44 | const tsPromise = (async () => {
45 | const directory = await fs.mkdtemp(
46 | path.join(os.tmpdir(), `tsbot-typescript-${version}-`),
47 | );
48 | const tarballUrl = tsPackageData!.versions[version].dist.tarball;
49 | const tarballResponse = await fetch(tarballUrl);
50 |
51 | await once(
52 | tarballResponse.body.pipe(tar.extract({ cwd: directory })),
53 | 'finish',
54 | );
55 |
56 | const ts: TypeScript = require(path.join(directory, 'package'));
57 |
58 | return ts;
59 | })();
60 |
61 | moduleMemo.set(version, tsPromise);
62 |
63 | return await tsPromise;
64 | }
65 |
66 | async function resolveVersion(version: string | null): Promise {
67 | version ??= 'latest';
68 | version = version.toLowerCase();
69 | if (version === 'nightly') version = 'next';
70 | tsPackageData ??= await getPackageData();
71 | if (version in tsPackageData['dist-tags'])
72 | version = tsPackageData['dist-tags'][version];
73 | if (version in tsPackageData.versions) return version;
74 | return null;
75 | }
76 |
77 | async function getPackageData() {
78 | tsPackageData = (await fetch.json('/typescript')) as any;
79 | setTimeout(() => (tsPackageData = undefined), tsPackageDataTimeout);
80 | return tsPackageData!;
81 | }
82 |
--------------------------------------------------------------------------------
/src/util/limitedSizeMap.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A FIFO style limited size map, used to prevent memory from growing unboundedly when tracking messages.
3 | */
4 | export class LimitedSizeMap extends Map implements Map {
5 | private _maxSize: number;
6 |
7 | constructor(maxSize: number) {
8 | super();
9 | this._maxSize = maxSize;
10 | }
11 |
12 | set(key: K, val: V) {
13 | super.set(key, val);
14 |
15 | if (this.size > this._maxSize) {
16 | // Keys returns an iterable in insertion order, so this removes the oldest entry from the map.
17 | this.delete(this.keys().next().value);
18 | }
19 |
20 | return this;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/util/send.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Message,
3 | MessageCreateOptions,
4 | MessagePayload,
5 | PartialMessage,
6 | User,
7 | } from 'discord.js';
8 | import { LimitedSizeMap } from './limitedSizeMap';
9 |
10 | const messageToUserId = new LimitedSizeMap<
11 | string,
12 | { owner: string; onDelete?: () => void }
13 | >(1000);
14 |
15 | export const DELETE_EMOJI = '🗑️';
16 |
17 | export async function sendWithMessageOwnership(
18 | message: Message,
19 | toSend: string | MessagePayload | MessageCreateOptions,
20 | onDelete?: () => void,
21 | ) {
22 | const sent = await message.channel.send(toSend);
23 | await addMessageOwnership(sent, message.author, onDelete);
24 | }
25 |
26 | export async function addMessageOwnership(
27 | message: Message,
28 | user: User,
29 | onDelete?: () => void,
30 | ) {
31 | await message.react(DELETE_EMOJI);
32 |
33 | messageToUserId.set(message.id, { owner: user.id, onDelete });
34 | }
35 |
36 | export function getMessageOwner(message: Message | PartialMessage) {
37 | return messageToUserId.get(message.id)?.owner;
38 | }
39 |
40 | export function ownsBotMessage(
41 | message: Message | PartialMessage,
42 | userId: string,
43 | ) {
44 | return messageToUserId.get(message.id)?.owner === userId;
45 | }
46 |
47 | export function clearMessageOwnership(message: Message | PartialMessage) {
48 | messageToUserId.get(message.id)?.onDelete?.();
49 | messageToUserId.delete(message.id);
50 | }
51 |
--------------------------------------------------------------------------------
/src/util/sendPaginatedMessage.ts:
--------------------------------------------------------------------------------
1 | import {
2 | GuildMember,
3 | EmbedBuilder,
4 | MessageReaction,
5 | TextBasedChannel,
6 | User,
7 | } from 'discord.js';
8 |
9 | const emojis = {
10 | back: '◀',
11 | first: '⏮',
12 | last: '⏭',
13 | next: '▶',
14 | stop: '⏹',
15 | };
16 |
17 | export async function sendPaginatedMessage(
18 | embed: EmbedBuilder,
19 | pages: string[],
20 | member: GuildMember,
21 | channel: TextBasedChannel,
22 | timeout: number = 100000,
23 | ) {
24 | let curPage = 0;
25 | const message = await channel.send({
26 | embeds: [
27 | embed
28 | .setDescription(pages[curPage])
29 | .setFooter({ text: `Page ${curPage + 1} of ${pages.length}` }),
30 | ],
31 | });
32 | if (pages.length === 1) return;
33 |
34 | await message.react(emojis.first);
35 | await message.react(emojis.back);
36 | await message.react(emojis.stop);
37 | await message.react(emojis.next);
38 | await message.react(emojis.last);
39 |
40 | const collector = message.createReactionCollector({
41 | filter: (_reaction, user) =>
42 | user.id === member.id && user.id !== message.author.id,
43 | time: timeout,
44 | });
45 |
46 | collector.on('collect', async (reaction: MessageReaction, user: User) => {
47 | await reaction.users.remove(user);
48 |
49 | switch (reaction.emoji.toString()) {
50 | case emojis.first:
51 | curPage = 0;
52 | break;
53 | case emojis.last:
54 | curPage = pages.length - 1;
55 | break;
56 | case emojis.stop:
57 | await message.reactions.removeAll();
58 | break;
59 | case emojis.back:
60 | curPage--;
61 | if (curPage < 0) curPage = pages.length - 1;
62 | break;
63 | case emojis.next:
64 | curPage++;
65 | if (curPage > pages.length - 1) curPage = 0;
66 | break;
67 | }
68 |
69 | await message.edit({
70 | embeds: [
71 | embed.setDescription(pages[curPage]).setFooter({
72 | text: `Page ${curPage + 1} of ${pages.length}`,
73 | }),
74 | ],
75 | });
76 | });
77 |
78 | collector.on('end', () => {
79 | message.reactions.removeAll();
80 | });
81 | }
82 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "Node16",
5 | "lib": ["ES2020"],
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "types": ["discord.js", "dotenv-safe"],
9 |
10 | "strict": true,
11 | "esModuleInterop": true,
12 |
13 | "noUnusedParameters": true,
14 | "noUnusedLocals": true,
15 | "skipLibCheck": true,
16 |
17 | "experimentalDecorators": true,
18 | "emitDecoratorMetadata": true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@algolia/cache-browser-local-storage@4.14.2":
6 | version "4.14.2"
7 | resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.2.tgz#d5b1b90130ca87c6321de876e167df9ec6524936"
8 | integrity sha512-FRweBkK/ywO+GKYfAWbrepewQsPTIEirhi1BdykX9mxvBPtGNKccYAxvGdDCumU1jL4r3cayio4psfzKMejBlA==
9 | dependencies:
10 | "@algolia/cache-common" "4.14.2"
11 |
12 | "@algolia/cache-common@4.14.2":
13 | version "4.14.2"
14 | resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.14.2.tgz#b946b6103c922f0c06006fb6929163ed2c67d598"
15 | integrity sha512-SbvAlG9VqNanCErr44q6lEKD2qoK4XtFNx9Qn8FK26ePCI8I9yU7pYB+eM/cZdS9SzQCRJBbHUumVr4bsQ4uxg==
16 |
17 | "@algolia/cache-in-memory@4.14.2":
18 | version "4.14.2"
19 | resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.14.2.tgz#88e4a21474f9ac05331c2fa3ceb929684a395a24"
20 | integrity sha512-HrOukWoop9XB/VFojPv1R5SVXowgI56T9pmezd/djh2JnVN/vXswhXV51RKy4nCpqxyHt/aGFSq2qkDvj6KiuQ==
21 | dependencies:
22 | "@algolia/cache-common" "4.14.2"
23 |
24 | "@algolia/client-account@4.14.2":
25 | version "4.14.2"
26 | resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.14.2.tgz#b76ac1ba9ea71e8c3f77a1805b48350dc0728a16"
27 | integrity sha512-WHtriQqGyibbb/Rx71YY43T0cXqyelEU0lB2QMBRXvD2X0iyeGl4qMxocgEIcbHyK7uqE7hKgjT8aBrHqhgc1w==
28 | dependencies:
29 | "@algolia/client-common" "4.14.2"
30 | "@algolia/client-search" "4.14.2"
31 | "@algolia/transporter" "4.14.2"
32 |
33 | "@algolia/client-analytics@4.14.2":
34 | version "4.14.2"
35 | resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.14.2.tgz#ca04dcaf9a78ee5c92c5cb5e9c74cf031eb2f1fb"
36 | integrity sha512-yBvBv2mw+HX5a+aeR0dkvUbFZsiC4FKSnfqk9rrfX+QrlNOKEhCG0tJzjiOggRW4EcNqRmaTULIYvIzQVL2KYQ==
37 | dependencies:
38 | "@algolia/client-common" "4.14.2"
39 | "@algolia/client-search" "4.14.2"
40 | "@algolia/requester-common" "4.14.2"
41 | "@algolia/transporter" "4.14.2"
42 |
43 | "@algolia/client-common@4.14.2":
44 | version "4.14.2"
45 | resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.14.2.tgz#e1324e167ffa8af60f3e8bcd122110fd0bfd1300"
46 | integrity sha512-43o4fslNLcktgtDMVaT5XwlzsDPzlqvqesRi4MjQz2x4/Sxm7zYg5LRYFol1BIhG6EwxKvSUq8HcC/KxJu3J0Q==
47 | dependencies:
48 | "@algolia/requester-common" "4.14.2"
49 | "@algolia/transporter" "4.14.2"
50 |
51 | "@algolia/client-personalization@4.14.2":
52 | version "4.14.2"
53 | resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.14.2.tgz#656bbb6157a3dd1a4be7de65e457fda136c404ec"
54 | integrity sha512-ACCoLi0cL8CBZ1W/2juehSltrw2iqsQBnfiu/Rbl9W2yE6o2ZUb97+sqN/jBqYNQBS+o0ekTMKNkQjHHAcEXNw==
55 | dependencies:
56 | "@algolia/client-common" "4.14.2"
57 | "@algolia/requester-common" "4.14.2"
58 | "@algolia/transporter" "4.14.2"
59 |
60 | "@algolia/client-search@4.14.2":
61 | version "4.14.2"
62 | resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.14.2.tgz#357bdb7e640163f0e33bad231dfcc21f67dc2e92"
63 | integrity sha512-L5zScdOmcZ6NGiVbLKTvP02UbxZ0njd5Vq9nJAmPFtjffUSOGEp11BmD2oMJ5QvARgx2XbX4KzTTNS5ECYIMWw==
64 | dependencies:
65 | "@algolia/client-common" "4.14.2"
66 | "@algolia/requester-common" "4.14.2"
67 | "@algolia/transporter" "4.14.2"
68 |
69 | "@algolia/logger-common@4.14.2":
70 | version "4.14.2"
71 | resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.14.2.tgz#b74b3a92431f92665519d95942c246793ec390ee"
72 | integrity sha512-/JGlYvdV++IcMHBnVFsqEisTiOeEr6cUJtpjz8zc0A9c31JrtLm318Njc72p14Pnkw3A/5lHHh+QxpJ6WFTmsA==
73 |
74 | "@algolia/logger-console@4.14.2":
75 | version "4.14.2"
76 | resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.14.2.tgz#ec49cb47408f5811d4792598683923a800abce7b"
77 | integrity sha512-8S2PlpdshbkwlLCSAB5f8c91xyc84VM9Ar9EdfE9UmX+NrKNYnWR1maXXVDQQoto07G1Ol/tYFnFVhUZq0xV/g==
78 | dependencies:
79 | "@algolia/logger-common" "4.14.2"
80 |
81 | "@algolia/requester-browser-xhr@4.14.2":
82 | version "4.14.2"
83 | resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.2.tgz#a2cd4d9d8d90d53109cc7f3682dc6ebf20f798f2"
84 | integrity sha512-CEh//xYz/WfxHFh7pcMjQNWgpl4wFB85lUMRyVwaDPibNzQRVcV33YS+63fShFWc2+42YEipFGH2iPzlpszmDw==
85 | dependencies:
86 | "@algolia/requester-common" "4.14.2"
87 |
88 | "@algolia/requester-common@4.14.2":
89 | version "4.14.2"
90 | resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.14.2.tgz#bc4e9e5ee16c953c0ecacbfb334a33c30c28b1a1"
91 | integrity sha512-73YQsBOKa5fvVV3My7iZHu1sUqmjjfs9TteFWwPwDmnad7T0VTCopttcsM3OjLxZFtBnX61Xxl2T2gmG2O4ehg==
92 |
93 | "@algolia/requester-node-http@4.14.2":
94 | version "4.14.2"
95 | resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.14.2.tgz#7c1223a1785decaab1def64c83dade6bea45e115"
96 | integrity sha512-oDbb02kd1o5GTEld4pETlPZLY0e+gOSWjWMJHWTgDXbv9rm/o2cF7japO6Vj1ENnrqWvLBmW1OzV9g6FUFhFXg==
97 | dependencies:
98 | "@algolia/requester-common" "4.14.2"
99 |
100 | "@algolia/transporter@4.14.2":
101 | version "4.14.2"
102 | resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.14.2.tgz#77c069047fb1a4359ee6a51f51829508e44a1e3d"
103 | integrity sha512-t89dfQb2T9MFQHidjHcfhh6iGMNwvuKUvojAj+JsrHAGbuSy7yE4BylhLX6R0Q1xYRoC4Vvv+O5qIw/LdnQfsQ==
104 | dependencies:
105 | "@algolia/cache-common" "4.14.2"
106 | "@algolia/logger-common" "4.14.2"
107 | "@algolia/requester-common" "4.14.2"
108 |
109 | "@cspotcode/source-map-support@^0.8.0":
110 | version "0.8.1"
111 | resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
112 | integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
113 | dependencies:
114 | "@jridgewell/trace-mapping" "0.3.9"
115 |
116 | "@discordjs/builders@^1.3.0":
117 | version "1.3.0"
118 | resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.3.0.tgz#20d4e3fbcd734ce2468df10407c7c6df22c9f77e"
119 | integrity sha512-Pvca6Nw8Hp+n3N+Wp17xjygXmMvggbh5ywUsOYE2Et4xkwwVRwgzxDJiMUuYapPtnYt4w/8aKlf5khc8ipLvhg==
120 | dependencies:
121 | "@discordjs/util" "^0.1.0"
122 | "@sapphire/shapeshift" "^3.7.0"
123 | discord-api-types "^0.37.12"
124 | fast-deep-equal "^3.1.3"
125 | ts-mixer "^6.0.1"
126 | tslib "^2.4.0"
127 |
128 | "@discordjs/collection@^1.2.0":
129 | version "1.2.0"
130 | resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.2.0.tgz#5cad4bb47521c6f0abd175bf55c84528d1ac94f7"
131 | integrity sha512-VvrrtGb7vbfPHzbhGq9qZB5o8FOB+kfazrxdt0OtxzSkoBuw9dURMkCwWizZ00+rDpiK2HmLHBZX+y6JsG9khw==
132 |
133 | "@discordjs/rest@^1.3.0":
134 | version "1.3.0"
135 | resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.3.0.tgz#26dd146eeda0c19a3076b3872a00ac9f323763c8"
136 | integrity sha512-U6X5J+r/MxYpPTlHFuPxXEf92aKsBaD2teBC7sWkKILIr30O8c9+XshfL7KFBCavnAqS/qE+PF9fgRilO3N44g==
137 | dependencies:
138 | "@discordjs/collection" "^1.2.0"
139 | "@discordjs/util" "^0.1.0"
140 | "@sapphire/async-queue" "^1.5.0"
141 | "@sapphire/snowflake" "^3.2.2"
142 | discord-api-types "^0.37.12"
143 | file-type "^18.0.0"
144 | tslib "^2.4.0"
145 | undici "^5.11.0"
146 |
147 | "@discordjs/util@^0.1.0":
148 | version "0.1.0"
149 | resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-0.1.0.tgz#e42ca1bf407bc6d9adf252877d1b206e32ba369a"
150 | integrity sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==
151 |
152 | "@jridgewell/resolve-uri@^3.0.3":
153 | version "3.1.0"
154 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
155 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
156 |
157 | "@jridgewell/sourcemap-codec@^1.4.10":
158 | version "1.4.14"
159 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
160 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
161 |
162 | "@jridgewell/trace-mapping@0.3.9":
163 | version "0.3.9"
164 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
165 | integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
166 | dependencies:
167 | "@jridgewell/resolve-uri" "^3.0.3"
168 | "@jridgewell/sourcemap-codec" "^1.4.10"
169 |
170 | "@npmcli/fs@^3.1.0":
171 | version "3.1.0"
172 | resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e"
173 | integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==
174 | dependencies:
175 | semver "^7.3.5"
176 |
177 | "@sapphire/async-queue@^1.5.0":
178 | version "1.5.0"
179 | resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8"
180 | integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==
181 |
182 | "@sapphire/shapeshift@^3.7.0":
183 | version "3.7.0"
184 | resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.7.0.tgz#488cf06857be75826292dac451c13eeb0143602d"
185 | integrity sha512-A6vI1zJoxhjWo4grsxpBRBgk96SqSdjLX5WlzKp9H+bJbkM07mvwcbtbVAmUZHbi/OG3HLfiZ1rlw4BhH6tsBQ==
186 | dependencies:
187 | fast-deep-equal "^3.1.3"
188 | lodash.uniqwith "^4.5.0"
189 |
190 | "@sapphire/snowflake@^3.2.2":
191 | version "3.2.2"
192 | resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.2.2.tgz#faacdc1b5f7c43145a71eddba917de2b707ef780"
193 | integrity sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==
194 |
195 | "@sqltools/formatter@^1.2.2":
196 | version "1.2.5"
197 | resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12"
198 | integrity sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==
199 |
200 | "@tokenizer/token@^0.3.0":
201 | version "0.3.0"
202 | resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
203 | integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==
204 |
205 | "@tootallnate/once@2":
206 | version "2.0.0"
207 | resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
208 | integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
209 |
210 | "@tsconfig/node10@^1.0.7":
211 | version "1.0.9"
212 | resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
213 | integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
214 |
215 | "@tsconfig/node12@^1.0.7":
216 | version "1.0.11"
217 | resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
218 | integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
219 |
220 | "@tsconfig/node14@^1.0.0":
221 | version "1.0.3"
222 | resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
223 | integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
224 |
225 | "@tsconfig/node16@^1.0.2":
226 | version "1.0.3"
227 | resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
228 | integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
229 |
230 | "@types/dotenv-safe@8.1.2":
231 | version "8.1.2"
232 | resolved "https://registry.yarnpkg.com/@types/dotenv-safe/-/dotenv-safe-8.1.2.tgz#72f126969f445af5654efd3167deabc2cbc6c24e"
233 | integrity sha512-R/B/wIMda6lRE2P1H0vwSoJsV78IOkhccE4vIvmKZQNXOIjiU0QyJsUXwSotBxOPZFZ/oOnjCa3+kK5kVJwGyw==
234 | dependencies:
235 | dotenv "^8.2.0"
236 |
237 | "@types/lz-string@1.3.34":
238 | version "1.3.34"
239 | resolved "https://registry.yarnpkg.com/@types/lz-string/-/lz-string-1.3.34.tgz#69bfadde419314b4a374bf2c8e58659c035ed0a5"
240 | integrity sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow==
241 |
242 | "@types/minimatch@^3.0.3":
243 | version "3.0.5"
244 | resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
245 | integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
246 |
247 | "@types/node-fetch@*":
248 | version "2.6.2"
249 | resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
250 | integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==
251 | dependencies:
252 | "@types/node" "*"
253 | form-data "^3.0.0"
254 |
255 | "@types/node@*":
256 | version "18.11.9"
257 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4"
258 | integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==
259 |
260 | "@types/node@16.18.3":
261 | version "16.18.3"
262 | resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc"
263 | integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==
264 |
265 | "@types/npm-package-arg@*":
266 | version "6.1.1"
267 | resolved "https://registry.yarnpkg.com/@types/npm-package-arg/-/npm-package-arg-6.1.1.tgz#9e2d8adc04d39824a3d9f36f738010a3f7da3c1a"
268 | integrity sha512-452/1Kp9IdM/oR10AyqAgZOxUt7eLbm+EMJ194L6oarMYdZNiFIFAOJ7IIr0OrZXTySgfHjJezh2oiyk2kc3ag==
269 |
270 | "@types/npm-registry-fetch@8.0.4":
271 | version "8.0.4"
272 | resolved "https://registry.yarnpkg.com/@types/npm-registry-fetch/-/npm-registry-fetch-8.0.4.tgz#77b2737cde22314ccda1dfdb9568fd7769e95b90"
273 | integrity sha512-R9yEj6+NDmXLpKNS19cIaMyaHfV0aHjy/1qbo8K9jiHyjyaYg0CEmuOV/L0Q91DZDi3SuxlYY+2XYwh9TbB+eQ==
274 | dependencies:
275 | "@types/node" "*"
276 | "@types/node-fetch" "*"
277 | "@types/npm-package-arg" "*"
278 | "@types/npmlog" "*"
279 | "@types/ssri" "*"
280 |
281 | "@types/npmlog@*":
282 | version "4.1.4"
283 | resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.4.tgz#30eb872153c7ead3e8688c476054ddca004115f6"
284 | integrity sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==
285 |
286 | "@types/prettier@2.7.1":
287 | version "2.7.1"
288 | resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e"
289 | integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==
290 |
291 | "@types/ssri@*":
292 | version "7.1.1"
293 | resolved "https://registry.yarnpkg.com/@types/ssri/-/ssri-7.1.1.tgz#2a2c94abf0d3a8c3b07bb4ff08142dd571407bb5"
294 | integrity sha512-DPP/jkDaqGiyU75MyMURxLWyYLwKSjnAuGe9ZCsLp9QZOpXmDfuevk769F0BS86TmRuD5krnp06qw9nSoNO+0g==
295 | dependencies:
296 | "@types/node" "*"
297 |
298 | "@types/strip-bom@^3.0.0":
299 | version "3.0.0"
300 | resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2"
301 | integrity sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==
302 |
303 | "@types/strip-json-comments@0.0.30":
304 | version "0.0.30"
305 | resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
306 | integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
307 |
308 | "@types/tar@6.1.3":
309 | version "6.1.3"
310 | resolved "https://registry.yarnpkg.com/@types/tar/-/tar-6.1.3.tgz#46a2ce7617950c4852dfd7e9cd41aa8161b9d750"
311 | integrity sha512-YzDOr5kdAeqS8dcO6NTTHTMJ44MUCBDoLEIyPtwEn7PssKqUYL49R1iCVJPeiPzPlKi6DbH33eZkpeJ27e4vHg==
312 | dependencies:
313 | "@types/node" "*"
314 | minipass "^3.3.5"
315 |
316 | "@types/ws@8.5.3", "@types/ws@^8.5.3":
317 | version "8.5.3"
318 | resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
319 | integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
320 | dependencies:
321 | "@types/node" "*"
322 |
323 | "@typescript/twoslash@^3.2.1":
324 | version "3.2.1"
325 | resolved "https://registry.yarnpkg.com/@typescript/twoslash/-/twoslash-3.2.1.tgz#7ba5fb2a230925ccd773967b02daf38efc1a3377"
326 | integrity sha512-tS4gLwOe1WCDspqBXhQCb2ESUqzEd1tOkmKpiZ1O+W1x+9l+9njETuXFkLErtH9is/uD1GSvClDjk/tEOJktjQ==
327 | dependencies:
328 | "@typescript/vfs" "1.4.0"
329 | debug "^4.1.1"
330 | lz-string "^1.4.4"
331 |
332 | "@typescript/vfs@1.4.0":
333 | version "1.4.0"
334 | resolved "https://registry.yarnpkg.com/@typescript/vfs/-/vfs-1.4.0.tgz#2d22985c7666c9d4ce26eb025405e6f156aa32b0"
335 | integrity sha512-Pood7yv5YWMIX+yCHo176OnF8WUlKGImFG7XlsuH14Zb1YN5+dYD3uUtS7lqZtsH7tAveNUi2NzdpQCN0yRbaw==
336 | dependencies:
337 | debug "^4.1.1"
338 |
339 | acorn-walk@^8.1.1:
340 | version "8.2.0"
341 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
342 | integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
343 |
344 | acorn@^8.4.1:
345 | version "8.8.1"
346 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
347 | integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
348 |
349 | agent-base@6, agent-base@^6.0.2:
350 | version "6.0.2"
351 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
352 | integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
353 | dependencies:
354 | debug "4"
355 |
356 | agentkeepalive@^4.2.1:
357 | version "4.2.1"
358 | resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717"
359 | integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==
360 | dependencies:
361 | debug "^4.1.0"
362 | depd "^1.1.2"
363 | humanize-ms "^1.2.1"
364 |
365 | aggregate-error@^3.0.0:
366 | version "3.1.0"
367 | resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
368 | integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
369 | dependencies:
370 | clean-stack "^2.0.0"
371 | indent-string "^4.0.0"
372 |
373 | algoliasearch@^4.14.2:
374 | version "4.14.2"
375 | resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.14.2.tgz#63f142583bfc3a9bd3cd4a1b098bf6fe58e56f6c"
376 | integrity sha512-ngbEQonGEmf8dyEh5f+uOIihv4176dgbuOZspiuhmTTBRBuzWu3KCGHre6uHj5YyuC7pNvQGzB6ZNJyZi0z+Sg==
377 | dependencies:
378 | "@algolia/cache-browser-local-storage" "4.14.2"
379 | "@algolia/cache-common" "4.14.2"
380 | "@algolia/cache-in-memory" "4.14.2"
381 | "@algolia/client-account" "4.14.2"
382 | "@algolia/client-analytics" "4.14.2"
383 | "@algolia/client-common" "4.14.2"
384 | "@algolia/client-personalization" "4.14.2"
385 | "@algolia/client-search" "4.14.2"
386 | "@algolia/logger-common" "4.14.2"
387 | "@algolia/logger-console" "4.14.2"
388 | "@algolia/requester-browser-xhr" "4.14.2"
389 | "@algolia/requester-common" "4.14.2"
390 | "@algolia/requester-node-http" "4.14.2"
391 | "@algolia/transporter" "4.14.2"
392 |
393 | ansi-regex@^5.0.1:
394 | version "5.0.1"
395 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
396 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
397 |
398 | ansi-styles@^4.0.0, ansi-styles@^4.1.0:
399 | version "4.3.0"
400 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
401 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
402 | dependencies:
403 | color-convert "^2.0.1"
404 |
405 | any-promise@^1.0.0:
406 | version "1.3.0"
407 | resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
408 | integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
409 |
410 | anymatch@~3.1.2:
411 | version "3.1.2"
412 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
413 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
414 | dependencies:
415 | normalize-path "^3.0.0"
416 | picomatch "^2.0.4"
417 |
418 | app-root-path@^3.0.0:
419 | version "3.1.0"
420 | resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.1.0.tgz#5971a2fc12ba170369a7a1ef018c71e6e47c2e86"
421 | integrity sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==
422 |
423 | arg@^4.1.0:
424 | version "4.1.3"
425 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
426 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
427 |
428 | argparse@^2.0.1:
429 | version "2.0.1"
430 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
431 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
432 |
433 | array-differ@^3.0.0:
434 | version "3.0.0"
435 | resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b"
436 | integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==
437 |
438 | array-union@^2.1.0:
439 | version "2.1.0"
440 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
441 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
442 |
443 | arrify@^2.0.1:
444 | version "2.0.1"
445 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
446 | integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
447 |
448 | asynckit@^0.4.0:
449 | version "0.4.0"
450 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
451 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
452 |
453 | balanced-match@^1.0.0:
454 | version "1.0.2"
455 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
456 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
457 |
458 | base64-js@^1.3.1:
459 | version "1.5.1"
460 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
461 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
462 |
463 | binary-extensions@^2.0.0:
464 | version "2.2.0"
465 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
466 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
467 |
468 | brace-expansion@^1.1.7:
469 | version "1.1.11"
470 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
471 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
472 | dependencies:
473 | balanced-match "^1.0.0"
474 | concat-map "0.0.1"
475 |
476 | brace-expansion@^2.0.1:
477 | version "2.0.1"
478 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
479 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
480 | dependencies:
481 | balanced-match "^1.0.0"
482 |
483 | braces@~3.0.2:
484 | version "3.0.2"
485 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
486 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
487 | dependencies:
488 | fill-range "^7.0.1"
489 |
490 | buffer-from@^1.0.0:
491 | version "1.1.2"
492 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
493 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
494 |
495 | buffer-writer@2.0.0:
496 | version "2.0.0"
497 | resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
498 | integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
499 |
500 | buffer@^6.0.3:
501 | version "6.0.3"
502 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
503 | integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
504 | dependencies:
505 | base64-js "^1.3.1"
506 | ieee754 "^1.2.1"
507 |
508 | builtins@^5.0.0:
509 | version "5.0.1"
510 | resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9"
511 | integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==
512 | dependencies:
513 | semver "^7.0.0"
514 |
515 | busboy@^1.6.0:
516 | version "1.6.0"
517 | resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
518 | integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
519 | dependencies:
520 | streamsearch "^1.1.0"
521 |
522 | cacache@^17.0.0:
523 | version "17.0.2"
524 | resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.0.2.tgz#ff2bd029bf45099b3fe711f56fbf138b846c8d6d"
525 | integrity sha512-rYUs2x4OjSgCQND7nTrh21AHIBFgd7s/ctAYvU3a8u+nK+R5YaX/SFPDYz4Azz7SGL6+6L9ZZWI4Kawpb7grzQ==
526 | dependencies:
527 | "@npmcli/fs" "^3.1.0"
528 | fs-minipass "^2.1.0"
529 | glob "^8.0.1"
530 | lru-cache "^7.7.1"
531 | minipass "^3.1.6"
532 | minipass-collect "^1.0.2"
533 | minipass-flush "^1.0.5"
534 | minipass-pipeline "^1.2.4"
535 | p-map "^4.0.0"
536 | promise-inflight "^1.0.1"
537 | ssri "^10.0.0"
538 | tar "^6.1.11"
539 | unique-filename "^3.0.0"
540 |
541 | chalk@^3.0.0:
542 | version "3.0.0"
543 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
544 | integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
545 | dependencies:
546 | ansi-styles "^4.1.0"
547 | supports-color "^7.1.0"
548 |
549 | chalk@^4.0.0, chalk@^4.1.0:
550 | version "4.1.2"
551 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
552 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
553 | dependencies:
554 | ansi-styles "^4.1.0"
555 | supports-color "^7.1.0"
556 |
557 | chokidar@^3.5.1:
558 | version "3.5.3"
559 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
560 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
561 | dependencies:
562 | anymatch "~3.1.2"
563 | braces "~3.0.2"
564 | glob-parent "~5.1.2"
565 | is-binary-path "~2.1.0"
566 | is-glob "~4.0.1"
567 | normalize-path "~3.0.0"
568 | readdirp "~3.6.0"
569 | optionalDependencies:
570 | fsevents "~2.3.2"
571 |
572 | chownr@^2.0.0:
573 | version "2.0.0"
574 | resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
575 | integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
576 |
577 | clean-stack@^2.0.0:
578 | version "2.2.0"
579 | resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
580 | integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
581 |
582 | cli-highlight@^2.1.11:
583 | version "2.1.11"
584 | resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf"
585 | integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==
586 | dependencies:
587 | chalk "^4.0.0"
588 | highlight.js "^10.7.1"
589 | mz "^2.4.0"
590 | parse5 "^5.1.1"
591 | parse5-htmlparser2-tree-adapter "^6.0.0"
592 | yargs "^16.0.0"
593 |
594 | cliui@^7.0.2:
595 | version "7.0.4"
596 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
597 | integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
598 | dependencies:
599 | string-width "^4.2.0"
600 | strip-ansi "^6.0.0"
601 | wrap-ansi "^7.0.0"
602 |
603 | cliui@^8.0.1:
604 | version "8.0.1"
605 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
606 | integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
607 | dependencies:
608 | string-width "^4.2.0"
609 | strip-ansi "^6.0.1"
610 | wrap-ansi "^7.0.0"
611 |
612 | color-convert@^2.0.1:
613 | version "2.0.1"
614 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
615 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
616 | dependencies:
617 | color-name "~1.1.4"
618 |
619 | color-name@~1.1.4:
620 | version "1.1.4"
621 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
622 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
623 |
624 | combined-stream@^1.0.8:
625 | version "1.0.8"
626 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
627 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
628 | dependencies:
629 | delayed-stream "~1.0.0"
630 |
631 | concat-map@0.0.1:
632 | version "0.0.1"
633 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
634 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
635 |
636 | create-require@^1.1.0:
637 | version "1.1.1"
638 | resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
639 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
640 |
641 | cross-spawn@^7.0.0:
642 | version "7.0.3"
643 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
644 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
645 | dependencies:
646 | path-key "^3.1.0"
647 | shebang-command "^2.0.0"
648 | which "^2.0.1"
649 |
650 | date-fns@^2.28.0:
651 | version "2.29.3"
652 | resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
653 | integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
654 |
655 | debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3:
656 | version "4.3.4"
657 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
658 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
659 | dependencies:
660 | ms "2.1.2"
661 |
662 | delayed-stream@~1.0.0:
663 | version "1.0.0"
664 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
665 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
666 |
667 | depd@^1.1.2:
668 | version "1.1.2"
669 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
670 | integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
671 |
672 | diff@^4.0.1:
673 | version "4.0.2"
674 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
675 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
676 |
677 | discord-api-types@^0.37.12:
678 | version "0.37.18"
679 | resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.18.tgz#1f0ca95cea4b2380ba77623a62b1d285f1237d7a"
680 | integrity sha512-mJ+9C8gmG5csssVZPH06Y8IGiJykljFyZc6n6F+T3vKo6yNBI5TtLIbwt6t9hJzsR5f1ITzRZ6cuPrTvRCUxqA==
681 |
682 | discord.js@^14.6.0:
683 | version "14.6.0"
684 | resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.6.0.tgz#896f7540d4c6f190dffe91d2f3e9a9f7cbc8bd26"
685 | integrity sha512-On1K7xpJZRe0KsziIaDih2ksYPhgxym/ZqV45i1f3yig4vUotikqs7qp5oXiTzQ/UTiNRCixUWFTh7vA1YBCqw==
686 | dependencies:
687 | "@discordjs/builders" "^1.3.0"
688 | "@discordjs/collection" "^1.2.0"
689 | "@discordjs/rest" "^1.3.0"
690 | "@discordjs/util" "^0.1.0"
691 | "@sapphire/snowflake" "^3.2.2"
692 | "@types/ws" "^8.5.3"
693 | discord-api-types "^0.37.12"
694 | fast-deep-equal "^3.1.3"
695 | lodash.snakecase "^4.1.1"
696 | tslib "^2.4.0"
697 | undici "^5.11.0"
698 | ws "^8.9.0"
699 |
700 | dotenv-safe@^8.2.0:
701 | version "8.2.0"
702 | resolved "https://registry.yarnpkg.com/dotenv-safe/-/dotenv-safe-8.2.0.tgz#8d548c7318a62c09a66c4dc8c31864cc007c78ba"
703 | integrity sha512-uWwWWdUQkSs5a3mySDB22UtNwyEYi0JtEQu+vDzIqr9OjbDdC2Ip13PnSpi/fctqlYmzkxCeabiyCAOROuAIaA==
704 | dependencies:
705 | dotenv "^8.2.0"
706 |
707 | dotenv@^16.0.0:
708 | version "16.0.3"
709 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07"
710 | integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==
711 |
712 | dotenv@^8.2.0:
713 | version "8.6.0"
714 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
715 | integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
716 |
717 | dynamic-dedupe@^0.3.0:
718 | version "0.3.0"
719 | resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1"
720 | integrity sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==
721 | dependencies:
722 | xtend "^4.0.0"
723 |
724 | emoji-regex@^8.0.0:
725 | version "8.0.0"
726 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
727 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
728 |
729 | encoding@^0.1.13:
730 | version "0.1.13"
731 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
732 | integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
733 | dependencies:
734 | iconv-lite "^0.6.2"
735 |
736 | end-of-stream@^1.1.0:
737 | version "1.4.4"
738 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
739 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
740 | dependencies:
741 | once "^1.4.0"
742 |
743 | err-code@^2.0.2:
744 | version "2.0.3"
745 | resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
746 | integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
747 |
748 | escalade@^3.1.1:
749 | version "3.1.1"
750 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
751 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
752 |
753 | execa@^4.0.0:
754 | version "4.1.0"
755 | resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
756 | integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
757 | dependencies:
758 | cross-spawn "^7.0.0"
759 | get-stream "^5.0.0"
760 | human-signals "^1.1.1"
761 | is-stream "^2.0.0"
762 | merge-stream "^2.0.0"
763 | npm-run-path "^4.0.0"
764 | onetime "^5.1.0"
765 | signal-exit "^3.0.2"
766 | strip-final-newline "^2.0.0"
767 |
768 | fast-deep-equal@^3.1.3:
769 | version "3.1.3"
770 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
771 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
772 |
773 | file-type@^18.0.0:
774 | version "18.0.0"
775 | resolved "https://registry.yarnpkg.com/file-type/-/file-type-18.0.0.tgz#7a39378f8657ddc02807a0c62cb77cb4dc318197"
776 | integrity sha512-jjMwFpnW8PKofLE/4ohlhqwDk5k0NC6iy0UHAJFKoY1fQeGMN0GDdLgHQrvCbSpMwbqzoCZhRI5dETCZna5qVA==
777 | dependencies:
778 | readable-web-to-node-stream "^3.0.2"
779 | strtok3 "^7.0.0"
780 | token-types "^5.0.1"
781 |
782 | fill-range@^7.0.1:
783 | version "7.0.1"
784 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
785 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
786 | dependencies:
787 | to-regex-range "^5.0.1"
788 |
789 | find-up@^4.1.0:
790 | version "4.1.0"
791 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
792 | integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
793 | dependencies:
794 | locate-path "^5.0.0"
795 | path-exists "^4.0.0"
796 |
797 | form-data@^3.0.0:
798 | version "3.0.1"
799 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
800 | integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
801 | dependencies:
802 | asynckit "^0.4.0"
803 | combined-stream "^1.0.8"
804 | mime-types "^2.1.12"
805 |
806 | fs-minipass@^2.0.0, fs-minipass@^2.1.0:
807 | version "2.1.0"
808 | resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
809 | integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
810 | dependencies:
811 | minipass "^3.0.0"
812 |
813 | fs.realpath@^1.0.0:
814 | version "1.0.0"
815 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
816 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
817 |
818 | fsevents@~2.3.2:
819 | version "2.3.2"
820 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
821 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
822 |
823 | function-bind@^1.1.1:
824 | version "1.1.1"
825 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
826 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
827 |
828 | get-caller-file@^2.0.5:
829 | version "2.0.5"
830 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
831 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
832 |
833 | get-stream@^5.0.0:
834 | version "5.2.0"
835 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
836 | integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
837 | dependencies:
838 | pump "^3.0.0"
839 |
840 | glob-parent@~5.1.2:
841 | version "5.1.2"
842 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
843 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
844 | dependencies:
845 | is-glob "^4.0.1"
846 |
847 | glob@^7.1.3, glob@^7.2.0:
848 | version "7.2.3"
849 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
850 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
851 | dependencies:
852 | fs.realpath "^1.0.0"
853 | inflight "^1.0.4"
854 | inherits "2"
855 | minimatch "^3.1.1"
856 | once "^1.3.0"
857 | path-is-absolute "^1.0.0"
858 |
859 | glob@^8.0.1:
860 | version "8.0.3"
861 | resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e"
862 | integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==
863 | dependencies:
864 | fs.realpath "^1.0.0"
865 | inflight "^1.0.4"
866 | inherits "2"
867 | minimatch "^5.0.1"
868 | once "^1.3.0"
869 |
870 | has-flag@^4.0.0:
871 | version "4.0.0"
872 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
873 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
874 |
875 | has@^1.0.3:
876 | version "1.0.3"
877 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
878 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
879 | dependencies:
880 | function-bind "^1.1.1"
881 |
882 | highlight.js@^10.7.1:
883 | version "10.7.3"
884 | resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
885 | integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
886 |
887 | hosted-git-info@^6.0.0:
888 | version "6.1.1"
889 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58"
890 | integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==
891 | dependencies:
892 | lru-cache "^7.5.1"
893 |
894 | html-entities@^2.3.3:
895 | version "2.3.3"
896 | resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46"
897 | integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==
898 |
899 | http-cache-semantics@^4.1.0:
900 | version "4.1.0"
901 | resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
902 | integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
903 |
904 | http-proxy-agent@^5.0.0:
905 | version "5.0.0"
906 | resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
907 | integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
908 | dependencies:
909 | "@tootallnate/once" "2"
910 | agent-base "6"
911 | debug "4"
912 |
913 | https-proxy-agent@^5.0.0:
914 | version "5.0.1"
915 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
916 | integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
917 | dependencies:
918 | agent-base "6"
919 | debug "4"
920 |
921 | human-signals@^1.1.1:
922 | version "1.1.1"
923 | resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
924 | integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
925 |
926 | humanize-ms@^1.2.1:
927 | version "1.2.1"
928 | resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
929 | integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
930 | dependencies:
931 | ms "^2.0.0"
932 |
933 | husky@^8.0.2:
934 | version "8.0.2"
935 | resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.2.tgz#5816a60db02650f1f22c8b69b928fd6bcd77a236"
936 | integrity sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg==
937 |
938 | iconv-lite@^0.6.2:
939 | version "0.6.3"
940 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
941 | integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
942 | dependencies:
943 | safer-buffer ">= 2.1.2 < 3.0.0"
944 |
945 | ieee754@^1.2.1:
946 | version "1.2.1"
947 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
948 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
949 |
950 | ignore@^5.1.4:
951 | version "5.2.0"
952 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
953 | integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
954 |
955 | imurmurhash@^0.1.4:
956 | version "0.1.4"
957 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
958 | integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
959 |
960 | indent-string@^4.0.0:
961 | version "4.0.0"
962 | resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
963 | integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
964 |
965 | inflight@^1.0.4:
966 | version "1.0.6"
967 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
968 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
969 | dependencies:
970 | once "^1.3.0"
971 | wrappy "1"
972 |
973 | inherits@2, inherits@^2.0.1, inherits@^2.0.3:
974 | version "2.0.4"
975 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
976 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
977 |
978 | ip@^2.0.0:
979 | version "2.0.0"
980 | resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
981 | integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
982 |
983 | is-binary-path@~2.1.0:
984 | version "2.1.0"
985 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
986 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
987 | dependencies:
988 | binary-extensions "^2.0.0"
989 |
990 | is-core-module@^2.9.0:
991 | version "2.11.0"
992 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144"
993 | integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==
994 | dependencies:
995 | has "^1.0.3"
996 |
997 | is-extglob@^2.1.1:
998 | version "2.1.1"
999 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
1000 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
1001 |
1002 | is-fullwidth-code-point@^3.0.0:
1003 | version "3.0.0"
1004 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
1005 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
1006 |
1007 | is-glob@^4.0.1, is-glob@~4.0.1:
1008 | version "4.0.3"
1009 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
1010 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
1011 | dependencies:
1012 | is-extglob "^2.1.1"
1013 |
1014 | is-lambda@^1.0.1:
1015 | version "1.0.1"
1016 | resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
1017 | integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==
1018 |
1019 | is-number@^7.0.0:
1020 | version "7.0.0"
1021 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
1022 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
1023 |
1024 | is-stream@^2.0.0:
1025 | version "2.0.1"
1026 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
1027 | integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
1028 |
1029 | isexe@^2.0.0:
1030 | version "2.0.0"
1031 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
1032 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
1033 |
1034 | js-yaml@^4.1.0:
1035 | version "4.1.0"
1036 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
1037 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
1038 | dependencies:
1039 | argparse "^2.0.1"
1040 |
1041 | jsonparse@^1.3.1:
1042 | version "1.3.1"
1043 | resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
1044 | integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
1045 |
1046 | locate-path@^5.0.0:
1047 | version "5.0.0"
1048 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
1049 | integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
1050 | dependencies:
1051 | p-locate "^4.1.0"
1052 |
1053 | lodash.snakecase@^4.1.1:
1054 | version "4.1.1"
1055 | resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
1056 | integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
1057 |
1058 | lodash.uniqwith@^4.5.0:
1059 | version "4.5.0"
1060 | resolved "https://registry.yarnpkg.com/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz#7a0cbf65f43b5928625a9d4d0dc54b18cadc7ef3"
1061 | integrity sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==
1062 |
1063 | lru-cache@^6.0.0:
1064 | version "6.0.0"
1065 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
1066 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
1067 | dependencies:
1068 | yallist "^4.0.0"
1069 |
1070 | lru-cache@^7.5.1, lru-cache@^7.7.1:
1071 | version "7.14.1"
1072 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.1.tgz#8da8d2f5f59827edb388e63e459ac23d6d408fea"
1073 | integrity sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==
1074 |
1075 | lz-string@^1.4.4:
1076 | version "1.4.4"
1077 | resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
1078 | integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==
1079 |
1080 | make-error@^1.1.1:
1081 | version "1.3.6"
1082 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
1083 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
1084 |
1085 | make-fetch-happen@^11.0.0:
1086 | version "11.0.1"
1087 | resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.0.1.tgz#b3c51663d018d9e11d57fdd4393a4c5a1a7d56eb"
1088 | integrity sha512-clv3IblugXn2CDUmqFhNzii3rjKa46u5wNeivc+QlLXkGI5FjLX3rGboo+y2kwf1pd8W0iDiC384cemeDtw9kw==
1089 | dependencies:
1090 | agentkeepalive "^4.2.1"
1091 | cacache "^17.0.0"
1092 | http-cache-semantics "^4.1.0"
1093 | http-proxy-agent "^5.0.0"
1094 | https-proxy-agent "^5.0.0"
1095 | is-lambda "^1.0.1"
1096 | lru-cache "^7.7.1"
1097 | minipass "^3.1.6"
1098 | minipass-collect "^1.0.2"
1099 | minipass-fetch "^3.0.0"
1100 | minipass-flush "^1.0.5"
1101 | minipass-pipeline "^1.2.4"
1102 | negotiator "^0.6.3"
1103 | promise-retry "^2.0.1"
1104 | socks-proxy-agent "^7.0.0"
1105 | ssri "^10.0.0"
1106 |
1107 | merge-stream@^2.0.0:
1108 | version "2.0.0"
1109 | resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
1110 | integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
1111 |
1112 | mime-db@1.52.0:
1113 | version "1.52.0"
1114 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
1115 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
1116 |
1117 | mime-types@^2.1.12:
1118 | version "2.1.35"
1119 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
1120 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
1121 | dependencies:
1122 | mime-db "1.52.0"
1123 |
1124 | mimic-fn@^2.1.0:
1125 | version "2.1.0"
1126 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
1127 | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
1128 |
1129 | minimatch@^3.0.4, minimatch@^3.1.1:
1130 | version "3.1.2"
1131 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
1132 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
1133 | dependencies:
1134 | brace-expansion "^1.1.7"
1135 |
1136 | minimatch@^5.0.1:
1137 | version "5.1.0"
1138 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
1139 | integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
1140 | dependencies:
1141 | brace-expansion "^2.0.1"
1142 |
1143 | minimist@^1.2.6:
1144 | version "1.2.7"
1145 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
1146 | integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
1147 |
1148 | minipass-collect@^1.0.2:
1149 | version "1.0.2"
1150 | resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
1151 | integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==
1152 | dependencies:
1153 | minipass "^3.0.0"
1154 |
1155 | minipass-fetch@^3.0.0:
1156 | version "3.0.0"
1157 | resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.0.tgz#02481219ddbd3d30eb0e354016f680b10c6f2bcb"
1158 | integrity sha512-NSx3k5gR4Q5Ts2poCM/19d45VwhVLBtJZ6ypYcthj2BwmDx/e7lW8Aadnyt3edd2W0ecb+b0o7FYLRYE2AGcQg==
1159 | dependencies:
1160 | minipass "^3.1.6"
1161 | minipass-sized "^1.0.3"
1162 | minizlib "^2.1.2"
1163 | optionalDependencies:
1164 | encoding "^0.1.13"
1165 |
1166 | minipass-flush@^1.0.5:
1167 | version "1.0.5"
1168 | resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373"
1169 | integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
1170 | dependencies:
1171 | minipass "^3.0.0"
1172 |
1173 | minipass-json-stream@^1.0.1:
1174 | version "1.0.1"
1175 | resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7"
1176 | integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==
1177 | dependencies:
1178 | jsonparse "^1.3.1"
1179 | minipass "^3.0.0"
1180 |
1181 | minipass-pipeline@^1.2.4:
1182 | version "1.2.4"
1183 | resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c"
1184 | integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
1185 | dependencies:
1186 | minipass "^3.0.0"
1187 |
1188 | minipass-sized@^1.0.3:
1189 | version "1.0.3"
1190 | resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70"
1191 | integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==
1192 | dependencies:
1193 | minipass "^3.0.0"
1194 |
1195 | minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6:
1196 | version "3.3.4"
1197 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae"
1198 | integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==
1199 | dependencies:
1200 | yallist "^4.0.0"
1201 |
1202 | minipass@^3.3.5:
1203 | version "3.3.5"
1204 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.5.tgz#6da7e53a48db8a856eeb9153d85b230a2119e819"
1205 | integrity sha512-rQ/p+KfKBkeNwo04U15i+hOwoVBVmekmm/HcfTkTN2t9pbQKCMm4eN5gFeqgrrSp/kH/7BYYhTIHOxGqzbBPaA==
1206 | dependencies:
1207 | yallist "^4.0.0"
1208 |
1209 | minizlib@^2.1.1, minizlib@^2.1.2:
1210 | version "2.1.2"
1211 | resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
1212 | integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
1213 | dependencies:
1214 | minipass "^3.0.0"
1215 | yallist "^4.0.0"
1216 |
1217 | mkdirp@^1.0.3, mkdirp@^1.0.4:
1218 | version "1.0.4"
1219 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
1220 | integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
1221 |
1222 | mri@^1.1.5:
1223 | version "1.2.0"
1224 | resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
1225 | integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
1226 |
1227 | ms@2.1.2:
1228 | version "2.1.2"
1229 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
1230 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
1231 |
1232 | ms@^2.0.0:
1233 | version "2.1.3"
1234 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
1235 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
1236 |
1237 | multimatch@^4.0.0:
1238 | version "4.0.0"
1239 | resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3"
1240 | integrity sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==
1241 | dependencies:
1242 | "@types/minimatch" "^3.0.3"
1243 | array-differ "^3.0.0"
1244 | array-union "^2.1.0"
1245 | arrify "^2.0.1"
1246 | minimatch "^3.0.4"
1247 |
1248 | mz@^2.4.0:
1249 | version "2.7.0"
1250 | resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
1251 | integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
1252 | dependencies:
1253 | any-promise "^1.0.0"
1254 | object-assign "^4.0.1"
1255 | thenify-all "^1.0.0"
1256 |
1257 | negotiator@^0.6.3:
1258 | version "0.6.3"
1259 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
1260 | integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
1261 |
1262 | normalize-path@^3.0.0, normalize-path@~3.0.0:
1263 | version "3.0.0"
1264 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
1265 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
1266 |
1267 | npm-package-arg@^10.0.0:
1268 | version "10.0.0"
1269 | resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-10.0.0.tgz#a34f4a4208a937074b1fff0943a684fbacc83977"
1270 | integrity sha512-7dkh8mRp7s0KwVHKIVJnFCJQ2B34gOGnzgBjDGyprycmARq/82SX/lhilQ95ZuacP/G/1gsS345iAkKmxWBQ2Q==
1271 | dependencies:
1272 | hosted-git-info "^6.0.0"
1273 | proc-log "^3.0.0"
1274 | semver "^7.3.5"
1275 | validate-npm-package-name "^5.0.0"
1276 |
1277 | npm-registry-fetch@^14.0.2:
1278 | version "14.0.2"
1279 | resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.2.tgz#f637630d9005aeebe4d7411226fb11fa1628c5e8"
1280 | integrity sha512-TMenrMagFA9KF81E2bkS5XRyzERK4KXu70vgXt5+i8FcrFeLNgNsc6e5hekTqjDwPDkL3HGn/holWcXDMfnFgw==
1281 | dependencies:
1282 | make-fetch-happen "^11.0.0"
1283 | minipass "^3.1.6"
1284 | minipass-fetch "^3.0.0"
1285 | minipass-json-stream "^1.0.1"
1286 | minizlib "^2.1.2"
1287 | npm-package-arg "^10.0.0"
1288 | proc-log "^3.0.0"
1289 |
1290 | npm-run-path@^4.0.0:
1291 | version "4.0.1"
1292 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
1293 | integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
1294 | dependencies:
1295 | path-key "^3.0.0"
1296 |
1297 | object-assign@^4.0.1:
1298 | version "4.1.1"
1299 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
1300 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
1301 |
1302 | once@^1.3.0, once@^1.3.1, once@^1.4.0:
1303 | version "1.4.0"
1304 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
1305 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
1306 | dependencies:
1307 | wrappy "1"
1308 |
1309 | onetime@^5.1.0:
1310 | version "5.1.2"
1311 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
1312 | integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
1313 | dependencies:
1314 | mimic-fn "^2.1.0"
1315 |
1316 | p-limit@^2.2.0:
1317 | version "2.3.0"
1318 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
1319 | integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
1320 | dependencies:
1321 | p-try "^2.0.0"
1322 |
1323 | p-locate@^4.1.0:
1324 | version "4.1.0"
1325 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
1326 | integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
1327 | dependencies:
1328 | p-limit "^2.2.0"
1329 |
1330 | p-map@^4.0.0:
1331 | version "4.0.0"
1332 | resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
1333 | integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
1334 | dependencies:
1335 | aggregate-error "^3.0.0"
1336 |
1337 | p-try@^2.0.0:
1338 | version "2.2.0"
1339 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
1340 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
1341 |
1342 | packet-reader@1.0.0:
1343 | version "1.0.0"
1344 | resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74"
1345 | integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==
1346 |
1347 | parse-duration@^1.0.2:
1348 | version "1.0.2"
1349 | resolved "https://registry.yarnpkg.com/parse-duration/-/parse-duration-1.0.2.tgz#b9aa7d3a1363cc7e8845bea8fd3baf8a11df5805"
1350 | integrity sha512-Dg27N6mfok+ow1a2rj/nRjtCfaKrHUZV2SJpEn/s8GaVUSlf4GGRCRP1c13Hj+wfPKVMrFDqLMLITkYKgKxyyg==
1351 |
1352 | parse-ms@^3.0.0:
1353 | version "3.0.0"
1354 | resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-3.0.0.tgz#3ea24a934913345fcc3656deda72df921da3a70e"
1355 | integrity sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==
1356 |
1357 | parse5-htmlparser2-tree-adapter@^6.0.0:
1358 | version "6.0.1"
1359 | resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
1360 | integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
1361 | dependencies:
1362 | parse5 "^6.0.1"
1363 |
1364 | parse5@^5.1.1:
1365 | version "5.1.1"
1366 | resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
1367 | integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
1368 |
1369 | parse5@^6.0.1:
1370 | version "6.0.1"
1371 | resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
1372 | integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
1373 |
1374 | path-exists@^4.0.0:
1375 | version "4.0.0"
1376 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
1377 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
1378 |
1379 | path-is-absolute@^1.0.0:
1380 | version "1.0.1"
1381 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
1382 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
1383 |
1384 | path-key@^3.0.0, path-key@^3.1.0:
1385 | version "3.1.1"
1386 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
1387 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
1388 |
1389 | path-parse@^1.0.7:
1390 | version "1.0.7"
1391 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
1392 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
1393 |
1394 | peek-readable@^5.0.0:
1395 | version "5.0.0"
1396 | resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec"
1397 | integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==
1398 |
1399 | pg-connection-string@^2.5.0:
1400 | version "2.5.0"
1401 | resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
1402 | integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
1403 |
1404 | pg-int8@1.0.1:
1405 | version "1.0.1"
1406 | resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
1407 | integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
1408 |
1409 | pg-pool@^3.5.2:
1410 | version "3.5.2"
1411 | resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.2.tgz#ed1bed1fb8d79f1c6fd5fb1c99e990fbf9ddf178"
1412 | integrity sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==
1413 |
1414 | pg-protocol@^1.5.0:
1415 | version "1.5.0"
1416 | resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.5.0.tgz#b5dd452257314565e2d54ab3c132adc46565a6a0"
1417 | integrity sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==
1418 |
1419 | pg-types@^2.1.0:
1420 | version "2.2.0"
1421 | resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
1422 | integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
1423 | dependencies:
1424 | pg-int8 "1.0.1"
1425 | postgres-array "~2.0.0"
1426 | postgres-bytea "~1.0.0"
1427 | postgres-date "~1.0.4"
1428 | postgres-interval "^1.1.0"
1429 |
1430 | pg@^8.8.0:
1431 | version "8.8.0"
1432 | resolved "https://registry.yarnpkg.com/pg/-/pg-8.8.0.tgz#a77f41f9d9ede7009abfca54667c775a240da686"
1433 | integrity sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==
1434 | dependencies:
1435 | buffer-writer "2.0.0"
1436 | packet-reader "1.0.0"
1437 | pg-connection-string "^2.5.0"
1438 | pg-pool "^3.5.2"
1439 | pg-protocol "^1.5.0"
1440 | pg-types "^2.1.0"
1441 | pgpass "1.x"
1442 |
1443 | pgpass@1.x:
1444 | version "1.0.5"
1445 | resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d"
1446 | integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==
1447 | dependencies:
1448 | split2 "^4.1.0"
1449 |
1450 | picomatch@^2.0.4, picomatch@^2.2.1:
1451 | version "2.3.1"
1452 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
1453 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
1454 |
1455 | postgres-array@~2.0.0:
1456 | version "2.0.0"
1457 | resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
1458 | integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==
1459 |
1460 | postgres-bytea@~1.0.0:
1461 | version "1.0.0"
1462 | resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35"
1463 | integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==
1464 |
1465 | postgres-date@~1.0.4:
1466 | version "1.0.7"
1467 | resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8"
1468 | integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==
1469 |
1470 | postgres-interval@^1.1.0:
1471 | version "1.2.0"
1472 | resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695"
1473 | integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==
1474 | dependencies:
1475 | xtend "^4.0.0"
1476 |
1477 | prettier@^2.7.1:
1478 | version "2.7.1"
1479 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64"
1480 | integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
1481 |
1482 | pretty-ms@^8.0.0:
1483 | version "8.0.0"
1484 | resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-8.0.0.tgz#a35563b2a02df01e595538f86d7de54ca23194a3"
1485 | integrity sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==
1486 | dependencies:
1487 | parse-ms "^3.0.0"
1488 |
1489 | pretty-quick@^3.1.3:
1490 | version "3.1.3"
1491 | resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-3.1.3.tgz#15281108c0ddf446675157ca40240099157b638e"
1492 | integrity sha512-kOCi2FJabvuh1as9enxYmrnBC6tVMoVOenMaBqRfsvBHB0cbpYHjdQEpSglpASDFEXVwplpcGR4CLEaisYAFcA==
1493 | dependencies:
1494 | chalk "^3.0.0"
1495 | execa "^4.0.0"
1496 | find-up "^4.1.0"
1497 | ignore "^5.1.4"
1498 | mri "^1.1.5"
1499 | multimatch "^4.0.0"
1500 |
1501 | proc-log@^3.0.0:
1502 | version "3.0.0"
1503 | resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8"
1504 | integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==
1505 |
1506 | promise-inflight@^1.0.1:
1507 | version "1.0.1"
1508 | resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
1509 | integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==
1510 |
1511 | promise-retry@^2.0.1:
1512 | version "2.0.1"
1513 | resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
1514 | integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
1515 | dependencies:
1516 | err-code "^2.0.2"
1517 | retry "^0.12.0"
1518 |
1519 | pump@^3.0.0:
1520 | version "3.0.0"
1521 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
1522 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
1523 | dependencies:
1524 | end-of-stream "^1.1.0"
1525 | once "^1.3.1"
1526 |
1527 | readable-stream@^3.6.0:
1528 | version "3.6.0"
1529 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
1530 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
1531 | dependencies:
1532 | inherits "^2.0.3"
1533 | string_decoder "^1.1.1"
1534 | util-deprecate "^1.0.1"
1535 |
1536 | readable-web-to-node-stream@^3.0.2:
1537 | version "3.0.2"
1538 | resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb"
1539 | integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==
1540 | dependencies:
1541 | readable-stream "^3.6.0"
1542 |
1543 | readdirp@~3.6.0:
1544 | version "3.6.0"
1545 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
1546 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
1547 | dependencies:
1548 | picomatch "^2.2.1"
1549 |
1550 | reflect-metadata@^0.1.13:
1551 | version "0.1.13"
1552 | resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
1553 | integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
1554 |
1555 | require-directory@^2.1.1:
1556 | version "2.1.1"
1557 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
1558 | integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
1559 |
1560 | resolve@^1.0.0:
1561 | version "1.22.1"
1562 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
1563 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
1564 | dependencies:
1565 | is-core-module "^2.9.0"
1566 | path-parse "^1.0.7"
1567 | supports-preserve-symlinks-flag "^1.0.0"
1568 |
1569 | retry@^0.12.0:
1570 | version "0.12.0"
1571 | resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
1572 | integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
1573 |
1574 | rimraf@^2.6.1:
1575 | version "2.7.1"
1576 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
1577 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
1578 | dependencies:
1579 | glob "^7.1.3"
1580 |
1581 | safe-buffer@^5.0.1, safe-buffer@~5.2.0:
1582 | version "5.2.1"
1583 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
1584 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
1585 |
1586 | "safer-buffer@>= 2.1.2 < 3.0.0":
1587 | version "2.1.2"
1588 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
1589 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
1590 |
1591 | sax@>=0.6.0:
1592 | version "1.2.4"
1593 | resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
1594 | integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
1595 |
1596 | semver@^7.0.0, semver@^7.3.5:
1597 | version "7.3.8"
1598 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
1599 | integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
1600 | dependencies:
1601 | lru-cache "^6.0.0"
1602 |
1603 | sha.js@^2.4.11:
1604 | version "2.4.11"
1605 | resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
1606 | integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
1607 | dependencies:
1608 | inherits "^2.0.1"
1609 | safe-buffer "^5.0.1"
1610 |
1611 | shebang-command@^2.0.0:
1612 | version "2.0.0"
1613 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
1614 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
1615 | dependencies:
1616 | shebang-regex "^3.0.0"
1617 |
1618 | shebang-regex@^3.0.0:
1619 | version "3.0.0"
1620 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
1621 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
1622 |
1623 | signal-exit@^3.0.2:
1624 | version "3.0.7"
1625 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
1626 | integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
1627 |
1628 | smart-buffer@^4.2.0:
1629 | version "4.2.0"
1630 | resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
1631 | integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
1632 |
1633 | socks-proxy-agent@^7.0.0:
1634 | version "7.0.0"
1635 | resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6"
1636 | integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==
1637 | dependencies:
1638 | agent-base "^6.0.2"
1639 | debug "^4.3.3"
1640 | socks "^2.6.2"
1641 |
1642 | socks@^2.6.2:
1643 | version "2.7.1"
1644 | resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
1645 | integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
1646 | dependencies:
1647 | ip "^2.0.0"
1648 | smart-buffer "^4.2.0"
1649 |
1650 | source-map-support@^0.5.12:
1651 | version "0.5.21"
1652 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
1653 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
1654 | dependencies:
1655 | buffer-from "^1.0.0"
1656 | source-map "^0.6.0"
1657 |
1658 | source-map@^0.6.0:
1659 | version "0.6.1"
1660 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
1661 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
1662 |
1663 | split2@^4.1.0:
1664 | version "4.1.0"
1665 | resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809"
1666 | integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==
1667 |
1668 | ssri@^10.0.0:
1669 | version "10.0.0"
1670 | resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.0.tgz#1e34554cbbc4728f5290674264e21b64aaf27ca7"
1671 | integrity sha512-64ghGOpqW0k+jh7m5jndBGdVEoPikWwGQmBNN5ks6jyUSMymzHDTlnNHOvzp+6MmHOljr2MokUzvRksnTwG0Iw==
1672 | dependencies:
1673 | minipass "^3.1.1"
1674 |
1675 | streamsearch@^1.1.0:
1676 | version "1.1.0"
1677 | resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
1678 | integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
1679 |
1680 | string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
1681 | version "4.2.3"
1682 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
1683 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
1684 | dependencies:
1685 | emoji-regex "^8.0.0"
1686 | is-fullwidth-code-point "^3.0.0"
1687 | strip-ansi "^6.0.1"
1688 |
1689 | string_decoder@^1.1.1:
1690 | version "1.3.0"
1691 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
1692 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
1693 | dependencies:
1694 | safe-buffer "~5.2.0"
1695 |
1696 | strip-ansi@^6.0.0, strip-ansi@^6.0.1:
1697 | version "6.0.1"
1698 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
1699 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
1700 | dependencies:
1701 | ansi-regex "^5.0.1"
1702 |
1703 | strip-bom@^3.0.0:
1704 | version "3.0.0"
1705 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
1706 | integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
1707 |
1708 | strip-final-newline@^2.0.0:
1709 | version "2.0.0"
1710 | resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
1711 | integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
1712 |
1713 | strip-json-comments@^2.0.0:
1714 | version "2.0.1"
1715 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
1716 | integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
1717 |
1718 | strtok3@^7.0.0:
1719 | version "7.0.0"
1720 | resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5"
1721 | integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==
1722 | dependencies:
1723 | "@tokenizer/token" "^0.3.0"
1724 | peek-readable "^5.0.0"
1725 |
1726 | supports-color@^7.1.0:
1727 | version "7.2.0"
1728 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
1729 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
1730 | dependencies:
1731 | has-flag "^4.0.0"
1732 |
1733 | supports-preserve-symlinks-flag@^1.0.0:
1734 | version "1.0.0"
1735 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
1736 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
1737 |
1738 | tar@^6.1.11, tar@^6.1.12:
1739 | version "6.1.12"
1740 | resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6"
1741 | integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==
1742 | dependencies:
1743 | chownr "^2.0.0"
1744 | fs-minipass "^2.0.0"
1745 | minipass "^3.0.0"
1746 | minizlib "^2.1.1"
1747 | mkdirp "^1.0.3"
1748 | yallist "^4.0.0"
1749 |
1750 | thenify-all@^1.0.0:
1751 | version "1.6.0"
1752 | resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
1753 | integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==
1754 | dependencies:
1755 | thenify ">= 3.1.0 < 4"
1756 |
1757 | "thenify@>= 3.1.0 < 4":
1758 | version "3.3.1"
1759 | resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
1760 | integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
1761 | dependencies:
1762 | any-promise "^1.0.0"
1763 |
1764 | to-regex-range@^5.0.1:
1765 | version "5.0.1"
1766 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
1767 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
1768 | dependencies:
1769 | is-number "^7.0.0"
1770 |
1771 | token-types@^5.0.1:
1772 | version "5.0.1"
1773 | resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4"
1774 | integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==
1775 | dependencies:
1776 | "@tokenizer/token" "^0.3.0"
1777 | ieee754 "^1.2.1"
1778 |
1779 | tree-kill@^1.2.2:
1780 | version "1.2.2"
1781 | resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
1782 | integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
1783 |
1784 | ts-mixer@^6.0.1:
1785 | version "6.0.2"
1786 | resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.2.tgz#3e4e4bb8daffb24435f6980b15204cb5b287e016"
1787 | integrity sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A==
1788 |
1789 | ts-node-dev@^2.0.0:
1790 | version "2.0.0"
1791 | resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-2.0.0.tgz#bdd53e17ab3b5d822ef519928dc6b4a7e0f13065"
1792 | integrity sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==
1793 | dependencies:
1794 | chokidar "^3.5.1"
1795 | dynamic-dedupe "^0.3.0"
1796 | minimist "^1.2.6"
1797 | mkdirp "^1.0.4"
1798 | resolve "^1.0.0"
1799 | rimraf "^2.6.1"
1800 | source-map-support "^0.5.12"
1801 | tree-kill "^1.2.2"
1802 | ts-node "^10.4.0"
1803 | tsconfig "^7.0.0"
1804 |
1805 | ts-node@^10.4.0:
1806 | version "10.9.1"
1807 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
1808 | integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
1809 | dependencies:
1810 | "@cspotcode/source-map-support" "^0.8.0"
1811 | "@tsconfig/node10" "^1.0.7"
1812 | "@tsconfig/node12" "^1.0.7"
1813 | "@tsconfig/node14" "^1.0.0"
1814 | "@tsconfig/node16" "^1.0.2"
1815 | acorn "^8.4.1"
1816 | acorn-walk "^8.1.1"
1817 | arg "^4.1.0"
1818 | create-require "^1.1.0"
1819 | diff "^4.0.1"
1820 | make-error "^1.1.1"
1821 | v8-compile-cache-lib "^3.0.1"
1822 | yn "3.1.1"
1823 |
1824 | tsconfig@^7.0.0:
1825 | version "7.0.0"
1826 | resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7"
1827 | integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==
1828 | dependencies:
1829 | "@types/strip-bom" "^3.0.0"
1830 | "@types/strip-json-comments" "0.0.30"
1831 | strip-bom "^3.0.0"
1832 | strip-json-comments "^2.0.0"
1833 |
1834 | tslib@^2.3.1, tslib@^2.4.0:
1835 | version "2.4.1"
1836 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
1837 | integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
1838 |
1839 | typeorm@^0.3.10:
1840 | version "0.3.10"
1841 | resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.10.tgz#aa2857fd4b078c912ca693b7eee01b6535704458"
1842 | integrity sha512-VMKiM84EpJQ+Mz9xDIPqnfplWhyUy1d8ccaKdMY9obifxJOTFnv8GYVyPsGwG8Lk7Nb8MlttHyHWENGAhBA3WA==
1843 | dependencies:
1844 | "@sqltools/formatter" "^1.2.2"
1845 | app-root-path "^3.0.0"
1846 | buffer "^6.0.3"
1847 | chalk "^4.1.0"
1848 | cli-highlight "^2.1.11"
1849 | date-fns "^2.28.0"
1850 | debug "^4.3.3"
1851 | dotenv "^16.0.0"
1852 | glob "^7.2.0"
1853 | js-yaml "^4.1.0"
1854 | mkdirp "^1.0.4"
1855 | reflect-metadata "^0.1.13"
1856 | sha.js "^2.4.11"
1857 | tslib "^2.3.1"
1858 | uuid "^8.3.2"
1859 | xml2js "^0.4.23"
1860 | yargs "^17.3.1"
1861 |
1862 | typescript@^4.9.3:
1863 | version "4.9.3"
1864 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db"
1865 | integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==
1866 |
1867 | undici@^5.11.0, undici@^5.12.0:
1868 | version "5.12.0"
1869 | resolved "https://registry.yarnpkg.com/undici/-/undici-5.12.0.tgz#c758ffa704fbcd40d506e4948860ccaf4099f531"
1870 | integrity sha512-zMLamCG62PGjd9HHMpo05bSLvvwWOZgGeiWlN/vlqu3+lRo3elxktVGEyLMX+IO7c2eflLjcW74AlkhEZm15mg==
1871 | dependencies:
1872 | busboy "^1.6.0"
1873 |
1874 | unique-filename@^3.0.0:
1875 | version "3.0.0"
1876 | resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea"
1877 | integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==
1878 | dependencies:
1879 | unique-slug "^4.0.0"
1880 |
1881 | unique-slug@^4.0.0:
1882 | version "4.0.0"
1883 | resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3"
1884 | integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==
1885 | dependencies:
1886 | imurmurhash "^0.1.4"
1887 |
1888 | util-deprecate@^1.0.1:
1889 | version "1.0.2"
1890 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
1891 | integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
1892 |
1893 | uuid@^8.3.2:
1894 | version "8.3.2"
1895 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
1896 | integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
1897 |
1898 | v8-compile-cache-lib@^3.0.1:
1899 | version "3.0.1"
1900 | resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
1901 | integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
1902 |
1903 | validate-npm-package-name@^5.0.0:
1904 | version "5.0.0"
1905 | resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz#f16afd48318e6f90a1ec101377fa0384cfc8c713"
1906 | integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==
1907 | dependencies:
1908 | builtins "^5.0.0"
1909 |
1910 | which@^2.0.1:
1911 | version "2.0.2"
1912 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
1913 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
1914 | dependencies:
1915 | isexe "^2.0.0"
1916 |
1917 | wrap-ansi@^7.0.0:
1918 | version "7.0.0"
1919 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
1920 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
1921 | dependencies:
1922 | ansi-styles "^4.0.0"
1923 | string-width "^4.1.0"
1924 | strip-ansi "^6.0.0"
1925 |
1926 | wrappy@1:
1927 | version "1.0.2"
1928 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
1929 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
1930 |
1931 | ws@^8.9.0:
1932 | version "8.11.0"
1933 | resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
1934 | integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
1935 |
1936 | xml2js@^0.4.23:
1937 | version "0.4.23"
1938 | resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
1939 | integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
1940 | dependencies:
1941 | sax ">=0.6.0"
1942 | xmlbuilder "~11.0.0"
1943 |
1944 | xmlbuilder@~11.0.0:
1945 | version "11.0.1"
1946 | resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
1947 | integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
1948 |
1949 | xtend@^4.0.0:
1950 | version "4.0.2"
1951 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
1952 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
1953 |
1954 | y18n@^5.0.5:
1955 | version "5.0.8"
1956 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
1957 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
1958 |
1959 | yallist@^4.0.0:
1960 | version "4.0.0"
1961 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
1962 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
1963 |
1964 | yargs-parser@^20.2.2:
1965 | version "20.2.9"
1966 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
1967 | integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
1968 |
1969 | yargs-parser@^21.1.1:
1970 | version "21.1.1"
1971 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
1972 | integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
1973 |
1974 | yargs@^16.0.0:
1975 | version "16.2.0"
1976 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
1977 | integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
1978 | dependencies:
1979 | cliui "^7.0.2"
1980 | escalade "^3.1.1"
1981 | get-caller-file "^2.0.5"
1982 | require-directory "^2.1.1"
1983 | string-width "^4.2.0"
1984 | y18n "^5.0.5"
1985 | yargs-parser "^20.2.2"
1986 |
1987 | yargs@^17.3.1:
1988 | version "17.6.2"
1989 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541"
1990 | integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==
1991 | dependencies:
1992 | cliui "^8.0.1"
1993 | escalade "^3.1.1"
1994 | get-caller-file "^2.0.5"
1995 | require-directory "^2.1.1"
1996 | string-width "^4.2.3"
1997 | y18n "^5.0.5"
1998 | yargs-parser "^21.1.1"
1999 |
2000 | yn@3.1.1:
2001 | version "3.1.1"
2002 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
2003 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
2004 |
--------------------------------------------------------------------------------