From 9b396aaf44b98a31902342671be2f898cef4b952 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Fri, 17 May 2024 12:03:07 -0700 Subject: [PATCH] chore: use biome for formatting and linting (#57) --- .eslintignore | 2 - .eslintrc.json | 16 - .prettierignore | 2 - .prettierrc | 5 - biome.json | 20 + examples/.eslintrc.json | 5 - examples/express_app.js | 34 +- examples/express_app_with_bodyparser.js | 34 +- examples/gcloud_function.js | 53 +- examples/message_components.js | 37 +- examples/modal_example.js | 90 +- examples/nextjs_api.js | 46 +- package-lock.json | 1314 +++-------------------- package.json | 11 +- src/__tests__/utils/SharedTestUtils.ts | 138 +-- src/__tests__/verifyKey.ts | 188 ++-- src/__tests__/verifyKeyMiddleware.ts | 409 +++---- src/components.ts | 170 +-- src/index.ts | 319 +++--- 19 files changed, 1015 insertions(+), 1878 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.json delete mode 100644 .prettierignore delete mode 100644 .prettierrc create mode 100644 biome.json delete mode 100644 examples/.eslintrc.json diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index de4d1f0..0000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist -node_modules diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index a4f3678..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "root": true, - "env": { - "node": true - }, - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "prettier"], - "rules": { - "prettier/prettier": "error" - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended" - ] -} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index de4d1f0..0000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -dist -node_modules diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index a0d1c9a..0000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "printWidth": 120, - "trailingComma": "all", - "singleQuote": true -} diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..cea9c4a --- /dev/null +++ b/biome.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.6.4/schema.json", + "files": { + "include": ["src/**/*.ts", "examples/*.js"] + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + } +} diff --git a/examples/.eslintrc.json b/examples/.eslintrc.json deleted file mode 100644 index 701b80d..0000000 --- a/examples/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "rules": { - "@typescript-eslint/no-var-requires": "off" - } -} diff --git a/examples/express_app.js b/examples/express_app.js index d41086d..1149968 100644 --- a/examples/express_app.js +++ b/examples/express_app.js @@ -1,20 +1,28 @@ const express = require('express'); -const { InteractionType, InteractionResponseType, verifyKeyMiddleware } = require('../dist'); +const { + InteractionType, + InteractionResponseType, + verifyKeyMiddleware, +} = require('../dist'); const app = express(); -app.post('/interactions', verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), (req, res) => { - const interaction = req.body; - if (interaction.type === InteractionType.APPLICATION_COMMAND) { - res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - content: 'Hello world', - }, - }); - } -}); +app.post( + '/interactions', + verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), + (req, res) => { + const interaction = req.body; + if (interaction.type === InteractionType.APPLICATION_COMMAND) { + res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + content: 'Hello world', + }, + }); + } + }, +); app.listen(8999, () => { - console.log('Example app listening at http://localhost:8999'); + console.log('Example app listening at http://localhost:8999'); }); diff --git a/examples/express_app_with_bodyparser.js b/examples/express_app_with_bodyparser.js index 4a8f215..97b2155 100644 --- a/examples/express_app_with_bodyparser.js +++ b/examples/express_app_with_bodyparser.js @@ -1,26 +1,34 @@ const express = require('express'); const bodyParser = require('body-parser'); -const { InteractionType, InteractionResponseType, verifyKeyMiddleware } = require('../dist'); +const { + InteractionType, + InteractionResponseType, + verifyKeyMiddleware, +} = require('../dist'); const app = express(); -app.post('/interactions', verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), (req, res) => { - const interaction = req.body; - if (interaction.type === InteractionType.APPLICATION_COMMAND) { - res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - content: 'Hello world', - }, - }); - } -}); +app.post( + '/interactions', + verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), + (req, res) => { + const interaction = req.body; + if (interaction.type === InteractionType.APPLICATION_COMMAND) { + res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + content: 'Hello world', + }, + }); + } + }, +); // It's best to set up body-parser so that it does NOT apply to interaction // routes. app.use(bodyParser.json()); app.listen(8999, () => { - console.log('Example app listening at http://localhost:8999'); + console.log('Example app listening at http://localhost:8999'); }); diff --git a/examples/gcloud_function.js b/examples/gcloud_function.js index 2e9e358..dd757e4 100644 --- a/examples/gcloud_function.js +++ b/examples/gcloud_function.js @@ -1,28 +1,37 @@ -const { InteractionResponseType, InteractionType, verifyKey } = require('discord-interactions'); +const { + InteractionResponseType, + InteractionType, + verifyKey, +} = require('discord-interactions'); const CLIENT_PUBLIC_KEY = process.env.CLIENT_PUBLIC_KEY; module.exports.myInteraction = async (req, res) => { - // Verify the request - const signature = req.get('X-Signature-Ed25519'); - const timestamp = req.get('X-Signature-Timestamp'); - const isValidRequest = await verifyKey(req.rawBody, signature, timestamp, CLIENT_PUBLIC_KEY); - if (!isValidRequest) { - return res.status(401).end('Bad request signature'); - } + // Verify the request + const signature = req.get('X-Signature-Ed25519'); + const timestamp = req.get('X-Signature-Timestamp'); + const isValidRequest = await verifyKey( + req.rawBody, + signature, + timestamp, + CLIENT_PUBLIC_KEY, + ); + if (!isValidRequest) { + return res.status(401).end('Bad request signature'); + } - // Handle the payload - const interaction = req.body; - if (interaction && interaction.type === InteractionType.APPLICATION_COMMAND) { - res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - content: `You used: ${interaction.data.name}`, - }, - }); - } else { - res.send({ - type: InteractionResponseType.PONG, - }); - } + // Handle the payload + const interaction = req.body; + if (interaction && interaction.type === InteractionType.APPLICATION_COMMAND) { + res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + content: `You used: ${interaction.data.name}`, + }, + }); + } else { + res.send({ + type: InteractionResponseType.PONG, + }); + } }; diff --git a/examples/message_components.js b/examples/message_components.js index a361787..ffcba8f 100644 --- a/examples/message_components.js +++ b/examples/message_components.js @@ -1,21 +1,30 @@ const express = require('express'); -const { InteractionType, InteractionResponseFlags, InteractionResponseType, verifyKeyMiddleware } = require('../dist'); +const { + InteractionType, + InteractionResponseFlags, + InteractionResponseType, + verifyKeyMiddleware, +} = require('../dist'); const app = express(); -app.post('/interactions', verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), (req, res) => { - const interaction = req.body; - if (interaction.type === InteractionType.MESSAGE_COMPONENT) { - res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - content: 'Hello, you interacted with a component.', - flags: InteractionResponseFlags.EPHEMERAL, - }, - }); - } -}); +app.post( + '/interactions', + verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), + (req, res) => { + const interaction = req.body; + if (interaction.type === InteractionType.MESSAGE_COMPONENT) { + res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + content: 'Hello, you interacted with a component.', + flags: InteractionResponseFlags.EPHEMERAL, + }, + }); + } + }, +); app.listen(8999, () => { - console.log('Example app listening at http://localhost:8999'); + console.log('Example app listening at http://localhost:8999'); }); diff --git a/examples/modal_example.js b/examples/modal_example.js index 47bc19f..74c5d25 100644 --- a/examples/modal_example.js +++ b/examples/modal_example.js @@ -1,48 +1,56 @@ const express = require('express'); -const { InteractionType, InteractionResponseType, verifyKeyMiddleware } = require('../dist'); +const { + InteractionType, + InteractionResponseType, + verifyKeyMiddleware, +} = require('../dist'); const app = express(); -app.post('/interactions', verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), (req, res) => { - const interaction = req.body; - if (interaction.type === InteractionType.APPLICATION_COMMAND) { - res.send({ - type: InteractionResponseType.APPLICATION_MODAL, - data: { - title: 'Test', - custom_id: 'test-modal', - components: [ - { - type: 1, - components: [ - { - type: 4, - style: 1, - label: 'Short Input', - custom_id: 'short-input', - placeholder: 'Short Input', - }, - ], - }, - { - type: 1, - components: [ - { - type: 4, - style: 1, - label: 'Paragraph Input', - custom_id: 'paragraph-input', - placeholder: 'Paragraph Input', - required: false, - }, - ], - }, - ], - }, - }); - } -}); +app.post( + '/interactions', + verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), + (req, res) => { + const interaction = req.body; + if (interaction.type === InteractionType.APPLICATION_COMMAND) { + res.send({ + type: InteractionResponseType.APPLICATION_MODAL, + data: { + title: 'Test', + custom_id: 'test-modal', + components: [ + { + type: 1, + components: [ + { + type: 4, + style: 1, + label: 'Short Input', + custom_id: 'short-input', + placeholder: 'Short Input', + }, + ], + }, + { + type: 1, + components: [ + { + type: 4, + style: 1, + label: 'Paragraph Input', + custom_id: 'paragraph-input', + placeholder: 'Paragraph Input', + required: false, + }, + ], + }, + ], + }, + }); + } + }, +); app.listen(8999, () => { - console.log('Example app listening at http://localhost:8999'); + console.log('Example app listening at http://localhost:8999'); }); diff --git a/examples/nextjs_api.js b/examples/nextjs_api.js index 18e0b0d..69eb63f 100644 --- a/examples/nextjs_api.js +++ b/examples/nextjs_api.js @@ -1,28 +1,36 @@ // ./pages/api/interaction.js -const { InteractionType, InteractionResponseType, verifyKeyMiddleware } = require('../dist'); +const { + InteractionType, + InteractionResponseType, + verifyKeyMiddleware, +} = require('../dist'); function runMiddleware(req, res, fn) { - return new Promise((resolve, reject) => { - req.header = (name) => req.headers[name.toLowerCase()]; - req.body = JSON.stringify(req.body); - fn(req, res, (result) => { - if (result instanceof Error) return reject(result); - return resolve(result); - }); - }); + return new Promise((resolve, reject) => { + req.header = (name) => req.headers[name.toLowerCase()]; + req.body = JSON.stringify(req.body); + fn(req, res, (result) => { + if (result instanceof Error) return reject(result); + return resolve(result); + }); + }); } export default async function handler(req, res) { - await runMiddleware(req, res, verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY)); + await runMiddleware( + req, + res, + verifyKeyMiddleware(process.env.CLIENT_PUBLIC_KEY), + ); - const interaction = req.body; - if (interaction.type === InteractionType.APPLICATION_COMMAND) { - res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - content: 'Hello world', - }, - }); - } + const interaction = req.body; + if (interaction.type === InteractionType.APPLICATION_COMMAND) { + res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + content: 'Hello world', + }, + }); + } } diff --git a/package-lock.json b/package-lock.json index 0594864..7b69119 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,18 +12,13 @@ "tweetnacl": "^1.0.3" }, "devDependencies": { + "@biomejs/biome": "^1.7.3", "@types/express": "^4.17.9", "@types/jest": "^29.0.0", "@types/node": "^20.0.0", - "@typescript-eslint/eslint-plugin": "^5.16.0", - "@typescript-eslint/parser": "^5.16.0", "body-parser": "^1.19.0", - "eslint": "^8.11.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.0.0", "express": "^4.17.1", "jest": "^29.0.0", - "prettier": "^2.2.1", "ts-jest": "^29.0.0", "typescript": "^5.0.0" }, @@ -626,94 +621,160 @@ "dev": true, "license": "MIT" }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@biomejs/biome": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.7.3.tgz", + "integrity": "sha512-ogFQI+fpXftr+tiahA6bIXwZ7CSikygASdqMtH07J2cUzrpjyTMVc9Y97v23c7/tL1xCZhM+W9k4hYIBm7Q6cQ==", "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "hasInstallScript": true, + "bin": { + "biome": "bin/biome" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=14.21.3" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.7.3", + "@biomejs/cli-darwin-x64": "1.7.3", + "@biomejs/cli-linux-arm64": "1.7.3", + "@biomejs/cli-linux-arm64-musl": "1.7.3", + "@biomejs/cli-linux-x64": "1.7.3", + "@biomejs/cli-linux-x64-musl": "1.7.3", + "@biomejs/cli-win32-arm64": "1.7.3", + "@biomejs/cli-win32-x64": "1.7.3" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.7.3.tgz", + "integrity": "sha512-eDvLQWmGRqrPIRY7AIrkPHkQ3visEItJKkPYSHCscSDdGvKzYjmBJwG1Gu8+QC5ed6R7eiU63LEC0APFBobmfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.7.3.tgz", + "integrity": "sha512-JXCaIseKRER7dIURsVlAJacnm8SG5I0RpxZ4ya3dudASYUc68WGl4+FEN03ABY3KMIq7hcK1tzsJiWlmXyosZg==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=14.21.3" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.7.3.tgz", + "integrity": "sha512-phNTBpo7joDFastnmZsFjYcDYobLTx4qR4oPvc9tJ486Bd1SfEVPHEvJdNJrMwUQK56T+TRClOQd/8X1nnjA9w==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.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" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=14.21.3" } }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.7.3.tgz", + "integrity": "sha512-c8AlO45PNFZ1BYcwaKzdt46kYbuP6xPGuGQ6h4j3XiEDpyseRRUy/h+6gxj07XovmyxKnSX9GSZ6nVbZvcVUAw==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=14.21.3" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.7.3.tgz", + "integrity": "sha512-vnedYcd5p4keT3iD48oSKjOIRPYcjSNNbd8MO1bKo9ajg3GwQXZLAH+0Cvlr+eMsO67/HddWmscSQwTFrC/uPA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10.10.0" + "node": ">=14.21.3" } }, - "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==", + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.7.3.tgz", + "integrity": "sha512-UdEHKtYGWEX3eDmVWvQeT+z05T9/Sdt2+F/7zmMOFQ7boANeX8pcO6EkJPK3wxMudrApsNEKT26rzqK6sZRTRA==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=14.21.3" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.7.3.tgz", + "integrity": "sha512-unNCDqUKjujYkkSxs7gFIfdasttbDC4+z0kYmcqzRk6yWVoQBL4dNLcCbdnJS+qvVDNdI9rHp2NwpQ0WAdla4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.7.3.tgz", + "integrity": "sha512-ZmByhbrnmz/UUFYB622CECwhKIPjJLLPr5zr3edhu04LzbfcOrz16VYeNq5dpO1ADG70FORhAJkaIGdaVBG00w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -1180,41 +1241,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "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, - "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, - "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, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1380,12 +1406,6 @@ "pretty-format": "^29.0.0" } }, - "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 - }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -1414,12 +1434,6 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -1465,200 +1479,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1672,43 +1492,6 @@ "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "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, - "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, - "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-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1774,27 +1557,12 @@ "node": ">= 8" } }, - "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 - }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -2299,12 +2067,6 @@ } } }, - "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 - }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -2371,30 +2133,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2482,173 +2220,6 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", - "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "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, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/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, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -2662,66 +2233,6 @@ "node": ">=4" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/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, - "engines": { - "node": ">=4.0" - } - }, - "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, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/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, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2838,67 +2349,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "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, - "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 }, - "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 - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -2909,18 +2365,6 @@ "bser": "2.1.1" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2966,42 +2410,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "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, - "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": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -3110,71 +2518,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/gopd": { @@ -3195,12 +2556,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3303,31 +2658,6 @@ "node": ">=0.10.0" } }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -3401,15 +2731,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3430,18 +2751,6 @@ "node": ">=6" } }, - "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, - "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", @@ -3451,15 +2760,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -4146,18 +3446,6 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "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, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -4170,12 +3458,6 @@ "node": ">=4" } }, - "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 - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -4183,18 +3465,6 @@ "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 - }, - "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 - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4207,15 +3477,6 @@ "node": ">=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, - "dependencies": { - "json-buffer": "3.0.1" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4236,19 +3497,6 @@ "node": ">=6" } }, - "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, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -4256,33 +3504,12 @@ "dev": true, "license": "MIT" }, - "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, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, - "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 - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4346,15 +3573,6 @@ "dev": true, "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, - "engines": { - "node": ">= 8" - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -4444,12 +3662,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -4541,23 +3753,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "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", @@ -4573,21 +3768,6 @@ "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, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -4597,18 +3777,6 @@ "node": ">=6" } }, - "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, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -4677,15 +3845,6 @@ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -4777,42 +3936,6 @@ "node": ">=8" } }, - "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, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -4868,15 +3991,6 @@ "node": ">= 0.10" } }, - "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, - "engines": { - "node": ">=6" - } - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -4909,26 +4023,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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" - } - ] - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -5009,15 +4103,6 @@ "node": ">=8" } }, - "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, - "engines": { - "node": ">=4" - } - }, "node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -5028,54 +4113,6 @@ "node": ">=10" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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" - } - ], - "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", @@ -5430,12 +4467,6 @@ "node": ">=8" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5517,44 +4548,11 @@ } } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, - "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, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5565,18 +4563,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -5650,15 +4636,6 @@ "browserslist": ">= 4.21.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, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -5717,15 +4694,6 @@ "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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index bcbdd90..1600807 100644 --- a/package.json +++ b/package.json @@ -26,26 +26,21 @@ "scripts": { "build": "tsc", "build:watch": "tsc --watch", - "format": "eslint --fix '**/*.ts' '**/*.js'", - "lint": "eslint '**/*.ts' '**/*.js'", + "fix": "biome check --apply .", + "lint": "biome check .", "test": "jest --verbose" }, "dependencies": { "tweetnacl": "^1.0.3" }, "devDependencies": { + "@biomejs/biome": "^1.7.3", "@types/express": "^4.17.9", "@types/jest": "^29.0.0", "@types/node": "^20.0.0", - "@typescript-eslint/eslint-plugin": "^5.16.0", - "@typescript-eslint/parser": "^5.16.0", "body-parser": "^1.19.0", - "eslint": "^8.11.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.0.0", "express": "^4.17.1", "jest": "^29.0.0", - "prettier": "^2.2.1", "ts-jest": "^29.0.0", "typescript": "^5.0.0" } diff --git a/src/__tests__/utils/SharedTestUtils.ts b/src/__tests__/utils/SharedTestUtils.ts index a180e8b..20b9a21 100644 --- a/src/__tests__/utils/SharedTestUtils.ts +++ b/src/__tests__/utils/SharedTestUtils.ts @@ -2,53 +2,53 @@ import nacl from 'tweetnacl'; // Example PING request body export const pingRequestBody = JSON.stringify({ - id: '787053080478613555', - token: 'ThisIsATokenFromDiscordThatIsVeryLong', - type: 1, - version: 1, + id: '787053080478613555', + token: 'ThisIsATokenFromDiscordThatIsVeryLong', + type: 1, + version: 1, }); // Example APPLICATION_COMMAND request body export const applicationCommandRequestBody = JSON.stringify({ - id: '787053080478613556', - token: 'ThisIsATokenFromDiscordThatIsVeryLong', - type: 2, - version: 1, - data: { - id: '787053080478613554', - name: 'test', - }, + id: '787053080478613556', + token: 'ThisIsATokenFromDiscordThatIsVeryLong', + type: 2, + version: 1, + data: { + id: '787053080478613554', + name: 'test', + }, }); // Example MESSAGE_COMPONENT request body export const messageComponentRequestBody = JSON.stringify({ - id: '787053080478613555', - token: 'ThisIsATokenFromDiscordThatIsVeryLong', - type: 3, - version: 1, - data: { - custom_id: 'test', - component_type: 2, - }, + id: '787053080478613555', + token: 'ThisIsATokenFromDiscordThatIsVeryLong', + type: 3, + version: 1, + data: { + custom_id: 'test', + component_type: 2, + }, }); // Example APPLICATION_COMMAND_AUTOCOMPLETE request body export const autocompleteRequestBody = JSON.stringify({ - id: '787053080478613555', - token: 'ThisIsATokenFromDiscordThatIsVeryLong', - type: 4, - version: 1, - data: { - id: '787053080478613554', - name: 'test', - type: 1, - version: '787053080478613554', - options: [ - { - type: 3, - name: 'option', - value: 'first_option', - focused: true, - }, - ], - }, + id: '787053080478613555', + token: 'ThisIsATokenFromDiscordThatIsVeryLong', + type: 4, + version: 1, + data: { + id: '787053080478613554', + name: 'test', + type: 1, + version: '787053080478613554', + options: [ + { + type: 3, + name: 'option', + value: 'first_option', + focused: true, + }, + ], + }, }); // Generate a "valid" keypair @@ -57,39 +57,47 @@ export const validKeyPair = nacl.sign.keyPair(); export const invalidKeyPair = nacl.sign.keyPair(); export type SignedRequest = { - body: string; - signature: string; - timestamp: string; + body: string; + signature: string; + timestamp: string; }; export type ExampleRequestResponse = { - status: number; - body: string; + status: number; + body: string; }; -export function signRequestWithKeyPair(body: string, privateKey: Uint8Array): SignedRequest { - const timestamp = String(Math.round(new Date().getTime() / 1000)); - const signature = Buffer.from( - nacl.sign.detached(Uint8Array.from(Buffer.concat([Buffer.from(timestamp), Buffer.from(body)])), privateKey), - ).toString('hex'); - return { - body, - signature, - timestamp, - }; +export function signRequestWithKeyPair( + body: string, + privateKey: Uint8Array, +): SignedRequest { + const timestamp = String(Math.round(new Date().getTime() / 1000)); + const signature = Buffer.from( + nacl.sign.detached( + Uint8Array.from( + Buffer.concat([Buffer.from(timestamp), Buffer.from(body)]), + ), + privateKey, + ), + ).toString('hex'); + return { + body, + signature, + timestamp, + }; } export async function sendExampleRequest( - url: string, - headers: { [key: string]: string }, - body: string, + url: string, + headers: { [key: string]: string }, + body: string, ): Promise { - const response = await fetch(url, { - method: 'POST', - headers, - body, - }); - return { - status: response.status, - body: await response.text(), - }; + const response = await fetch(url, { + method: 'POST', + headers, + body, + }); + return { + status: response.status, + body: await response.text(), + }; } diff --git a/src/__tests__/verifyKey.ts b/src/__tests__/verifyKey.ts index 0cbb1a9..1ea37f4 100644 --- a/src/__tests__/verifyKey.ts +++ b/src/__tests__/verifyKey.ts @@ -1,73 +1,137 @@ import { verifyKey } from '../index'; -import { invalidKeyPair, validKeyPair, pingRequestBody, signRequestWithKeyPair } from './utils/SharedTestUtils'; +import { + invalidKeyPair, + pingRequestBody, + signRequestWithKeyPair, + validKeyPair, +} from './utils/SharedTestUtils'; describe('verify key method', () => { - it('valid ping request', () => { - // Sign and verify a valid ping request - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - expect( - verifyKey(signedRequest.body, signedRequest.signature, signedRequest.timestamp, validKeyPair.publicKey), - ).toBe(true); - }); + it('valid ping request', () => { + // Sign and verify a valid ping request + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + expect( + verifyKey( + signedRequest.body, + signedRequest.signature, + signedRequest.timestamp, + validKeyPair.publicKey, + ), + ).toBe(true); + }); - it('valid application command', () => { - // Sign and verify a valid application command request - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - expect( - verifyKey(signedRequest.body, signedRequest.signature, signedRequest.timestamp, validKeyPair.publicKey), - ).toBe(true); - }); + it('valid application command', () => { + // Sign and verify a valid application command request + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + expect( + verifyKey( + signedRequest.body, + signedRequest.signature, + signedRequest.timestamp, + validKeyPair.publicKey, + ), + ).toBe(true); + }); - it('valid message component', () => { - // Sign and verify a valid message component request - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - expect( - verifyKey(signedRequest.body, signedRequest.signature, signedRequest.timestamp, validKeyPair.publicKey), - ).toBe(true); - }); + it('valid message component', () => { + // Sign and verify a valid message component request + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + expect( + verifyKey( + signedRequest.body, + signedRequest.signature, + signedRequest.timestamp, + validKeyPair.publicKey, + ), + ).toBe(true); + }); - it('valid autocomplete', () => { - // Sign and verify a valid autocomplete request - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - expect( - verifyKey(signedRequest.body, signedRequest.signature, signedRequest.timestamp, validKeyPair.publicKey), - ).toBe(true); - }); + it('valid autocomplete', () => { + // Sign and verify a valid autocomplete request + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + expect( + verifyKey( + signedRequest.body, + signedRequest.signature, + signedRequest.timestamp, + validKeyPair.publicKey, + ), + ).toBe(true); + }); - it('invalid key', () => { - // Sign a request with a different private key and verify with the valid public key - const signedRequest = signRequestWithKeyPair(pingRequestBody, invalidKeyPair.secretKey); - expect( - verifyKey(signedRequest.body, signedRequest.signature, signedRequest.timestamp, validKeyPair.publicKey), - ).toBe(false); - }); + it('invalid key', () => { + // Sign a request with a different private key and verify with the valid public key + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + invalidKeyPair.secretKey, + ); + expect( + verifyKey( + signedRequest.body, + signedRequest.signature, + signedRequest.timestamp, + validKeyPair.publicKey, + ), + ).toBe(false); + }); - it('invalid body', () => { - // Sign a valid request and verify with an invalid body - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - expect( - verifyKey('example invalid body', signedRequest.signature, signedRequest.timestamp, validKeyPair.publicKey), - ).toBe(false); - }); + it('invalid body', () => { + // Sign a valid request and verify with an invalid body + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + expect( + verifyKey( + 'example invalid body', + signedRequest.signature, + signedRequest.timestamp, + validKeyPair.publicKey, + ), + ).toBe(false); + }); - it('invalid signature', () => { - // Sign a valid request and verify with an invalid signature - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - expect( - verifyKey(signedRequest.body, 'example invalid signature', signedRequest.timestamp, validKeyPair.publicKey), - ).toBe(false); - }); + it('invalid signature', () => { + // Sign a valid request and verify with an invalid signature + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + expect( + verifyKey( + signedRequest.body, + 'example invalid signature', + signedRequest.timestamp, + validKeyPair.publicKey, + ), + ).toBe(false); + }); - it('invalid timestamp', () => { - // Sign a valid request and verify with an invalid timestamp - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - expect( - verifyKey( - 'example invalid body', - signedRequest.signature, - String(Math.round(new Date().getTime() / 1000) - 10000), - validKeyPair.publicKey, - ), - ).toBe(false); - }); + it('invalid timestamp', () => { + // Sign a valid request and verify with an invalid timestamp + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + expect( + verifyKey( + 'example invalid body', + signedRequest.signature, + String(Math.round(new Date().getTime() / 1000) - 10000), + validKeyPair.publicKey, + ), + ).toBe(false); + }); }); diff --git a/src/__tests__/verifyKeyMiddleware.ts b/src/__tests__/verifyKeyMiddleware.ts index ea9f4e3..4f71b5e 100644 --- a/src/__tests__/verifyKeyMiddleware.ts +++ b/src/__tests__/verifyKeyMiddleware.ts @@ -1,213 +1,254 @@ -import { Request, Response } from 'express'; -import * as http from 'http'; -import { InteractionResponseType, InteractionResponseFlags, InteractionType, verifyKeyMiddleware } from '../index'; -import { AddressInfo } from 'net'; +import type * as http from 'node:http'; +import type { AddressInfo } from 'node:net'; +import type { Request, Response } from 'express'; import { - autocompleteRequestBody, - applicationCommandRequestBody, - invalidKeyPair, - validKeyPair, - messageComponentRequestBody, - pingRequestBody, - sendExampleRequest, - signRequestWithKeyPair, + InteractionResponseFlags, + InteractionResponseType, + InteractionType, + verifyKeyMiddleware, +} from '../index'; +import { + applicationCommandRequestBody, + autocompleteRequestBody, + invalidKeyPair, + messageComponentRequestBody, + pingRequestBody, + sendExampleRequest, + signRequestWithKeyPair, + validKeyPair, } from './utils/SharedTestUtils'; import express from 'express'; const expressApp = express(); const exampleApplicationCommandResponse = { - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - content: 'Hello world', - }, + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + content: 'Hello world', + }, }; const exampleMessageComponentResponse = { - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - content: 'Hello, you interacted with a component.', - flags: InteractionResponseFlags.EPHEMERAL, - }, + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + content: 'Hello, you interacted with a component.', + flags: InteractionResponseFlags.EPHEMERAL, + }, }; const exampleAutocompleteResponse = { - type: InteractionResponseType.APPLICATION_COMMAND_AUTOCOMPLETE_RESULT, - data: { - choices: [ - { - name: 'The first option', - value: 'first_option', - }, - ], - }, + type: InteractionResponseType.APPLICATION_COMMAND_AUTOCOMPLETE_RESULT, + data: { + choices: [ + { + name: 'The first option', + value: 'first_option', + }, + ], + }, }; expressApp.post( - '/interactions', - verifyKeyMiddleware(Buffer.from(validKeyPair.publicKey).toString('hex')), - (req: Request, res: Response) => { - const interaction = req.body; - if (interaction.type === InteractionType.APPLICATION_COMMAND) { - res.send(exampleApplicationCommandResponse); - } else if (interaction.type === InteractionType.MESSAGE_COMPONENT) { - res.send(exampleMessageComponentResponse); - } else if (interaction.type === InteractionType.APPLICATION_COMMAND_AUTOCOMPLETE) { - res.send(exampleAutocompleteResponse); - } - }, + '/interactions', + verifyKeyMiddleware(Buffer.from(validKeyPair.publicKey).toString('hex')), + (req: Request, res: Response) => { + const interaction = req.body; + if (interaction.type === InteractionType.APPLICATION_COMMAND) { + res.send(exampleApplicationCommandResponse); + } else if (interaction.type === InteractionType.MESSAGE_COMPONENT) { + res.send(exampleMessageComponentResponse); + } else if ( + interaction.type === InteractionType.APPLICATION_COMMAND_AUTOCOMPLETE + ) { + res.send(exampleAutocompleteResponse); + } + }, ); let expressAppServer: http.Server; let exampleInteractionsUrl: string; beforeAll(async () => { - await new Promise((resolve) => { - expressAppServer = expressApp.listen(0); - resolve(); - }); - exampleInteractionsUrl = 'http://localhost:' + (expressAppServer.address() as AddressInfo).port + '/interactions'; + await new Promise((resolve) => { + expressAppServer = expressApp.listen(0); + resolve(); + }); + exampleInteractionsUrl = `http://localhost:${ + (expressAppServer.address() as AddressInfo).port + }/interactions`; }); describe('verify key middleware', () => { - it('valid ping', async () => { - // Sign and verify a valid ping request - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'x-signature-ed25519': signedRequest.signature, - 'x-signature-timestamp': signedRequest.timestamp, - 'content-type': 'application/json', - }, - signedRequest.body, - ); - expect(exampleRequestResponse.status).toBe(200); - }); - - it('valid application command', async () => { - // Sign and verify a valid application command request - const signedRequest = signRequestWithKeyPair(applicationCommandRequestBody, validKeyPair.secretKey); - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'x-signature-ed25519': signedRequest.signature, - 'x-signature-timestamp': signedRequest.timestamp, - 'content-type': 'application/json', - }, - signedRequest.body, - ); - const exampleRequestResponseBody = JSON.parse(exampleRequestResponse.body); - expect(exampleRequestResponseBody).toStrictEqual(exampleApplicationCommandResponse); - }); - - it('valid message component', async () => { - // Sign and verify a valid message component request - const signedRequest = signRequestWithKeyPair(messageComponentRequestBody, validKeyPair.secretKey); - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'x-signature-ed25519': signedRequest.signature, - 'x-signature-timestamp': signedRequest.timestamp, - 'content-type': 'application/json', - }, - signedRequest.body, - ); - const exampleRequestResponseBody = JSON.parse(exampleRequestResponse.body); - expect(exampleRequestResponseBody).toStrictEqual(exampleMessageComponentResponse); - }); - - it('valid autocomplete', async () => { - // Sign and verify a valid autocomplete request - const signedRequest = signRequestWithKeyPair(autocompleteRequestBody, validKeyPair.secretKey); - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'x-signature-ed25519': signedRequest.signature, - 'x-signature-timestamp': signedRequest.timestamp, - 'content-type': 'application/json', - }, - signedRequest.body, - ); - const exampleRequestResponseBody = JSON.parse(exampleRequestResponse.body); - expect(exampleRequestResponseBody).toStrictEqual(exampleAutocompleteResponse); - }); - - it('invalid key', async () => { - // Sign a request with a different private key and verify with the valid public key - const signedRequest = signRequestWithKeyPair(pingRequestBody, invalidKeyPair.secretKey); - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'x-signature-ed25519': signedRequest.signature, - 'x-signature-timestamp': signedRequest.timestamp, - 'content-type': 'application/json', - }, - signedRequest.body, - ); - expect(exampleRequestResponse.status).toBe(401); - }); - - it('invalid body', async () => { - // Sign a valid request and verify with an invalid body - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'x-signature-ed25519': signedRequest.signature, - 'x-signature-timestamp': signedRequest.timestamp, - 'content-type': 'application/json', - }, - 'example invalid body', - ); - expect(exampleRequestResponse.status).toBe(401); - }); - - it('invalid signature', async () => { - // Sign a valid request and verify with an invalid signature - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'x-signature-ed25519': 'example invalid signature', - 'x-signature-timestamp': signedRequest.timestamp, - 'content-type': 'application/json', - }, - signedRequest.body, - ); - expect(exampleRequestResponse.status).toBe(401); - }); - - it('invalid timestamp', async () => { - // Sign a valid request and verify with an invalid timestamp - const signedRequest = signRequestWithKeyPair(pingRequestBody, validKeyPair.secretKey); - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'x-signature-ed25519': signedRequest.signature, - 'x-signature-timestamp': String(Math.round(new Date().getTime() / 1000) - 10000), - 'content-type': 'application/json', - }, - signedRequest.body, - ); - expect(exampleRequestResponse.status).toBe(401); - }); - - it('missing headers', async () => { - // Sign a valid request and verify with an invalid timestamp - const exampleRequestResponse = await sendExampleRequest( - exampleInteractionsUrl, - { - 'content-type': 'application/json', - }, - exampleInteractionsUrl, - ); - expect(exampleRequestResponse.status).toBe(401); - }); + it('valid ping', async () => { + // Sign and verify a valid ping request + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'x-signature-ed25519': signedRequest.signature, + 'x-signature-timestamp': signedRequest.timestamp, + 'content-type': 'application/json', + }, + signedRequest.body, + ); + expect(exampleRequestResponse.status).toBe(200); + }); + + it('valid application command', async () => { + // Sign and verify a valid application command request + const signedRequest = signRequestWithKeyPair( + applicationCommandRequestBody, + validKeyPair.secretKey, + ); + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'x-signature-ed25519': signedRequest.signature, + 'x-signature-timestamp': signedRequest.timestamp, + 'content-type': 'application/json', + }, + signedRequest.body, + ); + const exampleRequestResponseBody = JSON.parse(exampleRequestResponse.body); + expect(exampleRequestResponseBody).toStrictEqual( + exampleApplicationCommandResponse, + ); + }); + + it('valid message component', async () => { + // Sign and verify a valid message component request + const signedRequest = signRequestWithKeyPair( + messageComponentRequestBody, + validKeyPair.secretKey, + ); + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'x-signature-ed25519': signedRequest.signature, + 'x-signature-timestamp': signedRequest.timestamp, + 'content-type': 'application/json', + }, + signedRequest.body, + ); + const exampleRequestResponseBody = JSON.parse(exampleRequestResponse.body); + expect(exampleRequestResponseBody).toStrictEqual( + exampleMessageComponentResponse, + ); + }); + + it('valid autocomplete', async () => { + // Sign and verify a valid autocomplete request + const signedRequest = signRequestWithKeyPair( + autocompleteRequestBody, + validKeyPair.secretKey, + ); + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'x-signature-ed25519': signedRequest.signature, + 'x-signature-timestamp': signedRequest.timestamp, + 'content-type': 'application/json', + }, + signedRequest.body, + ); + const exampleRequestResponseBody = JSON.parse(exampleRequestResponse.body); + expect(exampleRequestResponseBody).toStrictEqual( + exampleAutocompleteResponse, + ); + }); + + it('invalid key', async () => { + // Sign a request with a different private key and verify with the valid public key + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + invalidKeyPair.secretKey, + ); + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'x-signature-ed25519': signedRequest.signature, + 'x-signature-timestamp': signedRequest.timestamp, + 'content-type': 'application/json', + }, + signedRequest.body, + ); + expect(exampleRequestResponse.status).toBe(401); + }); + + it('invalid body', async () => { + // Sign a valid request and verify with an invalid body + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'x-signature-ed25519': signedRequest.signature, + 'x-signature-timestamp': signedRequest.timestamp, + 'content-type': 'application/json', + }, + 'example invalid body', + ); + expect(exampleRequestResponse.status).toBe(401); + }); + + it('invalid signature', async () => { + // Sign a valid request and verify with an invalid signature + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'x-signature-ed25519': 'example invalid signature', + 'x-signature-timestamp': signedRequest.timestamp, + 'content-type': 'application/json', + }, + signedRequest.body, + ); + expect(exampleRequestResponse.status).toBe(401); + }); + + it('invalid timestamp', async () => { + // Sign a valid request and verify with an invalid timestamp + const signedRequest = signRequestWithKeyPair( + pingRequestBody, + validKeyPair.secretKey, + ); + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'x-signature-ed25519': signedRequest.signature, + 'x-signature-timestamp': String( + Math.round(new Date().getTime() / 1000) - 10000, + ), + 'content-type': 'application/json', + }, + signedRequest.body, + ); + expect(exampleRequestResponse.status).toBe(401); + }); + + it('missing headers', async () => { + // Sign a valid request and verify with an invalid timestamp + const exampleRequestResponse = await sendExampleRequest( + exampleInteractionsUrl, + { + 'content-type': 'application/json', + }, + exampleInteractionsUrl, + ); + expect(exampleRequestResponse.status).toBe(401); + }); }); afterAll(() => { - if (expressAppServer) { - expressAppServer.close(); - } + if (expressAppServer) { + expressAppServer.close(); + } }); diff --git a/src/components.ts b/src/components.ts index d35a001..b526cf7 100644 --- a/src/components.ts +++ b/src/components.ts @@ -3,14 +3,14 @@ * @see {@link https://discord.com/developers/docs/interactions/message-components#component-object-component-types} */ export enum MessageComponentTypes { - ACTION_ROW = 1, - BUTTON = 2, - STRING_SELECT = 3, - INPUT_TEXT = 4, - USER_SELECT = 5, - ROLE_SELECT = 6, - MENTIONABLE_SELECT = 7, - CHANNEL_SELECT = 8, + ACTION_ROW = 1, + BUTTON = 2, + STRING_SELECT = 3, + INPUT_TEXT = 4, + USER_SELECT = 5, + ROLE_SELECT = 6, + MENTIONABLE_SELECT = 7, + CHANNEL_SELECT = 8, } export type MessageComponent = Button | ActionRow | StringSelect | InputText; @@ -20,26 +20,26 @@ export type MessageComponent = Button | ActionRow | StringSelect | InputText; * @see {@link https://discord.com/developers/docs/interactions/message-components#button-object-button-structure} */ export type Button = { - type: MessageComponentTypes.BUTTON; - style: - | ButtonStyleTypes.PRIMARY - | ButtonStyleTypes.SECONDARY - | ButtonStyleTypes.SUCCESS - | ButtonStyleTypes.DANGER - | ButtonStyleTypes.LINK; - label: string; - emoji?: Pick; - custom_id?: string; - url?: string; - disabled?: boolean; + type: MessageComponentTypes.BUTTON; + style: + | ButtonStyleTypes.PRIMARY + | ButtonStyleTypes.SECONDARY + | ButtonStyleTypes.SUCCESS + | ButtonStyleTypes.DANGER + | ButtonStyleTypes.LINK; + label: string; + emoji?: Pick; + custom_id?: string; + url?: string; + disabled?: boolean; }; export enum ButtonStyleTypes { - PRIMARY = 1, - SECONDARY = 2, - SUCCESS = 3, - DANGER = 4, - LINK = 5, + PRIMARY = 1, + SECONDARY = 2, + SUCCESS = 3, + DANGER = 4, + LINK = 5, } /** @@ -47,75 +47,90 @@ export enum ButtonStyleTypes { * @see {@link https://discord.com/developers/docs/interactions/message-components#action-rows} */ export type ActionRow = { - type: MessageComponentTypes.ACTION_ROW; - components: Exclude[]; + type: MessageComponentTypes.ACTION_ROW; + components: Exclude[]; }; export type SelectComponentType = - | MessageComponentTypes.STRING_SELECT - | MessageComponentTypes.USER_SELECT - | MessageComponentTypes.ROLE_SELECT - | MessageComponentTypes.MENTIONABLE_SELECT - | MessageComponentTypes.CHANNEL_SELECT; + | MessageComponentTypes.STRING_SELECT + | MessageComponentTypes.USER_SELECT + | MessageComponentTypes.ROLE_SELECT + | MessageComponentTypes.MENTIONABLE_SELECT + | MessageComponentTypes.CHANNEL_SELECT; // This parent type is to simplify the individual selects while keeping descriptive generated type hints export type SelectMenu = { - type: T; - custom_id: string; - placeholder?: string; - min_values?: number; - max_values?: number; - disabled?: boolean; - options: StringSelectOption[]; - channel_types?: ChannelTypes[]; + type: T; + custom_id: string; + placeholder?: string; + min_values?: number; + max_values?: number; + disabled?: boolean; + options: StringSelectOption[]; + channel_types?: ChannelTypes[]; }; /** * Text select menu component * @see {@link https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure} */ -export type StringSelect = Omit, 'channel_types'>; +export type StringSelect = Omit< + SelectMenu, + 'channel_types' +>; export type StringSelectOption = { - label: string; - value: string; - description?: string; - emoji?: Pick; - default?: boolean; + label: string; + value: string; + description?: string; + emoji?: Pick; + default?: boolean; }; /** * User select menu component * @see {@link https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure} */ -export type UserSelect = Omit, 'channel_types' | 'options'>; +export type UserSelect = Omit< + SelectMenu, + 'channel_types' | 'options' +>; /** * Role select menu component * @see {@link https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure} */ -export type RoleSelect = Omit, 'channel_types' | 'options'>; +export type RoleSelect = Omit< + SelectMenu, + 'channel_types' | 'options' +>; /** * Mentionable (role & user) select menu component * @see {@link https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure} */ -export type MentionableSelect = Omit, 'channel_types' | 'options'>; +export type MentionableSelect = Omit< + SelectMenu, + 'channel_types' | 'options' +>; /** * Channel select menu component * @see {@link https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure} */ -export type ChannelSelect = Omit, 'options'>; +export type ChannelSelect = Omit< + SelectMenu, + 'options' +>; export enum ChannelTypes { - DM = 1, - GROUP_DM = 3, - GUILD_TEXT = 0, - GUILD_VOICE = 2, - GUILD_CATEGORY = 4, - GUILD_ANNOUNCEMENT = 5, - GUILD_STORE = 6, + DM = 1, + GROUP_DM = 3, + GUILD_TEXT = 0, + GUILD_VOICE = 2, + GUILD_CATEGORY = 4, + GUILD_ANNOUNCEMENT = 5, + GUILD_STORE = 6, } /** @@ -123,30 +138,31 @@ export enum ChannelTypes { * @see {@link https://discord.com/developers/docs/interactions/message-components#text-inputs-text-input-structure} */ export type InputText = { - type: MessageComponentTypes.INPUT_TEXT; - custom_id: string; - style: TextStyleTypes.SHORT | TextStyleTypes.PARAGRAPH; - label: string; - min_length?: number; - max_length?: number; - required?: boolean; - value?: string; - placeholder?: string; + type: MessageComponentTypes.INPUT_TEXT; + custom_id: string; + style: TextStyleTypes.SHORT | TextStyleTypes.PARAGRAPH; + label: string; + min_length?: number; + max_length?: number; + required?: boolean; + value?: string; + placeholder?: string; }; export enum TextStyleTypes { - SHORT = 1, - PARAGRAPH = 2, + SHORT = 1, + PARAGRAPH = 2, } export type EmojiInfo = { - name: string | undefined; - id: string | undefined; - // Should define the user object in future - user?: { [key: string]: any }; - roles?: string[]; - require_colons?: boolean; - managed?: boolean; - available?: boolean; - animated?: boolean; + name: string | undefined; + id: string | undefined; + // Should define the user object in future + // biome-ignore lint/suspicious/noExplicitAny: + user?: { [key: string]: any }; + roles?: string[]; + require_colons?: boolean; + managed?: boolean; + available?: boolean; + animated?: boolean; }; diff --git a/src/index.ts b/src/index.ts index e6a1568..b646d13 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,79 +1,79 @@ +import type { NextFunction, Request, Response } from 'express'; import nacl from 'tweetnacl'; -import type { Request, Response, NextFunction } from 'express'; /** * The type of interaction this request is. */ export enum InteractionType { - /** - * A ping. - */ - PING = 1, - /** - * A command invocation. - */ - APPLICATION_COMMAND = 2, - /** - * Usage of a message's component. - */ - MESSAGE_COMPONENT = 3, - /** - * An interaction sent when an application command option is filled out. - */ - APPLICATION_COMMAND_AUTOCOMPLETE = 4, - /** - * An interaction sent when a modal is submitted. - */ - MODAL_SUBMIT = 5, + /** + * A ping. + */ + PING = 1, + /** + * A command invocation. + */ + APPLICATION_COMMAND = 2, + /** + * Usage of a message's component. + */ + MESSAGE_COMPONENT = 3, + /** + * An interaction sent when an application command option is filled out. + */ + APPLICATION_COMMAND_AUTOCOMPLETE = 4, + /** + * An interaction sent when a modal is submitted. + */ + MODAL_SUBMIT = 5, } /** * The type of response that is being sent. */ export enum InteractionResponseType { - /** - * Acknowledge a `PING`. - */ - PONG = 1, - /** - * Respond with a message, showing the user's input. - */ - CHANNEL_MESSAGE_WITH_SOURCE = 4, - /** - * Acknowledge a command without sending a message, showing the user's input. Requires follow-up. - */ - DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE = 5, - /** - * Acknowledge an interaction and edit the original message that contains the component later; the user does not see a loading state. - */ - DEFERRED_UPDATE_MESSAGE = 6, - /** - * Edit the message the component was attached to. - */ - UPDATE_MESSAGE = 7, - /* - * Callback for an app to define the results to the user. - */ - APPLICATION_COMMAND_AUTOCOMPLETE_RESULT = 8, - /* - * Respond with a modal. - */ - MODAL = 9, - /* - * Respond with an upgrade prompt. - */ - PREMIUM_REQUIRED = 10, + /** + * Acknowledge a `PING`. + */ + PONG = 1, + /** + * Respond with a message, showing the user's input. + */ + CHANNEL_MESSAGE_WITH_SOURCE = 4, + /** + * Acknowledge a command without sending a message, showing the user's input. Requires follow-up. + */ + DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE = 5, + /** + * Acknowledge an interaction and edit the original message that contains the component later; the user does not see a loading state. + */ + DEFERRED_UPDATE_MESSAGE = 6, + /** + * Edit the message the component was attached to. + */ + UPDATE_MESSAGE = 7, + /* + * Callback for an app to define the results to the user. + */ + APPLICATION_COMMAND_AUTOCOMPLETE_RESULT = 8, + /* + * Respond with a modal. + */ + MODAL = 9, + /* + * Respond with an upgrade prompt. + */ + PREMIUM_REQUIRED = 10, } /** * Flags that can be included in an Interaction Response. */ export enum InteractionResponseFlags { - /** - * Show the message only to the user that performed the interaction. Message - * does not persist between sessions. - */ - EPHEMERAL = 1 << 6, + /** + * Show the message only to the user that performed the interaction. Message + * does not persist between sessions. + */ + EPHEMERAL = 1 << 6, } /** @@ -83,36 +83,41 @@ export enum InteractionResponseFlags { * @param format - Format of value. Valid options: 'hex'. Defaults to utf-8. * @returns Value in Uint8Array form. */ -function valueToUint8Array(value: Uint8Array | ArrayBuffer | Buffer | string, format?: string): Uint8Array { - if (value == null) { - return new Uint8Array(); - } - if (typeof value === 'string') { - if (format === 'hex') { - const matches = value.match(/.{1,2}/g); - if (matches == null) { - throw new Error('Value is not a valid hex string'); - } - const hexVal = matches.map((byte: string) => parseInt(byte, 16)); - return new Uint8Array(hexVal); - } else { - return new TextEncoder().encode(value); - } - } - try { - if (Buffer.isBuffer(value)) { - return new Uint8Array(value); - } - } catch (ex) { - // Runtime doesn't have Buffer - } - if (value instanceof ArrayBuffer) { - return new Uint8Array(value); - } - if (value instanceof Uint8Array) { - return value; - } - throw new Error('Unrecognized value type, must be one of: string, Buffer, ArrayBuffer, Uint8Array'); +function valueToUint8Array( + value: Uint8Array | ArrayBuffer | Buffer | string, + format?: string, +): Uint8Array { + if (value == null) { + return new Uint8Array(); + } + if (typeof value === 'string') { + if (format === 'hex') { + const matches = value.match(/.{1,2}/g); + if (matches == null) { + throw new Error('Value is not a valid hex string'); + } + const hexVal = matches.map((byte: string) => Number.parseInt(byte, 16)); + return new Uint8Array(hexVal); + } + + return new TextEncoder().encode(value); + } + try { + if (Buffer.isBuffer(value)) { + return new Uint8Array(value); + } + } catch (ex) { + // Runtime doesn't have Buffer + } + if (value instanceof ArrayBuffer) { + return new Uint8Array(value); + } + if (value instanceof Uint8Array) { + return value; + } + throw new Error( + 'Unrecognized value type, must be one of: string, Buffer, ArrayBuffer, Uint8Array', + ); } /** @@ -123,10 +128,10 @@ function valueToUint8Array(value: Uint8Array | ArrayBuffer | Buffer | string, fo * @returns Concatenated arrays */ function concatUint8Arrays(arr1: Uint8Array, arr2: Uint8Array): Uint8Array { - const merged = new Uint8Array(arr1.length + arr2.length); - merged.set(arr1); - merged.set(arr2, arr1.length); - return merged; + const merged = new Uint8Array(arr1.length + arr2.length); + merged.set(arr1); + merged.set(arr2, arr1.length); + return merged; } /** @@ -139,22 +144,22 @@ function concatUint8Arrays(arr1: Uint8Array, arr2: Uint8Array): Uint8Array { * @returns Whether or not validation was successful */ export function verifyKey( - rawBody: Uint8Array | ArrayBuffer | Buffer | string, - signature: Uint8Array | ArrayBuffer | Buffer | string, - timestamp: Uint8Array | ArrayBuffer | Buffer | string, - clientPublicKey: Uint8Array | ArrayBuffer | Buffer | string, + rawBody: Uint8Array | ArrayBuffer | Buffer | string, + signature: Uint8Array | ArrayBuffer | Buffer | string, + timestamp: Uint8Array | ArrayBuffer | Buffer | string, + clientPublicKey: Uint8Array | ArrayBuffer | Buffer | string, ): boolean { - try { - const timestampData = valueToUint8Array(timestamp); - const bodyData = valueToUint8Array(rawBody); - const message = concatUint8Arrays(timestampData, bodyData); + try { + const timestampData = valueToUint8Array(timestamp); + const bodyData = valueToUint8Array(rawBody); + const message = concatUint8Arrays(timestampData, bodyData); - const signatureData = valueToUint8Array(signature, 'hex'); - const publicKeyData = valueToUint8Array(clientPublicKey, 'hex'); - return nacl.sign.detached.verify(message, signatureData, publicKeyData); - } catch (ex) { - return false; - } + const signatureData = valueToUint8Array(signature, 'hex'); + const publicKeyData = valueToUint8Array(clientPublicKey, 'hex'); + return nacl.sign.detached.verify(message, signatureData, publicKeyData); + } catch (ex) { + return false; + } } /** @@ -164,63 +169,63 @@ export function verifyKey( * @returns The middleware function */ export function verifyKeyMiddleware( - clientPublicKey: string, + clientPublicKey: string, ): (req: Request, res: Response, next: NextFunction) => void { - if (!clientPublicKey) { - throw new Error('You must specify a Discord client public key'); - } + if (!clientPublicKey) { + throw new Error('You must specify a Discord client public key'); + } - return function (req: Request, res: Response, next: NextFunction) { - const timestamp = (req.header('X-Signature-Timestamp') || '') as string; - const signature = (req.header('X-Signature-Ed25519') || '') as string; + return (req: Request, res: Response, next: NextFunction) => { + const timestamp = (req.header('X-Signature-Timestamp') || '') as string; + const signature = (req.header('X-Signature-Ed25519') || '') as string; - function onBodyComplete(rawBody: Buffer) { - if (!verifyKey(rawBody, signature, timestamp, clientPublicKey)) { - res.statusCode = 401; - res.end('[discord-interactions] Invalid signature'); - return; - } + function onBodyComplete(rawBody: Buffer) { + if (!verifyKey(rawBody, signature, timestamp, clientPublicKey)) { + res.statusCode = 401; + res.end('[discord-interactions] Invalid signature'); + return; + } - const body = JSON.parse(rawBody.toString('utf-8')) || {}; - if (body.type === InteractionType.PING) { - res.setHeader('Content-Type', 'application/json'); - res.end( - JSON.stringify({ - type: InteractionResponseType.PONG, - }), - ); - return; - } + const body = JSON.parse(rawBody.toString('utf-8')) || {}; + if (body.type === InteractionType.PING) { + res.setHeader('Content-Type', 'application/json'); + res.end( + JSON.stringify({ + type: InteractionResponseType.PONG, + }), + ); + return; + } - req.body = body; - next(); - } + req.body = body; + next(); + } - if (req.body) { - if (Buffer.isBuffer(req.body)) { - onBodyComplete(req.body); - } else if (typeof req.body === 'string') { - onBodyComplete(Buffer.from(req.body, 'utf-8')); - } else { - console.warn( - '[discord-interactions]: req.body was tampered with, probably by some other middleware. We recommend disabling middleware for interaction routes so that req.body is a raw buffer.', - ); - // Attempt to reconstruct the raw buffer. This works but is risky - // because it depends on JSON.stringify matching the Discord backend's - // JSON serialization. - onBodyComplete(Buffer.from(JSON.stringify(req.body), 'utf-8')); - } - } else { - const chunks: Array = []; - req.on('data', (chunk) => { - chunks.push(chunk); - }); - req.on('end', () => { - const rawBody = Buffer.concat(chunks); - onBodyComplete(rawBody); - }); - } - }; + if (req.body) { + if (Buffer.isBuffer(req.body)) { + onBodyComplete(req.body); + } else if (typeof req.body === 'string') { + onBodyComplete(Buffer.from(req.body, 'utf-8')); + } else { + console.warn( + '[discord-interactions]: req.body was tampered with, probably by some other middleware. We recommend disabling middleware for interaction routes so that req.body is a raw buffer.', + ); + // Attempt to reconstruct the raw buffer. This works but is risky + // because it depends on JSON.stringify matching the Discord backend's + // JSON serialization. + onBodyComplete(Buffer.from(JSON.stringify(req.body), 'utf-8')); + } + } else { + const chunks: Array = []; + req.on('data', (chunk) => { + chunks.push(chunk); + }); + req.on('end', () => { + const rawBody = Buffer.concat(chunks); + onBodyComplete(rawBody); + }); + } + }; } export * from './components';