diff --git a/.env.example b/.env.example
index dc7d96e..d25e432 100644
--- a/.env.example
+++ b/.env.example
@@ -32,9 +32,8 @@ DB_AUTH_PASS=
 DB_SYNC=
 DB_LOGGING=
 DB_DATA_LOCATION=./.temp/database
-
-DB_CARD_FILE=:memory:
+DB_ROOT_HOST=0.0.0.0
 
 EXPRESS_PORT=3302
 
-GDRIVESYNC_AUTO=true
+GDRIVESYNC_AUTO=false
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 29f04b8..0000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,45 +0,0 @@
-{
-    "env": {
-        "browser": true,
-        "es2021": true
-    },
-    "extends": [
-        "eslint:recommended",
-        "plugin:@typescript-eslint/recommended"
-    ],
-    "parser": "@typescript-eslint/parser",
-    "parserOptions": {
-        "ecmaVersion": "latest",
-        "sourceType": "module"
-    },
-    "plugins": [
-        "@typescript-eslint"
-    ],
-    "rules": {
-        "indent": [
-            "error",
-            4
-        ],
-        "linebreak-style": [
-            "error",
-            "unix"
-        ],
-        "quotes": [
-            "error",
-            "double"
-        ],
-        "semi": [
-            "error",
-            "always"
-        ]
-    },
-    "globals": {
-        "jest": true,
-        "require": true,
-        "exports": true,
-        "process": true
-    },
-    "ignorePatterns": [
-        "dist/**/*"
-    ]
-}
diff --git a/.forgejo/workflows/production.yml b/.forgejo/workflows/production.yml
index 110f5e8..cb4ed46 100644
--- a/.forgejo/workflows/production.yml
+++ b/.forgejo/workflows/production.yml
@@ -30,7 +30,7 @@ jobs:
     needs: build
     runs-on: node
     steps:
-    - uses: https://github.com/appleboy/ssh-action@v1.0.3
+    - 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 }}
diff --git a/.forgejo/workflows/stage.yml b/.forgejo/workflows/stage.yml
index 149a78e..b6c27da 100644
--- a/.forgejo/workflows/stage.yml
+++ b/.forgejo/workflows/stage.yml
@@ -30,7 +30,7 @@ jobs:
     needs: build
     runs-on: node
     steps:
