From 441715675c4d0ad285be73ed43bd5a3ecb157418 Mon Sep 17 00:00:00 2001 From: Michel Fedde Date: Tue, 17 Jun 2025 20:37:53 +0200 Subject: [PATCH] Adds eslint and linted & improved routing for interactions --- eslint.config.mjs | 15 + package-lock.json | 1599 +++++++++++++++++ package.json | 6 + public/icons/angle-left-solid.svg | 1 + public/icons/door-open-solid-white.svg | 1 + public/icons/folder-solid.svg | 2 +- public/icons/folder-tree-solid.svg | 1 - public/icons/pen-solid.svg | 1 + public/icons/people-group-solid.svg | 1 - source/Container/Services.ts | 42 +- source/Database/DatabaseConnection.ts | 12 +- source/Database/DatabaseUpdater.ts | 4 +- source/Discord/CommandDeployer.ts | 9 +- .../Discord/CommandPartials/GroupSelection.ts | 4 +- source/Discord/Commands/Command.ts | 1 - source/Discord/Commands/Groups.ts | 497 +++-- source/Discord/Commands/HelloWorldCommand.ts | 4 +- source/Discord/Commands/Playdates.ts | 12 +- source/Discord/DiscordClient.ts | 122 +- source/Discord/InteractionRouter.ts | 109 ++ source/Events/ElementCreatedEvent.ts | 2 +- source/Events/EventHandler.ts | 3 +- .../Handlers/SendCreatedNotification.ts | 4 +- source/Events/ReminderEvent.ts | 13 +- source/Groups/GroupConfigurationHandler.ts | 3 +- source/Groups/GroupConfigurationRenderer.ts | 50 +- .../Groups/GroupConfigurationTransformers.ts | 2 - source/Icons/IconCache.ts | 9 +- source/Icons/IconDeployer.ts | 5 +- source/Repositories/GroupRepository.ts | 5 +- source/Repositories/PlaydateRepository.ts | 8 +- source/Repositories/Repository.ts | 3 +- source/Utilities/ArrayUtils.ts | 2 +- source/deploy.ts | 1 - source/main.ts | 1 - 35 files changed, 2091 insertions(+), 463 deletions(-) create mode 100644 eslint.config.mjs create mode 100644 public/icons/angle-left-solid.svg create mode 100644 public/icons/door-open-solid-white.svg delete mode 100644 public/icons/folder-tree-solid.svg create mode 100644 public/icons/pen-solid.svg delete mode 100644 public/icons/people-group-solid.svg create mode 100644 source/Discord/InteractionRouter.ts diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..efd3518 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,15 @@ +import js from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; +import { defineConfig } from "eslint/config"; + + +export default defineConfig([ + { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"] }, + { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], languageOptions: { globals: globals.browser } }, + tseslint.configs.recommended, + { + rules: { + } + } +]); diff --git a/package-lock.json b/package-lock.json index 2b918b6..3db9cbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,12 @@ "node-cron": "^4.0.7", "object-path-set": "^1.0.2", "svg2img": "^1.0.0-beta.2" + }, + "devDependencies": { + "@eslint/js": "^9.29.0", + "eslint": "^9.29.0", + "globals": "^16.2.0", + "typescript-eslint": "^8.34.1" } }, "node_modules/@babel/runtime": { @@ -561,6 +567,252 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", + "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", + "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "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" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz", + "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", + "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", + "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@jimp/bmp": { "version": "0.16.13", "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.13.tgz", @@ -1013,6 +1265,44 @@ "regenerator-runtime": "^0.13.3" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@resvg/resvg-js": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", @@ -1301,6 +1591,13 @@ "integrity": "sha512-/0Ct/q5g+SgaACZ+A0ylY3071nEBN7QDnTWiCtaB3fx24UpoAQXf25yNVloOYVUis7jytM1F1WC78+EOwXkQJQ==", "license": "MIT" }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/express": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz", @@ -1330,6 +1627,13 @@ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/log4js": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/log4js/-/log4js-0.0.33.tgz", @@ -1396,6 +1700,263 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz", + "integrity": "sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.34.1", + "@typescript-eslint/type-utils": "8.34.1", + "@typescript-eslint/utils": "8.34.1", + "@typescript-eslint/visitor-keys": "8.34.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.34.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.1.tgz", + "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.34.1", + "@typescript-eslint/types": "8.34.1", + "@typescript-eslint/typescript-estree": "8.34.1", + "@typescript-eslint/visitor-keys": "8.34.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.1.tgz", + "integrity": "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.34.1", + "@typescript-eslint/types": "^8.34.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.1.tgz", + "integrity": "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.34.1", + "@typescript-eslint/visitor-keys": "8.34.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.1.tgz", + "integrity": "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.1.tgz", + "integrity": "sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.34.1", + "@typescript-eslint/utils": "8.34.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.1.tgz", + "integrity": "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.1.tgz", + "integrity": "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.34.1", + "@typescript-eslint/tsconfig-utils": "8.34.1", + "@typescript-eslint/types": "8.34.1", + "@typescript-eslint/visitor-keys": "8.34.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.1.tgz", + "integrity": "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.34.1", + "@typescript-eslint/types": "8.34.1", + "@typescript-eslint/typescript-estree": "8.34.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.1.tgz", + "integrity": "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.34.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vladfrangu/async_event_emitter": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz", @@ -1418,12 +1979,75 @@ "node": ">=6.5" } }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/any-base": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", "license": "MIT" }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -1436,6 +2060,13 @@ "node": ">= 4.5.0" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -1493,6 +2124,30 @@ "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", "license": "MIT" }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/btoa": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", @@ -1538,6 +2193,16 @@ "node": ">=0.4.0" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/centra": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", @@ -1547,12 +2212,71 @@ "follow-redirects": "^1.15.6" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "license": "ISC" }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/date-format": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", @@ -1603,6 +2327,13 @@ "node": ">=4.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -1723,6 +2454,174 @@ "@esbuild/win32-x64": "0.25.0" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz", + "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.1", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.29.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -1761,6 +2660,73 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/file-type": { "version": "16.5.4", "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", @@ -1784,6 +2750,50 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "license": "MIT" }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", @@ -1846,6 +2856,19 @@ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "license": "MIT" }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/global": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", @@ -1856,12 +2879,42 @@ "process": "^0.11.10" } }, + "node_modules/globals": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", + "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1882,6 +2935,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/image-q": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", @@ -1897,6 +2960,33 @@ "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", "license": "MIT" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1909,12 +2999,45 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-function": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", "license": "MIT" }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", @@ -1924,6 +3047,13 @@ "node": ">=0.10.0" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, "node_modules/jimp": { "version": "0.16.13", "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.16.13.tgz", @@ -1943,6 +3073,40 @@ "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", "license": "BSD-3-Clause" }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -1952,6 +3116,30 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/load-bmfont": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", @@ -1980,12 +3168,35 @@ "node": ">= 8" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", @@ -2014,6 +3225,30 @@ "integrity": "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==", "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2046,6 +3281,19 @@ "dom-walk": "^0.1.0" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -2085,6 +3333,13 @@ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "license": "MIT" }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-abi": { "version": "3.74.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", @@ -2127,12 +3382,75 @@ "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-bmfont-ascii": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", @@ -2161,6 +3479,26 @@ "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", "license": "MIT" }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/peek-readable": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", @@ -2181,6 +3519,19 @@ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "MIT" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pixelmatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", @@ -2228,6 +3579,16 @@ "node": ">=10" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -2247,6 +3608,37 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -2338,12 +3730,57 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "license": "MIT" }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2382,6 +3819,29 @@ "node": ">=10" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -2476,6 +3936,19 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/svg2img": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/svg2img/-/svg2img-1.0.0-beta.2.tgz", @@ -2531,6 +4004,19 @@ "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", "license": "MIT" }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/token-types": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", @@ -2548,6 +4034,19 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-mixer": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", @@ -2572,6 +4071,57 @@ "node": "*" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.1.tgz", + "integrity": "sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.34.1", + "@typescript-eslint/parser": "8.34.1", + "@typescript-eslint/utils": "8.34.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/undici": { "version": "6.21.1", "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", @@ -2596,6 +4146,16 @@ "node": ">= 4.0.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/utif": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", @@ -2611,6 +4171,32 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2686,6 +4272,19 @@ "engines": { "node": ">=0.4" } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 4d221ba..1f61d35 100644 --- a/package.json +++ b/package.json @@ -28,5 +28,11 @@ "node-cron": "^4.0.7", "object-path-set": "^1.0.2", "svg2img": "^1.0.0-beta.2" + }, + "devDependencies": { + "@eslint/js": "^9.29.0", + "eslint": "^9.29.0", + "globals": "^16.2.0", + "typescript-eslint": "^8.34.1" } } diff --git a/public/icons/angle-left-solid.svg b/public/icons/angle-left-solid.svg new file mode 100644 index 0000000..f6ebf96 --- /dev/null +++ b/public/icons/angle-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/door-open-solid-white.svg b/public/icons/door-open-solid-white.svg new file mode 100644 index 0000000..e08fe86 --- /dev/null +++ b/public/icons/door-open-solid-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/folder-solid.svg b/public/icons/folder-solid.svg index 4253902..a41ee6a 100644 --- a/public/icons/folder-solid.svg +++ b/public/icons/folder-solid.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/icons/folder-tree-solid.svg b/public/icons/folder-tree-solid.svg deleted file mode 100644 index f91740c..0000000 --- a/public/icons/folder-tree-solid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/pen-solid.svg b/public/icons/pen-solid.svg new file mode 100644 index 0000000..3f53cee --- /dev/null +++ b/public/icons/pen-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/people-group-solid.svg b/public/icons/people-group-solid.svg deleted file mode 100644 index 16e3fd8..0000000 --- a/public/icons/people-group-solid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/source/Container/Services.ts b/source/Container/Services.ts index cdc26d5..7c5b4ad 100644 --- a/source/Container/Services.ts +++ b/source/Container/Services.ts @@ -1,15 +1,16 @@ import {Environment} from "../Environment"; import {Container} from "./Container"; import {DatabaseConnection} from "../Database/DatabaseConnection"; -import {getLogger, configure, Logger} from "log4js"; +import {configure, getLogger, Logger} from "log4js"; import path from "node:path"; import {GroupRepository} from "../Repositories/GroupRepository"; import {PlaydateRepository} from "../Repositories/PlaydateRepository"; -import {GuildEmojiRoleManager} from "discord.js"; import {GroupConfigurationRepository} from "../Repositories/GroupConfigurationRepository"; import {DiscordClient} from "../Discord/DiscordClient"; import {IconCache} from "../Icons/IconCache"; import {EventHandler} from "../Events/EventHandler"; +import {InteractionRouter} from "../Discord/InteractionRouter"; +import Commands from "../Discord/Commands/Commands"; export enum ServiceHint { App, @@ -21,19 +22,35 @@ export class Services { const env = new Environment(); env.setup(); container.set(env); + + const logger = this.setupLogger(hint); + container.set(logger, 'logger'); const database = new DatabaseConnection(env.database); container.set(database); - const discordClient = new DiscordClient(env.discord.clientId); + const discordClient = new DiscordClient( + env.discord.clientId, + new InteractionRouter(new Commands(), logger) + ); container.set(discordClient); const iconCache = new IconCache(discordClient); container.set(iconCache); container.set(new EventHandler()); - - // @ts-ignore + this.setupRepositories(container); + } + + private static setupRepositories(container: Container) { + const db = container.get(DatabaseConnection.name); + container.set(new GroupRepository(db)); + container.set(new PlaydateRepository(db, container.get(GroupRepository.name))) + container.set(new GroupConfigurationRepository(db, container.get(GroupRepository.name))) + } + + private static setupLogger(hint: ServiceHint): Logger { + configure({ appenders: { out: { type: "stdout" }, @@ -46,7 +63,7 @@ export class Services { deploy: { appenders: ["out", "deployLogFile"], level: "debug" }, } }) - + let loggername = ''; switch (hint) { case ServiceHint.App: @@ -57,17 +74,6 @@ export class Services { break; } - const logger = getLogger(loggername); - - container.set(logger, 'logger'); - - this.setupRepositories(container); - } - - private static setupRepositories(container: Container) { - const db = container.get(DatabaseConnection.name); - container.set(new GroupRepository(db)); - container.set(new PlaydateRepository(db, container.get(GroupRepository.name))) - container.set(new GroupConfigurationRepository(db, container.get(GroupRepository.name))) + return getLogger(loggername); } } \ No newline at end of file diff --git a/source/Database/DatabaseConnection.ts b/source/Database/DatabaseConnection.ts index 1da0a43..d411a7d 100644 --- a/source/Database/DatabaseConnection.ts +++ b/source/Database/DatabaseConnection.ts @@ -1,22 +1,20 @@ -import {DatabaseEnvironment, Environment} from "../Environment"; +import {DatabaseEnvironment} from "../Environment"; import Sqlite3 from "better-sqlite3"; import Database from "better-sqlite3"; import {Container} from "../Container/Container"; import {Logger} from "log4js"; export class DatabaseConnection { - private static connection: DatabaseConnection; - private database: Sqlite3.Database; - constructor(private readonly env: DatabaseEnvironment) { + constructor(env: DatabaseEnvironment) { this.database = new Database(env.path, { nativeBinding: "node_modules/better-sqlite3/build/Release/better_sqlite3.node", }) this.database.pragma('journal_mode = WAL'); } - public execute(query: string, ...args: any[]): Sqlite3.RunResult { + public execute(query: string, ...args: unknown[]): Sqlite3.RunResult { try { const preparedQuery = this.database.prepare(query); return preparedQuery.run(args); @@ -26,11 +24,11 @@ export class DatabaseConnection { } } - public fetch(query: string, ...args: any[]): Result|undefined { + public fetch(query: string, ...args: unknown[]): Result|undefined { const preparedQuery = this.database.prepare(query); return preparedQuery.get(args); } - public fetchAll(query: string, ...args: any[]): Result[] { + public fetchAll(query: string, ...args: unknown[]): Result[] { const preparedQuery = this.database.prepare(query); return preparedQuery.all(args); } diff --git a/source/Database/DatabaseUpdater.ts b/source/Database/DatabaseUpdater.ts index a9f58fe..a5581c8 100644 --- a/source/Database/DatabaseUpdater.ts +++ b/source/Database/DatabaseUpdater.ts @@ -22,7 +22,7 @@ export class DatabaseUpdater { } private ensureTableColumns(definition: DatabaseDefinition) { - const DBSQLColumns = this.database.fetchAll<{}, {name: string, type: string}>( + const DBSQLColumns = this.database.fetchAll( `PRAGMA table_info("${definition.name}")` ); @@ -76,6 +76,6 @@ export class DatabaseUpdater { }).join(', '); const sql = `CREATE TABLE IF NOT EXISTS ${definition.name} (${columnsSQL})`; - const result = this.database.execute(sql); + this.database.execute(sql); } } \ No newline at end of file diff --git a/source/Discord/CommandDeployer.ts b/source/Discord/CommandDeployer.ts index 8635562..b0e3764 100644 --- a/source/Discord/CommandDeployer.ts +++ b/source/Discord/CommandDeployer.ts @@ -10,7 +10,7 @@ export class CommandDeployer { } public async deployAvailableServers() { - const commandInfos = []; + const commandInfos: object[] = []; this.client.Commands.allCommands.forEach((command) => { commandInfos.push(command.definition().toJSON()) }) @@ -27,11 +27,10 @@ export class CommandDeployer { this.logger.log(`Started refreshing ${commandInfos.length} application (/) commands for ${serverId}.`); // The put method is used to fully refresh all commands in the guild with the current set - const data = await this.client.RESTClient.put( - Routes.applicationGuildCommands(this.client.ApplicationId, serverId), - { body: commandInfos }, + await this.client.RESTClient.put( + Routes.applicationGuildCommands(this.client.ApplicationId, serverId), + { body: commandInfos }, ); - this.logger.log(`Successfully reloaded ${commandInfos.length} application (/) commands for ${serverId}.`); } } \ No newline at end of file diff --git a/source/Discord/CommandPartials/GroupSelection.ts b/source/Discord/CommandPartials/GroupSelection.ts index 4804428..8ecbdd8 100644 --- a/source/Discord/CommandPartials/GroupSelection.ts +++ b/source/Discord/CommandPartials/GroupSelection.ts @@ -1,9 +1,7 @@ import { AutocompleteInteraction, - ChatInputCommandInteraction, CommandInteraction, GuildMember, SlashCommandIntegerOption, - SlashCommandStringOption } from "discord.js"; import {Container} from "../../Container/Container"; import {GroupRepository} from "../../Repositories/GroupRepository"; @@ -22,7 +20,7 @@ export class GroupSelection { public static async handleAutocomplete(interaction: AutocompleteInteraction, onlyLeaders: boolean = false): Promise { const value = interaction.options.getFocused(); const repo = Container.get(GroupRepository.name); - let groups = repo.findGroupsByMember(interaction.member, onlyLeaders); + const groups = repo.findGroupsByMember(interaction.member, onlyLeaders); await interaction.respond( groups .filter((group) => group.name.startsWith(value)) diff --git a/source/Discord/Commands/Command.ts b/source/Discord/Commands/Command.ts index 72043e4..0296f6c 100644 --- a/source/Discord/Commands/Command.ts +++ b/source/Discord/Commands/Command.ts @@ -1,5 +1,4 @@ import {ChatInputCommandInteraction, Interaction, SlashCommandBuilder} from "discord.js"; -import Commands from "./Commands"; export interface Command { definition(): SlashCommandBuilder; diff --git a/source/Discord/Commands/Groups.ts b/source/Discord/Commands/Groups.ts index 6b07693..e025da5 100644 --- a/source/Discord/Commands/Groups.ts +++ b/source/Discord/Commands/Groups.ts @@ -1,12 +1,12 @@ import { - SlashCommandBuilder, - ChatInputCommandInteraction, + SlashCommandBuilder, + ChatInputCommandInteraction, MessageFlags, InteractionReplyOptions, GuildMember, EmbedBuilder, AutocompleteInteraction, - roleMention, time, userMention + roleMention, time, userMention, GuildMemberRoleManager } from "discord.js"; import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command"; import {GroupModel} from "../../Models/GroupModel"; @@ -19,265 +19,264 @@ import {GroupConfigurationRenderer} from "../../Groups/GroupConfigurationRendere import {GroupConfigurationHandler} from "../../Groups/GroupConfigurationHandler"; import {GroupConfigurationTransformers} from "../../Groups/GroupConfigurationTransformers"; import {GroupConfigurationRepository} from "../../Repositories/GroupConfigurationRepository"; -import {IconCache} from "../../Icons/IconCache"; import {PlaydateRepository} from "../../Repositories/PlaydateRepository"; +import {Nullable} from "../../types/Nullable"; export class GroupCommand implements Command, ChatInteractionCommand, AutocompleteCommand { - private static GOODBYE_MESSAGES: string[] = [ - 'Sad to see you go.', - 'May your next adventure be fruitful.', - 'I hope, I served you well.', - 'I wish you and your group good luck on your next adventures.', - ] - - private static INVALID_CHARACTER_SEQUENCES: string[] = [ - "http://", - "https://" - ] - - definition(): SlashCommandBuilder { - // @ts-ignore - return new SlashCommandBuilder() - .setName('groups') - .setDescription(`Manages groups`) - .addSubcommand(create => - create.setName("create") - .setDescription("Creates a new group, with executing user being the leader") - .addStringOption((option) => - option.setName("name") - .setDescription("Defines the name for the group.") - .setRequired(true) - .setMaxLength(64) - ) - .addRoleOption((builder) => - builder.setName("role") - .setDescription("Defines the role, where all the members are located in.") - .setRequired(true) - ) - ) - .addSubcommand(listCommand => - listCommand - .setName("list") - .setDescription("Displays the groups you are apart of.") - ) - .addSubcommand(command => command - .setName('config') - .setDescription("Starts the config manager for the group.") - .addIntegerOption(GroupSelection.createOptionSetup()) - ) - .addSubcommand(command => command - .setName("remove") - .setDescription("Deletes a group you are the leader for.") - .addIntegerOption(GroupSelection.createOptionSetup()) - ) - .addSubcommand(command => command - .setName("transfer") - .setDescription("Transfers leadership of a group to a different person") - .addIntegerOption(GroupSelection.createOptionSetup) - .addUserOption(option => option - .setName("target") - .setDescription("The member, that is the new leader") - .setRequired(true) - ) - ); - } + private static GOODBYE_MESSAGES: string[] = [ + 'Sad to see you go.', + 'May your next adventure be fruitful.', + 'I hope, I served you well.', + 'I wish you and your group good luck on your next adventures.', + ] - async execute(interaction: ChatInputCommandInteraction): Promise { - switch (interaction.options.getSubcommand()) { - case "create": - this.create(interaction); - break; - case "list": - this.list(interaction); - break; - case "remove": - await this.remove(interaction); - break; - case "config": - await this.runConfigurator(interaction); - break; - case "transfer": - await this.transferLeadership(interaction); - break; - default: - throw new Error("Unsupported command"); - } - - return Promise.resolve(); - } - - private create(interaction: ChatInputCommandInteraction): void { - const name = interaction.options.getString("name") ?? ''; - const role = interaction.options.getRole("role", true); - - if (role.id === interaction.guildId) { - throw new UserError("Creating a group for everyone is not permitted!"); - } - - if (!interaction.member?.roles.cache.has(role?.id) ?? true) { - throw new UserError( - "You are not part of the role, you try to create a group for.", - "Add yourself to the group or ask your admin to do so." - ); - } - - const validName = this.validateGroupName(name); - if (name !== true) { - throw new UserError(`Your group name contains one or more invalid character sequences: ${validName}`) - } - - const group: GroupModel = { - id: -1, - name: name, - leader: { - server: interaction.guildId ?? '', - memberid: interaction.member?.user.id ?? '' - }, - role: { - server: interaction.guildId ?? '', - roleid: role?.id ?? '' - } - } - - Container.get(GroupRepository.name).create(group); - - interaction.reply({content: `:white_check_mark: Created group \`${name}\``, flags: MessageFlags.Ephemeral }) - } - - private validateGroupName(name: string): true|string{ - const lowercaseName = name.toLowerCase(); - for (let invalidcharactersequence of GroupCommand.INVALID_CHARACTER_SEQUENCES) { - if (!lowercaseName.includes(invalidcharactersequence)) { - continue - } - - return invalidcharactersequence - } - return true; - } + private static INVALID_CHARACTER_SEQUENCES: string[] = [ + "http://", + "https://" + ] - private list(interaction: ChatInputCommandInteraction) { - const repo = Container.get(GroupRepository.name); - const groups = repo.findGroupsByMember(interaction.member); - - const playdateRepo = Container.get(PlaydateRepository.name); - - const iconCache = Container.get(IconCache.name); - - const embed = new EmbedBuilder() - .setTitle("Your groups on this server:") - .setFields( - groups.map(group => { - const nextPlaydate = playdateRepo.getNextPlaydateForGroup(group); - - const values = [ - `Role: ${iconCache.getEmoji("people_group_solid")} ${roleMention(group.role.roleid)}`, - `Leader/GM: ${userMention(group.leader.memberid)}` - ]; - - if (nextPlaydate) { - values.push( - `Next Playdate: ${iconCache.getEmoji("calendar_days_solid")} ${time(nextPlaydate.from_time, "F")}` - ) - } - - return { - name: group.name, - value: values.join("\n") - } - }) - ) - - const reply: InteractionReplyOptions = { - embeds: [ - embed - ], - allowedMentions: { roles: [] }, - flags: MessageFlags.Ephemeral - } - - interaction.reply(reply); - } - - private async remove(interaction: ChatInputCommandInteraction) { - const group = GroupSelection.getGroup(interaction); - - const repo = Container.get(GroupRepository.name); - if (group.leader.memberid != interaction.member?.user.id) { - throw new UserError("Can't remove group. You are not the leader."); - } - - repo.deleteGroup(group); - - const embed = new EmbedBuilder() - .setTitle("Group deleted.") - .setDescription( - `:x: Deleted \`${group.name}\`. ${ArrayUtils.chooseRandom(GroupCommand.GOODBYE_MESSAGES)}` - ) - - await interaction.reply({ - embeds: [ - embed - ], - flags: MessageFlags.Ephemeral, - }) - } - - async handleAutocomplete(interaction: AutocompleteInteraction): Promise { - const option = interaction.options.getFocused(true); - if (option.name == "group") { - await GroupSelection.handleAutocomplete(interaction, true); - return; - } - } - - private async runConfigurator(interaction: ChatInputCommandInteraction) { - const group = GroupSelection.getGroup(interaction); - - const configurationRenderer = new GroupConfigurationRenderer( - new GroupConfigurationHandler( - Container.get(GroupConfigurationRepository.name), - group - ), - new GroupConfigurationTransformers(), + definition(): SlashCommandBuilder { + // @ts-expect-error Slash command expects more than needed. + return new SlashCommandBuilder() + .setName('groups') + .setDescription(`Manages groups`) + .addSubcommand(create => + create.setName("create") + .setDescription("Creates a new group, with executing user being the leader") + .addStringOption((option) => + option.setName("name") + .setDescription("Defines the name for the group.") + .setRequired(true) + .setMaxLength(64) + ) + .addRoleOption((builder) => + builder.setName("role") + .setDescription("Defines the role, where all the members are located in.") + .setRequired(true) + ) + ) + .addSubcommand(listCommand => + listCommand + .setName("list") + .setDescription("Displays the groups you are apart of.") + ) + .addSubcommand(command => command + .setName('config') + .setDescription("Starts the config manager for the group.") + .addIntegerOption(GroupSelection.createOptionSetup()) + ) + .addSubcommand(command => command + .setName("remove") + .setDescription("Deletes a group you are the leader for.") + .addIntegerOption(GroupSelection.createOptionSetup()) + ) + .addSubcommand(command => command + .setName("transfer") + .setDescription("Transfers leadership of a group to a different person") + .addIntegerOption(GroupSelection.createOptionSetup) + .addUserOption(option => option + .setName("target") + .setDescription("The member, that is the new leader") + .setRequired(true) ) - - await configurationRenderer.setup(interaction); + ); + } + + async execute(interaction: ChatInputCommandInteraction): Promise { + switch (interaction.options.getSubcommand()) { + case "create": + this.create(interaction); + break; + case "list": + this.list(interaction); + break; + case "remove": + await this.remove(interaction); + break; + case "config": + await this.runConfigurator(interaction); + break; + case "transfer": + await this.transferLeadership(interaction); + break; + default: + throw new Error("Unsupported command"); } - private async transferLeadership(interaction: ChatInputCommandInteraction) { - const group = GroupSelection.getGroup(interaction); + return Promise.resolve(); + } - const repo = Container.get(GroupRepository.name); - if (group.leader.memberid != interaction.member?.user.id) { - throw new UserError( - "Can't transfer leadership. You are not the leader." - ); - } - - const newLeader = interaction.options.getMember("target", true); - if (!newLeader.roles.cache.has(group.role.roleid)) { - throw new UserError( - "Can't transfer leadership: The target member is not part of your group.", - "Add the user to the role this group is part in or ask your server admin to do it." - ); - } - - group.leader.memberid = newLeader.id - repo.update(group); + private create(interaction: ChatInputCommandInteraction): void { + const name = interaction.options.getString("name") ?? ''; + const role = interaction.options.getRole("role", true); + if (role.id === interaction.guildId) { + throw new UserError("Creating a group for everyone is not permitted!"); + } - const embed = new EmbedBuilder() - .setTitle("Leadership transfered") - .setDescription( - `Leadership was successfully transfered to ${userMention(newLeader.user.id)}` + if (!(>interaction.member?.roles)?.cache.has(role?.id)) { + throw new UserError( + "You are not part of the role, you try to create a group for.", + "Add yourself to the group or ask your admin to do so." + ); + } + + const validName = this.validateGroupName(name); + // @ts-expect-error Is correct, since the valid name can return either true or a string and the error should only be thrown if it's a string. + if (name !== true) { + throw new UserError(`Your group name contains one or more invalid character sequences: ${validName}`) + } + + const group: GroupModel = { + id: -1, + name: name, + leader: { + server: interaction.guildId ?? '', + memberid: interaction.member?.user.id ?? '' + }, + role: { + server: interaction.guildId ?? '', + roleid: role?.id ?? '' + } + } + + Container.get(GroupRepository.name).create(group); + + interaction.reply({content: `:white_check_mark: Created group \`${name}\``, flags: MessageFlags.Ephemeral }) + } + + private validateGroupName(name: string): true|string{ + const lowercaseName = name.toLowerCase(); + for (const invalidcharactersequence of GroupCommand.INVALID_CHARACTER_SEQUENCES) { + if (!lowercaseName.includes(invalidcharactersequence)) { + continue + } + + return invalidcharactersequence + } + return true; + } + + private list(interaction: ChatInputCommandInteraction) { + const repo = Container.get(GroupRepository.name); + const groups = repo.findGroupsByMember(interaction.member); + + const playdateRepo = Container.get(PlaydateRepository.name); + + const embed = new EmbedBuilder() + .setTitle("Your groups on this server:") + .setFields( + groups.map(group => { + const nextPlaydate = playdateRepo.getNextPlaydateForGroup(group); + + const values = [ + `Role: ${roleMention(group.role.roleid)}`, + `Leader/GM: ${userMention(group.leader.memberid)}` + ]; + + if (nextPlaydate) { + values.push( + `Next Playdate: ${time(nextPlaydate.from_time, "F")}` ) + } - await interaction.reply({ - embeds: [ - embed - ], - flags: MessageFlags.Ephemeral, + return { + name: group.name, + value: values.join("\n") + } }) + ) + + const reply: InteractionReplyOptions = { + embeds: [ + embed + ], + allowedMentions: { roles: [] }, + flags: MessageFlags.Ephemeral } + + interaction.reply(reply); + } + + private async remove(interaction: ChatInputCommandInteraction) { + const group = GroupSelection.getGroup(interaction); + + const repo = Container.get(GroupRepository.name); + if (group.leader.memberid != interaction.member?.user.id) { + throw new UserError("Can't remove group. You are not the leader."); + } + + repo.deleteGroup(group); + + const embed = new EmbedBuilder() + .setTitle("Group deleted.") + .setDescription( + `:x: Deleted \`${group.name}\`. ${ArrayUtils.chooseRandom(GroupCommand.GOODBYE_MESSAGES)}` + ) + + await interaction.reply({ + embeds: [ + embed + ], + flags: MessageFlags.Ephemeral, + }) + } + + async handleAutocomplete(interaction: AutocompleteInteraction): Promise { + const option = interaction.options.getFocused(true); + if (option.name == "group") { + await GroupSelection.handleAutocomplete(interaction, true); + return; + } + } + + private async runConfigurator(interaction: ChatInputCommandInteraction) { + const group = GroupSelection.getGroup(interaction); + + const configurationRenderer = new GroupConfigurationRenderer( + new GroupConfigurationHandler( + Container.get(GroupConfigurationRepository.name), + group + ), + new GroupConfigurationTransformers(), + ) + + await configurationRenderer.setup(interaction); + } + + private async transferLeadership(interaction: ChatInputCommandInteraction) { + const group = GroupSelection.getGroup(interaction); + + const repo = Container.get(GroupRepository.name); + if (group.leader.memberid != interaction.member?.user.id) { + throw new UserError( + "Can't transfer leadership. You are not the leader." + ); + } + + const newLeader = interaction.options.getMember("target"); + if (!newLeader.roles.cache.has(group.role.roleid)) { + throw new UserError( + "Can't transfer leadership: The target member is not part of your group.", + "Add the user to the role this group is part in or ask your server admin to do it." + ); + } + + group.leader.memberid = newLeader.id + repo.update(group); + + + const embed = new EmbedBuilder() + .setTitle("Leadership transferred") + .setDescription( + `Leadership was successfully transferred to ${userMention(newLeader.user.id)}` + ) + + await interaction.reply({ + embeds: [ + embed + ], + flags: MessageFlags.Ephemeral, + }) + } } \ No newline at end of file diff --git a/source/Discord/Commands/HelloWorldCommand.ts b/source/Discord/Commands/HelloWorldCommand.ts index 7191c47..c023c50 100644 --- a/source/Discord/Commands/HelloWorldCommand.ts +++ b/source/Discord/Commands/HelloWorldCommand.ts @@ -1,4 +1,4 @@ -import {SlashCommandBuilder, Interaction, CommandInteraction} from "discord.js"; +import {SlashCommandBuilder, CommandInteraction} from "discord.js"; import {Command} from "./Command"; export class HelloWorldCommand implements Command { @@ -6,7 +6,7 @@ export class HelloWorldCommand implements Command { 'Hello :)', 'zzzZ... ZzzzZ... huh? I am awake. I am awake!', 'Roll for initiative!', - 'I was an adventurerer like you...', + 'I was an adventurer like you...', 'Hello :) How are you?', ] diff --git a/source/Discord/Commands/Playdates.ts b/source/Discord/Commands/Playdates.ts index 7186493..51b1faa 100644 --- a/source/Discord/Commands/Playdates.ts +++ b/source/Discord/Commands/Playdates.ts @@ -1,22 +1,16 @@ import { SlashCommandBuilder, - Interaction, CommandInteraction, AutocompleteInteraction, - GuildMember, - EmbedBuilder, MessageFlags, ChatInputCommandInteraction, ModalSubmitFields, time, User + EmbedBuilder, MessageFlags, ChatInputCommandInteraction, time } from "discord.js"; import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command"; import {Container} from "../../Container/Container"; -import {GroupRepository} from "../../Repositories/GroupRepository"; import {GroupSelection} from "../CommandPartials/GroupSelection"; -import {setFlagsFromString} from "node:v8"; import {UserError} from "../UserError"; -import Playdate from "../../Database/tables/Playdate"; import {PlaydateModel} from "../../Models/PlaydateModel"; import {PlaydateRepository} from "../../Repositories/PlaydateRepository"; import {GroupModel} from "../../Models/GroupModel"; -import playdate from "../../Database/tables/Playdate"; export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInteractionCommand { static REGEX = [ @@ -24,7 +18,7 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter ] definition(): SlashCommandBuilder { - // @ts-ignore + // @ts-expect-error Command builder is improperly marked as incomplete. return new SlashCommandBuilder() .setName("playdates") .setDescription("Manage your playdates") @@ -106,7 +100,7 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter to_time: new Date(toDate), } - const id = playdateRepo.create(playdate); + playdateRepo.create(playdate); const embed = new EmbedBuilder() .setTitle("Created a play-date.") diff --git a/source/Discord/DiscordClient.ts b/source/Discord/DiscordClient.ts index 89ba606..c0d36ee 100644 --- a/source/Discord/DiscordClient.ts +++ b/source/Discord/DiscordClient.ts @@ -2,142 +2,66 @@ import { Client, GatewayIntentBits, Events, - Interaction, - ChatInputCommandInteraction, - MessageFlags, - Activity, - ActivityType, REST, inlineCode + ActivityType, REST } from "discord.js"; import Commands from "./Commands/Commands"; import {Container} from "../Container/Container"; import {Logger} from "log4js"; -import {UserError} from "./UserError"; +import {InteractionRouter} from "./InteractionRouter"; export class DiscordClient { private readonly client: Client; - private commands: Commands; - private readonly restClient: REST; - - public get Client (): Client { + + public get Client(): Client { return this.client; } - - public get Commands(): Commands { - return this.commands + + public get Commands(): Commands { + return this.router.commands } - + public get RESTClient(): REST { return this.restClient; } - + public get ApplicationId(): string { return this.applicationId; } - + constructor( - private readonly applicationId: string + private readonly applicationId: string, + private readonly router: InteractionRouter, + private readonly restClient: REST = new REST() ) { this.client = new Client({ intents: [GatewayIntentBits.Guilds] }) - - this.commands = new Commands(); - this.restClient = new REST(); } - + applyEvents() { this.client.once(Events.ClientReady, () => { + if (!this.client.user) { + return; + } + Container.get("logger").info(`Ready! Logged in as ${this.client.user.tag}`); this.client.user.setActivity('your PnP playdates', { type: ActivityType.Watching, }); }) - + this.client.on(Events.GuildAvailable, () => { Container.get("logger").info("Joined Guild?") }) - - this.client.on(Events.InteractionCreate, async (interaction: Interaction) => { - const method = this.findCommandMethod(interaction); - if (!method) { - Container.get("logger").error(`Could not find method for '${interaction.commandName}'`); - return; - } - - await method(); - }) + + this.client.on(Events.InteractionCreate, this.router.route.bind(this.router)); } - + connect(token: string) { this.client.login(token); } - + connectRESTClient(token: string) { this.restClient.setToken(token); } - - private findCommandMethod(interaction: Interaction) { - if (interaction.isChatInputCommand()) { - const command = this.commands.getCommand(interaction.commandName); - - if (!command) { - return null; - } - - if (!('execute' in command)) { - return null; - } - - return async () => { - Container.get("logger").debug(`Found chat command ${interaction.commandName}: running...`); - - try { - await command.execute(interaction) - } - catch (e: any) { - Container.get("logger").error(e) - - let userMessage = ":x: There was an error while executing this command!"; - if (e.constructor.name === UserError.name) { - userMessage = `:x: \`${e.message}\` - Please validate your request!` - if (e.tryInstead) { - userMessage += ` - -You can try the following: -${inlineCode(e.tryInstead)}` - } - } - if (interaction.replied || interaction.deferred) { - await interaction.followUp({ content: userMessage, flags: MessageFlags.Ephemeral }); - } else { - await interaction.reply({ content: userMessage, flags: MessageFlags.Ephemeral }); - } - } - } - } - - if (interaction.isAutocomplete()) { - const command = this.commands.getCommand(interaction.commandName); - - if (!command) { - return null; - } - - if (!('handleAutocomplete' in command)) { - return null; - } - - return async () => { - Container.get("logger").debug(`Found command ${interaction.commandName} for autocomplete: handling...`); - - try { - await command.handleAutocomplete(interaction); - } catch (e: any) { - Container.get('logger').error(e); - } - } - } - - return null; - } } \ No newline at end of file diff --git a/source/Discord/InteractionRouter.ts b/source/Discord/InteractionRouter.ts new file mode 100644 index 0000000..d40deec --- /dev/null +++ b/source/Discord/InteractionRouter.ts @@ -0,0 +1,109 @@ +import { + AutocompleteInteraction, + ChatInputCommandInteraction, + inlineCode, + Interaction, + MessageFlags, +} from "discord.js"; +import Commands from "./Commands/Commands"; +import {Logger} from "log4js"; +import {UserError} from "./UserError"; +import {Container} from "../Container/Container"; + +enum InteractionRoutingType { + Unrouted, + Command, + AutoComplete, +} + +export class InteractionRouter { + constructor( + public readonly commands: Commands, + public readonly logger: Logger + ) { + } + + async route(interaction: Interaction) { + const interactionType = this.findInteractionType(interaction); + + switch (interactionType) { + case InteractionRoutingType.Unrouted: + this.logger.debug("Unroutable interaction found...") + break; + case InteractionRoutingType.Command: + await this.handleCommand(interaction); + break; + case InteractionRoutingType.AutoComplete: + await this.handleAutocomplete(interaction) + break; + } + } + + private findInteractionType(interaction: Interaction): InteractionRoutingType { + if (interaction.isChatInputCommand()) { + return InteractionRoutingType.Command; + } + + if (interaction.isAutocomplete()) { + return InteractionRoutingType.AutoComplete; + } + + return InteractionRoutingType.Unrouted; + } + + private async handleCommand(interaction: ChatInputCommandInteraction) { + try { + const command = this.commands.getCommand(interaction.commandName); + if (!command) { + throw new UserError(`Requested command not found.`); + } + + if (!('execute' in command)) { + throw new UserError(`Requested command is not setup for a chat command.`); + } + + this.logger.debug(`Found chat command ${interaction.commandName}: running...`); + + await command.execute?.call(command, interaction); + } catch (e: any) { + this.logger.error(e) + + let userMessage = ":x: There was an error while executing this command!"; + if (e.constructor.name === UserError.name) { + userMessage = `:x: \`${e.message}\` - Please validate your request!` + if (e.tryInstead) { + userMessage += ` + +You can try the following: +${inlineCode(e.tryInstead)}` + } + } + if (interaction.replied || interaction.deferred) { + await interaction.followUp({ content: userMessage, flags: MessageFlags.Ephemeral }); + } else { + await interaction.reply({ content: userMessage, flags: MessageFlags.Ephemeral }); + } + } + } + + private async handleAutocomplete(interaction: AutocompleteInteraction) { + const command = this.commands.getCommand(interaction.commandName); + + if (!command) { + return null; + } + + if (!('handleAutocomplete' in command)) { + return null; + } + + Container.get("logger").debug(`Found command ${interaction.commandName} for autocomplete: handling...`); + + try { + await command.handleAutocomplete?.call(command, interaction); + } catch (e: unknown) { + Container.get('logger').error(e); + } + + } +} \ No newline at end of file diff --git a/source/Events/ElementCreatedEvent.ts b/source/Events/ElementCreatedEvent.ts index 9f2f149..0153f68 100644 --- a/source/Events/ElementCreatedEvent.ts +++ b/source/Events/ElementCreatedEvent.ts @@ -4,7 +4,7 @@ export class ElementCreatedEvent { constructor( public readonly tableName: string, public readonly instanceValues: Partial, - public readonly instanceId: number + public readonly instanceId: number|bigint ) { } } \ No newline at end of file diff --git a/source/Events/EventHandler.ts b/source/Events/EventHandler.ts index cbbe1fc..6615acf 100644 --- a/source/Events/EventHandler.ts +++ b/source/Events/EventHandler.ts @@ -1,6 +1,5 @@ import cron from "node-cron"; -import {Nullable} from "../types/Nullable"; -import {Class, ClassNamed} from "../types/Class"; +import {Class} from "../types/Class"; export type EventConfiguration = { name: string, diff --git a/source/Events/Handlers/SendCreatedNotification.ts b/source/Events/Handlers/SendCreatedNotification.ts index 73bd302..a258816 100644 --- a/source/Events/Handlers/SendCreatedNotification.ts +++ b/source/Events/Handlers/SendCreatedNotification.ts @@ -1,12 +1,10 @@ import {ElementCreatedEvent} from "../ElementCreatedEvent"; -import {DefaultHandler} from "../DefaultEvents"; import {PlaydateModel} from "../../Models/PlaydateModel"; import PlaydateTableConfiguration from "../../Database/tables/Playdate"; import {EmbedBuilder, roleMention, time} from "discord.js"; import {ArrayUtils} from "../../Utilities/ArrayUtils"; import {GroupConfigurationHandler} from "../../Groups/GroupConfigurationHandler"; import {Container} from "../../Container/Container"; -import {GroupConfigurationRenderer} from "../../Groups/GroupConfigurationRenderer"; import {GroupConfigurationRepository} from "../../Repositories/GroupConfigurationRepository"; import {DiscordClient} from "../../Discord/DiscordClient"; @@ -37,7 +35,7 @@ export async function sendCreatedNotificationEventHandler(event: ElementCreatedE return; } - const channel = await Container.get(DiscordClient.name).Client.channels.fetch(targetChannel) + const channel = await Container.get(DiscordClient.name).Client.channels.fetch(targetChannel) if (!channel) { return; } diff --git a/source/Events/ReminderEvent.ts b/source/Events/ReminderEvent.ts index c09ec64..1dcfbf8 100644 --- a/source/Events/ReminderEvent.ts +++ b/source/Events/ReminderEvent.ts @@ -1,13 +1,12 @@ -import {CronExpression, Event, EventConfiguration, TimedEvent} from "./EventHandler"; +import {EventConfiguration, TimedEvent} from "./EventHandler"; import {Container} from "../Container/Container"; -import Playdate from "../Database/tables/Playdate"; import {PlaydateRepository} from "../Repositories/PlaydateRepository"; import {GroupConfigurationHandler} from "../Groups/GroupConfigurationHandler"; import {GroupConfigurationRepository} from "../Repositories/GroupConfigurationRepository"; import {PlaydateModel} from "../Models/PlaydateModel"; import {ChannelId} from "../types/DiscordTypes"; import {DiscordClient} from "../Discord/DiscordClient"; -import {EmbedBuilder, MessageFlags, roleMention, time} from "discord.js"; +import {EmbedBuilder, roleMention, time} from "discord.js"; import {ArrayUtils} from "../Utilities/ArrayUtils"; export class ReminderEvent implements TimedEvent { @@ -27,9 +26,9 @@ export class ReminderEvent implements TimedEvent { name: "Reminders", } - cronExpression: CronExpression = "0 9 * * *" + cronExpression: string = "0 9 * * *" - private groupConfigurationRepository: GroupConfigurationRepository + private readonly groupConfigurationRepository: GroupConfigurationRepository private playdateRepository: PlaydateRepository private discordClient: DiscordClient @@ -72,13 +71,13 @@ export class ReminderEvent implements TimedEvent { return Promise.resolve(); } - return this.sendReminder(playdate, targetChannel, config.locale); + return this.sendReminder(playdate, targetChannel); }, this) await Promise.all(promises); } - private async sendReminder(playdate: PlaydateModel, targetChannel: ChannelId, locale: Intl.Locale) { + private async sendReminder(playdate: PlaydateModel, targetChannel: ChannelId) { if (!playdate.group) { return; } diff --git a/source/Groups/GroupConfigurationHandler.ts b/source/Groups/GroupConfigurationHandler.ts index f18dcc7..95dc9e8 100644 --- a/source/Groups/GroupConfigurationHandler.ts +++ b/source/Groups/GroupConfigurationHandler.ts @@ -2,10 +2,11 @@ import {RuntimeGroupConfiguration} from "./RuntimeGroupConfiguration"; import {GroupConfigurationRepository} from "../Repositories/GroupConfigurationRepository"; import {GroupModel} from "../Models/GroupModel"; import {GroupConfigurationResult, GroupConfigurationTransformers} from "./GroupConfigurationTransformers"; -// @ts-ignore +// @ts-expect-error set-path is provided import setPath from 'object-path-set'; import deepmerge from "deepmerge"; import {Nullable} from "../types/Nullable"; +// @ts-expect-error Any is fine import {isPlainObject} from "is-plain-object"; export class GroupConfigurationHandler { diff --git a/source/Groups/GroupConfigurationRenderer.ts b/source/Groups/GroupConfigurationRenderer.ts index 750e5c0..524f1f0 100644 --- a/source/Groups/GroupConfigurationRenderer.ts +++ b/source/Groups/GroupConfigurationRenderer.ts @@ -2,41 +2,26 @@ import {GroupConfigurationTransformers, TransformerType} from "./GroupConfigurat import {GroupConfigurationHandler} from "./GroupConfigurationHandler"; import { ActionRowBuilder, - AnyComponentBuilder, AnySelectMenuInteraction, - APISelectMenuComponent, + AnySelectMenuInteraction, ButtonBuilder, ButtonStyle, channelMention, - ChannelSelectMenuBuilder, ChannelSelectMenuInteraction, - ChannelType, - ChatInputCommandInteraction, codeBlock, - EmbedBuilder, inlineCode, - InteractionCallbackResponse, - InteractionEditReplyOptions, + ChannelSelectMenuBuilder, ChannelType, + ChatInputCommandInteraction, EmbedBuilder, inlineCode, Interaction, InteractionReplyOptions, InteractionUpdateOptions, italic, MessageFlags, - SelectMenuBuilder, StringSelectMenuBuilder, - StringSelectMenuOptionBuilder, TextBasedChannel, - UserSelectMenuBuilder + StringSelectMenuOptionBuilder, UserSelectMenuBuilder } from "discord.js"; import {Logger} from "log4js"; import {Container} from "../Container/Container"; import {Nullable} from "../types/Nullable"; -import GroupConfiguration from "../Database/tables/GroupConfiguration"; import { - BaseSelectMenuBuilder, MentionableSelectMenuBuilder, MessageActionRowComponentBuilder, RoleSelectMenuBuilder } from "@discordjs/builders"; -import {unwatchFile} from "node:fs"; -import {UserError} from "../Discord/UserError"; -import {RuntimeGroupConfiguration} from "./RuntimeGroupConfiguration"; import {ChannelId} from "../types/DiscordTypes"; import {IconCache} from "../Icons/IconCache"; -import {ifError} from "node:assert"; -import {DiscordClient} from "../Discord/DiscordClient"; -import {channel} from "node:diagnostics_channel"; type UIElementCollection = Record; type UIElement = { @@ -104,7 +89,7 @@ export class GroupConfigurationRenderer { let response = await interaction.reply(this.getReplyOptions()); let exit = false; let eventResponse; - const filter = i => i.user.id === interaction.user.id; + const filter = (i: Interaction) => i.user.id === interaction.user.id; do { if (eventResponse) { @@ -117,7 +102,7 @@ export class GroupConfigurationRenderer { filter: filter, time: 60_000 }); - } catch (e) { + } catch (_: unknown) { break; } @@ -139,7 +124,7 @@ export class GroupConfigurationRenderer { } if (eventResponse.customId.startsWith(GroupConfigurationRenderer.SETVALUE_COMMAND)) { - this.handleSelection(eventResponse); + this.handleSelection(eventResponse); continue; } @@ -150,7 +135,7 @@ export class GroupConfigurationRenderer { await eventResponse.update( this.getReplyOptions() ); - } catch (e) { + } catch (_) { } await eventResponse.deleteReply(); @@ -170,6 +155,7 @@ export class GroupConfigurationRenderer { private getReplyOptions(): InteractionUpdateOptions & InteractionReplyOptions & { withResponse: true } { const embed = this.createEmbed(); + const icons = Container.get(IconCache.name); embed.setAuthor({ name: "/ " + this.breadcrumbs.join(" / ") }); @@ -177,7 +163,8 @@ export class GroupConfigurationRenderer { const exitButton = new ButtonBuilder() .setLabel("Exit") .setStyle(ButtonStyle.Danger) - .setCustomId("exit"); + .setCustomId("exit") + .setEmoji(icons.get("door_open_solid_white") ?? ''); const actionrow = new ActionRowBuilder() @@ -185,7 +172,8 @@ export class GroupConfigurationRenderer { const backButton = new ButtonBuilder() .setLabel("Back") .setStyle(ButtonStyle.Secondary) - .setCustomId(GroupConfigurationRenderer.MOVEBACK_COMMAND); + .setCustomId(GroupConfigurationRenderer.MOVEBACK_COMMAND) + .setEmoji(icons.get("angle_left_solid") ?? ''); actionrow.addComponents(backButton) } @@ -201,7 +189,7 @@ export class GroupConfigurationRenderer { } private createEmbed(): EmbedBuilder { - const {currentCollection, currentElement} = this.findCurrentUI(); + const {currentElement} = this.findCurrentUI(); if (currentElement === null) { return new EmbedBuilder() @@ -224,7 +212,7 @@ export class GroupConfigurationRenderer { private getCurrentValueAsUI(): string { const path = this.breadcrumbs.join("."); - let value = this.configurationHandler.getConfigurationByPath(path); + const value = this.configurationHandler.getConfigurationByPath(path); if (value === undefined) return italic("None"); @@ -263,7 +251,7 @@ export class GroupConfigurationRenderer { if (currentElement?.isConfiguration ?? false) { return [ new ActionRowBuilder() - .addComponents(this.getSelectForBreadcrumbs(currentElement)) + .addComponents(this.getSelectForBreadcrumbs()) ] } @@ -274,13 +262,13 @@ export class GroupConfigurationRenderer { .setLabel(` ${elem.label}`) .setStyle(ButtonStyle.Primary) .setCustomId(GroupConfigurationRenderer.MOVETO_COMMAND + elem.key) - .setEmoji(icons.get("folder_tree_solid") ?? '') + .setEmoji(icons.get(elem.isConfiguration ? 'pen_solid' : "folder_solid") ?? '') ) ) ] } - private getSelectForBreadcrumbs(currentElement: UIElement): ChannelSelectMenuBuilder | MentionableSelectMenuBuilder | RoleSelectMenuBuilder | StringSelectMenuBuilder | UserSelectMenuBuilder { + private getSelectForBreadcrumbs(): ChannelSelectMenuBuilder | MentionableSelectMenuBuilder | RoleSelectMenuBuilder | StringSelectMenuBuilder | UserSelectMenuBuilder { const breadcrumbPath = this.breadcrumbs.join('.') const transformerType = this.transformers.getTransformerType(breadcrumbPath); if (transformerType === undefined) { @@ -300,7 +288,7 @@ export class GroupConfigurationRenderer { .setCustomId(GroupConfigurationRenderer.SETVALUE_COMMAND + breadcrumbPath) .setOptions( options.map(intl => new StringSelectMenuOptionBuilder() - .setLabel(displaynames.of(intl)) + .setLabel(displaynames.of(intl) ?? '') .setValue(intl) ) ) diff --git a/source/Groups/GroupConfigurationTransformers.ts b/source/Groups/GroupConfigurationTransformers.ts index ccb7d2b..afa3904 100644 --- a/source/Groups/GroupConfigurationTransformers.ts +++ b/source/Groups/GroupConfigurationTransformers.ts @@ -1,7 +1,5 @@ import {ChannelId} from "../types/DiscordTypes"; import {GroupConfigurationModel} from "../Models/GroupConfigurationModel"; -import {config} from "dotenv"; -import {transform} from "esbuild"; import {Nullable} from "../types/Nullable"; import {ArrayUtils} from "../Utilities/ArrayUtils"; diff --git a/source/Icons/IconCache.ts b/source/Icons/IconCache.ts index 65830b2..814c408 100644 --- a/source/Icons/IconCache.ts +++ b/source/Icons/IconCache.ts @@ -1,5 +1,6 @@ import {formatEmoji, Routes, Snowflake} from "discord.js"; import {DiscordClient} from "../Discord/DiscordClient"; +import {Nullable} from "../types/Nullable"; export class IconCache { private existingIcons: Map | undefined; @@ -22,7 +23,7 @@ export class IconCache { const id = this.get(iconName); return formatEmoji({ - id: id, + id, name: iconName }); } @@ -47,10 +48,14 @@ export class IconCache { return; } - const existingEmojis: DiscordIconRequest = await this.client.RESTClient.get( + const existingEmojis: Nullable = await this.client.RESTClient.get( Routes.applicationEmojis(this.client.ApplicationId) ) + if (!existingEmojis) { + return; + } + this.existingIcons = new Map( existingEmojis.items.map((item) => { return [ item.name, item.id ] diff --git a/source/Icons/IconDeployer.ts b/source/Icons/IconDeployer.ts index dabdece..8f72377 100644 --- a/source/Icons/IconDeployer.ts +++ b/source/Icons/IconDeployer.ts @@ -1,4 +1,3 @@ -import {REST, Routes} from "discord.js"; import path from "node:path"; import * as fs from "node:fs"; import svg2img from "svg2img"; @@ -14,7 +13,7 @@ export class IconDeployer { public async ensureExistance() { const directory = await fs.promises.opendir(IconDeployer.ICON_PATH); const addIconPromises: Promise[] = []; - for await (let dirname of directory) { + for await (const dirname of directory) { const iconName = path.basename(dirname.name, '.svg').replaceAll('-','_'); if (this.iconCache.get(iconName) !== null) { @@ -43,7 +42,7 @@ export class IconDeployer { } } }, - function (err, buffer) { + function (_err, buffer) { resolve(buffer); } ) diff --git a/source/Repositories/GroupRepository.ts b/source/Repositories/GroupRepository.ts index b5dd29d..0e673ce 100644 --- a/source/Repositories/GroupRepository.ts +++ b/source/Repositories/GroupRepository.ts @@ -2,7 +2,7 @@ import {Repository} from "./Repository"; import {GroupModel} from "../Models/GroupModel"; import Groups, {DBGroup} from "../Database/tables/Groups"; import {DatabaseConnection} from "../Database/DatabaseConnection"; -import {CacheType, CacheTypeReducer, Guild, GuildMember, GuildMemberRoleManager} from "discord.js"; +import {GuildMember} from "discord.js"; import {Nullable} from "../types/Nullable"; import {PlaydateRepository} from "./PlaydateRepository"; import {Container} from "../Container/Container"; @@ -33,7 +33,7 @@ export class GroupRepository extends Repository { } public findGroupsByRoles(server: string, roleIds: string[]): GroupModel[] { - const template = roleIds.map(roleId => '?').join(','); + const template = roleIds.map(_roleId => '?').join(','); const dbResult = this.database.fetchAll(` SELECT * FROM groups WHERE server = ? AND role IN (${template}) @@ -64,7 +64,6 @@ export class GroupRepository extends Repository { public deleteGroup(group: GroupModel): void { this.delete(group.id); - debugger const repo = Container.get(PlaydateRepository.name); const playdates = repo.findFromGroup(group, true) playdates.forEach((playdate) => { diff --git a/source/Repositories/PlaydateRepository.ts b/source/Repositories/PlaydateRepository.ts index 55f0197..1e1ff63 100644 --- a/source/Repositories/PlaydateRepository.ts +++ b/source/Repositories/PlaydateRepository.ts @@ -59,7 +59,7 @@ export class PlaydateRepository extends Repository { } getNextPlaydateForGroup(group: GroupModel): PlaydateModel | null { - let sql = `SELECT * FROM ${this.schema.name} WHERE groupid = ? AND time_from > ? ORDER BY time_from ASC LIMIT 1`; + const sql = `SELECT * FROM ${this.schema.name} WHERE groupid = ? AND time_from > ? ORDER BY time_from LIMIT 1`; const find = this.database.fetch( sql, @@ -78,14 +78,12 @@ export class PlaydateRepository extends Repository { if (!intermediateModel) { throw new Error("Unable to convert the playdate model"); } - const result: PlaydateModel = { + return { id: intermediateModel.id, group: fixedGroup ?? this.groupRepository.getById(intermediateModel.groupid), from_time: new Date(intermediateModel.time_from), to_time: new Date(intermediateModel.time_to), - } - - return result; + }; } protected convertToCreateObject(instance: Partial): object { diff --git a/source/Repositories/Repository.ts b/source/Repositories/Repository.ts index 630cd28..6023fa0 100644 --- a/source/Repositories/Repository.ts +++ b/source/Repositories/Repository.ts @@ -2,7 +2,6 @@ import {DatabaseConnection} from "../Database/DatabaseConnection"; import {Model} from "../Models/Model"; import { Nullable } from "../types/Nullable"; import {DatabaseDefinition} from "../Database/DatabaseDefinition"; -import {debug} from "node:util"; import {Container} from "../Container/Container"; import {EventHandler} from "../Events/EventHandler"; import {ElementCreatedEvent} from "../Events/ElementCreatedEvent"; @@ -62,7 +61,7 @@ export class Repository `${key} = ?`).join(',')} WHERE id = ?`; const result = this.database.execute(sql, ...Object.values(createObject), instance.id); - return result.lastInsertRowid; + return result.changes > 0; } public getById(id: number): Nullable { diff --git a/source/Utilities/ArrayUtils.ts b/source/Utilities/ArrayUtils.ts index 6f4db30..e969ff2 100644 --- a/source/Utilities/ArrayUtils.ts +++ b/source/Utilities/ArrayUtils.ts @@ -9,7 +9,7 @@ export class ArrayUtils { if (a == null || b == null) return false; if (a.length !== b.length) return false; - for (var i = 0; i < a.length; ++i) { + for (let i = 0; i < a.length; ++i) { if (a[i] !== b[i]) return false; } return true; diff --git a/source/deploy.ts b/source/deploy.ts index a2f3db9..93b805c 100644 --- a/source/deploy.ts +++ b/source/deploy.ts @@ -1,4 +1,3 @@ -import Commands from "./Discord/Commands/Commands"; import {Environment} from "./Environment"; import {DatabaseConnection} from "./Database/DatabaseConnection"; import {DatabaseUpdater} from "./Database/DatabaseUpdater"; diff --git a/source/main.ts b/source/main.ts index 4d77713..4ddc5ce 100644 --- a/source/main.ts +++ b/source/main.ts @@ -1,7 +1,6 @@ import {DiscordClient} from "./Discord/DiscordClient"; import {Environment} from "./Environment"; import {Container} from "./Container/Container"; -import {DatabaseConnection} from "./Database/DatabaseConnection"; import {ServiceHint, Services} from "./Container/Services"; import {IconCache} from "./Icons/IconCache"; import {DefaultEvents} from "./Events/DefaultEvents";