diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..479dae7 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,72 @@ +--- + +kind: pipeline +name: deployment + +steps: +- name: deploy + image: appleboy/drone-ssh + settings: + host: 192.168.1.115 + username: vylpes + password: + from_secret: ssh_password + port: 22 + script: + - sh /home/vylpes/scripts/vylbot/deploy_prod.sh + +trigger: + event: + - tag + +--- + +kind: pipeline +name: staging + +steps: +- name: stage + image: appleboy/drone-ssh + settings: + host: 192.168.1.115 + username: vylpes + password: + from_secret: ssh_password + port: 22 + script: + - sh /home/vylpes/scripts/vylbot/deploy_stage.sh + +trigger: + branch: + - develop + event: + - push + +--- + +kind: pipeline +name: integration + +steps: +- name: build + image: node + commands: + - yarn install --frozen-lockfile + - yarn build + +# - name: test +# image: node +# commands: +# - yarn install --frozen-lockfile +# - yarn test + +trigger: + branch: + - main + - develop + - hotfix/* + - feature/* + - renovate/* + event: + - push + - pull_request diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9c92b48 --- /dev/null +++ b/.env.example @@ -0,0 +1,28 @@ +# Security Warning! Do not commit this file to any VCS! +# This is a local file to speed up development process, +# so you don't have to change your environment variables. +# +# This is not applied to `.env.template`! +# Template files must be committed to the VCS, but must not contain +# any secret values. + +BOT_TOKEN= +BOT_VER=3.3.0 +BOT_AUTHOR=Vylpes +BOT_OWNERID=147392775707426816 +BOT_CLIENTID=682942374040961060 + +ABOUT_FUNDING=https://ko-fi.com/vylpes +ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app + +CACHE_INTERVAL=1800000 # 30 minutes + +DB_HOST=127.0.0.1 +DB_PORT=3101 +DB_NAME=vylbot +DB_AUTH_USER=dev +DB_AUTH_PASS=dev +DB_SYNC=true +DB_LOGGING=true +DB_ROOT_HOST=0.0.0.0 +DB_DATA_LOCATION=./.temp/database diff --git a/.env.template b/.env.template deleted file mode 100644 index d512aac..0000000 --- a/.env.template +++ /dev/null @@ -1,13 +0,0 @@ -# Security Warning! Do not commit this file to any VCS! -# This is a local file to speed up development process, -# so you don't have to change your environment variables. -# -# This is not applied to `.env.template`! -# Template files must be committed to the VCS, but must not contain -# any secret values. - -BOT_TOKEN= -BOT_VER=3.0 -BOT_AUTHOR=Vylpes -BOT_DATE=28 Nov 2021 -BOT_OWNERID=147392775707426816 \ No newline at end of file diff --git a/.forgejo/workflows/production.yml b/.forgejo/workflows/production.yml new file mode 100644 index 0000000..dc78e0f --- /dev/null +++ b/.forgejo/workflows/production.yml @@ -0,0 +1,67 @@ +name: Deploy To Production + +on: + push: + branches: + - main + +jobs: + build: + environment: prod + + runs-on: node + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test + + - name: "Copy files over to location" + run: rsync -rvzP --delete . ${{ secrets.PROD_REPO_PATH }} + + deploy: + environment: prod + needs: build + runs-on: node + steps: + - uses: https://github.com/appleboy/ssh-action@v1.1.0 + env: + DB_NAME: ${{ secrets.PROD_DB_NAME }} + DB_AUTH_USER: ${{ secrets.PROD_DB_AUTH_USER }} + DB_AUTH_PASS: ${{ secrets.PROD_DB_AUTH_PASS }} + DB_HOST: ${{ secrets.PROD_DB_HOST }} + DB_PORT: ${{ secrets.PROD_DB_PORT }} + DB_ROOT_HOST: ${{ secrets.PROD_DB_ROOT_HOST }} + DB_SYNC: ${{ secrets.PROD_DB_SYNC }} + DB_LOGGING: ${{ secrets.PROD_DB_LOGGING }} + DB_DATA_LOCATION: ${{ secrets.PROD_DB_DATA_LOCATION }} + SERVER_PATH: ${{ secrets.PROD_SSH_SERVER_PATH }} + BOT_TOKEN: ${{ secrets.PROD_BOT_TOKEN }} + BOT_VER: ${{ vars.PROD_BOT_VER }} + BOT_AUTHOR: ${{ vars.PROD_BOT_AUTHOR }} + BOT_OWNERID: ${{ vars.PROD_BOT_OWNERID }} + BOT_CLIENTID: ${{ vars.PROD_BOT_CLIENTID }} + ABOUT_FUNDING: ${{ vars.PROD_ABOUT_FUNDING }} + ABOUT_REPO: ${{ vars.PROD_ABOUT_REPO }} + CACHE_INTERVAL: ${{ vars.PROD_CACHE_INTERVAL }} + with: + host: ${{ secrets.PROD_SSH_HOST }} + username: ${{ secrets.PROD_SSH_USER }} + key: ${{ secrets.PROD_SSH_KEY }} + port: ${{ secrets.PROD_SSH_PORT }} + envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,CACHE_INTERVAL + script: | + source .sshrc \ + && cd /home/vylpes/apps/vylbot/vylbot_prod \ + && docker compose down \ + && (pm2 stop vylbot_prod || true) \ + && (pm2 delete vylbot_prod || true) \ + && docker compose up -d \ + && sleep 10 \ + && yarn db:up \ + && pm2 start --name vylbot_prod dist/vylbot.js diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml new file mode 100644 index 0000000..af9cdb7 --- /dev/null +++ b/.forgejo/workflows/stage.yml @@ -0,0 +1,69 @@ +name: Deploy To Stage + +on: + push: + branches: + - develop + - hotfix/* + - release/* + +jobs: + build: + environment: prod + + runs-on: node + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test + + - name: "Copy files over to location" + run: rsync -rvzP --delete . ${{ secrets.STAGE_REPO_PATH }} + + deploy: + environment: prod + needs: build + runs-on: node + steps: + - uses: https://github.com/appleboy/ssh-action@v1.1.0 + env: + DB_NAME: ${{ secrets.STAGE_DB_NAME }} + DB_AUTH_USER: ${{ secrets.STAGE_DB_AUTH_USER }} + DB_AUTH_PASS: ${{ secrets.STAGE_DB_AUTH_PASS }} + DB_HOST: ${{ secrets.STAGE_DB_HOST }} + DB_PORT: ${{ secrets.STAGE_DB_PORT }} + DB_ROOT_HOST: ${{ secrets.STAGE_DB_ROOT_HOST }} + DB_SYNC: ${{ secrets.STAGE_DB_SYNC }} + DB_LOGGING: ${{ secrets.STAGE_DB_LOGGING }} + DB_DATA_LOCATION: ${{ secrets.STAGE_DB_DATA_LOCATION }} + SERVER_PATH: ${{ secrets.STAGE_SSH_SERVER_PATH }} + BOT_TOKEN: ${{ secrets.STAGE_BOT_TOKEN }} + BOT_VER: ${{ github.ref_name }} + BOT_AUTHOR: ${{ vars.STAGE_BOT_AUTHOR }} + BOT_OWNERID: ${{ vars.STAGE_BOT_OWNERID }} + BOT_CLIENTID: ${{ vars.STAGE_BOT_CLIENTID }} + ABOUT_FUNDING: ${{ vars.STAGE_ABOUT_FUNDING }} + ABOUT_REPO: ${{ vars.STAGE_ABOUT_REPO }} + CACHE_INTERVAL: ${{ vars.STAGE_CACHE_INTERVAL }} + with: + host: ${{ secrets.STAGE_SSH_HOST }} + username: ${{ secrets.STAGE_SSH_USER }} + key: ${{ secrets.STAGE_SSH_KEY }} + port: ${{ secrets.STAGE_SSH_PORT }} + envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,CACHE_INTERVAL + script: | + source .sshrc \ + && cd /home/vylpes/apps/vylbot/vylbot_stage \ + && docker compose down \ + && (pm2 stop vylbot_stage || true) \ + && (pm2 delete vylbot_stage || true) \ + && docker compose up -d \ + && sleep 10 \ + && yarn db:up \ + && pm2 start --name vylbot_stage dist/vylbot.js diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml new file mode 100644 index 0000000..b531784 --- /dev/null +++ b/.forgejo/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test + +on: + push: + branches: + - feature/* + - renovate/* + +jobs: + build: + environment: stage + + runs-on: node + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test diff --git a/.gitea/ISSUE_TEMPLATE.md b/.gitea/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..090d6b3 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE.md @@ -0,0 +1,18 @@ +Epic: \ +Story Points: + +--- + +*No description* + +## Acceptance Criteria + +*No acceptance criteria* + +## Subtasks + +*No subtasks* + +## Notes + +*No notes* \ No newline at end of file diff --git a/.gitlab/merge_request_templates/default.md b/.gitea/PULL_REQUEST_TEMPLATE.md similarity index 52% rename from .gitlab/merge_request_templates/default.md rename to .gitea/PULL_REQUEST_TEMPLATE.md index f49bd50..dc57601 100644 --- a/.gitlab/merge_request_templates/default.md +++ b/.gitea/PULL_REQUEST_TEMPLATE.md @@ -8,14 +8,14 @@ Fixes # (issue) Please delete options that are not relevant. -- Bug fix (non-breaking change which fixes an issue) -- New feature (non-breaking change which adds functionality) -- Breaking change (fix or feature that would cause existing functionality to not work as expected) -- This change requires a documentation update +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update # How Has This Been Tested? -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration +Please describe the tests that you ran to verify the changes. Provide instructions so we can reproduce. Please also list any relevant details to your test configuration. # Checklist @@ -24,6 +24,6 @@ Please describe the tests that you ran to verify your changes. Provide instructi - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings -- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] I have added tests that provide my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes -- [ ] Any dependant changes have been merged and published in downstream modules +- [ ] Any dependent changes have been merged and published in downstream modules \ No newline at end of file diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml deleted file mode 100644 index 1f76252..0000000 --- a/.github/workflows/testing.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Testing - -on: - pull_request: - branches: - - main - - develop - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [16.x] - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: yarn install - - run: yarn build - - run: yarn test - diff --git a/.gitignore b/.gitignore index 1707d85..c6754e1 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,5 @@ dist config.json .DS_Store -ormconfig.json \ No newline at end of file +ormconfig.json +.temp/ diff --git a/.gitlab/.gitkeep b/.gitlab/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/.gitlab/merge_request_templates/.gitkeep b/.gitlab/merge_request_templates/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0a791bc --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[helpdesk@vylpes.com](mailto:helpdesk@vylpes.com). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bca88d1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,200 @@ +# Contributing to VylBot + +First off, thanks for taking the time to contribute! + +The following is a set of guidelines for contributing to VylBot. These are mostly guidelines, not rules. Use your best judgement, and feel free to propose changes to this document in a pull request. + +## Code of Conduct + +This project and everyone participating in it is governed by the VylBot Code of Conduct. By participating, you are expected to uphold this code. + +## Questions about VylBot + +> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below. + +You can ask a question about the project in the `#development` channel in the [Discord Server](https://go.vylpes.xyz/A6HcA). + +You can also email with queries and support if you'd prefer at [helpdesk@vylpes.com](mailto:helpdesk@vylpes.com). + +## What you should know + +### Javascript and Node + +VylBot uses [NodeJS](https://nodejs.org/), and therefore Javascript, as its runtime. You should know how to use this. + +## Conventions + +There are a few conventions that have developed over time for this project. When you create a pull request a check will be ran making sure that your code follows these conventions. + +We won't accept pull requests unless these checks pass. If yours fail, simply fix what the bot says until it passes and then get a repo member to review your code. + +* Variable names should use **Camel Case** +* Functions should put **braces on the same line** +* **No comma dangle**, i.e. having a commma after the last item in an object +* **Arrow body style** should have braces around the body only when needed +* **Arrow parameters** should have brackets around them only when needed +* **Arrow spacing** should have a space around the arrow (' => ') +* **No var** should be used, instead use either let or const when appropriate + +## How You Can Contribute + +### Reporting Bugs + +This section guides you through submitting a bug report for VylBot. Following these guidelines helps maintainers and the community understand your report. reproduce the behaviour, and find related reports. + +When you are creating a bug report, please include as many details as possible. + +> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### Before Submitting A Bug Report + +* **Perform a search** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. + +#### How You Can Submit A (Good) Bug Report + +Bugs are tracked as GitHub issues. After you've determined the bug you're reporting hasn't got a pre-existing **open** issue already, create an issue and provide information from below. + +* **Use a clear and descriptive title** for the issue to indentify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started VylBot (if you're using your own instance), which command exactly you used, and the output which the bot replied with. If its your own instance, provide information on what the terminal output said, if any. +* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pastable snippets, which you use in those examples. If you're providing snippets in the issue, use Markdown code blocks. +* **Describe the behaviour you observed after following the steps** and point out what exactly is the problem with that behaviour. +* **Explain which behaviour you expected to see instead and why.** +* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. +* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. + +Provide more context by answering these questions: + +* **Did the problem start happening recently** (e.g. after updating to a new version of VylBot) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of VylBot?** What's the most recently version in which the problem doesn't happen? You can download older versions of VylBot from the releases page. +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. + +Include details about your configuration and environment: + +* **Which version of VylBot are you using?** You can get the exact version by running the `about` command. +* **What's the name and version of the OS you're using?** +* **Are you running VylBot in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest? +* **What version of node do you have installed?** You can get this version by running the `node -v` command in your terminal. + +### Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for VylBot, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. + +When you are creating an enhancement suggestion, please include as many details as possible. Fill out the suggestion with the steps that you imagine you would take if the feature you're requesting existed. + +#### Before Submitting an Enhancement Suggestion + +* **Check if the feature already exists.** Make sure to check on the latest version +* **Perform a search** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Enhancement Suggestion? + +Enhancement suggestions are tracked as GitHub issues. After you've determined the feature doesn't already exist or been suggested before, create an issue on that repository and provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide specific examples to demonstrate the steps.** Include copy/pastable snippets which you use in those examples, as Markdown code blocks. +* **Describe the current behaviour** and **explain which behaviour you expected to see instead** and why. +* **Include screnshots and animated GIFs** which help you demonstrate the steps or point out the part of VylBot which the suggestion is related to. +* **Explain why this enhancement would be useful** to most VylBot users and isn't something that can or should be implemented as a custom command. +* **List some other bots where this enhancement exists.** +* **Specify which version of VylBot you're using.** You can get the exact version by running the `about` command. +* **Specify the name and version of the OS you're using.** + +#### Prerequisites + +In order to download necessary tools, clone the repository, and install dependencies via `yarn` you need network access. + +You'll need the following tools: + +* Git +* NodeJS +* Yarn + +Install and build all of the dependencies using `yarn` + +```bash +cd vylbot-app +yarn install +``` + +#### Build and Run + +If you want to understand how VylBot works or want to debug an issue, you'll want to get the source, build it, and run the tool locally. + +First, fork the VylBot repository so that you can make a pull request. Then, clone your fork locally: + +```bash +git clone https://git.vylpes.xyz//vylbot-app.git + +# OR + +git clone https://github.com/vylpes/vylbot-app.git +``` + +Occasionally, you will want to merge changes in the upstream repository (the official code repo) with your fork. + +```bash +cd vylbot-app +git checkout master +git pull https://git.vylpes.xyz/rabbitlabs/vylbot-app.git master +``` + +Manage any merge conflicts, commit them, and then push them to your fork. + +Go into `vylbot-app` and build the package using `yarn build`. + +#### Pull Requests + +The process described here has several goals: + +* Maintain VylBot's quality +* Fix problems that are important to users +* Engage the community in working toward the best possible VylBot +* Enable a sustainable system for VylBot's maintainers to review contributions + +Please follow these steps to have your contribution considered by maintainers: + +* You mention the issue id which this pull request aims to fix +* After you submit your pull request, verify that all status checks are passing. + +> **Note**: If a check fails the pull request it is important that you go and fix these issues, or let us know that you no longer want to work on this issue by commenting on the pull request. Doing this will give you a better chance of having your pull request merged. + +* When the checks have passed a maintainer will review your code and ask for any improvements or questions, and will merge it if they are satisifed. + +While the prerequesites above must be satisifed prior to having your pull reuqest accepted, the reviewer(s) may ask you to complete additional design ork, tests, or other changes before your pull request can be ultimately accepted. + +#### Submitting Changes via Email +If you're not within our forgejo instance and still like to contribute, you can send us your contributions to [git@vylpes.com](mailto:git@vylpes.com). + +For more information on how to do this, see the [git documentation](https://git-scm.com/docs/git-send-email). + +#### Submitting Changes via GitHub +This code is mirrored on GitHub, although main development is done on my self-hosted Forgejo instance, feel free to clone and create pull requests on there. I will merge it back into Forgejo once accepted. + +#### JavaScript Styleguide + +All JavaScript code is linted with `eslint`. + +* Prefer camelcase for variable names +* Prefer braces `{` to be on the same line +* Prefer no comma `,` dangle +* Prefer arrow function bodies to have brances `{}` only when needed +* Prefer arrow function parameters to have brackets `()` only when needed +* Prefer arrow function arrows `=>` to have a space before and after it +* Prefer `let` and `const` over `var` + +As well as eslint's recommended defaults. + +Example + +```js +function ban (member) { + let reason = "Example reason"; + + member.ban(reason).then(() => { + // handle then here + }).catch(err => { + // handle error here + }); +} +``` \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 51e8b21..0000000 --- a/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# Create app and work directory -FROM node:16 -WORKDIR /vylbot - -# Install dependencies -COPY package.json . -COPY yarn.lock . -RUN yarn install - -# Bundle app source -COPY . . -RUN yarn build - -# Run the app source -CMD [ "yarn", "start" ] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a5940a0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Vylpes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 9023e0c..33de069 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ Discord bot for Vylpes' Den Discord Server. ## Installation -Download the latest version from the [releases page](https://github.com/Vylpes/vylbot-app/releases). +Download the latest version from the [releases page](https://git.vylpes.xyz/rabbitlabs/vylbot-app/releases). Copy the config template file and fill in the strings. ## Requirements -- NodeJS v16 +- NodeJS v20 - Yarn ## Usage @@ -22,10 +22,10 @@ yarn install yarn build ``` -Setup the database (Recommended to use the docker-compose file) +Setup the database (Recommended to use the docker compose file) ```bash -docker-compose up -d +docker compose up -d ``` Copy and edit the settings files @@ -33,9 +33,6 @@ Copy and edit the settings files ```bash cp .env.template .env # Edit the .env file - -cp ormconfig.json.template ormconfig.json -# Edit the ormconfig.json file ``` > **NOTE:** Make sure you do *not* check in these files! These contain sensitive information and should be treated as private. @@ -44,12 +41,4 @@ Start the bot ```bash yarn start -``` - -Alternatively, you can start the bot in development mode using: - -```bash -yarn start --dev -``` - -> Dev mode ensures that the default prefix is different to the production mode, in case you have both running in the same server. \ No newline at end of file +``` \ No newline at end of file diff --git a/data/config.txt b/data/config.txt deleted file mode 100644 index 3156fb2..0000000 --- a/data/config.txt +++ /dev/null @@ -1,22 +0,0 @@ -USAGE: [value] - -===[ KEYS ]=== -bot.prefix: The bot prefix for the server (Default: "v!") - -commands.disabled: Disabled commands, separated by commas (Default: "") - -role.assignable: List of roles assignable to user (Default: []) -role.moderator: The moderator role name (Default: "Moderator") -role.administrator: The administrator role name (Default: "Administrator") -role.muted: The muted role name (Default: "Muted") - -rules.file: The location of the rules file (Default: "data/rules/rules") - -channels.logs.message: The channel message events will be logged to (Default: "message-logs") -channels.logs.member: The channel member events will be logged to (Default: "member-logs") -channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs") - -verification.enabled: Enables/Disables the verification feature (Default: "false") -verification.channel: The channel to listen to for entry codes (Default: "entry") -verification.role: The server access role (Default: "Entry") -verification.code: The entry code for the channel (Default: "") \ No newline at end of file diff --git a/data/lobbyConfig.txt b/data/lobbyConfig.txt deleted file mode 100644 index ed65f72..0000000 --- a/data/lobbyConfig.txt +++ /dev/null @@ -1,8 +0,0 @@ -USAGE: config [cooldown] [Game Name] - -===[ EXAMPLE ]=== -To add a channel: -- config add game-name role-name 30 Game Name - -To remove a channel: -- config remove game-name \ No newline at end of file diff --git a/data/rules/290149509969739776.json b/data/rules/290149509969739776.json index 9fa7a1e..a01ef0c 100644 --- a/data/rules/290149509969739776.json +++ b/data/rules/290149509969739776.json @@ -6,7 +6,7 @@ "title": "Vylpes' Den", "description": [ "Welcome to Vylpes' Den! Make sure to say hi!", - "Invite link: https://discord.gg/UyAhAVp" + "Invite link: https://go.vylpes.xyz/A6HcA" ] }, { @@ -70,19 +70,17 @@ "description": [ "This server uses a bot made by me, VylBot, to help moderate the server.", "For more information on it, see the GitHub repositories:", - "https://github.com/Vylpes/vylbot-core", - "https://github.com/Vylpes/vylbot-app" + "https://gitea.vylpes.xyz/rabbitlabs/vylbot-app" ] }, { "title": "Links", "description": [ - "YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA", + "YouTube: https://www.youtube.com/@vylpes", "Patreon: https://www.patreon.com/vylpes", "Twitch: https://www.twitch.tv/vylpes_", - "Twitter: https://twitter.com/vylpes", - "Blog: https://vylpes.xyz" + "Twitter: https://twitter.com/vylpes" ], - "footer": "Last updated 01/02/2022" + "footer": "Last updated 20/06/2023" } ] \ No newline at end of file diff --git a/data/rules/442730357897429002.json b/data/rules/442730357897429002.json index 6fef8cd..bf195b1 100644 --- a/data/rules/442730357897429002.json +++ b/data/rules/442730357897429002.json @@ -1,88 +1,12 @@ [ { - "image": "https://i.imgur.com/bjH1gza.png" - }, - { - "title": "Bot Testing Ground", + "title": "Welcome to Mankalor's Discord Server!", "description": [ - "Welcome to Vylpes' Den! Make sure to say hi!", - "Invite link: https://discord.gg/UyAhAVp" - ] - }, - { - "title": "Discord TOS", - "description": [ - "All servers are required to follow the Discord Terms of Service. This includes minimum age requirements (13+). If the moderation team discover a breach of TOS we are required by discord to ban. Make sure you know them!", - "https://discord.com/terms" - ] - }, - { - "title": "Rules", - "description": [ - "**English Only**", - "In order for everyone to understand each other we would like to ask everyone to speak in English only.", + "*You must follow Discord's TOS, including the rule where", "you must be 13 years or older.", + "If moderators know you're under 13, we will have to ban you!*", "", - "**No NSFW or Obscene Content**", - "This includes text, images, or links featuring nudity, sex, hard violence, or other graphically disturbing content.", - "", - "**Treat Everyone with Respect**", - "Absolutely no harassment, witch hunting, sexism, racism, or hate speech will be tolerated.", - "", - "**No spam or self promotion**", - "Outside of #self-promo. This includes DMing fellow members.", - "", - "**Keep Politics to #general**", - "And make sure it doesn't become too heated. Debate don't argue.", - "", - "**Drama From Other Servers**", - "Please don't bring up drama from other servers, keep that to DMs", - "", - "**Bot Abuse**", - "Don't abuse the bots or you will be blocked from using them", - "", - "**Event Spoilers**", - "Contents of events and keynotes, such as the Nintendo Direct, must be spoken about in events, this rule applies for up to 24 hours after the event ends. Even though we will only enforce talking there for a set time, please be considerate of those who haven't watched the event yet." + "You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.", + "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message <@147392775707426816>." ] - }, - { - "title": "Moderators Discretion", - "description": [ - "Don't argue with a mod's decision. A moderator's choice is final. If you have an issue with a member of the mod team DM me (Vylpes#0001)." - ] - }, - { - "title": "Supporters", - "description": [ - "If you are a Twitch Subscriber or a Patreon Member and have linked your profiles to your discord account you will get exclusive access to the Vylpes Plus channels, including early access to videos!" - ] - }, - { - "title": "Self-Assignable Roles", - "description": [ - "If you want to assign yourself roles, go to #bot-stuff and type v!role . The current roles you can get are:", - "Notify: Get pinged when a new stream or video releases.", - "VotePings: Get pinged when I start a new poll", - "ProjectUpdates: Get pinged when I update my projects as well as new for them" - ] - }, - { - "title": "VylBot", - "description": [ - "This server uses a bot made by me, VylBot, to help moderate the server.", - "For more information on it, see the GitHub repositories:", - "https://github.com/Vylpes/vylbot-core", - "https://github.com/Vylpes/vylbot-app" - ] - }, - { - "title": "Links", - "description": [ - "YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA", - "Patreon: https://www.patreon.com/vylpes", - "Twitch: https://www.twitch.tv/vylpes_", - "Twitter: https://twitter.com/vylpes", - "Blog: https://vylpes.xyz" - ], - "footer": "Last updated 01/02/2022" } ] \ No newline at end of file diff --git a/data/rules/501231711271780357.json b/data/rules/501231711271780357.json index 324c40e..5b1ef37 100644 --- a/data/rules/501231711271780357.json +++ b/data/rules/501231711271780357.json @@ -6,7 +6,7 @@ "If moderators know you're under 13, we will have to ban you!*", "", "You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.", - "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message Vylpes#5725." + "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message <@147392775707426816>." ] }, { diff --git a/data/usage/config.txt b/data/usage/config.txt new file mode 100644 index 0000000..6a11414 --- /dev/null +++ b/data/usage/config.txt @@ -0,0 +1,35 @@ +USAGE: [value] + +===[ KEYS ]=== +bot.prefix: The bot prefix for the server (Default: "v!") + +commands.disabled: Disabled commands, separated by commas (Default: "") + +role.moderator: The moderator role name (Default: "Moderator") +role.administrator: The administrator role name (Default: "Administrator") + +rules.file: The location of the rules file (Default: "data/rules/rules") + +channels.logs.message: The channel message events will be logged to (Default: "message-logs") +channels.logs.member: The channel member events will be logged to (Default: "member-logs") +channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs") + +verification.enabled: Enables/Disables the verification feature (Default: "false") +verification.channel: The channel to listen to for entry codes (Default: "entry") +verification.role: The server access role (Default: "Entry") +verification.code: The entry code for the channel (Default: "") + +event.message.delete.enabled: Enables/Disables the message delete log event (Default: "false") +event.message.delete.channel: Sets the channel the bot will log message delete events to (Default: "message-logs") + +event.message.update.enabled: Enables/Disables the message delete log event (Default: "false") +event.message.update.channel: Sets the channel the bot will log message delete events to (Default: "message-logs") + +event.member.add.enabled: Enables/Disables the member join log event (Default: "false") +event.member.add.channel: Sets the channel the bot will log member join events to (Default: "member-logs") + +event.member.remove.enabled: Enables/Disables the member leave log event (Default: "false") +event.member.remove.channel: Sets the channel the bot will log member leave events to (Default: "member-logs") + +event.member.update.enabled: Enables/Disables the member update log event (Default: "false") +event.member.update.channel: Sets the channel the bot will log member update events to (Default: "member-logs") \ No newline at end of file diff --git a/data/usage/lobby.txt b/data/usage/lobby.txt new file mode 100644 index 0000000..6fe4a3b --- /dev/null +++ b/data/usage/lobby.txt @@ -0,0 +1,8 @@ +USAGE: config [cooldown] [Game Name] + +===[ EXAMPLE ]=== +To add a channel: +- config add 000000000000000000 000000000000000000 30 Game Name + +To remove a channel: +- config remove 000000000000000000 \ No newline at end of file diff --git a/data/usage/role.txt b/data/usage/role.txt new file mode 100644 index 0000000..0e8aa07 --- /dev/null +++ b/data/usage/role.txt @@ -0,0 +1,8 @@ +USAGE: config + +===[ EXAMPLE ]=== +To add a role: +- config add 000000000000000000 + +To remove a role: +- config remove 000000000000000000 \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Audit.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Audit.sql new file mode 100644 index 0000000..fa4db1b --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Audit.sql @@ -0,0 +1,11 @@ +CREATE TABLE `audit` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `AuditId` varchar(255) NOT NULL, + `UserId` varchar(255) NOT NULL, + `AuditType` int NOT NULL, + `Reason` varchar(255) NOT NULL, + `ModeratorId` varchar(255) NOT NULL, + `ServerId` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/IgnoredChannel.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/IgnoredChannel.sql new file mode 100644 index 0000000..93d919b --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/IgnoredChannel.sql @@ -0,0 +1,5 @@ +CREATE TABLE `ignored_channel` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Lobby.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Lobby.sql new file mode 100644 index 0000000..e8ec996 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Lobby.sql @@ -0,0 +1,10 @@ +CREATE TABLE `lobby` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `ChannelId` varchar(255) NOT NULL, + `RoleId` varchar(255) NOT NULL, + `Cooldown` int NOT NULL, + `LastUsed` datetime NOT NULL, + `Name` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Role.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Role.sql new file mode 100644 index 0000000..7c781db --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Role.sql @@ -0,0 +1,7 @@ +CREATE TABLE `role` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `RoleId` varchar(255) NOT NULL, + `serverId` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Server.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Server.sql new file mode 100644 index 0000000..c6c721f --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Server.sql @@ -0,0 +1,5 @@ +CREATE TABLE `server` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/01-table/Setting.sql b/database/3.1/1662399171315-CreateBase/Up/01-table/Setting.sql new file mode 100644 index 0000000..dac609a --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/01-table/Setting.sql @@ -0,0 +1,8 @@ +CREATE TABLE `setting` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `Key` varchar(255) NOT NULL, + `Value` varchar(255) NOT NULL, + `serverId` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Audit.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Audit.sql new file mode 100644 index 0000000..b8411a1 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Audit.sql @@ -0,0 +1,2 @@ +ALTER TABLE `audit` + ADD PRIMARY KEY (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/IgnoredChannel.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/IgnoredChannel.sql new file mode 100644 index 0000000..1288be1 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/IgnoredChannel.sql @@ -0,0 +1,2 @@ +ALTER TABLE `ignored_channel` + ADD PRIMARY KEY (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Lobby.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Lobby.sql new file mode 100644 index 0000000..e8e34ef --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Lobby.sql @@ -0,0 +1,2 @@ +ALTER TABLE `lobby` + ADD PRIMARY KEY (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Role.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Role.sql new file mode 100644 index 0000000..5d0a591 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Role.sql @@ -0,0 +1,3 @@ +ALTER TABLE `role` + ADD PRIMARY KEY (`Id`), + ADD KEY `FK_d9e438d88cfb64f7f8e1ae593c3` (`serverId`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Server.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Server.sql new file mode 100644 index 0000000..951587f --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Server.sql @@ -0,0 +1,2 @@ +ALTER TABLE `server` + ADD PRIMARY KEY (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/02-key/Setting.sql b/database/3.1/1662399171315-CreateBase/Up/02-key/Setting.sql new file mode 100644 index 0000000..8d03bec --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/02-key/Setting.sql @@ -0,0 +1,3 @@ +ALTER TABLE `setting` + ADD PRIMARY KEY (`Id`), + ADD KEY `FK_a3623ec541bdb12fa0f58bdfde7` (`serverId`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/03-constraint/Role.sql b/database/3.1/1662399171315-CreateBase/Up/03-constraint/Role.sql new file mode 100644 index 0000000..22ffbc0 --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/03-constraint/Role.sql @@ -0,0 +1,2 @@ +ALTER TABLE `role` + ADD CONSTRAINT `FK_d9e438d88cfb64f7f8e1ae593c3` FOREIGN KEY (`serverId`) REFERENCES `server` (`Id`); \ No newline at end of file diff --git a/database/3.1/1662399171315-CreateBase/Up/03-constraint/Setting.sql b/database/3.1/1662399171315-CreateBase/Up/03-constraint/Setting.sql new file mode 100644 index 0000000..d5172ac --- /dev/null +++ b/database/3.1/1662399171315-CreateBase/Up/03-constraint/Setting.sql @@ -0,0 +1,2 @@ +ALTER TABLE `setting` + ADD CONSTRAINT `FK_a3623ec541bdb12fa0f58bdfde7` FOREIGN KEY (`serverId`) REFERENCES `server` (`Id`); \ No newline at end of file diff --git a/database/3.2.1/1709316734401-AddServerCacheDate/Up/01-Server.sql b/database/3.2.1/1709316734401-AddServerCacheDate/Up/01-Server.sql new file mode 100644 index 0000000..a3b92b9 --- /dev/null +++ b/database/3.2.1/1709316734401-AddServerCacheDate/Up/01-Server.sql @@ -0,0 +1,2 @@ +ALTER TABLE server +ADD LastCached datetime NOT NULL DEFAULT '2024-03-01 18:10:04'; \ No newline at end of file diff --git a/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql b/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql new file mode 100644 index 0000000..8412a02 --- /dev/null +++ b/database/3.2.4/1732973911304-createAutoKickConfig/Down/01-AutoKickConfig.sql @@ -0,0 +1 @@ +DROP TABLE auto_kick_config; diff --git a/database/3.2.4/1732973911304-createAutoKickConfig/Up/01-AutoKickConfig-Table.sql b/database/3.2.4/1732973911304-createAutoKickConfig/Up/01-AutoKickConfig-Table.sql new file mode 100644 index 0000000..e7d377a --- /dev/null +++ b/database/3.2.4/1732973911304-createAutoKickConfig/Up/01-AutoKickConfig-Table.sql @@ -0,0 +1,10 @@ +CREATE TABLE auto_kick_config ( + Id varchar(255) NOT NULL, + WhenCreated datetime NOT NULL, + WhenUpdated datetime NOT NULL, + ServerId varchar(255) NOT NULL, + RoleId varchar(255) NOT NULL, + KickTime int NOT NULL, + NoticeTime int NULL, + NoticeChannelId varchar(255) NULL +); diff --git a/database/3.2.4/1732973911304-createAutoKickConfig/Up/02-AutoKickConfig-Key.sql b/database/3.2.4/1732973911304-createAutoKickConfig/Up/02-AutoKickConfig-Key.sql new file mode 100644 index 0000000..e3fec43 --- /dev/null +++ b/database/3.2.4/1732973911304-createAutoKickConfig/Up/02-AutoKickConfig-Key.sql @@ -0,0 +1,2 @@ +ALTER TABLE auto_kick_config + ADD PRIMARY KEY (Id); diff --git a/database/3.3.0/1719856023429-CreateMoon/Up/01-Moon.sql b/database/3.3.0/1719856023429-CreateMoon/Up/01-Moon.sql new file mode 100644 index 0000000..744e5b4 --- /dev/null +++ b/database/3.3.0/1719856023429-CreateMoon/Up/01-Moon.sql @@ -0,0 +1,10 @@ +CREATE TABLE `moon` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `MoonNumber` int NOT NULL, + `UserId` varchar(255) NOT NULL, + `Description` varchar(255) NOT NULL, + `WhenArchived` datetime NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + diff --git a/database/3.3.0/1727286976268-CreateUserSetting/Up/01-UserSetting.sql b/database/3.3.0/1727286976268-CreateUserSetting/Up/01-UserSetting.sql new file mode 100644 index 0000000..6692756 --- /dev/null +++ b/database/3.3.0/1727286976268-CreateUserSetting/Up/01-UserSetting.sql @@ -0,0 +1,8 @@ +CREATE TABLE `user_setting` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `UserId` varchar(255) NOT NULL, + `Key` varchar(255) NOT NULL, + `Value` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/database/3.3.0/1727286976268-CreateUserSetting/Up/02-UserSettingKey.sql b/database/3.3.0/1727286976268-CreateUserSetting/Up/02-UserSettingKey.sql new file mode 100644 index 0000000..4c839b4 --- /dev/null +++ b/database/3.3.0/1727286976268-CreateUserSetting/Up/02-UserSettingKey.sql @@ -0,0 +1,2 @@ +ALTER TABLE user_setting + ADD PRIMARY KEY (Id); diff --git a/docker-compose.yml b/docker-compose.yml index a10aebd..025a674 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,24 +1,17 @@ version: "3.9" -services: - # discord: - # build: . +services: database: image: mysql/mysql-server command: --default-authentication-plugin=mysql_native_password restart: always environment: - - MYSQL_DATABASE=vylbot - - MYSQL_USER=dev - - MYSQL_PASSWORD=dev - - MYSQL_ROOT_PASSWORD=root + - MYSQL_DATABASE=$DB_NAME + - MYSQL_USER=$DB_AUTH_USER + - MYSQL_PASSWORD=$DB_AUTH_PASS + - MYSQL_ROOT_PASSWORD=$DB_AUTH_PASS + - MYSQL_ROOT_HOST=$DB_ROOT_HOST ports: - - 3306:3306 - - phpmyadmin: - image: phpmyadmin - restart: always - ports: - - 8080:80 - environment: - - PMA_ARBITRARY=1 \ No newline at end of file + - "$DB_PORT:3306" + volumes: + - $DB_DATA_LOCATION:/var/lib/mysql \ No newline at end of file diff --git a/ormconfig.json.template b/ormconfig.json.template deleted file mode 100644 index 6c48598..0000000 --- a/ormconfig.json.template +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "mysql", - "host": "localhost", - "port": 3306, - "username": "dev", - "password": "dev", - "database": "vylbot", - "synchronize": true, - "logging": false, - "entities": [ - "dist/entity/**/*.js" - ], - "migrations": [ - "dist/migration/**/*.js" - ], - "subscribers": [ - "dist/subscriber/**/*.js" - ], - "cli": { - "entitiesDir": "dist/entity", - "migrationsDir": "dist/migration", - "subscribersDir": "dist/subscriber" - } -} \ No newline at end of file diff --git a/package.json b/package.json index f25e13b..48dd379 100644 --- a/package.json +++ b/package.json @@ -1,38 +1,52 @@ { "name": "vylbot-app", - "version": "3.0", + "version": "3.2.4", "description": "A discord bot made for Vylpes' Den", "main": "./dist/vylbot", "typings": "./dist", "scripts": { + "clean": "rm -rf node_modules/ dist/", "build": "tsc", "start": "node ./dist/vylbot", - "test": "jest" + "test": "jest", + "db:up": "typeorm migration:run -d dist/database/dataSources/appDataSource.js", + "db:down": "typeorm migration:revert -d dist/database/dataSources/appDataSource.js", + "db:create": "typeorm migration:create ./src/database/migrations", + "release": "np --no-publish" }, "repository": { "type": "git", - "url": "git+https://github.com/Vylpes/vylbot-app.git" + "url": "https://github.com/Vylpes/vylbot-app" }, "author": "Vylpes", "license": "MIT", - "bugs": "https://github.com/Vylpes/vylbot-app/issues", + "bugs": { + "url": "https://github.com/Vylpes/vylbot-app/issues", + "email": "helpdesk@vylpes.com" + }, "homepage": "https://github.com/Vylpes/vylbot-app", + "funding": "https://ko-fi.com/vylpes", "dependencies": { - "@types/jest": "^27.0.3", - "@types/uuid": "^8.3.4", - "discord.js": "^13.6.0", - "dotenv": "^10.0.0", - "emoji-regex": "^9.2.0", - "jest": "^27.4.5", - "jest-mock-extended": "^2.0.4", + "@discordjs/rest": "^2.0.0", + "@types/uuid": "^10.0.0", + "cron": "^3.3.1", + "discord.js": "^14.3.0", + "dotenv": "^16.0.0", + "emoji-regex": "^10.0.0", + "minimatch": "10.0.1", "mysql": "^2.18.1", - "random-bunny": "^2.0.0", - "ts-jest": "^27.1.2", - "typeorm": "^0.2.44", - "uuid": "^8.3.2" + "random-bunny": "^2.1.6", + "typeorm": "^0.3.20" + }, + "resolutions": { }, "devDependencies": { - "@types/node": "^16.11.10", - "typescript": "^4.5.2" + "@types/jest": "^29.5.12", + "@types/node": "^22.0.0", + "jest": "^29.7.0", + "jest-mock-extended": "^3.0.7", + "np": "^10.0.0", + "ts-jest": "^29.2.4", + "typescript": "^5.0.0" } } diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..1116f5a --- /dev/null +++ b/renovate.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "baseBranches": ["develop"] +} diff --git a/scripts/deploy_prod.sh b/scripts/deploy_prod.sh new file mode 100644 index 0000000..7adef0a --- /dev/null +++ b/scripts/deploy_prod.sh @@ -0,0 +1,23 @@ +#! /bin/bash + +export PATH="$HOME/.yarn/bin:$PATH" +export PATH="$HOME/.nodeuse/bin:$PATH" + +export BOT_TOKEN=$(cat $HOME/scripts/vylbot/prod_key.txt) + +cd ~/apps/vylbot/vylbot_prod \ +&& git checkout main \ +&& git fetch \ +&& git pull \ +&& docker compose --file docker-compose.prod.yml down \ +&& (pm2 stop vylbot_prod || true) \ +&& (pm2 delete vylbot_prod || true) \ +&& cp .prod.env .env \ +&& yarn clean \ +&& yarn install --frozen-lockfile \ +&& yarn build \ +&& docker compose --file docker-compose.prod.yml up -d \ +&& echo "Sleeping for 10 seconds to let database load..." \ +&& sleep 10 \ +&& yarn run db:up \ +&& NODE_ENV=production pm2 start --name vylbot_prod dist/vylbot.js \ No newline at end of file diff --git a/scripts/deploy_stage.sh b/scripts/deploy_stage.sh new file mode 100644 index 0000000..d12a421 --- /dev/null +++ b/scripts/deploy_stage.sh @@ -0,0 +1,23 @@ +#! /bin/bash + +export PATH="$HOME/.yarn/bin:$PATH" +export PATH="$HOME/.nodeuse/bin:$PATH" + +export BOT_TOKEN=$(cat $HOME/scripts/vylbot/stage_key.txt) + +cd ~/apps/vylbot/vylbot_stage \ +&& git checkout develop \ +&& git fetch \ +&& git pull \ +&& docker compose --file docker-compose.stage.yml down \ +&& (pm2 stop vylbot_stage || true) \ +&& (pm2 delete vylbot_stage || true) \ +&& cp .stage.env .env \ +&& yarn clean \ +&& yarn install --frozen-lockfile \ +&& yarn build \ +&& docker compose --file docker-compose.stage.yml up -d \ +&& echo "Sleeping for 10 seconds to let database load..." \ +&& sleep 10 \ +&& yarn run db:up \ +&& NODE_ENV=production pm2 start --name vylbot_stage dist/vylbot.js \ No newline at end of file diff --git a/src/buttonEvents/304276391837302787/moons.ts b/src/buttonEvents/304276391837302787/moons.ts new file mode 100644 index 0000000..e13a4c0 --- /dev/null +++ b/src/buttonEvents/304276391837302787/moons.ts @@ -0,0 +1,14 @@ +import {ButtonInteraction} from "discord.js"; +import {ButtonEvent} from "../../type/buttonEvent"; +import List from "./moons/list"; + +export default class Moons extends ButtonEvent { + public override async execute(interaction: ButtonInteraction): Promise { + const action = interaction.customId.split(" ")[1]; + + switch (action) { + case "list": + await List(interaction); + } + } +} diff --git a/src/buttonEvents/304276391837302787/moons/list.ts b/src/buttonEvents/304276391837302787/moons/list.ts new file mode 100644 index 0000000..a2b3cc4 --- /dev/null +++ b/src/buttonEvents/304276391837302787/moons/list.ts @@ -0,0 +1,68 @@ +import {ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, EmbedBuilder} from "discord.js"; +import Moon from "../../../database/entities/304276391837302787/Moon"; +import EmbedColours from "../../../constants/EmbedColours"; +import UserSetting from "../../../database/entities/UserSetting"; + +export default async function List(interaction: ButtonInteraction) { + if (!interaction.guild) return; + + const userId = interaction.customId.split(" ")[2]; + const page = interaction.customId.split(" ")[3]; + + if (!userId || !page) return; + + const pageNumber = Number(page); + + const member = interaction.guild.members.cache.find(x => x.user.id == userId) || await interaction.guild.members.fetch(userId); + + const pageLength = 10; + + const moons = await Moon.FetchPaginatedMoonsByUserId(userId, pageLength, pageNumber); + + if (!moons) { + await interaction.reply(`${member?.user.username ?? "This user"} does not have any moons or page is invalid.`); + return; + } + + const moonSetting = await UserSetting.FetchOneByKey(userId, "moons"); + const totalMoons = moonSetting && Number(moonSetting.Value) ? Number(moonSetting.Value) : 0; + + const totalPages = Math.ceil(moons[1] / pageLength); + + let description = ["*none*"]; + + if (moons[0].length > 0) { + description = moons[0].flatMap(x => `**${x.MoonNumber} -** ${x.Description.slice(0, 15)}`); + } + + const moonDifference = totalMoons - moons[1]; + const isLastPage = pageNumber + 1 == totalPages || moons[0].length == 0; + + if (isLastPage && moonDifference > 0) { + description.push(`...plus ${moonDifference} more untracked`); + } + + const embed = new EmbedBuilder() + .setTitle(`${member.user.username}'s Moons`) + .setColor(EmbedColours.Ok) + .setDescription(description.join("\n")) + .setFooter({ text: `Page ${pageNumber + 1} of ${totalPages} ยท ${totalMoons} moons` }); + + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId(`moons list ${userId} ${pageNumber - 1}`) + .setLabel("Previous") + .setStyle(ButtonStyle.Primary) + .setDisabled(pageNumber == 0), + new ButtonBuilder() + .setCustomId(`moons list ${userId} ${pageNumber + 1}`) + .setLabel("Next") + .setStyle(ButtonStyle.Primary) + .setDisabled(isLastPage)); + + await interaction.update({ + embeds: [ embed ], + components: [ row ], + }); +} diff --git a/src/buttonEvents/verify.ts b/src/buttonEvents/verify.ts new file mode 100644 index 0000000..c3f696c --- /dev/null +++ b/src/buttonEvents/verify.ts @@ -0,0 +1,42 @@ +import { ButtonInteraction, CacheType } from "discord.js"; +import { ButtonEvent } from "../type/buttonEvent"; +import SettingsHelper from "../helpers/SettingsHelper"; + +export default class Verify extends ButtonEvent { + public override async execute(interaction: ButtonInteraction) { + if (!interaction.guildId || !interaction.guild) return; + + const roleName = await SettingsHelper.GetSetting("verification.role", interaction.guildId); + + if (!roleName) return; + + const role = interaction.guild.roles.cache.find(x => x.name == roleName); + + if (!role) { + await interaction.reply({ + content: `Unable to find the role, ${roleName}`, + ephemeral: true, + }); + + return; + } + + const member = interaction.guild.members.cache.find(x => x.id == interaction.user.id); + + if (!member || !member.manageable) { + await interaction.reply({ + content: "Unable to give role to user", + ephemeral: true, + }); + + return; + } + + await member.roles.add(role); + + await interaction.reply({ + content: "Given role", + ephemeral: true, + }); + } +} \ No newline at end of file diff --git a/src/client/client.ts b/src/client/client.ts index 36f738f..86b34bd 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,21 +1,28 @@ -import { Client } from "discord.js"; +import { Client, Partials } from "discord.js"; import * as dotenv from "dotenv"; -import { createConnection } from "typeorm"; -import DefaultValues from "../constants/DefaultValues"; +import { EventType } from "../constants/EventType"; import ICommandItem from "../contracts/ICommandItem"; import IEventItem from "../contracts/IEventItem"; import { Command } from "../type/command"; -import { Event } from "../type/event"; import { Events } from "./events"; import { Util } from "./util"; +import AppDataSource from "../database/dataSources/appDataSource"; +import ButtonEventItem from "../contracts/ButtonEventItem"; +import { ButtonEvent } from "../type/buttonEvent"; +import CacheHelper from "../helpers/CacheHelper"; +import TimerHelper from "../helpers/TimerHelper"; +import AutoKick from "../timers/AutoKick"; export class CoreClient extends Client { private static _commandItems: ICommandItem[]; private static _eventItems: IEventItem[]; - + private static _buttonEvents: ButtonEventItem[]; + private static _baseClient: Client; + private _events: Events; private _util: Util; + private _timerHelper: TimerHelper; public static get commandItems(): ICommandItem[] { return this._commandItems; @@ -25,17 +32,25 @@ export class CoreClient extends Client { return this._eventItems; } - constructor(intents: number[], devmode: boolean = false) { - super({ intents: intents }); - dotenv.config(); + public static get buttonEvents(): ButtonEventItem[] { + return this._buttonEvents; + } - DefaultValues.useDevPrefix = devmode; + public static get baseClient(): Client { + return this._baseClient; + } + + constructor(intents: number[], partials: Partials[]) { + super({ intents: intents, partials: partials }); + dotenv.config(); CoreClient._commandItems = []; CoreClient._eventItems = []; + CoreClient._buttonEvents = []; this._events = new Events(); this._util = new Util(); + this._timerHelper = new TimerHelper(); } public async start() { @@ -44,19 +59,29 @@ export class CoreClient extends Client { return; } - await createConnection().catch(e => { - console.error(e); - return; - }); + CoreClient._baseClient = this; - super.on("messageCreate", (message) => { - this._events.onMessageCreate(message, CoreClient._commandItems) - }); + await AppDataSource.initialize() + .then(() => { + console.log("Data Source Initialized"); + + this._timerHelper.AddTimer("0 * * * *", "Europe/London", AutoKick, false); + + this._timerHelper.StartAllTimers(); + }) + .catch((err) => console.error("Error Initialising Data Source", err)); + + super.on("interactionCreate", this._events.onInteractionCreate); super.on("ready", this._events.onReady); - super.login(process.env.BOT_TOKEN); + await super.login(process.env.BOT_TOKEN); + + this.guilds.cache.forEach(async (guild) => { + await CacheHelper.UpdateServerCache(guild); + }); this._util.loadEvents(this, CoreClient._eventItems); + this._util.loadSlashCommands(this); } public static RegisterCommand(name: string, command: Command, serverId?: string) { @@ -69,11 +94,21 @@ export class CoreClient extends Client { CoreClient._commandItems.push(item); } - public static RegisterEvent(event: Event) { + public static RegisterEvent(eventType: EventType, func: Function) { const item: IEventItem = { - Event: event, + EventType: eventType, + ExecutionFunction: func, }; CoreClient._eventItems.push(item); } + + public static RegisterButtonEvent(buttonId: string, event: ButtonEvent) { + const item: ButtonEventItem = { + ButtonId: buttonId, + Event: event, + }; + + CoreClient._buttonEvents.push(item); + } } diff --git a/src/client/events.ts b/src/client/events.ts index 059df52..97d8cc7 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,32 +1,17 @@ -import { Message } from "discord.js"; -import ICommandItem from "../contracts/ICommandItem"; -import SettingsHelper from "../helpers/SettingsHelper"; -import { Util } from "./util"; +import { Interaction } from "discord.js"; +import ChatInputCommand from "./interactionCreate/chatInputCommand"; +import Button from "./interactionCreate/button"; export class Events { - private _util: Util; + public async onInteractionCreate(interaction: Interaction) { + if (!interaction.guildId) return; - constructor() { - this._util = new Util(); - } + if (interaction.isChatInputCommand()) { + ChatInputCommand.onChatInput(interaction); + } - // Emit when a message is sent - // Used to check for commands - public async onMessageCreate(message: Message, commands: ICommandItem[]) { - if (!message.guild) return; - if (message.author.bot) return; - - const prefix = await SettingsHelper.GetSetting("bot.prefix", message.guild.id); - - if (!prefix) return; - - if (message.content.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase()) { - const args = message.content.substring(prefix.length).split(" "); - const name = args.shift(); - - if (!name) return; - - await this._util.loadCommand(name, args, message, commands); + if (interaction.isButton()) { + Button.onButtonClicked(interaction); } } diff --git a/src/client/interactionCreate/button.ts b/src/client/interactionCreate/button.ts new file mode 100644 index 0000000..f64f6c7 --- /dev/null +++ b/src/client/interactionCreate/button.ts @@ -0,0 +1,17 @@ +import { ButtonInteraction } from "discord.js"; +import { CoreClient } from "../client"; + +export default class Button { + public static async onButtonClicked(interaction: ButtonInteraction) { + if (!interaction.isButton) return; + + const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(" ")[0]); + + if (!item) { + await interaction.reply("Event not found."); + return; + } + + item.Event.execute(interaction); + } +} \ No newline at end of file diff --git a/src/client/interactionCreate/chatInputCommand.ts b/src/client/interactionCreate/chatInputCommand.ts new file mode 100644 index 0000000..8690994 --- /dev/null +++ b/src/client/interactionCreate/chatInputCommand.ts @@ -0,0 +1,27 @@ +import { Interaction } from "discord.js"; +import { CoreClient } from "../client"; +import ICommandItem from "../../contracts/ICommandItem"; + +export default class ChatInputCommand { + public static async onChatInput(interaction: Interaction) { + if (!interaction.isChatInputCommand()) return; + + const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId); + const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId); + + let itemToUse: ICommandItem; + + if (!itemForServer) { + if (!item) { + await interaction.reply("Command not found."); + return; + } + + itemToUse = item; + } else { + itemToUse = itemForServer; + } + + itemToUse.Command.execute(interaction); + } +} \ No newline at end of file diff --git a/src/client/util.ts b/src/client/util.ts index f136e26..e367324 100644 --- a/src/client/util.ts +++ b/src/client/util.ts @@ -1,101 +1,95 @@ -// Required Components -import { Client, Message } from "discord.js"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandItem from "../contracts/ICommandItem"; +import { Client, REST, Routes, SlashCommandBuilder } from "discord.js"; +import { EventType } from "../constants/EventType"; import IEventItem from "../contracts/IEventItem"; -import SettingsHelper from "../helpers/SettingsHelper"; -import StringTools from "../helpers/StringTools"; -import { CommandResponse } from "../constants/CommandResponse"; -import ErrorMessages from "../constants/ErrorMessages"; +import { CoreClient } from "./client"; -// Util Class export class Util { - public async loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]) { - if (!message.member) return; - if (!message.guild) return; + public loadSlashCommands(client: Client) { + const registeredCommands = CoreClient.commandItems; - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", message.guild?.id); - const disabledCommands = disabledCommandsString?.split(","); + const globalCommands = registeredCommands.filter(x => !x.ServerId); + const guildCommands = registeredCommands.filter(x => x.ServerId); - if (disabledCommands?.find(x => x == name)) { - message.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); - return; - } + const globalCommandData: SlashCommandBuilder[] = globalCommands + .filter(x => x.Command.CommandBuilder) + .flatMap(x => x.Command.CommandBuilder); - const item = commands.find(x => x.Name == name && !x.ServerId); - const itemForServer = commands.find(x => x.Name == name && x.ServerId == message.guild?.id); + const guildIds: string[] = []; - let itemToUse: ICommandItem; - - if (!itemForServer) { - if (!item) { - message.reply('Command not found'); - return; + for (let command of guildCommands) { + if (!guildIds.find(x => x == command.ServerId)) { + guildIds.push(command.ServerId!); } - - itemToUse = item; - } else { - itemToUse = itemForServer; } - const requiredRoles = itemToUse.Command.Roles; + const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN!); - if (message.author.id != process.env.BOT_OWNERID && message.author.id != message.guild.ownerId) { - for (const i in requiredRoles) { - if (message.guild) { - const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, message.guild?.id); - - if (!setting) { - message.reply("Unable to verify if you have this role, please contact your bot administrator"); - return; - } - - if (!message.member.roles.cache.find(role => role.name == setting)) { - message.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`); - return; - } + rest.put( + Routes.applicationCommands(process.env.BOT_CLIENTID!), + { + body: globalCommandData + } + ); + + for (let guild of guildIds) { + const guildCommandData = guildCommands.filter(x => x.ServerId == guild) + .filter(x => x.Command.CommandBuilder) + .flatMap(x => x.Command.CommandBuilder); + + if (!client.guilds.cache.has(guild)) continue; + + rest.put( + Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild), + { + body: guildCommandData } - } + ) } - - const context: ICommandContext = { - name: name, - args: args, - message: message - }; - - const precheckResponse = itemToUse.Command.precheck(context); - const precheckAsyncResponse = await itemToUse.Command.precheckAsync(context); - - if (precheckResponse != CommandResponse.Ok) { - message.reply(ErrorMessages.GetErrorMessage(precheckResponse)); - return; - } - - if (precheckAsyncResponse != CommandResponse.Ok) { - message.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse)); - return; - } - - itemToUse.Command.execute(context); } // Load the events loadEvents(client: Client, events: IEventItem[]) { events.forEach((e) => { - client.on('channelCreate', e.Event.channelCreate); - client.on('channelDelete', e.Event.channelDelete); - client.on('channelUpdate', e.Event.channelUpdate); - client.on('guildBanAdd', e.Event.guildBanAdd); - client.on('guildBanRemove', e.Event.guildBanRemove); - client.on('guildCreate', e.Event.guildCreate); - client.on('guildMemberAdd', e.Event.guildMemberAdd); - client.on('guildMemberRemove', e.Event.guildMemberRemove); - client.on('guildMemberUpdate', e.Event.guildMemberUpdate); - client.on('messageCreate', e.Event.messageCreate); - client.on('messageDelete', e.Event.messageDelete); - client.on('messageUpdate', e.Event.messageUpdate); - client.on('ready', e.Event.ready); + switch(e.EventType) { + case EventType.ChannelCreate: + client.on('channelCreate', (channel) => e.ExecutionFunction(channel)); + break; + case EventType.ChannelDelete: + client.on('channelDelete', (channel) => e.ExecutionFunction(channel)); + break; + case EventType.ChannelUpdate: + client.on('channelUpdate', (channel) => e.ExecutionFunction(channel)); + break; + case EventType.GuildBanAdd: + client.on('guildBanAdd', (ban) => e.ExecutionFunction(ban)); + break; + case EventType.GuildBanRemove: + client.on('guildBanRemove', (ban) => e.ExecutionFunction(ban)); + break; + case EventType.GuildCreate: + client.on('guildCreate', (guild) => e.ExecutionFunction(guild)); + break; + case EventType.GuildMemberAdd: + client.on('guildMemberAdd', (member) => e.ExecutionFunction(member)); + break; + case EventType.GuildMemberRemove: + client.on('guildMemberRemove', (member) => e.ExecutionFunction(member)); + break; + case EventType.GuildMemberUpdate: + client.on('guildMemberUpdate', (oldMember, newMember) => e.ExecutionFunction(oldMember, newMember)); + break; + case EventType.MessageCreate: + client.on('messageCreate', (message) => e.ExecutionFunction(message)); + break; + case EventType.MessageDelete: + client.on('messageDelete', (message) => e.ExecutionFunction(message)); + break; + case EventType.MessageUpdate: + client.on('messageUpdate', (oldMessage, newMessage) => e.ExecutionFunction(oldMessage, newMessage)); + break; + default: + console.error('Event not implemented.'); + } }); } } diff --git a/src/commands/304276391837302787/moons.ts b/src/commands/304276391837302787/moons.ts new file mode 100644 index 0000000..dc884a0 --- /dev/null +++ b/src/commands/304276391837302787/moons.ts @@ -0,0 +1,48 @@ +import { Command } from "../../type/command"; +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import ListMoons from "./moons/list"; +import AddMoon from "./moons/add"; + +export default class Moons extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName("moons") + .setDescription("View and create moons") + .addSubcommand(subcommand => + subcommand + .setName('list') + .setDescription('List moons you have obtained') + .addUserOption(option => + option + .setName("user") + .setDescription("The user to view (Defaults to yourself)")) + .addNumberOption(option => + option + .setName("page") + .setDescription("The page to start with"))) + .addSubcommand(subcommand => + subcommand + .setName('add') + .setDescription('Add a moon to your count!') + .addStringOption(option => + option + .setName("description") + .setDescription("What deserved a moon?") + .setRequired(true))); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + switch (interaction.options.getSubcommand()) { + case "list": + await ListMoons(interaction); + break; + case "add": + await AddMoon(interaction); + break; + } + } +} diff --git a/src/commands/304276391837302787/moons/add.ts b/src/commands/304276391837302787/moons/add.ts new file mode 100644 index 0000000..ffc79c0 --- /dev/null +++ b/src/commands/304276391837302787/moons/add.ts @@ -0,0 +1,40 @@ +import {CommandInteraction, EmbedBuilder} from "discord.js"; +import Moon from "../../../database/entities/304276391837302787/Moon"; +import EmbedColours from "../../../constants/EmbedColours"; +import UserSetting from "../../../database/entities/UserSetting"; + +export default async function AddMoon(interaction: CommandInteraction) { + const description = interaction.options.get("description", true).value?.toString(); + + if (!description || description.length > 255) { + await interaction.reply("Name must be less than 255 characters!"); + return; + } + + let moonSetting = await UserSetting.FetchOneByKey(interaction.user.id, "moons"); + const moonCount = moonSetting && Number(moonSetting.Value) ? Number(moonSetting.Value) : 0; + + if (moonSetting) { + moonSetting.UpdateValue(`${moonCount + 1}`); + } else { + moonSetting = new UserSetting(interaction.user.id, "moons", `${moonCount + 1}`); + } + + await moonSetting.Save(UserSetting, moonSetting); + + const allMoons = await Moon.FetchMoonCountByUserId(interaction.user.id); + + const moonNumber = allMoons + 1; + + const moon = new Moon(moonNumber, description, interaction.user.id); + + await moon.Save(Moon, moon); + + const embed = new EmbedBuilder() + .setTitle(`${interaction.user.globalName} Got A Moon!`) + .setColor(EmbedColours.Moon) + .setDescription(`**${moon.MoonNumber} -** ${moon.Description}`) + .setThumbnail("https://cdn.discordapp.com/emojis/374131312182689793.webp?size=96&quality=lossless"); + + await interaction.reply({ embeds: [ embed ] }); +} diff --git a/src/commands/304276391837302787/moons/list.ts b/src/commands/304276391837302787/moons/list.ts new file mode 100644 index 0000000..b865161 --- /dev/null +++ b/src/commands/304276391837302787/moons/list.ts @@ -0,0 +1,55 @@ +import {ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder} from "discord.js"; +import Moon from "../../../database/entities/304276391837302787/Moon"; +import EmbedColours from "../../../constants/EmbedColours"; +import UserSetting from "../../../database/entities/UserSetting"; + +export default async function ListMoons(interaction: CommandInteraction) { + const user = interaction.options.get("user")?.user ?? interaction.user; + const page = interaction.options.get("page")?.value as number ?? 0; + + const pageLength = 10; + + const moons = await Moon.FetchPaginatedMoonsByUserId(user.id, pageLength, page); + + const moonSetting = await UserSetting.FetchOneByKey(interaction.user.id, "moons"); + const totalMoons = moonSetting && Number(moonSetting.Value) ? Number(moonSetting.Value) : 0; + + const totalPages = Math.ceil(moons[1] / pageLength); + + let description = ["*none*"]; + + if (moons[0].length > 0) { + description = moons[0].flatMap(x => `**${x.MoonNumber} -** ${x.Description.slice(0, 15)}`); + } + + const moonDifference = totalMoons - moons[1]; + const isLastPage = page + 1 == totalPages || moons[0].length == 0; + + if (isLastPage && moonDifference > 0) { + description.push(`...plus ${moonDifference} more untracked`); + } + + const embed = new EmbedBuilder() + .setTitle(`${user.username}'s Moons`) + .setColor(EmbedColours.Ok) + .setDescription(description.join("\n")) + .setFooter({ text: `Page ${page + 1} of ${totalPages} ยท ${totalMoons} moons` }); + + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId(`moons list ${user.id} ${page - 1}`) + .setLabel("Previous") + .setStyle(ButtonStyle.Primary) + .setDisabled(page == 0), + new ButtonBuilder() + .setCustomId(`moons list ${user.id} ${page + 1}`) + .setLabel("Next") + .setStyle(ButtonStyle.Primary) + .setDisabled(isLastPage)); + + await interaction.reply({ + embeds: [ embed ], + components: [ row ], + }); +} diff --git a/src/commands/501231711271780357/Lobby/add.ts b/src/commands/501231711271780357/Lobby/add.ts new file mode 100644 index 0000000..b30f736 --- /dev/null +++ b/src/commands/501231711271780357/Lobby/add.ts @@ -0,0 +1,58 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby"; + +export default class AddRole extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName('addlobby') + .setDescription('Add lobby channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel') + .setRequired(true)) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role to ping on request') + .setRequired(true)) + .addNumberOption(option => + option + .setName('cooldown') + .setDescription('The cooldown in minutes') + .setRequired(true)) + .addStringOption(option => + option + .setName('name') + .setDescription('The game name') + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + const role = interaction.options.get('role'); + const cooldown = interaction.options.get('cooldown'); + const gameName = interaction.options.get('name'); + + if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) { + await interaction.reply('Fields are required.'); + return; + } + + const lobby = await eLobby.FetchOneByChannelId(channel.channel.id); + + if (lobby) { + await interaction.reply('This channel has already been setup.'); + return; + } + + const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string); + await entity.Save(eLobby, entity); + + await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown.value} minutes \` and will ping \`${role.name}\` on use`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/list.ts b/src/commands/501231711271780357/Lobby/list.ts new file mode 100644 index 0000000..fe2afde --- /dev/null +++ b/src/commands/501231711271780357/Lobby/list.ts @@ -0,0 +1,48 @@ +import { CacheType, CommandInteraction, EmbedBuilder, GuildBasedChannel, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby"; +import EmbedColours from "../../../constants/EmbedColours"; + +export default class ListLobby extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName('listlobby') + .setDescription('Lists all channels set up as lobbies') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild) { + await interaction.reply('Guild not found.'); + return; + } + + const channels: eLobby[] = []; + + for (let channel of interaction.guild.channels.cache.map(x => x)) { + const lobby = await eLobby.FetchOneByChannelId(channel.id); + + if (lobby) { + channels.push(lobby); + } + } + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Lobbies") + .setDescription(`Channels: ${channels.length}`); + + for (let lobby of channels) { + embed.addFields([ + { + name: `# ${lobby.Name}`, + value: `Last Used: ${lobby.LastUsed}` + } + ]); + } + + await interaction.reply({ embeds: [ embed ]}); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/lobby.ts b/src/commands/501231711271780357/Lobby/lobby.ts new file mode 100644 index 0000000..d2f5f27 --- /dev/null +++ b/src/commands/501231711271780357/Lobby/lobby.ts @@ -0,0 +1,41 @@ +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby"; + +export default class Lobby extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName('lobby') + .setDescription('Attempt to organise a lobby'); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.channelId) return; + + const lobby = await eLobby.FetchOneByChannelId(interaction.channelId); + + if (!lobby) { + await interaction.reply('This channel is disabled from using the lobby command.'); + return; + } + + const timeNow = Date.now(); + const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms + const timeAgo = timeNow - timeLength; + + // If it was less than x minutes ago + if (lobby.LastUsed.getTime() > timeAgo) { + const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60); + + await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); + return; + } + + lobby.MarkAsUsed(); + await lobby.Save(eLobby, lobby); + + await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/remove.ts b/src/commands/501231711271780357/Lobby/remove.ts new file mode 100644 index 0000000..eec99d5 --- /dev/null +++ b/src/commands/501231711271780357/Lobby/remove.ts @@ -0,0 +1,40 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby"; +import BaseEntity from "../../../contracts/BaseEntity"; + +export default class RemoveLobby extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName('removelobby') + .setDescription('Remove a lobby channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel') + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + + if (!channel || !channel.channel) { + await interaction.reply('Channel is required.'); + return; + } + + const entity = await eLobby.FetchOneByChannelId(channel.channel.id); + + if (!entity) { + await interaction.reply('Channel not found.'); + return; + } + + await BaseEntity.Remove(eLobby, entity); + + await interaction.reply(`Removed <#${channel.channel.id}> from the list of lobby channels`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/entry.ts b/src/commands/501231711271780357/entry.ts index 6f9dd83..d4b4acb 100644 --- a/src/commands/501231711271780357/entry.ts +++ b/src/commands/501231711271780357/entry.ts @@ -1,5 +1,5 @@ -import { ICommandContext } from "../../contracts/ICommandContext"; -import PublicEmbed from "../../helpers/embeds/PublicEmbed"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; import SettingsHelper from "../../helpers/SettingsHelper"; import { Command } from "../../type/command"; @@ -7,19 +7,25 @@ export default class Entry extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + this.CommandBuilder = new SlashCommandBuilder() + .setName('entry') + .setDescription('Sends the entry embed') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!interaction.channel) return; - const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", context.message.guild.id) || "rules"; + const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", interaction.guildId) || "rules"; - const embedInfo = new PublicEmbed(context, "", `Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Welcome") + .setDescription(`Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`); - embedInfo.SendToCurrentChannel(); + const channel = interaction.channel as TextChannel; + + await channel.send({ embeds: [ embed ]}); } } \ No newline at end of file diff --git a/src/commands/501231711271780357/lobby.ts b/src/commands/501231711271780357/lobby.ts deleted file mode 100644 index 78ca89b..0000000 --- a/src/commands/501231711271780357/lobby.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { TextChannel } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; -import { Command } from "../../type/command"; -import { default as eLobby } from "../../entity/501231711271780357/Lobby"; -import SettingsHelper from "../../helpers/SettingsHelper"; -import PublicEmbed from "../../helpers/embeds/PublicEmbed"; -import { readFileSync } from "fs"; -import ErrorEmbed from "../../helpers/embeds/ErrorEmbed"; -import BaseEntity from "../../contracts/BaseEntity"; - -export default class Lobby extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; - - switch (context.args[0]) { - case "config": - await this.UseConfig(context); - break; - default: - await this.UseDefault(context); - } - } - - // ======= - // Default - // ======= - - private async UseDefault(context: ICommandContext) { - const channel = context.message.channel as TextChannel; - const channelId = channel.id; - - const lobby = await eLobby.FetchOneByChannelId(channelId); - - if (!lobby) { - this.SendDisabled(context); - return; - } - - const timeNow = Date.now(); - const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms - const timeAgo = timeNow - timeLength; - - // If it was less than x minutes ago - if (lobby.LastUsed.getTime() > timeAgo) { - this.SendOnCooldown(context, timeLength, new Date(timeNow), lobby.LastUsed); - return; - } - - await this.RequestLobby(context, lobby); - } - - private async RequestLobby(context: ICommandContext, lobby: eLobby) { - lobby.MarkAsUsed(); - await lobby.Save(eLobby, lobby); - - context.message.channel.send(`${context.message.author} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`); - } - - private SendOnCooldown(context: ICommandContext, timeLength: number, timeNow: Date, timeUsed: Date) { - const timeLeft = Math.ceil((timeLength - (timeNow.getTime() - timeUsed.getTime())) / 1000 / 60); - - context.message.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); - } - - private SendDisabled(context: ICommandContext) { - context.message.reply("This channel hasn't been setup for lobbies."); - } - - // ====== - // Config - // ====== - private async UseConfig(context: ICommandContext) { - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id); - - if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) { - const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command"); - errorEmbed.SendToCurrentChannel(); - - return; - } - - switch (context.args[1]) { - case "add": - await this.AddLobbyConfig(context); - break; - case "remove": - await this.RemoveLobbyConfig(context); - break; - case "help": - default: - this.SendConfigHelp(context); - } - } - - private SendConfigHelp(context: ICommandContext) { - const helpText = readFileSync(`${process.cwd()}/data/lobbyConfig.txt`).toString(); - - const embed = new PublicEmbed(context, "Configure Lobby Command", helpText); - embed.SendToCurrentChannel(); - } - - private async AddLobbyConfig(context: ICommandContext) { - const channel = context.message.guild!.channels.cache.find(x => x.name == context.args[2]); - const role = context.message.guild!.roles.cache.find(x => x.name == context.args[3]); - const cooldown = context.args[4] || "30"; - const gameName = context.args.splice(5).join(" "); - - if (!channel || !role) { - this.SendConfigHelp(context); - return; - } - - const entity = new eLobby(channel.id, role.id, Number.parseInt(cooldown), gameName); - await entity.Save(eLobby, entity); - - const embed = new PublicEmbed(context, "", "Added new lobby channel"); - embed.SendToCurrentChannel(); - } - - private async RemoveLobbyConfig(context: ICommandContext) { - const channel = context.message.guild!.channels.cache.find(x => x.name == context.args[2]); - - if (!channel) { - this.SendConfigHelp(context); - return; - } - - const entity = await eLobby.FetchOneByChannelId(channel.id); - - if (entity) { - await BaseEntity.Remove(eLobby, entity); - } - - const embed = new PublicEmbed(context, "", "Removed lobby channel"); - embed.SendToCurrentChannel(); - } -} \ No newline at end of file diff --git a/src/commands/Role/config.ts b/src/commands/Role/config.ts new file mode 100644 index 0000000..ca653b6 --- /dev/null +++ b/src/commands/Role/config.ts @@ -0,0 +1,54 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../type/command"; +import { default as eRole } from "../../database/entities/Role"; +import Server from "../../database/entities/Server"; + +export default class ConfigRole extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName('configrole') + .setDescription('Toggle your roles') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role name') + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId || !interaction.guild) return; + if (!interaction.member) return; + + const role = interaction.options.get('role'); + + if (!role || !role.role) { + await interaction.reply('Fields are required.'); + return; + } + + const existingRole = await eRole.FetchOneByRoleId(role.role.id); + + if (existingRole) { + await eRole.Remove(eRole, existingRole); + + await interaction.reply('Removed role from configuration.'); + } else { + const server = await Server.FetchOneById(Server, interaction.guildId); + + if (!server) { + await interaction.reply('This server has not been setup.'); + return; + } + + const newRole = new eRole(role.role.id); + newRole.SetServer(server); + + await newRole.Save(eRole, newRole); + + await interaction.reply('Added role to configuration.'); + } + } +} diff --git a/src/commands/Role/role.ts b/src/commands/Role/role.ts new file mode 100644 index 0000000..09e5342 --- /dev/null +++ b/src/commands/Role/role.ts @@ -0,0 +1,109 @@ +import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../type/command"; +import { default as eRole } from "../../database/entities/Role"; +import EmbedColours from "../../constants/EmbedColours"; + +export default class Role extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName('role') + .setDescription('Toggle your roles') + .addSubcommand(subcommand => + subcommand + .setName('toggle') + .setDescription('Toggle your role') + .addRoleOption(option => + option + .setName('role') + .setDescription('The role name') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('list') + .setDescription('List togglable roles')); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + switch (interaction.options.getSubcommand()) { + case 'toggle': + await this.ToggleRole(interaction); + break; + case 'list': + await this.SendRolesList(interaction); + break; + default: + await interaction.reply('Subcommand not found.'); + } + } + + private async SendRolesList(interaction: CommandInteraction) { + const roles = await this.GetRolesList(interaction); + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Roles") + .setDescription(`Roles: ${roles.length}\n\n${roles.join("\n")}`); + + await interaction.reply({ embeds: [ embed ]}); + } + + private async ToggleRole(interaction: CommandInteraction) { + if (!interaction.guild) return; + if (!interaction.member) return; + + const roles = await this.GetRolesList(interaction); + const requestedRole = interaction.options.get('role'); + + if (!requestedRole || !requestedRole.role) { + await interaction.reply('Fields are required.'); + return; + } + + if (!roles.includes(requestedRole.role.name)) { + await interaction.reply('This role isn\'t marked as assignable.'); + return; + } + + const roleManager = interaction.member.roles as GuildMemberRoleManager; + + const userRole = roleManager.cache.find(x => x.name == requestedRole.role!.name); + const assignRole = interaction.guild.roles.cache.find(x => x.id == requestedRole.role!.id); + + if (!assignRole) return; + + if (!assignRole.editable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + + if (!userRole) { + await roleManager.add(assignRole); + await interaction.reply(`Gave role: \`${assignRole.name}\``); + } else { + await roleManager.remove(assignRole); + await interaction.reply(`Removed role: \`${assignRole.name}\``); + } + } + + private async GetRolesList(interaction: CommandInteraction): Promise { + if (!interaction.guildId || !interaction.guild) return []; + + const rolesArray = await eRole.FetchAllByServerId(interaction.guildId); + + const roles: string[] = []; + + for (let i = 0; i < rolesArray.length; i++) { + const serverRole = interaction.guild.roles.cache.find(x => x.id == rolesArray[i].RoleId); + + if (serverRole) { + roles.push(serverRole.name); + } + } + + return roles; + } +} diff --git a/src/commands/about.ts b/src/commands/about.ts index 0e2b7bf..e12d905 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -1,25 +1,56 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; import { Command } from "../type/command"; export default class About extends Command { constructor() { super(); - super.Category = "General"; + + this.CommandBuilder = new SlashCommandBuilder() + .setName('about') + .setDescription('About VylBot'); } - public override execute(context: ICommandContext): ICommandReturnContext { - const embed = new PublicEmbed(context, "About", "") - .addField("Version", process.env.BOT_VER!) - .addField("Author", process.env.BOT_AUTHOR!) - .addField("Date", process.env.BOT_DATE!); - - embed.SendToCurrentChannel(); + public override async execute(interaction: CommandInteraction) { + const fundingLink = process.env.ABOUT_FUNDING; + const repoLink = process.env.ABOUT_REPO; - return { - commandContext: context, - embeds: [embed] - }; + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("About") + .setDescription("Discord Bot made by Vylpes"); + + embed.addFields([ + { + name: "Version", + value: process.env.BOT_VER!, + inline: true, + }, + { + name: "Author", + value: process.env.BOT_AUTHOR!, + inline: true, + }, + ]); + + const row = new ActionRowBuilder(); + + if (repoLink) { + row.addComponents( + new ButtonBuilder() + .setURL(repoLink) + .setLabel("Repo") + .setStyle(ButtonStyle.Link)); + } + + if (fundingLink) { + row.addComponents( + new ButtonBuilder() + .setURL(fundingLink) + .setLabel("Funding") + .setStyle(ButtonStyle.Link)); + } + + await interaction.reply({ embeds: [ embed ], components: row.components.length > 0 ? [ row ] : [] }); } } \ No newline at end of file diff --git a/src/commands/audits.ts b/src/commands/audits.ts new file mode 100644 index 0000000..95005c1 --- /dev/null +++ b/src/commands/audits.ts @@ -0,0 +1,212 @@ +import Audit from "../database/entities/Audit"; +import AuditTools from "../helpers/AuditTools"; +import { Command } from "../type/command"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; + +export default class Audits extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName("audits") + .setDescription("View audits of a particular user in the server") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addSubcommand(subcommand => + subcommand + .setName('user') + .setDescription('View all audits done against a user') + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('view') + .setDescription('View a particular audit') + .addStringOption(option => + option + .setName('auditid') + .setDescription('The audit id in caps') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('clear') + .setDescription('Clears an audit from a user') + .addStringOption(option => + option + .setName('auditid') + .setDescription('The audit id in caps') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('add') + .setDescription('Manually add an audit') + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('type') + .setDescription('The type of audit') + .setRequired(true) + .addChoices( + { name: 'General', value: AuditType.General.toString() }, + { name: 'Warn', value: AuditType.Warn.toString() }, + { name: 'Mute', value: AuditType.Mute.toString() }, + { name: 'Kick', value: AuditType.Kick.toString() }, + { name: 'Ban', value: AuditType.Ban.toString() }, + ) + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason'))); + + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + switch (interaction.options.getSubcommand()) { + case "user": + await this.SendAuditForUser(interaction); + break; + case "view": + await this.SendAudit(interaction); + break; + case "clear": + await this.ClearAudit(interaction); + break; + case "add": + await this.AddAudit(interaction); + break; + default: + await interaction.reply("Subcommand doesn't exist."); + } + } + + private async SendAuditForUser(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const user = interaction.options.get('target', true).user!; + + if (!user) { + await interaction.reply("User not found."); + return; + } + + const audits = await Audit.FetchAuditsByUserId(user.id, interaction.guildId); + + if (!audits || audits.length == 0) { + await interaction.reply("There are no audits for this user."); + return; + } + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Audits") + .setDescription(`Audits: ${audits.length}`); + + for (let audit of audits) { + embed.addFields([ + { + name: `${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`, + value: audit.WhenCreated.toString(), + } + ]); + } + + await interaction.reply({ embeds: [ embed ]}); + } + + private async SendAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const auditId = interaction.options.get('auditid'); + + if (!auditId || !auditId.value) { + await interaction.reply("AuditId not found."); + return; + } + + const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId); + + if (!audit) { + await interaction.reply("Audit not found."); + return; + } + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Audit") + .setDescription(audit.AuditId.toUpperCase()) + .addFields([ + { + name: "Reason", + value: audit.Reason || "*none*", + inline: true, + }, + { + name: "Type", + value: AuditTools.TypeToFriendlyText(audit.AuditType), + inline: true, + }, + { + name: "Moderator", + value: `<@${audit.ModeratorId}>`, + inline: true, + }, + ]); + + await interaction.reply({ embeds: [ embed ]}); + } + + private async ClearAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const auditId = interaction.options.get('auditid'); + + if (!auditId || !auditId.value) { + await interaction.reply("AuditId not found."); + return; + } + + const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId); + + if (!audit) { + await interaction.reply("Audit not found."); + return; + } + + await Audit.Remove(Audit, audit); + + await interaction.reply("Audit cleared."); + } + + private async AddAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const user = interaction.options.get('target', true).user!; + const auditType = interaction.options.get('type'); + const reasonInput = interaction.options.get('reason'); + + if (!user || !auditType || !auditType.value) { + await interaction.reply("Invalid input."); + return; + } + + const type = auditType.value as AuditType; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : ""; + + const audit = new Audit(user.id, type, reason, interaction.user.id, interaction.guildId); + + await audit.Save(Audit, audit); + + await interaction.reply(`Created new audit with ID \`${audit.AuditId}\``); + } +} diff --git a/src/commands/autokick.ts b/src/commands/autokick.ts new file mode 100644 index 0000000..760441e --- /dev/null +++ b/src/commands/autokick.ts @@ -0,0 +1,113 @@ +import {ChatInputCommandInteraction, CommandInteraction, EmbedBuilder, PermissionFlagsBits, SlashCommandBuilder} from "discord.js"; +import {Command} from "../type/command"; +import TimeLengthInput from "../helpers/TimeLengthInput"; +import AutoKickHelper from "../helpers/AutoKickHelper"; + +export default class Autokick extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName("autokick") + .setDescription("Configure the auto kick functionality") + .setDefaultMemberPermissions(PermissionFlagsBits.KickMembers) + .addSubcommand(x => x + .setName("set") + .setDescription("Set the configuration") + .addRoleOption(y => y + .setName("role") + .setDescription("The role the user needs to be auto kicked") + .setRequired(true)) + .addStringOption(y => y + .setName("kicktime") + .setDescription("The time with the role before being kicked (Ex: 2h 30m)") + .setRequired(true)) + .addStringOption(y => y + .setName("noticetime") + .setDescription("The time before being kicked when a notification is sent (Ex: 2h 30m)")) + .addChannelOption(y => y + .setName("noticechannel") + .setDescription("The channel to send the notification to"))) + .addSubcommand(x => x + .setName("unset") + .setDescription("Unset the current configuration")); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + const subcommand = interaction.options.getSubcommand(); + + switch (subcommand) { + case "set": + await this.set(interaction); + break; + case "unset": + await this.unset(interaction); + break; + } + } + + private async set(interaction: ChatInputCommandInteraction) { + if (!interaction.guildId) return; + + const roleOption = interaction.options.getRole("role", true); + const kickTimeOption = interaction.options.getString("kicktime", true); + const noticeTimeOption = interaction.options.getString("noticetime"); + const noticeChannelOption = interaction.options.getChannel("noticechannel"); + + const roleId = roleOption.id; + const kickTimeInput = new TimeLengthInput(kickTimeOption); + const noticeTimeInput = noticeTimeOption ? new TimeLengthInput(noticeTimeOption) : undefined; + const noticeChannelId = noticeChannelOption?.id; + + if ((noticeTimeInput && !noticeTimeOption) || (!noticeTimeInput && noticeChannelOption)) { + await interaction.reply("Both `noticetime` and `noticechannel` must be set if you want a notification embed"); + return; + } + + await AutoKickHelper.SetSetting(interaction.guildId, roleId, kickTimeInput.GetMilliseconds(), noticeTimeInput?.GetMilliseconds(), noticeChannelId); + + const embed = new EmbedBuilder() + .setTitle("Auto Kick") + .setDescription("Configured auto kick for this server") + .addFields([ + { + name: "Role", + value: roleOption.name, + inline: true, + }, + { + name: "Kick Time", + value: kickTimeInput.GetLengthShort(), + inline: true, + }, + ]); + + if (noticeTimeInput) { + embed.addFields([ + { + name: "Notice Time", + value: noticeTimeInput.GetLengthShort(), + }, + { + name: "Notice Channel", + value: noticeChannelOption!.name!, + inline: true, + }, + ]); + } + + await interaction.reply({ + embeds: [ embed ], + }); + } + + private async unset(interaction: ChatInputCommandInteraction) { + if (!interaction.guildId) return; + + await AutoKickHelper.UnsetSetting(interaction.guildId); + + await interaction.reply("Unset the auto kick configuration for this server"); + } +} diff --git a/src/commands/ban.ts b/src/commands/ban.ts index 8656921..12c4278 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -1,80 +1,101 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import ErrorMessages from "../constants/ErrorMessages"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; +import Audit from "../database/entities/Audit"; +import { AuditType } from "../constants/AuditType"; +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; +import SettingsHelper from "../helpers/SettingsHelper"; export default class Ban extends Command { constructor() { super(); - - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + + this.CommandBuilder = new SlashCommandBuilder() + .setName("ban") + .setDescription("Ban a member from the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext): Promise { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; + if (!interaction.guild) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed], - }; + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply("User not found."); + return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const member = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed], - }; + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Banned") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .setThumbnail(targetUser.user.avatarURL()) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); + + if (!member.bannable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; } - const reasonArgs = context.args; - reasonArgs.splice(0, 1) - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [], - }; + await member.ban(); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - if (!targetMember.bannable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed], - }; + const dmEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(interaction.guild.name) + .setDescription("You have been banned by a moderator.") + .addFields([ + { + name: "Reason", + value: reason, + }, + ]); + + let replyText = "Successfully banned user."; + + try { + const dmChannel = await targetUser.user!.createDM(); + await dmChannel.send({ embeds: [ dmEmbed ] }); + } catch { + replyText += " *Note: I was unable to DM the user the reason.*"; } - const logEmbed = new LogEmbed(context, "Member Banned"); - logEmbed.AddUser("User", targetUser, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); + const audit = new Audit(targetUser.user.id, AuditType.Ban, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been banned`); - - await targetMember.ban({ reason: `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}` }); - - await logEmbed.SendToModLogsChannel(); - publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed], - }; + await interaction.reply(replyText); } } \ No newline at end of file diff --git a/src/commands/bunny.ts b/src/commands/bunny.ts new file mode 100644 index 0000000..3285083 --- /dev/null +++ b/src/commands/bunny.ts @@ -0,0 +1,47 @@ +import { Command } from "../type/command"; +import randomBunny from "random-bunny"; +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; + +export default class Bunny extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName("bunny") + .setDescription("Get a random picture of a rabbit."); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + await interaction.deferReply(); + + const subreddits = [ + 'rabbits', + 'bunnieswithhats', + 'buncomfortable', + 'bunnytongues', + 'dutchbunnymafia', + ]; + + const random = Math.floor(Math.random() * subreddits.length); + const selectedSubreddit = subreddits[random]; + + const result = await randomBunny(selectedSubreddit, 'hot'); + + if (result.IsSuccess) { + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(result.Result!.Title) + .setDescription(result.Result!.Permalink) + .setImage(result.Result!.Url) + .setURL(`https://reddit.com${result.Result!.Permalink}`) + .setFooter({ text: `r/${selectedSubreddit} ยท ${result.Result!.Ups} upvotes`}); + + await interaction.editReply({ embeds: [ embed ]}); + } else { + await interaction.editReply("There was an error running this command."); + } + } +} \ No newline at end of file diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 243454a..c7cd12b 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -1,50 +1,43 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import { TextChannel } from "discord.js"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; export default class Clear extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + this.CommandBuilder = new SlashCommandBuilder() + .setName("clear") + .setDescription("Clears the channel of messages") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages) + .addNumberOption(option => + option + .setName('count') + .setDescription('The amount to delete') + .setRequired(true) + .setMinValue(1) + .setMaxValue(100)); } - public override async execute(context: ICommandContext): Promise { - if (context.args.length == 0) { - const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); - errorEmbed.SendToCurrentChannel(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.channel) return; - return { - commandContext: context, - embeds: [errorEmbed] - }; - } - - const totalToClear = Number.parseInt(context.args[0]); + const totalToClear = interaction.options.getNumber('count'); if (!totalToClear || totalToClear <= 0 || totalToClear > 100) { - const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); - errorEmbed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [errorEmbed] - }; + await interaction.reply('Please specify an amount between 1 and 100.'); + return; } - await (context.message.channel as TextChannel).bulkDelete(totalToClear); + const channel = interaction.channel as TextChannel; - const embed = new PublicEmbed(context, "", `${totalToClear} message(s) were removed`); - embed.SendToCurrentChannel(); + if (!channel.manageable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } - return { - commandContext: context, - embeds: [embed] - }; + await channel.bulkDelete(totalToClear); + + await interaction.reply(`${totalToClear} message(s) were removed.`); } } \ No newline at end of file diff --git a/src/commands/code.ts b/src/commands/code.ts index 7c5c7da..09b6922 100644 --- a/src/commands/code.ts +++ b/src/commands/code.ts @@ -1,7 +1,4 @@ -import { CommandResponse } from "../constants/CommandResponse"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import StringTools from "../helpers/StringTools"; import { Command } from "../type/command"; @@ -10,85 +7,60 @@ export default class Code extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + this.CommandBuilder = new SlashCommandBuilder() + .setName('code') + .setDescription('Manage the verification code of the server') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addSubcommand(subcommand => + subcommand + .setName('randomise') + .setDescription('Regenerates the verification code for this server')) + .addSubcommand(subcommand => + subcommand + .setName('embed') + .setDescription('Sends the embed with the current code to the current channel')); } - public override async precheckAsync(context: ICommandContext): Promise { - if (!context.message.guild){ - return CommandResponse.NotInServer; - } + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - const isEnabled = await SettingsHelper.GetSetting("verification.enabled", context.message.guild?.id); - - if (!isEnabled) { - return CommandResponse.FeatureDisabled; - } - - if (isEnabled.toLocaleLowerCase() != 'true') { - return CommandResponse.FeatureDisabled; - } - - return CommandResponse.Ok; - } - - public override async execute(context: ICommandContext) { - const action = context.args[0]; - - switch (action) { + switch (interaction.options.getSubcommand()) { case "randomise": - await this.Randomise(context); + await this.Randomise(interaction); break; case "embed": - await this.SendEmbed(context); + await this.SendEmbed(interaction); break; - default: - await this.SendUsage(context); } } - private async SendUsage(context: ICommandContext) { - const description = [ - "USAGE: ", - "", - "randomise: Sets the server's entry code to a random code", - "embed: Sends an embed with the server's entry code" - ].join("\n"); - - const embed = new PublicEmbed(context, "", description); - embed.SendToCurrentChannel(); - } - - private async Randomise(context: ICommandContext) { - if (!context.message.guild) { - return; - } + private async Randomise(interaction: CommandInteraction) { + if (!interaction.guildId) return; const randomCode = StringTools.RandomString(5); - await SettingsHelper.SetSetting("verification.code", context.message.guild.id, randomCode); + await SettingsHelper.SetSetting("verification.code", interaction.guildId, randomCode); - const embed = new PublicEmbed(context, "Code", `Entry code has been set to \`${randomCode}\``); - embed.SendToCurrentChannel(); + await interaction.reply(`Entry code has been set to \`${randomCode}\``); } - private async SendEmbed(context: ICommandContext) { - if (!context.message.guild) { - return; - } + private async SendEmbed(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!interaction.channel) return; - const code = await SettingsHelper.GetSetting("verification.code", context.message.guild.id); + const code = await SettingsHelper.GetSetting("verification.code", interaction.guildId); if (!code || code == "") { - const errorEmbed = new ErrorEmbed(context, "There is no code for this server setup."); - errorEmbed.SendToCurrentChannel(); - + await interaction.reply("There is no code for this server setup."); return; } - const embed = new PublicEmbed(context, "Entry Code", code!); - embed.SendToCurrentChannel(); + const embed = new EmbedBuilder() + .setTitle("Entry Code") + .setDescription(code); + + const channel = interaction.channel as TextChannel; + + await channel.send({ embeds: [ embed ]}); } } \ No newline at end of file diff --git a/src/commands/config.ts b/src/commands/config.ts index aa46263..bc8a293 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,127 +1,192 @@ -import { Guild } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { readFileSync } from "fs"; -import { CommandResponse } from "../constants/CommandResponse"; import DefaultValues from "../constants/DefaultValues"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import Server from "../entity/Server"; -import Setting from "../entity/Setting"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import EmbedColours from "../constants/EmbedColours"; +import Server from "../database/entities/Server"; +import Setting from "../database/entities/Setting"; import { Command } from "../type/command"; export default class Config extends Command { constructor() { super(); - super.Category = "Administration"; - super.Roles = [ - "administrator" - ] + + this.CommandBuilder = new SlashCommandBuilder() + .setName('config') + .setDescription('Configure the current server') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) + .addSubcommand(subcommand => + subcommand + .setName('reset') + .setDescription('Reset a setting to the default') + .addStringOption(option => + option + .setName('key') + .setDescription('The key') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('get') + .setDescription('Gets a setting for the server') + .addStringOption(option => + option + .setName('key') + .setDescription('The key') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('set') + .setDescription('Sets a setting to a specified value') + .addStringOption(option => + option + .setName('key') + .setDescription('The key') + .setRequired(true)) + .addStringOption(option => + option + .setName('value') + .setDescription('The value') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('list') + .setDescription('Lists all settings')) } - public override async precheckAsync(context: ICommandContext): Promise { - if (!context.message.guild) { - return CommandResponse.ServerNotSetup; - } + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; - const server = await Server.FetchOneById(Server, context.message.guild?.id, [ + const server = await Server.FetchOneById(Server, interaction.guildId, [ "Settings", ]); if (!server) { - return CommandResponse.ServerNotSetup; - } - - return CommandResponse.Ok; - } - - public override async execute(context: ICommandContext) { - if (!context.message.guild) { + await interaction.reply('Server not setup. Please use the setup command,'); return; } - const server = await Server.FetchOneById(Server, context.message.guild?.id, [ + switch (interaction.options.getSubcommand()) { + case 'list': + await this.SendHelpText(interaction); + break; + case 'reset': + await this.ResetValue(interaction); + break; + case 'get': + await this.GetValue(interaction); + break; + case 'set': + await this.SetValue(interaction); + break; + default: + await interaction.reply('Subcommand not found.'); + } + } + + private async SendHelpText(interaction: CommandInteraction) { + const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString(); + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Config") + .setDescription(description); + + await interaction.reply({ embeds: [ embed ]}); + } + + private async GetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const key = interaction.options.get('key'); + + if (!key || !key.value) { + await interaction.reply('Fields are required.'); + return; + } + + const server = await Server.FetchOneById(Server, interaction.guildId, [ "Settings", ]); if (!server) { + await interaction.reply('Server not found.'); return; } - const key = context.args[0]; - const action = context.args[1]; - const value = context.args.splice(2).join(" "); + const setting = server.Settings.filter(x => x.Key == key.value)[0]; - if (!key) { - this.SendHelpText(context); - } else if (!action) { - this.GetValue(context, server, key); + if (setting) { + await interaction.reply(`\`${key.value}\`: \`${setting.Value}\``); } else { - switch(action) { - case 'reset': - this.ResetValue(context, server, key); - break; - case 'set': - if (!value) { - const errorEmbed = new ErrorEmbed(context, "Value is required when setting"); - errorEmbed.SendToCurrentChannel(); - return; - } + var defaultValue = DefaultValues.GetValue(key.value.toString()); - this.SetValue(context, server, key, value); - break; - default: - const errorEmbed = new ErrorEmbed(context, "Action must be either set or reset"); - errorEmbed.SendToCurrentChannel(); - return; + if (defaultValue) { + await interaction.reply(`\`${key.value}\`: \`${defaultValue}\` `); + } else { + await interaction.reply(`\`${key.value}\`: `); } } } - private async SendHelpText(context: ICommandContext) { - const description = readFileSync(`${process.cwd()}/data/config.txt`).toString(); + private async ResetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; - const embed = new PublicEmbed(context, "Config", description); + const key = interaction.options.get('key'); - embed.SendToCurrentChannel(); - } - - private async GetValue(context: ICommandContext, server: Server, key: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; - - if (setting) { - const embed = new PublicEmbed(context, "", `${key}: ${setting.Value}`); - embed.SendToCurrentChannel(); - } else { - const embed = new PublicEmbed(context, "", `${key}: ${DefaultValues.GetValue(key)} `); - embed.SendToCurrentChannel(); + if (!key || !key.value) { + await interaction.reply('Fields are required.'); + return; } - } - private async ResetValue(context: ICommandContext, server: Server, key: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; + const server = await Server.FetchOneById(Server, interaction.guildId, [ + "Settings", + ]); + + if (!server) { + await interaction.reply('Server not found.'); + return; + } + + const setting = server.Settings.filter(x => x.Key == key.value)[0]; if (!setting) { - const embed = new PublicEmbed(context, "", "Setting has been reset"); - embed.SendToCurrentChannel(); + await interaction.reply('Setting not found.'); return; } await Setting.Remove(Setting, setting); - const embed = new PublicEmbed(context, "", "Setting has been reset"); - embed.SendToCurrentChannel(); + await interaction.reply('The setting has been reset to the default.'); } - private async SetValue(context: ICommandContext, server: Server, key: string, value: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; + private async SetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const key = interaction.options.get('key'); + const value = interaction.options.get('value'); + + if (!key || !key.value || !value || !value.value) { + await interaction.reply('Fields are required.'); + return; + } + + const server = await Server.FetchOneById(Server, interaction.guildId, [ + "Settings", + ]); + + if (!server) { + await interaction.reply('Server not found.'); + return; + } + + const setting = server.Settings.filter(x => x.Key == key.value)[0]; if (setting) { - setting.UpdateBasicDetails(key, value); + setting.UpdateBasicDetails(key.value.toString(), value.value.toString()); await setting.Save(Setting, setting); } else { - const newSetting = new Setting(key, value); + const newSetting = new Setting(key.value.toString(), value.value.toString()); await newSetting.Save(Setting, newSetting); @@ -130,7 +195,6 @@ export default class Config extends Command { await server.Save(Server, server); } - const embed = new PublicEmbed(context, "", "Setting has been set"); - embed.SendToCurrentChannel(); + await interaction.reply('Setting has been set.'); } -} \ No newline at end of file +} diff --git a/src/commands/disable.ts b/src/commands/disable.ts index b1d9f36..3f19bdb 100644 --- a/src/commands/disable.ts +++ b/src/commands/disable.ts @@ -1,5 +1,4 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; @@ -7,87 +6,86 @@ export default class Disable extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + this.CommandBuilder = new SlashCommandBuilder() + .setName('disable') + .setDescription('Disables a command') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) + .addSubcommand(subcommand => + subcommand + .setName('add') + .setDescription('Disables a command for the server') + .addStringOption(option => + option + .setName('name') + .setDescription('The name of the command') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('remove') + .setDescription('Enables a command for the server') + .addStringOption(option => + option + .setName('name') + .setDescription('The name of the command') + .setRequired(true))); } - public override async execute(context: ICommandContext) { - const action = context.args[0]; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - switch (action) { + switch (interaction.options.getSubcommand()) { case "add": - await this.Add(context); + await this.Add(interaction); break; case "remove": - await this.Remove(context); + await this.Remove(interaction); break; default: - await this.SendUsage(context); + await interaction.reply('Subcommand not found.'); } } - private async SendUsage(context: ICommandContext) { - const description = [ - "USAGE: ", - "", - "add: Adds the command name to the server's disabled command string", - "remove: Removes the command name from the server's disabled command string", - "name: The name of the command to enable/disable" - ].join("\n"); - - const embed = new PublicEmbed(context, "", description); - embed.SendToCurrentChannel(); - } + private async Add(interaction: CommandInteraction) { + if (!interaction.guildId) return; - private async Add(context: ICommandContext) { - if (!context.message.guild) { + const commandName = interaction.options.get('name'); + + if (!commandName || !commandName.value) { + await interaction.reply('Fields are required.'); return; } - const commandName = context.args[1]; - - if (!commandName) { - this.SendUsage(context); - return; - } - - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; - disabledCommands?.push(commandName); + disabledCommands?.push(commandName.value.toString()); - await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); + await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(",")); - const embed = new PublicEmbed(context, "", `Disabled command: ${commandName}`); - embed.SendToCurrentChannel(); + await interaction.reply(`Disabled command ${commandName.value}`); } - private async Remove(context: ICommandContext) { - if (!context.message.guild) { + private async Remove(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const commandName = interaction.options.get('name'); + + if (!commandName || !commandName.value) { + await interaction.reply('Fields are required.'); return; } - const commandName = context.args[1]; - - if (!commandName) { - this.SendUsage(context); - return; - } - - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; - const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName); + const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName.value!.toString()); if (disabledCommandsInstance! > -1) { disabledCommands?.splice(disabledCommandsInstance!, 1); } - await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); + await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(",")); - const embed = new PublicEmbed(context, "", `Enabled command: ${commandName}`); - embed.SendToCurrentChannel(); + await interaction.reply(`Enabled command ${commandName.value}`); } } \ No newline at end of file diff --git a/src/commands/help.ts b/src/commands/help.ts deleted file mode 100644 index aa056d5..0000000 --- a/src/commands/help.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { existsSync, readdirSync } from "fs"; -import { CoreClient } from "../client/client"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import StringTools from "../helpers/StringTools"; -import { Command } from "../type/command"; - -export interface ICommandData { - Exists: boolean; - Name?: string; - Category?: string; - Roles?: string[]; -} - -export default class Help extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override execute(context: ICommandContext) { - if (context.args.length == 0) { - this.SendAll(context); - } else { - this.SendSingle(context); - } - } - - public SendAll(context: ICommandContext) { - const allCommands = CoreClient.commandItems - .filter(x => !x.ServerId || x.ServerId == context.message.guild?.id); - const cateogries = [...new Set(allCommands.map(x => x.Command.Category))]; - - const embed = new PublicEmbed(context, "Commands", ""); - - cateogries.forEach(category => { - let filtered = allCommands.filter(x => x.Command.Category == category); - - embed.addField(StringTools.Capitalise(category || "Uncategorised"), StringTools.CapitaliseArray(filtered.flatMap(x => x.Name)).join(", ")); - }); - - embed.SendToCurrentChannel(); - } - - public SendSingle(context: ICommandContext) { - const command = CoreClient.commandItems.find(x => x.Name == context.args[0] && !x.ServerId); - const exclusiveCommand = CoreClient.commandItems.find(x => x.Name == context.args[0] && x.ServerId == context.message.guild?.id); - - if (exclusiveCommand) { - const embed = new PublicEmbed(context, StringTools.Capitalise(exclusiveCommand.Name), ""); - embed.addField("Category", StringTools.Capitalise(exclusiveCommand.Command.Category || "Uncategorised")); - embed.addField("Required Roles", StringTools.Capitalise(exclusiveCommand.Command.Roles.join(", ")) || "Everyone"); - - embed.SendToCurrentChannel(); - } else if (command) { - const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name), ""); - embed.addField("Category", StringTools.Capitalise(command.Command.Category || "Uncategorised")); - embed.addField("Required Roles", StringTools.Capitalise(command.Command.Roles.join(", ")) || "Everyone"); - - embed.SendToCurrentChannel(); - } else { - const errorEmbed = new ErrorEmbed(context, "Command does not exist"); - errorEmbed.SendToCurrentChannel(); - - return; - } - } -} diff --git a/src/commands/ignore.ts b/src/commands/ignore.ts new file mode 100644 index 0000000..f1ab7ac --- /dev/null +++ b/src/commands/ignore.ts @@ -0,0 +1,39 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import IgnoredChannel from "../database/entities/IgnoredChannel"; +import { Command } from "../type/command"; + +export default class Ignore extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName('ignore') + .setDescription('Ignore events in this channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const isChannelIgnored = await IgnoredChannel.IsChannelIgnored(interaction.guildId); + + if (isChannelIgnored) { + const entity = await IgnoredChannel.FetchOneById(IgnoredChannel, interaction.guildId); + + if (!entity) { + await interaction.reply('Unable to find channel.'); + return; + } + + await IgnoredChannel.Remove(IgnoredChannel, entity); + + await interaction.reply('This channel will start being logged again.'); + } else { + const entity = new IgnoredChannel(interaction.guildId); + + await entity.Save(IgnoredChannel, entity); + + await interaction.reply('This channel will now be ignored from logging.'); + } + } +} \ No newline at end of file diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 9f75080..a58461e 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -1,83 +1,101 @@ -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; +import Audit from "../database/entities/Audit"; +import { AuditType } from "../constants/AuditType"; +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; +import SettingsHelper from "../helpers/SettingsHelper"; export default class Kick extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + this.CommandBuilder = new SlashCommandBuilder() + .setName("kick") + .setDescription("Kick a member from the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext): Promise { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; + if (!interaction.guild) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - embed.SendToCurrentChannel(); + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); - return { - commandContext: context, - embeds: [embed] - }; + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply("User not found."); + return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const member = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Kicked") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .setThumbnail(targetUser.user.avatarURL()) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); + + if (!member.kickable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; } - const reasonArgs = context.args; - reasonArgs.splice(0, 1) - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; + await member.kick(); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - if (!targetMember.kickable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + const dmEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(interaction.guild.name) + .setDescription("You have been kicked from the server.") + .addFields([ + { + name: "Reason", + value: reason, + }, + ]); + + let replyText = "Successfully kicked user."; + + try { + const dmChannel = await targetUser.user!.createDM(); + await dmChannel.send({ embeds: [ dmEmbed ] }); + } catch { + replyText += " *Note: I was unable to DM the user the reason.*"; } - const logEmbed = new LogEmbed(context, "Member Kicked"); - logEmbed.AddUser("User", targetUser, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); + const audit = new Audit(targetUser.user.id, AuditType.Kick, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been kicked`); - - await targetMember.kick(`Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - - await logEmbed.SendToModLogsChannel(); - publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; + await interaction.reply(replyText); } } \ No newline at end of file diff --git a/src/commands/linkonly.ts b/src/commands/linkonly.ts new file mode 100644 index 0000000..ca163bc --- /dev/null +++ b/src/commands/linkonly.ts @@ -0,0 +1,30 @@ +import { CommandInteraction, PermissionFlagsBits, SlashCommandBuilder } from "discord.js"; +import { Command } from "../type/command"; +import SettingsHelper from "../helpers/SettingsHelper"; + +export default class Linkonly extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName("linkonly") + .setDescription("Set the link only channel, leave blank to disable") + .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) + .addChannelOption(x => x + .setName("channel") + .setDescription("The channel")); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild) return; + + const channel = interaction.options.get("channel")?.channel; + + const channelid = channel?.id ?? ""; + const channelName = channel?.name ?? ""; + + await SettingsHelper.SetSetting("channel.linkonly", interaction.guild.id, channelid); + + await interaction.reply(`Set the link only channel to \`${channelName}\``); + } +} \ No newline at end of file diff --git a/src/commands/mute.ts b/src/commands/mute.ts deleted file mode 100644 index fbf0bcf..0000000 --- a/src/commands/mute.ts +++ /dev/null @@ -1,96 +0,0 @@ -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import { Command } from "../type/command"; - -export default class Mute extends Command { - constructor() { - super(); - - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - } - - public override async execute(context: ICommandContext): Promise { - const targetUser = context.message.mentions.users.first(); - - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); - - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - const reasonArgs = context.args; - reasonArgs.splice(0, 1); - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; - } - - if (!targetMember.manageable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - const logEmbed = new LogEmbed(context, "Member Muted"); - logEmbed.AddUser("User", targetUser, true) - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been muted`); - publicEmbed.AddReason(reason); - - const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); - - if (!mutedRole) { - const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - await targetMember.roles.add(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - - await logEmbed.SendToModLogsChannel(); - publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; - } -} \ No newline at end of file diff --git a/src/commands/poll.ts b/src/commands/poll.ts index be88869..37fbdb1 100644 --- a/src/commands/poll.ts +++ b/src/commands/poll.ts @@ -1,31 +1,62 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; import { Command } from "../type/command"; +import { EmbedBuilder } from "@discordjs/builders"; +import EmbedColours from "../constants/EmbedColours"; export default class Poll extends Command { constructor() { super(); - - super.Category = "General"; + + this.CommandBuilder = new SlashCommandBuilder() + .setName('poll') + .setDescription('Run a poll, automatically adding reaction emojis as options') + .addStringOption(option => + option + .setName('title') + .setDescription('Title of the poll') + .setRequired(true)) + .addStringOption(option => + option + .setName('option1') + .setDescription('Option 1') + .setRequired(true)) + .addStringOption(option => + option + .setName('option2') + .setDescription('Option 2') + .setRequired(true)) + .addStringOption(option => + option + .setName('option3') + .setDescription('Option 3')) + .addStringOption(option => + option + .setName('option4') + .setDescription('Option 4')) + .addStringOption(option => + option + .setName('option5') + .setDescription('Option 5')); } - public override async execute(context: ICommandContext): Promise { - const argsJoined = context.args.join(" "); - const argsSplit = argsJoined.split(";"); + public override async execute(interaction: CommandInteraction) { + const title = interaction.options.get('title'); + const option1 = interaction.options.get('option1'); + const option2 = interaction.options.get('option2'); + const option3 = interaction.options.get('option3'); + const option4 = interaction.options.get('option4'); + const option5 = interaction.options.get('option5'); - if (argsSplit.length < 3 || argsSplit.length > 10) { - const errorEmbed = new ErrorEmbed(context, "Usage: ;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options"); - errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; - } + if (!title || !option1 || !option2) return; - const title = argsSplit[0]; + const description = [ + option1.value as string, + option2.value as string, + option3?.value as string, + option4?.value as string, + option5?.value as string + ] + .filter(x => x != null); const arrayOfNumbers = [ ':one:', @@ -33,35 +64,28 @@ export default class Poll extends Command { ':three:', ':four:', ':five:', - ':six:', - ':seven:', - ':eight:', - ':nine:' ]; const reactionEmojis = ["1๏ธโƒฃ", "2๏ธโƒฃ", "3๏ธโƒฃ", "4๏ธโƒฃ", "5๏ธโƒฃ", "6๏ธโƒฃ", "7๏ธโƒฃ", "8๏ธโƒฃ", "9๏ธโƒฃ"]; - const description = arrayOfNumbers.splice(0, argsSplit.length - 1); - description.forEach((value, index) => { - description[index] = `${value} ${argsSplit[index + 1]}`; + description[index] = `${reactionEmojis[index]} ${description[index]}`; }); - const embed = new PublicEmbed(context, title, description.join("\n")); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(title.value as string) + .setDescription(description.join('\n')) + .setFooter({ + text: `Poll by ${interaction.user.username}`, + iconURL: interaction.user.avatarURL()!, + }); - const message = await context.message.channel.send({ embeds: [ embed ]}); + + const message = await interaction.reply({ embeds: [ embed ]}); description.forEach(async (value, index) => { - await message.react(reactionEmojis[index]); + await (await message.fetch()).react(reactionEmojis[index]); }); - - if (context.message.deletable) { - await context.message.delete(); - } - - return { - commandContext: context, - embeds: [embed] - }; } } \ No newline at end of file diff --git a/src/commands/role.ts b/src/commands/role.ts deleted file mode 100644 index 28586ed..0000000 --- a/src/commands/role.ts +++ /dev/null @@ -1,99 +0,0 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import { Role as DiscordRole } from "discord.js"; -import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; - -export default class Role extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override async execute(context: ICommandContext) { - const roles = process.env.COMMANDS_ROLE_ROLES!.split(','); - - if (context.args.length == 0) { - this.SendRolesList(context, roles); - } else { - await this.ToggleRole(context, roles); - } - } - - public SendRolesList(context: ICommandContext, roles: String[]): ICommandReturnContext { - const description = `Do ${process.env.BOT_PREFIX}role <role> to get the role!\n${roles.join('\n')}`; - - const embed = new PublicEmbed(context, "Roles", description); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - public async ToggleRole(context: ICommandContext, roles: String[]): Promise<ICommandReturnContext> { - const requestedRole = context.args[0]; - - if (!roles.includes(requestedRole)) { - const errorEmbed = new ErrorEmbed(context, "This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters"); - errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; - } - - const assignRole = context.message.guild?.roles.cache.find(x => x.name == requestedRole); - - if (!assignRole) { - const errorEmbed = new ErrorEmbed(context, "The current server doesn't have this role. Please contact the server's moderators"); - errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; - } - - const role = context.message.member?.roles.cache.find(x => x.name == requestedRole) - - if (!role) { - await this.AddRole(context, assignRole); - } else { - await this.RemoveRole(context, assignRole); - } - - return { - commandContext: context, - embeds: [] - }; - } - - public async AddRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> { - await context.message.member?.roles.add(role, "Toggled with role command"); - - const embed = new PublicEmbed(context, "", `Gave role: ${role.name}`); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - public async RemoveRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> { - await context.message.member?.roles.remove(role, "Toggled with role command"); - - const embed = new PublicEmbed(context, "", `Removed role: ${role.name}`); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } -} \ No newline at end of file diff --git a/src/commands/rules.ts b/src/commands/rules.ts index 414b141..83b03a3 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -1,9 +1,8 @@ +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { existsSync, readFileSync } from "fs"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import EmbedColours from "../constants/EmbedColours"; import { Command } from "../type/command"; +import SettingsHelper from "../helpers/SettingsHelper"; interface IRules { title?: string; @@ -16,42 +15,109 @@ export default class Rules extends Command { constructor() { super(); - super.Category = "Admin"; - super.Roles = [ - "administrator" - ]; + this.CommandBuilder = new SlashCommandBuilder() + .setName('rules') + .setDescription("Rules-related commands") + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) + .addSubcommand(x => + x + .setName('embeds') + .setDescription('Send the rules embeds for this server')) + .addSubcommand(x => + x + .setName('access') + .setDescription('Send the server verification embed button')); } - public override execute(context: ICommandContext): ICommandReturnContext { - if (!existsSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`)) { - const errorEmbed = new ErrorEmbed(context, "Rules file doesn't exist"); - errorEmbed.SendToCurrentChannel(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - return { - commandContext: context, - embeds: [errorEmbed] - }; + switch (interaction.options.getSubcommand()) { + case "embeds": + await this.SendEmbeds(interaction); + break; + case "access": + await this.SendAccessButton(interaction); + break; + default: + await interaction.reply("Subcommand doesn't exist."); + } + } + + private async SendEmbeds(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) { + await interaction.reply('Rules file doesn\'t exist.'); + return; } - const rulesFile = readFileSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`).toString(); + const rulesFile = readFileSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`).toString(); const rules = JSON.parse(rulesFile) as IRules[]; - const embeds: PublicEmbed[] = []; - - rules.forEach(rule => { - const embed = new PublicEmbed(context, rule.title || "", rule.description?.join("\n") || ""); + const embeds: EmbedBuilder[] = []; - embed.setImage(rule.image || ""); - embed.setFooter(rule.footer || ""); + if (rules.length == 0) { + await interaction.reply({ content: "No rules have been supplied within code base for this server.", ephemeral: true }); + return; + } + + rules.forEach(rule => { + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(rule.title || "Rules") + .setDescription(rule.description ? rule.description.join("\n") : "*none*"); + + if (rule.image) { + embed.setImage(rule.image); + } + + if (rule.footer) { + embed.setFooter({ text: rule.footer }); + } embeds.push(embed); }); - embeds.forEach(x => x.SendToCurrentChannel()); + const channel = interaction.channel as TextChannel; - return { - commandContext: context, - embeds: embeds - }; + if (!channel) { + await interaction.reply({ content: "Channel not found.", ephemeral: true }); + return; + } + + await channel.send({ embeds: embeds }); + + const successEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Success") + .setDescription("The rules have sent to this channel successfully"); + + await interaction.reply({ embeds: [ successEmbed ], ephemeral: true }); + } + + private async SendAccessButton(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const buttonLabel = await SettingsHelper.GetSetting("rules.access.label", interaction.guildId); + + const row = new ActionRowBuilder<ButtonBuilder>() + .addComponents([ + new ButtonBuilder() + .setCustomId('verify') + .setStyle(ButtonStyle.Primary) + .setLabel(buttonLabel || "Verify") + ]); + + const channel = interaction.channel as TextChannel; + + await channel.send({ + components: [ row ] + }); + + await interaction.reply({ + content: "Success", + ephemeral: true, + }); } } \ No newline at end of file diff --git a/src/commands/say.ts b/src/commands/say.ts new file mode 100644 index 0000000..7b1b8fd --- /dev/null +++ b/src/commands/say.ts @@ -0,0 +1,24 @@ +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; +import { Command } from "../type/command"; + +export default class Say extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName('say') + .setDescription('Have the bot reply with your message') + .addStringOption(x => + x + .setName("message") + .setDescription("The message to repeat") + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + const message = interaction.options.get("message", true); + + await interaction.reply(message.value as string); + } +} diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 87be5a5..3d5d4ce 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -1,36 +1,31 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import Server from "../entity/Server"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import Server from "../database/entities/Server"; import { Command } from "../type/command"; export default class Setup extends Command { constructor() { super(); - super.Category = "Administration"; - super.Roles = [ - "moderator" - ] + + this.CommandBuilder = new SlashCommandBuilder() + .setName('setup') + .setDescription('Makes the server ready to be configured') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) { - return; - } + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; - const server = await Server.FetchOneById(Server, context.message.guild?.id); + const server = await Server.FetchOneById(Server, interaction.guildId); if (server) { - const embed = new ErrorEmbed(context, "This server has already been setup, please configure using the config command"); - embed.SendToCurrentChannel(); + await interaction.reply('This server has already been setup, please configure using the config command.'); return; } - const newServer = new Server(context.message.guild?.id); + const newServer = new Server(interaction.guildId); await newServer.Save(Server, newServer); - const embed = new PublicEmbed(context, "Success", "Please configure using the config command"); - embed.SendToCurrentChannel(); + await interaction.reply('Success, please configure using the configure command.'); } } \ No newline at end of file diff --git a/src/commands/timeout.ts b/src/commands/timeout.ts new file mode 100644 index 0000000..9ada281 --- /dev/null +++ b/src/commands/timeout.ts @@ -0,0 +1,133 @@ +import { CacheType, CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; +import Audit from "../database/entities/Audit"; +import SettingsHelper from "../helpers/SettingsHelper"; +import TimeLengthInput from "../helpers/TimeLengthInput"; +import { Command } from "../type/command"; + +export default class Timeout extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName("timeout") + .setDescription("Timeouts a user out, sending them a DM with the reason if possible") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName("length") + .setDescription("How long to timeout for? (Example: 24h, 60m)") + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); + } + + public override async execute(interaction: CommandInteraction<CacheType>) { + if (!interaction.guild || !interaction.guildId) return; + + // Interaction Inputs + const targetUser = interaction.options.get('target'); + const lengthInput = interaction.options.get('length'); + const reasonInput = interaction.options.get('reason'); + + // Validation + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); + return; + } + + if (!lengthInput || !lengthInput.value) { + await interaction.reply('Fields are required.'); + return; + } + + if (targetUser.user.bot) { + await interaction.reply('Cannot timeout bots.'); + return; + } + + // General Variables + const targetMember = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : null; + + const timeLength = new TimeLengthInput(lengthInput.value.toString()); + + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Timed Out") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .setThumbnail(targetUser.user.avatarURL()) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason || "*none*", + }, + { + name: "Length", + value: timeLength.GetLengthShort(), + }, + { + name: "Until", + value: timeLength.GetDateFromNow().toString(), + }, + ]); + + // Bot Permissions Check + if (!targetMember.manageable) { + await interaction.reply('Insufficient bot permissions. Please contact a moderator.'); + return; + } + + // Execute Timeout + await targetMember.timeout(timeLength.GetMilliseconds(), reason || ""); + + // Log Embed To Channel + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); + } + + const dmEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(interaction.guild.name) + .setDescription("You have been given a warning by a moderator.") + .addFields([ + { + name: "Reason", + value: reason || "*none*", + }, + ]); + + let replyText = "Successfully warned user."; + + try { + const dmChannel = await targetUser.user!.createDM(); + await dmChannel.send({ embeds: [ dmEmbed ] }); + } catch { + replyText += " *Note: I was unable to DM the user the reason.*"; + } + + // Create Audit + const audit = new Audit(targetUser.user.id, AuditType.Timeout, reason || "*none*", interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); + + await interaction.reply(replyText); + } +} \ No newline at end of file diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts deleted file mode 100644 index 26b82a2..0000000 --- a/src/commands/unmute.ts +++ /dev/null @@ -1,96 +0,0 @@ -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import { Command } from "../type/command"; - -export default class Unmute extends Command { - constructor() { - super(); - - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - } - - public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { - const targetUser = context.message.mentions.users.first(); - - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); - - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - const reasonArgs = context.args; - reasonArgs.splice(0, 1); - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; - } - - if (!targetMember.manageable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - const logEmbed = new LogEmbed(context, "Member Unmuted"); - logEmbed.AddUser("User", targetUser, true) - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been unmuted`); - publicEmbed.AddReason(reason); - - const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); - - if (!mutedRole) { - const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound); - embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; - } - - await targetMember.roles.remove(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - - await logEmbed.SendToModLogsChannel(); - publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; - } -} \ No newline at end of file diff --git a/src/commands/warn.ts b/src/commands/warn.ts index 3f42318..96d9cf5 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -1,71 +1,91 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; +import Audit from "../database/entities/Audit"; +import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; export default class Warn extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; + this.CommandBuilder = new SlashCommandBuilder() + .setName("warn") + .setDescription("Warns a member in the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { - const user = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild || !interaction.guildId) return; - if (!user) { - const errorEmbed = new ErrorEmbed(context, "User does not exist"); - errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); + return; } - const member = context.message.guild?.members.cache.find(x => x.user.id == user.id); + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!member) { - const errorEmbed = new ErrorEmbed(context, "User is not in this server"); - errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Warned") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .setThumbnail(targetUser.user.avatarURL()) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - const reasonArgs = context.args; - reasonArgs.splice(0, 1); + const dmEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(interaction.guild.name) + .setDescription("You have been given a warning by a moderator.") + .addFields([ + { + name: "Reason", + value: reason, + }, + ]); - const reason = reasonArgs.join(" "); + let replyText = "Successfully warned user."; - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; + try { + const dmChannel = await targetUser.user!.createDM(); + await dmChannel.send({ embeds: [ dmEmbed ] }); + } catch { + replyText += " *Note: I was unable to DM the user the reason.*"; } - const logEmbed = new LogEmbed(context, "Member Warned"); - logEmbed.AddUser("User", user, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); + const audit = new Audit(targetUser.user.id, AuditType.Warn, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); - const publicEmbed = new PublicEmbed(context, "", `${user} has been warned`); - publicEmbed.AddReason(reason); - - await logEmbed.SendToModLogsChannel(); - publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; + await interaction.reply(replyText); } } \ No newline at end of file diff --git a/src/constants/AuditType.ts b/src/constants/AuditType.ts new file mode 100644 index 0000000..0fd7325 --- /dev/null +++ b/src/constants/AuditType.ts @@ -0,0 +1,8 @@ +export enum AuditType { + General, + Warn, + Mute, + Kick, + Ban, + Timeout, +} \ No newline at end of file diff --git a/src/constants/CommandResponse.ts b/src/constants/CommandResponse.ts deleted file mode 100644 index b876ce8..0000000 --- a/src/constants/CommandResponse.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum CommandResponse { - Ok, - Unauthorised, - ServerNotSetup, - NotInServer, - FeatureDisabled, -} \ No newline at end of file diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index 6ed44dc..82bc70e 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -6,7 +6,7 @@ export default class DefaultValues { this.SetValues(); const res = this.values.find(x => x.Key == key); - + if (!res) { return undefined; } @@ -17,11 +17,7 @@ export default class DefaultValues { private static SetValues() { if (this.values.length == 0) { // Bot - if (this.useDevPrefix) { - this.values.push({ Key: "bot.prefix", Value: "d!" }); - } else { - this.values.push({ Key: "bot.prefix", Value: "v!" }); - } + this.values.push({ Key: "bot.prefix", Value: process.env.BOT_PREFIX || "v!" }) // Commands this.values.push({ Key: "commands.disabled", Value: "" }); @@ -35,6 +31,7 @@ export default class DefaultValues { // Rules (Command) this.values.push({ Key: "rules.file", Value: "data/rules/rules" }); + this.values.push({ Key: "rules.access.label", Value: "Verify" }); // Channels this.values.push({ Key: "channels.logs.message", Value: "message-logs" }); @@ -46,6 +43,26 @@ export default class DefaultValues { this.values.push({ Key: "verification.channel", Value: "entry" }); this.values.push({ Key: "verification.role", Value: "Entry" }); this.values.push({ Key: "verification.code", Value: "" }); + + // Gif Only Mode + this.values.push({ Key: "channel.linkonly", Value: "" }) + + // Event + this.values.push({ Key: "event.message.delete.enabled", Value: "false" }); + this.values.push({ Key: "event.message.delete.channel", Value: "message-logs" }); + + this.values.push({ Key: "event.message.update.enabled", Value: "false" }); + this.values.push({ Key: "event.message.update.channel", Value: "message-logs" }); + + this.values.push({ Key: "event.member.add.enabled", Value: "false" }); + this.values.push({ Key: "event.member.add.channel", Value: "member-logs" }); + + this.values.push({ Key: "event.member.remove.enabled", Value: "false" }); + this.values.push({ Key: "event.member.remove.channel", Value: "member-logs" }); + + this.values.push({ Key: "event.member.update.enabled", Value: "false" }); + this.values.push({ Key: "event.member.update.channel", Value: "member-logs" }); + } } } diff --git a/src/constants/EmbedColours.ts b/src/constants/EmbedColours.ts new file mode 100644 index 0000000..5c130c4 --- /dev/null +++ b/src/constants/EmbedColours.ts @@ -0,0 +1,7 @@ +export default class EmbedColours { + public static readonly Ok = 0x3050ba; + public static readonly Warning = 0xffbf00; + public static readonly Danger = 0xd2042d; + + public static readonly Moon = 0x50C878; +} \ No newline at end of file diff --git a/src/constants/ErrorMessages.ts b/src/constants/ErrorMessages.ts deleted file mode 100644 index 588a143..0000000 --- a/src/constants/ErrorMessages.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { CommandResponse } from "./CommandResponse"; - -export default class ErrorMessages { - public static readonly InsufficientBotPermissions = "Unable to do this action, am I missing permissions?"; - public static readonly ChannelNotFound = "Unable to find channel"; - public static readonly RoleNotFound = "Unable to find role"; - - public static readonly UserUnauthorised = "You are not authorised to use this command"; - public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command"; - public static readonly NotInServer = "This command requires to be ran inside of a server"; - public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator"; - - public static GetErrorMessage(response: CommandResponse): string { - switch (response) { - case CommandResponse.Unauthorised: - return this.UserUnauthorised; - case CommandResponse.ServerNotSetup: - return this.ServerNotSetup; - case CommandResponse.NotInServer: - return this.NotInServer; - case CommandResponse.FeatureDisabled: - return this.FeatureDisabled; - default: - return ""; - } - } -} \ No newline at end of file diff --git a/src/constants/EventType.ts b/src/constants/EventType.ts new file mode 100644 index 0000000..c27c6ed --- /dev/null +++ b/src/constants/EventType.ts @@ -0,0 +1,15 @@ +export enum EventType { + ChannelCreate, + ChannelDelete, + ChannelUpdate, + GuildBanAdd, + GuildBanRemove, + GuildCreate, + GuildMemberAdd, + GuildMemberRemove, + GuildMemberUpdate, + MessageCreate, + MessageDelete, + MessageUpdate, + Ready, +} \ No newline at end of file diff --git a/src/contracts/BaseEntity.ts b/src/contracts/BaseEntity.ts index 18c2b76..941ba6f 100644 --- a/src/contracts/BaseEntity.ts +++ b/src/contracts/BaseEntity.ts @@ -1,10 +1,11 @@ -import { Column, DeepPartial, EntityTarget, getConnection, PrimaryColumn } from "typeorm"; +import { Column, DeepPartial, EntityTarget, PrimaryColumn, ObjectLiteral, FindOptionsWhere } from "typeorm"; import { v4 } from "uuid"; +import AppDataSource from "../database/dataSources/appDataSource"; export default class BaseEntity { constructor() { this.Id = v4(); - + this.WhenCreated = new Date(); this.WhenUpdated = new Date(); } @@ -18,48 +19,38 @@ export default class BaseEntity { @Column() WhenUpdated: Date; - public async Save<T>(target: EntityTarget<T>, entity: DeepPartial<T>): Promise<void> { + public async Save<T extends BaseEntity>(target: EntityTarget<T>, entity: DeepPartial<T>): Promise<void> { this.WhenUpdated = new Date(); - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + const repository = AppDataSource.getRepository<T>(target); await repository.save(entity); } - public static async Remove<T>(target: EntityTarget<T>, entity: T): Promise<void> { - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + public static async Remove<T extends BaseEntity>(target: EntityTarget<T>, entity: T): Promise<void> { + const repository = AppDataSource.getRepository<T>(target); await repository.remove(entity); } - public static async FetchAll<T>(target: EntityTarget<T>, relations?: string[]): Promise<T[]> { - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + public static async FetchAll<T extends BaseEntity>(target: EntityTarget<T>, relations?: string[]): Promise<T[]> { + const repository = AppDataSource.getRepository<T>(target); const all = await repository.find({ relations: relations || [] }); return all; } - public static async FetchOneById<T>(target: EntityTarget<T>, id: string, relations?: string[]): Promise<T | undefined> { - const connection = getConnection(); + public static async FetchOneById<T extends BaseEntity>(target: EntityTarget<T>, id: string, relations?: string[]): Promise<T | null> { + const repository = AppDataSource.getRepository<T>(target); - const repository = connection.getRepository<T>(target); - - const single = await repository.findOne(id, { relations: relations || [] }); + const single = await repository.findOne({ where: ({ Id: id } as FindOptionsWhere<T>), relations: relations || {} }); return single; } - public static async Any<T>(target: EntityTarget<T>): Promise<boolean> { - const connection = getConnection(); - - const repository = connection.getRepository<T>(target); + public static async Any<T extends ObjectLiteral>(target: EntityTarget<T>): Promise<boolean> { + const repository = AppDataSource.getRepository<T>(target); const any = await repository.find(); diff --git a/src/contracts/ButtonEventItem.ts b/src/contracts/ButtonEventItem.ts new file mode 100644 index 0000000..4a4c368 --- /dev/null +++ b/src/contracts/ButtonEventItem.ts @@ -0,0 +1,8 @@ +import { ButtonEvent } from "../type/buttonEvent"; + +interface ButtonEventItem { + ButtonId: string, + Event: ButtonEvent, +} + +export default ButtonEventItem; \ No newline at end of file diff --git a/src/contracts/ICommandContext.ts b/src/contracts/ICommandContext.ts deleted file mode 100644 index 78f0d17..0000000 --- a/src/contracts/ICommandContext.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Message } from "discord.js"; - -export interface ICommandContext { - name: string; - args: string[]; - message: Message; -} \ No newline at end of file diff --git a/src/contracts/ICommandReturnContext.ts b/src/contracts/ICommandReturnContext.ts deleted file mode 100644 index 144e981..0000000 --- a/src/contracts/ICommandReturnContext.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { MessageEmbed } from "discord.js"; -import { ICommandContext } from "./ICommandContext"; - -export default interface ICommandReturnContext { - commandContext: ICommandContext, - embeds: MessageEmbed[] -} \ No newline at end of file diff --git a/src/contracts/IEventItem.ts b/src/contracts/IEventItem.ts index 51bb881..ea32a3e 100644 --- a/src/contracts/IEventItem.ts +++ b/src/contracts/IEventItem.ts @@ -1,6 +1,7 @@ -import { Event } from "../type/event"; +import { EventType } from "../constants/EventType"; export default interface IEventItem { - Event: Event, + EventType: EventType, + ExecutionFunction: Function, } \ No newline at end of file diff --git a/src/contracts/IEventReturnContext.ts b/src/contracts/IEventReturnContext.ts deleted file mode 100644 index ccbe56d..0000000 --- a/src/contracts/IEventReturnContext.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { MessageEmbed } from "discord.js"; -import { ICommandContext } from "./ICommandContext"; - -export default interface ICommandReturnContext { - embeds: MessageEmbed[] -} \ No newline at end of file diff --git a/src/database/dataSources/appDataSource.ts b/src/database/dataSources/appDataSource.ts new file mode 100644 index 0000000..89cd27f --- /dev/null +++ b/src/database/dataSources/appDataSource.ts @@ -0,0 +1,26 @@ +import { DataSource } from "typeorm"; +import * as dotenv from "dotenv"; + +dotenv.config(); + +const AppDataSource = new DataSource({ + type: "mysql", + host: process.env.DB_HOST, + port: Number(process.env.DB_PORT), + username: process.env.DB_AUTH_USER, + password: process.env.DB_AUTH_PASS, + database: process.env.DB_NAME, + synchronize: process.env.DB_SYNC == "true", + logging: process.env.DB_LOGGING == "true", + entities: [ + "dist/database/entities/**/*.js", + ], + migrations: [ + "dist/database/migrations/**/*.js", + ], + subscribers: [ + "dist/database/subscribers/**/*.js", + ], +}); + +export default AppDataSource; \ No newline at end of file diff --git a/src/database/entities/304276391837302787/Moon.ts b/src/database/entities/304276391837302787/Moon.ts new file mode 100644 index 0000000..06a2d48 --- /dev/null +++ b/src/database/entities/304276391837302787/Moon.ts @@ -0,0 +1,57 @@ +import { Column, Entity, IsNull } from "typeorm"; +import BaseEntity from "../../../contracts/BaseEntity"; +import AppDataSource from "../../dataSources/appDataSource"; + +@Entity() +export default class Moon extends BaseEntity { + constructor(moonNumber: number, description: string, userId: string) { + super(); + + this.MoonNumber = moonNumber; + this.Description = description; + this.UserId = userId; + } + + @Column() + MoonNumber: number; + + @Column() + Description: string; + + @Column({ nullable: true }) + WhenArchived?: Date; + + @Column() + UserId: string; + + public static async FetchMoonsByUserId(userId: string): Promise<Moon[] | null> { + const repository = AppDataSource.getRepository(Moon); + + const all = await repository.find({ where: { UserId: userId } }); + + return all; + } + + public static async FetchPaginatedMoonsByUserId(userId: string, pageLength: number, page: number): Promise<[ Moon[], number ]> { + const rangeStart = page * pageLength; + + const repository = AppDataSource.getRepository(Moon); + + const moons = await repository.findAndCount({ + where: { UserId: userId, WhenArchived: IsNull() }, + order: { MoonNumber: "ASC" }, + skip: rangeStart, + take: pageLength, + }); + + return moons; + } + + public static async FetchMoonCountByUserId(userId: string): Promise<number> { + const repository = AppDataSource.getRepository(Moon); + + const count = await repository.count({ where: { UserId: userId } }); + + return count; + } +} diff --git a/src/entity/501231711271780357/Lobby.ts b/src/database/entities/501231711271780357/Lobby.ts similarity index 65% rename from src/entity/501231711271780357/Lobby.ts rename to src/database/entities/501231711271780357/Lobby.ts index b6ffd80..95eafc8 100644 --- a/src/entity/501231711271780357/Lobby.ts +++ b/src/database/entities/501231711271780357/Lobby.ts @@ -1,5 +1,6 @@ -import { Column, Entity, getConnection } from "typeorm"; -import BaseEntity from "../../contracts/BaseEntity"; +import { Column, Entity } from "typeorm"; +import BaseEntity from "../../../contracts/BaseEntity"; +import AppDataSource from "../../dataSources/appDataSource"; @Entity() export default class Lobby extends BaseEntity { @@ -33,12 +34,10 @@ export default class Lobby extends BaseEntity { this.LastUsed = new Date(); } - public static async FetchOneByChannelId(channelId: string, relations?: string[]): Promise<Lobby | undefined> { - const connection = getConnection(); + public static async FetchOneByChannelId(channelId: string, relations?: string[]): Promise<Lobby | null> { + const repository = AppDataSource.getRepository(Lobby); - const repository = connection.getRepository(Lobby); - - const single = await repository.findOne({ ChannelId: channelId }, { relations: relations || [] }); + const single = await repository.findOne({ where: { ChannelId: channelId }, relations: relations || [] }); return single; } diff --git a/src/database/entities/Audit.ts b/src/database/entities/Audit.ts new file mode 100644 index 0000000..af4b1a5 --- /dev/null +++ b/src/database/entities/Audit.ts @@ -0,0 +1,53 @@ +import { Column, Entity } from "typeorm"; +import { AuditType } from "../../constants/AuditType"; +import BaseEntity from "../../contracts/BaseEntity"; +import StringTools from "../../helpers/StringTools"; +import AppDataSource from "../dataSources/appDataSource"; + +@Entity() +export default class Audit extends BaseEntity { + constructor(userId: string, auditType: AuditType, reason: string, moderatorId: string, serverId: string) { + super(); + + this.AuditId = StringTools.RandomString(5).toUpperCase(); + this.UserId = userId; + this.AuditType = auditType; + this.Reason = reason; + this.ModeratorId = moderatorId; + this.ServerId = serverId; + } + + @Column() + AuditId: string; + + @Column() + UserId: string; + + @Column() + AuditType: AuditType; + + @Column() + Reason: string; + + @Column() + ModeratorId: string; + + @Column() + ServerId: string; + + public static async FetchAuditsByUserId(userId: string, serverId: string): Promise<Audit[] | null> { + const repository = AppDataSource.getRepository(Audit); + + const all = await repository.find({ where: { UserId: userId, ServerId: serverId } }); + + return all; + } + + public static async FetchAuditByAuditId(auditId: string, serverId: string): Promise<Audit | null> { + const repository = AppDataSource.getRepository(Audit); + + const single = await repository.findOne({ where: { AuditId: auditId, ServerId: serverId } }); + + return single; + } +} \ No newline at end of file diff --git a/src/database/entities/AutoKickConfig.ts b/src/database/entities/AutoKickConfig.ts new file mode 100644 index 0000000..6a82ba5 --- /dev/null +++ b/src/database/entities/AutoKickConfig.ts @@ -0,0 +1,61 @@ +import {Column, Entity} from "typeorm"; +import AppDataSource from "../dataSources/appDataSource"; +import BaseEntity from "../../contracts/BaseEntity"; + +@Entity() +export default class AutoKickConfig extends BaseEntity { + constructor(serverId: string, roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) { + super(); + + this.ServerId = serverId; + this.RoleId = roleId; + this.KickTime = kickTime; + this.NoticeTime = noticeTime; + this.NoticeChannelId = noticeChannelId; + } + + @Column() + ServerId: string; + + @Column() + RoleId: string; + + @Column({ type: "int" }) + KickTime: number; + + @Column({ type: "int", nullable: true }) + NoticeTime?: number; + + @Column({ nullable: true }) + NoticeChannelId?: string; + + public UpdateBasicDetails(roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) { + this.RoleId = roleId; + this.KickTime = kickTime; + this.NoticeTime = noticeTime; + this.NoticeChannelId = noticeChannelId; + } + + public static async FetchOneByServerIdAndRoleId(serverId: string, roleId: string): Promise<AutoKickConfig | null> { + const repository = AppDataSource.getRepository(AutoKickConfig); + + const query = repository + .createQueryBuilder("config") + .where("config.serverId = :serverId", { serverId }) + .andWhere("config.roleId = :roleId", { roleId }) + .getOne(); + + return query; + } + + public static async FetchAllByServerId(serverId: string): Promise<AutoKickConfig[]> { + const repository = AppDataSource.getRepository(AutoKickConfig); + + const query = repository + .createQueryBuilder("config") + .where("config.serverId = :serverId", { serverId }) + .getMany(); + + return query; + } +} diff --git a/src/database/entities/IgnoredChannel.ts b/src/database/entities/IgnoredChannel.ts new file mode 100644 index 0000000..7b40be7 --- /dev/null +++ b/src/database/entities/IgnoredChannel.ts @@ -0,0 +1,20 @@ +import { Entity } from "typeorm"; +import BaseEntity from "../../contracts/BaseEntity"; +import AppDataSource from "../dataSources/appDataSource"; + +@Entity() +export default class IgnoredChannel extends BaseEntity { + constructor(channelId: string) { + super(); + + this.Id = channelId; + } + + public static async IsChannelIgnored(channelId: string): Promise<boolean> { + const repository = AppDataSource.getRepository(IgnoredChannel); + + const single = await repository.findOne({ where: { Id: channelId } }); + + return single != undefined; + } +} \ No newline at end of file diff --git a/src/database/entities/Role.ts b/src/database/entities/Role.ts new file mode 100644 index 0000000..6f83bf5 --- /dev/null +++ b/src/database/entities/Role.ts @@ -0,0 +1,45 @@ +import { Column, Entity, ManyToOne } from "typeorm"; +import BaseEntity from "../../contracts/BaseEntity" +import Server from "./Server"; +import AppDataSource from "../dataSources/appDataSource"; + +@Entity() +export default class Role extends BaseEntity { + constructor(roleId: string) { + super(); + + this.RoleId = roleId; + } + + @Column() + RoleId: string; + + @ManyToOne(() => Server, x => x.Roles) + Server: Server; + + public SetServer(server: Server) { + this.Server = server; + } + + public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise<Role | null> { + const repository = AppDataSource.getRepository(Role); + + const single = await repository.findOne({ where: { RoleId: roleId }, relations: relations || []}); + + return single; + } + + public static async FetchAllByServerId(serverId: string): Promise<Role[]> { + const repository = AppDataSource.getRepository(Server); + + const all = await repository.findOne({ where: { Id: serverId }, relations: [ + "Roles", + ] }); + + if (!all) { + return []; + } + + return all.Roles; + } +} \ No newline at end of file diff --git a/src/database/entities/Server.ts b/src/database/entities/Server.ts new file mode 100644 index 0000000..623f816 --- /dev/null +++ b/src/database/entities/Server.ts @@ -0,0 +1,35 @@ +import { Column, Entity, OneToMany } from "typeorm"; +import BaseEntity from "../../contracts/BaseEntity"; +import Role from "./Role"; +import Setting from "./Setting"; + +@Entity() +export default class Server extends BaseEntity { + constructor(serverId: string) { + super(); + + this.Id = serverId; + this.LastCached = new Date(); + } + + @Column({ default: "2024-03-01 18:10:04" }) + LastCached: Date; + + @OneToMany(() => Setting, x => x.Server) + Settings: Setting[]; + + @OneToMany(() => Role, x => x.Server) + Roles: Role[]; + + public UpdateLastCached(lastCached: Date) { + this.LastCached = lastCached; + } + + public AddSettingToServer(setting: Setting) { + this.Settings.push(setting); + } + + public AddRoleToServer(role: Role) { + this.Roles.push(role); + } +} \ No newline at end of file diff --git a/src/entity/Setting.ts b/src/database/entities/Setting.ts similarity index 60% rename from src/entity/Setting.ts rename to src/database/entities/Setting.ts index bab0ad9..e7b27d1 100644 --- a/src/entity/Setting.ts +++ b/src/database/entities/Setting.ts @@ -1,6 +1,7 @@ -import { Column, Entity, getConnection, ManyToOne } from "typeorm"; -import BaseEntity from "../contracts/BaseEntity"; +import { Column, Entity, ManyToOne } from "typeorm"; +import BaseEntity from "../../contracts/BaseEntity"; import Server from "./Server"; +import AppDataSource from "../dataSources/appDataSource"; @Entity() export default class Setting extends BaseEntity { @@ -25,12 +26,10 @@ export default class Setting extends BaseEntity { this.Value = value; } - public static async FetchOneByKey(key: string, relations?: string[]): Promise<Setting | undefined> { - const connection = getConnection(); + public static async FetchOneByKey(key: string, relations?: string[]): Promise<Setting | null> { + const repository = AppDataSource.getRepository(Setting); - const repository = connection.getRepository(Setting); - - const single = await repository.findOne({ Key: key }, { relations: relations || [] }); + const single = await repository.findOne({ where: { Key: key }, relations: relations || {} }); return single; } diff --git a/src/database/entities/UserSetting.ts b/src/database/entities/UserSetting.ts new file mode 100644 index 0000000..e5f9ce4 --- /dev/null +++ b/src/database/entities/UserSetting.ts @@ -0,0 +1,35 @@ +import { Column, Entity} from "typeorm"; +import AppDataSource from "../dataSources/appDataSource"; +import BaseEntity from "../../contracts/BaseEntity"; + +@Entity() +export default class UserSetting extends BaseEntity { + constructor(userId: string, key: string, value: string) { + super(); + + this.UserId = userId; + this.Key = key; + this.Value = value; + } + + @Column() + UserId: string; + + @Column() + Key: string; + + @Column() + Value: string; + + public UpdateValue(value: string) { + this.Value = value; + } + + public static async FetchOneByKey(userId: string, key: string, relations?: string[]): Promise<UserSetting | null> { + const repository = AppDataSource.getRepository(UserSetting); + + const single = await repository.findOne({ where: { UserId: userId, Key: key }, relations: relations || {} }); + + return single; + } +} diff --git a/src/database/migrations/3.1/1662399171315-CreateBase.ts b/src/database/migrations/3.1/1662399171315-CreateBase.ts new file mode 100644 index 0000000..b05aee7 --- /dev/null +++ b/src/database/migrations/3.1/1662399171315-CreateBase.ts @@ -0,0 +1,30 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import MigrationHelper from "../../../helpers/MigrationHelper" + +export class vylbot1662399171315 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise<void> { + MigrationHelper.Up('1662399171315-CreateBase', '3.1', [ + "01-table/Audit", + "01-table/IgnoredChannel", + "01-table/Lobby", + "01-table/Role", + "01-table/Server", + "01-table/Setting", + + "02-key/Audit", + "02-key/IgnoredChannel", + "02-key/Lobby", + "02-key/Role", + "02-key/Server", + "02-key/Setting", + + "03-constraint/Role", + "03-constraint/Setting", + ], queryRunner); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + } + +} diff --git a/src/database/migrations/3.2.1/1709316734401-AddServerCacheDate.ts b/src/database/migrations/3.2.1/1709316734401-AddServerCacheDate.ts new file mode 100644 index 0000000..5104655 --- /dev/null +++ b/src/database/migrations/3.2.1/1709316734401-AddServerCacheDate.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import MigrationHelper from "../../../helpers/MigrationHelper" + +export class AddServerCacheDate1709316734401 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise<void> { + MigrationHelper.Up('1709316734401-AddServerCacheDate', '3.2.1', [ + "01-Server", + ], queryRunner); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + } + +} diff --git a/src/database/migrations/3.2.4/1732973911304-createAutoKickConfig.ts b/src/database/migrations/3.2.4/1732973911304-createAutoKickConfig.ts new file mode 100644 index 0000000..c4e9a74 --- /dev/null +++ b/src/database/migrations/3.2.4/1732973911304-createAutoKickConfig.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import MigrationHelper from "../../../helpers/MigrationHelper"; + +export class CreateAutoKickConfig1732973911304 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise<void> { + MigrationHelper.Up("1732973911304-createAutoKickConfig", "3.2.4", [ + "01-AutoKickConfig-Table", + "02-AutoKickConfig-Key", + ], queryRunner) + } + + public async down(queryRunner: QueryRunner): Promise<void> { + MigrationHelper.Down("1732973911304-createAutoKickConfig", "3.2.4", [ + "01-AutoKickConfig", + ], queryRunner) + } + +} diff --git a/src/database/migrations/3.3/1719856023429-CreateMoon.ts b/src/database/migrations/3.3/1719856023429-CreateMoon.ts new file mode 100644 index 0000000..0ccc820 --- /dev/null +++ b/src/database/migrations/3.3/1719856023429-CreateMoon.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import MigrationHelper from "../../../helpers/MigrationHelper" + +export class CreateMoon1719856023429 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise<void> { + MigrationHelper.Up('1719856023429-CreateMoon', '3.3.0', [ + "01-Moon", + ], queryRunner); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + } + +} diff --git a/src/database/migrations/3.3/1727286976268-CreateUserSetting.ts b/src/database/migrations/3.3/1727286976268-CreateUserSetting.ts new file mode 100644 index 0000000..42144ca --- /dev/null +++ b/src/database/migrations/3.3/1727286976268-CreateUserSetting.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import MigrationHelper from "../../../helpers/MigrationHelper"; + +export class CreateUserSetting1727286976268 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise<void> { + MigrationHelper.Up('1727286976268-CreateUserSetting', '3.3.0', [ + "01-UserSetting", + "02-UserSettingKey", + ], queryRunner); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + } + +} diff --git a/src/entity/Server.ts b/src/entity/Server.ts deleted file mode 100644 index 180aef0..0000000 --- a/src/entity/Server.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Column, Entity, getConnection, OneToMany } from "typeorm"; -import BaseEntity from "../contracts/BaseEntity"; -import Setting from "./Setting"; - -@Entity() -export default class Server extends BaseEntity { - constructor(serverId: string) { - super(); - - this.Id = serverId; - } - - @OneToMany(() => Setting, x => x.Server) - Settings: Setting[]; - - public AddSettingToServer(setting: Setting) { - this.Settings.push(setting); - } -} \ No newline at end of file diff --git a/src/events/MemberEvents.ts b/src/events/MemberEvents.ts deleted file mode 100644 index ff111d0..0000000 --- a/src/events/MemberEvents.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Event } from "../type/event"; -import { GuildMember } from "discord.js"; -import EventEmbed from "../helpers/embeds/EventEmbed"; -import GuildMemberUpdate from "./MemberEvents/GuildMemberUpdate"; -import IEventReturnContext from "../contracts/IEventReturnContext"; - -export default class MemberEvents extends Event { - constructor() { - super(); - } - - public override async guildMemberAdd(member: GuildMember): Promise<IEventReturnContext> { - const embed = new EventEmbed(member.guild, "Member Joined"); - embed.AddUser("User", member.user, true); - embed.addField("Created", member.user.createdAt.toISOString()); - embed.setFooter({ text: `Id: ${member.user.id}` }); - - await embed.SendToMemberLogsChannel(); - - return { - embeds: [embed] - }; - } - - public override async guildMemberRemove(member: GuildMember): Promise<IEventReturnContext> { - const embed = new EventEmbed(member.guild, "Member Left"); - embed.AddUser("User", member.user, true); - embed.addField("Joined", member.joinedAt?.toISOString() || "n/a"); - embed.setFooter({ text: `Id: ${member.user.id}` }); - - await embed.SendToMemberLogsChannel(); - - return { - embeds: [embed] - }; - } - - public override async guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember): Promise<IEventReturnContext> { - const handler = new GuildMemberUpdate(oldMember, newMember); - - if (oldMember.nickname != newMember.nickname) { // Nickname change - await handler.NicknameChanged(); - } - - return { - embeds: [] - }; - } -} \ No newline at end of file diff --git a/src/events/MemberEvents/GuildMemberAdd.ts b/src/events/MemberEvents/GuildMemberAdd.ts new file mode 100644 index 0000000..55f91a1 --- /dev/null +++ b/src/events/MemberEvents/GuildMemberAdd.ts @@ -0,0 +1,38 @@ +import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; +import SettingsHelper from "../../helpers/SettingsHelper"; +import CacheHelper from "../../helpers/CacheHelper"; + +export default async function GuildMemberAdd(member: GuildMember) { + if (!member.guild) return; + + await CacheHelper.UpdateServerCache(member.guild); + + const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Member Joined') + .setDescription(`${member.user} \`${member.user.tag}\``) + .setFooter({ text: `Id: ${member.user.id}` }) + .setThumbnail(member.avatarURL()) + .addFields([ + { + name: 'Created', + value: member.user.createdAt.toISOString(), + } + ]); + + const channelSetting = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id); + + if (!channelSetting) return; + + const channel = member.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); +} \ No newline at end of file diff --git a/src/events/MemberEvents/GuildMemberRemove.ts b/src/events/MemberEvents/GuildMemberRemove.ts new file mode 100644 index 0000000..0f24f47 --- /dev/null +++ b/src/events/MemberEvents/GuildMemberRemove.ts @@ -0,0 +1,38 @@ +import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; +import SettingsHelper from "../../helpers/SettingsHelper"; +import CacheHelper from "../../helpers/CacheHelper"; + +export default async function GuildMemberRemove(member: GuildMember) { + if (!member.guild) return; + + await CacheHelper.UpdateServerCache(member.guild); + + const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Member Left') + .setDescription(`${member.user} \`${member.user.tag}\``) + .setFooter({ text: `Id: ${member.user.id}` }) + .setThumbnail(member.avatarURL()) + .addFields([ + { + name: 'Joined', + value: member.joinedAt ? member.joinedAt.toISOString() : "*none*", + } + ]); + + const channelSetting = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id); + + if (!channelSetting) return; + + const channel = member.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); +} \ No newline at end of file diff --git a/src/events/MemberEvents/GuildMemberUpdate.ts b/src/events/MemberEvents/GuildMemberUpdate.ts index 0f438f8..efb1bc4 100644 --- a/src/events/MemberEvents/GuildMemberUpdate.ts +++ b/src/events/MemberEvents/GuildMemberUpdate.ts @@ -1,30 +1,13 @@ import { GuildMember } from "discord.js"; -import IEventReturnContext from "../../contracts/IEventReturnContext"; -import EventEmbed from "../../helpers/embeds/EventEmbed"; +import NicknameChanged from "./GuildMemberUpdate/NicknameChanged"; +import CacheHelper from "../../helpers/CacheHelper"; -export default class GuildMemberUpdate { - public oldMember: GuildMember; - public newMember: GuildMember; +export default async function GuildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) { + const updatedFromCache = await CacheHelper.UpdateServerCache(newMember.guild); - constructor(oldMember: GuildMember, newMember: GuildMember) { - this.oldMember = oldMember; - this.newMember = newMember; - } + if (updatedFromCache) return; - public async NicknameChanged(): Promise<IEventReturnContext> { - const oldNickname = this.oldMember.nickname || "*none*"; - const newNickname = this.newMember.nickname || "*none*"; - - const embed = new EventEmbed(this.newMember.guild, "Nickname Changed"); - embed.AddUser("User", this.newMember.user, true); - embed.addField("Before", oldNickname, true); - embed.addField("After", newNickname, true); - embed.setFooter({ text: `Id: ${this.newMember.user.id}` }); - - await embed.SendToMemberLogsChannel(); - - return { - embeds: [embed] - }; + if (oldMember.nickname !== newMember.nickname) { // Nickname change + await NicknameChanged(oldMember, newMember); } } \ No newline at end of file diff --git a/src/events/MemberEvents/GuildMemberUpdate/NicknameChanged.ts b/src/events/MemberEvents/GuildMemberUpdate/NicknameChanged.ts new file mode 100644 index 0000000..671d755 --- /dev/null +++ b/src/events/MemberEvents/GuildMemberUpdate/NicknameChanged.ts @@ -0,0 +1,40 @@ +import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; +import EmbedColours from "../../../constants/EmbedColours"; +import SettingsHelper from "../../../helpers/SettingsHelper"; + +export default async function NicknameChanged(oldMember: GuildMember, newMember: GuildMember) { + const enabled = await SettingsHelper.GetSetting("event.member.update.enabled", newMember.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const oldNickname = oldMember.nickname || "*none*"; + const newNickname = newMember.nickname || "*none*"; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Nickname Changed') + .setDescription(`${newMember.user} \`${newMember.user.tag}\``) + .setFooter({ text: `Id: ${newMember.user.id}` }) + .setThumbnail(newMember.avatarURL()) + .addFields([ + { + name: 'Before', + value: oldNickname, + }, + { + name: 'After', + value: newNickname, + }, + ]); + + const channelSetting = await SettingsHelper.GetSetting("event.member.update.channel", newMember.guild.id); + + if (!channelSetting) return; + + const channel = newMember.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); +} \ No newline at end of file diff --git a/src/events/MessageEvents.ts b/src/events/MessageEvents.ts deleted file mode 100644 index 6f1d2f5..0000000 --- a/src/events/MessageEvents.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Event } from "../type/event"; -import { Message } from "discord.js"; -import EventEmbed from "../helpers/embeds/EventEmbed"; -import IEventReturnContext from "../contracts/IEventReturnContext"; -import SettingsHelper from "../helpers/SettingsHelper"; -import OnMessage from "./MessageEvents/OnMessage"; - -export default class MessageEvents extends Event { - constructor() { - super(); - } - - public override async messageDelete(message: Message): Promise<IEventReturnContext> { - if (!message.guild) { - return { - embeds: [] - }; - } - - if (message.author.bot) { - return { - embeds: [] - }; - } - - const embed = new EventEmbed(message.guild, "Message Deleted"); - embed.AddUser("User", message.author, true); - embed.addField("Channel", message.channel.toString(), true); - embed.addField("Content", `\`\`\`${message.content || "*none*"}\`\`\``); - - if (message.attachments.size > 0) { - embed.addField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``); - } - - await embed.SendToMessageLogsChannel(); - - return { - embeds: [embed] - }; - } - - public override async messageUpdate(oldMessage: Message, newMessage: Message): Promise<IEventReturnContext> { - if (!newMessage.guild){ - return { - embeds: [] - }; - } - - if (newMessage.author.bot) { - return { - embeds: [] - }; - } - - if (oldMessage.content == newMessage.content) { - return { - embeds: [] - }; - } - - const embed = new EventEmbed(newMessage.guild, "Message Edited"); - embed.AddUser("User", newMessage.author, true); - embed.addField("Channel", newMessage.channel.toString(), true); - embed.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``); - embed.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``); - - await embed.SendToMessageLogsChannel(); - - return { - embeds: [embed] - }; - } - - public override async messageCreate(message: Message) { - if (!message.guild) return; - if (message.author.bot) return; - - const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id); - - if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") { - await OnMessage.VerificationCheck(message); - } - } -} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageCreate.ts b/src/events/MessageEvents/MessageCreate.ts new file mode 100644 index 0000000..88cba71 --- /dev/null +++ b/src/events/MessageEvents/MessageCreate.ts @@ -0,0 +1,20 @@ +import { Message } from "discord.js"; +import SettingsHelper from "../../helpers/SettingsHelper"; +import VerificationCheck from "./MessageCreate/VerificationCheck"; +import CacheHelper from "../../helpers/CacheHelper"; +import LinkOnlyMode from "./MessageCreate/LinkOnlyMode"; + +export default async function MessageCreate(message: Message) { + if (!message.guild) return; + if (message.author.bot) return; + + await CacheHelper.UpdateServerCache(message.guild); + + await LinkOnlyMode(message); + + const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id); + + if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") { + await VerificationCheck(message); + } +} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts b/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts new file mode 100644 index 0000000..0c9e53f --- /dev/null +++ b/src/events/MessageEvents/MessageCreate/LinkOnlyMode.ts @@ -0,0 +1,16 @@ +import { Message } from "discord.js"; +import SettingsHelper from "../../../helpers/SettingsHelper"; + +export default async function LinkOnlyMode(message: Message) { + if (!message.guild) return; + + const gifOnlyMode = await SettingsHelper.GetSetting("channel.linkonly", message.guild.id); + + if (gifOnlyMode != message.channel.id) return; + + if (message.content.startsWith("https://") || message.content.startsWith("http://")) return; + + if (!message.deletable) return; + + await message.delete(); +} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageCreate/VerificationCheck.ts b/src/events/MessageEvents/MessageCreate/VerificationCheck.ts new file mode 100644 index 0000000..1dba4e3 --- /dev/null +++ b/src/events/MessageEvents/MessageCreate/VerificationCheck.ts @@ -0,0 +1,57 @@ +import { Message } from "discord.js"; +import SettingsHelper from "../../../helpers/SettingsHelper"; + +export default async function VerificationCheck(message: Message) { + if (!message.guild) return; + + const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id); + + if (!verificationChannel) { + return; + } + + const channel = message.guild.channels.cache.find(x => x.name == verificationChannel); + + if (!channel) { + return; + } + + const currentChannel = message.guild.channels.cache.find(x => x == message.channel); + + if (!currentChannel || currentChannel.name != verificationChannel) { + return; + } + + const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id); + + if (!verificationCode || verificationCode == "") { + await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team."); + await message.delete(); + + return; + } + + const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id); + + if (!verificationRoleName) { + await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team."); + await message.delete(); + return; + } + + const role = message.guild.roles.cache.find(x => x.name == verificationRoleName); + + if (!role) { + await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team."); + await message.delete(); + return; + } + + if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) { + await message.delete(); + return; + } + + await message.member?.roles.add(role); + await message.delete(); +} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageDelete.ts b/src/events/MessageEvents/MessageDelete.ts new file mode 100644 index 0000000..f24ec5d --- /dev/null +++ b/src/events/MessageEvents/MessageDelete.ts @@ -0,0 +1,56 @@ +import { EmbedBuilder, Message, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; +import IgnoredChannel from "../../database/entities/IgnoredChannel"; +import SettingsHelper from "../../helpers/SettingsHelper"; +import CacheHelper from "../../helpers/CacheHelper"; + +export default async function MessageDelete(message: Message) { + if (!message.guild) return; + if (message.author.bot) return; + + await CacheHelper.UpdateServerCache(message.guild); + + const enabled = await SettingsHelper.GetSetting("event.message.delete.enabled", message.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const ignored = await IgnoredChannel.IsChannelIgnored(message.channel.id); + if (ignored) return; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Message Deleted") + .setDescription(`${message.author} \`${message.author.tag}\``) + .setThumbnail(message.author.avatarURL()) + .addFields([ + { + name: "Channel", + value: message.channel.toString(), + inline: true, + }, + { + name: "Content", + value: `\`\`\`${message.content || "*none*"}\`\`\``, + } + ]); + + if (message.attachments.size > 0) { + embed.addFields([ + { + name: `Attachments (${message.attachments.size})`, + value: `\`\`\`${message.attachments.map(x => x.name).join("\n")}\`\`\`` + } + ]); + } + + const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id); + + if (!channelSetting) return; + + const channel = message.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [ embed ]}); +} \ No newline at end of file diff --git a/src/events/MessageEvents/MessageUpdate.ts b/src/events/MessageEvents/MessageUpdate.ts new file mode 100644 index 0000000..27369a4 --- /dev/null +++ b/src/events/MessageEvents/MessageUpdate.ts @@ -0,0 +1,53 @@ +import { EmbedBuilder, Message, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; +import IgnoredChannel from "../../database/entities/IgnoredChannel"; +import SettingsHelper from "../../helpers/SettingsHelper"; +import CacheHelper from "../../helpers/CacheHelper"; + +export default async function MessageUpdate(oldMessage: Message, newMessage: Message) { + if (!newMessage.guild) return; + if (newMessage.author.bot) return; + + await CacheHelper.UpdateServerCache(newMessage.guild); + + if (oldMessage.content == newMessage.content) return; + + const enabled = await SettingsHelper.GetSetting("event.message.update.enabled", newMessage.guild.id); + if (!enabled || enabled.toLowerCase() != "true") return; + + const ignored = await IgnoredChannel.IsChannelIgnored(newMessage.channel.id); + if (ignored) return; + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Message Edited") + .setDescription(`${newMessage.author} \`${newMessage.author.tag}\``) + .setThumbnail(newMessage.author.avatarURL()) + .addFields([ + { + name: "Channel", + value: newMessage.channel.toString(), + inline: true, + }, + { + name: "Before", + value: `\`\`\`${oldMessage.content || "*none*"}\`\`\``, + }, + { + name: "After", + value: `\`\`\`${newMessage.content || "*none*"}\`\`\``, + } + ]); + + const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", newMessage.guild.id); + + if (!channelSetting) return; + + const channel = newMessage.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [ embed ]}); +} \ No newline at end of file diff --git a/src/events/MessageEvents/OnMessage.ts b/src/events/MessageEvents/OnMessage.ts deleted file mode 100644 index 18c2a57..0000000 --- a/src/events/MessageEvents/OnMessage.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Message as Message } from "discord.js"; -import SettingsHelper from "../../helpers/SettingsHelper"; - -export default class OnMessage { - public static async VerificationCheck(message: Message) { - if (!message.guild) return; - - const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id); - - if (!verificationChannel) { - return; - } - - const channel = message.guild.channels.cache.find(x => x.name == verificationChannel); - - if (!channel) { - return; - } - - const currentChannel = message.guild.channels.cache.find(x => x == message.channel); - - if (!currentChannel || currentChannel.name != verificationChannel) { - return; - } - - const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id); - - if (!verificationCode || verificationCode == "") { - await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team."); - await message.delete(); - - return; - } - - const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id); - - if (!verificationRoleName) { - await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team."); - await message.delete(); - return; - } - - const role = message.guild.roles.cache.find(x => x.name == verificationRoleName); - - if (!role) { - await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team."); - await message.delete(); - return; - } - - if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) { - await message.delete(); - return; - } - - await message.member?.roles.add(role); - await message.delete(); - } -} \ No newline at end of file diff --git a/src/helpers/AuditTools.ts b/src/helpers/AuditTools.ts new file mode 100644 index 0000000..9833a3c --- /dev/null +++ b/src/helpers/AuditTools.ts @@ -0,0 +1,41 @@ +import { AuditType } from "../constants/AuditType"; + +export default class AuditTools { + public static TypeToFriendlyText(auditType: AuditType): string { + switch (auditType) { + case AuditType.General: + return "General"; + case AuditType.Warn: + return "Warn"; + case AuditType.Mute: + return "Mute"; + case AuditType.Kick: + return "Kick"; + case AuditType.Ban: + return "Ban"; + case AuditType.Timeout: + return "Timeout"; + default: + return "Other"; + } + } + + public static StringToType(str: string): AuditType { + switch (str.toLowerCase()) { + case "general": + return AuditType.General; + case "warn": + return AuditType.Warn; + case "mute": + return AuditType.Mute; + case "kick": + return AuditType.Kick; + case "ban": + return AuditType.Ban; + case "timeout": + return AuditType.Timeout; + default: + return AuditType.General; + } + } +} \ No newline at end of file diff --git a/src/helpers/AutoKickHelper.ts b/src/helpers/AutoKickHelper.ts new file mode 100644 index 0000000..a02a934 --- /dev/null +++ b/src/helpers/AutoKickHelper.ts @@ -0,0 +1,37 @@ +import AutoKickConfig from "../database/entities/AutoKickConfig"; + +export default class AutoKickHelper { + public static async GetSetting(serverId: string): Promise<AutoKickConfig | null> { + const configs = await AutoKickConfig.FetchAllByServerId(serverId); + + if (configs.length != 1) { + return null; + } + + return configs[0]; + } + + public static async SetSetting(serverId: string, roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) { + const configs = await AutoKickConfig.FetchAllByServerId(serverId); + + if (configs.length == 0) { + const config = new AutoKickConfig(serverId, roleId, kickTime, noticeTime, noticeChannelId); + await config.Save(AutoKickConfig, config); + + return; + } + + const config = configs[0]; + + config.UpdateBasicDetails(roleId, kickTime, noticeTime, noticeChannelId); + await config.Save(AutoKickConfig, config); + } + + public static async UnsetSetting(serverId: string) { + const configs = await AutoKickConfig.FetchAllByServerId(serverId); + + for (let config of configs) { + await AutoKickConfig.Remove(AutoKickConfig, config); + } + } +} diff --git a/src/helpers/CacheHelper.ts b/src/helpers/CacheHelper.ts new file mode 100644 index 0000000..03bf06c --- /dev/null +++ b/src/helpers/CacheHelper.ts @@ -0,0 +1,33 @@ +import { Guild } from "discord.js"; +import Server from "../database/entities/Server"; + +export default class CacheHelper { + public static async UpdateServerCache(guild: Guild): Promise<boolean> { + const cacheInterval = process.env.CACHE_INTERVAL; + + if (!cacheInterval) return false; + + let server = await Server.FetchOneById(Server, guild.id); + + if (!server) { + server = new Server(guild.id); + await server.Save(Server, server); + + await CacheHelper.UpdateCache(guild); + + return true; + } else if (server.LastCached.getTime() + Number(cacheInterval) > Date.now()) { + await CacheHelper.UpdateCache(guild); + + return true; + } + + return false; + } + + private static async UpdateCache(guild: Guild) { + console.log(`Updating cache for ${guild.name} (${guild.id})`); + + await guild.members.fetch(); + } +} \ No newline at end of file diff --git a/src/helpers/MigrationHelper.ts b/src/helpers/MigrationHelper.ts new file mode 100644 index 0000000..69fee4a --- /dev/null +++ b/src/helpers/MigrationHelper.ts @@ -0,0 +1,20 @@ +import { readFileSync } from "fs"; +import { QueryRunner } from "typeorm"; + +export default class MigrationHelper { + public static Up(migrationName: string, version: string, queryFiles: string[], queryRunner: QueryRunner) { + for (let path of queryFiles) { + const query = readFileSync(`${process.cwd()}/database/${version}/${migrationName}/Up/${path}.sql`).toString(); + + queryRunner.query(query); + } + } + + public static Down(migrationName: string, version: string, queryFiles: string[], queryRunner: QueryRunner) { + for (let path of queryFiles) { + const query = readFileSync(`${process.cwd()}/database/${version}/${migrationName}/Down/${path}.sql`).toString(); + + queryRunner.query(query); + } + } +} \ No newline at end of file diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index 35bc342..66c92a8 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -1,7 +1,6 @@ -import { getConnection } from "typeorm"; import DefaultValues from "../constants/DefaultValues"; -import Server from "../entity/Server"; -import Setting from "../entity/Setting"; +import Server from "../database/entities/Server"; +import Setting from "../database/entities/Setting"; export default class SettingsHelper { public static async GetSetting(key: string, serverId: string): Promise<string | undefined> { @@ -47,4 +46,14 @@ export default class SettingsHelper { await server.Save(Server, server); } } + + public static async GetServerPrefix(serverId: string): Promise<string> { + const setting = await this.GetSetting("bot.prefix", serverId); + + if (!setting) { + return "v!"; + } + + return setting; + } } \ No newline at end of file diff --git a/src/helpers/StringTools.ts b/src/helpers/StringTools.ts index 5119f94..0254dc5 100644 --- a/src/helpers/StringTools.ts +++ b/src/helpers/StringTools.ts @@ -35,4 +35,8 @@ export default class StringTools { return result; } + + public static ReplaceAll(str: string, find: string, replace: string) { + return str.replace(new RegExp(find, 'g'), replace); + } } \ No newline at end of file diff --git a/src/helpers/TimeLengthInput.ts b/src/helpers/TimeLengthInput.ts new file mode 100644 index 0000000..95befa6 --- /dev/null +++ b/src/helpers/TimeLengthInput.ts @@ -0,0 +1,121 @@ +import StringTools from "./StringTools"; + +export default class TimeLengthInput { + public readonly value: string; + + constructor(input: string) { + this.value = StringTools.ReplaceAll(input, ',', ''); + } + + public GetDays(): number { + return this.GetValue('d'); + } + + public GetHours(): number { + return this.GetValue('h'); + } + + public GetMinutes(): number { + return this.GetValue('m'); + } + + public GetSeconds(): number { + return this.GetValue('s'); + } + + public GetMilliseconds(): number { + const days = this.GetDays(); + const hours = this.GetHours(); + const minutes = this.GetMinutes(); + const seconds = this.GetSeconds(); + + let milliseconds = 0; + + milliseconds += seconds * 1000; + milliseconds += minutes * 60 * 1000; + milliseconds += hours * 60 * 60 * 1000; + milliseconds += days * 24 * 60 * 60 * 1000; + + return milliseconds; + } + + public GetDateFromNow(): Date { + const now = Date.now(); + + const dateFromNow = now + + (1000 * this.GetSeconds()) + + (1000 * 60 * this.GetMinutes()) + + (1000 * 60 * 60 * this.GetHours()) + + (1000 * 60 * 60 * 24 * this.GetDays()); + + return new Date(dateFromNow); + } + + public GetLength(): string { + const days = this.GetDays(); + const hours = this.GetHours(); + const minutes = this.GetMinutes(); + const seconds = this.GetSeconds(); + + const value = []; + + if (days) { + value.push(`${days} days`); + } + + if (hours) { + value.push(`${hours} hours`); + } + + if (minutes) { + value.push(`${minutes} minutes`); + } + + if (seconds) { + value.push(`${seconds} seconds`); + } + + return value.join(", "); + } + + public GetLengthShort(): string { + const days = this.GetDays(); + const hours = this.GetHours(); + const minutes = this.GetMinutes(); + const seconds = this.GetSeconds(); + + const value = []; + + if (days) { + value.push(`${days}d`); + } + + if (hours) { + value.push(`${hours}h`); + } + + if (minutes) { + value.push(`${minutes}m`); + } + + if (seconds) { + value.push(`${seconds}s`); + } + + return value.join(" "); + } + + private GetValue(designation: string): number { + const valueSplit = this.value.split(' '); + + const desString = valueSplit.find(x => x.charAt(x.length - 1) == designation); + + if (!desString) return 0; + + const desNumber = Number(desString.substring(0, desString.length - 1)); + + if (!desNumber) return 0; + + return desNumber; + } +} \ No newline at end of file diff --git a/src/helpers/TimerHelper.ts b/src/helpers/TimerHelper.ts new file mode 100644 index 0000000..3cc4246 --- /dev/null +++ b/src/helpers/TimerHelper.ts @@ -0,0 +1,81 @@ +import {CronJob} from "cron"; +import {primitive} from "../type/primitive"; +import {v4} from "uuid"; + +interface Timer { + id: string; + job: CronJob; + context: Map<string, primitive>; + onTick: ((context: Map<string, primitive>) => void) | ((context: Map<string, primitive>) => Promise<void>); + runOnStart: boolean; +} + +export default class TimerHelper { + private _timers: Timer[]; + + constructor() { + this._timers = []; + } + + public AddTimer( + cronTime: string, + timeZone: string, + onTick: ((context: Map<string, primitive>) => void) | ((context: Map<string, primitive>) => Promise<void>), + runOnStart: boolean = false): string { + const context = new Map<string, primitive>(); + + const job = new CronJob( + cronTime, + () => { + onTick(context); + }, + null, + false, + timeZone, + ); + + const id = v4(); + + this._timers.push({ + id, + job, + context, + onTick, + runOnStart, + }); + + return id; + } + + public StartAllTimers() { + this._timers.forEach(timer => this.StartJob(timer)); + } + + public StopAllTimers() { + this._timers.forEach(timer => timer.job.stop()); + } + + public StartTimer(id: string) { + const timer = this._timers.find(x => x.id == id); + + if (!timer) return; + + this.StartJob(timer); + } + + public StopTimer(id: string) { + const timer = this._timers.find(x => x.id == id); + + if (!timer) return; + + timer.job.stop(); + } + + private StartJob(timer: Timer) { + timer.job.start(); + + if (timer.runOnStart) { + timer.onTick(timer.context); + } + } +} diff --git a/src/helpers/embeds/ErrorEmbed.ts b/src/helpers/embeds/ErrorEmbed.ts deleted file mode 100644 index 146461f..0000000 --- a/src/helpers/embeds/ErrorEmbed.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MessageEmbed } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; - -export default class ErrorEmbed extends MessageEmbed { - public context: ICommandContext; - - constructor(context: ICommandContext, message: string) { - super(); - - super.setColor(0xd52803); - super.setDescription(message); - - this.context = context; - } - - public SendToCurrentChannel() { - this.context.message.channel.send({ embeds: [ this ]}); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/EventEmbed.ts b/src/helpers/embeds/EventEmbed.ts deleted file mode 100644 index 3dfd5dc..0000000 --- a/src/helpers/embeds/EventEmbed.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { MessageEmbed, TextChannel, User, Guild } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; -import SettingsHelper from "../SettingsHelper"; - -export default class EventEmbed extends MessageEmbed { - public guild: Guild; - - constructor(guild: Guild, title: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); - - this.guild = guild; - } - - // Detail methods - public AddUser(title: string, user: User, setThumbnail: boolean = false) { - this.addField(title, `${user} \`${user.tag}\``, true); - - if (setThumbnail) { - this.setThumbnail(user.displayAvatarURL()); - } - } - - public AddReason(message: string) { - this.addField("Reason", message || "*none*"); - } - - // Send methods - public SendToChannel(name: string) { - const channel = this.guild.channels.cache - .find(channel => channel.name == name) as TextChannel; - - if (!channel) { - console.error(`Unable to find channel ${name}`); - return; - } - - channel.send({embeds: [ this ]}); - } - - public async SendToMessageLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.guild.id); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } - - public async SendToMemberLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.guild.id); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } - - public async SendToModLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.guild.id); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/LogEmbed.ts b/src/helpers/embeds/LogEmbed.ts deleted file mode 100644 index 0967b8b..0000000 --- a/src/helpers/embeds/LogEmbed.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { MessageEmbed, TextChannel, User } from "discord.js"; -import ErrorMessages from "../../constants/ErrorMessages"; -import { ICommandContext } from "../../contracts/ICommandContext"; -import SettingsHelper from "../SettingsHelper"; -import ErrorEmbed from "./ErrorEmbed"; - -export default class LogEmbed extends MessageEmbed { - public context: ICommandContext; - - constructor(context: ICommandContext, title: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); - - this.context = context; - } - - // Detail methods - public AddUser(title: string, user: User, setThumbnail: boolean = false) { - this.addField(title, `${user} \`${user.tag}\``, true); - - if (setThumbnail) { - this.setThumbnail(user.displayAvatarURL()); - } - } - - public AddReason(message: string) { - this.addField("Reason", message || "*none*"); - } - - // Send methods - public SendToCurrentChannel() { - this.context.message.channel.send({ embeds: [ this ]}); - } - - public SendToChannel(name: string) { - const channel = this.context.message.guild?.channels.cache - .find(channel => channel.name == name) as TextChannel; - - if (!channel) { - const errorEmbed = new ErrorEmbed(this.context, ErrorMessages.ChannelNotFound); - errorEmbed.SendToCurrentChannel(); - return; - } - - channel.send({ embeds: [ this ]}); - } - - public async SendToMessageLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } - - public async SendToMemberLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } - - public async SendToModLogsChannel() { - const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - this.SendToChannel(channelName); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/PublicEmbed.ts b/src/helpers/embeds/PublicEmbed.ts deleted file mode 100644 index ae28197..0000000 --- a/src/helpers/embeds/PublicEmbed.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MessageEmbed } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; - -export default class PublicEmbed extends MessageEmbed { - public context: ICommandContext; - - constructor(context: ICommandContext, title: string, description: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); - super.setDescription(description); - - this.context = context; - } - - // Detail methods - public AddReason(message: string) { - this.addField("Reason", message || "*none*"); - } - - // Send methods - public SendToCurrentChannel() { - this.context.message.channel.send({ embeds: [ this ]}); - } -} \ No newline at end of file diff --git a/src/registry.ts b/src/registry.ts index 19a673a..ce54808 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -1,59 +1,105 @@ import { CoreClient } from "./client/client"; +import { EventType } from "./constants/EventType"; // Command Imports import About from "./commands/about"; +import Audits from "./commands/audits"; +import Autokick from "./commands/autokick"; import Ban from "./commands/ban"; +import Bunny from "./commands/bunny"; import Clear from "./commands/clear"; import Code from "./commands/code"; import Config from "./commands/config"; import Disable from "./commands/disable"; -import Help from "./commands/help"; +import Ignore from "./commands/ignore"; import Kick from "./commands/kick"; -import Mute from "./commands/mute"; +import Linkonly from "./commands/linkonly"; import Poll from "./commands/poll"; -import Role from "./commands/role"; +import Role from "./commands/Role/role"; +import ConfigRole from "./commands/Role/config"; import Rules from "./commands/rules"; +import Say from "./commands/say"; import Setup from "./commands/setup"; -import Unmute from "./commands/unmute"; +import Timeout from "./commands/timeout"; import Warn from "./commands/warn"; // Command Imports: MankBot import Entry from "./commands/501231711271780357/entry"; -import Lobby from "./commands/501231711271780357/lobby"; +import Lobby from "./commands/501231711271780357/Lobby/lobby"; +import AddLobby from "./commands/501231711271780357/Lobby/add"; +import RemoveLobby from "./commands/501231711271780357/Lobby/remove"; +import ListLobby from "./commands/501231711271780357/Lobby/list"; + +// Command Imports: Potato Talk +import Moons from "./commands/304276391837302787/moons"; // Event Imports -import MemberEvents from "./events/MemberEvents"; -import MessageEvents from "./events/MessageEvents"; +import GuildMemberAdd from "./events/MemberEvents/GuildMemberAdd"; +import GuildMemberRemove from "./events/MemberEvents/GuildMemberRemove"; +import GuildMemberUpdate from "./events/MemberEvents/GuildMemberUpdate"; +import MessageDelete from "./events/MessageEvents/MessageDelete"; +import MessageUpdate from "./events/MessageEvents/MessageUpdate"; +import MessageCreate from "./events/MessageEvents/MessageCreate"; + +// Button Event Imports +import Verify from "./buttonEvents/verify"; +import MoonsButtonEvent from "./buttonEvents/304276391837302787/moons"; export default class Registry { public static RegisterCommands() { CoreClient.RegisterCommand("about", new About()); + CoreClient.RegisterCommand("audits", new Audits()); + CoreClient.RegisterCommand("autokick", new Autokick()); CoreClient.RegisterCommand("ban", new Ban()); + CoreClient.RegisterCommand("bunny", new Bunny()); CoreClient.RegisterCommand("clear", new Clear()); - CoreClient.RegisterCommand("help", new Help()); - CoreClient.RegisterCommand("kick", new Kick()); - CoreClient.RegisterCommand("mute", new Mute()); - CoreClient.RegisterCommand("poll", new Poll()); - CoreClient.RegisterCommand("role", new Role()); - CoreClient.RegisterCommand("rules", new Rules()); - CoreClient.RegisterCommand("unmute", new Unmute()); - CoreClient.RegisterCommand("warn", new Warn()); - CoreClient.RegisterCommand("setup", new Setup()); - CoreClient.RegisterCommand("config", new Config()); CoreClient.RegisterCommand("code", new Code()); + CoreClient.RegisterCommand("config", new Config()); CoreClient.RegisterCommand("disable", new Disable()); + CoreClient.RegisterCommand("ignore", new Ignore()); + CoreClient.RegisterCommand("kick", new Kick()); + CoreClient.RegisterCommand("linkonly", new Linkonly()); + CoreClient.RegisterCommand("poll", new Poll()); + CoreClient.RegisterCommand("rules", new Rules()); + CoreClient.RegisterCommand("say", new Say()); + CoreClient.RegisterCommand("setup", new Setup()); + CoreClient.RegisterCommand("timeout", new Timeout()); + CoreClient.RegisterCommand("warn", new Warn()); + + CoreClient.RegisterCommand("role", new Role()); + CoreClient.RegisterCommand("configrole", new ConfigRole()); // Exclusive Commands: MankBot CoreClient.RegisterCommand("lobby", new Lobby(), "501231711271780357"); + CoreClient.RegisterCommand("lobbyAdd", new AddLobby(), "501231711271780357"); + CoreClient.RegisterCommand("lobbyRemove", new RemoveLobby(), "501231711271780357"); + CoreClient.RegisterCommand("listlobby", new ListLobby(), "501231711271780357"); CoreClient.RegisterCommand("entry", new Entry(), "501231711271780357"); + // Exclusive Commands: Potato Talk + CoreClient.RegisterCommand("moons", new Moons(), "304276391837302787"); + // Add Exclusive Commands to Test Server CoreClient.RegisterCommand("lobby", new Lobby(), "442730357897429002"); + CoreClient.RegisterCommand("addlobby", new AddLobby(), "442730357897429002"); + CoreClient.RegisterCommand("removelobby", new RemoveLobby(), "442730357897429002"); + CoreClient.RegisterCommand("listlobby", new ListLobby(), "442730357897429002"); CoreClient.RegisterCommand("entry", new Entry(), "442730357897429002"); + CoreClient.RegisterCommand("moons", new Moons(), "442730357897429002"); } public static RegisterEvents() { - CoreClient.RegisterEvent(new MemberEvents()); - CoreClient.RegisterEvent(new MessageEvents()); + CoreClient.RegisterEvent(EventType.GuildMemberAdd, GuildMemberAdd); + CoreClient.RegisterEvent(EventType.GuildMemberRemove, GuildMemberRemove); + CoreClient.RegisterEvent(EventType.GuildMemberUpdate, GuildMemberUpdate); + + CoreClient.RegisterEvent(EventType.MessageDelete, MessageDelete); + CoreClient.RegisterEvent(EventType.MessageUpdate, MessageUpdate); + CoreClient.RegisterEvent(EventType.MessageCreate, MessageCreate); } -} \ No newline at end of file + + public static RegisterButtonEvents() { + CoreClient.RegisterButtonEvent("verify", new Verify()); + CoreClient.RegisterButtonEvent("moons", new MoonsButtonEvent()); + } +} diff --git a/src/timers/AutoKick.ts b/src/timers/AutoKick.ts new file mode 100644 index 0000000..fcaac56 --- /dev/null +++ b/src/timers/AutoKick.ts @@ -0,0 +1,114 @@ +import { EmbedBuilder } from "discord.js"; +import {CoreClient} from "../client/client"; +import AutoKickConfig from "../database/entities/AutoKickConfig"; +import EmbedColours from "../constants/EmbedColours"; + +export default async function AutoKick() { + const client = CoreClient.baseClient; + const autoKickConfigs = await AutoKickConfig.FetchAll(AutoKickConfig); + + for (let config of autoKickConfigs) { + const guild = client.guilds.cache.find(x => x.id == config.ServerId) || await client.guilds.fetch(config.ServerId); + + if (!guild) { + console.error("Guild not found"); + + continue; + } + + await guild.members.fetch(); + + const role = guild.roles.cache.find(x => x.id == config.RoleId); + + if (!role) { + console.error("Role not found in guild"); + + continue; + } + + for (let memberEntity of role.members) { + const member = memberEntity[1]; + + if (!member.kickable) { + console.error("Member not kickable"); + + continue; + } + + const whenToKick = new Date(member.joinedTimestamp! + config.KickTime); + const now = new Date(); + + if (whenToKick < now) { + await member.kick("Auto Kicked"); + + if (config.NoticeChannelId) { + const channel = guild.channels.cache.find(x => x.id == config.NoticeChannelId) || await guild.channels.fetch(config.NoticeChannelId); + + if (!channel?.isSendable()) { + console.log("Channel not sendable"); + + continue; + } + + const embed = new EmbedBuilder() + .setTitle("Auto Kicked User") + .setColor(EmbedColours.Danger) + .setThumbnail(member.user.avatarURL()) + .addFields([ + { + name: "User", + value: `<@${member.user.id}> \`${member.user.username}\``, + inline: true, + }, + ]); + + await channel.send({ + embeds: [ embed ], + }); + } + } else if (config.NoticeChannelId && config.NoticeTime) { + const whenToNotice = new Date(whenToKick.getTime() - config.NoticeTime); + whenToNotice.setMinutes(0, 0, 0); + whenToNotice.setHours(whenToNotice.getHours() + 1); + + const channel = guild.channels.cache.find(x => x.id == config.NoticeChannelId) || await guild.channels.fetch(config.NoticeChannelId); + + if (!channel?.isSendable()) { + console.error("Channel not sendable"); + + continue; + } + + if (now.getMonth() == whenToNotice.getMonth() + && now.getDate() == whenToNotice.getDate() + && now.getHours() == whenToNotice.getHours()) { + + const nextHour = new Date(whenToKick); + nextHour.setMinutes(0, 0, 0); + nextHour.setHours(whenToKick.getHours() + 1); + + const embed = new EmbedBuilder() + .setTitle("Auto Kick Notice") + .setColor(EmbedColours.Warning) + .setThumbnail(member.user.avatarURL()) + .addFields([ + { + name: "User", + value: `<@${member.user.id}> \`${member.user.username}\``, + inline: true, + }, + { + name: "When To Kick", + value: `<t:${Math.round(nextHour.getTime() / 1000)}:R>`, + inline: true, + }, + ]); + + await channel.send({ + embeds: [ embed ], + }); + } + } + } + } +} diff --git a/src/type/buttonEvent.ts b/src/type/buttonEvent.ts new file mode 100644 index 0000000..8a45bd0 --- /dev/null +++ b/src/type/buttonEvent.ts @@ -0,0 +1,5 @@ +import { ButtonInteraction } from "discord.js"; + +export abstract class ButtonEvent { + abstract execute(interaction: ButtonInteraction): Promise<void>; +} \ No newline at end of file diff --git a/src/type/command.ts b/src/type/command.ts index 1d04686..10d091d 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,23 +1,9 @@ -import { CommandResponse } from "../constants/CommandResponse"; -import { ICommandContext } from "../contracts/ICommandContext"; +import { CommandInteraction } from "discord.js"; export class Command { - public Roles: string[]; - public Category?: string; - - constructor() { - this.Roles = []; - } - - public precheck(context: ICommandContext): CommandResponse { - return CommandResponse.Ok; - } - - public async precheckAsync(context: ICommandContext): Promise<CommandResponse> { - return CommandResponse.Ok; - } - - public execute(context: ICommandContext) { + public CommandBuilder: any; + + public execute(interaction: CommandInteraction) { } } diff --git a/src/type/event.ts b/src/type/event.ts deleted file mode 100644 index 77771de..0000000 --- a/src/type/event.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Channel, Guild, GuildMember, Message, PartialDMChannel, PartialGuildMember, PartialMessage, GuildBan } from "discord.js"; - -export class Event { - public channelCreate(channel: Channel) { - - } - - public channelDelete(channel: Channel | PartialDMChannel) { - - } - - public channelUpdate(oldChannel: Channel, newChannel: Channel) { - - } - - public guildBanAdd(ban: GuildBan) { - - } - - public guildBanRemove(ban: GuildBan) { - - } - - public guildCreate(guild: Guild) { - - } - - public guildMemberAdd(member: GuildMember) { - - } - - public guildMemberRemove(member: GuildMember | PartialGuildMember) { - - } - - public guildMemberUpdate(oldMember: GuildMember | PartialGuildMember, newMember: GuildMember) { - - } - - public messageCreate(message: Message) { - - } - - public messageDelete(message: Message | PartialMessage) { - - } - - public messageUpdate(oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage) { - - } - - public ready() { - - } -} diff --git a/src/type/primitive.ts b/src/type/primitive.ts new file mode 100644 index 0000000..9093737 --- /dev/null +++ b/src/type/primitive.ts @@ -0,0 +1 @@ +export type primitive = string | number | boolean; diff --git a/src/vylbot.ts b/src/vylbot.ts index 50f1d87..c57ebd6 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -1,7 +1,7 @@ import { CoreClient } from "./client/client"; import * as dotenv from "dotenv"; import registry from "./registry"; -import { Intents } from "discord.js"; +import { IntentsBitField, Partials } from "discord.js"; dotenv.config(); @@ -9,8 +9,14 @@ const requiredConfigs: string[] = [ "BOT_TOKEN", "BOT_VER", "BOT_AUTHOR", - "BOT_DATE", "BOT_OWNERID", + "BOT_CLIENTID", + "DB_HOST", + "DB_PORT", + "DB_AUTH_USER", + "DB_AUTH_PASS", + "DB_SYNC", + "DB_LOGGING", ]; requiredConfigs.forEach(config => { @@ -19,15 +25,18 @@ requiredConfigs.forEach(config => { } }); -const devmode = process.argv.find(x => x.toLowerCase() == "--dev") != null; - const client = new CoreClient([ - Intents.FLAGS.GUILDS, - Intents.FLAGS.GUILD_MESSAGES, - Intents.FLAGS.GUILD_MEMBERS, -], devmode); + IntentsBitField.Flags.Guilds, + IntentsBitField.Flags.GuildMessages, + IntentsBitField.Flags.GuildMembers, + IntentsBitField.Flags.MessageContent, +], [ + Partials.GuildMember, + Partials.User, +]); registry.RegisterCommands(); registry.RegisterEvents(); +registry.RegisterButtonEvents(); -client.start(); \ No newline at end of file +client.start(); diff --git a/tests/_mocks/commands/mockCmd.ts b/tests/_mocks/commands/mockCmd.ts deleted file mode 100644 index 16f1947..0000000 --- a/tests/_mocks/commands/mockCmd.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Command } from "../../../src/type/command"; - -export default class MockCmd extends Command { - constructor() { - super(); - - super._category = "General"; - super._roles = ["Moderator"]; - } -} \ No newline at end of file diff --git a/tests/_mocks/rules/rules.json b/tests/_mocks/rules/rules.json deleted file mode 100644 index bcc28b9..0000000 --- a/tests/_mocks/rules/rules.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - { - "image": "IMAGEURL" - }, - { - "title": "TITLE 1", - "description": [ - "DESCRIPTION 1A", - "DESCRIPTION 1B" - ], - "footer": "FOOTER 1" - } -] \ No newline at end of file diff --git a/tests/buttonEvents/304276391837302787/moons.test.ts b/tests/buttonEvents/304276391837302787/moons.test.ts new file mode 100644 index 0000000..83e60d7 --- /dev/null +++ b/tests/buttonEvents/304276391837302787/moons.test.ts @@ -0,0 +1,21 @@ +import { ButtonInteraction } from "discord.js"; +import Moons from "../../../src/buttonEvents/304276391837302787/moons"; +import * as List from "../../../src/buttonEvents/304276391837302787/moons/list"; + +describe("GIVEN interaction action is list", () => { + const interaction = { + customId: "moons list", + } as unknown as ButtonInteraction; + + const listSpy = jest.spyOn(List, "default"); + + beforeAll(async () => { + const moons = new Moons(); + await moons.execute(interaction); + }); + + test("EXPECT List function to be called", () => { + expect(listSpy).toHaveBeenCalledTimes(1); + expect(listSpy).toHaveBeenCalledWith(interaction); + }); +}); \ No newline at end of file diff --git a/tests/buttonEvents/304276391837302787/moons/__snapshots__/list.test.ts.snap b/tests/buttonEvents/304276391837302787/moons/__snapshots__/list.test.ts.snap new file mode 100644 index 0000000..61f75d5 --- /dev/null +++ b/tests/buttonEvents/304276391837302787/moons/__snapshots__/list.test.ts.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GIVEN happy flow EXPECT embed to be updated 1`] = ` +[ + { + "color": 3166394, + "description": "**1 -** Test Descriptio", + "footer": { + "icon_url": undefined, + "text": "Page 1 of 1 ยท 0 moons", + }, + "title": "username's Moons", + }, +] +`; + +exports[`GIVEN happy flow EXPECT row to be updated 1`] = ` +[ + { + "components": [ + { + "custom_id": "moons list userId -1", + "disabled": true, + "emoji": undefined, + "label": "Previous", + "style": 1, + "type": 2, + }, + { + "custom_id": "moons list userId 1", + "disabled": true, + "emoji": undefined, + "label": "Next", + "style": 1, + "type": 2, + }, + ], + "type": 1, + }, +] +`; diff --git a/tests/buttonEvents/304276391837302787/moons/list.test.ts b/tests/buttonEvents/304276391837302787/moons/list.test.ts new file mode 100644 index 0000000..fcb80c6 --- /dev/null +++ b/tests/buttonEvents/304276391837302787/moons/list.test.ts @@ -0,0 +1,268 @@ +import {ActionRowBuilder, ButtonBuilder, ButtonInteraction, EmbedBuilder} from "discord.js"; +import List from "../../../../src/buttonEvents/304276391837302787/moons/list"; +import UserSetting from "../../../../src/database/entities/UserSetting"; +import Moon from "../../../../src/database/entities/304276391837302787/Moon"; + +describe("GIVEN happy flow", () => { + let updatedWithEmbeds: EmbedBuilder[] | undefined; + let updatedWithRows: ActionRowBuilder<ButtonBuilder>[] | undefined; + + const interaction = { + guild: { + members: { + cache: { + find: jest.fn().mockReturnValue({ + user: { + username: "username", + }, + }), + }, + }, + }, + reply: jest.fn(), + update: jest.fn((options: any) => { + updatedWithEmbeds = options.embeds; + updatedWithRows = options.components; + }), + customId: "moons list userId 0", + } as unknown as ButtonInteraction; + + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn(); + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue([ + [ + { + MoonNumber: 1, + Description: "Test Description", + } + ], + 1, + ]); + + await List(interaction); + }); + + test("EXPECT moons to be fetched", () => { + expect(Moon.FetchPaginatedMoonsByUserId).toHaveBeenCalledTimes(1); + expect(Moon.FetchPaginatedMoonsByUserId).toHaveBeenCalledWith("userId", 10, 0); + }); + + test("EXPECT interaction.update to be called", () => { + expect(interaction.update).toHaveBeenCalledTimes(1); + }); + + test("EXPECT embed to be updated", () => { + expect(updatedWithEmbeds).toMatchSnapshot(); + }); + + test("EXPECT row to be updated", () => { + expect(updatedWithRows).toMatchSnapshot(); + }); +}); + +describe("GIVEN interaction.guild is null", () => { + const interaction = { + guild: null, + reply: jest.fn(), + update: jest.fn(), + } as unknown as ButtonInteraction; + + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn(); + + await List(interaction); + }); + + test("EXPECT function to return", () => { + expect(interaction.reply).not.toHaveBeenCalled(); + expect(interaction.update).not.toHaveBeenCalled(); + expect(UserSetting.FetchOneByKey).not.toHaveBeenCalled(); + }); +}); + +describe("GIVEN userId parameter is undefined", () => { + const interaction = { + guild: {}, + reply: jest.fn(), + update: jest.fn(), + customId: "moons list", + } as unknown as ButtonInteraction; + + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn(); + + await List(interaction); + }); + + test("EXPECT function to return", () => { + expect(interaction.reply).not.toHaveBeenCalled(); + expect(interaction.update).not.toHaveBeenCalled(); + expect(UserSetting.FetchOneByKey).not.toHaveBeenCalled(); + }); +}); + +describe("GIVEN page parameter is undefined", () => { + const interaction = { + guild: {}, + reply: jest.fn(), + update: jest.fn(), + customId: "moons list userId", + } as unknown as ButtonInteraction; + + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn(); + + await List(interaction); + }); + + test("EXPECT function to return", () => { + expect(interaction.reply).not.toHaveBeenCalled(); + expect(interaction.update).not.toHaveBeenCalled(); + expect(UserSetting.FetchOneByKey).not.toHaveBeenCalled(); + }); +}); + +describe("GIVEN no moons for the user is returned", () => { + const interaction = { + guild: { + members: { + cache: { + find: jest.fn().mockReturnValue({ + user: { + username: "username", + }, + }), + }, + }, + }, + reply: jest.fn(), + update: jest.fn(), + customId: "moons list userId 0", + } as unknown as ButtonInteraction; + + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn(); + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue(undefined) + + await List(interaction); + }); + + test("EXPECT moons function to be called", () => { + expect(Moon.FetchPaginatedMoonsByUserId).toHaveBeenCalledTimes(1); + }); + + test("EXPECT error replied", () => { + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("username does not have any moons or page is invalid."); + }); + + describe("GIVEN member is not in cache", () => { + const interaction = { + guild: { + members: { + cache: { + find: jest.fn().mockReturnValue(undefined), + }, + fetch: jest.fn().mockResolvedValue({ + user: { + username: "username", + }, + }), + }, + }, + reply: jest.fn(), + update: jest.fn(), + customId: "moons list userId 0", + } as unknown as ButtonInteraction; + + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn(); + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue(undefined) + + await List(interaction); + }); + + test("EXPECT API to be called", () => { + expect(interaction.guild?.members.fetch).toHaveBeenCalledTimes(1); + expect(interaction.guild?.members.fetch).toHaveBeenCalledWith("userId"); + }); + + test("EXPECT error replied with username", () => { + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("username does not have any moons or page is invalid."); + }); + }); + + describe("GIVEN member can not be found", () => { + const interaction = { + guild: { + members: { + cache: { + find: jest.fn().mockReturnValue(undefined), + }, + fetch: jest.fn().mockResolvedValue(undefined), + }, + }, + reply: jest.fn(), + update: jest.fn(), + customId: "moons list userId 0", + } as unknown as ButtonInteraction; + + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn(); + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue(undefined) + + await List(interaction); + }); + + test("EXPECT API to be called", () => { + expect(interaction.guild?.members.fetch).toHaveBeenCalledTimes(1); + expect(interaction.guild?.members.fetch).toHaveBeenCalledWith("userId"); + }); + + test("EXPECT error replied with username", () => { + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("This user does not have any moons or page is invalid."); + }); + }); +}); + +describe("GIVEN no moons on current page", () => { + let updatedWith: EmbedBuilder[] | undefined; + + const interaction = { + guild: { + members: { + cache: { + find: jest.fn().mockReturnValue({ + user: { + username: "username", + }, + }), + }, + }, + }, + reply: jest.fn(), + update: jest.fn((options: any) => { + updatedWith = options.embeds; + }), + customId: "moons list userId 0", + } as unknown as ButtonInteraction; + + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn(); + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue([ + [], + 0, + ]); + + await List(interaction); + }); + + test("EXPECT description to say so", () => { + expect(updatedWith).toBeDefined(); + expect(updatedWith?.length).toBe(1); + + expect(updatedWith![0].data.description).toBe("*none*"); + }); +}); \ No newline at end of file diff --git a/tests/client/client.test.ts b/tests/client/client.test.ts deleted file mode 100644 index 7f0086f..0000000 --- a/tests/client/client.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { mock } from "jest-mock-extended"; - -const connectionMock = mock<Connection>(); -const qbuilderMock = mock<SelectQueryBuilder<any>>(); - -let repositoryMock = mock<Repository<any>>(); -let settingMock = mock<Setting>(); - -jest.mock('typeorm', () => { - qbuilderMock.where.mockReturnThis(); - qbuilderMock.select.mockReturnThis(); - repositoryMock.createQueryBuilder.mockReturnValue(qbuilderMock); - repositoryMock.findOne.mockImplementation(async () => { - return settingMock; - }); - connectionMock.getRepository.mockReturnValue(repositoryMock); - - return { - getConnection: () => connectionMock, - createConnection: () => connectionMock, - - BaseEntity: class Mock {}, - ObjectType: () => {}, - Entity: () => {}, - InputType: () => {}, - Index: () => {}, - PrimaryColumn: () => {}, - Column: () => {}, - CreateDateColumn: () => {}, - UpdateDateColumn: () => {}, - OneToMany: () => {}, - ManyToOne: () => {}, - } -}); - -jest.mock("discord.js"); -jest.mock("dotenv"); -jest.mock("../../src/client/events"); -jest.mock("../../src/client/util"); -jest.mock("../../src/constants/DefaultValues"); - -import { CoreClient } from "../../src/client/client"; - -import { Client } from "discord.js"; -import * as dotenv from "dotenv"; -import { Events } from "../../src/client/events"; -import { Util } from "../../src/client/util"; -import { Command } from "../../src/type/command"; -import { Event } from "../../src/type/event"; -import DefaultValues from "../../src/constants/DefaultValues"; -import { Connection, Repository, SelectQueryBuilder } from "typeorm"; -import Setting from "../../src/entity/Setting"; - -beforeEach(() => { - jest.resetAllMocks(); - jest.resetModules(); -}) - -describe('Constructor', () => { - test('Expect Successful Initialisation', () => { - const coreClient = new CoreClient(); - - expect(coreClient).toBeInstanceOf(Client); - expect(dotenv.config).toBeCalledTimes(1); - expect(Events).toBeCalledTimes(1); - expect(Util).toBeCalledTimes(1); - expect(DefaultValues.useDevPrefix).toBe(false); - }); - - test('Given devmode parameter is true, Expect devmode prefix to be true', () => { - const coreClient = new CoreClient(true); - - expect(coreClient).toBeInstanceOf(Client); - expect(dotenv.config).toBeCalledTimes(1); - expect(Events).toBeCalledTimes(1); - expect(Util).toBeCalledTimes(1); - expect(DefaultValues.useDevPrefix).toBe(true); - }); -}); - -describe('Start', () => { - test('Given Env Is Valid, Expect Successful Start', async () => { - process.env = { - BOT_TOKEN: "TOKEN", - }; - - const coreClient = new CoreClient(); - - await coreClient.start(); - - expect(coreClient.on).toBeCalledWith("message", expect.any(Function)); - expect(coreClient.on).toBeCalledWith("ready", expect.any(Function)); - }); - - test('Given BOT_TOKEN Is Null, Expect Failure', async () => { - process.env = {}; - - const consoleError = jest.fn(); - - console.error = consoleError; - - const coreClient = new CoreClient(); - - await coreClient.start(); - - expect(consoleError).toBeCalledWith("BOT_TOKEN is not defined in .env"); - expect(coreClient.on).not.toBeCalled(); - expect(coreClient.login).not.toBeCalled(); - }); - - test('Given BOT_TOKEN Is Empty, Expect Failure', async () => { - process.env = { - BOT_TOKEN: '', - } - - const consoleError = jest.fn(); - - console.error = consoleError; - - const coreClient = new CoreClient(); - - await coreClient.start(); - - expect(consoleError).toBeCalledWith("BOT_TOKEN is not defined in .env"); - expect(coreClient.on).not.toBeCalled(); - expect(coreClient.login).not.toBeCalled(); - }); -}); - -describe('RegisterCommand', () => { - test('Expect command added to register', () => { - const cmd = mock<Command>(); - - const client = new CoreClient(); - client.RegisterCommand("test", cmd); - - expect(client.commandItems.length).toBe(1); - expect(client.commandItems[0].Name).toBe("test"); - expect(client.commandItems[0].Command).toBe(cmd); - }); -}); - -describe('RegisterEvent', () => { - test('Expect event added to register', () => { - const evt = mock<Event>(); - - const client = new CoreClient(); - client.RegisterEvent(evt); - - expect(client.eventItems.length).toBe(1); - expect(client.eventItems[0].Event).toBe(evt); - }); -}); \ No newline at end of file diff --git a/tests/client/events.test.ts b/tests/client/events.test.ts deleted file mode 100644 index 50eb557..0000000 --- a/tests/client/events.test.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { Events } from "../../src/client/events"; -import { Message } from "discord.js"; -import { Util } from "../../src/client/util"; -import ICommandItem from "../../src/contracts/ICommandItem"; -import { Command } from "../../src/type/command"; -import { mock } from "jest-mock-extended"; - -jest.mock("../../src/client/util"); - -beforeEach(() => { - Util.prototype.loadCommand = jest.fn(); -}); - -describe('OnMessage', () => { - test('Given Message Is Valid Expect Message Sent', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - }; - - Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true }); - - const message = { - guild: {}, - author: { - bot: false, - }, - content: "!test first", - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const events = new Events(); - - const result = await events.onMessage(message, commands); - - expect(result.valid).toBeTruthy(); - - expect(result.context?.prefix).toBe('!'); - expect(result.context?.name).toBe('test'); - expect(result.context?.args.length).toBe(1); - expect(result.context?.args[0]).toBe('first'); - expect(result.context?.message).toBe(message); - }); - - test('Given Guild Is Null, Expect Failed Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true }); - - const message = { - guild: null, - author: { - bot: false, - }, - content: "!test first", - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const events = new Events(); - - const result = await events.onMessage(message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("Message was not sent in a guild, ignoring."); - }); - - test('Given Author Is A Bot, Expect Failed Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true }); - - const message = { - guild: {}, - author: { - bot: true, - }, - content: "!test first", - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const events = new Events(); - - const result = await events.onMessage(message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("Message was sent by a bot, ignoring."); - }); - - test('Given Message Content Was Not A Command, Expect Failed Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true }); - - const message = { - guild: {}, - author: { - bot: false, - }, - content: "This is a standard message", - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const events = new Events(); - - const result = await events.onMessage(message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("Message was not a command, ignoring."); - }); - - test('Given Message Had No Command Name, Expect Failed Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true }); - - const message = { - guild: {}, - author: { - bot: false, - }, - content: "!", - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const events = new Events(); - - const result = await events.onMessage(message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("Command name was not found"); - }); - - test('Given Command Failed To Execute, Expect Failed Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: false, message: "Command failed" }); - - const message = { - guild: {}, - author: { - bot: false, - }, - content: "!test first", - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const events = new Events(); - - const result = await events.onMessage(message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("Command failed"); - }); -}); - -describe('OnReady', () => { - test('Expect Console Log', () => { - console.log = jest.fn(); - - const events = new Events(); - - events.onReady(); - - expect(console.log).toBeCalledWith("Ready"); - }); -}); diff --git a/tests/client/util.test.ts b/tests/client/util.test.ts deleted file mode 100644 index fe59118..0000000 --- a/tests/client/util.test.ts +++ /dev/null @@ -1,370 +0,0 @@ -import { Util } from "../../src/client/util"; - -import { Client, Message } from "discord.js"; -import fs from "fs"; -import { mock } from "jest-mock-extended"; -import { Command } from "../../src/type/command"; -import ICommandItem from "../../src/contracts/ICommandItem"; -import IEventItem from "../../src/contracts/IEventItem"; -import { Event } from "../../src/type/event"; - -jest.mock("fs"); - -beforeEach(() => { - fs.existsSync = jest.fn(); -}); - -describe('LoadCommand', () => { - test('Given Successful Exection, Expect Successful Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const message = { - member: { - roles: { - cache: { - find: jest.fn().mockReturnValue(true), - } - }, - }, - reply: jest.fn(), - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const util = new Util(); - - const result = await util.loadCommand("test", [ "first" ], message, commands); - - expect(result.valid).toBeTruthy(); - expect(cmd.execute).toBeCalled(); - }); - - test('Given Member Is Null, Expect Failed Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const message = { - member: null - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const util = new Util(); - - const result = await util.loadCommand("test", [ "first" ], message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("Member is not part of message"); - expect(cmd.execute).not.toBeCalled(); - }); - - test('Given User Does Have Role, Expect Successful Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const message = { - member: { - roles: { - cache: { - find: jest.fn().mockReturnValue(true), - } - }, - }, - reply: jest.fn(), - } as unknown as Message; - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const util = new Util(); - - const result = await util.loadCommand("test", [ "first" ], message, commands); - - expect(result.valid).toBeTruthy(); - expect(cmd.execute).toBeCalled(); - }); - - test('Given User Does Not Have Role, Expect Failed Result', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const message = { - member: { - roles: { - cache: { - find: jest.fn().mockReturnValue(false), - } - }, - }, - reply: jest.fn(), - } as unknown as Message; - - const cmd = mock<Command>(); - cmd._roles = [ "Moderator" ]; - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const util = new Util(); - - const result = await util.loadCommand("test", [ "first" ], message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("You require the `Moderator` role to run this command"); - expect(cmd.execute).not.toBeCalled(); - }); - - test('Given command is set to disabled, Expect command to not fire', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - COMMANDS_DISABLED: 'test', - COMMANDS_DISABLED_MESSAGE: 'disabled', - } - - const message = { - member: { - roles: { - cache: { - find: jest.fn().mockReturnValue(true), - } - }, - }, - reply: jest.fn(), - } as unknown as Message; - - const messageReply = jest.spyOn(message, 'reply'); - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const util = new Util(); - - const result = await util.loadCommand("test", [ "first" ], message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("Command is disabled"); - expect(messageReply).toBeCalledWith("disabled"); - expect(cmd.execute).not.toBeCalled(); - }); - - test('Given command COMMANDS_DISABLED_MESSAGE is empty, Expect default message sent', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - COMMANDS_DISABLED: 'test', - } - - const message = { - member: { - roles: { - cache: { - find: jest.fn().mockReturnValue(true), - } - }, - }, - reply: jest.fn(), - } as unknown as Message; - - const messageReply = jest.spyOn(message, 'reply'); - - const cmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const commands: ICommandItem[] = [ commandItem ]; - - const util = new Util(); - - const result = await util.loadCommand("test", [ "first" ], message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe("Command is disabled"); - expect(messageReply).toBeCalledWith("This command is disabled."); - expect(cmd.execute).not.toBeCalled(); - }); - - test('Given a different command is disabled, Expect command to still fire', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - COMMANDS_DISABLED: 'other', - } - - const message = { - member: { - roles: { - cache: { - find: jest.fn().mockReturnValue(true), - } - }, - }, - reply: jest.fn(), - } as unknown as Message; - - const cmd = mock<Command>(); - const otherCmd = mock<Command>(); - - const commandItem: ICommandItem = { - Name: "test", - Command: cmd - }; - - const otherCommandItem: ICommandItem = { - Name: "other", - Command: otherCmd, - } - - const commands: ICommandItem[] = [ commandItem, otherCommandItem ]; - - const util = new Util(); - - const result = await util.loadCommand("test", [ "first" ], message, commands); - - expect(result.valid).toBeTruthy(); - expect(cmd.execute).toBeCalled(); - expect(otherCmd.execute).not.toBeCalled(); - }); - - test('Given command is not found in register, expect command not found error', async () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const message = { - member: { - roles: { - cache: { - find: jest.fn().mockReturnValue(true), - } - }, - }, - reply: jest.fn(), - } as unknown as Message; - - const commands: ICommandItem[] = []; - - const util = new Util(); - - const result = await util.loadCommand("test", [ "first" ], message, commands); - - expect(result.valid).toBeFalsy(); - expect(result.message).toBe('Command not found'); - expect(message.reply).toBeCalledWith('Command not found'); - }); -}); - -describe('LoadEvents', () => { - test('Given Events Are Loaded, Expect Successful Result', () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const client = { - on: jest.fn(), - } as unknown as Client; - - const evt = mock<Event>(); - - const eventItem: IEventItem = { - Event: evt - }; - - const eventItems: IEventItem[] = [ eventItem ]; - - const util = new Util(); - - const result = util.loadEvents(client, eventItems); - - const clientOn = jest.spyOn(client, 'on'); - - expect(result.valid).toBeTruthy(); - expect(clientOn).toBeCalledTimes(13); - }); - - test('Given No Events Found, Expect Successful Result', () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const client = { - on: jest.fn(), - } as unknown as Client; - - const eventItems: IEventItem[] = []; - - const util = new Util(); - - const result = util.loadEvents(client, eventItems); - - const clientOn = jest.spyOn(client, 'on'); - - expect(result.valid).toBeTruthy(); - expect(clientOn).toBeCalledTimes(0); - }); -}); diff --git a/tests/commands/304276391837302787/__snapshots__/moons.test.ts.snap b/tests/commands/304276391837302787/__snapshots__/moons.test.ts.snap new file mode 100644 index 0000000..ec8a85e --- /dev/null +++ b/tests/commands/304276391837302787/__snapshots__/moons.test.ts.snap @@ -0,0 +1,69 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`constructor EXPECT CommandBuilder to be defined correctly 1`] = ` +{ + "contexts": undefined, + "default_member_permissions": undefined, + "default_permission": undefined, + "description": "View and create moons", + "description_localizations": undefined, + "dm_permission": undefined, + "integration_types": undefined, + "name": "moons", + "name_localizations": undefined, + "nsfw": undefined, + "options": [ + { + "description": "List moons you have obtained", + "description_localizations": undefined, + "name": "list", + "name_localizations": undefined, + "options": [ + { + "description": "The user to view (Defaults to yourself)", + "description_localizations": undefined, + "name": "user", + "name_localizations": undefined, + "required": false, + "type": 6, + }, + { + "autocomplete": undefined, + "choices": undefined, + "description": "The page to start with", + "description_localizations": undefined, + "max_value": undefined, + "min_value": undefined, + "name": "page", + "name_localizations": undefined, + "required": false, + "type": 10, + }, + ], + "type": 1, + }, + { + "description": "Add a moon to your count!", + "description_localizations": undefined, + "name": "add", + "name_localizations": undefined, + "options": [ + { + "autocomplete": undefined, + "choices": undefined, + "description": "What deserved a moon?", + "description_localizations": undefined, + "max_length": undefined, + "min_length": undefined, + "name": "description", + "name_localizations": undefined, + "required": true, + "type": 3, + }, + ], + "type": 1, + }, + ], + "type": 1, +} +`; diff --git a/tests/commands/304276391837302787/moons.test.ts b/tests/commands/304276391837302787/moons.test.ts new file mode 100644 index 0000000..e122941 --- /dev/null +++ b/tests/commands/304276391837302787/moons.test.ts @@ -0,0 +1,113 @@ +import { ChatInputCommandInteraction, CommandInteraction } from "discord.js"; +import Moons from "../../../src/commands/304276391837302787/moons"; +import * as AddMoon from "../../../src/commands/304276391837302787/moons/add"; +import * as ListMoons from "../../../src/commands/304276391837302787/moons/list"; + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe("constructor", () => { + let moons: Moons; + + beforeEach(() => { + moons = new Moons(); + }); + + test("EXPECT CommandBuilder to be defined correctly", () => { + expect(moons.CommandBuilder).toMatchSnapshot(); + }); +}); + +describe("execute", () => { + describe("GIVEN interaction is not a chat input command", () => { + const moons = new Moons(); + + let interaction: CommandInteraction; + + let listMoonsSpy: jest.SpyInstance; + let addMoonSpy: jest.SpyInstance; + + beforeEach(async () => { + listMoonsSpy = jest.spyOn(ListMoons, "default"); + addMoonSpy = jest.spyOn(AddMoon, "default"); + + interaction = { + isChatInputCommand: jest.fn().mockReturnValue(false), + } as unknown as CommandInteraction; + + await moons.execute(interaction); + }); + + test("EXPECT interaction.isChatInputCommand to have been called", () => { + expect(interaction.isChatInputCommand).toHaveBeenCalledTimes(1); + }); + + test("EXPECT nothing to happen", () => { + expect(listMoonsSpy).not.toHaveBeenCalled(); + expect(addMoonSpy).not.toHaveBeenCalled(); + }); + }); + + describe("GIVEN interaction subcommand is list", () => { + const moons = new Moons(); + + let interaction: ChatInputCommandInteraction; + + let listMoonsSpy: jest.SpyInstance; + + beforeEach(async () => { + listMoonsSpy = jest.spyOn(ListMoons, "default") + .mockImplementation(); + + interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + options: { + getSubcommand: jest.fn().mockReturnValue("list"), + }, + } as unknown as ChatInputCommandInteraction; + + await moons.execute(interaction); + }); + + test("EXPECT interaction.options.getSubcommand to have been called", () => { + expect(interaction.options.getSubcommand).toHaveBeenCalledTimes(1); + }); + + test("EXPECT ListMoons to be called", () => { + expect(listMoonsSpy).toHaveBeenCalledTimes(1); + expect(listMoonsSpy).toHaveBeenCalledWith(interaction); + }); + }); + + describe("GIVEN interaction subcommand is add", () => { + const moons = new Moons(); + + let interaction: ChatInputCommandInteraction; + + let addMoonSpy: jest.SpyInstance; + + beforeEach(async () => { + addMoonSpy = jest.spyOn(AddMoon, "default") + .mockImplementation(); + + interaction = { + isChatInputCommand: jest.fn().mockReturnValue(true), + options: { + getSubcommand: jest.fn().mockReturnValue("add"), + }, + } as unknown as ChatInputCommandInteraction; + + await moons.execute(interaction); + }); + + test("EXPECT interaction.options.getSubcommand to have been called", () => { + expect(interaction.options.getSubcommand).toHaveBeenCalledTimes(1); + }); + + test("EXPECT AddMoon to be called", () => { + expect(addMoonSpy).toHaveBeenCalledTimes(1); + expect(addMoonSpy).toHaveBeenCalledWith(interaction); + }); + }); +}); \ No newline at end of file diff --git a/tests/commands/304276391837302787/moons/__snapshots__/add.test.ts.snap b/tests/commands/304276391837302787/moons/__snapshots__/add.test.ts.snap new file mode 100644 index 0000000..ace8150 --- /dev/null +++ b/tests/commands/304276391837302787/moons/__snapshots__/add.test.ts.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GIVEN happy flow EXPECT embed to be replied 1`] = ` +[ + { + "color": 5294200, + "description": "**2 -** Test Description", + "thumbnail": { + "url": "https://cdn.discordapp.com/emojis/374131312182689793.webp?size=96&quality=lossless", + }, + "title": "undefined Got A Moon!", + }, +] +`; + +exports[`GIVEN happy flow EXPECT moon to be saved 1`] = ` +{ + "Description": "Test Description", + "Id": Any<String>, + "MoonNumber": 2, + "UserId": "userId", + "WhenCreated": Any<Date>, + "WhenUpdated": Any<Date>, +} +`; diff --git a/tests/commands/304276391837302787/moons/__snapshots__/list.test.ts.snap b/tests/commands/304276391837302787/moons/__snapshots__/list.test.ts.snap new file mode 100644 index 0000000..105e226 --- /dev/null +++ b/tests/commands/304276391837302787/moons/__snapshots__/list.test.ts.snap @@ -0,0 +1,40 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GIVEN happy flow EXPECT interaction to be replied 1`] = ` +{ + "components": [ + { + "components": [ + { + "custom_id": "moons list userId -1", + "disabled": true, + "emoji": undefined, + "label": "Previous", + "style": 1, + "type": 2, + }, + { + "custom_id": "moons list userId 01", + "disabled": true, + "emoji": undefined, + "label": "Next", + "style": 1, + "type": 2, + }, + ], + "type": 1, + }, + ], + "embeds": [ + { + "color": 3166394, + "description": "**1 -** Test Descriptio", + "footer": { + "icon_url": undefined, + "text": "Page 01 of 1 ยท 0 moons", + }, + "title": "undefined's Moons", + }, + ], +} +`; diff --git a/tests/commands/304276391837302787/moons/add.test.ts b/tests/commands/304276391837302787/moons/add.test.ts new file mode 100644 index 0000000..a66eab3 --- /dev/null +++ b/tests/commands/304276391837302787/moons/add.test.ts @@ -0,0 +1,194 @@ +import { CommandInteraction, EmbedBuilder } from "discord.js"; +import AddMoon from "../../../../src/commands/304276391837302787/moons/add"; +import Moon from "../../../../src/database/entities/304276391837302787/Moon"; +import UserSetting from "../../../../src/database/entities/UserSetting"; + +describe("GIVEN happy flow", () => { + let repliedWithEmbed: EmbedBuilder[] | undefined; + let savedMoon: Moon | undefined; + + const interaction = { + reply: jest.fn((options: any) => { + repliedWithEmbed = options.embeds; + }), + options: { + get: jest.fn() + .mockReturnValueOnce({ + value: "Test Description", + }), + }, + user: { + id: "userId", + }, + } as unknown as CommandInteraction; + + const userSetting = { + Value: 1, + UpdateValue: jest.fn(), + Save: jest.fn(), + }; + + beforeAll(async () => { + Moon.FetchMoonCountByUserId = jest.fn().mockResolvedValue(1); + Moon.prototype.Save = jest.fn().mockImplementation((_, entity: Moon) => { + savedMoon = entity; + }); + + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue(userSetting); + + await AddMoon(interaction); + }); + + test("EXPECT description option to have been fetched", () => { + expect(interaction.options.get).toHaveBeenCalledTimes(1); + expect(interaction.options.get).toHaveBeenCalledWith("description", true); + }); + + test("EXPECT UserSetting to have been fetched", () => { + expect(UserSetting.FetchOneByKey).toHaveBeenCalledTimes(1); + expect(UserSetting.FetchOneByKey).toHaveBeenCalledWith("userId", "moons"); + }); + + test("EXPECT moonCount to be updated +1", () => { + expect(userSetting.UpdateValue).toHaveBeenCalledTimes(1); + expect(userSetting.UpdateValue).toHaveBeenCalledWith("2"); + }); + + test("EXPECT setting to be saved", () => { + expect(userSetting.Save).toHaveBeenCalledTimes(1); + expect(userSetting.Save).toHaveBeenCalledWith(UserSetting, userSetting); + }); + + test("EXPECT moon to be saved", () => { + expect(Moon.prototype.Save).toHaveBeenCalledTimes(1); + expect(Moon.prototype.Save).toHaveBeenCalledWith(Moon, expect.any(Moon)); + + expect(savedMoon).toBeDefined(); + expect(savedMoon).toMatchSnapshot({ + Id: expect.any(String), + WhenCreated: expect.any(Date), + WhenUpdated: expect.any(Date), + }); + }); + + test("EXPECT embed to be replied", () => { + expect(interaction.reply).toHaveBeenCalledTimes(1); + + expect(repliedWithEmbed).toBeDefined(); + expect(repliedWithEmbed).toMatchSnapshot(); + }); +}); + +describe("GIVEN description is null", () => { + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn().mockReturnValue({ + value: null, + }), + }, + } as unknown as CommandInteraction; + + beforeEach(async () => { + await AddMoon(interaction); + }); + + test("EXPECT error replied", () => { + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Name must be less than 255 characters!"); + }); +}); + +describe("GIVEN description is greater than 255 characters", () => { + const optionGet = jest.fn(); + + const interaction = { + reply: jest.fn(), + options: { + get: optionGet, + }, + } as unknown as CommandInteraction; + + beforeEach(async () => { + let longString = ""; + + for (let i = 0; i < 30; i++) { + longString += "1234567890"; + } + + optionGet.mockReturnValue({ + value: longString, + }); + + await AddMoon(interaction); + }); + + test("EXPECT error replied", () => { + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(interaction.reply).toHaveBeenCalledWith("Name must be less than 255 characters!"); + }); +}); + +describe("GIVEN moon count setting exists", () => { + const moonSetting = { + Value: "1", + UpdateValue: jest.fn(), + Save: jest.fn(), + }; + + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn().mockReturnValue({ + value: "Test Description", + }), + }, + user: { + id: "userId", + }, + } as unknown as CommandInteraction; + + beforeEach(async () => { + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue(moonSetting); + + await AddMoon(interaction); + }); + + test("EXPECT existing entity to be updated", () => { + expect(moonSetting.UpdateValue).toHaveBeenCalledTimes(1); + expect(moonSetting.UpdateValue).toHaveBeenCalledWith("2"); + }); +}); + +describe("GIVEN moon count setting does not exist", () => { + let savedSetting: UserSetting | undefined; + + const interaction = { + reply: jest.fn(), + options: { + get: jest.fn().mockReturnValue({ + value: "Test Description", + }), + }, + user: { + id: "userId", + }, + } as unknown as CommandInteraction; + + beforeEach(async () => { + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue(null); + UserSetting.prototype.Save = jest.fn().mockImplementation((_, setting: UserSetting) => { + savedSetting = setting; + }); + + await AddMoon(interaction); + }); + + test("EXPECT new entity to be created", () => { + // Expect the entity to have the new entity. + // Probably the best way we can really imply a new entity + // that I can think of + expect(savedSetting).toBeDefined(); + expect(savedSetting?.Value).toBe("1"); + }); +}); \ No newline at end of file diff --git a/tests/commands/304276391837302787/moons/list.test.ts b/tests/commands/304276391837302787/moons/list.test.ts new file mode 100644 index 0000000..2d0b6b8 --- /dev/null +++ b/tests/commands/304276391837302787/moons/list.test.ts @@ -0,0 +1,249 @@ +import { ActionRowBuilder, APIEmbed, ButtonBuilder, CommandInteraction, EmbedBuilder, InteractionReplyOptions } from "discord.js"; +import List from "../../../../src/commands/304276391837302787/moons/list"; +import Moon from "../../../../src/database/entities/304276391837302787/Moon"; +import UserSetting from "../../../../src/database/entities/UserSetting"; + +describe("GIVEN happy flow", () => { + let repliedWith: InteractionReplyOptions | undefined; + + const interaction = { + reply: jest.fn((options) => { + repliedWith = options; + }), + options: { + get: jest.fn() + .mockReturnValueOnce(undefined) // User + .mockReturnValue({ + value: "0", + }), // Page + }, + user: { + id: "userId", + } + } as unknown as CommandInteraction; + + beforeAll(async () => { + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue([ + [ + { + MoonNumber: 1, + Description: "Test Description", + } + ], + 1, + ]); + + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue({ + Value: "0", + }); + + await List(interaction); + }); + + test("EXPECT interaction to be replied", () => { + expect(interaction.reply).toHaveBeenCalledTimes(1); + expect(repliedWith).toMatchSnapshot(); + }); +}); + +describe("GIVEN moons returned is empty", () => { + let repliedWith: InteractionReplyOptions | undefined; + + const interaction = { + reply: jest.fn((options) => { + repliedWith = options; + }), + options: { + get: jest.fn() + .mockReturnValueOnce(undefined) // User + .mockReturnValue({ + value: "0", + }), // Page + }, + user: { + id: "userId", + } + } as unknown as CommandInteraction; + + beforeAll(async () => { + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue([ + [], + 0, + ]); + + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue({ + Value: "0", + }); + + await List(interaction); + }); + + test("EXPECT none description", () => { + expect(repliedWith).toBeDefined(); + + expect(repliedWith!.embeds).toBeDefined(); + expect(repliedWith!.embeds!.length).toBe(1); + + const repliedWithEmbed = repliedWith!.embeds![0] as EmbedBuilder; + + expect(repliedWithEmbed.data.description).toBe("*none*"); + }); +}); + +describe("GIVEN it is the first page", () => { + let repliedWith: InteractionReplyOptions | undefined; + + const interaction = { + reply: jest.fn((options) => { + repliedWith = options; + }), + options: { + get: jest.fn() + .mockReturnValueOnce(undefined) // User + .mockReturnValue({ + value: "0", + }), // Page + }, + user: { + id: "userId", + } + } as unknown as CommandInteraction; + + beforeAll(async () => { + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue([ + [ + { + MoonNumber: 1, + Description: "Test Description", + } + ], + 1, + ]); + + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue({ + Value: "0", + }); + + await List(interaction); + }); + + test("EXPECT Previous button to be disabled", () => { + expect(repliedWith).toBeDefined(); + + expect(repliedWith!.components).toBeDefined(); + expect(repliedWith!.components!.length).toBe(1); + + const repliedWithRow = repliedWith!.components![0] as ActionRowBuilder<ButtonBuilder>; + + expect(repliedWithRow.components[0].data.disabled).toBe(true); + }); +}); + +describe("GIVEN it is the last page", () => { + let repliedWith: InteractionReplyOptions | undefined; + + const interaction = { + reply: jest.fn((options) => { + repliedWith = options; + }), + options: { + get: jest.fn() + .mockReturnValueOnce(undefined) // User + .mockReturnValue({ + value: "0", + }), // Page + }, + user: { + id: "userId", + } + } as unknown as CommandInteraction; + + beforeAll(async () => { + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue([ + [ + { + MoonNumber: 1, + Description: "Test Description", + } + ], + 1, + ]); + + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue({ + Value: "0", + }); + + await List(interaction); + }); + + test("EXPECT Next button to be disabled", () => { + expect(repliedWith).toBeDefined(); + + expect(repliedWith!.components).toBeDefined(); + expect(repliedWith!.components!.length).toBe(1); + + const repliedWithRow = repliedWith!.components![0] as ActionRowBuilder<ButtonBuilder>; + + expect(repliedWithRow.components[1].data.disabled).toBe(true); + }); + + describe("GIVEN moon count is greater than the amount of moons in the database", () => { + beforeAll(async () => { + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue({ + Value: "2", + }); + + await List(interaction); + }); + + test("EXPECT untracked counter to be shown", () => { + const repliedWithEmbed = repliedWith!.embeds![0] as EmbedBuilder; + + expect(repliedWithEmbed.data.description).toContain("...plus 1 more untracked"); + }); + }); +}); + +describe("GIVEN moon count is empty", () => { + let repliedWith: InteractionReplyOptions | undefined; + + const interaction = { + reply: jest.fn((options) => { + repliedWith = options; + }), + options: { + get: jest.fn() + .mockReturnValueOnce(undefined) // User + .mockReturnValue({ + value: "0", + }), // Page + }, + user: { + id: "userId", + } + } as unknown as CommandInteraction; + + beforeAll(async () => { + Moon.FetchPaginatedMoonsByUserId = jest.fn().mockResolvedValue([ + [], + 0, + ]); + + UserSetting.FetchOneByKey = jest.fn().mockResolvedValue({ + Value: "0", + }); + + await List(interaction); + }); + + test("EXPECT Next button to be disabled", () => { + expect(repliedWith).toBeDefined(); + + expect(repliedWith!.components).toBeDefined(); + expect(repliedWith!.components!.length).toBe(1); + + const repliedWithRow = repliedWith!.components![0] as ActionRowBuilder<ButtonBuilder>; + + expect(repliedWithRow.components[1].data.disabled).toBe(true); + }); +}); diff --git a/tests/commands/about.test.ts b/tests/commands/about.test.ts deleted file mode 100644 index 6699cc2..0000000 --- a/tests/commands/about.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { Message } from "discord.js"; -import { mock } from "jest-mock-extended"; -import About from "../../src/commands/about"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; -import PublicEmbed from "../../src/helpers/embeds/PublicEmbed"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect values set', () => { - const about = new About(); - - expect(about._category).toBe("General"); - }); -}); - -describe('Execute', () => { - test('Expect embed to be made and sent to the current channel', async () => { - process.env = { - BOT_VER: "BOT_VER", - BOT_AUTHOR: "BOT_AUTHOR", - BOT_DATE: "BOT_DATE" - }; - - const message = mock<Message>(); - message.channel.send = jest.fn(); - - const context: ICommandContext = { - name: "about", - args: [], - message: message - }; - - const about = new About(); - - const result = await about.execute(context); - - expect(message.channel.send).toBeCalledTimes(1); - }); - - test('Expect embed send to have values', async () => { - process.env = { - BOT_VER: "BOT_VER", - BOT_AUTHOR: "BOT_AUTHOR", - BOT_DATE: "BOT_DATE" - }; - - const message = mock<Message>(); - message.channel.send = jest.fn(); - - const context: ICommandContext = { - name: "about", - args: [], - message: message - }; - - const about = new About(); - - const result = await about.execute(context); - - expect(result.embeds.length).toBe(1); - - const embed = result.embeds[0]; - - expect(embed.title).toBe('About'); - expect(embed.description).toBe(''); - expect(embed.fields.length).toBe(3); - }); - - test('Expect version field to have values', async () => { - process.env = { - BOT_VER: "BOT_VER", - BOT_AUTHOR: "BOT_AUTHOR", - BOT_DATE: "BOT_DATE" - }; - - const message = mock<Message>(); - message.channel.send = jest.fn(); - - const context: ICommandContext = { - name: "about", - args: [], - message: message - }; - - const about = new About(); - - const result = await about.execute(context); - - const embed = result.embeds[0]; - const field = embed.fields[0]; - - expect(field.name).toBe('Version'); - expect(field.value).toBe('BOT_VER'); - }); - - test('Expect author field to have values', async () => { - process.env = { - BOT_VER: "BOT_VER", - BOT_AUTHOR: "BOT_AUTHOR", - BOT_DATE: "BOT_DATE" - }; - - const message = mock<Message>(); - message.channel.send = jest.fn(); - - const context: ICommandContext = { - name: "about", - args: [], - message: message - }; - - const about = new About(); - - const result = await about.execute(context); - - const embed = result.embeds[0]; - const field = embed.fields[1]; - - expect(field.name).toBe('Author'); - expect(field.value).toBe('BOT_AUTHOR'); - }); - - test('Expect version field to have values', async () => { - process.env = { - BOT_VER: "BOT_VER", - BOT_AUTHOR: "BOT_AUTHOR", - BOT_DATE: "BOT_DATE" - }; - - const message = mock<Message>(); - message.channel.send = jest.fn(); - - const context: ICommandContext = { - name: "about", - args: [], - message: message - }; - - const about = new About(); - - const result = await about.execute(context); - - const embed = result.embeds[0]; - const field = embed.fields[2]; - - expect(field.name).toBe('Date'); - expect(field.value).toBe('BOT_DATE'); - }); -}); \ No newline at end of file diff --git a/tests/commands/ban.test.ts b/tests/commands/ban.test.ts deleted file mode 100644 index 8391abb..0000000 --- a/tests/commands/ban.test.ts +++ /dev/null @@ -1,724 +0,0 @@ -import { GuildMember, Message, TextChannel, User } from "discord.js"; -import Ban from "../../src/commands/ban"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect values to be set', () => { - process.env.ROLES_MODERATOR = 'Moderator'; - - const ban = new Ban(); - - expect(ban._category).toBe('Moderation'); - expect(ban._roles.length).toBe(1); - expect(ban._roles[0]).toBe('Moderator'); - }); -}); - -describe('Execute', () => { - test('Given user has permission, expect user to be banned', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - bannable: true, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban', 'Test', 'Reason'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).toBeCalledTimes(1); - expect(mentionedMember.ban).toBeCalledWith({ reason: 'Moderator: AUTHORTAG, Reason: Test Reason' }); - }); - - test('Given moderator did not supply a reason, expect default message', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - bannable: true, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).toBeCalledTimes(1); - expect(mentionedMember.ban).toBeCalledWith({ reason: 'Moderator: AUTHORTAG, Reason: *none*' }); - }); - - test('Given user has permissions, expect embeds to be correct', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - bannable: true, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban', 'Test', 'Reason'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - expect(result.embeds.length).toBe(2); - - const logEmbed = result.embeds[0]; - const publicEmbed = result.embeds[1]; - - expect(logEmbed.title).toBe('Member Banned'); - expect(publicEmbed.title).toBe(""); - expect(publicEmbed.description).toBe('[object Object] has been banned'); - expect(logEmbed.fields.length).toBe(3); - expect(publicEmbed.fields.length).toBe(0); - }); - - test('Given user has permission, expect logEmbed fields to be correct', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn().mockReturnValue('URL'), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - bannable: true, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban', 'Test', 'Reason'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - const logEmbed = result.embeds[0]; - - const fieldUser = logEmbed.fields[0]; - const fieldModerator = logEmbed.fields[1]; - const fieldReason = logEmbed.fields[2]; - - expect(fieldUser.name).toBe("User"); - expect(fieldUser.value).toBe("[object Object] `USERTAG`"); - expect(logEmbed.thumbnail?.url).toBe("URL"); - - expect(fieldModerator.name).toBe('Moderator'); - expect(fieldModerator.value).toBe('[object Object] `AUTHORTAG`'); - - expect(fieldReason.name).toBe('Reason'); - expect(fieldReason.value).toBe('Test Reason'); - }); - - test('Given moderator did not supply a reason, expect reason field to be default message', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn().mockReturnValue('URL'), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - bannable: true, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - const logEmbed = result.embeds[0]; - - const fieldUser = logEmbed.fields[0]; - const fieldModerator = logEmbed.fields[1]; - const fieldReason = logEmbed.fields[2]; - - expect(fieldUser.name).toBe("User"); - expect(fieldUser.value).toBe("[object Object] `USERTAG`"); - expect(logEmbed.thumbnail?.url).toBe("URL"); - - expect(fieldModerator.name).toBe('Moderator'); - expect(fieldModerator.value).toBe('[object Object] `AUTHORTAG`'); - - expect(fieldReason.name).toBe('Reason'); - expect(fieldReason.value).toBe('*none*'); - }); - - test('Given user is not mentioned, expect error embed to be sent', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedMember = { - bannable: true, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(null); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban', 'Test', 'Reason'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).not.toBeCalled(); - expect(mentionedMember.ban).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - const embedError = result.embeds[0]; - - expect(embedError.description).toBe('User does not exist'); - }); - - test('Given member is not in server, expect error embed to be sent', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - bannable: true, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(null); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban', 'Test', 'Reason'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).not.toBeCalled(); - expect(mentionedMember.ban).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - const embedError = result.embeds[0]; - - expect(embedError.description).toBe('User is not in this server'); - }); - - test('Given guild is unavailable, expect return and do nothing', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - bannable: true, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: false - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban', 'Test', 'Reason'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - expect(messageChannelSend).not.toBeCalled(); - expect(logChannel.send).not.toBeCalled(); - expect(mentionedMember.ban).not.toBeCalled(); - expect(result.embeds.length).toBe(0); - }); - - test('Given bot cant ban user, expect error embed to be sent', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - bannable: false, - ban: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'ban', - args: ['ban', 'Test', 'Reason'], - message: message - }; - - const ban = new Ban(); - - const result = await ban.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).not.toBeCalled(); - expect(mentionedMember.ban).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - const embedError = result.embeds[0]; - - expect(embedError.description).toBe('Unable to do this action, am I missing permissions?'); - }); -}); \ No newline at end of file diff --git a/tests/commands/clear.test.ts b/tests/commands/clear.test.ts deleted file mode 100644 index ad27d62..0000000 --- a/tests/commands/clear.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { Message } from "discord.js"; -import Clear from "../../src/commands/clear"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect values to be set', () => { - process.env = { - ROLES_MODERATOR: "Moderator" - }; - - const clear = new Clear(); - - expect(clear._category).toBe('Moderation'); - expect(clear._roles.length).toBe(1); - expect(clear._roles[0]).toBe('Moderator'); - }); -}); - -describe('Execute', () => { - test('Given valid arguments, expect messages to be cleared', async () => { - const messageChannelSend = jest.fn(); - const messageChannelBulkDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend, - bulkDelete: messageChannelBulkDelete - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'clear', - args: ['5'], - message: message - }; - - const clear = new Clear(); - const result = await clear.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageChannelBulkDelete).toBeCalledWith(5); - expect(result.embeds.length).toBe(1); - - // PublicEmbed - const publicEmbed = result.embeds[0]; - - expect(publicEmbed.title).toBe(''); - expect(publicEmbed.description).toBe('5 message(s) were removed'); - }); - - test('Given argument is not given, expect error embed to be sent', async () => { - const messageChannelSend = jest.fn(); - const messageChannelBulkDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend, - bulkDelete: messageChannelBulkDelete - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'clear', - args: [], - message: message - }; - - const clear = new Clear(); - const result = await clear.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageChannelBulkDelete).not.toBeCalled(); - expect(result.embeds.length).toBe(1); - - // ErrorEmbed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.title).toBeNull(); - expect(errorEmbed.description).toBe('Please specify an amount between 1 and 100'); - }); - - test('Given argument is not a number, expect error embed to be sent', async () => { - const messageChannelSend = jest.fn(); - const messageChannelBulkDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend, - bulkDelete: messageChannelBulkDelete - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'clear', - args: ['A'], - message: message - }; - - const clear = new Clear(); - const result = await clear.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageChannelBulkDelete).not.toBeCalled(); - expect(result.embeds.length).toBe(1); - - // ErrorEmbed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.title).toBeNull(); - expect(errorEmbed.description).toBe('Please specify an amount between 1 and 100'); - }); - - test('Given argument is less than 1, expect error embed to be sent', async () => { - const messageChannelSend = jest.fn(); - const messageChannelBulkDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend, - bulkDelete: messageChannelBulkDelete - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'clear', - args: ['0'], - message: message - }; - - const clear = new Clear(); - const result = await clear.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageChannelBulkDelete).not.toBeCalled(); - expect(result.embeds.length).toBe(1); - - // ErrorEmbed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.title).toBeNull(); - expect(errorEmbed.description).toBe('Please specify an amount between 1 and 100'); - }); - - test('Given argument is more than 100, expect error embed to be sent', async () => { - const messageChannelSend = jest.fn(); - const messageChannelBulkDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend, - bulkDelete: messageChannelBulkDelete - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'clear', - args: ['101'], - message: message - }; - - const clear = new Clear(); - const result = await clear.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageChannelBulkDelete).not.toBeCalled(); - expect(result.embeds.length).toBe(1); - - // ErrorEmbed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.title).toBeNull(); - expect(errorEmbed.description).toBe('Please specify an amount between 1 and 100'); - }); -}); \ No newline at end of file diff --git a/tests/commands/eval.test.ts b/tests/commands/eval.test.ts deleted file mode 100644 index a9c4929..0000000 --- a/tests/commands/eval.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { Message } from "discord.js"; -import Evaluate from "../../src/commands/eval"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect values to be set', () => { - const evaluate = new Evaluate(); - - expect(evaluate._category).toBe('Owner'); - }); -}); - -describe('Execute', () => { - test('Given user has permission, expect eval statement ran', () => { - process.env = { - BOT_OWNERID: 'OWNERID' - }; - - console.log = jest.fn(); - global.eval = jest.fn() - .mockReturnValue('General Kenobi'); - - const messageChannelSend = jest.fn(); - - const message = { - author: { - id: 'OWNERID' - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'eval', - args: ['echo', 'Hello', 'there'], - message: message - }; - - const evaluate = new Evaluate(); - - const result = evaluate.execute(context); - - expect(console.log).toBeCalledWith('Eval Statement: echo Hello there'); - expect(global.eval).toBeCalledWith('echo Hello there'); - expect(result.embeds.length).toBe(1); - - // PublicEmbed - const publicEmbed = result.embeds[0]; - - expect(publicEmbed.title).toBe(''); - expect(publicEmbed.description).toBe('General Kenobi'); - }); - - test('Given user does not have permission, expect nothing to occur', () => { - process.env = { - BOT_OWNERID: 'DIFFERENT' - }; - - console.log = jest.fn(); - global.eval = jest.fn() - .mockReturnValue('General Kenobi'); - - const messageChannelSend = jest.fn(); - - const message = { - author: { - id: 'OWNERID' - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'eval', - args: ['echo', 'Hello', 'there'], - message: message - }; - - const evaluate = new Evaluate(); - - const result = evaluate.execute(context); - - expect(console.log).not.toBeCalled(); - expect(global.eval).not.toBeCalled(); - expect(result.embeds.length).toBe(0); - }); - - test('Given eval failed, expect error embed to be sent', () => { - process.env = { - BOT_OWNERID: 'OWNERID' - }; - - console.log = jest.fn(); - global.eval = jest.fn() - .mockImplementation(() => { - throw new Error('Error message'); - }); - - const messageChannelSend = jest.fn(); - - const message = { - author: { - id: 'OWNERID' - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'eval', - args: ['echo', 'Hello', 'there'], - message: message - }; - - const evaluate = new Evaluate(); - - const result = evaluate.execute(context); - - expect(console.log).toBeCalledWith('Eval Statement: echo Hello there'); - expect(global.eval).toBeCalledWith('echo Hello there'); - expect(result.embeds.length).toBe(1); - - // ErrorEmbed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.title).toBeNull(); - expect(errorEmbed.description).toBe('Error: Error message'); - }); -}); \ No newline at end of file diff --git a/tests/commands/help.test.ts b/tests/commands/help.test.ts deleted file mode 100644 index 8a0fa32..0000000 --- a/tests/commands/help.test.ts +++ /dev/null @@ -1,267 +0,0 @@ -import Help, { ICommandData } from "../../src/commands/help"; -import { Message } from "discord.js"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -const oldCwd = process.cwd(); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - const help = new Help(); - - expect(help._category).toBe('General'); - }); -}); - -describe('Execute', () => { - test('Given no arguments were given, expect SendAll to be executed', () => { - const message = {} as unknown as Message; - - const context: ICommandContext = { - name: 'help', - args: [], - message: message - }; - - const help = new Help(); - - help.SendAll = jest.fn(); - help.SendSingle = jest.fn(); - - help.execute(context); - - expect(help.SendAll).toBeCalled(); - expect(help.SendSingle).not.toBeCalled(); - }); - - test('Given an argument was given, expect SendSingle to be executed', () => { - const message = {} as unknown as Message; - - const context: ICommandContext = { - name: 'help', - args: ['about'], - message: message - }; - - const help = new Help(); - - help.SendAll = jest.fn(); - help.SendSingle = jest.fn(); - - help.execute(context); - - expect(help.SendAll).not.toBeCalled(); - expect(help.SendSingle).toBeCalled(); - }); -}); - -describe('SendAll', () => { - test('Expect embed with all commands to be sent', () => { - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'help', - args: [], - message: message - }; - - const help = new Help(); - - const commandData0: ICommandData = { - Exists: true, - Name: 'about', - Category: 'general', - Roles: [] - }; - - const commandData1: ICommandData = { - Exists: true, - Name: 'role', - Category: 'general', - Roles: [] - }; - - help.GetAllCommandData = jest.fn() - .mockReturnValue([commandData0, commandData1]); - - const result = help.SendAll(context); - - expect(help.GetAllCommandData).toBeCalled(); - expect(messageChannelSend).toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // PublicEmbed - const publicEmbed = result.embeds[0]; - - expect(publicEmbed.fields.length).toBe(1); - - // PublicEmbed -> GeneralCategory Field - const publicEmbedFieldGeneral = publicEmbed.fields[0]; - - expect(publicEmbedFieldGeneral.name).toBe('General'); - expect(publicEmbedFieldGeneral.value).toBe('about, role'); - }); -}); - -describe('SendSingle', () => { - test('Given command exists, expect embed to be sent with command fields', () => { - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'help', - args: ['about'], - message: message - }; - - const commandData: ICommandData = { - Exists: true, - Name: 'about', - Category: 'general', - Roles: ['role1', 'role2'] - }; - - const help = new Help(); - - help.GetCommandData = jest.fn() - .mockReturnValue(commandData); - - const result = help.SendSingle(context); - - expect(help.GetCommandData).toBeCalledWith('about'); - expect(messageChannelSend).toBeCalled(); - expect(result.embeds.length).toBe(1); - - // PublicEmbed - const publicEmbed = result.embeds[0]; - - expect(publicEmbed.title).toBe('About'); - expect(publicEmbed.description).toBe(''); - expect(publicEmbed.fields.length).toBe(2); - - // PublicEmbed -> Category Field - const fieldCategory = publicEmbed.fields[0]; - - expect(fieldCategory.name).toBe('Category'); - expect(fieldCategory.value).toBe('General'); - - // PublicEmbed -> RequiredRoles Field - const fieldRoles = publicEmbed.fields[1]; - - expect(fieldRoles.name).toBe('Required Roles'); - expect(fieldRoles.value).toBe('Role1, Role2'); - }); - - test('Given command does not exist, expect error embed to be sent', () => { - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'help', - args: ['about'], - message: message - }; - - const commandData: ICommandData = { - Exists: false - }; - - const help = new Help(); - - help.GetCommandData = jest.fn() - .mockReturnValue(commandData); - - const result = help.SendSingle(context); - - expect(help.GetCommandData).toBeCalledWith('about'); - expect(messageChannelSend).toBeCalled(); - expect(result.embeds.length).toBe(1); - - // ErrorEmbed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('Command does not exist'); - }); -}); - -describe('GetAllCommandData', () => { - test('Expect array of command data to be returned', () => { - process.env = { - FOLDERS_COMMANDS: "commands" - }; - - process.cwd = jest.fn() - .mockReturnValue(`${oldCwd}/tests/_mocks`); - - const help = new Help(); - - const result = help.GetAllCommandData(); - - expect(result.length).toBe(1); - - // Mock Command - const mockCommand = result[0]; - - expect(mockCommand.Exists).toBeTruthy(); - expect(mockCommand.Name).toBe("mockCmd"); - expect(mockCommand.Category).toBe("General"); - - expect(mockCommand.Roles!.length).toBe(1); - expect(mockCommand.Roles![0]).toBe("Moderator"); - }); -}); - -describe('GetCommandData', () => { - test('Given command exists, expect data to be returned', () => { - process.env = { - FOLDERS_COMMANDS: "commands" - }; - - process.cwd = jest.fn() - .mockReturnValue(`${oldCwd}/tests/_mocks`); - - const help = new Help(); - - const result = help.GetCommandData('mockCmd'); - - expect(result.Exists).toBeTruthy(); - expect(result.Name).toBe("mockCmd"); - expect(result.Category).toBe("General"); - - expect(result.Roles!.length).toBe(1); - expect(result.Roles![0]).toBe("Moderator"); - }); - - test('Given command does not exist, expect exists false to be returned', () => { - process.env = { - FOLDERS_COMMANDS: "commands" - }; - - const oldCwd = process.cwd(); - - process.cwd = jest.fn() - .mockReturnValue(`${oldCwd}/tests/_mocks`); - - const help = new Help(); - - const result = help.GetCommandData('none'); - - expect(result.Exists).toBeFalsy(); - }); -}); diff --git a/tests/commands/kick.test.ts b/tests/commands/kick.test.ts deleted file mode 100644 index f0fc6a4..0000000 --- a/tests/commands/kick.test.ts +++ /dev/null @@ -1,549 +0,0 @@ -import { mock } from "jest-mock-extended"; - -import { GuildMember, Message, TextChannel, User } from "discord.js"; -import Kick from "../../src/commands/kick"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - process.env = { - ROLES_MODERATOR: "Moderator" - }; - - const kick = new Kick(); - - expect(kick._category).toBe('Moderation'); - expect(kick._roles.length).toBe(1); - expect(kick._roles[0]).toBe('Moderator'); - }); -}); - -describe('Execute', () => { - test('Given user has permission, expect user to be kicked', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const member = { - kickable: true, - kick: jest.fn() - } as unknown as GuildMember; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: "kick", - args: ["USER", "Test", "Reason"], - message: message - } - - const kick = new Kick(); - - const result = await kick.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).toBeCalledTimes(1); - expect(member.kick).toBeCalledWith('Moderator: AUTHORTAG, Reason: Test Reason'); - - expect(result.embeds.length).toBe(2); - - // Log Embed - const logEmbed = result.embeds[0]; - - expect(logEmbed.title).toBe('Member Kicked'); - expect(logEmbed.fields.length).toBe(3); - - // Log Embed -> User Field - const logEmbedFieldUser = logEmbed.fields[0]; - - expect(logEmbedFieldUser.name).toBe('User'); - expect(logEmbedFieldUser.value).toBe('[object Object] `USERTAG`'); - expect(logEmbedFieldUser.inline).toBeTruthy(); - - // Log Embed -> Moderator Field - const logEmbedFieldModerator = logEmbed.fields[1]; - - expect(logEmbedFieldModerator.name).toBe('Moderator'); - expect(logEmbedFieldModerator.value).toBe('[object Object] `AUTHORTAG`'); - - // Log Embed -> Reason Field - const logEmbedFieldReason = logEmbed.fields[2]; - - expect(logEmbedFieldReason.name).toBe('Reason'); - expect(logEmbedFieldReason.value).toBe('Test Reason'); - }); - - test('Given moderator did not supply a reason, expect default reason to be added', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const member = { - kickable: true, - kick: jest.fn() - } as unknown as GuildMember; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: "kick", - args: ["USER"], - message: message - } - - const kick = new Kick(); - - const result = await kick.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).toBeCalledTimes(1); - expect(member.kick).toBeCalledWith('Moderator: AUTHORTAG, Reason: *none*'); - - expect(result.embeds.length).toBe(2); - - // Log Embed - const logEmbed = result.embeds[0]; - - expect(logEmbed.title).toBe('Member Kicked'); - expect(logEmbed.fields.length).toBe(3); - - // Log Embed -> User Field - const logEmbedFieldUser = logEmbed.fields[0]; - - expect(logEmbedFieldUser.name).toBe('User'); - expect(logEmbedFieldUser.value).toBe('[object Object] `USERTAG`'); - expect(logEmbedFieldUser.inline).toBeTruthy(); - - // Log Embed -> Moderator Field - const logEmbedFieldModerator = logEmbed.fields[1]; - - expect(logEmbedFieldModerator.name).toBe('Moderator'); - expect(logEmbedFieldModerator.value).toBe('[object Object] `AUTHORTAG`'); - - // Log Embed -> Reason Field - const logEmbedFieldReason = logEmbed.fields[2]; - - expect(logEmbedFieldReason.name).toBe('Reason'); - expect(logEmbedFieldReason.value).toBe('*none*'); - }); - - test('Given target user is not found, expect user does not exist error', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const member = { - kickable: true, - kick: jest.fn() - } as unknown as GuildMember; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(null); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: "kick", - args: ["USER", "Test", "Reason"], - message: message - } - - const kick = new Kick(); - - const result = await kick.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).not.toBeCalled(); - expect(member.kick).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe(null); - expect(embed.description).toBe('User does not exist'); - }); - - test('Given target member is not found, expect user is not in this server error', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const member = { - kickable: true, - kick: jest.fn() - } as unknown as GuildMember; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(null); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: "kick", - args: ["USER", "Test", "Reason"], - message: message - } - - const kick = new Kick(); - - const result = await kick.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).not.toBeCalled(); - expect(member.kick).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe(null); - expect(embed.description).toBe('User is not in this server'); - }); - - test('Given guild is not available, expect to stop', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const member = { - kickable: true, - kick: jest.fn() - } as unknown as GuildMember; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: false - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: "kick", - args: ["USER", "Test", "Reason"], - message: message - } - - const kick = new Kick(); - - const result = await kick.execute(context); - - expect(messageChannelSend).not.toBeCalled(); - expect(logChannel.send).not.toBeCalled(); - expect(member.kick).not.toBeCalled(); - - expect(result.embeds.length).toBe(0); - }); - - test('Given client can not kick member, expect error', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const member = { - kickable: false, - kick: jest.fn() - } as unknown as GuildMember; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: "kick", - args: ["USER", "Test", "Reason"], - message: message - } - - const kick = new Kick(); - - const result = await kick.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).not.toBeCalled(); - expect(member.kick).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe(null); - expect(embed.description).toBe('Unable to do this action, am I missing permissions?'); - }); -}); \ No newline at end of file diff --git a/tests/commands/mute.test.ts b/tests/commands/mute.test.ts deleted file mode 100644 index 974f676..0000000 --- a/tests/commands/mute.test.ts +++ /dev/null @@ -1,815 +0,0 @@ -import { mock } from "jest-mock-extended"; - -import { GuildMember, Message, Role, TextChannel, User } from "discord.js"; -import Mute from "../../src/commands/mute"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - process.env = { - ROLES_MODERATOR: 'Moderator' - }; - - const mute = new Mute(); - - expect(mute._category).toBe("Moderation"); - expect(mute._roles.length).toBe(1); - expect(mute._roles[0]).toBe('Moderator'); - }); -}); - -describe('Execute', () => { - test('Given user has permission, expect user to be given muted role', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - add: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Mute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageChannelSend).toBeCalledTimes(1); - expect(member.roles.add).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: Test Reason'); - - expect(result.embeds.length).toBe(2); - - // Log Embed - const logEmbed = result.embeds[0]; - - expect(logEmbed.title).toBe('Member Muted'); - expect(logEmbed.fields.length).toBe(3); - - // Log Embed -> User Field - const logEmbedUserField = logEmbed.fields[0]; - - expect(logEmbedUserField.name).toBe('User'); - expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`'); - expect(logEmbedUserField.inline).toBeTruthy(); - - // Log Embed -> Moderator Field - const logEmbedModeratorField = logEmbed.fields[1]; - - expect(logEmbedModeratorField.name).toBe('Moderator'); - expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`'); - - // Log Embed -> Reason Field - const logEmbedFieldReason = logEmbed.fields[2]; - - expect(logEmbedFieldReason.name).toBe('Reason'); - expect(logEmbedFieldReason.value).toBe('Test Reason'); - - // Public Embed - const publicEmbed = result.embeds[1]; - - expect(publicEmbed.title).toBe(''); - expect(publicEmbed.description).toBe('[object Object] has been muted'); - }); - - test('Given moderator did not supply a reason, expect default reason used', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - add: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER'], - message: message - }; - - const mute = new Mute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageChannelSend).toBeCalledTimes(1); - expect(member.roles.add).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: *none*'); - - expect(result.embeds.length).toBe(2); - - // Log Embed - const logEmbed = result.embeds[0]; - - expect(logEmbed.title).toBe('Member Muted'); - expect(logEmbed.fields.length).toBe(3); - - // Log Embed -> User Field - const logEmbedUserField = logEmbed.fields[0]; - - expect(logEmbedUserField.name).toBe('User'); - expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`'); - expect(logEmbedUserField.inline).toBeTruthy(); - - // Log Embed -> Moderator Field - const logEmbedModeratorField = logEmbed.fields[1]; - - expect(logEmbedModeratorField.name).toBe('Moderator'); - expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`'); - - // Log Embed -> Reason Field - const logEmbedFieldReason = logEmbed.fields[2]; - - expect(logEmbedFieldReason.name).toBe('Reason'); - expect(logEmbedFieldReason.value).toBe('*none*'); - - // Public Embed - const publicEmbed = result.embeds[1]; - - expect(publicEmbed.title).toBe(''); - expect(publicEmbed.description).toBe('[object Object] has been muted'); - }); - - test('Given user did not mention a user, expect user not to exist', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - add: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(null); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Mute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).not.toBeCalled(); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('User does not exist'); - }); - - test('Given member can not be found from user, expect user to not be in server', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - add: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(null); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Mute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('User is not in this server'); - }); - - test('Given guild is unavailable, expect execution to stop', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - add: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: false, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Mute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).not.toBeCalled(); - - expect(result.embeds.length).toBe(0); - }); - - test('Given client can not manage user, expect insufficient permissions', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: false, - roles: { - add: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Mute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('Unable to do this action, am I missing permissions?'); - }); - - test('Given muted role can not be found, expect role not found', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - add: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockReturnValue(undefined); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Mute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('Unable to find role'); - }); -}); \ No newline at end of file diff --git a/tests/commands/poll.test.ts b/tests/commands/poll.test.ts deleted file mode 100644 index 4f3727d..0000000 --- a/tests/commands/poll.test.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { Message, MessageEmbed } from "discord.js"; -import Poll from "../../src/commands/poll"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -describe('Constructor', () => { - test('Expect properties to be set', () => { - const poll = new Poll(); - - expect(poll._category).toBe('General'); - }); -}); - -describe('Execute', () => { - test('Given input is valid, expect poll to be generated', async () => { - const returnMessageReact = jest.fn(); - - const returnMessage = { - react: returnMessageReact - } as unknown as Message; - - const messageChannelSend = jest.fn() - .mockReturnValue(returnMessage); - const messageDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - delete: messageDelete, - deletable: true - } as unknown as Message; - - const context: ICommandContext = { - name: 'poll', - args: ['Test', 'title;', 'one;', 'two'], - message: message - }; - - const poll = new Poll(); - - const result = await poll.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageDelete).toBeCalledTimes(1); - expect(returnMessageReact).toBeCalledTimes(2); - - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Test title'); - expect(embed.description).toBe(':one: one\n:two: two'); - }); - - test('Given message is not deletable by client, expect it not to attempt deletion', async () => { - const returnMessageReact = jest.fn(); - - const returnMessage = { - react: returnMessageReact - } as unknown as Message; - - const messageChannelSend = jest.fn() - .mockReturnValue(returnMessage); - const messageDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - delete: messageDelete, - deletable: false - } as unknown as Message; - - const context: ICommandContext = { - name: 'poll', - args: ['Test', 'title;', 'one;', 'two'], - message: message - }; - - const poll = new Poll(); - - const result = await poll.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageDelete).not.toBeCalled(); - expect(returnMessageReact).toBeCalledTimes(2); - - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Test title'); - expect(embed.description).toBe(':one: one\n:two: two'); - }); - - test('Given no arguments, expect error embed', async () => { - const returnMessageReact = jest.fn(); - - const returnMessage = { - react: returnMessageReact - } as unknown as Message; - - const messageChannelSend = jest.fn() - .mockReturnValue(returnMessage); - const messageDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - delete: messageDelete, - deletable: true - } as unknown as Message; - - const context: ICommandContext = { - name: 'poll', - args: [], - message: message - }; - - const poll = new Poll(); - - const result = await poll.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageDelete).not.toBeCalled(); - expect(returnMessageReact).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options'); - }); - - test('Given only 1 option, expect error embed', async () => { - const returnMessageReact = jest.fn(); - - const returnMessage = { - react: returnMessageReact - } as unknown as Message; - - const messageChannelSend = jest.fn() - .mockReturnValue(returnMessage); - const messageDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - delete: messageDelete, - deletable: true - } as unknown as Message; - - const context: ICommandContext = { - name: 'poll', - args: ['Test', 'title;', 'one'], - message: message - }; - - const poll = new Poll(); - - const result = await poll.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageDelete).not.toBeCalled(); - expect(returnMessageReact).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options'); - }); - - test('Given 9 options, expect poll to be generated', async () => { - const returnMessageReact = jest.fn(); - - const returnMessage = { - react: returnMessageReact - } as unknown as Message; - - const messageChannelSend = jest.fn() - .mockReturnValue(returnMessage); - const messageDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - delete: messageDelete, - deletable: true - } as unknown as Message; - - const context: ICommandContext = { - name: 'poll', - args: ['Test', 'title;', 'one;', 'two;', 'three;', 'four;', 'five;', 'six;', 'seven;', 'eight;', 'nine'], - message: message - }; - - const poll = new Poll(); - - const result = await poll.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageDelete).toBeCalledTimes(1); - expect(returnMessageReact).toBeCalledTimes(9); - - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Test title'); - expect(embed.description).toBe(':one: one\n:two: two\n:three: three\n:four: four\n:five: five\n:six: six\n:seven: seven\n:eight: eight\n:nine: nine'); - }); - - test('Given 10 options, expect error embed', async () => { - const returnMessageReact = jest.fn(); - - const returnMessage = { - react: returnMessageReact - } as unknown as Message; - - const messageChannelSend = jest.fn() - .mockReturnValue(returnMessage); - const messageDelete = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - delete: messageDelete, - deletable: true - } as unknown as Message; - - const context: ICommandContext = { - name: 'poll', - args: ['Test', 'title;', 'one;', 'two;', 'three;', 'four;', 'five;', 'six;', 'seven;', 'eight;', 'nine;', 'ten'], - message: message - }; - - const poll = new Poll(); - - const result = await poll.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(messageDelete).not.toBeCalled(); - expect(returnMessageReact).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options'); - }); -}); \ No newline at end of file diff --git a/tests/commands/role.test.ts b/tests/commands/role.test.ts deleted file mode 100644 index d14d56a..0000000 --- a/tests/commands/role.test.ts +++ /dev/null @@ -1,411 +0,0 @@ -import { GuildMemberRoleManager, Message, Role as DiscordRole } from "discord.js"; -import { mock } from "jest-mock-extended"; -import Role from "../../src/commands/role"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect properties are set', () => { - const role = new Role(); - - expect(role._category).toBe("General"); - }); -}); - -describe('Execute', () => { - test('Given no arguments were given, expect SendRolesList to be executed', async () => { - process.env = { - COMMANDS_ROLE_ROLES: 'One,Two' - }; - - const message = {} as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: [], - message: message - }; - - const role = new Role(); - - role.SendRolesList = jest.fn(); - role.ToggleRole = jest.fn(); - - await role.execute(context); - - expect(role.SendRolesList).toBeCalledWith(context, ['One', 'Two']); - expect(role.ToggleRole).not.toBeCalled(); - }); - - test('Given an argument was given, expect ToggleRole to be executed', async () => { - process.env = { - COMMANDS_ROLE_ROLES: 'One,Two' - }; - - const message = {} as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: ['One'], - message: message - }; - - const role = new Role(); - - role.SendRolesList = jest.fn(); - role.ToggleRole = jest.fn(); - - await role.execute(context); - - expect(role.SendRolesList).not.toBeCalled(); - expect(role.ToggleRole).toBeCalledWith(context, ['One', 'Two']); - }); -}); - -describe('SendRolesList', () => { - test('Expect embed with roles to be sent to the current channel', () => { - process.env = { - BOT_PREFIX: '!' - }; - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: [], - message: message - }; - - const roles = ['One', 'Two']; - - const role = new Role(); - - const result = role.SendRolesList(context, roles); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Roles'); - expect(embed.description).toBe('Do !role <role> to get the role!\nOne\nTwo'); - }); -}); - -describe('ToggleRole', () => { - test('Given role name is a valid role AND user does not have the role, expect role to be added', async () => { - const discordRole = {} as unknown as DiscordRole; - - const messageMemberRolesCacheFind = jest.fn() - .mockReturnValue(undefined); - const messageGuildRolesCacheFind = jest.fn() - .mockReturnValue(discordRole); - const messageChannelSend = jest.fn(); - - const message = { - member: { - roles: { - cache: { - find: messageMemberRolesCacheFind - } - } - }, - guild: { - roles: { - cache: { - find: messageGuildRolesCacheFind - } - } - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: ['One'], - message: message - }; - - const roles = ['One', 'Two']; - - const role = new Role(); - - role.AddRole = jest.fn(); - role.RemoveRole = jest.fn(); - - const result = await role.ToggleRole(context, roles); - - expect(messageMemberRolesCacheFind).toBeCalledTimes(1); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageChannelSend).not.toBeCalled(); - expect(role.AddRole).toBeCalledWith(context, discordRole); - expect(role.RemoveRole).not.toBeCalled(); - - expect(result.embeds.length).toBe(0); - }); - - test('Given role name is a valid role AND user has the role, expect role to be removed', async () => { - const discordRole = {} as unknown as DiscordRole; - - const messageMemberRolesCacheFind = jest.fn() - .mockReturnValue(discordRole); - const messageGuildRolesCacheFind = jest.fn() - .mockReturnValue(discordRole); - const messageChannelSend = jest.fn(); - - const message = { - member: { - roles: { - cache: { - find: messageMemberRolesCacheFind - } - } - }, - guild: { - roles: { - cache: { - find: messageGuildRolesCacheFind - } - } - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: ['One'], - message: message - }; - - const roles = ['One', 'Two']; - - const role = new Role(); - - role.AddRole = jest.fn(); - role.RemoveRole = jest.fn(); - - const result = await role.ToggleRole(context, roles); - - expect(messageMemberRolesCacheFind).toBeCalledTimes(1); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageChannelSend).not.toBeCalled(); - expect(role.AddRole).not.toBeCalled(); - expect(role.RemoveRole).toBeCalledWith(context, discordRole); - - expect(result.embeds.length).toBe(0); - }); - - test('Given role requested is not in the roles array, expect role not assignable error', async () => { - const discordRole = {} as unknown as DiscordRole; - - const messageMemberRolesCacheFind = jest.fn() - .mockReturnValue(undefined); - const messageGuildRolesCacheFind = jest.fn() - .mockReturnValue(discordRole); - const messageChannelSend = jest.fn(); - - const message = { - member: { - roles: { - cache: { - find: messageMemberRolesCacheFind - } - } - }, - guild: { - roles: { - cache: { - find: messageGuildRolesCacheFind - } - } - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: ['Three'], - message: message - }; - - const roles = ['One', 'Two']; - - const role = new Role(); - - role.AddRole = jest.fn(); - role.RemoveRole = jest.fn(); - - const result = await role.ToggleRole(context, roles); - - expect(messageMemberRolesCacheFind).not.toBeCalled(); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - expect(role.AddRole).not.toBeCalled(); - expect(role.RemoveRole).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe("This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters"); - }); - - test('Given the role is not in the guild, expect error', async () => { - const discordRole = {} as unknown as DiscordRole; - - const messageMemberRolesCacheFind = jest.fn() - .mockReturnValue(undefined); - const messageGuildRolesCacheFind = jest.fn() - .mockReturnValue(undefined); - const messageChannelSend = jest.fn(); - - const message = { - member: { - roles: { - cache: { - find: messageMemberRolesCacheFind - } - } - }, - guild: { - roles: { - cache: { - find: messageGuildRolesCacheFind - } - } - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: ['One'], - message: message - }; - - const roles = ['One', 'Two']; - - const role = new Role(); - - role.AddRole = jest.fn(); - role.RemoveRole = jest.fn(); - - const result = await role.ToggleRole(context, roles); - - expect(messageMemberRolesCacheFind).not.toBeCalled(); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageChannelSend).toBeCalledTimes(1); - expect(role.AddRole).not.toBeCalled(); - expect(role.RemoveRole).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe("The current server doesn't have this role. Please contact the server's moderators"); - }); -}); - -describe('AddRole', () => { - test('Expect role to be added to user', async () => { - const messageChannelSend = jest.fn(); - - const guildMemberRoleManager = mock<GuildMemberRoleManager>(); - - const message = { - member: { - roles: guildMemberRoleManager - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: ['One'], - message: message - }; - - const discordRole = { - name: 'One' - } as unknown as DiscordRole; - - const role = new Role(); - - const result = await role.AddRole(context, discordRole); - - expect(guildMemberRoleManager.add).toBeCalledWith(discordRole, "Toggled with role command"); - expect(messageChannelSend).toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe(''); - expect(embed.description).toBe('Gave role: One'); - }); -}); - -describe('RemoveRole', () => { - test('Expect role to be removed from user', async () => { - const messageChannelSend = jest.fn(); - - const guildMemberRoleManager = mock<GuildMemberRoleManager>(); - - const message = { - member: { - roles: guildMemberRoleManager - }, - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'role', - args: ['One'], - message: message - }; - - const discordRole = { - name: 'One' - } as unknown as DiscordRole; - - const role = new Role(); - - const result = await role.RemoveRole(context, discordRole); - - expect(guildMemberRoleManager.remove).toBeCalledWith(discordRole, "Toggled with role command"); - expect(messageChannelSend).toBeCalled(); - - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe(''); - expect(embed.description).toBe('Removed role: One'); - }); -}); \ No newline at end of file diff --git a/tests/commands/rules.test.ts b/tests/commands/rules.test.ts deleted file mode 100644 index 4c390a5..0000000 --- a/tests/commands/rules.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { Message } from "discord.js"; -import Rules from "../../src/commands/rules"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -const oldCwd = process.cwd(); - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - process.env = { - ROLES_MODERATOR: "Moderator" - }; - - const rules = new Rules(); - - expect(rules._category).toBe("Admin"); - expect(rules._roles.length).toBe(1); - expect(rules._roles[0]).toBe("Moderator"); - }); -}); - -describe('Execute', () => { - test('Given rules exist, expect rules to be sent to current channel', () => { - process.env = { - COMMANDS_RULES_FILE: 'rules/rules.json' - }; - - process.cwd = jest.fn() - .mockReturnValue(`${oldCwd}/tests/_mocks`); - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'rules', - args: [], - message: message - }; - - const rules = new Rules(); - - const result = rules.execute(context); - - expect(messageChannelSend).toBeCalledTimes(2); - expect(result.embeds.length).toBe(2); - - // Header Embed - const embedHeader = result.embeds[0]; - - expect(embedHeader.title).toBe(""); - expect(embedHeader.description).toBe(""); - expect(embedHeader.image?.url).toBe("IMAGEURL"); - expect(embedHeader.footer?.text).toBe(""); - - // Main Embed - const embedMain = result.embeds[1]; - - expect(embedMain.title).toBe("TITLE 1"); - expect(embedMain.description).toBe("DESCRIPTION 1A\nDESCRIPTION 1B"); - expect(embedMain.image?.url).toBe(""); - expect(embedMain.footer?.text).toBe("FOOTER 1"); - }); - - test('Given rules file does not exist, expect does not exist error', () => { - process.env = { - COMMANDS_RULES_FILE: 'rules/none.json' - }; - - process.cwd = jest.fn() - .mockReturnValue(`${oldCwd}/tests/_mocks`); - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'rules', - args: [], - message: message - }; - - const rules = new Rules(); - - const result = rules.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe("Rules file doesn't exist"); - }); -}); \ No newline at end of file diff --git a/tests/commands/unmute.test.ts b/tests/commands/unmute.test.ts deleted file mode 100644 index b61a8f8..0000000 --- a/tests/commands/unmute.test.ts +++ /dev/null @@ -1,813 +0,0 @@ -import { GuildMember, Message, Role, TextChannel, User } from "discord.js"; -import Unmute from "../../src/commands/unmute"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - process.env = { - ROLES_MODERATOR: 'Moderator' - }; - - const mute = new Unmute(); - - expect(mute._category).toBe("Moderation"); - expect(mute._roles.length).toBe(1); - expect(mute._roles[0]).toBe('Moderator'); - }); -}); - -describe('Execute', () => { - test('Given user has permission, expect user to be given muted role', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - remove: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Unmute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageChannelSend).toBeCalledTimes(1); - expect(member.roles.remove).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: Test Reason'); - - expect(result.embeds.length).toBe(2); - - // Log Embed - const logEmbed = result.embeds[0]; - - expect(logEmbed.title).toBe('Member Unmuted'); - expect(logEmbed.fields.length).toBe(3); - - // Log Embed -> User Field - const logEmbedUserField = logEmbed.fields[0]; - - expect(logEmbedUserField.name).toBe('User'); - expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`'); - expect(logEmbedUserField.inline).toBeTruthy(); - - // Log Embed -> Moderator Field - const logEmbedModeratorField = logEmbed.fields[1]; - - expect(logEmbedModeratorField.name).toBe('Moderator'); - expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`'); - - // Log Embed -> Reason Field - const logEmbedFieldReason = logEmbed.fields[2]; - - expect(logEmbedFieldReason.name).toBe('Reason'); - expect(logEmbedFieldReason.value).toBe('Test Reason'); - - // Public Embed - const publicEmbed = result.embeds[1]; - - expect(publicEmbed.title).toBe(''); - expect(publicEmbed.description).toBe('[object Object] has been unmuted'); - }); - - test('Given moderator did not supply a reason, expect default reason is used', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - remove: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER'], - message: message - }; - - const mute = new Unmute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageChannelSend).toBeCalledTimes(1); - expect(member.roles.remove).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: *none*'); - - expect(result.embeds.length).toBe(2); - - // Log Embed - const logEmbed = result.embeds[0]; - - expect(logEmbed.title).toBe('Member Unmuted'); - expect(logEmbed.fields.length).toBe(3); - - // Log Embed -> User Field - const logEmbedUserField = logEmbed.fields[0]; - - expect(logEmbedUserField.name).toBe('User'); - expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`'); - expect(logEmbedUserField.inline).toBeTruthy(); - - // Log Embed -> Moderator Field - const logEmbedModeratorField = logEmbed.fields[1]; - - expect(logEmbedModeratorField.name).toBe('Moderator'); - expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`'); - - // Log Embed -> Reason Field - const logEmbedFieldReason = logEmbed.fields[2]; - - expect(logEmbedFieldReason.name).toBe('Reason'); - expect(logEmbedFieldReason.value).toBe('*none*'); - - // Public Embed - const publicEmbed = result.embeds[1]; - - expect(publicEmbed.title).toBe(''); - expect(publicEmbed.description).toBe('[object Object] has been unmuted'); - }); - - test('Given user did not mention a user, expect user not to exist', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - remove: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(null); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Unmute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).not.toBeCalled(); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('User does not exist'); - }); - - test('Given member can not be found from user, expect user to not be in server', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - remove: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(null); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Unmute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('User is not in this server'); - }); - - test('Given guild is unavailable, expect execution to stop', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - remove: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: false, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Unmute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).not.toBeCalled(); - - expect(result.embeds.length).toBe(0); - }); - - test('Given client can not manage user, expect insufficient permissions', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: false, - roles: { - remove: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockImplementation((callback): Role | undefined => { - const result = callback(role); - - if (!result) { - return undefined; - } - - return role; - }); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Unmute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).not.toBeCalled(); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('Unable to do this action, am I missing permissions?'); - }); - - test('Given muted role can not be found, expect role not found', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs', - ROLES_MUTED: 'Muted' - }; - - const user = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - - const messageAuthor = { - tag: 'AUTHORTAG' - } as unknown as User; - - const member = { - manageable: true, - roles: { - remove: jest.fn() - } - } as unknown as GuildMember; - - const role = { - name: 'Muted' - } as unknown as Role; - - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(user); - const messageGuildMember = jest.fn() - .mockReturnValue(member); - const messageGuildRolesCacheFind = jest.fn() - .mockReturnValue(undefined); - const messageChannelSend = jest.fn(); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember, - available: true, - roles: { - cache: { - find: messageGuildRolesCacheFind - } - }, - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - } - }, - channel: { - send: messageChannelSend - }, - author: messageAuthor - } as unknown as Message; - - const context: ICommandContext = { - name: 'mute', - args: ['USER', 'Test', 'Reason'], - message: message - }; - - const mute = new Unmute(); - - const result = await mute.execute(context); - - expect(messageMentionsUsersFirst).toBeCalledTimes(1); - expect(messageGuildMember).toBeCalledWith(user); - expect(messageGuildRolesCacheFind).toBeCalledTimes(1); - expect(messageGuildChannelsCacheFind).not.toBeCalled(); - expect(messageChannelSend).toBeCalledTimes(1); - - expect(result.embeds.length).toBe(1); - - // Error Embed - const errorEmbed = result.embeds[0]; - - expect(errorEmbed.description).toBe('Unable to find role'); - }); -}); \ No newline at end of file diff --git a/tests/commands/warn.test.ts b/tests/commands/warn.test.ts deleted file mode 100644 index f77ba0a..0000000 --- a/tests/commands/warn.test.ts +++ /dev/null @@ -1,485 +0,0 @@ -import { GuildMember, Message, TextChannel, User } from "discord.js"; -import Warn from "../../src/commands/warn"; -import { ICommandContext } from "../../src/contracts/ICommandContext"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect values to be set', () => { - process.env.ROLES_MODERATOR = 'Moderator'; - - const warn = new Warn(); - - expect(warn._category).toBe('Moderation'); - expect(warn._roles.length).toBe(1); - expect(warn._roles[0]).toBe('Moderator'); - }); -}); - -describe('Execute', () => { - test('Given user has permission, expect user to be warnned', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - warnnable: true, - warn: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'warn', - args: ['warn', 'Test', 'Reason'], - message: message - }; - - const warn = new Warn(); - - const result = await warn.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).toBeCalledTimes(1); - }); - - test('Given user has permissions, expect embeds to be correct', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - warnnable: true, - warn: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'warn', - args: ['warn', 'Test', 'Reason'], - message: message - }; - - const warn = new Warn(); - - const result = await warn.execute(context); - - expect(result.embeds.length).toBe(2); - - const logEmbed = result.embeds[0]; - const publicEmbed = result.embeds[1]; - - expect(logEmbed.title).toBe('Member Warned'); - expect(publicEmbed.title).toBe(""); - expect(publicEmbed.description).toBe('[object Object] has been warned'); - expect(logEmbed.fields.length).toBe(3); - expect(publicEmbed.fields.length).toBe(1); - expect(publicEmbed.fields[0].name).toBe('Reason'); - expect(publicEmbed.fields[0].value).toBe('Test Reason'); - }); - - test('Given user has permission, expect logEmbed fields to be correct', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn().mockReturnValue('URL'), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - warnnable: true, - warn: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'warn', - args: ['warn', 'Test', 'Reason'], - message: message - }; - - const warn = new Warn(); - - const result = await warn.execute(context); - - const logEmbed = result.embeds[0]; - - const fieldUser = logEmbed.fields[0]; - const fieldModerator = logEmbed.fields[1]; - const fieldReason = logEmbed.fields[2]; - - expect(fieldUser.name).toBe("User"); - expect(fieldUser.value).toBe("[object Object] `USERTAG`"); - expect(logEmbed.thumbnail?.url).toBe("URL"); - - expect(fieldModerator.name).toBe('Moderator'); - expect(fieldModerator.value).toBe('[object Object] `AUTHORTAG`'); - - expect(fieldReason.name).toBe('Reason'); - expect(fieldReason.value).toBe('Test Reason'); - }); - - test('Given user is not mentioned, expect error embed to be sent', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedMember = { - warnnable: true, - warn: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(null); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'warn', - args: ['warn', 'Test', 'Reason'], - message: message - }; - - const warn = new Warn(); - - const result = await warn.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - const embedError = result.embeds[0]; - - expect(embedError.description).toBe('User does not exist'); - }); - - test('Given member is not in server, expect error embed to be sent', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - warnnable: true, - warn: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(null); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: true - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'warn', - args: ['warn', 'Test', 'Reason'], - message: message - }; - - const warn = new Warn(); - - const result = await warn.execute(context); - - expect(messageChannelSend).toBeCalledTimes(1); - expect(logChannel.send).not.toBeCalled(); - - expect(result.embeds.length).toBe(1); - - const embedError = result.embeds[0]; - - expect(embedError.description).toBe('User is not in this server'); - }); - - test('Given guild is unavailable, expect return and do nothing', async () => { - process.env = { - ROLES_MODERATOR: 'Moderator', - CHANNELS_LOGS_MOD: 'mod-logs' - }; - - const mentionedUser = { - displayAvatarURL: jest.fn(), - tag: 'USERTAG' - } as unknown as User; - const mentionedMember = { - warnnable: true, - warn: jest.fn() - } as unknown as GuildMember; - const logChannel = { - name: 'mod-logs', - send: jest.fn() - } as unknown as TextChannel; - - const messageChannelSend = jest.fn(); - const messageMentionsUsersFirst = jest.fn() - .mockReturnValue(mentionedUser); - const messageGuildMember = jest.fn() - .mockReturnValue(mentionedMember); - const messageGuildChannelsCacheFind = jest.fn() - .mockImplementation((callback): TextChannel | undefined => { - const result = callback(logChannel); - - if (!result) { - return undefined; - } - - return logChannel; - }); - - const message = { - channel: { - send: messageChannelSend - }, - mentions: { - users: { - first: messageMentionsUsersFirst - } - }, - guild: { - member: messageGuildMember , - channels: { - cache: { - find: messageGuildChannelsCacheFind - } - }, - available: false - }, - author: { - tag: 'AUTHORTAG' - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'warn', - args: ['warn', 'Test', 'Reason'], - message: message - }; - - const warn = new Warn(); - - const result = await warn.execute(context); - - expect(messageChannelSend).not.toBeCalled(); - expect(logChannel.send).not.toBeCalled(); - expect(result.embeds.length).toBe(0); - }); -}); \ No newline at end of file diff --git a/tests/database/entites/UserSetting.test.ts b/tests/database/entites/UserSetting.test.ts new file mode 100644 index 0000000..f062cc7 --- /dev/null +++ b/tests/database/entites/UserSetting.test.ts @@ -0,0 +1,65 @@ +import AppDataSource from "../../../src/database/dataSources/appDataSource"; +import UserSetting from "../../../src/database/entities/UserSetting"; + +describe("constructor", () => { + let userSetting: UserSetting; + + beforeEach(() => { + userSetting = new UserSetting("userId", "key", "value"); + }); + + test("EXPECT settings to be configured", () => { + expect(userSetting).toMatchSnapshot({ + Id: expect.any(String), + WhenCreated: expect.any(Date), + WhenUpdated: expect.any(Date), + }); + }); +}); + +describe("UpdateValue", () => { + let userSetting: UserSetting; + + beforeEach(() => { + userSetting = new UserSetting("userId", "key", "value"); + + userSetting.UpdateValue("newValue"); + }); + + test("EXPECT value to be updated", () => { + expect(userSetting.Value).toBe("newValue"); + }); +}); + +describe("FetchOneByKey", () => { + let result: UserSetting | null; + let userSetting: UserSetting; + + let findOneMock: jest.Mock; + + beforeEach(async () => { + userSetting = new UserSetting("userId", "key", "value"); + + findOneMock = jest.fn().mockResolvedValue(userSetting); + + AppDataSource.getRepository = jest.fn().mockReturnValue({ + findOne: findOneMock, + }); + + result = await UserSetting.FetchOneByKey("userId", "key"); + }); + + test("EXPECT getRepository to have been called", () => { + expect(AppDataSource.getRepository).toHaveBeenCalledTimes(1); + expect(AppDataSource.getRepository).toHaveBeenCalledWith(UserSetting); + }); + + test("EXPECT repository.findOne to have been called", () => { + expect(findOneMock).toHaveBeenCalledTimes(1); + expect(findOneMock).toHaveBeenCalledWith({ where: { UserId: "userId", Key: "key" }, relations: {}}); + }) + + test("EXPECT single entity returned", () => { + expect(result).toBe(userSetting); + }); +}); diff --git a/tests/database/entites/__snapshots__/UserSetting.test.ts.snap b/tests/database/entites/__snapshots__/UserSetting.test.ts.snap new file mode 100644 index 0000000..1542d21 --- /dev/null +++ b/tests/database/entites/__snapshots__/UserSetting.test.ts.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`constructor EXPECT settings to be configured 1`] = ` +{ + "Id": Any<String>, + "Key": "key", + "UserId": "userId", + "Value": "value", + "WhenCreated": Any<Date>, + "WhenUpdated": Any<Date>, +} +`; diff --git a/tests/events/MemberEvents.test.ts b/tests/events/MemberEvents.test.ts deleted file mode 100644 index 2d4581a..0000000 --- a/tests/events/MemberEvents.test.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { GuildMember, TextChannel, User } from "discord.js"; -import MemberEvents from "../../src/events/MemberEvents"; -import GuildMemberUpdate from "../../src/events/MemberEvents/GuildMemberUpdate"; - -describe('GuildMemberAdd', () => { - test('When event is fired, expect embed to be sent to logs channel', async () => { - const currentDate = new Date(); - - const textChannel = { - send: jest.fn() - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const userDisplayAvatarURL = jest.fn(); - - const guildUser = { - tag: 'USERTAG', - createdAt: currentDate, - id: 'USERID', - displayAvatarURL: userDisplayAvatarURL - } as unknown as User; - - const guildMember = { - user: guildUser, - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - } - } as unknown as GuildMember; - - const memberEvents = new MemberEvents(); - - const result = await memberEvents.guildMemberAdd(guildMember); - - expect(textChannel.send).toBeCalledTimes(1); - expect(userDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe("Member Joined"); - expect(embed.footer?.text).toBe("Id: USERID"); - expect(embed.fields.length).toBe(2); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe("User"); - expect(embedFieldUser.value).toBe("[object Object] `USERTAG`"); - expect(embedFieldUser.inline).toBeTruthy(); - - // Embed -> Created Field - const embedFieldCreated = embed.fields[1]; - - expect(embedFieldCreated.name).toBe("Created"); - expect(embedFieldCreated.value).toBe(currentDate.toString()); - }); -}); - -describe('GuildMemberRemove', () => { - test('When event is fired, expect embed to be sent to logs channel', async () => { - const currentDate = new Date(); - - const textChannel = { - send: jest.fn() - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const userDisplayAvatarURL = jest.fn(); - - const guildUser = { - tag: 'USERTAG', - createdAt: currentDate, - id: 'USERID', - displayAvatarURL: userDisplayAvatarURL - } as unknown as User; - - const guildMember = { - user: guildUser, - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - joinedAt: currentDate - } as unknown as GuildMember; - - const memberEvents = new MemberEvents(); - - const result = await memberEvents.guildMemberRemove(guildMember); - - expect(textChannel.send).toBeCalledTimes(1); - expect(userDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe("Member Left"); - expect(embed.footer?.text).toBe("Id: USERID"); - expect(embed.fields.length).toBe(2); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe("User"); - expect(embedFieldUser.value).toBe("[object Object] `USERTAG`"); - expect(embedFieldUser.inline).toBeTruthy(); - - // Embed -> Joined Field - const embedFieldJoined = embed.fields[1]; - - expect(embedFieldJoined.name).toBe("Joined"); - expect(embedFieldJoined.value).toBe(currentDate.toString()); - }); -}); - -describe('GuildMemberUpdate', () => { - test('Given nicknames are the same, expect NicknameChanged NOT to be called', async () => { - const member = { - nickname: 'member' - } as unknown as GuildMember; - - const nicknameChanged = jest.fn(); - - GuildMemberUpdate.prototype.NicknameChanged = nicknameChanged; - - const memberEvents = new MemberEvents(); - - const result = await memberEvents.guildMemberUpdate(member, member); - - expect(result.embeds.length).toBe(0); - expect(nicknameChanged).not.toBeCalled(); - }); - - test('Given nicknames are the different, expect NicknameChanged to be called', async () => { - const oldMember = { - nickname: 'oldMember' - } as unknown as GuildMember; - - const newMember = { - nickname: 'newMember' - } as unknown as GuildMember; - - const nicknameChanged = jest.fn(); - - GuildMemberUpdate.prototype.NicknameChanged = nicknameChanged; - - const memberEvents = new MemberEvents(); - - const result = await memberEvents.guildMemberUpdate(oldMember, newMember); - - expect(result.embeds.length).toBe(0); - expect(nicknameChanged).toBeCalledTimes(1); - }); -}); \ No newline at end of file diff --git a/tests/events/MemberEvents/GuildMemberUpdate.test.ts b/tests/events/MemberEvents/GuildMemberUpdate.test.ts deleted file mode 100644 index 8acec92..0000000 --- a/tests/events/MemberEvents/GuildMemberUpdate.test.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { GuildMember, TextChannel } from "discord.js"; -import GuildMemberUpdate from "../../../src/events/MemberEvents/GuildMemberUpdate"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect properties are set', () => { - const oldMember = { - nickname: 'Old Nickname' - } as unknown as GuildMember; - - const newMember = { - nickname: 'New Nickname' - } as unknown as GuildMember; - - const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember); - - expect(guildMemberUpdate.oldMember).toBe(oldMember); - expect(guildMemberUpdate.newMember).toBe(newMember); - }); -}); - -describe('NicknameChanged', () => { - test('Given nickname has changed from one to another, expect embed to be sent with both', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const memberUserDisplayAvatarURL = jest.fn(); - - const oldMember = { - nickname: 'Old Nickname' - } as unknown as GuildMember; - - const newMember = { - nickname: 'New Nickname', - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - user: { - tag: 'USERTAG', - id: 'USERID', - displayAvatarURL: memberUserDisplayAvatarURL - } - } as unknown as GuildMember; - - const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember); - - const result = await guildMemberUpdate.NicknameChanged(); - - expect(channelSend).toBeCalledTimes(1); - expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); - expect(memberUserDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Nickname Changed'); - expect(embed.footer?.text).toBe('Id: USERID'); - expect(embed.fields.length).toBe(3); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe('User'); - expect(embedFieldUser.value).toBe('[object Object] `USERTAG`'); - - // Embed -> Before Field - const embedFieldBefore = embed.fields[1]; - - expect(embedFieldBefore.name).toBe('Before'); - expect(embedFieldBefore.value).toBe('Old Nickname'); - - // Embed -> After Field - const embedFieldAfter = embed.fields[2]; - - expect(embedFieldAfter.name).toBe('After'); - expect(embedFieldAfter.value).toBe('New Nickname'); - }); - - test('Given old nickname was null, expect embed to say old nickname was none', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const memberUserDisplayAvatarURL = jest.fn(); - - const oldMember = {} as unknown as GuildMember; - - const newMember = { - nickname: 'New Nickname', - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - user: { - tag: 'USERTAG', - id: 'USERID', - displayAvatarURL: memberUserDisplayAvatarURL - } - } as unknown as GuildMember; - - const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember); - - const result = await guildMemberUpdate.NicknameChanged(); - - expect(channelSend).toBeCalledTimes(1); - expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); - expect(memberUserDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Nickname Changed'); - expect(embed.footer?.text).toBe('Id: USERID'); - expect(embed.fields.length).toBe(3); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe('User'); - expect(embedFieldUser.value).toBe('[object Object] `USERTAG`'); - - // Embed -> Before Field - const embedFieldBefore = embed.fields[1]; - - expect(embedFieldBefore.name).toBe('Before'); - expect(embedFieldBefore.value).toBe('*none*'); - - // Embed -> After Field - const embedFieldAfter = embed.fields[2]; - - expect(embedFieldAfter.name).toBe('After'); - expect(embedFieldAfter.value).toBe('New Nickname'); - }); - - test('Given new nickname was null, expect embed to say new nickname was none', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const memberUserDisplayAvatarURL = jest.fn(); - - const oldMember = { - nickname: 'Old Nickname' - } as unknown as GuildMember; - - const newMember = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - user: { - tag: 'USERTAG', - id: 'USERID', - displayAvatarURL: memberUserDisplayAvatarURL - } - } as unknown as GuildMember; - - const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember); - - const result = await guildMemberUpdate.NicknameChanged(); - - expect(channelSend).toBeCalledTimes(1); - expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); - expect(memberUserDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Nickname Changed'); - expect(embed.footer?.text).toBe('Id: USERID'); - expect(embed.fields.length).toBe(3); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe('User'); - expect(embedFieldUser.value).toBe('[object Object] `USERTAG`'); - - // Embed -> Before Field - const embedFieldBefore = embed.fields[1]; - - expect(embedFieldBefore.name).toBe('Before'); - expect(embedFieldBefore.value).toBe('Old Nickname'); - - // Embed -> After Field - const embedFieldAfter = embed.fields[2]; - - expect(embedFieldAfter.name).toBe('After'); - expect(embedFieldAfter.value).toBe('*none*'); - }); -}); \ No newline at end of file diff --git a/tests/events/MessageEvents.test.ts b/tests/events/MessageEvents.test.ts deleted file mode 100644 index d509bdc..0000000 --- a/tests/events/MessageEvents.test.ts +++ /dev/null @@ -1,648 +0,0 @@ -import { Collection, Message, MessageAttachment, TextChannel } from "discord.js"; -import MessageEvents from "../../src/events/MessageEvents"; - -beforeEach(() => { - process.env = {}; -}); - -describe('MessageDelete', () => { - test('Given message was in a guild AND user was NOT a bot, expect message deleted embed to be sent', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const messageAttachments = new Collection<string, MessageAttachment>([ - [ - "0", - { - url: 'image0.png' - } as unknown as MessageAttachment - ], - [ - "1", - { - url: 'image1.png' - } as unknown as MessageAttachment - ] - ]); - - const message = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - author: { - bot: false, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - channel: {}, - content: 'Message Content', - attachments: messageAttachments - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageDelete(message); - - expect(channelSend).toBeCalledTimes(1); - expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Message Deleted'); - expect(embed.fields.length).toBe(4); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe('User'); - expect(embedFieldUser.value).toBe('[object Object] `USERTAG`'); - - // Embed -> Channel Field - const embedFieldChannel = embed.fields[1]; - - expect(embedFieldChannel.name).toBe('Channel'); - expect(embedFieldChannel.value).toBe('[object Object]'); - - // Embed -> Content Field - const embedFieldContent = embed.fields[2]; - - expect(embedFieldContent.name).toBe('Content'); - expect(embedFieldContent.value).toBe('```Message Content```'); - - // Embed -> Attachments Field - const embedFieldAttachments = embed.fields[3]; - - expect(embedFieldAttachments.name).toBe('Attachments'); - expect(embedFieldAttachments.value).toBe('```image0.png\nimage1.png```'); - }); - - test('Given message was not sent in a guild, expect execution stopped', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const messageAttachments = new Collection<string, MessageAttachment>([ - [ - "0", - { - url: 'image0.png' - } as unknown as MessageAttachment - ], - [ - "1", - { - url: 'image1.png' - } as unknown as MessageAttachment - ] - ]); - - const message = { - author: { - bot: false, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - channel: {}, - content: 'Message Content', - attachments: messageAttachments - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageDelete(message); - - expect(channelSend).not.toBeCalled(); - expect(memberGuildChannelsCacheFind).not.toBeCalled(); - expect(messageAuthorDisplayAvatarURL).not.toBeCalled(); - expect(result.embeds.length).toBe(0); - }); - - test('Given author is a bot, expect execution stopped', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const messageAttachments = new Collection<string, MessageAttachment>([ - [ - "0", - { - url: 'image0.png' - } as unknown as MessageAttachment - ], - [ - "1", - { - url: 'image1.png' - } as unknown as MessageAttachment - ] - ]); - - const message = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - author: { - bot: true, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - channel: {}, - content: 'Message Content', - attachments: messageAttachments - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageDelete(message); - - expect(channelSend).not.toBeCalled(); - expect(memberGuildChannelsCacheFind).not.toBeCalled(); - expect(messageAuthorDisplayAvatarURL).not.toBeCalled(); - expect(result.embeds.length).toBe(0); - }); - - test('Given message does not contain any attachments, expect attachments field to be omitted', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const messageAttachments = new Collection<string, MessageAttachment>([]); - - const message = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - author: { - bot: false, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - channel: {}, - content: 'Message Content', - attachments: messageAttachments - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageDelete(message); - - expect(channelSend).toBeCalledTimes(1); - expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Message Deleted'); - expect(embed.fields.length).toBe(3); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe('User'); - expect(embedFieldUser.value).toBe('[object Object] `USERTAG`'); - - // Embed -> Channel Field - const embedFieldChannel = embed.fields[1]; - - expect(embedFieldChannel.name).toBe('Channel'); - expect(embedFieldChannel.value).toBe('[object Object]'); - - // Embed -> Content Field - const embedFieldContent = embed.fields[2]; - - expect(embedFieldContent.name).toBe('Content'); - expect(embedFieldContent.value).toBe('```Message Content```'); - }); -}); - -describe('MessageUpdate', () => { - test('Given message is in a guild AND user is not a bot AND the content has actually changed, e xpect log embed to be sent', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const oldMessage = { - content: 'Old Message' - } as unknown as Message; - - const newMessage = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - author: { - bot: false, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - content: 'New Message', - channel: {}, - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageUpdate(oldMessage, newMessage); - - expect(channelSend).toBeCalledTimes(1); - expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Message Edited'); - expect(embed.fields.length).toBe(4); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe('User'); - expect(embedFieldUser.value).toBe('[object Object] `USERTAG`'); - expect(embedFieldUser.inline).toBeTruthy(); - - // Embed -> Channel Field - const embedFieldChannel = embed.fields[1]; - - expect(embedFieldChannel.name).toBe('Channel'); - expect(embedFieldChannel.value).toBe('[object Object]'); - expect(embedFieldChannel.inline).toBeTruthy(); - - // Embed -> Before Field - const embedFieldBefore = embed.fields[2]; - - expect(embedFieldBefore.name).toBe('Before'); - expect(embedFieldBefore.value).toBe('```Old Message```'); - - // Embed -> After Field - const embedFieldAfter = embed.fields[3]; - - expect(embedFieldAfter.name).toBe('After'); - expect(embedFieldAfter.value).toBe('```New Message```'); - }); - - test('Given message was not in a guild, expect execution stopped', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const oldMessage = { - content: 'Old Message' - } as unknown as Message; - - const newMessage = { - author: { - bot: false, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - content: 'New Message', - channel: {}, - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageUpdate(oldMessage, newMessage); - - expect(channelSend).not.toBeCalled(); - expect(memberGuildChannelsCacheFind).not.toBeCalled(); - expect(messageAuthorDisplayAvatarURL).not.toBeCalled(); - expect(result.embeds.length).toBe(0); - }); - - test('Given author is a bot, expect execution stopped', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const oldMessage = { - content: 'Old Message' - } as unknown as Message; - - const newMessage = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - author: { - bot: true, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - content: 'New Message', - channel: {}, - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageUpdate(oldMessage, newMessage); - - expect(channelSend).not.toBeCalled(); - expect(memberGuildChannelsCacheFind).not.toBeCalled(); - expect(messageAuthorDisplayAvatarURL).not.toBeCalled(); - expect(result.embeds.length).toBe(0); - }); - - test('Given the message contents are the same, expect execution stopped', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const oldMessage = { - content: 'Message' - } as unknown as Message; - - const newMessage = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - author: { - bot: false, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - content: 'Message', - channel: {}, - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageUpdate(oldMessage, newMessage); - - expect(channelSend).not.toBeCalled(); - expect(memberGuildChannelsCacheFind).not.toBeCalled(); - expect(messageAuthorDisplayAvatarURL).not.toBeCalled(); - expect(result.embeds.length).toBe(0); - }); - - test('Given Old Message did not have a content, expect field to account for this', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const oldMessage = {} as unknown as Message; - - const newMessage = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - author: { - bot: false, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - content: 'New Message', - channel: {}, - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageUpdate(oldMessage, newMessage); - - expect(channelSend).toBeCalledTimes(1); - expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Message Edited'); - expect(embed.fields.length).toBe(4); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe('User'); - expect(embedFieldUser.value).toBe('[object Object] `USERTAG`'); - expect(embedFieldUser.inline).toBeTruthy(); - - // Embed -> Channel Field - const embedFieldChannel = embed.fields[1]; - - expect(embedFieldChannel.name).toBe('Channel'); - expect(embedFieldChannel.value).toBe('[object Object]'); - expect(embedFieldChannel.inline).toBeTruthy(); - - // Embed -> Before Field - const embedFieldBefore = embed.fields[2]; - - expect(embedFieldBefore.name).toBe('Before'); - expect(embedFieldBefore.value).toBe('```*none*```'); - - // Embed -> After Field - const embedFieldAfter = embed.fields[3]; - - expect(embedFieldAfter.name).toBe('After'); - expect(embedFieldAfter.value).toBe('```New Message```'); - }); - - test('Given New Message does not have a content, expect field to account for this', async () => { - process.env = { - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const textChannel = { - name: 'mod-logs', - send: channelSend - } as unknown as TextChannel; - - const memberGuildChannelsCacheFind = jest.fn() - .mockReturnValue(textChannel); - const messageAuthorDisplayAvatarURL = jest.fn(); - - const oldMessage = { - content: 'Old Message' - } as unknown as Message; - - const newMessage = { - guild: { - channels: { - cache: { - find: memberGuildChannelsCacheFind - } - } - }, - author: { - bot: false, - displayAvatarURL: messageAuthorDisplayAvatarURL, - tag: 'USERTAG' - }, - channel: {}, - } as unknown as Message; - - const messageEvents = new MessageEvents(); - - const result = await messageEvents.messageUpdate(oldMessage, newMessage); - - expect(channelSend).toBeCalledTimes(1); - expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); - expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1); - expect(result.embeds.length).toBe(1); - - // Embed - const embed = result.embeds[0]; - - expect(embed.title).toBe('Message Edited'); - expect(embed.fields.length).toBe(4); - - // Embed -> User Field - const embedFieldUser = embed.fields[0]; - - expect(embedFieldUser.name).toBe('User'); - expect(embedFieldUser.value).toBe('[object Object] `USERTAG`'); - expect(embedFieldUser.inline).toBeTruthy(); - - // Embed -> Channel Field - const embedFieldChannel = embed.fields[1]; - - expect(embedFieldChannel.name).toBe('Channel'); - expect(embedFieldChannel.value).toBe('[object Object]'); - expect(embedFieldChannel.inline).toBeTruthy(); - - // Embed -> Before Field - const embedFieldBefore = embed.fields[2]; - - expect(embedFieldBefore.name).toBe('Before'); - expect(embedFieldBefore.value).toBe('```Old Message```'); - - // Embed -> After Field - const embedFieldAfter = embed.fields[3]; - - expect(embedFieldAfter.name).toBe('After'); - expect(embedFieldAfter.value).toBe('```*none*```'); - }); -}); \ No newline at end of file diff --git a/tests/helpers/StringTools.test.ts b/tests/helpers/StringTools.test.ts deleted file mode 100644 index ae6134b..0000000 --- a/tests/helpers/StringTools.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import StringTools from "../../src/helpers/StringTools"; - -describe('Capitalise', () => { - test('Expect sentence to be captilised', () => { - const inputString = 'the big brown fox jumps over the lazy dog'; - - const result = StringTools.Capitalise(inputString); - - expect(result).toBe('The Big Brown Fox Jumps Over The Lazy Dog'); - }); -}); \ No newline at end of file diff --git a/tests/helpers/embeds/ErrorEmbed.test.ts b/tests/helpers/embeds/ErrorEmbed.test.ts deleted file mode 100644 index 76f0b9b..0000000 --- a/tests/helpers/embeds/ErrorEmbed.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Message } from "discord.js"; -import { ICommandContext } from "../../../src/contracts/ICommandContext"; -import ErrorEmbed from "../../../src/helpers/embeds/ErrorEmbed"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - process.env = { - EMBED_COLOUR_ERROR: '0xd52803' - } - - const message = {} as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new ErrorEmbed(context, 'Error Message'); - - expect(errorEmbed.color?.toString()).toBe('13969411'); // 0xd52803 in decimal - expect(errorEmbed.description).toBe('Error Message'); - expect(errorEmbed.context).toBe(context); - }); -}); - -describe('SendToCurrentChannel', () => { - test('Expect embed to be sent to the current channel in context', () => { - process.env = { - EMBED_COLOUR_ERROR: '0xd52803' - } - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new ErrorEmbed(context, 'Error Message'); - - errorEmbed.SendToCurrentChannel(); - - expect(messageChannelSend).toBeCalledWith(errorEmbed); - }); -}); \ No newline at end of file diff --git a/tests/helpers/embeds/EventEmbed.test.ts b/tests/helpers/embeds/EventEmbed.test.ts deleted file mode 100644 index 7cb9251..0000000 --- a/tests/helpers/embeds/EventEmbed.test.ts +++ /dev/null @@ -1,292 +0,0 @@ -import { Guild, Message, TextChannel, User } from "discord.js"; -import { ICommandContext } from "../../../src/contracts/ICommandContext"; -import EventEmbed from "../../../src/helpers/embeds/EventEmbed"; -import SettingsHelper from "../../../src/helpers/SettingsHelper"; - -beforeEach(() => { - process.env = {}; - jest.resetAllMocks(); -}); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - const guild = {} as unknown as Guild; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - expect(errorEmbed.color?.toString()).toBe('3166394'); // 0x3050ba in decimal - expect(errorEmbed.title).toBe('Event Message'); - expect(errorEmbed.guild).toBe(guild); - }); -}); - -describe('AddUser', () => { - test('Given setThumbnail is false, add field WITHOUT user thumbnail', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const addField = jest.fn(); - const setThumbnail = jest.fn(); - - const guild = {} as unknown as Guild; - - const user = { - tag: 'USERTAG' - } as unknown as User; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.addField = addField; - errorEmbed.setThumbnail = setThumbnail; - - errorEmbed.AddUser('User', user); - - expect(addField).toBeCalledWith('User', '[object Object] `USERTAG`', true); - expect(setThumbnail).not.toBeCalled(); - }); - - test('Given setThumbnail is true, add field WITH user thumbnail', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const addField = jest.fn(); - const setThumbnail = jest.fn(); - const displayAvatarURL = jest.fn() - .mockReturnValue('image0.png'); - - const guild = {} as unknown as Guild; - - const user = { - tag: 'USERTAG', - displayAvatarURL: displayAvatarURL - } as unknown as User; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.addField = addField; - errorEmbed.setThumbnail = setThumbnail; - - errorEmbed.AddUser('User', user, true); - - expect(addField).toBeCalledWith('User', '[object Object] `USERTAG`', true); - expect(setThumbnail).toBeCalledWith('image0.png'); - expect(displayAvatarURL).toBeCalled(); - }); -}); - -describe('AddReason', () => { - test('Given a non-empty string is supplied, expect field with message', () => { - const guild = {} as Guild; - - const eventEmbed = new EventEmbed(guild, "Event Embed"); - - eventEmbed.addField = jest.fn(); - - eventEmbed.AddReason("Test reason"); - - expect(eventEmbed.addField).toBeCalledWith("Reason", "Test reason"); - }); - - test('Given an empty string is supplied, expect field with default message', () => { - const guild = {} as Guild; - - const eventEmbed = new EventEmbed(guild, "Event Embed"); - - eventEmbed.addField = jest.fn(); - - eventEmbed.AddReason(""); - - expect(eventEmbed.addField).toBeCalledWith("Reason", "*none*"); - }); -}); - -describe('SendToChannel', () => { - test('Given channel can be found, expect embed to be sent to that channel', () => { - const channelSend = jest.fn(); - - const channel = { - send: channelSend - } as unknown as TextChannel; - - const guildChannelsCacheFind = jest.fn() - .mockReturnValue(channel); - - const guild = { - channels: { - cache: { - find: guildChannelsCacheFind - } - } - } as unknown as Guild; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.SendToChannel('channel-name'); - - expect(guildChannelsCacheFind).toBeCalledTimes(1); - expect(channelSend).toBeCalledWith(errorEmbed); - }); - - test('Given channel can NOT be found, expect error logged', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const guildChannelsCacheFind = jest.fn() - .mockReturnValue(null); - - const guild = { - channels: { - cache: { - find: guildChannelsCacheFind - } - } - } as unknown as Guild; - - console.error = jest.fn(); - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.SendToChannel('channel-name'); - - expect(guildChannelsCacheFind).toBeCalledTimes(1); - expect(console.error).toBeCalledWith('Unable to find channel channel-name'); - }); -}); - -describe('SendToMessageLogsChannel', () => { - test('Given setting is set, expect SendToChannel to be called with value', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue("message-logs"); - - const guild = { - id: "guildId" - } as unknown as Guild; - - SettingsHelper.GetSetting = getSetting; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.SendToChannel = sendToChannel; - - await errorEmbed.SendToMessageLogsChannel(); - - expect(sendToChannel).toBeCalledWith('message-logs'); - expect(getSetting).toBeCalledWith("channels.logs.message", "guildId"); - }); - - test('Given setting is not set, expect function to return', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue(undefined); - - const guild = { - id: "guildId" - } as unknown as Guild; - - SettingsHelper.GetSetting = getSetting; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.SendToChannel = sendToChannel; - - await errorEmbed.SendToMessageLogsChannel(); - - expect(sendToChannel).not.toBeCalled(); - expect(getSetting).toBeCalledWith("channels.logs.message", "guildId"); - }); -}); - -describe('SendToMemberLogsChannel', () => { - test('Given setting is set, expect SendToChannel to be called with value', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue("member-logs"); - - const guild = { - id: "guildId" - } as unknown as Guild; - - SettingsHelper.GetSetting = getSetting; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.SendToChannel = sendToChannel; - - await errorEmbed.SendToMemberLogsChannel(); - - expect(sendToChannel).toBeCalledWith('member-logs'); - expect(getSetting).toBeCalledWith("channels.logs.member", "guildId"); - }); - - test('Given setting is not set, expect function to return', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue(undefined); - - const guild = { - id: "guildId" - } as unknown as Guild; - - SettingsHelper.GetSetting = getSetting; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.SendToChannel = sendToChannel; - - await errorEmbed.SendToMemberLogsChannel(); - - expect(sendToChannel).not.toBeCalled(); - expect(getSetting).toBeCalledWith("channels.logs.member", "guildId"); - }); -}); - -describe('SendToModLogsChannel', () => { - test('Given setting is set, expect SendToChannel to be called with value', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue("mod-logs"); - - const guild = { - id: "guildId" - } as unknown as Guild; - - SettingsHelper.GetSetting = getSetting; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.SendToChannel = sendToChannel; - - await errorEmbed.SendToModLogsChannel(); - - expect(sendToChannel).toBeCalledWith('mod-logs'); - expect(getSetting).toBeCalledWith("channels.logs.mod", "guildId"); - }); - - test('Given setting is not set, expect function to return', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue(undefined); - - const guild = { - id: "guildId" - } as unknown as Guild; - - SettingsHelper.GetSetting = getSetting; - - const errorEmbed = new EventEmbed(guild, 'Event Message'); - - errorEmbed.SendToChannel = sendToChannel; - - await errorEmbed.SendToModLogsChannel(); - - expect(sendToChannel).not.toBeCalled(); - expect(getSetting).toBeCalledWith("channels.logs.mod", "guildId"); - }); -}); \ No newline at end of file diff --git a/tests/helpers/embeds/LogEmbed.test.ts b/tests/helpers/embeds/LogEmbed.test.ts deleted file mode 100644 index 4bad4ef..0000000 --- a/tests/helpers/embeds/LogEmbed.test.ts +++ /dev/null @@ -1,407 +0,0 @@ -import { Guild, Message, TextChannel, User } from "discord.js"; -import { ICommandContext } from "../../../src/contracts/ICommandContext"; -import LogEmbed from "../../../src/helpers/embeds/LogEmbed"; -import SettingsHelper from "../../../src/helpers/SettingsHelper"; - -beforeEach(() => { - process.env = {}; - jest.resetAllMocks(); -}); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new LogEmbed(context, 'Log Message'); - - expect(errorEmbed.color?.toString()).toBe('3166394'); // 0x3050ba in decimal - expect(errorEmbed.title).toBe('Log Message'); - expect(errorEmbed.context).toBe(context); - }); -}); - -describe('AddUser', () => { - test('Given setThumbnail is false, add field WITHOUT user thumbnail', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const addField = jest.fn(); - const setThumbnail = jest.fn(); - - const user = { - tag: 'USERTAG' - } as unknown as User; - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - author: user - } as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new LogEmbed(context, 'Event Message'); - - errorEmbed.addField = addField; - errorEmbed.setThumbnail = setThumbnail; - - errorEmbed.AddUser('User', user); - - expect(addField).toBeCalledWith('User', '[object Object] `USERTAG`', true); - expect(setThumbnail).not.toBeCalled(); - }); - - test('Given setThumbnail is true, add field WITH user thumbnail', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const addField = jest.fn(); - const setThumbnail = jest.fn(); - const displayAvatarURL = jest.fn() - .mockReturnValue('image0.png'); - - const user = { - tag: 'USERTAG', - displayAvatarURL: displayAvatarURL - } as unknown as User; - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - author: user - } as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new LogEmbed(context, 'Event Message'); - - errorEmbed.addField = addField; - errorEmbed.setThumbnail = setThumbnail; - - errorEmbed.AddUser('User', user, true); - - expect(addField).toBeCalledWith('User', '[object Object] `USERTAG`', true); - expect(setThumbnail).toBeCalledWith('image0.png'); - expect(displayAvatarURL).toBeCalled(); - }); -}); - -describe('SendToChannel', () => { - test('Given channel can be found, expect embed to be sent to that channel', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const channelSend = jest.fn(); - - const channel = { - send: channelSend - } as unknown as TextChannel; - - const guildChannelsCacheFind = jest.fn() - .mockReturnValue(channel); - - const guild = { - channels: { - cache: { - find: guildChannelsCacheFind - } - } - } as unknown as Guild; - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - guild: guild - } as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new LogEmbed(context, 'Event Message'); - - errorEmbed.SendToChannel('channel-name'); - - expect(guildChannelsCacheFind).toBeCalledTimes(1); - expect(channelSend).toBeCalledWith(errorEmbed); - }); - - test('Given channel can NOT be found, expect error logged', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const guildChannelsCacheFind = jest.fn() - .mockReturnValue(null); - - const guild = { - channels: { - cache: { - find: guildChannelsCacheFind - } - } - } as unknown as Guild; - - console.error = jest.fn(); - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - }, - guild: guild - } as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new LogEmbed(context, 'Event Message'); - - errorEmbed.SendToChannel('channel-name'); - - expect(guildChannelsCacheFind).toBeCalledTimes(1); - }); -}); - -describe('SendToMessageLogsChannel', () => { - test('Given setting is set, expect SendToChannel to be called with value', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue("message-logs"); - - const guild = { - id: "guildId" - } as unknown as Guild; - - const message = { - guild: guild - } as Message; - - const context: ICommandContext = { - name: 'log', - args: [], - message: message - }; - - SettingsHelper.GetSetting = getSetting; - - const logEmbed = new LogEmbed(context, 'Event Message'); - - logEmbed.SendToChannel = sendToChannel; - - await logEmbed.SendToMessageLogsChannel(); - - expect(sendToChannel).toBeCalledWith('message-logs'); - expect(getSetting).toBeCalledWith("channels.logs.message", "guildId"); - }); - - test('Given setting is not set, expect function to return', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue(undefined); - - const guild = { - id: "guildId" - } as unknown as Guild; - - const message = { - guild: guild - } as Message; - - const context: ICommandContext = { - name: 'log', - args: [], - message: message - }; - - SettingsHelper.GetSetting = getSetting; - - const logEmbed = new LogEmbed(context, 'Event Message'); - - logEmbed.SendToChannel = sendToChannel; - - await logEmbed.SendToMessageLogsChannel(); - - expect(sendToChannel).not.toBeCalled(); - expect(getSetting).toBeCalledWith("channels.logs.message", "guildId"); - }); -}); - -describe('SendToMemberLogsChannel', () => { - test('Given setting is set, expect SendToChannel to be called with value', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue("member-logs"); - - const guild = { - id: "guildId" - } as unknown as Guild; - - const message = { - guild: guild - } as Message; - - const context: ICommandContext = { - name: 'log', - args: [], - message: message - }; - - SettingsHelper.GetSetting = getSetting; - - const logEmbed = new LogEmbed(context, 'Event Message'); - - logEmbed.SendToChannel = sendToChannel; - - await logEmbed.SendToMemberLogsChannel(); - - expect(sendToChannel).toBeCalledWith('member-logs'); - expect(getSetting).toBeCalledWith("channels.logs.member", "guildId"); - }); - - test('Given setting is not set, expect function to return', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue(undefined); - - const guild = { - id: "guildId" - } as unknown as Guild; - - const message = { - guild: guild - } as Message; - - const context: ICommandContext = { - name: 'log', - args: [], - message: message - }; - - SettingsHelper.GetSetting = getSetting; - - const logEmbed = new LogEmbed(context, 'Event Message'); - - logEmbed.SendToChannel = sendToChannel; - - await logEmbed.SendToMemberLogsChannel(); - - expect(sendToChannel).not.toBeCalled(); - expect(getSetting).toBeCalledWith("channels.logs.member", "guildId"); - }); -}); - -describe('SendToModLogsChannel', () => { - test('Given setting is set, expect SendToChannel to be called with value', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue("mod-logs"); - - const guild = { - id: "guildId" - } as unknown as Guild; - - const message = { - guild: guild - } as Message; - - const context: ICommandContext = { - name: 'log', - args: [], - message: message - }; - - SettingsHelper.GetSetting = getSetting; - - const logEmbed = new LogEmbed(context, 'Event Message'); - - logEmbed.SendToChannel = sendToChannel; - - await logEmbed.SendToModLogsChannel(); - - expect(sendToChannel).toBeCalledWith('mod-logs'); - expect(getSetting).toBeCalledWith("channels.logs.mod", "guildId"); - }); - - test('Given setting is not set, expect function to return', async () => { - const sendToChannel = jest.fn(); - const getSetting = jest.fn().mockResolvedValue(undefined); - - const guild = { - id: "guildId" - } as unknown as Guild; - - const message = { - guild: guild - } as Message; - - const context: ICommandContext = { - name: 'log', - args: [], - message: message - }; - - SettingsHelper.GetSetting = getSetting; - - const logEmbed = new LogEmbed(context, 'Event Message'); - - logEmbed.SendToChannel = sendToChannel; - - await logEmbed.SendToModLogsChannel(); - - expect(sendToChannel).not.toBeCalled(); - expect(getSetting).toBeCalledWith("channels.logs.mod", "guildId"); - }); -}); \ No newline at end of file diff --git a/tests/helpers/embeds/PublicEmbed.test.ts b/tests/helpers/embeds/PublicEmbed.test.ts deleted file mode 100644 index 6b45797..0000000 --- a/tests/helpers/embeds/PublicEmbed.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Guild, Message, TextChannel, User } from "discord.js"; -import { ICommandContext } from "../../../src/contracts/ICommandContext"; -import PublicEmbed from "../../../src/helpers/embeds/PublicEmbed"; - -beforeEach(() => { - process.env = {}; -}); - -describe('Constructor', () => { - test('Expect properties to be set', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new PublicEmbed(context, 'Log Message', 'Log Description'); - - expect(errorEmbed.color?.toString()).toBe('13969411'); // 0xd52803 in decimal - expect(errorEmbed.title).toBe('Log Message'); - expect(errorEmbed.description).toBe('Log Description'); - expect(errorEmbed.context).toBe(context); - }); -}); - -describe('SendToCurrentChannel', () => { - test('Expect embed to be sent to the current channel in context', () => { - process.env = { - EMBED_COLOUR_ERROR: '0xd52803' - } - - const messageChannelSend = jest.fn(); - - const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; - - const context: ICommandContext = { - name: 'command', - args: [], - message: message - }; - - const errorEmbed = new PublicEmbed(context, 'Message', 'Description'); - - errorEmbed.SendToCurrentChannel(); - - expect(messageChannelSend).toBeCalledWith(errorEmbed); - }); -}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c188b92..1baacfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,160 +2,122 @@ # yarn lockfile v1 -"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" - integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: - "@babel/highlight" "^7.16.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" -"@babel/compat-data@^7.16.0": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" - integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== - -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.7.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.5.tgz#924aa9e1ae56e1e55f7184c8bf073a50d8677f5c" - integrity sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.0.tgz#9374b5cd068d128dac0b94ff482594273b1c2815" + integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g== dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.5" - "@babel/helper-compilation-targets" "^7.16.3" - "@babel/helper-module-transforms" "^7.16.5" - "@babel/helpers" "^7.16.5" - "@babel/parser" "^7.16.5" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.5" - "@babel/types" "^7.16.0" - convert-source-map "^1.7.0" + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/compat-data@^7.25.9": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.0.tgz#f02ba6d34e88fadd5e8861e8b38902f43cc1c819" + integrity sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" + json5 "^2.2.3" + semver "^6.3.1" -"@babel/generator@^7.16.5", "@babel/generator@^7.7.2": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.5.tgz#26e1192eb8f78e0a3acaf3eede3c6fc96d22bedf" - integrity sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA== +"@babel/generator@^7.25.9", "@babel/generator@^7.26.0", "@babel/generator@^7.7.2": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.0.tgz#505cc7c90d92513f458a477e5ef0703e7c91b8d7" + integrity sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w== dependencies: - "@babel/types" "^7.16.0" - jsesc "^2.5.1" - source-map "^0.5.0" + "@babel/parser" "^7.26.0" + "@babel/types" "^7.26.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" -"@babel/helper-compilation-targets@^7.16.3": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz#5b480cd13f68363df6ec4dc8ac8e2da11363cbf0" - integrity sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA== +"@babel/helper-compilation-targets@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" + integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== dependencies: - "@babel/compat-data" "^7.16.0" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.17.5" - semver "^6.3.0" + "@babel/compat-data" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" -"@babel/helper-environment-visitor@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz#f6a7f38b3c6d8b07c88faea083c46c09ef5451b8" - integrity sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg== +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== dependencies: - "@babel/types" "^7.16.0" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-function-name@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" - integrity sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog== +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== dependencies: - "@babel/helper-get-function-arity" "^7.16.0" - "@babel/template" "^7.16.0" - "@babel/types" "^7.16.0" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/helper-get-function-arity@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" - integrity sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" + integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helpers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== dependencies: - "@babel/types" "^7.16.0" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.0" -"@babel/helper-hoist-variables@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" - integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0": + version "7.26.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.1.tgz#44e02499960df2cdce2c456372a3e8e0c3c5c975" + integrity sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw== dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-module-imports@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz#90538e60b672ecf1b448f5f4f5433d37e79a3ec3" - integrity sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-module-transforms@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz#530ebf6ea87b500f60840578515adda2af470a29" - integrity sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ== - dependencies: - "@babel/helper-environment-visitor" "^7.16.5" - "@babel/helper-module-imports" "^7.16.0" - "@babel/helper-simple-access" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.5" - "@babel/types" "^7.16.0" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.5", "@babel/helper-plugin-utils@^7.8.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz#afe37a45f39fce44a3d50a7958129ea5b1a5c074" - integrity sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ== - -"@babel/helper-simple-access@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz#21d6a27620e383e37534cf6c10bba019a6f90517" - integrity sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-split-export-declaration@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" - integrity sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-validator-identifier@^7.15.7": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== - -"@babel/helpers@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.5.tgz#29a052d4b827846dd76ece16f565b9634c554ebd" - integrity sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw== - dependencies: - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.5" - "@babel/types" "^7.16.0" - -"@babel/highlight@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" - integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.0", "@babel/parser@^7.16.5", "@babel/parser@^7.7.2": - version "7.16.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.6.tgz#8f194828193e8fa79166f34a4b4e52f3e769a314" - integrity sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ== + "@babel/types" "^7.26.0" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -171,14 +133,28 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-import-attributes@^7.24.7": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -192,7 +168,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -206,7 +189,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -234,7 +217,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== @@ -242,65 +232,128 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.5.tgz#f47a33e4eee38554f00fb6b2f894fa1f5649b0b3" - integrity sha512-/d4//lZ1Vqb4mZ5xTep3dDK888j7BGM/iKqBmndBaoYAFPlPKrGU608VVBz5JeyAb6YQDjRu1UKqj86UhwWVgw== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/template@^7.16.0", "@babel/template@^7.3.3": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" - integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A== +"@babel/template@^7.25.9", "@babel/template@^7.3.3": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/parser" "^7.16.0" - "@babel/types" "^7.16.0" + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.16.5", "@babel/traverse@^7.7.2": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.5.tgz#d7d400a8229c714a59b87624fc67b0f1fbd4b2b3" - integrity sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ== +"@babel/traverse@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84" + integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.5" - "@babel/helper-environment-visitor" "^7.16.5" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-hoist-variables" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/parser" "^7.16.5" - "@babel/types" "^7.16.0" - debug "^4.1.0" + "@babel/code-frame" "^7.25.9" + "@babel/generator" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/template" "^7.25.9" + "@babel/types" "^7.25.9" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" - integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.3.3": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" + integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@discordjs/builders@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.11.0.tgz#4102abe3e0cd093501f3f71931df43eb92f5b0cc" - integrity sha512-ZTB8yJdJKrKlq44dpWkNUrAtEJEq0gqpb7ASdv4vmq6/mZal5kOv312hQ56I/vxwMre+VIkoHquNUAfnTbiYtg== +"@discordjs/builders@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.9.0.tgz#71fa6de91132bd1deaff2a9daea7aa5d5c9f124a" + integrity sha512-0zx8DePNVvQibh5ly5kCEei5wtPBIUbSoE9n+91Rlladz4tgtFbJ36PZMxxZrTEOQ7AHMZ/b0crT/0fCy6FTKg== dependencies: - "@sindresorhus/is" "^4.2.0" - discord-api-types "^0.26.0" - ts-mixer "^6.0.0" - tslib "^2.3.1" - zod "^3.11.6" + "@discordjs/formatters" "^0.5.0" + "@discordjs/util" "^1.1.1" + "@sapphire/shapeshift" "^4.0.0" + discord-api-types "0.37.97" + fast-deep-equal "^3.1.3" + ts-mixer "^6.0.4" + tslib "^2.6.3" -"@discordjs/collection@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.4.0.tgz#b6488286a1cc7b41b644d7e6086f25a1c1e6f837" - integrity sha512-zmjq+l/rV35kE6zRrwe8BHqV78JvIh2ybJeZavBi5NySjWXqN3hmmAKg7kYMMXSeiWtSsMoZ/+MQi0DiQWy2lw== +"@discordjs/collection@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.3.tgz#5a1250159ebfff9efa4f963cfa7e97f1b291be18" + integrity sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ== + +"@discordjs/collection@^2.1.0", "@discordjs/collection@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-2.1.1.tgz#901917bc538c12b9c3613036d317847baee08cae" + integrity sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg== + +"@discordjs/formatters@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.5.0.tgz#2d284c4271bc41984339936df1d0164e470f3b7a" + integrity sha512-98b3i+Y19RFq1Xke4NkVY46x8KjJQjldHUuEbCqMvp1F5Iq9HgnGpu91jOi/Ufazhty32eRsKnnzS8n4c+L93g== + dependencies: + discord-api-types "0.37.97" + +"@discordjs/rest@^2.0.0", "@discordjs/rest@^2.3.0", "@discordjs/rest@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.4.0.tgz#63bfc816af58af844914e3589d7eae609cd199b5" + integrity sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw== + dependencies: + "@discordjs/collection" "^2.1.1" + "@discordjs/util" "^1.1.1" + "@sapphire/async-queue" "^1.5.3" + "@sapphire/snowflake" "^3.5.3" + "@vladfrangu/async_event_emitter" "^2.4.6" + discord-api-types "0.37.97" + magic-bytes.js "^1.10.0" + tslib "^2.6.3" + undici "6.19.8" + +"@discordjs/util@^1.1.0", "@discordjs/util@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.1.1.tgz#bafcde0faa116c834da1258d78ec237080bbab29" + integrity sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g== + +"@discordjs/ws@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-1.1.1.tgz#bffbfd46838258ab09054ed98ddef1a36f6507a3" + integrity sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA== + dependencies: + "@discordjs/collection" "^2.1.0" + "@discordjs/rest" "^2.3.0" + "@discordjs/util" "^1.1.0" + "@sapphire/async-queue" "^1.5.2" + "@types/ws" "^8.5.10" + "@vladfrangu/async_event_emitter" "^2.2.4" + discord-api-types "0.37.83" + tslib "^2.6.2" + ws "^8.16.0" + +"@inquirer/figures@^1.0.3": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.7.tgz#d050ccc0eabfacc0248c4ff647a9dfba1b01594b" + integrity sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -313,339 +366,435 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" -"@istanbuljs/schema@^0.1.2": +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.4.2.tgz#7a95612d38c007ddb528ee446fe5e5e785e685ce" - integrity sha512-xknHThRsPB/To1FUbi6pCe43y58qFC03zfb6R7fDb/FfC7k2R3i1l+izRBJf8DI46KhYGRaF14Eo9A3qbBoixg== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.4.2" - jest-util "^27.4.2" + jest-message-util "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" -"@jest/core@^27.4.5": - version "27.4.5" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.5.tgz#cae2dc34259782f4866c6606c3b480cce920ed4c" - integrity sha512-3tm/Pevmi8bDsgvo73nX8p/WPng6KWlCyScW10FPEoN1HU4pwI83tJ3TsFvi1FfzsjwUlMNEPowgb/rPau/LTQ== +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: - "@jest/console" "^27.4.2" - "@jest/reporters" "^27.4.5" - "@jest/test-result" "^27.4.2" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.8.1" + ci-info "^3.2.0" exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^27.4.2" - jest-config "^27.4.5" - jest-haste-map "^27.4.5" - jest-message-util "^27.4.2" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.5" - jest-resolve-dependencies "^27.4.5" - jest-runner "^27.4.5" - jest-runtime "^27.4.5" - jest-snapshot "^27.4.5" - jest-util "^27.4.2" - jest-validate "^27.4.2" - jest-watcher "^27.4.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" micromatch "^4.0.4" - rimraf "^3.0.0" + pretty-format "^29.7.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.4.4": - version "27.4.4" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.4.4.tgz#66ebebc79673d84aad29d2bb70a8c51e6c29bb4d" - integrity sha512-q+niMx7cJgt/t/b6dzLOh4W8Ef/8VyKG7hxASK39jakijJzbFBGpptx3RXz13FFV7OishQ9lTbv+dQ5K3EhfDQ== +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: - "@jest/fake-timers" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^27.4.2" + jest-mock "^29.7.0" -"@jest/fake-timers@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.4.2.tgz#d217f86c3ba2027bf29e0b731fd0cb761a72d093" - integrity sha512-f/Xpzn5YQk5adtqBgvw1V6bF8Nx3hY0OIRRpCvWcfPl0EAjdqWPdhH3t/3XpiWZqtjIEHDyMKP9ajpva1l4Zmg== +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: - "@jest/types" "^27.4.2" - "@sinonjs/fake-timers" "^8.0.1" + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" + +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^27.4.2" - jest-mock "^27.4.2" - jest-util "^27.4.2" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -"@jest/globals@^27.4.4": - version "27.4.4" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.4.4.tgz#fe501a80c23ea2dab585c42be2a519bb5e38530d" - integrity sha512-bqpqQhW30BOreXM8bA8t8JbOQzsq/WnPTnBl+It3UxAD9J8yxEAaBEylHx1dtBapAr/UBk8GidXbzmqnee8tYQ== +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: - "@jest/environment" "^27.4.4" - "@jest/types" "^27.4.2" - expect "^27.4.2" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" -"@jest/reporters@^27.4.5": - version "27.4.5" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.4.5.tgz#e229acca48d18ea39e805540c1c322b075ae63ad" - integrity sha512-3orsG4vi8zXuBqEoy2LbnC1kuvkg1KQUgqNxmxpQgIOQEPeV0onvZu+qDQnEoX8qTQErtqn/xzcnbpeTuOLSiA== +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.4.2" - "@jest/test-result" "^27.4.2" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" - glob "^7.1.2" - graceful-fs "^4.2.4" + glob "^7.1.3" + graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" + istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^27.4.5" - jest-resolve "^27.4.5" - jest-util "^27.4.2" - jest-worker "^27.4.5" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" slash "^3.0.0" - source-map "^0.6.0" string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" -"@jest/source-map@^27.4.0": - version "27.4.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6" - integrity sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" - graceful-fs "^4.2.4" - source-map "^0.6.0" + graceful-fs "^4.2.9" -"@jest/test-result@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.4.2.tgz#05fd4a5466ec502f3eae0b39dff2b93ea4d5d9ec" - integrity sha512-kr+bCrra9jfTgxHXHa2UwoQjxvQk3Am6QbpAiJ5x/50LW8llOYrxILkqY0lZRW/hu8FXesnudbql263+EW9iNA== +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: - "@jest/console" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.4.5": - version "27.4.5" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.4.5.tgz#1d7e026844d343b60d2ca7fd82c579a17b445d7d" - integrity sha512-n5woIn/1v+FT+9hniymHPARA9upYUmfi5Pw9ewVwXCDlK4F5/Gkees9v8vdjGdAIJ2MPHLHodiajLpZZanWzEQ== +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: - "@jest/test-result" "^27.4.2" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.5" - jest-runtime "^27.4.5" - -"@jest/transform@^27.4.5": - version "27.4.5" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.4.5.tgz#3dfe2e3680cd4aa27356172bf25617ab5b94f195" - integrity sha512-PuMet2UlZtlGzwc6L+aZmR3I7CEBpqadO03pU40l2RNY2fFJ191b9/ITB44LNOhVtsyykx0OZvj0PCyuLm7Eew== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.4.2" - babel-plugin-istanbul "^6.0.0" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.5" - jest-regex-util "^27.4.0" - jest-util "^27.4.2" - micromatch "^4.0.4" - pirates "^4.0.1" + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" -"@jest/types@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5" - integrity sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg== +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" - "@types/yargs" "^16.0.0" + "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@sapphire/async-queue@^1.1.9": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.1.tgz#9d861e626dbffae02d808e13f823d4510e450a78" - integrity sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g== +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" -"@sindresorhus/is@^4.0.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.2.0.tgz#667bfc6186ae7c9e0b45a08960c551437176e1ca" - integrity sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@sindresorhus/is@^4.2.0": +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@pnpm/config.env-replace@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz#ab29da53df41e8948a00f2433f085f54de8b3a4c" + integrity sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w== + +"@pnpm/network.ca-file@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz#2ab05e09c1af0cdf2fcf5035bea1484e222f7983" + integrity sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA== + dependencies: + graceful-fs "4.2.10" + +"@pnpm/npm-conf@^2.1.0": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz#bb375a571a0bd63ab0a23bece33033c683e9b6b0" + integrity sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw== + dependencies: + "@pnpm/config.env-replace" "^1.1.0" + "@pnpm/network.ca-file" "^1.0.1" + config-chain "^1.1.11" + +"@samverschueren/stream-to-observable@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" + integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ== + dependencies: + any-observable "^0.3.0" + +"@sapphire/async-queue@^1.5.2", "@sapphire/async-queue@^1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.3.tgz#03cd2a2f3665068f314736bdc56eee2025352422" + integrity sha512-x7zadcfJGxFka1Q3f8gCts1F0xMwCKbZweM85xECGI0hBTeIZJGGCrHgLggihBoprlQ/hBmDR5LKfIPqnmHM3w== + +"@sapphire/shapeshift@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz#86c1b41002ff5d0b2ad21cbc3418b06834b89040" + integrity sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg== + dependencies: + fast-deep-equal "^3.1.3" + lodash "^4.17.21" + +"@sapphire/snowflake@3.5.3", "@sapphire/snowflake@^3.5.3": + version "3.5.3" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.3.tgz#0c102aa2ec5b34f806e9bc8625fc6a5e1d0a0c6a" + integrity sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ== + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sindresorhus/is@4.6.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^3.0.0" -"@sqltools/formatter@^1.2.2": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.3.tgz#1185726610acc37317ddab11c3c7f9066966bd20" - integrity sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg== +"@sqltools/formatter@^1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12" + integrity sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw== -"@szmarczak/http-timer@^4.0.5": +"@szmarczak/http-timer@4.0.6": version "4.0.6" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== dependencies: defer-to-connect "^2.0.0" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.17" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.17.tgz#f50ac9d20d64153b510578d84f9643f9a3afbe64" - integrity sha512-6zzkezS9QEIL8yCBvXWxPTJPNuMeECJVxSOhxNY/jfq9LxOTHivaYTqr37n9LknWWRTIkzqH2UilS5QFvfa90A== +"@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" "@types/babel__generator" "*" "@types/babel__template" "*" "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.3" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.3.tgz#f456b4b2ce79137f768aa130d2423d2f0ccfaba5" - integrity sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA== + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.4.1" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" - integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" - integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== dependencies: - "@babel/types" "^7.3.0" + "@babel/types" "^7.20.7" -"@types/cacheable-request@^6.0.1": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" - integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "*" - "@types/node" "*" - "@types/responselike" "*" - -"@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== +"@types/graceful-fs@^4.1.3": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: "@types/node" "*" -"@types/http-cache-semantics@*": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" - integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^27.0.3": - version "27.0.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.3.tgz#0cf9dfe9009e467f70a342f0f94ead19842a783a" - integrity sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg== +"@types/jest@^29.5.12": + version "29.5.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== dependencies: - jest-diff "^27.0.0" - pretty-format "^27.0.0" + expect "^29.0.0" + pretty-format "^29.0.0" -"@types/keyv@*": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.3.tgz#1c9aae32872ec1f20dcdaee89a9f3ba88f465e41" - integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== - dependencies: - "@types/node" "*" - -"@types/node-fetch@^2.5.12": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" - integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== - dependencies: - "@types/node" "*" - form-data "^3.0.0" +"@types/luxon@~3.4.0": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" + integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== "@types/node@*": - version "16.11.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.11.tgz#6ea7342dfb379ea1210835bada87b3c512120234" - integrity sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw== + version "22.7.5" + resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== + dependencies: + undici-types "~6.19.2" -"@types/node@^16.11.10": - version "16.11.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.10.tgz#2e3ad0a680d96367103d3e670d41c2fed3da61ae" - integrity sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA== +"@types/node@^22.0.0": + version "22.8.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.1.tgz#b39d4b98165e2ae792ce213f610c7c6108ccfa16" + integrity sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg== + dependencies: + undici-types "~6.19.8" -"@types/prettier@^2.1.5": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281" - integrity sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA== +"@types/normalize-package-data@^2.4.3": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== -"@types/responselike@*", "@types/responselike@^1.0.0": +"@types/responselike@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== @@ -653,86 +802,103 @@ "@types/node" "*" "@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== -"@types/uuid@^8.3.4": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/uuid@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== -"@types/ws@^8.2.2": - version "8.5.3" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" - integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== +"@types/ws@^8.5.10": + version "8.5.12" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" + integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== dependencies: "@types/node" "*" "@types/yargs-parser@*": - version "20.2.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" - integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== -"@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" -"@types/zen-observable@0.8.3": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3" - integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== +"@vladfrangu/async_event_emitter@^2.2.4", "@vladfrangu/async_event_emitter@^2.4.6": + version "2.4.6" + resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz#508b6c45b03f917112a9008180b308ba0e4d1805" + integrity sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA== -abab@^2.0.3, abab@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== - -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== +aggregate-error@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-4.0.1.tgz#25091fe1573b9e0be892aeda15c7c66a545f758e" + integrity sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w== dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" + clean-stack "^4.0.0" + indent-string "^5.0.0" -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.2.4: - version "8.6.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" - integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== - -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== +ansi-align@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== dependencies: - debug "4" + string-width "^4.1.0" -ansi-escapes@^4.2.1: +ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== + dependencies: + type-fest "^1.0.2" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -752,23 +918,33 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0, ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-observable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" + integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== anymatch@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" -app-root-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" - integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== +app-root-path@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.1.0.tgz#5971a2fc12ba170369a7a1ef018c71e6e47c2e86" + integrity sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA== argparse@^1.0.7: version "1.0.10" @@ -782,26 +958,33 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== -babel-jest@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.5.tgz#d38bd0be8ea71d8b97853a5fc9f76deeb095c709" - integrity sha512-3uuUTjXbgtODmSv/DXO9nZfD52IyC2OYTFaXGRzL0kpykzroaquCrD5+lZNafTvZlnNqZHt5pb0M08qVBZnsnA== +atomically@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/atomically/-/atomically-2.0.3.tgz#27e47bbe39994d324918491ba7c0edb7783e56cb" + integrity sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw== dependencies: - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + stubborn-fs "^1.2.5" + when-exit "^2.1.1" + +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== + dependencies: + "@jest/transform" "^29.7.0" "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^27.4.0" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" slash "^3.0.0" -babel-plugin-istanbul@^6.0.0: +babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== @@ -812,40 +995,43 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6" - integrity sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" + "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" + integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-import-attributes" "^7.24.7" + "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-jest@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca" - integrity sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg== +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: - babel-plugin-jest-hoist "^27.4.0" + babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -863,6 +1049,29 @@ bignumber.js@9.0.0: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A== +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +boxen@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-8.0.1.tgz#7e9fcbb45e11a2d7e6daa8fdcebfc3242fc19fe3" + integrity sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw== + dependencies: + ansi-align "^3.0.1" + camelcase "^8.0.0" + chalk "^5.3.0" + cli-boxes "^3.0.0" + string-width "^7.2.0" + type-fest "^4.21.0" + widest-line "^5.0.0" + wrap-ansi "^9.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -871,30 +1080,31 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: - fill-range "^7.0.1" + balanced-match "^1.0.0" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - -browserslist@^4.17.5: - version "4.19.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" - integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - caniuse-lite "^1.0.30001286" - electron-to-chromium "^1.4.17" - escalade "^3.1.1" - node-releases "^2.0.1" - picocolors "^1.0.0" + fill-range "^7.1.1" -bs-logger@0.x: +browserslist@^4.24.0: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== + dependencies: + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" + +bs-logger@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== @@ -913,6 +1123,14 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -921,12 +1139,19 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" -cacheable-request@^7.0.2: +cacheable-lookup@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" + integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== + +cacheable-request@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== @@ -950,16 +1175,39 @@ camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.2.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" - integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001286: - version "1.0.30001291" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001291.tgz#08a8d2cfea0b2cf2e1d94dd795942d0daef6108c" - integrity sha512-roMV5V0HNGgJ88s42eE70sstqGW/gwFndosYrikHthw98N5tLnOTxFqMLQjZVRxTWFlJ4rn+MsgXrR7MDPY4jA== +camelcase@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-8.0.0.tgz#c0d36d418753fb6ad9c5e0437579745c1c14a534" + integrity sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA== -chalk@^2.0.0: +caniuse-lite@^1.0.30001669: + version "1.0.30001673" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001673.tgz#5aa291557af1c71340e809987367410aab7a5a9e" + integrity sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw== + +chalk-template@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-1.1.0.tgz#ffc55db6dd745e9394b85327c8ac8466edb7a7b1" + integrity sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg== + dependencies: + chalk "^5.2.0" + +chalk@^1.0.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -968,7 +1216,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -976,20 +1224,56 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.2.0, chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + ci-info@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" - integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + version "1.4.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" + integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== + +clean-stack@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-4.2.0.tgz#c464e4cde4ac789f4e0735c5d75beb49d7b30b31" + integrity sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg== + dependencies: + escape-string-regexp "5.0.0" + +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== + +cli-cursor@^2.0.0, cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== + dependencies: + restore-cursor "^2.0.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" cli-highlight@^2.1.11: version "2.1.11" @@ -1003,6 +1287,34 @@ cli-highlight@^2.1.11: parse5-htmlparser2-tree-adapter "^6.0.0" yargs "^16.0.0" +cli-spinners@^2.5.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + integrity sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg== + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1012,22 +1324,41 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== dependencies: mimic-response "^1.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0: version "1.9.3" @@ -1046,38 +1377,83 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" +commander@^12.0.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== +config-chain@^1.1.11: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: - safe-buffer "~5.1.1" + ini "^1.3.4" + proto-list "~1.2.1" + +configstore@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-7.0.0.tgz#4461561fc51cb40e5ee1161230bc0337e069cc6b" + integrity sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ== + dependencies: + atomically "^2.0.3" + dot-prop "^9.0.0" + graceful-fs "^4.2.11" + xdg-basedir "^5.1.0" + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cross-spawn@^7.0.3: +cosmiconfig@^8.3.6: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + +cron@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/cron/-/cron-3.3.1.tgz#03c56b4a3ad52606160adfba1fab932c53838807" + integrity sha512-KpvuzJEbeTMTfLsXhUuDfsFYr8s5roUlLKb4fa68GszWrA4783C7q6m9yj4vyc6neyD/V9e0YiADSX2c+yRDXg== + dependencies: + "@types/luxon" "~3.4.0" + luxon "~3.5.0" + +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1086,50 +1462,22 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== +date-fns@^1.27.2: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== +dayjs@^1.11.9: + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: - cssom "~0.3.6" - -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - -debug@4, debug@^4.1.0, debug@^4.3.1: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== - dependencies: - ms "2.1.2" - -debug@^4.1.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -decimal.js@^10.2.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== + ms "^2.1.3" decompress-response@^6.0.0: version "6.0.0" @@ -1138,94 +1486,195 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +dedent@^1.0.0: + version "1.5.3" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== -deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" defer-to-connect@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +del@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/del/-/del-7.1.0.tgz#0de0044d556b649ff05387f1fa7c885e155fd1b6" + integrity sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg== + dependencies: + globby "^13.1.2" + graceful-fs "^4.2.10" + is-glob "^4.0.3" + is-path-cwd "^3.0.0" + is-path-inside "^4.0.0" + p-map "^5.5.0" + rimraf "^3.0.2" + slash "^4.0.0" detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -diff-sequences@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" - integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== -discord-api-types@^0.26.0: - version "0.26.1" - resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.26.1.tgz#726f766ddc37d60da95740991d22cb6ef2ed787b" - integrity sha512-T5PdMQ+Y1MEECYMV5wmyi9VEYPagEDEi4S0amgsszpWY0VB9JJ/hEvM6BgLhbdnKky4gfmZEXtEEtojN8ZKJQQ== - -discord.js@^13.6.0: - version "13.6.0" - resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.6.0.tgz#d8a8a591dbf25cbcf9c783d5ddf22c4694860475" - integrity sha512-tXNR8zgsEPxPBvGk3AQjJ9ljIIC6/LOPjzKwpwz8Y1Q2X66Vi3ZqFgRHYwnHKC0jC0F+l4LzxlhmOJsBZDNg9g== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: - "@discordjs/builders" "^0.11.0" - "@discordjs/collection" "^0.4.0" - "@sapphire/async-queue" "^1.1.9" - "@types/node-fetch" "^2.5.12" - "@types/ws" "^8.2.2" - discord-api-types "^0.26.0" - form-data "^4.0.0" - node-fetch "^2.6.1" - ws "^8.4.0" + path-type "^4.0.0" -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== +discord-api-types@0.37.100: + version "0.37.100" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.100.tgz#5979892d39511bc7f1dbb9660d2d2cad698b3de7" + integrity sha512-a8zvUI0GYYwDtScfRd/TtaNBDTXwP5DiDVX7K5OmE+DRT57gBqKnwtOC5Ol8z0mRW8KQfETIgiB8U0YZ9NXiCA== + +discord-api-types@0.37.83: + version "0.37.83" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.83.tgz#a22a799729ceded8176ea747157837ddf4708b1f" + integrity sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA== + +discord-api-types@0.37.97: + version "0.37.97" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.97.tgz#d658573f726ad179261d538dbad4e7e8eca48d11" + integrity sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA== + +discord.js@^14.3.0: + version "14.16.3" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.16.3.tgz#9553366953c992469f47a55af2a11c2054a9babe" + integrity sha512-EPCWE9OkA9DnFFNrO7Kl1WHHDYFXu3CNVFJg63bfU7hVtjZGyhShwZtSBImINQRWxWP2tgo2XI+QhdXx28r0aA== dependencies: - webidl-conversions "^5.0.0" + "@discordjs/builders" "^1.9.0" + "@discordjs/collection" "1.5.3" + "@discordjs/formatters" "^0.5.0" + "@discordjs/rest" "^2.4.0" + "@discordjs/util" "^1.1.1" + "@discordjs/ws" "1.1.1" + "@sapphire/snowflake" "3.5.3" + discord-api-types "0.37.100" + fast-deep-equal "3.1.3" + lodash.snakecase "4.1.1" + tslib "^2.6.3" + undici "6.19.8" -dotenv@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" -dotenv@^8.2.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" - integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -electron-to-chromium@^1.4.17: - version "1.4.25" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.25.tgz#ce95e6678f8c6893ae892c7e95a5000e83f1957f" - integrity sha512-bTwub9Y/76EiNmfaiJih+hAy6xn7Ns95S4KvI2NuKNOz8TEEKKQUu44xuy0PYMudjM9zdjKRS1bitsUvHTfuUg== +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +domutils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dot-prop@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-9.0.0.tgz#bae5982fe6dc6b8fddb92efef4f2ddff26779e92" + integrity sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ== + dependencies: + type-fest "^4.18.2" + +dotenv@^16.0.0, dotenv@^16.0.3: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ejs@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.5.41: + version "1.5.47" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz#ef0751bc19b28be8ee44cd8405309de3bf3b20c7" + integrity sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ== + +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + integrity sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +emoji-regex@^10.0.0, emoji-regex@^10.3.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4" + integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.0: +emoji-regex@^9.2.2: version "9.2.2" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== @@ -1237,48 +1686,48 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +entities@^4.2.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -escape-string-regexp@^1.0.5: +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-goat@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-4.0.0.tgz#9424820331b510b0666b98f7873fe11ac4aa8081" + integrity sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg== + +escape-string-regexp@5.0.0, escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -1294,47 +1743,127 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +exit-hook@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-4.0.0.tgz#c1e16ebd03d3166f837b1502dac755bb5c460d58" + integrity sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ== + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.2.tgz#4429b0f7e307771d176de9bdf23229b101db6ef6" - integrity sha512-BjAXIDC6ZOW+WBFNg96J22D27Nq5ohn+oGcuP2rtOtcjuxNoV9McpQ60PcQWhdFOSBIQdR72e+4HdnbZTFSTyg== +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: - "@jest/types" "^27.4.2" - ansi-styles "^5.0.0" - jest-get-type "^27.4.0" - jest-matcher-utils "^27.4.2" - jest-message-util "^27.4.2" - jest-regex-util "^27.4.0" + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +external-editor@^3.0.3, external-editor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@3.1.3, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +figures@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ== + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA== + dependencies: + escape-string-regexp "^1.0.5" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" +find-up-simple@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-up-simple/-/find-up-simple-1.0.0.tgz#21d035fde9fdbd56c8f4d2f63f32fd93a1cfc368" + integrity sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw== + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -1343,38 +1872,33 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" + cross-spawn "^7.0.0" + signal-exit "^4.0.1" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -1386,6 +1910,11 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-east-asian-width@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389" + integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -1398,11 +1927,28 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +github-url-from-git@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/github-url-from-git/-/github-url-from-git-1.5.0.tgz#f985fedcc0a9aa579dc88d7aff068d55cc6251a0" + integrity sha512-WWOec4aRI7YAykQ9+BHmzjyNlkfJFG8QLXnDTsLz/kZefq7qkzdfo4p6fkYYMIq1aj+gZcQs/1HQhQh3DPPxlQ== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob-parent@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" @@ -1410,130 +1956,191 @@ glob-parent@^6.0.0: dependencies: is-glob "^4.0.3" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" +global-directory@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/global-directory/-/global-directory-4.0.1.tgz#4d7ac7cfd2cb73f304c53b8810891748df5e361e" + integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q== + dependencies: + ini "4.1.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -got@^11.8.3: - version "11.8.3" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770" - integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg== +globby@^13.1.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.2" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" -graceful-fs@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +got-cjs@^12.5.4: + version "12.5.4" + resolved "https://registry.yarnpkg.com/got-cjs/-/got-cjs-12.5.4.tgz#b46419c0e8e5fb5503b926941807408049ae2e11" + integrity sha512-Uas6lAsP8bRCt5WXGMhjFf/qEHTrm4v4qxGR02rLG2kdG9qedctvlkdwXVcDJ7Cs84X+r4dPU7vdwGjCaspXug== + dependencies: + "@sindresorhus/is" "4.6.0" + "@szmarczak/http-timer" "4.0.6" + "@types/responselike" "1.0.0" + cacheable-lookup "6.1.0" + cacheable-request "7.0.2" + decompress-response "^6.0.0" + form-data-encoder "1.7.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "2.0.0" + p-cancelable "2.1.1" + responselike "2.0.1" + +graceful-fs@4.2.10: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== + dependencies: + ansi-regex "^2.0.0" has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: - function-bind "^1.1.1" + function-bind "^1.1.2" highlight.js@^10.7.1: version "10.7.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== +hosted-git-info@^7.0.0, hosted-git-info@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== dependencies: - whatwg-encoding "^1.0.5" + lru-cache "^10.0.1" html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== +htmlparser2@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.1.0" + entities "^4.5.0" -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== dependencies: quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" + resolve-alpn "^1.2.0" human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@0.4.24: +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -import-local@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.3.tgz#4d51c2c495ca9393da259ec66b62e022920211e0" - integrity sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA== +ignore-walk@^6.0.4: + version "6.0.5" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.5.tgz#ef8d61eab7da169078723d1f82833b36e200b0dd" + integrity sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A== + dependencies: + minimatch "^9.0.0" + +ignore@^5.2.4: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2, import-local@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -1541,32 +2148,144 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ== + +indent-string@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5" + integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== + +index-to-position@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/index-to-position/-/index-to-position-0.1.2.tgz#e11bfe995ca4d8eddb1ec43274488f3c201a7f09" + integrity sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g== inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -is-core-module@^2.2.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" - integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== +ini@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1" + integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== + +ini@^1.3.4, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inquirer-autosubmit-prompt@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/inquirer-autosubmit-prompt/-/inquirer-autosubmit-prompt-0.2.0.tgz#a10f952af4f7bac9c43010e3e9e0891d7e8d15a1" + integrity sha512-mzNrusCk5L6kSzlN0Ioddn8yzrhYNLli+Sn2ZxMuLechMYAzakiFCIULxsxlQb5YKzthLGfrFACcWoAvM7p04Q== dependencies: - has "^1.0.3" + chalk "^2.4.1" + inquirer "^6.2.1" + rxjs "^6.3.3" + +inquirer@^6.2.1: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +inquirer@^9.2.15: + version "9.3.7" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.3.7.tgz#0b562bf843812208844741c9aec9244c939b83d4" + integrity sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w== + dependencies: + "@inquirer/figures" "^1.0.3" + ansi-escapes "^4.3.2" + cli-width "^4.1.0" + external-editor "^3.1.0" + mute-stream "1.0.0" + ora "^5.4.1" + run-async "^3.0.0" + rxjs "^7.8.1" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -1578,62 +2297,143 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.3: +is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" +is-in-ci@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-in-ci/-/is-in-ci-1.0.0.tgz#9a86bbda7e42c6129902e0574c54b018fbb6ab88" + integrity sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg== + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-installed-globally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-1.0.0.tgz#08952c43758c33d815692392f7f8437b9e436d5a" + integrity sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ== + dependencies: + global-directory "^4.0.1" + is-path-inside "^4.0.0" + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-npm@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-6.0.0.tgz#b59e75e8915543ca5d881ecff864077cba095261" + integrity sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-observable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" + integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== + dependencies: + symbol-observable "^1.1.0" + +is-path-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-3.0.0.tgz#889b41e55c8588b1eb2a96a61d05740a674521c7" + integrity sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA== + +is-path-inside@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-4.0.0.tgz#805aeb62c47c1b12fc3fd13bfb3ed1e7430071db" + integrity sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA== + +is-promise@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-scoped@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-scoped/-/is-scoped-3.0.0.tgz#2f9fc6e37c17d432d8e38d3c749aab8c76d1bdd8" + integrity sha512-ezxLUq30kiTvP0w/5n9tj4qTOKlrA07Oty1hwTQ+lcqw11x6uc8sp7VRb2OVGRzKfCHZ2A22T5Zsau/Q2Akb0g== + dependencies: + scoped-regex "^3.0.0" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + +is-url-superb@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-6.1.0.tgz#182f0d92b482412afeadfba8e6ea2c76680e3631" + integrity sha512-LXdhGlYqUPdvEyIhWPEEwYYK3yrUiPcBjmFGlZNv1u5GtIL5qQRf7ddDyPNAvsMFqdzS923FROpTQU97tLe3JQ== + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +issue-regex@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/issue-regex/-/issue-regex-4.3.0.tgz#e73db1b475fa12b93ddc66c36ebd5a5f57d0d838" + integrity sha512-7731a/t2llyrk8Hdwl1x3LkhIFGzxHQGpJA7Ur9cIRViakQF2y25Lwhx8Ziy1B068+kBYUmYPBzw5uo3DdWrdQ== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== - -istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^5.0.4: - version "5.1.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" - integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: "@babel/core" "^7.12.3" "@babel/parser" "^7.14.7" @@ -1641,13 +2441,24 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" + make-dir "^4.0.0" supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: @@ -1659,431 +2470,397 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.1.tgz#7085857f17d2441053c6ce5c3b8fdf6882289397" - integrity sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw== +istanbul-reports@^3.1.3: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" - integrity sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A== +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: - "@jest/types" "^27.4.2" - execa "^5.0.0" - throat "^6.0.1" + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" -jest-circus@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.4.5.tgz#70bfb78e0200cab9b84747bf274debacaa538467" - integrity sha512-eTNWa9wsvBwPykhMMShheafbwyakcdHZaEYh5iRrQ0PFJxkDP/e3U/FvzGuKWu2WpwUA3C3hPlfpuzvOdTVqnw== +jake@^10.8.5: + version "10.9.2" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" + integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== dependencies: - "@jest/environment" "^27.4.4" - "@jest/test-result" "^27.4.2" - "@jest/types" "^27.4.2" + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" - expect "^27.4.2" + dedent "^1.0.0" is-generator-fn "^2.0.0" - jest-each "^27.4.2" - jest-matcher-utils "^27.4.2" - jest-message-util "^27.4.2" - jest-runtime "^27.4.5" - jest-snapshot "^27.4.5" - jest-util "^27.4.2" - pretty-format "^27.4.2" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" - throat "^6.0.1" -jest-cli@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.5.tgz#8708f54c28d13681f3255ec9026a2b15b03d41e8" - integrity sha512-hrky3DSgE0u7sQxaCL7bdebEPHx5QzYmrGuUjaPLmPE8jx5adtvGuOlRspvMoVLTTDOHRnZDoRLYJuA+VCI7Hg== +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: - "@jest/core" "^27.4.5" - "@jest/test-result" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" + create-jest "^29.7.0" exit "^0.1.2" - graceful-fs "^4.2.4" import-local "^3.0.2" - jest-config "^27.4.5" - jest-util "^27.4.2" - jest-validate "^27.4.2" - prompts "^2.0.1" - yargs "^16.2.0" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" -jest-config@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.5.tgz#77ed7f2ba7bcfd7d740ade711d0d13512e08a59e" - integrity sha512-t+STVJtPt+fpqQ8GBw850NtSQbnDOw/UzdPfzDaHQ48/AylQlW7LHj3dH+ndxhC1UxJ0Q3qkq7IH+nM1skwTwA== +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^27.4.5" - "@jest/types" "^27.4.2" - babel-jest "^27.4.5" + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" - glob "^7.1.1" - graceful-fs "^4.2.4" - jest-circus "^27.4.5" - jest-environment-jsdom "^27.4.4" - jest-environment-node "^27.4.4" - jest-get-type "^27.4.0" - jest-jasmine2 "^27.4.5" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.5" - jest-runner "^27.4.5" - jest-util "^27.4.2" - jest-validate "^27.4.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" micromatch "^4.0.4" - pretty-format "^27.4.2" + parse-json "^5.2.0" + pretty-format "^29.7.0" slash "^3.0.0" + strip-json-comments "^3.1.1" -jest-diff@^27.0.0, jest-diff@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.2.tgz#786b2a5211d854f848e2dcc1e324448e9481f36f" - integrity sha512-ujc9ToyUZDh9KcqvQDkk/gkbf6zSaeEg9AiBxtttXW59H/AcqEYp1ciXAtJp+jXWva5nAf/ePtSsgWwE5mqp4Q== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" - diff-sequences "^27.4.0" - jest-get-type "^27.4.0" - pretty-format "^27.4.2" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-docblock@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" - integrity sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg== +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" -jest-each@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.4.2.tgz#19364c82a692d0d26557642098d1f4619c9ee7d3" - integrity sha512-53V2MNyW28CTruB3lXaHNk6PkiIFuzdOC9gR3C6j8YE/ACfrPnz+slB0s17AgU1TtxNzLuHyvNlLJ+8QYw9nBg== +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^29.6.3" chalk "^4.0.0" - jest-get-type "^27.4.0" - jest-util "^27.4.2" - pretty-format "^27.4.2" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" -jest-environment-jsdom@^27.4.4: - version "27.4.4" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.4.4.tgz#94f738e99514d7a880e8ed8e03e3a321d43b49db" - integrity sha512-cYR3ndNfHBqQgFvS1RL7dNqSvD//K56j/q1s2ygNHcfTCAp12zfIromO1w3COmXrxS8hWAh7+CmZmGCIoqGcGA== +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== dependencies: - "@jest/environment" "^27.4.4" - "@jest/fake-timers" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^27.4.2" - jest-util "^27.4.2" - jsdom "^16.6.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -jest-environment-node@^27.4.4: - version "27.4.4" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.4.4.tgz#42fe5e3b224cb69b99811ebf6f5eaa5a59618514" - integrity sha512-D+v3lbJ2GjQTQR23TK0kY3vFVmSeea05giInI41HHOaJnAwOnmUHTZgUaZL+VxUB43pIzoa7PMwWtCVlIUoVoA== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: - "@jest/environment" "^27.4.4" - "@jest/fake-timers" "^27.4.2" - "@jest/types" "^27.4.2" - "@types/node" "*" - jest-mock "^27.4.2" - jest-util "^27.4.2" - -jest-get-type@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" - integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== - -jest-haste-map@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.4.5.tgz#c2921224a59223f91e03ec15703905978ef0cc1a" - integrity sha512-oJm1b5qhhPs78K24EDGifWS0dELYxnoBiDhatT/FThgB9yxqUm5F6li3Pv+Q+apMBmmPNzOBnZ7ZxWMB1Leq1Q== - dependencies: - "@jest/types" "^27.4.2" - "@types/graceful-fs" "^4.1.2" + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^27.4.0" - jest-serializer "^27.4.0" - jest-util "^27.4.2" - jest-worker "^27.4.5" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" micromatch "^4.0.4" - walker "^1.0.7" + walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.4.5.tgz#ff79d11561679ff6c89715b0cd6b1e8c0dfbc6dc" - integrity sha512-oUnvwhJDj2LhOiUB1kdnJjkx8C5PwgUZQb9urF77mELH9DGR4e2GqpWQKBOYXWs5+uTN9BGDqRz3Aeg5Wts7aw== +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^27.4.4" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.2" - "@jest/types" "^27.4.2" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.4.2" - is-generator-fn "^2.0.0" - jest-each "^27.4.2" - jest-matcher-utils "^27.4.2" - jest-message-util "^27.4.2" - jest-runtime "^27.4.5" - jest-snapshot "^27.4.5" - jest-util "^27.4.2" - pretty-format "^27.4.2" - throat "^6.0.1" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-leak-detector@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.4.2.tgz#7fc3120893a7a911c553f3f2bdff9faa4454abbb" - integrity sha512-ml0KvFYZllzPBJWDei3mDzUhyp/M4ubKebX++fPaudpe8OsxUE+m+P6ciVLboQsrzOCWDjE20/eXew9QMx/VGw== - dependencies: - jest-get-type "^27.4.0" - pretty-format "^27.4.2" - -jest-matcher-utils@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.2.tgz#d17c5038607978a255e0a9a5c32c24e984b6c60b" - integrity sha512-jyP28er3RRtMv+fmYC/PKG8wvAmfGcSNproVTW2Y0P/OY7/hWUOmsPfxN1jOhM+0u2xU984u2yEagGivz9OBGQ== +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" - jest-diff "^27.4.2" - jest-get-type "^27.4.0" - pretty-format "^27.4.2" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-message-util@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.4.2.tgz#07f3f1bf207d69cf798ce830cc57f1a849f99388" - integrity sha512-OMRqRNd9E0DkBLZpFtZkAGYOXl6ZpoMtQJWTAREJKDOFa0M6ptB7L67tp+cszMBkvSgKOhNtQp2Vbcz3ZZKo/w== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.4.2" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.4.2" + pretty-format "^29.7.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock-extended@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-2.0.4.tgz#2bb430ba0adb9e10ea6a68d08731f2129330c8fe" - integrity sha512-MgL3B3GjURQFjjPGqbCANydA5BFNPygv0mYp4Tjfxohh9MWwxxX8Eq2p6ncCt/Vt+RAnaLlDaI7gwrDRD7Pt9A== +jest-mock-extended@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-3.0.7.tgz#3d902dabad99d7831bbe5fccee85ab0371c22675" + integrity sha512-7lsKdLFcW9B9l5NzZ66S/yTQ9k8rFtnwYdCNuRU/81fqDWicNDVhitTSPnrGmNeNm0xyw0JHexEOShrIKRCIRQ== dependencies: - ts-essentials "^7.0.3" + ts-essentials "^10.0.0" -jest-mock@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.4.2.tgz#184ff197a25491bfe4570c286daa5d62eb760b88" - integrity sha512-PDDPuyhoukk20JrQKeofK12hqtSka7mWH0QQuxSNgrdiPsrnYYLS6wbzu/HDlxZRzji5ylLRULeuI/vmZZDrYA== +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^29.6.3" "@types/node" "*" + jest-util "^29.7.0" jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" - integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.5.tgz#9398af854bdb12d6a9e5a8a536ee401f889a3ecf" - integrity sha512-elEVvkvRK51y037NshtEkEnukMBWvlPzZHiL847OrIljJ8yIsujD2GXRPqDXC4rEVKbcdsy7W0FxoZb4WmEs7w== +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: - "@jest/types" "^27.4.2" - jest-regex-util "^27.4.0" - jest-snapshot "^27.4.5" + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" -jest-resolve@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.4.5.tgz#8dc44f5065fb8d58944c20f932cb7b9fe9760cca" - integrity sha512-xU3z1BuOz/hUhVUL+918KqUgK+skqOuUsAi7A+iwoUldK6/+PW+utK8l8cxIWT9AW7IAhGNXjSAh1UYmjULZZw== +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: - "@jest/types" "^27.4.2" chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.5" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" jest-pnp-resolver "^1.2.2" - jest-util "^27.4.2" - jest-validate "^27.4.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.4.5.tgz#daba2ba71c8f34137dc7ac45616add35370a681e" - integrity sha512-/irauncTfmY1WkTaRQGRWcyQLzK1g98GYG/8QvIPviHgO1Fqz1JYeEIsSfF+9mc/UTA6S+IIHFgKyvUrtiBIZg== +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: - "@jest/console" "^27.4.2" - "@jest/environment" "^27.4.4" - "@jest/test-result" "^27.4.2" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-docblock "^27.4.0" - jest-environment-jsdom "^27.4.4" - jest-environment-node "^27.4.4" - jest-haste-map "^27.4.5" - jest-leak-detector "^27.4.2" - jest-message-util "^27.4.2" - jest-resolve "^27.4.5" - jest-runtime "^27.4.5" - jest-util "^27.4.2" - jest-worker "^27.4.5" - source-map-support "^0.5.6" - throat "^6.0.1" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" -jest-runtime@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.4.5.tgz#97703ad2a1799d4f50ab59049bd21a9ceaed2813" - integrity sha512-CIYqwuJQXHQtPd/idgrx4zgJ6iCb6uBjQq1RSAGQrw2S8XifDmoM1Ot8NRd80ooAm+ZNdHVwsktIMGlA1F1FAQ== +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== dependencies: - "@jest/console" "^27.4.2" - "@jest/environment" "^27.4.4" - "@jest/globals" "^27.4.4" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.2" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" - "@types/yargs" "^16.0.0" + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" - exit "^0.1.2" glob "^7.1.3" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.5" - jest-message-util "^27.4.2" - jest-mock "^27.4.2" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.5" - jest-snapshot "^27.4.5" - jest-util "^27.4.2" - jest-validate "^27.4.2" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^16.2.0" -jest-serializer@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a" - integrity sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ== +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: - "@types/node" "*" - graceful-fs "^4.2.4" - -jest-snapshot@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.4.5.tgz#2ea909b20aac0fe62504bc161331f730b8a7ecc7" - integrity sha512-eCi/iM1YJFrJWiT9de4+RpWWWBqsHiYxFG9V9o/n0WXs6GpW4lUt4FAHAgFPTLPqCUVzrMQmSmTZSgQzwqR7IQ== - dependencies: - "@babel/core" "^7.7.2" + "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" - "@babel/parser" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" - "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.1.5" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.4.2" - graceful-fs "^4.2.4" - jest-diff "^27.4.2" - jest-get-type "^27.4.0" - jest-haste-map "^27.4.5" - jest-matcher-utils "^27.4.2" - jest-message-util "^27.4.2" - jest-resolve "^27.4.5" - jest-util "^27.4.2" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" natural-compare "^1.4.0" - pretty-format "^27.4.2" - semver "^7.3.2" + pretty-format "^29.7.0" + semver "^7.5.3" -jest-util@^27.0.0, jest-util@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621" - integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA== +jest-util@^29.0.0, jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.4.2.tgz#eecfcc1b1c9429aa007da08a2bae4e32a81bbbc3" - integrity sha512-hWYsSUej+Fs8ZhOm5vhWzwSLmVaPAxRy+Mr+z5MzeaHm9AxUpXdoVMEW4R86y5gOobVfBsMFLk4Rb+QkiEpx1A== +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^29.6.3" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.4.0" + jest-get-type "^29.6.3" leven "^3.1.0" - pretty-format "^27.4.2" + pretty-format "^29.7.0" -jest-watcher@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.4.2.tgz#c9037edfd80354c9fe90de4b6f8b6e2b8e736744" - integrity sha512-NJvMVyyBeXfDezhWzUOCOYZrUmkSCiatpjpm+nFUid74OZEHk6aMLrZAukIiFDwdbqp6mTM6Ui1w4oc+8EobQg== +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: - "@jest/test-result" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.4.2" + emittery "^0.13.1" + jest-util "^29.7.0" string-length "^4.0.1" -jest-worker@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.5.tgz#d696e3e46ae0f24cff3fa7195ffba22889262242" - integrity sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.5.tgz#66e45acba44137fac26be9d3cc5bb031e136dc0f" - integrity sha512-uT5MiVN3Jppt314kidCk47MYIRilJjA/l2mxwiuzzxGUeJIvA8/pDaJOAX5KWvjAo7SCydcW0/4WEtgbLMiJkg== +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: - "@jest/core" "^27.4.5" + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" import-local "^3.0.2" - jest-cli "^27.4.5" + jest-cli "^29.7.0" js-tokens@^4.0.0: version "4.0.0" @@ -2098,67 +2875,37 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.0.0: +js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json5@2.x, json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== keyv@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.4.tgz#f040b236ea2b06ed15ed86fbef8407e1a1c8e376" - integrity sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg== + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" @@ -2167,18 +2914,86 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +ky@^1.2.0: + version "1.7.2" + resolved "https://registry.yarnpkg.com/ky/-/ky-1.7.2.tgz#b97d9b997ba51ff1e152f0815d3d27b86513eb1c" + integrity sha512-OzIvbHKKDpi60TnF9t7UUVAF1B4mcqc02z5PIvrm08Wyb+yOcz63GRvEuVxNT18a9E1SrNouhB4W2NNLeD7Ykg== + +latest-version@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-9.0.0.tgz#e91ed216e7a4badc6f73b66c65adb46c58ec6ba1" + integrity sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA== + dependencies: + package-json "^10.0.0" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +linqts@^1.14.4: + version "1.15.0" + resolved "https://registry.yarnpkg.com/linqts/-/linqts-1.15.0.tgz#99195db380da7c039961c00e637e6ae57d5dee5e" + integrity sha512-DYRi1eGy8R0/dPzTTE0q3gQKvAj960CumYNikVGaUBc8TJCs/zeIbEmHb9lGx7Y9ALvTKle2cuBpkKgIeCSRTg== + +listr-input@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/listr-input/-/listr-input-0.2.1.tgz#ce735c34530683580388fdf9462ecfebd3b66126" + integrity sha512-oa8iVG870qJq+OuuMK3DjGqFcwsK1SDu+kULp9kEq09TY231aideIZenr3lFOQdASpAr6asuyJBbX62/a3IIhg== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + inquirer "^7.0.0" + inquirer-autosubmit-prompt "^0.2.0" + rxjs "^6.5.3" + through "^2.3.8" + +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + integrity sha512-L26cIFm7/oZeSNVhWB6faeorXhMg4HNlb/dS/7jHhr708jxlXrtrBWo4YUxZQkc6dGoxEAe6J/D3juTRBUzjtA== + +listr-update-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" + integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^2.3.0" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" + integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== + dependencies: + chalk "^2.4.1" + cli-cursor "^2.1.0" + date-fns "^1.27.2" + figures "^2.0.0" + +listr@^0.14.3: + version "0.14.3" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" + integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== + dependencies: + "@samverschueren/stream-to-observable" "^0.3.0" + is-observable "^1.1.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.5.0" + listr-verbose-renderer "^0.5.0" + p-map "^2.0.0" + rxjs "^6.3.3" locate-path@^5.0.0: version "5.0.0" @@ -2187,36 +3002,93 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash.memoize@4.x: +lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -lodash@^4.7.0: +lodash.snakecase@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.zip@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" + integrity sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg== + +lodash@^4.17.12, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -lowercase-keys@^2.0.0: +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + integrity sha512-mmPrW0Fh2fxOzdBbFv4g1m6pR72haFLPJ2G5SJEELf1y+iaQrDG6cWCPjy54RHYbZAt7X+ls690Kw62AdWXBzQ== + dependencies: + chalk "^1.0.0" + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-symbols@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-6.0.0.tgz#bb95e5f05322651cac30c0feb6404f9f2a8a9439" + integrity sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw== + dependencies: + chalk "^5.3.0" + is-unicode-supported "^1.3.0" + +log-update@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" + integrity sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg== + dependencies: + ansi-escapes "^3.0.0" + cli-cursor "^2.0.0" + wrap-ansi "^3.0.1" + +lowercase-keys@2.0.0, lowercase-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" +lru-cache@^10.0.1, lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== -make-dir@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: - semver "^6.0.0" + yallist "^3.0.2" -make-error@1.x: +luxon@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" + integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== + +magic-bytes.js@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz#c41cf4bc2f802992b05e64962411c9dd44fdef92" + integrity sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ== + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -2228,36 +3100,49 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +meow@^13.2.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-13.2.0.tgz#6b7d63f913f984063b3cc261b6e8800c4cd3474f" + integrity sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + braces "^3.0.3" + picomatch "^2.3.1" -mime-db@1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== - -mime-types@^2.1.12: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== - dependencies: - mime-db "1.51.0" +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -2268,27 +3153,68 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +minimatch@^9.0.0, minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +minimist@^1.2.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +mkdirp@^2.1.3: + version "2.1.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" + integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +mute-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== mysql@^2.18.1: version "2.18.1" @@ -2312,24 +3238,33 @@ mz@^2.4.0: natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-fetch@^2.6.1: - version "2.6.6" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89" - integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== +new-github-release-url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/new-github-release-url/-/new-github-release-url-2.0.0.tgz#335189b91f52bbb9569042a7485900a205a0500b" + integrity sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ== dependencies: - whatwg-url "^5.0.0" + type-fest "^2.5.1" node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" - integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + +normalize-package-data@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" + integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== + dependencies: + hosted-git-info "^7.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" normalize-path@^3.0.0: version "3.0.0" @@ -2341,6 +3276,63 @@ normalize-url@^6.0.1: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +np@^10.0.0: + version "10.0.7" + resolved "https://registry.yarnpkg.com/np/-/np-10.0.7.tgz#6f0dc3c7440c8ac95d55a2fa5d488c3d0848b7a5" + integrity sha512-vIPKQwOYKpQU40PU5x/vLfN2haj8ObxMvR1QGt7EZnBPWdm4WEbHdumYAnMV7AeR9kACsMqcqAP37sAo5cW5jA== + dependencies: + chalk "^5.3.0" + chalk-template "^1.1.0" + cosmiconfig "^8.3.6" + del "^7.1.0" + escape-goat "^4.0.0" + escape-string-regexp "^5.0.0" + execa "^8.0.1" + exit-hook "^4.0.0" + github-url-from-git "^1.5.0" + hosted-git-info "^7.0.1" + ignore-walk "^6.0.4" + import-local "^3.1.0" + inquirer "^9.2.15" + is-installed-globally "^1.0.0" + is-interactive "^2.0.0" + is-scoped "^3.0.0" + issue-regex "^4.1.0" + listr "^0.14.3" + listr-input "^0.2.1" + log-symbols "^6.0.0" + meow "^13.2.0" + new-github-release-url "^2.0.0" + npm-name "^8.0.0" + onetime "^7.0.0" + open "^10.0.4" + p-memoize "^7.1.1" + p-timeout "^6.1.2" + path-exists "^5.0.0" + pkg-dir "^8.0.0" + read-package-up "^11.0.0" + read-pkg "^9.0.1" + rxjs "^7.8.1" + semver "^7.6.0" + symbol-observable "^4.0.0" + terminal-link "^3.0.0" + update-notifier "^7.0.0" + +npm-name@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/npm-name/-/npm-name-8.0.0.tgz#05aeda9748706ffad66d9ecec8188b99d6e3908b" + integrity sha512-DIuCGcKYYhASAZW6Xh/tiaGMko8IHOHe0n3zOA7SzTi0Yvy00x8L7sa5yNiZ75Ny58O/KeRtNouy8Ut6gPbKiw== + dependencies: + is-scoped "^3.0.0" + is-url-superb "^6.1.0" + ky "^1.2.0" + lodash.zip "^4.2.0" + org-regex "^1.0.0" + p-map "^7.0.1" + registry-auth-token "^5.0.2" + registry-url "^6.0.1" + validate-npm-package-name "^5.0.0" + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -2348,43 +3340,94 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" -object-assign@^4.0.1: +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== + +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -onetime@^5.1.2: +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== + dependencies: + mimic-fn "^1.0.0" + +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + mimic-fn "^4.0.0" -p-cancelable@^2.0.0: +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + dependencies: + mimic-function "^5.0.0" + +open@^10.0.4: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +org-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/org-regex/-/org-regex-1.0.0.tgz#67ebb9ab3cb124fea5841289d60b59434f041a59" + integrity sha512-7bqkxkEJwzJQUAlyYniqEZ3Ilzjh0yoa62c7gL6Ijxj5bEpPL+8IE1Z0PFj0ywjjXQcdrwR51g9MIcLezR0hKQ== + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +p-cancelable@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== @@ -2396,6 +3439,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -2403,11 +3453,82 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-map@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-5.5.0.tgz#054ca8ca778dfa4cf3f8db6638ccb5b937266715" + integrity sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg== + dependencies: + aggregate-error "^4.0.0" + +p-map@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-7.0.2.tgz#7c5119fada4755660f70199a66aa3fe2f85a1fe8" + integrity sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q== + +p-memoize@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/p-memoize/-/p-memoize-7.1.1.tgz#53b1d0e6007288f7261cfa11a7603b84c9261bfa" + integrity sha512-DZ/bONJILHkQ721hSr/E9wMz5Am/OTJ9P6LhLFo2Tu+jL8044tgc9LwHO8g4PiaYePnlVVRAJcKmgy8J9MVFrA== + dependencies: + mimic-fn "^4.0.0" + type-fest "^3.0.0" + +p-timeout@^6.1.2: + version "6.1.3" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-6.1.3.tgz#9635160c4e10c7b4c3db45b7d5d26f911d9fd853" + integrity sha512-UJUyfKbwvr/uZSV6btANfb+0t/mOhKV/KXcCUTp8FcQI+v/0d+wXqH4htrW0E4rR6WiEO/EPvUFiV9D5OI4vlw== + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +package-json@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-10.0.1.tgz#e49ee07b8de63b638e7f1b5bb353733e428fe7d7" + integrity sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg== + dependencies: + ky "^1.2.0" + registry-auth-token "^5.0.2" + registry-url "^6.0.1" + semver "^7.6.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-json@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-8.1.0.tgz#91cdc7728004e955af9cb734de5684733b24a717" + integrity sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA== + dependencies: + "@babel/code-frame" "^7.22.13" + index-to-position "^0.1.2" + type-fest "^4.7.1" + parse5-htmlparser2-tree-adapter@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" @@ -2415,50 +3536,73 @@ parse5-htmlparser2-tree-adapter@^6.0.0: dependencies: parse5 "^6.0.1" -parse5@6.0.1, parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - parse5@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -picomatch@^2.0.4, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pirates@^4.0.1: - version "4.0.4" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.4.tgz#07df81e61028e402735cdd49db701e4885b4e6e6" - integrity sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw== +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^4.2.0: version "4.2.0" @@ -2467,20 +3611,21 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -pretty-format@^27.0.0, pretty-format@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.2.tgz#e4ce92ad66c3888423d332b40477c87d1dac1fb8" - integrity sha512-p0wNtJ9oLuvgOQDEIZ9zQjZffK7KtyR6Si0jnXULIDwrlNF8Cuir3AZP0hHv0jmKuNN/edOnbMjnzd4uTcmWiw== +pkg-dir@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-8.0.0.tgz#8f3de8ba83d46b72a05c80bfd4e579f060fa91e2" + integrity sha512-4peoBq4Wks0riS0z8741NVv+/8IiTvqnZAr8QGgtdifrtpdXbNw/FxRS1l6NFqm4EMzuS0EDqNNx4XGaz8cuyQ== dependencies: - "@jest/types" "^27.4.2" - ansi-regex "^5.0.1" + find-up-simple "^1.0.0" + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" - react-is "^17.0.1" + react-is "^18.0.0" process-nextick-args@~2.0.0: version "2.0.1" @@ -2495,41 +3640,86 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + version "3.0.2" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" + integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== dependencies: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pupa@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-3.1.0.tgz#f15610274376bbcc70c9a3aa8b505ea23f41c579" + integrity sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug== + dependencies: + escape-goat "^4.0.0" + +pure-rand@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -random-bunny@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/random-bunny/-/random-bunny-2.0.0.tgz#f9950f53c7f5183a6dad26386db14f702d477cf7" - integrity sha512-xIDaPghs0nslKWNfxDLAGNtsUPUlmNagUmdNAHP7U4LSrGySbgSfAzCV8eECTeuny2csmf0SL5dBxEZdVvHgOA== +random-bunny@^2.1.6: + version "2.3.0" + resolved "https://registry.yarnpkg.com/random-bunny/-/random-bunny-2.3.0.tgz#091fd4a67ab9aad62c48e59f274cda00d3aa8144" + integrity sha512-MVYQk1JDyQtb/7VIyGPIeCYFIYMDDTgt6GYA9FjEcVkvRH087GZGFZFcgt7HasATfOm/PljrKg6FOjDk4PxNEA== dependencies: + commander "^12.0.0" glob-parent "^6.0.0" - got "^11.8.3" + got-cjs "^12.5.4" + htmlparser2 "^9.1.0" + linqts "^1.14.4" -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +rc@1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +read-package-up@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/read-package-up/-/read-package-up-11.0.0.tgz#71fb879fdaac0e16891e6e666df22de24a48d5ba" + integrity sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ== + dependencies: + find-up-simple "^1.0.0" + read-pkg "^9.0.0" + type-fest "^4.6.0" + +read-pkg@^9.0.0, read-pkg@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-9.0.1.tgz#b1b81fb15104f5dbb121b6bbdee9bbc9739f569b" + integrity sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA== + dependencies: + "@types/normalize-package-data" "^2.4.3" + normalize-package-data "^6.0.0" + parse-json "^8.0.0" + type-fest "^4.6.0" + unicorn-magic "^0.1.0" readable-stream@2.3.7: version "2.3.7" @@ -2544,17 +3734,40 @@ readable-stream@2.3.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -reflect-metadata@^0.1.13: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +reflect-metadata@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== + +registry-auth-token@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.0.2.tgz#8b026cc507c8552ebbe06724136267e63302f756" + integrity sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ== + dependencies: + "@pnpm/npm-conf" "^2.1.0" + +registry-url@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-6.0.1.tgz#056d9343680f2f64400032b1e199faa692286c58" + integrity sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q== + dependencies: + rc "1.2.8" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -resolve-alpn@^1.0.0: +resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -2566,44 +3779,107 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== resolve@^1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" -responselike@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" - integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== +responselike@2.0.1, responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== dependencies: lowercase-keys "^2.0.0" -rimraf@^3.0.0: +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + +run-async@^2.2.0, run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-async@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad" + integrity sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +rxjs@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1: +safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -2613,29 +3889,20 @@ safe-buffer@^5.0.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +scoped-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-3.0.0.tgz#cd7ede7d942f2ae90da53272102ff2d73129c46f" + integrity sha512-yEsN6TuxZhZ1Tl9iB81frTNS292m0I/IG7+w8lTvfcJQP2x3vnpOoevjBoE3Np5A6KnZM2+RtVenihj9t6NiYg== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@7.x, semver@^7.3.2: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== sha.js@^2.4.11: version "2.4.11" @@ -2657,10 +3924,15 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.6" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" - integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== sisteransi@^1.0.5: version "1.0.5" @@ -2672,43 +3944,69 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -source-map-support@^0.5.6: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw== + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.20" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89" + integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw== sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== sqlstring@2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40" - integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A= + integrity sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ== stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" @@ -2720,6 +4018,32 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -2729,6 +4053,31 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^7.0.0, string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -2736,6 +4085,34 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2743,6 +4120,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" @@ -2753,6 +4137,31 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +stubborn-fs@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/stubborn-fs/-/stubborn-fs-1.2.5.tgz#e5e244223166921ddf66ed5e062b6b3bf285bfd2" + integrity sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g== + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -2774,26 +4183,36 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== +supports-hyperlinks@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== dependencies: has-flag "^4.0.0" supports-color "^7.0.0" -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== +symbol-observable@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +symbol-observable@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" + integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== + +terminal-link@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-3.0.0.tgz#91c82a66b52fc1684123297ce384429faf72ac5c" + integrity sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg== dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" + ansi-escapes "^5.0.0" + supports-hyperlinks "^2.2.0" test-exclude@^6.0.0: version "6.0.0" @@ -2807,7 +4226,7 @@ test-exclude@^6.0.0: thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== dependencies: thenify ">= 3.1.0 < 4" @@ -2818,21 +4237,23 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -throat@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== +through@^2.3.6, through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2840,62 +4261,40 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== +ts-essentials@^10.0.0: + version "10.0.2" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-10.0.2.tgz#8c7aa74ed79580ffe49df5ca28d06cc6bea0ff3c" + integrity sha512-Xwag0TULqriaugXqVdDiGZ5wuZpqABZlpwQ2Ho4GDyiu/R2Xjkp/9+zcFxL7uzeLl/QCPrflnvpVYyS3ouT7Zw== + +ts-jest@^29.2.4: + version "29.2.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63" + integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA== dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" + bs-logger "^0.2.6" + ejs "^3.1.10" + fast-json-stable-stringify "^2.1.0" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.6.3" + yargs-parser "^21.1.1" -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" +ts-mixer@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.4.tgz#1da39ceabc09d947a82140d9f09db0f84919ca28" + integrity sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA== -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -ts-essentials@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" - integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== - -ts-jest@^27.1.2: - version "27.1.2" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.2.tgz#5991d6eb3fd8e1a8d4b8f6de3ec0a3cc567f3151" - integrity sha512-eSOiJOWq6Hhs6Khzk5wKC5sgWIXgXqOCiIl1+3lfnearu58Hj4QpE5tUhQcA3xtZrELbcvAGCsd6HB8OsaVaTA== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^27.0.0" - json5 "2.x" - lodash.memoize "4.x" - make-error "1.x" - semver "7.x" - yargs-parser "20.x" - -ts-mixer@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.1.tgz#7c2627fb98047eb5f3c7f2fee39d1521d18fe87a" - integrity sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg== - -tslib@^2.1.0, tslib@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" +tslib@^2.1.0, tslib@^2.5.0, tslib@^2.6.2, tslib@^2.6.3: + version "2.8.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" + integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== type-detect@4.0.8: version "4.0.8" @@ -2907,129 +4306,141 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== -typeorm@^0.2.44: - version "0.2.44" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.44.tgz#4cc07eb1eb7a0e7f3ec9e65ded9eb3c3aedbb3e1" - integrity sha512-yFyb9Ts73vGaS/O06TvLpzvT5U/ngO31GeciNc0eoH7P1QcG8kVZdOy9FHJqkTeDmIljMRgWjbYUoMw53ZY7Xw== +type-fest@^2.5.1: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +type-fest@^3.0.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + +type-fest@^4.18.2, type-fest@^4.21.0, type-fest@^4.6.0, type-fest@^4.7.1: + version "4.26.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.26.1.tgz#a4a17fa314f976dd3e6d6675ef6c775c16d7955e" + integrity sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg== + +typeorm@^0.3.20: + version "0.3.20" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.20.tgz#4b61d737c6fed4e9f63006f88d58a5e54816b7ab" + integrity sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q== dependencies: - "@sqltools/formatter" "^1.2.2" - app-root-path "^3.0.0" + "@sqltools/formatter" "^1.2.5" + app-root-path "^3.1.0" buffer "^6.0.3" - chalk "^4.1.0" + chalk "^4.1.2" cli-highlight "^2.1.11" - debug "^4.3.1" - dotenv "^8.2.0" - glob "^7.1.6" - js-yaml "^4.0.0" - mkdirp "^1.0.4" - reflect-metadata "^0.1.13" + dayjs "^1.11.9" + debug "^4.3.4" + dotenv "^16.0.3" + glob "^10.3.10" + mkdirp "^2.1.3" + reflect-metadata "^0.2.1" sha.js "^2.4.11" - tslib "^2.1.0" - uuid "^8.3.2" - xml2js "^0.4.23" - yargs "^17.0.1" - zen-observable-ts "^1.0.0" + tslib "^2.5.0" + uuid "^9.0.0" + yargs "^17.6.2" -typescript@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" - integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== +typescript@^5.0.0: + version "5.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== -universalify@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +undici-types@~6.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -util-deprecate@~1.0.1: +undici@6.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici/-/undici-6.19.8.tgz#002d7c8a28f8cc3a44ff33c3d4be4d85e15d40e1" + integrity sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g== + +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.0" + +update-notifier@^7.0.0: + version "7.3.1" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-7.3.1.tgz#49af1ad6acfa0ea01c0d0f3c04047c154ead7096" + integrity sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA== + dependencies: + boxen "^8.0.1" + chalk "^5.3.0" + configstore "^7.0.0" + is-in-ci "^1.0.0" + is-installed-globally "^1.0.0" + is-npm "^6.0.0" + latest-version "^9.0.0" + pupa "^3.1.0" + semver "^7.6.3" + xdg-basedir "^5.1.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== -v8-to-istanbul@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz#0aeb763894f1a0a1676adf8a8b7612a38902446c" - integrity sha512-/PRhfd8aTNp9Ggr62HPzXg2XasNFGy5PBt0Rp04du7/8GNNSgxFL6WBTkgMKSL9bFjH+8kKEG3f37FmxiTqUUA== +v8-to-istanbul@^9.0.1: + version "9.3.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: + "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - source-map "^0.7.3" + convert-source-map "^2.0.0" -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== +validate-npm-package-license@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: - browser-process-hrtime "^1.0.0" + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" +validate-npm-package-name@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" + integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== -walker@^1.0.7: +walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: - iconv-lite "0.4.24" + defaults "^1.0.3" -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" +when-exit@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/when-exit/-/when-exit-2.1.3.tgz#5831cdbed8ad4984645da98c4a00d4ee3a3757e7" + integrity sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw== which@^2.0.1: version "2.0.2" @@ -3038,10 +4449,38 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +widest-line@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-5.0.0.tgz#b74826a1e480783345f0cd9061b49753c9da70d0" + integrity sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA== + dependencies: + string-width "^7.0.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" + integrity sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" wrap-ansi@^7.0.0: version "7.0.0" @@ -3052,75 +4491,68 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" + signal-exit "^3.0.7" -ws@^7.4.6: - version "7.5.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" - integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== +ws@^8.16.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== -ws@^8.4.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xml2js@^0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xdg-basedir@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-5.1.0.tgz#1efba19425e73be1bc6f2a6ceb52a3d2c884c0c9" + integrity sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ== y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs-parser@20.x, yargs-parser@^20.2.2: +yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^21.0.0: - version "21.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" - integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^16.0.0, yargs@^16.2.0: +yargs@^16.0.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -3133,33 +4565,25 @@ yargs@^16.0.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.0.1: - version "17.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" - integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== +yargs@^17.3.1, yargs@^17.6.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: - cliui "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^21.0.0" + yargs-parser "^21.1.1" -zen-observable-ts@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83" - integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA== - dependencies: - "@types/zen-observable" "0.8.3" - zen-observable "0.8.15" +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zen-observable@0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" - integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== - -zod@^3.11.6: - version "3.14.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.4.tgz#e678fe9e5469f4663165a5c35c8f3dc66334a5d6" - integrity sha512-U9BFLb2GO34Sfo9IUYp0w3wJLlmcyGoMd75qU9yf+DrdGA4kEx6e+l9KOkAlyUO0PSQzZCa3TR4qVlcmwqSDuw== +yoctocolors-cjs@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" + integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==