-    - uses: https://github.com/appleboy/ssh-action@v1.0.3
+    - 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 }}
diff --git a/database/0.9/1729962056556-createUserEffect/Down/01-table-userEffect.sql b/database/0.9/1729962056556-createUserEffect/Down/01-table-userEffect.sql
new file mode 100644
index 0000000..ca2a800
--- /dev/null
+++ b/database/0.9/1729962056556-createUserEffect/Down/01-table-userEffect.sql
@@ -0,0 +1 @@
+DROP TABLE `user_effect`;
diff --git a/database/0.9/1729962056556-createUserEffect/Up/01-table-userEffect.sql b/database/0.9/1729962056556-createUserEffect/Up/01-table-userEffect.sql
new file mode 100644
index 0000000..17c1811
--- /dev/null
+++ b/database/0.9/1729962056556-createUserEffect/Up/01-table-userEffect.sql
@@ -0,0 +1,10 @@
+CREATE TABLE `user_effect` (
+    `Id` varchar(255) NOT NULL,
+    `WhenCreated` datetime NOT NULL,
+    `WhenUpdated` datetime NOT NULL,
+    `Name` varchar(255) NOT NULL,
+    `UserId` varchar(255) NOT NULL,
+    `Unused` int NOT NULL DEFAULT 0,
+    `WhenExpires` datetime NULL,
+    PRIMARY KEY (`Id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
diff --git a/docs/google-drive-sync.md b/docs/google-drive-sync.md
new file mode 100644
index 0000000..1847f70
--- /dev/null
+++ b/docs/google-drive-sync.md
@@ -0,0 +1,35 @@
+# Google Drive Sync
+
+The bot relies on an external sync between the local file system and Google
+Drive in order to get newer cards to the bot. This is done using
+[Rclone](https://rclone.org/).
+
+The process for this is done by once the `/gdrivesync` command is executed by
+an admin user of the bot, which calls the system shell to run rclone to the
+card folder.
+
+- The admins who can run the command is specifed in `$BOT_ADMINS`, which are
+discord user ids separated by commas.
+- The card folder is located at `$DATA_DIR/cards`.
+- The source requires rclone's remote to be setup as `card-drop-gdrive`.
+
+The exact command it runs is: `rclone sync card-drop-gdrive: $DATA_DIR/cards`.
+
+Once it syncs the database will reread all the cards for updates and then load
+them into the bot to be given.
+
+## Safe Mode
+Safe mode is a function of the bot which disables the `/drop` command function
+and any other functions which rely on the card metadata. Safe mode is activated
+upon failure to sync properly. It is disabled once errors are resolved.
+
+The reason for safe mode is to ensure that the bot stays online for admins to
+be able to resync the bot in case there's an error without it crashing.
+
+## Google Drive
+Please see the Rclone documentation on how to setup a remote using Google
+Drive. You will need to make an app password for this.
+
+- scope: `drive.readonly`
+- root\_folder\_id: The folder id where the cards are located, this can be found
+  by looking at the url when viewing the folder in the browser in google drive.
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..64bf3e2
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,55 @@
+import js from "@eslint/js";
+import ts from "typescript-eslint";
+
+export default [
+    {
+        ignores: [
+            "**/dist/",
+            "eslint.config.mjs",
+            "jest.config.cjs",
+            "jest.setup.js",
+            "**/.temp/**/*"
+        ],
+    },
+    js.configs.recommended,
+    ...ts.configs.recommended,
+    {
+        languageOptions: {
+            globals: {
+                exports: "writable",
+                module: "writable",
+                require: "writable",
+                process: "writable",
+                console: "writable",
+                jest: "writable",
+            },
+    
+            ecmaVersion: 6,
+            sourceType: "script",
+        },
+    
+        files: [
+            "./src",
+            "./tests"
+        ],
+    
+        rules: {
+            camelcase: "error",
+            "brace-style": ["error", "1tbs"],
+            "comma-dangle": ["error", "never"],
+    
+            "comma-spacing": ["error", {
+                before: false,
+                after: true,
+            }],
+    
+            "comma-style": ["error", "last"],
+            "arrow-body-style": ["error", "as-needed"],
+            "arrow-parens": ["error", "as-needed"],
+            "arrow-spacing": "error",
+            "no-var": "error",
+            "prefer-template": "error",
+            "prefer-const": "error",
+        },
+    }
+];
diff --git a/jest.setup.js b/jest.setup.js
index d583d1a..8e9ae9a 100644
--- a/jest.setup.js
+++ b/jest.setup.js
@@ -1,3 +1,4 @@
 jest.setTimeout(1 * 1000); // 1 second
 jest.resetModules();
-jest.resetAllMocks();
\ No newline at end of file
+jest.resetAllMocks();
+jest.useFakeTimers();
\ No newline at end of file
diff --git a/package.json b/package.json
index 0e0ce30..5a0ef42 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
 {
   "name": "card-drop",
-  "version": "0.8.4",
+  "version": "0.9.0",
   "main": "./dist/bot.js",
   "typings": "./dist",
   "scripts": {
     "clean": "rm -rf node_modules/ dist/",
     "build": "tsc",
     "start": "node ./dist/bot.js",
-    "test": "echo true",
+    "test": "jest",
     "lint": "eslint .",
     "lint:fix": "eslint . --fix",
     "db:up": "typeorm migration:run -d dist/database/dataSources/appDataSource.js",
@@ -27,18 +27,19 @@
   "dependencies": {
     "@discordjs/rest": "^2.0.0",
     "@types/clone-deep": "^4.0.4",
-    "@types/express": "^4.17.20",
-    "@types/jest": "^29.0.0",
-    "@types/uuid": "^9.0.0",
+    "@types/express": "^5.0.0",
+    "@types/jest": "^29.5.14",
+    "@types/uuid": "^10.0.0",
     "axios": "^1.8.4",
     "body-parser": "^1.20.2",
     "canvas": "^2.11.2",
     "clone-deep": "^4.0.1",
     "cron": "^3.1.7",
-    "discord.js": "^14.15.3",
+    "discord.js": "^14.16.3",
     "dotenv": "^16.0.0",
     "express": "^4.18.2",
-    "glob": "^10.3.10",
+    "fuse.js": "^7.0.0",
+    "glob": "^11.0.0",
     "jest": "^29.0.0",
     "jest-mock-extended": "^3.0.0",
     "jimp": "^1.6.0",
@@ -46,19 +47,18 @@
     "mysql": "^2.18.1",
     "ts-jest": "^29.0.0",
     "typeorm": "0.3.20",
-    "winston": "^3.11.0",
+    "winston": "^3.15.0",
     "winston-daily-rotate-file": "^5.0.0",
     "winston-discord-transport": "^1.3.0"
   },
-  "resolutions": {
-    "**/ws": "^8.17.1"
-  },
+  "resolutions": {},
   "devDependencies": {
-    "@types/node": "^20.0.0",
-    "@typescript-eslint/eslint-plugin": "^7.0.0",
-    "@typescript-eslint/parser": "^6.16.0",
-    "eslint": "^8.56.0",
-    "np": "^9.0.0",
-    "typescript": "^5.0.0"
+    "@types/node": "^22.8.1",
+    "@typescript-eslint/eslint-plugin": "^8.11.0",
+    "@typescript-eslint/parser": "^8.11.0",
+    "eslint": "^9.13.0",
+    "np": "^10.0.7",
+    "typescript": "^5.0.0",
+    "typescript-eslint": "^8.11.0"
   }
 }
diff --git a/src/bot.ts b/src/bot.ts
index e5e25a3..871b927 100644
--- a/src/bot.ts
+++ b/src/bot.ts
@@ -38,6 +38,7 @@ const client = new CoreClient([
 
 Registry.RegisterCommands();
 Registry.RegisterButtonEvents();
+Registry.RegisterStringDropdownEvents();
 
 if (!existsSync(`${process.env.DATA_DIR}/cards`) && process.env.GDRIVESYNC_AUTO && process.env.GDRIVESYNC_AUTO == "true") {
     console.log("Card directory not found, syncing...");
diff --git a/src/buttonEvents/Claim.ts b/src/buttonEvents/Claim.ts
index 4d5d97a..d139f42 100644
--- a/src/buttonEvents/Claim.ts
+++ b/src/buttonEvents/Claim.ts
@@ -1,17 +1,18 @@
 import { ButtonInteraction } from "discord.js";
 import { ButtonEvent } from "../type/buttonEvent";
 import Inventory from "../database/entities/app/Inventory";
-import { CoreClient } from "../client/client";
 import { default as eClaim } from "../database/entities/app/Claim";
 import AppLogger from "../client/appLogger";
-import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
 import User from "../database/entities/app/User";
 import CardConstants from "../constants/CardConstants";
+import GetCardsHelper from "../helpers/DropHelpers/GetCardsHelper";
+import DropEmbedHelper from "../helpers/DropHelpers/DropEmbedHelper";
 
 export default class Claim extends ButtonEvent {
     public override async execute(interaction: ButtonInteraction) {
         if (!interaction.guild || !interaction.guildId) return;
         if (!interaction.channel) return;
+        if (!interaction.channel.isSendable()) return;
 
         await interaction.deferUpdate();
 
@@ -21,10 +22,10 @@ export default class Claim extends ButtonEvent {
         const userId = interaction.user.id;
 
         const whenDropped = interaction.message.createdAt;
-        const lastClaimableDate = new Date(Date.now() - (1000 * 60 * 5)); // 5 minutes ago
+        const lastClaimableDate = new Date(Date.now() - (1000 * 60 * 2)); // 2 minutes ago
 
         if (whenDropped < lastClaimableDate) {
-            await interaction.channel.send(`${interaction.user}, Cards can only be claimed within 5 minutes of it being dropped!`);
+            await interaction.channel.send(`${interaction.user}, Cards can only be claimed within 2 minutes of it being dropped!`);
             return;
         }
 
@@ -34,11 +35,6 @@ export default class Claim extends ButtonEvent {
 
         AppLogger.LogSilly("Button/Claim", `${user.Id} has ${user.Currency} currency`);
 
-        if (!user.RemoveCurrency(CardConstants.ClaimCost)) {
-            await interaction.channel.send(`${interaction.user}, Not enough currency! You need ${CardConstants.ClaimCost} currency, you have ${user.Currency}!`);
-            return;
-        }
-
         const claimed = await eClaim.FetchOneByClaimId(claimId);
 
         if (claimed) {
@@ -46,19 +42,12 @@ export default class Claim extends ButtonEvent {
             return;
         }
 
-        if (claimId == CoreClient.ClaimId && userId != droppedBy) {
-            await interaction.channel.send(`${interaction.user}, The latest dropped card can only be claimed by the user who dropped it!`);
-            return;
-        }
-
-        await user.Save(User, user);
-
         let inventory = await Inventory.FetchOneByCardNumberAndUserId(userId, cardNumber);
 
         if (!inventory) {
             inventory = new Inventory(userId, cardNumber, 1);
         } else {
-            inventory.SetQuantity(inventory.Quantity + 1);
+            inventory.AddQuantity(1);
         }
 
         await inventory.Save(Inventory, inventory);
@@ -68,16 +57,18 @@ export default class Claim extends ButtonEvent {
 
         await claim.Save(eClaim, claim);
 
-        const card = CardDropHelperMetadata.GetCardByCardNumber(cardNumber);
+        const card = GetCardsHelper.GetCardByCardNumber(cardNumber);
 
         if (!card) {
+            AppLogger.LogError("Button/Claim", `Unable to find card, ${cardNumber}`);
+
             return;
         }
 
         const imageFileName = card.card.path.split("/").pop()!;
 
-        const embed = CardDropHelperMetadata.GenerateDropEmbed(card, inventory.Quantity, imageFileName, interaction.user.username, user.Currency);
-        const row = CardDropHelperMetadata.GenerateDropButtons(card, claimId, interaction.user.id, true);
+        const embed = DropEmbedHelper.GenerateDropEmbed(card, inventory.Quantity, imageFileName, interaction.user.username, user.Currency);
+        const row = DropEmbedHelper.GenerateDropButtons(card, claimId, interaction.user.id, true);
 
         await interaction.editReply({
             embeds: [ embed ],
diff --git a/src/buttonEvents/Effects.ts b/src/buttonEvents/Effects.ts
new file mode 100644
index 0000000..cd1a765
--- /dev/null
+++ b/src/buttonEvents/Effects.ts
@@ -0,0 +1,26 @@
+import { ButtonInteraction } from "discord.js";
+import { ButtonEvent } from "../type/buttonEvent";
+import List from "./Effects/List";
+import Use from "./Effects/Use";
+import AppLogger from "../client/appLogger";
+import Buy from "./Effects/Buy";
+
+export default class Effects extends ButtonEvent {
+    public override async execute(interaction: ButtonInteraction) {
+        const action = interaction.customId.split(" ")[1];
+
+        switch (action) {
+            case "list":
+                await List(interaction);
+                break;
+            case "use":
+                await Use.Execute(interaction);
+                break;
+            case "buy":
+                await Buy.Execute(interaction);
+                break;
+            default:
+                AppLogger.LogError("Buttons/Effects", `Unknown action, ${action}`);
+        }
+    }
+}
diff --git a/src/buttonEvents/Effects/Buy.ts b/src/buttonEvents/Effects/Buy.ts
new file mode 100644
index 0000000..49cc75f
--- /dev/null
+++ b/src/buttonEvents/Effects/Buy.ts
@@ -0,0 +1,120 @@
+import {ButtonInteraction} from "discord.js";
+import AppLogger from "../../client/appLogger";
+import EffectHelper from "../../helpers/EffectHelper";
+import EmbedColours from "../../constants/EmbedColours";
+import User from "../../database/entities/app/User";
+import {EffectDetails} from "../../constants/EffectDetails";
+
+export default class Buy {
+    public static async Execute(interaction: ButtonInteraction) {
+        const subaction = interaction.customId.split(" ")[2];
+
+        switch (subaction) {
+            case "confirm":
+                await this.Confirm(interaction);
+                break;
+            case "cancel":
+                await this.Cancel(interaction);
+                break;
+            default:
+                AppLogger.LogError("Buy", `Unknown subaction, effects ${subaction}`);
+        }
+    }
+
+    private static async Confirm(interaction: ButtonInteraction) {
+        const id = interaction.customId.split(" ")[3];
+        const quantity = interaction.customId.split(" ")[4];
+
+        if (!id || !quantity) {
+            AppLogger.LogError("Buy Confirm", "Not enough parameters");
+            return;
+        }
+        
+        const effectDetail = EffectDetails.get(id);
+
+        if (!effectDetail) {
+            AppLogger.LogError("Buy Confirm", "Effect detail not found!");
+            return;
+        }
+
+        const quantityNumber = Number(quantity);
+        
+        if (!quantityNumber || quantityNumber < 1) {
+            AppLogger.LogError("Buy Confirm", "Invalid number");
+            return;
+        }
+
+        const totalCost = effectDetail.cost * quantityNumber;
+
+        const user = await User.FetchOneById(User, interaction.user.id);
+
+        if (!user) {
+            AppLogger.LogError("Buy Confirm", "Unable to find user");
+            return;
+        }
+
+        if (user.Currency < totalCost) {
+            interaction.reply(`You don't have enough currency to buy this! You have \`${user.Currency} Currency\` and need \`${totalCost} Currency\`!`);
+            return;
+        }
+
+        user.RemoveCurrency(totalCost);
+        await user.Save(User, user);
+
+        await EffectHelper.AddEffectToUserInventory(interaction.user.id, id, quantityNumber);
+
+        const generatedEmbed = await EffectHelper.GenerateEffectBuyEmbed(interaction.user.id, id, quantityNumber, true);
+
+        if (typeof generatedEmbed == "string") {
+            await interaction.reply(generatedEmbed);
+            return;
+        }
+
+        generatedEmbed.embed.setColor(EmbedColours.Success);
+        generatedEmbed.embed.setFooter({ text: "Purchased" });
+
+        await interaction.update({
+            embeds: [ generatedEmbed.embed ],
+            components: [ generatedEmbed.row ],
+        });
+    }
+
+    private static async Cancel(interaction: ButtonInteraction) {
+        const id = interaction.customId.split(" ")[3];
+        const quantity = interaction.customId.split(" ")[4];
+
+        if (!id || !quantity) {
+            AppLogger.LogError("Buy Cancel", "Not enough parameters");
+            return;
+        }
+        
+        const effectDetail = EffectDetails.get(id);
+
+        if (!effectDetail) {
+            AppLogger.LogError("Buy Cancel", "Effect detail not found!");
+            return;
+        }
+
+        const quantityNumber = Number(quantity);
+        
+        if (!quantityNumber || quantityNumber < 1) {
+            AppLogger.LogError("Buy Cancel", "Invalid number");
+            return;
+        }
+
+        const generatedEmbed = await EffectHelper.GenerateEffectBuyEmbed(interaction.user.id, id, quantityNumber, true);
+
+        if (typeof generatedEmbed == "string") {
+            await interaction.reply(generatedEmbed);
+            return;
+        }
+
+        generatedEmbed.embed.setColor(EmbedColours.Error);
+        generatedEmbed.embed.setFooter({ text: "Cancelled" });
+
+        await interaction.update({
+            embeds: [ generatedEmbed.embed ],
+            components: [ generatedEmbed.row ],
+        });
+    }
+}
diff --git a/src/buttonEvents/Effects/List.ts b/src/buttonEvents/Effects/List.ts
new file mode 100644
index 0000000..d86dfce
--- /dev/null
+++ b/src/buttonEvents/Effects/List.ts
@@ -0,0 +1,20 @@
+import { ButtonInteraction } from "discord.js";
+import EffectHelper from "../../helpers/EffectHelper";
+
+export default async function List(interaction: ButtonInteraction) {
+    const pageOption = interaction.customId.split(" ")[2];
+
+    const page = Number(pageOption);
+
+    if (!page) {
+        await interaction.reply("Page option is not a valid number");
+        return;
+    }
+
+    const result = await EffectHelper.GenerateEffectListEmbed(interaction.user.id, page);
+
+    await interaction.update({
+        embeds: [ result.embed ],
+        components: [ result.row ],
+    });
+}
\ No newline at end of file
diff --git a/src/buttonEvents/Effects/Use.ts b/src/buttonEvents/Effects/Use.ts
new file mode 100644
index 0000000..5bfe2fd
--- /dev/null
+++ b/src/buttonEvents/Effects/Use.ts
@@ -0,0 +1,132 @@
+import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, EmbedBuilder } from "discord.js";
+import { EffectDetails } from "../../constants/EffectDetails";
+import EffectHelper from "../../helpers/EffectHelper";
+import EmbedColours from "../../constants/EmbedColours";
+import TimeLengthInput from "../../helpers/TimeLengthInput";
+import AppLogger from "../../client/appLogger";
+
+export default class Use {
+    public static async Execute(interaction: ButtonInteraction) {
+        const subaction = interaction.customId.split(" ")[2];
+
+        switch (subaction) {
+            case "confirm":
+                await this.UseConfirm(interaction);
+                break;
+            case "cancel":
+                await this.UseCancel(interaction);
+                break;
+        }
+    }
+
+    private static async UseConfirm(interaction: ButtonInteraction) {
+        const id = interaction.customId.split(" ")[3];
+
+        const effectDetail = EffectDetails.get(id);
+
+        if (!effectDetail) {
+            AppLogger.LogError("Button/Effects/Use", `Effect not found, ${id}`);
+
+            await interaction.reply("Effect not found in system!");
+            return;
+        }
+
+        const now = new Date();
+
+        const whenExpires = new Date(now.getTime() + effectDetail.duration);
+
+        const result = await EffectHelper.UseEffect(interaction.user.id, id, whenExpires);
+
+        if (!result) {
+            await interaction.reply("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown");
+            return;
+        }
+
+        const embed = new EmbedBuilder()
+            .setTitle("Effect Used")
+            .setDescription("You now have an active effect!")
+            .setColor(EmbedColours.Green)
+            .addFields([
+                {
+                    name: "Effect",
+                    value: effectDetail.friendlyName,
+                    inline: true,
+                },
+                {
+                    name: "Expires",
+                    value: `<t:${Math.round(whenExpires.getTime() / 1000)}:f>`,
+                    inline: true,
+                },
+            ]);
+
+        const row = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents([
+                new ButtonBuilder()
+                    .setLabel("Confirm")
+                    .setCustomId(`effects use confirm ${effectDetail.id}`)
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(true),
+                new ButtonBuilder()
+                    .setLabel("Cancel")
+                    .setCustomId(`effects use cancel ${effectDetail.id}`)
+                    .setStyle(ButtonStyle.Danger)
+                    .setDisabled(true),
+            ]);
+
+        await interaction.update({
+            embeds: [ embed ],
+            components: [ row ],
+        });
+    }
+
+    private static async UseCancel(interaction: ButtonInteraction) {
+        const id = interaction.customId.split(" ")[3];
+
+        const effectDetail = EffectDetails.get(id);
+
+        if (!effectDetail) {
+            AppLogger.LogError("Button/Effects/Cancel", `Effect not found, ${id}`);
+
+            await interaction.reply("Effect not found in system!");
+            return;
+        }
+
+        const timeLengthInput = TimeLengthInput.ConvertFromMilliseconds(effectDetail.duration);
+
+        const embed = new EmbedBuilder()
+            .setTitle("Effect Use Cancelled")
+            .setDescription("The effect from your inventory has not been used")
+            .setColor(EmbedColours.Grey)
+            .addFields([
+                {
+                    name: "Effect",
+                    value: effectDetail.friendlyName,
+                    inline: true,
+                },
+                {
+                    name: "Expires",
+                    value: timeLengthInput.GetLengthShort(),
+                    inline: true,
+                },
+            ]);
+
+        const row = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents([
+                new ButtonBuilder()
+                    .setLabel("Confirm")
+                    .setCustomId(`effects use confirm ${effectDetail.id}`)
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(true),
+                new ButtonBuilder()
+                    .setLabel("Cancel")
+                    .setCustomId(`effects use cancel ${effectDetail.id}`)
+                    .setStyle(ButtonStyle.Danger)
+                    .setDisabled(true),
+            ]);
+
+        await interaction.update({
+            embeds: [ embed ],
+            components: [ row ],
+        });
+    }
+}
diff --git a/src/buttonEvents/Inventory.ts b/src/buttonEvents/Inventory.ts
index 8356305..73c7019 100644
--- a/src/buttonEvents/Inventory.ts
+++ b/src/buttonEvents/Inventory.ts
@@ -11,7 +11,7 @@ export default class Inventory extends ButtonEvent {
         const page = interaction.customId.split(" ")[2];
 
         AppLogger.LogSilly("Button/Inventory", `Parameters: userid=${userid}, page=${page}`);
-        
+
         await interaction.deferUpdate();
 
         const member = interaction.guild.members.cache.find(x => x.id == userid) || await interaction.guild.members.fetch(userid);
@@ -34,7 +34,7 @@ export default class Inventory extends ButtonEvent {
             await interaction.editReply({
                 files: [ embed.image ],
                 embeds: [ embed.embed ],
-                components: [ embed.row ],
+                components: [ embed.row1, embed.row2 ],
             });
         } catch (e) {
             AppLogger.LogError("Button/Inventory", `Error generating inventory page for ${member.user.username} with id ${member.user.id}: ${e}`);
diff --git a/src/buttonEvents/Multidrop.ts b/src/buttonEvents/Multidrop.ts
new file mode 100644
index 0000000..e6ea7c2
--- /dev/null
+++ b/src/buttonEvents/Multidrop.ts
@@ -0,0 +1,214 @@
+import { AttachmentBuilder, ButtonInteraction, EmbedBuilder } from "discord.js";
+import { ButtonEvent } from "../type/buttonEvent";
+import AppLogger from "../client/appLogger";
+import Inventory from "../database/entities/app/Inventory";
+import EmbedColours from "../constants/EmbedColours";
+import { readFileSync } from "fs";
+import path from "path";
+import ErrorMessages from "../constants/ErrorMessages";
+import User from "../database/entities/app/User";
+import { GetSacrificeAmount } from "../constants/CardRarity";
+import GetCardsHelper from "../helpers/DropHelpers/GetCardsHelper";
+import MultidropEmbedHelper from "../helpers/DropHelpers/MultidropEmbedHelper";
+
+export default class Multidrop extends ButtonEvent {
+    public override async execute(interaction: ButtonInteraction) {
+        const action = interaction.customId.split(" ")[1];
+
+        switch (action) {
+        case "keep":
+            await this.Keep(interaction);
+            break;
+        case "sacrifice":
+            await this.Sacrifice(interaction);
+            break;
+        default:
+            await interaction.reply("Invalid action");
+            AppLogger.LogError("Button/Multidrop", `Invalid action, ${action}`);
+        }
+    }
+
+    private async Keep(interaction: ButtonInteraction) {
+        const cardNumber = interaction.customId.split(" ")[2];
+        let cardsRemaining = Number(interaction.customId.split(" ")[3]) || 0;
+        const userId = interaction.customId.split(" ")[4];
+
+        if (interaction.user.id != userId) {
+            await interaction.reply("You're not the user this drop was made for!");
+            return;
+        }
+
+        const card = GetCardsHelper.GetCardByCardNumber(cardNumber);
+
+        if (!card) {
+            await interaction.reply("Unable to find card.");
+            AppLogger.LogWarn("Button/Multidrop/Keep", `Card not found, ${cardNumber}`);
+            return;
+        }
+
+        if (cardsRemaining < 0) {
+            await interaction.reply("Your multidrop has ran out! Please buy a new one!");
+            return;
+        }
+
+        const user = await User.FetchOneById(User, interaction.user.id);
+
+        if (!user) {
+            AppLogger.LogWarn("Button/Multidrop/Keep", ErrorMessages.UnableToFetchUser);
+            await interaction.reply(ErrorMessages.UnableToFetchUser);
+            return;
+        }
+
+        // Claim
+        let inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, cardNumber);
+
+        if (!inventory) {
+            inventory = new Inventory(interaction.user.id, cardNumber, 1);
+        } else {
+            inventory.AddQuantity(1);
+        }
+
+        await inventory.Save(Inventory, inventory);
+
+        // Pack has ran out
+        if (cardsRemaining == 0) {
+            const embed = new EmbedBuilder()
+                .setDescription("Your multidrop has ran out! Please buy a new one!")
+                .setColor(EmbedColours.Ok);
+
+            await interaction.update({
+                embeds: [ embed ],
+                attachments: [],
+                components: [],
+            });
+
+            return;
+        }
+
+        // Drop next card
+        const randomCard = GetCardsHelper.GetRandomCard();
+        cardsRemaining -= 1;
+
+        if (!randomCard) {
+            AppLogger.LogWarn("Button/Multidrop/Keep", ErrorMessages.UnableToFetchCard);
+            await interaction.reply(ErrorMessages.UnableToFetchCard);
+            return;
+        }
+
+        await interaction.deferUpdate();
+
+        try {
+            const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", randomCard.card.path));
+            const imageFileName = randomCard.card.path.split("/").pop()!;
+
+            const attachment = new AttachmentBuilder(image, { name: imageFileName });
+
+            const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.card.id);
+            const quantityClaimed = inventory ? inventory.Quantity : 0;
+
+            const embed = MultidropEmbedHelper.GenerateMultidropEmbed(randomCard, quantityClaimed, imageFileName, cardsRemaining, undefined, user.Currency);
+
+            const row = MultidropEmbedHelper.GenerateMultidropButtons(randomCard, cardsRemaining, interaction.user.id, cardsRemaining < 0);
+
+            await interaction.editReply({
+                embeds: [ embed ],
+                files: [ attachment ],
+                components: [ row ],
+            });
+        } catch (e) {
+            AppLogger.LogError("Button/Multidrop/Keep", `Error sending next drop for card ${randomCard.card.id}: ${e}`);
+
+            await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`);
+        }
+    }
+
+    private async Sacrifice(interaction: ButtonInteraction) {
+        const cardNumber = interaction.customId.split(" ")[2];
+        let cardsRemaining = Number(interaction.customId.split(" ")[3]) || 0;
+        const userId = interaction.customId.split(" ")[4];
+
+        if (interaction.user.id != userId) {
+            await interaction.reply("You're not the user this drop was made for!");
+            return;
+        }
+
+        const card = GetCardsHelper.GetCardByCardNumber(cardNumber);
+
+        if (!card) {
+            await interaction.reply("Unable to find card.");
+            AppLogger.LogWarn("Button/Multidrop/Sacrifice", `Card not found, ${cardNumber}`);
+            return;
+        }
+
+        if (cardsRemaining < 0) {
+            await interaction.reply("Your multidrop has ran out! Please buy a new one!");
+            return;
+        }
+
+        const user = await User.FetchOneById(User, interaction.user.id);
+
+        if (!user) {
+            AppLogger.LogWarn("Button/Multidrop/Sacrifice", ErrorMessages.UnableToFetchUser);
+            await interaction.reply(ErrorMessages.UnableToFetchUser);
+            return;
+        }
+
+        // Sacrifice
+        const sacrificeAmount = GetSacrificeAmount(card.card.type);
+
+        user.AddCurrency(sacrificeAmount);
+
+        await user.Save(User, user);
+
+        // Pack has ran out
+        if (cardsRemaining == 0) {
+            const embed = new EmbedBuilder()
+                .setDescription("Your multidrop has ran out! Please buy a new one!")
+                .setColor(EmbedColours.Ok);
+
+            await interaction.update({
+                embeds: [ embed ],
+                attachments: [],
+                components: [],
+            });
+
+            return;
+        }
+
+        // Drop next card
+        const randomCard = GetCardsHelper.GetRandomCard();
+        cardsRemaining -= 1;
+
+        if (!randomCard) {
+            AppLogger.LogWarn("Button/Multidrop/Sacrifice", ErrorMessages.UnableToFetchCard);
+            await interaction.reply(ErrorMessages.UnableToFetchCard);
+            return;
+        }
+
+        await interaction.deferUpdate();
+
+        try {
+            const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", randomCard.card.path));
+            const imageFileName = randomCard.card.path.split("/").pop()!;
+
+            const attachment = new AttachmentBuilder(image, { name: imageFileName });
+
+            const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.card.id);
+            const quantityClaimed = inventory ? inventory.Quantity : 0;
+
+            const embed = MultidropEmbedHelper.GenerateMultidropEmbed(randomCard, quantityClaimed, imageFileName, cardsRemaining, undefined, user.Currency);
+
+            const row = MultidropEmbedHelper.GenerateMultidropButtons(randomCard, cardsRemaining, interaction.user.id, cardsRemaining < 0);
+
+            await interaction.editReply({
+                embeds: [ embed ],
+                files: [ attachment ],
+                components: [ row ],
+            });
+        } catch (e) {
+            AppLogger.LogError("Button/Multidrop/Sacrifice", `Error sending next drop for card ${randomCard.card.id}: ${e}`);
+
+            await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/buttonEvents/Reroll.ts b/src/buttonEvents/Reroll.ts
index dc9622a..6d97024 100644
--- a/src/buttonEvents/Reroll.ts
+++ b/src/buttonEvents/Reroll.ts
@@ -5,11 +5,12 @@ import { v4 } from "uuid";
 import { CoreClient } from "../client/client";
 import Inventory from "../database/entities/app/Inventory";
 import Config from "../database/entities/app/Config";
-import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
 import path from "path";
 import AppLogger from "../client/appLogger";
 import User from "../database/entities/app/User";
 import CardConstants from "../constants/CardConstants";
+import GetCardsHelper from "../helpers/DropHelpers/GetCardsHelper";
+import DropEmbedHelper from "../helpers/DropHelpers/DropEmbedHelper";
 
 export default class Reroll extends ButtonEvent {
     public override async execute(interaction: ButtonInteraction) {
@@ -34,12 +35,14 @@ export default class Reroll extends ButtonEvent {
             AppLogger.LogInfo("Commands/Drop", `New user (${interaction.user.id}) saved to the database`);
         }
 
-        if (user.Currency < CardConstants.ClaimCost) {
+        if (!user.RemoveCurrency(CardConstants.ClaimCost)) {
             await interaction.reply(`Not enough currency! You need ${CardConstants.ClaimCost} currency, you have ${user.Currency}!`);
             return;
         }
 
-        const randomCard = CardDropHelperMetadata.GetRandomCard();
+        await user.Save(User, user);
+
+        const randomCard = await GetCardsHelper.FetchCard(interaction.user.id);
 
         if (!randomCard) {
             await interaction.reply("Unable to fetch card, please try again.");
@@ -66,19 +69,17 @@ export default class Reroll extends ButtonEvent {
             const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.card.id);
             const quantityClaimed = inventory ? inventory.Quantity : 0;
 
-            const embed = CardDropHelperMetadata.GenerateDropEmbed(randomCard, quantityClaimed, imageFileName, undefined, user.Currency);
+            const embed = DropEmbedHelper.GenerateDropEmbed(randomCard, quantityClaimed, imageFileName, undefined, user.Currency);
 
             const claimId = v4();
 
-            const row = CardDropHelperMetadata.GenerateDropButtons(randomCard, claimId, interaction.user.id);
+            const row = DropEmbedHelper.GenerateDropButtons(randomCard, claimId, interaction.user.id);
 
             await interaction.editReply({
                 embeds: [ embed ],
                 files: files,
                 components: [ row ],
             });
-
-            CoreClient.ClaimId = claimId;
         } catch (e) {
             AppLogger.LogError("Button/Reroll", `Error sending next drop for card ${randomCard.card.id}: ${e}`);
 
diff --git a/src/buttonEvents/Sacrifice.ts b/src/buttonEvents/Sacrifice.ts
index a82ba81..de0bb77 100644
--- a/src/buttonEvents/Sacrifice.ts
+++ b/src/buttonEvents/Sacrifice.ts
@@ -1,10 +1,10 @@
 import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, EmbedBuilder } from "discord.js";
 import { ButtonEvent } from "../type/buttonEvent";
 import Inventory from "../database/entities/app/Inventory";
-import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
 import { CardRarityToString, GetSacrificeAmount } from "../constants/CardRarity";
 import EmbedColours from "../constants/EmbedColours";
 import User from "../database/entities/app/User";
+import GetCardsHelper from "../helpers/DropHelpers/GetCardsHelper";
 
 export default class Sacrifice extends ButtonEvent {
     public override async execute(interaction: ButtonInteraction) {
@@ -23,6 +23,7 @@ export default class Sacrifice extends ButtonEvent {
     private async confirm(interaction: ButtonInteraction) {
         const userId = interaction.customId.split(" ")[2];
         const cardNumber = interaction.customId.split(" ")[3];
+        const quantity = Number(interaction.customId.split(" ")[4]) || 1;
 
         if (userId != interaction.user.id) {
             await interaction.reply("Only the user who created this sacrifice can confirm it.");
@@ -31,12 +32,17 @@ export default class Sacrifice extends ButtonEvent {
 
         const cardInInventory = await Inventory.FetchOneByCardNumberAndUserId(userId, cardNumber);
 
-        if (!cardInInventory) {
+        if (!cardInInventory || cardInInventory.Quantity == 0) {
             await interaction.reply("Unable to find card in inventory.");
             return;
         }
 
-        const cardData = CardDropHelperMetadata.GetCardByCardNumber(cardNumber);
+        if (cardInInventory.Quantity < quantity) {
+            await interaction.reply("You can only sacrifice what you own.");
+            return;
+        }
+
+        const cardData = GetCardsHelper.GetCardByCardNumber(cardNumber);
 
         if (!cardData) {
             await interaction.reply("Unable to find card in the database.");
@@ -50,11 +56,11 @@ export default class Sacrifice extends ButtonEvent {
             return;
         }
 
-        cardInInventory.RemoveQuantity(1);
+        cardInInventory.RemoveQuantity(quantity);
 
         await cardInInventory.Save(Inventory, cardInInventory);
 
-        const cardValue = GetSacrificeAmount(cardData.card.type);
+        const cardValue = GetSacrificeAmount(cardData.card.type) * quantity;
         const cardRarityString = CardRarityToString(cardData.card.type);
 
         user.AddCurrency(cardValue);
@@ -66,6 +72,7 @@ export default class Sacrifice extends ButtonEvent {
             `Series: ${cardData.series.name}`,
             `Rarity: ${cardRarityString}`,
             `Quantity Owned: ${cardInInventory.Quantity}`,
+            `Quantity To Sacrifice: ${quantity}`,
             `Sacrifice Amount: ${cardValue}`,
         ];
 
@@ -98,6 +105,7 @@ export default class Sacrifice extends ButtonEvent {
     private async cancel(interaction: ButtonInteraction) {
         const userId = interaction.customId.split(" ")[2];
         const cardNumber = interaction.customId.split(" ")[3];
+        const quantity = Number(interaction.customId.split(" ")[4]) || 1;
 
         if (userId != interaction.user.id) {
             await interaction.reply("Only the user who created this sacrifice can cancel it.");
@@ -106,19 +114,24 @@ export default class Sacrifice extends ButtonEvent {
 
         const cardInInventory = await Inventory.FetchOneByCardNumberAndUserId(userId, cardNumber);
 
-        if (!cardInInventory) {
+        if (!cardInInventory || cardInInventory.Quantity == 0) {
             await interaction.reply("Unable to find card in inventory.");
             return;
         }
 
-        const cardData = CardDropHelperMetadata.GetCardByCardNumber(cardNumber);
+        if (cardInInventory.Quantity < quantity) {
+            await interaction.reply("You can only sacrifice what you own.");
+            return;
+        }
+
+        const cardData = GetCardsHelper.GetCardByCardNumber(cardNumber);
 
         if (!cardData) {
             await interaction.reply("Unable to find card in the database.");
             return;
         }
 
-        const cardValue = GetSacrificeAmount(cardData.card.type);
+        const cardValue = GetSacrificeAmount(cardData.card.type) * quantity;
         const cardRarityString = CardRarityToString(cardData.card.type);
 
         const description = [
@@ -126,6 +139,7 @@ export default class Sacrifice extends ButtonEvent {
             `Series: ${cardData.series.name}`,
             `Rarity: ${cardRarityString}`,
             `Quantity Owned: ${cardInInventory.Quantity}`,
+            `Quantity To Sacrifice: ${quantity}`,
             `Sacrifice Amount: ${cardValue}`,
         ];
 
diff --git a/src/buttonEvents/Trade.ts b/src/buttonEvents/Trade.ts
index ef0d1c5..fb8449d 100644
--- a/src/buttonEvents/Trade.ts
+++ b/src/buttonEvents/Trade.ts
@@ -28,8 +28,10 @@ export default class Trade extends ButtonEvent {
         const user2CardNumber = interaction.customId.split(" ")[5];
         const expiry = interaction.customId.split(" ")[6];
         const timeoutId = interaction.customId.split(" ")[7];
+        const user1Quantity = Number(interaction.customId.split(" ")[8]) || 1;
+        const user2Quantity = Number(interaction.customId.split(" ")[9]) || 1;
 
-        AppLogger.LogSilly("Button/Trade/AcceptTrade", `Parameters: user1UserId=${user1UserId}, user2UserId=${user2UserId}, user1CardNumber=${user1CardNumber}, user2CardNumber=${user2CardNumber}, expiry=${expiry}, timeoutId=${timeoutId}`);
+        AppLogger.LogSilly("Button/Trade/AcceptTrade", `Parameters: user1UserId=${user1UserId}, user2UserId=${user2UserId}, user1CardNumber=${user1CardNumber}, user2CardNumber=${user2CardNumber}, expiry=${expiry}, timeoutId=${timeoutId} user1Quantity=${user1Quantity} user2Quantity=${user2Quantity}`);
 
         const expiryDate = new Date(expiry);
 
@@ -67,13 +69,13 @@ export default class Trade extends ButtonEvent {
             return;
         }
 
-        if (user1UserInventory1.Quantity < 1 || user2UserInventory1.Quantity < 1) {
+        if (user1UserInventory1.Quantity < user1Quantity || user2UserInventory1.Quantity < user2Quantity) {
             await interaction.reply("One or more of the items you are trying to trade does not exist.");
             return;
         }
 
-        user1UserInventory1.RemoveQuantity(1);
-        user2UserInventory1.RemoveQuantity(1);
+        user1UserInventory1.RemoveQuantity(user1Quantity);
+        user2UserInventory1.RemoveQuantity(user2Quantity);
 
         await user1UserInventory1.Save(Inventory, user1UserInventory1);
         await user2UserInventory1.Save(Inventory, user2UserInventory1);
@@ -82,15 +84,15 @@ export default class Trade extends ButtonEvent {
         let user2UserInventory2 = await Inventory.FetchOneByCardNumberAndUserId(user2UserId, user1CardNumber);
 
         if (!user1UserInventory2) {
-            user1UserInventory2 = new Inventory(user1UserId, user2CardNumber, 1);
+            user1UserInventory2 = new Inventory(user1UserId, user2CardNumber, user2Quantity);
         } else {
-            user1UserInventory2.AddQuantity(1);
+            user1UserInventory2.AddQuantity(user2Quantity);
         }
 
         if (!user2UserInventory2) {
-            user2UserInventory2 = new Inventory(user2UserId, user1CardNumber, 1);
+            user2UserInventory2 = new Inventory(user2UserId, user1CardNumber, user1Quantity);
         } else {
-            user2UserInventory2.AddQuantity(1);
+            user2UserInventory2.AddQuantity(user1Quantity);
         }
 
         await user1UserInventory2.Save(Inventory, user1UserInventory2);
@@ -106,12 +108,12 @@ export default class Trade extends ButtonEvent {
             .addFields([
                 {
                     name: `${user1User.username} Receives`,
-                    value: `${user2Item.id}: ${user2Item.name}`,
+                    value: `${user2Item.id}: ${user2Item.name} x${user2Quantity}`,
                     inline: true,
                 },
                 {
                     name: `${user2User.username} Receives`,
-                    value: `${user1Item.id}: ${user1Item.name}`,
+                    value: `${user1Item.id}: ${user1Item.name} x${user1Quantity}`,
                     inline: true,
                 },
                 {
@@ -144,6 +146,8 @@ export default class Trade extends ButtonEvent {
         const user2CardNumber = interaction.customId.split(" ")[5];
         // No need to get expiry date
         const timeoutId = interaction.customId.split(" ")[7];
+        const user1Quantity = Number(interaction.customId.split(" ")[8]) || 1;
+        const user2Quantity = Number(interaction.customId.split(" ")[9]) || 1;
 
         AppLogger.LogSilly("Button/Trade/DeclineTrade", `Parameters: user1UserId=${user1UserId}, user2UserId=${user2UserId}, user1CardNumber=${user1CardNumber}, user2CardNumber=${user2CardNumber}, timeoutId=${timeoutId}`);
 
@@ -178,12 +182,12 @@ export default class Trade extends ButtonEvent {
             .addFields([
                 {
                     name: `${user1User.username} Receives`,
-                    value: `${user2Item.id}: ${user2Item.name}`,
+                    value: `${user2Item.id}: ${user2Item.name} x${user2Quantity}`,
                     inline: true,
                 },
                 {
                     name: `${user2User.username} Receives`,
-                    value: `${user1Item.id}: ${user1Item.name}`,
+                    value: `${user1Item.id}: ${user1Item.name} x${user1Quantity}`,
                     inline: true,
                 },
                 {
diff --git a/src/buttonEvents/View.ts b/src/buttonEvents/View.ts
new file mode 100644
index 0000000..007a911
--- /dev/null
+++ b/src/buttonEvents/View.ts
@@ -0,0 +1,25 @@
+import {ButtonInteraction} from "discord.js";
+import {ButtonEvent} from "../type/buttonEvent.js";
+import CardSearchHelper from "../helpers/CardSearchHelper.js";
+
+export default class View extends ButtonEvent {
+    public override async execute(interaction: ButtonInteraction) {
+        const page = interaction.customId.split(" ")[1];
+        const results = interaction.customId.split(" ").splice(2);
+
+        await interaction.deferUpdate();
+
+        const searchResult = await CardSearchHelper.GenerateSearchPageFromQuery(results, interaction.user.id, Number(page));
+
+        if (!searchResult) {
+            await interaction.followUp("No results found");
+            return;
+        }
+
+        await interaction.editReply({
+            embeds: [ searchResult.embed ],
+            components: [ searchResult.row ],
+            files: searchResult.attachments,
+        });
+    }
+}
diff --git a/src/client/client.ts b/src/client/client.ts
index 117bdb9..57551fb 100644
--- a/src/client/client.ts
+++ b/src/client/client.ts
@@ -17,18 +17,20 @@ import AppLogger from "./appLogger";
 import TimerHelper from "../helpers/TimerHelper";
 import GiveCurrency from "../timers/GiveCurrency";
 import PurgeClaims from "../timers/PurgeClaims";
+import StringDropdownEventItem from "../contracts/StringDropdownEventItem";
+import {StringDropdownEvent} from "../type/stringDropdownEvent";
 
 export class CoreClient extends Client {
     private static _commandItems: ICommandItem[];
     private static _eventExecutors: EventExecutors;
     private static _buttonEvents: IButtonEventItem[];
+    private static _stringDropdowns: StringDropdownEventItem[];
 
     private _events: Events;
     private _util: Util;
     private _webhooks: Webhooks;
     private _timerHelper: TimerHelper;
 
-    public static ClaimId: string;
     public static Environment: Environment;
     public static AllowDrops: boolean;
     public static Cards: SeriesMetadata[];
@@ -45,6 +47,10 @@ export class CoreClient extends Client {
         return this._buttonEvents;
     }
 
+    public static get stringDropdowns(): StringDropdownEventItem[] {
+        return this._stringDropdowns;
+    }
+
     constructor(intents: number[]) {
         super({ intents: intents });
         dotenv.config();
@@ -59,6 +65,7 @@ export class CoreClient extends Client {
 
         CoreClient._commandItems = [];
         CoreClient._buttonEvents = [];
+        CoreClient._stringDropdowns = [];
 
         this._events = new Events();
         this._util = new Util();
@@ -408,4 +415,19 @@ export class CoreClient extends Client {
             AppLogger.LogVerbose("Client", `Registered Button Event: ${buttonId}`);
         }
     }
+
+    public static RegisterStringDropdownEvent(dropdownId: string, event: StringDropdownEvent, environment: Environment = Environment.All) {
+        const item: StringDropdownEventItem = {
+            DropdownId: dropdownId,
+            Event: event,
+            Environment: environment,
+        };
+
+        if ((environment & CoreClient.Environment) == CoreClient.Environment) {
+            CoreClient._stringDropdowns.push(item);
+
+            AppLogger.LogVerbose("Client", `Registered String Dropdown Event: ${dropdownId}`);
+        }
+    }
 }
+
diff --git a/src/client/events.ts b/src/client/events.ts
index f02fb9c..5f61da5 100644
--- a/src/client/events.ts
+++ b/src/client/events.ts
@@ -3,6 +3,7 @@ import ChatInputCommand from "./interactionCreate/ChatInputCommand";
 import Button from "./interactionCreate/Button";
 import AppLogger from "./appLogger";
 import NewUserDiscovery from "./interactionCreate/middleware/NewUserDiscovery";
+import StringDropdown from "./interactionCreate/StringDropdown";
 
 export class Events {
     public async onInteractionCreate(interaction: Interaction) {
@@ -19,6 +20,11 @@ export class Events {
             AppLogger.LogVerbose("Client", `Button: ${interaction.customId}`);
             Button.onButtonClicked(interaction);
         }
+
+        if (interaction.isStringSelectMenu()) {
+            AppLogger.LogVerbose("Client", `StringDropdown: ${interaction.customId}`);
+            StringDropdown.onStringDropdownSelected(interaction);
+        }
     }
 
     // Emit when bot is logged in and ready to use
diff --git a/src/client/interactionCreate/StringDropdown.ts b/src/client/interactionCreate/StringDropdown.ts
new file mode 100644
index 0000000..608b81b
--- /dev/null
+++ b/src/client/interactionCreate/StringDropdown.ts
@@ -0,0 +1,29 @@
+import {StringSelectMenuInteraction} from "discord.js";
+import {CoreClient} from "../client";
+import AppLogger from "../appLogger";
+
+export default class StringDropdown {
+    public static async onStringDropdownSelected(interaction: StringSelectMenuInteraction) {
+        if (!interaction.isStringSelectMenu()) return;
+
+        const item = CoreClient.stringDropdowns.find(x => x.DropdownId == interaction.customId.split(" ")[0]);
+
+        if (!item) {
+            AppLogger.LogVerbose("StringDropdown", `Event not found: ${interaction.customId}`);
+
+            await interaction.reply("Event not found");
+            return;
+        }
+
+        try {
+            AppLogger.LogDebug("StringDropdown", `Executing ${interaction.customId}`);
+
+            item.Event.execute(interaction);
+        } catch (e) {
+            AppLogger.LogError("StringDropdown", `Error occurred while executing event: ${interaction.customId}`);
+            AppLogger.LogError("StringDropdown", e as string);
+
+            await interaction.reply("An error occurred while executing the event");
+        }
+    }
+}
diff --git a/src/commands/drop.ts b/src/commands/drop.ts
index 66558e3..ac8008c 100644
--- a/src/commands/drop.ts
+++ b/src/commands/drop.ts
@@ -5,11 +5,13 @@ import { CoreClient } from "../client/client";
 import { v4 } from "uuid";
 import Inventory from "../database/entities/app/Inventory";
 import Config from "../database/entities/app/Config";
-import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
 import path from "path";
 import AppLogger from "../client/appLogger";
 import User from "../database/entities/app/User";
 import CardConstants from "../constants/CardConstants";
+import ErrorMessages from "../constants/ErrorMessages";
+import GetCardsHelper from "../helpers/DropHelpers/GetCardsHelper";
+import DropEmbedHelper from "../helpers/DropHelpers/DropEmbedHelper";
 
 export default class Drop extends Command {
     constructor() {
@@ -22,14 +24,13 @@ export default class Drop extends Command {
 
     public override async execute(interaction: CommandInteraction) {
         if (!CoreClient.AllowDrops) {
-            await interaction.reply("Bot is currently syncing, please wait until its done.");
+            await interaction.reply(ErrorMessages.BotSyncing);
             return;
         }
 
         if (await Config.GetValue("safemode") == "true") {
-            AppLogger.LogWarn("Commands/Drop", "Safe Mode is active, refusing to send next drop.");
-
-            await interaction.reply("Safe Mode has been activated, please resync to continue.");
+            AppLogger.LogWarn("Commands/Drop", ErrorMessages.SafeMode);
+            await interaction.reply(ErrorMessages.SafeMode);
             return;
         }
 
@@ -42,17 +43,18 @@ export default class Drop extends Command {
             AppLogger.LogInfo("Commands/Drop", `New user (${interaction.user.id}) saved to the database`);
         }
 
-        if (user.Currency < CardConstants.ClaimCost) {
-            await interaction.reply(`Not enough currency! You need ${CardConstants.ClaimCost} currency, you have ${user.Currency}!`);
+        if (!user.RemoveCurrency(CardConstants.ClaimCost)) {
+            await interaction.reply(ErrorMessages.NotEnoughCurrency(CardConstants.ClaimCost, user.Currency));
             return;
         }
 
-        const randomCard = CardDropHelperMetadata.GetRandomCard();
+        await user.Save(User, user);
+
+        const randomCard = await GetCardsHelper.FetchCard(interaction.user.id);
 
         if (!randomCard) {
-            AppLogger.LogWarn("Commands/Drop", "Unable to fetch card, please try again. (randomCard is null)");
-
-            await interaction.reply("Unable to fetch card, please try again.");
+            AppLogger.LogWarn("Commands/Drop", ErrorMessages.UnableToFetchCard);
+            await interaction.reply(ErrorMessages.UnableToFetchCard);
             return;
         }
 
@@ -74,11 +76,11 @@ export default class Drop extends Command {
             const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.card.id);
             const quantityClaimed = inventory ? inventory.Quantity : 0;
 
-            const embed = CardDropHelperMetadata.GenerateDropEmbed(randomCard, quantityClaimed, imageFileName, undefined, user.Currency);
+            const embed = DropEmbedHelper.GenerateDropEmbed(randomCard, quantityClaimed, imageFileName, undefined, user.Currency);
 
             const claimId = v4();
 
-            const row = CardDropHelperMetadata.GenerateDropButtons(randomCard, claimId, interaction.user.id);
+            const row = DropEmbedHelper.GenerateDropButtons(randomCard, claimId, interaction.user.id);
 
             await interaction.editReply({
                 embeds: [ embed ],
@@ -86,8 +88,6 @@ export default class Drop extends Command {
                 components: [ row ],
             });
 
-            CoreClient.ClaimId = claimId;
-
         } catch (e) {
             AppLogger.LogError("Commands/Drop", `Error sending next drop for card ${randomCard.card.id}: ${e}`);
 
diff --git a/src/commands/effects.ts b/src/commands/effects.ts
new file mode 100644
index 0000000..cd6d1d4
--- /dev/null
+++ b/src/commands/effects.ts
@@ -0,0 +1,64 @@
+import { CommandInteraction, SlashCommandBuilder } from "discord.js";
+import { Command } from "../type/command";
+import { EffectChoices } from "../constants/EffectDetails";
+import AppLogger from "../client/appLogger";
+import List from "./effects/List";
+import Use from "./effects/Use";
+import Buy from "./effects/Buy";
+
+export default class Effects extends Command {
+    constructor() {
+        super();
+
+        this.CommandBuilder = new SlashCommandBuilder()
+            .setName("effects")
+            .setDescription("Effects")
+            .addSubcommand(x => x
+                .setName("list")
+                .setDescription("List all effects I have")
+                .addNumberOption(x => x
+                    .setName("page")
+                    .setDescription("The page number")
+                    .setMinValue(1)))
+            .addSubcommand(x => x
+                .setName("use")
+                .setDescription("Use an effect in your inventory")
+                .addStringOption(y => y
+                    .setName("id")
+                    .setDescription("The effect id to use")
+                    .setRequired(true)
+                    .setChoices(EffectChoices)))
+            .addSubcommand(x => x
+                .setName("buy")
+                .setDescription("Buy more effects")
+                .addStringOption(y => y
+                    .setName("id")
+                    .setDescription("The effect id to buy")
+                    .setRequired(true)
+                    .setChoices(EffectChoices))
+                .addNumberOption(y => y
+                    .setName("quantity")
+                    .setDescription("The amount to buy")
+                    .setMinValue(1)));
+    }
+
+    public override async execute(interaction: CommandInteraction) {
+        if (!interaction.isChatInputCommand()) return;
+
+        const subcommand = interaction.options.getSubcommand();
+
+        switch (subcommand) {
+            case "list":
+                await List(interaction);
+                break;
+            case "use":
+                await Use(interaction);
+                break;
+            case "buy":
+                await Buy(interaction);
+                break;
+            default:
+                AppLogger.LogError("Commands/Effects", `Invalid subcommand: ${subcommand}`);
+        }
+    }
+}
diff --git a/src/commands/effects/Buy.ts b/src/commands/effects/Buy.ts
new file mode 100644
index 0000000..3ebf587
--- /dev/null
+++ b/src/commands/effects/Buy.ts
@@ -0,0 +1,22 @@
+import { CommandInteraction } from "discord.js";
+import EffectHelper from "../../helpers/EffectHelper";
+
+export default async function Buy(interaction: CommandInteraction) {
+    const id = interaction.options.get("id", true).value!;
+    const quantity = interaction.options.get("quantity")?.value ?? 1;
+
+    const idValue = id.toString();
+    const quantityValue = Number(quantity);
+
+    const result = await EffectHelper.GenerateEffectBuyEmbed(interaction.user.id, idValue, quantityValue, false);
+
+    if (typeof result == "string") {
+        await interaction.reply(result);
+        return;
+    }
+
+    await interaction.reply({
+        embeds: [ result.embed ],
+        components: [ result.row ],
+    });
+}
\ No newline at end of file
diff --git a/src/commands/effects/List.ts b/src/commands/effects/List.ts
new file mode 100644
index 0000000..14e6085
--- /dev/null
+++ b/src/commands/effects/List.ts
@@ -0,0 +1,15 @@
+import { CommandInteraction } from "discord.js";
+import EffectHelper from "../../helpers/EffectHelper";
+
+export default async function List(interaction: CommandInteraction) {
+    const pageOption = interaction.options.get("page");
+
+    const page = !isNaN(Number(pageOption?.value)) ? Number(pageOption?.value) : 1;
+
+    const result = await EffectHelper.GenerateEffectListEmbed(interaction.user.id, page);
+
+    await interaction.reply({
+        embeds: [ result.embed ],
+        components: [ result.row ],
+    });
+}
\ No newline at end of file
diff --git a/src/commands/effects/Use.ts b/src/commands/effects/Use.ts
new file mode 100644
index 0000000..9f72ae0
--- /dev/null
+++ b/src/commands/effects/Use.ts
@@ -0,0 +1,62 @@
+import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder } from "discord.js";
+import { EffectDetails } from "../../constants/EffectDetails";
+import AppLogger from "../../client/appLogger";
+import EffectHelper from "../../helpers/EffectHelper";
+import TimeLengthInput from "../../helpers/TimeLengthInput";
+import EmbedColours from "../../constants/EmbedColours";
+
+export default async function Use(interaction: CommandInteraction) {
+    const id = interaction.options.get("id", true).value!.toString();
+
+    const effectDetail = EffectDetails.get(id);
+
+    if (!effectDetail) {
+        AppLogger.LogWarn("Commands/Effects", `Unable to find effect details for ${id}`);
+
+        await interaction.reply("Unable to find effect!");
+        return;
+    }
+
+    const canUseEffect = await EffectHelper.CanUseEffect(interaction.user.id, id);
+
+    if (!canUseEffect) {
+        await interaction.reply("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown");
+        return;
+    }
+
+    const timeLengthInput = TimeLengthInput.ConvertFromMilliseconds(effectDetail.duration);
+
+    const embed = new EmbedBuilder()
+        .setTitle("Effect Confirmation")
+        .setDescription("Would you like to use this effect?")
+        .setColor(EmbedColours.Ok)
+        .addFields([
+            {
+                name: "Effect",
+                value: effectDetail.friendlyName,
+                inline: true,
+            },
+            {
+                name: "Length",
+                value: timeLengthInput.GetLengthShort(),
+                inline: true,
+            },
+        ]);
+
+    const row = new ActionRowBuilder<ButtonBuilder>()
+        .addComponents([
+            new ButtonBuilder()
+                .setLabel("Confirm")
+                .setCustomId(`effects use confirm ${effectDetail.id}`)
+                .setStyle(ButtonStyle.Primary),
+            new ButtonBuilder()
+                .setLabel("Cancel")
+                .setCustomId(`effects use cancel ${effectDetail.id}`)
+                .setStyle(ButtonStyle.Danger),
+        ]);
+
+    await interaction.reply({
+        embeds: [ embed ],
+        components: [ row ],
+    });
+}
\ No newline at end of file
diff --git a/src/commands/give.ts b/src/commands/give.ts
index 3ffbe8f..35dfa04 100644
--- a/src/commands/give.ts
+++ b/src/commands/give.ts
@@ -2,10 +2,10 @@ import { CacheType, CommandInteraction, PermissionsBitField, SlashCommandBuilder
 import { Command } from "../type/command";
 import { CoreClient } from "../client/client";
 import Config from "../database/entities/app/Config";
-import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
 import Inventory from "../database/entities/app/Inventory";
 import AppLogger from "../client/appLogger";
 import User from "../database/entities/app/User";
+import GetCardsHelper from "../helpers/DropHelpers/GetCardsHelper";
 
 export default class Give extends Command {
     constructor() {
@@ -81,7 +81,7 @@ export default class Give extends Command {
 
         AppLogger.LogSilly("Commands/Give/GiveCard", `Parameters: cardNumber=${cardNumber.value}, user=${user.id}`);
 
-        const card = CardDropHelperMetadata.GetCardByCardNumber(cardNumber.value!.toString());
+        const card = GetCardsHelper.GetCardByCardNumber(cardNumber.value!.toString());
 
         if (!card) {
             await interaction.reply("Unable to fetch card, please try again.");
diff --git a/src/commands/id.ts b/src/commands/id.ts
new file mode 100644
index 0000000..0f11aaa
--- /dev/null
+++ b/src/commands/id.ts
@@ -0,0 +1,80 @@
+import { AttachmentBuilder, CommandInteraction, DiscordAPIError, SlashCommandBuilder } from "discord.js";
+import { Command } from "../type/command";
+import { CoreClient } from "../client/client";
+import { readFileSync } from "fs";
+import path from "path";
+import Inventory from "../database/entities/app/Inventory";
+import AppLogger from "../client/appLogger";
+import DropEmbedHelper from "../helpers/DropHelpers/DropEmbedHelper";
+
+export default class Id extends Command {
+    constructor() {
+        super();
+
+        this.CommandBuilder = new SlashCommandBuilder()
+            .setName("id")
+            .setDescription("View a specific command by its id")
+            .addStringOption(x =>
+                x
+                    .setName("cardnumber")
+                    .setDescription("The card number to view")
+                    .setRequired(true));
+    }
+
+    public override async execute(interaction: CommandInteraction) {
+        const cardNumber = interaction.options.get("cardnumber");
+
+        AppLogger.LogSilly("Commands/View", `Parameters: cardNumber=${cardNumber?.value}`);
+
+        if (!cardNumber || !cardNumber.value) {
+            await interaction.reply("Card number is required.");
+            return;
+        }
+
+        const card = CoreClient.Cards
+            .flatMap(x => x.cards)
+            .find(x => x.id == cardNumber.value);
+
+        if (!card) {
+            await interaction.reply("Card not found.");
+            return;
+        }
+
+        const series = CoreClient.Cards
+            .find(x => x.cards.includes(card))!;
+
+        const files = [];
+        let imageFileName = "";
+
+        if (!(card.path.startsWith("http://") || card.path.startsWith("https://"))) {
+            const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.path));
+            imageFileName = card.path.split("/").pop()!;
+
+            const attachment = new AttachmentBuilder(image, { name: imageFileName });
+
+            files.push(attachment);
+        }
+
+        await interaction.deferReply();
+
+        const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.id);
+        const quantityClaimed = inventory ? inventory.Quantity : 0;
+
+        const embed = DropEmbedHelper.GenerateDropEmbed({ card, series }, quantityClaimed, imageFileName);
+
+        try {
+            await interaction.editReply({
+                embeds: [ embed ],
+                files: files,
+            });
+        } catch (e) {
+            AppLogger.LogError("Commands/View", `Error sending view for card ${card.id}: ${e}`);
+
+            if (e instanceof DiscordAPIError) {
+                await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. Code: ${e.code}.`);
+            } else {
+                await interaction.editReply("Unable to send next drop. Please try again, and report this if it keeps happening. Code: UNKNOWN.");
+            }
+        }
+    }
+}
diff --git a/src/commands/inventory.ts b/src/commands/inventory.ts
index 8d2ef2c..deb4735 100644
--- a/src/commands/inventory.ts
+++ b/src/commands/inventory.ts
@@ -47,7 +47,7 @@ export default class Inventory extends Command {
             await interaction.followUp({
                 files: [ embed.image ],
                 embeds: [ embed.embed ],
-                components: [ embed.row ],
+                components: [ embed.row1, embed.row2 ],
             });
         } catch (e) {
             AppLogger.LogError("Commands/Inventory", e as string);
diff --git a/src/commands/multidrop.ts b/src/commands/multidrop.ts
new file mode 100644
index 0000000..aa42686
--- /dev/null
+++ b/src/commands/multidrop.ts
@@ -0,0 +1,88 @@
+import { AttachmentBuilder, CommandInteraction, SlashCommandBuilder } from "discord.js";
+import { Command } from "../type/command";
+import { CoreClient } from "../client/client";
+import ErrorMessages from "../constants/ErrorMessages";
+import Config from "../database/entities/app/Config";
+import AppLogger from "../client/appLogger";
+import User from "../database/entities/app/User";
+import CardConstants from "../constants/CardConstants";
+import { readFileSync } from "fs";
+import path from "path";
+import Inventory from "../database/entities/app/Inventory";
+import GetCardsHelper from "../helpers/DropHelpers/GetCardsHelper";
+import MultidropEmbedHelper from "../helpers/DropHelpers/MultidropEmbedHelper";
+
+export default class Multidrop extends Command {
+    constructor() {
+        super();
+
+        this.CommandBuilder = new SlashCommandBuilder()
+            .setName("multidrop")
+            .setDescription("Drop 11 cards for the price of 10!");
+    }
+
+    public override async execute(interaction: CommandInteraction) {
+        if (!CoreClient.AllowDrops) {
+            await interaction.reply(ErrorMessages.BotSyncing);
+            return;
+        }
+
+        if (await Config.GetValue("safemode") == "true") {
+            AppLogger.LogWarn("Commands/Multidrop", ErrorMessages.SafeMode);
+            await interaction.reply(ErrorMessages.SafeMode);
+            return;
+        }
+
+        let user = await User.FetchOneById(User, interaction.user.id);
+
+        if (!user) {
+            user = new User(interaction.user.id, CardConstants.StartingCurrency);
+            await user.Save(User, user);
+
+            AppLogger.LogInfo("Commands/Multidrop", `New user (${interaction.user.id}) saved to the database`);
+        }
+
+        if (user.Currency < CardConstants.MultidropCost) {
+            await interaction.reply(ErrorMessages.NotEnoughCurrency(CardConstants.MultidropCost, user.Currency));
+            return;
+        }
+
+        user.RemoveCurrency(CardConstants.MultidropCost);
+        await user.Save(User, user);
+
+        const randomCard = GetCardsHelper.GetRandomCard();
+        const cardsRemaining = CardConstants.MultidropQuantity - 1;
+
+        if (!randomCard) {
+            AppLogger.LogWarn("Commands/Multidrop", ErrorMessages.UnableToFetchCard);
+            await interaction.reply(ErrorMessages.UnableToFetchCard);
+            return;
+        }
+
+        await interaction.deferReply();
+
+        try {
+            const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", randomCard.card.path));
+            const imageFileName = randomCard.card.path.split("/").pop()!;
+
+            const attachment = new AttachmentBuilder(image, { name: imageFileName });
+
+            const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.card.id);
+            const quantityClaimed = inventory ? inventory.Quantity : 0;
+
+            const embed = MultidropEmbedHelper.GenerateMultidropEmbed(randomCard, quantityClaimed, imageFileName, cardsRemaining, undefined, user.Currency);
+
+            const row = MultidropEmbedHelper.GenerateMultidropButtons(randomCard, cardsRemaining, interaction.user.id);
+
+            await interaction.editReply({
+                embeds: [ embed ],
+                files: [ attachment ],
+                components: [ row ],
+            });
+        } catch (e) {
+            AppLogger.LogError("Commands/Multidrop", `Error sending next drop for card ${randomCard.card.id}: ${e}`);
+
+            await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/commands/sacrifice.ts b/src/commands/sacrifice.ts
index 4d1c51a..a44a69e 100644
--- a/src/commands/sacrifice.ts
+++ b/src/commands/sacrifice.ts
@@ -2,8 +2,8 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CacheType, CommandInterac
 import { Command } from "../type/command";
 import Inventory from "../database/entities/app/Inventory";
 import { CardRarityToString, GetSacrificeAmount } from "../constants/CardRarity";
-import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
 import EmbedColours from "../constants/EmbedColours";
+import GetCardsHelper from "../helpers/DropHelpers/GetCardsHelper";
 
 export default class Sacrifice extends Command {
     constructor() {
@@ -16,11 +16,18 @@ export default class Sacrifice extends Command {
                 x
                     .setName("cardnumber")
                     .setDescription("The card to sacrifice from your inventory")
-                    .setRequired(true));
+                    .setRequired(true))
+            .addNumberOption(x =>
+                x
+                    .setName("quantity")
+                    .setDescription("The amount to sacrifice (default 1)"));
     }
 
     public override async execute(interaction: CommandInteraction<CacheType>): Promise<void> {
         const cardnumber = interaction.options.get("cardnumber", true);
+        const quantityInput = interaction.options.get("quantity")?.value ?? 1;
+
+        const quantity = Number(quantityInput) || 1;
 
         const cardInInventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, cardnumber.value! as string);
 
@@ -29,14 +36,19 @@ export default class Sacrifice extends Command {
             return;
         }
 
-        const cardData = CardDropHelperMetadata.GetCardByCardNumber(cardnumber.value! as string);
+        if (cardInInventory.Quantity < quantity) {
+            await interaction.reply(`You can only sacrifice what you own! You have ${cardInInventory.Quantity} of this card`);
+            return;
+        }
+
+        const cardData = GetCardsHelper.GetCardByCardNumber(cardnumber.value! as string);
 
         if (!cardData) {
             await interaction.reply("Unable to find card in the database.");
             return;
         }
 
-        const cardValue = GetSacrificeAmount(cardData.card.type);
+        const cardValue = GetSacrificeAmount(cardData.card.type) * quantity;
         const cardRarityString = CardRarityToString(cardData.card.type);
 
         const description = [
@@ -44,6 +56,7 @@ export default class Sacrifice extends Command {
             `Series: ${cardData.series.name}`,
             `Rarity: ${cardRarityString}`,
             `Quantity Owned: ${cardInInventory.Quantity}`,
+            `Quantity To Sacrifice: ${quantity}`,
             `Sacrifice Amount: ${cardValue}`,
         ];
 
@@ -56,11 +69,11 @@ export default class Sacrifice extends Command {
         const row = new ActionRowBuilder<ButtonBuilder>()
             .addComponents([
                 new ButtonBuilder()
-                    .setCustomId(`sacrifice confirm ${interaction.user.id} ${cardnumber.value!}`)
+                    .setCustomId(`sacrifice confirm ${interaction.user.id} ${cardnumber.value!} ${quantity}`)
                     .setLabel("Confirm")
                     .setStyle(ButtonStyle.Success),
                 new ButtonBuilder()
-                    .setCustomId(`sacrifice cancel ${interaction.user.id} ${cardnumber.value!}`)
+                    .setCustomId(`sacrifice cancel ${interaction.user.id} ${cardnumber.value!} ${quantity}`)
                     .setLabel("Cancel")
                     .setStyle(ButtonStyle.Secondary),
             ]);
diff --git a/src/commands/stage/dropnumber.ts b/src/commands/stage/dropnumber.ts
index caeccd0..c61ed43 100644
--- a/src/commands/stage/dropnumber.ts
+++ b/src/commands/stage/dropnumber.ts
@@ -5,7 +5,7 @@ import Inventory from "../../database/entities/app/Inventory";
 import { v4 } from "uuid";
 import { CoreClient } from "../../client/client";
 import path from "path";
-import CardDropHelperMetadata from "../../helpers/CardDropHelperMetadata";
+import DropEmbedHelper from "../../helpers/DropHelpers/DropEmbedHelper";
 import AppLogger from "../../client/appLogger";
 
 export default class Dropnumber extends Command {
@@ -41,44 +41,40 @@ export default class Dropnumber extends Command {
             return;
         }
 
+        const claimId = v4();
+        await interaction.deferReply();
+
+    try {
+        const files = [];
+        let imageFileName = "";
+
+        if (!(card.path.startsWith("http://") || card.path.startsWith("https://"))) {
+            const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.path));
+            imageFileName = card.path.split("/").pop()!;
+
+            const attachment = new AttachmentBuilder(image, { name: imageFileName });
+
+            files.push(attachment);
+        }
+
         const series = CoreClient.Cards
             .find(x => x.cards.includes(card))!;
 
-        const claimId = v4();
+        const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.id);
+        const quantityClaimed = inventory ? inventory.Quantity : 0;
 
+        const embed = DropEmbedHelper.GenerateDropEmbed({ card, series }, quantityClaimed, imageFileName);
 
-        await interaction.deferReply();
+        const row = DropEmbedHelper.GenerateDropButtons({ card, series }, claimId, interaction.user.id);
 
-        try {
-            const files = [];
-            let imageFileName = "";
-
-            if (!(card.path.startsWith("http://") || card.path.startsWith("https://"))) {
-                const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.path));
-                imageFileName = card.path.split("/").pop()!;
-
-                const attachment = new AttachmentBuilder(image, { name: imageFileName });
-
-                files.push(attachment);
-            }
-
-            const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.id);
-            const quantityClaimed = inventory ? inventory.Quantity : 0;
-
-            const embed = CardDropHelperMetadata.GenerateDropEmbed({ card, series }, quantityClaimed, imageFileName);
-
-            const row = CardDropHelperMetadata.GenerateDropButtons({ card, series }, claimId, interaction.user.id);
-
-            await interaction.editReply({
-                embeds: [ embed ],
-                files: files,
-                components: [ row ],
-            });
+        await interaction.editReply({
+            embeds: [ embed ],
+            files: files,
+            components: [ row ],
+        });
         } catch (e) {
             AppLogger.CatchError("Dropnumber", e);
             await interaction.editReply("Unable to send next drop. Please try again, and report this if it keeps happening");
         }
-
-        CoreClient.ClaimId = claimId;
     }
 }
\ No newline at end of file
diff --git a/src/commands/stage/droprarity.ts b/src/commands/stage/droprarity.ts
index cf2eded..f776930 100644
--- a/src/commands/stage/droprarity.ts
+++ b/src/commands/stage/droprarity.ts
@@ -4,9 +4,9 @@ import { CardRarity, CardRarityChoices, CardRarityParse } from "../../constants/
 import { readFileSync } from "fs";
 import Inventory from "../../database/entities/app/Inventory";
 import { v4 } from "uuid";
-import { CoreClient } from "../../client/client";
-import CardDropHelperMetadata from "../../helpers/CardDropHelperMetadata";
 import path from "path";
+import GetCardsHelper from "../../helpers/DropHelpers/GetCardsHelper";
+import DropEmbedHelper from "../../helpers/DropHelpers/DropEmbedHelper";
 import AppLogger from "../../client/appLogger";
 
 export default class Droprarity extends Command {
@@ -41,7 +41,7 @@ export default class Droprarity extends Command {
             return;
         }
 
-        const card = CardDropHelperMetadata.GetRandomCardByRarity(rarityType);
+        const card = GetCardsHelper.GetRandomCardByRarity(rarityType);
 
         if (!card) {
             await interaction.reply("Card not found");
@@ -49,7 +49,6 @@ export default class Droprarity extends Command {
         }
 
         const claimId = v4();
-
         await interaction.deferReply();
 
         try {
@@ -68,9 +67,9 @@ export default class Droprarity extends Command {
             const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.card.id);
             const quantityClaimed = inventory ? inventory.Quantity : 0;
 
-            const embed = CardDropHelperMetadata.GenerateDropEmbed(card, quantityClaimed, imageFileName);
+            const embed = DropEmbedHelper.GenerateDropEmbed(card, quantityClaimed, imageFileName);
 
-            const row = CardDropHelperMetadata.GenerateDropButtons(card, claimId, interaction.user.id);
+            const row = DropEmbedHelper.GenerateDropButtons(card, claimId, interaction.user.id);
 
             await interaction.editReply({
                 embeds: [ embed ],
@@ -81,7 +80,5 @@ export default class Droprarity extends Command {
             AppLogger.CatchError("Droprarity", e);
             await interaction.editReply("Unable to send next drop. Please try again, and report this if it keeps happening");
         }
-
-        CoreClient.ClaimId = claimId;
     }
 }
\ No newline at end of file
diff --git a/src/commands/trade.ts b/src/commands/trade.ts
index 569e88f..fcd312a 100644
--- a/src/commands/trade.ts
+++ b/src/commands/trade.ts
@@ -26,13 +26,26 @@ export default class Trade extends Command {
                 x
                     .setName("receive")
                     .setDescription("Item to receive")
-                    .setRequired(true));
+                    .setRequired(true))
+            .addNumberOption(x =>
+                x
+                    .setName("givequantity")
+                    .setDescription("Amount to give"))
+            .addNumberOption(x =>
+                x
+                    .setName("receivequantity")
+                    .setDescription("Amount to receive"));
     }
 
     public override async execute(interaction: CommandInteraction) {
         const user = interaction.options.get("user", true).user!;
         const give = interaction.options.get("give", true);
         const receive = interaction.options.get("receive", true);
+        const givequantityInput = interaction.options.get("givequantity")?.value ?? 1;
+        const receivequantityInput = interaction.options.get("receivequantity")?.value ?? 1;
+        
+        const givequantity = Number(givequantityInput) || 1;
+        const receivequantity = Number(receivequantityInput) || 1;
 
         AppLogger.LogSilly("Commands/Trade", `Parameters: user=${user.id}, give=${give.value}, receive=${receive.value}`);
 
@@ -44,12 +57,12 @@ export default class Trade extends Command {
         const user1ItemEntity = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, give.value!.toString());
         const user2ItemEntity = await Inventory.FetchOneByCardNumberAndUserId(user.id, receive.value!.toString());
 
-        if (!user1ItemEntity) {
+        if (!user1ItemEntity || user1ItemEntity.Quantity < givequantity) {
             await interaction.reply("You do not have the item you are trying to trade.");
             return;
         }
 
-        if (!user2ItemEntity) {
+        if (!user2ItemEntity || user2ItemEntity.Quantity < receivequantity) {
             await interaction.reply("The user you are trying to trade with does not have the item you are trying to trade for.");
             return;
         }
@@ -78,12 +91,12 @@ export default class Trade extends Command {
             .addFields([
                 {
                     name: `${interaction.user.username} Receives`,
-                    value: `${user2Item.id}: ${user2Item.name}`,
+                    value: `${user2Item.id}: ${user2Item.name} x${receivequantity}`,
                     inline: true,
                 },
                 {
                     name: `${user.username} Receives`,
-                    value: `${user1Item.id}: ${user1Item.name}`,
+                    value: `${user1Item.id}: ${user1Item.name} x${givequantity}`,
                     inline: true,
                 },
                 {
@@ -92,16 +105,16 @@ export default class Trade extends Command {
                 }
             ]);
 
-        const timeoutId = setTimeout(async () => this.autoDecline(interaction, interaction.user.username, user.username, user1Item.id, user2Item.id, user1Item.name, user2Item.name), 1000 * 60 * 15); // 15 minutes
+        const timeoutId = setTimeout(async () => this.autoDecline(interaction, interaction.user.username, user.username, user1Item.id, user2Item.id, user1Item.name, user2Item.name, givequantity, receivequantity), 1000 * 60 * 15); // 15 minutes
 
         const row = new ActionRowBuilder<ButtonBuilder>()
             .addComponents([
                 new ButtonBuilder()
-                    .setCustomId(`trade accept ${interaction.user.id} ${user.id} ${user1Item.id} ${user2Item.id} ${expiry} ${timeoutId}`)
+                    .setCustomId(`trade accept ${interaction.user.id} ${user.id} ${user1Item.id} ${user2Item.id} ${expiry} ${timeoutId} ${givequantity} ${receivequantity}`)
                     .setLabel("Accept")
                     .setStyle(ButtonStyle.Success),
                 new ButtonBuilder()
-                    .setCustomId(`trade decline ${interaction.user.id} ${user.id} ${user1Item.id} ${user2Item.id} ${expiry} ${timeoutId}`)
+                    .setCustomId(`trade decline ${interaction.user.id} ${user.id} ${user1Item.id} ${user2Item.id} ${expiry} ${timeoutId} ${givequantity} ${receivequantity}`)
                     .setLabel("Decline")
                     .setStyle(ButtonStyle.Danger),
             ]);
@@ -109,7 +122,7 @@ export default class Trade extends Command {
         await interaction.reply({ content: `${user}`, embeds: [ tradeEmbed ], components: [ row ] });
     }
 
-    private async autoDecline(interaction: CommandInteraction, user1Username: string, user2Username: string, user1CardNumber: string, user2CardNumber: string, user1CardName: string, user2CardName: string) {
+    private async autoDecline(interaction: CommandInteraction, user1Username: string, user2Username: string, user1CardNumber: string, user2CardNumber: string, user1CardName: string, user2CardName: string, user1Quantity: number, user2Quantity: number) {
         AppLogger.LogSilly("Commands/Trade/AutoDecline", `Auto declining trade between ${user1Username} and ${user2Username}`);
 
         const tradeEmbed = new EmbedBuilder()
@@ -120,12 +133,12 @@ export default class Trade extends Command {
             .addFields([
                 {
                     name: `${user1Username} Receives`,
-                    value: `${user2CardNumber}: ${user2CardName}`,
+                    value: `${user2CardNumber}: ${user2CardName} x${user2Quantity}`,
                     inline: true,
                 },
                 {
                     name: `${user2Username} Receives`,
-                    value: `${user1CardNumber}: ${user1CardName}`,
+                    value: `${user1CardNumber}: ${user1CardName} x${user1Quantity}`,
                     inline: true,
                 },
                 {
diff --git a/src/commands/view.ts b/src/commands/view.ts
index 0e29db5..9a1d447 100644
--- a/src/commands/view.ts
+++ b/src/commands/view.ts
@@ -1,11 +1,7 @@
-import { AttachmentBuilder, CommandInteraction, DiscordAPIError, SlashCommandBuilder } from "discord.js";
+import { CommandInteraction, SlashCommandBuilder } from "discord.js";
 import { Command } from "../type/command";
-import { CoreClient } from "../client/client";
-import { readFileSync } from "fs";
-import path from "path";
-import Inventory from "../database/entities/app/Inventory";
-import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
 import AppLogger from "../client/appLogger";
+import CardSearchHelper from "../helpers/CardSearchHelper";
 
 export default class View extends Command {
     constructor() {
@@ -13,68 +9,32 @@ export default class View extends Command {
 
         this.CommandBuilder = new SlashCommandBuilder()
             .setName("view")
-            .setDescription("View a specific command")
+            .setDescription("Search for a card by its name")
             .addStringOption(x =>
                 x
-                    .setName("cardnumber")
-                    .setDescription("The card number to view")
+                    .setName("name")
+                    .setDescription("The card name to search for")
                     .setRequired(true));
     }
 
     public override async execute(interaction: CommandInteraction) {
-        const cardNumber = interaction.options.get("cardnumber");
+        const name = interaction.options.get("name", true);
 
-        AppLogger.LogSilly("Commands/View", `Parameters: cardNumber=${cardNumber?.value}`);
-
-        if (!cardNumber || !cardNumber.value) {
-            await interaction.reply("Card number is required.");
-            return;
-        }
-
-        const card = CoreClient.Cards
-            .flatMap(x => x.cards)
-            .find(x => x.id == cardNumber.value);
-
-        if (!card) {
-            await interaction.reply("Card not found.");
-            return;
-        }
-
-        const series = CoreClient.Cards
-            .find(x => x.cards.includes(card))!;
-
-        const files = [];
-        let imageFileName = "";
-
-        if (!(card.path.startsWith("http://") || card.path.startsWith("https://"))) {
-            const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.path));
-            imageFileName = card.path.split("/").pop()!;
-
-            const attachment = new AttachmentBuilder(image, { name: imageFileName });
-
-            files.push(attachment);
-        }
+        AppLogger.LogSilly("Commands/View", `Parameters: name=${name.value}`);
 
         await interaction.deferReply();
 
-        const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.id);
-        const quantityClaimed = inventory ? inventory.Quantity : 0;
+        const searchResult = await CardSearchHelper.GenerateSearchQuery(name.value!.toString(), interaction.user.id, 7);
 
-        const embed = CardDropHelperMetadata.GenerateDropEmbed({ card, series }, quantityClaimed, imageFileName);
-
-        try {
-            await interaction.editReply({
-                embeds: [ embed ],
-                files: files,
-            });
-        } catch (e) {
-            AppLogger.LogError("Commands/View", `Error sending view for card ${card.id}: ${e}`);
-
-            if (e instanceof DiscordAPIError) {
-                await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. Code: ${e.code}.`);
-            } else {
-                await interaction.editReply("Unable to send next drop. Please try again, and report this if it keeps happening. Code: UNKNOWN.");
-            }
+        if (!searchResult) {
+            await interaction.editReply("No results found");
+            return;
         }
+
+        await interaction.editReply({
+            embeds: [ searchResult.embed ],
+            components: [ searchResult.row ],
+            files: searchResult.attachments,
+        });
     }
 }
\ No newline at end of file
diff --git a/src/constants/CardConstants.ts b/src/constants/CardConstants.ts
index 0a87e22..60a7f59 100644
--- a/src/constants/CardConstants.ts
+++ b/src/constants/CardConstants.ts
@@ -3,4 +3,11 @@ export default class CardConstants {
     public static readonly TimerGiveAmount = 10;
     public static readonly DailyCurrency = 100;
     public static readonly StartingCurrency = 300;
+
+    // Multidrop
+    public static readonly MultidropCost = this.ClaimCost * 10;
+    public static readonly MultidropQuantity = 11;
+
+    // Effects
+    public static readonly UnusedChanceUpChance = 0.5;
 }
\ No newline at end of file
diff --git a/src/constants/EffectDetails.ts b/src/constants/EffectDetails.ts
new file mode 100644
index 0000000..9d1f2b6
--- /dev/null
+++ b/src/constants/EffectDetails.ts
@@ -0,0 +1,23 @@
+class EffectDetail {
+    public readonly id: string;
+    public readonly friendlyName: string;
+    public readonly duration: number;
+    public readonly cost: number;
+    public readonly cooldown: number;
+
+    constructor(id: string, friendlyName: string, duration: number, cost: number, cooldown: number) {
+        this.id = id;
+        this.friendlyName = friendlyName;
+        this.duration = duration;
+        this.cost = cost;
+        this.cooldown = cooldown;
+    }
+};
+
+export const EffectDetails = new Map<string, EffectDetail>([
+    [ "unclaimed", new EffectDetail("unclaimed", "Unclaimed Chance Up", 10 * 60 * 1000, 100, 3 * 60 * 60 * 1000) ],
+]);
+
+export const EffectChoices = [
+    { name: "Unclaimed Chance Up", value: "unclaimed" },
+];
diff --git a/src/constants/ErrorMessages.ts b/src/constants/ErrorMessages.ts
new file mode 100644
index 0000000..c8749ec
--- /dev/null
+++ b/src/constants/ErrorMessages.ts
@@ -0,0 +1,8 @@
+export default class ErrorMessages {
+    public static readonly BotSyncing = "Bot is currently syncing, please wait until its done.";
+    public static readonly SafeMode = "Safe Mode has been activated, please resync to continue.";
+    public static readonly UnableToFetchCard = "Unable to fetch card, please try again.";
+    public static readonly UnableToFetchUser = "Unable to fetch user, please try again.";
+
+    public static readonly NotEnoughCurrency = (need: number, have: number) => `Not enough currency! You need ${need} currency, you have ${have}!`;
+}
\ No newline at end of file
diff --git a/src/contracts/StringDropdownEventItem.ts b/src/contracts/StringDropdownEventItem.ts
new file mode 100644
index 0000000..1a7b0aa
--- /dev/null
+++ b/src/contracts/StringDropdownEventItem.ts
@@ -0,0 +1,10 @@
+import {Environment} from "../constants/Environment";
+import {StringDropdownEvent} from "../type/stringDropdownEvent";
+
+interface StringDropdownEventItem {
+    DropdownId: string,
+    Event: StringDropdownEvent,
+    Environment: Environment,
+}
+
+export default StringDropdownEventItem;
diff --git a/src/database/entities/app/Inventory.ts b/src/database/entities/app/Inventory.ts
index 469cbdc..c780dad 100644
--- a/src/database/entities/app/Inventory.ts
+++ b/src/database/entities/app/Inventory.ts
@@ -29,16 +29,16 @@ export default class Inventory extends AppBaseEntity {
         this.Quantity = quantity;
     }
 
-    public AddQuantity(amount: number) {
-        this.Quantity += amount;
-    }
-
     public RemoveQuantity(amount: number) {
         if (this.Quantity < amount) return;
 
         this.Quantity -= amount;
     }
 
+    public AddQuantity(amount: number) {
+        this.Quantity += amount;
+    }
+
     public AddClaim(claim: Claim) {
         this.Claims.push(claim);
     }
diff --git a/src/database/entities/app/User.ts b/src/database/entities/app/User.ts
index c3d8437..3a9018d 100644
--- a/src/database/entities/app/User.ts
+++ b/src/database/entities/app/User.ts
@@ -13,7 +13,7 @@ export default class User extends AppBaseEntity {
     @Column()
         Currency: number;
 
-    @Column()
+    @Column({ nullable: true })
         LastUsedDaily?: Date;
 
     public AddCurrency(amount: number) {
diff --git a/src/database/entities/app/UserEffect.ts b/src/database/entities/app/UserEffect.ts
new file mode 100644
index 0000000..447b9c5
--- /dev/null
+++ b/src/database/entities/app/UserEffect.ts
@@ -0,0 +1,86 @@
+import {Column, Entity} from "typeorm";
+import AppBaseEntity from "../../../contracts/AppBaseEntity";
+import AppDataSource from "../../dataSources/appDataSource";
+
+@Entity()
+export default class UserEffect extends AppBaseEntity {
+    constructor(name: string, userId: string, unused: number, WhenExpires?: Date) {
+        super();
+
+        this.Name = name;
+        this.UserId = userId;
+        this.Unused = unused;
+        this.WhenExpires = WhenExpires;
+    }
+
+    @Column()
+    Name: string;
+
+    @Column()
+    UserId: string;
+
+    @Column()
+    Unused: number;
+
+    @Column({ nullable: true })
+    WhenExpires?: Date;
+
+    public AddUnused(amount: number) {
+        this.Unused += amount;
+    }
+
+    public UseEffect(whenExpires: Date): boolean {
+        if (this.Unused == 0) {
+            return false;
+        }
+
+        this.Unused -= 1;
+        this.WhenExpires = whenExpires;
+
+        return true;
+    }
+
+    public IsEffectActive(): boolean {
+        const now = new Date();
+
+        if (this.WhenExpires && now < this.WhenExpires) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public static async FetchOneByUserIdAndName(userId: string, name: string): Promise<UserEffect | null> {
+        const repository = AppDataSource.getRepository(UserEffect);
+
+        const single = await repository.findOne({ where: { UserId: userId, Name: name } });
+
+        return single;
+    }
+
+    public static async FetchAllByUserIdPaginated(userId: string, page: number = 0, itemsPerPage: number = 10): Promise<[UserEffect[], number]> {
+        const repository = AppDataSource.getRepository(UserEffect);
+
+        const query = await repository.createQueryBuilder("effect")
+            .where("effect.UserId = :userId", { userId })
+            .andWhere("effect.Unused > 0")
+            .orderBy("effect.Name", "ASC")
+            .skip(page * itemsPerPage)
+            .take(itemsPerPage)
+            .getManyAndCount();
+
+        return query;
+    }
+
+    public static async FetchActiveEffectByUserId(userId: string): Promise<UserEffect | null> {
+        const repository = AppDataSource.getRepository(UserEffect);
+
+        const query = await repository.createQueryBuilder("effect")
+            .where("effect.UserId = :userId", { userId })
+            .andWhere("effect.WhenExpires IS NOT NULL")
+            .andWhere("effect.WhenExpires > :now", { now: new Date() })
+            .getOne();
+
+        return query;
+    }
+}
diff --git a/src/database/migrations/app/0.9/1729962056556-createUserEffect.ts b/src/database/migrations/app/0.9/1729962056556-createUserEffect.ts
new file mode 100644
index 0000000..f59b7d4
--- /dev/null
+++ b/src/database/migrations/app/0.9/1729962056556-createUserEffect.ts
@@ -0,0 +1,18 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+import MigrationHelper from "../../../../helpers/MigrationHelper";
+
+export class CreateUserEffect1729962056556 implements MigrationInterface {
+
+    public async up(queryRunner: QueryRunner): Promise<void> {
+        MigrationHelper.Up("1729962056556-createUserEffect", "0.9", [
+            "01-table-userEffect",
+        ], queryRunner);
+    }
+
+    public async down(queryRunner: QueryRunner): Promise<void> {
+        MigrationHelper.Down("1729962056556-createUserEffect", "0.9", [
+            "01-table-userEffect",
+        ], queryRunner);
+    }
+
+}
diff --git a/src/helpers/CardDropHelperMetadata.ts b/src/helpers/CardDropHelperMetadata.ts
deleted file mode 100644
index 342f347..0000000
--- a/src/helpers/CardDropHelperMetadata.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
-import { CardRarity, CardRarityToColour, CardRarityToString } from "../constants/CardRarity";
-import CardRarityChances from "../constants/CardRarityChances";
-import { DropResult } from "../contracts/SeriesMetadata";
-import { CoreClient } from "../client/client";
-import AppLogger from "../client/appLogger";
-import CardConstants from "../constants/CardConstants";
-import StringTools from "./StringTools";
-
-export default class CardDropHelperMetadata {
-    public static GetRandomCard(): DropResult | undefined {
-        const randomRarity = Math.random() * 100;
-
-        let cardRarity: CardRarity;
-
-        const bronzeChance = CardRarityChances.Bronze;
-        const silverChance = bronzeChance + CardRarityChances.Silver;
-        const goldChance = silverChance + CardRarityChances.Gold;
-        const mangaChance = goldChance + CardRarityChances.Manga;
-
-        if (randomRarity < bronzeChance) cardRarity = CardRarity.Bronze;
-        else if (randomRarity < silverChance) cardRarity = CardRarity.Silver;
-        else if (randomRarity < goldChance) cardRarity = CardRarity.Gold;
-        else if (randomRarity < mangaChance) cardRarity = CardRarity.Manga;
-        else cardRarity = CardRarity.Legendary;
-
-        const randomCard = this.GetRandomCardByRarity(cardRarity);
-
-        AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCard", `Random card: ${randomCard?.card.id} ${randomCard?.card.name}`);
-
-        return randomCard;
-    }
-
-    public static GetRandomCardByRarity(rarity: CardRarity): DropResult | undefined {
-        AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCardByRarity", `Parameters: rarity=${rarity}`);
-
-        const allCards = CoreClient.Cards
-            .flatMap(x => x.cards)
-            .filter(x => x.type == rarity);
-
-        const randomCardIndex = Math.floor(Math.random() * allCards.length);
-
-        const card = allCards[randomCardIndex];
-        const series = CoreClient.Cards
-            .find(x => x.cards.includes(card));
-
-        if (!series) {
-            AppLogger.LogWarn("CardDropHelperMetadata/GetRandomCardByRarity", `Series not found for card ${card.id}`);
-
-            return undefined;
-        }
-
-        AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCardByRarity", `Random card: ${card.id} ${card.name}`);
-
-        return {
-            series: series,
-            card: card,
-        };
-    }
-
-    public static GetCardByCardNumber(cardNumber: string): DropResult | undefined {
-        AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Parameters: cardNumber=${cardNumber}`);
-
-        const card = CoreClient.Cards
-            .flatMap(x => x.cards)
-            .find(x => x.id == cardNumber);
-
-        const series = CoreClient.Cards
-            .find(x => x.cards.find(y => y.id == card?.id));
-
-        AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Card: ${card?.id} ${card?.name}`);
-        AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Series: ${series?.id} ${series?.name}`);
-
-        if (!card || !series) {
-            AppLogger.LogVerbose("CardDropHelperMetadata/GetCardByCardNumber", `Unable to find card metadata: ${cardNumber}`);
-            return undefined;
-        }
-
-        return { card, series };
-    }
-
-    public static GenerateDropEmbed(drop: DropResult, quantityClaimed: number, imageFileName: string, claimedBy?: string, currency?: number): EmbedBuilder {
-        AppLogger.LogSilly("CardDropHelperMetadata/GenerateDropEmbed", `Parameters: drop=${drop.card.id}, quantityClaimed=${quantityClaimed}, imageFileName=${imageFileName}`);
-
-        const description = drop.card.subseries ?? drop.series.name;
-        let colour = CardRarityToColour(drop.card.type);
-
-        if (drop.card.colour && StringTools.IsHexCode(drop.card.colour)) {
-            const hexCode = Number("0x" + drop.card.colour);
-
-            if (hexCode) {
-                colour = hexCode;
-            } else {
-                AppLogger.LogWarn("CardDropHelperMetadata/GenerateDropEmbed", `Card's colour override is invalid: ${drop.card.id}, ${drop.card.colour}`);
-            }
-        } else if (drop.card.colour) {
-            AppLogger.LogWarn("CardDropHelperMetadata/GenerateDropEmbed", `Card's colour override is invalid: ${drop.card.id}, ${drop.card.colour}`);
-        }
-
-        let imageUrl = `attachment://${imageFileName}`;
-
-        if (drop.card.path.startsWith("http://") || drop.card.path.startsWith("https://")) {
-            imageUrl = drop.card.path;
-        }
-
-        const embed = new EmbedBuilder()
-            .setTitle(drop.card.name)
-            .setDescription(description)
-            .setFooter({ text: `${CardRarityToString(drop.card.type)} · ${drop.card.id}` })
-            .setColor(colour)
-            .setImage(imageUrl)
-            .addFields([
-                {
-                    name: "Claimed",
-                    value: `${quantityClaimed}`,
-                    inline: true,
-                }
-            ]);
-
-        if (claimedBy != null) {
-            embed.addFields([
-                {
-                    name: "Claimed by",
-                    value: claimedBy,
-                    inline: true,
-                }
-            ]);
-        }
-
-        if (currency != null) {
-            embed.addFields([
-                {
-                    name: "Currency",
-                    value: `${currency}`,
-                    inline: true,
-                }
-            ]);
-        }
-
-        return embed;
-    }
-
-    public static GenerateDropButtons(drop: DropResult, claimId: string, userId: string, disabled: boolean = false): ActionRowBuilder<ButtonBuilder> {
-        AppLogger.LogSilly("CardDropHelperMetadata/GenerateDropButtons", `Parameters: drop=${drop.card.id}, claimId=${claimId}, userId=${userId}`);
-
-        return new ActionRowBuilder<ButtonBuilder>()
-            .addComponents(
-                new ButtonBuilder()
-                    .setCustomId(`claim ${drop.card.id} ${claimId} ${userId}`)
-                    .setLabel(`Claim (${CardConstants.ClaimCost} 🪙)`)
-                    .setStyle(ButtonStyle.Primary)
-                    .setDisabled(disabled),
-                new ButtonBuilder()
-                    .setCustomId("reroll")
-                    .setLabel("Reroll")
-                    .setStyle(ButtonStyle.Secondary));
-    }
-}
diff --git a/src/helpers/CardSearchHelper.ts b/src/helpers/CardSearchHelper.ts
new file mode 100644
index 0000000..df0265c
--- /dev/null
+++ b/src/helpers/CardSearchHelper.ts
@@ -0,0 +1,116 @@
+import {ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder} from "discord.js";
+import Fuse from "fuse.js";
+import {CoreClient} from "../client/client.js";
+import Inventory from "../database/entities/app/Inventory.js";
+import {readFileSync} from "fs";
+import path from "path";
+import AppLogger from "../client/appLogger.js";
+import GetCardsHelper from "./DropHelpers/GetCardsHelper.js";
+import DropEmbedHelper from "./DropHelpers/DropEmbedHelper.js";
+
+interface ReturnedPage {
+    embed: EmbedBuilder,
+    row: ActionRowBuilder<ButtonBuilder>,
+    attachments: AttachmentBuilder[],
+    results: string[],
+}
+
+export default class CardSearchHelper {
+    public static async GenerateSearchQuery(query: string, userid: string, pages: number): Promise<ReturnedPage | undefined> {
+        AppLogger.LogSilly("CardSearchHelper/GenerateSearchQuery", `Parameters: query=${query}, userid=${userid}, pages=${pages}`);
+
+        const fzf = new Fuse(CoreClient.Cards.flatMap(x => x.cards), { keys: ["name"] });
+        const entries = fzf.search(query)
+            .splice(0, pages);
+
+        const entry = entries[0];
+        const results = entries
+            .flatMap(x => x.item.id);
+
+        if (!entry) {
+            AppLogger.LogVerbose("CardSearchHelper/GenerateSearchQuery", `Unable to find entry: ${query}`);
+
+            return undefined;
+        }
+
+        const card = GetCardsHelper.GetCardByCardNumber(entry.item.id);
+
+        if (!card) return undefined;
+
+        const attachments = [];
+        let imageFileName = "";
+
+        if (!(card.card.path.startsWith("http://") || card.card.path.startsWith("https://"))) {
+            const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.card.path));
+            imageFileName = card.card.path.split("/").pop()!;
+
+            const attachment = new AttachmentBuilder(image, { name: imageFileName });
+
+            attachments.push(attachment);
+        }
+
+        const inventory = await Inventory.FetchOneByCardNumberAndUserId(userid, card.card.id);
+        const quantityClaimed = inventory?.Quantity ?? 0;
+
+        const embed = DropEmbedHelper.GenerateDropEmbed(card, quantityClaimed, imageFileName);
+
+        const row = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
+                new ButtonBuilder()
+                    .setCustomId(`view 0 ${results.join(" ")}`)
+                    .setLabel("Previous")
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(true),
+                new ButtonBuilder()
+                    .setCustomId(`view 2 ${results.join(" ")}`)
+                    .setLabel("Next")
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(pages == 1));
+
+        return { embed, row, attachments, results };
+    }
+
+    public static async GenerateSearchPageFromQuery(results: string[], userid: string, page: number): Promise<ReturnedPage | undefined> {
+        const currentPageId = results[page - 1];
+
+        const card = GetCardsHelper.GetCardByCardNumber(currentPageId);
+
+        if (!card) {
+            AppLogger.LogError("CardSearchHelper/GenerateSearchPageFromQuery", `Unable to find card by id: ${currentPageId}.`);
+
+            return undefined;
+        }
+
+        const attachments = [];
+        let imageFileName = "";
+
+        if (!(card.card.path.startsWith("http://") || card.card.path.startsWith("https://"))) {
+            const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.card.path));
+            imageFileName = card.card.path.split("/").pop()!;
+
+            const attachment = new AttachmentBuilder(image, { name: imageFileName });
+
+            attachments.push(attachment);
+        }
+
+        const inventory = await Inventory.FetchOneByCardNumberAndUserId(userid, card.card.id);
+        const quantityClaimed = inventory?.Quantity ?? 0;
+
+        const embed = DropEmbedHelper.GenerateDropEmbed(card, quantityClaimed, imageFileName);
+
+        const row = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
+                new ButtonBuilder()
+                    .setCustomId(`view ${page - 1} ${results.join(" ")}`)
+                    .setLabel("Previous")
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(page - 1 == 0),
+                new ButtonBuilder()
+                    .setCustomId(`view ${page + 1} ${results.join(" ")}`)
+                    .setLabel("Next")
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(page == results.length));
+
+        return { embed, row, attachments, results };
+    }
+}
diff --git a/src/helpers/DropHelpers/DropEmbedHelper.ts b/src/helpers/DropHelpers/DropEmbedHelper.ts
new file mode 100644
index 0000000..08b5813
--- /dev/null
+++ b/src/helpers/DropHelpers/DropEmbedHelper.ts
@@ -0,0 +1,88 @@
+import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
+import { DropResult } from "../../contracts/SeriesMetadata";
+import AppLogger from "../../client/appLogger";
+import { CardRarityToColour, CardRarityToString } from "../../constants/CardRarity";
+import StringTools from "../StringTools";
+
+export default class DropEmbedHelper {
+    public static GenerateDropEmbed(drop: DropResult, quantityClaimed: number, imageFileName: string, claimedBy?: string, currency?: number): EmbedBuilder {
+        AppLogger.LogSilly("CardDropHelperMetadata/GenerateDropEmbed", `Parameters: drop=${drop.card.id}, quantityClaimed=${quantityClaimed}, imageFileName=${imageFileName}`);
+
+        const description = drop.card.subseries ?? drop.series.name;
+        let colour = CardRarityToColour(drop.card.type);
+
+        if (drop.card.colour && StringTools.IsHexCode(drop.card.colour)) {
+            const hexCode = Number("0x" + drop.card.colour);
+
+            if (hexCode) {
+                colour = hexCode;
+            } else {
+                AppLogger.LogWarn("CardDropHelperMetadata/GenerateDropEmbed", `Card's colour override is invalid: ${drop.card.id}, ${drop.card.colour}`);
+            }
+        } else if (drop.card.colour) {
+            AppLogger.LogWarn("CardDropHelperMetadata/GenerateDropEmbed", `Card's colour override is invalid: ${drop.card.id}, ${drop.card.colour}`);
+        }
+
+        let imageUrl = `attachment://${imageFileName}`;
+
+        if (drop.card.path.startsWith("http://") || drop.card.path.startsWith("https://")) {
+            imageUrl = drop.card.path;
+        }
+
+        const embed = new EmbedBuilder()
+            .setTitle(drop.card.name)
+            .setDescription(description)
+            .setFooter({ text: `${CardRarityToString(drop.card.type)} · ${drop.card.id}` })
+            .setColor(colour)
+            .setImage(imageUrl)
+            .addFields([
+                {
+                    name: "Claimed",
+                    value: `${quantityClaimed}`,
+                    inline: true,
+                }
+            ]);
+
+        if (claimedBy != null) {
+            embed.addFields([
+                {
+                    name: "Claimed by",
+                    value: claimedBy,
+                    inline: true,
+                }
+            ]);
+        }
+
+        if (currency != null) {
+            embed.addFields([
+                {
+                    name: "Currency",
+                    value: `${currency}`,
+                    inline: true,
+                }
+            ]);
+        }
+
+        return embed;
+    }
+
+    public static GenerateDropButtons(drop: DropResult, claimId: string, userId: string, disabled: boolean = false): ActionRowBuilder<ButtonBuilder> {
+        AppLogger.LogSilly("CardDropHelperMetadata/GenerateDropButtons", `Parameters: drop=${drop.card.id}, claimId=${claimId}, userId=${userId}`);
+
+        return new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
+                new ButtonBuilder()
+                    .setCustomId(`claim ${drop.card.id} ${claimId} ${userId}`)
+                    .setLabel("Claim")
+                    .setStyle(ButtonStyle.Success)
+                    .setDisabled(disabled),
+                new ButtonBuilder()
+                    .setCustomId(`sacrifice confirm ${userId} ${drop.card.id} 1`)
+                    .setLabel(`Sacrifice`)
+                    .setStyle(ButtonStyle.Danger),
+                new ButtonBuilder()
+                    .setCustomId("reroll")
+                    .setEmoji("🔁")
+                    .setStyle(ButtonStyle.Primary),);
+    }
+}
\ No newline at end of file
diff --git a/src/helpers/DropHelpers/GetCardsHelper.ts b/src/helpers/DropHelpers/GetCardsHelper.ts
new file mode 100644
index 0000000..8bc4fe7
--- /dev/null
+++ b/src/helpers/DropHelpers/GetCardsHelper.ts
@@ -0,0 +1,91 @@
+import AppLogger from "../../client/appLogger";
+import { CoreClient } from "../../client/client";
+import CardConstants from "../../constants/CardConstants";
+import { CardRarity } from "../../constants/CardRarity";
+import CardRarityChances from "../../constants/CardRarityChances";
+import { DropResult } from "../../contracts/SeriesMetadata";
+import EffectHelper from "../EffectHelper";
+import GetUnclaimedCardsHelper from "./GetUnclaimedCardsHelper";
+
+export default class GetCardsHelper {
+    public static async FetchCard(userId: string): Promise<DropResult | undefined> {
+        const hasChanceUpEffect = await EffectHelper.HasEffect(userId, "unclaimed");
+
+        if (hasChanceUpEffect && Math.random() <= CardConstants.UnusedChanceUpChance) {
+            return await GetUnclaimedCardsHelper.GetRandomCardUnclaimed(userId);
+        }
+
+        return this.GetRandomCard();
+    }
+
+    public static GetRandomCard(): DropResult | undefined {
+        const randomRarity = Math.random() * 100;
+
+        let cardRarity: CardRarity;
+
+        const bronzeChance = CardRarityChances.Bronze;
+        const silverChance = bronzeChance + CardRarityChances.Silver;
+        const goldChance = silverChance + CardRarityChances.Gold;
+        const mangaChance = goldChance + CardRarityChances.Manga;
+
+        if (randomRarity < bronzeChance) cardRarity = CardRarity.Bronze;
+        else if (randomRarity < silverChance) cardRarity = CardRarity.Silver;
+        else if (randomRarity < goldChance) cardRarity = CardRarity.Gold;
+        else if (randomRarity < mangaChance) cardRarity = CardRarity.Manga;
+        else cardRarity = CardRarity.Legendary;
+
+        const randomCard = this.GetRandomCardByRarity(cardRarity);
+
+        AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCard", `Random card: ${randomCard?.card.id} ${randomCard?.card.name}`);
+
+        return randomCard;
+    }
+
+    public static GetRandomCardByRarity(rarity: CardRarity): DropResult | undefined {
+        AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCardByRarity", `Parameters: rarity=${rarity}`);
+
+        const allCards = CoreClient.Cards
+            .flatMap(x => x.cards)
+            .filter(x => x.type == rarity);
+
+        const randomCardIndex = Math.floor(Math.random() * allCards.length);
+
+        const card = allCards[randomCardIndex];
+        const series = CoreClient.Cards
+            .find(x => x.cards.includes(card));
+
+        if (!series) {
+            AppLogger.LogWarn("CardDropHelperMetadata/GetRandomCardByRarity", `Series not found for card ${card.id}`);
+
+            return undefined;
+        }
+
+        AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCardByRarity", `Random card: ${card.id} ${card.name}`);
+
+        return {
+            series: series,
+            card: card,
+        };
+    }
+
+    public static GetCardByCardNumber(cardNumber: string): DropResult | undefined {
+        AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Parameters: cardNumber=${cardNumber}`);
+
+        const card = CoreClient.Cards
+            .flatMap(x => x.cards)
+            .find(x => x.id == cardNumber);
+
+        const series = CoreClient.Cards
+            .find(x => x.cards.find(y => y.id == card?.id));
+
+        AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Card: ${card?.id} ${card?.name}`);
+        AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Series: ${series?.id} ${series?.name}`);
+
+        if (!card || !series) {
+            AppLogger.LogVerbose("CardDropHelperMetadata/GetCardByCardNumber", `Unable to find card metadata: ${cardNumber}`);
+            return undefined;
+        }
+
+        return { card, series };
+    }
+}
diff --git a/src/helpers/DropHelpers/GetUnclaimedCardsHelper.ts b/src/helpers/DropHelpers/GetUnclaimedCardsHelper.ts
new file mode 100644
index 0000000..87fafea
--- /dev/null
+++ b/src/helpers/DropHelpers/GetUnclaimedCardsHelper.ts
@@ -0,0 +1,63 @@
+import AppLogger from "../../client/appLogger";
+import { CoreClient } from "../../client/client";
+import { CardRarity } from "../../constants/CardRarity";
+import CardRarityChances from "../../constants/CardRarityChances";
+import { DropResult } from "../../contracts/SeriesMetadata";
+import Inventory from "../../database/entities/app/Inventory";
+import GetCardsHelper from "./GetCardsHelper";
+
+export default class GetUnclaimedCardsHelper {
+    public static async GetRandomCardUnclaimed(userId: string): Promise<DropResult | undefined> {
+        const randomRarity = Math.random() * 100;
+
+        let cardRarity: CardRarity;
+
+        const bronzeChance = CardRarityChances.Bronze;
+        const silverChance = bronzeChance + CardRarityChances.Silver;
+        const goldChance = silverChance + CardRarityChances.Gold;
+        const mangaChance = goldChance + CardRarityChances.Manga;
+
+        if (randomRarity < bronzeChance) cardRarity = CardRarity.Bronze;
+        else if (randomRarity < silverChance) cardRarity = CardRarity.Silver;
+        else if (randomRarity < goldChance) cardRarity = CardRarity.Gold;
+        else if (randomRarity < mangaChance) cardRarity = CardRarity.Manga;
+        else cardRarity = CardRarity.Legendary;
+
+        const randomCard = await this.GetRandomCardByRarityUnclaimed(cardRarity, userId);
+
+        return randomCard;
+    }
+
+    public static async GetRandomCardByRarityUnclaimed(rarity: CardRarity, userId: string): Promise<DropResult | undefined> {
+        const claimedCards = await Inventory.FetchAllByUserId(userId);
+
+        if (!claimedCards) {
+            // They don't have any cards, so safe to get any random card
+            return GetCardsHelper.GetRandomCardByRarity(rarity);
+        }
+
+        const allCards = CoreClient.Cards
+            .flatMap(x => x.cards)
+            .filter(x => x.type == rarity)
+            .filter(x => !claimedCards.find(y => y.CardNumber == x.id));
+
+        if (!allCards) return undefined;
+
+        const randomCardIndex = Math.floor(Math.random() * allCards.length);
+
+        const card = allCards[randomCardIndex];
+        const series = CoreClient.Cards
+            .find(x => x.cards.includes(card));
+
+        if (!series) {
+            AppLogger.LogWarn("CardDropHelperMetadata/GetRandomCardByRarityUnclaimed", `Series not found for card ${card.id}`);
+
+            return undefined;
+        }
+
+        return {
+            series: series,
+            card: card,
+        };
+    }
+}
diff --git a/src/helpers/DropHelpers/MultidropEmbedHelper.ts b/src/helpers/DropHelpers/MultidropEmbedHelper.ts
new file mode 100644
index 0000000..526993e
--- /dev/null
+++ b/src/helpers/DropHelpers/MultidropEmbedHelper.ts
@@ -0,0 +1,28 @@
+import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
+import { DropResult } from "../../contracts/SeriesMetadata";
+import { GetSacrificeAmount } from "../../constants/CardRarity";
+import DropEmbedHelper from "./DropEmbedHelper";
+
+export default class MultidropEmbedHelper {
+    public static GenerateMultidropEmbed(drop: DropResult, quantityClaimed: number, imageFileName: string, cardsRemaining: number, claimedBy?: string, currency?: number): EmbedBuilder {
+        const dropEmbed = DropEmbedHelper.GenerateDropEmbed(drop, quantityClaimed, imageFileName, claimedBy, currency);
+
+        dropEmbed.setFooter({ text: `${dropEmbed.data.footer?.text} · ${cardsRemaining} Remaining`});
+
+        return dropEmbed;
+    }
+
+    public static GenerateMultidropButtons(drop: DropResult, cardsRemaining: number, userId: string, disabled = false): ActionRowBuilder<ButtonBuilder> {
+        return new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
+                new ButtonBuilder()
+                    .setCustomId(`multidrop keep ${drop.card.id} ${cardsRemaining} ${userId}`)
+                    .setLabel("Keep")
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(disabled),
+                new ButtonBuilder()
+                    .setCustomId(`multidrop sacrifice ${drop.card.id} ${cardsRemaining} ${userId}`)
+                    .setLabel(`Sacrifice (+${GetSacrificeAmount(drop.card.type)} 🪙)`)
+                    .setStyle(ButtonStyle.Secondary));
+    }
+}
diff --git a/src/helpers/EffectHelper.ts b/src/helpers/EffectHelper.ts
new file mode 100644
index 0000000..235ea08
--- /dev/null
+++ b/src/helpers/EffectHelper.ts
@@ -0,0 +1,194 @@
+import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
+import UserEffect from "../database/entities/app/UserEffect";
+import EmbedColours from "../constants/EmbedColours";
+import { EffectDetails } from "../constants/EffectDetails";
+import User from "../database/entities/app/User";
+import CardConstants from "../constants/CardConstants";
+import AppLogger from "../client/appLogger";
+
+export default class EffectHelper {
+    public static async AddEffectToUserInventory(userId: string, name: string, quantity: number = 1) {
+        let effect = await UserEffect.FetchOneByUserIdAndName(userId, name);
+
+        if (!effect) {
+            effect = new UserEffect(name, userId, quantity);
+        } else {
+            effect.AddUnused(quantity);
+        }
+
+        await effect.Save(UserEffect, effect);
+    }
+
+    public static async UseEffect(userId: string, name: string, whenExpires: Date): Promise<boolean> {
+        const canUseEffect = await this.CanUseEffect(userId, name);
+
+        if (!canUseEffect) return false;
+
+        const effect = await UserEffect.FetchOneByUserIdAndName(userId, name);
+
+        effect!.UseEffect(whenExpires);
+
+        await effect!.Save(UserEffect, effect!);
+
+        return true;
+    }
+
+    public static async CanUseEffect(userId: string, name: string): Promise<boolean> {
+        const effect = await UserEffect.FetchOneByUserIdAndName(userId, name);
+        const now = new Date();
+
+        if (!effect || effect.Unused == 0) {
+            return false;
+        }
+
+        const effectDetail = EffectDetails.get(effect.Name);
+
+        if (!effectDetail) {
+            return false;
+        }
+
+        if (effect.WhenExpires && now < new Date(effect.WhenExpires.getTime() + effectDetail.cooldown)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public static async HasEffect(userId: string, name: string): Promise<boolean> {
+        const effect = await UserEffect.FetchOneByUserIdAndName(userId, name);
+        const now = new Date();
+
+        if (!effect || !effect.WhenExpires) {
+            return false;
+        }
+
+        if (now > effect.WhenExpires) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public static async GenerateEffectListEmbed(userId: string, page: number): Promise<{
+        embed: EmbedBuilder,
+        row: ActionRowBuilder<ButtonBuilder>,
+    }> {
+        const itemsPerPage = 10;
+
+        const query = await UserEffect.FetchAllByUserIdPaginated(userId, page - 1, itemsPerPage);
+        const activeEffect = await UserEffect.FetchActiveEffectByUserId(userId);
+
+        const effects = query[0];
+        const count = query[1];
+
+        const totalPages = count > 0 ? Math.ceil(count / itemsPerPage) : 1;
+
+        let description = "*none*";
+
+        if (effects.length > 0) {
+            description = effects.map(x => `${EffectDetails.get(x.Name)?.friendlyName} x${x.Unused}`).join("\n");
+        }
+
+        const embed = new EmbedBuilder()
+            .setTitle("Effects")
+            .setDescription(description)
+            .setColor(EmbedColours.Ok)
+            .setFooter({ text: `Page ${page} of ${totalPages}` });
+
+        if (activeEffect) {
+            embed.addFields([
+                {
+                    name: "Active",
+                    value: `${EffectDetails.get(activeEffect.Name)?.friendlyName}`,
+                    inline: true,
+                },
+                {
+                    name: "Expires",
+                    value: `<t:${Math.round(activeEffect.WhenExpires!.getTime() / 1000)}>`,
+                    inline: true,
+                },
+            ]);
+        }
+
+        const row = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
+                new ButtonBuilder()
+                    .setCustomId(`effects list ${page - 1}`)
+                    .setLabel("Previous")
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(page == 1),
+                new ButtonBuilder()
+                    .setCustomId(`effects list ${page + 1}`)
+                    .setLabel("Next")
+                    .setStyle(ButtonStyle.Primary)
+                    .setDisabled(page == totalPages),
+            );
+
+        return {
+            embed,
+            row,
+        };
+    }
+
+    public static async GenerateEffectBuyEmbed(userId: string, id: string, quantity: number, disabled: boolean): Promise<{
+        embed: EmbedBuilder,
+        row: ActionRowBuilder<ButtonBuilder>,
+    } | string> {
+        const effectDetail = EffectDetails.get(id);
+
+        if (!effectDetail) {
+            return "Effect detail not found!";
+        }
+
+        const totalCost = effectDetail.cost * quantity;
+
+        let user = await User.FetchOneById(User, userId);
+
+        if (!user) {
+            user = new User(userId, CardConstants.StartingCurrency);
+            await user.Save(User, user);
+
+            AppLogger.LogInfo("EffectHelper", `Created initial user entity for : ${userId}`);
+        }
+
+        if (user.Currency < totalCost) {
+            return `You don't have enough currency to buy this! You have \`${user.Currency} Currency\` and need \`${totalCost} Currency\`!`;
+        }
+
+        const embed = new EmbedBuilder()
+            .setTitle("Buy Effect")
+            .setDescription(effectDetail.friendlyName)
+            .setColor(EmbedColours.Ok)
+            .addFields([
+                {
+                    name: "Cost",
+                    value: `${totalCost}`,
+                    inline: true,
+                },
+                {
+                    name: "Quantity",
+                    value: `${quantity}`,
+                    inline: true,
+                },
+            ]);
+
+        const row = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents([
+                new ButtonBuilder()
+                    .setCustomId(`effects buy confirm ${id} ${quantity}`)
+                    .setLabel("Confirm")
+                    .setStyle(ButtonStyle.Success)
+                    .setDisabled(disabled),
+                new ButtonBuilder()
+                    .setCustomId(`effects buy cancel ${id} ${quantity}`)
+                    .setLabel("Cancel")
+                    .setStyle(ButtonStyle.Danger)
+                    .setDisabled(disabled),
+            ]);
+
+        return {
+            embed,
+            row,
+        }
+    }
+}
diff --git a/src/helpers/ImageHelper.ts b/src/helpers/ImageHelper.ts
index 1b0c856..6156938 100644
--- a/src/helpers/ImageHelper.ts
+++ b/src/helpers/ImageHelper.ts
@@ -68,7 +68,7 @@ export default class ImageHelper {
 
                 ctx.drawImage(image, imageX, imageY);
             }
-            catch (e) {
+            catch {
                 // TODO: Enable once we've investigated a fix
                 //AppLogger.CatchError("ImageHelper", e);
             }
diff --git a/src/helpers/InventoryHelper.ts b/src/helpers/InventoryHelper.ts
index 8b8ffa1..3a7fbff 100644
--- a/src/helpers/InventoryHelper.ts
+++ b/src/helpers/InventoryHelper.ts
@@ -1,4 +1,4 @@
-import { ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
+import { ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from "discord.js";
 import Inventory from "../database/entities/app/Inventory";
 import { CoreClient } from "../client/client";
 import EmbedColours from "../constants/EmbedColours";
@@ -24,7 +24,8 @@ interface InventoryPageCards {
 
 interface ReturnedInventoryPage {
     embed: EmbedBuilder,
-    row: ActionRowBuilder<ButtonBuilder>,
+    row1: ActionRowBuilder<ButtonBuilder>,
+    row2: ActionRowBuilder<StringSelectMenuBuilder>,
     image: AttachmentBuilder,
 }
 
@@ -99,7 +100,7 @@ export default class InventoryHelper {
             .setColor(EmbedColours.Ok)
             .setImage("attachment://page.png");
 
-        const row = new ActionRowBuilder<ButtonBuilder>()
+        const row1 = new ActionRowBuilder<ButtonBuilder>()
             .addComponents(
                 new ButtonBuilder()
                     .setCustomId(`inventory ${userid} ${page - 1}`)
@@ -112,9 +113,23 @@ export default class InventoryHelper {
                     .setStyle(ButtonStyle.Primary)
                     .setDisabled(page + 1 == pages.length));
 
+        let pageNum = 0;
+
+        const row2 = new ActionRowBuilder<StringSelectMenuBuilder>()
+            .addComponents(
+                new StringSelectMenuBuilder()
+                    .setCustomId("inventory")
+                    .setPlaceholder(`${currentPage.name} (${currentPage.seriesSubpage + 1})`)
+                    .addOptions(...pages.map(x =>
+                        new StringSelectMenuOptionBuilder()
+                            .setLabel(`${x.name} (${x.seriesSubpage + 1})`.substring(0, 100))
+                            .setDescription(`Page ${pageNum + 1}`)
+                            .setDefault(currentPage.id == x.id)
+                            .setValue(`${userid} ${pageNum++}`))));
+
         const buffer = await ImageHelper.GenerateCardImageGrid(currentPage.cards.map(x => ({ id: x.id, path: x.path })));
         const image = new AttachmentBuilder(buffer, { name: "page.png" });
 
-        return { embed, row, image };
+        return { embed, row1, row2, image };
     }
 }
diff --git a/src/helpers/TimeLengthInput.ts b/src/helpers/TimeLengthInput.ts
index d1d8734..aef58ba 100644
--- a/src/helpers/TimeLengthInput.ts
+++ b/src/helpers/TimeLengthInput.ts
@@ -118,4 +118,19 @@ export default class TimeLengthInput {
 
         return desNumber;
     }
+
+    public static ConvertFromMilliseconds(ms: number): TimeLengthInput {
+        const seconds = Math.floor(ms / 1000);
+        const minutes = Math.floor(seconds / 60);
+        const hours = Math.floor(minutes / 60);
+        const days = Math.floor(hours / 24);
+
+        const remainingSeconds = seconds % 60;
+        const remainingMinutes = minutes % 60;
+        const remainingHours = hours % 24;
+
+        const timeString = `${days}d ${remainingHours}h ${remainingMinutes}m ${remainingSeconds}s`;
+
+        return new TimeLengthInput(timeString);
+    }
 }
\ No newline at end of file
diff --git a/src/registry.ts b/src/registry.ts
index 5ba3af6..3ae885d 100644
--- a/src/registry.ts
+++ b/src/registry.ts
@@ -7,9 +7,12 @@ import AllBalance from "./commands/allbalance";
 import Balance from "./commands/balance";
 import Daily from "./commands/daily";
 import Drop from "./commands/drop";
+import Effects from "./commands/effects";
 import Gdrivesync from "./commands/gdrivesync";
 import Give from "./commands/give";
+import Id from "./commands/id";
 import Inventory from "./commands/inventory";
+import Multidrop from "./commands/multidrop";
 import Resync from "./commands/resync";
 import Sacrifice from "./commands/sacrifice";
 import Series from "./commands/series";
@@ -23,11 +26,17 @@ import Droprarity from "./commands/stage/droprarity";
 
 // Button Event Imports
 import Claim from "./buttonEvents/Claim";
+import EffectsButtonEvent from "./buttonEvents/Effects";
 import InventoryButtonEvent from "./buttonEvents/Inventory";
+import MultidropButtonEvent from "./buttonEvents/Multidrop";
 import Reroll from "./buttonEvents/Reroll";
 import SacrificeButtonEvent from "./buttonEvents/Sacrifice";
 import SeriesEvent from "./buttonEvents/Series";
 import TradeButtonEvent from "./buttonEvents/Trade";
+import ViewButtonEvent from "./buttonEvents/View";
+
+// String Dropdown Event Imports
+import InventoryStringDropdown from "./stringDropdowns/Inventory";
 
 export default class Registry {
     public static RegisterCommands() {
@@ -37,9 +46,12 @@ export default class Registry {
         CoreClient.RegisterCommand("balance", new Balance());
         CoreClient.RegisterCommand("daily", new Daily());
         CoreClient.RegisterCommand("drop", new Drop());
+        CoreClient.RegisterCommand("effects", new Effects());
         CoreClient.RegisterCommand("gdrivesync", new Gdrivesync());
         CoreClient.RegisterCommand("give", new Give());
+        CoreClient.RegisterCommand("id", new Id());
         CoreClient.RegisterCommand("inventory", new Inventory());
+        CoreClient.RegisterCommand("multidrop", new Multidrop());
         CoreClient.RegisterCommand("resync", new Resync());
         CoreClient.RegisterCommand("sacrifice", new Sacrifice());
         CoreClient.RegisterCommand("series", new Series());
@@ -54,10 +66,17 @@ export default class Registry {
 
     public static RegisterButtonEvents() {
         CoreClient.RegisterButtonEvent("claim", new Claim());
+        CoreClient.RegisterButtonEvent("effects", new EffectsButtonEvent());
         CoreClient.RegisterButtonEvent("inventory", new InventoryButtonEvent());
+        CoreClient.RegisterButtonEvent("multidrop", new MultidropButtonEvent());
         CoreClient.RegisterButtonEvent("reroll", new Reroll());
         CoreClient.RegisterButtonEvent("sacrifice", new SacrificeButtonEvent());
         CoreClient.RegisterButtonEvent("series", new SeriesEvent());
         CoreClient.RegisterButtonEvent("trade", new TradeButtonEvent());
+        CoreClient.RegisterButtonEvent("view", new ViewButtonEvent());
+    }
+
+    public static RegisterStringDropdownEvents() {
+        CoreClient.RegisterStringDropdownEvent("inventory", new InventoryStringDropdown());
     }
 }
\ No newline at end of file
diff --git a/src/stringDropdowns/Inventory.ts b/src/stringDropdowns/Inventory.ts
new file mode 100644
index 0000000..2a218ea
--- /dev/null
+++ b/src/stringDropdowns/Inventory.ts
@@ -0,0 +1,43 @@
+import {StringSelectMenuInteraction} from "discord.js";
+import {StringDropdownEvent} from "../type/stringDropdownEvent";
+import AppLogger from "../client/appLogger";
+import InventoryHelper from "../helpers/InventoryHelper";
+
+export default class Inventory extends StringDropdownEvent {
+    public override async execute(interaction: StringSelectMenuInteraction) {
+        if (!interaction.guild) return;
+
+        const userid = interaction.values[0].split(" ")[0];
+        const page = interaction.values[0].split(" ")[1];
+
+        AppLogger.LogDebug("StringDropdown/Inventory", `Parameters: userid=${userid}, page=${page}`);
+
+        await interaction.deferUpdate();
+
+        const member = interaction.guild.members.cache.find(x => x.id == userid) || await interaction.guild.members.fetch(userid);
+
+        if (!member) {
+            await interaction.reply("Unable to find user.");
+            return;
+        }
+
+        try {
+            const embed = await InventoryHelper.GenerateInventoryPage(member.user.username, member.user.id, Number(page));
+
+            if (!embed) {
+                await interaction.followUp("No page for user found.");
+                return;
+            }
+
+            await interaction.editReply({
+                files: [ embed.image ],
+                embeds: [ embed.embed ],
+                components: [ embed.row1, embed.row2 ],
+            });
+        } catch (e) {
+            AppLogger.LogError("StringDropdown/Inventory", `Error generating inventory page for ${member.user.username} with id ${member.user.id}: ${e}`);
+
+            await interaction.followUp("An error has occurred running this command.");
+        }
+    }
+}
diff --git a/src/timers/PurgeClaims.ts b/src/timers/PurgeClaims.ts
index a0ed9d0..4ca8902 100644
--- a/src/timers/PurgeClaims.ts
+++ b/src/timers/PurgeClaims.ts
@@ -4,7 +4,7 @@ import Claim from "../database/entities/app/Claim";
 export default async function PurgeClaims() {
     const claims = await Claim.FetchAll(Claim);
 
-    const whenLastClaimable = new Date(Date.now() - (1000 * 60 * 5)); // 5 minutes ago
+    const whenLastClaimable = new Date(Date.now() - (1000 * 60 * 2)); // 2 minutes ago
 
     const expiredClaims = claims.filter(x => x.WhenCreated < whenLastClaimable);
 
diff --git a/src/type/stringDropdownEvent.ts b/src/type/stringDropdownEvent.ts
new file mode 100644
index 0000000..c097cda
--- /dev/null
+++ b/src/type/stringDropdownEvent.ts
@@ -0,0 +1,5 @@
+import {StringSelectMenuInteraction} from "discord.js";
+
+export abstract class StringDropdownEvent {
+    abstract execute(interaction: StringSelectMenuInteraction): Promise<void>;
+}
diff --git a/tests/.gitkeep b/tests/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/__functions__/discord.js/GenerateButtonInteractionMock.ts b/tests/__functions__/discord.js/GenerateButtonInteractionMock.ts
new file mode 100644
index 0000000..2199477
--- /dev/null
+++ b/tests/__functions__/discord.js/GenerateButtonInteractionMock.ts
@@ -0,0 +1,23 @@
+import { ButtonInteraction } from "../../__types__/discord.js";
+
+export default function GenerateButtonInteractionMock(): ButtonInteraction {
+    return {
+        guild: {},
+        guildId: "guildId",
+        channel: {
+            isSendable: jest.fn().mockReturnValue(true),
+            send: jest.fn(),
+        },
+        deferUpdate: jest.fn(),
+        editReply: jest.fn(),
+        message: {
+            createdAt: new Date(1000 * 60 * 27),
+        },
+        user: {
+            id: "userId",
+        },
+        customId: "customId",
+        update: jest.fn(),
+        reply: jest.fn(),
+    };
+}
\ No newline at end of file
diff --git a/tests/__functions__/discord.js/GenerateCommandInteractionMock.ts b/tests/__functions__/discord.js/GenerateCommandInteractionMock.ts
new file mode 100644
index 0000000..128c25b
--- /dev/null
+++ b/tests/__functions__/discord.js/GenerateCommandInteractionMock.ts
@@ -0,0 +1,17 @@
+import { CommandInteraction } from "../../__types__/discord.js";
+
+export default function GenerateCommandInteractionMock(options?: {
+    subcommand?: string,
+}): CommandInteraction {
+    return {
+        deferReply: jest.fn(),
+        editReply: jest.fn(),
+        isChatInputCommand: jest.fn().mockReturnValue(true),
+        options: {
+            getSubcommand: jest.fn().mockReturnValue(options?.subcommand),
+        },
+        user: {
+            id: "userId",
+        },
+    };
+}
\ No newline at end of file
diff --git a/tests/__types__/discord.js.ts b/tests/__types__/discord.js.ts
new file mode 100644
index 0000000..3304af4
--- /dev/null
+++ b/tests/__types__/discord.js.ts
@@ -0,0 +1,31 @@
+export type ButtonInteraction = {
+    guild: object | null,
+    guildId: string | null,
+    channel: {
+        isSendable: jest.Func,
+        send: jest.Func,
+    } | null,
+    deferUpdate: jest.Func,
+    editReply: jest.Func,
+    message: {
+        createdAt: Date,
+    } | null,
+    user: {
+        id: string,
+    } | null,
+    customId: string,
+    update: jest.Func,
+    reply: jest.Func,
+}
+
+export type CommandInteraction = {
+    deferReply: jest.Func,
+    editReply: jest.Func,
+    isChatInputCommand: jest.Func,
+    options: {
+        getSubcommand: jest.Func,
+    },
+    user: {
+        id: string,
+    },
+}
\ No newline at end of file
diff --git a/tests/buttonEvents/Claim.test.ts b/tests/buttonEvents/Claim.test.ts
new file mode 100644
index 0000000..1e7027c
--- /dev/null
+++ b/tests/buttonEvents/Claim.test.ts
@@ -0,0 +1,90 @@
+import { ButtonInteraction, TextChannel } from "discord.js";
+import Claim from "../../src/buttonEvents/Claim";
+import { ButtonInteraction as ButtonInteractionType } from "../__types__/discord.js";
+import GenerateButtonInteractionMock from "../__functions__/discord.js/GenerateButtonInteractionMock";
+
+jest.mock("../../src/client/appLogger");
+
+let interaction: ButtonInteractionType;
+
+beforeEach(() => {
+    jest.useFakeTimers();
+    jest.setSystemTime(1000 * 60 * 30);
+
+    interaction = GenerateButtonInteractionMock();
+    interaction.customId = "claim cardNumber claimId droppedBy userId";
+});
+
+afterAll(() => {
+    jest.useRealTimers();
+});
+
+test("GIVEN interaction.guild is null, EXPECT nothing to happen", async () => {
+    // Arrange
+    interaction.guild = null;
+
+    // Act
+    const claim = new Claim();
+    await claim.execute(interaction as unknown as ButtonInteraction);
+
+    // Assert
+    expect(interaction.deferUpdate).not.toHaveBeenCalled();
+    expect(interaction.editReply).not.toHaveBeenCalled();
+    expect((interaction.channel as TextChannel).send).not.toHaveBeenCalled();
+});
+
+test("GIVEN interaction.guildId is null, EXPECT nothing to happen", async () => {
+    // Arrange
+    interaction.guildId = null;
+
+    // Act
+    const claim = new Claim();
+    await claim.execute(interaction as unknown as ButtonInteraction);
+
+    // Assert
+    expect(interaction.deferUpdate).not.toHaveBeenCalled();
+    expect(interaction.editReply).not.toHaveBeenCalled();
+    expect((interaction.channel as TextChannel).send).not.toHaveBeenCalled();
+});
+
+test("GIVEN interaction.channel is null, EXPECT nothing to happen", async () => {
+    // Arrange
+    interaction.channel = null;
+
+    // Act
+    const claim = new Claim();
+    await claim.execute(interaction as unknown as ButtonInteraction);
+
+    // Assert
+    expect(interaction.deferUpdate).not.toHaveBeenCalled();
+    expect(interaction.editReply).not.toHaveBeenCalled();
+});
+
+test("GIVEN channel is not sendable, EXPECT nothing to happen", async () => {
+    // Arrange
+    interaction.channel!.isSendable = jest.fn().mockReturnValue(false);
+
+    // Act
+    const claim = new Claim();
+    await claim.execute(interaction as unknown as ButtonInteraction);
+
+    // Assert
+    expect(interaction.deferUpdate).not.toHaveBeenCalled();
+    expect(interaction.editReply).not.toHaveBeenCalled();
+    expect((interaction.channel as TextChannel).send).not.toHaveBeenCalled();
+});
+
+test("GIVEN interaction.message was created more than 5 minutes ago, EXPECT error", async () => {
+    // Arrange
+    interaction.message!.createdAt = new Date(0);
+
+    // Act
+    const claim = new Claim();
+    await claim.execute(interaction as unknown as ButtonInteraction);
+
+    // Assert
+    expect(interaction.channel!.send).toHaveBeenCalledTimes(1);
+    expect(interaction.channel!.send).toHaveBeenCalledWith("[object Object], Cards can only be claimed within 2 minutes of it being dropped!");
+
+    expect(interaction.editReply).not.toHaveBeenCalled();
+});
\ No newline at end of file
diff --git a/tests/buttonEvents/Effects.test.ts b/tests/buttonEvents/Effects.test.ts
new file mode 100644
index 0000000..8fb1023
--- /dev/null
+++ b/tests/buttonEvents/Effects.test.ts
@@ -0,0 +1,68 @@
+import { ButtonInteraction } from "discord.js";
+import Effects from "../../src/buttonEvents/Effects";
+import GenerateButtonInteractionMock from "../__functions__/discord.js/GenerateButtonInteractionMock";
+import { ButtonInteraction as ButtonInteractionType } from "../__types__/discord.js";
+import List from "../../src/buttonEvents/Effects/List";
+import Use from "../../src/buttonEvents/Effects/Use";
+import AppLogger from "../../src/client/appLogger";
+
+jest.mock("../../src/client/appLogger");
+jest.mock("../../src/buttonEvents/Effects/List");
+jest.mock("../../src/buttonEvents/Effects/Use");
+
+let interaction: ButtonInteractionType;
+
+beforeEach(() => {
+    jest.resetAllMocks();
+
+    interaction = GenerateButtonInteractionMock();
+    interaction.customId = "effects";
+});
+
+test("GIVEN action is list, EXPECT list function to be called", async () => {
+    // Arrange
+    interaction.customId = "effects list";
+
+    // Act
+    const effects = new Effects();
+    await effects.execute(interaction as unknown as ButtonInteraction);
+
+    // Assert
+    expect(List).toHaveBeenCalledTimes(1);
+    expect(List).toHaveBeenCalledWith(interaction);
+
+    expect(Use.Execute).not.toHaveBeenCalled();
+});
+
+test("GIVEN action is use, EXPECT use function to be called", async () => {
+    // Arrange
+    interaction.customId = "effects use";
+
+    // Act
+    const effects = new Effects();
+    await effects.execute(interaction as unknown as ButtonInteraction);
+
+    // Assert
+    expect(Use.Execute).toHaveBeenCalledTimes(1);
+    expect(Use.Execute).toHaveBeenCalledWith(interaction);
+
+    expect(List).not.toHaveBeenCalled();
+});
+
+test.todo("GIVEN action is buy, EXPECT buy function to be called");
+
+test("GIVEN action is invalid, EXPECT nothing to be called", async () => {
+    // Arrange
+    interaction.customId = "effects invalid";
+
+    // Act
+    const effects = new Effects();
+    await effects.execute(interaction as unknown as ButtonInteraction);
+
+    // Assert
+    expect(List).not.toHaveBeenCalled();
+    expect(Use.Execute).not.toHaveBeenCalled();
+
+    expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+    expect(AppLogger.LogError).toHaveBeenCalledWith("Buttons/Effects", "Unknown action, invalid");
+});
diff --git a/tests/buttonEvents/Effects/Buy.test.ts b/tests/buttonEvents/Effects/Buy.test.ts
new file mode 100644
index 0000000..3898504
--- /dev/null
+++ b/tests/buttonEvents/Effects/Buy.test.ts
@@ -0,0 +1,350 @@
+import {ButtonInteraction} from "discord.js";
+import Buy from "../../../src/buttonEvents/Effects/Buy";
+import GenerateButtonInteractionMock from "../../__functions__/discord.js/GenerateButtonInteractionMock";
+import { ButtonInteraction as ButtonInteractionType } from "../../__types__/discord.js";
+import AppLogger from "../../../src/client/appLogger";
+import EffectHelper from "../../../src/helpers/EffectHelper";
+import EmbedColours from "../../../src/constants/EmbedColours";
+import User from "../../../src/database/entities/app/User";
+
+jest.mock("../../../src/client/appLogger");
+jest.mock("../../../src/helpers/EffectHelper");
+jest.mock("../../../src/database/entities/app/User");
+
+let interaction: ButtonInteractionType;
+
+beforeEach(() => {
+    jest.resetAllMocks();
+
+    interaction = GenerateButtonInteractionMock();
+    interaction.customId = "effects buy";
+
+});
+
+describe("Execute", () => {
+    test("GIVEN subaction is invalid, EXPECT error logged", async () => {
+        // Arrange
+        interaction.customId += " invalid";
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy", "Unknown subaction, effects invalid");
+    });
+});
+
+describe("Confirm", () => {
+    let user: User;
+
+    beforeEach(() => {
+        interaction.customId += " confirm";
+
+        user = {
+            Currency: 1000,
+            Save: jest.fn(),
+            RemoveCurrency: jest.fn(),
+        } as unknown as User;
+
+        (User.FetchOneById as jest.Mock).mockResolvedValue(user);
+    });
+
+    test("EXPECT success embed generated", async () => {
+        // Assert
+        interaction.customId += " unclaimed 1";
+
+        const embed = {
+            id: "embed",
+            setColor: jest.fn(),
+            setFooter: jest.fn(),
+        };
+        const row = {
+            id: "row",
+        };
+        
+        (EffectHelper.GenerateEffectBuyEmbed as jest.Mock).mockResolvedValue({
+            embed,
+            row,
+        });
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(interaction.update).toHaveBeenCalledTimes(1);
+        expect(interaction.update).toHaveBeenCalledWith({
+            embeds: [ embed ],
+            components: [ row ],
+        });
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).toHaveBeenCalledTimes(1);
+        expect(EffectHelper.GenerateEffectBuyEmbed).toHaveBeenCalledWith("userId", "unclaimed", 1, true);
+
+        expect(embed.setColor).toHaveBeenCalledTimes(1);
+        expect(embed.setColor).toHaveBeenCalledWith(EmbedColours.Success);
+
+        expect(embed.setFooter).toHaveBeenCalledTimes(1);
+        expect(embed.setFooter).toHaveBeenCalledWith({ text: "Purchased" });
+
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(AppLogger.LogError).not.toHaveBeenCalled();
+
+        expect(User.FetchOneById).toHaveBeenCalledTimes(1);
+        expect(User.FetchOneById).toHaveBeenCalledWith(User, "userId");
+
+        expect(user.RemoveCurrency).toHaveBeenCalledTimes(1);
+        expect(user.RemoveCurrency).toHaveBeenCalledWith(100);
+
+        expect(user.Save).toHaveBeenCalledTimes(1);
+        expect(user.Save).toHaveBeenCalledWith(User, user);
+
+        expect(EffectHelper.AddEffectToUserInventory).toHaveBeenCalledTimes(1);
+        expect(EffectHelper.AddEffectToUserInventory).toHaveBeenCalledWith("userId", "unclaimed", 1);
+    });
+
+    test("GIVEN id is not supplied, EXPECT error", async () => {
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Confirm", "Not enough parameters");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).not.toHaveBeenCalled();
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN quantity is not supplied, EXPECT error", async () => {
+        // Assert
+        interaction.customId += " unclaimed";
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Confirm", "Not enough parameters");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).not.toHaveBeenCalled();
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN quantity is not a number, EXPECT error", async () => {
+        // Assert
+        interaction.customId += " unclaimed invalid";
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Confirm", "Invalid number");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).not.toHaveBeenCalled();
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN quantity is 0, EXPECT error", async () => {
+        // Assert
+        interaction.customId += " unclaimed 0";
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Confirm", "Invalid number");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).not.toHaveBeenCalled();
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN user is not found, EXPECT error", async () => {
+        // Assert
+        interaction.customId += " unclaimed 1";
+
+        (User.FetchOneById as jest.Mock).mockResolvedValue(undefined);
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Confirm", "Unable to find user");
+
+        expect(EffectHelper.AddEffectToUserInventory).not.toHaveBeenCalled();
+
+        expect(interaction.update).not.toHaveBeenCalled();
+        expect(interaction.reply).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN user does not have enough currency, EXPECT error", async () => {
+        // Assert
+        interaction.customId += " unclaimed 1";
+
+        user.Currency = 0;
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(interaction.reply).toHaveBeenCalledTimes(1);
+        expect(interaction.reply).toHaveBeenCalledWith("You don't have enough currency to buy this! You have `0 Currency` and need `100 Currency`!");
+        
+        expect(EffectHelper.AddEffectToUserInventory).not.toHaveBeenCalled();
+
+        expect(interaction.update).not.toHaveBeenCalled();
+
+        expect(AppLogger.LogError).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN GenerateEffectBuyEmbed returns with a string, EXPECT error replied", async () => {
+        // Assert
+        interaction.customId += " unclaimed 1";
+        
+        (EffectHelper.GenerateEffectBuyEmbed as jest.Mock).mockResolvedValue("Test error");
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(interaction.reply).toHaveBeenCalledTimes(1);
+        expect(interaction.reply).toHaveBeenCalledWith("Test error");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).toHaveBeenCalledTimes(1);
+
+        expect(interaction.update).not.toHaveBeenCalled();
+        expect(AppLogger.LogError).not.toHaveBeenCalled();
+    });
+});
+
+describe("Cancel", () => {
+    beforeEach(() => {
+        interaction.customId += " cancel";
+    });
+
+    test("EXPECT embed generated", async () => {
+        // Assert
+        interaction.customId += " unclaimed 1";
+
+        const embed = {
+            id: "embed",
+            setColor: jest.fn(),
+            setFooter: jest.fn(),
+        };
+        const row = {
+            id: "row",
+        };
+        
+        (EffectHelper.GenerateEffectBuyEmbed as jest.Mock).mockResolvedValue({
+            embed,
+            row,
+        });
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(interaction.update).toHaveBeenCalledTimes(1);
+        expect(interaction.update).toHaveBeenCalledWith({
+            embeds: [ embed ],
+            components: [ row ],
+        });
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).toHaveBeenCalledTimes(1);
+        expect(EffectHelper.GenerateEffectBuyEmbed).toHaveBeenCalledWith("userId", "unclaimed", 1, true);
+
+        expect(embed.setColor).toHaveBeenCalledTimes(1);
+        expect(embed.setColor).toHaveBeenCalledWith(EmbedColours.Error);
+
+        expect(embed.setFooter).toHaveBeenCalledTimes(1);
+        expect(embed.setFooter).toHaveBeenCalledWith({ text: "Cancelled" });
+
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(AppLogger.LogError).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN id is not supplied, EXPECT error", async () => {
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Cancel", "Not enough parameters");
+
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN quantity is not supplied, EXPECT error", async () => {
+        // Assert
+        interaction.customId += " unclaimed";
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Cancel", "Not enough parameters");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).not.toHaveBeenCalled();
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN quantity is not a number, EXPECT error", async () => {
+        // Assert
+        interaction.customId += " unclaimed invalid";
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Cancel", "Invalid number");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).not.toHaveBeenCalled();
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN quantity is 0, EXPECT error", async () => {
+        // Assert
+        interaction.customId += " unclaimed 0";
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Buy Cancel", "Invalid number");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).not.toHaveBeenCalled();
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN GenerateEffectBuyEmbed returns with a string, EXPECT error replied", async () => {
+        // Assert
+        interaction.customId += " unclaimed 1";
+        
+        (EffectHelper.GenerateEffectBuyEmbed as jest.Mock).mockResolvedValue("Test error");
+
+        // Act
+        await Buy.Execute(interaction as unknown as ButtonInteraction);
+
+        // Assert
+        expect(interaction.reply).toHaveBeenCalledTimes(1);
+        expect(interaction.reply).toHaveBeenCalledWith("Test error");
+
+        expect(EffectHelper.GenerateEffectBuyEmbed).toHaveBeenCalledTimes(1);
+
+        expect(interaction.update).not.toHaveBeenCalled();
+        expect(AppLogger.LogError).not.toHaveBeenCalled();
+    });
+});
diff --git a/tests/buttonEvents/Effects/List.test.ts b/tests/buttonEvents/Effects/List.test.ts
new file mode 100644
index 0000000..5b42c61
--- /dev/null
+++ b/tests/buttonEvents/Effects/List.test.ts
@@ -0,0 +1,50 @@
+import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, EmbedBuilder } from "discord.js";
+import List from "../../../src/buttonEvents/Effects/List";
+import EffectHelper from "../../../src/helpers/EffectHelper";
+import { mock } from "jest-mock-extended";
+
+jest.mock("../../../src/helpers/EffectHelper");
+
+let interaction: ReturnType<typeof mock<ButtonInteraction>>;
+
+beforeEach(() => {
+    jest.resetAllMocks();
+
+    (EffectHelper.GenerateEffectListEmbed as jest.Mock).mockResolvedValue({
+        embed: mock<EmbedBuilder>(),
+        row: mock<ActionRowBuilder<ButtonBuilder>>(),
+    });
+
+    interaction = mock<ButtonInteraction>();
+    interaction.user.id = "userId";
+    interaction.customId = "effects list 1";
+});
+
+test("GIVEN pageOption is NOT a number, EXPECT error", async () => {
+    // Arrange
+    interaction.customId = "effects list invalid";
+
+    // Act
+    await List(interaction);
+
+    // Assert
+    expect(interaction.reply).toHaveBeenCalledTimes(1);
+    expect(interaction.reply).toHaveBeenCalledWith("Page option is not a valid number")
+
+    expect(EffectHelper.GenerateEffectListEmbed).not.toHaveBeenCalled();
+    expect(interaction.update).not.toHaveBeenCalled();
+});
+
+test("GIVEN pageOption is a number, EXPECT interaction updated", async () => {
+    // Arrange
+    interaction.customId = "effects list 1";
+
+    // Act
+    await List(interaction);
+
+    // Assert
+    expect(EffectHelper.GenerateEffectListEmbed).toHaveBeenCalledTimes(1);
+    expect(EffectHelper.GenerateEffectListEmbed).toHaveBeenCalledWith("userId", 1);
+
+    expect(interaction.update).toHaveBeenCalledTimes(1);
+});
\ No newline at end of file
diff --git a/tests/buttonEvents/Effects/Use.test.ts b/tests/buttonEvents/Effects/Use.test.ts
new file mode 100644
index 0000000..86391cc
--- /dev/null
+++ b/tests/buttonEvents/Effects/Use.test.ts
@@ -0,0 +1,148 @@
+import { ButtonInteraction, InteractionResponse, InteractionUpdateOptions, MessagePayload } from "discord.js";
+import Use from "../../../src/buttonEvents/Effects/Use";
+import { mock } from "jest-mock-extended";
+import AppLogger from "../../../src/client/appLogger";
+import EffectHelper from "../../../src/helpers/EffectHelper";
+
+jest.mock("../../../src/client/appLogger");
+jest.mock("../../../src/helpers/EffectHelper");
+
+beforeEach(() => {
+    jest.resetAllMocks();
+
+    jest.useFakeTimers();
+    jest.setSystemTime(0);
+});
+
+afterAll(() => {
+    jest.useRealTimers();
+});
+
+describe("Execute", () => {
+    test("GIVEN subaction is unknown, EXPECT nothing to be called", async () => {
+        // Arrange
+        const interaction = mock<ButtonInteraction>();
+        interaction.customId = "effects use invalud";
+
+        // Act
+        await Use.Execute(interaction);
+
+        // Assert
+        expect(interaction.reply).not.toHaveBeenCalled();
+        expect(interaction.update).not.toHaveBeenCalled();
+    });
+});
+
+describe("UseConfirm", () => {
+    let interaction = mock<ButtonInteraction>();
+
+    beforeEach(() => {
+        interaction = mock<ButtonInteraction>();
+        interaction.customId = "effects use confirm";
+    });
+
+    test("GIVEN effectDetail is not found, EXPECT error", async () => {
+        // Arrange
+        interaction.customId += " invalid";
+
+        // Act
+        await Use.Execute(interaction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Button/Effects/Use", "Effect not found, invalid");
+
+        expect(interaction.reply).toHaveBeenCalledTimes(1);
+        expect(interaction.reply).toHaveBeenCalledWith("Effect not found in system!");
+    });
+
+    test("GIVEN EffectHelper.UseEffect failed, EXPECT error", async () => {
+        // Arrange
+        interaction.customId += " unclaimed";
+        interaction.user.id = "userId";
+
+        (EffectHelper.UseEffect as jest.Mock).mockResolvedValue(false);
+
+        const whenExpires = new Date(Date.now() + 10 * 60 * 1000);
+
+        // Act
+        await Use.Execute(interaction);
+
+        // Assert
+        expect(EffectHelper.UseEffect).toHaveBeenCalledTimes(1);
+        expect(EffectHelper.UseEffect).toHaveBeenCalledWith("userId", "unclaimed", whenExpires);
+
+        expect(interaction.reply).toHaveBeenCalledTimes(1);
+        expect(interaction.reply).toHaveBeenCalledWith("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown");
+    });
+
+    test("GIVEN EffectHelper.UseEffect succeeded, EXPECT interaction updated", async () => {
+        let updatedWith;
+
+        // Arrange
+        interaction.customId += " unclaimed";
+        interaction.user.id = "userId";
+        interaction.update.mockImplementation(async (opts: string | MessagePayload | InteractionUpdateOptions) => {
+            updatedWith = opts;
+
+            return mock<InteractionResponse<boolean>>();
+        });
+
+        (EffectHelper.UseEffect as jest.Mock).mockResolvedValue(true);
+
+        const whenExpires = new Date(Date.now() + 10 * 60 * 1000);
+
+        // Act
+        await Use.Execute(interaction);
+
+        // Assert
+        expect(EffectHelper.UseEffect).toHaveBeenCalledTimes(1);
+        expect(EffectHelper.UseEffect).toHaveBeenCalledWith("userId", "unclaimed", whenExpires);
+
+        expect(interaction.update).toHaveBeenCalledTimes(1);
+        expect(updatedWith).toMatchSnapshot();
+    });
+});
+
+describe("UseCancel", () => {
+    let interaction = mock<ButtonInteraction>();
+
+    beforeEach(() => {
+        interaction = mock<ButtonInteraction>();
+        interaction.customId = "effects use cancel";
+    });
+
+    test("GIVEN effectDetail is not found, EXPECT error", async () => {
+        // Arrange
+        interaction.customId += " invalid";
+
+        // Act
+        await Use.Execute(interaction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Button/Effects/Cancel", "Effect not found, invalid");
+
+        expect(interaction.reply).toHaveBeenCalledTimes(1);
+        expect(interaction.reply).toHaveBeenCalledWith("Effect not found in system!");
+    });
+
+    test("GIVEN effectDetail is found, EXPECT interaction updated", async () => {
+        let updatedWith;
+
+        // Arrange
+        interaction.customId += " unclaimed";
+        interaction.user.id = "userId";
+        interaction.update.mockImplementation(async (opts: string | MessagePayload | InteractionUpdateOptions) => {
+            updatedWith = opts;
+
+            return mock<InteractionResponse<boolean>>();
+        });
+        // Act
+        await Use.Execute(interaction);
+
+        // Assert
+        expect(interaction.update).toHaveBeenCalledTimes(1);
+        expect(updatedWith).toMatchSnapshot();
+    });
+});
\ No newline at end of file
diff --git a/tests/buttonEvents/Effects/__snapshots__/Use.test.ts.snap b/tests/buttonEvents/Effects/__snapshots__/Use.test.ts.snap
new file mode 100644
index 0000000..6cec0f4
--- /dev/null
+++ b/tests/buttonEvents/Effects/__snapshots__/Use.test.ts.snap
@@ -0,0 +1,95 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`UseCancel GIVEN effectDetail is found, EXPECT interaction updated 1`] = `
+{
+  "components": [
+    {
+      "components": [
+        {
+          "custom_id": "effects use confirm unclaimed",
+          "disabled": true,
+          "emoji": undefined,
+          "label": "Confirm",
+          "style": 1,
+          "type": 2,
+        },
+        {
+          "custom_id": "effects use cancel unclaimed",
+          "disabled": true,
+          "emoji": undefined,
+          "label": "Cancel",
+          "style": 4,
+          "type": 2,
+        },
+      ],
+      "type": 1,
+    },
+  ],
+  "embeds": [
+    {
+      "color": 13882323,
+      "description": "The effect from your inventory has not been used",
+      "fields": [
+        {
+          "inline": true,
+          "name": "Effect",
+          "value": "Unclaimed Chance Up",
+        },
+        {
+          "inline": true,
+          "name": "Expires",
+          "value": "10m",
+        },
+      ],
+      "title": "Effect Use Cancelled",
+    },
+  ],
+}
+`;
+
+exports[`UseConfirm GIVEN EffectHelper.UseEffect succeeded, EXPECT interaction updated 1`] = `
+{
+  "components": [
+    {
+      "components": [
+        {
+          "custom_id": "effects use confirm unclaimed",
+          "disabled": true,
+          "emoji": undefined,
+          "label": "Confirm",
+          "style": 1,
+          "type": 2,
+        },
+        {
+          "custom_id": "effects use cancel unclaimed",
+          "disabled": true,
+          "emoji": undefined,
+          "label": "Cancel",
+          "style": 4,
+          "type": 2,
+        },
+      ],
+      "type": 1,
+    },
+  ],
+  "embeds": [
+    {
+      "color": 2263842,
+      "description": "You now have an active effect!",
+      "fields": [
+        {
+          "inline": true,
+          "name": "Effect",
+          "value": "Unclaimed Chance Up",
+        },
+        {
+          "inline": true,
+          "name": "Expires",
+          "value": "<t:600:f>",
+        },
+      ],
+      "title": "Effect Used",
+    },
+  ],
+}
+`;
diff --git a/tests/buttonEvents/Reroll.test.ts b/tests/buttonEvents/Reroll.test.ts
new file mode 100644
index 0000000..2021aac
--- /dev/null
+++ b/tests/buttonEvents/Reroll.test.ts
@@ -0,0 +1,7 @@
+describe("GIVEN valid conditions", () => {
+    test.todo("EXPECT user.RemoveCurrency to be called");
+
+    test.todo("GIVEN user is saved");
+});
+
+test.todo("GIVEN user.RemoveCurrency fails, EXPECT error replied");
\ No newline at end of file
diff --git a/tests/commands/__snapshots__/effects.test.ts.snap b/tests/commands/__snapshots__/effects.test.ts.snap
new file mode 100644
index 0000000..474b505
--- /dev/null
+++ b/tests/commands/__snapshots__/effects.test.ts.snap
@@ -0,0 +1,106 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EXPECT CommandBuilder to be defined 1`] = `
+{
+  "contexts": undefined,
+  "default_member_permissions": undefined,
+  "default_permission": undefined,
+  "description": "Effects",
+  "description_localizations": undefined,
+  "dm_permission": undefined,
+  "integration_types": undefined,
+  "name": "effects",
+  "name_localizations": undefined,
+  "nsfw": undefined,
+  "options": [
+    {
+      "description": "List all effects I have",
+      "description_localizations": undefined,
+      "name": "list",
+      "name_localizations": undefined,
+      "options": [
+        {
+          "autocomplete": undefined,
+          "choices": undefined,
+          "description": "The page number",
+          "description_localizations": undefined,
+          "max_value": undefined,
+          "min_value": 1,
+          "name": "page",
+          "name_localizations": undefined,
+          "required": false,
+          "type": 10,
+        },
+      ],
+      "type": 1,
+    },
+    {
+      "description": "Use an effect in your inventory",
+      "description_localizations": undefined,
+      "name": "use",
+      "name_localizations": undefined,
+      "options": [
+        {
+          "autocomplete": undefined,
+          "choices": [
+            {
+              "name": "Unclaimed Chance Up",
+              "name_localizations": undefined,
+              "value": "unclaimed",
+            },
+          ],
+          "description": "The effect id to use",
+          "description_localizations": undefined,
+          "max_length": undefined,
+          "min_length": undefined,
+          "name": "id",
+          "name_localizations": undefined,
+          "required": true,
+          "type": 3,
+        },
+      ],
+      "type": 1,
+    },
+    {
+      "description": "Buy more effects",
+      "description_localizations": undefined,
+      "name": "buy",
+      "name_localizations": undefined,
+      "options": [
+        {
+          "autocomplete": undefined,
+          "choices": [
+            {
+              "name": "Unclaimed Chance Up",
+              "name_localizations": undefined,
+              "value": "unclaimed",
+            },
+          ],
+          "description": "The effect id to buy",
+          "description_localizations": undefined,
+          "max_length": undefined,
+          "min_length": undefined,
+          "name": "id",
+          "name_localizations": undefined,
+          "required": true,
+          "type": 3,
+        },
+        {
+          "autocomplete": undefined,
+          "choices": undefined,
+          "description": "The amount to buy",
+          "description_localizations": undefined,
+          "max_value": undefined,
+          "min_value": 1,
+          "name": "quantity",
+          "name_localizations": undefined,
+          "required": false,
+          "type": 10,
+        },
+      ],
+      "type": 1,
+    },
+  ],
+  "type": 1,
+}
+`;
diff --git a/tests/commands/drop.test.ts b/tests/commands/drop.test.ts
new file mode 100644
index 0000000..81b5f4a
--- /dev/null
+++ b/tests/commands/drop.test.ts
@@ -0,0 +1,142 @@
+import { CommandInteraction } from "discord.js";
+import Drop from "../../src/commands/drop";
+import GenerateCommandInteractionMock from "../__functions__/discord.js/GenerateCommandInteractionMock";
+import { CommandInteraction as CommandInteractionMock } from "../__types__/discord.js";
+import { CoreClient } from "../../src/client/client";
+import Config from "../../src/database/entities/app/Config";
+import User from "../../src/database/entities/app/User";
+import GetCardsHelper from "../../src/helpers/DropHelpers/GetCardsHelper";
+import Inventory from "../../src/database/entities/app/Inventory";
+import DropEmbedHelper from "../../src/helpers/DropHelpers/DropEmbedHelper";
+import CardConstants from "../../src/constants/CardConstants";
+import * as uuid from "uuid";
+
+jest.mock("../../src/database/entities/app/Config");
+jest.mock("../../src/database/entities/app/User");
+jest.mock("../../src/helpers/DropHelpers/GetCardsHelper");
+jest.mock("../../src/database/entities/app/Inventory");
+jest.mock("../../src/helpers/DropHelpers/DropEmbedHelper");
+
+jest.mock("uuid");
+
+beforeEach(() => {
+    (Config.GetValue as jest.Mock).mockResolvedValue("false");
+});
+
+describe("execute", () => {
+    describe("GIVEN user is in the database", () => {
+        let interaction: CommandInteractionMock;
+        let user: User;
+        const randomCard = {
+            card: {
+                id: "cardId",
+                path: "https://google.com/",
+            }
+        };
+
+        beforeAll(async () => {
+            // Arrange
+            CoreClient.AllowDrops = true;
+
+            interaction = GenerateCommandInteractionMock();
+
+            user = {
+                Currency: 500,
+                RemoveCurrency: jest.fn().mockReturnValue(true),
+                Save: jest.fn(),
+            } as unknown as User;
+
+            (User.FetchOneById as jest.Mock).mockResolvedValue(user);
+            (GetCardsHelper.FetchCard as jest.Mock).mockResolvedValue(randomCard);
+            (Inventory.FetchOneByCardNumberAndUserId as jest.Mock).mockResolvedValue({
+                Quantity: 1,
+            });
+            (DropEmbedHelper.GenerateDropEmbed as jest.Mock).mockReturnValue({
+                type: "Embed",
+            });
+            (DropEmbedHelper.GenerateDropButtons as jest.Mock).mockReturnValue({
+                type: "Button",
+            });
+
+            (uuid.v4 as jest.Mock).mockReturnValue("uuid");
+
+            // Act
+            const drop = new Drop();
+            await drop.execute(interaction as unknown as CommandInteraction);
+        });
+
+        test("EXPECT user to be fetched", () => {
+            expect(User.FetchOneById).toHaveBeenCalledTimes(1);
+            expect(User.FetchOneById).toHaveBeenCalledWith(User, "userId");
+        });
+
+        test("EXPECT user.RemoveCurrency to be called", () => {
+            expect(user.RemoveCurrency).toHaveBeenCalledTimes(1);
+            expect(user.RemoveCurrency).toHaveBeenCalledWith(CardConstants.ClaimCost);
+        });
+
+        test("EXPECT user to be saved", () => {
+            expect(user.Save).toHaveBeenCalledTimes(1);
+            expect(user.Save).toHaveBeenCalledWith(User, user);
+        });
+
+        test("EXPECT random card to be fetched", () => {
+            expect(GetCardsHelper.FetchCard).toHaveBeenCalledTimes(1);
+            expect(GetCardsHelper.FetchCard).toHaveBeenCalledWith("userId");
+        });
+
+        test("EXPECT interaction to be deferred", () => {
+            expect(interaction.deferReply).toHaveBeenCalledTimes(1);
+        });
+
+        test("EXPECT Inventory.FetchOneByCardNumberAndUserId to be called", () => {
+            expect(Inventory.FetchOneByCardNumberAndUserId).toHaveBeenCalledTimes(1);
+            expect(Inventory.FetchOneByCardNumberAndUserId).toHaveBeenCalledWith("userId", "cardId");
+        });
+
+        test("EXPECT DropEmbedHelper.GenerateDropEmbed to be called", () => {
+            expect(DropEmbedHelper.GenerateDropEmbed).toHaveBeenCalledTimes(1);
+            expect(DropEmbedHelper.GenerateDropEmbed).toHaveBeenCalledWith(randomCard, 1, "", undefined, 500);
+        });
+
+        test("EXPECT DropEmbedHelper.GenerateDropButtons to be called", () => {
+            expect(DropEmbedHelper.GenerateDropButtons).toHaveBeenCalledTimes(1);
+            expect(DropEmbedHelper.GenerateDropButtons).toHaveBeenCalledWith(randomCard, "uuid", "userId");
+        });
+
+        test("EXPECT interaction to be edited", () => {
+            expect(interaction.editReply).toHaveBeenCalledTimes(1);
+            expect(interaction.editReply).toHaveBeenCalledWith({
+                embeds: [ { type: "Embed" } ],
+                files: [],
+                components: [ { type: "Button" } ],
+            });
+        });
+
+        describe("AND randomCard path is not a url", () => {
+            test.todo("EXPECT image read from file system");
+
+            test.todo("EXPECT files on the embed to contain the image as an attachment");
+        });
+    });
+
+    describe("GIVEN user is not in the database", () => {
+        test.todo("EXPECT new user to be created");
+    });
+
+    describe("GIVEN user.RemoveCurrency fails", () => {
+        test.todo("EXPECT error replied");
+    });
+    
+    describe("GIVEN randomCard returns null", () => {
+        test.todo("EXPECT error logged");
+
+        test.todo("EXPECT error replied");
+    });
+
+    describe("GIVEN the code throws an error", () => {
+        test.todo("EXPECT error logged");
+
+        test.todo("EXPECT interaction edited with error");
+    });
+});
\ No newline at end of file
diff --git a/tests/commands/effects.test.ts b/tests/commands/effects.test.ts
new file mode 100644
index 0000000..33f612d
--- /dev/null
+++ b/tests/commands/effects.test.ts
@@ -0,0 +1,105 @@
+import Effects from "../../src/commands/effects";
+import List from "../../src/commands/effects/List";
+import Use from "../../src/commands/effects/Use";
+import Buy from "../../src/commands/effects/Buy";
+import AppLogger from "../../src/client/appLogger";
+import GenerateCommandInteractionMock from "../__functions__/discord.js/GenerateCommandInteractionMock";
+import { CommandInteraction } from "discord.js";
+
+jest.mock("../../src/commands/effects/List");
+jest.mock("../../src/commands/effects/Use");
+jest.mock("../../src/commands/effects/Buy");
+jest.mock("../../src/client/appLogger");
+
+beforeEach(() => {
+    jest.resetAllMocks();
+});
+
+test("EXPECT CommandBuilder to be defined", async () => {
+    // Act
+    const effects = new Effects();
+
+    // Assert
+    expect(effects.CommandBuilder).toMatchSnapshot();
+});
+
+describe("execute", () => {
+    test("GIVEN interaction subcommand is list, EXPECT buy function called", async () => {
+        // Arrange
+        const interaction = GenerateCommandInteractionMock({
+            subcommand: "list",
+        });
+
+        // Act
+        const effects = new Effects();
+        await effects.execute(interaction as unknown as CommandInteraction);
+
+        // Assert
+        expect(List).toHaveBeenCalledTimes(1);
+        expect(List).toHaveBeenCalledWith(interaction);
+
+        expect(Use).not.toHaveBeenCalled();
+        expect(Buy).not.toHaveBeenCalled();
+
+        expect(AppLogger.LogError).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN interaction subcommand is use, EXPECT buy function called", async () => {
+        // Arrange
+        const interaction = GenerateCommandInteractionMock({
+            subcommand: "use",
+        });
+
+        // Act
+        const effects = new Effects();
+        await effects.execute(interaction as unknown as CommandInteraction);
+
+        // Assert
+        expect(Use).toHaveBeenCalledTimes(1);
+        expect(Use).toHaveBeenCalledWith(interaction);
+
+        expect(List).not.toHaveBeenCalled();
+        expect(Buy).not.toHaveBeenCalled();
+
+        expect(AppLogger.LogError).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN interaction subcommand is buy, EXPECT buy function called", async () => {
+        // Arrange
+        const interaction = GenerateCommandInteractionMock({
+            subcommand: "buy",
+        });
+
+        // Act
+        const effects = new Effects();
+        await effects.execute(interaction as unknown as CommandInteraction);
+
+        // Assert
+        expect(Buy).toHaveBeenCalledTimes(1);
+        expect(Buy).toHaveBeenCalledWith(interaction);
+
+        expect(List).not.toHaveBeenCalled();
+        expect(Use).not.toHaveBeenCalled();
+
+        expect(AppLogger.LogError).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN interaction subcommand is invalid, EXPECT error logged", async () => {
+        // Arrange
+        const interaction = GenerateCommandInteractionMock({
+            subcommand: "invalid",
+        });
+
+        // Act
+        const effects = new Effects();
+        await effects.execute(interaction as unknown as CommandInteraction);
+
+        // Assert
+        expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
+        expect(AppLogger.LogError).toHaveBeenCalledWith("Commands/Effects", "Invalid subcommand: invalid");
+
+        expect(List).not.toHaveBeenCalled();
+        expect(Use).not.toHaveBeenCalled();
+        expect(Buy).not.toHaveBeenCalled();
+    });
+});
\ No newline at end of file
diff --git a/tests/commands/effects/Buy.test.ts b/tests/commands/effects/Buy.test.ts
new file mode 100644
index 0000000..87e4219
--- /dev/null
+++ b/tests/commands/effects/Buy.test.ts
@@ -0,0 +1,9 @@
+jest.mock("../../../src/helpers/EffectHelper");
+
+describe("Buy", () => {
+    test.todo("GIVEN result returns a string, EXPECT interaction replied with string");
+
+    test.todo("GIVEN result returns an embed, EXPECT interaction replied with embed and row");
+
+    test.todo("GIVEN quantity option is not supplied, EXPECT quantity to default to 1");
+});
diff --git a/tests/helpers/DropHelpers/DropEmbedHelper.test.ts b/tests/helpers/DropHelpers/DropEmbedHelper.test.ts
new file mode 100644
index 0000000..743f6c7
--- /dev/null
+++ b/tests/helpers/DropHelpers/DropEmbedHelper.test.ts
@@ -0,0 +1,3 @@
+describe("GenerateDropButtons", () => {
+    test.todo("EXPECT row to be returned");
+});
\ No newline at end of file
diff --git a/tests/helpers/DropHelpers/GetCardsHelper.test.ts b/tests/helpers/DropHelpers/GetCardsHelper.test.ts
new file mode 100644
index 0000000..421d4ef
--- /dev/null
+++ b/tests/helpers/DropHelpers/GetCardsHelper.test.ts
@@ -0,0 +1,68 @@
+import GetCardsHelper from "../../../src/helpers/DropHelpers/GetCardsHelper";
+import EffectHelper from "../../../src/helpers/EffectHelper";
+import GetUnclaimedCardsHelper from "../../../src/helpers/DropHelpers/GetUnclaimedCardsHelper";
+import CardConstants from "../../../src/constants/CardConstants";
+
+jest.mock("../../../src/helpers/EffectHelper");
+jest.mock("../../../src/helpers/DropHelpers/GetUnclaimedCardsHelper");
+
+beforeEach(() => {
+    jest.resetAllMocks();
+});
+
+describe("FetchCard", () => {
+    test("GIVEN user has the unclaimed effect AND unused chance is within constraint, EXPECT unclaimed card returned", async () => {
+        // Arrange
+        (EffectHelper.HasEffect as jest.Mock).mockResolvedValue(true);
+        GetCardsHelper.GetRandomCard = jest.fn();
+        Math.random = jest.fn().mockReturnValue(CardConstants.UnusedChanceUpChance - 0.1);
+
+        // Act
+        await GetCardsHelper.FetchCard("userId");
+
+        // Assert
+        expect(EffectHelper.HasEffect).toHaveBeenCalledTimes(1);
+        expect(EffectHelper.HasEffect).toHaveBeenCalledWith("userId", "unclaimed");
+
+        expect(GetUnclaimedCardsHelper.GetRandomCardUnclaimed).toHaveBeenCalledTimes(1);
+        expect(GetUnclaimedCardsHelper.GetRandomCardUnclaimed).toHaveBeenCalledWith("userId");
+
+        expect(GetCardsHelper.GetRandomCard).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN user has unclaimed effect AND unused chance is NOT within constraint, EXPECT random card returned", async () => {
+        // Arrange
+        (EffectHelper.HasEffect as jest.Mock).mockResolvedValue(true);
+        GetCardsHelper.GetRandomCard = jest.fn();
+        Math.random = jest.fn().mockReturnValue(CardConstants.UnusedChanceUpChance + 0.1);
+
+        // Act
+        await GetCardsHelper.FetchCard("userId");
+
+        // Assert
+        expect(EffectHelper.HasEffect).toHaveBeenCalledTimes(1);
+        expect(EffectHelper.HasEffect).toHaveBeenCalledWith("userId", "unclaimed");
+
+        expect(GetCardsHelper.GetRandomCard).toHaveBeenCalledTimes(1);
+
+        expect(GetUnclaimedCardsHelper.GetRandomCardUnclaimed).not.toHaveBeenCalled();
+    });
+
+    test("GIVEN user does NOT have unclaimed effect, EXPECT random card returned", async () => {
+        // Arrange
+        (EffectHelper.HasEffect as jest.Mock).mockResolvedValue(false);
+        GetCardsHelper.GetRandomCard = jest.fn();
+        Math.random = jest.fn().mockReturnValue(CardConstants.UnusedChanceUpChance + 0.1);
+
+        // Act
+        await GetCardsHelper.FetchCard("userId");
+
+        // Assert
+        expect(EffectHelper.HasEffect).toHaveBeenCalledTimes(1);
+        expect(EffectHelper.HasEffect).toHaveBeenCalledWith("userId", "unclaimed");
+
+        expect(GetCardsHelper.GetRandomCard).toHaveBeenCalledTimes(1);
+
+        expect(GetUnclaimedCardsHelper.GetRandomCardUnclaimed).not.toHaveBeenCalled();
+    });
+});
\ No newline at end of file
diff --git a/tests/helpers/EffectHelper.test.ts b/tests/helpers/EffectHelper.test.ts
new file mode 100644
index 0000000..dcab744
--- /dev/null
+++ b/tests/helpers/EffectHelper.test.ts
@@ -0,0 +1,127 @@
+import EffectHelper from "../../src/helpers/EffectHelper";
+import UserEffect from "../../src/database/entities/app/UserEffect";
+
+jest.mock("../../src/database/entities/app/UserEffect");
+
+describe("GenerateEffectListEmbed", () => {
+    test("GIVEN user has an effect, EXPECT detailed embed to be returned", async () => {
+        // Arrange
+        (UserEffect.FetchAllByUserIdPaginated as jest.Mock).mockResolvedValue([
+            [
+                {
+                    Name: "unclaimed",
+                    Unused: 1,
+                }
+            ],
+            1,
+        ]);
+
+        // Act
+        const result = await EffectHelper.GenerateEffectListEmbed("userId", 1);
+
+        // Assert
+        expect(result).toMatchSnapshot();
+    });
+
+    test("GIVEN user has more than 1 page of effects, EXPECT pagination enabled", async () => {
+        const effects: {
+            Name: string,
+            Unused: number,
+        }[] = [];
+
+        for (let i = 0; i < 15; i++) {
+            effects.push({
+                Name: "unclaimed",
+                Unused: 1,
+            });
+        }
+
+        // Arrange
+        (UserEffect.FetchAllByUserIdPaginated as jest.Mock).mockResolvedValue([
+            effects,
+            15,
+        ]);
+
+        // Act
+        const result = await EffectHelper.GenerateEffectListEmbed("userId", 1);
+
+        // Assert
+        expect(result).toMatchSnapshot();
+    });
+
+    test("GIVEN user is on a page other than 1, EXPECT pagination enabled", async () => {
+        const effects: {
+            Name: string,
+            Unused: number,
+        }[] = [];
+
+        for (let i = 0; i < 15; i++) {
+            effects.push({
+                Name: "unclaimed",
+                Unused: 1,
+            });
+        }
+
+        // Arrange
+        (UserEffect.FetchAllByUserIdPaginated as jest.Mock).mockResolvedValue([
+            effects,
+            15,
+        ]);
+
+        // Act
+        const result = await EffectHelper.GenerateEffectListEmbed("userId", 2);
+
+        // Assert
+        expect(result).toMatchSnapshot();
+    });
+
+    test("GIVEN user does NOT have an effect, EXPECT empty embed to be returned", async () => {
+        // Arrange
+        (UserEffect.FetchAllByUserIdPaginated as jest.Mock).mockResolvedValue([
+            [],
+            0,
+        ]);
+
+        // Act
+        const result = await EffectHelper.GenerateEffectListEmbed("userId", 1);
+
+        // Assert
+        expect(result).toMatchSnapshot();
+    });
+
+    test("GIVEN there is an active effect, EXPECT field added", async () => {
+        // Arrange
+        (UserEffect.FetchAllByUserIdPaginated as jest.Mock).mockResolvedValue([
+            [
+                {
+                    Name: "unclaimed",
+                    Unused: 1,
+                }
+            ],
+            1,
+        ]);
+
+        (UserEffect.FetchActiveEffectByUserId as jest.Mock).mockResolvedValue({
+            Name: "unclaimed",
+            WhenExpires: new Date(1738174052),
+        });
+
+        // Act
+        const result = await EffectHelper.GenerateEffectListEmbed("userId", 1);
+
+        // Assert
+        expect(result).toMatchSnapshot();
+    });
+});
+
+describe("GenerateEffectBuyEmbed", () => {
+    test.todo("GIVEN Effect Details are not found, EXPECT error");
+
+    test.todo("GIVEN user is not in database, EXPECT blank user created");
+
+    test.todo("GIVEN user does not have enough currency, EXPECT error");
+
+    test.todo("GIVEN user does have enough currency, EXPECT embed returned");
+
+    test.todo("GIVEN disabled boolean is true, EXPECT buttons to be disabled");
+});
\ No newline at end of file
diff --git a/tests/helpers/TimeLengthInput.test.ts b/tests/helpers/TimeLengthInput.test.ts
new file mode 100644
index 0000000..6a23d67
--- /dev/null
+++ b/tests/helpers/TimeLengthInput.test.ts
@@ -0,0 +1,38 @@
+import TimeLengthInput from "../../src/helpers/TimeLengthInput";
+
+describe("ConvertFromMilliseconds", () => {
+    test("EXPECT 1000ms to be outputted as a second", () => {
+        const timeLength = TimeLengthInput.ConvertFromMilliseconds(1000);
+        expect(timeLength.GetLengthShort()).toBe("1s");
+    });
+
+    test("EXPECT 60000ms to be outputted as a minute", () => {
+        const timeLength = TimeLengthInput.ConvertFromMilliseconds(60000);
+        expect(timeLength.GetLengthShort()).toBe("1m");
+    });
+
+    test("EXPECT 3600000ms to be outputted as an hour", () => {
+        const timeLength = TimeLengthInput.ConvertFromMilliseconds(3600000);
+        expect(timeLength.GetLengthShort()).toBe("1h");
+    });
+
+    test("EXPECT 86400000ms to be outputted as a day", () => {
+        const timeLength = TimeLengthInput.ConvertFromMilliseconds(86400000);
+        expect(timeLength.GetLengthShort()).toBe("1d");
+    });
+
+    test("EXPECT a combination to be outputted correctly", () => {
+        const timeLength = TimeLengthInput.ConvertFromMilliseconds(90061000);
+        expect(timeLength.GetLengthShort()).toBe("1d 1h 1m 1s");
+    });
+
+    test("EXPECT 0ms to be outputted as empty", () => {
+        const timeLength = TimeLengthInput.ConvertFromMilliseconds(0);
+        expect(timeLength.GetLengthShort()).toBe("");
+    });
+
+    test("EXPECT 123456789ms to be outputted correctly", () => {
+        const timeLength = TimeLengthInput.ConvertFromMilliseconds(123456789);
+        expect(timeLength.GetLengthShort()).toBe("1d 10h 17m 36s");
+    });
+});
\ No newline at end of file
diff --git a/tests/helpers/__snapshots__/EffectHelper.test.ts.snap b/tests/helpers/__snapshots__/EffectHelper.test.ts.snap
new file mode 100644
index 0000000..fc3317e
--- /dev/null
+++ b/tests/helpers/__snapshots__/EffectHelper.test.ts.snap
@@ -0,0 +1,216 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`GenerateEffectListEmbed GIVEN there is an active effect, EXPECT field added 1`] = `
+{
+  "embed": {
+    "color": 3166394,
+    "description": "Unclaimed Chance Up x1",
+    "fields": [
+      {
+        "inline": true,
+        "name": "Active",
+        "value": "Unclaimed Chance Up",
+      },
+      {
+        "inline": true,
+        "name": "Expires",
+        "value": "<t:1738174>",
+      },
+    ],
+    "footer": {
+      "icon_url": undefined,
+      "text": "Page 1 of 1",
+    },
+    "title": "Effects",
+  },
+  "row": {
+    "components": [
+      {
+        "custom_id": "effects list 0",
+        "disabled": true,
+        "emoji": undefined,
+        "label": "Previous",
+        "style": 1,
+        "type": 2,
+      },
+      {
+        "custom_id": "effects list 2",
+        "disabled": true,
+        "emoji": undefined,
+        "label": "Next",
+        "style": 1,
+        "type": 2,
+      },
+    ],
+    "type": 1,
+  },
+}
+`;
+
+exports[`GenerateEffectListEmbed GIVEN user does NOT have an effect, EXPECT empty embed to be returned 1`] = `
+{
+  "embed": {
+    "color": 3166394,
+    "description": "*none*",
+    "footer": {
+      "icon_url": undefined,
+      "text": "Page 1 of 1",
+    },
+    "title": "Effects",
+  },
+  "row": {
+    "components": [
+      {
+        "custom_id": "effects list 0",
+        "disabled": true,
+        "emoji": undefined,
+        "label": "Previous",
+        "style": 1,
+        "type": 2,
+      },
+      {
+        "custom_id": "effects list 2",
+        "disabled": true,
+        "emoji": undefined,
+        "label": "Next",
+        "style": 1,
+        "type": 2,
+      },
+    ],
+    "type": 1,
+  },
+}
+`;
+
+exports[`GenerateEffectListEmbed GIVEN user has an effect, EXPECT detailed embed to be returned 1`] = `
+{
+  "embed": {
+    "color": 3166394,
+    "description": "Unclaimed Chance Up x1",
+    "footer": {
+      "icon_url": undefined,
+      "text": "Page 1 of 1",
+    },
+    "title": "Effects",
+  },
+  "row": {
+    "components": [
+      {
+        "custom_id": "effects list 0",
+        "disabled": true,
+        "emoji": undefined,
+        "label": "Previous",
+        "style": 1,
+        "type": 2,
+      },
+      {
+        "custom_id": "effects list 2",
+        "disabled": true,
+        "emoji": undefined,
+        "label": "Next",
+        "style": 1,
+        "type": 2,
+      },
+    ],
+    "type": 1,
+  },
+}
+`;
+
+exports[`GenerateEffectListEmbed GIVEN user has more than 1 page of effects, EXPECT pagination enabled 1`] = `
+{
+  "embed": {
+    "color": 3166394,
+    "description": "Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1",
+    "footer": {
+      "icon_url": undefined,
+      "text": "Page 1 of 2",
+    },
+    "title": "Effects",
+  },
+  "row": {
+    "components": [
+      {
+        "custom_id": "effects list 0",
+        "disabled": true,
+        "emoji": undefined,
+        "label": "Previous",
+        "style": 1,
+        "type": 2,
+      },
+      {
+        "custom_id": "effects list 2",
+        "disabled": false,
+        "emoji": undefined,
+        "label": "Next",
+        "style": 1,
+        "type": 2,
+      },
+    ],
+    "type": 1,
+  },
+}
+`;
+
+exports[`GenerateEffectListEmbed GIVEN user is on a page other than 1, EXPECT pagination enabled 1`] = `
+{
+  "embed": {
+    "color": 3166394,
+    "description": "Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1
+Unclaimed Chance Up x1",
+    "footer": {
+      "icon_url": undefined,
+      "text": "Page 2 of 2",
+    },
+    "title": "Effects",
+  },
+  "row": {
+    "components": [
+      {
+        "custom_id": "effects list 1",
+        "disabled": false,
+        "emoji": undefined,
+        "label": "Previous",
+        "style": 1,
+        "type": 2,
+      },
+      {
+        "custom_id": "effects list 3",
+        "disabled": true,
+        "emoji": undefined,
+        "label": "Next",
+        "style": 1,
+        "type": 2,
+      },
+    ],
+    "type": 1,
+  },
+}
+`;
diff --git a/tests/registry.test.ts b/tests/registry.test.ts
deleted file mode 100644
index 71d80db..0000000
--- a/tests/registry.test.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import {CoreClient} from "../src/client/client";
-import Registry from "../src/registry";
-import fs from "fs";
-import path from "path";
-
-describe("RegisterCommands", () => {
-    test("EXPECT every command in the commands folder to be registered", () => {
-        const registeredCommands: string[] = [];
-
-        CoreClient.RegisterCommand = jest.fn().mockImplementation((name: string) => {
-            registeredCommands.push(name);
-        });
-
-        Registry.RegisterCommands();
-
-        const commandFiles = getFilesInDirectory(path.join(process.cwd(), "src", "commands"))
-            .filter(x => x.endsWith(".ts"));
-
-        for (const file of commandFiles) {
-            expect(registeredCommands).toContain(file.split("/").pop()!.split(".")[0]);
-        }
-
-        expect(commandFiles.length).toBe(registeredCommands.length);
-    });
-});
-
-describe("RegisterButtonEvents", () => {
-    test("EXEPCT every button event in the button events folder to be registered", () => {
-        const registeredButtonEvents: string[] = [];
-
-        CoreClient.RegisterButtonEvent = jest.fn().mockImplementation((name: string) => {
-            registeredButtonEvents.push(name);
-        });
-
-        Registry.RegisterButtonEvents();
-
-        const eventFiles = getFilesInDirectory(path.join(process.cwd(), "src", "buttonEvents"))
-            .filter(x => x.endsWith(".ts"));
-
-        for (const file of eventFiles) {
-            expect(registeredButtonEvents).toContain(file.split("/").pop()!.split(".")[0].toLowerCase());
-        }
-
-        expect(eventFiles.length).toBe(registeredButtonEvents.length);
-    });
-});
-
-function getFilesInDirectory(dir: string): string[] {
-    let results: string[] = [];
-    const list = fs.readdirSync(dir);
-
-    list.forEach(file => {
-        file = path.join(dir, file);
-        const stat = fs.statSync(file);
-
-        if (stat && stat.isDirectory()) {
-            /* recurse into a subdirectory */
-            results = results.concat(getFilesInDirectory(file));
-        } else {
-            /* is a file */
-            results.push(file);
-        }
-    });
-
-    return results;
-}
diff --git a/tests/timers/PurgeClaims.test.ts b/tests/timers/PurgeClaims.test.ts
new file mode 100644
index 0000000..6d0d09b
--- /dev/null
+++ b/tests/timers/PurgeClaims.test.ts
@@ -0,0 +1,7 @@
+describe("PurgeClaims", () => {
+    test.todo("EXPECT claims to be fetched");
+
+    test.todo("EXPECT Claim.RemoveMany to remove the claims older than 2 minutes");
+
+    test.todo("EXPECT info logged");
+});
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 23f0771..c63e681 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10,155 +10,114 @@
     "@jridgewell/gen-mapping" "^0.3.5"
     "@jridgewell/trace-mapping" "^0.3.24"
 
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.6.tgz#ab88da19344445c3d8889af2216606d3329f3ef2"
-  integrity sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==
+"@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/highlight" "^7.24.6"
+    "@babel/helper-validator-identifier" "^7.25.9"
+    js-tokens "^4.0.0"
     picocolors "^1.0.0"
 
-"@babel/compat-data@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.6.tgz#b3600217688cabb26e25f8e467019e66d71b7ae2"
-  integrity sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==
+"@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.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.6.tgz#8650e0e4b03589ebe886c4e4a60398db0a7ec787"
-  integrity sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==
+  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.24.6"
-    "@babel/generator" "^7.24.6"
-    "@babel/helper-compilation-targets" "^7.24.6"
-    "@babel/helper-module-transforms" "^7.24.6"
-    "@babel/helpers" "^7.24.6"
-    "@babel/parser" "^7.24.6"
-    "@babel/template" "^7.24.6"
-    "@babel/traverse" "^7.24.6"
-    "@babel/types" "^7.24.6"
+    "@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.2.3"
     semver "^6.3.1"
 
-"@babel/generator@^7.24.6", "@babel/generator@^7.7.2":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.6.tgz#dfac82a228582a9d30c959fe50ad28951d4737a7"
-  integrity sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==
+"@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.24.6"
+    "@babel/parser" "^7.26.0"
+    "@babel/types" "^7.26.0"
     "@jridgewell/gen-mapping" "^0.3.5"
     "@jridgewell/trace-mapping" "^0.3.25"
-    jsesc "^2.5.1"
+    jsesc "^3.0.2"
 
-"@babel/helper-compilation-targets@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz#4a51d681f7680043d38e212715e2a7b1ad29cb51"
-  integrity sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==
+"@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.24.6"
-    "@babel/helper-validator-option" "^7.24.6"
-    browserslist "^4.22.2"
+    "@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.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz#ac7ad5517821641550f6698dd5468f8cef78620d"
-  integrity sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==
-
-"@babel/helper-function-name@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz#cebdd063386fdb95d511d84b117e51fc68fec0c8"
-  integrity sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==
+"@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/template" "^7.24.6"
-    "@babel/types" "^7.24.6"
+    "@babel/traverse" "^7.25.9"
+    "@babel/types" "^7.25.9"
 
-"@babel/helper-hoist-variables@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz#8a7ece8c26756826b6ffcdd0e3cf65de275af7f9"
-  integrity sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==
+"@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/types" "^7.24.6"
+    "@babel/helper-module-imports" "^7.25.9"
+    "@babel/helper-validator-identifier" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
 
-"@babel/helper-module-imports@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz#65e54ffceed6a268dc4ce11f0433b82cfff57852"
-  integrity sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==
+"@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.24.6"
+    "@babel/template" "^7.25.9"
+    "@babel/types" "^7.26.0"
 
-"@babel/helper-module-transforms@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz#22346ed9df44ce84dee850d7433c5b73fab1fe4e"
-  integrity sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==
+"@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/helper-environment-visitor" "^7.24.6"
-    "@babel/helper-module-imports" "^7.24.6"
-    "@babel/helper-simple-access" "^7.24.6"
-    "@babel/helper-split-export-declaration" "^7.24.6"
-    "@babel/helper-validator-identifier" "^7.24.6"
-
-"@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.24.6", "@babel/helper-plugin-utils@^7.8.0":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz#fa02a32410a15a6e8f8185bcbf608f10528d2a24"
-  integrity sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==
-
-"@babel/helper-simple-access@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz#1d6e04d468bba4fc963b4906f6dac6286cfedff1"
-  integrity sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==
-  dependencies:
-    "@babel/types" "^7.24.6"
-
-"@babel/helper-split-export-declaration@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz#e830068f7ba8861c53b7421c284da30ae656d7a3"
-  integrity sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==
-  dependencies:
-    "@babel/types" "^7.24.6"
-
-"@babel/helper-string-parser@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz#28583c28b15f2a3339cfafafeaad42f9a0e828df"
-  integrity sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==
-
-"@babel/helper-validator-identifier@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz#08bb6612b11bdec78f3feed3db196da682454a5e"
-  integrity sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==
-
-"@babel/helper-validator-option@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz#59d8e81c40b7d9109ab7e74457393442177f460a"
-  integrity sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==
-
-"@babel/helpers@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.6.tgz#cd124245299e494bd4e00edda0e4ea3545c2c176"
-  integrity sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==
-  dependencies:
-    "@babel/template" "^7.24.6"
-    "@babel/types" "^7.24.6"
-
-"@babel/highlight@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.6.tgz#6d610c1ebd2c6e061cade0153bf69b0590b7b3df"
-  integrity sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==
-  dependencies:
-    "@babel/helper-validator-identifier" "^7.24.6"
-    chalk "^2.4.2"
-    js-tokens "^4.0.0"
-    picocolors "^1.0.0"
-
-"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.6.tgz#5e030f440c3c6c78d195528c3b688b101a365328"
-  integrity sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==
+    "@babel/types" "^7.26.0"
 
 "@babel/plugin-syntax-async-generators@^7.8.4":
   version "7.8.4"
@@ -174,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==
@@ -196,13 +169,13 @@
     "@babel/helper-plugin-utils" "^7.8.0"
 
 "@babel/plugin-syntax-jsx@^7.7.2":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.6.tgz#bcca2964150437f88f65e3679e3d68762287b9c8"
-  integrity sha512-lWfvAIFNWMlCsU0DRUun2GpFwZdGTukLaHJqRh1JRb80NdAP5Sb1HDHB5X9P9OtgZHQl089UzQkpYlBq2VTPRw==
+  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.24.6"
+    "@babel/helper-plugin-utils" "^7.25.9"
 
-"@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
+"@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==
@@ -216,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==
@@ -244,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==
@@ -252,60 +232,47 @@
     "@babel/helper-plugin-utils" "^7.14.5"
 
 "@babel/plugin-syntax-typescript@^7.7.2":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.6.tgz#769daf2982d60308bc83d8936eaecb7582463c87"
-  integrity sha512-TzCtxGgVTEJWWwcYwQhCIQ6WaKlo80/B+Onsk4RRCcYqpYGFcG9etPW94VToGte5AAcxRrhjPUFvUS3Y2qKi4A==
+  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.24.6"
+    "@babel/helper-plugin-utils" "^7.25.9"
 
-"@babel/template@^7.24.6", "@babel/template@^7.3.3":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.6.tgz#048c347b2787a6072b24c723664c8d02b67a44f9"
-  integrity sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==
+"@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.24.6"
-    "@babel/parser" "^7.24.6"
-    "@babel/types" "^7.24.6"
+    "@babel/code-frame" "^7.25.9"
+    "@babel/parser" "^7.25.9"
+    "@babel/types" "^7.25.9"
 
-"@babel/traverse@^7.24.6":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.6.tgz#0941ec50cdeaeacad0911eb67ae227a4f8424edc"
-  integrity sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==
+"@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.24.6"
-    "@babel/generator" "^7.24.6"
-    "@babel/helper-environment-visitor" "^7.24.6"
-    "@babel/helper-function-name" "^7.24.6"
-    "@babel/helper-hoist-variables" "^7.24.6"
-    "@babel/helper-split-export-declaration" "^7.24.6"
-    "@babel/parser" "^7.24.6"
-    "@babel/types" "^7.24.6"
+    "@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.20.7", "@babel/types@^7.24.6", "@babel/types@^7.3.3":
-  version "7.24.6"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.6.tgz#ba4e1f59870c10dc2fa95a274ac4feec23b21912"
-  integrity sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==
+"@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-string-parser" "^7.24.6"
-    "@babel/helper-validator-identifier" "^7.24.6"
-    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==
 
-"@bconnorwhite/module@^2.0.2":
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/@bconnorwhite/module/-/module-2.0.2.tgz#557846110bb89412e9689ac778358bc2b1af0c4a"
-  integrity sha512-ck1me5WMgZKp06gnJrVKEkytpehTTQbvsAMbF1nGPeHri/AZNhj87++PSE2LOxmZqM0EtGMaqeLdx7Lw7SUnTA==
-  dependencies:
-    find-up "^5.0.0"
-    read-json-safe "^1.0.5"
-    types-pkg-json "^1.1.0"
-
 "@colors/colors@1.6.0", "@colors/colors@^1.6.0":
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0"
@@ -320,57 +287,57 @@
     enabled "2.0.x"
     kuler "^2.0.0"
 
-"@discordjs/builders@^1.8.2":
-  version "1.8.2"
-  resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.8.2.tgz#535d970331ee40f20dec9ef8079e43092f323ce9"
-  integrity sha512-6wvG3QaCjtMu0xnle4SoOIeFB4y6fKMN6WZfy3BMKJdQQtPLik8KGzDwBVL/+wTtcE/ZlFjgEk74GublyEVZ7g==
+"@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:
-    "@discordjs/formatters" "^0.4.0"
-    "@discordjs/util" "^1.1.0"
-    "@sapphire/shapeshift" "^3.9.7"
-    discord-api-types "0.37.83"
+    "@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.2"
+    tslib "^2.6.3"
 
 "@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":
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-2.1.0.tgz#f327d944ab2dcf9a1f674470a481f78a120a5e3b"
-  integrity sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==
+"@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.4.0":
-  version "0.4.0"
-  resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.4.0.tgz#066a2c2163b26ac066e6f621f17445be9690c6a9"
-  integrity sha512-fJ06TLC1NiruF35470q3Nr1bi95BdvKFAF+T5bNfZJ4bNdqZ3VZ+Ttg6SThqTxm6qumSG3choxLBHMC69WXNXQ==
+"@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.83"
+    discord-api-types "0.37.97"
 
-"@discordjs/rest@^2.0.0", "@discordjs/rest@^2.3.0":
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.3.0.tgz#06d37c7fb54a9be61134b5bbb201abd760343472"
-  integrity sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg==
+"@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.0"
-    "@discordjs/util" "^1.1.0"
-    "@sapphire/async-queue" "^1.5.2"
+    "@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.2.4"
-    discord-api-types "0.37.83"
+    "@vladfrangu/async_event_emitter" "^2.4.6"
+    discord-api-types "0.37.97"
     magic-bytes.js "^1.10.0"
-    tslib "^2.6.2"
-    undici "6.13.0"
+    tslib "^2.6.3"
+    undici "6.19.8"
 
-"@discordjs/util@^1.1.0":
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.1.0.tgz#dcffd2b61aab8eadd66bea67811bc34fc769bb2a"
-  integrity sha512-IndcI5hzlNZ7GS96RV3Xw1R2kaDuXEp7tRIy/KlhidpN/BQ1qh1NZt3377dMLTa44xDUNKT7hnXkA/oUAzD/lg==
+"@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":
+"@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==
@@ -386,65 +353,90 @@
     ws "^8.16.0"
 
 "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
-  version "4.4.0"
-  resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
-  integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56"
+  integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==
   dependencies:
-    eslint-visitor-keys "^3.3.0"
+    eslint-visitor-keys "^3.4.3"
 
-"@eslint-community/regexpp@^4.10.0":
-  version "4.11.0"
-  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
-  integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==
+"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0":
+  version "4.12.1"
+  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
+  integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
 
-"@eslint-community/regexpp@^4.6.1":
-  version "4.10.1"
-  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0"
-  integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==
+"@eslint/config-array@^0.18.0":
+  version "0.18.0"
+  resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d"
+  integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==
+  dependencies:
+    "@eslint/object-schema" "^2.1.4"
+    debug "^4.3.1"
+    minimatch "^3.1.2"
 
-"@eslint/eslintrc@^2.1.4":
-  version "2.1.4"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
-  integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==
+"@eslint/core@^0.7.0":
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3"
+  integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==
+
+"@eslint/eslintrc@^3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6"
+  integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==
   dependencies:
     ajv "^6.12.4"
     debug "^4.3.2"
-    espree "^9.6.0"
-    globals "^13.19.0"
+    espree "^10.0.1"
+    globals "^14.0.0"
     ignore "^5.2.0"
     import-fresh "^3.2.1"
     js-yaml "^4.1.0"
     minimatch "^3.1.2"
     strip-json-comments "^3.1.1"
 
-"@eslint/js@8.57.0":
-  version "8.57.0"
-  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
-  integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
+"@eslint/js@9.13.0":
+  version "9.13.0"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.13.0.tgz#c5f89bcd57eb54d5d4fa8b77693e9c28dc97e547"
+  integrity sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==
 
-"@humanwhocodes/config-array@^0.11.14":
-  version "0.11.14"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
-  integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==
+"@eslint/object-schema@^2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843"
+  integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
+
+"@eslint/plugin-kit@^0.2.0":
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz#cd14fe2db79fa639839dfef4105e83bad1814482"
+  integrity sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==
   dependencies:
-    "@humanwhocodes/object-schema" "^2.0.2"
-    debug "^4.3.1"
-    minimatch "^3.0.5"
+    levn "^0.4.1"
+
+"@humanfs/core@^0.19.0":
+  version "0.19.0"
+  resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.0.tgz#08db7a8c73bb07673d9ebd925f2dad746411fcec"
+  integrity sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==
+
+"@humanfs/node@^0.16.5":
+  version "0.16.5"
+  resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.5.tgz#a9febb7e7ad2aff65890fdc630938f8d20aa84ba"
+  integrity sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==
+  dependencies:
+    "@humanfs/core" "^0.19.0"
+    "@humanwhocodes/retry" "^0.3.0"
 
 "@humanwhocodes/module-importer@^1.0.1":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
   integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
 
-"@humanwhocodes/object-schema@^2.0.2":
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
-  integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
+"@humanwhocodes/retry@^0.3.0", "@humanwhocodes/retry@^0.3.1":
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a"
+  integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==
 
 "@inquirer/figures@^1.0.3":
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.3.tgz#1227cc980f88e6d6ab85abadbf164f5038041edd"
-  integrity sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==
+  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"
@@ -959,9 +951,9 @@
   integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
 
 "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
-  version "1.4.15"
-  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
-  integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+  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"
@@ -971,13 +963,6 @@
     "@jridgewell/resolve-uri" "^3.1.0"
     "@jridgewell/sourcemap-codec" "^1.4.14"
 
-"@ljharb/through@^2.3.13":
-  version "2.3.13"
-  resolved "https://registry.yarnpkg.com/@ljharb/through/-/through-2.3.13.tgz#b7e4766e0b65aa82e529be945ab078de79874edc"
-  integrity sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==
-  dependencies:
-    call-bind "^1.0.7"
-
 "@mapbox/node-pre-gyp@^1.0.0":
   version "1.0.11"
   resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
@@ -1006,7 +991,7 @@
   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", "@nodelib/fs.walk@^1.2.8":
+"@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==
@@ -1032,9 +1017,9 @@
     graceful-fs "4.2.10"
 
 "@pnpm/npm-conf@^2.1.0":
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz#0058baf1c26cbb63a828f0193795401684ac86f0"
-  integrity sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==
+  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"
@@ -1047,15 +1032,15 @@
   dependencies:
     any-observable "^0.3.0"
 
-"@sapphire/async-queue@^1.5.2":
-  version "1.5.2"
-  resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.2.tgz#2982dce16e5b8b1ea792604d20c23c0585877b97"
-  integrity sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg==
+"@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@^3.9.7":
-  version "3.9.7"
-  resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.7.tgz#43e23243cac8a0c046bf1e73baf3dbf407d33a0c"
-  integrity sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g==
+"@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"
@@ -1070,16 +1055,6 @@
   resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
   integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
 
-"@sindresorhus/is@^4.0.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==
-
-"@sindresorhus/is@^5.2.0", "@sindresorhus/is@^5.3.0":
-  version "5.6.0"
-  resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668"
-  integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==
-
 "@sinonjs/commons@^3.0.0":
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd"
@@ -1099,20 +1074,6 @@
   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":
-  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"
-
-"@szmarczak/http-timer@^5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a"
-  integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==
-  dependencies:
-    defer-to-connect "^2.0.1"
-
 "@tokenizer/token@^0.3.0":
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
@@ -1159,16 +1120,6 @@
     "@types/connect" "*"
     "@types/node" "*"
 
-"@types/cacheable-request@^6.0.1":
-  version "6.0.3"
-  resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183"
-  integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==
-  dependencies:
-    "@types/http-cache-semantics" "*"
-    "@types/keyv" "^3.1.4"
-    "@types/node" "*"
-    "@types/responselike" "^1.0.0"
-
 "@types/clone-deep@^4.0.4":
   version "4.0.4"
   resolved "https://registry.yarnpkg.com/@types/clone-deep/-/clone-deep-4.0.4.tgz#6932f2d0a4b4ef337d97cb72e6ecdf091c05145d"
@@ -1181,23 +1132,28 @@
   dependencies:
     "@types/node" "*"
 
-"@types/express-serve-static-core@^4.17.33":
-  version "4.19.3"
-  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz#e469a13e4186c9e1c0418fb17be8bc8ff1b19a7a"
-  integrity sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==
+"@types/estree@^1.0.6":
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
+  integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
+
+"@types/express-serve-static-core@^5.0.0":
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz#3c9997ae9d00bc236e45c6374e84f2596458d9db"
+  integrity sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==
   dependencies:
     "@types/node" "*"
     "@types/qs" "*"
     "@types/range-parser" "*"
     "@types/send" "*"
 
-"@types/express@^4.17.20":
-  version "4.17.21"
-  resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d"
-  integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==
+"@types/express@^5.0.0":
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c"
+  integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==
   dependencies:
     "@types/body-parser" "*"
-    "@types/express-serve-static-core" "^4.17.33"
+    "@types/express-serve-static-core" "^5.0.0"
     "@types/qs" "*"
     "@types/serve-static" "*"
 
@@ -1208,11 +1164,6 @@
   dependencies:
     "@types/node" "*"
 
-"@types/http-cache-semantics@*", "@types/http-cache-semantics@^4.0.2":
-  version "4.0.4"
-  resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
-  integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==
-
 "@types/http-errors@*":
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
@@ -1237,20 +1188,18 @@
   dependencies:
     "@types/istanbul-lib-report" "*"
 
-"@types/jest@^29.0.0":
-  version "29.5.12"
-  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544"
-  integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==
+"@types/jest@^29.5.14":
+  version "29.5.14"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5"
+  integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==
   dependencies:
     expect "^29.0.0"
     pretty-format "^29.0.0"
 
-"@types/keyv@^3.1.4":
-  version "3.1.4"
-  resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
-  integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
-  dependencies:
-    "@types/node" "*"
+"@types/json-schema@^7.0.15":
+  version "7.0.15"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+  integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
 
 "@types/luxon@~3.4.0":
   version "3.4.2"
@@ -1262,47 +1211,33 @@
   resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
   integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==
 
-"@types/node@*":
-  version "20.14.9"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420"
-  integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==
+"@types/node@*", "@types/node@^22.8.1":
+  version "22.8.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.1.tgz#b39d4b98165e2ae792ce213f610c7c6108ccfa16"
+  integrity sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==
   dependencies:
-    undici-types "~5.26.4"
+    undici-types "~6.19.8"
 
 "@types/node@16.9.1":
   version "16.9.1"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708"
   integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==
 
-"@types/node@^20.0.0":
-  version "20.14.11"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.11.tgz#09b300423343460455043ddd4d0ded6ac579b74b"
-  integrity sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==
-  dependencies:
-    undici-types "~5.26.4"
-
 "@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/qs@*":
-  version "6.9.15"
-  resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce"
-  integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==
+  version "6.9.16"
+  resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.16.tgz#52bba125a07c0482d26747d5d4947a64daf8f794"
+  integrity sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==
 
 "@types/range-parser@*":
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
   integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
 
-"@types/responselike@^1.0.0":
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50"
-  integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==
-  dependencies:
-    "@types/node" "*"
-
 "@types/send@*":
   version "0.17.4"
   resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
@@ -1330,15 +1265,15 @@
   resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c"
   integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==
 
-"@types/uuid@^9.0.0":
-  version "9.0.8"
-  resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba"
-  integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==
+"@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.5.10":
-  version "8.5.10"
-  resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787"
-  integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==
+  version "8.5.12"
+  resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e"
+  integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==
   dependencies:
     "@types/node" "*"
 
@@ -1348,143 +1283,110 @@
   integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==
 
 "@types/yargs@^17.0.8":
-  version "17.0.32"
-  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229"
-  integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==
+  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" "*"
 
-"@typescript-eslint/eslint-plugin@^7.0.0":
-  version "7.15.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz#8eaf396ac2992d2b8f874b68eb3fcd6b179cb7f3"
-  integrity sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==
+"@typescript-eslint/eslint-plugin@8.11.0", "@typescript-eslint/eslint-plugin@^8.11.0":
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz#c3f087d20715fa94310b30666c08b3349e0ab084"
+  integrity sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==
   dependencies:
     "@eslint-community/regexpp" "^4.10.0"
-    "@typescript-eslint/scope-manager" "7.15.0"
-    "@typescript-eslint/type-utils" "7.15.0"
-    "@typescript-eslint/utils" "7.15.0"
-    "@typescript-eslint/visitor-keys" "7.15.0"
+    "@typescript-eslint/scope-manager" "8.11.0"
+    "@typescript-eslint/type-utils" "8.11.0"
+    "@typescript-eslint/utils" "8.11.0"
+    "@typescript-eslint/visitor-keys" "8.11.0"
     graphemer "^1.4.0"
     ignore "^5.3.1"
     natural-compare "^1.4.0"
     ts-api-utils "^1.3.0"
 
-"@typescript-eslint/parser@^6.16.0":
-  version "6.21.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b"
-  integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==
+"@typescript-eslint/parser@8.11.0", "@typescript-eslint/parser@^8.11.0":
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.11.0.tgz#2ad1481388dc1c937f50b2d138c9ca57cc6c5cce"
+  integrity sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==
   dependencies:
-    "@typescript-eslint/scope-manager" "6.21.0"
-    "@typescript-eslint/types" "6.21.0"
-    "@typescript-eslint/typescript-estree" "6.21.0"
-    "@typescript-eslint/visitor-keys" "6.21.0"
+    "@typescript-eslint/scope-manager" "8.11.0"
+    "@typescript-eslint/types" "8.11.0"
+    "@typescript-eslint/typescript-estree" "8.11.0"
+    "@typescript-eslint/visitor-keys" "8.11.0"
     debug "^4.3.4"
 
-"@typescript-eslint/scope-manager@6.21.0":
-  version "6.21.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1"
-  integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==
+"@typescript-eslint/scope-manager@8.11.0":
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz#9d399ce624118966732824878bc9a83593a30405"
+  integrity sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==
   dependencies:
-    "@typescript-eslint/types" "6.21.0"
-    "@typescript-eslint/visitor-keys" "6.21.0"
+    "@typescript-eslint/types" "8.11.0"
+    "@typescript-eslint/visitor-keys" "8.11.0"
 
-"@typescript-eslint/scope-manager@7.15.0":
-  version "7.15.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz#201b34b0720be8b1447df17b963941bf044999b2"
-  integrity sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==
+"@typescript-eslint/type-utils@8.11.0":
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz#b7f9e6120c1ddee8a1a07615646642ad85fc91b5"
+  integrity sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==
   dependencies:
-    "@typescript-eslint/types" "7.15.0"
-    "@typescript-eslint/visitor-keys" "7.15.0"
-
-"@typescript-eslint/type-utils@7.15.0":
-  version "7.15.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz#5b83c904c6de91802fb399305a50a56d10472c39"
-  integrity sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==
-  dependencies:
-    "@typescript-eslint/typescript-estree" "7.15.0"
-    "@typescript-eslint/utils" "7.15.0"
+    "@typescript-eslint/typescript-estree" "8.11.0"
+    "@typescript-eslint/utils" "8.11.0"
     debug "^4.3.4"
     ts-api-utils "^1.3.0"
 
-"@typescript-eslint/types@6.21.0":
-  version "6.21.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d"
-  integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==
+"@typescript-eslint/types@8.11.0":
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.11.0.tgz#7c766250502097f49bbc2e651132e6bf489e20b8"
+  integrity sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==
 
-"@typescript-eslint/types@7.15.0":
-  version "7.15.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.15.0.tgz#fb894373a6e3882cbb37671ffddce44f934f62fc"
-  integrity sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==
-
-"@typescript-eslint/typescript-estree@6.21.0":
-  version "6.21.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46"
-  integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==
+"@typescript-eslint/typescript-estree@8.11.0":
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz#35fe5d3636fc5727c52429393415412e552e222b"
+  integrity sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==
   dependencies:
-    "@typescript-eslint/types" "6.21.0"
-    "@typescript-eslint/visitor-keys" "6.21.0"
+    "@typescript-eslint/types" "8.11.0"
+    "@typescript-eslint/visitor-keys" "8.11.0"
     debug "^4.3.4"
-    globby "^11.1.0"
-    is-glob "^4.0.3"
-    minimatch "9.0.3"
-    semver "^7.5.4"
-    ts-api-utils "^1.0.1"
-
-"@typescript-eslint/typescript-estree@7.15.0":
-  version "7.15.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz#e323bfa3966e1485b638ce751f219fc1f31eba37"
-  integrity sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==
-  dependencies:
-    "@typescript-eslint/types" "7.15.0"
-    "@typescript-eslint/visitor-keys" "7.15.0"
-    debug "^4.3.4"
-    globby "^11.1.0"
+    fast-glob "^3.3.2"
     is-glob "^4.0.3"
     minimatch "^9.0.4"
     semver "^7.6.0"
     ts-api-utils "^1.3.0"
 
-"@typescript-eslint/utils@7.15.0":
-  version "7.15.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.15.0.tgz#9e6253c4599b6e7da2fb64ba3f549c73eb8c1960"
-  integrity sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==
+"@typescript-eslint/utils@8.11.0":
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.11.0.tgz#4480d1e9f2bb18ea3510c79f870a1aefc118103d"
+  integrity sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==
   dependencies:
     "@eslint-community/eslint-utils" "^4.4.0"
-    "@typescript-eslint/scope-manager" "7.15.0"
-    "@typescript-eslint/types" "7.15.0"
-    "@typescript-eslint/typescript-estree" "7.15.0"
+    "@typescript-eslint/scope-manager" "8.11.0"
+    "@typescript-eslint/types" "8.11.0"
+    "@typescript-eslint/typescript-estree" "8.11.0"
 
-"@typescript-eslint/visitor-keys@6.21.0":
-  version "6.21.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47"
-  integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==
+"@typescript-eslint/visitor-keys@8.11.0":
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz#273de1cbffe63d9f9cd7dfc20b5a5af66310cb92"
+  integrity sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==
   dependencies:
-    "@typescript-eslint/types" "6.21.0"
-    eslint-visitor-keys "^3.4.1"
-
-"@typescript-eslint/visitor-keys@7.15.0":
-  version "7.15.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz#1da0726201a859343fe6a05742a7c1792fff5b66"
-  integrity sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==
-  dependencies:
-    "@typescript-eslint/types" "7.15.0"
+    "@typescript-eslint/types" "8.11.0"
     eslint-visitor-keys "^3.4.3"
 
-"@ungap/structured-clone@^1.2.0":
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
-  integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
-
-"@vladfrangu/async_event_emitter@^2.2.4":
-  version "2.2.4"
-  resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz#d3537432c6db6444680a596271dff8ea407343b3"
-  integrity sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==
+"@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==
 
 abbrev@1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
   integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
 
+abort-controller@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
+  integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
+  dependencies:
+    event-target-shim "^5.0.0"
+
 accepts@~1.3.8:
   version "1.3.8"
   resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
@@ -1498,10 +1400,10 @@ acorn-jsx@^5.3.2:
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
   integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
 
-acorn@^8.9.0:
-  version "8.11.3"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
-  integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
+acorn@^8.12.0:
+  version "8.14.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
+  integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
 
 agent-base@6:
   version "6.0.2"
@@ -1528,17 +1430,6 @@ ajv@^6.12.4:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-all-package-names@^2.0.2:
-  version "2.0.897"
-  resolved "https://registry.yarnpkg.com/all-package-names/-/all-package-names-2.0.897.tgz#d672ca5d9566eef046b996523dec84b21512e20c"
-  integrity sha512-ZWd7bk4fK2Doei5nLP28ytHa5hTD+qr8bWuJQh1Bbi36Ij4RVXSL6tM0zEni85usEhX0iQ32IuYlyoYM0sKFyQ==
-  dependencies:
-    commander-version "^1.1.0"
-    p-lock "^2.0.0"
-    parse-json-object "^2.0.1"
-    progress "^2.0.3"
-    types-json "^1.2.2"
-
 ansi-align@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
@@ -1586,9 +1477,9 @@ ansi-regex@^5.0.1:
   integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
 
 ansi-regex@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
-  integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
+  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"
@@ -1614,7 +1505,7 @@ 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.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==
@@ -1677,21 +1568,24 @@ array-flatten@1.1.1:
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
   integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
 
-array-union@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
-  integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-
 async@^3.2.3:
-  version "3.2.5"
-  resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
-  integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
+  version "3.2.6"
+  resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
+  integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
 
 asynckit@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
   integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
 
+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:
+    stubborn-fs "^1.2.5"
+    when-exit "^2.1.1"
+
 await-to-js@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/await-to-js/-/await-to-js-3.0.0.tgz#70929994185616f4675a91af6167eb61cc92868f"
@@ -1741,22 +1635,25 @@ babel-plugin-jest-hoist@^29.6.3:
     "@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@^29.6.3:
   version "29.6.3"
@@ -1776,11 +1673,6 @@ base64-js@^1.3.1:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-big-integer@^1.6.44:
-  version "1.6.52"
-  resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85"
-  integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==
-
 bignumber.js@9.0.0:
   version "9.0.0"
   resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075"
@@ -1800,10 +1692,10 @@ bmp-ts@^1.0.9:
   resolved "https://registry.yarnpkg.com/bmp-ts/-/bmp-ts-1.0.9.tgz#0fd124ba812be9b786b29e5b186ee76d74ff5538"
   integrity sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==
 
-body-parser@1.20.2, body-parser@^1.20.2:
-  version "1.20.2"
-  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
-  integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
+body-parser@1.20.3, body-parser@^1.20.2:
+  version "1.20.3"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
+  integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
   dependencies:
     bytes "3.1.2"
     content-type "~1.0.5"
@@ -1813,31 +1705,24 @@ body-parser@1.20.2, body-parser@^1.20.2:
     http-errors "2.0.0"
     iconv-lite "0.4.24"
     on-finished "2.4.1"
-    qs "6.11.0"
+    qs "6.13.0"
     raw-body "2.5.2"
     type-is "~1.6.18"
     unpipe "1.0.0"
 
-boxen@^7.1.1:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.1.1.tgz#f9ba525413c2fec9cdb88987d835c4f7cad9c8f4"
-  integrity sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==
+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 "^7.0.1"
-    chalk "^5.2.0"
+    camelcase "^8.0.0"
+    chalk "^5.3.0"
     cli-boxes "^3.0.0"
-    string-width "^5.1.2"
-    type-fest "^2.13.0"
-    widest-line "^4.0.1"
-    wrap-ansi "^8.1.0"
-
-bplist-parser@^0.2.0:
-  version "0.2.0"
-  resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e"
-  integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==
-  dependencies:
-    big-integer "^1.6.44"
+    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"
@@ -1861,17 +1746,17 @@ braces@^3.0.3:
   dependencies:
     fill-range "^7.1.1"
 
-browserslist@^4.22.2:
-  version "4.23.0"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab"
-  integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==
+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.30001587"
-    electron-to-chromium "^1.4.668"
-    node-releases "^2.0.14"
-    update-browserslist-db "^1.0.13"
+    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.x:
+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==
@@ -1906,59 +1791,18 @@ buffer@^6.0.3:
     base64-js "^1.3.1"
     ieee754 "^1.2.1"
 
-builtins@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
-  integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==
-
-bundle-name@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a"
-  integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==
+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 "^5.0.0"
+    run-applescript "^7.0.0"
 
 bytes@3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
   integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
 
-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==
-
-cacheable-lookup@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27"
-  integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==
-
-cacheable-request@^10.2.8:
-  version "10.2.14"
-  resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.14.tgz#eb915b665fda41b79652782df3f553449c406b9d"
-  integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==
-  dependencies:
-    "@types/http-cache-semantics" "^4.0.2"
-    get-stream "^6.0.1"
-    http-cache-semantics "^4.1.1"
-    keyv "^4.5.3"
-    mimic-response "^4.0.0"
-    normalize-url "^8.0.0"
-    responselike "^3.0.0"
-
-cacheable-request@^7.0.2:
-  version "7.0.4"
-  resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817"
-  integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==
-  dependencies:
-    clone-response "^1.0.2"
-    get-stream "^5.1.0"
-    http-cache-semantics "^4.0.0"
-    keyv "^4.0.0"
-    lowercase-keys "^2.0.0"
-    normalize-url "^6.0.1"
-    responselike "^2.0.0"
-
 call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
@@ -1983,11 +1827,6 @@ callsites@^3.0.0:
   resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
   integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
 
-callsites@^4.0.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/callsites/-/callsites-4.1.0.tgz#de72b98612eed4e1e2564c952498677faa9d86c2"
-  integrity sha512-aBMbD1Xxay75ViYezwT40aQONfr+pSXTHwNKvIXhXD6+LY3F1dLIcceoC5OZKBVHbXcysz1hL9D2w0JJIMXpUw==
-
 camelcase@^5.3.1:
   version "5.3.1"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
@@ -1998,15 +1837,15 @@ camelcase@^6.2.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
   integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
 
-camelcase@^7.0.1:
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048"
-  integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==
+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==
 
-caniuse-lite@^1.0.30001587:
-  version "1.0.30001627"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz#8071c42d468e06ed2fb2c545efe79a663fd326ab"
-  integrity sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==
+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==
 
 canvas@^2.11.2:
   version "2.11.2"
@@ -2044,7 +1883,7 @@ chalk@^2.4.1, chalk@^2.4.2:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
-chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
+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==
@@ -2078,9 +1917,9 @@ ci-info@^3.2.0:
   integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
 
 cjs-module-lexer@^1.0.0:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c"
-  integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==
+  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"
@@ -2175,13 +2014,6 @@ clone-deep@^4.0.1:
     kind-of "^6.0.2"
     shallow-clone "^3.0.0"
 
-clone-response@^1.0.2:
-  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"
@@ -2267,19 +2099,6 @@ combined-stream@^1.0.8:
   dependencies:
     delayed-stream "~1.0.0"
 
-commander-version@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/commander-version/-/commander-version-1.1.0.tgz#fbfaea4632921a42f8f855f96bcaa3d9920a6296"
-  integrity sha512-9aNW4N6q6EPDUszLRH6k9IwO6OoGYh3HRgUF/fA7Zs+Mz1v1x5akSqT7QGB8JsGY7AG7qMA7oRRB/4yyn33FYA==
-  dependencies:
-    "@bconnorwhite/module" "^2.0.2"
-    commander "^6.1.0"
-
-commander@^6.1.0:
-  version "6.2.1"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
-  integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
-
 component-emitter@^1.3.0:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17"
@@ -2298,16 +2117,15 @@ config-chain@^1.1.11:
     ini "^1.3.4"
     proto-list "~1.2.1"
 
-configstore@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/configstore/-/configstore-6.0.0.tgz#49eca2ebc80983f77e09394a1a56e0aca8235566"
-  integrity sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==
+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:
-    dot-prop "^6.0.1"
-    graceful-fs "^4.2.6"
-    unique-string "^3.0.0"
-    write-file-atomic "^3.0.3"
-    xdg-basedir "^5.0.1"
+    atomically "^2.0.3"
+    dot-prop "^9.0.0"
+    graceful-fs "^4.2.11"
+    xdg-basedir "^5.1.0"
 
 console-control-strings@^1.0.0, console-control-strings@^1.1.0:
   version "1.1.0"
@@ -2336,10 +2154,10 @@ cookie-signature@1.0.6:
   resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
   integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
 
-cookie@0.6.0:
-  version "0.6.0"
-  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
-  integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
+cookie@0.7.1:
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
+  integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
 
 cookiejar@^2.1.2:
   version "2.1.4"
@@ -2391,22 +2209,15 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
     shebang-command "^2.0.0"
     which "^2.0.1"
 
-crypto-random-string@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-4.0.0.tgz#5a3cc53d7dd86183df5da0312816ceeeb5bb1fc2"
-  integrity sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==
-  dependencies:
-    type-fest "^1.0.1"
-
 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==
 
 dayjs@^1.11.9:
-  version "1.11.11"
-  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e"
-  integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
+  version "1.11.13"
+  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
+  integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
 
 debug@2.6.9:
   version "2.6.9"
@@ -2416,11 +2227,11 @@ debug@2.6.9:
     ms "2.0.0"
 
 debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
-  version "4.3.5"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
-  integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
+  version "4.3.7"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+  integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
   dependencies:
-    ms "2.1.2"
+    ms "^2.1.3"
 
 decompress-response@^4.2.0:
   version "4.2.1"
@@ -2429,13 +2240,6 @@ decompress-response@^4.2.0:
   dependencies:
     mimic-response "^2.0.0"
 
-decompress-response@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
-  integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
-  dependencies:
-    mimic-response "^3.1.0"
-
 dedent@^1.0.0:
   version "1.5.3"
   resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a"
@@ -2456,23 +2260,18 @@ deepmerge@^4.2.2:
   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@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c"
-  integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==
-  dependencies:
-    bplist-parser "^0.2.0"
-    untildify "^4.0.0"
+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@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da"
-  integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==
+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 "^3.0.0"
-    default-browser-id "^3.0.0"
-    execa "^7.1.1"
-    titleize "^3.0.0"
+    bundle-name "^4.1.0"
+    default-browser-id "^5.0.0"
 
 defaults@^1.0.3:
   version "1.0.4"
@@ -2481,11 +2280,6 @@ defaults@^1.0.3:
   dependencies:
     clone "^1.0.2"
 
-defer-to-connect@^2.0.0, defer-to-connect@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
-  integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
-
 define-data-property@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
@@ -2556,49 +2350,45 @@ dir-glob@^3.0.1:
   dependencies:
     path-type "^4.0.0"
 
+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.js@^14.15.3:
-  version "14.15.3"
-  resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.15.3.tgz#b2a67a1a4ef192be498fb8b6784224a42906f1be"
-  integrity sha512-/UJDQO10VuU6wQPglA4kz2bw2ngeeSbogiIPx/TsnctfzV/tNf+q+i1HlgtX1OGpeOBpJH9erZQNO5oRM2uAtQ==
+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.16.3:
+  version "14.16.3"
+  resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.16.3.tgz#9553366953c992469f47a55af2a11c2054a9babe"
+  integrity sha512-EPCWE9OkA9DnFFNrO7Kl1WHHDYFXu3CNVFJg63bfU7hVtjZGyhShwZtSBImINQRWxWP2tgo2XI+QhdXx28r0aA==
   dependencies:
-    "@discordjs/builders" "^1.8.2"
+    "@discordjs/builders" "^1.9.0"
     "@discordjs/collection" "1.5.3"
-    "@discordjs/formatters" "^0.4.0"
-    "@discordjs/rest" "^2.3.0"
-    "@discordjs/util" "^1.1.0"
-    "@discordjs/ws" "^1.1.1"
+    "@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.83"
+    discord-api-types "0.37.100"
     fast-deep-equal "3.1.3"
     lodash.snakecase "4.1.1"
-    tslib "2.6.2"
-    undici "6.13.0"
+    tslib "^2.6.3"
+    undici "6.19.8"
 
-doctrine@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
-  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+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:
-    esutils "^2.0.2"
-
-dot-prop@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083"
-  integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==
-  dependencies:
-    is-obj "^2.0.0"
-
-dot-prop@^7.2.0:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-7.2.0.tgz#468172a3529779814d21a779c1ba2f6d76609809"
-  integrity sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==
-  dependencies:
-    type-fest "^2.11.2"
+    type-fest "^4.18.2"
 
 dotenv@^16.0.0, dotenv@^16.0.3:
   version "16.4.5"
@@ -2624,10 +2414,17 @@ ee-first@1.1.1:
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
 
-electron-to-chromium@^1.4.668:
-  version "1.4.788"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.788.tgz#a3545959d5cfa0a266d3e551386c040be34e7e06"
-  integrity sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==
+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"
@@ -2639,6 +2436,11 @@ emittery@^0.13.1:
   resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
   integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
 
+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"
@@ -2659,12 +2461,10 @@ encodeurl@~1.0.2:
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
   integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
 
-end-of-stream@^1.1.0:
-  version "1.4.4"
-  resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
-  integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
-  dependencies:
-    once "^1.4.0"
+encodeurl@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
+  integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
 
 error-ex@^1.3.1:
   version "1.3.2"
@@ -2707,11 +2507,16 @@ es-set-tostringtag@^2.1.0:
     has-tostringtag "^1.0.2"
     hasown "^2.0.2"
 
-escalade@^3.1.1, escalade@^3.1.2:
+escalade@^3.1.1:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
   integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
 
+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"
@@ -2742,81 +2547,83 @@ escape-string-regexp@^4.0.0:
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
 
-eslint-scope@^7.2.2:
-  version "7.2.2"
-  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
-  integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
+eslint-scope@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.1.0.tgz#70214a174d4cbffbc3e8a26911d8bf51b9ae9d30"
+  integrity sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==
   dependencies:
     esrecurse "^4.3.0"
     estraverse "^5.2.0"
 
-eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
+eslint-visitor-keys@^3.4.3:
   version "3.4.3"
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
   integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
 
-eslint@^8.56.0:
-  version "8.57.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668"
-  integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==
+eslint-visitor-keys@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz#1f785cc5e81eb7534523d85922248232077d2f8c"
+  integrity sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==
+
+eslint@^9.13.0:
+  version "9.13.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.13.0.tgz#7659014b7dda1ff876ecbd990f726e11c61596e6"
+  integrity sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==
   dependencies:
     "@eslint-community/eslint-utils" "^4.2.0"
-    "@eslint-community/regexpp" "^4.6.1"
-    "@eslint/eslintrc" "^2.1.4"
-    "@eslint/js" "8.57.0"
-    "@humanwhocodes/config-array" "^0.11.14"
+    "@eslint-community/regexpp" "^4.11.0"
+    "@eslint/config-array" "^0.18.0"
+    "@eslint/core" "^0.7.0"
+    "@eslint/eslintrc" "^3.1.0"
+    "@eslint/js" "9.13.0"
+    "@eslint/plugin-kit" "^0.2.0"
+    "@humanfs/node" "^0.16.5"
     "@humanwhocodes/module-importer" "^1.0.1"
-    "@nodelib/fs.walk" "^1.2.8"
-    "@ungap/structured-clone" "^1.2.0"
+    "@humanwhocodes/retry" "^0.3.1"
+    "@types/estree" "^1.0.6"
+    "@types/json-schema" "^7.0.15"
     ajv "^6.12.4"
     chalk "^4.0.0"
     cross-spawn "^7.0.2"
     debug "^4.3.2"
-    doctrine "^3.0.0"
     escape-string-regexp "^4.0.0"
-    eslint-scope "^7.2.2"
-    eslint-visitor-keys "^3.4.3"
-    espree "^9.6.1"
-    esquery "^1.4.2"
+    eslint-scope "^8.1.0"
+    eslint-visitor-keys "^4.1.0"
+    espree "^10.2.0"
+    esquery "^1.5.0"
     esutils "^2.0.2"
     fast-deep-equal "^3.1.3"
-    file-entry-cache "^6.0.1"
+    file-entry-cache "^8.0.0"
     find-up "^5.0.0"
     glob-parent "^6.0.2"
-    globals "^13.19.0"
-    graphemer "^1.4.0"
     ignore "^5.2.0"
     imurmurhash "^0.1.4"
     is-glob "^4.0.0"
-    is-path-inside "^3.0.3"
-    js-yaml "^4.1.0"
     json-stable-stringify-without-jsonify "^1.0.1"
-    levn "^0.4.1"
     lodash.merge "^4.6.2"
     minimatch "^3.1.2"
     natural-compare "^1.4.0"
     optionator "^0.9.3"
-    strip-ansi "^6.0.1"
     text-table "^0.2.0"
 
-espree@^9.6.0, espree@^9.6.1:
-  version "9.6.1"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
-  integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
+espree@^10.0.1, espree@^10.2.0:
+  version "10.2.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-10.2.0.tgz#f4bcead9e05b0615c968e85f83816bc386a45df6"
+  integrity sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==
   dependencies:
-    acorn "^8.9.0"
+    acorn "^8.12.0"
     acorn-jsx "^5.3.2"
-    eslint-visitor-keys "^3.4.1"
+    eslint-visitor-keys "^4.1.0"
 
 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==
 
-esquery@^1.4.2:
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
-  integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
+esquery@^1.5.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
+  integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
   dependencies:
     estraverse "^5.1.0"
 
@@ -2842,6 +2649,16 @@ etag@~1.8.1:
   resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
   integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
 
+event-target-shim@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
+  integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
+
+events@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
+  integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
 execa@^5.0.0:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@@ -2857,21 +2674,6 @@ execa@^5.0.0:
     signal-exit "^3.0.3"
     strip-final-newline "^2.0.0"
 
-execa@^7.1.1:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9"
-  integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==
-  dependencies:
-    cross-spawn "^7.0.3"
-    get-stream "^6.0.1"
-    human-signals "^4.3.0"
-    is-stream "^3.0.0"
-    merge-stream "^2.0.0"
-    npm-run-path "^5.1.0"
-    onetime "^6.0.0"
-    signal-exit "^3.0.7"
-    strip-final-newline "^3.0.0"
-
 execa@^8.0.1:
   version "8.0.1"
   resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c"
@@ -2914,36 +2716,36 @@ expect@^29.0.0, expect@^29.7.0:
     jest-util "^29.7.0"
 
 express@^4.18.2:
-  version "4.19.2"
-  resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
-  integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
+  version "4.21.1"
+  resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281"
+  integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==
   dependencies:
     accepts "~1.3.8"
     array-flatten "1.1.1"
-    body-parser "1.20.2"
+    body-parser "1.20.3"
     content-disposition "0.5.4"
     content-type "~1.0.4"
-    cookie "0.6.0"
+    cookie "0.7.1"
     cookie-signature "1.0.6"
     debug "2.6.9"
     depd "2.0.0"
-    encodeurl "~1.0.2"
+    encodeurl "~2.0.0"
     escape-html "~1.0.3"
     etag "~1.8.1"
-    finalhandler "1.2.0"
+    finalhandler "1.3.1"
     fresh "0.5.2"
     http-errors "2.0.0"
-    merge-descriptors "1.0.1"
+    merge-descriptors "1.0.3"
     methods "~1.1.2"
     on-finished "2.4.1"
     parseurl "~1.3.3"
-    path-to-regexp "0.1.7"
+    path-to-regexp "0.1.10"
     proxy-addr "~2.0.7"
-    qs "6.11.0"
+    qs "6.13.0"
     range-parser "~1.2.1"
     safe-buffer "5.2.1"
-    send "0.18.0"
-    serve-static "1.15.0"
+    send "0.19.0"
+    serve-static "1.16.2"
     setprototypeof "1.2.0"
     statuses "2.0.1"
     type-is "~1.6.18"
@@ -2964,7 +2766,7 @@ fast-deep-equal@3.1.3, fast-deep-equal@^3.1.1, fast-deep-equal@^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.2.9, fast-glob@^3.3.0:
+fast-glob@^3.3.0, fast-glob@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
   integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
@@ -3036,12 +2838,12 @@ figures@^3.0.0:
   dependencies:
     escape-string-regexp "^1.0.5"
 
-file-entry-cache@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
-  integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+file-entry-cache@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
+  integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
   dependencies:
-    flat-cache "^3.0.4"
+    flat-cache "^4.0.0"
 
 file-stream-rotator@^0.6.1:
   version "0.6.1"
@@ -3059,6 +2861,13 @@ file-type@^16.0.0:
     strtok3 "^6.2.4"
     token-types "^4.1.1"
 
+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"
@@ -3066,13 +2875,13 @@ fill-range@^7.1.1:
   dependencies:
     to-regex-range "^5.0.1"
 
-finalhandler@1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
-  integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
+finalhandler@1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
+  integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==
   dependencies:
     debug "2.6.9"
-    encodeurl "~1.0.2"
+    encodeurl "~2.0.0"
     escape-html "~1.0.3"
     on-finished "2.4.1"
     parseurl "~1.3.3"
@@ -3100,14 +2909,13 @@ find-up@^5.0.0:
     locate-path "^6.0.0"
     path-exists "^4.0.0"
 
-flat-cache@^3.0.4:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
-  integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
+flat-cache@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
+  integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
   dependencies:
     flatted "^3.2.9"
-    keyv "^4.5.3"
-    rimraf "^3.0.2"
+    keyv "^4.5.4"
 
 flatted@^3.2.9:
   version "3.3.1"
@@ -3120,27 +2928,22 @@ fn.name@1.x.x:
   integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
 
 follow-redirects@^1.15.6:
-  version "1.15.6"
-  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
-  integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
+  version "1.15.9"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
+  integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
 
 foreground-child@^3.1.0:
-  version "3.2.1"
-  resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7"
-  integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==
+  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:
     cross-spawn "^7.0.0"
     signal-exit "^4.0.1"
 
-form-data-encoder@^2.1.2:
-  version "2.1.4"
-  resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5"
-  integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==
-
 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==
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.2.tgz#83ad9ced7c03feaad97e293d6f6091011e1659c8"
+  integrity sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==
   dependencies:
     asynckit "^0.4.0"
     combined-stream "^1.0.8"
@@ -3193,6 +2996,11 @@ function-bind@^1.1.2:
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
   integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
 
+fuse.js@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.0.0.tgz#6573c9fcd4c8268e403b4fc7d7131ffcf99a9eb2"
+  integrity sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==
+
 gauge@^3.0.0:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395"
@@ -3218,6 +3026,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-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
@@ -3258,14 +3071,7 @@ get-proto@^1.0.1:
     dunder-proto "^1.0.1"
     es-object-atoms "^1.0.0"
 
-get-stream@^5.1.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
-  integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
-  dependencies:
-    pump "^3.0.0"
-
-get-stream@^6.0.0, get-stream@^6.0.1:
+get-stream@^6.0.0:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
   integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
@@ -3314,6 +3120,18 @@ glob@^10.3.10:
     package-json-from-dist "^1.0.0"
     path-scurry "^1.11.1"
 
+glob@^11.0.0:
+  version "11.0.0"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e"
+  integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==
+  dependencies:
+    foreground-child "^3.1.0"
+    jackspeak "^4.0.1"
+    minimatch "^10.0.0"
+    minipass "^7.1.2"
+    package-json-from-dist "^1.0.0"
+    path-scurry "^2.0.0"
+
 glob@^7.1.3, glob@^7.1.4:
   version "7.2.3"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
@@ -3333,36 +3151,15 @@ global-directory@^4.0.1:
   dependencies:
     ini "4.1.1"
 
-global-dirs@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485"
-  integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==
-  dependencies:
-    ini "2.0.0"
-
 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==
 
-globals@^13.19.0:
-  version "13.24.0"
-  resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
-  integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
-  dependencies:
-    type-fest "^0.20.2"
-
-globby@^11.1.0:
-  version "11.1.0"
-  resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
-  integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
-  dependencies:
-    array-union "^2.1.0"
-    dir-glob "^3.0.1"
-    fast-glob "^3.2.9"
-    ignore "^5.2.0"
-    merge2 "^1.4.1"
-    slash "^3.0.0"
+globals@^14.0.0:
+  version "14.0.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
+  integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
 
 globby@^13.1.2:
   version "13.2.2"
@@ -3387,46 +3184,12 @@ gopd@^1.2.0:
   resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
   integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
 
-got@^11.8.5:
-  version "11.8.6"
-  resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
-  integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==
-  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"
-
-got@^12.1.0:
-  version "12.6.1"
-  resolved "https://registry.yarnpkg.com/got/-/got-12.6.1.tgz#8869560d1383353204b5a9435f782df9c091f549"
-  integrity sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==
-  dependencies:
-    "@sindresorhus/is" "^5.2.0"
-    "@szmarczak/http-timer" "^5.0.1"
-    cacheable-lookup "^7.0.0"
-    cacheable-request "^10.2.8"
-    decompress-response "^6.0.0"
-    form-data-encoder "^2.1.2"
-    get-stream "^6.0.1"
-    http2-wrapper "^2.1.10"
-    lowercase-keys "^3.0.0"
-    p-cancelable "^3.0.0"
-    responselike "^3.0.0"
-
 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.6, graceful-fs@^4.2.9:
+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==
@@ -3487,11 +3250,6 @@ has-unicode@^2.0.1:
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
   integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
 
-has-yarn@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-3.0.0.tgz#c3c21e559730d1d3b57e28af1f30d06fac38147d"
-  integrity sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==
-
 hasown@^2.0.0, hasown@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
@@ -3516,11 +3274,6 @@ html-escaper@^2.0.0:
   resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
   integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
 
-http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1:
-  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==
-
 http-errors@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
@@ -3532,22 +3285,6 @@ http-errors@2.0.0:
     statuses "2.0.1"
     toidentifier "1.0.1"
 
-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==
-  dependencies:
-    quick-lru "^5.1.1"
-    resolve-alpn "^1.0.0"
-
-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.2.0"
-
 https-proxy-agent@^5.0.0:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
@@ -3561,11 +3298,6 @@ human-signals@^2.1.0:
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
   integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
 
-human-signals@^4.3.0:
-  version "4.3.1"
-  resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2"
-  integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==
-
 human-signals@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
@@ -3583,7 +3315,7 @@ ieee754@^1.1.13, ieee754@^1.2.1:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
-ignore-walk@^6.0.3:
+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==
@@ -3591,9 +3323,9 @@ ignore-walk@^6.0.3:
     minimatch "^9.0.0"
 
 ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
-  integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
+  integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
 
 image-q@^4.0.0:
   version "4.0.0"
@@ -3610,15 +3342,10 @@ import-fresh@^3.2.1, import-fresh@^3.3.0:
     parent-module "^1.0.0"
     resolve-from "^4.0.0"
 
-import-lazy@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
-  integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
-
 import-local@^3.0.2, import-local@^3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
-  integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
+  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"
@@ -3656,11 +3383,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
 
-ini@2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
-  integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
-
 ini@4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1"
@@ -3718,19 +3440,15 @@ inquirer@^7.0.0:
     strip-ansi "^6.0.0"
     through "^2.3.6"
 
-inquirer@^9.2.12:
-  version "9.2.23"
-  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.2.23.tgz#cd2fe34edca12315b624fbc3c8cb99b1c4f61f90"
-  integrity sha512-kod5s+FBPIDM2xiy9fu+6wdU/SkK5le5GS9lh4FEBjBHqiMgD9lLFbCbuqFNAjNL2ZOy9Wd9F694IOzN9pZHBA==
+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"
-    "@ljharb/through" "^2.3.13"
     ansi-escapes "^4.3.2"
-    chalk "^5.3.0"
-    cli-cursor "^3.1.0"
     cli-width "^4.1.0"
     external-editor "^3.1.0"
-    lodash "^4.17.21"
     mute-stream "1.0.0"
     ora "^5.4.1"
     run-async "^3.0.0"
@@ -3738,6 +3456,7 @@ inquirer@^9.2.12:
     string-width "^4.2.3"
     strip-ansi "^6.0.1"
     wrap-ansi "^6.2.0"
+    yoctocolors-cjs "^2.1.2"
 
 ipaddr.js@1.9.1:
   version "1.9.1"
@@ -3754,17 +3473,12 @@ is-arrayish@^0.3.1:
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
   integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
 
-is-core-module@^2.13.0, is-core-module@^2.8.1:
-  version "2.13.1"
-  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
-  integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
+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.0"
-
-is-docker@^2.0.0:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
-  integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+    hasown "^2.0.2"
 
 is-docker@^3.0.0:
   version "3.0.0"
@@ -3805,10 +3519,10 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
   dependencies:
     is-extglob "^2.1.1"
 
-is-in-ci@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/is-in-ci/-/is-in-ci-0.1.0.tgz#5e07d6a02ec3a8292d3f590973357efa3fceb0d3"
-  integrity sha512-d9PXLEY0v1iJ64xLiQMJ51J128EYHAaOR4yZqQi8aHGfw6KgifM3/Viw1oZZ1GCVmb3gBuyhLyHj0HgR2DhSXQ==
+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"
@@ -3817,14 +3531,6 @@ is-inside-container@^1.0.0:
   dependencies:
     is-docker "^3.0.0"
 
-is-installed-globally@^0.4.0:
-  version "0.4.0"
-  resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520"
-  integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==
-  dependencies:
-    global-dirs "^3.0.0"
-    is-path-inside "^3.0.2"
-
 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"
@@ -3843,15 +3549,6 @@ is-interactive@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90"
   integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==
 
-is-name-taken@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/is-name-taken/-/is-name-taken-2.0.0.tgz#c36e6515e07621dc48cd026b6b015a504942fae1"
-  integrity sha512-W+FUWF5g7ONVJTx3rldZeVizmPzrMMUdscpSQ96vyYerx+4b2NcqaujLJJDWruGzE0FjzGZO9RFIipOGxx/WIw==
-  dependencies:
-    all-package-names "^2.0.2"
-    package-name-conflict "^1.0.3"
-    validate-npm-package-name "^3.0.0"
-
 is-npm@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-6.0.0.tgz#b59e75e8915543ca5d881ecff864077cba095261"
@@ -3862,11 +3559,6 @@ is-number@^7.0.0:
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
   integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
 
-is-obj@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
-  integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
-
 is-observable@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e"
@@ -3879,11 +3571,6 @@ is-path-cwd@^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@^3.0.2, is-path-inside@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
-  integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
-
 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"
@@ -3923,11 +3610,6 @@ is-stream@^3.0.0:
   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
   integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
 
-is-typedarray@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
-  integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
-
 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"
@@ -3943,12 +3625,12 @@ is-url-superb@^6.1.0:
   resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-6.1.0.tgz#182f0d92b482412afeadfba8e6ea2c76680e3631"
   integrity sha512-LXdhGlYqUPdvEyIhWPEEwYYK3yrUiPcBjmFGlZNv1u5GtIL5qQRf7ddDyPNAvsMFqdzS923FROpTQU97tLe3JQ==
 
-is-wsl@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
-  integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+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-docker "^2.0.0"
+    is-inside-container "^1.0.0"
 
 isarray@~1.0.0:
   version "1.0.0"
@@ -3966,9 +3648,9 @@ isobject@^3.0.1:
   integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
 
 issue-regex@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/issue-regex/-/issue-regex-4.1.0.tgz#e2039123748a48e6711eed7a9eb392f2c17c9341"
-  integrity sha512-X3HBmm7+Th+l4/kMtqwcHHgELD0Lfl0Ina6S3+grr+mKmTxsrM84NAO1UuRPIxIbGLIl3TCEu45S1kdu21HYbQ==
+  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.2"
@@ -3987,9 +3669,9 @@ istanbul-lib-instrument@^5.0.4:
     semver "^6.3.0"
 
 istanbul-lib-instrument@^6.0.0:
-  version "6.0.2"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1"
-  integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==
+  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"
@@ -4032,6 +3714,23 @@ jackspeak@^3.1.2:
   optionalDependencies:
     "@pkgjs/parseargs" "^0.11.0"
 
+jackspeak@^4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015"
+  integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==
+  dependencies:
+    "@isaacs/cliui" "^8.0.2"
+
+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:
+    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"
@@ -4455,10 +4154,10 @@ js-yaml@^4.1.0:
   dependencies:
     argparse "^2.0.1"
 
-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"
@@ -4485,7 +4184,7 @@ json5@^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, keyv@^4.5.3:
+keyv@^4.5.4:
   version "4.5.4"
   resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
   integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
@@ -4507,12 +4206,17 @@ kuler@^2.0.0:
   resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
   integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
 
-latest-version@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-7.0.0.tgz#843201591ea81a4d404932eeb61240fe04e9e5da"
-  integrity sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==
+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 "^8.1.0"
+    package-json "^10.0.0"
 
 leven@^3.1.0:
   version "3.1.0"
@@ -4600,12 +4304,7 @@ locate-path@^6.0.0:
   dependencies:
     p-locate "^5.0.0"
 
-lodash.isequal@^4.5.0:
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
-  integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
-
-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 sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
@@ -4685,26 +4384,16 @@ logform@^2.6.0, logform@^2.6.1:
     safe-stable-stringify "^2.3.1"
     triple-beam "^1.3.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==
-
-lowercase-keys@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2"
-  integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==
-
-lru-cache@^10.0.1:
-  version "10.2.2"
-  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878"
-  integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==
-
-lru-cache@^10.2.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==
 
+lru-cache@^11.0.0:
+  version "11.0.1"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.1.tgz#3a732fbfedb82c5ba7bca6564ad3f42afcb6e147"
+  integrity sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==
+
 lru-cache@^5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@@ -4736,7 +4425,7 @@ make-dir@^4.0.0:
   dependencies:
     semver "^7.5.3"
 
-make-error@1.x:
+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==
@@ -4758,15 +4447,15 @@ media-typer@0.3.0:
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
   integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
 
-meow@^12.1.1:
-  version "12.1.1"
-  resolved "https://registry.yarnpkg.com/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6"
-  integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==
+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-descriptors@1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
-  integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
+merge-descriptors@1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
+  integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
 
 merge-stream@^2.0.0:
   version "2.0.0"
@@ -4784,9 +4473,9 @@ methods@^1.1.2, methods@~1.1.2:
   integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
 
 micromatch@^4.0.4:
-  version "4.0.7"
-  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5"
-  integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==
+  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.3"
     picomatch "^2.3.1"
@@ -4838,51 +4527,36 @@ mimic-function@^5.0.0:
   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"
-  integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
-
 mimic-response@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
   integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
 
-mimic-response@^3.1.0:
-  version "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==
-
-mimic-response@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f"
-  integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==
-
-minimatch@9.0.3:
-  version "9.0.3"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
-  integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
-  dependencies:
-    brace-expansion "^2.0.1"
-
-minimatch@9.0.5, minimatch@^9.0.4:
+minimatch@9.0.5, 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"
 
-minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+minimatch@^10.0.0:
+  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"
 
-minimatch@^9.0.0:
-  version "9.0.4"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51"
-  integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==
+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"
 
@@ -4936,12 +4610,7 @@ ms@2.0.0:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
   integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
 
-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==
-
-ms@2.1.3, ms@^2.1.1:
+ms@2.1.3, ms@^2.1.1, 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==
@@ -4981,9 +4650,9 @@ mz@^2.4.0:
     thenify-all "^1.0.0"
 
 nan@^2.17.0:
-  version "2.20.0"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3"
-  integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==
+  version "2.22.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3"
+  integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==
 
 natural-compare@^1.4.0:
   version "1.4.0"
@@ -5014,10 +4683,10 @@ node-int64@^0.4.0:
   resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
   integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
 
-node-releases@^2.0.14:
-  version "2.0.14"
-  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
-  integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==
+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==
 
 nopt@^5.0.0:
   version "5.0.0"
@@ -5027,12 +4696,11 @@ nopt@^5.0.0:
     abbrev "1"
 
 normalize-package-data@^6.0.0:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.1.tgz#fa69e9452210f0fabf4d79ee08d0c2870c51ed88"
-  integrity sha512-6rvCfeRW+OEZagAB4lMLSNuTNYZWLVtKccK79VSTf//yTY5VOCgcpH80O+bZK8Neps7pUnd5G+QlMg1yV/2iZQ==
+  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"
-    is-core-module "^2.8.1"
     semver "^7.3.5"
     validate-npm-package-license "^3.0.4"
 
@@ -5041,20 +4709,10 @@ normalize-path@^3.0.0:
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
   integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
 
-normalize-url@^6.0.1:
-  version "6.1.0"
-  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
-  integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
-
-normalize-url@^8.0.0:
-  version "8.0.1"
-  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.1.tgz#9b7d96af9836577c58f5883e939365fa15623a4a"
-  integrity sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==
-
-np@^9.0.0:
-  version "9.2.0"
-  resolved "https://registry.yarnpkg.com/np/-/np-9.2.0.tgz#93323e1535b47317f02885e726f2aa0f41c14ac8"
-  integrity sha512-VYA8IKyx5XJqROr2vu8NAstfgBznrnOu3PUByFl3TBwzQVkeGC+gzOPzGbvYKM1QwlX6Gt7kVSPMzDP4qkSs1g==
+np@^10.0.7:
+  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"
@@ -5065,11 +4723,10 @@ np@^9.0.0:
     execa "^8.0.1"
     exit-hook "^4.0.0"
     github-url-from-git "^1.5.0"
-    has-yarn "^3.0.0"
     hosted-git-info "^7.0.1"
-    ignore-walk "^6.0.3"
+    ignore-walk "^6.0.4"
     import-local "^3.1.0"
-    inquirer "^9.2.12"
+    inquirer "^9.2.15"
     is-installed-globally "^1.0.0"
     is-interactive "^2.0.0"
     is-scoped "^3.0.0"
@@ -5077,12 +4734,11 @@ np@^9.0.0:
     listr "^0.14.3"
     listr-input "^0.2.1"
     log-symbols "^6.0.0"
-    meow "^12.1.1"
+    meow "^13.2.0"
     new-github-release-url "^2.0.0"
-    npm-name "^7.1.1"
+    npm-name "^8.0.0"
     onetime "^7.0.0"
-    open "^9.1.0"
-    ow "^1.1.1"
+    open "^10.0.4"
     p-memoize "^7.1.1"
     p-timeout "^6.1.2"
     path-exists "^5.0.0"
@@ -5090,26 +4746,25 @@ np@^9.0.0:
     read-package-up "^11.0.0"
     read-pkg "^9.0.1"
     rxjs "^7.8.1"
-    semver "^7.5.4"
+    semver "^7.6.0"
     symbol-observable "^4.0.0"
     terminal-link "^3.0.0"
     update-notifier "^7.0.0"
 
-npm-name@^7.1.1:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/npm-name/-/npm-name-7.1.1.tgz#373774cc044b4dab7835e1d02c84f60680f7f748"
-  integrity sha512-lyOwsFndLoozriMEsaqJ5lXvhCATYOEhDvxlom8TNvB9a/htDXuLgpVhMUOBd9zCewUXCyBXAPxrGr2TK2adgQ==
+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:
-    got "^11.8.5"
-    is-name-taken "^2.0.0"
     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 "^5.5.0"
-    registry-auth-token "^4.2.2"
+    p-map "^7.0.1"
+    registry-auth-token "^5.0.2"
     registry-url "^6.0.1"
-    validate-npm-package-name "^3.0.0"
+    validate-npm-package-name "^5.0.0"
 
 npm-run-path@^4.0.1:
   version "4.0.1"
@@ -5151,9 +4806,9 @@ object-hash@^3.0.0:
   integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
 
 object-inspect@^1.13.1:
-  version "1.13.1"
-  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
-  integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
+  integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
 
 omggif@^1.0.10:
   version "1.0.10"
@@ -5167,7 +4822,7 @@ on-finished@2.4.1:
   dependencies:
     ee-first "1.1.1"
 
-once@^1.3.0, once@^1.3.1, once@^1.4.0:
+once@^1.3.0, once@^1.3.1:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
   integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
@@ -5209,15 +4864,15 @@ onetime@^7.0.0:
   dependencies:
     mimic-function "^5.0.0"
 
-open@^9.1.0:
-  version "9.1.0"
-  resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6"
-  integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==
+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 "^4.0.0"
+    default-browser "^5.2.1"
     define-lazy-prop "^3.0.0"
     is-inside-container "^1.0.0"
-    is-wsl "^2.2.0"
+    is-wsl "^3.1.0"
 
 optionator@^0.9.3:
   version "0.9.4"
@@ -5256,27 +4911,6 @@ os-tmpdir@~1.0.2:
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
   integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
 
-ow@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/ow/-/ow-1.1.1.tgz#354a0f7df9d8d0cf961b29116daf972ef6be1632"
-  integrity sha512-sJBRCbS5vh1Jp9EOgwp1Ws3c16lJrUkJYlvWTYC03oyiYVwS/ns7lKRWow4w4XjDyTrA2pplQv4B2naWSR6yDA==
-  dependencies:
-    "@sindresorhus/is" "^5.3.0"
-    callsites "^4.0.0"
-    dot-prop "^7.2.0"
-    lodash.isequal "^4.5.0"
-    vali-date "^1.0.0"
-
-p-cancelable@^2.0.0:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
-  integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
-
-p-cancelable@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050"
-  integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==
-
 p-limit@^2.2.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@@ -5305,11 +4939,6 @@ p-locate@^5.0.0:
   dependencies:
     p-limit "^3.0.2"
 
-p-lock@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/p-lock/-/p-lock-2.1.0.tgz#6f9dbc55d3aac5b0f75c8ec47f3a6f1b954135f8"
-  integrity sha512-pi2yT8gNhVrV4LgsUvJWQy58TXH1HG2+NXDby9+UrsS/9fXb0FJH9aCxbdHJ0EAQ6XC7ggSP6GAzuR5puDArUQ==
-
 p-map@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
@@ -5322,6 +4951,11 @@ p-map@^5.5.0:
   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"
@@ -5331,9 +4965,9 @@ p-memoize@^7.1.1:
     type-fest "^3.0.0"
 
 p-timeout@^6.1.2:
-  version "6.1.2"
-  resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-6.1.2.tgz#22b8d8a78abf5e103030211c5fc6dee1166a6aa5"
-  integrity sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==
+  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"
@@ -5341,24 +4975,19 @@ p-try@^2.0.0:
   integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
 
 package-json-from-dist@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00"
-  integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==
+  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@^8.1.0:
-  version "8.1.1"
-  resolved "https://registry.yarnpkg.com/package-json/-/package-json-8.1.1.tgz#3e9948e43df40d1e8e78a85485f1070bf8f03dc8"
-  integrity sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==
+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:
-    got "^12.1.0"
-    registry-auth-token "^5.0.1"
-    registry-url "^6.0.0"
-    semver "^7.3.7"
-
-package-name-conflict@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/package-name-conflict/-/package-name-conflict-1.0.3.tgz#5147d35e8ae7f93401f1b3f9f3763c070463f25c"
-  integrity sha512-DPBNWSUWC0wPofXeNThao0uP4a93J7r90UyhagmJS0QcacTTkorZwXYsOop70phn1hKdcf/2e9lJIhazS8bx5A==
+    ky "^1.2.0"
+    registry-auth-token "^5.0.2"
+    registry-url "^6.0.1"
+    semver "^7.6.0"
 
 pako@^1.0.11:
   version "1.0.11"
@@ -5390,20 +5019,6 @@ parse-bmfont-xml@^1.1.6:
     xml-parse-from-string "^1.0.0"
     xml2js "^0.5.0"
 
-parse-json-object@^1.0.5:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/parse-json-object/-/parse-json-object-1.1.0.tgz#eef60211cec368259723d8586ecec7252f8fcdb2"
-  integrity sha512-4w5s6uJY1tW9REY8UwUOyaZKSKsrbQrMEzlV/Le/g5t4iMWuuyK83pZZ0OZimSOL9iyv2ORvRSgz71Ekd7iD3g==
-  dependencies:
-    types-json "^1.0.6"
-
-parse-json-object@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/parse-json-object/-/parse-json-object-2.0.1.tgz#a441bd8c36d2c33a69516286e7e4138a23607ee0"
-  integrity sha512-/oF7PUUBjCqHmMEE6xIQeX5ZokQ9+miudACzPt4KBU2qi6CxZYPdisPXx4ad7wpZJYi2ZpcW2PacLTU3De3ebw==
-  dependencies:
-    types-json "^1.2.0"
-
 parse-json@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@@ -5483,10 +5098,18 @@ path-scurry@^1.11.1:
     lru-cache "^10.2.0"
     minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
 
-path-to-regexp@0.1.7:
-  version "0.1.7"
-  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
-  integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+path-scurry@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580"
+  integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==
+  dependencies:
+    lru-cache "^11.0.0"
+    minipass "^7.1.2"
+
+path-to-regexp@0.1.10:
+  version "0.1.10"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
+  integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
 
 path-type@^4.0.0:
   version "4.0.0"
@@ -5498,11 +5121,16 @@ peek-readable@^4.1.0:
   resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72"
   integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==
 
-picocolors@^1.0.0, picocolors@^1.0.1:
+picocolors@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
   integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
 
+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"
@@ -5563,10 +5191,10 @@ process-nextick-args@~2.0.0:
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
   integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
 
-progress@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
-  integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
 
 prompts@^2.0.1:
   version "2.4.2"
@@ -5594,14 +5222,6 @@ proxy-from-env@^1.1.0:
   resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
   integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
 
-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==
-  dependencies:
-    end-of-stream "^1.1.0"
-    once "^1.3.1"
-
 punycode@^2.1.0:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
@@ -5619,17 +5239,10 @@ pure-rand@^6.0.0:
   resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2"
   integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==
 
-qs@6.11.0:
-  version "6.11.0"
-  resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
-  integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
-  dependencies:
-    side-channel "^1.0.4"
-
-qs@^6.9.1:
-  version "6.12.1"
-  resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a"
-  integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==
+qs@6.13.0, qs@^6.9.1:
+  version "6.13.0"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
+  integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
   dependencies:
     side-channel "^1.0.6"
 
@@ -5638,11 +5251,6 @@ queue-microtask@^1.2.2:
   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==
-
 range-parser@~1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
@@ -5673,19 +5281,6 @@ react-is@^18.0.0:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
   integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
 
-read-file-safe@^1.0.5:
-  version "1.0.10"
-  resolved "https://registry.yarnpkg.com/read-file-safe/-/read-file-safe-1.0.10.tgz#9ac9118f12cb122614612211f90dad9daf732e39"
-  integrity sha512-qW25fd2uMX3dV6Ui/R0jYK1MhTpjx8FO/VHaHTXzwWsGnkNwLRcqYfCXd9qDM+NZ273DPUvP2RaimYuLSu1K/g==
-
-read-json-safe@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/read-json-safe/-/read-json-safe-1.0.5.tgz#c459443b8e6eb3a2672cf68ddc81d2f4ef95c8d7"
-  integrity sha512-SJyNY/U9+vW35FPus22Qvv1oilnR7PCfN2E70uKQEGaJS313A5/cz9Yhv7ZtWzZ+XIwrtEPxXf10BOyYemHehA==
-  dependencies:
-    parse-json-object "^1.0.5"
-    read-file-safe "^1.0.5"
-
 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"
@@ -5732,7 +5327,7 @@ readable-stream@^2.3.6:
     string_decoder "~1.1.1"
     util-deprecate "~1.0.1"
 
-readable-stream@^3.4.0, readable-stream@^3.6.0, readable-stream@^3.6.2:
+readable-stream@^3.4.0, readable-stream@^3.6.0:
   version "3.6.2"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
   integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
@@ -5741,6 +5336,17 @@ readable-stream@^3.4.0, readable-stream@^3.6.0, readable-stream@^3.6.2:
     string_decoder "^1.1.1"
     util-deprecate "^1.0.1"
 
+readable-stream@^4.5.2:
+  version "4.5.2"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09"
+  integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==
+  dependencies:
+    abort-controller "^3.0.0"
+    buffer "^6.0.3"
+    events "^3.3.0"
+    process "^0.11.10"
+    string_decoder "^1.3.0"
+
 readable-web-to-node-stream@^3.0.0:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb"
@@ -5753,21 +5359,14 @@ reflect-metadata@^0.2.1:
   resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b"
   integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==
 
-registry-auth-token@^4.2.2:
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac"
-  integrity sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==
-  dependencies:
-    rc "1.2.8"
-
-registry-auth-token@^5.0.1:
+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.0, registry-url@^6.0.1:
+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==
@@ -5779,11 +5378,6 @@ require-directory@^2.1.1:
   resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
   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==
-
 resolve-cwd@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -5815,20 +5409,6 @@ resolve@^1.20.0:
     path-parse "^1.0.7"
     supports-preserve-symlinks-flag "^1.0.0"
 
-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"
-
-responselike@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626"
-  integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==
-  dependencies:
-    lowercase-keys "^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"
@@ -5857,12 +5437,10 @@ rimraf@^3.0.2:
   dependencies:
     glob "^7.1.3"
 
-run-applescript@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c"
-  integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==
-  dependencies:
-    execa "^5.0.0"
+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"
@@ -5906,9 +5484,9 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0:
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
 
 safe-stable-stringify@^2.3.1:
-  version "2.4.3"
-  resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886"
-  integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd"
+  integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==
 
 "safer-buffer@>= 2.1.2 < 3":
   version "2.1.2"
@@ -5925,27 +5503,20 @@ scoped-regex@^3.0.0:
   resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-3.0.0.tgz#cd7ede7d942f2ae90da53272102ff2d73129c46f"
   integrity sha512-yEsN6TuxZhZ1Tl9iB81frTNS292m0I/IG7+w8lTvfcJQP2x3vnpOoevjBoE3Np5A6KnZM2+RtVenihj9t6NiYg==
 
-semver-diff@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-4.0.0.tgz#3afcf5ed6d62259f5c72d0d5d50dffbdc9680df5"
-  integrity sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==
-  dependencies:
-    semver "^7.3.5"
-
 semver@^6.0.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.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0:
-  version "7.6.2"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
-  integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
+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==
 
-send@0.18.0:
-  version "0.18.0"
-  resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
-  integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
+send@0.19.0:
+  version "0.19.0"
+  resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
+  integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
   dependencies:
     debug "2.6.9"
     depd "2.0.0"
@@ -5961,15 +5532,15 @@ send@0.18.0:
     range-parser "~1.2.1"
     statuses "2.0.1"
 
-serve-static@1.15.0:
-  version "1.15.0"
-  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
-  integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+serve-static@1.16.2:
+  version "1.16.2"
+  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296"
+  integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==
   dependencies:
-    encodeurl "~1.0.2"
+    encodeurl "~2.0.0"
     escape-html "~1.0.3"
     parseurl "~1.3.3"
-    send "0.18.0"
+    send "0.19.0"
 
 set-blocking@^2.0.0:
   version "2.0.0"
@@ -6020,7 +5591,7 @@ shebang-regex@^3.0.0:
   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
   integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
 
-side-channel@^1.0.4, side-channel@^1.0.6:
+side-channel@^1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
   integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
@@ -6121,9 +5692,9 @@ spdx-expression-parse@^3.0.0:
     spdx-license-ids "^3.0.0"
 
 spdx-license-ids@^3.0.0:
-  version "3.0.18"
-  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz#22aa922dcf2f2885a6494a261f2d8b75345d0326"
-  integrity sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==
+  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"
@@ -6204,7 +5775,16 @@ string-width@^5.0.1, string-width@^5.1.2:
     emoji-regex "^9.2.2"
     strip-ansi "^7.0.1"
 
-string_decoder@^1.1.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, string_decoder@^1.3.0:
   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==
@@ -6253,7 +5833,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   dependencies:
     ansi-regex "^5.0.1"
 
-strip-ansi@^7.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==
@@ -6293,6 +5873,11 @@ strtok3@^6.2.4:
     "@tokenizer/token" "^0.3.0"
     peek-readable "^4.1.0"
 
+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==
+
 superagent@5.2.2:
   version "5.2.2"
   resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.2.2.tgz#6ff726c5642795b2c27009e92687c8e69a6bb07d"
@@ -6422,11 +6007,6 @@ tinycolor2@^1.6.0:
   resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
   integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==
 
-titleize@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53"
-  integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==
-
 tmp@^0.0.33:
   version "0.0.33"
   resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -6439,11 +6019,6 @@ tmpl@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 sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
-
 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"
@@ -6474,45 +6049,46 @@ triple-beam@^1.2.0, triple-beam@^1.3.0, triple-beam@^1.4.1:
   resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984"
   integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==
 
-ts-api-utils@^1.0.1, ts-api-utils@^1.3.0:
+ts-api-utils@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
   integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
 
 ts-essentials@^10.0.0:
-  version "10.0.0"
-  resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-10.0.0.tgz#4abb58d4ed9219e313a10cf86758d257470e4d38"
-  integrity sha512-77FHNJEyysF9+1s4G6eejuA1lxw7uMchT3ZPy3CIbh7GIunffpshtM8pTe5G6N5dpOzNevqRHew859ceLWVBfw==
+  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.0.0:
-  version "29.1.5"
-  resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.5.tgz#d6c0471cc78bffa2cb4664a0a6741ef36cfe8f69"
-  integrity sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==
+  version "29.2.5"
+  resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63"
+  integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==
   dependencies:
-    bs-logger "0.x"
-    fast-json-stable-stringify "2.x"
+    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.x"
-    make-error "1.x"
-    semver "^7.5.3"
-    yargs-parser "^21.0.1"
+    lodash.memoize "^4.1.2"
+    make-error "^1.3.6"
+    semver "^7.6.3"
+    yargs-parser "^21.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==
 
-tslib@2.6.2, tslib@^2.1.0, tslib@^2.5.0, tslib@^2.6.2:
-  version "2.6.2"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
-  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
-
 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==
 
+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-check@^0.4.0, type-check@~0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -6525,22 +6101,17 @@ type-detect@4.0.8:
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
   integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
 
-type-fest@^0.20.2:
-  version "0.20.2"
-  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
-  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
-
 type-fest@^0.21.3:
   version "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==
 
-type-fest@^1.0.1, type-fest@^1.0.2:
+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==
 
-type-fest@^2.11.2, type-fest@^2.13.0, type-fest@^2.5.1:
+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==
@@ -6550,10 +6121,15 @@ type-fest@^3.0.0:
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706"
   integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==
 
-type-fest@^4.6.0, type-fest@^4.7.1:
-  version "4.18.3"
-  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.18.3.tgz#5249f96e7c2c3f0f1561625f54050e343f1c8f68"
-  integrity sha512-Q08/0IrpvM+NMY9PA2rti9Jb+JejTddwmwmVQGskAlhtcrw1wsRzoR6ode6mR+OAabNa75w/dxedSUY2mlphaQ==
+type-fest@^4.18.2:
+  version "4.40.0"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.40.0.tgz#62bc09caccb99a75e1ad6b9b4653e8805e5e1eee"
+  integrity sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==
+
+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==
 
 type-is@~1.6.18:
   version "1.6.18"
@@ -6563,13 +6139,6 @@ type-is@~1.6.18:
     media-typer "0.3.0"
     mime-types "~2.1.24"
 
-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"
-
 typeorm@0.3.20:
   version "0.3.20"
   resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.20.tgz#4b61d737c6fed4e9f63006f88d58a5e54816b7ab"
@@ -6591,87 +6160,62 @@ typeorm@0.3.20:
     uuid "^9.0.0"
     yargs "^17.6.2"
 
-types-eslintrc@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/types-eslintrc/-/types-eslintrc-1.0.3.tgz#b277d301caca6c330477cbaa67bf6f79ac3d6fee"
-  integrity sha512-zKTR6aKHEudQpl+JoZjS3qh0B5IzSpQK/BCpYBECujcnKtqL87DJJ1sJKe5B8k/y8/UJ5sukq42QDvlaJyCO2w==
+typescript-eslint@^8.11.0:
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.11.0.tgz#74a0551972d675b4141672cec3acc5139b7399c0"
+  integrity sha512-cBRGnW3FSlxaYwU8KfAewxFK5uzeOAp0l2KebIlPDOT5olVi65KDG/yjBooPBG0kGW/HLkoz1c/iuBFehcS3IA==
   dependencies:
-    types-json "^1.2.2"
-
-types-json@^1.0.6, types-json@^1.2.0, types-json@^1.2.2:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/types-json/-/types-json-1.2.2.tgz#91ebe6de59e741ab38a98b071708a29494cedfe6"
-  integrity sha512-VfVLISHypS7ayIHvhacOESOTib4Sm4mAhnsgR8fzQdGp89YoBwMqvGmqENjtYehUQzgclT+7NafpEXkK/MHKwA==
-
-types-pkg-json@^1.1.0:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/types-pkg-json/-/types-pkg-json-1.2.1.tgz#890fe4f231000a721299831ef3e17a489b1e635e"
-  integrity sha512-Wj75lCkPwfj1BhmaJxMPpTQj9YGpihjs3WICigt1IjTAswr7zPXP0iJYPZjU0Rw/IriODhMJjAImkCIxt9KeuQ==
-  dependencies:
-    types-eslintrc "^1.0.3"
-    types-json "^1.2.2"
+    "@typescript-eslint/eslint-plugin" "8.11.0"
+    "@typescript-eslint/parser" "8.11.0"
+    "@typescript-eslint/utils" "8.11.0"
 
 typescript@^5.0.0:
-  version "5.4.5"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611"
-  integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==
+  version "5.6.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
+  integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
 
-undici-types@~5.26.4:
-  version "5.26.5"
-  resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
-  integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
+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==
 
-undici@6.13.0:
-  version "6.13.0"
-  resolved "https://registry.yarnpkg.com/undici/-/undici-6.13.0.tgz#7edbf4b7f3aac5f8a681d515151bf55cb3589d72"
-  integrity sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==
+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==
 
-unique-string@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-3.0.0.tgz#84a1c377aff5fd7a8bc6b55d8244b2bd90d75b9a"
-  integrity sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==
-  dependencies:
-    crypto-random-string "^4.0.0"
-
 unpipe@1.0.0, unpipe@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
   integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
 
-untildify@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
-  integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
-
-update-browserslist-db@^1.0.13:
-  version "1.0.16"
-  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356"
-  integrity sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==
+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.1.2"
-    picocolors "^1.0.1"
+    escalade "^3.2.0"
+    picocolors "^1.1.0"
 
 update-notifier@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-7.0.0.tgz#295aa782dadab784ed4073f7ffaea1fb2123031c"
-  integrity sha512-Hv25Bh+eAbOLlsjJreVPOs4vd51rrtCrmhyOJtbpAojro34jS4KQaEp4/EvlHJX7jSO42VvEFpkastVyXyIsdQ==
+  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 "^7.1.1"
+    boxen "^8.0.1"
     chalk "^5.3.0"
-    configstore "^6.0.0"
-    import-lazy "^4.0.0"
-    is-in-ci "^0.1.0"
-    is-installed-globally "^0.4.0"
+    configstore "^7.0.0"
+    is-in-ci "^1.0.0"
+    is-installed-globally "^1.0.0"
     is-npm "^6.0.0"
-    latest-version "^7.0.0"
+    latest-version "^9.0.0"
     pupa "^3.1.0"
-    semver "^7.5.4"
-    semver-diff "^4.0.0"
+    semver "^7.6.3"
     xdg-basedir "^5.1.0"
 
 uri-js@^4.2.2:
@@ -6704,19 +6248,14 @@ uuid@^9.0.0:
   integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
 
 v8-to-istanbul@^9.0.1:
-  version "9.2.0"
-  resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad"
-  integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==
+  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 "^2.0.0"
 
-vali-date@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6"
-  integrity sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==
-
 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"
@@ -6725,12 +6264,10 @@ validate-npm-package-license@^3.0.4:
     spdx-correct "^3.0.0"
     spdx-expression-parse "^3.0.0"
 
-validate-npm-package-name@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e"
-  integrity sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==
-  dependencies:
-    builtins "^1.0.3"
+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==
 
 vary@~1.1.2:
   version "1.1.2"
@@ -6764,6 +6301,11 @@ whatwg-url@^5.0.0:
     tr46 "~0.0.3"
     webidl-conversions "^3.0.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"
   resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
@@ -6778,12 +6320,12 @@ wide-align@^1.1.2:
   dependencies:
     string-width "^1.0.2 || 2 || 3 || 4"
 
-widest-line@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2"
-  integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==
+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 "^5.0.1"
+    string-width "^7.0.0"
 
 winston-daily-rotate-file@^5.0.0:
   version "5.0.0"
@@ -6813,18 +6355,18 @@ winston-transport@4.3.0:
     triple-beam "^1.2.0"
 
 winston-transport@^4.7.0:
-  version "4.7.1"
-  resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.1.tgz#52ff1bcfe452ad89991a0aaff9c3b18e7f392569"
-  integrity sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.8.0.tgz#a15080deaeb80338455ac52c863418c74fcf38ea"
+  integrity sha512-qxSTKswC6llEMZKgCQdaWgDuMJQnhuvF5f2Nk3SNXc4byfQ+voo2mX1Px9dkNOuR8p0KAjfPG29PuYUSIb+vSA==
   dependencies:
     logform "^2.6.1"
-    readable-stream "^3.6.2"
+    readable-stream "^4.5.2"
     triple-beam "^1.3.0"
 
-winston@^3.11.0:
-  version "3.13.1"
-  resolved "https://registry.yarnpkg.com/winston/-/winston-3.13.1.tgz#53ddadb9c2332eb12cff8306413b3480dc82b6c3"
-  integrity sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==
+winston@^3.15.0:
+  version "3.15.0"
+  resolved "https://registry.yarnpkg.com/winston/-/winston-3.15.0.tgz#4df7b70be091bc1a38a4f45b969fa79589b73ff5"
+  integrity sha512-RhruH2Cj0bV0WgNL+lOfoUBI4DVfdUNjVnJGVovWZmrcKtrFTTRzgXYK2O9cymSGjrERCtaAeHwMNnUWXlwZow==
   dependencies:
     "@colors/colors" "^1.6.0"
     "@dabh/diagnostics" "^2.0.2"
@@ -6887,21 +6429,20 @@ wrap-ansi@^8.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 sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
-write-file-atomic@^3.0.3:
-  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==
-  dependencies:
-    imurmurhash "^0.1.4"
-    is-typedarray "^1.0.0"
-    signal-exit "^3.0.2"
-    typedarray-to-buffer "^3.1.5"
-
 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"
@@ -6910,12 +6451,12 @@ write-file-atomic@^4.0.2:
     imurmurhash "^0.1.4"
     signal-exit "^3.0.7"
 
-ws@^8.16.0, ws@^8.17.1:
+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==
 
-xdg-basedir@^5.0.1, xdg-basedir@^5.1.0:
+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==
@@ -6958,7 +6499,7 @@ yargs-parser@^20.2.2:
   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.1, yargs-parser@^21.1.1:
+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==
@@ -6994,6 +6535,11 @@ yocto-queue@^0.1.0:
   resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
   integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
 
+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==
+
 zod@^3.23.8:
   version "3.24.3"
   resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.3.tgz#1f40f750a05e477396da64438e0e1c0995dafd87"