diff --git a/.eslintrc.json b/.eslintrc.json index 16388c807..e4944ecf1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,7 +7,8 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", - "plugin:jest/recommended" + "plugin:jest/recommended", + "plugin:prettier/recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { @@ -17,7 +18,7 @@ "createDefaultProgram": true }, "plugins": [ - "@typescript-eslint", + "@typescript-eslint/eslint-plugin", "jest" ], "rules": { @@ -29,7 +30,7 @@ }, "overrides": [ { - "files": ["*.spec.ts"], + "files": ["*.spec.ts", "**/__mocks__/**"], "env": { "jest/globals": true }, diff --git a/package-lock.json b/package-lock.json index 3f9c241c3..903f9a542 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,9 @@ "@typescript-eslint/parser": "4.21.0", "copyfiles": "2.4.1", "eslint": "7.23.0", + "eslint-config-prettier": "8.1.0", "eslint-plugin-jest": "24.3.4", + "eslint-plugin-prettier": "3.3.1", "jest": "26.6.3", "mongodb-memory-server": "6.9.6", "prettier": "2.2.1", @@ -173,11 +175,21 @@ "readable-stream": "^3.4.0" } }, - "node_modules/@angular-devkit/schematics/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "node_modules/@angular-devkit/schematics/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, "node_modules/@angular-devkit/schematics/node_modules/ora": { "version": "5.3.0", @@ -201,20 +213,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/schematics/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", @@ -227,35 +225,6 @@ "npm": ">=2.0.0" } }, - "node_modules/@angular-devkit/schematics/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/@angular-devkit/schematics/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/@angular-devkit/schematics/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -263,80 +232,66 @@ "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "dependencies": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.10.4" } }, + "node_modules/@babel/compat-data": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz", + "integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==", + "dev": true + }, "node_modules/@babel/core": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.13.tgz", - "integrity": "sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz", + "integrity": "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.12.13", - "@babel/helper-module-transforms": "^7.12.13", - "@babel/helpers": "^7.12.13", - "@babel/parser": "^7.12.13", + "@babel/generator": "^7.13.9", + "@babel/helper-compilation-targets": "^7.13.13", + "@babel/helper-module-transforms": "^7.13.14", + "@babel/helpers": "^7.13.10", + "@babel/parser": "^7.13.15", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13", + "@babel/traverse": "^7.13.15", + "@babel/types": "^7.13.14", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", + "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "lodash": "^4.17.19", - "semver": "^5.4.1", + "semver": "^6.3.0", "source-map": "^0.5.0" }, "engines": { "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" }, - "engines": { - "node": ">=6.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "node_modules/@babel/core/node_modules/@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" + "@babel/highlight": "^7.12.13" } }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@babel/core/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" } }, "node_modules/@babel/core/node_modules/source-map": { @@ -349,12 +304,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.12.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", - "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", + "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", "dev": true, "dependencies": { - "@babel/types": "^7.12.13", + "@babel/types": "^7.13.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -368,6 +323,30 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz", + "integrity": "sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.13.12", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-function-name": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", @@ -389,38 +368,37 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz", - "integrity": "sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", "dev": true, "dependencies": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.13.12" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", - "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", "dev": true, "dependencies": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.13.12" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", - "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", + "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13", - "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", "@babel/helper-split-export-declaration": "^7.12.13", "@babel/helper-validator-identifier": "^7.12.11", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13", - "lodash": "^4.17.19" + "@babel/traverse": "^7.13.13", + "@babel/types": "^7.13.14" } }, "node_modules/@babel/helper-optimise-call-expression": { @@ -433,30 +411,30 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", - "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", "dev": true }, "node_modules/@babel/helper-replace-supers": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", - "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", + "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", "dev": true, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.12" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", - "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", "dev": true, "dependencies": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.13.12" } }, "node_modules/@babel/helper-split-export-declaration": { @@ -474,21 +452,27 @@ "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", "dev": true }, + "node_modules/@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "dev": true + }, "node_modules/@babel/helpers": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.13.tgz", - "integrity": "sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", + "integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", "dev": true, "dependencies": { "@babel/template": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" } }, "node_modules/@babel/highlight": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", - "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.12.11", @@ -559,9 +543,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.12.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", - "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", + "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -577,6 +561,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-bigint": { @@ -586,6 +573,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-class-properties": { @@ -595,6 +585,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-import-meta": { @@ -604,6 +597,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-json-strings": { @@ -613,6 +609,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { @@ -622,6 +621,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { @@ -631,6 +633,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-numeric-separator": { @@ -640,6 +645,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-object-rest-spread": { @@ -649,6 +657,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-optional-catch-binding": { @@ -658,6 +669,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-optional-chaining": { @@ -667,6 +681,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-top-level-await": { @@ -676,6 +693,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/template": { @@ -689,33 +709,38 @@ "@babel/types": "^7.12.13" } }, - "node_modules/@babel/traverse": { + "node_modules/@babel/template/node_modules/@babel/code-frame": { "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", - "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.12.13" + } + }, + "node_modules/@babel/traverse": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", + "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.12.13", + "@babel/generator": "^7.13.9", "@babel/helper-function-name": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13", + "@babel/parser": "^7.13.15", + "@babel/types": "^7.13.14", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "globals": "^11.1.0" } }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "node_modules/@babel/traverse/node_modules/@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" + "@babel/highlight": "^7.12.13" } }, "node_modules/@babel/traverse/node_modules/globals": { @@ -727,16 +752,10 @@ "node": ">=4" } }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@babel/types": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", - "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", + "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.12.11", @@ -804,37 +823,35 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", "dev": true, "dependencies": { - "ms": "2.1.2" + "type-fest": "^0.8.1" }, "engines": { - "node": ">=6.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, "engines": { - "node": ">= 4" + "node": ">=8" } }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@hapi/address": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.1.0.tgz", "integrity": "sha512-SkszZf13HVgGmChdHo/PxchnSaCJ6cetVqLzyciudzZRT0jcOouIF/Q93mgjw8cce+D+4F4C1Z/WrfFN+O3VHQ==", + "deprecated": "Moved to 'npm install @sideway/address'", "dependencies": { "@hapi/hoek": "^9.0.0" } @@ -842,7 +859,8 @@ "node_modules/@hapi/formula": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz", - "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==" + "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==", + "deprecated": "Moved to 'npm install @sideway/formula'" }, "node_modules/@hapi/hoek": { "version": "9.1.1", @@ -853,6 +871,7 @@ "version": "17.1.1", "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz", "integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==", + "deprecated": "Switch to 'npm install joi'", "dependencies": { "@hapi/address": "^4.0.1", "@hapi/formula": "^2.0.0", @@ -864,7 +883,8 @@ "node_modules/@hapi/pinpoint": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw==" + "integrity": "sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw==", + "deprecated": "Moved to 'npm install @sideway/pinpoint'" }, "node_modules/@hapi/topo": { "version": "5.0.0", @@ -906,9 +926,9 @@ } }, "node_modules/@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "engines": { "node": ">=8" @@ -931,6 +951,22 @@ "node": ">= 10.14.2" } }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@jest/core": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", @@ -970,6 +1006,22 @@ "node": ">= 10.14.2" } }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@jest/environment": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", @@ -1054,12 +1106,28 @@ "node-notifier": "^8.0.0" } }, - "node_modules/@jest/reporters/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { "node": ">=0.10.0" } }, @@ -1143,6 +1211,22 @@ "node": ">= 10.14.2" } }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@jest/transform/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1168,6 +1252,22 @@ "node": ">= 10.14.2" } }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@nestjs/cli": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-7.6.0.tgz", @@ -1204,28 +1304,16 @@ "npm": ">= 6.11.0" } }, - "node_modules/@nestjs/cli/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/@nestjs/cli/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/@nestjs/cli/node_modules/acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=8" + "node": ">=0.4.0" } }, "node_modules/@nestjs/cli/node_modules/commander": { @@ -1237,92 +1325,44 @@ "node": ">= 6" } }, - "node_modules/@nestjs/cli/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/@nestjs/cli/node_modules/ora": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", - "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", + "node_modules/@nestjs/cli/node_modules/enhanced-resolve": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", + "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", "dev": true, "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.13.0" } }, - "node_modules/@nestjs/cli/node_modules/ora/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "node_modules/@nestjs/cli/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=10" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/@nestjs/cli/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/@nestjs/cli/node_modules/tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" - } - }, - "node_modules/@nestjs/cli/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/@nestjs/cli/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "node": ">=6" } }, "node_modules/@nestjs/cli/node_modules/typescript": { @@ -1338,6 +1378,52 @@ "node": ">=4.2.0" } }, + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.28.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.28.0.tgz", + "integrity": "sha512-1xllYVmA4dIvRjHzwELgW4KjIU1fW4PEuEnjsylz7k7H5HgPOctIq7W1jrt3sKH9yG5d72//XWzsHhfoWvsQVg==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.46", + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/wasm-edit": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0", + "acorn": "^8.0.4", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.7.0", + "es-module-lexer": "^0.4.0", + "eslint-scope": "^5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.1", + "watchpack": "^2.0.0", + "webpack-sources": "^2.1.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, "node_modules/@nestjs/common": { "version": "7.6.15", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-7.6.15.tgz", @@ -1382,6 +1468,11 @@ "lodash.has": "4.5.2", "lodash.set": "4.3.2", "uuid": "8.3.2" + }, + "peerDependencies": { + "@nestjs/common": "^6.10.0 || ^7.0.0", + "reflect-metadata": "^0.1.12", + "rxjs": "^6.0.0" } }, "node_modules/@nestjs/core": { @@ -1425,12 +1516,23 @@ "node_modules/@nestjs/mongoose": { "version": "7.2.4", "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-7.2.4.tgz", - "integrity": "sha512-NTE/IwijFUEJytPHLoHRYe0X5p16W1Awf8tm6I3mWsIUuBnSDfMyN0fy30uVaM/exYvCkMwEsAVpeSeKVPMHxg==" + "integrity": "sha512-NTE/IwijFUEJytPHLoHRYe0X5p16W1Awf8tm6I3mWsIUuBnSDfMyN0fy30uVaM/exYvCkMwEsAVpeSeKVPMHxg==", + "peerDependencies": { + "@nestjs/common": "^6.0.0 || ^7.0.0", + "@nestjs/core": "^6.0.0 || ^7.0.0", + "mongoose": "^5.11.15", + "reflect-metadata": "^0.1.12", + "rxjs": "^6.0.0" + } }, "node_modules/@nestjs/passport": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-7.1.5.tgz", - "integrity": "sha512-Hu9hPxTdBZA0C4GrWTsSflzwsJ99oAk9jqAwpcszdFNqfjMjkPGuCM9QsVZbBP2bE8fxrVrPsNOILS6puY8e/A==" + "integrity": "sha512-Hu9hPxTdBZA0C4GrWTsSflzwsJ99oAk9jqAwpcszdFNqfjMjkPGuCM9QsVZbBP2bE8fxrVrPsNOILS6puY8e/A==", + "peerDependencies": { + "@nestjs/common": "^6.0.0 || ^7.0.0", + "passport": "^0.4.0" + } }, "node_modules/@nestjs/platform-express": { "version": "7.6.15", @@ -1544,11 +1646,21 @@ "readable-stream": "^3.4.0" } }, - "node_modules/@nestjs/schematics/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "node_modules/@nestjs/schematics/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, "node_modules/@nestjs/schematics/node_modules/ora": { "version": "5.3.0", @@ -1572,20 +1684,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nestjs/schematics/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@nestjs/schematics/node_modules/rxjs": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", @@ -1598,35 +1696,6 @@ "npm": ">=2.0.0" } }, - "node_modules/@nestjs/schematics/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/@nestjs/schematics/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/@nestjs/schematics/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -1639,6 +1708,10 @@ "integrity": "sha512-w2PpLKzQOB8rJ+vMOy28xm8jwE8VjJfA9U+KOm0H0OY62g2oOWJ+OQPSDogP7XxAzZwq+Bt8wNU2oS8+z6v6Zg==", "dependencies": { "path-to-regexp": "0.1.7" + }, + "peerDependencies": { + "@nestjs/common": "^6.0.0 || ^7.0.0", + "@nestjs/core": "^6.0.0 || ^7.0.0" } }, "node_modules/@nestjs/serve-static/node_modules/path-to-regexp": { @@ -1741,6 +1814,21 @@ "npm": ">=5.0.0" } }, + "node_modules/@nuxtjs/opencollective/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@octokit/auth-token": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", @@ -1776,15 +1864,6 @@ "universal-user-agent": "^6.0.0" } }, - "node_modules/@octokit/endpoint/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@octokit/graphql": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.1.tgz", @@ -1863,15 +1942,6 @@ "once": "^1.4.0" } }, - "node_modules/@octokit/request/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@octokit/rest": { "version": "18.5.2", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.5.2.tgz", @@ -1926,50 +1996,9 @@ }, "engines": { "node": ">=8" - } - }, - "node_modules/@release-it/conventional-changelog/node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/@release-it/conventional-changelog/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@release-it/conventional-changelog/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "node_modules/@release-it/conventional-changelog/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "peerDependencies": { + "release-it": "^14.0.0" } }, "node_modules/@schematics/schematics": { @@ -1993,12 +2022,15 @@ "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==", "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, "node_modules/@sinonjs/commons": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", - "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" @@ -2037,12 +2069,16 @@ }, "engines": { "node": ">=10.15.0" + }, + "peerDependencies": { + "@types/mongoose": "^5.10.2", + "mongoose": "5.10.0 - 5.10.18" } }, "node_modules/@types/babel__core": { - "version": "7.1.12", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", - "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "version": "7.1.14", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", + "integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -2072,9 +2108,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", - "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz", + "integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==", "dev": true, "dependencies": { "@babel/types": "^7.3.0" @@ -2094,7 +2130,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -2141,9 +2176,9 @@ } }, "node_modules/@types/eslint": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", - "integrity": "sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw==", + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.8.tgz", + "integrity": "sha512-RTKvBsfz0T8CKOGZMfuluDNyMFHnu5lvNr4hWEsQeHXH6FcmIDIozOyWMh36nLGMwVd5UFNXC2xztA8lln22MQ==", "dev": true, "dependencies": { "@types/estree": "*", @@ -2179,9 +2214,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.18", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz", - "integrity": "sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", "dev": true, "dependencies": { "@types/node": "*", @@ -2196,9 +2231,9 @@ "dev": true }, "node_modules/@types/graceful-fs": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", - "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", "dev": true, "dependencies": { "@types/node": "*" @@ -2274,6 +2309,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/jsonschema/-/jsonschema-1.1.1.tgz", "integrity": "sha1-CHA9/gdAEOjoKRIxEVlK9zH1exo=", + "deprecated": "This is a stub types definition for jsonschema (https://github.com/tdegrunt/jsonschema). jsonschema provides its own type definitions, so you don't need @types/jsonschema installed!", "dev": true, "dependencies": { "jsonschema": "*" @@ -2315,10 +2351,9 @@ "dev": true }, "node_modules/@types/mongodb": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.5.tgz", - "integrity": "sha512-XbG9+2wNaEwUn5DlhgN4ogjUYYzvjIsH6gwPvXXoTgfiQqUNq41RNxOqO+lrdpCjlRKtt/Pv7ZgSl7paQ/GUjw==", - "dev": true, + "version": "3.6.12", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.12.tgz", + "integrity": "sha512-49aEzQD5VdHPxyd5dRyQdqEveAg9LanwrH8RQipnMuulwzKmODXIZRp0umtxi1eBUfEusRkoy8AVOMr+kVuFog==", "dependencies": { "@types/bson": "*", "@types/node": "*" @@ -2328,7 +2363,6 @@ "version": "5.10.1", "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.10.1.tgz", "integrity": "sha512-5yqbLHOyCQhUb7GPGW0A2dauUbhwgBvUWMzYcaUQiHdLZ8slgRp2R6i8FETZ+t5xeXpfhylYp9U7dAng7WamqQ==", - "dev": true, "dependencies": { "@types/mongodb": "*", "@types/node": "*" @@ -2388,15 +2422,15 @@ } }, "node_modules/@types/prettier": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.6.tgz", - "integrity": "sha512-6gOkRe7OIioWAXfnO/2lFiv+SJichKVSys1mSsgyrYHSEjk8Ctv4tSR/Odvnu+HWlH2C8j53dahU03XmQdd5fA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", + "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", "dev": true }, "node_modules/@types/qs": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", - "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", "dev": true }, "node_modules/@types/range-parser": { @@ -2541,29 +2575,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@typescript-eslint/experimental-utils": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz", @@ -2615,29 +2626,6 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@typescript-eslint/scope-manager": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.21.0.tgz", @@ -2695,29 +2683,6 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.21.0.tgz", @@ -2923,9 +2888,9 @@ } }, "node_modules/acorn": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.5.tgz", - "integrity": "sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2944,23 +2909,14 @@ "acorn-walk": "^7.1.1" } }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/acorn-jsx": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, "node_modules/acorn-walk": { "version": "7.2.0", @@ -2994,29 +2950,6 @@ "node": ">= 6.0.0" } }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3026,13 +2959,20 @@ "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/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } }, "node_modules/amdefine": { "version": "1.0.1", @@ -3060,21 +3000,6 @@ "node": ">=6" } }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/ansi-align/node_modules/string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -3111,15 +3036,30 @@ } }, "node_modules/ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "dependencies": { - "type-fest": "^0.11.0" + "type-fest": "^0.21.3" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { @@ -3139,6 +3079,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/any-promise": { @@ -3147,9 +3090,9 @@ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" }, "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -3378,6 +3321,25 @@ }, "engines": { "node": ">= 10.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/babel-plugin-istanbul": { @@ -3429,6 +3391,9 @@ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/babel-preset-jest": { @@ -3442,6 +3407,9 @@ }, "engines": { "node": ">= 10.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/backo2": { @@ -3450,9 +3418,9 @@ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/barse": { "version": "0.4.3", @@ -3462,6 +3430,11 @@ "readable-stream": "~1.0.2" } }, + "node_modules/barse/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, "node_modules/barse/node_modules/readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -3473,6 +3446,11 @@ "string_decoder": "~0.10.x" } }, + "node_modules/barse/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "node_modules/base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", @@ -3503,44 +3481,6 @@ "node": ">=0.10.0" } }, - "node_modules/base/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/base64-arraybuffer": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", @@ -3553,7 +3493,21 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "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/base64id": { "version": "2.0.0", @@ -3641,6 +3595,11 @@ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" }, + "node_modules/bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, "node_modules/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -3661,15 +3620,28 @@ "node": ">= 0.8" } }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, "node_modules/boxen": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.0.tgz", - "integrity": "sha512-5bvsqw+hhgUi3oYGK0Vf4WpIkyemp60WBInn7+WNfoISzAqk/HX4L7WNROq38E6UR/y3YADpv6pEm4BfkeEAdA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", + "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", "dev": true, "dependencies": { "ansi-align": "^3.0.0", @@ -3683,6 +3655,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/boxen/node_modules/camelcase": { @@ -3692,15 +3667,54 @@ "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/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==", + "node_modules/boxen/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/boxen/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/brace-expansion": { @@ -3747,6 +3761,10 @@ }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" } }, "node_modules/bs-logger": { @@ -3771,9 +3789,9 @@ } }, "node_modules/bson": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", - "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", "engines": { "node": ">=0.6.19" } @@ -3783,6 +3801,20 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "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": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -3819,6 +3851,27 @@ "node": ">=0.8.0" } }, + "node_modules/busboy/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/busboy/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/busboy/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -3882,12 +3935,17 @@ "node": ">=8" } }, - "node_modules/cacheable-request/node_modules/normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "engines": { - "node": ">=8" + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/callsites": { @@ -3918,14 +3976,26 @@ "map-obj": "^4.0.0", "quick-lru": "^4.0.1" }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001192", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001192.tgz", - "integrity": "sha512-63OrUnwJj5T1rUmoyqYTdRWBqFFxZFlyZnRRjDR8NSUQFB6A+j/uBORU/SyJ5WzDLg4SPiZH40hQCBNdZ/jmAw==", + "version": "1.0.30001208", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", + "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==", "dev": true }, "node_modules/capture-exit": { @@ -3946,15 +4016,16 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, "node_modules/char-regex": { @@ -3973,32 +4044,34 @@ "dev": true }, "node_modules/cheerio": { - "version": "1.0.0-rc.5", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.5.tgz", - "integrity": "sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw==", + "version": "1.0.0-rc.6", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.6.tgz", + "integrity": "sha512-hjx1XE1M/D5pAtMgvWwE21QClmAEeGHOIDfycgmndisdNgI6PE1cGRQkMGBcsbUbmEQyWu5PJLUcAOjtQS8DWw==", "dependencies": { - "cheerio-select-tmp": "^0.1.0", - "dom-serializer": "~1.2.0", - "domhandler": "^4.0.0", - "entities": "~2.1.0", - "htmlparser2": "^6.0.0", - "parse5": "^6.0.0", - "parse5-htmlparser2-tree-adapter": "^6.0.0" + "cheerio-select": "^1.3.0", + "dom-serializer": "^1.3.1", + "domhandler": "^4.1.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1" }, "engines": { "node": ">= 0.12" } }, - "node_modules/cheerio-select-tmp": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz", - "integrity": "sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ==", + "node_modules/cheerio-select": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.3.0.tgz", + "integrity": "sha512-mLgqdHxVOQyhOIkG5QnRkDg7h817Dkf0dAvlCio2TJMmR72cJKH0bF28SHXvLkVrGcGOiub0/Bs/CMnPeQO7qw==", "dependencies": { - "css-select": "^3.1.2", - "css-what": "^4.0.0", - "domelementtype": "^2.1.0", - "domhandler": "^4.0.0", - "domutils": "^2.4.4" + "css-select": "^4.0.0", + "css-what": "^5.0.0", + "domelementtype": "^2.2.0", + "domhandler": "^4.1.0", + "domutils": "^2.5.2" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, "node_modules/chokidar": { @@ -4023,23 +4096,14 @@ } }, "node_modules/chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, "engines": { "node": ">=6.0" } }, - "node_modules/chrome-trace-event/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/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -4084,101 +4148,135 @@ "node": ">=0.10.0" } }, - "node_modules/class-validator": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.1.tgz", - "integrity": "sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg==", + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "dependencies": { - "@types/validator": "^13.1.3", - "libphonenumber-js": "^1.9.7", - "validator": "^13.5.2" + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "dependencies": { - "restore-cursor": "^3.1.0" + "kind-of": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/cli-spinners": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz", - "integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==", + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/cli-table3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "dependencies": { - "object-assign": "^4.1.0", - "string-width": "^2.1.1" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "colors": "^1.1.2" + "node": ">=0.10.0" } }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "node_modules/class-validator": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.1.tgz", + "integrity": "sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg==", + "dependencies": { + "@types/validator": "^13.1.3", + "libphonenumber-js": "^1.9.7", + "validator": "^13.5.2" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "node_modules/cli-spinners": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", + "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", "dev": true, "dependencies": { - "ansi-regex": "^3.0.0" + "object-assign": "^4.1.0", + "string-width": "^2.1.1" }, "engines": { - "node": ">=4" + "node": ">=6" + }, + "optionalDependencies": { + "colors": "^1.1.2" } }, "node_modules/cli-width": { @@ -4201,6 +4299,35 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -4357,46 +4484,20 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, "engines": [ - "node >= 0.8" + "node >= 6.0" ], "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", @@ -4696,50 +4797,6 @@ "node": ">=10" } }, - "node_modules/conventional-recommended-bump/node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/conventional-recommended-bump/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/conventional-recommended-bump/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "node_modules/conventional-recommended-bump/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -4802,18 +4859,6 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "node_modules/copyfiles/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/copyfiles/node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -4919,23 +4964,29 @@ } }, "node_modules/css-select": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", - "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.0.0.tgz", + "integrity": "sha512-I7favumBlDP/nuHBKLfL5RqvlvRdn/W29evvWJ+TaoGPm7QD+xSIN5eY2dyGjtkUmemh02TZrqJb4B8DWo6PoQ==", "dependencies": { "boolbase": "^1.0.0", - "css-what": "^4.0.0", - "domhandler": "^4.0.0", - "domutils": "^2.4.3", + "css-what": "^5.0.0", + "domhandler": "^4.1.0", + "domutils": "^2.5.1", "nth-check": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, "node_modules/css-what": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", - "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.0.tgz", + "integrity": "sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA==", "engines": { "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, "node_modules/cssom": { @@ -5022,11 +5073,20 @@ } }, "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decamelize": { @@ -5084,6 +5144,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/decompress-response/node_modules/mimic-response": { @@ -5092,6 +5155,9 @@ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-extend": { @@ -5127,9 +5193,9 @@ } }, "node_modules/defer-to-connect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", - "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "engines": { "node": ">=10" } @@ -5147,44 +5213,6 @@ "node": ">=0.10.0" } }, - "node_modules/define-property/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -5254,13 +5282,34 @@ "node": ">=0.8.0" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" + "node_modules/dicer/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/dicer/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/dicer/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" } }, "node_modules/diff-sequences": { @@ -5315,19 +5364,28 @@ } }, "node_modules/dom-serializer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", - "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", + "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, "node_modules/domelementtype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", - "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, "node_modules/domexception": { "version": "2.0.1", @@ -5351,24 +5409,30 @@ } }, "node_modules/domhandler": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", - "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.1.0.tgz", + "integrity": "sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ==", "dependencies": { - "domelementtype": "^2.1.0" + "domelementtype": "^2.2.0" }, "engines": { "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, "node_modules/domutils": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", - "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.5.2.tgz", + "integrity": "sha512-MHTthCb1zj8f1GVfRpeZUbohQf/HdBos0oX5gZcQFepOZPLLRyj6Wn7XS7EMnY7CVpwv8863u2vyE83Hfu28HQ==", "dependencies": { "dom-serializer": "^1.0.1", - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0" + "domelementtype": "^2.2.0", + "domhandler": "^4.1.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, "node_modules/dot-prop": { @@ -5425,9 +5489,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-to-chromium": { - "version": "1.3.674", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.674.tgz", - "integrity": "sha512-DBmEKRVYLZAoQSW+AmLcTF5Bpwhk4RUkobtzXVDlfPPYIlbsH3Jfg3QbBjAfFcRARzMIo4YiMhp3N+RnMuo1Eg==", + "version": "1.3.711", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.711.tgz", + "integrity": "sha512-XbklBVCDiUeho0PZQCjC25Ha6uBwqqJeyDhPLwLwfWRAo4x+FZFsmu1pPPkXT+B4MQMQoQULfyaMltDopfeiHQ==", "dev": true }, "node_modules/emittery": { @@ -5437,12 +5501,15 @@ "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "node_modules/emojis-list": { @@ -5487,9 +5554,9 @@ } }, "node_modules/engine.io-client": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", - "integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.1.tgz", + "integrity": "sha512-oVu9kBkGbcggulyVF0kz6BV3ganqUeqXvD79WOFKa+11oK692w1NyFkuEj4xrkFRpZhn92QOqTk4RQq5LiBXbQ==", "dependencies": { "component-emitter": "~1.3.0", "component-inherit": "0.0.3", @@ -5512,6 +5579,11 @@ "ms": "2.0.0" } }, + "node_modules/engine.io-client/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "node_modules/engine.io-parser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", @@ -5536,15 +5608,11 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", "dependencies": { "ms": "^2.1.1" } }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/enhanced-resolve": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", @@ -5572,9 +5640,12 @@ } }, "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/errno": { "version": "0.1.8", @@ -5635,13 +5706,13 @@ } }, "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "dependencies": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1" }, @@ -5650,12 +5721,21 @@ "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=4.0" + "node": ">=6.0" }, "optionalDependencies": { "source-map": "~0.6.1" } }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/escodegen/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -5771,6 +5851,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", + "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-plugin-jest": { "version": "24.3.4", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.3.4.tgz", @@ -5792,6 +5884,27 @@ } } }, + "node_modules/eslint-plugin-prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", + "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "prettier": ">=1.13.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", @@ -5815,6 +5928,9 @@ }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { @@ -5835,67 +5951,20 @@ "node": ">=10" } }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz", - "integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==", + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/eslint/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" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/espree": { @@ -5912,18 +5981,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/espree/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/espree/node_modules/eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", @@ -6025,21 +6082,22 @@ "node_modules/event-to-promise": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/event-to-promise/-/event-to-promise-0.7.0.tgz", - "integrity": "sha1-ywffzUGNoiIdkPd+q3E7wjXiCQ8=" + "integrity": "sha1-ywffzUGNoiIdkPd+q3E7wjXiCQ8=", + "deprecated": "Use promise-toolbox/fromEvent instead" }, "node_modules/events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "engines": { "node": ">=0.8.x" } }, "node_modules/exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", "dev": true }, "node_modules/execa": { @@ -6060,6 +6118,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/exit": { @@ -6089,6 +6150,15 @@ "node": ">=0.10.0" } }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/expand-brackets/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -6113,6 +6183,92 @@ "node": ">=0.10.0" } }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/expect": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", @@ -6170,13 +6326,26 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/extend": { - "version": "3.0.2", + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/extend": { + "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, @@ -6193,18 +6362,6 @@ "node": ">=0.10.0" } }, - "node_modules/extend-shallow/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -6262,40 +6419,11 @@ "node": ">=0.10.0" } }, - "node_modules/extglob/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, "engines": { "node": ">=0.10.0" } @@ -6313,6 +6441,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", @@ -6347,9 +6481,9 @@ "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, "node_modules/fastq": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.1.tgz", - "integrity": "sha512-AWuv6Ery3pM+dY7LYS8YIaCiQvUaos9OB1RyNgaOWnaX+Tik7Onvcsf8x8c+YtDeT0maYLniBip2hox5KtEXXA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -6383,6 +6517,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/file-entry-cache": { @@ -6435,6 +6572,19 @@ "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "node_modules/find-cache-dir": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", @@ -6500,11 +6650,22 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz", - "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", + "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, "node_modules/for-in": { @@ -6548,24 +6709,44 @@ "yarn": ">=1.0.0" } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, "node_modules/formidable": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", - "dev": true + "dev": true, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } }, "node_modules/forwarded": { "version": "0.1.2", @@ -6617,9 +6798,9 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz", - "integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", "dev": true }, "node_modules/fs.realpath": { @@ -6725,6 +6906,20 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -6785,12 +6980,6 @@ "node": ">=0.10.0" } }, - "node_modules/get-pkg-repo/node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, "node_modules/get-pkg-repo/node_modules/indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -6809,6 +6998,22 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "node_modules/get-pkg-repo/node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/get-pkg-repo/node_modules/map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", @@ -6851,6 +7056,18 @@ "validate-npm-package-license": "^3.0.1" } }, + "node_modules/get-pkg-repo/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/get-pkg-repo/node_modules/path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -6950,6 +7167,18 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/get-pkg-repo/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/get-pkg-repo/node_modules/strip-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", @@ -7014,6 +7243,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/get-value": { @@ -7132,12 +7364,15 @@ }, "engines": { "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "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" @@ -7162,6 +7397,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/global-dirs/node_modules/ini": { @@ -7174,24 +7412,18 @@ } }, "node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", + "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", "dev": true, "dependencies": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" - } - }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { @@ -7214,6 +7446,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby/node_modules/ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/got": { "version": "11.8.2", "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", @@ -7239,9 +7480,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "node_modules/graceful-readlink": { @@ -7298,6 +7539,7 @@ "version": "5.1.5", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -7335,11 +7577,6 @@ "isarray": "2.0.1" } }, - "node_modules/has-binary2/node_modules/isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - }, "node_modules/has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", @@ -7353,6 +7590,18 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -7434,16 +7683,10 @@ } }, "node_modules/hosted-git-info": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", - "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, "node_modules/html-encoding-sniffer": { "version": "2.0.1", @@ -7464,13 +7707,20 @@ "dev": true }, "node_modules/htmlparser2": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.0.0.tgz", - "integrity": "sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", - "domutils": "^2.4.4", + "domutils": "^2.5.2", "entities": "^2.0.0" } }, @@ -7494,6 +7744,11 @@ "node": ">= 0.6" } }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -7509,9 +7764,9 @@ } }, "node_modules/http2-wrapper": { - "version": "1.0.0-beta.5.2", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", - "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" @@ -7520,14 +7775,6 @@ "node": ">=10.19.0" } }, - "node_modules/http2-wrapper/node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - } - }, "node_modules/https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -7541,33 +7788,10 @@ "node": ">= 6" } }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, "engines": { "node": ">=8.12.0" @@ -7588,12 +7812,26 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "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/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true, "engines": { "node": ">= 4" @@ -7622,15 +7860,9 @@ }, "engines": { "node": ">=6" - } - }, - "node_modules/import-fresh/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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/import-from": { @@ -7712,9 +7944,9 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -7746,6 +7978,51 @@ "node": ">=8.0.0" } }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/inquirer/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -7755,15 +8032,6 @@ "node": ">= 0.10" } }, - "node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -7773,24 +8041,12 @@ } }, "node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "kind-of": "^6.0.0" }, "engines": { "node": ">=0.10.0" @@ -7814,6 +8070,21 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -7844,59 +8115,41 @@ "dev": true, "dependencies": { "has": "^1.0.3" - } - }, - "node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "kind-of": "^6.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, "optional": true, "bin": { @@ -7904,13 +8157,31 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, "engines": { "node": ">=0.10.0" } @@ -7931,15 +8202,18 @@ "dev": true, "engines": { "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "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", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/is-generator-fn": { @@ -7974,6 +8248,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-interactive": { @@ -7991,6 +8268,9 @@ "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-number": { @@ -8002,6 +8282,18 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -8012,9 +8304,9 @@ } }, "node_modules/is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "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" @@ -8030,21 +8322,18 @@ } }, "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-potential-custom-element-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, "node_modules/is-ssh": { @@ -8065,6 +8354,18 @@ "node": ">=8" } }, + "node_modules/is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", @@ -8128,9 +8429,9 @@ "dev": true }, "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, "node_modules/isexe": { "version": "2.0.0", @@ -8213,24 +8514,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/istanbul-lib-source-maps/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8292,9 +8575,147 @@ "node": ">= 10.14.2" } }, - "node_modules/jest-config": { + "node_modules/jest-cli": { "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/jest-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/jest-cli/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", "dev": true, "dependencies": { @@ -8319,6 +8740,30 @@ }, "engines": { "node": ">= 10.14.2" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/jest-diff": { @@ -8336,6 +8781,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-docblock": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", @@ -8364,6 +8825,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-environment-jsdom": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", @@ -8464,6 +8941,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-jasmine2/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-leak-detector": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", @@ -8492,6 +8985,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-message-util": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", @@ -8512,6 +9021,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-mock": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", @@ -8532,6 +9057,14 @@ "dev": true, "engines": { "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, "node_modules/jest-regex-util": { @@ -8576,11 +9109,21 @@ "node": ">= 10.14.2" } }, - "node_modules/jest-resolve/node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, "node_modules/jest-resolve/node_modules/normalize-package-data": { "version": "2.5.0", @@ -8621,6 +9164,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/jest-resolve/node_modules/read-pkg/node_modules/type-fest": { @@ -8681,6 +9227,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-runtime": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", @@ -8722,6 +9284,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-runtime/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -8733,15 +9311,35 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/jest-runtime/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "node_modules/jest-runtime/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/jest-runtime/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { "node": ">=8" } }, + "node_modules/jest-runtime/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-runtime/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -8757,9 +9355,9 @@ } }, "node_modules/jest-runtime/node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "node_modules/jest-runtime/node_modules/yargs": { @@ -8837,6 +9435,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-util": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", @@ -8854,6 +9468,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-validate": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", @@ -8878,6 +9508,25 @@ "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/jest-watcher": { @@ -8898,6 +9547,22 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", @@ -8912,99 +9577,6 @@ "node": ">= 10.13.0" } }, - "node_modules/jest/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/jest/node_modules/jest-cli": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", - "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", - "dev": true, - "dependencies": { - "@jest/core": "^26.6.3", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.6.3", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "prompts": "^2.0.1", - "yargs": "^15.4.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest/node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "node_modules/jest/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9030,72 +9602,60 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "node_modules/jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.2.tgz", + "integrity": "sha512-JxNtPt9C1ut85boCbJmffaQ06NBnzkQY/MWO3YxPW8IWS38A26z+B1oBvA9LwKrytewdfymnhi4UNH3/RAgZrg==", "dev": true, "dependencies": { - "abab": "^2.0.3", - "acorn": "^7.1.1", + "abab": "^2.0.5", + "acorn": "^8.1.0", "acorn-globals": "^6.0.0", "cssom": "^0.4.4", - "cssstyle": "^2.2.0", + "cssstyle": "^2.3.0", "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", + "decimal.js": "^10.2.1", "domexception": "^2.0.1", - "escodegen": "^1.14.1", + "escodegen": "^2.0.0", "html-encoding-sniffer": "^2.0.1", "is-potential-custom-element-name": "^1.0.0", "nwsapi": "^2.2.0", - "parse5": "5.1.1", + "parse5": "6.0.1", "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", + "tough-cookie": "^4.0.0", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", + "whatwg-url": "^8.5.0", + "ws": "^7.4.4", "xml-name-validator": "^3.0.0" }, "engines": { "node": ">=10" - } - }, - "node_modules/jsdom/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsdom/node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } }, - "node_modules/jsdom/node_modules/tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "node_modules/jsdom/node_modules/acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", "dev": true, - "dependencies": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=6" + "node": ">=0.4.0" } }, "node_modules/jsesc": { @@ -9149,15 +9709,18 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "dependencies": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsonc-parser": { @@ -9172,8 +9735,10 @@ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.6", "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, "node_modules/jsonparse": { @@ -9230,11 +9795,6 @@ "npm": ">=1.4.28" } }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/jsonwebtoken/node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -9350,9 +9910,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.9.tgz", - "integrity": "sha512-RhtAvacOkq4wc+RrswYE9MteJQy/1yd4lgLJTdL+HdYYRLZbERFv4ZRj/Nt+/gIB2xVSgW0MLNqb32hx042W+g==" + "version": "1.9.16", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.16.tgz", + "integrity": "sha512-PaHT7nTtnejZ0HHekAaA0olv6BUTKZGtKM4SCQS0yE3XjFuVo/tjePMHUAr32FKwIZfyPky1ExMUuaiBAUmV6w==" }, "node_modules/lines-and-columns": { "version": "1.1.6", @@ -9361,43 +9921,49 @@ "dev": true }, "node_modules/load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "dependencies": { "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, "node_modules/load-json-file/node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "dependencies": { - "error-ex": "^1.2.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" } }, "node_modules/load-json-file/node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true, - "dependencies": { - "is-utf8": "^0.2.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, "node_modules/loader-runner": { @@ -9423,21 +9989,6 @@ "node": ">=8.9.0" } }, - "node_modules/loader-utils/node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -9464,6 +10015,18 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -9520,18 +10083,18 @@ "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, "node_modules/lodash.toarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", "dev": true }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -9547,12 +10110,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/loglevel": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==", "engines": { "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" } }, "node_modules/long": { @@ -9599,6 +10181,9 @@ "dev": true, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/magic-string": { @@ -9620,6 +10205,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/make-dir/node_modules/semver": { @@ -9656,12 +10244,15 @@ } }, "node_modules/map-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", - "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/map-visit": { @@ -9697,12 +10288,12 @@ } }, "node_modules/memfs": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz", - "integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.2.tgz", + "integrity": "sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==", "dev": true, "dependencies": { - "fs-monkey": "1.0.1" + "fs-monkey": "1.0.3" }, "engines": { "node": ">= 4.0.0" @@ -9777,14 +10368,11 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, "node_modules/meow/node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -9812,6 +10400,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { @@ -9860,6 +10451,9 @@ "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/merge-descriptors": { @@ -9987,6 +10581,17 @@ "node": ">=4" } }, + "node_modules/migrate/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/migrate/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -10096,27 +10701,16 @@ "node": ">=0.10.0" } }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, "bin": { "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/modify-values": { @@ -10137,9 +10731,9 @@ } }, "node_modules/moment-timezone": { - "version": "0.5.32", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", - "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", "dependencies": { "moment": ">= 2.9.0" }, @@ -10148,14 +10742,16 @@ } }, "node_modules/mongodb": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", - "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz", + "integrity": "sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==", + "dev": true, + "optional": true, "dependencies": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", - "require_optional": "^1.0.1", + "optional-require": "^1.0.2", "safe-buffer": "^5.1.2" }, "engines": { @@ -10163,6 +10759,26 @@ }, "optionalDependencies": { "saslprep": "^1.0.0" + }, + "peerDependenciesMeta": { + "aws4": { + "optional": true + }, + "bson-ext": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "mongodb-extjson": { + "optional": true + }, + "snappy": { + "optional": true + } } }, "node_modules/mongodb-memory-server": { @@ -10217,41 +10833,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mongodb-memory-server-core/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mongodb-memory-server-core/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mongodb-memory-server-core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mongodb-memory-server-core/node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -10283,22 +10864,76 @@ }, "engines": { "node": ">=4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", + "peerDependencies": { + "mongoose": "*" + } + }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", + "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", + "dependencies": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=4" + }, + "optionalDependencies": { + "saslprep": "^1.0.0" + }, + "peerDependenciesMeta": { + "aws4": { + "optional": true + }, + "bson-ext": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "mongodb-extjson": { + "optional": true + }, + "snappy": { + "optional": true + } } }, - "node_modules/mongoose-legacy-pluralize": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", - "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" - }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/mongoose/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/mpath": { "version": "0.7.0", @@ -10323,11 +10958,6 @@ "node": ">=4.0.0" } }, - "node_modules/mquery/node_modules/bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, "node_modules/mquery/node_modules/debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -10336,11 +10966,16 @@ "ms": "2.0.0" } }, - "node_modules/ms": { + "node_modules/mquery/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/multer": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", @@ -10359,6 +10994,58 @@ "node": ">= 0.10.0" } }, + "node_modules/multer/node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/multer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/multer/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/multer/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -10415,6 +11102,47 @@ }, "engines": { "node": ">=0.10" + }, + "peerDependencies": { + "@nestjs/common": "^6 || ^7", + "@nestjs/core": "^6 || ^7", + "commander": "^5 || ^6" + } + }, + "node_modules/nestjs-console/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/nestjs-console/node_modules/ora": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", + "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==", + "dependencies": { + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.4.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/nestjs-typegoose": { @@ -10427,6 +11155,12 @@ }, "engines": { "node": ">=8.10.0" + }, + "peerDependencies": { + "@nestjs/common": "^6.10.1 || ^7.0.0", + "@nestjs/core": "^6.10.1 || ^7.0.0", + "@typegoose/typegoose": "^6.2.1 || ^7.0.0", + "mongoose": "^5.10.6" } }, "node_modules/nice-try": { @@ -10468,9 +11202,9 @@ } }, "node_modules/node-notifier": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", - "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", "dev": true, "optional": true, "dependencies": { @@ -10498,6 +11232,12 @@ "readable-stream": "~1.0.31" } }, + "node_modules/noms/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "node_modules/noms/node_modules/readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -10510,21 +11250,39 @@ "string_decoder": "~0.10.x" } }, + "node_modules/noms/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, "node_modules/normalize-package-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", - "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", "dev": true, "dependencies": { - "hosted-git-info": "^3.0.6", - "resolve": "^1.17.0", - "semver": "^7.3.2", + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" }, "engines": { "node": ">=10" } }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -10535,12 +11293,11 @@ } }, "node_modules/normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", - "dev": true, + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/npm-run-path": { @@ -10561,6 +11318,9 @@ "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", "dependencies": { "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, "node_modules/nwsapi": { @@ -10611,6 +11371,53 @@ "node": ">=0.10.0" } }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-copy/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -10631,6 +11438,15 @@ "node": ">= 6" } }, + "node_modules/object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -10683,12 +11499,15 @@ }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/openid": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/openid/-/openid-2.0.7.tgz", - "integrity": "sha512-xH6qaz/hS55rEX8xURz4HRUO96cpj821WY6UEG9rgcusZ8Jsq54jGWP1EMCjGvgngonw8vgSljM1i2OESv16Gw==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/openid/-/openid-2.0.8.tgz", + "integrity": "sha512-ljI4GE6p4RYn9dLftlXw6TvlA+untAkoWBRpj4qIB4AJQWcDZ2lOVOJQ2tq346ok38mtGDBYRBvp3Q+AsuCBnQ==", "dependencies": { "request": "^2.88.2" }, @@ -10702,6 +11521,16 @@ "integrity": "sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==", "dev": true }, + "node_modules/optional-require": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.2.tgz", + "integrity": "sha512-HZubVd6IfHsbnpdNF/ICaSAzBUEW1TievpkjY3tB4Jnk8L7+pJ3conPzUt3Mn/6OZx9uzTDOHYPGA8/AxYHBOg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=4" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -10720,21 +11549,53 @@ } }, "node_modules/ora": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", - "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", + "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", + "dev": true, "dependencies": { + "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", - "cli-spinners": "^2.4.0", + "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "mute-stream": "0.0.8", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/os-name": { @@ -10748,6 +11609,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/os-tmpdir": { @@ -10760,9 +11624,9 @@ } }, "node_modules/p-cancelable": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", - "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", + "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==", "engines": { "node": ">=8" } @@ -10774,6 +11638,9 @@ "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-finally": { @@ -10795,6 +11662,9 @@ }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { @@ -10872,27 +11742,6 @@ "node": ">=8" } }, - "node_modules/package-json/node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/package-json/node_modules/decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", @@ -10911,18 +11760,6 @@ "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", "dev": true }, - "node_modules/package-json/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/package-json/node_modules/got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -10945,22 +11782,19 @@ "node": ">=8.6" } }, - "node_modules/package-json/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "node_modules/package-json/node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "node_modules/package-json/node_modules/got/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "dependencies": { - "json-buffer": "3.0.0" + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/package-json/node_modules/lowercase-keys": { + "node_modules/package-json/node_modules/got/node_modules/lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", @@ -10969,13 +11803,19 @@ "node": ">=0.10.0" } }, - "node_modules/package-json/node_modules/normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "node_modules/package-json/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "node_modules/package-json/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "json-buffer": "3.0.0" } }, "node_modules/package-json/node_modules/p-cancelable": { @@ -10996,6 +11836,15 @@ "lowercase-keys": "^1.0.0" } }, + "node_modules/package-json/node_modules/responselike/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/package-json/node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -11036,6 +11885,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parse-path": { @@ -11051,12 +11903,18 @@ } }, "node_modules/parse-path/node_modules/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/parse-url": { @@ -11071,6 +11929,15 @@ "protocols": "^1.4.0" } }, + "node_modules/parse-url/node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -11221,6 +12088,9 @@ "dev": true, "engines": { "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pify": { @@ -11338,6 +12208,18 @@ "node": ">=10.13.0" } }, + "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": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", @@ -11393,9 +12275,9 @@ } }, "node_modules/prompts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", - "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", "dev": true, "dependencies": { "kleur": "^3.0.3", @@ -11482,9 +12364,9 @@ } }, "node_modules/query-string": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.0.tgz", - "integrity": "sha512-In3o+lUxlgejoVJgwEdYtdxrmlL0cQWJXj0+kkI7RWVo7hg5AhFtybeKlC9Dpgbr8eOC4ydpEh8017WwyfzqVQ==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", "dev": true, "dependencies": { "decode-uri-component": "^0.2.0", @@ -11494,15 +12376,40 @@ }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "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/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/randombytes": { @@ -11569,9 +12476,9 @@ } }, "node_modules/react-is": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", - "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, "node_modules/read-pkg": { @@ -11668,27 +12575,6 @@ "node": ">=4" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "node_modules/read-pkg/node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/read-pkg/node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -11701,19 +12587,6 @@ "validate-npm-package-license": "^3.0.1" } }, - "node_modules/read-pkg/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/read-pkg/node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -11745,14 +12618,17 @@ } }, "node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/readdirp": { @@ -11822,6 +12698,9 @@ "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, "node_modules/registry-auth-token": { @@ -11890,15 +12769,20 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/bl": { + "node_modules/release-it/node_modules/chalk": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/release-it/node_modules/ci-info": { @@ -11923,17 +12807,11 @@ "node": ">=10" } }, - "node_modules/release-it/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } + "node_modules/release-it/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/release-it/node_modules/execa": { "version": "5.0.0", @@ -11953,6 +12831,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/release-it/node_modules/find-up": { @@ -11966,20 +12847,9 @@ }, "engines": { "node": ">=10" - } - }, - "node_modules/release-it/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" }, - "engines": { - "node": ">= 6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/release-it/node_modules/get-stream": { @@ -11989,6 +12859,9 @@ "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/release-it/node_modules/human-signals": { @@ -12000,12 +12873,6 @@ "node": ">=10.17.0" } }, - "node_modules/release-it/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "node_modules/release-it/node_modules/inquirer": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", @@ -12035,11 +12902,20 @@ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", "integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==", "dev": true, - "dependencies": { - "ci-info": "^3.1.1" - }, - "bin": { - "is-ci": "bin.js" + "dependencies": { + "ci-info": "^3.1.1" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/release-it/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/release-it/node_modules/locate-path": { @@ -12050,32 +12926,6 @@ "dependencies": { "p-locate": "^5.0.0" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/release-it/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/release-it/node_modules/ora": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", - "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, "engines": { "node": ">=10" }, @@ -12093,6 +12943,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/release-it/node_modules/p-locate": { @@ -12105,49 +12958,23 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/release-it/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/release-it/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/release-it/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/release-it/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "node": ">=8" } }, "node_modules/remove-trailing-separator": { @@ -12157,9 +12984,9 @@ "dev": true }, "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12190,6 +13017,7 @@ "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -12226,12 +13054,16 @@ }, "engines": { "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" } }, "node_modules/request-promise-native": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", "dev": true, "dependencies": { "request-promise-core": "1.1.4", @@ -12240,6 +13072,35 @@ }, "engines": { "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" } }, "node_modules/request/node_modules/qs": { @@ -12250,6 +13111,18 @@ "node": ">=0.6" } }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/request/node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -12267,6 +13140,14 @@ "semver": "^5.1.0" } }, + "node_modules/require_optional/node_modules/resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require_optional/node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -12300,13 +13181,16 @@ "dev": true }, "node_modules/resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "dependencies": { - "is-core-module": "^2.1.0", + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/resolve-alpn": { @@ -12336,17 +13220,19 @@ } }, "node_modules/resolve-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "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": ">=0.10.0" + "node": ">=4" } }, "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", "dev": true }, "node_modules/responselike": { @@ -12406,6 +13292,9 @@ }, "bin": { "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rsvp": { @@ -12427,10 +13316,27 @@ } }, "node_modules/run-parallel": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", - "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", - "dev": true + "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/rxjs": { "version": "6.6.7", @@ -12606,6 +13512,15 @@ "node": ">=6" } }, + "node_modules/sane/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sane/node_modules/is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -12856,6 +13771,19 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "node_modules/send/node_modules/ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -12917,6 +13845,27 @@ "node": ">=0.10.0" } }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -12972,6 +13921,20 @@ "dev": true, "optional": true }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/sift": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", @@ -13009,6 +13972,18 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/sliced": { @@ -13033,96 +14008,139 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "is-descriptor": "^1.0.0" + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "dependencies": { - "kind-of": "^6.0.0" + "is-extendable": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "dependencies": { - "kind-of": "^6.0.0" + "kind-of": "^3.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "dependencies": { - "kind-of": "^3.2.0" + "kind-of": "^3.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-util/node_modules/kind-of": { + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", @@ -13134,30 +14152,44 @@ "node": ">=0.10.0" } }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "dependencies": { - "is-descriptor": "^0.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, "engines": { "node": ">=0.10.0" } }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/snapdragon/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -13211,10 +14243,10 @@ "ms": "2.0.0" } }, - "node_modules/socket.io-client/node_modules/isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + "node_modules/socket.io-client/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "node_modules/socket.io-client/node_modules/socket.io-parser": { "version": "3.3.2", @@ -13245,33 +14277,20 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", "dependencies": { "ms": "^2.1.1" } }, - "node_modules/socket.io-parser/node_modules/isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/socket.io/node_modules/debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", "dependencies": { "ms": "^2.1.1" } }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/socketio-jwt-auth": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/socketio-jwt-auth/-/socketio-jwt-auth-0.2.1.tgz", @@ -13423,35 +14442,6 @@ "readable-stream": "^3.0.0" } }, - "node_modules/split2/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/split2/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "node_modules/split2/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -13481,6 +14471,11 @@ "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, "engines": { "node": ">=0.10.0" } @@ -13536,6 +14531,77 @@ "node": ">=0.10.0" } }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -13590,14 +14656,38 @@ } }, "node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "dependencies": { "char-regex": "^1.0.2", @@ -13643,18 +14733,38 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "ansi-regex": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/strip-ansi": { @@ -13669,12 +14779,12 @@ } }, "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-eof": { @@ -13714,6 +14824,9 @@ "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/superagent": { @@ -13738,22 +14851,10 @@ "node": ">= 7.0.0" } }, - "node_modules/superagent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, "node_modules/superagent/node_modules/form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dev": true, "dependencies": { "asynckit": "^0.4.0", @@ -13765,9 +14866,9 @@ } }, "node_modules/superagent/node_modules/mime": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.0.tgz", - "integrity": "sha512-ft3WayFSFUVBuJj7BMLKAQcSlItKtfjsKDDsii3rqFDAZ7t11zRe8ASw/GlmivGwVUYtwkQrxiGGpL6gFvB0ag==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true, "bin": { "mime": "cli.js" @@ -13776,48 +14877,19 @@ "node": ">=4.0.0" } }, - "node_modules/superagent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/superagent/node_modules/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/superagent/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "side-channel": "^1.0.4" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/superagent/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "node_modules/superagent/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/supertest": { @@ -13845,9 +14917,9 @@ } }, "node_modules/supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", "dev": true, "dependencies": { "has-flag": "^4.0.0", @@ -13873,13 +14945,18 @@ "dev": true }, "node_modules/table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", - "dev": true, - "dependencies": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", + "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "lodash.clonedeep": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.0" }, @@ -13888,15 +14965,34 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", - "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.0.5.tgz", + "integrity": "sha512-RkiLa/AeJx7+9OvniQ/qeWu0w74A8DiPPBclQ6ji3ZQkv5KamO+QGpqmi7O4JIw3rHGUXZ6CoP9tsAkn3gyazg==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/table/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/table/node_modules/json-schema-traverse": { @@ -13905,6 +15001,20 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -13941,55 +15051,6 @@ "readable-stream": "^3.4.0" } }, - "node_modules/tar-stream/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tar-stream/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/tar-stream/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", @@ -14035,12 +15096,15 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/terser": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.0.tgz", - "integrity": "sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.1.tgz", + "integrity": "sha512-yv9YLFQQ+3ZqgWCUk+pvNJwgUTdlIxUk1WTN+RnaFJe2L7ipG2csPT0ra2XRm7Cs8cxN7QXmK1rFzEwYEQkzXw==", "dev": true, "dependencies": { "commander": "^2.20.0", @@ -14069,6 +15133,13 @@ }, "engines": { "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" } }, "node_modules/terser-webpack-plugin/node_modules/p-limit": { @@ -14081,6 +15152,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { @@ -14095,6 +15169,10 @@ }, "engines": { "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/terser-webpack-plugin/node_modules/source-map": { @@ -14162,35 +15240,6 @@ "readable-stream": "3" } }, - "node_modules/through2/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/through2/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -14292,15 +15341,26 @@ } }, "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" }, "engines": { - "node": ">=0.8" + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" } }, "node_modules/tr46": { @@ -14370,33 +15430,6 @@ "typescript": ">=3.8 <5.0" } }, - "node_modules/ts-jest/node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ts-jest/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-loader": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.1.0.tgz", @@ -14417,6 +15450,22 @@ "webpack": "*" } }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", @@ -14438,6 +15487,9 @@ }, "engines": { "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" } }, "node_modules/tsconfig-paths": { @@ -14463,6 +15515,22 @@ "tsconfig-paths": "^3.9.0" } }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/tsconfig-paths-webpack-plugin/node_modules/enhanced-resolve": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", @@ -14485,21 +15553,45 @@ "node": ">=6" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" }, "node_modules/tsutils": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz", - "integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==", + "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/tsutils/node_modules/tslib": { @@ -14546,12 +15638,15 @@ } }, "node_modules/type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "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": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/type-is": { @@ -14599,9 +15694,9 @@ } }, "node_modules/uglify-js": { - "version": "3.12.8", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", - "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.3.tgz", + "integrity": "sha512-otIc7O9LyxpUcQoXzj2hL4LPWKklO6LJWoJUzNa8A17Xgi4fOeDC8FBDOLHnC/Slo1CQgsZMcM6as0M76BZaig==", "dev": true, "optional": true, "bin": { @@ -14634,6 +15729,15 @@ "node": ">=0.10.0" } }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -14755,6 +15859,25 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/uri-js": { @@ -14769,6 +15892,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, "node_modules/url-join": { @@ -14820,15 +15944,15 @@ } }, "node_modules/v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "node_modules/v8-to-istanbul": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz", - "integrity": "sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz", + "integrity": "sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "^2.0.1", @@ -14944,10 +16068,11 @@ } }, "node_modules/webpack": { - "version": "5.28.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.28.0.tgz", - "integrity": "sha512-1xllYVmA4dIvRjHzwELgW4KjIU1fW4PEuEnjsylz7k7H5HgPOctIq7W1jrt3sKH9yG5d72//XWzsHhfoWvsQVg==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.31.0.tgz", + "integrity": "sha512-3fUfZT/FUuThWSSyL32Fsh7weUUfYP/Fjc/cGSbla5KiSo0GtI1JMssCRUopJTvmLjrw05R2q7rlLtiKdSzkzQ==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.46", @@ -15017,11 +16142,25 @@ "node": ">=0.10.0" } }, + "node_modules/webpack/node_modules/acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", + "dev": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/webpack/node_modules/enhanced-resolve": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", "dev": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -15035,6 +16174,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.6", "ajv": "^6.12.5", @@ -15042,6 +16182,10 @@ }, "engines": { "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/webpack/node_modules/tapable": { @@ -15049,6 +16193,7 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", "dev": true, + "peer": true, "engines": { "node": ">=6" } @@ -15069,12 +16214,12 @@ "dev": true }, "node_modules/whatwg-url": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", - "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz", + "integrity": "sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==", "dev": true, "dependencies": { - "lodash.sortby": "^4.7.0", + "lodash": "^4.7.0", "tr46": "^2.0.2", "webidl-conversions": "^6.1.0" }, @@ -15115,6 +16260,35 @@ "node": ">=8" } }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/widest-line/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/windows-release": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", @@ -15125,6 +16299,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/word-wrap": { @@ -15154,6 +16331,38 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/wrappy": { @@ -15252,9 +16461,9 @@ } }, "node_modules/y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "engines": { "node": ">=10" @@ -15301,6 +16510,35 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -15332,6 +16570,9 @@ "dev": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } }, @@ -15388,11 +16629,15 @@ "readable-stream": "^3.4.0" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, "ora": { "version": "5.3.0", @@ -15410,17 +16655,6 @@ "wcwidth": "^1.0.1" } }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, "rxjs": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", @@ -15430,21 +16664,6 @@ "tslib": "^1.9.0" } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -15469,65 +16688,56 @@ } }, "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.10.4" } }, + "@babel/compat-data": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz", + "integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==", + "dev": true + }, "@babel/core": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.13.tgz", - "integrity": "sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz", + "integrity": "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.12.13", - "@babel/helper-module-transforms": "^7.12.13", - "@babel/helpers": "^7.12.13", - "@babel/parser": "^7.12.13", + "@babel/generator": "^7.13.9", + "@babel/helper-compilation-targets": "^7.13.13", + "@babel/helper-module-transforms": "^7.13.14", + "@babel/helpers": "^7.13.10", + "@babel/parser": "^7.13.15", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13", + "@babel/traverse": "^7.13.15", + "@babel/types": "^7.13.14", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", + "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "lodash": "^4.17.19", - "semver": "^5.4.1", + "semver": "^6.3.0", "source-map": "^0.5.0" }, "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "minimist": "^1.2.5" + "@babel/highlight": "^7.12.13" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "source-map": { @@ -15539,12 +16749,12 @@ } }, "@babel/generator": { - "version": "7.12.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", - "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", + "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", "dev": true, "requires": { - "@babel/types": "^7.12.13", + "@babel/types": "^7.13.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -15557,6 +16767,26 @@ } } }, + "@babel/helper-compilation-targets": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz", + "integrity": "sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.12", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "@babel/helper-function-name": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", @@ -15578,38 +16808,37 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz", - "integrity": "sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.13.12" } }, "@babel/helper-module-imports": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", - "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.13.12" } }, "@babel/helper-module-transforms": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", - "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", + "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13", - "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", "@babel/helper-split-export-declaration": "^7.12.13", "@babel/helper-validator-identifier": "^7.12.11", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13", - "lodash": "^4.17.19" + "@babel/traverse": "^7.13.13", + "@babel/types": "^7.13.14" } }, "@babel/helper-optimise-call-expression": { @@ -15622,30 +16851,30 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", - "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", "dev": true }, "@babel/helper-replace-supers": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", - "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", + "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.12" } }, "@babel/helper-simple-access": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", - "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", "dev": true, "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.13.12" } }, "@babel/helper-split-export-declaration": { @@ -15663,21 +16892,27 @@ "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", "dev": true }, + "@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "dev": true + }, "@babel/helpers": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.13.tgz", - "integrity": "sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", + "integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", "dev": true, "requires": { "@babel/template": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" } }, "@babel/highlight": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", - "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -15738,9 +16973,9 @@ } }, "@babel/parser": { - "version": "7.12.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", - "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", + "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -15860,32 +17095,42 @@ "@babel/code-frame": "^7.12.13", "@babel/parser": "^7.12.13", "@babel/types": "^7.12.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + } } }, "@babel/traverse": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", - "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", + "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.12.13", + "@babel/generator": "^7.13.9", "@babel/helper-function-name": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13", + "@babel/parser": "^7.13.15", + "@babel/types": "^7.13.14", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "globals": "^11.1.0" }, "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "ms": "2.1.2" + "@babel/highlight": "^7.12.13" } }, "globals": { @@ -15893,19 +17138,13 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true } } }, "@babel/types": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", - "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", + "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -15961,25 +17200,19 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", "dev": true, "requires": { - "ms": "2.1.2" + "type-fest": "^0.8.1" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true } } @@ -16055,9 +17288,9 @@ } }, "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, "@jest/console": { @@ -16072,6 +17305,18 @@ "jest-message-util": "^26.6.2", "jest-util": "^26.6.2", "slash": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jest/core": { @@ -16108,6 +17353,18 @@ "rimraf": "^3.0.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jest/environment": { @@ -16180,6 +17437,16 @@ "v8-to-istanbul": "^7.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -16255,6 +17522,16 @@ "write-file-atomic": "^3.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -16274,6 +17551,18 @@ "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@nestjs/cli": { @@ -16305,26 +17594,11 @@ "webpack-node-externals": "2.5.2" }, "dependencies": { - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", + "dev": true }, "commander": { "version": "4.1.1", @@ -16332,72 +17606,69 @@ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ora": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", - "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", + "enhanced-resolve": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", + "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", "dev": true, "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" } }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", "dev": true }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, "typescript": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", "dev": true + }, + "webpack": { + "version": "5.28.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.28.0.tgz", + "integrity": "sha512-1xllYVmA4dIvRjHzwELgW4KjIU1fW4PEuEnjsylz7k7H5HgPOctIq7W1jrt3sKH9yG5d72//XWzsHhfoWvsQVg==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.46", + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/wasm-edit": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0", + "acorn": "^8.0.4", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.7.0", + "es-module-lexer": "^0.4.0", + "eslint-scope": "^5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.1", + "watchpack": "^2.0.0", + "webpack-sources": "^2.1.1" + } } } }, @@ -16442,12 +17713,14 @@ "@nestjs/mongoose": { "version": "7.2.4", "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-7.2.4.tgz", - "integrity": "sha512-NTE/IwijFUEJytPHLoHRYe0X5p16W1Awf8tm6I3mWsIUuBnSDfMyN0fy30uVaM/exYvCkMwEsAVpeSeKVPMHxg==" + "integrity": "sha512-NTE/IwijFUEJytPHLoHRYe0X5p16W1Awf8tm6I3mWsIUuBnSDfMyN0fy30uVaM/exYvCkMwEsAVpeSeKVPMHxg==", + "requires": {} }, "@nestjs/passport": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-7.1.5.tgz", - "integrity": "sha512-Hu9hPxTdBZA0C4GrWTsSflzwsJ99oAk9jqAwpcszdFNqfjMjkPGuCM9QsVZbBP2bE8fxrVrPsNOILS6puY8e/A==" + "integrity": "sha512-Hu9hPxTdBZA0C4GrWTsSflzwsJ99oAk9jqAwpcszdFNqfjMjkPGuCM9QsVZbBP2bE8fxrVrPsNOILS6puY8e/A==", + "requires": {} }, "@nestjs/platform-express": { "version": "7.6.15", @@ -16527,11 +17800,15 @@ "readable-stream": "^3.4.0" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, "ora": { "version": "5.3.0", @@ -16549,17 +17826,6 @@ "wcwidth": "^1.0.1" } }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, "rxjs": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", @@ -16569,21 +17835,6 @@ "tslib": "^1.9.0" } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -16660,6 +17911,17 @@ "chalk": "^4.1.0", "consola": "^2.15.0", "node-fetch": "^2.6.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@octokit/auth-token": { @@ -16695,14 +17957,6 @@ "@octokit/types": "^6.0.3", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true - } } }, "@octokit/graphql": { @@ -16762,14 +18016,6 @@ "node-fetch": "^2.6.1", "once": "^1.4.0", "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true - } } }, "@octokit/request-error": { @@ -16828,46 +18074,6 @@ "conventional-changelog": "^3.1.24", "conventional-recommended-bump": "^6.1.0", "prepend-file": "^2.0.0" - }, - "dependencies": { - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - } } }, "@schematics/schematics": { @@ -16886,9 +18092,9 @@ "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==" }, "@sinonjs/commons": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", - "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -16924,9 +18130,9 @@ } }, "@types/babel__core": { - "version": "7.1.12", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", - "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "version": "7.1.14", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", + "integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -16956,9 +18162,9 @@ } }, "@types/babel__traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", - "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz", + "integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -16978,7 +18184,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", - "dev": true, "requires": { "@types/node": "*" } @@ -17025,9 +18230,9 @@ } }, "@types/eslint": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", - "integrity": "sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw==", + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.8.tgz", + "integrity": "sha512-RTKvBsfz0T8CKOGZMfuluDNyMFHnu5lvNr4hWEsQeHXH6FcmIDIozOyWMh36nLGMwVd5UFNXC2xztA8lln22MQ==", "dev": true, "requires": { "@types/estree": "*", @@ -17063,9 +18268,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.18", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz", - "integrity": "sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", "dev": true, "requires": { "@types/node": "*", @@ -17080,9 +18285,9 @@ "dev": true }, "@types/graceful-fs": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", - "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", "dev": true, "requires": { "@types/node": "*" @@ -17199,10 +18404,9 @@ "dev": true }, "@types/mongodb": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.5.tgz", - "integrity": "sha512-XbG9+2wNaEwUn5DlhgN4ogjUYYzvjIsH6gwPvXXoTgfiQqUNq41RNxOqO+lrdpCjlRKtt/Pv7ZgSl7paQ/GUjw==", - "dev": true, + "version": "3.6.12", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.12.tgz", + "integrity": "sha512-49aEzQD5VdHPxyd5dRyQdqEveAg9LanwrH8RQipnMuulwzKmODXIZRp0umtxi1eBUfEusRkoy8AVOMr+kVuFog==", "requires": { "@types/bson": "*", "@types/node": "*" @@ -17212,7 +18416,6 @@ "version": "5.10.1", "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.10.1.tgz", "integrity": "sha512-5yqbLHOyCQhUb7GPGW0A2dauUbhwgBvUWMzYcaUQiHdLZ8slgRp2R6i8FETZ+t5xeXpfhylYp9U7dAng7WamqQ==", - "dev": true, "requires": { "@types/mongodb": "*", "@types/node": "*" @@ -17272,15 +18475,15 @@ } }, "@types/prettier": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.6.tgz", - "integrity": "sha512-6gOkRe7OIioWAXfnO/2lFiv+SJichKVSys1mSsgyrYHSEjk8Ctv4tSR/Odvnu+HWlH2C8j53dahU03XmQdd5fA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", + "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", "dev": true }, "@types/qs": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", - "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", "dev": true }, "@types/range-parser": { @@ -17407,23 +18610,6 @@ "regexpp": "^3.0.0", "semver": "^7.3.2", "tsutils": "^3.17.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "@typescript-eslint/experimental-utils": { @@ -17448,25 +18634,8 @@ "requires": { "@typescript-eslint/scope-manager": "4.21.0", "@typescript-eslint/types": "4.21.0", - "@typescript-eslint/typescript-estree": "4.21.0", - "debug": "^4.1.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "@typescript-eslint/typescript-estree": "4.21.0", + "debug": "^4.1.1" } }, "@typescript-eslint/scope-manager": { @@ -17498,23 +18667,6 @@ "is-glob": "^4.0.1", "semver": "^7.3.2", "tsutils": "^3.17.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "@typescript-eslint/visitor-keys": { @@ -17709,9 +18861,9 @@ } }, "acorn": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.5.tgz", - "integrity": "sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, "acorn-globals": { @@ -17722,21 +18874,14 @@ "requires": { "acorn": "^7.1.1", "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } } }, "acorn-jsx": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "7.2.0", @@ -17762,23 +18907,6 @@ "dev": true, "requires": { "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "ajv": { @@ -17796,7 +18924,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "amdefine": { "version": "1.0.1", @@ -17818,18 +18947,6 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -17859,12 +18976,20 @@ "dev": true }, "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { - "type-fest": "^0.11.0" + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } } }, "ansi-regex": { @@ -17886,9 +19011,9 @@ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -18069,6 +19194,18 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "slash": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "babel-plugin-istanbul": { @@ -18132,9 +19269,9 @@ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "barse": { "version": "0.4.3", @@ -18144,6 +19281,11 @@ "readable-stream": "~1.0.2" }, "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, "readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -18154,6 +19296,11 @@ "isarray": "0.0.1", "string_decoder": "~0.10.x" } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } }, @@ -18180,35 +19327,6 @@ "requires": { "is-descriptor": "^1.0.0" } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } } } }, @@ -18304,6 +19422,11 @@ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -18319,6 +19442,21 @@ "qs": "6.7.0", "raw-body": "2.4.0", "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "boolbase": { @@ -18327,9 +19465,9 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, "boxen": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.0.tgz", - "integrity": "sha512-5bvsqw+hhgUi3oYGK0Vf4WpIkyemp60WBInn7+WNfoISzAqk/HX4L7WNROq38E6UR/y3YADpv6pEm4BfkeEAdA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", + "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", "dev": true, "requires": { "ansi-align": "^3.0.0", @@ -18348,11 +19486,38 @@ "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, - "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==", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } } } }, @@ -18412,9 +19577,9 @@ } }, "bson": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", - "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" }, "buffer": { "version": "5.7.1", @@ -18449,6 +19614,29 @@ "requires": { "dicer": "0.2.5", "readable-stream": "1.1.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } } }, "bytes": { @@ -18500,13 +19688,16 @@ "lowercase-keys": "^2.0.0", "normalize-url": "^4.1.0", "responselike": "^2.0.0" - }, - "dependencies": { - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" - } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" } }, "callsites": { @@ -18530,12 +19721,20 @@ "camelcase": "^5.3.1", "map-obj": "^4.0.0", "quick-lru": "^4.0.1" + }, + "dependencies": { + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + } } }, "caniuse-lite": { - "version": "1.0.30001192", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001192.tgz", - "integrity": "sha512-63OrUnwJj5T1rUmoyqYTdRWBqFFxZFlyZnRRjDR8NSUQFB6A+j/uBORU/SyJ5WzDLg4SPiZH40hQCBNdZ/jmAw==", + "version": "1.0.30001208", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", + "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==", "dev": true }, "capture-exit": { @@ -18553,9 +19752,10 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18574,29 +19774,28 @@ "dev": true }, "cheerio": { - "version": "1.0.0-rc.5", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.5.tgz", - "integrity": "sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw==", + "version": "1.0.0-rc.6", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.6.tgz", + "integrity": "sha512-hjx1XE1M/D5pAtMgvWwE21QClmAEeGHOIDfycgmndisdNgI6PE1cGRQkMGBcsbUbmEQyWu5PJLUcAOjtQS8DWw==", "requires": { - "cheerio-select-tmp": "^0.1.0", - "dom-serializer": "~1.2.0", - "domhandler": "^4.0.0", - "entities": "~2.1.0", - "htmlparser2": "^6.0.0", - "parse5": "^6.0.0", - "parse5-htmlparser2-tree-adapter": "^6.0.0" + "cheerio-select": "^1.3.0", + "dom-serializer": "^1.3.1", + "domhandler": "^4.1.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1" } }, - "cheerio-select-tmp": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz", - "integrity": "sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ==", + "cheerio-select": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.3.0.tgz", + "integrity": "sha512-mLgqdHxVOQyhOIkG5QnRkDg7h817Dkf0dAvlCio2TJMmR72cJKH0bF28SHXvLkVrGcGOiub0/Bs/CMnPeQO7qw==", "requires": { - "css-select": "^3.1.2", - "css-what": "^4.0.0", - "domelementtype": "^2.1.0", - "domhandler": "^4.0.0", - "domutils": "^2.4.4" + "css-select": "^4.0.0", + "css-what": "^5.0.0", + "domelementtype": "^2.2.0", + "domhandler": "^4.1.0", + "domutils": "^2.5.2" } }, "chokidar": { @@ -18616,21 +19815,10 @@ } }, "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true }, "ci-info": { "version": "2.0.0", @@ -18669,6 +19857,63 @@ "requires": { "is-descriptor": "^0.1.0" } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -18697,9 +19942,9 @@ } }, "cli-spinners": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz", - "integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", + "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==" }, "cli-table3": { "version": "0.5.1", @@ -18709,40 +19954,7 @@ "requires": { "colors": "^1.1.2", "object-assign": "^4.1.0", - "string-width": "^2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "string-width": "^2.1.1" } }, "cli-width": { @@ -18760,6 +19972,31 @@ "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } } }, "clone": { @@ -18892,43 +20129,15 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "configstore": { @@ -19161,46 +20370,6 @@ "git-semver-tags": "^4.1.1", "meow": "^8.0.0", "q": "^1.5.1" - }, - "dependencies": { - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - } } }, "convert-source-map": { @@ -19255,12 +20424,6 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -19356,21 +20519,21 @@ "dev": true }, "css-select": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", - "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.0.0.tgz", + "integrity": "sha512-I7favumBlDP/nuHBKLfL5RqvlvRdn/W29evvWJ+TaoGPm7QD+xSIN5eY2dyGjtkUmemh02TZrqJb4B8DWo6PoQ==", "requires": { "boolbase": "^1.0.0", - "css-what": "^4.0.0", - "domhandler": "^4.0.0", - "domutils": "^2.4.3", + "css-what": "^5.0.0", + "domhandler": "^4.1.0", + "domutils": "^2.5.1", "nth-check": "^2.0.0" } }, "css-what": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", - "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.0.tgz", + "integrity": "sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA==" }, "cssom": { "version": "0.4.4", @@ -19440,11 +20603,12 @@ "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } }, "decamelize": { @@ -19525,9 +20689,9 @@ } }, "defer-to-connect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", - "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" }, "define-property": { "version": "2.0.2", @@ -19537,37 +20701,6 @@ "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } } }, "delayed-stream": { @@ -19619,6 +20752,29 @@ "requires": { "readable-stream": "1.1.x", "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } } }, "diff": { @@ -19667,9 +20823,9 @@ } }, "dom-serializer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", - "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", + "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", "requires": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", @@ -19677,9 +20833,9 @@ } }, "domelementtype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", - "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" }, "domexception": { "version": "2.0.1", @@ -19699,21 +20855,21 @@ } }, "domhandler": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", - "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.1.0.tgz", + "integrity": "sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ==", "requires": { - "domelementtype": "^2.1.0" + "domelementtype": "^2.2.0" } }, "domutils": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", - "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.5.2.tgz", + "integrity": "sha512-MHTthCb1zj8f1GVfRpeZUbohQf/HdBos0oX5gZcQFepOZPLLRyj6Wn7XS7EMnY7CVpwv8863u2vyE83Hfu28HQ==", "requires": { "dom-serializer": "^1.0.1", - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0" + "domelementtype": "^2.2.0", + "domhandler": "^4.1.0" } }, "dot-prop": { @@ -19764,9 +20920,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.674", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.674.tgz", - "integrity": "sha512-DBmEKRVYLZAoQSW+AmLcTF5Bpwhk4RUkobtzXVDlfPPYIlbsH3Jfg3QbBjAfFcRARzMIo4YiMhp3N+RnMuo1Eg==", + "version": "1.3.711", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.711.tgz", + "integrity": "sha512-XbklBVCDiUeho0PZQCjC25Ha6uBwqqJeyDhPLwLwfWRAo4x+FZFsmu1pPPkXT+B4MQMQoQULfyaMltDopfeiHQ==", "dev": true }, "emittery": { @@ -19776,9 +20932,9 @@ "dev": true }, "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "emojis-list": { @@ -19825,18 +20981,13 @@ "requires": { "ms": "^2.1.1" } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "engine.io-client": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", - "integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.1.tgz", + "integrity": "sha512-oVu9kBkGbcggulyVF0kz6BV3ganqUeqXvD79WOFKa+11oK692w1NyFkuEj4xrkFRpZhn92QOqTk4RQq5LiBXbQ==", "requires": { "component-emitter": "~1.3.0", "component-inherit": "0.0.3", @@ -19858,6 +21009,11 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -19894,9 +21050,9 @@ } }, "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" }, "errno": { "version": "0.1.8", @@ -19945,18 +21101,24 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "requires": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" }, "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -20050,53 +21212,25 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz", - "integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "type-fest": "^0.20.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "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 } } }, + "eslint-config-prettier": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", + "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", + "dev": true, + "requires": {} + }, "eslint-plugin-jest": { "version": "24.3.4", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.3.4.tgz", @@ -20106,6 +21240,15 @@ "@typescript-eslint/experimental-utils": "^4.0.1" } }, + "eslint-plugin-prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", + "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -20150,12 +21293,6 @@ "eslint-visitor-keys": "^1.3.0" }, "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, "eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", @@ -20232,15 +21369,15 @@ "integrity": "sha1-ywffzUGNoiIdkPd+q3E7wjXiCQ8=" }, "events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, "exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", "dev": true }, "execa": { @@ -20281,6 +21418,15 @@ "to-regex": "^3.0.1" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -20298,6 +21444,75 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -20352,6 +21567,19 @@ "vary": "~1.1.2" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -20372,17 +21600,6 @@ "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } } }, "external-editor": { @@ -20430,34 +21647,11 @@ "is-extendable": "^0.1.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true } } }, @@ -20471,6 +21665,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", @@ -20502,9 +21702,9 @@ "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, "fastq": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.1.tgz", - "integrity": "sha512-AWuv6Ery3pM+dY7LYS8YIaCiQvUaos9OB1RyNgaOWnaX+Tik7Onvcsf8x8c+YtDeT0maYLniBip2hox5KtEXXA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -20573,6 +21773,21 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "find-cache-dir": { @@ -20625,9 +21840,9 @@ "dev": true }, "follow-redirects": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz", - "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==" + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", + "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==" }, "for-in": { "version": "1.0.2", @@ -20658,15 +21873,28 @@ "schema-utils": "2.7.0", "semver": "^7.3.2", "tapable": "^1.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, @@ -20714,9 +21942,9 @@ } }, "fs-monkey": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz", - "integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", "dev": true }, "fs.realpath": { @@ -20799,6 +22027,17 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -20844,12 +22083,6 @@ "pinkie-promise": "^2.0.0" } }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -20865,6 +22098,19 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", @@ -20901,6 +22147,15 @@ "validate-npm-package-license": "^3.0.1" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -20982,6 +22237,15 @@ "safe-buffer": "~5.1.0" } }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, "strip-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", @@ -21126,9 +22390,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -21158,20 +22422,12 @@ } }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", + "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", "dev": true, "requires": { - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } + "type-fest": "^0.20.2" } }, "globby": { @@ -21186,6 +22442,14 @@ "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } } }, "got": { @@ -21207,9 +22471,9 @@ } }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "graceful-readlink": { @@ -21280,13 +22544,6 @@ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", "requires": { "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - } } }, "has-cors": { @@ -21299,6 +22556,12 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -21363,13 +22626,10 @@ "integrity": "sha512-G8tp0wUMI7i8wkMk2xLcEvESg5PiCitFMYgGRc/PwULB0RVhTP5GFdxOwvJwp9XVha8CuS8mnhmE8I/8dx/pbw==" }, "hosted-git-info": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", - "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, "html-encoding-sniffer": { "version": "2.0.1", @@ -21387,13 +22647,13 @@ "dev": true }, "htmlparser2": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.0.0.tgz", - "integrity": "sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", "requires": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", - "domutils": "^2.4.4", + "domutils": "^2.5.2", "entities": "^2.0.0" } }, @@ -21412,6 +22672,13 @@ "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } } }, "http-signature": { @@ -21425,19 +22692,12 @@ } }, "http2-wrapper": { - "version": "1.0.0-beta.5.2", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", - "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "requires": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" - }, - "dependencies": { - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" - } } }, "https-proxy-agent": { @@ -21448,23 +22708,6 @@ "requires": { "agent-base": "6", "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "human-signals": { @@ -21488,9 +22731,9 @@ "dev": true }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "import-cwd": { @@ -21510,14 +22753,6 @@ "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" - }, - "dependencies": { - "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 - } } }, "import-from": { @@ -21580,9 +22815,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.8", @@ -21609,6 +22844,41 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } } }, "interpret": { @@ -21617,35 +22887,18 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "kind-of": "^6.0.0" } }, "is-arrayish": { @@ -21663,6 +22916,15 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -21692,57 +22954,52 @@ "has": "^1.0.3" } }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, "optional": true }, "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + }, + "dependencies": { + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + } + } }, "is-extglob": { "version": "2.1.1", @@ -21757,9 +23014,9 @@ "dev": true }, "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", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "is-generator-fn": { @@ -21804,6 +23061,12 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -21811,9 +23074,9 @@ "dev": true }, "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "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 }, "is-plain-obj": { @@ -21823,18 +23086,15 @@ "dev": true }, "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true }, "is-potential-custom-element-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, "is-ssh": { @@ -21852,6 +23112,12 @@ "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, "is-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", @@ -21900,9 +23166,9 @@ "dev": true }, "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, "isexe": { "version": "2.0.0", @@ -21969,21 +23235,6 @@ "source-map": "^0.6.1" }, "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -22016,8 +23267,50 @@ "@jest/core": "^26.6.3", "import-local": "^3.0.2", "jest-cli": "^26.6.3" + } + }, + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + } + }, + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -22029,25 +23322,27 @@ "wrap-ansi": "^6.2.0" } }, - "jest-cli": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", - "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { - "@jest/core": "^26.6.3", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.6.3", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "prompts": "^2.0.1", - "yargs": "^15.4.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "wrap-ansi": { @@ -22062,9 +23357,9 @@ } }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs": { @@ -22098,17 +23393,6 @@ } } }, - "jest-changed-files": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", - "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "execa": "^4.0.0", - "throat": "^5.0.0" - } - }, "jest-config": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", @@ -22133,6 +23417,18 @@ "jest-validate": "^26.6.2", "micromatch": "^4.0.2", "pretty-format": "^26.6.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-diff": { @@ -22145,6 +23441,18 @@ "diff-sequences": "^26.6.2", "jest-get-type": "^26.3.0", "pretty-format": "^26.6.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-docblock": { @@ -22167,6 +23475,18 @@ "jest-get-type": "^26.3.0", "jest-util": "^26.6.2", "pretty-format": "^26.6.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-environment-jsdom": { @@ -22250,6 +23570,18 @@ "jest-util": "^26.6.2", "pretty-format": "^26.6.2", "throat": "^5.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-leak-detector": { @@ -22272,6 +23604,18 @@ "jest-diff": "^26.6.2", "jest-get-type": "^26.3.0", "pretty-format": "^26.6.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-message-util": { @@ -22289,6 +23633,18 @@ "pretty-format": "^26.6.2", "slash": "^3.0.0", "stack-utils": "^2.0.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-mock": { @@ -22305,7 +23661,8 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true + "dev": true, + "requires": {} }, "jest-regex-util": { "version": "26.0.0", @@ -22329,11 +23686,15 @@ "slash": "^3.0.0" }, "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, "normalize-package-data": { "version": "2.5.0", @@ -22429,6 +23790,18 @@ "jest-worker": "^26.6.2", "source-map-support": "^0.5.6", "throat": "^5.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-runtime": { @@ -22466,23 +23839,50 @@ "yargs": "^15.4.1" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -22495,9 +23895,9 @@ } }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs": { @@ -22563,6 +23963,18 @@ "natural-compare": "^1.4.0", "pretty-format": "^26.6.2", "semver": "^7.3.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-util": { @@ -22577,6 +23989,18 @@ "graceful-fs": "^4.2.4", "is-ci": "^2.0.0", "micromatch": "^4.0.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-validate": { @@ -22598,6 +24022,16 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, @@ -22614,6 +24048,18 @@ "chalk": "^4.0.0", "jest-util": "^26.6.2", "string-length": "^4.0.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-worker": { @@ -22649,61 +24095,44 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.2.tgz", + "integrity": "sha512-JxNtPt9C1ut85boCbJmffaQ06NBnzkQY/MWO3YxPW8IWS38A26z+B1oBvA9LwKrytewdfymnhi4UNH3/RAgZrg==", "dev": true, "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", + "abab": "^2.0.5", + "acorn": "^8.1.0", "acorn-globals": "^6.0.0", "cssom": "^0.4.4", - "cssstyle": "^2.2.0", + "cssstyle": "^2.3.0", "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", + "decimal.js": "^10.2.1", "domexception": "^2.0.1", - "escodegen": "^1.14.1", + "escodegen": "^2.0.0", "html-encoding-sniffer": "^2.0.1", "is-potential-custom-element-name": "^1.0.0", "nwsapi": "^2.2.0", - "parse5": "5.1.1", + "parse5": "6.0.1", "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", + "tough-cookie": "^4.0.0", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", + "whatwg-url": "^8.5.0", + "ws": "^7.4.4", "xml-name-validator": "^3.0.0" }, "dependencies": { "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", "dev": true - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true - }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } } } }, @@ -22752,12 +24181,12 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" } }, "jsonc-parser": { @@ -22814,11 +24243,6 @@ "semver": "^5.6.0" }, "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -22912,9 +24336,9 @@ } }, "libphonenumber-js": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.9.tgz", - "integrity": "sha512-RhtAvacOkq4wc+RrswYE9MteJQy/1yd4lgLJTdL+HdYYRLZbERFv4ZRj/Nt+/gIB2xVSgW0MLNqb32hx042W+g==" + "version": "1.9.16", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.16.tgz", + "integrity": "sha512-PaHT7nTtnejZ0HHekAaA0olv6BUTKZGtKM4SCQS0yE3XjFuVo/tjePMHUAr32FKwIZfyPky1ExMUuaiBAUmV6w==" }, "lines-and-columns": { "version": "1.1.6", @@ -22923,35 +24347,38 @@ "dev": true }, "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "dependencies": { "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true } } }, @@ -22970,17 +24397,6 @@ "big.js": "^5.2.2", "emojis-list": "^3.0.0", "json5": "^2.1.2" - }, - "dependencies": { - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } } }, "locate-path": { @@ -23006,6 +24422,18 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -23062,18 +24490,18 @@ "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, "lodash.toarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", "dev": true }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -23081,6 +24509,17 @@ "requires": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "loglevel": { @@ -23170,9 +24609,9 @@ "dev": true }, "map-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", - "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", "dev": true }, "map-visit": { @@ -23196,12 +24635,12 @@ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "memfs": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz", - "integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.2.tgz", + "integrity": "sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==", "dev": true, "requires": { - "fs-monkey": "1.0.1" + "fs-monkey": "1.0.3" } }, "memory-fs": { @@ -23271,12 +24710,6 @@ "yargs-parser": "^20.2.3" }, "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -23435,6 +24868,14 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -23511,26 +24952,13 @@ "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } } }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true }, "modify-values": { "version": "1.0.1", @@ -23544,22 +24972,24 @@ "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "moment-timezone": { - "version": "0.5.32", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", - "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", "requires": { "moment": ">= 2.9.0" } }, "mongodb": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", - "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz", + "integrity": "sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==", + "dev": true, + "optional": true, "requires": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", - "require_optional": "^1.0.1", + "optional-require": "^1.0.2", "safe-buffer": "^5.1.2", "saslprep": "^1.0.0" } @@ -23604,27 +25034,6 @@ "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -23654,10 +25063,18 @@ "sliced": "1.0.1" }, "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "mongodb": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", + "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", + "requires": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } }, "safe-buffer": { "version": "5.2.1", @@ -23669,7 +25086,8 @@ "mongoose-legacy-pluralize": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", - "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", + "requires": {} }, "mpath": { "version": "0.7.0", @@ -23688,11 +25106,6 @@ "sliced": "1.0.1" }, "dependencies": { - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -23700,13 +25113,18 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multer": { "version": "1.4.2", @@ -23721,6 +25139,54 @@ "on-finished": "^2.3.0", "type-is": "^1.6.4", "xtend": "^4.0.0" + }, + "dependencies": { + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "mute-stream": { @@ -23770,6 +25236,32 @@ "integrity": "sha512-2bFdKHS/8+z6pHaDehdRJuuacjMnHnSuPVYgn1J+6b0dEdGH66hjV+hgrbrdJvCubYwtBxghv3XAoXSZcT7TxA==", "requires": { "ora": "5.1.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "ora": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", + "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==", + "requires": { + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.4.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + } } }, "nestjs-typegoose": { @@ -23814,9 +25306,9 @@ "dev": true }, "node-notifier": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", - "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", "dev": true, "optional": true, "requires": { @@ -23844,6 +25336,12 @@ "readable-stream": "~1.0.31" }, "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -23855,19 +25353,36 @@ "isarray": "0.0.1", "string_decoder": "~0.10.x" } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true } } }, "normalize-package-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", - "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", "dev": true, "requires": { - "hosted-git-info": "^3.0.6", - "resolve": "^1.17.0", - "semver": "^7.3.2", + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "normalize-path": { @@ -23877,10 +25392,9 @@ "dev": true }, "normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", - "dev": true + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" }, "npm-run-path": { "version": "4.0.1", @@ -23935,6 +25449,43 @@ "is-descriptor": "^0.1.0" } }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -23951,6 +25502,12 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==" }, + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -23994,9 +25551,9 @@ } }, "openid": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/openid/-/openid-2.0.7.tgz", - "integrity": "sha512-xH6qaz/hS55rEX8xURz4HRUO96cpj821WY6UEG9rgcusZ8Jsq54jGWP1EMCjGvgngonw8vgSljM1i2OESv16Gw==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/openid/-/openid-2.0.8.tgz", + "integrity": "sha512-ljI4GE6p4RYn9dLftlXw6TvlA+untAkoWBRpj4qIB4AJQWcDZ2lOVOJQ2tq346ok38mtGDBYRBvp3Q+AsuCBnQ==", "requires": { "request": "^2.88.2" } @@ -24007,6 +25564,13 @@ "integrity": "sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==", "dev": true }, + "optional-require": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.2.tgz", + "integrity": "sha512-HZubVd6IfHsbnpdNF/ICaSAzBUEW1TievpkjY3tB4Jnk8L7+pJ3conPzUt3Mn/6OZx9uzTDOHYPGA8/AxYHBOg==", + "dev": true, + "optional": true + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -24022,18 +25586,43 @@ } }, "ora": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", - "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", + "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", + "dev": true, "requires": { + "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", - "cli-spinners": "^2.4.0", + "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "mute-stream": "0.0.8", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" + }, + "dependencies": { + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "os-name": { @@ -24053,9 +25642,9 @@ "dev": true }, "p-cancelable": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", - "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", + "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==" }, "p-each-series": { "version": "2.2.0", @@ -24133,23 +25722,6 @@ "lowercase-keys": "^2.0.0", "normalize-url": "^4.1.0", "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } } }, "decompress-response": { @@ -24167,15 +25739,6 @@ "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -24193,6 +25756,23 @@ "p-cancelable": "^1.0.0", "to-readable-stream": "^1.0.0", "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + } } }, "json-buffer": { @@ -24210,18 +25790,6 @@ "json-buffer": "3.0.0" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "dev": true - }, "p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", @@ -24235,6 +25803,14 @@ "dev": true, "requires": { "lowercase-keys": "^1.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + } } }, "semver": { @@ -24285,10 +25861,13 @@ }, "dependencies": { "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", - "dev": true + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } } } }, @@ -24302,6 +25881,14 @@ "normalize-url": "^3.3.0", "parse-path": "^4.0.0", "protocols": "^1.4.0" + }, + "dependencies": { + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true + } } }, "parse5": { @@ -24505,6 +26092,15 @@ "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", "dev": true }, + "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, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-format": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", @@ -24535,9 +26131,9 @@ "dev": true }, "prompts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", - "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", "dev": true, "requires": { "kleur": "^3.0.3", @@ -24605,9 +26201,9 @@ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, "query-string": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.0.tgz", - "integrity": "sha512-In3o+lUxlgejoVJgwEdYtdxrmlL0cQWJXj0+kkI7RWVo7hg5AhFtybeKlC9Dpgbr8eOC4ydpEh8017WwyfzqVQ==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", "dev": true, "requires": { "decode-uri-component": "^0.2.0", @@ -24616,12 +26212,17 @@ "strict-uri-encode": "^2.0.0" } }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "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 }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -24676,9 +26277,9 @@ } }, "react-is": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", - "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, "read-pkg": { @@ -24692,24 +26293,6 @@ "path-type": "^3.0.0" }, "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -24722,16 +26305,6 @@ "validate-npm-package-license": "^3.0.1" } }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -24817,14 +26390,14 @@ } }, "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "readdirp": { @@ -24935,15 +26508,14 @@ "yargs-parser": "20.2.7" }, "dependencies": { - "bl": { + "chalk": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, "ci-info": { @@ -24965,14 +26537,11 @@ "yaml": "^1.10.0" } }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "execa": { "version": "5.0.0", @@ -25001,17 +26570,6 @@ "path-exists": "^4.0.0" } }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "get-stream": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", @@ -25024,12 +26582,6 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "inquirer": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", @@ -25060,6 +26612,12 @@ "ci-info": "^3.1.1" } }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -25069,29 +26627,6 @@ "p-locate": "^5.0.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "ora": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", - "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - } - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -25103,37 +26638,22 @@ }, "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, - "requires": { - "p-limit": "^3.0.2" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "p-limit": "^3.0.2" } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { - "safe-buffer": "~5.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } } } @@ -25145,9 +26665,9 @@ "dev": true }, "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", "dev": true }, "repeat-string": { @@ -25192,11 +26712,30 @@ "uuid": "^3.3.2" }, "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -25222,6 +26761,18 @@ "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "require_optional": { @@ -25233,6 +26784,11 @@ "semver": "^5.1.0" }, "dependencies": { + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -25259,12 +26815,12 @@ "dev": true }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { - "is-core-module": "^2.1.0", + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -25291,9 +26847,10 @@ } }, "resolve-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + "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 }, "resolve-url": { "version": "0.2.1", @@ -25357,10 +26914,13 @@ "dev": true }, "run-parallel": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", - "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", - "dev": true + "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, + "requires": { + "queue-microtask": "^1.2.2" + } }, "rxjs": { "version": "6.6.7", @@ -25512,6 +27072,12 @@ "pump": "^3.0.0" } }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -25704,6 +27270,21 @@ "statuses": "~1.5.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -25757,6 +27338,21 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } } } }, @@ -25803,6 +27399,17 @@ "dev": true, "optional": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "sift": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", @@ -25834,6 +27441,14 @@ "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } } }, "sliced": { @@ -25865,6 +27480,15 @@ "use": "^3.1.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -25883,6 +27507,75 @@ "is-extendable": "^0.1.0" } }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -25910,35 +27603,6 @@ "requires": { "is-descriptor": "^1.0.0" } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } } } }, @@ -25982,11 +27646,6 @@ "requires": { "ms": "^2.1.1" } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -26021,10 +27680,10 @@ "ms": "2.0.0" } }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "socket.io-parser": { "version": "3.3.2", @@ -26060,16 +27719,6 @@ "requires": { "ms": "^2.1.1" } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -26209,34 +27858,6 @@ "dev": true, "requires": { "readable-stream": "^3.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - } } }, "sprintf-js": { @@ -26311,6 +27932,63 @@ "requires": { "is-descriptor": "^0.1.0" } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -26353,14 +28031,26 @@ "dev": true }, "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } }, "string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "requires": { "char-regex": "^1.0.2", @@ -26406,14 +28096,30 @@ } }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "strip-ansi": { @@ -26425,9 +28131,9 @@ } }, "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, "strip-eof": { @@ -26476,19 +28182,10 @@ "semver": "^7.3.2" }, "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -26497,47 +28194,18 @@ } }, "mime": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.0.tgz", - "integrity": "sha512-ft3WayFSFUVBuJj7BMLKAQcSlItKtfjsKDDsii3rqFDAZ7t11zRe8ASw/GlmivGwVUYtwkQrxiGGpL6gFvB0ag==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", - "dev": true - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", "dev": true, "requires": { - "safe-buffer": "~5.2.0" + "side-channel": "^1.0.4" } } } @@ -26561,9 +28229,9 @@ } }, "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", "dev": true, "requires": { "has-flag": "^4.0.0", @@ -26583,21 +28251,26 @@ "dev": true }, "table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", - "dev": true, - "requires": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", + "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "lodash.clonedeep": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.0" }, "dependencies": { "ajv": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", - "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.0.5.tgz", + "integrity": "sha512-RkiLa/AeJx7+9OvniQ/qeWu0w74A8DiPPBclQ6ji3ZQkv5KamO+QGpqmi7O4JIw3rHGUXZ6CoP9tsAkn3gyazg==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -26606,11 +28279,34 @@ "uri-js": "^4.2.2" } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } } } }, @@ -26643,38 +28339,6 @@ "inherits": "^2.0.4", "readable-stream": "^3.4.0" } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } } } }, @@ -26716,9 +28380,9 @@ } }, "terser": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.0.tgz", - "integrity": "sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.1.tgz", + "integrity": "sha512-yv9YLFQQ+3ZqgWCUk+pvNJwgUTdlIxUk1WTN+RnaFJe2L7ipG2csPT0ra2XRm7Cs8cxN7QXmK1rFzEwYEQkzXw==", "dev": true, "requires": { "commander": "^2.20.0", @@ -26818,34 +28482,6 @@ "dev": true, "requires": { "readable-stream": "3" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - } } }, "tmp": { @@ -26927,12 +28563,22 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "dependencies": { + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } } }, "tr46": { @@ -26978,23 +28624,6 @@ "mkdirp": "1.x", "semver": "7.x", "yargs-parser": "20.x" - }, - "dependencies": { - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } } }, "ts-loader": { @@ -27008,6 +28637,18 @@ "loader-utils": "^2.0.0", "micromatch": "^4.0.0", "semver": "^7.3.4" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "ts-node": { @@ -27034,6 +28675,23 @@ "json5": "^1.0.1", "minimist": "^1.2.0", "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } } }, "tsconfig-paths-webpack-plugin": { @@ -27047,6 +28705,16 @@ "tsconfig-paths": "^3.9.0" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "enhanced-resolve": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", @@ -27071,9 +28739,9 @@ "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" }, "tsutils": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz", - "integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -27116,9 +28784,9 @@ "dev": true }, "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "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 }, "type-is": { @@ -27156,9 +28824,9 @@ "dev": true }, "uglify-js": { - "version": "3.12.8", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", - "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.3.tgz", + "integrity": "sha512-otIc7O9LyxpUcQoXzj2hL4LPWKklO6LJWoJUzNa8A17Xgi4fOeDC8FBDOLHnC/Slo1CQgsZMcM6as0M76BZaig==", "dev": true, "optional": true }, @@ -27177,6 +28845,14 @@ "get-value": "^2.0.6", "is-extendable": "^0.1.1", "set-value": "^2.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + } } }, "unique-string": { @@ -27277,6 +28953,18 @@ "semver": "^7.3.4", "semver-diff": "^3.1.1", "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "uri-js": { @@ -27330,15 +29018,15 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "v8-to-istanbul": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz", - "integrity": "sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz", + "integrity": "sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.1", @@ -27433,10 +29121,11 @@ "dev": true }, "webpack": { - "version": "5.28.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.28.0.tgz", - "integrity": "sha512-1xllYVmA4dIvRjHzwELgW4KjIU1fW4PEuEnjsylz7k7H5HgPOctIq7W1jrt3sKH9yG5d72//XWzsHhfoWvsQVg==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.31.0.tgz", + "integrity": "sha512-3fUfZT/FUuThWSSyL32Fsh7weUUfYP/Fjc/cGSbla5KiSo0GtI1JMssCRUopJTvmLjrw05R2q7rlLtiKdSzkzQ==", "dev": true, + "peer": true, "requires": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.46", @@ -27463,11 +29152,19 @@ "webpack-sources": "^2.1.1" }, "dependencies": { + "acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", + "dev": true, + "peer": true + }, "enhanced-resolve": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", "dev": true, + "peer": true, "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -27478,6 +29175,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, + "peer": true, "requires": { "@types/json-schema": "^7.0.6", "ajv": "^6.12.5", @@ -27488,7 +29186,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", - "dev": true + "dev": true, + "peer": true } } }, @@ -27532,12 +29231,12 @@ "dev": true }, "whatwg-url": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", - "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz", + "integrity": "sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==", "dev": true, "requires": { - "lodash.sortby": "^4.7.0", + "lodash": "^4.7.0", "tr46": "^2.0.2", "webidl-conversions": "^6.1.0" } @@ -27564,6 +29263,31 @@ "dev": true, "requires": { "string-width": "^4.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } } }, "windows-release": { @@ -27596,6 +29320,31 @@ "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } } }, "wrappy": { @@ -27664,9 +29413,9 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { @@ -27693,6 +29442,31 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } } }, "yargs-parser": { diff --git a/package.json b/package.json index 1ed217081..ed76ad4e0 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,9 @@ "@typescript-eslint/parser": "4.21.0", "copyfiles": "2.4.1", "eslint": "7.23.0", + "eslint-config-prettier": "8.1.0", "eslint-plugin-jest": "24.3.4", + "eslint-plugin-prettier": "3.3.1", "jest": "26.6.3", "mongodb-memory-server": "6.9.6", "prettier": "2.2.1", diff --git a/src/app.controller.spec.ts b/src/app.controller.spec.ts index 9507c691c..79d5bb7a0 100644 --- a/src/app.controller.spec.ts +++ b/src/app.controller.spec.ts @@ -13,9 +13,7 @@ describe('AppController', () => { beforeEach(async () => { const app: TestingModule = await Test.createTestingModule({ controllers: [AppController], - providers: [ - { provide: Environment, useClass: EnvironmentStub }, - ], + providers: [{ provide: Environment, useClass: EnvironmentStub }], }).compile(); appController = app.get(AppController); diff --git a/src/app.controller.ts b/src/app.controller.ts index 217639602..9f1b83410 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -4,10 +4,7 @@ import { Environment } from './environment/environment'; @Controller() export class AppController { - - constructor( - private environment: Environment, - ) { } + constructor(private environment: Environment) {} @Get() index() { diff --git a/src/app.module.ts b/src/app.module.ts index 0adec59c8..d547532e8 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -36,8 +36,8 @@ import { createMongoDbUri } from './utils/create-mongo-db-uri'; }, }), TypegooseModule.forRootAsync({ - imports: [ EnvironmentModule ], - inject: [ Environment ], + imports: [EnvironmentModule], + inject: [Environment], useFactory: async (environment: Environment) => ({ uri: createMongoDbUri({ host: environment.mongoDbHost, @@ -74,11 +74,7 @@ import { createMongoDbUri } from './utils/create-mongo-db-uri'; PluginsModule.configure(), LogReceiverModule, ], - controllers: [ - AppController, - ], - providers: [ - AppService, - ], + controllers: [AppController], + providers: [AppService], }) -export class AppModule { } +export class AppModule {} diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 955781ae3..56a601323 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -25,7 +25,7 @@ const passportModule = PassportModule.register({ @Module({ imports: [ passportModule, - TypegooseModule.forFeature([ RefreshToken, Key ]), + TypegooseModule.forFeature([RefreshToken, Key]), PlayersModule, ], @@ -33,49 +33,61 @@ const passportModule = PassportModule.register({ { // keys used to sign & validate auth JWT provide: 'AUTH_TOKEN_KEY', - inject: [ getModelToken(Key.name), Environment ], - useFactory: async (keyModel: ReturnModelType, environment: Environment) => - await importOrGenerateKeys(keyModel, KeyName.auth, environment.keyStorePassphare), + inject: [getModelToken(Key.name), Environment], + useFactory: async ( + keyModel: ReturnModelType, + environment: Environment, + ) => + await importOrGenerateKeys( + keyModel, + KeyName.auth, + environment.keyStorePassphare, + ), }, { // keys used to sign & validate refresh JWT provide: 'REFRESH_TOKEN_KEY', - inject: [ getModelToken(Key.name), Environment ], - useFactory: async (keyModel: ReturnModelType, environment: Environment) => - await importOrGenerateKeys(keyModel, KeyName.refresh, environment.keyStorePassphare), + inject: [getModelToken(Key.name), Environment], + useFactory: async ( + keyModel: ReturnModelType, + environment: Environment, + ) => + await importOrGenerateKeys( + keyModel, + KeyName.refresh, + environment.keyStorePassphare, + ), }, { provide: 'WEBSOCKET_SECRET', - useFactory: () => generate({ length: 32, numbers: true, uppercase: true }), + useFactory: () => + generate({ length: 32, numbers: true, uppercase: true }), }, { provide: 'CONTEXT_TOKEN_KEY', - inject: [ getModelToken(Key.name), Environment ], - useFactory: async (keyModel: ReturnModelType, environment: Environment) => - await importOrGenerateKeys(keyModel, KeyName.context, environment.keyStorePassphare), + inject: [getModelToken(Key.name), Environment], + useFactory: async ( + keyModel: ReturnModelType, + environment: Environment, + ) => + await importOrGenerateKeys( + keyModel, + KeyName.context, + environment.keyStorePassphare, + ), }, AuthService, SteamStrategy, JwtStrategy, AuthGateway, ], - controllers: [ - AuthController, - ], - exports: [ - passportModule, - AuthService, - ], + controllers: [AuthController], + exports: [passportModule, AuthService], }) export class AuthModule implements NestModule { - configure(consumer: MiddlewareConsumer) { consumer - .apply( - setRedirectUrlCookie, - authenticate('steam', { session: false }), - ) + .apply(setRedirectUrlCookie, authenticate('steam', { session: false })) .forRoutes('/auth/steam'); } - } diff --git a/src/auth/controllers/auth.controller.spec.ts b/src/auth/controllers/auth.controller.spec.ts index a1f8f7a3c..70d3ce1f4 100644 --- a/src/auth/controllers/auth.controller.spec.ts +++ b/src/auth/controllers/auth.controller.spec.ts @@ -18,9 +18,13 @@ class HttpAdapterHostStub { } class AuthServiceStub { - authToken = 'eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkNDQ4ODc1Yjk2M2ZmN2UwMGM2YjZiMyIsImlhdCI6MTU3Njc5NzQxNCwiZXhwIjoxNTc2Nzk4MzE0fQ.AXeWhEMSRS_kB7ISiRUt5vk9T71_mXUWevl_wT_tnyiS9vHQScMIY4qVzQnrx21FUwfyAmUVOdRdFNPpndGFPW4kARaPbeVkOSgF4vPt4MHJqvlXrA-B97Z7u9ahRqMFcdNyCIWbbR-bQ4592TJLdoGdx1Mqbc0brSYDlLaaG2aGPYN'; - refreshToken = 'eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkNDQ4ODc1Yjk2M2ZmN2UwMGM2YjZiMyIsImlhdCI6MTU3Njc5NzQxNCwiZXhwIjoxNTc3NDAyMjE0fQ.AAOwakFw8lKXRwmeLqPWksQTKjXqYVAb-Gc_sXhXolU36fPr69Flxgf5c2YZ6qCghZlloZ3TR5PaJ1PT3b2s1je5AfQw01fcZ56E10LGUyfLvV02Mgy5ler9PzbLi7sZzRs1QgDUeq3Ml9WYRIZirP_r9VxVS_4eDluP3NNlpFVqymDs'; - generateJwtToken(name: string, userId: string) { return ''; } + authToken = + 'eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkNDQ4ODc1Yjk2M2ZmN2UwMGM2YjZiMyIsImlhdCI6MTU3Njc5NzQxNCwiZXhwIjoxNTc2Nzk4MzE0fQ.AXeWhEMSRS_kB7ISiRUt5vk9T71_mXUWevl_wT_tnyiS9vHQScMIY4qVzQnrx21FUwfyAmUVOdRdFNPpndGFPW4kARaPbeVkOSgF4vPt4MHJqvlXrA-B97Z7u9ahRqMFcdNyCIWbbR-bQ4592TJLdoGdx1Mqbc0brSYDlLaaG2aGPYN'; + refreshToken = + 'eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkNDQ4ODc1Yjk2M2ZmN2UwMGM2YjZiMyIsImlhdCI6MTU3Njc5NzQxNCwiZXhwIjoxNTc3NDAyMjE0fQ.AAOwakFw8lKXRwmeLqPWksQTKjXqYVAb-Gc_sXhXolU36fPr69Flxgf5c2YZ6qCghZlloZ3TR5PaJ1PT3b2s1je5AfQw01fcZ56E10LGUyfLvV02Mgy5ler9PzbLi7sZzRs1QgDUeq3Ml9WYRIZirP_r9VxVS_4eDluP3NNlpFVqymDs'; + generateJwtToken(name: string, userId: string) { + return ''; + } refreshTokens(oldToken: string) { return { authToken: this.authToken, @@ -63,20 +67,33 @@ describe('Auth Controller', () => { }); it('should reject if the old refresh token is not present', async () => { - await expect(controller.refreshToken(undefined)).rejects.toThrow(BadRequestException); + await expect(controller.refreshToken(undefined)).rejects.toThrow( + BadRequestException, + ); }); it('should be rejected if the token is invalid', async () => { - jest.spyOn(authService, 'refreshTokens').mockRejectedValue('invalid token' as never); - await expect(controller.refreshToken('OLD_REFRESH_TOKEN')).rejects.toThrow(BadRequestException) + jest + .spyOn(authService, 'refreshTokens') + .mockRejectedValue('invalid token' as never); + await expect( + controller.refreshToken('OLD_REFRESH_TOKEN'), + ).rejects.toThrow(BadRequestException); }); }); describe('#refreshWsToken()', () => { it('should generate ws token', async () => { - const spy = jest.spyOn(authService, 'generateJwtToken').mockImplementation(() => 'FAKE_WS_TOKEN'); - const result = await controller.refreshWsToken({ id: 'FAKE_USER_ID' } as Player); - expect(spy).toHaveBeenCalledWith(JwtTokenPurpose.websocket, 'FAKE_USER_ID'); + const spy = jest + .spyOn(authService, 'generateJwtToken') + .mockImplementation(() => 'FAKE_WS_TOKEN'); + const result = await controller.refreshWsToken({ + id: 'FAKE_USER_ID', + } as Player); + expect(spy).toHaveBeenCalledWith( + JwtTokenPurpose.websocket, + 'FAKE_USER_ID', + ); expect(result).toEqual({ wsToken: 'FAKE_WS_TOKEN' }); }); }); diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index a0879d35b..0789a31d0 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -1,4 +1,11 @@ -import { Controller, Get, Post, Query, BadRequestException, Logger } from '@nestjs/common'; +import { + Controller, + Get, + Post, + Query, + BadRequestException, + Logger, +} from '@nestjs/common'; import { HttpAdapterHost } from '@nestjs/core'; import { authenticate } from 'passport'; import { Environment } from '@/environment/environment'; @@ -11,7 +18,6 @@ import { JwtTokenPurpose } from '../jwt-token-purpose'; @Controller('auth') export class AuthController { - private logger = new Logger(AuthController.name); constructor( @@ -22,31 +28,46 @@ export class AuthController { // The steam return route has to be defined like that. Any nestjs route decorators // would screw up some request params, resulting in OpenID throwing an error. // https://github.com/liamcurry/passport-steam/issues/57 - this.adapterHost.httpAdapter?.get('/auth/steam/return', (req, res, next) => { - return authenticate('steam', async (error, player: Player) => { - const url = req.cookies?.[redirectUrlCookieName] || this.environment.clientUrl; + this.adapterHost.httpAdapter?.get( + '/auth/steam/return', + (req, res, next) => { + return authenticate('steam', async (error, player: Player) => { + const url = + req.cookies?.[redirectUrlCookieName] || this.environment.clientUrl; - if (error) { - this.logger.warn(`Login error: ${error}`); - return res.redirect(`${url}/auth-error?error=${error.message}`); - } + if (error) { + this.logger.warn(`Login error: ${error}`); + return res.redirect(`${url}/auth-error?error=${error.message}`); + } - if (!player) { - return res.sendStatus(401); - } + if (!player) { + return res.sendStatus(401); + } - const refreshToken = await this.authService.generateJwtToken(JwtTokenPurpose.refresh, player.id); - const authToken = await this.authService.generateJwtToken(JwtTokenPurpose.auth, player.id); - return res.redirect(`${url}?refresh_token=${refreshToken}&auth_token=${authToken}`); - })(req, res, next); - }); + const refreshToken = await this.authService.generateJwtToken( + JwtTokenPurpose.refresh, + player.id, + ); + const authToken = await this.authService.generateJwtToken( + JwtTokenPurpose.auth, + player.id, + ); + return res.redirect( + `${url}?refresh_token=${refreshToken}&auth_token=${authToken}`, + ); + })(req, res, next); + }, + ); } @Post() async refreshToken(@Query('refresh_token') oldRefreshToken: string) { if (oldRefreshToken !== undefined) { try { - const { refreshToken, authToken } = await this.authService.refreshTokens(oldRefreshToken); + const { + refreshToken, + authToken, + } = await this.authService.refreshTokens(oldRefreshToken); return { refreshToken, authToken }; } catch (error) { throw new BadRequestException(error.message); @@ -59,8 +80,10 @@ export class AuthController { @Get('wstoken') @Auth() async refreshWsToken(@User() user: Player) { - const wsToken = await this.authService.generateJwtToken(JwtTokenPurpose.websocket, user.id); + const wsToken = await this.authService.generateJwtToken( + JwtTokenPurpose.websocket, + user.id, + ); return { wsToken }; } - } diff --git a/src/auth/decorators/user.decorator.ts b/src/auth/decorators/user.decorator.ts index 2eaf2dd11..8868b2b45 100644 --- a/src/auth/decorators/user.decorator.ts +++ b/src/auth/decorators/user.decorator.ts @@ -1,4 +1,7 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; // tslint:disable-next-line: variable-name -export const User = createParamDecorator((data: unknown, ctx: ExecutionContext) => ctx.switchToHttp().getRequest().user); +export const User = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => + ctx.switchToHttp().getRequest().user, +); diff --git a/src/auth/decorators/ws-authorized.decorator.ts b/src/auth/decorators/ws-authorized.decorator.ts index 370c905ac..0f05c9b5e 100644 --- a/src/auth/decorators/ws-authorized.decorator.ts +++ b/src/auth/decorators/ws-authorized.decorator.ts @@ -2,7 +2,5 @@ import { applyDecorators, UseGuards } from '@nestjs/common'; import { WsAuthorizedGuard } from '../guards/ws-authorized.guard'; export function WsAuthorized() { - return applyDecorators( - UseGuards(WsAuthorizedGuard), - ); + return applyDecorators(UseGuards(WsAuthorizedGuard)); } diff --git a/src/auth/errors/invalid-token.error.ts b/src/auth/errors/invalid-token.error.ts index 43692d251..c9172a4de 100644 --- a/src/auth/errors/invalid-token.error.ts +++ b/src/auth/errors/invalid-token.error.ts @@ -1,7 +1,5 @@ export class InvalidTokenError extends Error { - constructor() { super('invalid token'); } - } diff --git a/src/auth/gateways/auth.gateway.spec.ts b/src/auth/gateways/auth.gateway.spec.ts index 98b570fc2..c6fb7394c 100644 --- a/src/auth/gateways/auth.gateway.spec.ts +++ b/src/auth/gateways/auth.gateway.spec.ts @@ -2,9 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthGateway } from './auth.gateway'; import { PlayersService } from '@/players/services/players.service'; -class PlayersServiceStub { - -} +class PlayersServiceStub {} describe('AuthGateway', () => { let gateway: AuthGateway; @@ -26,7 +24,9 @@ describe('AuthGateway', () => { }); describe('#onModuleInit()', () => { - beforeEach(() => gateway.server = { use: jest.fn().mockReturnValue(null) } as any); + beforeEach( + () => (gateway.server = { use: jest.fn().mockReturnValue(null) } as any), + ); it('should register middleware', () => { gateway.onModuleInit(); diff --git a/src/auth/gateways/auth.gateway.ts b/src/auth/gateways/auth.gateway.ts index 6c8fec1f3..64a228880 100644 --- a/src/auth/gateways/auth.gateway.ts +++ b/src/auth/gateways/auth.gateway.ts @@ -6,31 +6,34 @@ import { PlayersService } from '@/players/services/players.service'; @WebSocketGateway() export class AuthGateway implements OnModuleInit { - @WebSocketServer() server: Server; constructor( private playersService: PlayersService, @Inject('WEBSOCKET_SECRET') private websocketSecret: string, - ) { } + ) {} onModuleInit() { - this.server?.use(authenticate({ - secret: this.websocketSecret, - succeedWithoutToken: true, - }, async (payload, done) => { - if (payload && payload.id) { - try { - const player = await this.playersService.getById(payload.id); - return done(null, player); - } catch (error) { - return done(error, false); - } - } else { - return done(); - } - })); + this.server?.use( + authenticate( + { + secret: this.websocketSecret, + succeedWithoutToken: true, + }, + async (payload, done) => { + if (payload && payload.id) { + try { + const player = await this.playersService.getById(payload.id); + return done(null, player); + } catch (error) { + return done(error, false); + } + } else { + return done(); + } + }, + ), + ); } - } diff --git a/src/auth/guards/role.guard.spec.ts b/src/auth/guards/role.guard.spec.ts index b6cd995a5..55e6d01a1 100644 --- a/src/auth/guards/role.guard.spec.ts +++ b/src/auth/guards/role.guard.spec.ts @@ -14,7 +14,7 @@ describe('RoleGuard', () => { let guard: RoleGuard; beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ }).compile(); + const module: TestingModule = await Test.createTestingModule({}).compile(); reflector = module.get(Reflector); guard = new RoleGuard(reflector); }); @@ -29,11 +29,13 @@ describe('RoleGuard', () => { }); it('should allow when the user has the required role', () => { - jest.spyOn(reflector, 'get').mockImplementation(() => [ PlayerRole.superUser ]); + jest + .spyOn(reflector, 'get') + .mockImplementation(() => [PlayerRole.superUser]); context.switchToHttp.mockImplementation(() => ({ getRequest: () => ({ user: { - roles: [ PlayerRole.superUser ] + roles: [PlayerRole.superUser], }, }), })); @@ -41,14 +43,18 @@ describe('RoleGuard', () => { }); it('should deny when the user does not have the required role', () => { - jest.spyOn(reflector, 'get').mockImplementation(() => [ PlayerRole.superUser ]); + jest + .spyOn(reflector, 'get') + .mockImplementation(() => [PlayerRole.superUser]); context.switchToHttp.mockImplementation(() => ({ getRequest: () => ({ user: { - roles: [ PlayerRole.admin ], + roles: [PlayerRole.admin], }, }), })); - expect(() => guard.canActivate(context as any)).toThrow(UnauthorizedException); + expect(() => guard.canActivate(context as any)).toThrow( + UnauthorizedException, + ); }); }); diff --git a/src/auth/guards/role.guard.ts b/src/auth/guards/role.guard.ts index 0656768e4..3338ef592 100644 --- a/src/auth/guards/role.guard.ts +++ b/src/auth/guards/role.guard.ts @@ -1,22 +1,27 @@ -import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { PlayerRole } from '@/players/models/player-role'; import { Player } from '@/players/models/player'; @Injectable() export class RoleGuard implements CanActivate { - - constructor( - private reflector: Reflector, - ) { } + constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { - const roles = this.reflector.get('roles', context.getHandler()); + const roles = this.reflector.get( + 'roles', + context.getHandler(), + ); if (roles?.length) { const request = context.switchToHttp().getRequest(); const user = request.user as Player; - if (!user || !roles.some(r => user.roles.includes(r))) { + if (!user || !roles.some((r) => user.roles.includes(r))) { throw new UnauthorizedException(); } } diff --git a/src/auth/guards/ws-authorized.guard.spec.ts b/src/auth/guards/ws-authorized.guard.spec.ts index 986d382b6..ea5150833 100644 --- a/src/auth/guards/ws-authorized.guard.spec.ts +++ b/src/auth/guards/ws-authorized.guard.spec.ts @@ -20,7 +20,9 @@ describe('WsAuthorizedGuard', () => { it('should deny if the user is not authenticated', () => { user.logged_in = false; const guard = new WsAuthorizedGuard(); - expect(() => guard.canActivate(context as any)).toThrowError('unauthorized'); + expect(() => guard.canActivate(context as any)).toThrowError( + 'unauthorized', + ); }); it('should pass if the user is authenticated', () => { diff --git a/src/auth/guards/ws-authorized.guard.ts b/src/auth/guards/ws-authorized.guard.ts index 501f3a9c0..e97fa3361 100644 --- a/src/auth/guards/ws-authorized.guard.ts +++ b/src/auth/guards/ws-authorized.guard.ts @@ -4,7 +4,6 @@ import { WsClient } from '../ws-client'; @Injectable() export class WsAuthorizedGuard implements CanActivate { - canActivate(context: ExecutionContext): boolean { const client = context.switchToWs().getClient() as WsClient; if (client?.request?.user?.logged_in) { diff --git a/src/auth/import-or-generate-keys.spec.ts b/src/auth/import-or-generate-keys.spec.ts index dc215a052..4a40d81f1 100644 --- a/src/auth/import-or-generate-keys.spec.ts +++ b/src/auth/import-or-generate-keys.spec.ts @@ -28,48 +28,65 @@ describe('importOrGenerateKeys()', () => { typegooseTestingModule(mongod), TypegooseModule.forFeature([Key]), ], - providers: [ - { provide: Environment, useValue: environment }, - ], + providers: [{ provide: Environment, useValue: environment }], }).compile(); keyModel = module.get(getModelToken(Key.name)); }); - afterEach(async () => await keyModel.deleteMany({ })); + afterEach(async () => await keyModel.deleteMany({})); describe('when the key is in the database', () => { let generatedKeyPair: KeyPair; beforeEach(async () => { - const generateKeyPairAsync = promisify(generateKeyPair) - generatedKeyPair = await generateKeyPairAsync('ec', { namedCurve: 'secp521r1' }); + const generateKeyPairAsync = promisify(generateKeyPair); + generatedKeyPair = await generateKeyPairAsync('ec', { + namedCurve: 'secp521r1', + }); await keyModel.create({ name: KeyName.auth, - publicKeyEncoded: generatedKeyPair.publicKey.export({ - format: 'pem', - type: 'spki', - }).toString(), - privateKeyEncoded: generatedKeyPair.privateKey.export({ - format: 'pem', - type: 'pkcs8', - passphrase: environment.keyStorePassphare, - cipher: 'aes-256-cbc', - }).toString(), + publicKeyEncoded: generatedKeyPair.publicKey + .export({ + format: 'pem', + type: 'spki', + }) + .toString(), + privateKeyEncoded: generatedKeyPair.privateKey + .export({ + format: 'pem', + type: 'pkcs8', + passphrase: environment.keyStorePassphare, + cipher: 'aes-256-cbc', + }) + .toString(), }); }); it('should return the same keys', async () => { - const keyPair = await importOrGenerateKeys(keyModel, KeyName.auth, environment.keyStorePassphare); - expect(keyPair.publicKey.export({ format: 'pem', type: 'spki' }).toString()) - .toEqual(generatedKeyPair.publicKey.export({ format: 'pem', type: 'spki' }).toString()) + const keyPair = await importOrGenerateKeys( + keyModel, + KeyName.auth, + environment.keyStorePassphare, + ); + expect( + keyPair.publicKey.export({ format: 'pem', type: 'spki' }).toString(), + ).toEqual( + generatedKeyPair.publicKey + .export({ format: 'pem', type: 'spki' }) + .toString(), + ); }); }); describe('when the key is not yet in the database', () => { it('should generate them', async () => { - await importOrGenerateKeys(keyModel, KeyName.auth, environment.keyStorePassphare); + await importOrGenerateKeys( + keyModel, + KeyName.auth, + environment.keyStorePassphare, + ); expect(await keyModel.findOne({ name: KeyName.auth })).toBeTruthy(); }); }); diff --git a/src/auth/import-or-generate-keys.ts b/src/auth/import-or-generate-keys.ts index bb733b94b..4bb9556f4 100644 --- a/src/auth/import-or-generate-keys.ts +++ b/src/auth/import-or-generate-keys.ts @@ -1,5 +1,9 @@ import { mongoose, ReturnModelType } from '@typegoose/typegoose'; -import { createPrivateKey, createPublicKey, generateKeyPair as generateKeyPairCb } from 'crypto'; +import { + createPrivateKey, + createPublicKey, + generateKeyPair as generateKeyPairCb, +} from 'crypto'; import { promisify } from 'util'; import { KeyName } from './key-name'; import { Key } from './models/key'; @@ -11,13 +15,25 @@ const generateKeyPair = promisify(generateKeyPairCb); /** * Try to fetch the key */ -export const importOrGenerateKeys = async (keyModel: ReturnModelType, name: KeyName, passphrase: string): Promise => { +export const importOrGenerateKeys = async ( + keyModel: ReturnModelType, + name: KeyName, + passphrase: string, +): Promise => { const logger = new Logger('AuthModule'); logger.debug(`Importing ${name} keys...`); try { - const { privateKeyEncoded, publicKeyEncoded } = await keyModel.findOne({ name }).orFail().lean().exec(); - const privateKey = createPrivateKey({ key: privateKeyEncoded, format: 'pem', passphrase: passphrase }); + const { privateKeyEncoded, publicKeyEncoded } = await keyModel + .findOne({ name }) + .orFail() + .lean() + .exec(); + const privateKey = createPrivateKey({ + key: privateKeyEncoded, + format: 'pem', + passphrase: passphrase, + }); const publicKey = createPublicKey({ key: publicKeyEncoded, format: 'pem' }); logger.debug(`${name} keys imported.`); return { publicKey, privateKey }; @@ -26,22 +42,33 @@ export const importOrGenerateKeys = async (keyModel: ReturnModelType logger.debug(`${name} keys not found, generating new ones...`); const keys = await generateKeyPair('ec', { namedCurve: 'secp521r1' }); - await keyModel.findOneAndUpdate({ name }, { - privateKeyEncoded: keys.privateKey.export({ - format: 'pem', - type: 'pkcs8', - passphrase: passphrase, - cipher: 'aes-256-cbc', - }).toString(), - publicKeyEncoded: keys.publicKey.export({ - format: 'pem', - type: 'spki', - }).toString(), - }, { upsert: true }).lean().exec(); + await keyModel + .findOneAndUpdate( + { name }, + { + privateKeyEncoded: keys.privateKey + .export({ + format: 'pem', + type: 'pkcs8', + passphrase: passphrase, + cipher: 'aes-256-cbc', + }) + .toString(), + publicKeyEncoded: keys.publicKey + .export({ + format: 'pem', + type: 'spki', + }) + .toString(), + }, + { upsert: true }, + ) + .lean() + .exec(); return keys; } else { throw error; } } -} +}; diff --git a/src/auth/jwt-token-purpose.ts b/src/auth/jwt-token-purpose.ts index 014ade618..da5fd4adb 100644 --- a/src/auth/jwt-token-purpose.ts +++ b/src/auth/jwt-token-purpose.ts @@ -1,6 +1,6 @@ export enum JwtTokenPurpose { - auth, /**< used by the client to make standard API calls (expiration time: 15 minutes) */ - refresh, /**< used by the client to obtain the new auth token (expiration time: 7 days, number of uses: 1) */ - websocket, /**< used by the client to identify himself over the websocket (expiration time: 10 minutes) */ - context, /**< used by the server to keep the context across external calls (e.g. twitch.tv OAuth) */ + auth /**< used by the client to make standard API calls (expiration time: 15 minutes) */, + refresh /**< used by the client to obtain the new auth token (expiration time: 7 days, number of uses: 1) */, + websocket /**< used by the client to identify himself over the websocket (expiration time: 10 minutes) */, + context /**< used by the server to keep the context across external calls (e.g. twitch.tv OAuth) */, } diff --git a/src/auth/middleware/set-redirect-url-cookie.spec.ts b/src/auth/middleware/set-redirect-url-cookie.spec.ts index f4b516134..f66f89ec4 100644 --- a/src/auth/middleware/set-redirect-url-cookie.spec.ts +++ b/src/auth/middleware/set-redirect-url-cookie.spec.ts @@ -13,12 +13,16 @@ describe('setRedirectUrlCookie', () => { }); it('should set the cookie', () => { - setRedirectUrlCookie({ query: { url: 'FAKE_URL' }} as any, response as any, nextFn); + setRedirectUrlCookie( + { query: { url: 'FAKE_URL' } } as any, + response as any, + nextFn, + ); expect(response.cookie).toHaveBeenCalledWith('redirect-url', 'FAKE_URL'); }); it('should call the next function', () => { - setRedirectUrlCookie({ query: { }} as any, response as any, nextFn); + setRedirectUrlCookie({ query: {} } as any, response as any, nextFn); expect(nextFn).toHaveBeenCalled(); }); }); diff --git a/src/auth/middleware/set-redirect-url-cookie.ts b/src/auth/middleware/set-redirect-url-cookie.ts index 47928453a..240a500a7 100644 --- a/src/auth/middleware/set-redirect-url-cookie.ts +++ b/src/auth/middleware/set-redirect-url-cookie.ts @@ -2,11 +2,15 @@ import { NextFunction, Request, Response } from 'express'; export const redirectUrlCookieName = 'redirect-url'; -export const setRedirectUrlCookie = (req: Request, res: Response, next: NextFunction) => { +export const setRedirectUrlCookie = ( + req: Request, + res: Response, + next: NextFunction, +) => { // Set the current url as a cookie so we can redirect to the exact url afterwards if (req.query.url) { res.cookie(redirectUrlCookieName, req.query.url); } next(); -} +}; diff --git a/src/auth/models/key.ts b/src/auth/models/key.ts index 88984633d..20044ed7f 100644 --- a/src/auth/models/key.ts +++ b/src/auth/models/key.ts @@ -1,7 +1,6 @@ import { prop } from '@typegoose/typegoose'; export class Key { - @prop({ required: true, unique: true }) name: string; @@ -10,5 +9,4 @@ export class Key { @prop({ required: true }) publicKeyEncoded: string; - } diff --git a/src/auth/models/refresh-token.ts b/src/auth/models/refresh-token.ts index f496c494e..0650fa44b 100644 --- a/src/auth/models/refresh-token.ts +++ b/src/auth/models/refresh-token.ts @@ -2,11 +2,9 @@ import { prop, index } from '@typegoose/typegoose'; @index({ value: 'hashed' }) export class RefreshToken { - @prop({ required: true }) value!: string; @prop({ default: () => new Date() }) public createdAt?: Date; - } diff --git a/src/auth/services/auth.service.spec.ts b/src/auth/services/auth.service.spec.ts index ac42dfc19..d689fba09 100644 --- a/src/auth/services/auth.service.spec.ts +++ b/src/auth/services/auth.service.spec.ts @@ -26,10 +26,22 @@ describe('AuthService', () => { ], providers: [ AuthService, - { provide: 'AUTH_TOKEN_KEY', useFactory: () => generateKeyPairSync('ec', { namedCurve: 'secp521r1' }) }, - { provide: 'REFRESH_TOKEN_KEY', useFactory: () => generateKeyPairSync('ec', { namedCurve: 'secp521r1' }) }, + { + provide: 'AUTH_TOKEN_KEY', + useFactory: () => + generateKeyPairSync('ec', { namedCurve: 'secp521r1' }), + }, + { + provide: 'REFRESH_TOKEN_KEY', + useFactory: () => + generateKeyPairSync('ec', { namedCurve: 'secp521r1' }), + }, { provide: 'WEBSOCKET_SECRET', useValue: 'websocket_secret' }, - { provide: 'CONTEXT_TOKEN_KEY', useFactory: () => generateKeyPairSync('ec', { namedCurve: 'secp521r1' }) }, + { + provide: 'CONTEXT_TOKEN_KEY', + useFactory: () => + generateKeyPairSync('ec', { namedCurve: 'secp521r1' }), + }, ], }).compile(); @@ -39,7 +51,7 @@ describe('AuthService', () => { refreshKeys = module.get('REFRESH_TOKEN_KEY'); }); - afterEach(async () => await refreshTokenModel.deleteMany({ })); + afterEach(async () => await refreshTokenModel.deleteMany({})); it('should be defined', () => { expect(service).toBeDefined(); @@ -48,21 +60,38 @@ describe('AuthService', () => { describe('#generateJwtToken()', () => { describe('auth', () => { it('should encode user id', async () => { - const token = await service.generateJwtToken(JwtTokenPurpose.auth, 'FAKE_USER_ID'); - const decoded = decode(token) as { id: string; iat: number; exp: number }; + const token = await service.generateJwtToken( + JwtTokenPurpose.auth, + 'FAKE_USER_ID', + ); + const decoded = decode(token) as { + id: string; + iat: number; + exp: number; + }; expect(decoded.id).toEqual('FAKE_USER_ID'); }); }); describe('refresh', () => { it('should encode user id', async () => { - const token = await service.generateJwtToken(JwtTokenPurpose.refresh, 'FAKE_USER_ID'); - const decoded = decode(token) as { id: string; iat: number; exp: number }; + const token = await service.generateJwtToken( + JwtTokenPurpose.refresh, + 'FAKE_USER_ID', + ); + const decoded = decode(token) as { + id: string; + iat: number; + exp: number; + }; expect(decoded.id).toEqual('FAKE_USER_ID'); }); it('should store the token in the database', async () => { - const value = await service.generateJwtToken(JwtTokenPurpose.refresh, 'FAKE_USER_ID'); + const value = await service.generateJwtToken( + JwtTokenPurpose.refresh, + 'FAKE_USER_ID', + ); const key = await refreshTokenModel.findOne({ value }); expect(key).toBeDefined(); }); @@ -70,16 +99,30 @@ describe('AuthService', () => { describe('ws', () => { it('should encode user id', async () => { - const token = await service.generateJwtToken(JwtTokenPurpose.websocket, 'FAKE_USER_ID'); - const decoded = decode(token) as { id: string, iat: number, exp: number }; + const token = await service.generateJwtToken( + JwtTokenPurpose.websocket, + 'FAKE_USER_ID', + ); + const decoded = decode(token) as { + id: string; + iat: number; + exp: number; + }; expect(decoded.id).toEqual('FAKE_USER_ID'); }); }); describe('context', () => { it('should encode user id', async () => { - const token = await service.generateJwtToken(JwtTokenPurpose.context, 'FAKE_USER_ID'); - const decoded = decode(token) as { id: string; iat: number; exp: number }; + const token = await service.generateJwtToken( + JwtTokenPurpose.context, + 'FAKE_USER_ID', + ); + const decoded = decode(token) as { + id: string; + iat: number; + exp: number; + }; expect(decoded.id).toEqual('FAKE_USER_ID'); }); }); @@ -87,66 +130,122 @@ describe('AuthService', () => { describe('#refreshTokens()', () => { it('should throw an error if the refresh token is not in the database', async () => { - await expect(service.refreshTokens('some fake token')).rejects.toThrow(InvalidTokenError); + await expect(service.refreshTokens('some fake token')).rejects.toThrow( + InvalidTokenError, + ); }); it('should throw an error if the refresh token has expired', async () => { - const key = refreshKeys.privateKey.export({ format: 'pem', type: 'pkcs8' }); + const key = refreshKeys.privateKey.export({ + format: 'pem', + type: 'pkcs8', + }); // issue a token that has already expired const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); - const token = sign({ id: 'FAKE_USER_ID', iat: Math.floor(oneWeekAgo.getDate() / 1000) - 60 }, key, { algorithm: 'ES512', expiresIn: '7d' }); + const token = sign( + { + id: 'FAKE_USER_ID', + iat: Math.floor(oneWeekAgo.getDate() / 1000) - 60, + }, + key, + { algorithm: 'ES512', expiresIn: '7d' }, + ); await refreshTokenModel.create({ value: token }); - await expect(service.refreshTokens(token)).rejects.toThrowError('jwt expired'); + await expect(service.refreshTokens(token)).rejects.toThrowError( + 'jwt expired', + ); }); it('should throw an error unless the refresh token matches', async () => { const key = generateKeyPairSync('ec', { namedCurve: 'secp521r1' }); - const token = sign({ id: 'FAKE_USER_ID' }, key.privateKey.export({ format: 'pem', type: 'pkcs8' }), { algorithm: 'ES512', expiresIn: '7d' }); + const token = sign( + { id: 'FAKE_USER_ID' }, + key.privateKey.export({ format: 'pem', type: 'pkcs8' }), + { algorithm: 'ES512', expiresIn: '7d' }, + ); await refreshTokenModel.create({ value: token }); - await expect(service.refreshTokens(token)).rejects.toThrowError('invalid signature'); + await expect(service.refreshTokens(token)).rejects.toThrowError( + 'invalid signature', + ); }); it('should generate auth and refresh tokens', async () => { - const oldRefreshToken = await service.generateJwtToken(JwtTokenPurpose.refresh, 'FAKE_USER_ID'); - const { refreshToken, authToken } = await service.refreshTokens(oldRefreshToken); + const oldRefreshToken = await service.generateJwtToken( + JwtTokenPurpose.refresh, + 'FAKE_USER_ID', + ); + const { refreshToken, authToken } = await service.refreshTokens( + oldRefreshToken, + ); - const refreshPublicKey = refreshKeys.publicKey.export({ format: 'pem', type: 'spki' }); + const refreshPublicKey = refreshKeys.publicKey.export({ + format: 'pem', + type: 'spki', + }); - const refreshTokenDecoded = verify(refreshToken, refreshPublicKey, { algorithms: ['ES512'] }) as { id: string; iat: number; exp: number }; + const refreshTokenDecoded = verify(refreshToken, refreshPublicKey, { + algorithms: ['ES512'], + }) as { id: string; iat: number; exp: number }; expect(refreshTokenDecoded.id).toEqual('FAKE_USER_ID'); - const authPublicKey = authKeys.publicKey.export({ format: 'pem', type: 'spki' }); - const authTokenDecoded = verify(authToken, authPublicKey, { algorithms: ['ES512'] }) as { id: string, iat: number, exp: number }; + const authPublicKey = authKeys.publicKey.export({ + format: 'pem', + type: 'spki', + }); + const authTokenDecoded = verify(authToken, authPublicKey, { + algorithms: ['ES512'], + }) as { id: string; iat: number; exp: number }; expect(authTokenDecoded.id).toEqual('FAKE_USER_ID'); }); it('should remove the old refresh token', async () => { - const oldRefreshToken = await service.generateJwtToken(JwtTokenPurpose.refresh, 'FAKE_USER_ID'); + const oldRefreshToken = await service.generateJwtToken( + JwtTokenPurpose.refresh, + 'FAKE_USER_ID', + ); await service.refreshTokens(oldRefreshToken); - expect(await refreshTokenModel.findOne({ value: oldRefreshToken })).toBeNull(); + expect( + await refreshTokenModel.findOne({ value: oldRefreshToken }), + ).toBeNull(); }); }); describe('#verifyToken()', () => { it('should verify auth token', async () => { - const token = await service.generateJwtToken(JwtTokenPurpose.auth, 'FAKE_USER_ID'); - expect(service.verifyToken(JwtTokenPurpose.auth, token).id).toEqual('FAKE_USER_ID'); + const token = await service.generateJwtToken( + JwtTokenPurpose.auth, + 'FAKE_USER_ID', + ); + expect(service.verifyToken(JwtTokenPurpose.auth, token).id).toEqual( + 'FAKE_USER_ID', + ); }); it('should verify context token', async () => { - const token = await service.generateJwtToken(JwtTokenPurpose.context, 'FAKE_USER_ID'); - expect(service.verifyToken(JwtTokenPurpose.context, token).id).toEqual('FAKE_USER_ID'); + const token = await service.generateJwtToken( + JwtTokenPurpose.context, + 'FAKE_USER_ID', + ); + expect(service.verifyToken(JwtTokenPurpose.context, token).id).toEqual( + 'FAKE_USER_ID', + ); }); }); describe('#removeOldRefreshTokens()', () => { it('should remove old tokens', async () => { - const key = refreshKeys.privateKey.export({ format: 'pem', type: 'pkcs8' }); - const token = sign({ id: 'FAKE_USER_ID' }, key, { algorithm: 'ES512', expiresIn: '7d' }); + const key = refreshKeys.privateKey.export({ + format: 'pem', + type: 'pkcs8', + }); + const token = sign({ id: 'FAKE_USER_ID' }, key, { + algorithm: 'ES512', + expiresIn: '7d', + }); const createdAt = new Date(); createdAt.setDate(createdAt.getDate() - 8); diff --git a/src/auth/services/auth.service.ts b/src/auth/services/auth.service.ts index ed6be2457..c5baa1fa2 100644 --- a/src/auth/services/auth.service.ts +++ b/src/auth/services/auth.service.ts @@ -10,56 +10,97 @@ import { KeyPair } from '../key-pair'; @Injectable() export class AuthService implements OnModuleInit { - private logger = new Logger(AuthService.name); constructor( - @InjectModel(RefreshToken) private refreshTokenModel: ReturnModelType, + @InjectModel(RefreshToken) + private refreshTokenModel: ReturnModelType, @Inject('AUTH_TOKEN_KEY') private authTokenKey: KeyPair, @Inject('REFRESH_TOKEN_KEY') private refreshTokenKey: KeyPair, @Inject('WEBSOCKET_SECRET') private websocketSecret: string, @Inject('CONTEXT_TOKEN_KEY') private contextTokenKey: KeyPair, - ) { } + ) {} onModuleInit() { this.removeOldRefreshTokens(); } - async generateJwtToken(purpose: JwtTokenPurpose, userId: string): Promise { + async generateJwtToken( + purpose: JwtTokenPurpose, + userId: string, + ): Promise { switch (purpose) { case JwtTokenPurpose.auth: { - const key = this.authTokenKey.privateKey.export({ format: 'pem', type: 'pkcs8' }); - return sign({ id: userId }, key, { algorithm: 'ES512', expiresIn: '15m' }); + const key = this.authTokenKey.privateKey.export({ + format: 'pem', + type: 'pkcs8', + }); + return sign({ id: userId }, key, { + algorithm: 'ES512', + expiresIn: '15m', + }); } case JwtTokenPurpose.refresh: { - const key = this.refreshTokenKey.privateKey.export({ format: 'pem', type: 'pkcs8' }); - const token = sign({ id: userId }, key, { algorithm: 'ES512', expiresIn: '7d' }); + const key = this.refreshTokenKey.privateKey.export({ + format: 'pem', + type: 'pkcs8', + }); + const token = sign({ id: userId }, key, { + algorithm: 'ES512', + expiresIn: '7d', + }); await this.refreshTokenModel.create({ value: token }); return token; } case JwtTokenPurpose.websocket: { const key = this.websocketSecret; - return sign({ id: userId }, key, { algorithm: 'HS256', expiresIn: '10m' }); + return sign({ id: userId }, key, { + algorithm: 'HS256', + expiresIn: '10m', + }); } case JwtTokenPurpose.context: { - const key = this.contextTokenKey.privateKey.export({ format: 'pem', type: 'pkcs8' }); - return sign({ id: userId }, key, { algorithm: 'ES512', expiresIn: '1m' }) + const key = this.contextTokenKey.privateKey.export({ + format: 'pem', + type: 'pkcs8', + }); + return sign({ id: userId }, key, { + algorithm: 'ES512', + expiresIn: '1m', + }); } } } - async refreshTokens(oldRefreshToken: string): Promise<{ refreshToken: string, authToken: string }> { + async refreshTokens( + oldRefreshToken: string, + ): Promise<{ refreshToken: string; authToken: string }> { try { - await this.refreshTokenModel.deleteOne({ value: oldRefreshToken }).orFail().lean().exec(); - const key = this.refreshTokenKey.publicKey.export({ format: 'pem', type: 'spki' }); - const decoded = verify(oldRefreshToken, key, { algorithms: ['ES512'] }) as { id: string; iat: number; exp: number }; + await this.refreshTokenModel + .deleteOne({ value: oldRefreshToken }) + .orFail() + .lean() + .exec(); + const key = this.refreshTokenKey.publicKey.export({ + format: 'pem', + type: 'spki', + }); + const decoded = verify(oldRefreshToken, key, { + algorithms: ['ES512'], + }) as { id: string; iat: number; exp: number }; const userId = decoded.id; - const refreshToken = await this.generateJwtToken(JwtTokenPurpose.refresh, userId); - const authToken = await this.generateJwtToken(JwtTokenPurpose.auth, userId); + const refreshToken = await this.generateJwtToken( + JwtTokenPurpose.refresh, + userId, + ); + const authToken = await this.generateJwtToken( + JwtTokenPurpose.auth, + userId, + ); return { refreshToken, authToken }; } catch (error) { if (error instanceof mongoose.Error.DocumentNotFoundError) { @@ -70,20 +111,33 @@ export class AuthService implements OnModuleInit { } } - verifyToken(purpose: JwtTokenPurpose.auth | JwtTokenPurpose.context, token: string): { id: string; iat: number; exp: number } { + verifyToken( + purpose: JwtTokenPurpose.auth | JwtTokenPurpose.context, + token: string, + ): { id: string; iat: number; exp: number } { let key: string | Buffer; switch (purpose) { case JwtTokenPurpose.auth: - key = this.authTokenKey.publicKey.export({ format: 'pem', type: 'spki' }); + key = this.authTokenKey.publicKey.export({ + format: 'pem', + type: 'spki', + }); break; case JwtTokenPurpose.context: - key = this.contextTokenKey.publicKey.export({ format: 'pem', type: 'spki' }); + key = this.contextTokenKey.publicKey.export({ + format: 'pem', + type: 'spki', + }); break; } - return verify(token, key, { algorithms: ['ES512'] }) as { id: string; iat: number; exp: number }; + return verify(token, key, { algorithms: ['ES512'] }) as { + id: string; + iat: number; + exp: number; + }; } @Cron(CronExpression.EVERY_WEEK) @@ -92,7 +146,9 @@ export class AuthService implements OnModuleInit { const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); - this.logger.verbose(`removing refresh tokens that are older than ${oneWeekAgo.toString()}`); + this.logger.verbose( + `removing refresh tokens that are older than ${oneWeekAgo.toString()}`, + ); const { n } = await this.refreshTokenModel.deleteMany({ createdAt: { $lt: oneWeekAgo, @@ -100,5 +156,4 @@ export class AuthService implements OnModuleInit { }); this.logger.verbose(`removed ${n} refresh token(s)`); } - } diff --git a/src/auth/strategies/jwt.strategy.ts b/src/auth/strategies/jwt.strategy.ts index 954c30d34..71469c7da 100644 --- a/src/auth/strategies/jwt.strategy.ts +++ b/src/auth/strategies/jwt.strategy.ts @@ -7,7 +7,6 @@ import { KeyPair } from '../key-pair'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { - constructor( private playersService: PlayersService, @Inject('AUTH_TOKEN_KEY') authTokenKey: KeyPair, @@ -15,7 +14,10 @@ export class JwtStrategy extends PassportStrategy(Strategy) { super({ jsonWebTokenOptions: { algorithms: ['ES512'] }, jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - secretOrKey: authTokenKey.publicKey.export({ format: 'pem', type: 'spki' }), + secretOrKey: authTokenKey.publicKey.export({ + format: 'pem', + type: 'spki', + }), }); } @@ -30,5 +32,4 @@ export class JwtStrategy extends PassportStrategy(Strategy) { } } } - } diff --git a/src/auth/strategies/steam.strategy.spec.ts b/src/auth/strategies/steam.strategy.spec.ts index e6a9410bf..12f1d81e6 100644 --- a/src/auth/strategies/steam.strategy.spec.ts +++ b/src/auth/strategies/steam.strategy.spec.ts @@ -15,7 +15,9 @@ const mockPlayer: Player = { jest.mock('@/players/services/players.service', () => ({ PlayersService: jest.fn().mockImplementation(() => ({ findBySteamId: jest.fn().mockResolvedValue(mockPlayer), - createPlayer: jest.fn().mockResolvedValue({ ...mockPlayer, hasAcceptedRules: false }), + createPlayer: jest + .fn() + .mockResolvedValue({ ...mockPlayer, hasAcceptedRules: false }), updatePlayer: jest.fn().mockResolvedValue(mockPlayer), })), })); @@ -48,8 +50,13 @@ describe('SteamStrategy', () => { describe('#validate()', () => { describe('when the player does have an account', () => { - it('should return that player\'s profile', async () => { - const player = await strategy.validate('FAKE_STEAM_ID', { provider: 'steam', id: 'FAKE_STEAM_ID', displayName: 'FAKE_PLAYER', photos: [] }); + it("should return that player's profile", async () => { + const player = await strategy.validate('FAKE_STEAM_ID', { + provider: 'steam', + id: 'FAKE_STEAM_ID', + displayName: 'FAKE_PLAYER', + photos: [], + }); expect(playersService.createPlayer).not.toHaveBeenCalled(); expect(player).toEqual(mockPlayer); }); @@ -57,13 +64,18 @@ describe('SteamStrategy', () => { describe('when the player does not have an account', () => { beforeEach(() => { - playersService.findBySteamId.mockImplementation(steamId => { + playersService.findBySteamId.mockImplementation((steamId) => { throw new mongoose.Error.DocumentNotFoundError({ steamId }); }); }); it('should create it', async () => { - const player = await strategy.validate('FAKE_STEAM_ID', { provider: 'steam', id: 'FAKE_STEAM_ID', displayName: 'FAKE_PLAYER', photos: [] }); + const player = await strategy.validate('FAKE_STEAM_ID', { + provider: 'steam', + id: 'FAKE_STEAM_ID', + displayName: 'FAKE_PLAYER', + photos: [], + }); expect(playersService.createPlayer).toHaveBeenCalled(); expect(player.steamId).toEqual('FAKE_STEAM_ID'); expect(player.hasAcceptedRules).toBe(false); diff --git a/src/auth/strategies/steam.strategy.ts b/src/auth/strategies/steam.strategy.ts index ea359dc8f..0f2d39bb6 100644 --- a/src/auth/strategies/steam.strategy.ts +++ b/src/auth/strategies/steam.strategy.ts @@ -8,11 +8,7 @@ import { mongoose } from '@typegoose/typegoose'; @Injectable() export class SteamStrategy extends PassportStrategy(steam.Strategy) { - - constructor( - environment: Environment, - private playerService: PlayersService, - ) { + constructor(environment: Environment, private playerService: PlayersService) { super({ returnURL: `${environment.apiUrl}/auth/steam/return`, realm: environment.apiUrl, @@ -40,5 +36,4 @@ export class SteamStrategy extends PassportStrategy(steam.Strategy) { } } } - } diff --git a/src/auth/ws-client.ts b/src/auth/ws-client.ts index 700eef3ac..94fd83bba 100644 --- a/src/auth/ws-client.ts +++ b/src/auth/ws-client.ts @@ -6,11 +6,11 @@ type NotLoggedIn = { logged_in: false }; export interface WsClient { request: { user: LoggedIn | NotLoggedIn; - } + }; } export interface AuthorizedWsClient { request: { user: LoggedIn; - } + }; } diff --git a/src/configuration/configuration.module.ts b/src/configuration/configuration.module.ts index bfc5c95bd..0fec811ba 100644 --- a/src/configuration/configuration.module.ts +++ b/src/configuration/configuration.module.ts @@ -15,14 +15,8 @@ import { ConfigurationEntry } from './models/configuration-entry'; }, ]), ], - providers: [ - ConfigurationService, - ], - controllers: [ - ConfigurationController, - ], - exports: [ - ConfigurationService, - ], + providers: [ConfigurationService], + controllers: [ConfigurationController], + exports: [ConfigurationService], }) -export class ConfigurationModule { } +export class ConfigurationModule {} diff --git a/src/configuration/controllers/configuration.controller.spec.ts b/src/configuration/controllers/configuration.controller.spec.ts index 21a6dba65..2f2de92f6 100644 --- a/src/configuration/controllers/configuration.controller.spec.ts +++ b/src/configuration/controllers/configuration.controller.spec.ts @@ -18,9 +18,7 @@ describe('ConfigurationController', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [ConfigurationController], - providers: [ - ConfigurationService, - ], + providers: [ConfigurationService], }).compile(); controller = module.get(ConfigurationController); @@ -32,14 +30,21 @@ describe('ConfigurationController', () => { }); describe('#getDefaultPlayerSkill()', () => { - const defaultPlayerSkill = new Map([[Tf2ClassName.soldier, 2], [Tf2ClassName.scout, 4]]); + const defaultPlayerSkill = new Map([ + [Tf2ClassName.soldier, 2], + [Tf2ClassName.scout, 4], + ]); beforeEach(() => { - configurationService.getDefaultPlayerSkill.mockResolvedValue(defaultPlayerSkill); + configurationService.getDefaultPlayerSkill.mockResolvedValue( + defaultPlayerSkill, + ); }); it('should return default player skill', async () => { - expect(await controller.getDefaultPlayerSkill()).toEqual(new DefaultPlayerSkill(defaultPlayerSkill)); + expect(await controller.getDefaultPlayerSkill()).toEqual( + new DefaultPlayerSkill(defaultPlayerSkill), + ); }); }); @@ -47,13 +52,19 @@ describe('ConfigurationController', () => { const defaultPlayerSkill = new Map([[Tf2ClassName.medic, 5]]); beforeEach(() => { - configurationService.setDefaultPlayerSkill.mockResolvedValue(defaultPlayerSkill); + configurationService.setDefaultPlayerSkill.mockResolvedValue( + defaultPlayerSkill, + ); }); it('should set default player skill', async () => { - const ret = await controller.setDefaultPlayerSkill(new DefaultPlayerSkill(new Map([[Tf2ClassName.medic, 5]]))); + const ret = await controller.setDefaultPlayerSkill( + new DefaultPlayerSkill(new Map([[Tf2ClassName.medic, 5]])), + ); expect(ret).toEqual(new DefaultPlayerSkill(defaultPlayerSkill)); - expect(configurationService.setDefaultPlayerSkill).toHaveBeenCalledWith(defaultPlayerSkill); + expect(configurationService.setDefaultPlayerSkill).toHaveBeenCalledWith( + defaultPlayerSkill, + ); }); }); @@ -63,7 +74,9 @@ describe('ConfigurationController', () => { }); it('should return the whitelist id', async () => { - expect(await controller.getWhitelistId()).toEqual(new WhitelistId('etf2l_6v6')); + expect(await controller.getWhitelistId()).toEqual( + new WhitelistId('etf2l_6v6'), + ); }); }); @@ -75,7 +88,9 @@ describe('ConfigurationController', () => { it('should set the whitelist id', async () => { const ret = await controller.setWhitelistId(new WhitelistId('etf2l_6v6')); expect(ret).toEqual(new WhitelistId('etf2l_6v6')); - expect(configurationService.setWhitelistId).toHaveBeenCalledWith('etf2l_6v6'); + expect(configurationService.setWhitelistId).toHaveBeenCalledWith( + 'etf2l_6v6', + ); }); }); @@ -85,7 +100,9 @@ describe('ConfigurationController', () => { }); it('should return the value', async () => { - expect(await controller.isEtf2lAccountRequired()).toEqual(new Etf2lAccountRequired(true)); + expect(await controller.isEtf2lAccountRequired()).toEqual( + new Etf2lAccountRequired(true), + ); }); }); @@ -95,10 +112,14 @@ describe('ConfigurationController', () => { }); it('should set the value', async () => { - const ret = await controller.setEtf2lAccountRequired(new Etf2lAccountRequired(false)); + const ret = await controller.setEtf2lAccountRequired( + new Etf2lAccountRequired(false), + ); expect(ret).toEqual(new Etf2lAccountRequired(false)); - expect(configurationService.setEtf2lAccountRequired).toHaveBeenCalledWith(false); - }) + expect(configurationService.setEtf2lAccountRequired).toHaveBeenCalledWith( + false, + ); + }); }); describe('#getMinimumTf2InGameHours()', () => { @@ -107,7 +128,9 @@ describe('ConfigurationController', () => { }); it('should return the value', async () => { - expect(await controller.getMinimumTf2InGameHours()).toEqual(new MinimumTf2InGameHours(500)); + expect(await controller.getMinimumTf2InGameHours()).toEqual( + new MinimumTf2InGameHours(500), + ); }); }); @@ -117,31 +140,53 @@ describe('ConfigurationController', () => { }); it('should set the value', async () => { - const ret = await controller.setMinimumTf2InGameHours(new MinimumTf2InGameHours(1000)); + const ret = await controller.setMinimumTf2InGameHours( + new MinimumTf2InGameHours(1000), + ); expect(ret).toEqual(new MinimumTf2InGameHours(1000)); - expect(configurationService.setMinimumTf2InGameHours).toHaveBeenCalledWith(1000); + expect( + configurationService.setMinimumTf2InGameHours, + ).toHaveBeenCalledWith(1000); }); }); describe('#getVoiceSErver()', () => { beforeEach(() => { - configurationService.getVoiceServer.mockResolvedValue({ type: 'mumble', url: 'mumble.melkor.tf', port: 64738 }); + configurationService.getVoiceServer.mockResolvedValue({ + type: 'mumble', + url: 'mumble.melkor.tf', + port: 64738, + }); }); it('should return the value', async () => { - expect(await controller.getVoiceServer()).toEqual(new VoiceServer({ type: 'mumble', url: 'mumble.melkor.tf', port: 64738 })); + expect(await controller.getVoiceServer()).toEqual( + new VoiceServer({ + type: 'mumble', + url: 'mumble.melkor.tf', + port: 64738, + }), + ); }); }); describe('#setVoiceServer', () => { beforeEach(() => { - configurationService.setVoiceServer.mockImplementation(value => Promise.resolve(value)); + configurationService.setVoiceServer.mockImplementation((value) => + Promise.resolve(value), + ); }); it('should set the voice server config', async () => { - const voiceServer: MumbleOptions = { type: 'mumble', url: 'mumble.melkor.tf', port: 64738 }; + const voiceServer: MumbleOptions = { + type: 'mumble', + url: 'mumble.melkor.tf', + port: 64738, + }; const ret = await controller.setVoiceServer(new VoiceServer(voiceServer)); - expect(configurationService.setVoiceServer).toHaveBeenCalledWith(voiceServer); + expect(configurationService.setVoiceServer).toHaveBeenCalledWith( + voiceServer, + ); expect(ret).toEqual(new VoiceServer(voiceServer)); }); }); diff --git a/src/configuration/controllers/configuration.controller.ts b/src/configuration/controllers/configuration.controller.ts index d717a3f80..90ff3829a 100644 --- a/src/configuration/controllers/configuration.controller.ts +++ b/src/configuration/controllers/configuration.controller.ts @@ -1,6 +1,14 @@ import { Auth } from '@/auth/decorators/auth.decorator'; import { PlayerRole } from '@/players/models/player-role'; -import { Body, ClassSerializerInterceptor, Controller, Get, Put, UseInterceptors, ValidationPipe } from '@nestjs/common'; +import { + Body, + ClassSerializerInterceptor, + Controller, + Get, + Put, + UseInterceptors, + ValidationPipe, +} from '@nestjs/common'; import { DefaultPlayerSkill } from '../dto/default-player-skill'; import { Etf2lAccountRequired } from '../dto/etf2l-account-required'; import { MinimumTf2InGameHours } from '../dto/minimum-tf2-in-game-hours'; @@ -10,24 +18,26 @@ import { ConfigurationService } from '../services/configuration.service'; @Controller('configuration') export class ConfigurationController { - - constructor( - private configurationService: ConfigurationService, - ) { } + constructor(private configurationService: ConfigurationService) {} @Get('default-player-skill') @UseInterceptors(ClassSerializerInterceptor) async getDefaultPlayerSkill() { - return new DefaultPlayerSkill(await this.configurationService.getDefaultPlayerSkill()); + return new DefaultPlayerSkill( + await this.configurationService.getDefaultPlayerSkill(), + ); } @Put('default-player-skill') @Auth(PlayerRole.admin) @UseInterceptors(ClassSerializerInterceptor) async setDefaultPlayerSkill( - @Body(new ValidationPipe({ transform: true })) { value }: DefaultPlayerSkill, + @Body(new ValidationPipe({ transform: true })) + { value }: DefaultPlayerSkill, ) { - return new DefaultPlayerSkill(await this.configurationService.setDefaultPlayerSkill(value)); + return new DefaultPlayerSkill( + await this.configurationService.setDefaultPlayerSkill(value), + ); } @Get('whitelist-id') @@ -40,13 +50,17 @@ export class ConfigurationController { @Auth(PlayerRole.admin) @UseInterceptors(ClassSerializerInterceptor) async setWhitelistId(@Body(new ValidationPipe()) { value }: WhitelistId) { - return new WhitelistId(await this.configurationService.setWhitelistId(value)); + return new WhitelistId( + await this.configurationService.setWhitelistId(value), + ); } @Get('etf2l-account-required') @UseInterceptors(ClassSerializerInterceptor) async isEtf2lAccountRequired() { - return new Etf2lAccountRequired(await this.configurationService.isEtf2lAccountRequired()); + return new Etf2lAccountRequired( + await this.configurationService.isEtf2lAccountRequired(), + ); } @Put('etf2l-account-required') @@ -55,13 +69,17 @@ export class ConfigurationController { async setEtf2lAccountRequired( @Body(new ValidationPipe()) { value }: Etf2lAccountRequired, ) { - return new Etf2lAccountRequired(await this.configurationService.setEtf2lAccountRequired(value)); + return new Etf2lAccountRequired( + await this.configurationService.setEtf2lAccountRequired(value), + ); } @Get('minimum-tf2-in-game-hours') @UseInterceptors(ClassSerializerInterceptor) async getMinimumTf2InGameHours() { - return new MinimumTf2InGameHours(await this.configurationService.getMinimumTf2InGameHours()); + return new MinimumTf2InGameHours( + await this.configurationService.getMinimumTf2InGameHours(), + ); } @Put('minimum-tf2-in-game-hours') @@ -70,7 +88,9 @@ export class ConfigurationController { async setMinimumTf2InGameHours( @Body(new ValidationPipe()) { value }: MinimumTf2InGameHours, ) { - return new MinimumTf2InGameHours(await this.configurationService.setMinimumTf2InGameHours(value)); + return new MinimumTf2InGameHours( + await this.configurationService.setMinimumTf2InGameHours(value), + ); } @Get('voice-server') @@ -82,10 +102,9 @@ export class ConfigurationController { @Put('voice-server') @Auth(PlayerRole.admin) @UseInterceptors(ClassSerializerInterceptor) - async setVoiceServer( - @Body(new ValidationPipe()) { value }: VoiceServer, - ) { - return new VoiceServer(await this.configurationService.setVoiceServer(value)); + async setVoiceServer(@Body(new ValidationPipe()) { value }: VoiceServer) { + return new VoiceServer( + await this.configurationService.setVoiceServer(value), + ); } - } diff --git a/src/configuration/dto/default-player-skill.ts b/src/configuration/dto/default-player-skill.ts index 1fb64f2fb..5794924d6 100644 --- a/src/configuration/dto/default-player-skill.ts +++ b/src/configuration/dto/default-player-skill.ts @@ -4,7 +4,6 @@ import { Equals, IsNumber } from 'class-validator'; import { ConfigurationEntryKey } from '../models/configuration-entry-key'; export class DefaultPlayerSkill { - constructor(value: Map) { this.key = ConfigurationEntryKey.defaultPlayerSkill; this.value = value; @@ -13,8 +12,7 @@ export class DefaultPlayerSkill { @Equals(ConfigurationEntryKey.defaultPlayerSkill) key: string; - @IsNumber({ }, { each: true }) + @IsNumber({}, { each: true }) @Type(() => Number) value: Map; - } diff --git a/src/configuration/dto/etf2l-account-required.ts b/src/configuration/dto/etf2l-account-required.ts index 8f00f2c3f..8b525a614 100644 --- a/src/configuration/dto/etf2l-account-required.ts +++ b/src/configuration/dto/etf2l-account-required.ts @@ -2,7 +2,6 @@ import { Equals, IsBoolean } from 'class-validator'; import { ConfigurationEntryKey } from '../models/configuration-entry-key'; export class Etf2lAccountRequired { - constructor(value: boolean) { this.key = ConfigurationEntryKey.etf2lAccountRequired; this.value = value; @@ -13,5 +12,4 @@ export class Etf2lAccountRequired { @IsBoolean() value: boolean; - } diff --git a/src/configuration/dto/minimum-tf2-in-game-hours.ts b/src/configuration/dto/minimum-tf2-in-game-hours.ts index 4a4c324b3..b19772907 100644 --- a/src/configuration/dto/minimum-tf2-in-game-hours.ts +++ b/src/configuration/dto/minimum-tf2-in-game-hours.ts @@ -2,7 +2,6 @@ import { Equals, IsNumber } from 'class-validator'; import { ConfigurationEntryKey } from '../models/configuration-entry-key'; export class MinimumTf2InGameHours { - constructor(value: number) { this.key = ConfigurationEntryKey.minimumTf2InGameHours; this.value = value; @@ -13,5 +12,4 @@ export class MinimumTf2InGameHours { @IsNumber() value: number; - } diff --git a/src/configuration/dto/voice-server.ts b/src/configuration/dto/voice-server.ts index 002acc9b8..934a0386a 100644 --- a/src/configuration/dto/voice-server.ts +++ b/src/configuration/dto/voice-server.ts @@ -3,7 +3,6 @@ import { ConfigurationEntryKey } from '../models/configuration-entry-key'; import { MumbleOptions } from '../models/mumble-options'; export class VoiceServer { - constructor(value: MumbleOptions) { this.key = ConfigurationEntryKey.voiceServer; this.value = value; @@ -13,5 +12,4 @@ export class VoiceServer { key: string; value: MumbleOptions; - } diff --git a/src/configuration/dto/whitelist-id.ts b/src/configuration/dto/whitelist-id.ts index 311f0f822..29a269e94 100644 --- a/src/configuration/dto/whitelist-id.ts +++ b/src/configuration/dto/whitelist-id.ts @@ -2,7 +2,6 @@ import { Equals, IsString } from 'class-validator'; import { ConfigurationEntryKey } from '../models/configuration-entry-key'; export class WhitelistId { - constructor(value: string) { this.key = ConfigurationEntryKey.whitelistId; this.value = value; @@ -13,5 +12,4 @@ export class WhitelistId { @IsString() value: string; - } diff --git a/src/configuration/models/configuration-entry.ts b/src/configuration/models/configuration-entry.ts index ea4a54606..d3be21971 100644 --- a/src/configuration/models/configuration-entry.ts +++ b/src/configuration/models/configuration-entry.ts @@ -2,11 +2,9 @@ import { prop } from '@typegoose/typegoose'; import { ConfigurationEntryKey } from './configuration-entry-key'; export class ConfigurationEntry { - @prop({ enum: ConfigurationEntryKey, unique: true }) key: ConfigurationEntryKey; @prop() value: string; - } diff --git a/src/configuration/models/mumble-options.ts b/src/configuration/models/mumble-options.ts index 1c37f3e21..80e44f487 100644 --- a/src/configuration/models/mumble-options.ts +++ b/src/configuration/models/mumble-options.ts @@ -1,5 +1,5 @@ export interface MumbleOptions { - type: 'mumble', + type: 'mumble'; url: string; port: number; password?: string; diff --git a/src/configuration/services/configuration.service.spec.ts b/src/configuration/services/configuration.service.spec.ts index a29d9a254..aabf82b7f 100644 --- a/src/configuration/services/configuration.service.spec.ts +++ b/src/configuration/services/configuration.service.spec.ts @@ -14,7 +14,7 @@ describe('ConfigurationService', () => { let mongod: MongoMemoryServer; let configurationEntryModel: ReturnModelType; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -23,13 +23,13 @@ describe('ConfigurationService', () => { typegooseTestingModule(mongod), TypegooseModule.forFeature([ConfigurationEntry]), ], - providers: [ - ConfigurationService, - ], + providers: [ConfigurationService], }).compile(); service = module.get(ConfigurationService); - configurationEntryModel = module.get(getModelToken(ConfigurationEntry.name)); + configurationEntryModel = module.get( + getModelToken(ConfigurationEntry.name), + ); }); beforeEach(async () => { @@ -37,8 +37,8 @@ describe('ConfigurationService', () => { }); afterEach(async () => { - await configurationEntryModel.deleteMany({ }); - }) + await configurationEntryModel.deleteMany({}); + }); it('should be defined', () => { expect(service).toBeDefined(); @@ -49,7 +49,9 @@ describe('ConfigurationService', () => { }); it('should set default player skill', async () => { - expect(await service.setDefaultPlayerSkill(new Map([[Tf2ClassName.soldier, 3]]))).toBeTruthy(); + expect( + await service.setDefaultPlayerSkill(new Map([[Tf2ClassName.soldier, 3]])), + ).toBeTruthy(); }); it('should get whitelist id', async () => { @@ -91,8 +93,11 @@ describe('ConfigurationService', () => { url: 'melkor.tf', port: 64738, }; - await configurationEntryModel.updateOne({ key: ConfigurationEntryKey.voiceServer }, { value: JSON.stringify(voiceServer) }, { upsert: true }); + await configurationEntryModel.updateOne( + { key: ConfigurationEntryKey.voiceServer }, + { value: JSON.stringify(voiceServer) }, + { upsert: true }, + ); expect(await service.getVoiceServer()).toEqual(voiceServer); }); - }); diff --git a/src/configuration/services/configuration.service.ts b/src/configuration/services/configuration.service.ts index 25da27eff..99bbef249 100644 --- a/src/configuration/services/configuration.service.ts +++ b/src/configuration/services/configuration.service.ts @@ -7,33 +7,49 @@ import { ConfigurationEntryKey } from '../models/configuration-entry-key'; import { MumbleOptions } from '../models/mumble-options'; // this name wtf -const defaultDefaultPlayerSkill = new Map(Object.keys(Tf2ClassName).map(className => [className, 1])); +const defaultDefaultPlayerSkill = new Map( + Object.keys(Tf2ClassName).map((className) => [className, 1]), +); type VoiceServer = MumbleOptions; @Injectable() export class ConfigurationService implements OnModuleInit { - constructor( - @InjectModel(ConfigurationEntry) private configurationEntryModel: ReturnModelType, - ) { } + @InjectModel(ConfigurationEntry) + private configurationEntryModel: ReturnModelType, + ) {} async onModuleInit() { await Promise.all([ - this.loadDefault(ConfigurationEntryKey.defaultPlayerSkill, JSON.stringify(Object.fromEntries(defaultDefaultPlayerSkill.entries()))), + this.loadDefault( + ConfigurationEntryKey.defaultPlayerSkill, + JSON.stringify(Object.fromEntries(defaultDefaultPlayerSkill.entries())), + ), this.loadDefault(ConfigurationEntryKey.whitelistId, ''), - this.loadDefault(ConfigurationEntryKey.etf2lAccountRequired, true.toString()), + this.loadDefault( + ConfigurationEntryKey.etf2lAccountRequired, + true.toString(), + ), this.loadDefault(ConfigurationEntryKey.minimumTf2InGameHours, '500'), ]); } async getDefaultPlayerSkill(): Promise> { const value = await this.retrieve(ConfigurationEntryKey.defaultPlayerSkill); - return new Map(Object.entries(JSON.parse(value))) as Map; + return new Map(Object.entries(JSON.parse(value))) as Map< + Tf2ClassName, + number + >; } - async setDefaultPlayerSkill(defaultPlayerSkill: Map): Promise> { - await this.store(ConfigurationEntryKey.defaultPlayerSkill, JSON.stringify(Object.fromEntries(defaultPlayerSkill))); + async setDefaultPlayerSkill( + defaultPlayerSkill: Map, + ): Promise> { + await this.store( + ConfigurationEntryKey.defaultPlayerSkill, + JSON.stringify(Object.fromEntries(defaultPlayerSkill)), + ); return defaultPlayerSkill; } @@ -47,20 +63,36 @@ export class ConfigurationService implements OnModuleInit { } async isEtf2lAccountRequired(): Promise { - return (await this.retrieve(ConfigurationEntryKey.etf2lAccountRequired)) === 'true'; + return ( + (await this.retrieve(ConfigurationEntryKey.etf2lAccountRequired)) === + 'true' + ); } - async setEtf2lAccountRequired(etf2lAccountRequired: boolean): Promise { - await this.store(ConfigurationEntryKey.etf2lAccountRequired, etf2lAccountRequired.toString()); + async setEtf2lAccountRequired( + etf2lAccountRequired: boolean, + ): Promise { + await this.store( + ConfigurationEntryKey.etf2lAccountRequired, + etf2lAccountRequired.toString(), + ); return etf2lAccountRequired; } async getMinimumTf2InGameHours(): Promise { - return parseInt(await this.retrieve(ConfigurationEntryKey.minimumTf2InGameHours), 10); + return parseInt( + await this.retrieve(ConfigurationEntryKey.minimumTf2InGameHours), + 10, + ); } - async setMinimumTf2InGameHours(minimumTf2InGameHours: number): Promise { - await this.store(ConfigurationEntryKey.minimumTf2InGameHours, minimumTf2InGameHours.toString()); + async setMinimumTf2InGameHours( + minimumTf2InGameHours: number, + ): Promise { + await this.store( + ConfigurationEntryKey.minimumTf2InGameHours, + minimumTf2InGameHours.toString(), + ); return minimumTf2InGameHours; } @@ -69,17 +101,27 @@ export class ConfigurationService implements OnModuleInit { } async setVoiceServer(voiceServer: VoiceServer): Promise { - await this.store(ConfigurationEntryKey.voiceServer, JSON.stringify(voiceServer)); + await this.store( + ConfigurationEntryKey.voiceServer, + JSON.stringify(voiceServer), + ); return voiceServer; } private async retrieve(key: ConfigurationEntryKey): Promise { - const ret = await this.configurationEntryModel.findOne({ key }).orFail().lean().exec() + const ret = await this.configurationEntryModel + .findOne({ key }) + .orFail() + .lean() + .exec(); return ret.value; } private async store(key: ConfigurationEntryKey, value: string) { - await this.configurationEntryModel.updateOne({ key }, { value }, { upsert: true }).lean().exec(); + await this.configurationEntryModel + .updateOne({ key }, { value }, { upsert: true }) + .lean() + .exec(); } private async loadDefault(key: ConfigurationEntryKey, value: string) { @@ -89,5 +131,4 @@ export class ConfigurationService implements OnModuleInit { await this.store(key, value); } } - } diff --git a/src/console.ts b/src/console.ts index f459b70e3..186ed3c1c 100644 --- a/src/console.ts +++ b/src/console.ts @@ -2,16 +2,16 @@ import { BootstrapConsole } from 'nestjs-console'; import { AppModule } from './app.module'; const bootstrap = new BootstrapConsole({ - module: AppModule, - useDecorators: true + module: AppModule, + useDecorators: true, }); -bootstrap.init().then(async app => { - try { - await app.init(); - await bootstrap.boot(); - process.exit(0); - } catch (e) { - process.exit(1); - } +bootstrap.init().then(async (app) => { + try { + await app.init(); + await bootstrap.boot(); + process.exit(0); + } catch (e) { + process.exit(1); + } }); diff --git a/src/documents/controllers/documents.controller.spec.ts b/src/documents/controllers/documents.controller.spec.ts index bb64fe7df..51db2722b 100644 --- a/src/documents/controllers/documents.controller.spec.ts +++ b/src/documents/controllers/documents.controller.spec.ts @@ -11,9 +11,7 @@ describe('Documents Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [DocumentsController], - providers: [ - DocumentsService, - ], + providers: [DocumentsService], }).compile(); controller = module.get(DocumentsController); @@ -26,7 +24,9 @@ describe('Documents Controller', () => { describe('#getDocument', () => { beforeEach(() => { - documentsService.getDocument.mockImplementation((name, language) => Promise.resolve({ name, language, body: 'testing' })); + documentsService.getDocument.mockImplementation((name, language) => + Promise.resolve({ name, language, body: 'testing' }), + ); }); it('should query the service', async () => { @@ -35,14 +35,24 @@ describe('Documents Controller', () => { }); }); - describe('#saveDocument', () =>{ + describe('#saveDocument', () => { beforeEach(() => { - documentsService.saveDocument.mockImplementation((name, language, body) => Promise.resolve({ name, language, body })); + documentsService.saveDocument.mockImplementation((name, language, body) => + Promise.resolve({ name, language, body }), + ); }); it('should call the service', async () => { - await controller.saveDocument('test', 'en', { name: 'test', language: 'en', body: 'just testing' }); - expect(documentsService.saveDocument).toHaveBeenCalledWith('test', 'en', 'just testing'); + await controller.saveDocument('test', 'en', { + name: 'test', + language: 'en', + body: 'just testing', + }); + expect(documentsService.saveDocument).toHaveBeenCalledWith( + 'test', + 'en', + 'just testing', + ); }); }); }); diff --git a/src/documents/controllers/documents.controller.ts b/src/documents/controllers/documents.controller.ts index 442df673e..0e52d2725 100644 --- a/src/documents/controllers/documents.controller.ts +++ b/src/documents/controllers/documents.controller.ts @@ -1,16 +1,26 @@ import { Auth } from '@/auth/decorators/auth.decorator'; import { PlayerRole } from '@/players/models/player-role'; import { DocumentNotFoundFilter } from '@/shared/filters/document-not-found.filter'; -import { Body, ClassSerializerInterceptor, Controller, DefaultValuePipe, Get, Param, Put, Query, UseFilters, UseInterceptors, UsePipes, ValidationPipe } from '@nestjs/common'; +import { + Body, + ClassSerializerInterceptor, + Controller, + DefaultValuePipe, + Get, + Param, + Put, + Query, + UseFilters, + UseInterceptors, + UsePipes, + ValidationPipe, +} from '@nestjs/common'; import { Document } from '../models/document'; import { DocumentsService } from '../services/documents.service'; @Controller('documents') export class DocumentsController { - - constructor( - private documentsService: DocumentsService, - ) { } + constructor(private documentsService: DocumentsService) {} @Get(':name') @UseInterceptors(ClassSerializerInterceptor) @@ -33,5 +43,4 @@ export class DocumentsController { ) { return this.documentsService.saveDocument(name, language, document.body); } - } diff --git a/src/documents/documents.module.ts b/src/documents/documents.module.ts index 76866ba15..9de3b0ace 100644 --- a/src/documents/documents.module.ts +++ b/src/documents/documents.module.ts @@ -5,14 +5,8 @@ import { Document } from './models/document'; import { DocumentsService } from './services/documents.service'; @Module({ - imports: [ - TypegooseModule.forFeature([ Document ]), - ], - controllers: [ - DocumentsController, - ], - providers: [ - DocumentsService, - ], + imports: [TypegooseModule.forFeature([Document])], + controllers: [DocumentsController], + providers: [DocumentsService], }) export class DocumentsModule {} diff --git a/src/documents/models/document.ts b/src/documents/models/document.ts index 84d00f153..116c121ab 100644 --- a/src/documents/models/document.ts +++ b/src/documents/models/document.ts @@ -5,7 +5,6 @@ import { IsLocale, IsOptional, IsString } from 'class-validator'; @Exclude() @index({ name: 1, language: 1 }, { unique: true }) export class Document { - @IsString() @Expose() @prop({ required: true }) @@ -21,5 +20,4 @@ export class Document { @Expose() @prop() body?: string; - } diff --git a/src/documents/services/documents.service.spec.ts b/src/documents/services/documents.service.spec.ts index 6a43562f1..43243b1e0 100644 --- a/src/documents/services/documents.service.spec.ts +++ b/src/documents/services/documents.service.spec.ts @@ -11,7 +11,7 @@ describe('DocumentsService', () => { let mongod: MongoMemoryServer; let documentModel: ReturnModelType; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -20,16 +20,14 @@ describe('DocumentsService', () => { typegooseTestingModule(mongod), TypegooseModule.forFeature([Document]), ], - providers: [ - DocumentsService, - ], + providers: [DocumentsService], }).compile(); service = module.get(DocumentsService); documentModel = module.get(getModelToken(Document.name)); }); - afterEach(async () => await documentModel.deleteMany({ })); + afterEach(async () => await documentModel.deleteMany({})); it('should be defined', () => { expect(service).toBeDefined(); @@ -38,14 +36,20 @@ describe('DocumentsService', () => { describe('#onModuleInit()', () => { it('should create empty rules document', async () => { await service.onModuleInit(); - expect(await documentModel.findOne({ name: 'rules', language: 'en' })).toBeTruthy(); + expect( + await documentModel.findOne({ name: 'rules', language: 'en' }), + ).toBeTruthy(); }); }); describe('#getDocument()', () => { describe('when a given document exists', () => { beforeEach(async () => { - await documentModel.create({ name: 'test', language: 'en', body: 'just testing' }); + await documentModel.create({ + name: 'test', + language: 'en', + body: 'just testing', + }); }); it('should return the document', async () => { @@ -59,7 +63,9 @@ describe('DocumentsService', () => { describe('when the given document does not exist', () => { it('should throw an error', async () => { - await expect(service.getDocument('test')).rejects.toThrow(mongoose.Error.DocumentNotFoundError); + await expect(service.getDocument('test')).rejects.toThrow( + mongoose.Error.DocumentNotFoundError, + ); }); }); }); @@ -76,5 +82,4 @@ describe('DocumentsService', () => { expect(doc).toMatchObject(ret); }); }); - }); diff --git a/src/documents/services/documents.service.ts b/src/documents/services/documents.service.ts index bfae25a34..499886231 100644 --- a/src/documents/services/documents.service.ts +++ b/src/documents/services/documents.service.ts @@ -6,10 +6,10 @@ import { Document } from '../models/document'; @Injectable() export class DocumentsService implements OnModuleInit { - constructor( - @InjectModel(Document) private documentModel: ReturnModelType, - ) { } + @InjectModel(Document) + private documentModel: ReturnModelType, + ) {} async onModuleInit() { // ensure we have the rules document created @@ -25,17 +25,27 @@ export class DocumentsService implements OnModuleInit { } async getDocument(name: string, language = 'en'): Promise { - const pojo = await this.documentModel.findOne({ name, language }).orFail().lean().exec(); + const pojo = await this.documentModel + .findOne({ name, language }) + .orFail() + .lean() + .exec(); return plainToClass(Document, pojo); } - async saveDocument(name: string, language: string, body: string): Promise { - const pojo = await this.documentModel.findOneAndUpdate( - { name, language }, - { body }, - { upsert: true, new: true }, - ).lean().exec(); + async saveDocument( + name: string, + language: string, + body: string, + ): Promise { + const pojo = await this.documentModel + .findOneAndUpdate( + { name, language }, + { body }, + { upsert: true, new: true }, + ) + .lean() + .exec(); return plainToClass(Document, pojo); } - } diff --git a/src/environment-validation-schema.ts b/src/environment-validation-schema.ts index 3a7a8e782..1fd171062 100644 --- a/src/environment-validation-schema.ts +++ b/src/environment-validation-schema.ts @@ -15,9 +15,7 @@ export default object({ STEAM_API_KEY: string().required(), KEY_STORE_PASSPHARE: string().required(), SUPER_USER: string().required(), - QUEUE_CONFIG: string() - .valid('test', '6v6', '9v9') - .default('6v6'), + QUEUE_CONFIG: string().valid('test', '6v6', '9v9').default('6v6'), LOG_RELAY_ADDRESS: string().required(), LOG_RELAY_PORT: number().required(), DISCORD_BOT_TOKEN: any().optional(), diff --git a/src/environment/environment.module.ts b/src/environment/environment.module.ts index d89ad48be..09f0b406c 100644 --- a/src/environment/environment.module.ts +++ b/src/environment/environment.module.ts @@ -3,11 +3,7 @@ import { Environment } from './environment'; @Global() @Module({ - providers: [ - Environment, - ], - exports: [ - Environment, - ], + providers: [Environment], + exports: [Environment], }) -export class EnvironmentModule { } +export class EnvironmentModule {} diff --git a/src/environment/environment.spec.ts b/src/environment/environment.spec.ts index 5dd555ab4..2d8781f60 100644 --- a/src/environment/environment.spec.ts +++ b/src/environment/environment.spec.ts @@ -10,16 +10,13 @@ describe('Environment', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ - Environment, - ConfigService, - ], + providers: [Environment, ConfigService], }).compile(); environment = module.get(Environment); configService = module.get(ConfigService); - configService.get = varName => varName; + configService.get = (varName) => varName; }); it('should be defined', () => { @@ -48,7 +45,7 @@ describe('Environment', () => { 'DISCORD_ADMIN_NOTIFICATIONS_CHANNEL', 'TWITCH_CLIENT_ID', 'TWITCH_CLIENT_SECRET', - ].forEach(varName => { + ].forEach((varName) => { const getterName = varName .toLowerCase() .replace(/_(.)/g, (_, p1) => p1.toString().toUpperCase()) diff --git a/src/environment/environment.ts b/src/environment/environment.ts index d15a15799..7ec01a730 100644 --- a/src/environment/environment.ts +++ b/src/environment/environment.ts @@ -3,10 +3,7 @@ import { ConfigService } from '@nestjs/config'; @Injectable() export class Environment { - - constructor( - private configService: ConfigService, - ) { } + constructor(private configService: ConfigService) {} get apiUrl() { return this.configService.get('API_URL'); @@ -77,15 +74,21 @@ export class Environment { } get discordQueueNotificationsChannel() { - return this.configService.get('DISCORD_QUEUE_NOTIFICATIONS_CHANNEL'); + return this.configService.get( + 'DISCORD_QUEUE_NOTIFICATIONS_CHANNEL', + ); } get discordQueueNotificationsMentionRole() { - return this.configService.get('DISCORD_QUEUE_NOTIFICATIONS_MENTION_ROLE'); + return this.configService.get( + 'DISCORD_QUEUE_NOTIFICATIONS_MENTION_ROLE', + ); } get discordAdminNotificationsChannel() { - return this.configService.get('DISCORD_ADMIN_NOTIFICATIONS_CHANNEL'); + return this.configService.get( + 'DISCORD_ADMIN_NOTIFICATIONS_CHANNEL', + ); } get twitchClientId() { @@ -95,5 +98,4 @@ export class Environment { get twitchClientSecret() { return this.configService.get('TWITCH_CLIENT_SECRET'); } - } diff --git a/src/events/events.module.ts b/src/events/events.module.ts index 682ccfad3..ad3e36e69 100644 --- a/src/events/events.module.ts +++ b/src/events/events.module.ts @@ -3,11 +3,7 @@ import { Events } from './events'; @Global() @Module({ - providers: [ - Events, - ], - exports: [ - Events, - ], + providers: [Events], + exports: [Events], }) -export class EventsModule { } +export class EventsModule {} diff --git a/src/events/events.ts b/src/events/events.ts index 929a3169c..10586c353 100644 --- a/src/events/events.ts +++ b/src/events/events.ts @@ -23,39 +23,57 @@ interface PlayerSkillChangedEventProps { */ @Injectable() export class Events { - private logger = new Logger(Events.name); readonly playerRegisters = new Subject<{ player: Player }>(); - readonly playerUpdates = new Subject<{ oldPlayer: Player, newPlayer: Player, adminId?: string }>(); + readonly playerUpdates = new Subject<{ + oldPlayer: Player; + newPlayer: Player; + adminId?: string; + }>(); readonly playerDisconnects = new Subject<{ playerId: string }>(); readonly playerBanAdded = new Subject<{ ban: PlayerBan }>(); - readonly playerBanRevoked = new Subject<{ ban: PlayerBan, adminId?: string }>(); + readonly playerBanRevoked = new Subject<{ + ban: PlayerBan; + adminId?: string; + }>(); readonly playerSkillChanged = new Subject(); readonly playerJoinsQueue = new Subject<{ playerId: string }>(); - readonly playerLeavesQueue = new Subject<{ playerId: string, reason: 'manual' | 'kicked' }>(); + readonly playerLeavesQueue = new Subject<{ + playerId: string; + reason: 'manual' | 'kicked'; + }>(); readonly queueSlotsChange = new Subject<{ slots: QueueSlot[] }>(); readonly queueStateChange = new Subject<{ state: QueueState }>(); - readonly queueFriendshipsChange = new Subject<{ friendships: Friendship[] }>(); + readonly queueFriendshipsChange = new Subject<{ + friendships: Friendship[]; + }>(); readonly mapVotesChange = new Subject<{ results: MapVoteResult[] }>(); readonly mapPoolChange = new Subject<{ maps: Map[] }>(); readonly gameCreated = new Subject<{ game: Game }>(); - readonly gameChanges = new Subject<{ game: Game, adminId?: string }>(); + readonly gameChanges = new Subject<{ game: Game; adminId?: string }>(); readonly substituteRequestsChange = new Subject(); - readonly gameServerAdded = new Subject<{ gameServer: GameServer, adminId?: string }>(); - readonly gameServerRemoved = new Subject<{ gameServer: GameServer, adminId?: string }>(); + readonly gameServerAdded = new Subject<{ + gameServer: GameServer; + adminId?: string; + }>(); + readonly gameServerRemoved = new Subject<{ + gameServer: GameServer; + adminId?: string; + }>(); constructor() { for (const eventName in this) { const prop = this[eventName] as any; if (prop instanceof Subject) { - prop.subscribe((...args) => this.logger.debug(`${eventName}: ${JSON.stringify(args)}`)); + prop.subscribe((...args) => + this.logger.debug(`${eventName}: ${JSON.stringify(args)}`), + ); } } } - } diff --git a/src/game-servers/controllers/game-server-diagnostics.controller.spec.ts b/src/game-servers/controllers/game-server-diagnostics.controller.spec.ts index d181471cd..c8c91365e 100644 --- a/src/game-servers/controllers/game-server-diagnostics.controller.spec.ts +++ b/src/game-servers/controllers/game-server-diagnostics.controller.spec.ts @@ -4,7 +4,7 @@ import { GameServerDiagnosticsController } from './game-server-diagnostics.contr jest.mock('../services/game-server-diagnostics.service', () => ({ GameServerDiagnosticsService: jest.fn().mockImplementation(() => ({ - getDiagnosticRunById: id => Promise.resolve({ id }), + getDiagnosticRunById: (id) => Promise.resolve({ id }), })), })); @@ -14,12 +14,12 @@ describe('GameServerDiagnosticsController', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [GameServerDiagnosticsController], - providers: [ - GameServerDiagnosticsService, - ], + providers: [GameServerDiagnosticsService], }).compile(); - controller = module.get(GameServerDiagnosticsController); + controller = module.get( + GameServerDiagnosticsController, + ); }); it('should be defined', () => { @@ -31,5 +31,5 @@ describe('GameServerDiagnosticsController', () => { const ret = await controller.getDiagnosticRun('FAKE_ID'); expect(ret).toEqual({ id: 'FAKE_ID' }); }); - }) + }); }); diff --git a/src/game-servers/controllers/game-server-diagnostics.controller.ts b/src/game-servers/controllers/game-server-diagnostics.controller.ts index b2f3a2d93..34f157bc9 100644 --- a/src/game-servers/controllers/game-server-diagnostics.controller.ts +++ b/src/game-servers/controllers/game-server-diagnostics.controller.ts @@ -2,23 +2,30 @@ import { Auth } from '@/auth/decorators/auth.decorator'; import { PlayerRole } from '@/players/models/player-role'; import { DocumentNotFoundFilter } from '@/shared/filters/document-not-found.filter'; import { ObjectIdValidationPipe } from '@/shared/pipes/object-id-validation.pipe'; -import { ClassSerializerInterceptor, Controller, Get, Param, UseFilters, UseInterceptors } from '@nestjs/common'; +import { + ClassSerializerInterceptor, + Controller, + Get, + Param, + UseFilters, + UseInterceptors, +} from '@nestjs/common'; import { GameServerDiagnosticRun } from '../models/game-server-diagnostic-run'; import { GameServerDiagnosticsService } from '../services/game-server-diagnostics.service'; @Controller('game-server-diagnostics') export class GameServerDiagnosticsController { - constructor( private gameServerDiagnosticsService: GameServerDiagnosticsService, - ) { } + ) {} @Get(':id') @Auth(PlayerRole.superUser) @UseInterceptors(ClassSerializerInterceptor) @UseFilters(DocumentNotFoundFilter) - async getDiagnosticRun(@Param('id', ObjectIdValidationPipe) id: string): Promise { + async getDiagnosticRun( + @Param('id', ObjectIdValidationPipe) id: string, + ): Promise { return this.gameServerDiagnosticsService.getDiagnosticRunById(id); } - } diff --git a/src/game-servers/controllers/game-servers.controller.spec.ts b/src/game-servers/controllers/game-servers.controller.spec.ts index aaece57b0..cc80fcd0e 100644 --- a/src/game-servers/controllers/game-servers.controller.spec.ts +++ b/src/game-servers/controllers/game-servers.controller.spec.ts @@ -49,13 +49,13 @@ describe('GameServers Controller', () => { describe('#getAllGameServers()', () => { beforeEach(() => { - gameServersService.getAllGameServers.mockResolvedValue([ mockGameServer ]); + gameServersService.getAllGameServers.mockResolvedValue([mockGameServer]); }); it('should call service', async () => { const ret = await controller.getAllGameServers(); expect(gameServersService.getAllGameServers).toHaveBeenCalled(); - expect(ret).toEqual([ mockGameServer ]); + expect(ret).toEqual([mockGameServer]); }); }); @@ -77,9 +77,19 @@ describe('GameServers Controller', () => { }); it('should add the game server', async () => { - const dto = { name: 'FAKE_SERVER_NAME', port: '27015', address: 'FAKE_SERVER_ADDRESS', rconPassword: 'FAKE_RCON_PASSWORD' }; - const ret = await controller.addGameServer(dto, { id: 'FAKE_ADMIN_ID' } as Player); - expect(gameServersService.addGameServer).toHaveBeenCalledWith(dto, 'FAKE_ADMIN_ID'); + const dto = { + name: 'FAKE_SERVER_NAME', + port: '27015', + address: 'FAKE_SERVER_ADDRESS', + rconPassword: 'FAKE_RCON_PASSWORD', + }; + const ret = await controller.addGameServer(dto, { + id: 'FAKE_ADMIN_ID', + } as Player); + expect(gameServersService.addGameServer).toHaveBeenCalledWith( + dto, + 'FAKE_ADMIN_ID', + ); expect(ret).toEqual(mockGameServer); }); }); @@ -90,25 +100,34 @@ describe('GameServers Controller', () => { }); it('should call the service', async () => { - await controller.removeGameServer('FAKE_ID', { id: 'FAKE_ADMIN_ID' } as Player); - expect(gameServersService.removeGameServer).toHaveBeenCalledWith('FAKE_ID', 'FAKE_ADMIN_ID'); + await controller.removeGameServer('FAKE_ID', { + id: 'FAKE_ADMIN_ID', + } as Player); + expect(gameServersService.removeGameServer).toHaveBeenCalledWith( + 'FAKE_ID', + 'FAKE_ADMIN_ID', + ); }); }); describe('#runDiagnostics()', () => { beforeEach(() => { - gameServerDiagnosticsService.runDiagnostics.mockResolvedValue('FAKE_DIAGNOSTICS_ID'); + gameServerDiagnosticsService.runDiagnostics.mockResolvedValue( + 'FAKE_DIAGNOSTICS_ID', + ); }); it('should call the service', async () => { const ret = await controller.runDiagnostics('FAKE_GAME_SERVER_ID'); - expect(gameServerDiagnosticsService.runDiagnostics).toHaveBeenCalledWith('FAKE_GAME_SERVER_ID'); + expect(gameServerDiagnosticsService.runDiagnostics).toHaveBeenCalledWith( + 'FAKE_GAME_SERVER_ID', + ); expect(ret).toEqual({ diagnosticRunId: 'FAKE_DIAGNOSTICS_ID', tracking: { url: 'FAKE_API_URL/game-server-diagnostics/FAKE_DIAGNOSTICS_ID', }, - }) + }); }); }); }); diff --git a/src/game-servers/controllers/game-servers.controller.ts b/src/game-servers/controllers/game-servers.controller.ts index 43b261200..aef34f7c3 100644 --- a/src/game-servers/controllers/game-servers.controller.ts +++ b/src/game-servers/controllers/game-servers.controller.ts @@ -1,5 +1,17 @@ -import { Controller, Get, Param, Post, Body, UsePipes, ValidationPipe, Delete, UseInterceptors, - ClassSerializerInterceptor, UseFilters, HttpCode } from '@nestjs/common'; +import { + Controller, + Get, + Param, + Post, + Body, + UsePipes, + ValidationPipe, + Delete, + UseInterceptors, + ClassSerializerInterceptor, + UseFilters, + HttpCode, +} from '@nestjs/common'; import { GameServersService } from '../services/game-servers.service'; import { ObjectIdValidationPipe } from '@/shared/pipes/object-id-validation.pipe'; import { Auth } from '@/auth/decorators/auth.decorator'; @@ -13,12 +25,11 @@ import { PlayerRole } from '@/players/models/player-role'; @Controller('game-servers') export class GameServersController { - constructor( private gameServersService: GameServersService, private gameServerDiagnosticsService: GameServerDiagnosticsService, private environment: Environment, - ) { } + ) {} @Get() @UseInterceptors(ClassSerializerInterceptor) @@ -29,7 +40,9 @@ export class GameServersController { @Get(':id') @UseInterceptors(ClassSerializerInterceptor) @UseFilters(DocumentNotFoundFilter) - async getGameServer(@Param('id', ObjectIdValidationPipe) gameServerId: string) { + async getGameServer( + @Param('id', ObjectIdValidationPipe) gameServerId: string, + ) { return await this.gameServersService.getById(gameServerId); } @@ -37,21 +50,31 @@ export class GameServersController { @Auth(PlayerRole.superUser) @UsePipes(ValidationPipe) @UseInterceptors(ClassSerializerInterceptor) - async addGameServer(@Body() gameServer: AddGameServer, @User() admin: Player) { + async addGameServer( + @Body() gameServer: AddGameServer, + @User() admin: Player, + ) { return this.gameServersService.addGameServer(gameServer, admin.id); } @Delete(':id') @Auth(PlayerRole.superUser) - async removeGameServer(@Param('id', ObjectIdValidationPipe) gameServerId: string, @User() admin: Player) { + async removeGameServer( + @Param('id', ObjectIdValidationPipe) gameServerId: string, + @User() admin: Player, + ) { await this.gameServersService.removeGameServer(gameServerId, admin.id); } @Post(':id/diagnostics') @Auth(PlayerRole.superUser) @HttpCode(202) - async runDiagnostics(@Param('id', ObjectIdValidationPipe) gameServerId: string) { - const id = await this.gameServerDiagnosticsService.runDiagnostics(gameServerId); + async runDiagnostics( + @Param('id', ObjectIdValidationPipe) gameServerId: string, + ) { + const id = await this.gameServerDiagnosticsService.runDiagnostics( + gameServerId, + ); return { diagnosticRunId: id, tracking: { @@ -59,5 +82,4 @@ export class GameServersController { }, }; } - } diff --git a/src/game-servers/diagnostic-checks/log-forwarding.ts b/src/game-servers/diagnostic-checks/log-forwarding.ts index e06e79924..53b5d8b1d 100644 --- a/src/game-servers/diagnostic-checks/log-forwarding.ts +++ b/src/game-servers/diagnostic-checks/log-forwarding.ts @@ -7,18 +7,17 @@ import { LogMessage, LogReceiver } from 'srcds-log-receiver'; import { DiagnosticCheckResult } from '../interfaces/diagnostic-check-result'; import { DiagnosticCheckRunner } from '../interfaces/diagnostic-check-runner'; -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); @Injectable({ scope: Scope.TRANSIENT }) export class LogForwarding implements DiagnosticCheckRunner { - name = 'log forwarding'; critical = true; constructor( private logReceiver: LogReceiver, private environment: Environment, - ) { } + ) {} async run({ effects }): Promise { const rcon: Rcon = effects.get('rcon connection'); @@ -32,11 +31,17 @@ export class LogForwarding implements DiagnosticCheckRunner { return new Promise((resolve) => { const secret = generate({ length: 32, numbers: true }); - const timer = setTimeout(() => resolve({ - success: false, - reportedErrors: [`No logs received over the UDP protocol on port ${this.logReceiver.opts.port}. Check your firewall settings.`], - reportedWarnings: [], - }), 5000); + const timer = setTimeout( + () => + resolve({ + success: false, + reportedErrors: [ + `No logs received over the UDP protocol on port ${this.logReceiver.opts.port}. Check your firewall settings.`, + ], + reportedWarnings: [], + }), + 5000, + ); this.logReceiver.on('data', (data: LogMessage) => { if (new RegExp(`Console.+say\\s"${secret}"$`).test(data.message)) { @@ -44,18 +49,17 @@ export class LogForwarding implements DiagnosticCheckRunner { resolve({ success: true, reportedErrors: [], - reportedWarnings:[], + reportedWarnings: [], }); } }); const logAddress = `${this.environment.logRelayAddress}:${this.environment.logRelayPort}`; - rcon.send(logAddressAdd(logAddress)) + rcon + .send(logAddressAdd(logAddress)) .then(() => sleep(1000)) .then(() => rcon.send(`say ${secret}`)) .then(() => rcon.send(logAddressDel(logAddress))); }); - } - } diff --git a/src/game-servers/diagnostic-checks/rcon-connection.ts b/src/game-servers/diagnostic-checks/rcon-connection.ts index 55b9552dc..b2625c976 100644 --- a/src/game-servers/diagnostic-checks/rcon-connection.ts +++ b/src/game-servers/diagnostic-checks/rcon-connection.ts @@ -5,24 +5,24 @@ import { DiagnosticCheckRunner } from '../interfaces/diagnostic-check-runner'; @Injectable({ scope: Scope.TRANSIENT }) export class RconConnection implements DiagnosticCheckRunner { - name = 'rcon connection'; critical = true; async run({ gameServer }): Promise { - const createRcon = () => new Promise((resolve, reject) => { - const rcon = new Rcon({ - host: gameServer.address, - port: parseInt(gameServer.port, 10), - password: gameServer.rconPassword, - timeout: 30000, - }); + const createRcon = () => + new Promise((resolve, reject) => { + const rcon = new Rcon({ + host: gameServer.address, + port: parseInt(gameServer.port, 10), + password: gameServer.rconPassword, + timeout: 30000, + }); - rcon.on('authenticated', () => resolve(rcon)); - rcon.on('error', () => reject()); + rcon.on('authenticated', () => resolve(rcon)); + rcon.on('error', () => reject()); - return rcon.connect().catch(reject); - }); + return rcon.connect().catch(reject); + }); try { const rcon = await createRcon(); @@ -40,5 +40,4 @@ export class RconConnection implements DiagnosticCheckRunner { }; } } - } diff --git a/src/game-servers/diagnostic-checks/server-discovery.ts b/src/game-servers/diagnostic-checks/server-discovery.ts index 528665217..f3a811ae4 100644 --- a/src/game-servers/diagnostic-checks/server-discovery.ts +++ b/src/game-servers/diagnostic-checks/server-discovery.ts @@ -5,13 +5,16 @@ import { DiagnosticCheckRunner } from '../interfaces/diagnostic-check-runner'; @Injectable({ scope: Scope.TRANSIENT }) export class ServerDiscovery implements DiagnosticCheckRunner { - name = 'server discovery'; critical = false; async run({ gameServer }): Promise { try { - const result = await query({ type: 'tf2', host: gameServer.address, port: parseInt(gameServer.port, 10) }); + const result = await query({ + type: 'tf2', + host: gameServer.address, + port: parseInt(gameServer.port, 10), + }); const reportedWarnings = []; if (!result.password) { @@ -26,10 +29,9 @@ export class ServerDiscovery implements DiagnosticCheckRunner { } catch (error) { return { success: false, - reportedErrors: [ error.toString() ], + reportedErrors: [error.toString()], reportedWarnings: [], }; } } - } diff --git a/src/game-servers/dto/add-game-server.ts b/src/game-servers/dto/add-game-server.ts index c763f6a1e..ea4ba4b4b 100644 --- a/src/game-servers/dto/add-game-server.ts +++ b/src/game-servers/dto/add-game-server.ts @@ -1,7 +1,6 @@ import { IsPort, IsString } from 'class-validator'; export class AddGameServer { - @IsString() name: string; @@ -13,5 +12,4 @@ export class AddGameServer { @IsString() rconPassword: string; - } diff --git a/src/game-servers/game-servers.module.ts b/src/game-servers/game-servers.module.ts index 5d037a81a..3a34a730d 100644 --- a/src/game-servers/game-servers.module.ts +++ b/src/game-servers/game-servers.module.ts @@ -13,7 +13,7 @@ import { LogReceiverModule } from '@/log-receiver/log-receiver.module'; @Module({ imports: [ - TypegooseModule.forFeature([ GameServer, GameServerDiagnosticRun ]), + TypegooseModule.forFeature([GameServer, GameServerDiagnosticRun]), LogReceiverModule, ], providers: [ @@ -23,12 +23,7 @@ import { LogReceiverModule } from '@/log-receiver/log-receiver.module'; RconConnection, LogForwarding, ], - exports: [ - GameServersService, - ], - controllers: [ - GameServersController, - GameServerDiagnosticsController, - ], + exports: [GameServersService], + controllers: [GameServersController, GameServerDiagnosticsController], }) -export class GameServersModule { } +export class GameServersModule {} diff --git a/src/game-servers/interfaces/diagnostic-check-runner.ts b/src/game-servers/interfaces/diagnostic-check-runner.ts index b93ad25cb..3d183412d 100644 --- a/src/game-servers/interfaces/diagnostic-check-runner.ts +++ b/src/game-servers/interfaces/diagnostic-check-runner.ts @@ -2,13 +2,11 @@ import { GameServer } from '../models/game-server'; import { DiagnosticCheckResult } from './diagnostic-check-result'; export interface DiagnosticCheckRunner { - name: string; critical: boolean; run(params: { - gameServer: GameServer, - effects: Map, + gameServer: GameServer; + effects: Map; }): Promise; - } diff --git a/src/game-servers/models/diagnostic-check.ts b/src/game-servers/models/diagnostic-check.ts index 99c45cf3b..94dbba92a 100644 --- a/src/game-servers/models/diagnostic-check.ts +++ b/src/game-servers/models/diagnostic-check.ts @@ -2,7 +2,6 @@ import { prop } from '@typegoose/typegoose'; import { DiagnosticCheckStatus } from './diagnostic-check-status'; export class DiagnosticCheck { - @prop({ required: true }) name: string; @@ -17,5 +16,4 @@ export class DiagnosticCheck { @prop({ required: true }) critical: boolean; - } diff --git a/src/game-servers/models/game-server-diagnostic-run.ts b/src/game-servers/models/game-server-diagnostic-run.ts index 0e94b9299..9f16dce1f 100644 --- a/src/game-servers/models/game-server-diagnostic-run.ts +++ b/src/game-servers/models/game-server-diagnostic-run.ts @@ -6,7 +6,6 @@ import { DiagnosticRunStatus } from './diagnostic-run-status'; import { GameServer } from './game-server'; export class GameServerDiagnosticRun extends MongooseDocument { - @Expose() @Transform(({ value, obj }) => value ?? obj._id?.toString()) id?: string; @@ -14,7 +13,7 @@ export class GameServerDiagnosticRun extends MongooseDocument { @prop({ default: () => new Date() }) launchedAt?: Date; - @Transform(({ value }) => isRefType(value) ? value.toString() : value) + @Transform(({ value }) => (isRefType(value) ? value.toString() : value)) @prop({ required: true, ref: () => GameServer }) gameServer: Ref; @@ -26,7 +25,6 @@ export class GameServerDiagnosticRun extends MongooseDocument { status?: DiagnosticRunStatus; getCheckByName(name: string) { - return this.checks.find(check => check.name === name); + return this.checks.find((check) => check.name === name); } - } diff --git a/src/game-servers/models/game-server.ts b/src/game-servers/models/game-server.ts index 931aea003..e2e1f98f7 100644 --- a/src/game-servers/models/game-server.ts +++ b/src/game-servers/models/game-server.ts @@ -4,7 +4,6 @@ import { MongooseDocument } from '@/utils/mongoose-document'; import { Exclude, Expose, Transform } from 'class-transformer'; export class GameServer extends MongooseDocument { - @Expose() @Transform(({ value, obj }) => value ?? obj._id?.toString()) id?: string; @@ -37,12 +36,11 @@ export class GameServer extends MongooseDocument { @prop() mumbleChannelName?: string; - @Transform(({ value }) => isRefType(value) ? value.toString() : value) + @Transform(({ value }) => (isRefType(value) ? value.toString() : value)) @prop({ ref: () => Game }) game?: Ref; // currently running game @Exclude({ toPlainOnly: true }) @prop({ default: false }) deleted?: boolean; - } diff --git a/src/game-servers/services/game-server-diagnostics.service.spec.ts b/src/game-servers/services/game-server-diagnostics.service.spec.ts index e36e171ca..08c9fad99 100644 --- a/src/game-servers/services/game-server-diagnostics.service.spec.ts +++ b/src/game-servers/services/game-server-diagnostics.service.spec.ts @@ -19,9 +19,11 @@ jest.mock('../diagnostic-checks/server-discovery'); describe('GameServerDiagnosticsService', () => { let service: GameServerDiagnosticsService; let mongod: MongoMemoryServer; - let gameServerDiagnosticRunModel: ReturnModelType; + let gameServerDiagnosticRunModel: ReturnModelType< + typeof GameServerDiagnosticRun + >; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -39,12 +41,16 @@ describe('GameServerDiagnosticsService', () => { ], }).compile(); - service = module.get(GameServerDiagnosticsService); - gameServerDiagnosticRunModel = module.get(getModelToken(GameServerDiagnosticRun.name)); + service = module.get( + GameServerDiagnosticsService, + ); + gameServerDiagnosticRunModel = module.get( + getModelToken(GameServerDiagnosticRun.name), + ); }); afterEach(async () => { - await gameServerDiagnosticRunModel.deleteMany({ }); + await gameServerDiagnosticRunModel.deleteMany({}); }); it('should be defined', () => { @@ -56,7 +62,12 @@ describe('GameServerDiagnosticsService', () => { let id: string; beforeEach(async () => { - id = (await gameServerDiagnosticRunModel.create({ gameServer: new mongoose.Types.ObjectId().toString(), checks: [] })).id; + id = ( + await gameServerDiagnosticRunModel.create({ + gameServer: new mongoose.Types.ObjectId().toString(), + checks: [], + }) + ).id; }); it('should return the given run', async () => { @@ -67,8 +78,11 @@ describe('GameServerDiagnosticsService', () => { describe('when the given run does not exist', () => { it('should throw an error', async () => { - await expect(() => service.getDiagnosticRunById(new mongoose.Types.ObjectId().toString())).rejects - .toThrow(mongoose.Error.DocumentNotFoundError); + await expect(() => + service.getDiagnosticRunById( + new mongoose.Types.ObjectId().toString(), + ), + ).rejects.toThrow(mongoose.Error.DocumentNotFoundError); }); }); }); diff --git a/src/game-servers/services/game-server-diagnostics.service.ts b/src/game-servers/services/game-server-diagnostics.service.ts index 326ba7af9..0f9c6a1b1 100644 --- a/src/game-servers/services/game-server-diagnostics.service.ts +++ b/src/game-servers/services/game-server-diagnostics.service.ts @@ -16,34 +16,59 @@ import { GameServersService } from './game-servers.service'; @Injectable() export class GameServerDiagnosticsService { - private logger = new Logger(GameServerDiagnosticsService.name); constructor( - @InjectModel(GameServerDiagnosticRun) private gameServerDiagnosticRunModel: ReturnModelType, + @InjectModel(GameServerDiagnosticRun) + private gameServerDiagnosticRunModel: ReturnModelType< + typeof GameServerDiagnosticRun + >, private gameServersService: GameServersService, private moduleRef: ModuleRef, - ) { } + ) {} async getDiagnosticRunById(id: string): Promise { - return plainToClass(GameServerDiagnosticRun, await this.gameServerDiagnosticRunModel.findById(id).orFail().lean().exec()); + return plainToClass( + GameServerDiagnosticRun, + await this.gameServerDiagnosticRunModel + .findById(id) + .orFail() + .lean() + .exec(), + ); } async runDiagnostics(gameServerId: string): Promise { await this.gameServersService.getById(gameServerId); const runners = await this.collectAllRunners(); - const checks = runners.map(runner => ({ name: runner.name, critical: runner.critical })); + const checks = runners.map((runner) => ({ + name: runner.name, + critical: runner.critical, + })); const { id } = await this.gameServerDiagnosticRunModel.create({ gameServer: gameServerId, checks, }); - const run$ = this.executeAllRunners(await this.getDiagnosticRunById(id), runners); - run$.pipe( - tap(run => this.logger.debug(JSON.stringify(run, null, 2))), - concatMap(run => from(this.gameServerDiagnosticRunModel.findOneAndUpdate({ _id: run.id }, run).orFail().lean().exec())), - ).subscribe(); + const run$ = this.executeAllRunners( + await this.getDiagnosticRunById(id), + runners, + ); + run$ + .pipe( + tap((run) => this.logger.debug(JSON.stringify(run, null, 2))), + concatMap((run) => + from( + this.gameServerDiagnosticRunModel + .findOneAndUpdate({ _id: run.id }, run) + .orFail() + .lean() + .exec(), + ), + ), + ) + .subscribe(); return id; } @@ -56,14 +81,19 @@ export class GameServerDiagnosticsService { ]); } - private executeAllRunners(diagnosticRun: GameServerDiagnosticRun, runners: DiagnosticCheckRunner[]): Observable { - return new Observable(subscriber => { + private executeAllRunners( + diagnosticRun: GameServerDiagnosticRun, + runners: DiagnosticCheckRunner[], + ): Observable { + return new Observable((subscriber) => { let shouldStop = false; const fn = async () => { - const gameServer = isRefType(diagnosticRun.gameServer) ? - await this.gameServersService.getById(diagnosticRun.gameServer.toString()) : - diagnosticRun.gameServer; + const gameServer = isRefType(diagnosticRun.gameServer) + ? await this.gameServersService.getById( + diagnosticRun.gameServer.toString(), + ) + : diagnosticRun.gameServer; this.logger.log(`Starting diagnostics of ${gameServer.name}...`); @@ -87,7 +117,9 @@ export class GameServerDiagnosticsService { const result = await runner.run({ gameServer, effects }); check.reportedErrors = result.reportedErrors; check.reportedWarnings = result.reportedWarnings; - check.status = result.success ? DiagnosticCheckStatus.completed : DiagnosticCheckStatus.failed; + check.status = result.success + ? DiagnosticCheckStatus.completed + : DiagnosticCheckStatus.failed; subscriber.next(run); if (result.effects) { @@ -96,17 +128,21 @@ export class GameServerDiagnosticsService { } run = classToClass(run); - run.status = run.checks.every(check => check.status === DiagnosticCheckStatus.completed) ? - DiagnosticRunStatus.completed : DiagnosticRunStatus.failed; + run.status = run.checks.every( + (check) => check.status === DiagnosticCheckStatus.completed, + ) + ? DiagnosticRunStatus.completed + : DiagnosticRunStatus.failed; subscriber.next(run); - this.logger.log(`Diagnostics of ${gameServer.name} done. Status: ${run.status}`); + this.logger.log( + `Diagnostics of ${gameServer.name} done. Status: ${run.status}`, + ); subscriber.complete(); }; fn(); - return () => shouldStop = true; + return () => (shouldStop = true); }); } - } diff --git a/src/game-servers/services/game-servers.service.spec.ts b/src/game-servers/services/game-servers.service.spec.ts index b5586ca06..cea4d14b9 100644 --- a/src/game-servers/services/game-servers.service.spec.ts +++ b/src/game-servers/services/game-servers.service.spec.ts @@ -20,7 +20,7 @@ describe('GameServersService', () => { let testGameServer: DocumentType; let events: Events; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -29,10 +29,7 @@ describe('GameServersService', () => { typegooseTestingModule(mongod), TypegooseModule.forFeature([GameServer, Game]), ], - providers: [ - GameServersService, - Events, - ], + providers: [GameServersService, Events], }).compile(); service = module.get(GameServersService); @@ -47,13 +44,13 @@ describe('GameServersService', () => { address: 'localhost', port: '27015', rconPassword: '123456', - resolvedIpAddresses: [ '127.0.0.1' ], + resolvedIpAddresses: ['127.0.0.1'], }); }); afterEach(async () => { - await gameServerModel.deleteMany({ }); - await gameModel.deleteMany({ }); + await gameServerModel.deleteMany({}); + await gameModel.deleteMany({}); }); it('should be defined', () => { @@ -165,21 +162,25 @@ describe('GameServersService', () => { }); }); - it('should emit the gameServerAdded event', async () => new Promise(resolve => { - events.gameServerAdded.subscribe(({ gameServer, adminId }) => { - expect(adminId).toEqual('FAKE_ADMIN_ID'); - expect(gameServer.name).toEqual('fake game server'); - resolve(); - }); + it('should emit the gameServerAdded event', async () => + new Promise((resolve) => { + events.gameServerAdded.subscribe(({ gameServer, adminId }) => { + expect(adminId).toEqual('FAKE_ADMIN_ID'); + expect(gameServer.name).toEqual('fake game server'); + resolve(); + }); - service.addGameServer({ - name: 'fake game server', - address: '127.0.0.1', - port: '27017', - rconPassword: 'test rcon password', - mumbleChannelName: '', - }, 'FAKE_ADMIN_ID'); - })) + service.addGameServer( + { + name: 'fake game server', + address: '127.0.0.1', + port: '27017', + rconPassword: 'test rcon password', + mumbleChannelName: '', + }, + 'FAKE_ADMIN_ID', + ); + })); }); describe('#removeGameServer()', () => { @@ -189,15 +190,16 @@ describe('GameServersService', () => { expect((await gameServerModel.findById(ret.id)).deleted).toBe(true); }); - it('should emit the gameServerRemoved event', async () => new Promise(resolve => { - events.gameServerRemoved.subscribe(({ gameServer, adminId }) => { - expect(gameServer.id).toEqual(testGameServer.id); - expect(adminId).toEqual('FAKE_ADMIN_ID'); - resolve(); - }) + it('should emit the gameServerRemoved event', async () => + new Promise((resolve) => { + events.gameServerRemoved.subscribe(({ gameServer, adminId }) => { + expect(gameServer.id).toEqual(testGameServer.id); + expect(adminId).toEqual('FAKE_ADMIN_ID'); + resolve(); + }); - service.removeGameServer(testGameServer.id, 'FAKE_ADMIN_ID'); - })); + service.removeGameServer(testGameServer.id, 'FAKE_ADMIN_ID'); + })); }); describe('#findFreeGameServer()', () => { @@ -209,7 +211,9 @@ describe('GameServersService', () => { }); it('should throw an error', async () => { - await expect(service.findFreeGameServer()).rejects.toThrow(mongoose.Error.DocumentNotFoundError); + await expect(service.findFreeGameServer()).rejects.toThrow( + mongoose.Error.DocumentNotFoundError, + ); }); }); @@ -220,7 +224,9 @@ describe('GameServersService', () => { }); it('should throw an error', async () => { - await expect(service.findFreeGameServer()).rejects.toThrow(mongoose.Error.DocumentNotFoundError); + await expect(service.findFreeGameServer()).rejects.toThrow( + mongoose.Error.DocumentNotFoundError, + ); }); }); @@ -231,7 +237,9 @@ describe('GameServersService', () => { }); it('should throw an error', async () => { - await expect(service.findFreeGameServer()).rejects.toThrow(mongoose.Error.DocumentNotFoundError); + await expect(service.findFreeGameServer()).rejects.toThrow( + mongoose.Error.DocumentNotFoundError, + ); }); }); @@ -242,7 +250,9 @@ describe('GameServersService', () => { }); it('should return this game server', async () => { - expect((await service.findFreeGameServer()).id).toEqual(testGameServer.id); + expect((await service.findFreeGameServer()).id).toEqual( + testGameServer.id, + ); }); }); }); @@ -251,7 +261,11 @@ describe('GameServersService', () => { let game: DocumentType; beforeEach(async () => { - game = await gameModel.create({ number: 1, map: 'cp_badlands', slots: [] }); + game = await gameModel.create({ + number: 1, + map: 'cp_badlands', + slots: [], + }); testGameServer.isOnline = true; await testGameServer.save(); @@ -265,7 +279,11 @@ describe('GameServersService', () => { describe('when there are no free game servers', () => { beforeEach(async () => { - const game2 = await gameModel.create({ number: 2, map: 'cp_badlands', slots: [] }); + const game2 = await gameModel.create({ + number: 2, + map: 'cp_badlands', + slots: [], + }); await service.assignFreeGameServer(game2); }); @@ -283,14 +301,19 @@ describe('GameServersService', () => { describe('when the game server does not exist', () => { it('should throw an error', async () => { - await expect(service.releaseServer(new ObjectId().toString())).rejects.toThrow(mongoose.Error.DocumentNotFoundError); + await expect( + service.releaseServer(new ObjectId().toString()), + ).rejects.toThrow(mongoose.Error.DocumentNotFoundError); }); }); }); describe('#getGameServerByEventSource()', () => { it('should return the correct server', async () => { - const server = await service.getGameServerByEventSource({ address: '127.0.0.1', port: 27015 }); + const server = await service.getGameServerByEventSource({ + address: '127.0.0.1', + port: 27015, + }); expect(server.id).toEqual(testGameServer.id); }); }); diff --git a/src/game-servers/services/game-servers.service.ts b/src/game-servers/services/game-servers.service.ts index 8b0039fae..49ea79764 100644 --- a/src/game-servers/services/game-servers.service.ts +++ b/src/game-servers/services/game-servers.service.ts @@ -15,34 +15,53 @@ const resolve = promisify(resolveCb); @Injectable() export class GameServersService { - private readonly logger = new Logger(GameServersService.name); private readonly mutex = new Mutex(); constructor( - @InjectModel(GameServer) private gameServerModel: ReturnModelType, + @InjectModel(GameServer) + private gameServerModel: ReturnModelType, private events: Events, - ) { } + ) {} async getAllGameServers(): Promise { - return plainToClass(GameServer, await this.gameServerModel.find({ deleted: false }).lean().exec()); + return plainToClass( + GameServer, + await this.gameServerModel.find({ deleted: false }).lean().exec(), + ); } async getById(gameServerId: string): Promise { - return plainToClass(GameServer, await this.gameServerModel.findById(gameServerId).orFail().lean().exec()); + return plainToClass( + GameServer, + await this.gameServerModel.findById(gameServerId).orFail().lean().exec(), + ); } - async addGameServer(params: GameServer, adminId?: string): Promise { - if (/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/.test(params.address)) { // raw ip address was given - params.resolvedIpAddresses = [ params.address ]; + async addGameServer( + params: GameServer, + adminId?: string, + ): Promise { + if ( + /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/.test( + params.address, + ) + ) { + // raw ip address was given + params.resolvedIpAddresses = [params.address]; } else { const resolvedIpAddresses = await resolve(params.address); - this.logger.verbose(`resolved addresses for ${params.address}: ${resolvedIpAddresses}`); + this.logger.verbose( + `resolved addresses for ${params.address}: ${resolvedIpAddresses}`, + ); params.resolvedIpAddresses = resolvedIpAddresses; } if (!params.mumbleChannelName) { - const latestServer = await this.gameServerModel.findOne({ mumbleChannelName: { $ne: null }, deleted: false }).sort({ createdAt: -1 }).exec(); + const latestServer = await this.gameServerModel + .findOne({ mumbleChannelName: { $ne: null }, deleted: false }) + .sort({ createdAt: -1 }) + .exec(); if (latestServer) { const id = parseInt(latestServer.mumbleChannelName, 10) + 1; params.mumbleChannelName = `${id}`; @@ -58,31 +77,53 @@ export class GameServersService { return gameServer; } - async updateGameServer(gameServerId: string, update: Partial): Promise { - return plainToClass(GameServer, - await this.gameServerModel.findByIdAndUpdate(gameServerId, update, { new: true }).lean().exec()); + async updateGameServer( + gameServerId: string, + update: Partial, + ): Promise { + return plainToClass( + GameServer, + await this.gameServerModel + .findByIdAndUpdate(gameServerId, update, { new: true }) + .lean() + .exec(), + ); } - async removeGameServer(gameServerId: string, adminId?: string): Promise { - const gameServer = await this.updateGameServer(gameServerId, { deleted: true }); + async removeGameServer( + gameServerId: string, + adminId?: string, + ): Promise { + const gameServer = await this.updateGameServer(gameServerId, { + deleted: true, + }); this.events.gameServerRemoved.next({ gameServer, adminId }); return gameServer; } async findFreeGameServer(): Promise { - return plainToClass(GameServer, - await this.gameServerModel.findOne({ deleted: false, isOnline: true, game: { $exists: false } }).orFail().lean().exec()); + return plainToClass( + GameServer, + await this.gameServerModel + .findOne({ deleted: false, isOnline: true, game: { $exists: false } }) + .orFail() + .lean() + .exec(), + ); } async assignFreeGameServer(game: DocumentType): Promise { return this.mutex.runExclusive(async () => { try { - const gameServer = await this.updateGameServer((await this.findFreeGameServer()).id, { game: game._id }); + const gameServer = await this.updateGameServer( + (await this.findFreeGameServer()).id, + { game: game._id }, + ); game.gameServer = gameServer._id; await game.save(); this.events.gameChanges.next({ game: game.toJSON() }); return gameServer; - } catch(error) { + } catch (error) { if (error instanceof mongoose.Error.DocumentNotFoundError) { throw new Error('no free game server available'); } else { @@ -92,20 +133,38 @@ export class GameServersService { }); } - async releaseServer(gameServerId: string): Promise { - const gameServer = plainToClass(GameServer, - await this.gameServerModel.findByIdAndUpdate(gameServerId, { $unset: { game: 1 } }, { new: true }).orFail().lean().exec()); - - this.logger.debug(`game server ${gameServerId} (${gameServer.name}) marked as free`); + async releaseServer(gameServerId: string): Promise { + const gameServer = plainToClass( + GameServer, + await this.gameServerModel + .findByIdAndUpdate(gameServerId, { $unset: { game: 1 } }, { new: true }) + .orFail() + .lean() + .exec(), + ); + + this.logger.debug( + `game server ${gameServerId} (${gameServer.name}) marked as free`, + ); return gameServer; } - async getGameServerByEventSource(eventSource: { address: string; port: number; }): Promise { - return plainToClass(GameServer, await this.gameServerModel.findOne({ - deleted: false, - resolvedIpAddresses: eventSource.address, - port: `${eventSource.port}`, - }).orFail().lean().exec()); + async getGameServerByEventSource(eventSource: { + address: string; + port: number; + }): Promise { + return plainToClass( + GameServer, + await this.gameServerModel + .findOne({ + deleted: false, + resolvedIpAddresses: eventSource.address, + port: `${eventSource.port}`, + }) + .orFail() + .lean() + .exec(), + ); } @Cron(CronExpression.EVERY_MINUTE) @@ -113,11 +172,15 @@ export class GameServersService { this.logger.debug('checking all servers...'); const allGameServers = await this.getAllGameServers(); for (const server of allGameServers) { - const isOnline = await isServerOnline(server.address, parseInt(server.port, 10)); + const isOnline = await isServerOnline( + server.address, + parseInt(server.port, 10), + ); await this.updateGameServer(server.id, { isOnline }); - this.logger.debug(`server ${server.name} is ${isOnline ? 'online' : 'offline'}`); + this.logger.debug( + `server ${server.name} is ${isOnline ? 'online' : 'offline'}`, + ); // TODO verify rcon password } } - } diff --git a/src/game-servers/utils/is-server-online.ts b/src/game-servers/utils/is-server-online.ts index 653ad2700..bd9cad8a2 100644 --- a/src/game-servers/utils/is-server-online.ts +++ b/src/game-servers/utils/is-server-online.ts @@ -1,6 +1,9 @@ import { query } from 'gamedig'; -export async function isServerOnline(address: string, port: number): Promise { +export async function isServerOnline( + address: string, + port: number, +): Promise { try { await query({ type: 'tf2', host: address, port }); return true; diff --git a/src/games/controllers/games-with-substitution-requests.controller.spec.ts b/src/games/controllers/games-with-substitution-requests.controller.spec.ts index 2c3269800..dad95de60 100644 --- a/src/games/controllers/games-with-substitution-requests.controller.spec.ts +++ b/src/games/controllers/games-with-substitution-requests.controller.spec.ts @@ -9,7 +9,9 @@ const game = { }; class GamesServiceStub { - getGamesWithSubstitutionRequests() { return [ game ]; } + getGamesWithSubstitutionRequests() { + return [game]; + } } describe('GamesWithSubstitutionRequests Controller', () => { @@ -18,13 +20,13 @@ describe('GamesWithSubstitutionRequests Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ - { provide: GamesService, useClass: GamesServiceStub }, - ], + providers: [{ provide: GamesService, useClass: GamesServiceStub }], controllers: [GamesWithSubstitutionRequestsController], }).compile(); - controller = module.get(GamesWithSubstitutionRequestsController); + controller = module.get( + GamesWithSubstitutionRequestsController, + ); gamesService = module.get(GamesService); }); @@ -37,7 +39,7 @@ describe('GamesWithSubstitutionRequests Controller', () => { const spy = jest.spyOn(gamesService, 'getGamesWithSubstitutionRequests'); const ret = await controller.getGamesWithSubstitutionRequests(); expect(spy).toHaveBeenCalled(); - expect(ret).toEqual([ game ] as any); + expect(ret).toEqual([game] as any); }); }); }); diff --git a/src/games/controllers/games-with-substitution-requests.controller.ts b/src/games/controllers/games-with-substitution-requests.controller.ts index d8d772b83..0a3bf09e3 100644 --- a/src/games/controllers/games-with-substitution-requests.controller.ts +++ b/src/games/controllers/games-with-substitution-requests.controller.ts @@ -3,14 +3,10 @@ import { GamesService } from '../services/games.service'; @Controller('games-with-substitution-requests') export class GamesWithSubstitutionRequestsController { - - constructor( - private gamesService: GamesService, - ) { } + constructor(private gamesService: GamesService) {} @Get() async getGamesWithSubstitutionRequests() { return this.gamesService.getGamesWithSubstitutionRequests(); } - } diff --git a/src/games/controllers/games.controller.spec.ts b/src/games/controllers/games.controller.spec.ts index 59b7d903b..7cc7991f9 100644 --- a/src/games/controllers/games.controller.spec.ts +++ b/src/games/controllers/games.controller.spec.ts @@ -12,14 +12,34 @@ jest.mock('../services/player-substitution.service'); class GamesServiceStub { games: Game[] = [ - { number: 1, map: 'cp_fake_rc1', state: 'ended', assignedSkills: new Map([['FAKE_PLAYER_ID', 1]]) } as Game, - { number: 2, map: 'cp_fake_rc2', state: 'launching', assignedSkills: new Map([['FAKE_PLAYER_ID', 5]]) } as Game, + { + number: 1, + map: 'cp_fake_rc1', + state: 'ended', + assignedSkills: new Map([['FAKE_PLAYER_ID', 1]]), + } as Game, + { + number: 2, + map: 'cp_fake_rc2', + state: 'launching', + assignedSkills: new Map([['FAKE_PLAYER_ID', 5]]), + } as Game, ]; - getById(id: string) { return Promise.resolve(this.games[0]); } - getGames(sort: any, limit: number, skip: number) { return Promise.resolve(this.games); } - getGameCount() { return Promise.resolve(2); } - getPlayerGames(playerId: string, sort: any, limit: number, skip: number) { return Promise.resolve(this.games); } - getPlayerGameCount(playerId: string) { return Promise.resolve(1); } + getById(id: string) { + return Promise.resolve(this.games[0]); + } + getGames(sort: any, limit: number, skip: number) { + return Promise.resolve(this.games); + } + getGameCount() { + return Promise.resolve(2); + } + getPlayerGames(playerId: string, sort: any, limit: number, skip: number) { + return Promise.resolve(this.games); + } + getPlayerGameCount(playerId: string) { + return Promise.resolve(1); + } } describe('Games Controller', () => { @@ -80,8 +100,18 @@ describe('Games Controller', () => { describe('when sorting with -launched_at', () => { it('should return player games', async () => { const spy = jest.spyOn(gamesService, 'getPlayerGames'); - const ret = await controller.getGames(10, 0, '-launched_at', 'FAKE_PLAYER_ID'); - expect(spy).toHaveBeenCalledWith('FAKE_PLAYER_ID', { launchedAt: -1 }, 10, 0); + const ret = await controller.getGames( + 10, + 0, + '-launched_at', + 'FAKE_PLAYER_ID', + ); + expect(spy).toHaveBeenCalledWith( + 'FAKE_PLAYER_ID', + { launchedAt: -1 }, + 10, + 0, + ); expect(ret).toEqual({ results: gamesService.games, itemCount: 1, @@ -93,8 +123,18 @@ describe('Games Controller', () => { it('should return player games', async () => { const spy = jest.spyOn(gamesService, 'getPlayerGames'); - const ret = await controller.getGames(30, 2, 'launched_at', 'FAKE_PLAYER_ID'); - expect(spy).toHaveBeenCalledWith('FAKE_PLAYER_ID', { launchedAt: 1 }, 30, 2); + const ret = await controller.getGames( + 30, + 2, + 'launched_at', + 'FAKE_PLAYER_ID', + ); + expect(spy).toHaveBeenCalledWith( + 'FAKE_PLAYER_ID', + { launchedAt: 1 }, + 30, + 2, + ); expect(ret).toEqual({ results: gamesService.games, itemCount: 1, @@ -111,10 +151,14 @@ describe('Games Controller', () => { }); describe('when requesting a non-existing game', () => { - beforeEach(() => jest.spyOn(gamesService, 'getById').mockResolvedValue(null)); + beforeEach(() => + jest.spyOn(gamesService, 'getById').mockResolvedValue(null), + ); it('should return 404', async () => { - await expect(controller.getGame('FAKE_GAME_ID')).rejects.toThrow(NotFoundException); + await expect(controller.getGame('FAKE_GAME_ID')).rejects.toThrow( + NotFoundException, + ); }); }); }); @@ -126,10 +170,14 @@ describe('Games Controller', () => { }); describe('when requesting assigned skills for a non-existing game', () => { - beforeEach(() => jest.spyOn(gamesService, 'getById').mockResolvedValue(null)); + beforeEach(() => + jest.spyOn(gamesService, 'getById').mockResolvedValue(null), + ); it('should return 404', async () => { - await expect(controller.getGameSkills('FAKE_GAME_ID')).rejects.toThrow(NotFoundException); + await expect(controller.getGameSkills('FAKE_GAME_ID')).rejects.toThrow( + NotFoundException, + ); }); }); }); @@ -137,25 +185,56 @@ describe('Games Controller', () => { describe('#takeAdminAction()', () => { it('should reinitialize server', async () => { const spy = jest.spyOn(gameRuntimeService, 'reconfigure'); - await controller.takeAdminAction('FAKE_GAME_ID', '', undefined, undefined, undefined, { id: 'FAKE_ADMIN_ID' } as Player); + await controller.takeAdminAction( + 'FAKE_GAME_ID', + '', + undefined, + undefined, + undefined, + { id: 'FAKE_ADMIN_ID' } as Player, + ); expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID'); }); it('should force end the game', async () => { const spy = jest.spyOn(gameRuntimeService, 'forceEnd'); - await controller.takeAdminAction('FAKE_GAME_ID', undefined, '', undefined, undefined, { id: 'FAKE_ADMIN_ID' } as Player); + await controller.takeAdminAction( + 'FAKE_GAME_ID', + undefined, + '', + undefined, + undefined, + { id: 'FAKE_ADMIN_ID' } as Player, + ); expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', 'FAKE_ADMIN_ID'); }); it('should substitute player', async () => { const spy = jest.spyOn(playerSubstitutionService, 'substitutePlayer'); - await controller.takeAdminAction('FAKE_GAME_ID', undefined, undefined, 'FAKE_PLAYER_ID', undefined, { id: 'FAKE_ADMIN_ID' } as Player); + await controller.takeAdminAction( + 'FAKE_GAME_ID', + undefined, + undefined, + 'FAKE_PLAYER_ID', + undefined, + { id: 'FAKE_ADMIN_ID' } as Player, + ); expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', 'FAKE_PLAYER_ID'); }); it('should cancel substitution request', async () => { - const spy = jest.spyOn(playerSubstitutionService, 'cancelSubstitutionRequest'); - await controller.takeAdminAction('FAKE_GAME_ID', undefined, undefined, undefined, 'FAKE_PLAYER_ID', { id: 'FAKE_ADMIN_ID' } as Player); + const spy = jest.spyOn( + playerSubstitutionService, + 'cancelSubstitutionRequest', + ); + await controller.takeAdminAction( + 'FAKE_GAME_ID', + undefined, + undefined, + undefined, + 'FAKE_PLAYER_ID', + { id: 'FAKE_ADMIN_ID' } as Player, + ); expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', 'FAKE_PLAYER_ID'); }); }); diff --git a/src/games/controllers/games.controller.ts b/src/games/controllers/games.controller.ts index a47f530aa..94ca5f73a 100644 --- a/src/games/controllers/games.controller.ts +++ b/src/games/controllers/games.controller.ts @@ -1,4 +1,14 @@ -import { Controller, Get, Query, ParseIntPipe, Param, NotFoundException, Post, HttpCode, DefaultValuePipe } from '@nestjs/common'; +import { + Controller, + Get, + Query, + ParseIntPipe, + Param, + NotFoundException, + Post, + HttpCode, + DefaultValuePipe, +} from '@nestjs/common'; import { GamesService } from '../services/games.service'; import { ObjectIdValidationPipe } from '@/shared/pipes/object-id-validation.pipe'; import { Auth } from '@/auth/decorators/auth.decorator'; @@ -19,18 +29,22 @@ const sortOptions: string[] = [ @Controller('games') export class GamesController { - constructor( private gamesService: GamesService, private gameRuntimeService: GameRuntimeService, private playerSubstitutionService: PlayerSubstitutionService, - ) { } + ) {} @Get() async getGames( @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number, @Query('offset', new DefaultValuePipe(0), ParseIntPipe) offset: number, - @Query('sort', new DefaultValuePipe('-launched_at'), new IsOneOfPipe(sortOptions)) sort: string, + @Query( + 'sort', + new DefaultValuePipe('-launched_at'), + new IsOneOfPipe(sortOptions), + ) + sort: string, @Query('playerId') playerId?: string, ) { let sortParam: { launchedAt: 1 | -1 }; @@ -50,12 +64,12 @@ export class GamesController { let itemCount: number; if (playerId === undefined) { - [ results, itemCount ] = await Promise.all([ + [results, itemCount] = await Promise.all([ this.gamesService.getGames(sortParam, limit, offset), this.gamesService.getGameCount(), ]); } else { - [ results, itemCount ] = await Promise.all([ + [results, itemCount] = await Promise.all([ this.gamesService.getPlayerGames(playerId, sortParam, limit, offset), this.gamesService.getPlayerGameCount(playerId), ]); @@ -105,12 +119,17 @@ export class GamesController { } if (substitutePlayerId !== undefined) { - await this.playerSubstitutionService.substitutePlayer(gameId, substitutePlayerId); + await this.playerSubstitutionService.substitutePlayer( + gameId, + substitutePlayerId, + ); } if (cancelSubstitutePlayerId !== undefined) { - await this.playerSubstitutionService.cancelSubstitutionRequest(gameId, cancelSubstitutePlayerId); + await this.playerSubstitutionService.cancelSubstitutionRequest( + gameId, + cancelSubstitutePlayerId, + ); } } - } diff --git a/src/games/games.module.ts b/src/games/games.module.ts index 80788b922..e2ea1cdd8 100644 --- a/src/games/games.module.ts +++ b/src/games/games.module.ts @@ -22,7 +22,9 @@ import { LogReceiverModule } from '@/log-receiver/log-receiver.module'; @Module({ imports: [ - TypegooseModule.forFeature([ standardSchemaOptions(Game, removeGameAssignedSkills) ]), + TypegooseModule.forFeature([ + standardSchemaOptions(Game, removeGameAssignedSkills), + ]), GameServersModule, forwardRef(() => PlayersModule), QueueModule, @@ -41,12 +43,7 @@ import { LogReceiverModule } from '@/log-receiver/log-receiver.module'; GameEventHandlerService, PlayerSubstitutionService, ], - exports: [ - GamesService, - ], - controllers: [ - GamesController, - GamesWithSubstitutionRequestsController, - ], + exports: [GamesService], + controllers: [GamesController, GamesWithSubstitutionRequestsController], }) -export class GamesModule { } +export class GamesModule {} diff --git a/src/games/gateways/games.gateway.spec.ts b/src/games/gateways/games.gateway.spec.ts index 1fe1f0e9a..9ff936764 100644 --- a/src/games/gateways/games.gateway.spec.ts +++ b/src/games/gateways/games.gateway.spec.ts @@ -23,11 +23,7 @@ describe('GamesGateway', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ - GamesGateway, - PlayerSubstitutionService, - Events, - ], + providers: [GamesGateway, PlayerSubstitutionService, Events], }).compile(); gateway = module.get(GamesGateway); @@ -54,10 +50,16 @@ describe('GamesGateway', () => { describe('#replacePlayer()', () => { it('should replace the player', async () => { const ret = await gateway.replacePlayer( - { request: { user: { id: 'FAKE_REPLACEMENT_ID', logged_in: true } } } as AuthorizedWsClient, + { + request: { user: { id: 'FAKE_REPLACEMENT_ID', logged_in: true } }, + } as AuthorizedWsClient, { gameId: 'FAKE_GAME_ID', replaceeId: 'FAKE_REPLACEE_ID' }, ); - expect(playerSubstitutionService.replacePlayer).toHaveBeenCalledWith('FAKE_GAME_ID', 'FAKE_REPLACEE_ID', 'FAKE_REPLACEMENT_ID'); + expect(playerSubstitutionService.replacePlayer).toHaveBeenCalledWith( + 'FAKE_GAME_ID', + 'FAKE_REPLACEE_ID', + 'FAKE_REPLACEMENT_ID', + ); expect(ret).toEqual(mockGame as any); }); }); diff --git a/src/games/gateways/games.gateway.ts b/src/games/gateways/games.gateway.ts index 32a93d4b4..4e6cc96db 100644 --- a/src/games/gateways/games.gateway.ts +++ b/src/games/gateways/games.gateway.ts @@ -1,4 +1,8 @@ -import { WebSocketGateway, OnGatewayInit, SubscribeMessage } from '@nestjs/websockets'; +import { + WebSocketGateway, + OnGatewayInit, + SubscribeMessage, +} from '@nestjs/websockets'; import { Socket } from 'socket.io'; import { WsAuthorized } from '@/auth/decorators/ws-authorized.decorator'; import { PlayerSubstitutionService } from '../services/player-substitution.service'; @@ -9,18 +13,25 @@ import { WebsocketEvent } from '@/websocket-event'; @WebSocketGateway() export class GamesGateway implements OnGatewayInit, OnModuleInit { - private socket: Socket; constructor( - @Inject(forwardRef(() => PlayerSubstitutionService)) private playerSubstitutionService: PlayerSubstitutionService, + @Inject(forwardRef(() => PlayerSubstitutionService)) + private playerSubstitutionService: PlayerSubstitutionService, private events: Events, - ) { } + ) {} @WsAuthorized() @SubscribeMessage('replace player') - async replacePlayer(client: AuthorizedWsClient, payload: { gameId: string, replaceeId: string }) { - return await this.playerSubstitutionService.replacePlayer(payload.gameId, payload.replaceeId, client.request.user.id); + async replacePlayer( + client: AuthorizedWsClient, + payload: { gameId: string; replaceeId: string }, + ) { + return await this.playerSubstitutionService.replacePlayer( + payload.gameId, + payload.replaceeId, + client.request.user.id, + ); } afterInit(socket: Socket) { @@ -28,8 +39,11 @@ export class GamesGateway implements OnGatewayInit, OnModuleInit { } onModuleInit() { - this.events.gameCreated.subscribe(({ game }) => this.socket.emit(WebsocketEvent.gameCreated, game)); - this.events.gameChanges.subscribe(({ game }) => this.socket.emit(WebsocketEvent.gameUpdated, game)); + this.events.gameCreated.subscribe(({ game }) => + this.socket.emit(WebsocketEvent.gameCreated, game), + ); + this.events.gameChanges.subscribe(({ game }) => + this.socket.emit(WebsocketEvent.gameUpdated, game), + ); } - } diff --git a/src/games/models/game-slot.ts b/src/games/models/game-slot.ts index 1d617a2ef..3fb95203f 100644 --- a/src/games/models/game-slot.ts +++ b/src/games/models/game-slot.ts @@ -6,7 +6,6 @@ import { SlotStatus } from './slot-status'; import { Tf2ClassName } from '@/shared/models/tf2-class-name'; export class GameSlot { - @prop({ required: true, ref: Player, index: true }) player!: Ref; @@ -19,7 +18,9 @@ export class GameSlot { @prop({ index: true, enum: SlotStatus, default: SlotStatus.active }) status?: SlotStatus; - @prop({ enum: PlayerConnectionStatus, default: PlayerConnectionStatus.offline }) + @prop({ + enum: PlayerConnectionStatus, + default: PlayerConnectionStatus.offline, + }) connectionStatus?: PlayerConnectionStatus; - } diff --git a/src/games/models/game.ts b/src/games/models/game.ts index 149a9c342..892f16159 100644 --- a/src/games/models/game.ts +++ b/src/games/models/game.ts @@ -49,11 +49,14 @@ export class Game { stvConnectString?: string; findPlayerSlot(playerId: string) { - return this.slots.find(s => s.player.toString().localeCompare(playerId) === 0); + return this.slots.find( + (s) => s.player.toString().localeCompare(playerId) === 0, + ); } activeSlots() { - return this.slots.filter(slot => slot.status.match(/active|waiting for substitute/)); + return this.slots.filter((slot) => + slot.status.match(/active|waiting for substitute/), + ); } - } diff --git a/src/games/services/__mocks__/games.service.ts b/src/games/services/__mocks__/games.service.ts index 77c2c7e66..9189bbf42 100644 --- a/src/games/services/__mocks__/games.service.ts +++ b/src/games/services/__mocks__/games.service.ts @@ -9,39 +9,41 @@ import { Tf2ClassName } from '@/shared/models/tf2-class-name'; @Injectable() export class GamesService { - private lastGameId = 0; constructor( @InjectModel(Game) private gameModel: ReturnModelType, - ) { } + ) {} async getById(gameId: string) { return await this.gameModel.findById(gameId); } async update(gameId: string, update: Partial) { - return await this.gameModel.findByIdAndUpdate(gameId, update, { new: true }); + return await this.gameModel.findByIdAndUpdate(gameId, update, { + new: true, + }); } - async getPlayerActiveGame(playerId: string) { return Promise.resolve(null); } + async getPlayerActiveGame(playerId: string) { + return Promise.resolve(null); + } async _createOne(players?: Player[]) { let lastTeamId = 0; - const teams = [ 'red', 'blu' ]; + const teams = ['red', 'blu']; return await this.gameModel.create({ number: ++this.lastGameId, map: 'cp_badlands', - slots: players?.map(p => ({ + slots: players?.map((p) => ({ player: p._id, - team: teams[`${(lastTeamId++) % 2}`], + team: teams[`${lastTeamId++ % 2}`], gameClass: Tf2ClassName.soldier, })), }); } async _reset() { - await this.gameModel.deleteMany({ }); + await this.gameModel.deleteMany({}); } - } diff --git a/src/games/services/game-event-handler.service.spec.ts b/src/games/services/game-event-handler.service.spec.ts index f16df51f5..081ce54ac 100644 --- a/src/games/services/game-event-handler.service.spec.ts +++ b/src/games/services/game-event-handler.service.spec.ts @@ -34,14 +34,17 @@ describe('GameEventHandlerService', () => { let gamesService: GamesService; let events: Events; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([standardSchemaOptions(Game, removeGameAssignedSkills), Player]), + TypegooseModule.forFeature([ + standardSchemaOptions(Game, removeGameAssignedSkills), + Player, + ]), ], providers: [ GameEventHandlerService, @@ -67,7 +70,7 @@ describe('GameEventHandlerService', () => { player2 = await playersService._createOne(); // await gamesService.initialize(); // @ts-expect-error - mockGame = await gamesService._createOne([ player1, player2 ]); + mockGame = await gamesService._createOne([player1, player2]); }); afterEach(async () => { @@ -87,14 +90,15 @@ describe('GameEventHandlerService', () => { expect(game.state).toEqual('started'); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id, state: 'started' }); - resolve(); - }); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id, state: 'started' }); + resolve(); + }); - service.onMatchStarted(mockGame.id); - })); + service.onMatchStarted(mockGame.id); + })); describe('when the match has ended', () => { beforeEach(async () => { @@ -137,14 +141,15 @@ describe('GameEventHandlerService', () => { jest.useRealTimers(); }); - it('should emit the gameChanges events', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id, state: 'ended' }); - resolve(); - }); + it('should emit the gameChanges events', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id, state: 'ended' }); + resolve(); + }); - service.onMatchEnded(mockGame.id); - })); + service.onMatchEnded(mockGame.id); + })); describe('with player awaiting a substitute', () => { beforeEach(async () => { @@ -155,14 +160,17 @@ describe('GameEventHandlerService', () => { it('should set his status back to active', async () => { const game = await service.onMatchEnded(mockGame.id); - expect(game.slots.every(s => s.status === SlotStatus.active)).toBe(true); + expect(game.slots.every((s) => s.status === SlotStatus.active)).toBe( + true, + ); }); // eslint-disable-next-line jest/expect-expect - it('should emit the substituteRequestsChange event', async () => new Promise(resolve => { - events.substituteRequestsChange.subscribe(resolve); - service.onMatchEnded(mockGame.id); - })); + it('should emit the substituteRequestsChange event', async () => + new Promise((resolve) => { + events.substituteRequestsChange.subscribe(resolve); + service.onMatchEnded(mockGame.id); + })); }); }); @@ -172,14 +180,18 @@ describe('GameEventHandlerService', () => { expect(game.logsUrl).toEqual('FAKE_LOGS_URL'); }); - it('should emit the gameChanges events', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id, logsUrl: 'FAKE_LOGS_URL' }); - resolve(); - }); - - service.onLogsUploaded(mockGame._id, 'FAKE_LOGS_URL'); - })); + it('should emit the gameChanges events', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ + id: mockGame.id, + logsUrl: 'FAKE_LOGS_URL', + }); + resolve(); + }); + + service.onLogsUploaded(mockGame._id, 'FAKE_LOGS_URL'); + })); }); describe('#onDemoUploaded()', () => { @@ -188,72 +200,96 @@ describe('GameEventHandlerService', () => { expect(game.demoUrl).toEqual('FAKE_DEMO_URL'); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id, demoUrl: 'FAKE_DEMO_URL' }); - resolve(); - }); - - service.onDemoUploaded(mockGame._id, 'FAKE_DEMO_URL'); - })); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ + id: mockGame.id, + demoUrl: 'FAKE_DEMO_URL', + }); + resolve(); + }); + + service.onDemoUploaded(mockGame._id, 'FAKE_DEMO_URL'); + })); describe('when a wrong gameId is captured', () => { it('should return null', async () => { - expect(await service.onDemoUploaded(new ObjectId().toString(), 'FAKE_DEMO_URL')).toBe(null); + expect( + await service.onDemoUploaded( + new ObjectId().toString(), + 'FAKE_DEMO_URL', + ), + ).toBe(null); }); }); }); describe('#onPlayerJoining()', () => { - it('should update the player\'s online state', async () => { - const game = await service.onPlayerJoining(mockGame.id, player1.steamId); - expect(game.findPlayerSlot(player1.id).connectionStatus).toEqual('joining'); + it("should update the player's online state", async () => { + const game = await service.onPlayerJoining(mockGame.id, player1.steamId); + expect(game.findPlayerSlot(player1.id).connectionStatus).toEqual( + 'joining', + ); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id }); - resolve(); - }); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id }); + resolve(); + }); - service.onPlayerJoining(mockGame.id, player1.steamId); - })); + service.onPlayerJoining(mockGame.id, player1.steamId); + })); }); describe('#onPlayerConnected()', () => { - it('should update the player\'s online state', async () => { - const game = await service.onPlayerConnected(mockGame.id, player1.steamId); - expect(game.findPlayerSlot(player1.id).connectionStatus).toEqual('connected'); + it("should update the player's online state", async () => { + const game = await service.onPlayerConnected( + mockGame.id, + player1.steamId, + ); + expect(game.findPlayerSlot(player1.id).connectionStatus).toEqual( + 'connected', + ); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id }); - resolve(); - }); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id }); + resolve(); + }); - service.onPlayerConnected(mockGame.id, player1.steamId); - })); + service.onPlayerConnected(mockGame.id, player1.steamId); + })); }); describe('#onPlayerDisconnected()', () => { - it('should update the player\'s online state', async () => { - const game = await service.onPlayerDisconnected(mockGame.id, player1.steamId); - expect(game.findPlayerSlot(player1.id).connectionStatus).toEqual('offline'); + it("should update the player's online state", async () => { + const game = await service.onPlayerDisconnected( + mockGame.id, + player1.steamId, + ); + expect(game.findPlayerSlot(player1.id).connectionStatus).toEqual( + 'offline', + ); }); - it('should emit an the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id }); - resolve(); - }); + it('should emit an the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id }); + resolve(); + }); - service.onPlayerDisconnected(mockGame.id, player1.steamId); - })); + service.onPlayerDisconnected(mockGame.id, player1.steamId); + })); }); describe('#onScoreReported()', () => { - it('should update the game\'s score', async () => { + it("should update the game's score", async () => { let game = await service.onScoreReported(mockGame.id, 'Red', '2'); expect(game.score.get('red')).toEqual(2); @@ -261,13 +297,14 @@ describe('GameEventHandlerService', () => { expect(game.score.get('blu')).toEqual(5); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id }); - resolve(); - }); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id }); + resolve(); + }); - service.onScoreReported(mockGame.id, 'Red', '2'); - })); + service.onScoreReported(mockGame.id, 'Red', '2'); + })); }); }); diff --git a/src/games/services/game-event-handler.service.ts b/src/games/services/game-event-handler.service.ts index daf37a6fc..2b6d510c7 100644 --- a/src/games/services/game-event-handler.service.ts +++ b/src/games/services/game-event-handler.service.ts @@ -12,7 +12,6 @@ import { GameState } from '../models/game-state'; @Injectable() export class GameEventHandlerService { - private logger = new Logger(GameEventHandlerService.name); constructor( @@ -20,10 +19,14 @@ export class GameEventHandlerService { private playersService: PlayersService, private gameRuntimeService: GameRuntimeService, private events: Events, - ) { } + ) {} async onMatchStarted(gameId: string) { - const game = await this.gameModel.findOneAndUpdate({ _id: gameId, state: GameState.launching }, { state: GameState.started }, { new: true }); + const game = await this.gameModel.findOneAndUpdate( + { _id: gameId, state: GameState.launching }, + { state: GameState.started }, + { new: true }, + ); if (game) { this.events.gameChanges.next({ game: game.toJSON() }); } @@ -43,13 +46,16 @@ export class GameEventHandlerService { arrayFilters: [ { 'element.status': { $eq: `${SlotStatus.waitingForSubstitute}` } }, ], - } + }, ); if (game) { this.events.gameChanges.next({ game: game.toJSON() }); this.events.substituteRequestsChange.next(); - setTimeout(() => this.gameRuntimeService.cleanupServer(game.gameServer.toString()), serverCleanupDelay); + setTimeout( + () => this.gameRuntimeService.cleanupServer(game.gameServer.toString()), + serverCleanupDelay, + ); } else { this.logger.warn(`no such game: ${gameId}`); } @@ -58,7 +64,11 @@ export class GameEventHandlerService { } async onLogsUploaded(gameId: string, logsUrl: string) { - const game = await this.gameModel.findOneAndUpdate({ _id: gameId }, { logsUrl }, { new: true }); + const game = await this.gameModel.findOneAndUpdate( + { _id: gameId }, + { logsUrl }, + { new: true }, + ); if (game) { this.events.gameChanges.next({ game: game.toJSON() }); } else { @@ -69,7 +79,11 @@ export class GameEventHandlerService { } async onDemoUploaded(gameId: string, demoUrl: string) { - const game = await this.gameModel.findByIdAndUpdate(gameId, { demoUrl }, { new: true }); + const game = await this.gameModel.findByIdAndUpdate( + gameId, + { demoUrl }, + { new: true }, + ); if (game) { this.events.gameChanges.next({ game: game.toJSON() }); } else { @@ -80,20 +94,36 @@ export class GameEventHandlerService { } async onPlayerJoining(gameId: string, steamId: string) { - return await this.setPlayerConnectionStatus(gameId, steamId, PlayerConnectionStatus.joining); + return await this.setPlayerConnectionStatus( + gameId, + steamId, + PlayerConnectionStatus.joining, + ); } async onPlayerConnected(gameId: string, steamId: string) { - return await this.setPlayerConnectionStatus(gameId, steamId, PlayerConnectionStatus.connected); + return await this.setPlayerConnectionStatus( + gameId, + steamId, + PlayerConnectionStatus.connected, + ); } async onPlayerDisconnected(gameId: string, steamId: string) { - return await this.setPlayerConnectionStatus(gameId, steamId, PlayerConnectionStatus.offline); + return await this.setPlayerConnectionStatus( + gameId, + steamId, + PlayerConnectionStatus.offline, + ); } async onScoreReported(gameId: string, teamName: string, score: string) { const fixedTeamName = teamName.toLowerCase().substring(0, 3); // converts Red to 'red' and Blue to 'blu' - const game = await this.gameModel.findOneAndUpdate({ _id: gameId }, { [`score.${fixedTeamName}`]: parseInt(score, 10) }, { new: true }); + const game = await this.gameModel.findOneAndUpdate( + { _id: gameId }, + { [`score.${fixedTeamName}`]: parseInt(score, 10) }, + { new: true }, + ); if (game) { this.events.gameChanges.next({ game: game.toJSON() }); } else { @@ -103,7 +133,11 @@ export class GameEventHandlerService { return game; } - private async setPlayerConnectionStatus(gameId: string, steamId: string, connectionStatus: PlayerConnectionStatus) { + private async setPlayerConnectionStatus( + gameId: string, + steamId: string, + connectionStatus: PlayerConnectionStatus, + ) { const player = await this.playersService.findBySteamId(steamId); if (!player) { this.logger.warn(`no such player: ${steamId}`); @@ -117,10 +151,8 @@ export class GameEventHandlerService { }, { new: true, // return updated document - arrayFilters: [ - { 'element.player': { $eq: player.id } }, - ], - } + arrayFilters: [{ 'element.player': { $eq: player.id } }], + }, ); if (game) { @@ -131,5 +163,4 @@ export class GameEventHandlerService { return game; } - } diff --git a/src/games/services/game-event-listener.service.spec.ts b/src/games/services/game-event-listener.service.spec.ts index ca1849a23..133f1dcfc 100644 --- a/src/games/services/game-event-listener.service.spec.ts +++ b/src/games/services/game-event-listener.service.spec.ts @@ -21,7 +21,9 @@ class GameServersServiceStub { game: 'FAKE_GAME_ID', }; - getGameServerByEventSource(eventSource: any) { return Promise.resolve(this.mockGameServer); } + getGameServerByEventSource(eventSource: any) { + return Promise.resolve(this.mockGameServer); + } } class GamesServiceStub { @@ -30,7 +32,9 @@ class GamesServiceStub { number: 1, }; - getById(gameId: string) { return Promise.resolve(this.mockGame); } + getById(gameId: string) { + return Promise.resolve(this.mockGame); + } } class LogReceiverStub extends EventEmitter { @@ -80,93 +84,121 @@ describe('GameEventListenerService', () => { }); describe('should handle game events', () => { - it('match started', async () => new Promise(resolve => { + it('match started', async () => + new Promise((resolve) => { + const spy = jest.spyOn(gameEventHandlerService, 'onMatchStarted'); + logReceiver.mockEvent( + '01/26/2020 - 20:40:20: World triggered "Round_Start"', + ); + setTimeout(() => { + expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID'); + resolve(); + }); + })); + + it('match ended', async () => + new Promise((resolve) => { + const spy = jest.spyOn(gameEventHandlerService, 'onMatchEnded'); + logReceiver.mockEvent( + '01/26/2020 - 20:38:49: World triggered "Game_Over" reason "Reached Time Limit"', + ); + setTimeout(() => { + expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID'); + resolve(); + }, 0); + })); + + it('logs uploaded', async () => + new Promise((resolve) => { + const spy = jest.spyOn(gameEventHandlerService, 'onLogsUploaded'); + logReceiver.mockEvent( + '01/26/2020 - 20:38:52: [TFTrue] The log is available here: http://logs.tf/2458457. Type !log to view it.', + ); + setTimeout(() => { + expect(spy).toHaveBeenCalledWith( + 'FAKE_GAME_ID', + 'http://logs.tf/2458457', + ); + resolve(); + }, 0); + })); + + it('demo uploaded', async () => + new Promise((resolve) => { + const spy = jest.spyOn(gameEventHandlerService, 'onDemoUploaded'); + logReceiver.mockEvent( + '06/19/2020 - 00:04:28: [demos.tf]: STV available at: https://demos.tf/427407', + ); + setTimeout(() => { + expect(spy).toBeCalledWith('FAKE_GAME_ID', 'https://demos.tf/427407'); + resolve(); + }, 0); + })); + + it('player connected', async () => + new Promise((resolve) => { + const spy = jest.spyOn(gameEventHandlerService, 'onPlayerJoining'); + logReceiver.mockEvent( + '01/26/2020 - 20:03:44: "mały #tf2pickup.pl<366><[U:1:114143419]><>" connected, address "83.29.150.132:27005"', + ); + setTimeout(() => { + expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', '76561198074409147'); + resolve(); + }, 0); + })); + + it('player joined team', async () => + new Promise((resolve) => { + const spy = jest.spyOn(gameEventHandlerService, 'onPlayerConnected'); + logReceiver.mockEvent( + '01/26/2020 - 20:03:51: "maly<366><[U:1:114143419]>" joined team "Blue"', + ); + setTimeout(() => { + expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', '76561198074409147'); + resolve(); + }, 0); + })); + + it('player disconnected', async () => + new Promise((resolve) => { + const spy = jest.spyOn(gameEventHandlerService, 'onPlayerDisconnected'); + logReceiver.mockEvent( + '01/26/2020 - 20:38:43: "maly<366><[U:1:114143419]>" disconnected (reason "Disconnect by user.")', + ); + setTimeout(() => { + expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', '76561198074409147'); + resolve(); + }, 0); + })); + + it('score reported', async () => + new Promise((resolve) => { + const spy = jest.spyOn(gameEventHandlerService, 'onScoreReported'); + logReceiver.mockEvent( + '01/26/2020 - 20:38:49: Team "Blue" final score "2" with "3" players', + ); + setTimeout(() => { + expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', 'Blue', '2'); + resolve(); + }, 0); + })); + }); + + it('should discard invalid messages', async () => + new Promise((resolve) => { const spy = jest.spyOn(gameEventHandlerService, 'onMatchStarted'); - logReceiver.mockEvent('01/26/2020 - 20:40:20: World triggered "Round_Start"'); - setTimeout(() => { - expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID'); - resolve(); + logReceiver.emit('data', { + isValid: false, + receivedFrom: { + address: '127.0.0.1', + port: 1234, + }, + message: '01/26/2020 - 20:40:20: World triggered "Round_Start"', }); - })); - - it('match ended', async () => new Promise(resolve => { - const spy = jest.spyOn(gameEventHandlerService, 'onMatchEnded'); - logReceiver.mockEvent('01/26/2020 - 20:38:49: World triggered "Game_Over" reason "Reached Time Limit"'); - setTimeout(() => { - expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID'); - resolve(); - }, 0); - })); - it('logs uploaded', async () => new Promise(resolve => { - const spy = jest.spyOn(gameEventHandlerService, 'onLogsUploaded'); - logReceiver.mockEvent('01/26/2020 - 20:38:52: [TFTrue] The log is available here: http://logs.tf/2458457. Type !log to view it.'); setTimeout(() => { - expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', 'http://logs.tf/2458457'); + expect(spy).not.toHaveBeenCalled(); resolve(); }, 0); })); - - it('demo uploaded', async () => new Promise(resolve => { - const spy = jest.spyOn(gameEventHandlerService, 'onDemoUploaded'); - logReceiver.mockEvent('06/19/2020 - 00:04:28: [demos.tf]: STV available at: https://demos.tf/427407'); - setTimeout(() => { - expect(spy).toBeCalledWith('FAKE_GAME_ID', 'https://demos.tf/427407'); - resolve(); - }, 0); - })); - - it('player connected', async () => new Promise(resolve => { - const spy = jest.spyOn(gameEventHandlerService, 'onPlayerJoining'); - logReceiver.mockEvent('01/26/2020 - 20:03:44: "mały #tf2pickup.pl<366><[U:1:114143419]><>" connected, address "83.29.150.132:27005"'); - setTimeout(() => { - expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', '76561198074409147'); - resolve(); - }, 0); - })); - - it('player joined team', async () => new Promise(resolve => { - const spy = jest.spyOn(gameEventHandlerService, 'onPlayerConnected'); - logReceiver.mockEvent('01/26/2020 - 20:03:51: "maly<366><[U:1:114143419]>" joined team "Blue"'); - setTimeout(() => { - expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', '76561198074409147'); - resolve(); - }, 0); - })); - - it('player disconnected', async () => new Promise(resolve => { - const spy = jest.spyOn(gameEventHandlerService, 'onPlayerDisconnected'); - logReceiver.mockEvent('01/26/2020 - 20:38:43: "maly<366><[U:1:114143419]>" disconnected (reason "Disconnect by user.")'); - setTimeout(() => { - expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', '76561198074409147'); - resolve(); - }, 0); - })); - - it('score reported', async () => new Promise(resolve => { - const spy = jest.spyOn(gameEventHandlerService, 'onScoreReported'); - logReceiver.mockEvent('01/26/2020 - 20:38:49: Team "Blue" final score "2" with "3" players'); - setTimeout(() => { - expect(spy).toHaveBeenCalledWith('FAKE_GAME_ID', 'Blue', '2'); - resolve(); - }, 0); - })); - }); - - it('should discard invalid messages', async () => new Promise(resolve => { - const spy = jest.spyOn(gameEventHandlerService, 'onMatchStarted'); - logReceiver.emit('data', { - isValid: false, - receivedFrom: { - address: '127.0.0.1', - port: 1234, - }, - message: '01/26/2020 - 20:40:20: World triggered "Round_Start"', - }); - - setTimeout(() => { - expect(spy).not.toHaveBeenCalled(); - resolve(); - }, 0); - })); }); diff --git a/src/games/services/game-event-listener.service.ts b/src/games/services/game-event-listener.service.ts index 931a54c1e..a655257e4 100644 --- a/src/games/services/game-event-listener.service.ts +++ b/src/games/services/game-event-listener.service.ts @@ -19,7 +19,6 @@ interface GameEvent { @Injectable() export class GameEventListenerService implements OnModuleInit { - private logger = new Logger(GameEventListenerService.name); // events @@ -27,12 +26,12 @@ export class GameEventListenerService implements OnModuleInit { { name: 'match started', regex: /^[\d/\s-:]+World triggered "Round_Start"$/, - handle: gameId => this.gameEventHandlerService.onMatchStarted(gameId), + handle: (gameId) => this.gameEventHandlerService.onMatchStarted(gameId), }, { name: 'match ended', regex: /^[\d/\s-:]+World triggered "Game_Over" reason ".*"$/, - handle: gameId => this.gameEventHandlerService.onMatchEnded(gameId), + handle: (gameId) => this.gameEventHandlerService.onMatchEnded(gameId), }, { name: 'logs uploaded', @@ -49,7 +48,10 @@ export class GameEventListenerService implements OnModuleInit { handle: (gameId, matches) => { const steamId = new SteamID(matches[5]); if (steamId.isValid()) { - this.gameEventHandlerService.onPlayerJoining(gameId, steamId.getSteamID64()); + this.gameEventHandlerService.onPlayerJoining( + gameId, + steamId.getSteamID64(), + ); } }, }, @@ -60,7 +62,10 @@ export class GameEventListenerService implements OnModuleInit { handle: (gameId, matches) => { const steamId = new SteamID(matches[5]); if (steamId.isValid()) { - this.gameEventHandlerService.onPlayerConnected(gameId, steamId.getSteamID64()); + this.gameEventHandlerService.onPlayerConnected( + gameId, + steamId.getSteamID64(), + ); } }, }, @@ -71,7 +76,10 @@ export class GameEventListenerService implements OnModuleInit { handle: (gameId, matches) => { const steamId = new SteamID(matches[5]); if (steamId.isValid()) { - this.gameEventHandlerService.onPlayerDisconnected(gameId, steamId.getSteamID64()); + this.gameEventHandlerService.onPlayerDisconnected( + gameId, + steamId.getSteamID64(), + ); } }, }, @@ -91,7 +99,7 @@ export class GameEventListenerService implements OnModuleInit { handle: (gameId, matches) => { const demoUrl = matches[1]; this.gameEventHandlerService.onDemoUploaded(gameId, demoUrl); - } + }, }, ]; @@ -100,30 +108,43 @@ export class GameEventListenerService implements OnModuleInit { private gameSeversService: GameServersService, private gamesService: GamesService, private logReceiver: LogReceiver, - ) { } + ) {} onModuleInit() { - this.logger.verbose(`listening for incoming logs at ${this.logReceiver.opts.address}:${this.logReceiver.opts.port}`); + this.logger.verbose( + `listening for incoming logs at ${this.logReceiver.opts.address}:${this.logReceiver.opts.port}`, + ); this.logReceiver.on('data', (msg: LogMessage) => { if (msg.isValid) { this.logger.debug(msg.message); - this.testForGameEvent(msg.message, { address: msg.receivedFrom.address, port: msg.receivedFrom.port }); + this.testForGameEvent(msg.message, { + address: msg.receivedFrom.address, + port: msg.receivedFrom.port, + }); } }); } - private async testForGameEvent(message: string, eventSource: { address: string, port: number }) { + private async testForGameEvent( + message: string, + eventSource: { address: string; port: number }, + ) { for (const gameEvent of this.gameEvents) { const matches = message.match(gameEvent.regex); if (matches) { - const gameServer = await this.gameSeversService.getGameServerByEventSource(eventSource); - const game = isRefType(gameServer.game) ? await this.gamesService.getById(gameServer.game) : gameServer.game; - this.logger.debug(`#${game.number}/${gameServer.name}: ${gameEvent.name}`); + const gameServer = await this.gameSeversService.getGameServerByEventSource( + eventSource, + ); + const game = isRefType(gameServer.game) + ? await this.gamesService.getById(gameServer.game) + : gameServer.game; + this.logger.debug( + `#${game.number}/${gameServer.name}: ${gameEvent.name}`, + ); gameEvent.handle(game.id, matches); break; } } } - } diff --git a/src/games/services/game-launcher.service.spec.ts b/src/games/services/game-launcher.service.spec.ts index b06f017d8..a625d27cb 100644 --- a/src/games/services/game-launcher.service.spec.ts +++ b/src/games/services/game-launcher.service.spec.ts @@ -22,8 +22,12 @@ const mockGame = { }; class GamesServiceStub { - getById() { return new Promise(resolve => resolve(mockGame)); } - getOrphanedGames() { return new Promise(resolve => resolve([ mockGame ])); } + getById() { + return new Promise((resolve) => resolve(mockGame)); + } + getOrphanedGames() { + return new Promise((resolve) => resolve([mockGame])); + } } const mockGameServer = { @@ -36,10 +40,14 @@ const mockGameServer = { }; class ServerConfiguratorServiceStub { - configureServer(server: any, game: any) { return new Promise(resolve => resolve({ - connectString: 'FAKE_CONNECT_STRING', - stvConnectString: 'FAKE_STV_CONNECT_STRING', - })); } + configureServer(server: any, game: any) { + return new Promise((resolve) => + resolve({ + connectString: 'FAKE_CONNECT_STRING', + stvConnectString: 'FAKE_STV_CONNECT_STRING', + }), + ); + } } describe('GameLauncherService', () => { @@ -55,7 +63,10 @@ describe('GameLauncherService', () => { GameLauncherService, { provide: GamesService, useClass: GamesServiceStub }, GameServersService, - { provide: ServerConfiguratorService, useClass: ServerConfiguratorServiceStub }, + { + provide: ServerConfiguratorService, + useClass: ServerConfiguratorServiceStub, + }, Events, ConfigurationService, ], @@ -69,7 +80,9 @@ describe('GameLauncherService', () => { }); beforeEach(() => { - gameServersService.assignFreeGameServer.mockResolvedValue(mockGameServer as DocumentType); + gameServersService.assignFreeGameServer.mockResolvedValue( + mockGameServer as DocumentType, + ); }); it('should be defined', () => { @@ -92,21 +105,28 @@ describe('GameLauncherService', () => { }); it('should throw', async () => { - await expect(service.launch('FAKE_GAME_ID')).rejects.toThrowError('no such game'); + await expect(service.launch('FAKE_GAME_ID')).rejects.toThrowError( + 'no such game', + ); }); }); it('should configure the game server', async () => { const spy = jest.spyOn(serverConfiguratorService, 'configureServer'); const ret = await service.launch('FAKE_GAME_ID'); - expect(spy).toHaveBeenCalledWith(expect.objectContaining({ id: mockGameServer.id }), expect.objectContaining({ id: mockGame.id })); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ id: mockGameServer.id }), + expect.objectContaining({ id: mockGame.id }), + ); expect(ret.connectString).toEqual('FAKE_CONNECT_STRING'); expect(ret.stvConnectString).toEqual('FAKE_STV_CONNECT_STRING'); }); it('should setup a valid mumble url', async () => { const ret = await service.launch('FAKE_GAME_ID'); - expect(ret.mumbleUrl).toEqual('mumble://mumble.melkor.tf:64738/tf2pickuppl/FAKE_SERVER_MUMBLE_CHANNEL_NAME'); + expect(ret.mumbleUrl).toEqual( + 'mumble://mumble.melkor.tf:64738/tf2pickuppl/FAKE_SERVER_MUMBLE_CHANNEL_NAME', + ); }); }); diff --git a/src/games/services/game-launcher.service.ts b/src/games/services/game-launcher.service.ts index f93c916f1..3e2a57478 100644 --- a/src/games/services/game-launcher.service.ts +++ b/src/games/services/game-launcher.service.ts @@ -15,7 +15,6 @@ import { ConfigurationService } from '@/configuration/services/configuration.ser */ @Injectable() export class GameLauncherService { - private logger = new Logger(GameLauncherService.name); constructor( @@ -24,7 +23,7 @@ export class GameLauncherService { private serverConfiguratorService: ServerConfiguratorService, private events: Events, private configurationService: ConfigurationService, - ) { } + ) {} /** * Launches the given game. @@ -38,14 +37,23 @@ export class GameLauncherService { throw new Error('no such game'); } - if (game.state === GameState.ended || game.state === GameState.interrupted) { - this.logger.warn(`trying to launch game #${game.number} that has already been ended`); + if ( + game.state === GameState.ended || + game.state === GameState.interrupted + ) { + this.logger.warn( + `trying to launch game #${game.number} that has already been ended`, + ); } try { // step 1: obtain a free server - const gameServer = await this.gameServersService.assignFreeGameServer(game); - this.logger.verbose(`using server ${gameServer.name} for game #${game.number}`); + const gameServer = await this.gameServersService.assignFreeGameServer( + game, + ); + this.logger.verbose( + `using server ${gameServer.name} for game #${game.number}`, + ); // step 2: set mumble url const mumbleUrl = await this.getMumbleUrl(gameServer.mumbleChannelName); @@ -55,7 +63,13 @@ export class GameLauncherService { this.events.gameChanges.next({ game: game.toJSON() }); // step 3: configure server - const { connectString, stvConnectString } = await this.serverConfiguratorService.configureServer(gameServer, game); + const { + connectString, + stvConnectString, + } = await this.serverConfiguratorService.configureServer( + gameServer, + game, + ); game.connectString = connectString; game.stvConnectString = stvConnectString; await game.save(); @@ -81,5 +95,4 @@ export class GameLauncherService { const mumbleOptions = await this.configurationService.getVoiceServer(); return `mumble://${mumbleOptions.url}:${mumbleOptions.port}/${mumbleOptions.channelName}/${gameServerChannelName}`; } - } diff --git a/src/games/services/game-runtime.service.spec.ts b/src/games/services/game-runtime.service.spec.ts index 8bbecfadd..6c94a9754 100644 --- a/src/games/services/game-runtime.service.spec.ts +++ b/src/games/services/game-runtime.service.spec.ts @@ -28,8 +28,12 @@ jest.mock('./rcon-factory.service'); jest.mock('@/players/services/players.service'); class RconStub { - send(cmd: string) { return Promise.resolve(); } - end() { return Promise.resolve(); } + send(cmd: string) { + return Promise.resolve(); + } + end() { + return Promise.resolve(); + } } describe('GameRuntimeService', () => { @@ -45,14 +49,17 @@ describe('GameRuntimeService', () => { let mockGame: DocumentType; let events: Events; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ standardSchemaOptions(Game, removeGameAssignedSkills), Player ]), + TypegooseModule.forFeature([ + standardSchemaOptions(Game, removeGameAssignedSkills), + Player, + ]), ], providers: [ GameRuntimeService, @@ -85,8 +92,12 @@ describe('GameRuntimeService', () => { gameServersService.getById.mockResolvedValue(mockGameServer as any); - // @ts-expect-error - mockPlayers = await Promise.all([ playersService._createOne(), playersService._createOne() ]); + mockPlayers = await Promise.all([ + // @ts-expect-error + playersService._createOne(), + // @ts-expect-error + playersService._createOne(), + ]); // @ts-expect-error mockGame = await gamesService._createOne(mockPlayers); @@ -94,10 +105,11 @@ describe('GameRuntimeService', () => { mockGame.gameServer = new ObjectId(mockGameServer.id); await mockGame.save(); - serverConfiguratorService.configureServer = () => Promise.resolve({ - connectString: 'FAKE_CONNECT_STRING', - stvConnectString: 'FAKE_STV_CONNECT_STRING', - }); + serverConfiguratorService.configureServer = () => + Promise.resolve({ + connectString: 'FAKE_CONNECT_STRING', + stvConnectString: 'FAKE_STV_CONNECT_STRING', + }); }); afterEach(async () => { @@ -115,13 +127,18 @@ describe('GameRuntimeService', () => { it('should configure the server again', async () => { const spy = jest.spyOn(serverConfiguratorService, 'configureServer'); const ret = await service.reconfigure(mockGame.id); - expect(spy).toHaveBeenCalledWith(expect.objectContaining({ id: mockGameServer.id }), expect.objectContaining({ id: mockGame.id })); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ id: mockGameServer.id }), + expect.objectContaining({ id: mockGame.id }), + ); expect(ret.connectString).toEqual('FAKE_CONNECT_STRING'); }); describe('when the given game does not exist', () => { it('should throw an error', async () => { - await expect(service.reconfigure(new ObjectId().toString())).rejects.toThrowError('no such game'); + await expect( + service.reconfigure(new ObjectId().toString()), + ).rejects.toThrowError('no such game'); }); }); @@ -134,13 +151,16 @@ describe('GameRuntimeService', () => { }); it('should throw an error', async () => { - await expect(service.reconfigure(anotherGame.id)).rejects.toThrowError('this game has no server assigned'); + await expect(service.reconfigure(anotherGame.id)).rejects.toThrowError( + 'this game has no server assigned', + ); }); }); describe('when an rcon error occurs', () => { beforeEach(() => { - serverConfiguratorService.configureServer = () => Promise.reject('FAKE_RCON_ERROR'); + serverConfiguratorService.configureServer = () => + Promise.reject('FAKE_RCON_ERROR'); }); it('should handle the error', async () => { @@ -164,31 +184,36 @@ describe('GameRuntimeService', () => { expect(releaseSpy).toHaveBeenCalledWith(mockGameServer.id); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game, adminId }) => { - expect(game.id).toEqual(mockGame.id); - expect(adminId).toEqual('FAKE_ADMIN_ID'); - resolve(); - }); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game, adminId }) => { + expect(game.id).toEqual(mockGame.id); + expect(adminId).toEqual('FAKE_ADMIN_ID'); + resolve(); + }); - service.forceEnd(mockGame.id, 'FAKE_ADMIN_ID'); - })); + service.forceEnd(mockGame.id, 'FAKE_ADMIN_ID'); + })); // eslint-disable-next-line jest/expect-expect - it('should emit the substituteRequestsChange event', async () => new Promise(resolve => { - events.substituteRequestsChange.subscribe(resolve); - service.forceEnd(mockGame.id); - })); + it('should emit the substituteRequestsChange event', async () => + new Promise((resolve) => { + events.substituteRequestsChange.subscribe(resolve); + service.forceEnd(mockGame.id); + })); describe('when the given game does not exist', () => { it('should reject', async () => { - await expect(service.forceEnd(new ObjectId().toString())).rejects.toThrowError('no such game'); + await expect( + service.forceEnd(new ObjectId().toString()), + ).rejects.toThrowError('no such game'); }); }); describe('when an rcon error occurs', () => { beforeEach(() => { - serverConfiguratorService.configureServer = () => Promise.reject('FAKE_RCON_ERROR'); + serverConfiguratorService.configureServer = () => + Promise.reject('FAKE_RCON_ERROR'); }); it('should handle the error', async () => { @@ -224,7 +249,9 @@ describe('GameRuntimeService', () => { }); it('should throw an error', async () => { - await expect(service.replacePlayer('FAKE_GAME_ID', 'FAKE_REPLACEE_ID', null)).rejects.toThrowError('no such game'); + await expect( + service.replacePlayer('FAKE_GAME_ID', 'FAKE_REPLACEE_ID', null), + ).rejects.toThrowError('no such game'); }); }); @@ -237,8 +264,13 @@ describe('GameRuntimeService', () => { }); it('should throw an error', async () => { - await expect(service.replacePlayer(anotherGame.id, mockPlayers[0].id, mockPlayers[1].id)) - .rejects.toThrowError('this game has no server assigned'); + await expect( + service.replacePlayer( + anotherGame.id, + mockPlayers[0].id, + mockPlayers[1].id, + ), + ).rejects.toThrowError('this game has no server assigned'); }); }); @@ -286,7 +318,9 @@ describe('GameRuntimeService', () => { }); it('should throw an error', async () => { - await expect(service.sayChat(mockGameServer.id, 'some message')).rejects.toThrowError('game server does not exist'); + await expect( + service.sayChat(mockGameServer.id, 'some message'), + ).rejects.toThrowError('game server does not exist'); }); }); diff --git a/src/games/services/game-runtime.service.ts b/src/games/services/game-runtime.service.ts index 77211180b..a0d7ee15a 100644 --- a/src/games/services/game-runtime.service.ts +++ b/src/games/services/game-runtime.service.ts @@ -14,7 +14,6 @@ import { GameState } from '../models/game-state'; @Injectable() export class GameRuntimeService { - private logger = new Logger(GameRuntimeService.name); constructor( @@ -22,9 +21,10 @@ export class GameRuntimeService { private gameServersService: GameServersService, private serverConfiguratorService: ServerConfiguratorService, private rconFactoryService: RconFactoryService, - @Inject(forwardRef(() => PlayersService)) private playersService: PlayersService, + @Inject(forwardRef(() => PlayersService)) + private playersService: PlayersService, private events: Events, - ) { } + ) {} async reconfigure(gameId: string) { const game = await this.gamesService.getById(gameId); @@ -42,9 +42,16 @@ export class GameRuntimeService { await game.save(); this.events.gameChanges.next({ game }); - const gameServer = await this.gameServersService.getById(game.gameServer.toString()); + const gameServer = await this.gameServersService.getById( + game.gameServer.toString(), + ); try { - const { connectString } = await this.serverConfiguratorService.configureServer(gameServer, game); + const { + connectString, + } = await this.serverConfiguratorService.configureServer( + gameServer, + game, + ); game.connectString = connectString; await game.save(); this.events.gameChanges.next({ game }); @@ -65,7 +72,9 @@ export class GameRuntimeService { game.state = GameState.interrupted; game.error = 'ended by admin'; - game.slots.filter(s => s.status === SlotStatus.waitingForSubstitute).forEach(s => s.status = SlotStatus.active); + game.slots + .filter((s) => s.status === SlotStatus.waitingForSubstitute) + .forEach((s) => (s.status = SlotStatus.active)); await game.save(); this.events.gameChanges.next({ game, adminId }); this.events.substituteRequestsChange.next(); @@ -77,7 +86,11 @@ export class GameRuntimeService { return game; } - async replacePlayer(gameId: string, replaceeId: string, replacementSlot: GameSlot) { + async replacePlayer( + gameId: string, + replaceeId: string, + replacementSlot: GameSlot, + ) { const game = await this.gamesService.getById(gameId); if (!game) { throw new Error('no such game'); @@ -87,14 +100,23 @@ export class GameRuntimeService { throw new Error('this game has no server assigned'); } - const gameServer = await this.gameServersService.getById(game.gameServer.toString()); + const gameServer = await this.gameServersService.getById( + game.gameServer.toString(), + ); let rcon: Rcon; try { rcon = await this.rconFactoryService.createRcon(gameServer); - const player = isRefType(replacementSlot.player) ? await this.playersService.getById(replacementSlot.player) : replacementSlot.player; - - const cmd = addGamePlayer(player.steamId, player.name, replacementSlot.team, replacementSlot.gameClass); + const player = isRefType(replacementSlot.player) + ? await this.playersService.getById(replacementSlot.player) + : replacementSlot.player; + + const cmd = addGamePlayer( + player.steamId, + player.name, + replacementSlot.team, + replacementSlot.gameClass, + ); this.logger.debug(cmd); await rcon.send(cmd); @@ -103,7 +125,9 @@ export class GameRuntimeService { this.logger.debug(cmd2); await rcon.send(cmd2); } catch (e) { - this.logger.error(`Error replacing the player on the game server: ${e.message}`); + this.logger.error( + `Error replacing the player on the game server: ${e.message}`, + ); } finally { await rcon?.end(); } @@ -137,5 +161,4 @@ export class GameRuntimeService { await rcon?.end(); } } - } diff --git a/src/games/services/games.service.spec.ts b/src/games/services/games.service.spec.ts index f9872492b..7717f5299 100644 --- a/src/games/services/games.service.spec.ts +++ b/src/games/services/games.service.spec.ts @@ -48,7 +48,7 @@ describe('GamesService', () => { let playerSkillService: jest.Mocked; let configurationService: jest.Mocked; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -80,7 +80,7 @@ describe('GamesService', () => { afterEach(async () => { // @ts-expect-error await playersService._reset(); - await gameModel.deleteMany({ }); + await gameModel.deleteMany({}); }); it('should be defined', () => { @@ -100,7 +100,11 @@ describe('GamesService', () => { let game: DocumentType; beforeEach(async () => { - game = await gameModel.create({ number: 1, map: 'cp_badlands', slots: [] }); + game = await gameModel.create({ + number: 1, + map: 'cp_badlands', + slots: [], + }); }); it('should get the game by its id', async () => { @@ -115,15 +119,32 @@ describe('GamesService', () => { let endedGame: DocumentType; beforeEach(async () => { - launchingGame = await gameModel.create({ number: 1, map: 'cp_badlands', state: GameState.launching, slots: [] }); - runningGame = await gameModel.create({ number: 2, map: 'cp_badlands', state: GameState.started, slots: [] }); - endedGame = await gameModel.create({ number: 3, map: 'cp_badlands', state: GameState.ended, slots: [] }); + launchingGame = await gameModel.create({ + number: 1, + map: 'cp_badlands', + state: GameState.launching, + slots: [], + }); + runningGame = await gameModel.create({ + number: 2, + map: 'cp_badlands', + state: GameState.started, + slots: [], + }); + endedGame = await gameModel.create({ + number: 3, + map: 'cp_badlands', + state: GameState.ended, + slots: [], + }); }); it('should get only running games', async () => { const ret = await service.getRunningGames(); expect(ret.length).toEqual(2); - expect(JSON.stringify(ret)).toEqual(JSON.stringify([ launchingGame, runningGame ])); + expect(JSON.stringify(ret)).toEqual( + JSON.stringify([launchingGame, runningGame]), + ); }); }); @@ -192,7 +213,7 @@ describe('GamesService', () => { playerId = new ObjectId().toString(); player2Id = new ObjectId().toString(); - await gameModel.create({ + await gameModel.create({ number: 1, map: 'cp_badlands', state: GameState.started, @@ -224,7 +245,7 @@ describe('GamesService', () => { beforeEach(async () => { playerId = new ObjectId().toString(); - await gameModel.create({ + await gameModel.create({ number: 1, map: 'cp_badlands', state: GameState.ended, @@ -250,38 +271,112 @@ describe('GamesService', () => { beforeEach(async () => { slots = [ - // @ts-expect-error - { id: 0, gameClass: Tf2ClassName.scout, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 1, gameClass: Tf2ClassName.scout, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 2, gameClass: Tf2ClassName.scout, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 3, gameClass: Tf2ClassName.scout, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 4, gameClass: Tf2ClassName.soldier, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 5, gameClass: Tf2ClassName.soldier, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 6, gameClass: Tf2ClassName.soldier, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 7, gameClass: Tf2ClassName.soldier, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 8, gameClass: Tf2ClassName.demoman, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 9, gameClass: Tf2ClassName.demoman, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 10, gameClass: Tf2ClassName.medic, playerId: (await playersService._createOne())._id, ready: true, friend: null }, - // @ts-expect-error - { id: 11, gameClass: Tf2ClassName.medic, playerId: (await playersService._createOne())._id, ready: true, friend: null }, + { + id: 0, + gameClass: Tf2ClassName.scout, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 1, + gameClass: Tf2ClassName.scout, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 2, + gameClass: Tf2ClassName.scout, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 3, + gameClass: Tf2ClassName.scout, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 4, + gameClass: Tf2ClassName.soldier, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 5, + gameClass: Tf2ClassName.soldier, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 6, + gameClass: Tf2ClassName.soldier, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 7, + gameClass: Tf2ClassName.soldier, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 8, + gameClass: Tf2ClassName.demoman, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 9, + gameClass: Tf2ClassName.demoman, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 10, + gameClass: Tf2ClassName.medic, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, + { + id: 11, + gameClass: Tf2ClassName.medic, + // @ts-expect-error + playerId: (await playersService._createOne())._id, + ready: true, + friend: null, + }, ] as any; - configurationService.getDefaultPlayerSkill.mockResolvedValue(new Map([ - [Tf2ClassName.scout, 2], - [Tf2ClassName.soldier, 3], - [Tf2ClassName.demoman, 4], - [Tf2ClassName.medic, 5], - ])); + configurationService.getDefaultPlayerSkill.mockResolvedValue( + new Map([ + [Tf2ClassName.scout, 2], + [Tf2ClassName.soldier, 3], + [Tf2ClassName.demoman, 4], + [Tf2ClassName.medic, 5], + ]), + ); }); describe('when the queue is not full', () => { @@ -291,55 +386,78 @@ describe('GamesService', () => { }); it('should fail', async () => { - await expect(service.create(slots, 'cp_fake')).rejects.toThrow('queue not full'); + await expect(service.create(slots, 'cp_fake')).rejects.toThrow( + 'queue not full', + ); }); }); it('should create a game', async () => { const game = await service.create(slots, 'cp_fake'); - expect(game.toObject()).toEqual(expect.objectContaining({ - number: 1, - map: 'cp_fake', - slots: expect.any(Array), - assignedSkills: expect.any(Object), - state: 'launching', - launchedAt: expect.any(Date), - })); + expect(game.toObject()).toEqual( + expect.objectContaining({ + number: 1, + map: 'cp_fake', + slots: expect.any(Array), + assignedSkills: expect.any(Object), + state: 'launching', + launchedAt: expect.any(Date), + }), + ); }); - it('should emit the gameCreated event', async () => new Promise(resolve => { - events.gameCreated.subscribe(({ game }) => { - expect(game).toMatchObject({ number: 1, map: 'cp_fake', state: 'launching' }); - resolve(); - }); + it('should emit the gameCreated event', async () => + new Promise((resolve) => { + events.gameCreated.subscribe(({ game }) => { + expect(game).toMatchObject({ + number: 1, + map: 'cp_fake', + state: 'launching', + }); + resolve(); + }); - service.create(slots, 'cp_fake'); - })); + service.create(slots, 'cp_fake'); + })); describe('when skill for a player is defined', () => { beforeEach(() => { - playerSkillService.getPlayerSkill.mockImplementation(playerId => { + playerSkillService.getPlayerSkill.mockImplementation((playerId) => { if (playerId === slots[0].playerId) { return Promise.resolve(new Map([[Tf2ClassName.scout, 9]])); } else { return Promise.resolve(null); } - }) + }); }); it('should record the given skill', async () => { const game = await service.create(slots, 'cp_fake'); - expect(game.assignedSkills.get(slots[0].playerId.toString())).toEqual(9); + expect(game.assignedSkills.get(slots[0].playerId.toString())).toEqual( + 9, + ); }); }); describe('when skill for the player is not defined', () => { it('should assign default skill', async () => { const game = await service.create(slots, 'cp_fale'); - const scouts = game.slots.filter(s => s.gameClass === Tf2ClassName.scout); - expect(scouts.every(s => game.assignedSkills.get(s.player.toString()) === 2)).toBe(true); - const soldiers = game.slots.filter(s => s.gameClass === Tf2ClassName.soldier); - expect(soldiers.every(s => game.assignedSkills.get(s.player.toString()) === 3)).toBe(true); + const scouts = game.slots.filter( + (s) => s.gameClass === Tf2ClassName.scout, + ); + expect( + scouts.every( + (s) => game.assignedSkills.get(s.player.toString()) === 2, + ), + ).toBe(true); + const soldiers = game.slots.filter( + (s) => s.gameClass === Tf2ClassName.soldier, + ); + expect( + soldiers.every( + (s) => game.assignedSkills.get(s.player.toString()) === 3, + ), + ).toBe(true); }); }); @@ -403,7 +521,10 @@ describe('GamesService', () => { it('should return the most active players', async () => { const ret = await service.getMostActivePlayers(); - expect(ret).toEqual([{ player: player1.id.toString(), count: 2 }, { player: player2.id.toString(), count: 1 }]); + expect(ret).toEqual([ + { player: player1.id.toString(), count: 2 }, + { player: player2.id.toString(), count: 1 }, + ]); }); }); @@ -442,9 +563,7 @@ describe('GamesService', () => { it('should return games with substitution requests', async () => { const ret = await service.getGamesWithSubstitutionRequests(); - expect(ret).toEqual([ - expect.objectContaining({ id: game.id }), - ]); + expect(ret).toEqual([expect.objectContaining({ id: game.id })]); }); }); diff --git a/src/games/services/games.service.ts b/src/games/services/games.service.ts index c88052634..52e7ad787 100644 --- a/src/games/services/games.service.ts +++ b/src/games/services/games.service.ts @@ -26,18 +26,19 @@ interface GetPlayerGameCountOptions { @Injectable() export class GamesService { - private logger = new Logger(GamesService.name); constructor( @InjectModel(Game) private gameModel: ReturnModelType, - @Inject(forwardRef(() => PlayersService)) private playersService: PlayersService, + @Inject(forwardRef(() => PlayersService)) + private playersService: PlayersService, private playerSkillService: PlayerSkillService, private queueConfigService: QueueConfigService, - @Inject(forwardRef(() => GameLauncherService)) private gameLauncherService: GameLauncherService, + @Inject(forwardRef(() => GameLauncherService)) + private gameLauncherService: GameLauncherService, private events: Events, private configurationService: ConfigurationService, - ) { } + ) {} async getGameCount(): Promise { return await this.gameModel.estimatedDocumentCount(); @@ -51,15 +52,20 @@ export class GamesService { return await this.gameModel.find({ state: /launching|started/ }); } - async getGames(sort: GameSortOptions = { launchedAt: -1 }, limit: number, skip: number) { - return await this.gameModel - .find() - .sort(sort) - .limit(limit) - .skip(skip); + async getGames( + sort: GameSortOptions = { launchedAt: -1 }, + limit: number, + skip: number, + ) { + return await this.gameModel.find().sort(sort).limit(limit).skip(skip); } - async getPlayerGames(playerId: string, sort: GameSortOptions = { launchedAt: -1 }, limit = 10, skip = 0) { + async getPlayerGames( + playerId: string, + sort: GameSortOptions = { launchedAt: -1 }, + limit = 10, + skip = 0, + ) { return await this.gameModel .find({ 'slots.player': new ObjectId(playerId) }) .sort(sort) @@ -67,7 +73,10 @@ export class GamesService { .skip(skip); } - async getPlayerGameCount(playerId: string, options: GetPlayerGameCountOptions = { }) { + async getPlayerGameCount( + playerId: string, + options: GetPlayerGameCountOptions = {}, + ) { const defaultOptions: GetPlayerGameCountOptions = { endedOnly: false }; const _options = { ...defaultOptions, ...options }; @@ -79,15 +88,20 @@ export class GamesService { return await this.gameModel.countDocuments(criteria); } - async getPlayerPlayedClassCount(playerId: string): Promise<{ [gameClass in Tf2ClassName]?: number }> { + async getPlayerPlayedClassCount( + playerId: string, + ): Promise<{ [gameClass in Tf2ClassName]?: number }> { // FIXME store player stats in a separate model to avoid this query - const allGames = await this.gameModel.find({ 'slots.player': new ObjectId(playerId), state: GameState.ended }); + const allGames = await this.gameModel.find({ + 'slots.player': new ObjectId(playerId), + state: GameState.ended, + }); return this.queueConfigService.queueConfig.classes - .map(cls => cls.name) + .map((cls) => cls.name) .reduce((prev, gameClass) => { - prev[gameClass] = allGames - .filter(g => g.findPlayerSlot(playerId)?.gameClass === gameClass) - .length; + prev[gameClass] = allGames.filter( + (g) => g.findPlayerSlot(playerId)?.gameClass === gameClass, + ).length; return prev; }, {}); } @@ -97,22 +111,33 @@ export class GamesService { state: /launching|started/, slots: { $elemMatch: { - status: { $in: [ SlotStatus.active, SlotStatus.waitingForSubstitute ] }, + status: { $in: [SlotStatus.active, SlotStatus.waitingForSubstitute] }, player: playerId, }, }, }); } - async create(queueSlots: QueueSlot[], map: string, friends: string[][] = []): Promise> { - if (!queueSlots.every(slot => !!slot.playerId)) { + async create( + queueSlots: QueueSlot[], + map: string, + friends: string[][] = [], + ): Promise> { + if (!queueSlots.every((slot) => !!slot.playerId)) { throw new Error('queue not full'); } - const players: PlayerSlot[] = await Promise.all(queueSlots.map(slot => this.queueSlotToPlayerSlot(slot))); - const assignedSkills = players.reduce((prev, curr) => { prev[curr.playerId] = curr.skill; return prev; }, { }); - const slots = pickTeams(shuffle(players), { friends }) - .map(s => ({ ...s, player: s.playerId })); + const players: PlayerSlot[] = await Promise.all( + queueSlots.map((slot) => this.queueSlotToPlayerSlot(slot)), + ); + const assignedSkills = players.reduce((prev, curr) => { + prev[curr.playerId] = curr.skill; + return prev; + }, {}); + const slots = pickTeams(shuffle(players), { friends }).map((s) => ({ + ...s, + player: s.playerId, + })); const gameNo = await this.getNextGameNumber(); const game = await this.gameModel.create({ @@ -159,22 +184,22 @@ export class GamesService { } async getGamesWithSubstitutionRequests(): Promise[]> { - return this.gameModel - .find({ - 'state': { $in: [ GameState.launching, GameState.started ] }, - 'slots.status': SlotStatus.waitingForSubstitute, - }); + return this.gameModel.find({ + state: { $in: [GameState.launching, GameState.started] }, + 'slots.status': SlotStatus.waitingForSubstitute, + }); } async getOrphanedGames(): Promise[]> { - return this.gameModel - .find({ - state: GameState.launching, - gameServer: { $exists: false }, - }); + return this.gameModel.find({ + state: GameState.launching, + gameServer: { $exists: false }, + }); } - private async queueSlotToPlayerSlot(queueSlot: QueueSlot): Promise { + private async queueSlotToPlayerSlot( + queueSlot: QueueSlot, + ): Promise { const { playerId, gameClass } = queueSlot; const player = await this.playersService.getById(playerId); if (!player) { @@ -192,12 +217,14 @@ export class GamesService { } private async getNextGameNumber(): Promise { - const latestGame = await this.gameModel.findOne().sort({ launchedAt: -1 }).exec(); + const latestGame = await this.gameModel + .findOne() + .sort({ launchedAt: -1 }) + .exec(); if (latestGame) { return latestGame.number + 1; } else { return 1; } } - } diff --git a/src/games/services/player-substitution.service.spec.ts b/src/games/services/player-substitution.service.spec.ts index 541ee35e4..51a634bb7 100644 --- a/src/games/services/player-substitution.service.spec.ts +++ b/src/games/services/player-substitution.service.spec.ts @@ -48,14 +48,17 @@ describe('PlayerSubstitutionService', () => { let discordService: jest.Mocked; let events: Events; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ Player, standardSchemaOptions(Game, removeGameAssignedSkills) ]), + TypegooseModule.forFeature([ + Player, + standardSchemaOptions(Game, removeGameAssignedSkills), + ]), ], providers: [ PlayerSubstitutionService, @@ -88,7 +91,7 @@ describe('PlayerSubstitutionService', () => { // @ts-expect-error player3 = await playersService._createOne(); // @ts-expect-error - mockGame = await gamesService._createOne([ player1, player2 ]); + mockGame = await gamesService._createOne([player1, player2]); mockGame.gameServer = new ObjectId(); await mockGame.save(); @@ -110,13 +113,17 @@ describe('PlayerSubstitutionService', () => { describe('#substitutePlayer()', () => { describe('when the given game does not exist', () => { it('should throw an error', async () => { - await expect(service.substitutePlayer(new ObjectId().toString(), player1.id)).rejects.toThrowError('no such game'); + await expect( + service.substitutePlayer(new ObjectId().toString(), player1.id), + ).rejects.toThrowError('no such game'); }); }); describe('when the target player does not exist', () => { it('should throw an error', async () => { - await expect(service.substitutePlayer(mockGame.id, new ObjectId().toString())).rejects.toThrowError('no such player'); + await expect( + service.substitutePlayer(mockGame.id, new ObjectId().toString()), + ).rejects.toThrowError('no such player'); }); }); @@ -127,7 +134,9 @@ describe('PlayerSubstitutionService', () => { }); it('should throw an error', async () => { - await expect(service.substitutePlayer(mockGame.id, player1.id)).rejects.toThrowError('this player has already been replaced'); + await expect( + service.substitutePlayer(mockGame.id, player1.id), + ).rejects.toThrowError('this player has already been replaced'); }); }); @@ -138,14 +147,15 @@ describe('PlayerSubstitutionService', () => { expect(slot.status).toEqual(SlotStatus.waitingForSubstitute); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id }); - resolve(); - }); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id }); + resolve(); + }); - service.substitutePlayer(mockGame.id, player1.id); - })); + service.substitutePlayer(mockGame.id, player1.id); + })); describe('when the game has already ended', () => { beforeEach(async () => { @@ -155,21 +165,26 @@ describe('PlayerSubstitutionService', () => { }); it('should reject', async () => { - await expect(service.substitutePlayer(mockGame.id, player1.id)).rejects.toThrowError('the game has already ended'); + await expect( + service.substitutePlayer(mockGame.id, player1.id), + ).rejects.toThrowError('the game has already ended'); }); }); it('should notify on discord', async () => { const spy = jest.spyOn(discordService.getPlayersChannel(), 'send'); await service.substitutePlayer(mockGame.id, player1.id); - expect(spy).toHaveBeenCalledWith('&', { embed: expect.any(Object) }); + expect(spy).toHaveBeenCalledWith('&', { + embed: expect.any(Object), + }); }); // eslint-disable-next-line jest/expect-expect - it('should emit the substituteRequestsChange event', async () => new Promise(resolve => { - events.substituteRequestsChange.subscribe(resolve); - service.substitutePlayer(mockGame.id, player1.id); - })); + it('should emit the substituteRequestsChange event', async () => + new Promise((resolve) => { + events.substituteRequestsChange.subscribe(resolve); + service.substitutePlayer(mockGame.id, player1.id); + })); it('should do nothing if the player is already marked', async () => { const game1 = await service.substitutePlayer(mockGame.id, player1.id); @@ -183,7 +198,7 @@ describe('PlayerSubstitutionService', () => { const channel = { send: () => discordMessage }; beforeEach(async () => { - discordMessage = await discordService.getPlayersChannel().send({ }); + discordMessage = await discordService.getPlayersChannel().send({}); // @ts-expect-error discordService.getPlayersChannel = () => channel; @@ -192,13 +207,23 @@ describe('PlayerSubstitutionService', () => { describe('when the given game does not exist', () => { it('should throw an error', async () => { - await expect(service.cancelSubstitutionRequest(new ObjectId().toString(), player1.id)).rejects.toThrowError('no such game'); + await expect( + service.cancelSubstitutionRequest( + new ObjectId().toString(), + player1.id, + ), + ).rejects.toThrowError('no such game'); }); }); describe('when the given player does not exist', () => { it('should throw an error', async () => { - await expect(service.cancelSubstitutionRequest(mockGame.id, new ObjectId().toString())).rejects.toThrowError('no such player'); + await expect( + service.cancelSubstitutionRequest( + mockGame.id, + new ObjectId().toString(), + ), + ).rejects.toThrowError('no such player'); }); }); @@ -208,24 +233,30 @@ describe('PlayerSubstitutionService', () => { }); it('should throw an error', async () => { - await expect(service.cancelSubstitutionRequest(mockGame.id, player1.id)).rejects.toThrowError('this player has already been replaced'); + await expect( + service.cancelSubstitutionRequest(mockGame.id, player1.id), + ).rejects.toThrowError('this player has already been replaced'); }); }); it('should update the player status', async () => { - const game = await service.cancelSubstitutionRequest(mockGame.id, player1.id); + const game = await service.cancelSubstitutionRequest( + mockGame.id, + player1.id, + ); const slot = game.findPlayerSlot(player1.id); expect(slot.status).toEqual(SlotStatus.active); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id }); - resolve(); - }); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id }); + resolve(); + }); - service.cancelSubstitutionRequest(mockGame.id, player1.id); - })); + service.cancelSubstitutionRequest(mockGame.id, player1.id); + })); describe('when the game is no longer running', () => { beforeEach(async () => { @@ -235,15 +266,18 @@ describe('PlayerSubstitutionService', () => { }); it('should reject', async () => { - await expect(service.cancelSubstitutionRequest(mockGame.id, player1.id)).rejects.toThrowError('the game has already ended'); + await expect( + service.cancelSubstitutionRequest(mockGame.id, player1.id), + ).rejects.toThrowError('the game has already ended'); }); }); // eslint-disable-next-line jest/expect-expect - it('should emit the substituteRequestsChange event', async () => new Promise(resolve => { - events.substituteRequestsChange.subscribe(resolve); - service.cancelSubstitutionRequest(mockGame.id, player1.id); - })); + it('should emit the substituteRequestsChange event', async () => + new Promise((resolve) => { + events.substituteRequestsChange.subscribe(resolve); + service.cancelSubstitutionRequest(mockGame.id, player1.id); + })); it('should get rid of discord announcement', async () => { const spy = jest.spyOn(discordMessage, 'delete'); @@ -257,7 +291,7 @@ describe('PlayerSubstitutionService', () => { const channel = { send: () => discordMessage }; beforeEach(async () => { - discordMessage = await discordService.getPlayersChannel().send({ }); + discordMessage = await discordService.getPlayersChannel().send({}); // @ts-expect-error discordService.getPlayersChannel = () => channel; @@ -266,7 +300,11 @@ describe('PlayerSubstitutionService', () => { it('should replace the player', async () => { // replace player 1 with player 3 - const game = await service.replacePlayer(mockGame.id, player1.id, player3.id); + const game = await service.replacePlayer( + mockGame.id, + player1.id, + player3.id, + ); expect(game.id).toEqual(mockGame.id); const replaceeSlot = game.findPlayerSlot(player1.id); expect(replaceeSlot.status).toEqual(SlotStatus.replaced); @@ -275,47 +313,66 @@ describe('PlayerSubstitutionService', () => { expect(replacementSlot.status).toEqual(SlotStatus.active); }); - it('should emit the gameChanges event', async () => new Promise(resolve => { - events.gameChanges.subscribe(({ game }) => { - expect(game).toMatchObject({ id: mockGame.id }); - resolve(); - }); - - service.replacePlayer(mockGame.id, player1.id, player3.id); - })); - - it('should replace the player in-game', async () => new Promise(resolve => { - setTimeout(() => { - expect(gameRuntimeService.replacePlayer).toHaveBeenCalledWith(mockGame.id, player1.id, expect.objectContaining({ player: player3._id })); - resolve(); - }, 100); - service.replacePlayer(mockGame.id, player1.id, player3.id); - })); + it('should emit the gameChanges event', async () => + new Promise((resolve) => { + events.gameChanges.subscribe(({ game }) => { + expect(game).toMatchObject({ id: mockGame.id }); + resolve(); + }); + + service.replacePlayer(mockGame.id, player1.id, player3.id); + })); + + it('should replace the player in-game', async () => + new Promise((resolve) => { + setTimeout(() => { + expect(gameRuntimeService.replacePlayer).toHaveBeenCalledWith( + mockGame.id, + player1.id, + expect.objectContaining({ player: player3._id }), + ); + resolve(); + }, 100); + service.replacePlayer(mockGame.id, player1.id, player3.id); + })); describe('when the replacement player is banned', () => { beforeEach(() => { const end = new Date(); end.setHours(end.getHours() + 1); - playerBansService.getPlayerActiveBans = () => Promise.resolve([ - { player: 'FAKE_PLAYERID', admin: 'FAKE_ADMINID', start: new Date(), end } as any, - ]); + playerBansService.getPlayerActiveBans = () => + Promise.resolve([ + { + player: 'FAKE_PLAYERID', + admin: 'FAKE_ADMINID', + start: new Date(), + end, + } as any, + ]); }); it('should reject', async () => { - await expect(service.replacePlayer(mockGame.id, player1.id, player3.id)).rejects.toThrowError('player is banned'); + await expect( + service.replacePlayer(mockGame.id, player1.id, player3.id), + ).rejects.toThrowError('player is banned'); }); }); describe('when replacing an active player', () => { it('should reject', async () => { - await expect(service.replacePlayer(mockGame.id, player2.id, player3.id)) - .rejects.toThrowError(); - }) + await expect( + service.replacePlayer(mockGame.id, player2.id, player3.id), + ).rejects.toThrowError(); + }); }); describe('when a player is subbing himself', () => { it('should mark the slot back as active', async () => { - const game = await service.replacePlayer(mockGame.id, player1.id, player1.id); + const game = await service.replacePlayer( + mockGame.id, + player1.id, + player1.id, + ); expect(game.id).toEqual(mockGame.id); const slot = game.findPlayerSlot(player1.id); expect(slot.status).toBe(SlotStatus.active); @@ -335,31 +392,42 @@ describe('PlayerSubstitutionService', () => { }); it('should reject', async () => { - await expect(service.replacePlayer(mockGame.id, player1.id, player3.id)).rejects - .toThrowError(); + await expect( + service.replacePlayer(mockGame.id, player1.id, player3.id), + ).rejects.toThrowError(); }); }); describe('when the given player is involved in another game', () => { beforeEach(async () => { - gamesService.getPlayerActiveGame = () => Promise.resolve({ id: new ObjectId().toString() } as any); + gamesService.getPlayerActiveGame = () => + Promise.resolve({ id: new ObjectId().toString() } as any); }); it('should reject', async () => { - await expect(service.replacePlayer(mockGame.id, player1.id, player3.id)).rejects - .toThrowError('player is involved in a currently running game'); + await expect( + service.replacePlayer(mockGame.id, player1.id, player3.id), + ).rejects.toThrowError( + 'player is involved in a currently running game', + ); }); }); // eslint-disable-next-line jest/expect-expect - it('should emit the substituteRequestsChange event', async () => new Promise(resolve => { - events.substituteRequestsChange.subscribe(resolve); - service.replacePlayer(mockGame.id, player1.id, player3.id); - })); + it('should emit the substituteRequestsChange event', async () => + new Promise((resolve) => { + events.substituteRequestsChange.subscribe(resolve); + service.replacePlayer(mockGame.id, player1.id, player3.id); + })); it('should reject if the replacement player does not exist', async () => { - await expect(service.replacePlayer(mockGame.id, player1.id, new ObjectId().toString())).rejects - .toThrow(mongoose.Error.DocumentNotFoundError); + await expect( + service.replacePlayer( + mockGame.id, + player1.id, + new ObjectId().toString(), + ), + ).rejects.toThrow(mongoose.Error.DocumentNotFoundError); }); it('should kick the replacement player from the queue', async () => { @@ -369,7 +437,10 @@ describe('PlayerSubstitutionService', () => { it('should announce the replacement in the game', async () => { await service.replacePlayer(mockGame.id, player1.id, player3.id); - expect(gameRuntimeService.sayChat).toHaveBeenCalledWith(mockGame.gameServer.toString(), 'fake_player_3 is replacing fake_player_1 on soldier.'); + expect(gameRuntimeService.sayChat).toHaveBeenCalledWith( + mockGame.gameServer.toString(), + 'fake_player_3 is replacing fake_player_1 on soldier.', + ); }); it('should delete the discord announcement', async () => { @@ -384,10 +455,18 @@ describe('PlayerSubstitutionService', () => { await service.substitutePlayer(mockGame.id, player2.id); }); - it('player1 should be able to take player2\'s slot', async () => { - const game = await service.replacePlayer(mockGame.id, player2.id, player1.id); - expect(game.slots.filter(s => s.player.toString().localeCompare(player1.id) == 0).length).toEqual(2); - }) + it("player1 should be able to take player2's slot", async () => { + const game = await service.replacePlayer( + mockGame.id, + player2.id, + player1.id, + ); + expect( + game.slots.filter( + (s) => s.player.toString().localeCompare(player1.id) == 0, + ).length, + ).toEqual(2); + }); }); }); }); diff --git a/src/games/services/player-substitution.service.ts b/src/games/services/player-substitution.service.ts index 5159a318b..1ad128f2e 100644 --- a/src/games/services/player-substitution.service.ts +++ b/src/games/services/player-substitution.service.ts @@ -1,4 +1,10 @@ -import { Injectable, Logger, Inject, forwardRef, Optional } from '@nestjs/common'; +import { + Injectable, + Logger, + Inject, + forwardRef, + Optional, +} from '@nestjs/common'; import { GamesService } from './games.service'; import { PlayersService } from '@/players/services/players.service'; import { PlayerBansService } from '@/players/services/player-bans.service'; @@ -20,21 +26,24 @@ import { mongoose } from '@typegoose/typegoose'; */ @Injectable() export class PlayerSubstitutionService { - private logger = new Logger(PlayerSubstitutionService.name); private discordNotifications = new Map(); // playerId <-> message pairs constructor( @Inject(forwardRef(() => GamesService)) private gamesService: GamesService, - @Inject(forwardRef(() => PlayersService)) private playersService: PlayersService, + @Inject(forwardRef(() => PlayersService)) + private playersService: PlayersService, private playerBansService: PlayerBansService, - @Inject(forwardRef(() => GameRuntimeService)) private gameRuntimeService: GameRuntimeService, + @Inject(forwardRef(() => GameRuntimeService)) + private gameRuntimeService: GameRuntimeService, private queueSevice: QueueService, @Optional() private discordService: DiscordService, private environment: Environment, private events: Events, ) { - this.logger.verbose(`Discord plugin will ${this.discordService ? '' : 'not '}be used`); + this.logger.verbose( + `Discord plugin will ${this.discordService ? '' : 'not '}be used`, + ); } async substitutePlayer(gameId: string, playerId: string) { @@ -53,7 +62,9 @@ export class PlayerSubstitutionService { } const player = await this.playersService.getById(playerId); - this.logger.debug(`player ${player.name} taking part in game #${game.number} is marked as 'waiting for substitute'`); + this.logger.debug( + `player ${player.name} taking part in game #${game.number} is marked as 'waiting for substitute'`, + ); slot.status = SlotStatus.waitingForSubstitute; await game.save(); @@ -69,7 +80,9 @@ export class PlayerSubstitutionService { gameUrl: `${this.environment.clientUrl}/game/${game.id}`, }); - const roleToMention = this.discordService.findRole(this.environment.discordQueueNotificationsMentionRole); + const roleToMention = this.discordService.findRole( + this.environment.discordQueueNotificationsMentionRole, + ); let message: Message; if (roleToMention?.mentionable) { @@ -100,7 +113,9 @@ export class PlayerSubstitutionService { } const player = await this.playersService.getById(playerId); - this.logger.verbose(`player ${player.name} taking part in game #${game.number} is marked as 'active'`); + this.logger.verbose( + `player ${player.name} taking part in game #${game.number} is marked as 'active'`, + ); slot.status = SlotStatus.active; await game.save(); @@ -116,10 +131,17 @@ export class PlayerSubstitutionService { return game; } - async replacePlayer(gameId: string, replaceeId: string, replacementId: string) { + async replacePlayer( + gameId: string, + replaceeId: string, + replacementId: string, + ) { const replacement = await this.playersService.getById(replacementId); - if ((await this.playerBansService.getPlayerActiveBans(replacementId)).length > 0) { + if ( + (await this.playerBansService.getPlayerActiveBans(replacementId)).length > + 0 + ) { throw new Error('player is banned'); } @@ -128,7 +150,11 @@ export class PlayerSubstitutionService { throw new Error('no such game'); } - const slot = game.slots.find(slot => slot.status === SlotStatus.waitingForSubstitute && slot.player.toString().localeCompare(replaceeId) === 0); + const slot = game.slots.find( + (slot) => + slot.status === SlotStatus.waitingForSubstitute && + slot.player.toString().localeCompare(replaceeId) === 0, + ); if (!slot) { throw new Error(`no such slot (playerId: ${replaceeId})`); } @@ -172,9 +198,17 @@ export class PlayerSubstitutionService { await this.deleteDiscordAnnouncement(replaceeId); - this.logger.verbose(`player ${replacement.name} is replacing ${replacee.name} on ${replacementSlot.gameClass} in game #${game.number}`); + this.logger.verbose( + `player ${replacement.name} is replacing ${replacee.name} on ${replacementSlot.gameClass} in game #${game.number}`, + ); - setImmediate(() => this.gameRuntimeService.replacePlayer(game.id, replaceeId, replacementSlot)); + setImmediate(() => + this.gameRuntimeService.replacePlayer( + game.id, + replaceeId, + replacementSlot, + ), + ); return game; } @@ -199,5 +233,4 @@ export class PlayerSubstitutionService { this.discordNotifications.delete(replaceeId); } } - } diff --git a/src/games/services/rcon-factory.service.ts b/src/games/services/rcon-factory.service.ts index 842744c34..30ba2a0f3 100644 --- a/src/games/services/rcon-factory.service.ts +++ b/src/games/services/rcon-factory.service.ts @@ -4,7 +4,6 @@ import { Rcon } from 'rcon-client'; @Injectable() export class RconFactoryService { - async createRcon(gameServer: GameServer): Promise { return new Promise((resolve, reject) => { const rcon = new Rcon({ @@ -14,12 +13,11 @@ export class RconFactoryService { timeout: 30000, }); - rcon.on('error', error => { + rcon.on('error', (error) => { return reject(error); }); rcon.connect().then(resolve).catch(reject); }); } - } diff --git a/src/games/services/server-configurator.service.spec.ts b/src/games/services/server-configurator.service.spec.ts index a8976c443..5deeb5ab9 100644 --- a/src/games/services/server-configurator.service.spec.ts +++ b/src/games/services/server-configurator.service.spec.ts @@ -4,8 +4,20 @@ import { Environment } from '@/environment/environment'; import { PlayersService } from '@/players/services/players.service'; import { QueueConfigService } from '@/queue/services/queue-config.service'; import { RconFactoryService } from './rcon-factory.service'; -import { logAddressAdd, kickAll, changelevel, execConfig, addGamePlayer, enablePlayerWhitelist, tvPort, tvPassword, - logAddressDel, delAllGamePlayers, disablePlayerWhitelist, tftrueWhitelistId } from '../utils/rcon-commands'; +import { + logAddressAdd, + kickAll, + changelevel, + execConfig, + addGamePlayer, + enablePlayerWhitelist, + tvPort, + tvPassword, + logAddressDel, + delAllGamePlayers, + disablePlayerWhitelist, + tftrueWhitelistId, +} from '../utils/rcon-commands'; import { QueueConfig } from '@/queue/queue-config'; import { Tf2Team } from '../models/tf2-team'; import { Game } from '../models/game'; @@ -32,9 +44,7 @@ class EnvironmentStub { class QueueConfigServiceStub { queueConfig = { - maps: [ - { name: 'cp_badlands', configName: '5cp' }, - ], + maps: [{ name: 'cp_badlands', configName: '5cp' }], configs: { '5cp': 'etf2l_6v6_5cp', }, @@ -60,14 +70,14 @@ describe('ServerConfiguratorService', () => { let mapPoolService: jest.Mocked; let configurationService: jest.Mocked; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ Player ]), + TypegooseModule.forFeature([Player]), ], providers: [ ServerConfiguratorService, @@ -107,8 +117,12 @@ describe('ServerConfiguratorService', () => { let game: Game; beforeEach(async () => { - // @ts-expect-error - [ player1, player2 ] = [ await playersService._createOne(), await playersService._createOne() ]; + [player1, player2] = [ + // @ts-expect-error + await playersService._createOne(), + // @ts-expect-error + await playersService._createOne(), + ]; game = new Game(); game.launchedAt = new Date(); @@ -130,9 +144,14 @@ describe('ServerConfiguratorService', () => { game.map = 'cp_badlands'; rcon = new RconStub(); - rconFactoryService.createRcon.mockResolvedValue(rcon as unknown as Rcon); + rconFactoryService.createRcon.mockResolvedValue( + (rcon as unknown) as Rcon, + ); jest.useFakeTimers(); - jest.spyOn(global, 'setTimeout').mockImplementation((cb: () => void) => { cb(); return null; }); + jest.spyOn(global, 'setTimeout').mockImplementation((cb: () => void) => { + cb(); + return null; + }); }); afterEach(async () => { @@ -144,13 +163,31 @@ describe('ServerConfiguratorService', () => { it('should execute correct rcon commands', async () => { await service.configureServer(gameServer as any, game); - expect(rcon.send).toHaveBeenCalledWith(logAddressAdd('FAKE_RELAY_ADDRESS:1234')); + expect(rcon.send).toHaveBeenCalledWith( + logAddressAdd('FAKE_RELAY_ADDRESS:1234'), + ); expect(rcon.send).toHaveBeenCalledWith(kickAll()); expect(rcon.send).toHaveBeenCalledWith(changelevel('cp_badlands')); expect(rcon.send).toHaveBeenCalledWith(execConfig('etf2l_6v6_5cp')); - expect(rcon.send).toHaveBeenCalledWith(expect.stringMatching(/^sv_password\s.+$/)); - expect(rcon.send).toHaveBeenCalledWith(addGamePlayer(player1.steamId, player1.name, Tf2Team.blu, Tf2ClassName.soldier)); - expect(rcon.send).toHaveBeenCalledWith(addGamePlayer(player2.steamId, player2.name, Tf2Team.red, Tf2ClassName.soldier)); + expect(rcon.send).toHaveBeenCalledWith( + expect.stringMatching(/^sv_password\s.+$/), + ); + expect(rcon.send).toHaveBeenCalledWith( + addGamePlayer( + player1.steamId, + player1.name, + Tf2Team.blu, + Tf2ClassName.soldier, + ), + ); + expect(rcon.send).toHaveBeenCalledWith( + addGamePlayer( + player2.steamId, + player2.name, + Tf2Team.red, + Tf2ClassName.soldier, + ), + ); expect(rcon.send).toHaveBeenCalledWith(enablePlayerWhitelist()); expect(rcon.send).toHaveBeenCalledWith(tvPort()); expect(rcon.send).toHaveBeenCalledWith(tvPassword()); @@ -158,12 +195,16 @@ describe('ServerConfiguratorService', () => { describe('when the whitelistId is set', () => { beforeEach(() => { - configurationService.getWhitelistId.mockResolvedValue('FAKE_WHITELIST_ID'); + configurationService.getWhitelistId.mockResolvedValue( + 'FAKE_WHITELIST_ID', + ); }); it('should set the whitelist', async () => { await service.configureServer(gameServer as any, game); - expect(rcon.send).toHaveBeenCalledWith(tftrueWhitelistId('FAKE_WHITELIST_ID')); + expect(rcon.send).toHaveBeenCalledWith( + tftrueWhitelistId('FAKE_WHITELIST_ID'), + ); }); }); @@ -188,8 +229,22 @@ describe('ServerConfiguratorService', () => { it('should not add this player to the game', async () => { await service.configureServer(gameServer as any, game); - expect(rcon.send).toHaveBeenCalledWith(addGamePlayer(player1.steamId, player1.name, Tf2Team.blu, Tf2ClassName.soldier)); - expect(rcon.send).not.toHaveBeenCalledWith(addGamePlayer(player2.steamId, player2.name, Tf2Team.red, Tf2ClassName.soldier)); + expect(rcon.send).toHaveBeenCalledWith( + addGamePlayer( + player1.steamId, + player1.name, + Tf2Team.blu, + Tf2ClassName.soldier, + ), + ); + expect(rcon.send).not.toHaveBeenCalledWith( + addGamePlayer( + player2.steamId, + player2.name, + Tf2Team.red, + Tf2ClassName.soldier, + ), + ); }); }); @@ -198,18 +253,25 @@ describe('ServerConfiguratorService', () => { expect(rcon.end).toHaveBeenCalled(); }); - describe('when player\'s name contains non-english characters', () => { + describe("when player's name contains non-english characters", () => { beforeEach(async () => { jest.useRealTimers(); player1.name = 'mąły'; await player1.save(); jest.useFakeTimers(); - jest.spyOn(global, 'setTimeout').mockImplementation((cb: () => void) => { cb(); return null; }); + jest + .spyOn(global, 'setTimeout') + .mockImplementation((cb: () => void) => { + cb(); + return null; + }); }); it('should deburr player nicknames', async () => { await service.configureServer(gameServer as any, game as any); - expect(rcon.send).toHaveBeenCalledWith(addGamePlayer(player1.steamId, 'maly', Tf2Team.blu, 'soldier')); + expect(rcon.send).toHaveBeenCalledWith( + addGamePlayer(player1.steamId, 'maly', Tf2Team.blu, 'soldier'), + ); }); }); @@ -219,7 +281,9 @@ describe('ServerConfiguratorService', () => { }); it('should close the rcon connection even though an RCON command failed', async () => { - await expect(service.configureServer(gameServer as any, game as any)).rejects.toThrowError(); + await expect( + service.configureServer(gameServer as any, game as any), + ).rejects.toThrowError(); expect(rcon.end).toHaveBeenCalled(); }); }); @@ -236,7 +300,9 @@ describe('ServerConfiguratorService', () => { it('should execute correct rcon commands', async () => { await service.cleanupServer(gameServer as any); - expect(rcon.send).toHaveBeenCalledWith(logAddressDel('FAKE_RELAY_ADDRESS:1234')); + expect(rcon.send).toHaveBeenCalledWith( + logAddressDel('FAKE_RELAY_ADDRESS:1234'), + ); expect(rcon.send).toHaveBeenCalledWith(delAllGamePlayers()); expect(rcon.send).toHaveBeenCalledWith(disablePlayerWhitelist()); }); @@ -249,7 +315,9 @@ describe('ServerConfiguratorService', () => { it('should close the rcon connection even though an RCON command failed', async () => { rcon.send.mockRejectedValue('some random RCON error'); - await expect(service.cleanupServer(gameServer as any)).rejects.toThrowError(); + await expect( + service.cleanupServer(gameServer as any), + ).rejects.toThrowError(); expect(rcon.end).toHaveBeenCalled(); }); }); diff --git a/src/games/services/server-configurator.service.ts b/src/games/services/server-configurator.service.ts index ee69ce2ad..154bf03ab 100644 --- a/src/games/services/server-configurator.service.ts +++ b/src/games/services/server-configurator.service.ts @@ -6,8 +6,21 @@ import { generate } from 'generate-password'; import { PlayersService } from '@/players/services/players.service'; import { QueueConfigService } from '@/queue/services/queue-config.service'; import { RconFactoryService } from './rcon-factory.service'; -import { logAddressAdd, changelevel, execConfig, setPassword, addGamePlayer, logAddressDel, delAllGamePlayers, - kickAll, enablePlayerWhitelist, disablePlayerWhitelist, tvPort, tvPassword, tftrueWhitelistId } from '../utils/rcon-commands'; +import { + logAddressAdd, + changelevel, + execConfig, + setPassword, + addGamePlayer, + logAddressDel, + delAllGamePlayers, + kickAll, + enablePlayerWhitelist, + disablePlayerWhitelist, + tvPort, + tvPassword, + tftrueWhitelistId, +} from '../utils/rcon-commands'; import { deburr } from 'lodash'; import { extractConVarValue } from '../utils/extract-con-var-value'; import { Rcon } from 'rcon-client/lib'; @@ -15,25 +28,27 @@ import { isRefType } from '@typegoose/typegoose'; import { MapPoolService } from '@/queue/services/map-pool.service'; import { ConfigurationService } from '@/configuration/services/configuration.service'; -const wait = () => new Promise(resolve => setTimeout(resolve, 10 * 1000)); +const wait = () => new Promise((resolve) => setTimeout(resolve, 10 * 1000)); @Injectable() export class ServerConfiguratorService { - private logger = new Logger(ServerConfiguratorService.name); constructor( private environment: Environment, - @Inject(forwardRef(() => PlayersService)) private playersService: PlayersService, + @Inject(forwardRef(() => PlayersService)) + private playersService: PlayersService, private queueConfigService: QueueConfigService, private rconFactoryService: RconFactoryService, private mapPoolService: MapPoolService, private configurationService: ConfigurationService, - ) { } + ) {} async configureServer(server: GameServer, game: Game) { this.logger.verbose(`configuring server ${server.name}...`); - this.logger.debug(`[${server.name}] using rcon password ${server.rconPassword}`); + this.logger.debug( + `[${server.name}] using rcon password ${server.rconPassword}`, + ); const whitelistId = await this.configurationService.getWhitelistId(); let rcon: Rcon; @@ -53,7 +68,7 @@ export class ServerConfiguratorService { await wait(); const maps = await this.mapPoolService.getMaps(); - const config = maps.find(m => m.name === game.map)?.execConfig; + const config = maps.find((m) => m.name === game.map)?.execConfig; if (config) { this.logger.debug(`[${server.name}] executing ${config}...`); await rcon.send(execConfig(config)); @@ -61,7 +76,9 @@ export class ServerConfiguratorService { } if (whitelistId) { - this.logger.debug(`[${server.name}] setting whitelist ${whitelistId}...`); + this.logger.debug( + `[${server.name}] setting whitelist ${whitelistId}...`, + ); await rcon.send(tftrueWhitelistId(whitelistId)); } @@ -70,10 +87,17 @@ export class ServerConfiguratorService { await rcon.send(setPassword(password)); for (const slot of game.activeSlots()) { - const player = isRefType(slot.player) ? await this.playersService.getById(slot.player) : slot.player; + const player = isRefType(slot.player) + ? await this.playersService.getById(slot.player) + : slot.player; const playerName = deburr(player.name); - const cmd = addGamePlayer(player.steamId, playerName, slot.team, slot.gameClass); + const cmd = addGamePlayer( + player.steamId, + playerName, + slot.team, + slot.gameClass, + ); this.logger.debug(`[${server.name}] ${cmd}`); await rcon.send(cmd); } @@ -99,7 +123,9 @@ export class ServerConfiguratorService { stvConnectString, }; } catch (error) { - throw new Error(`could not configure server ${server.name} (${error.message})`); + throw new Error( + `could not configure server ${server.name} (${error.message})`, + ); } finally { await rcon?.end(); } @@ -111,16 +137,19 @@ export class ServerConfiguratorService { rcon = await this.rconFactoryService.createRcon(server); const logAddress = `${this.environment.logRelayAddress}:${this.environment.logRelayPort}`; - this.logger.debug(`[${server.name}] removing log address ${logAddress}...`); + this.logger.debug( + `[${server.name}] removing log address ${logAddress}...`, + ); await rcon.send(logAddressDel(logAddress)); await rcon.send(delAllGamePlayers()); await rcon.send(disablePlayerWhitelist()); this.logger.verbose(`[${server.name}] server cleaned up`); } catch (error) { - throw new Error(`could not cleanup server ${server.name} (${error.message})`); + throw new Error( + `could not cleanup server ${server.name} (${error.message})`, + ); } finally { await rcon?.end(); } } - } diff --git a/src/games/utils/extract-con-var-value.spec.ts b/src/games/utils/extract-con-var-value.spec.ts index d6921126a..35a01d3f4 100644 --- a/src/games/utils/extract-con-var-value.spec.ts +++ b/src/games/utils/extract-con-var-value.spec.ts @@ -2,21 +2,27 @@ import { extractConVarValue } from './extract-con-var-value'; describe('extractConVarValue()', () => { it('should parse the rcon response when the values are sane', () => { - expect(extractConVarValue(`"sv_password" = "some password" ( def. "" ) + expect( + extractConVarValue(`"sv_password" = "some password" ( def. "" ) notify - - Server password for entry into multiplayer games`)).toEqual('some password'); + - Server password for entry into multiplayer games`), + ).toEqual('some password'); }); it('should handle empty values', () => { - expect(extractConVarValue(`"tv_password" = "" + expect( + extractConVarValue(`"tv_password" = "" notify - - SourceTV password for all clients`)).toEqual(''); + - SourceTV password for all clients`), + ).toEqual(''); }); it('should handle non-cvar responses', () => { - expect(extractConVarValue(` + expect( + extractConVarValue(` 0:2:"melkor TV" - 1 users`)).toBeUndefined(); + 1 users`), + ).toBeUndefined(); }); it('should handle undefined', () => { diff --git a/src/games/utils/extract-con-var-value.ts b/src/games/utils/extract-con-var-value.ts index f6f6fb09e..8b94f3a65 100644 --- a/src/games/utils/extract-con-var-value.ts +++ b/src/games/utils/extract-con-var-value.ts @@ -5,9 +5,11 @@ * @param rconResponse The plain rcon response */ export function extractConVarValue(rconResponse: string): string { - return rconResponse - ?.split(/\r?\n/)[0] - // https://regex101.com/r/jeIrq2/1 - ?.match(/^"(.[^"]*)"\s=\s"(.*)"(\s\(\s?def\.\s"(.*)"\s?\))?$/)?.[2] - ?.toString(); + return ( + rconResponse + ?.split(/\r?\n/)[0] + // https://regex101.com/r/jeIrq2/1 + ?.match(/^"(.[^"]*)"\s=\s"(.*)"(\s\(\s?def\.\s"(.*)"\s?\))?$/)?.[2] + ?.toString() + ); } diff --git a/src/games/utils/pick-teams.spec.ts b/src/games/utils/pick-teams.spec.ts index 58fa4a2fa..9c819bd46 100644 --- a/src/games/utils/pick-teams.spec.ts +++ b/src/games/utils/pick-teams.spec.ts @@ -12,10 +12,30 @@ describe('pickTeams', () => { it('should pick teams', () => { expect(pickTeams(playerIds)).toEqual([ - { playerId: 'a', gameClass: Tf2ClassName.soldier, skill: 1, team: 'blu' }, - { playerId: 'd', gameClass: Tf2ClassName.soldier, skill: 4, team: 'blu' }, - { playerId: 'b', gameClass: Tf2ClassName.soldier, skill: 2, team: 'red' }, - { playerId: 'c', gameClass: Tf2ClassName.soldier, skill: 3, team: 'red' }, + { + playerId: 'a', + gameClass: Tf2ClassName.soldier, + skill: 1, + team: 'blu', + }, + { + playerId: 'd', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'blu', + }, + { + playerId: 'b', + gameClass: Tf2ClassName.soldier, + skill: 2, + team: 'red', + }, + { + playerId: 'c', + gameClass: Tf2ClassName.soldier, + skill: 3, + team: 'red', + }, ]); }); @@ -23,33 +43,71 @@ describe('pickTeams', () => { describe('having all the friends valid', () => { const overrides: TeamOverrides = { friends: [ - [ 'a', 'c' ], // 'a' and 'c' will be in the same team + ['a', 'c'], // 'a' and 'c' will be in the same team ], }; it('should pick teams', () => { expect(pickTeams(playerIds, overrides)).toEqual([ - { playerId: 'a', gameClass: Tf2ClassName.soldier, skill: 1, team: 'blu' }, - { playerId: 'c', gameClass: Tf2ClassName.soldier, skill: 3, team: 'blu' }, - { playerId: 'b', gameClass: Tf2ClassName.soldier, skill: 2, team: 'red' }, - { playerId: 'd', gameClass: Tf2ClassName.soldier, skill: 4, team: 'red' }, + { + playerId: 'a', + gameClass: Tf2ClassName.soldier, + skill: 1, + team: 'blu', + }, + { + playerId: 'c', + gameClass: Tf2ClassName.soldier, + skill: 3, + team: 'blu', + }, + { + playerId: 'b', + gameClass: Tf2ClassName.soldier, + skill: 2, + team: 'red', + }, + { + playerId: 'd', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'red', + }, ]); }); }); describe('missing one friend', () => { const overrides: TeamOverrides = { - friends: [ - [ 'a', 'e' ], - ], + friends: [['a', 'e']], }; it('should pick teams', () => { expect(pickTeams(playerIds, overrides)).toEqual([ - { playerId: 'a', gameClass: Tf2ClassName.soldier, skill: 1, team: 'blu' }, - { playerId: 'd', gameClass: Tf2ClassName.soldier, skill: 4, team: 'blu' }, - { playerId: 'b', gameClass: Tf2ClassName.soldier, skill: 2, team: 'red' }, - { playerId: 'c', gameClass: Tf2ClassName.soldier, skill: 3, team: 'red' }, + { + playerId: 'a', + gameClass: Tf2ClassName.soldier, + skill: 1, + team: 'blu', + }, + { + playerId: 'd', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'blu', + }, + { + playerId: 'b', + gameClass: Tf2ClassName.soldier, + skill: 2, + team: 'red', + }, + { + playerId: 'c', + gameClass: Tf2ClassName.soldier, + skill: 3, + team: 'red', + }, ]); }); }); @@ -76,15 +134,45 @@ describe('pickTeams', () => { expect(pickTeams(playerIds)).toEqual([ { playerId: 'a', gameClass: Tf2ClassName.scout, skill: 3, team: 'blu' }, { playerId: 'b', gameClass: Tf2ClassName.scout, skill: 2, team: 'blu' }, - { playerId: 'e', gameClass: Tf2ClassName.soldier, skill: 4, team: 'blu' }, - { playerId: 'f', gameClass: Tf2ClassName.soldier, skill: 4, team: 'blu' }, - { playerId: 'i', gameClass: Tf2ClassName.demoman, skill: 1, team: 'blu' }, + { + playerId: 'e', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'blu', + }, + { + playerId: 'f', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'blu', + }, + { + playerId: 'i', + gameClass: Tf2ClassName.demoman, + skill: 1, + team: 'blu', + }, { playerId: 'l', gameClass: Tf2ClassName.medic, skill: 4, team: 'blu' }, { playerId: 'c', gameClass: Tf2ClassName.scout, skill: 2, team: 'red' }, { playerId: 'd', gameClass: Tf2ClassName.scout, skill: 2, team: 'red' }, - { playerId: 'g', gameClass: Tf2ClassName.soldier, skill: 5, team: 'red' }, - { playerId: 'h', gameClass: Tf2ClassName.soldier, skill: 4, team: 'red' }, - { playerId: 'j', gameClass: Tf2ClassName.demoman, skill: 3, team: 'red' }, + { + playerId: 'g', + gameClass: Tf2ClassName.soldier, + skill: 5, + team: 'red', + }, + { + playerId: 'h', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'red', + }, + { + playerId: 'j', + gameClass: Tf2ClassName.demoman, + skill: 3, + team: 'red', + }, { playerId: 'k', gameClass: Tf2ClassName.medic, skill: 2, team: 'red' }, ]); }); @@ -92,26 +180,86 @@ describe('pickTeams', () => { describe('with friends', () => { const overrides: TeamOverrides = { friends: [ - [ 'k', 'i' ], - [ 'l', 'g' ], + ['k', 'i'], + ['l', 'g'], ], }; it('should pick teams', () => { expect(pickTeams(playerIds, overrides)).toEqual([ - { playerId: 'a', gameClass: Tf2ClassName.scout, skill: 3, team: 'blu' }, - { playerId: 'b', gameClass: Tf2ClassName.scout, skill: 2, team: 'blu' }, - { playerId: 'e', gameClass: Tf2ClassName.soldier, skill: 4, team: 'blu' }, - { playerId: 'f', gameClass: Tf2ClassName.soldier, skill: 4, team: 'blu' }, - { playerId: 'i', gameClass: Tf2ClassName.demoman, skill: 1, team: 'blu' }, - { playerId: 'k', gameClass: Tf2ClassName.medic, skill: 2, team: 'blu' }, - - { playerId: 'c', gameClass: Tf2ClassName.scout, skill: 2, team: 'red' }, - { playerId: 'd', gameClass: Tf2ClassName.scout, skill: 2, team: 'red' }, - { playerId: 'g', gameClass: Tf2ClassName.soldier, skill: 5, team: 'red' }, - { playerId: 'h', gameClass: Tf2ClassName.soldier, skill: 4, team: 'red' }, - { playerId: 'j', gameClass: Tf2ClassName.demoman, skill: 3, team: 'red' }, - { playerId: 'l', gameClass: Tf2ClassName.medic, skill: 4, team: 'red' }, + { + playerId: 'a', + gameClass: Tf2ClassName.scout, + skill: 3, + team: 'blu', + }, + { + playerId: 'b', + gameClass: Tf2ClassName.scout, + skill: 2, + team: 'blu', + }, + { + playerId: 'e', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'blu', + }, + { + playerId: 'f', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'blu', + }, + { + playerId: 'i', + gameClass: Tf2ClassName.demoman, + skill: 1, + team: 'blu', + }, + { + playerId: 'k', + gameClass: Tf2ClassName.medic, + skill: 2, + team: 'blu', + }, + + { + playerId: 'c', + gameClass: Tf2ClassName.scout, + skill: 2, + team: 'red', + }, + { + playerId: 'd', + gameClass: Tf2ClassName.scout, + skill: 2, + team: 'red', + }, + { + playerId: 'g', + gameClass: Tf2ClassName.soldier, + skill: 5, + team: 'red', + }, + { + playerId: 'h', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'red', + }, + { + playerId: 'j', + gameClass: Tf2ClassName.demoman, + skill: 3, + team: 'red', + }, + { + playerId: 'l', + gameClass: Tf2ClassName.medic, + skill: 4, + team: 'red', + }, ]); }); }); @@ -141,19 +289,79 @@ describe('pickTeams', () => { it('should pick teams', () => { expect(pickTeams(players, overrides)).toEqual([ - { playerId: 'zinner', gameClass: Tf2ClassName.soldier, skill: 4, team: 'blu' }, - { playerId: 'mielzky', gameClass: Tf2ClassName.soldier, skill: 2, team: 'blu' }, - { playerId: 'cieniu97', gameClass: Tf2ClassName.demoman, skill: 2, team: 'blu' }, - { playerId: 'stan', gameClass: Tf2ClassName.medic, skill: 4, team: 'blu' }, - { playerId: 'graba', gameClass: Tf2ClassName.scout, skill: 4, team: 'blu' }, - { playerId: 'crzje', gameClass: Tf2ClassName.scout, skill: 4, team: 'blu' }, - - { playerId: 'wonder', gameClass: Tf2ClassName.soldier, skill: 3, team: 'red' }, - { playerId: 'loww', gameClass: Tf2ClassName.soldier, skill: 3, team: 'red' }, - { playerId: 'mejf', gameClass: Tf2ClassName.demoman, skill: 3, team: 'red' }, - { playerId: 'bobair', gameClass: Tf2ClassName.medic, skill: 1, team: 'red' }, - { playerId: 'kwq', gameClass: Tf2ClassName.scout, skill: 7, team: 'red' }, - { playerId: 'antro15cm', gameClass: Tf2ClassName.scout, skill: 2, team: 'red' }, + { + playerId: 'zinner', + gameClass: Tf2ClassName.soldier, + skill: 4, + team: 'blu', + }, + { + playerId: 'mielzky', + gameClass: Tf2ClassName.soldier, + skill: 2, + team: 'blu', + }, + { + playerId: 'cieniu97', + gameClass: Tf2ClassName.demoman, + skill: 2, + team: 'blu', + }, + { + playerId: 'stan', + gameClass: Tf2ClassName.medic, + skill: 4, + team: 'blu', + }, + { + playerId: 'graba', + gameClass: Tf2ClassName.scout, + skill: 4, + team: 'blu', + }, + { + playerId: 'crzje', + gameClass: Tf2ClassName.scout, + skill: 4, + team: 'blu', + }, + + { + playerId: 'wonder', + gameClass: Tf2ClassName.soldier, + skill: 3, + team: 'red', + }, + { + playerId: 'loww', + gameClass: Tf2ClassName.soldier, + skill: 3, + team: 'red', + }, + { + playerId: 'mejf', + gameClass: Tf2ClassName.demoman, + skill: 3, + team: 'red', + }, + { + playerId: 'bobair', + gameClass: Tf2ClassName.medic, + skill: 1, + team: 'red', + }, + { + playerId: 'kwq', + gameClass: Tf2ClassName.scout, + skill: 7, + team: 'red', + }, + { + playerId: 'antro15cm', + gameClass: Tf2ClassName.scout, + skill: 2, + team: 'red', + }, ]); }); }); @@ -184,22 +392,62 @@ describe('pickTeams', () => { it('should pick teams', () => { expect(pickTeams(playerIds)).toEqual([ { playerId: 'a', gameClass: Tf2ClassName.scout, skill: 1, team: 'blu' }, - { playerId: 'c', gameClass: Tf2ClassName.soldier, skill: 2, team: 'blu' }, + { + playerId: 'c', + gameClass: Tf2ClassName.soldier, + skill: 2, + team: 'blu', + }, { playerId: 'e', gameClass: Tf2ClassName.pyro, skill: 3, team: 'blu' }, - { playerId: 'g', gameClass: Tf2ClassName.demoman, skill: 4, team: 'blu' }, + { + playerId: 'g', + gameClass: Tf2ClassName.demoman, + skill: 4, + team: 'blu', + }, { playerId: 'i', gameClass: Tf2ClassName.heavy, skill: 5, team: 'blu' }, - { playerId: 'k', gameClass: Tf2ClassName.engineer, skill: 6, team: 'blu' }, + { + playerId: 'k', + gameClass: Tf2ClassName.engineer, + skill: 6, + team: 'blu', + }, { playerId: 'm', gameClass: Tf2ClassName.medic, skill: 7, team: 'blu' }, - { playerId: 'o', gameClass: Tf2ClassName.sniper, skill: 8, team: 'blu' }, + { + playerId: 'o', + gameClass: Tf2ClassName.sniper, + skill: 8, + team: 'blu', + }, { playerId: 'q', gameClass: Tf2ClassName.spy, skill: 9, team: 'blu' }, { playerId: 'b', gameClass: Tf2ClassName.scout, skill: 9, team: 'red' }, - { playerId: 'd', gameClass: Tf2ClassName.soldier, skill: 8, team: 'red' }, + { + playerId: 'd', + gameClass: Tf2ClassName.soldier, + skill: 8, + team: 'red', + }, { playerId: 'f', gameClass: Tf2ClassName.pyro, skill: 7, team: 'red' }, - { playerId: 'h', gameClass: Tf2ClassName.demoman, skill: 6, team: 'red' }, + { + playerId: 'h', + gameClass: Tf2ClassName.demoman, + skill: 6, + team: 'red', + }, { playerId: 'j', gameClass: Tf2ClassName.heavy, skill: 5, team: 'red' }, - { playerId: 'l', gameClass: Tf2ClassName.engineer, skill: 4, team: 'red' }, + { + playerId: 'l', + gameClass: Tf2ClassName.engineer, + skill: 4, + team: 'red', + }, { playerId: 'n', gameClass: Tf2ClassName.medic, skill: 3, team: 'red' }, - { playerId: 'p', gameClass: Tf2ClassName.sniper, skill: 2, team: 'red' }, + { + playerId: 'p', + gameClass: Tf2ClassName.sniper, + skill: 2, + team: 'red', + }, { playerId: 'r', gameClass: Tf2ClassName.spy, skill: 1, team: 'red' }, ]); }); diff --git a/src/games/utils/pick-teams.ts b/src/games/utils/pick-teams.ts index 71d779390..5130669f0 100644 --- a/src/games/utils/pick-teams.ts +++ b/src/games/utils/pick-teams.ts @@ -31,7 +31,10 @@ type PossibleLineup = Record; * For 9v9, it makes 512 teams (2 ^ 9). * @return gameClass <=> lineup pairs. */ -function makeAllPossibleLineups(gameClasses: string[], players: PlayerSlot[]): PossibleLineup[] { +function makeAllPossibleLineups( + gameClasses: string[], + players: PlayerSlot[], +): PossibleLineup[] { // First off, let's make all possible lineups for each class. // i.e. for scouts (A, B, C, D) make the following: // ([A, B], [C, D]), ([A, C], [B, D]), ([A, D], [B, C]) @@ -39,48 +42,53 @@ function makeAllPossibleLineups(gameClasses: string[], players: PlayerSlot[]): P // ([A, B]) and ([B, A]) const gameClassLineups = new Map(); for (const gameClass of gameClasses) { - const playersOfGameClass = players.filter(p => p.gameClass === gameClass); + const playersOfGameClass = players.filter((p) => p.gameClass === gameClass); strict(playersOfGameClass.length % 2 === 0); if (playersOfGameClass.length === 2) { gameClassLineups.set(gameClass, [ { - 0: { lineup: [ playersOfGameClass[0] ] }, - 1: { lineup: [ playersOfGameClass[1] ] }, + 0: { lineup: [playersOfGameClass[0]] }, + 1: { lineup: [playersOfGameClass[1]] }, }, { - 0: { lineup: [ playersOfGameClass[1] ] }, - 1: { lineup: [ playersOfGameClass[0] ] }, + 0: { lineup: [playersOfGameClass[1]] }, + 1: { lineup: [playersOfGameClass[0]] }, }, ]); } else if (playersOfGameClass.length === 4) { gameClassLineups.set(gameClass, [ { - 0: { lineup: [ playersOfGameClass[0], playersOfGameClass[1] ] }, - 1: { lineup: [ playersOfGameClass[2], playersOfGameClass[3] ] }, + 0: { lineup: [playersOfGameClass[0], playersOfGameClass[1]] }, + 1: { lineup: [playersOfGameClass[2], playersOfGameClass[3]] }, }, { - 0: { lineup: [ playersOfGameClass[0], playersOfGameClass[2] ] }, - 1: { lineup: [ playersOfGameClass[1], playersOfGameClass[3] ] }, + 0: { lineup: [playersOfGameClass[0], playersOfGameClass[2]] }, + 1: { lineup: [playersOfGameClass[1], playersOfGameClass[3]] }, }, { - 0: { lineup: [ playersOfGameClass[0], playersOfGameClass[3] ] }, - 1: { lineup: [ playersOfGameClass[1], playersOfGameClass[2] ] }, + 0: { lineup: [playersOfGameClass[0], playersOfGameClass[3]] }, + 1: { lineup: [playersOfGameClass[1], playersOfGameClass[2]] }, }, { - 0: { lineup: [ playersOfGameClass[2], playersOfGameClass[3] ] }, - 1: { lineup: [ playersOfGameClass[0], playersOfGameClass[1] ] }, + 0: { lineup: [playersOfGameClass[2], playersOfGameClass[3]] }, + 1: { lineup: [playersOfGameClass[0], playersOfGameClass[1]] }, }, ]); } else { - throw new NotImplementedException('more than two players of one class in a team is not implemented'); + throw new NotImplementedException( + 'more than two players of one class in a team is not implemented', + ); } } // The next thing to do is to make all combinations of the game class lineup possibilities above. const possibleLineups: PossibleLineup[] = []; - function makeLineup(prev: PossibleLineup = { 0: { lineup: [] }, 1: { lineup: [] } }, i = 0) { + function makeLineup( + prev: PossibleLineup = { 0: { lineup: [] }, 1: { lineup: [] } }, + i = 0, + ) { if (i === gameClasses.length) { possibleLineups.push(prev); } else { @@ -88,9 +96,9 @@ function makeAllPossibleLineups(gameClasses: string[], players: PlayerSlot[]): P const gcLineups = gameClassLineups.get(gameClass); for (const lineup of gcLineups) { const tmp: PossibleLineup = { - 0: { lineup: [ ...prev[0].lineup, ...lineup[0].lineup ] }, - 1: { lineup: [ ...prev[1].lineup, ...lineup[1].lineup ] }, - } + 0: { lineup: [...prev[0].lineup, ...lineup[0].lineup] }, + 1: { lineup: [...prev[1].lineup, ...lineup[1].lineup] }, + }; makeLineup(tmp, i + 1); } } @@ -112,56 +120,74 @@ interface LineupWithSkillAverageDifference extends PossibleLineup { skillAverageDifference: number; } -function calculateAverageSkillDifference(lineup: Record): LineupWithSkillAverageDifference { - return { ...lineup, skillAverageDifference: Math.abs(lineup[0].skillAverage - lineup[1].skillAverage) }; +function calculateAverageSkillDifference( + lineup: Record, +): LineupWithSkillAverageDifference { + return { + ...lineup, + skillAverageDifference: Math.abs( + lineup[0].skillAverage - lineup[1].skillAverage, + ), + }; } -function respectsOverrides(lineup: Record, overrides?: TeamOverrides): boolean { +function respectsOverrides( + lineup: Record, + overrides?: TeamOverrides, +): boolean { if (overrides === undefined) { return true; } function findPlayersTeam(player: string): TeamId | null { - if (lineup[0].lineup.find(p => p.playerId === player)) { + if (lineup[0].lineup.find((p) => p.playerId === player)) { return 0; - } else if (lineup[1].lineup.find(p => p.playerId === player)) { + } else if (lineup[1].lineup.find((p) => p.playerId === player)) { return 1; } else { return null; } } - return overrides.friends - .every(friendPair => [ ...new Set( - friendPair - .map(friend => findPlayersTeam(friend)) - .filter(teamId => teamId !== null) - ) ].length < 2 - ); + return overrides.friends.every( + (friendPair) => + [ + ...new Set( + friendPair + .map((friend) => findPlayersTeam(friend)) + .filter((teamId) => teamId !== null), + ), + ].length < 2, + ); } /** * From the given pool of players make two teams that make the smallest average skill difference. */ -export function pickTeams(players: PlayerSlot[], overrides?: TeamOverrides): PlayerSlotWithTeam[] { +export function pickTeams( + players: PlayerSlot[], + overrides?: TeamOverrides, +): PlayerSlotWithTeam[] { const teams: Record = { 0: Tf2Team.blu, 1: Tf2Team.red }; - const gameClasses = [ ...new Set(players.map(p => p.gameClass)) ]; + const gameClasses = [...new Set(players.map((p) => p.gameClass))]; const allPossibleLineups = makeAllPossibleLineups(gameClasses, players) - .filter(lineup => respectsOverrides(lineup, overrides)) - .map(lineup => { + .filter((lineup) => respectsOverrides(lineup, overrides)) + .map((lineup) => { return { 0: calculateAverageSkill(lineup[0]), 1: calculateAverageSkill(lineup[1]), }; }) - .map(lineup => calculateAverageSkillDifference(lineup)) + .map((lineup) => calculateAverageSkillDifference(lineup)) .sort((a, b) => a.skillAverageDifference - b.skillAverageDifference); const selectedLineup = allPossibleLineups[0]; - return [0, 1] - .flatMap(teamId => (selectedLineup[teamId] as TeamLineup).lineup - .map(slot => ({ ...slot, team: teams[teamId] })) - ); + return [0, 1].flatMap((teamId) => + (selectedLineup[teamId] as TeamLineup).lineup.map((slot) => ({ + ...slot, + team: teams[teamId], + })), + ); } diff --git a/src/games/utils/rcon-commands.ts b/src/games/utils/rcon-commands.ts index 36545f1a9..5545201e7 100644 --- a/src/games/utils/rcon-commands.ts +++ b/src/games/utils/rcon-commands.ts @@ -1,4 +1,9 @@ -export function addGamePlayer(steamId: string, name: string, team: string, gameClass: string) { +export function addGamePlayer( + steamId: string, + name: string, + team: string, + gameClass: string, +) { return [ `sm_game_player_add ${steamId}`, `-name "${name}"`, diff --git a/src/log-receiver/log-receiver.module.ts b/src/log-receiver/log-receiver.module.ts index 2308f033f..b3de6659e 100644 --- a/src/log-receiver/log-receiver.module.ts +++ b/src/log-receiver/log-receiver.module.ts @@ -5,19 +5,15 @@ import { LogReceiverService } from './services/log-receiver/log-receiver.service const logReceiverProvider = { provide: LogReceiver, - useFactory: (environment: Environment) => new LogReceiver({ - port: parseInt(environment.logRelayPort, 10), - }), - inject: [ Environment ], + useFactory: (environment: Environment) => + new LogReceiver({ + port: parseInt(environment.logRelayPort, 10), + }), + inject: [Environment], }; @Module({ - providers: [ - logReceiverProvider, - LogReceiverService, - ], - exports: [ - logReceiverProvider, - ], + providers: [logReceiverProvider, LogReceiverService], + exports: [logReceiverProvider], }) export class LogReceiverModule {} diff --git a/src/log-receiver/services/log-receiver/log-receiver.service.spec.ts b/src/log-receiver/services/log-receiver/log-receiver.service.spec.ts index 180737644..050464a26 100644 --- a/src/log-receiver/services/log-receiver/log-receiver.service.spec.ts +++ b/src/log-receiver/services/log-receiver/log-receiver.service.spec.ts @@ -4,7 +4,7 @@ import { LogReceiverService } from './log-receiver.service'; class MockLogReceiver { socket = { - close: jest.fn().mockImplementation(cb => cb()), + close: jest.fn().mockImplementation((cb) => cb()), }; } @@ -30,7 +30,7 @@ describe('LogReceiverService', () => { it('should destroy log receiver on module destruction', async () => { await service.onModuleDestroy(); - + expect(mockLogReceiver.socket.close).toHaveBeenCalled(); }); }); diff --git a/src/log-receiver/services/log-receiver/log-receiver.service.ts b/src/log-receiver/services/log-receiver/log-receiver.service.ts index 886f1db13..65e41d866 100644 --- a/src/log-receiver/services/log-receiver/log-receiver.service.ts +++ b/src/log-receiver/services/log-receiver/log-receiver.service.ts @@ -3,16 +3,14 @@ import { LogReceiver } from 'srcds-log-receiver'; @Injectable() export class LogReceiverService implements OnModuleDestroy { - private logger = new Logger(LogReceiverService.name); - constructor( - private logReceiver: LogReceiver, - ) { } + constructor(private logReceiver: LogReceiver) {} async onModuleDestroy() { - await new Promise(resolve => this.logReceiver.socket.close(resolve)); + await new Promise((resolve) => + this.logReceiver.socket.close(resolve), + ); this.logger.debug('LogReceiver closed'); } - } diff --git a/src/main.ts b/src/main.ts index 6b35112c8..0dd69b672 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,13 +18,13 @@ async function bootstrap() { app.enableCors(); app.use(helmet()); - app.use(helmet - .contentSecurityPolicy({ + app.use( + helmet.contentSecurityPolicy({ directives: { - defaultSrc: ['\'none\''], - baseUri: ['\'none\''], - formAction: ['\'none\''], - scriptSrc: ['\'self\'', '\'unsafe-inline\''], + defaultSrc: ["'none'"], + baseUri: ["'none'"], + formAction: ["'none'"], + scriptSrc: ["'self'", "'unsafe-inline'"], }, }), ); diff --git a/src/player-preferences/models/player-preferences.ts b/src/player-preferences/models/player-preferences.ts index a157e3cb6..328393787 100644 --- a/src/player-preferences/models/player-preferences.ts +++ b/src/player-preferences/models/player-preferences.ts @@ -2,11 +2,9 @@ import { Player } from '@/players/models/player'; import { prop, Ref } from '@typegoose/typegoose'; export class PlayerPreferences { - @prop({ ref: () => Player, unique: true }) player?: Ref; @prop({ type: String }) preferences: Map; - } diff --git a/src/player-preferences/player-preferences.module.ts b/src/player-preferences/player-preferences.module.ts index 3805dbb3f..1fa4bae93 100644 --- a/src/player-preferences/player-preferences.module.ts +++ b/src/player-preferences/player-preferences.module.ts @@ -4,14 +4,8 @@ import { PlayerPreferences } from './models/player-preferences'; import { PlayerPreferencesService } from './services/player-preferences.service'; @Module({ - imports: [ - TypegooseModule.forFeature([ PlayerPreferences ]), - ], - providers: [ - PlayerPreferencesService, - ], - exports: [ - PlayerPreferencesService, - ], + imports: [TypegooseModule.forFeature([PlayerPreferences])], + providers: [PlayerPreferencesService], + exports: [PlayerPreferencesService], }) export class PlayerPreferencesModule {} diff --git a/src/player-preferences/services/player-preferences.service.spec.ts b/src/player-preferences/services/player-preferences.service.spec.ts index e162ad345..4d52d25af 100644 --- a/src/player-preferences/services/player-preferences.service.spec.ts +++ b/src/player-preferences/services/player-preferences.service.spec.ts @@ -12,7 +12,7 @@ describe('PlayerPreferencesService', () => { let mongod: MongoMemoryServer; let playerPreferencesModel: ReturnModelType; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -21,9 +21,7 @@ describe('PlayerPreferencesService', () => { typegooseTestingModule(mongod), TypegooseModule.forFeature([PlayerPreferences]), ], - providers: [ - PlayerPreferencesService, - ], + providers: [PlayerPreferencesService], }).compile(); service = module.get(PlayerPreferencesService); @@ -31,7 +29,7 @@ describe('PlayerPreferencesService', () => { }); afterEach(async () => { - await playerPreferencesModel.deleteMany({ }); + await playerPreferencesModel.deleteMany({}); }); it('should be defined', () => { @@ -73,10 +71,15 @@ describe('PlayerPreferencesService', () => { describe('when saving for the first time', () => { it('should upsert', async () => { - const prefs = await service.updatePlayerPreferences(playerId, new Map([['sound-volume', '0.7']])); + const prefs = await service.updatePlayerPreferences( + playerId, + new Map([['sound-volume', '0.7']]), + ); expect(prefs.size).toEqual(1); expect(prefs.get('sound-volume')).toEqual('0.7'); - expect(await playerPreferencesModel.findOne({ player: playerId })).toBeTruthy(); + expect( + await playerPreferencesModel.findOne({ player: playerId }), + ).toBeTruthy(); }); }); @@ -91,7 +94,10 @@ describe('PlayerPreferencesService', () => { it('should update', async () => { preferences.set('sound-pack', 'default'); preferences.set('sound-volume', '0.6'); - const prefs = await service.updatePlayerPreferences(playerId, preferences); + const prefs = await service.updatePlayerPreferences( + playerId, + preferences, + ); expect(prefs.size).toEqual(2); expect(prefs.get('sound-volume')).toEqual('0.6'); expect(prefs.get('sound-pack')).toEqual('default'); diff --git a/src/player-preferences/services/player-preferences.service.ts b/src/player-preferences/services/player-preferences.service.ts index 7fc171919..3e25d8623 100644 --- a/src/player-preferences/services/player-preferences.service.ts +++ b/src/player-preferences/services/player-preferences.service.ts @@ -7,18 +7,27 @@ export type PreferencesType = Map; @Injectable() export class PlayerPreferencesService { - constructor( - @InjectModel(PlayerPreferences) private playerPreferences: ReturnModelType, - ) { } + @InjectModel(PlayerPreferences) + private playerPreferences: ReturnModelType, + ) {} async getPlayerPreferences(playerId: string): Promise { - return (await this.playerPreferences.findOne({ player: playerId }))?.preferences ?? new Map(); + return ( + (await this.playerPreferences.findOne({ player: playerId })) + ?.preferences ?? new Map() + ); } - async updatePlayerPreferences(playerId: string, preferences: PreferencesType): Promise { - const ret = await this.playerPreferences.findOneAndUpdate({ player: playerId }, { preferences }, { new: true, upsert: true }); + async updatePlayerPreferences( + playerId: string, + preferences: PreferencesType, + ): Promise { + const ret = await this.playerPreferences.findOneAndUpdate( + { player: playerId }, + { preferences }, + { new: true, upsert: true }, + ); return ret.preferences; } - } diff --git a/src/players/controllers/hall-of-fame.controller.spec.ts b/src/players/controllers/hall-of-fame.controller.spec.ts index 3a3106fc9..d8cbff73d 100644 --- a/src/players/controllers/hall-of-fame.controller.spec.ts +++ b/src/players/controllers/hall-of-fame.controller.spec.ts @@ -3,8 +3,12 @@ import { HallOfFameController } from './hall-of-fame.controller'; import { GamesService } from '@/games/services/games.service'; class GamesServiceStub { - getMostActivePlayers() { return [ ]; } - getMostActiveMedics() { return [ ]; } + getMostActivePlayers() { + return []; + } + getMostActiveMedics() { + return []; + } } describe('HallOfFame Controller', () => { @@ -14,9 +18,7 @@ describe('HallOfFame Controller', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [HallOfFameController], - providers: [ - { provide: GamesService, useClass: GamesServiceStub }, - ], + providers: [{ provide: GamesService, useClass: GamesServiceStub }], }).compile(); controller = module.get(HallOfFameController); diff --git a/src/players/controllers/hall-of-fame.controller.ts b/src/players/controllers/hall-of-fame.controller.ts index dfb551f05..cdcd5f0e6 100644 --- a/src/players/controllers/hall-of-fame.controller.ts +++ b/src/players/controllers/hall-of-fame.controller.ts @@ -3,10 +3,7 @@ import { GamesService } from '@/games/services/games.service'; @Controller('hall-of-fame') export class HallOfFameController { - - constructor( - private gamesService: GamesService, - ) { } + constructor(private gamesService: GamesService) {} @Get() async getHallOfFame() { @@ -14,5 +11,4 @@ export class HallOfFameController { const mostActiveMedics = await this.gamesService.getMostActiveMedics(); return { mostActivePlayers, mostActiveMedics }; } - } diff --git a/src/players/controllers/players.controller.spec.ts b/src/players/controllers/players.controller.spec.ts index feff1a328..594e381bc 100644 --- a/src/players/controllers/players.controller.spec.ts +++ b/src/players/controllers/players.controller.spec.ts @@ -8,7 +8,11 @@ import { PlayerStats } from '../dto/player-stats'; import { PlayerSkillService } from '../services/player-skill.service'; import { PlayerSkill } from '../models/player-skill'; import { PlayerBansService } from '../services/player-bans.service'; -import { NotFoundException, BadRequestException, CacheModule } from '@nestjs/common'; +import { + NotFoundException, + BadRequestException, + CacheModule, +} from '@nestjs/common'; import { Tf2ClassName } from '@/shared/models/tf2-class-name'; import { plainToClass } from 'class-transformer'; import { PlayerBan } from '../models/player-ban'; @@ -25,17 +29,27 @@ class PlayersServiceStub { player: 'FAKE_ID', gamesPlayed: 220, classesPlayed: new Map([ - [ Tf2ClassName.scout, 19 ], - [ Tf2ClassName.soldier, 102 ], - [ Tf2ClassName.demoman, 0 ], - [ Tf2ClassName.medic, 92 ], + [Tf2ClassName.scout, 19], + [Tf2ClassName.soldier, 102], + [Tf2ClassName.demoman, 0], + [Tf2ClassName.medic, 92], ]), }); - getAll() { return new Promise(resolve => resolve([ this.player ])); } - getById(id: string) { return new Promise(resolve => resolve(this.player)); } - forceCreatePlayer(player: Player) { return new Promise(resolve => resolve(player)); } - updatePlayer(playerId: string, update: Partial) { return new Promise(resolve => resolve(this.player)); } - getPlayerStats(playerId: string) { return new Promise(resolve => resolve(this.stats)); } + getAll() { + return new Promise((resolve) => resolve([this.player])); + } + getById(id: string) { + return new Promise((resolve) => resolve(this.player)); + } + forceCreatePlayer(player: Player) { + return new Promise((resolve) => resolve(player)); + } + updatePlayer(playerId: string, update: Partial) { + return new Promise((resolve) => resolve(this.player)); + } + getPlayerStats(playerId: string) { + return new Promise((resolve) => resolve(this.stats)); + } } class GamesServiceStub { @@ -43,11 +57,18 @@ class GamesServiceStub { { number: 1, map: 'cp_fake_rc1', state: 'ended', slots: [] } as Game, { number: 2, map: 'cp_fake_rc2', state: 'launching', slots: [] } as Game, ]; - getPlayerGames(playerId: string, sort: any = { launchedAt: -1 }, limit = 10, skip = 0) { + getPlayerGames( + playerId: string, + sort: any = { launchedAt: -1 }, + limit = 10, + skip = 0, + ) { return Promise.resolve(this.games); } - getPlayerGameCount() { return Promise.resolve(2); } + getPlayerGameCount() { + return Promise.resolve(2); + } } class PlayerSkillServiceStub { @@ -60,48 +81,58 @@ class PlayerSkillServiceStub { [Tf2ClassName.medic, 2], ]), }; - getPlayerSkill(playerId: string) { return new Promise(resolve => resolve(this.skill)); } - setPlayerSkill(playerId: string, skill: Map) { return Promise.resolve(this.skill); } - getAll() { return new Promise(resolve => resolve([ this.skill ])); } + getPlayerSkill(playerId: string) { + return new Promise((resolve) => resolve(this.skill)); + } + setPlayerSkill(playerId: string, skill: Map) { + return Promise.resolve(this.skill); + } + getAll() { + return new Promise((resolve) => resolve([this.skill])); + } } class PlayerBansServiceStub { bans = [ { - player: '5d448875b963ff7e00c6b6b3', - admin: '5d448875b963ff7e00c6b6b3', - start: '2019-12-16T00:23:55.000Z', - end: '2019-12-17T01:51:49.183Z', - reason: 'test', - id: '5df833c256e77d8768130f9a', + player: '5d448875b963ff7e00c6b6b3', + admin: '5d448875b963ff7e00c6b6b3', + start: '2019-12-16T00:23:55.000Z', + end: '2019-12-17T01:51:49.183Z', + reason: 'test', + id: '5df833c256e77d8768130f9a', }, { - player: '5d448875b963ff7e00c6b6b3', - start: '2019-10-29T13:23:38.626Z', - end: '2019-10-29T14:23:38.626Z', - reason: 'test', - admin: '5d448875b963ff7e00c6b6b3', - id: '5db83d5a593cc645933cce54', + player: '5d448875b963ff7e00c6b6b3', + start: '2019-10-29T13:23:38.626Z', + end: '2019-10-29T14:23:38.626Z', + reason: 'test', + admin: '5d448875b963ff7e00c6b6b3', + id: '5db83d5a593cc645933cce54', }, { - player: '5d448875b963ff7e00c6b6b3', - start: '2019-10-25T12:15:54.882Z', - end: '2019-10-25T12:16:25.442Z', - reason: 'test', - admin: '5d448875b963ff7e00c6b6b3', - id: '5db2e77abbf17f2a9101a9f6', + player: '5d448875b963ff7e00c6b6b3', + start: '2019-10-25T12:15:54.882Z', + end: '2019-10-25T12:16:25.442Z', + reason: 'test', + admin: '5d448875b963ff7e00c6b6b3', + id: '5db2e77abbf17f2a9101a9f6', }, { - player: '5d448875b963ff7e00c6b6b3', - start: '2019-10-25T12:06:20.123Z', - end: '2019-10-25T12:06:28.919Z', - reason: 'test', - admin: '5d448875b963ff7e00c6b6b3', - id: '5db2e53c80e22f6e05200875', + player: '5d448875b963ff7e00c6b6b3', + start: '2019-10-25T12:06:20.123Z', + end: '2019-10-25T12:06:28.919Z', + reason: 'test', + admin: '5d448875b963ff7e00c6b6b3', + id: '5db2e53c80e22f6e05200875', }, ]; - getPlayerBans(playerId: string) { return new Promise(resolve => resolve(this.bans)); } - addPlayerBan(ban: any) { return new Promise(resolve => resolve(ban)); } + getPlayerBans(playerId: string) { + return new Promise((resolve) => resolve(this.bans)); + } + addPlayerBan(ban: any) { + return new Promise((resolve) => resolve(ban)); + } } describe('Players Controller', () => { @@ -120,9 +151,7 @@ describe('Players Controller', () => { { provide: PlayerBansService, useClass: PlayerBansServiceStub }, ], controllers: [PlayersController], - imports: [ - CacheModule.register(), - ], + imports: [CacheModule.register()], }).compile(); controller = module.get(PlayersController); @@ -141,7 +170,7 @@ describe('Players Controller', () => { const spy = jest.spyOn(playersService, 'getAll'); const players = await controller.getAllPlayers(); expect(spy).toHaveBeenCalled(); - expect(players).toEqual([ playersService.player ] as any[]); + expect(players).toEqual([playersService.player] as any[]); }); }); @@ -157,16 +186,32 @@ describe('Players Controller', () => { describe('#forceCreatePlayer()', () => { it('should call the service', async () => { const spy = jest.spyOn(playersService, 'forceCreatePlayer'); - await controller.forceCreatePlayer({ name: 'FAKE_PLAYER_NAME', steamId: 'FAKE_PLAYER_STEAM_ID' }); - expect(spy).toHaveBeenCalledWith(expect.objectContaining({ name: 'FAKE_PLAYER_NAME', steamId: 'FAKE_PLAYER_STEAM_ID' })); + await controller.forceCreatePlayer({ + name: 'FAKE_PLAYER_NAME', + steamId: 'FAKE_PLAYER_STEAM_ID', + }); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ + name: 'FAKE_PLAYER_NAME', + steamId: 'FAKE_PLAYER_STEAM_ID', + }), + ); }); }); describe('#updatePlayer()', () => { it('should update the player', async () => { const spy = jest.spyOn(playersService, 'updatePlayer'); - const ret = await controller.updatePlayer('FAKE_ID', { name: 'FAKE_NEW_NAME' }, { id: 'FAKE_ADMIN_ID' } as any); - expect(spy).toHaveBeenCalledWith('FAKE_ID', { name: 'FAKE_NEW_NAME' }, 'FAKE_ADMIN_ID'); + const ret = await controller.updatePlayer( + 'FAKE_ID', + { name: 'FAKE_NEW_NAME' }, + { id: 'FAKE_ADMIN_ID' } as any, + ); + expect(spy).toHaveBeenCalledWith( + 'FAKE_ID', + { name: 'FAKE_NEW_NAME' }, + 'FAKE_ADMIN_ID', + ); expect(ret).toEqual(playersService.player as any); }); }); @@ -176,7 +221,12 @@ describe('Players Controller', () => { const spy1 = jest.spyOn(gamesService, 'getPlayerGames'); const spy2 = jest.spyOn(gamesService, 'getPlayerGameCount'); - const ret = await controller.getPlayerGames('FAKE_ID', 44, 52, 'launched_at'); + const ret = await controller.getPlayerGames( + 'FAKE_ID', + 44, + 52, + 'launched_at', + ); expect(spy1).toHaveBeenCalledWith('FAKE_ID', { launchedAt: 1 }, 44, 52); expect(spy2).toHaveBeenCalled(); expect(ret).toEqual({ @@ -186,7 +236,9 @@ describe('Players Controller', () => { }); it('should throw an error unless the sort param is correct', async () => { - await expect(controller.getPlayerGames('FAKE_ID', 3, 5, 'lol')).rejects.toThrow(); + await expect( + controller.getPlayerGames('FAKE_ID', 3, 5, 'lol'), + ).rejects.toThrow(); }); }); @@ -199,12 +251,12 @@ describe('Players Controller', () => { }); }); - describe('#getAllPlayerSkills()', () => { - it('should return all players\' skills', async () => { + describe('#getAllPlayerSkills()', () => { + it("should return all players' skills", async () => { const spy = jest.spyOn(playerSkillService, 'getAll'); const ret = await controller.getAllPlayerSkills(); expect(spy).toHaveBeenCalled(); - expect(ret).toEqual([ playerSkillService.skill ] as any); + expect(ret).toEqual([playerSkillService.skill] as any); }); }); @@ -218,7 +270,9 @@ describe('Players Controller', () => { it('should return 404', async () => { jest.spyOn(playerSkillService, 'getPlayerSkill').mockResolvedValue(null); - await expect(controller.getPlayerSkill('FAKE_ID')).rejects.toThrow(NotFoundException); + await expect(controller.getPlayerSkill('FAKE_ID')).rejects.toThrow( + NotFoundException, + ); }); }); @@ -226,8 +280,17 @@ describe('Players Controller', () => { it('should set player skill', async () => { const skill = { soldier: 1, medic: 2 }; const spy = jest.spyOn(playerSkillService, 'setPlayerSkill'); - const ret = await controller.setPlayerSkill('FAKE_ID', skill, { id: 'FAKE_ADMIN_ID' } as any); - expect(spy).toHaveBeenCalledWith('FAKE_ID', new Map([['soldier', 1], ['medic', 2]]), 'FAKE_ADMIN_ID'); + const ret = await controller.setPlayerSkill('FAKE_ID', skill, { + id: 'FAKE_ADMIN_ID', + } as any); + expect(spy).toHaveBeenCalledWith( + 'FAKE_ID', + new Map([ + ['soldier', 1], + ['medic', 2], + ]), + 'FAKE_ADMIN_ID', + ); expect(ret).toEqual(playerSkillService.skill); }); }); @@ -253,14 +316,18 @@ describe('Players Controller', () => { it('should add player ban', async () => { const spy = jest.spyOn(playerBansService, 'addPlayerBan'); - const ret = await controller.addPlayerBan(ban as PlayerBan, { id: '5d448875b963ff7e00c6b6b3' } as Player); + const ret = await controller.addPlayerBan( + ban as PlayerBan, + { id: '5d448875b963ff7e00c6b6b3' } as Player, + ); expect(spy).toHaveBeenCalledWith(ban); expect(ret).toEqual(ban as any); }); - it('should fail if the authorized user id is not the same as admin\'s', async () => { - await expect(controller.addPlayerBan(ban as any, { id: 'SOME_ID' } as any)) - .rejects.toThrow(BadRequestException); + it("should fail if the authorized user id is not the same as admin's", async () => { + await expect( + controller.addPlayerBan(ban as any, { id: 'SOME_ID' } as any), + ).rejects.toThrow(BadRequestException); }); }); }); diff --git a/src/players/controllers/players.controller.ts b/src/players/controllers/players.controller.ts index 68a62dbf7..82ab2ce7d 100644 --- a/src/players/controllers/players.controller.ts +++ b/src/players/controllers/players.controller.ts @@ -1,5 +1,25 @@ -import { Controller, Get, Param, NotFoundException, Patch, Body, BadRequestException, ParseIntPipe, Query, Put, Post, UsePipes, ValidationPipe, - HttpCode, Header, UseInterceptors, CacheInterceptor, CacheTTL, ClassSerializerInterceptor, UseFilters } from '@nestjs/common'; +import { + Controller, + Get, + Param, + NotFoundException, + Patch, + Body, + BadRequestException, + ParseIntPipe, + Query, + Put, + Post, + UsePipes, + ValidationPipe, + HttpCode, + Header, + UseInterceptors, + CacheInterceptor, + CacheTTL, + ClassSerializerInterceptor, + UseFilters, +} from '@nestjs/common'; import { PlayersService } from '../services/players.service'; import { ObjectIdValidationPipe } from '@/shared/pipes/object-id-validation.pipe'; import { Player } from '../models/player'; @@ -18,13 +38,12 @@ import { PlayerRole } from '../models/player-role'; @Controller('players') @UseInterceptors(CacheInterceptor) export class PlayersController { - constructor( private playersService: PlayersService, private gamesService: GamesService, private playerSkillService: PlayerSkillService, private playerBansService: PlayerBansService, - ) { } + ) {} @Get() @UseInterceptors(ClassSerializerInterceptor) @@ -50,14 +69,22 @@ export class PlayersController { @Patch(':id') @Auth(PlayerRole.admin) @UseInterceptors(ClassSerializerInterceptor) - async updatePlayer(@Param('id', ObjectIdValidationPipe) playerId: string, @Body() player: Partial, @User() admin: Player) { + async updatePlayer( + @Param('id', ObjectIdValidationPipe) playerId: string, + @Body() player: Partial, + @User() admin: Player, + ) { return await this.playersService.updatePlayer(playerId, player, admin.id); } @Get(':id/games') @Header('Warning', '299 - "Deprecated API"') - async getPlayerGames(@Param('id', ObjectIdValidationPipe) playerId: string, @Query('limit', ParseIntPipe) limit = 10, - @Query('offset', ParseIntPipe) offset = 0, @Query('sort') sort = '-launched_at') { + async getPlayerGames( + @Param('id', ObjectIdValidationPipe) playerId: string, + @Query('limit', ParseIntPipe) limit = 10, + @Query('offset', ParseIntPipe) offset = 0, + @Query('sort') sort = '-launched_at', + ) { let sortParam: { launchedAt: 1 | -1 }; switch (sort) { case '-launched_at': @@ -74,7 +101,7 @@ export class PlayersController { throw new BadRequestException('invalid value for the sort parameter'); } - const [ results, itemCount ] = await Promise.all([ + const [results, itemCount] = await Promise.all([ this.gamesService.getPlayerGames(playerId, sortParam, limit, offset), this.gamesService.getPlayerGameCount(playerId), ]); @@ -85,7 +112,9 @@ export class PlayersController { @CacheTTL(12 * 60 * 60) @Get(':id/stats') @UseInterceptors(ClassSerializerInterceptor) - async getPlayerStats(@Param('id', ObjectIdValidationPipe) playerId: string): Promise { + async getPlayerStats( + @Param('id', ObjectIdValidationPipe) playerId: string, + ): Promise { return await this.playersService.getPlayerStats(playerId); } @@ -114,8 +143,15 @@ export class PlayersController { @Body() newSkill: { [className in Tf2ClassName]?: number }, @User() user: Player, ) { - const newSkillAsMap = new Map(Object.entries(newSkill)) as Map; - return this.playerSkillService.setPlayerSkill(playerId, newSkillAsMap, user.id); + const newSkillAsMap = new Map(Object.entries(newSkill)) as Map< + Tf2ClassName, + number + >; + return this.playerSkillService.setPlayerSkill( + playerId, + newSkillAsMap, + user.id, + ); } @Get(':id/bans') @@ -131,7 +167,9 @@ export class PlayersController { @UseInterceptors(ClassSerializerInterceptor) async addPlayerBan(@Body() playerBan: PlayerBan, @User() user: Player) { if (playerBan.admin.toString() !== user.id) { - throw new BadRequestException('the admin field must be the same as authorized user\'s id'); + throw new BadRequestException( + "the admin field must be the same as authorized user's id", + ); } return await this.playerBansService.addPlayerBan(playerBan); } @@ -140,8 +178,12 @@ export class PlayersController { @Auth(PlayerRole.admin) @UseInterceptors(ClassSerializerInterceptor) @HttpCode(200) - async updatePlayerBan(@Param('playerId', ObjectIdValidationPipe) playerId: string, @Param('banId', ObjectIdValidationPipe) banId: string, - @Query('revoke') revoke: any, @User() user: Player) { + async updatePlayerBan( + @Param('playerId', ObjectIdValidationPipe) playerId: string, + @Param('banId', ObjectIdValidationPipe) banId: string, + @Query('revoke') revoke: any, + @User() user: Player, + ) { const player = await this.playersService.getById(playerId); if (!player) { throw new NotFoundException('player not found'); @@ -153,12 +195,11 @@ export class PlayersController { } if (ban.player.toString() !== playerId) { - throw new BadRequestException('the given ban is not of the user\'s'); + throw new BadRequestException("the given ban is not of the user's"); } if (revoke !== undefined) { return this.playerBansService.revokeBan(banId, user.id); } } - } diff --git a/src/players/dto/force-create-player.ts b/src/players/dto/force-create-player.ts index d0b53cbd0..d84bcb67a 100644 --- a/src/players/dto/force-create-player.ts +++ b/src/players/dto/force-create-player.ts @@ -1,11 +1,9 @@ import { IsString, Matches } from 'class-validator'; export class ForceCreatePlayer { - @IsString() name: string; @Matches(/^\d{17}$/) steamId: string; - } diff --git a/src/players/dto/player-stats.ts b/src/players/dto/player-stats.ts index 82141cfba..da17d0cdb 100644 --- a/src/players/dto/player-stats.ts +++ b/src/players/dto/player-stats.ts @@ -8,7 +8,6 @@ interface PlayerStatsParams { } export class PlayerStats { - constructor(params: PlayerStatsParams) { this.player = params.player; this.gamesPlayed = params.gamesPlayed; diff --git a/src/players/errors/account-banned.error.ts b/src/players/errors/account-banned.error.ts index ba2938be1..00a974e8a 100644 --- a/src/players/errors/account-banned.error.ts +++ b/src/players/errors/account-banned.error.ts @@ -1,7 +1,5 @@ export class AccountBannedError extends Error { - constructor(message: string) { super(message); } - } diff --git a/src/players/errors/insufficient-tf2-in-game-hours.error.ts b/src/players/errors/insufficient-tf2-in-game-hours.error.ts index 2c74f5dc7..90836997e 100644 --- a/src/players/errors/insufficient-tf2-in-game-hours.error.ts +++ b/src/players/errors/insufficient-tf2-in-game-hours.error.ts @@ -1,7 +1,5 @@ export class InsufficientTf2InGameHoursError extends Error { - constructor() { super('insufficient TF2 in-game hours'); } - } diff --git a/src/players/errors/tf2-in-game-hours-verification.error.ts b/src/players/errors/tf2-in-game-hours-verification.error.ts index 1e7e32d5a..ce5e144b4 100644 --- a/src/players/errors/tf2-in-game-hours-verification.error.ts +++ b/src/players/errors/tf2-in-game-hours-verification.error.ts @@ -1,9 +1,5 @@ export class Tf2InGameHoursVerificationError extends Error { - - constructor( - public verificationErrorMessage: string, - ) { + constructor(public verificationErrorMessage: string) { super(`cannot verify in-game hours for TF2`); } - } diff --git a/src/players/etf2l-profile.ts b/src/players/etf2l-profile.ts index 06d55bcca..859d1a475 100644 --- a/src/players/etf2l-profile.ts +++ b/src/players/etf2l-profile.ts @@ -3,7 +3,7 @@ export interface Etf2lProfile { name: string; country: string; classes: string[]; - bans?: { end: number, reason: string, start: number }[]; + bans?: { end: number; reason: string; start: number }[]; registered?: number; steam?: { avatar: string; diff --git a/src/players/gateways/players.gateway.spec.ts b/src/players/gateways/players.gateway.spec.ts index 9e505200c..5c6faad41 100644 --- a/src/players/gateways/players.gateway.spec.ts +++ b/src/players/gateways/players.gateway.spec.ts @@ -16,21 +16,23 @@ describe('PlayersGateway', () => { expect(gateway).toBeDefined(); }); - it('should emit playerConnected', async () => new Promise(resolve => { - const socket = { id: 'fasldfhasdkjfh' }; - gateway.playerConnected.subscribe(s => { - expect(s).toEqual(socket as any); - resolve(); - }); - gateway.handleConnection(socket as any); - })); + it('should emit playerConnected', async () => + new Promise((resolve) => { + const socket = { id: 'fasldfhasdkjfh' }; + gateway.playerConnected.subscribe((s) => { + expect(s).toEqual(socket as any); + resolve(); + }); + gateway.handleConnection(socket as any); + })); - it('should emit playerDisconnected', async () => new Promise(resolve => { - const socket = { id: 'fklsdafhf984' }; - gateway.playerDisconnected.subscribe(s => { - expect(s).toEqual(socket as any); - resolve(); - }); - gateway.handleDisconnect(socket as any); - })); + it('should emit playerDisconnected', async () => + new Promise((resolve) => { + const socket = { id: 'fklsdafhf984' }; + gateway.playerDisconnected.subscribe((s) => { + expect(s).toEqual(socket as any); + resolve(); + }); + gateway.handleDisconnect(socket as any); + })); }); diff --git a/src/players/gateways/players.gateway.ts b/src/players/gateways/players.gateway.ts index 1bd93e0c9..ad880bcf5 100644 --- a/src/players/gateways/players.gateway.ts +++ b/src/players/gateways/players.gateway.ts @@ -1,10 +1,14 @@ -import { WebSocketGateway, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets'; +import { + WebSocketGateway, + OnGatewayConnection, + OnGatewayDisconnect, +} from '@nestjs/websockets'; import { Socket } from 'socket.io'; import { Subject } from 'rxjs'; @WebSocketGateway() -export class PlayersGateway implements OnGatewayConnection, OnGatewayDisconnect { - +export class PlayersGateway + implements OnGatewayConnection, OnGatewayDisconnect { private _playerConnected = new Subject(); private _playerDisconnected = new Subject(); @@ -23,5 +27,4 @@ export class PlayersGateway implements OnGatewayConnection, OnGatewayDisconnect handleDisconnect(socket: Socket) { this._playerDisconnected.next(socket); } - } diff --git a/src/players/models/future-player-skill.ts b/src/players/models/future-player-skill.ts index ad492f597..719a57d62 100644 --- a/src/players/models/future-player-skill.ts +++ b/src/players/models/future-player-skill.ts @@ -5,11 +5,9 @@ import { prop } from '@typegoose/typegoose'; * Imported skills for players that have not registered their account yet. */ export class FuturePlayerSkill { - @prop({ required: true, unique: true }) steamId!: string; @prop({ type: Number }) skill?: Map; - } diff --git a/src/players/models/player-avatar.ts b/src/players/models/player-avatar.ts index 1d6c936ba..9d157f8b9 100644 --- a/src/players/models/player-avatar.ts +++ b/src/players/models/player-avatar.ts @@ -2,7 +2,6 @@ import { MongooseDocument } from '@/utils/mongoose-document'; import { prop } from '@typegoose/typegoose'; export class PlayerAvatar extends MongooseDocument { - @prop() small: string; // 32x32 px @@ -11,5 +10,4 @@ export class PlayerAvatar extends MongooseDocument { @prop() large: string; // 184x184 px - } diff --git a/src/players/models/player-ban.ts b/src/players/models/player-ban.ts index 7d53dad51..55221f586 100644 --- a/src/players/models/player-ban.ts +++ b/src/players/models/player-ban.ts @@ -5,18 +5,17 @@ import { MongooseDocument } from '@/utils/mongoose-document'; import { Expose, Transform, Type } from 'class-transformer'; export class PlayerBan extends MongooseDocument { - @Expose() @Transform(({ value, obj }) => value ?? obj._id?.toString()) id?: string; @IsMongoId() - @Transform(({ value }) => isRefType(value) ? value.toString() : value) + @Transform(({ value }) => (isRefType(value) ? value.toString() : value)) @prop({ ref: () => Player, required: true }) player!: Ref; @IsMongoId() - @Transform(({ value }) => isRefType(value) ? value.toString() : value) + @Transform(({ value }) => (isRefType(value) ? value.toString() : value)) @prop({ ref: () => Player, required: true }) admin!: Ref; diff --git a/src/players/models/player-role.ts b/src/players/models/player-role.ts index b2254dbdc..cbe7cc5da 100644 --- a/src/players/models/player-role.ts +++ b/src/players/models/player-role.ts @@ -3,4 +3,3 @@ export enum PlayerRole { admin = 'admin', bot = 'bot', } - diff --git a/src/players/models/player-skill.ts b/src/players/models/player-skill.ts index 9fdf2b003..64f8b1657 100644 --- a/src/players/models/player-skill.ts +++ b/src/players/models/player-skill.ts @@ -3,11 +3,9 @@ import { prop, Ref } from '@typegoose/typegoose'; import { Player } from './player'; export class PlayerSkill { - @prop({ ref: () => Player, unique: true }) player?: Ref; @prop({ type: Number }) skill?: Map; - } diff --git a/src/players/models/player.ts b/src/players/models/player.ts index e52969ab5..6323b4778 100644 --- a/src/players/models/player.ts +++ b/src/players/models/player.ts @@ -7,7 +7,6 @@ import { TwitchTvUser } from './twitch-tv-user'; @index({ steamId: 'hashed' }) export class Player extends MongooseDocument { - @Expose() @Transform(({ value, obj }) => value ?? obj._id.toString()) id?: string; @@ -38,5 +37,4 @@ export class Player extends MongooseDocument { @Type(() => TwitchTvUser) @prop({ type: TwitchTvUser }) twitchTvUser?: TwitchTvUser; - } diff --git a/src/players/models/twitch-tv-user.ts b/src/players/models/twitch-tv-user.ts index 3b43a08d4..767834abb 100644 --- a/src/players/models/twitch-tv-user.ts +++ b/src/players/models/twitch-tv-user.ts @@ -2,7 +2,6 @@ import { MongooseDocument } from '@/utils/mongoose-document'; import { prop } from '@typegoose/typegoose'; export class TwitchTvUser extends MongooseDocument { - @prop({ required: true }) userId!: string; @@ -14,5 +13,4 @@ export class TwitchTvUser extends MongooseDocument { @prop() profileImageUrl?: string; - } diff --git a/src/players/players.module.ts b/src/players/players.module.ts index 8308f635d..d94b349b8 100644 --- a/src/players/players.module.ts +++ b/src/players/players.module.ts @@ -50,9 +50,6 @@ import { ConfigurationModule } from '@/configuration/configuration.module'; PlayerSkillService, OnlinePlayersService, ], - controllers: [ - PlayersController, - HallOfFameController, - ], + controllers: [PlayersController, HallOfFameController], }) -export class PlayersModule { } +export class PlayersModule {} diff --git a/src/players/services/__mocks__/players.service.ts b/src/players/services/__mocks__/players.service.ts index 051cb2154..5a2bbfb35 100644 --- a/src/players/services/__mocks__/players.service.ts +++ b/src/players/services/__mocks__/players.service.ts @@ -7,16 +7,18 @@ import { plainToClass } from 'class-transformer'; @Injectable() export class PlayersService { - private lastId = 0; playerRegistered = new Subject(); constructor( @InjectModel(Player) private playerModel: ReturnModelType, - ) { } + ) {} async getById(id: string) { - return plainToClass(Player, await this.playerModel.findById(id).orFail().lean().exec()); + return plainToClass( + Player, + await this.playerModel.findById(id).orFail().lean().exec(), + ); } async getAll() { @@ -24,14 +26,17 @@ export class PlayersService { } async findBySteamId(steamId: string) { - return plainToClass(Player, await this.playerModel.findOne({ steamId }).orFail().lean().exec()); + return plainToClass( + Player, + await this.playerModel.findOne({ steamId }).orFail().lean().exec(), + ); } async _reset() { - await this.playerModel.deleteMany({ }).exec(); + await this.playerModel.deleteMany({}).exec(); } - async _createOne(overrides: Partial = { }) { + async _createOne(overrides: Partial = {}) { const player = { name: `fake_player_${++this.lastId}`, steamId: `steamid_${this.lastId}`, @@ -41,5 +46,4 @@ export class PlayersService { }; return await this.playerModel.create(player); } - } diff --git a/src/players/services/__mocks__/steam-api.service.ts b/src/players/services/__mocks__/steam-api.service.ts index bf0b5d901..5a1d1a9cc 100644 --- a/src/players/services/__mocks__/steam-api.service.ts +++ b/src/players/services/__mocks__/steam-api.service.ts @@ -2,9 +2,7 @@ import { Injectable } from '@nestjs/common'; @Injectable() export class SteamApiService { - async getTf2InGameHours(steamId64: string): Promise { return Promise.resolve(1000); } - } diff --git a/src/players/services/etf2l-profile.service.spec.ts b/src/players/services/etf2l-profile.service.spec.ts index bc705addd..91c585651 100644 --- a/src/players/services/etf2l-profile.service.spec.ts +++ b/src/players/services/etf2l-profile.service.spec.ts @@ -8,7 +8,7 @@ const mockEtf2lProfile: Etf2lProfile = { id: 12345, name: 'FAKE_ETF2L_NAME', country: 'SOME_COUNTRY', - classes: [ 'FAKE_CLASS_1', 'FAKE_CLASS_2' ], + classes: ['FAKE_CLASS_1', 'FAKE_CLASS_2'], }; class HttpServiceStub { @@ -44,18 +44,28 @@ describe('Etf2lProfileService', () => { it('should query the ETF2L API', async () => { const spy = jest.spyOn(httpService, 'get'); const res = await service.fetchPlayerInfo('FAKE_STEAM_ID'); - expect(spy).toHaveBeenCalledWith('http://api.etf2l.org/player/FAKE_STEAM_ID.json'); + expect(spy).toHaveBeenCalledWith( + 'http://api.etf2l.org/player/FAKE_STEAM_ID.json', + ); expect(res).toEqual(mockEtf2lProfile); }); it('should handle 404', async () => { - jest.spyOn(httpService, 'get').mockReturnValue(of({ status: 404 } as any)); - await expect(service.fetchPlayerInfo('')).rejects.toThrowError('no etf2l profile'); + jest + .spyOn(httpService, 'get') + .mockReturnValue(of({ status: 404 } as any)); + await expect(service.fetchPlayerInfo('')).rejects.toThrowError( + 'no etf2l profile', + ); }); it('should forward any other error', async () => { - jest.spyOn(httpService, 'get').mockReturnValue(of({ status: 403, statusText: 'HAHAHA no.' } as any)); - await expect(service.fetchPlayerInfo('')).rejects.toThrowError('403: HAHAHA no.'); + jest + .spyOn(httpService, 'get') + .mockReturnValue(of({ status: 403, statusText: 'HAHAHA no.' } as any)); + await expect(service.fetchPlayerInfo('')).rejects.toThrowError( + '403: HAHAHA no.', + ); }); }); }); diff --git a/src/players/services/etf2l-profile.service.ts b/src/players/services/etf2l-profile.service.ts index 7118b5988..2724cfe9f 100644 --- a/src/players/services/etf2l-profile.service.ts +++ b/src/players/services/etf2l-profile.service.ts @@ -13,40 +13,43 @@ interface Etf2lPlayerResponse { @Injectable() export class Etf2lProfileService { - private readonly etf2lEndpoint = 'http://api.etf2l.org'; - constructor( - private httpService: HttpService, - ) { } + constructor(private httpService: HttpService) {} // TODO This is steamId or etf2lProfileId async fetchPlayerInfo(steamId: string): Promise { - return this.httpService.get(`${this.etf2lEndpoint}/player/${steamId}.json`).pipe( - catchError(error => { - const response = error.response; - switch (response.status) { - case 404: - return throwError(new Error('no etf2l profile')); - - default: - return throwError(new Error(`${response.status}: ${response.statusText}`)); - } - }), - switchMap(response => { - if (response.status === 200) { - return of(response.data.player); - } else { + return this.httpService + .get(`${this.etf2lEndpoint}/player/${steamId}.json`) + .pipe( + catchError((error) => { + const response = error.response; switch (response.status) { case 404: return throwError(new Error('no etf2l profile')); default: - return throwError(new Error(`${response.status}: ${response.statusText}`)); + return throwError( + new Error(`${response.status}: ${response.statusText}`), + ); } - } - }), - ).toPromise(); + }), + switchMap((response) => { + if (response.status === 200) { + return of(response.data.player); + } else { + switch (response.status) { + case 404: + return throwError(new Error('no etf2l profile')); + + default: + return throwError( + new Error(`${response.status}: ${response.statusText}`), + ); + } + } + }), + ) + .toPromise(); } - } diff --git a/src/players/services/future-player-skill.service.spec.ts b/src/players/services/future-player-skill.service.spec.ts index 658385bc2..f7213c7d6 100644 --- a/src/players/services/future-player-skill.service.spec.ts +++ b/src/players/services/future-player-skill.service.spec.ts @@ -12,7 +12,7 @@ describe('FuturePlayerSkillService', () => { let mongod: MongoMemoryServer; let futurePlayerSkillModel: ReturnModelType; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -21,46 +21,54 @@ describe('FuturePlayerSkillService', () => { typegooseTestingModule(mongod), TypegooseModule.forFeature([FuturePlayerSkill]), ], - providers: [ - FuturePlayerSkillService, - ], + providers: [FuturePlayerSkillService], }).compile(); service = module.get(FuturePlayerSkillService); futurePlayerSkillModel = module.get(getModelToken('FuturePlayerSkill')); }); - afterEach(async () => await futurePlayerSkillModel.deleteMany({ })); + afterEach(async () => await futurePlayerSkillModel.deleteMany({})); it('should be defined', () => { expect(service).toBeDefined(); }); describe('#registerSkill()', () => { - const skill = new Map([[ Tf2ClassName.soldier, 5 ]]); + const skill = new Map([[Tf2ClassName.soldier, 5]]); it('should insert player skill', async () => { await service.registerSkill('FAKE_STEAM_ID', skill); const doc = await futurePlayerSkillModel.findOne(); - expect(doc.toObject()).toEqual(expect.objectContaining({ steamId: 'FAKE_STEAM_ID', skill })); + expect(doc.toObject()).toEqual( + expect.objectContaining({ steamId: 'FAKE_STEAM_ID', skill }), + ); }); describe('when there is skill for the given player registered already', () => { beforeEach(async () => { - await futurePlayerSkillModel.create({ steamId: 'FAKE_STEAM_ID', skill }); + await futurePlayerSkillModel.create({ + steamId: 'FAKE_STEAM_ID', + skill, + }); }); it('should update player skill', async () => { - const newSkill = new Map([[ Tf2ClassName.soldier, 6 ]]); + const newSkill = new Map([[Tf2ClassName.soldier, 6]]); await service.registerSkill('FAKE_STEAM_ID', newSkill); const doc = await futurePlayerSkillModel.findOne(); - expect(doc.toObject()).toEqual(expect.objectContaining({ steamId: 'FAKE_STEAM_ID', skill: newSkill })); + expect(doc.toObject()).toEqual( + expect.objectContaining({ + steamId: 'FAKE_STEAM_ID', + skill: newSkill, + }), + ); }); }); }); describe('#findSkill()', () => { - const skill = new Map([[ 'soldier', 5 ]]); + const skill = new Map([['soldier', 5]]); beforeEach(async () => { await futurePlayerSkillModel.create({ steamId: 'FAKE_STEAM_ID', skill }); @@ -68,7 +76,9 @@ describe('FuturePlayerSkillService', () => { it('should find the skill', async () => { const ret = await service.findSkill('FAKE_STEAM_ID'); - expect(ret.toObject()).toEqual(expect.objectContaining({ steamId: 'FAKE_STEAM_ID', skill })); + expect(ret.toObject()).toEqual( + expect.objectContaining({ steamId: 'FAKE_STEAM_ID', skill }), + ); }); it('should return null', async () => { diff --git a/src/players/services/future-player-skill.service.ts b/src/players/services/future-player-skill.service.ts index 4bf8e08ab..f1b53aa6d 100644 --- a/src/players/services/future-player-skill.service.ts +++ b/src/players/services/future-player-skill.service.ts @@ -6,17 +6,20 @@ import { Tf2ClassName } from '@/shared/models/tf2-class-name'; @Injectable() export class FuturePlayerSkillService { - constructor( - @InjectModel(FuturePlayerSkill) private futurePlayerSkillModel: ReturnModelType, - ) { } + @InjectModel(FuturePlayerSkill) + private futurePlayerSkillModel: ReturnModelType, + ) {} async registerSkill(steamId: string, skill: Map) { - return await this.futurePlayerSkillModel.findOneAndUpdate({ steamId }, { skill }, { new: true, upsert: true }); + return await this.futurePlayerSkillModel.findOneAndUpdate( + { steamId }, + { skill }, + { new: true, upsert: true }, + ); } async findSkill(steamId: string) { return await this.futurePlayerSkillModel.findOne({ steamId }); } - } diff --git a/src/players/services/online-players.service.spec.ts b/src/players/services/online-players.service.spec.ts index be977bb22..dc33d1168 100644 --- a/src/players/services/online-players.service.spec.ts +++ b/src/players/services/online-players.service.spec.ts @@ -37,24 +37,28 @@ describe('OnlinePlayersService', () => { expect(service).toBeDefined(); }); - it('should handle player connections and disconnections properly', async () => new Promise(resolve => { - expect(service.getSocketsForPlayer('FAKE_ID')).toEqual([]); + it('should handle player connections and disconnections properly', async () => + new Promise((resolve) => { + expect(service.getSocketsForPlayer('FAKE_ID')).toEqual([]); - const socket = { id: 'FAKE_SOCKET_ID', request: { user: { logged_in: true, id: 'FAKE_ID' } } }; - playersGateway.playerConnected.next(socket); - expect(service.getSocketsForPlayer('FAKE_ID')).toEqual([ socket ] as any); + const socket = { + id: 'FAKE_SOCKET_ID', + request: { user: { logged_in: true, id: 'FAKE_ID' } }, + }; + playersGateway.playerConnected.next(socket); + expect(service.getSocketsForPlayer('FAKE_ID')).toEqual([socket] as any); - playersGateway.playerConnected.next(socket); - expect(service.getSocketsForPlayer('FAKE_ID')).toEqual([ socket ] as any); + playersGateway.playerConnected.next(socket); + expect(service.getSocketsForPlayer('FAKE_ID')).toEqual([socket] as any); - playersGateway.playerDisconnected.next(socket); - expect(service.getSocketsForPlayer('FAKE_ID')).toEqual([]); + playersGateway.playerDisconnected.next(socket); + expect(service.getSocketsForPlayer('FAKE_ID')).toEqual([]); - events.playerDisconnects.subscribe(({ playerId }) => { - expect(playerId).toEqual('FAKE_ID'); - resolve(); - }); + events.playerDisconnects.subscribe(({ playerId }) => { + expect(playerId).toEqual('FAKE_ID'); + resolve(); + }); - jest.runAllTimers(); - })); + jest.runAllTimers(); + })); }); diff --git a/src/players/services/online-players.service.ts b/src/players/services/online-players.service.ts index 30c451549..dc9476f72 100644 --- a/src/players/services/online-players.service.ts +++ b/src/players/services/online-players.service.ts @@ -8,35 +8,37 @@ type SocketList = Socket[]; @Injectable() export class OnlinePlayersService implements OnModuleInit { - private readonly verifyPlayerTimeout = 10 * 1000; // 10 seconds private logger = new Logger(OnlinePlayersService.name); private sockets = new Map(); - constructor( - private playersGateway: PlayersGateway, - private events: Events, - ) { } + constructor(private playersGateway: PlayersGateway, private events: Events) {} onModuleInit() { - this.playersGateway.playerConnected.subscribe(socket => { + this.playersGateway.playerConnected.subscribe((socket) => { if (socket.request.user.logged_in) { const player = socket.request.user as Player; const sockets = this.sockets.get(player.id) || []; if (!sockets.includes(socket)) { this.logger.debug(`${player.name} connected`); - this.sockets.set(player.id, [ ...sockets, socket ]); + this.sockets.set(player.id, [...sockets, socket]); } } }); - this.playersGateway.playerDisconnected.subscribe(socket => { + this.playersGateway.playerDisconnected.subscribe((socket) => { if (socket.request.user.logged_in) { const player = socket.request.user as Player; this.logger.debug(`${player.name} disconnected`); const sockets = this.getSocketsForPlayer(player.id); - this.sockets.set(player.id, sockets.filter(s => s !== socket)); - setTimeout(() => this.verifyPlayer(player.id), this.verifyPlayerTimeout); + this.sockets.set( + player.id, + sockets.filter((s) => s !== socket), + ); + setTimeout( + () => this.verifyPlayer(player.id), + this.verifyPlayerTimeout, + ); } }); } @@ -51,5 +53,4 @@ export class OnlinePlayersService implements OnModuleInit { this.events.playerDisconnects.next({ playerId }); } } - } diff --git a/src/players/services/player-bans.service.spec.ts b/src/players/services/player-bans.service.spec.ts index 9a9537f07..a82c465ce 100644 --- a/src/players/services/player-bans.service.spec.ts +++ b/src/players/services/player-bans.service.spec.ts @@ -14,7 +14,9 @@ import { Events } from '@/events/events'; jest.mock('./players.service'); class OnlinePlayersServiceStub { - getSocketsForPlayer(playerId: string) { return []; } + getSocketsForPlayer(playerId: string) { + return []; + } } describe('PlayerBansService', () => { @@ -28,14 +30,14 @@ describe('PlayerBansService', () => { let player: DocumentType; let events: Events; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => mongod.stop()); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ PlayerBan, Player ]), + TypegooseModule.forFeature([PlayerBan, Player]), ], providers: [ PlayerBansService, @@ -88,7 +90,9 @@ describe('PlayerBansService', () => { describe('when the given ban does not exist', () => { it('should throw', async () => { - await expect(service.getById(new mongoose.Types.ObjectId().toString())).rejects.toThrow(mongoose.Error.DocumentNotFoundError); + await expect( + service.getById(new mongoose.Types.ObjectId().toString()), + ).rejects.toThrow(mongoose.Error.DocumentNotFoundError); }); }); }); @@ -131,27 +135,29 @@ describe('PlayerBansService', () => { expect(ret).toMatchObject(newBan); }); - it('should emit the playerBanAdded event', async () => new Promise(resolve => { - events.playerBanAdded.subscribe(({ ban }) => { - expect(ban.player.toString()).toEqual(player.id.toString()); - resolve(); - }); - - service.addPlayerBan(newBan); - })); - - it('should emit profile update event on player\'s socket', async () => new Promise(resolve => { - const socket = { - emit: (eventName: string, update: any) => { - expect(eventName).toEqual('profile update'); - expect(update.bans.length).toEqual(2); + it('should emit the playerBanAdded event', async () => + new Promise((resolve) => { + events.playerBanAdded.subscribe(({ ban }) => { + expect(ban.player.toString()).toEqual(player.id.toString()); resolve(); - }, - }; - - onlinePlayersService.getSocketsForPlayer = () => [ socket ]; - service.addPlayerBan(newBan); - })); + }); + + service.addPlayerBan(newBan); + })); + + it("should emit profile update event on player's socket", async () => + new Promise((resolve) => { + const socket = { + emit: (eventName: string, update: any) => { + expect(eventName).toEqual('profile update'); + expect(update.bans.length).toEqual(2); + resolve(); + }, + }; + + onlinePlayersService.getSocketsForPlayer = () => [socket]; + service.addPlayerBan(newBan); + })); }); describe('when adding a ban that has invalid player id', () => { @@ -182,15 +188,16 @@ describe('PlayerBansService', () => { expect(ban.end.getTime()).toBeLessThanOrEqual(new Date().getTime()); }); - it('should emit the playerBanRevoked event', async () => new Promise(resolve => { - events.playerBanRevoked.subscribe(({ ban, adminId }) => { - expect(adminId).toEqual(admin.id); - expect(ban.player.toString()).toEqual(player.id.toString()); - resolve(); - }); + it('should emit the playerBanRevoked event', async () => + new Promise((resolve) => { + events.playerBanRevoked.subscribe(({ ban, adminId }) => { + expect(adminId).toEqual(admin.id); + expect(ban.player.toString()).toEqual(player.id.toString()); + resolve(); + }); - service.revokeBan(mockPlayerBan.id, admin.id); - })); + service.revokeBan(mockPlayerBan.id, admin.id); + })); describe('when attempting to revoke an already expired ban', () => { beforeEach(async () => { @@ -199,7 +206,9 @@ describe('PlayerBansService', () => { }); it('should reject', async () => { - await expect(service.revokeBan(mockPlayerBan.id, admin.id)).rejects.toThrowError(); + await expect( + service.revokeBan(mockPlayerBan.id, admin.id), + ).rejects.toThrowError(); }); }); }); diff --git a/src/players/services/player-bans.service.ts b/src/players/services/player-bans.service.ts index 565bc6047..c1e58505a 100644 --- a/src/players/services/player-bans.service.ts +++ b/src/players/services/player-bans.service.ts @@ -1,4 +1,10 @@ -import { Injectable, OnModuleInit, Logger, Inject, forwardRef } from '@nestjs/common'; +import { + Injectable, + OnModuleInit, + Logger, + Inject, + forwardRef, +} from '@nestjs/common'; import { InjectModel } from 'nestjs-typegoose'; import { PlayerBan } from '../models/player-ban'; import { ReturnModelType } from '@typegoose/typegoose'; @@ -11,42 +17,59 @@ import { WebsocketEvent } from '@/websocket-event'; @Injectable() export class PlayerBansService implements OnModuleInit { - private logger = new Logger(PlayerBansService.name); constructor( - @InjectModel(PlayerBan) private playerBanModel: ReturnModelType, + @InjectModel(PlayerBan) + private playerBanModel: ReturnModelType, private onlinePlayersService: OnlinePlayersService, - @Inject(forwardRef(() => PlayersService)) private playersService: PlayersService, + @Inject(forwardRef(() => PlayersService)) + private playersService: PlayersService, private events: Events, - ) { } + ) {} onModuleInit() { - merge( - this.events.playerBanAdded, - this.events.playerBanRevoked, - ).subscribe(async ({ ban }) => { - const playerId = ban.player.toString(); - const bans = await this.getPlayerActiveBans(playerId); - this.onlinePlayersService.getSocketsForPlayer(playerId).forEach(socket => socket.emit(WebsocketEvent.profileUpdate, { bans })); - }); + merge(this.events.playerBanAdded, this.events.playerBanRevoked).subscribe( + async ({ ban }) => { + const playerId = ban.player.toString(); + const bans = await this.getPlayerActiveBans(playerId); + this.onlinePlayersService + .getSocketsForPlayer(playerId) + .forEach((socket) => + socket.emit(WebsocketEvent.profileUpdate, { bans }), + ); + }, + ); } async getById(banId: string): Promise { - return plainToClass(PlayerBan, await this.playerBanModel.findById(banId).orFail().lean().exec()); + return plainToClass( + PlayerBan, + await this.playerBanModel.findById(banId).orFail().lean().exec(), + ); } async getPlayerBans(playerId: string): Promise { - return plainToClass(PlayerBan, await this.playerBanModel.find({ player: playerId }).sort({ start: -1 }).lean().exec()); + return plainToClass( + PlayerBan, + await this.playerBanModel + .find({ player: playerId }) + .sort({ start: -1 }) + .lean() + .exec(), + ); } async getPlayerActiveBans(playerId: string): Promise { - const plain = await this.playerBanModel.find({ - player: playerId, - end: { - $gte: new Date(), - }, - }).lean().exec(); + const plain = await this.playerBanModel + .find({ + player: playerId, + end: { + $gte: new Date(), + }, + }) + .lean() + .exec(); return plainToClass(PlayerBan, plain); } @@ -54,7 +77,9 @@ export class PlayerBansService implements OnModuleInit { const player = await this.playersService.getById(props.player.toString()); const { id } = await this.playerBanModel.create(props); const addedBan = await this.getById(id); - this.logger.verbose(`ban added for player ${player.id} (reason: ${addedBan.reason})`); + this.logger.verbose( + `ban added for player ${player.id} (reason: ${addedBan.reason})`, + ); this.events.playerBanAdded.next({ ban: addedBan }); return addedBan; } @@ -72,8 +97,17 @@ export class PlayerBansService implements OnModuleInit { return newBan; } - private async updateBan(banId: string, update: Partial): Promise { - return plainToClass(PlayerBan, await this.playerBanModel.findOneAndUpdate({ _id: banId }, update, { new: true }).orFail().lean().exec()); + private async updateBan( + banId: string, + update: Partial, + ): Promise { + return plainToClass( + PlayerBan, + await this.playerBanModel + .findOneAndUpdate({ _id: banId }, update, { new: true }) + .orFail() + .lean() + .exec(), + ); } - } diff --git a/src/players/services/player-skill.service.spec.ts b/src/players/services/player-skill.service.spec.ts index 84e960ab9..9ceaf7da9 100644 --- a/src/players/services/player-skill.service.spec.ts +++ b/src/players/services/player-skill.service.spec.ts @@ -22,9 +22,7 @@ jest.mock('./etf2l-profile.service'); class QueueConfigServiceStub { queueConfig = { - classes: [ - { name: 'soldier' }, - ], + classes: [{ name: 'soldier' }], }; } @@ -42,14 +40,14 @@ describe('PlayerSkillService', () => { let futurePlayerSkillService: FuturePlayerSkillService; let events: Events; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ PlayerSkill, Player ]), + TypegooseModule.forFeature([PlayerSkill, Player]), ], providers: [ PlayerSkillService, @@ -84,7 +82,7 @@ describe('PlayerSkillService', () => { }); afterEach(async () => { - await playerSkillModel.deleteMany({ }); + await playerSkillModel.deleteMany({}); // @ts-expect-error await playersService._reset(); }); @@ -102,30 +100,40 @@ describe('PlayerSkillService', () => { newPlayer = await playersService._createOne(); }); - it('should not update player\'s skill', async () => new Promise(resolve => { - // @ts-expect-error - playersService.playerRegistered.next(newPlayer.id.toString()); - setTimeout(async () => { - expect(await playerSkillModel.findOne({ player: newPlayer.id })).toBe(null); - resolve(); - }, 100); - })); + it("should not update player's skill", async () => + new Promise((resolve) => { + // @ts-expect-error + playersService.playerRegistered.next(newPlayer.id.toString()); + setTimeout(async () => { + expect( + await playerSkillModel.findOne({ player: newPlayer.id }), + ).toBe(null); + resolve(); + }, 100); + })); }); describe('when there is future skill for the given player', () => { beforeEach(() => { - // @ts-expect-error - futurePlayerSkillService.findSkill = () => Promise.resolve({ steamId: mockPlayer.steamId, skill: new Map([['soldier', 2]]) }); + futurePlayerSkillService.findSkill = () => + // @ts-expect-error + Promise.resolve({ + steamId: mockPlayer.steamId, + skill: new Map([['soldier', 2]]), + }); }); - it('should update player\'s skill', async () => new Promise(resolve => { - // @ts-expect-error - playersService.playerRegistered.next(mockPlayer.id.toString()); - setTimeout(async () => { - expect(await playerSkillModel.findOne({ player: mockPlayer.id })).toBeTruthy(); - resolve(); - }, 100); - })); + it("should update player's skill", async () => + new Promise((resolve) => { + // @ts-expect-error + playersService.playerRegistered.next(mockPlayer.id.toString()); + setTimeout(async () => { + expect( + await playerSkillModel.findOne({ player: mockPlayer.id }), + ).toBeTruthy(); + resolve(); + }, 100); + })); }); }); @@ -153,22 +161,31 @@ describe('PlayerSkillService', () => { describe('#setPlayerSkill()', () => { it('should update player skill', async () => { - const ret = await service.setPlayerSkill(mockPlayer.id, new Map([[Tf2ClassName.soldier, 2]])); + const ret = await service.setPlayerSkill( + mockPlayer.id, + new Map([[Tf2ClassName.soldier, 2]]), + ); expect(ret.size).toEqual(1); expect(ret.get(Tf2ClassName.soldier)).toEqual(2); }); - it('should emit the playerSkillChanged event', async () => new Promise(resolve => { - events.playerSkillChanged.subscribe(({ playerId, oldSkill, newSkill, adminId }) => { - expect(playerId).toEqual(mockPlayer.id); - expect(oldSkill.get(Tf2ClassName.soldier)).toEqual(4); - expect(newSkill.get(Tf2ClassName.soldier)).toEqual(2); - expect(adminId).toBeUndefined(); - resolve(); - }); - - service.setPlayerSkill(mockPlayer.id, new Map([[Tf2ClassName.soldier, 2]])); - })); + it('should emit the playerSkillChanged event', async () => + new Promise((resolve) => { + events.playerSkillChanged.subscribe( + ({ playerId, oldSkill, newSkill, adminId }) => { + expect(playerId).toEqual(mockPlayer.id); + expect(oldSkill.get(Tf2ClassName.soldier)).toEqual(4); + expect(newSkill.get(Tf2ClassName.soldier)).toEqual(2); + expect(adminId).toBeUndefined(); + resolve(); + }, + ); + + service.setPlayerSkill( + mockPlayer.id, + new Map([[Tf2ClassName.soldier, 2]]), + ); + })); }); describe('#importPlayerSkills()', () => { diff --git a/src/players/services/player-skill.service.ts b/src/players/services/player-skill.service.ts index 8b2d8c4d1..1678b8bfd 100644 --- a/src/players/services/player-skill.service.ts +++ b/src/players/services/player-skill.service.ts @@ -16,19 +16,22 @@ export type PlayerSkillType = PlayerSkill['skill']; @Injectable() @Console() export class PlayerSkillService implements OnModuleInit { - constructor( - @InjectModel(PlayerSkill) private playerSkillModel: ReturnModelType, - @Inject(forwardRef(() => PlayersService)) private playersService: PlayersService, + @InjectModel(PlayerSkill) + private playerSkillModel: ReturnModelType, + @Inject(forwardRef(() => PlayersService)) + private playersService: PlayersService, private queueConfigService: QueueConfigService, private futurePlayerSkillService: FuturePlayerSkillService, private etf2lProfileService: Etf2lProfileService, private events: Events, - ) { } + ) {} onModuleInit() { this.events.playerRegisters.subscribe(async ({ player }) => { - const futureSkill = await this.futurePlayerSkillService.findSkill(player.steamId); + const futureSkill = await this.futurePlayerSkillService.findSkill( + player.steamId, + ); if (futureSkill) { await this.setPlayerSkill(player.id, futureSkill.skill); } @@ -36,17 +39,34 @@ export class PlayerSkillService implements OnModuleInit { } async getAll(): Promise { - return await this.playerSkillModel.find({ }); + return await this.playerSkillModel.find({}); } async getPlayerSkill(playerId: string): Promise { return (await this.playerSkillModel.findOne({ player: playerId }))?.skill; } - async setPlayerSkill(playerId: string, skill: PlayerSkillType, adminId?: string): Promise { - const oldSkill = (await this.playerSkillModel.findOne({ player: playerId }))?.skill || new Map(); - const newSkill = (await this.playerSkillModel.findOneAndUpdate({ player: playerId }, { skill }, { new: true, upsert: true })).skill; - this.events.playerSkillChanged.next({ playerId, oldSkill, newSkill, adminId }); + async setPlayerSkill( + playerId: string, + skill: PlayerSkillType, + adminId?: string, + ): Promise { + const oldSkill = + (await this.playerSkillModel.findOne({ player: playerId }))?.skill || + new Map(); + const newSkill = ( + await this.playerSkillModel.findOneAndUpdate( + { player: playerId }, + { skill }, + { new: true, upsert: true }, + ) + ).skill; + this.events.playerSkillChanged.next({ + playerId, + oldSkill, + newSkill, + adminId, + }); return newSkill; } @@ -70,20 +90,29 @@ export class PlayerSkillService implements OnModuleInit { let i = 0; for await (const line of rl) { - const [ etf2lProfileId, ...rawSkill ] = line.split(/;|,/); + const [etf2lProfileId, ...rawSkill] = line.split(/;|,/); const skill = new Map( - this.queueConfigService.queueConfig.classes - .map((c, index) => [ c.name, parseInt(rawSkill[index], 10) ]) + this.queueConfigService.queueConfig.classes.map((c, index) => [ + c.name, + parseInt(rawSkill[index], 10), + ]), ); try { - const player = await this.playersService.findByEtf2lProfileId(parseInt(etf2lProfileId, 10)); + const player = await this.playersService.findByEtf2lProfileId( + parseInt(etf2lProfileId, 10), + ); if (player) { await this.setPlayerSkill(player.id, skill); } else { - const etf2lProfile = await this.etf2lProfileService.fetchPlayerInfo(etf2lProfileId); - await this.futurePlayerSkillService.registerSkill(etf2lProfile.steam.id64, skill); + const etf2lProfile = await this.etf2lProfileService.fetchPlayerInfo( + etf2lProfileId, + ); + await this.futurePlayerSkillService.registerSkill( + etf2lProfile.steam.id64, + skill, + ); } i += 1; } catch (e) { @@ -93,5 +122,4 @@ export class PlayerSkillService implements OnModuleInit { spinner.succeed(`Imported skills for ${i} players.`); } - } diff --git a/src/players/services/players.service.spec.ts b/src/players/services/players.service.spec.ts index db362e5fe..aebd4a377 100644 --- a/src/players/services/players.service.spec.ts +++ b/src/players/services/players.service.spec.ts @@ -23,7 +23,7 @@ import { PlayerRole } from '../models/player-role'; import { ConfigurationService } from '@/configuration/services/configuration.service'; jest.mock('./etf2l-profile.service'); -jest.mock( './online-players.service'); +jest.mock('./online-players.service'); jest.mock('@/configuration/services/configuration.service'); class EnvironmentStub { @@ -39,12 +39,18 @@ class GamesServiceStub { medic: 92, }; - getPlayerGameCount(playerId: string, options: any) { return 220; } - getPlayerPlayedClassCount(playerId: string) { return this.classCount; } + getPlayerGameCount(playerId: string, options: any) { + return 220; + } + getPlayerPlayedClassCount(playerId: string) { + return this.classCount; + } } class SteamApiServiceStub { - getTf2InGameHours(steamId64: string) { return Promise.resolve(800); } + getTf2InGameHours(steamId64: string) { + return Promise.resolve(800); + } } describe('PlayersService', () => { @@ -61,7 +67,7 @@ describe('PlayersService', () => { let socket: Socket; let configurationService: jest.Mocked; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -104,10 +110,7 @@ describe('PlayersService', () => { etf2lProfileService.fetchPlayerInfo.mockResolvedValue({ // http://api.etf2l.org/player/112758 bans: null, - classes: [ - 'Soldier', - 'Medic', - ], + classes: ['Soldier', 'Medic'], country: 'Poland', id: 112758, name: 'maly', @@ -121,7 +124,7 @@ describe('PlayersService', () => { await service.onModuleInit(); }); - afterEach(async () => await playerModel.deleteMany({ })); + afterEach(async () => await playerModel.deleteMany({})); it('should be defined', () => { expect(service).toBeDefined(); @@ -183,11 +186,13 @@ describe('PlayersService', () => { it('should query playerModel', async () => { const player = await service.findByTwitchUserId('FAKE_TWITCH_TV_USER_ID'); - expect(player).toEqual(expect.objectContaining({ - twitchTvUser: expect.objectContaining({ - userId: 'FAKE_TWITCH_TV_USER_ID', + expect(player).toEqual( + expect.objectContaining({ + twitchTvUser: expect.objectContaining({ + userId: 'FAKE_TWITCH_TV_USER_ID', + }), }), - })); + ); }); }); @@ -195,7 +200,7 @@ describe('PlayersService', () => { it('should find the bot', async () => { expect(await service.findBot()).toMatchObject({ name: 'FAKE_BOT_NAME', - roles: [ PlayerRole.bot ], + roles: [PlayerRole.bot], }); }); }); @@ -217,13 +222,17 @@ describe('PlayersService', () => { configurationService.getMinimumTf2InGameHours.mockResolvedValue(500); }); - describe('when an ETF2L profile doesn\'t exist', () => { + describe("when an ETF2L profile doesn't exist", () => { beforeEach(() => { - etf2lProfileService.fetchPlayerInfo.mockRejectedValue(new Error('no etf2l profile')); + etf2lProfileService.fetchPlayerInfo.mockRejectedValue( + new Error('no etf2l profile'), + ); }); it('should deny creating tf2pickup.pl profile', async () => { - await expect(service.createPlayer(mockSteamProfile)).rejects.toThrowError('no etf2l profile'); + await expect( + service.createPlayer(mockSteamProfile), + ).rejects.toThrowError('no etf2l profile'); }); }); @@ -237,22 +246,22 @@ describe('PlayersService', () => { start: 0, }, ], - classes: [ - 'Scout', - 'Soldier', - 'Sniper', - ], + classes: ['Scout', 'Soldier', 'Sniper'], country: 'Russia', id: 129205, name: 'Tixx', }; beforeEach(() => { - etf2lProfileService.fetchPlayerInfo.mockResolvedValue(blacklistedProfile); + etf2lProfileService.fetchPlayerInfo.mockResolvedValue( + blacklistedProfile, + ); }); it('should deny creating tf2pickup.pl profile', async () => { - await expect(service.createPlayer(mockSteamProfile)).rejects.toThrowError('this account is banned on ETF2L'); + await expect( + service.createPlayer(mockSteamProfile), + ).rejects.toThrowError('this account is banned on ETF2L'); }); }); @@ -277,14 +286,15 @@ describe('PlayersService', () => { expect(ret.roles.includes(PlayerRole.superUser)).toBe(true); }); - it('should emit the playerRegisters event', async () => new Promise(resolve => { - events.playerRegisters.subscribe(({ player }) => { - expect(player).toBeTruthy(); - expect(player.steamId).toEqual(mockSteamProfile.id); - resolve(); - }); - service.createPlayer(mockSteamProfile); - })); + it('should emit the playerRegisters event', async () => + new Promise((resolve) => { + events.playerRegisters.subscribe(({ player }) => { + expect(player).toBeTruthy(); + expect(player.steamId).toEqual(mockSteamProfile.id); + resolve(); + }); + service.createPlayer(mockSteamProfile); + })); describe('when TF2 in-game hours requirements are not met', () => { beforeEach(() => { @@ -292,17 +302,23 @@ describe('PlayersService', () => { }); it('should deny', async () => { - await expect(service.createPlayer(mockSteamProfile)).rejects.toThrow(InsufficientTf2InGameHoursError); + await expect(service.createPlayer(mockSteamProfile)).rejects.toThrow( + InsufficientTf2InGameHoursError, + ); }); }); describe('when TF2 in-game hours could not be fetched', () => { beforeEach(() => { - jest.spyOn(steamApiService, 'getTf2InGameHours').mockRejectedValue(new Tf2InGameHoursVerificationError('')); + jest + .spyOn(steamApiService, 'getTf2InGameHours') + .mockRejectedValue(new Tf2InGameHoursVerificationError('')); }); it('should deny', async () => { - await expect(service.createPlayer(mockSteamProfile)).rejects.toThrow(Tf2InGameHoursVerificationError); + await expect(service.createPlayer(mockSteamProfile)).rejects.toThrow( + Tf2InGameHoursVerificationError, + ); }); describe('and nobody cares', () => { @@ -311,7 +327,9 @@ describe('PlayersService', () => { }); it('should pass', async () => { - await expect(service.createPlayer(mockSteamProfile)).resolves.toBeTruthy(); + await expect( + service.createPlayer(mockSteamProfile), + ).resolves.toBeTruthy(); }); }); }); @@ -319,16 +337,25 @@ describe('PlayersService', () => { describe('#forceCreatePlayer()', () => { it('should create player', async () => { - const player = await service.forceCreatePlayer({ name: 'FAKE_FORCE_PLAYER_NAME', steamId: 'FAKE_FORCE_STEAM_ID' }); - expect(player).toMatchObject({ name: 'FAKE_FORCE_PLAYER_NAME', steamId: 'FAKE_FORCE_STEAM_ID' }); + const player = await service.forceCreatePlayer({ + name: 'FAKE_FORCE_PLAYER_NAME', + steamId: 'FAKE_FORCE_STEAM_ID', + }); + expect(player).toMatchObject({ + name: 'FAKE_FORCE_PLAYER_NAME', + steamId: 'FAKE_FORCE_STEAM_ID', + }); expect(await playerModel.findById(player._id)).toBeTruthy(); }); describe('when the player has ETF2L account', () => { it('should automatically assign ETF2L id', async () => { - const player = await service.forceCreatePlayer({ name: 'FAKE_FORCE_PLAYER_NAME', steamId: 'FAKE_FORCE_STEAM_ID' }); + const player = await service.forceCreatePlayer({ + name: 'FAKE_FORCE_PLAYER_NAME', + steamId: 'FAKE_FORCE_STEAM_ID', + }); expect(player.etf2lProfileId).toEqual(112758); - }) + }); }); }); @@ -340,15 +367,20 @@ describe('PlayersService', () => { describe('when the given user does not exist', () => { it('should throw an error', async () => { - await expect(service.registerTwitchAccount(new ObjectId().toString(), { - userId: 'FAKE_TWITCH_TV_USER_ID', - login: 'FAKE_TWITCH_TV_LOGIN', - })).rejects.toThrow(mongoose.Error.DocumentNotFoundError); + await expect( + service.registerTwitchAccount(new ObjectId().toString(), { + userId: 'FAKE_TWITCH_TV_USER_ID', + login: 'FAKE_TWITCH_TV_LOGIN', + }), + ).rejects.toThrow(mongoose.Error.DocumentNotFoundError); }); }); it('should save the twitch user id', async () => { - const ret = await service.registerTwitchAccount(mockPlayer.id, twitchTvUser); + const ret = await service.registerTwitchAccount( + mockPlayer.id, + twitchTvUser, + ); expect(ret.twitchTvUser).toEqual(expect.objectContaining(twitchTvUser)); }); @@ -358,7 +390,7 @@ describe('PlayersService', () => { await service.registerTwitchAccount(mockPlayer.id, twitchTvUser); expect(socket.emit).toHaveBeenCalledWith(WebsocketEvent.profileUpdate, { - player: expect.objectContaining({ twitchTvUser }) + player: expect.objectContaining({ twitchTvUser }), }); }); }); @@ -375,7 +407,9 @@ describe('PlayersService', () => { it('should remove the twitchTvProfile', async () => { const ret = await service.removeTwitchTvProfile(mockPlayer.id); expect(ret.twitchTvUser).toBe(undefined); - expect((await playerModel.findById(mockPlayer.id)).twitchTvUser).toBe(undefined); + expect((await playerModel.findById(mockPlayer.id)).twitchTvUser).toBe( + undefined, + ); }); }); @@ -409,27 +443,42 @@ describe('PlayersService', () => { }); it('should update player name', async () => { - const ret = await service.updatePlayer(mockPlayer.id, { name: 'NEW_NAME' }, admin.id); + const ret = await service.updatePlayer( + mockPlayer.id, + { name: 'NEW_NAME' }, + admin.id, + ); expect(ret.name).toEqual('NEW_NAME'); }); it('should update player roles', async () => { - const ret1 = await service.updatePlayer(mockPlayer.id, { roles: [ PlayerRole.admin ] }, admin.id); - expect(ret1.roles).toEqual([ PlayerRole.admin ]); - - const ret2 = await service.updatePlayer(mockPlayer.id, { roles: [] }, admin.id); + const ret1 = await service.updatePlayer( + mockPlayer.id, + { roles: [PlayerRole.admin] }, + admin.id, + ); + expect(ret1.roles).toEqual([PlayerRole.admin]); + + const ret2 = await service.updatePlayer( + mockPlayer.id, + { roles: [] }, + admin.id, + ); expect(ret2.roles).toEqual([]); }); it('should emit updated player over websocket', async () => { await service.updatePlayer(mockPlayer.id, { name: 'NEW_NAME' }, admin.id); - expect(socket.emit).toHaveBeenCalledWith(WebsocketEvent.profileUpdate, - { player: expect.objectContaining({ name: 'NEW_NAME' }) }); + expect(socket.emit).toHaveBeenCalledWith(WebsocketEvent.profileUpdate, { + player: expect.objectContaining({ name: 'NEW_NAME' }), + }); }); describe('when the given player does not exist', () => { it('should reject', async () => { - await expect(service.updatePlayer(new ObjectId().toString(), { }, admin.id)).rejects.toThrowError(); + await expect( + service.updatePlayer(new ObjectId().toString(), {}, admin.id), + ).rejects.toThrowError(); }); }); }); @@ -440,8 +489,10 @@ describe('PlayersService', () => { expect(ret.hasAcceptedRules).toBe(true); }); - it('should fail if the given user doesn\'t exist', async () => { - await expect(service.acceptTerms(new ObjectId().toString())).rejects.toThrow(mongoose.Error.DocumentNotFoundError); + it("should fail if the given user doesn't exist", async () => { + await expect( + service.acceptTerms(new ObjectId().toString()), + ).rejects.toThrow(mongoose.Error.DocumentNotFoundError); }); }); @@ -452,10 +503,10 @@ describe('PlayersService', () => { player: 'FAKE_ID', gamesPlayed: 220, classesPlayed: new Map([ - [ Tf2ClassName.scout, 19 ], - [ Tf2ClassName.soldier, 102 ], - [ Tf2ClassName.demoman, 0 ], - [ Tf2ClassName.medic, 92 ], + [Tf2ClassName.scout, 19], + [Tf2ClassName.soldier, 102], + [Tf2ClassName.demoman, 0], + [Tf2ClassName.medic, 92], ]), }); }); diff --git a/src/players/services/players.service.ts b/src/players/services/players.service.ts index f4c43c658..4cb03cc34 100644 --- a/src/players/services/players.service.ts +++ b/src/players/services/players.service.ts @@ -1,4 +1,10 @@ -import { Injectable, Logger, Inject, forwardRef, OnModuleInit } from '@nestjs/common'; +import { + Injectable, + Logger, + Inject, + forwardRef, + OnModuleInit, +} from '@nestjs/common'; import { Environment } from '@/environment/environment'; import { Player } from '../models/player'; import { mongoose, ReturnModelType } from '@typegoose/typegoose'; @@ -28,7 +34,6 @@ type ForceCreatePlayerOptions = Pick; @Injectable() export class PlayersService implements OnModuleInit { - private logger = new Logger(PlayersService.name); constructor( @@ -40,7 +45,7 @@ export class PlayersService implements OnModuleInit { private events: Events, private onlinePlayersService: OnlinePlayersService, private configurationService: ConfigurationService, - ) { } + ) {} async onModuleInit() { try { @@ -49,7 +54,7 @@ export class PlayersService implements OnModuleInit { if (error instanceof mongoose.Error.DocumentNotFoundError) { await this.playerModel.create({ name: this.environment.botName, - roles: [ PlayerRole.bot ], + roles: [PlayerRole.bot], }); } else { throw error; @@ -59,34 +64,65 @@ export class PlayersService implements OnModuleInit { this.events.playerUpdates.subscribe(({ newPlayer }) => { this.onlinePlayersService .getSocketsForPlayer(newPlayer.id) - .forEach(socket => socket.emit(WebsocketEvent.profileUpdate, { - player: classToPlain(newPlayer), - })); + .forEach((socket) => + socket.emit(WebsocketEvent.profileUpdate, { + player: classToPlain(newPlayer), + }), + ); }); } async getAll(): Promise { - return plainToClass(Player, await this.playerModel.find({ roles: { $ne: PlayerRole.bot } }).lean().exec()); + return plainToClass( + Player, + await this.playerModel + .find({ roles: { $ne: PlayerRole.bot } }) + .lean() + .exec(), + ); } async getById(id: string | ObjectId): Promise { - return plainToClass(Player, await this.playerModel.findById(id).orFail().lean().exec()); + return plainToClass( + Player, + await this.playerModel.findById(id).orFail().lean().exec(), + ); } async findBySteamId(steamId: string): Promise { - return plainToClass(Player, await this.playerModel.findOne({ steamId }).orFail().lean().exec()); + return plainToClass( + Player, + await this.playerModel.findOne({ steamId }).orFail().lean().exec(), + ); } async findByEtf2lProfileId(etf2lProfileId: number): Promise { - return plainToClass(Player, await this.playerModel.findOne({ etf2lProfileId }).orFail().lean().exec()); + return plainToClass( + Player, + await this.playerModel.findOne({ etf2lProfileId }).orFail().lean().exec(), + ); } async findByTwitchUserId(twitchTvUserId: string): Promise { - return plainToClass(Player, await this.playerModel.findOne({ 'twitchTvUser.userId': twitchTvUserId }).orFail().lean().exec()); + return plainToClass( + Player, + await this.playerModel + .findOne({ 'twitchTvUser.userId': twitchTvUserId }) + .orFail() + .lean() + .exec(), + ); } async findBot(): Promise { - return plainToClass(Player, this.playerModel.findOne({ name: this.environment.botName }).orFail().lean().exec()); + return plainToClass( + Player, + this.playerModel + .findOne({ name: this.environment.botName }) + .orFail() + .lean() + .exec(), + ); } async createPlayer(steamProfile: SteamProfile): Promise { @@ -96,9 +132,14 @@ export class PlayersService implements OnModuleInit { let name = steamProfile.displayName; try { - etf2lProfile = await this.etf2lProfileService.fetchPlayerInfo(steamProfile.id); + etf2lProfile = await this.etf2lProfileService.fetchPlayerInfo( + steamProfile.id, + ); - if (etf2lProfile.bans?.filter(ban => ban.end > Date.now() / 1000).length > 0) { + if ( + etf2lProfile.bans?.filter((ban) => ban.end > Date.now() / 1000).length > + 0 + ) { throw new AccountBannedError('this account is banned on ETF2L'); } @@ -115,14 +156,19 @@ export class PlayersService implements OnModuleInit { large: steamProfile.photos[2].value, }; - const id = (await this.playerModel.create({ - steamId: steamProfile.id, - name, - avatar, - roles: this.environment.superUser === steamProfile.id ? [ PlayerRole.superUser, PlayerRole.admin ] : [], - etf2lProfileId: etf2lProfile?.id, - hasAcceptedRules: false, - }))._id; + const id = ( + await this.playerModel.create({ + steamId: steamProfile.id, + name, + avatar, + roles: + this.environment.superUser === steamProfile.id + ? [PlayerRole.superUser, PlayerRole.admin] + : [], + etf2lProfileId: etf2lProfile?.id, + hasAcceptedRules: false, + }) + )._id; const player = await this.getById(id); this.logger.verbose(`created new player (name: ${player?.name})`); @@ -133,12 +179,16 @@ export class PlayersService implements OnModuleInit { /** * Create player account, omitting all checks. */ - async forceCreatePlayer(playerData: ForceCreatePlayerOptions): Promise { + async forceCreatePlayer( + playerData: ForceCreatePlayerOptions, + ): Promise { let etf2lProfile: Etf2lProfile; try { - etf2lProfile = await this.etf2lProfileService.fetchPlayerInfo(playerData.steamId); - // eslint-disable-next-line no-empty - } catch (error) { } + etf2lProfile = await this.etf2lProfileService.fetchPlayerInfo( + playerData.steamId, + ); + // eslint-disable-next-line no-empty + } catch (error) {} const { id } = await this.playerModel.create({ etf2lProfileId: etf2lProfile?.id, @@ -151,7 +201,10 @@ export class PlayersService implements OnModuleInit { } // FIXME Move this method to TwitchModule - async registerTwitchAccount(playerId: string, twitchTvUser: TwitchTvUser): Promise { + async registerTwitchAccount( + playerId: string, + twitchTvUser: TwitchTvUser, + ): Promise { return this.updatePlayer(playerId, { twitchTvUser }); } @@ -161,12 +214,28 @@ export class PlayersService implements OnModuleInit { } async getUsersWithTwitchTvAccount(): Promise { - return plainToClass(Player, await this.playerModel.find({ twitchTvUser: { $exists: true } }).lean().exec()); + return plainToClass( + Player, + await this.playerModel + .find({ twitchTvUser: { $exists: true } }) + .lean() + .exec(), + ); } - async updatePlayer(playerId: string, update: UpdateQuery, adminId?: string): Promise { + async updatePlayer( + playerId: string, + update: UpdateQuery, + adminId?: string, + ): Promise { const oldPlayer = await this.getById(playerId); - const newPlayer = plainToClass(Player, await this.playerModel.findOneAndUpdate({ _id: playerId }, update, { new: true }).lean().exec()); + const newPlayer = plainToClass( + Player, + await this.playerModel + .findOneAndUpdate({ _id: playerId }, update, { new: true }) + .lean() + .exec(), + ); this.events.playerUpdates.next({ oldPlayer, newPlayer, adminId }); return newPlayer; } @@ -182,8 +251,14 @@ export class PlayersService implements OnModuleInit { async getPlayerStats(playerId: string): Promise { return new PlayerStats({ player: playerId, - gamesPlayed: await this.gamesService.getPlayerGameCount(playerId, { endedOnly: true }), - classesPlayed: new Map(Object.entries(await this.gamesService.getPlayerPlayedClassCount(playerId))) as Map, + gamesPlayed: await this.gamesService.getPlayerGameCount(playerId, { + endedOnly: true, + }), + classesPlayed: new Map( + Object.entries( + await this.gamesService.getPlayerPlayedClassCount(playerId), + ), + ) as Map, }); } @@ -195,12 +270,14 @@ export class PlayersService implements OnModuleInit { throw new InsufficientTf2InGameHoursError(); } } catch (error) { - if (error instanceof Tf2InGameHoursVerificationError && minimumTf2InGameHours <= 0) { + if ( + error instanceof Tf2InGameHoursVerificationError && + minimumTf2InGameHours <= 0 + ) { return; } else { throw error; } } } - } diff --git a/src/players/services/steam-api.service.spec.ts b/src/players/services/steam-api.service.spec.ts index b0c38a7ef..7b69b5d76 100644 --- a/src/players/services/steam-api.service.spec.ts +++ b/src/players/services/steam-api.service.spec.ts @@ -7,7 +7,9 @@ import { HttpService } from '@nestjs/common'; import { Environment } from '@/environment/environment'; import { Tf2InGameHoursVerificationError } from '../errors/tf2-in-game-hours-verification.error'; -const steamApiResponseJson = readFileSync(resolve(__dirname, '..', 'steam-api-response.json')); +const steamApiResponseJson = readFileSync( + resolve(__dirname, '..', 'steam-api-response.json'), +); class HttpServiceStub { get(url: string) { @@ -47,7 +49,9 @@ describe('SteamApiService', () => { it('should query the correct URL', async () => { const spy = jest.spyOn(httpService, 'get'); await service.getTf2InGameHours('FAKE_STEAM_ID'); - expect(spy).toHaveBeenCalledWith('http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=440&key=FAKE_STEAM_API_KEY&steamid=FAKE_STEAM_ID&format=json'); + expect(spy).toHaveBeenCalledWith( + 'http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=440&key=FAKE_STEAM_API_KEY&steamid=FAKE_STEAM_ID&format=json', + ); }); describe('with response 200', () => { @@ -59,11 +63,15 @@ describe('SteamApiService', () => { describe('with response code 500', () => { beforeEach(() => { - jest.spyOn(httpService, 'get').mockReturnValue(throwError({ status: 500 } as any)); + jest + .spyOn(httpService, 'get') + .mockReturnValue(throwError({ status: 500 } as any)); }); it('should throw an error', async () => { - await expect(service.getTf2InGameHours('FAKE_STEAM_ID')).rejects.toThrow(Tf2InGameHoursVerificationError); + await expect( + service.getTf2InGameHours('FAKE_STEAM_ID'), + ).rejects.toThrow(Tf2InGameHoursVerificationError); }); }); }); diff --git a/src/players/services/steam-api.service.ts b/src/players/services/steam-api.service.ts index df39f9d4c..aa9387c8c 100644 --- a/src/players/services/steam-api.service.ts +++ b/src/players/services/steam-api.service.ts @@ -11,12 +11,11 @@ interface UserStatsForGameResponse { gameName: string; stats: { name: string; value: number }[]; achievements: { name: string; achieved: 1 }[]; - } + }; } @Injectable() export class SteamApiService { - private readonly steamApiEndpoint = 'http://api.steampowered.com'; private readonly userStatsEndpoint = `${this.steamApiEndpoint}/ISteamUserStats`; private readonly userStatsForGameEndpoint = `${this.userStatsEndpoint}/GetUserStatsForGame/v0002`; @@ -25,20 +24,26 @@ export class SteamApiService { constructor( private httpService: HttpService, private environment: Environment, - ) { } + ) {} async getTf2InGameHours(steamId64: string): Promise { - return this.httpService.get(`${this.userStatsForGameEndpoint}/?appid=${this.tf2AppId}&key=${this.environment.steamApiKey}&steamid=${steamId64}&format=json`).pipe( - switchMap(response => { - return of( - response.data.playerstats.stats - .filter(s => /\.accum\.iPlayTime$/.test(s.name)) - .reduce((sum, curr) => sum + curr.value, 0) - ); - }), - map(seconds => floor(seconds / 60 / 60)), - catchError(error => throwError(new Tf2InGameHoursVerificationError(error))), - ).toPromise(); + return this.httpService + .get( + `${this.userStatsForGameEndpoint}/?appid=${this.tf2AppId}&key=${this.environment.steamApiKey}&steamid=${steamId64}&format=json`, + ) + .pipe( + switchMap((response) => { + return of( + response.data.playerstats.stats + .filter((s) => /\.accum\.iPlayTime$/.test(s.name)) + .reduce((sum, curr) => sum + curr.value, 0), + ); + }), + map((seconds) => floor(seconds / 60 / 60)), + catchError((error) => + throwError(new Tf2InGameHoursVerificationError(error)), + ), + ) + .toPromise(); } - } diff --git a/src/plugins/discord/discord.module.ts b/src/plugins/discord/discord.module.ts index 978897b8f..c0648fd04 100644 --- a/src/plugins/discord/discord.module.ts +++ b/src/plugins/discord/discord.module.ts @@ -7,17 +7,8 @@ import { AdminNotificationsService } from './services/admin-notifications.servic @Global() @Module({ - imports: [ - forwardRef(() => QueueModule), - forwardRef(() => PlayersModule), - ], - providers: [ - DiscordService, - QueuePromptsService, - AdminNotificationsService, - ], - exports: [ - DiscordService, - ], + imports: [forwardRef(() => QueueModule), forwardRef(() => PlayersModule)], + providers: [DiscordService, QueuePromptsService, AdminNotificationsService], + exports: [DiscordService], }) -export class DiscordModule { } +export class DiscordModule {} diff --git a/src/plugins/discord/emojis-to-install.ts b/src/plugins/discord/emojis-to-install.ts index 9666807a6..3dd20dd1a 100644 --- a/src/plugins/discord/emojis-to-install.ts +++ b/src/plugins/discord/emojis-to-install.ts @@ -1,11 +1,38 @@ -export const emojisToInstall: { name: string, sourceUrl: string }[] = [ - { name: 'tf2scout', sourceUrl: `https://wiki.teamfortress.com/w/images/a/ad/Leaderboard_class_scout.png` }, - { name: 'tf2soldier', sourceUrl: `https://wiki.teamfortress.com/w/images/9/96/Leaderboard_class_soldier.png` }, - { name: 'tf2pyro', sourceUrl: `https://wiki.teamfortress.com/w/images/8/80/Leaderboard_class_pyro.png` }, - { name: 'tf2demoman', sourceUrl: `https://wiki.teamfortress.com/w/images/4/47/Leaderboard_class_demoman.png` }, - { name: 'tf2heavy', sourceUrl: `https://wiki.teamfortress.com/w/images/5/5a/Leaderboard_class_heavy.png` }, - { name: 'tf2engineer', sourceUrl: `https://wiki.teamfortress.com/w/images/1/12/Leaderboard_class_engineer.png` }, - { name: 'tf2medic', sourceUrl: `https://wiki.teamfortress.com/w/images/e/e5/Leaderboard_class_medic.png` }, - { name: 'tf2sniper', sourceUrl: `https://wiki.teamfortress.com/w/images/f/fe/Leaderboard_class_sniper.png` }, - { name: 'tf2spy', sourceUrl: `https://wiki.teamfortress.com/w/images/3/33/Leaderboard_class_spy.png` }, +export const emojisToInstall: { name: string; sourceUrl: string }[] = [ + { + name: 'tf2scout', + sourceUrl: `https://wiki.teamfortress.com/w/images/a/ad/Leaderboard_class_scout.png`, + }, + { + name: 'tf2soldier', + sourceUrl: `https://wiki.teamfortress.com/w/images/9/96/Leaderboard_class_soldier.png`, + }, + { + name: 'tf2pyro', + sourceUrl: `https://wiki.teamfortress.com/w/images/8/80/Leaderboard_class_pyro.png`, + }, + { + name: 'tf2demoman', + sourceUrl: `https://wiki.teamfortress.com/w/images/4/47/Leaderboard_class_demoman.png`, + }, + { + name: 'tf2heavy', + sourceUrl: `https://wiki.teamfortress.com/w/images/5/5a/Leaderboard_class_heavy.png`, + }, + { + name: 'tf2engineer', + sourceUrl: `https://wiki.teamfortress.com/w/images/1/12/Leaderboard_class_engineer.png`, + }, + { + name: 'tf2medic', + sourceUrl: `https://wiki.teamfortress.com/w/images/e/e5/Leaderboard_class_medic.png`, + }, + { + name: 'tf2sniper', + sourceUrl: `https://wiki.teamfortress.com/w/images/f/fe/Leaderboard_class_sniper.png`, + }, + { + name: 'tf2spy', + sourceUrl: `https://wiki.teamfortress.com/w/images/3/33/Leaderboard_class_spy.png`, + }, ]; diff --git a/src/plugins/discord/notifications/game-force-ended.ts b/src/plugins/discord/notifications/game-force-ended.ts index b9dce4157..6da908552 100644 --- a/src/plugins/discord/notifications/game-force-ended.ts +++ b/src/plugins/discord/notifications/game-force-ended.ts @@ -17,10 +17,17 @@ interface GameForceEndedOptions { }; } -export const gameForceEnded = (options: GameForceEndedOptions) => new MessageEmbed() - .setColor(Colors.GameForceEnded) - .setAuthor(options.admin.name, options.admin.avatarUrl, options.admin.profileUrl) - .setTitle('Game force-ended') - .setDescription(`Game number: **[${options.game.number}](${options.game.url})**`) - .setFooter(options.client.name, options.client.iconUrl) - .setTimestamp(); +export const gameForceEnded = (options: GameForceEndedOptions) => + new MessageEmbed() + .setColor(Colors.GameForceEnded) + .setAuthor( + options.admin.name, + options.admin.avatarUrl, + options.admin.profileUrl, + ) + .setTitle('Game force-ended') + .setDescription( + `Game number: **[${options.game.number}](${options.game.url})**`, + ) + .setFooter(options.client.name, options.client.iconUrl) + .setTimestamp(); diff --git a/src/plugins/discord/notifications/game-server-added.ts b/src/plugins/discord/notifications/game-server-added.ts index 9e5889c9a..96e43952c 100644 --- a/src/plugins/discord/notifications/game-server-added.ts +++ b/src/plugins/discord/notifications/game-server-added.ts @@ -16,10 +16,15 @@ interface GameServerAddedOptions { }; } -export const gameServerAdded = (options: GameServerAddedOptions) => new MessageEmbed() - .setColor(Colors.GameServerAdded) - .setAuthor(options.admin.name, options.admin.avatarUrl, options.admin.profileUrl) - .setTitle('Game server added') - .setDescription(options.gameServer.name) - .setFooter(options.client.name, options.client.iconUrl) - .setTimestamp(); +export const gameServerAdded = (options: GameServerAddedOptions) => + new MessageEmbed() + .setColor(Colors.GameServerAdded) + .setAuthor( + options.admin.name, + options.admin.avatarUrl, + options.admin.profileUrl, + ) + .setTitle('Game server added') + .setDescription(options.gameServer.name) + .setFooter(options.client.name, options.client.iconUrl) + .setTimestamp(); diff --git a/src/plugins/discord/notifications/game-server-removed.ts b/src/plugins/discord/notifications/game-server-removed.ts index 99228c822..8632f0a65 100644 --- a/src/plugins/discord/notifications/game-server-removed.ts +++ b/src/plugins/discord/notifications/game-server-removed.ts @@ -16,10 +16,15 @@ interface GameServerRemovedOptions { }; } -export const gameServerRemoved = (options: GameServerRemovedOptions) => new MessageEmbed() - .setColor(Colors.GameServerRemoved) - .setAuthor(options.admin.name, options.admin.avatarUrl, options.admin.profileUrl) - .setTitle('Game server removed') - .setDescription(options.gameServer.name) - .setFooter(options.client.name, options.client.iconUrl) - .setTimestamp(); +export const gameServerRemoved = (options: GameServerRemovedOptions) => + new MessageEmbed() + .setColor(Colors.GameServerRemoved) + .setAuthor( + options.admin.name, + options.admin.avatarUrl, + options.admin.profileUrl, + ) + .setTitle('Game server removed') + .setDescription(options.gameServer.name) + .setFooter(options.client.name, options.client.iconUrl) + .setTimestamp(); diff --git a/src/plugins/discord/notifications/player-ban-added.ts b/src/plugins/discord/notifications/player-ban-added.ts index 50f5cf806..b1b2728c0 100644 --- a/src/plugins/discord/notifications/player-ban-added.ts +++ b/src/plugins/discord/notifications/player-ban-added.ts @@ -21,15 +21,22 @@ interface PlayerBanAddedOptions { ends: Date; } -export const playerBanAdded = (options: PlayerBanAddedOptions) => new MessageEmbed() - .setColor(Colors.PlayerBanAdded) - .setAuthor(options.admin.name, options.admin.avatarUrl, options.admin.profileUrl) - .setTitle('Player ban added') - .setThumbnail(options.player.avatarUrl) - .setDescription([ - `Player: **[${options.player.name}](${options.player.profileUrl})**`, - `Reason: **${options.reason}**`, - `Ends: **${moment(options.ends).fromNow()}**` - ].join('\n')) - .setFooter(options.client.name, options.client.iconUrl) - .setTimestamp(); +export const playerBanAdded = (options: PlayerBanAddedOptions) => + new MessageEmbed() + .setColor(Colors.PlayerBanAdded) + .setAuthor( + options.admin.name, + options.admin.avatarUrl, + options.admin.profileUrl, + ) + .setTitle('Player ban added') + .setThumbnail(options.player.avatarUrl) + .setDescription( + [ + `Player: **[${options.player.name}](${options.player.profileUrl})**`, + `Reason: **${options.reason}**`, + `Ends: **${moment(options.ends).fromNow()}**`, + ].join('\n'), + ) + .setFooter(options.client.name, options.client.iconUrl) + .setTimestamp(); diff --git a/src/plugins/discord/notifications/player-ban-revoked.ts b/src/plugins/discord/notifications/player-ban-revoked.ts index 02df26f09..57b3b88e0 100644 --- a/src/plugins/discord/notifications/player-ban-revoked.ts +++ b/src/plugins/discord/notifications/player-ban-revoked.ts @@ -19,14 +19,21 @@ interface PlayerBanRevokedOptions { reason: string; } -export const playerBanRevoked = (options: PlayerBanRevokedOptions) => new MessageEmbed() - .setColor(Colors.PlayerBanRevoked) - .setAuthor(options.admin.name, options.admin.avatarUrl, options.admin.profileUrl) - .setTitle('Player ban revoked') - .setThumbnail(options.player.avatarUrl) - .setDescription([ - `Player: **[${options.player.name}](${options.player.profileUrl})**`, - `Reason: **${options.reason}**`, - ].join('\n')) - .setFooter(options.client.name, options.client.iconUrl) - .setTimestamp(); +export const playerBanRevoked = (options: PlayerBanRevokedOptions) => + new MessageEmbed() + .setColor(Colors.PlayerBanRevoked) + .setAuthor( + options.admin.name, + options.admin.avatarUrl, + options.admin.profileUrl, + ) + .setTitle('Player ban revoked') + .setThumbnail(options.player.avatarUrl) + .setDescription( + [ + `Player: **[${options.player.name}](${options.player.profileUrl})**`, + `Reason: **${options.reason}**`, + ].join('\n'), + ) + .setFooter(options.client.name, options.client.iconUrl) + .setTimestamp(); diff --git a/src/plugins/discord/notifications/player-profile-updated.ts b/src/plugins/discord/notifications/player-profile-updated.ts index c02022ed3..b6eb51f97 100644 --- a/src/plugins/discord/notifications/player-profile-updated.ts +++ b/src/plugins/discord/notifications/player-profile-updated.ts @@ -27,17 +27,28 @@ interface PlayerProfileUpdatedOptions { const generateChangesText = (changes: Record) => { const changesText = []; for (const name in changes) { - changesText.push(`${name}: **${changes[name].old} => ${changes[name].new}**`); + changesText.push( + `${name}: **${changes[name].old} => ${changes[name].new}**`, + ); } return changesText.join('\n'); -} +}; -export const playerProfileUpdated = (options: PlayerProfileUpdatedOptions) => new MessageEmbed() - .setColor(Colors.PlayerProfileUpdated) - .setAuthor(options.admin.name, options.admin.avatarUrl, options.admin.profileUrl) - .setTitle('Player profile updated') - .setThumbnail(options.player.avatarUrl) - .setDescription(`Player: **[${options.player.name}](${options.player.profileUrl})**\n${generateChangesText(options.changes)}`) - .setFooter(options.client.name, options.client.iconUrl) - .setTimestamp(); +export const playerProfileUpdated = (options: PlayerProfileUpdatedOptions) => + new MessageEmbed() + .setColor(Colors.PlayerProfileUpdated) + .setAuthor( + options.admin.name, + options.admin.avatarUrl, + options.admin.profileUrl, + ) + .setTitle('Player profile updated') + .setThumbnail(options.player.avatarUrl) + .setDescription( + `Player: **[${options.player.name}](${ + options.player.profileUrl + })**\n${generateChangesText(options.changes)}`, + ) + .setFooter(options.client.name, options.client.iconUrl) + .setTimestamp(); diff --git a/src/plugins/discord/notifications/player-skill-changed.ts b/src/plugins/discord/notifications/player-skill-changed.ts index d9c944eef..64e19cb92 100644 --- a/src/plugins/discord/notifications/player-skill-changed.ts +++ b/src/plugins/discord/notifications/player-skill-changed.ts @@ -21,18 +21,35 @@ interface PlayerSkillChangedOptions { newSkill: PlayerSkillType; } -const generateChangesText = (oldSkill: PlayerSkillType, newSkill: PlayerSkillType): string => { +const generateChangesText = ( + oldSkill: PlayerSkillType, + newSkill: PlayerSkillType, +): string => { return Array.from(newSkill.keys()) - .filter(gameClass => newSkill.get(gameClass) !== oldSkill.get(gameClass)) - .map(gameClass => `${gameClass}: ${oldSkill.get(gameClass) ?? 'not set'} => **${newSkill.get(gameClass)}**`) + .filter((gameClass) => newSkill.get(gameClass) !== oldSkill.get(gameClass)) + .map( + (gameClass) => + `${gameClass}: ${ + oldSkill.get(gameClass) ?? 'not set' + } => **${newSkill.get(gameClass)}**`, + ) .join('\n'); -} +}; -export const playerSkillChanged = (options: PlayerSkillChangedOptions) => new MessageEmbed() - .setColor(Colors.SkillChanged) - .setAuthor(options.admin.name, options.admin.avatarUrl, options.admin.profileUrl) - .setTitle('Player skill updated') - .setThumbnail(options.player.avatarUrl) - .setDescription(`Player: **[${options.player.name}](${options.player.profileUrl})**\n${generateChangesText(options.oldSkill, options.newSkill)}`) - .setFooter(options.client.name, options.client.iconUrl) - .setTimestamp(); +export const playerSkillChanged = (options: PlayerSkillChangedOptions) => + new MessageEmbed() + .setColor(Colors.SkillChanged) + .setAuthor( + options.admin.name, + options.admin.avatarUrl, + options.admin.profileUrl, + ) + .setTitle('Player skill updated') + .setThumbnail(options.player.avatarUrl) + .setDescription( + `Player: **[${options.player.name}](${ + options.player.profileUrl + })**\n${generateChangesText(options.oldSkill, options.newSkill)}`, + ) + .setFooter(options.client.name, options.client.iconUrl) + .setTimestamp(); diff --git a/src/plugins/discord/notifications/queue-preview.ts b/src/plugins/discord/notifications/queue-preview.ts index 9bddaea63..37991f566 100644 --- a/src/plugins/discord/notifications/queue-preview.ts +++ b/src/plugins/discord/notifications/queue-preview.ts @@ -5,7 +5,7 @@ import { Colors } from './colors'; interface QueuePreviewGameClassData { gameClass: Tf2ClassName; emoji: Emoji; - players: { name: string; }[]; + players: { name: string }[]; playersRequired: number; } @@ -18,18 +18,23 @@ interface QueuePreviewOptions { gameClassData: QueuePreviewGameClassData[]; } -export const queuePreview = (options: QueuePreviewOptions): MessageEmbedOptions => ({ +export const queuePreview = ( + options: QueuePreviewOptions, +): MessageEmbedOptions => ({ color: Colors.QueuePreview, title: `**${options.playerCount}/${options.requiredPlayerCount} players in the queue!**`, description: `Join [${options.clientName}](${options.clientUrl}) to play the next game!`, thumbnail: { url: options.iconUrl, }, - fields: options.gameClassData.map(gameClassData => ({ + fields: options.gameClassData.map((gameClassData) => ({ name: `${gameClassData.emoji} ${gameClassData.gameClass} (${gameClassData.players.length}/${gameClassData.playersRequired})`, - value: gameClassData.players.length > 0 ? gameClassData.players - .map(player => `\u2000\u25CF\u2000${player.name}`) - .join('\n') : '\u200B', + value: + gameClassData.players.length > 0 + ? gameClassData.players + .map((player) => `\u2000\u25CF\u2000${player.name}`) + .join('\n') + : '\u200B', inline: true, })), footer: { diff --git a/src/plugins/discord/notifications/substitute-request.ts b/src/plugins/discord/notifications/substitute-request.ts index c158e6e6f..83f6b9ffd 100644 --- a/src/plugins/discord/notifications/substitute-request.ts +++ b/src/plugins/discord/notifications/substitute-request.ts @@ -8,7 +8,9 @@ interface SubstituteRequestFields { gameUrl: string; } -export function substituteRequest(fields: SubstituteRequestFields): MessageEmbedOptions { +export function substituteRequest( + fields: SubstituteRequestFields, +): MessageEmbedOptions { return { color: Colors.SubstituteRequest, title: 'A substitute is needed', diff --git a/src/plugins/discord/services/__mocks__/discord-notifications.service.ts b/src/plugins/discord/services/__mocks__/discord-notifications.service.ts index 6149f00e7..244baee95 100644 --- a/src/plugins/discord/services/__mocks__/discord-notifications.service.ts +++ b/src/plugins/discord/services/__mocks__/discord-notifications.service.ts @@ -4,7 +4,6 @@ import { Player } from '@/players/models/player'; import { SubstituteRequest } from '@/queue/substitute-request'; export class DiscordNotificationsService { - notifyQueue(currentPlayerCount: number, targetPlayerCount: number) { return null; } @@ -29,8 +28,11 @@ export class DiscordNotificationsService { return Promise.resolve(); } - async notifySkillChange(playerId: string, oldSkill: Map, newSkill: Map) { + async notifySkillChange( + playerId: string, + oldSkill: Map, + newSkill: Map, + ) { return Promise.resolve(); } - } diff --git a/src/plugins/discord/services/__mocks__/discord.service.ts b/src/plugins/discord/services/__mocks__/discord.service.ts index cfa436f3e..aff4456fb 100644 --- a/src/plugins/discord/services/__mocks__/discord.service.ts +++ b/src/plugins/discord/services/__mocks__/discord.service.ts @@ -10,7 +10,6 @@ class Message { @Injectable() export class DiscordService { - _lastMessage = null; playersChannel = { @@ -50,5 +49,4 @@ export class DiscordService { findEmoji(name: string) { return ``; } - } diff --git a/src/plugins/discord/services/admin-notifications.service.spec.ts b/src/plugins/discord/services/admin-notifications.service.spec.ts index ceabfe8e4..7cb51c3db 100644 --- a/src/plugins/discord/services/admin-notifications.service.spec.ts +++ b/src/plugins/discord/services/admin-notifications.service.spec.ts @@ -19,7 +19,7 @@ jest.mock('@/players/services/players.service'); const environment = { clientUrl: 'http://localhost', -} +}; describe('AdminNotificationsService', () => { let service: AdminNotificationsService; @@ -30,7 +30,7 @@ describe('AdminNotificationsService', () => { let sendSpy: jest.SpyInstance; let sentMessages: Subject; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => mongod.stop()); beforeEach(() => { @@ -41,7 +41,7 @@ describe('AdminNotificationsService', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ Player ]), + TypegooseModule.forFeature([Player]), ], providers: [ AdminNotificationsService, @@ -56,10 +56,12 @@ describe('AdminNotificationsService', () => { events = module.get(Events); playersService = module.get(PlayersService); discordService = module.get(DiscordService); - sendSpy = jest.spyOn(discordService.getAdminsChannel(), 'send').mockImplementation(message => { - sentMessages.next(message); - return Promise.resolve(message); - }); + sendSpy = jest + .spyOn(discordService.getAdminsChannel(), 'send') + .mockImplementation((message) => { + sentMessages.next(message); + return Promise.resolve(message); + }); }); beforeEach(() => { @@ -96,24 +98,34 @@ describe('AdminNotificationsService', () => { admin = await playersService._createOne(); }); - it('should send a message', async () => new Promise(resolve => { - sentMessages.subscribe(message => { - expect(message.embed).toBeTruthy(); - expect(message.embed.title).toEqual('Player profile updated'); - resolve(); - }); - - events.playerUpdates.next({ oldPlayer: player, newPlayer: { ...player, name: 'NEW_PLAYER_NAME' }, adminId: admin.id }); - })); - - describe('when the update doesn\'t change anything', () => { - it('should not send any messages', async () => new Promise(resolve => { - events.playerUpdates.next({ oldPlayer: player, newPlayer: player, adminId: admin.id }); - setTimeout(() => { - expect(sendSpy).not.toHaveBeenCalled(); + it('should send a message', async () => + new Promise((resolve) => { + sentMessages.subscribe((message) => { + expect(message.embed).toBeTruthy(); + expect(message.embed.title).toEqual('Player profile updated'); resolve(); - }, 1000); + }); + + events.playerUpdates.next({ + oldPlayer: player, + newPlayer: { ...player, name: 'NEW_PLAYER_NAME' }, + adminId: admin.id, + }); })); + + describe("when the update doesn't change anything", () => { + it('should not send any messages', async () => + new Promise((resolve) => { + events.playerUpdates.next({ + oldPlayer: player, + newPlayer: player, + adminId: admin.id, + }); + setTimeout(() => { + expect(sendSpy).not.toHaveBeenCalled(); + resolve(); + }, 1000); + })); }); }); @@ -128,15 +140,24 @@ describe('AdminNotificationsService', () => { admin = await playersService._createOne(); }); - it('should send a message', async () => new Promise(resolve => { - sentMessages.subscribe(message => { - expect(message.embed).toBeTruthy(); - expect(message.embed.title).toEqual('Player ban added'); - resolve(); - }); - - events.playerBanAdded.next({ ban: { player, admin, start: new Date(), end: new Date(), reason: 'FAKE_BAN' } }); - })); + it('should send a message', async () => + new Promise((resolve) => { + sentMessages.subscribe((message) => { + expect(message.embed).toBeTruthy(); + expect(message.embed.title).toEqual('Player ban added'); + resolve(); + }); + + events.playerBanAdded.next({ + ban: { + player, + admin, + start: new Date(), + end: new Date(), + reason: 'FAKE_BAN', + }, + }); + })); }); describe('when the playerBanRevoked event emits', () => { @@ -150,15 +171,24 @@ describe('AdminNotificationsService', () => { admin = await playersService._createOne(); }); - it('should send a message', async () => new Promise(resolve => { - sentMessages.subscribe(message => { - expect(message.embed).toBeTruthy(); - expect(message.embed.title).toEqual('Player ban revoked'); - resolve(); - }); - - events.playerBanRevoked.next({ ban: { player, admin, start: new Date(), end: new Date(), reason: 'FAKE_BAN' } }); - })); + it('should send a message', async () => + new Promise((resolve) => { + sentMessages.subscribe((message) => { + expect(message.embed).toBeTruthy(); + expect(message.embed.title).toEqual('Player ban revoked'); + resolve(); + }); + + events.playerBanRevoked.next({ + ban: { + player, + admin, + start: new Date(), + end: new Date(), + reason: 'FAKE_BAN', + }, + }); + })); }); describe('when the playerSkillChanged event emits', () => { @@ -172,27 +202,39 @@ describe('AdminNotificationsService', () => { admin = await playersService._createOne(); }); - it('should send a message', async () => new Promise(resolve => { - sentMessages.subscribe(message => { - expect(message.embed).toBeTruthy(); - expect(message.embed.title).toEqual('Player skill updated'); - resolve(); - }); - - const oldSkill = new Map([[Tf2ClassName.soldier, 2]]); - const newSkill = new Map([[Tf2ClassName.soldier, 4]]); - events.playerSkillChanged.next({ playerId: player.id, oldSkill, newSkill, adminId: admin.id }); - })); + it('should send a message', async () => + new Promise((resolve) => { + sentMessages.subscribe((message) => { + expect(message.embed).toBeTruthy(); + expect(message.embed.title).toEqual('Player skill updated'); + resolve(); + }); - describe('when the skill doesn\'t really change', () => { - it('should not send any message', async () => new Promise(resolve => { const oldSkill = new Map([[Tf2ClassName.soldier, 2]]); - events.playerSkillChanged.next({ playerId: player.id, oldSkill, newSkill: oldSkill, adminId: admin.id }); - setTimeout(() => { - expect(sendSpy).not.toHaveBeenCalled(); - resolve(); - }, 1000); + const newSkill = new Map([[Tf2ClassName.soldier, 4]]); + events.playerSkillChanged.next({ + playerId: player.id, + oldSkill, + newSkill, + adminId: admin.id, + }); })); + + describe("when the skill doesn't really change", () => { + it('should not send any message', async () => + new Promise((resolve) => { + const oldSkill = new Map([[Tf2ClassName.soldier, 2]]); + events.playerSkillChanged.next({ + playerId: player.id, + oldSkill, + newSkill: oldSkill, + adminId: admin.id, + }); + setTimeout(() => { + expect(sendSpy).not.toHaveBeenCalled(); + resolve(); + }, 1000); + })); }); }); @@ -204,15 +246,19 @@ describe('AdminNotificationsService', () => { admin = await playersService._createOne(); }); - it('should send a message', async () => new Promise(resolve => { - sentMessages.subscribe(message => { - expect(message.embed).toBeTruthy(); - expect(message.embed.title).toEqual('Game server added'); - resolve(); - }); + it('should send a message', async () => + new Promise((resolve) => { + sentMessages.subscribe((message) => { + expect(message.embed).toBeTruthy(); + expect(message.embed.title).toEqual('Game server added'); + resolve(); + }); - events.gameServerAdded.next({ gameServer: { name: 'fake game server' } as GameServer, adminId: admin.id }); - })); + events.gameServerAdded.next({ + gameServer: { name: 'fake game server' } as GameServer, + adminId: admin.id, + }); + })); }); describe('when the gameServerRemoved event emits', () => { @@ -223,15 +269,19 @@ describe('AdminNotificationsService', () => { admin = await playersService._createOne(); }); - it('should send a message', async () => new Promise(resolve => { - sentMessages.subscribe(message => { - expect(message.embed).toBeTruthy(); - expect(message.embed.title).toEqual('Game server removed'); - resolve(); - }); + it('should send a message', async () => + new Promise((resolve) => { + sentMessages.subscribe((message) => { + expect(message.embed).toBeTruthy(); + expect(message.embed.title).toEqual('Game server removed'); + resolve(); + }); - events.gameServerRemoved.next({ gameServer: { name: 'fake game server' } as GameServer, adminId: admin.id }); - })); + events.gameServerRemoved.next({ + gameServer: { name: 'fake game server' } as GameServer, + adminId: admin.id, + }); + })); }); describe('when a game is force-ended', () => { @@ -242,15 +292,29 @@ describe('AdminNotificationsService', () => { admin = await playersService._createOne(); }); - it('should send a message', async () => new Promise(resolve => { - sentMessages.subscribe(message => { - expect(message.embed).toBeTruthy(); - expect(message.embed.title).toEqual('Game force-ended'); - resolve(); - }); - - events.gameChanges.next(({ game: { number: 1, state: GameState.started, id: 'FAKE_GAME_ID' } as Game })); - events.gameChanges.next(({ game: { number: 1, state: GameState.interrupted, id: 'FAKE_GAME_ID' } as Game, adminId: admin.id } )); - })); + it('should send a message', async () => + new Promise((resolve) => { + sentMessages.subscribe((message) => { + expect(message.embed).toBeTruthy(); + expect(message.embed.title).toEqual('Game force-ended'); + resolve(); + }); + + events.gameChanges.next({ + game: { + number: 1, + state: GameState.started, + id: 'FAKE_GAME_ID', + } as Game, + }); + events.gameChanges.next({ + game: { + number: 1, + state: GameState.interrupted, + id: 'FAKE_GAME_ID', + } as Game, + adminId: admin.id, + }); + })); }); }); diff --git a/src/plugins/discord/services/admin-notifications.service.ts b/src/plugins/discord/services/admin-notifications.service.ts index d67e886d7..7e92b3191 100644 --- a/src/plugins/discord/services/admin-notifications.service.ts +++ b/src/plugins/discord/services/admin-notifications.service.ts @@ -11,47 +11,77 @@ import { iconUrlPath } from '@configs/discord'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { isRefType } from '@typegoose/typegoose'; import { distinctUntilChanged, filter } from 'rxjs/operators'; -import { newPlayer, playerBanAdded, playerBanRevoked, playerSkillChanged, playerProfileUpdated, gameServerAdded, gameServerRemoved, gameForceEnded } from '../notifications'; +import { + newPlayer, + playerBanAdded, + playerBanRevoked, + playerSkillChanged, + playerProfileUpdated, + gameServerAdded, + gameServerRemoved, + gameForceEnded, +} from '../notifications'; import { DiscordService } from './discord.service'; -const playerSkillEqual = (oldSkill: PlayerSkillType, newSkill: PlayerSkillType) => { +const playerSkillEqual = ( + oldSkill: PlayerSkillType, + newSkill: PlayerSkillType, +) => { if (oldSkill.size !== newSkill.size) { return false; } for (const [key, value] of oldSkill) { const currentSkill = newSkill.get(key); - if (currentSkill !== value || (currentSkill === undefined && !newSkill.has(key))) { + if ( + currentSkill !== value || + (currentSkill === undefined && !newSkill.has(key)) + ) { return false; } } return true; -} +}; @Injectable() export class AdminNotificationsService implements OnModuleInit { - constructor( private discordService: DiscordService, private events: Events, private environment: Environment, private playersService: PlayersService, - ) { } + ) {} onModuleInit() { - this.events.playerRegisters.subscribe(({ player }) => this.onPlayerRegisters(player)); - this.events.playerUpdates.subscribe(({ oldPlayer, newPlayer, adminId }) => this.onPlayerUpdates(oldPlayer, newPlayer, adminId)); - this.events.playerBanAdded.subscribe(({ ban }) => this.onPlayerBanAdded(ban)); - this.events.playerBanRevoked.subscribe(({ ban }) => this.onPlayerBanRevoked(ban)); - this.events.playerSkillChanged.subscribe(({ playerId, oldSkill, newSkill, adminId }) => - this.onPlayerSkillChanged(playerId, oldSkill, newSkill, adminId)); - this.events.gameServerAdded.subscribe(({ gameServer, adminId }) => this.onGameServerAdded(gameServer, adminId)); - this.events.gameServerRemoved.subscribe(({ gameServer, adminId }) => this.onGameServerRemoved(gameServer, adminId)); - this.events.gameChanges.pipe( - distinctUntilChanged((x, y) => x.game.state === y.game.state), - filter(({ game }) => game.state === GameState.interrupted), - ).subscribe(({ game, adminId }) => this.onGameForceEnded(game, adminId)); + this.events.playerRegisters.subscribe(({ player }) => + this.onPlayerRegisters(player), + ); + this.events.playerUpdates.subscribe(({ oldPlayer, newPlayer, adminId }) => + this.onPlayerUpdates(oldPlayer, newPlayer, adminId), + ); + this.events.playerBanAdded.subscribe(({ ban }) => + this.onPlayerBanAdded(ban), + ); + this.events.playerBanRevoked.subscribe(({ ban }) => + this.onPlayerBanRevoked(ban), + ); + this.events.playerSkillChanged.subscribe( + ({ playerId, oldSkill, newSkill, adminId }) => + this.onPlayerSkillChanged(playerId, oldSkill, newSkill, adminId), + ); + this.events.gameServerAdded.subscribe(({ gameServer, adminId }) => + this.onGameServerAdded(gameServer, adminId), + ); + this.events.gameServerRemoved.subscribe(({ gameServer, adminId }) => + this.onGameServerRemoved(gameServer, adminId), + ); + this.events.gameChanges + .pipe( + distinctUntilChanged((x, y) => x.game.state === y.game.state), + filter(({ game }) => game.state === GameState.interrupted), + ) + .subscribe(({ game, adminId }) => this.onGameForceEnded(game, adminId)); } private onPlayerRegisters(player: Player) { @@ -63,14 +93,18 @@ export class AdminNotificationsService implements OnModuleInit { }); } - private async onPlayerUpdates(oldPlayer: Player, newPlayer: Player, adminId: string) { + private async onPlayerUpdates( + oldPlayer: Player, + newPlayer: Player, + adminId: string, + ) { if (!adminId) { return; } const admin = await this.playersService.getById(adminId); - const changes: Record = { }; + const changes: Record = {}; if (oldPlayer.name !== newPlayer.name) { changes.name = { old: oldPlayer.name, new: newPlayer.name }; } @@ -108,8 +142,12 @@ export class AdminNotificationsService implements OnModuleInit { } private async onPlayerBanAdded(ban: PlayerBan) { - const admin = isRefType(ban.admin) ? await this.playersService.getById(ban.admin) : ban.admin; - const player = isRefType(ban.player) ? await this.playersService.getById(ban.player) : ban.player; + const admin = isRefType(ban.admin) + ? await this.playersService.getById(ban.admin) + : ban.admin; + const player = isRefType(ban.player) + ? await this.playersService.getById(ban.player) + : ban.player; this.discordService.getAdminsChannel()?.send({ embed: playerBanAdded({ @@ -134,8 +172,12 @@ export class AdminNotificationsService implements OnModuleInit { } private async onPlayerBanRevoked(ban: PlayerBan) { - const admin = isRefType(ban.admin) ? await this.playersService.getById(ban.admin) : ban.admin; - const player = isRefType(ban.player) ? await this.playersService.getById(ban.player) : ban.player; + const admin = isRefType(ban.admin) + ? await this.playersService.getById(ban.admin) + : ban.admin; + const player = isRefType(ban.player) + ? await this.playersService.getById(ban.player) + : ban.player; this.discordService.getAdminsChannel()?.send({ embed: playerBanRevoked({ @@ -158,7 +200,12 @@ export class AdminNotificationsService implements OnModuleInit { }); } - private async onPlayerSkillChanged(playerId: string, oldSkill: PlayerSkillType, newSkill: PlayerSkillType, adminId: string) { + private async onPlayerSkillChanged( + playerId: string, + oldSkill: PlayerSkillType, + newSkill: PlayerSkillType, + adminId: string, + ) { if (!adminId) { return; } @@ -262,5 +309,4 @@ export class AdminNotificationsService implements OnModuleInit { }), }); } - } diff --git a/src/plugins/discord/services/discord.service.spec.ts b/src/plugins/discord/services/discord.service.spec.ts index 6463a6e2b..acfb9c32c 100644 --- a/src/plugins/discord/services/discord.service.spec.ts +++ b/src/plugins/discord/services/discord.service.spec.ts @@ -1,6 +1,11 @@ import { Test, TestingModule } from '@nestjs/testing'; import { DiscordService } from './discord.service'; -import { Client, playersChannel, pickupsRole, adminChannel } from '@mocks/discord.js'; +import { + Client, + playersChannel, + pickupsRole, + adminChannel, +} from '@mocks/discord.js'; import { Environment } from '@/environment/environment'; class EnvironmentStub { @@ -39,15 +44,16 @@ describe('DiscordService', () => { expect(spy).toHaveBeenCalledWith('FAKE_DISCORD_BOT_TOKEN'); }); - it('should send notification', async () => new Promise(resolve => { - const spy = jest.spyOn(adminChannel, 'send'); - service.onModuleInit(); - client.emit('ready'); - setTimeout(() => { - expect(spy).toHaveBeenCalled(); - resolve(); - }, 0); - })); + it('should send notification', async () => + new Promise((resolve) => { + const spy = jest.spyOn(adminChannel, 'send'); + service.onModuleInit(); + client.emit('ready'); + setTimeout(() => { + expect(spy).toHaveBeenCalled(); + resolve(); + }, 0); + })); }); describe('when logged in', () => { @@ -74,5 +80,4 @@ describe('DiscordService', () => { }); }); }); - }); diff --git a/src/plugins/discord/services/discord.service.ts b/src/plugins/discord/services/discord.service.ts index 4a650ae27..ae82a8e1c 100644 --- a/src/plugins/discord/services/discord.service.ts +++ b/src/plugins/discord/services/discord.service.ts @@ -6,14 +6,11 @@ import { emojisToInstall } from '../emojis-to-install'; @Injectable() export class DiscordService implements OnModuleInit { - private client = new Client(); private guild: Guild; private logger = new Logger(DiscordService.name); - constructor( - private environment: Environment, - ) { } + constructor(private environment: Environment) {} onModuleInit() { if (this.environment.discordBotToken) { @@ -24,8 +21,9 @@ export class DiscordService implements OnModuleInit { this.installEmojis(); }); - this.client.login(this.environment.discordBotToken) - .catch(error => this.logger.error(error.toString())); + this.client + .login(this.environment.discordBotToken) + .catch((error) => this.logger.error(error.toString())); } } @@ -38,46 +36,59 @@ export class DiscordService implements OnModuleInit { } findRole(name: string): Role | null { - return this.guild?.roles?.cache.find(role => role.name === name); + return this.guild?.roles?.cache.find((role) => role.name === name); } findEmoji(name: string): Emoji { - return this.guild?.emojis?.cache.find(emoji => emoji.name === name); + return this.guild?.emojis?.cache.find((emoji) => emoji.name === name); } private enable() { - this.guild = this.client.guilds.cache.find(guild => guild.name === this.environment.discordGuild); + this.guild = this.client.guilds.cache.find( + (guild) => guild.name === this.environment.discordGuild, + ); if (!this.guild?.available) { - this.logger.warn(`guild '${this.environment.discordGuild}' is not available; discord notifications will not work`); + this.logger.warn( + `guild '${this.environment.discordGuild}' is not available; discord notifications will not work`, + ); } } private findChannel(name: string) { return this.guild?.channels?.cache - .filter(c => c instanceof TextChannel) - .find(c => (c as TextChannel).name === name) as TextChannel; + .filter((c) => c instanceof TextChannel) + .find((c) => (c as TextChannel).name === name) as TextChannel; } private async installEmojis() { const installedEmojis: Emoji[] = []; for (const emoji of emojisToInstall) { - const found = this.guild?.emojis.cache.find(e => e.name === emoji.name); + const found = this.guild?.emojis.cache.find((e) => e.name === emoji.name); if (!found) { try { - const e = await this.guild.emojis.create(emoji.sourceUrl, emoji.name, { reason: 'required by the tf2pickup.org server' }); + const e = await this.guild.emojis.create( + emoji.sourceUrl, + emoji.name, + { reason: 'required by the tf2pickup.org server' }, + ); installedEmojis.push(e); this.logger.log(`Installed emoji ${emoji.name}`); } catch (error) { - this.logger.error(`Failed installing emoji '${emoji.name}' (${error}).`); + this.logger.error( + `Failed installing emoji '${emoji.name}' (${error}).`, + ); } } } if (installedEmojis.length > 0) { - this.getAdminsChannel()?.send(`The following emoji${installedEmojis.length > 1 ? 's have' : ' has'}` + - ` been installed: ${installedEmojis.map(e => e.toString()).join(' ')}`); + this.getAdminsChannel()?.send( + `The following emoji${installedEmojis.length > 1 ? 's have' : ' has'}` + + ` been installed: ${installedEmojis + .map((e) => e.toString()) + .join(' ')}`, + ); } } - } diff --git a/src/plugins/discord/services/queue-prompts.service.spec.ts b/src/plugins/discord/services/queue-prompts.service.spec.ts index 5abb7bb57..dd02a5c32 100644 --- a/src/plugins/discord/services/queue-prompts.service.spec.ts +++ b/src/plugins/discord/services/queue-prompts.service.spec.ts @@ -37,34 +37,42 @@ describe('QueuePromptsService', () => { let discordService: DiscordService; let players: Player[]; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(() => { - (QueueService as jest.MockedClass).mockImplementation(() => ({ - slots: [], - requiredPlayerCount: 12, - } as any)); - - (QueueConfigService as jest.MockedClass).mockImplementation(() => ({ - queueConfig: { - teamCount: 2, - classes: [ - { name: Tf2ClassName.scout, count: 2 }, - { name: Tf2ClassName.soldier, count: 2 }, - { name: Tf2ClassName.demoman, count: 1 }, - { name: Tf2ClassName.medic, count: 1 }, - ], - whitelistId: '12345', - }, - } as any)); + (QueueService as jest.MockedClass).mockImplementation( + () => + ({ + slots: [], + requiredPlayerCount: 12, + } as any), + ); + + (QueueConfigService as jest.MockedClass< + typeof QueueConfigService + >).mockImplementation( + () => + ({ + queueConfig: { + teamCount: 2, + classes: [ + { name: Tf2ClassName.scout, count: 2 }, + { name: Tf2ClassName.soldier, count: 2 }, + { name: Tf2ClassName.demoman, count: 1 }, + { name: Tf2ClassName.medic, count: 1 }, + ], + whitelistId: '12345', + }, + } as any), + ); }); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ Player ]), + TypegooseModule.forFeature([Player]), ], providers: [ QueuePromptsService, @@ -98,17 +106,32 @@ describe('QueuePromptsService', () => { ]; queueService.slots = [ - { id: 0, gameClass: Tf2ClassName.scout, playerId: players[0].id, ready: false }, + { + id: 0, + gameClass: Tf2ClassName.scout, + playerId: players[0].id, + ready: false, + }, { id: 1, gameClass: Tf2ClassName.scout, playerId: null, ready: false }, { id: 2, gameClass: Tf2ClassName.scout, playerId: null, ready: false }, { id: 3, gameClass: Tf2ClassName.scout, playerId: null, ready: false }, { id: 4, gameClass: Tf2ClassName.soldier, playerId: null, ready: false }, - { id: 5, gameClass: Tf2ClassName.soldier, playerId: players[1].id, ready: false }, + { + id: 5, + gameClass: Tf2ClassName.soldier, + playerId: players[1].id, + ready: false, + }, { id: 6, gameClass: Tf2ClassName.soldier, playerId: null, ready: false }, { id: 7, gameClass: Tf2ClassName.soldier, playerId: null, ready: false }, { id: 8, gameClass: Tf2ClassName.demoman, playerId: null, ready: false }, { id: 9, gameClass: Tf2ClassName.demoman, playerId: null, ready: false }, - { id: 10, gameClass: Tf2ClassName.medic, playerId: players[2].id, ready: false }, + { + id: 10, + gameClass: Tf2ClassName.medic, + playerId: players[2].id, + ready: false, + }, { id: 11, gameClass: Tf2ClassName.medic, playerId: null, ready: false }, ]; }); @@ -127,17 +150,22 @@ describe('QueuePromptsService', () => { }); describe('when slots change', () => { - beforeEach(async () => new Promise(resolve => { - events.queueSlotsChange.next({ slots: queueService.slots }); - setTimeout(resolve, 3500); - })); + beforeEach( + async () => + new Promise((resolve) => { + events.queueSlotsChange.next({ slots: queueService.slots }); + setTimeout(resolve, 3500); + }), + ); it('should send a new prompt', () => { const channel = discordService.getPlayersChannel(); expect(channel.send).toHaveBeenCalledWith({ embed: expect.objectContaining({ title: expect.stringMatching(/3\/12 players in the queue!/), - description: expect.stringMatching(/\[tf2pickup.pl\]\(https:\/\/tf2pickup.pl\)/), + description: expect.stringMatching( + /\[tf2pickup.pl\]\(https:\/\/tf2pickup.pl\)/, + ), fields: [ { name: ' scout (1/4)', @@ -165,30 +193,95 @@ describe('QueuePromptsService', () => { }); describe('when slots change again', () => { - beforeEach(async () => new Promise(resolve => { - queueService.slots = [ - { id: 0, gameClass: Tf2ClassName.scout, playerId: players[0].id, ready: false }, - { id: 1, gameClass: Tf2ClassName.scout, playerId: null, ready: false }, - { id: 2, gameClass: Tf2ClassName.scout, playerId: null, ready: false }, - { id: 3, gameClass: Tf2ClassName.scout, playerId: null, ready: false }, - { id: 4, gameClass: Tf2ClassName.soldier, playerId: null, ready: false }, - { id: 5, gameClass: Tf2ClassName.soldier, playerId: players[1].id, ready: false }, - { id: 6, gameClass: Tf2ClassName.soldier, playerId: players[2].id, ready: false }, - { id: 7, gameClass: Tf2ClassName.soldier, playerId: null, ready: false }, - { id: 8, gameClass: Tf2ClassName.demoman, playerId: null, ready: false }, - { id: 9, gameClass: Tf2ClassName.demoman, playerId: null, ready: false }, - { id: 10, gameClass: Tf2ClassName.medic, playerId: null, ready: false }, - { id: 11, gameClass: Tf2ClassName.medic, playerId: null, ready: false }, - ]; - events.queueSlotsChange.next({ slots: queueService.slots }); - setTimeout(resolve, 3500); - })); + beforeEach( + async () => + new Promise((resolve) => { + queueService.slots = [ + { + id: 0, + gameClass: Tf2ClassName.scout, + playerId: players[0].id, + ready: false, + }, + { + id: 1, + gameClass: Tf2ClassName.scout, + playerId: null, + ready: false, + }, + { + id: 2, + gameClass: Tf2ClassName.scout, + playerId: null, + ready: false, + }, + { + id: 3, + gameClass: Tf2ClassName.scout, + playerId: null, + ready: false, + }, + { + id: 4, + gameClass: Tf2ClassName.soldier, + playerId: null, + ready: false, + }, + { + id: 5, + gameClass: Tf2ClassName.soldier, + playerId: players[1].id, + ready: false, + }, + { + id: 6, + gameClass: Tf2ClassName.soldier, + playerId: players[2].id, + ready: false, + }, + { + id: 7, + gameClass: Tf2ClassName.soldier, + playerId: null, + ready: false, + }, + { + id: 8, + gameClass: Tf2ClassName.demoman, + playerId: null, + ready: false, + }, + { + id: 9, + gameClass: Tf2ClassName.demoman, + playerId: null, + ready: false, + }, + { + id: 10, + gameClass: Tf2ClassName.medic, + playerId: null, + ready: false, + }, + { + id: 11, + gameClass: Tf2ClassName.medic, + playerId: null, + ready: false, + }, + ]; + events.queueSlotsChange.next({ slots: queueService.slots }); + setTimeout(resolve, 3500); + }), + ); it('should edit the previous embed', () => { expect((discordService as any)._lastMessage.edit).toHaveBeenCalledWith({ embed: expect.objectContaining({ title: expect.stringMatching(/3\/12 players in the queue!/), - description: expect.stringMatching(/\[tf2pickup.pl\]\(https:\/\/tf2pickup.pl\)/), + description: expect.stringMatching( + /\[tf2pickup.pl\]\(https:\/\/tf2pickup.pl\)/, + ), fields: [ { name: ' scout (1/4)', @@ -197,7 +290,8 @@ describe('QueuePromptsService', () => { }, { name: ' soldier (2/4)', - value: '\u2000\u25CF\u2000fake_player_2\n\u2000\u25CF\u2000fake_player_3', + value: + '\u2000\u25CF\u2000fake_player_2\n\u2000\u25CF\u2000fake_player_3', inline: true, }, { @@ -226,7 +320,7 @@ describe('QueuePromptsService', () => { it('should delete the message', async () => { service.ensurePromptIsVisible(); - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); expect(message.delete).toHaveBeenCalled(); }); }); diff --git a/src/plugins/discord/services/queue-prompts.service.ts b/src/plugins/discord/services/queue-prompts.service.ts index 1029ee7de..298af6a5f 100644 --- a/src/plugins/discord/services/queue-prompts.service.ts +++ b/src/plugins/discord/services/queue-prompts.service.ts @@ -14,7 +14,6 @@ import { DiscordService } from './discord.service'; @Injectable() export class QueuePromptsService implements OnModuleInit { - private requiredPlayerCount = 0; private message?: Message; @@ -25,12 +24,12 @@ export class QueuePromptsService implements OnModuleInit { private queueService: QueueService, private playersService: PlayersService, private queueConfigService: QueueConfigService, - ) { } + ) {} onModuleInit() { - this.events.queueSlotsChange.pipe( - debounceTime(3000), - ).subscribe(() => this.refreshPrompt(this.queueService.slots)); + this.events.queueSlotsChange + .pipe(debounceTime(3000)) + .subscribe(() => this.refreshPrompt(this.queueService.slots)); } private async refreshPrompt(slots: QueueSlot[]) { @@ -50,38 +49,53 @@ export class QueuePromptsService implements OnModuleInit { this.message.edit({ embed }); } else { if (this.playerThresholdMet()) { - this.message = await this.discordService.getPlayersChannel()?.send({ embed }); + this.message = await this.discordService + .getPlayersChannel() + ?.send({ embed }); } } } private async slotsToGameClassData(slots: QueueSlot[]) { - const playerData = await Promise.all(slots - .filter(slot => !!slot.playerId) - .map(slot => this.playersService.getById(slot.playerId).then(player => ({ name: player.name, gameClass: slot.gameClass }))) + const playerData = await Promise.all( + slots + .filter((slot) => !!slot.playerId) + .map((slot) => + this.playersService.getById(slot.playerId).then((player) => ({ + name: player.name, + gameClass: slot.gameClass, + })), + ), ); - return this.queueConfigService.queueConfig.classes - .map(gameClass => ({ - gameClass: gameClass.name, - emoji: this.discordService.findEmoji(`tf2${gameClass.name}`), - playersRequired: gameClass.count * this.queueConfigService.queueConfig.teamCount, - players: playerData.filter(p => p.gameClass === gameClass.name), - })); + return this.queueConfigService.queueConfig.classes.map((gameClass) => ({ + gameClass: gameClass.name, + emoji: this.discordService.findEmoji(`tf2${gameClass.name}`), + playersRequired: + gameClass.count * this.queueConfigService.queueConfig.teamCount, + players: playerData.filter((p) => p.gameClass === gameClass.name), + })); } private playerThresholdMet() { - return this.queueService.playerCount >= (this.requiredPlayerCount * promptPlayerThresholdRatio); + return ( + this.queueService.playerCount >= + this.requiredPlayerCount * promptPlayerThresholdRatio + ); } @Cron(CronExpression.EVERY_5_MINUTES) async ensurePromptIsVisible() { - const messages = await this.discordService.getPlayersChannel().messages.fetch({ limit: 1 }); - if (messages?.first()?.id !== this.message?.id && this.playerThresholdMet()) { + const messages = await this.discordService + .getPlayersChannel() + .messages.fetch({ limit: 1 }); + if ( + messages?.first()?.id !== this.message?.id && + this.playerThresholdMet() + ) { await this.message?.delete(); delete this.message; this.refreshPrompt(this.queueService.slots); } } - } diff --git a/src/plugins/plugins.module.ts b/src/plugins/plugins.module.ts index 506602051..73b4ca285 100644 --- a/src/plugins/plugins.module.ts +++ b/src/plugins/plugins.module.ts @@ -1,18 +1,15 @@ import { DynamicModule, Module } from '@nestjs/common'; import { DiscordModule } from './discord/discord.module'; -const discordModule = () => process.env.DISCORD_BOT_TOKEN ? [ DiscordModule ] : []; +const discordModule = () => + process.env.DISCORD_BOT_TOKEN ? [DiscordModule] : []; -@Module({ }) +@Module({}) export class PluginsModule { - static configure(): DynamicModule { return { module: PluginsModule, - imports: [ - ...discordModule(), - ], + imports: [...discordModule()], }; } - } diff --git a/src/profile/controllers/profile.controller.spec.ts b/src/profile/controllers/profile.controller.spec.ts index 8f9c05caa..7c47ce5ab 100644 --- a/src/profile/controllers/profile.controller.spec.ts +++ b/src/profile/controllers/profile.controller.spec.ts @@ -11,19 +11,27 @@ import { PlayerPreferencesService } from '@/player-preferences/services/player-p jest.mock('@/player-preferences/services/player-preferences.service'); class PlayersServiceStub { - acceptTerms(playerId: string) { return null; } + acceptTerms(playerId: string) { + return null; + } } class GamesServiceStub { - getPlayerActiveGame(playerId: string) { return new Promise(resolve => resolve(null)); } + getPlayerActiveGame(playerId: string) { + return new Promise((resolve) => resolve(null)); + } } class PlayerBansServiceStub { - getPlayerActiveBans(playerId: string) { return new Promise(resolve => resolve([])); } + getPlayerActiveBans(playerId: string) { + return new Promise((resolve) => resolve([])); + } } class MapVoteServiceStub { - playerVote(playerId: string) { return 'cp_badlands'; } + playerVote(playerId: string) { + return 'cp_badlands'; + } } describe('Profile Controller', () => { @@ -49,8 +57,12 @@ describe('Profile Controller', () => { }); beforeEach(() => { - playerPreferencesService.getPlayerPreferences.mockResolvedValue(new Map([['sound-volume', '0.5']])); - playerPreferencesService.updatePlayerPreferences.mockResolvedValue(new Map([['sound-volume', '0.9']])); + playerPreferencesService.getPlayerPreferences.mockResolvedValue( + new Map([['sound-volume', '0.5']]), + ); + playerPreferencesService.updatePlayerPreferences.mockResolvedValue( + new Map([['sound-volume', '0.9']]), + ); }); it('should be defined', () => { @@ -58,7 +70,7 @@ describe('Profile Controller', () => { }); describe('#getProfile()', () => { - it('should return the logged-in user\'s profile', async () => { + it("should return the logged-in user's profile", async () => { const profile = { player: { id: 'FAKE_ID', @@ -68,25 +80,36 @@ describe('Profile Controller', () => { activeGameId: null, bans: [], mapVote: 'cp_badlands', - preferences: new Map([['sound-volume', '0.5']]) }; + preferences: new Map([['sound-volume', '0.5']]), + }; expect(await controller.getProfile(profile.player)).toEqual(profile); }); }); describe('#getPreferences()', () => { - it('should return the user\'s preferences', async () => { - const ret = await controller.getPreferences({ id: 'FAKE_USER_ID' } as Player); + it("should return the user's preferences", async () => { + const ret = await controller.getPreferences({ + id: 'FAKE_USER_ID', + } as Player); expect(ret.size).toEqual(1); expect(ret.get('sound-volume')).toEqual('0.5'); }); }); describe('#savePreferences()', () => { - it('should update user\'s preferences', async () => { - const ret = await controller.savePreferences({ id: 'FAKE_USER_ID' } as Player, { 'sound-volume': '0.9' }); + it("should update user's preferences", async () => { + const ret = await controller.savePreferences( + { id: 'FAKE_USER_ID' } as Player, + { 'sound-volume': '0.9' }, + ); expect(ret.size).toEqual(1); expect(ret.get('sound-volume')).toEqual('0.9'); - expect(playerPreferencesService.updatePlayerPreferences).toHaveBeenCalledWith('FAKE_USER_ID', new Map([['sound-volume', '0.9']])); + expect( + playerPreferencesService.updatePlayerPreferences, + ).toHaveBeenCalledWith( + 'FAKE_USER_ID', + new Map([['sound-volume', '0.9']]), + ); }); }); @@ -98,7 +121,9 @@ describe('Profile Controller', () => { }); it('should reject invalid requests', async () => { - await expect(controller.acceptTerms({ id: 'FAKE_ID' } as Player, undefined)).rejects.toThrow(BadRequestException); + await expect( + controller.acceptTerms({ id: 'FAKE_ID' } as Player, undefined), + ).rejects.toThrow(BadRequestException); }); }); }); diff --git a/src/profile/controllers/profile.controller.ts b/src/profile/controllers/profile.controller.ts index aa86a4eae..b302bcab2 100644 --- a/src/profile/controllers/profile.controller.ts +++ b/src/profile/controllers/profile.controller.ts @@ -1,4 +1,15 @@ -import { Controller, Get, Post, Query, HttpCode, BadRequestException, Body, Put, UseInterceptors, ClassSerializerInterceptor } from '@nestjs/common'; +import { + Controller, + Get, + Post, + Query, + HttpCode, + BadRequestException, + Body, + Put, + UseInterceptors, + ClassSerializerInterceptor, +} from '@nestjs/common'; import { Auth } from '@/auth/decorators/auth.decorator'; import { User } from '@/auth/decorators/user.decorator'; import { Player } from '@/players/models/player'; @@ -11,14 +22,13 @@ import { Profile } from '../dto/profile'; @Controller('profile') export class ProfileController { - constructor( private playersService: PlayersService, private gamesService: GamesService, private playerBansService: PlayerBansService, private mapVoteService: MapVoteService, private playerPreferencesService: PlayerPreferencesService, - ) { } + ) {} @Get() @Auth() @@ -26,10 +36,13 @@ export class ProfileController { async getProfile(@User() user: Player): Promise { return new Profile({ player: user, - activeGameId: (await this.gamesService.getPlayerActiveGame(user.id))?.id ?? null, + activeGameId: + (await this.gamesService.getPlayerActiveGame(user.id))?.id ?? null, bans: await this.playerBansService.getPlayerActiveBans(user.id), mapVote: this.mapVoteService.playerVote(user.id), - preferences: await this.playerPreferencesService.getPlayerPreferences(user.id), + preferences: await this.playerPreferencesService.getPlayerPreferences( + user.id, + ), }); } @@ -41,20 +54,28 @@ export class ProfileController { @Auth() @Put('/preferences') - async savePreferences(@User() user: Player, @Body() preferences: { [key: string]: string }) { - return this.playerPreferencesService.updatePlayerPreferences(user.id, new Map(Object.entries(preferences))); + async savePreferences( + @User() user: Player, + @Body() preferences: { [key: string]: string }, + ) { + return this.playerPreferencesService.updatePlayerPreferences( + user.id, + new Map(Object.entries(preferences)), + ); } @Post() @Auth() @UseInterceptors(ClassSerializerInterceptor) @HttpCode(204) - async acceptTerms(@User() user: Player, @Query('accept_terms') acceptTerms: string) { + async acceptTerms( + @User() user: Player, + @Query('accept_terms') acceptTerms: string, + ) { if (acceptTerms !== undefined) { await this.playersService.acceptTerms(user.id); } else { throw new BadRequestException(); } } - } diff --git a/src/profile/dto/profile.ts b/src/profile/dto/profile.ts index 1fbfcde15..eb2334396 100644 --- a/src/profile/dto/profile.ts +++ b/src/profile/dto/profile.ts @@ -12,7 +12,6 @@ interface ProfileParams { } export class Profile { - constructor(params: ProfileParams) { this.player = params.player; this.activeGameId = params.activeGameId; @@ -36,5 +35,4 @@ export class Profile { preferences: PreferencesType; hasAcceptedRules: boolean; - } diff --git a/src/profile/profile.module.ts b/src/profile/profile.module.ts index e642886f4..c6f3f9524 100644 --- a/src/profile/profile.module.ts +++ b/src/profile/profile.module.ts @@ -14,8 +14,6 @@ import { PlayerPreferencesModule } from '@/player-preferences/player-preferences QueueModule, PlayerPreferencesModule, ], - controllers: [ - ProfileController, - ], + controllers: [ProfileController], }) -export class ProfileModule { } +export class ProfileModule {} diff --git a/src/queue/controllers/queue.controller.spec.ts b/src/queue/controllers/queue.controller.spec.ts index a1cb4834c..ae518f3ce 100644 --- a/src/queue/controllers/queue.controller.spec.ts +++ b/src/queue/controllers/queue.controller.spec.ts @@ -57,7 +57,12 @@ describe('Queue Controller', () => { queueConfigService.queueConfig = { some_param: 'some_value' }; queueService.slots = [ - { id: 0, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_ID' }, + { + id: 0, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_ID', + }, { id: 1, gameClass: Tf2ClassName.soldier, ready: false, playerId: null }, ]; queueService.state = 'waiting'; @@ -71,14 +76,28 @@ describe('Queue Controller', () => { }, ]); - friendsService.friendships = [{ sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }]; + friendsService.friendships = [ + { sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }, + ]; // @ts-expect-error mapVoteService.results = []; playerPopulatorService.populatePlayers.mockResolvedValue([ - { id: 0, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_ID', player: { id: 'FAKE_ID' } as Player }, - { id: 1, gameClass: Tf2ClassName.soldier, ready: false, playerId: null, player: null }, + { + id: 0, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_ID', + player: { id: 'FAKE_ID' } as Player, + }, + { + id: 1, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: null, + player: null, + }, ]); }); @@ -102,7 +121,9 @@ describe('Queue Controller', () => { describe('#getQueueConfig()', () => { it('should return queue config', () => { - expect(controller.getQueueConfig()).toEqual(queueConfigService.queueConfig); + expect(controller.getQueueConfig()).toEqual( + queueConfigService.queueConfig, + ); }); }); @@ -115,8 +136,18 @@ describe('Queue Controller', () => { describe('#getQueueSlots()', () => { it('should return queue slots', () => { expect(controller.getQueueSlots()).toEqual([ - { id: 0, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_ID' }, - { id: 1, gameClass: Tf2ClassName.soldier, ready: false, playerId: null }, + { + id: 0, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_ID', + }, + { + id: 1, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: null, + }, ]); }); }); @@ -149,7 +180,9 @@ describe('Queue Controller', () => { describe('#getFriendships()', () => { it('should return the frienships', () => { - expect(controller.getFriendships()).toEqual([{ sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }]); + expect(controller.getFriendships()).toEqual([ + { sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }, + ]); }); }); @@ -161,25 +194,39 @@ describe('Queue Controller', () => { }); it('should return the maps in the map pool', async () => { - expect(await controller.getMaps()).toEqual([{ name: 'cp_badlands', execConfig: 'etf2l_6v6_5cp' }]); + expect(await controller.getMaps()).toEqual([ + { name: 'cp_badlands', execConfig: 'etf2l_6v6_5cp' }, + ]); }); }); describe('#addMap()', () => { beforeEach(() => { - mapPoolService.addMap.mockResolvedValue({ name: 'cp_badlands', execConfig: 'etf2l_6v6_5cp' }); + mapPoolService.addMap.mockResolvedValue({ + name: 'cp_badlands', + execConfig: 'etf2l_6v6_5cp', + }); }); it('should add the map', async () => { - const ret = await controller.addMap({ name: 'cp_badlands', execConfig: 'etf2l_6v6_5cp' }); + const ret = await controller.addMap({ + name: 'cp_badlands', + execConfig: 'etf2l_6v6_5cp', + }); expect(ret).toEqual({ name: 'cp_badlands', execConfig: 'etf2l_6v6_5cp' }); - expect(mapPoolService.addMap).toHaveBeenCalledWith({ name: 'cp_badlands', execConfig: 'etf2l_6v6_5cp' }) + expect(mapPoolService.addMap).toHaveBeenCalledWith({ + name: 'cp_badlands', + execConfig: 'etf2l_6v6_5cp', + }); }); }); describe('#deleteMap()', () => { beforeEach(() => { - mapPoolService.removeMap.mockResolvedValue({ name: 'cp_badlands', execConfig: 'etf2l_6v6_5cp' }); + mapPoolService.removeMap.mockResolvedValue({ + name: 'cp_badlands', + execConfig: 'etf2l_6v6_5cp', + }); }); it('should remote the map', async () => { @@ -191,13 +238,25 @@ describe('Queue Controller', () => { describe('#setMaps()', () => { beforeEach(() => { - mapPoolService.setMaps.mockResolvedValue([{ name: 'cp_badlands' }, { name: 'cp_process_final', execConfig: 'etf2l_6v6_5cp' }]); + mapPoolService.setMaps.mockResolvedValue([ + { name: 'cp_badlands' }, + { name: 'cp_process_final', execConfig: 'etf2l_6v6_5cp' }, + ]); }); it('should set the maps', async () => { - const ret = await controller.setMaps([{ name: 'cp_badlands' }, { name: 'cp_process_final', execConfig: 'etf2l_6v6_5cp' }]); - expect(ret).toEqual([{ name: 'cp_badlands' }, { name: 'cp_process_final', execConfig: 'etf2l_6v6_5cp' }]); - expect(mapPoolService.setMaps).toHaveBeenCalledWith([{ name: 'cp_badlands' }, { name: 'cp_process_final', execConfig: 'etf2l_6v6_5cp' }]); + const ret = await controller.setMaps([ + { name: 'cp_badlands' }, + { name: 'cp_process_final', execConfig: 'etf2l_6v6_5cp' }, + ]); + expect(ret).toEqual([ + { name: 'cp_badlands' }, + { name: 'cp_process_final', execConfig: 'etf2l_6v6_5cp' }, + ]); + expect(mapPoolService.setMaps).toHaveBeenCalledWith([ + { name: 'cp_badlands' }, + { name: 'cp_process_final', execConfig: 'etf2l_6v6_5cp' }, + ]); }); }); }); diff --git a/src/queue/controllers/queue.controller.ts b/src/queue/controllers/queue.controller.ts index 465d5b95d..255ca0afa 100644 --- a/src/queue/controllers/queue.controller.ts +++ b/src/queue/controllers/queue.controller.ts @@ -1,4 +1,14 @@ -import { Body, Controller, Delete, Get, Param, Post, Put, UsePipes, ValidationPipe } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + Param, + Post, + Put, + UsePipes, + ValidationPipe, +} from '@nestjs/common'; import { QueueConfigService } from '../services/queue-config.service'; import { QueueService } from '../services/queue.service'; import { MapVoteService } from '../services/map-vote.service'; @@ -13,7 +23,6 @@ import { PlayerRole } from '@/players/models/player-role'; @Controller('queue') export class QueueController { - constructor( private queueConfigService: QueueConfigService, private queueService: QueueService, @@ -22,13 +31,15 @@ export class QueueController { private friendsService: FriendsService, private playerPopulatorService: PlayerPopulatorService, private mapPoolService: MapPoolService, - ) { } + ) {} @Get() async getQueue() { return { config: this.queueConfigService.queueConfig, - slots: await this.playerPopulatorService.populatePlayers(this.getQueueSlots()), + slots: await this.playerPopulatorService.populatePlayers( + this.getQueueSlots(), + ), state: this.queueService.state, mapVoteResults: this.mapVoteService.results, substituteRequests: await this.queueAnnouncementsService.substituteRequests(), @@ -97,5 +108,4 @@ export class QueueController { async setMaps(@Body() maps: Map[]) { return await this.mapPoolService.setMaps(maps); } - } diff --git a/src/queue/decorators/populate-players.decorator.ts b/src/queue/decorators/populate-players.decorator.ts index 481ee2d41..42d22d8b7 100644 --- a/src/queue/decorators/populate-players.decorator.ts +++ b/src/queue/decorators/populate-players.decorator.ts @@ -1,4 +1,5 @@ import { UseInterceptors } from '@nestjs/common'; import { PopulatePlayersInterceptor } from '../interceptors/populate-players.interceptor'; -export const PopulatePlayers = () => UseInterceptors(PopulatePlayersInterceptor); +export const PopulatePlayers = () => + UseInterceptors(PopulatePlayersInterceptor); diff --git a/src/queue/gateways/queue.gateway.spec.ts b/src/queue/gateways/queue.gateway.spec.ts index fca12c52f..4a2e9f639 100644 --- a/src/queue/gateways/queue.gateway.spec.ts +++ b/src/queue/gateways/queue.gateway.spec.ts @@ -18,7 +18,14 @@ jest.mock('../services/queue-announcements.service'); jest.mock('../services/friends.service'); jest.mock('../services/player-populator.service'); -const mockSubstituteRequests = [{ gameId: 'FAKE_GAME_ID', gameNumber: 5, gameClass: Tf2ClassName.scout, team: 'BLU' }]; +const mockSubstituteRequests = [ + { + gameId: 'FAKE_GAME_ID', + gameNumber: 5, + gameClass: Tf2ClassName.scout, + team: 'BLU', + }, +]; describe('QueueGateway', () => { let gateway: QueueGateway; @@ -53,10 +60,29 @@ describe('QueueGateway', () => { }); beforeEach(() => { - queueService.join.mockResolvedValue([{ id: 5, playerId: 'FAKE_PLAYER_ID', gameClass: Tf2ClassName.scout, ready: false }]); - queueService.leave.mockReturnValue({ id: 0, playerId: 'FAKE_PLAYER_ID', gameClass: Tf2ClassName.scout, ready: false }); - queueService.readyUp.mockReturnValue({ id: 0, playerId: 'FAKE_PLAYER_ID', gameClass: Tf2ClassName.scout, ready: true }); - queueAnnouncementsService.substituteRequests.mockResolvedValue(mockSubstituteRequests); + queueService.join.mockResolvedValue([ + { + id: 5, + playerId: 'FAKE_PLAYER_ID', + gameClass: Tf2ClassName.scout, + ready: false, + }, + ]); + queueService.leave.mockReturnValue({ + id: 0, + playerId: 'FAKE_PLAYER_ID', + gameClass: Tf2ClassName.scout, + ready: false, + }); + queueService.readyUp.mockReturnValue({ + id: 0, + playerId: 'FAKE_PLAYER_ID', + gameClass: Tf2ClassName.scout, + ready: true, + }); + queueAnnouncementsService.substituteRequests.mockResolvedValue( + mockSubstituteRequests, + ); socket = { emit: jest.fn(), @@ -74,39 +100,75 @@ describe('QueueGateway', () => { describe('#joinQueue()', () => { it('should join the queue', async () => { - const ret = await gateway.joinQueue({ request: { user: { id: 'FAKE_PLAYER_ID' } } } as AuthorizedWsClient, { slotId: 5 }); + const ret = await gateway.joinQueue( + { request: { user: { id: 'FAKE_PLAYER_ID' } } } as AuthorizedWsClient, + { slotId: 5 }, + ); expect(queueService.join).toHaveBeenCalledWith(5, 'FAKE_PLAYER_ID'); - expect(ret).toEqual([ { id: 5, playerId: 'FAKE_PLAYER_ID', gameClass: Tf2ClassName.scout, ready: false } ]); + expect(ret).toEqual([ + { + id: 5, + playerId: 'FAKE_PLAYER_ID', + gameClass: Tf2ClassName.scout, + ready: false, + }, + ]); }); }); describe('#leaveQueue()', () => { it('should leave the queue', () => { - const ret = gateway.leaveQueue({ request: { user: { id: 'FAKE_PLAYER_ID' } } } as AuthorizedWsClient); + const ret = gateway.leaveQueue({ + request: { user: { id: 'FAKE_PLAYER_ID' } }, + } as AuthorizedWsClient); expect(queueService.leave).toHaveBeenCalledWith('FAKE_PLAYER_ID'); - expect(ret).toEqual({ id: 0, playerId: 'FAKE_PLAYER_ID', gameClass: Tf2ClassName.scout, ready: false }); + expect(ret).toEqual({ + id: 0, + playerId: 'FAKE_PLAYER_ID', + gameClass: Tf2ClassName.scout, + ready: false, + }); }); }); describe('#playerReady()', () => { it('should ready up the player', () => { - const ret = gateway.playerReady({ request: { user: { id: 'FAKE_PLAYER_ID' } } } as AuthorizedWsClient); + const ret = gateway.playerReady({ + request: { user: { id: 'FAKE_PLAYER_ID' } }, + } as AuthorizedWsClient); expect(queueService.readyUp).toHaveBeenCalledWith('FAKE_PLAYER_ID'); - expect(ret).toEqual({ id: 0, playerId: 'FAKE_PLAYER_ID', gameClass: Tf2ClassName.scout, ready: true }); + expect(ret).toEqual({ + id: 0, + playerId: 'FAKE_PLAYER_ID', + gameClass: Tf2ClassName.scout, + ready: true, + }); }); }); describe('#markFriend()', () => { it('should mark friend', async () => { - gateway.markFriend({ request: { user: { id: 'FAKE_PLAYER_ID' } } } as AuthorizedWsClient, { friendPlayerId: 'FAKE_FRIEND_ID' }); - expect(friendsService.markFriend).toHaveBeenCalledWith('FAKE_PLAYER_ID', 'FAKE_FRIEND_ID'); + gateway.markFriend( + { request: { user: { id: 'FAKE_PLAYER_ID' } } } as AuthorizedWsClient, + { friendPlayerId: 'FAKE_FRIEND_ID' }, + ); + expect(friendsService.markFriend).toHaveBeenCalledWith( + 'FAKE_PLAYER_ID', + 'FAKE_FRIEND_ID', + ); }); }); describe('#voteForMap()', () => { it('should vote for the map', () => { - const ret = gateway.voteForMap({ request: { user: { id: 'FAKE_PLAYER_ID' } } } as AuthorizedWsClient, { map: 'cp_badlands' }); - expect(mapVoteService.voteForMap).toHaveBeenCalledWith('FAKE_PLAYER_ID', 'cp_badlands'); + const ret = gateway.voteForMap( + { request: { user: { id: 'FAKE_PLAYER_ID' } } } as AuthorizedWsClient, + { map: 'cp_badlands' }, + ); + expect(mapVoteService.voteForMap).toHaveBeenCalledWith( + 'FAKE_PLAYER_ID', + 'cp_badlands', + ); expect(ret).toEqual('cp_badlands'); }); }); @@ -114,14 +176,35 @@ describe('QueueGateway', () => { describe('when the queueSlotsChange event is fired', () => { beforeEach(() => { playerPopulatorService.populatePlayers.mockResolvedValue([ - { id: 5, gameClass: Tf2ClassName.soldier, ready: true, playerId: 'FAKE_PLAYER_ID', player: { id: 'FAKE_PLAYER_ID' } as Player }, + { + id: 5, + gameClass: Tf2ClassName.soldier, + ready: true, + playerId: 'FAKE_PLAYER_ID', + player: { id: 'FAKE_PLAYER_ID' } as Player, + }, ]); - events.queueSlotsChange.next({ slots: [ { id: 0, playerId: 'FAKE_PLAYER_ID', ready: true, gameClass: Tf2ClassName.soldier } ] }); + events.queueSlotsChange.next({ + slots: [ + { + id: 0, + playerId: 'FAKE_PLAYER_ID', + ready: true, + gameClass: Tf2ClassName.soldier, + }, + ], + }); }); it('should emit the event over the socket', () => { expect(socket.emit).toHaveBeenCalledWith('queue slots update', [ - { id: 5, gameClass: Tf2ClassName.soldier, ready: true, playerId: 'FAKE_PLAYER_ID', player: { id: 'FAKE_PLAYER_ID' } }, + { + id: 5, + gameClass: Tf2ClassName.soldier, + ready: true, + playerId: 'FAKE_PLAYER_ID', + player: { id: 'FAKE_PLAYER_ID' }, + }, ]); }); }); @@ -158,7 +241,10 @@ describe('QueueGateway', () => { }); it('should emit the event over the socket', () => { - expect(socket.emit).toHaveBeenCalledWith('map vote results update', results); + expect(socket.emit).toHaveBeenCalledWith( + 'map vote results update', + results, + ); }); }); @@ -168,7 +254,10 @@ describe('QueueGateway', () => { }); it('should emit the event over the socket', () => { - expect(socket.emit).toHaveBeenCalledWith('substitute requests update', mockSubstituteRequests); + expect(socket.emit).toHaveBeenCalledWith( + 'substitute requests update', + mockSubstituteRequests, + ); }); }); }); diff --git a/src/queue/gateways/queue.gateway.ts b/src/queue/gateways/queue.gateway.ts index c05c0480b..386294263 100644 --- a/src/queue/gateways/queue.gateway.ts +++ b/src/queue/gateways/queue.gateway.ts @@ -1,4 +1,8 @@ -import { SubscribeMessage, WebSocketGateway, OnGatewayInit } from '@nestjs/websockets'; +import { + SubscribeMessage, + WebSocketGateway, + OnGatewayInit, +} from '@nestjs/websockets'; import { QueueService } from '../services/queue.service'; import { WsAuthorized } from '@/auth/decorators/ws-authorized.decorator'; import { Socket } from 'socket.io'; @@ -15,7 +19,6 @@ import { WebsocketEvent } from '@/websocket-event'; @WebSocketGateway() export class QueueGateway implements OnGatewayInit, OnModuleInit { - private socket: Socket; constructor( @@ -25,21 +28,32 @@ export class QueueGateway implements OnGatewayInit, OnModuleInit { private friendsService: FriendsService, private events: Events, private playerPopulatorService: PlayerPopulatorService, - ) { } + ) {} onModuleInit() { this.events.queueSlotsChange .pipe(distinctUntilChanged()) - .subscribe(async ({ slots }) => this.socket.emit(WebsocketEvent.queueSlotsUpdate, await this.playerPopulatorService.populatePlayers(slots))); + .subscribe(async ({ slots }) => + this.socket.emit( + WebsocketEvent.queueSlotsUpdate, + await this.playerPopulatorService.populatePlayers(slots), + ), + ); this.events.queueStateChange .pipe(distinctUntilChanged()) - .subscribe(({ state }) => this.socket.emit(WebsocketEvent.queueStateUpdate, state)); + .subscribe(({ state }) => + this.socket.emit(WebsocketEvent.queueStateUpdate, state), + ); this.events.queueFriendshipsChange .pipe(distinctUntilChanged()) - .subscribe(({ friendships }) => this.socket.emit(WebsocketEvent.friendshipsUpdate, friendships)); + .subscribe(({ friendships }) => + this.socket.emit(WebsocketEvent.friendshipsUpdate, friendships), + ); this.events.mapVotesChange .pipe(distinctUntilChanged()) - .subscribe(({ results }) => this.socket.emit(WebsocketEvent.mapVoteResultsUpdate, results)); + .subscribe(({ results }) => + this.socket.emit(WebsocketEvent.mapVoteResultsUpdate, results), + ); this.events.substituteRequestsChange.subscribe(async () => { const requests = await this.queueAnnouncementsService.substituteRequests(); this.socket.emit(WebsocketEvent.substituteRequestsUpdate, requests); @@ -70,7 +84,10 @@ export class QueueGateway implements OnGatewayInit, OnModuleInit { @WsAuthorized() @SubscribeMessage('mark friend') markFriend(client: AuthorizedWsClient, payload: { friendPlayerId: string }) { - return this.friendsService.markFriend(client.request.user.id, payload.friendPlayerId); + return this.friendsService.markFriend( + client.request.user.id, + payload.friendPlayerId, + ); } @WsAuthorized() @@ -83,5 +100,4 @@ export class QueueGateway implements OnGatewayInit, OnModuleInit { afterInit(socket: Socket) { this.socket = socket; } - } diff --git a/src/queue/interceptors/populate-players.interceptor.spec.ts b/src/queue/interceptors/populate-players.interceptor.spec.ts index 39a7f933e..93469d0b4 100644 --- a/src/queue/interceptors/populate-players.interceptor.spec.ts +++ b/src/queue/interceptors/populate-players.interceptor.spec.ts @@ -12,19 +12,29 @@ describe('PopulatePlayersInterceptor', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ - PlayerPopulatorService, - ], + providers: [PlayerPopulatorService], }).compile(); - const playerPopulatorService = module.get(PlayerPopulatorService) as jest.Mocked; + const playerPopulatorService = module.get( + PlayerPopulatorService, + ) as jest.Mocked; interceptor = new PopulatePlayersInterceptor(playerPopulatorService); - playerPopulatorService.populatePlayer.mockResolvedValue( - { id: 2, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_PLAYER_ID', player: { id: 'FAKE_PLAYER_ID' } as Player, } - ); + playerPopulatorService.populatePlayer.mockResolvedValue({ + id: 2, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_PLAYER_ID', + player: { id: 'FAKE_PLAYER_ID' } as Player, + }); playerPopulatorService.populatePlayers.mockResolvedValue([ - { id: 2, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_PLAYER_ID', player: { id: 'FAKE_PLAYER_ID' } as Player, } + { + id: 2, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_PLAYER_ID', + player: { id: 'FAKE_PLAYER_ID' } as Player, + }, ]); }); @@ -32,27 +42,55 @@ describe('PopulatePlayersInterceptor', () => { expect(interceptor).toBeDefined(); }); - it('should resolve for single slot', async () => new Promise(resolve => { - const next = { - handle: () => of({ id: 2, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_PLAYER_ID' }), - }; + it('should resolve for single slot', async () => + new Promise((resolve) => { + const next = { + handle: () => + of({ + id: 2, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_PLAYER_ID', + }), + }; - interceptor.intercept(null, next).subscribe(data => { - expect(data).toEqual({ id: 2, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_PLAYER_ID', player: { id: 'FAKE_PLAYER_ID' } }); - resolve(); - }); - })); - - it('should resolve for slots array', async () => new Promise(resolve => { - const next = { - handle: () => of([ { id: 2, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_PLAYER_ID' } ]), - }; - - interceptor.intercept(null, next).subscribe(data => { - expect(data).toEqual([ - { id: 2, gameClass: Tf2ClassName.soldier, ready: false, playerId: 'FAKE_PLAYER_ID', player: { id: 'FAKE_PLAYER_ID' }, }, - ]); - resolve(); - }); - })); + interceptor.intercept(null, next).subscribe((data) => { + expect(data).toEqual({ + id: 2, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_PLAYER_ID', + player: { id: 'FAKE_PLAYER_ID' }, + }); + resolve(); + }); + })); + + it('should resolve for slots array', async () => + new Promise((resolve) => { + const next = { + handle: () => + of([ + { + id: 2, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_PLAYER_ID', + }, + ]), + }; + + interceptor.intercept(null, next).subscribe((data) => { + expect(data).toEqual([ + { + id: 2, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: 'FAKE_PLAYER_ID', + player: { id: 'FAKE_PLAYER_ID' }, + }, + ]); + resolve(); + }); + })); }); diff --git a/src/queue/interceptors/populate-players.interceptor.ts b/src/queue/interceptors/populate-players.interceptor.ts index 8d501cb5f..8b0651d12 100644 --- a/src/queue/interceptors/populate-players.interceptor.ts +++ b/src/queue/interceptors/populate-players.interceptor.ts @@ -1,4 +1,9 @@ -import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; import { from } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { QueueSlot } from '../queue-slot'; @@ -6,10 +11,7 @@ import { PlayerPopulatorService } from '../services/player-populator.service'; @Injectable() export class PopulatePlayersInterceptor implements NestInterceptor { - - constructor( - private playerPopulatorService: PlayerPopulatorService, - ) { } + constructor(private playerPopulatorService: PlayerPopulatorService) {} intercept(context: ExecutionContext, next: CallHandler) { return next.handle().pipe( diff --git a/src/queue/models/map.ts b/src/queue/models/map.ts index 8b10e7a33..abb75daf9 100644 --- a/src/queue/models/map.ts +++ b/src/queue/models/map.ts @@ -2,7 +2,6 @@ import { prop } from '@typegoose/typegoose'; import { IsOptional, IsString } from 'class-validator'; export class Map { - @IsString() @prop({ required: true, unique: true }) name!: string; @@ -15,5 +14,4 @@ export class Map { // when the cooldown is 0, we're good to use this map again @prop({ default: 0 }) cooldown?: number; - } diff --git a/src/queue/queue.module.ts b/src/queue/queue.module.ts index a382daeac..b3491193d 100644 --- a/src/queue/queue.module.ts +++ b/src/queue/queue.module.ts @@ -21,9 +21,7 @@ import { Map } from './models/map'; @Module({ imports: [ - TypegooseModule.forFeature([ - standardSchemaOptions(Map), - ]), + TypegooseModule.forFeature([standardSchemaOptions(Map)]), forwardRef(() => PlayersModule), forwardRef(() => GamesModule), @@ -39,10 +37,14 @@ import { Map } from './models/map'; { provide: 'QUEUE_CONFIG_JSON', useFactory: async (environment: Environment) => { - const configFileName = join('configs', 'queue', `${environment.queueConfig}.json`); + const configFileName = join( + 'configs', + 'queue', + `${environment.queueConfig}.json`, + ); return promisify(readFile)(configFileName, 'utf-8'); }, - inject: [ Environment ], + inject: [Environment], }, PlayerPopulatorService, MapPoolService, @@ -54,8 +56,6 @@ import { Map } from './models/map'; QueueGateway, MapPoolService, ], - controllers: [ - QueueController, - ], + controllers: [QueueController], }) -export class QueueModule { } +export class QueueModule {} diff --git a/src/queue/services/auto-game-launcher.service.spec.ts b/src/queue/services/auto-game-launcher.service.spec.ts index 1e854f12b..bced58094 100644 --- a/src/queue/services/auto-game-launcher.service.spec.ts +++ b/src/queue/services/auto-game-launcher.service.spec.ts @@ -29,14 +29,26 @@ describe('AutoGameLauncherService', () => { // @ts-expect-error FriendsService.mockImplementation(() => ({ - friendships: [{ sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }], + friendships: [ + { sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }, + ], })); // @ts-expect-error QueueService.mockImplementation(() => ({ slots: [ - { id: 0, playerId: 'FAKE_PLAYER_ID_1', gameClass: 'soldier', ready: true }, - { id: 1, playerId: 'FAKE_PLAYER_ID_2', gameClass: 'soldier', ready: true }, + { + id: 0, + playerId: 'FAKE_PLAYER_ID_1', + gameClass: 'soldier', + ready: true, + }, + { + id: 1, + playerId: 'FAKE_PLAYER_ID_2', + gameClass: 'soldier', + ready: true, + }, ], reset: jest.fn(), })); @@ -67,12 +79,16 @@ describe('AutoGameLauncherService', () => { }); it('should launch the game and reset the queue', async () => { - return new Promise(resolve => { + return new Promise((resolve) => { events.queueStateChange.next({ state: 'launching' }); setImmediate(() => { expect(queueService.reset).toHaveBeenCalled(); - expect(gamesService.create).toHaveBeenCalledWith(queueService.slots, 'cp_badlands', [['FAKE_MEDIC', 'FAKE_DM_CLASS']]); + expect(gamesService.create).toHaveBeenCalledWith( + queueService.slots, + 'cp_badlands', + [['FAKE_MEDIC', 'FAKE_DM_CLASS']], + ); expect(gamesService.launch).toHaveBeenCalledWith('FAKE_GAME_ID'); resolve(); }); diff --git a/src/queue/services/auto-game-launcher.service.ts b/src/queue/services/auto-game-launcher.service.ts index d31814859..e8a4a04de 100644 --- a/src/queue/services/auto-game-launcher.service.ts +++ b/src/queue/services/auto-game-launcher.service.ts @@ -14,26 +14,31 @@ import { Events } from '@/events/events'; */ @Injectable() export class AutoGameLauncherService { - constructor( private queueService: QueueService, private mapVoteService: MapVoteService, private gamesService: GamesService, private friendsService: FriendsService, private events: Events, - ) { } + ) {} onModuleInit() { - this.events.queueStateChange.pipe( - filter(({ state }) => state === 'launching'), - ).subscribe(() => this.launchGame()); + this.events.queueStateChange + .pipe(filter(({ state }) => state === 'launching')) + .subscribe(() => this.launchGame()); } private async launchGame() { - const friends = this.friendsService.friendships.map(f => [ f.sourcePlayerId, f.targetPlayerId ]); - const game = await this.gamesService.create(this.queueService.slots, await this.mapVoteService.getWinner(), friends); + const friends = this.friendsService.friendships.map((f) => [ + f.sourcePlayerId, + f.targetPlayerId, + ]); + const game = await this.gamesService.create( + this.queueService.slots, + await this.mapVoteService.getWinner(), + friends, + ); this.queueService.reset(); await this.gamesService.launch(game.id); } - } diff --git a/src/queue/services/friends.service.spec.ts b/src/queue/services/friends.service.spec.ts index c7e6ce732..01be0506e 100644 --- a/src/queue/services/friends.service.spec.ts +++ b/src/queue/services/friends.service.spec.ts @@ -8,11 +8,28 @@ import { Tf2ClassName } from '@/shared/models/tf2-class-name'; class QueueServiceStub { state = 'waiting'; slots: QueueSlot[] = [ - { id: 0, playerId: 'FAKE_MEDIC', gameClass: Tf2ClassName.medic, ready: false }, - { id: 1, playerId: 'FAKE_DM_CLASS', gameClass: Tf2ClassName.soldier, ready: false }, - { id: 2, playerId: 'FAKE_2ND_MEDIC', gameClass: Tf2ClassName.medic, ready: false }, + { + id: 0, + playerId: 'FAKE_MEDIC', + gameClass: Tf2ClassName.medic, + ready: false, + }, + { + id: 1, + playerId: 'FAKE_DM_CLASS', + gameClass: Tf2ClassName.soldier, + ready: false, + }, + { + id: 2, + playerId: 'FAKE_2ND_MEDIC', + gameClass: Tf2ClassName.medic, + ready: false, + }, ]; - findSlotByPlayerId(playerId: string) { return this.slots.find(s => s.playerId === playerId); } + findSlotByPlayerId(playerId: string) { + return this.slots.find((s) => s.playerId === playerId); + } } describe('FriendsService', () => { @@ -43,37 +60,57 @@ describe('FriendsService', () => { describe('markFriend()', () => { it('should fail if the queue is in launching state', () => { queueService.state = 'launching'; - expect(() => service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS')).toThrowError('cannot make friends at this stage'); + expect(() => + service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'), + ).toThrowError('cannot make friends at this stage'); }); it('should fail if the medic is not in the queue', () => { - queueService.slots = queueService.slots.filter(s => s.playerId !== 'FAKE_MEDIC'); - expect(() => service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS')).toThrowError('player not in the queue'); + queueService.slots = queueService.slots.filter( + (s) => s.playerId !== 'FAKE_MEDIC', + ); + expect(() => + service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'), + ).toThrowError('player not in the queue'); }); it('should fail if the friend is not in the queue', () => { - queueService.slots = queueService.slots.filter(s => s.playerId !== 'FAKE_DM_CLASS'); - expect(() => service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS')).toThrowError('player not in the queue'); + queueService.slots = queueService.slots.filter( + (s) => s.playerId !== 'FAKE_DM_CLASS', + ); + expect(() => + service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'), + ).toThrowError('player not in the queue'); }); it('should fail if the medic is not a medic after all', () => { queueService.slots[0].gameClass = Tf2ClassName.scout; - expect(() => service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS')).toThrowError('only medics can make friends'); + expect(() => + service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'), + ).toThrowError('only medics can make friends'); }); it('should fail the friend is also a medic', () => { queueService.slots[1].gameClass = Tf2ClassName.medic; - expect(() => service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS')).toThrowError('cannot make the other medic as a friend'); + expect(() => + service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'), + ).toThrowError('cannot make the other medic as a friend'); }); it('should fail if the target player is already marked as a friend', () => { service.markFriend('FAKE_2ND_MEDIC', 'FAKE_DM_CLASS'); - expect(() => service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS')).toThrowError('this player is already marked as a friend by another player'); + expect(() => + service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'), + ).toThrowError( + 'this player is already marked as a friend by another player', + ); }); it('should mark friends', () => { const friendships = service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'); - expect(friendships).toEqual([{ sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }]); + expect(friendships).toEqual([ + { sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }, + ]); }); it('should unmark friends', () => { @@ -83,20 +120,30 @@ describe('FriendsService', () => { }); it('should remove previous frienship', () => { - queueService.slots.push({ id: 2, playerId: 'ANOTHER_PLAYER', gameClass: Tf2ClassName.soldier, ready: false }); + queueService.slots.push({ + id: 2, + playerId: 'ANOTHER_PLAYER', + gameClass: Tf2ClassName.soldier, + ready: false, + }); service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'); const friendships = service.markFriend('FAKE_MEDIC', 'ANOTHER_PLAYER'); - expect(friendships).toEqual([{ sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'ANOTHER_PLAYER' }]); + expect(friendships).toEqual([ + { sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'ANOTHER_PLAYER' }, + ]); }); - it('should emit the queueFriendshipsChange event', async () => new Promise(resolve => { - events.queueFriendshipsChange.subscribe(({ friendships }) => { - expect(friendships).toEqual([{ sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }]); - resolve(); - }); - - service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'); - })); + it('should emit the queueFriendshipsChange event', async () => + new Promise((resolve) => { + events.queueFriendshipsChange.subscribe(({ friendships }) => { + expect(friendships).toEqual([ + { sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }, + ]); + resolve(); + }); + + service.markFriend('FAKE_MEDIC', 'FAKE_DM_CLASS'); + })); }); describe('onSlotsChange', () => { @@ -105,7 +152,9 @@ describe('FriendsService', () => { }); it('should remove frienship if the medic leaves the queue', () => { - queueService.slots = queueService.slots.filter(s => s.playerId !== 'FAKE_MEDIC'); + queueService.slots = queueService.slots.filter( + (s) => s.playerId !== 'FAKE_MEDIC', + ); events.queueSlotsChange.next({ slots: queueService.slots }); expect(service.friendships).toEqual([]); }); @@ -117,9 +166,13 @@ describe('FriendsService', () => { }); it('should not remove frienship if the friend leaves the queue', () => { - queueService.slots = queueService.slots.filter(s => s.playerId !== 'FAKE_DM_CLASS'); + queueService.slots = queueService.slots.filter( + (s) => s.playerId !== 'FAKE_DM_CLASS', + ); events.queueSlotsChange.next({ slots: queueService.slots }); - expect(service.friendships).toEqual([{ sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }]); + expect(service.friendships).toEqual([ + { sourcePlayerId: 'FAKE_MEDIC', targetPlayerId: 'FAKE_DM_CLASS' }, + ]); }); }); }); diff --git a/src/queue/services/friends.service.ts b/src/queue/services/friends.service.ts index 2a61d7701..8b9451100 100644 --- a/src/queue/services/friends.service.ts +++ b/src/queue/services/friends.service.ts @@ -9,13 +9,9 @@ export interface Friendship { @Injectable() export class FriendsService implements OnModuleInit { - friendships: Friendship[] = []; - constructor( - private queueService: QueueService, - private events: Events, - ) { } + constructor(private queueService: QueueService, private events: Events) {} onModuleInit() { this.events.queueSlotsChange.subscribe(() => this.cleanupFriendships()); @@ -26,13 +22,18 @@ export class FriendsService implements OnModuleInit { throw new Error('cannot make friends at this stage'); } - if (targetPlayerId === null) { // only removing frienship + if (targetPlayerId === null) { + // only removing frienship this.friendships = [ - ...this.friendships.filter(f => f.sourcePlayerId !== sourcePlayerId), + ...this.friendships.filter((f) => f.sourcePlayerId !== sourcePlayerId), ]; } else { - const sourcePlayerSlot = this.queueService.findSlotByPlayerId(sourcePlayerId); - const targetPlayerSlot = this.queueService.findSlotByPlayerId(targetPlayerId); + const sourcePlayerSlot = this.queueService.findSlotByPlayerId( + sourcePlayerId, + ); + const targetPlayerSlot = this.queueService.findSlotByPlayerId( + targetPlayerId, + ); if (!sourcePlayerSlot || !targetPlayerSlot) { throw new Error('player not in the queue'); } @@ -45,12 +46,17 @@ export class FriendsService implements OnModuleInit { throw new Error('cannot make the other medic as a friend'); } - if (targetPlayerId !== null && !!this.friendships.find(f => f.targetPlayerId === targetPlayerId)) { - throw new Error('this player is already marked as a friend by another player'); + if ( + targetPlayerId !== null && + !!this.friendships.find((f) => f.targetPlayerId === targetPlayerId) + ) { + throw new Error( + 'this player is already marked as a friend by another player', + ); } this.friendships = [ - ...this.friendships.filter(f => f.sourcePlayerId !== sourcePlayerId), + ...this.friendships.filter((f) => f.sourcePlayerId !== sourcePlayerId), { sourcePlayerId, targetPlayerId }, ]; } @@ -60,8 +66,11 @@ export class FriendsService implements OnModuleInit { } private cleanupFriendships() { - this.friendships = this.friendships.filter(f => this.queueService.findSlotByPlayerId(f.sourcePlayerId)?.gameClass === 'medic'); + this.friendships = this.friendships.filter( + (f) => + this.queueService.findSlotByPlayerId(f.sourcePlayerId)?.gameClass === + 'medic', + ); this.events.queueFriendshipsChange.next({ friendships: this.friendships }); } - } diff --git a/src/queue/services/map-pool.service.spec.ts b/src/queue/services/map-pool.service.spec.ts index ee2a1f78a..36431ff69 100644 --- a/src/queue/services/map-pool.service.spec.ts +++ b/src/queue/services/map-pool.service.spec.ts @@ -14,7 +14,7 @@ describe('MapPoolService', () => { let mapModel: ReturnModelType; let events: Events; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -23,10 +23,7 @@ describe('MapPoolService', () => { typegooseTestingModule(mongod), TypegooseModule.forFeature([Map]), ], - providers: [ - MapPoolService, - Events, - ], + providers: [MapPoolService, Events], }).compile(); service = module.get(MapPoolService); @@ -35,7 +32,7 @@ describe('MapPoolService', () => { }); afterEach(async () => { - await mapModel.deleteMany({ }); + await mapModel.deleteMany({}); }); it('should be defined', () => { @@ -62,17 +59,20 @@ describe('MapPoolService', () => { it('should list the maps', async () => { await service.onModuleInit(); - expect(await service.getMaps()).toMatchObject([ { name: 'cp_badlands', cooldown: 0 } ]); + expect(await service.getMaps()).toMatchObject([ + { name: 'cp_badlands', cooldown: 0 }, + ]); }); - it('should emit an event', async () => new Promise(resolve => { - events.mapPoolChange.subscribe(({ maps }) => { - expect(maps).toMatchObject([ { name: 'cp_badlands', cooldown: 0 } ]); - resolve(); - }); + it('should emit an event', async () => + new Promise((resolve) => { + events.mapPoolChange.subscribe(({ maps }) => { + expect(maps).toMatchObject([{ name: 'cp_badlands', cooldown: 0 }]); + resolve(); + }); - service.onModuleInit(); - })); + service.onModuleInit(); + })); }); }); @@ -80,17 +80,22 @@ describe('MapPoolService', () => { it('should add the map', async () => { const map = await service.addMap({ name: 'cp_obscure_final' }); expect(map).toMatchObject({ name: 'cp_obscure_final', cooldown: 0 }); - expect(await service.getMaps()).toEqual(expect.arrayContaining([ expect.objectContaining({ name: 'cp_obscure_final' }) ])); + expect(await service.getMaps()).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'cp_obscure_final' }), + ]), + ); }); - it('should emit the event', async () => new Promise(resolve => { - events.mapPoolChange.subscribe(({ maps }) => { - expect(maps.find(m => m.name === 'cp_obscure_final')).toBeTruthy(); - resolve(); - }); + it('should emit the event', async () => + new Promise((resolve) => { + events.mapPoolChange.subscribe(({ maps }) => { + expect(maps.find((m) => m.name === 'cp_obscure_final')).toBeTruthy(); + resolve(); + }); - service.addMap({ name: 'cp_obscure_final' }); - })); + service.addMap({ name: 'cp_obscure_final' }); + })); }); describe('#removeMap()', () => { @@ -103,14 +108,17 @@ describe('MapPoolService', () => { expect(map).toMatchObject({ name: 'cp_obscure_final' }); }); - it('should emit the event', async () => new Promise(resolve => { - events.mapPoolChange.pipe(skip(1)).subscribe(({ maps }) => { - expect(maps.find(m => m.name === 'cp_obscure_final')).toBe(undefined); - resolve(); - }); + it('should emit the event', async () => + new Promise((resolve) => { + events.mapPoolChange.pipe(skip(1)).subscribe(({ maps }) => { + expect(maps.find((m) => m.name === 'cp_obscure_final')).toBe( + undefined, + ); + resolve(); + }); - service.removeMap('cp_obscure_final'); - })); + service.removeMap('cp_obscure_final'); + })); }); describe('#setMaps()', () => { @@ -125,19 +133,20 @@ describe('MapPoolService', () => { ]); }); - it('should emit the event', async () => new Promise(resolve => { - events.mapPoolChange.subscribe(({ maps }) => { - expect(maps).toEqual([ - expect.objectContaining({ name: 'cp_badlands' }), - expect.objectContaining({ name: 'cp_obscure_final' }), - ]); - resolve(); - }); + it('should emit the event', async () => + new Promise((resolve) => { + events.mapPoolChange.subscribe(({ maps }) => { + expect(maps).toEqual([ + expect.objectContaining({ name: 'cp_badlands' }), + expect.objectContaining({ name: 'cp_obscure_final' }), + ]); + resolve(); + }); - service.setMaps([ - { name: 'cp_badlands' }, - { name: 'cp_obscure_final' }, - ]); - })); + service.setMaps([ + { name: 'cp_badlands' }, + { name: 'cp_obscure_final' }, + ]); + })); }); }); diff --git a/src/queue/services/map-pool.service.ts b/src/queue/services/map-pool.service.ts index 12be6a0b8..fb304b1e0 100644 --- a/src/queue/services/map-pool.service.ts +++ b/src/queue/services/map-pool.service.ts @@ -9,20 +9,23 @@ import { Map } from '../models/map'; @Injectable() export class MapPoolService implements OnModuleInit { - private logger = new Logger(MapPoolService.name); constructor( @InjectModel(Map) private mapModel: ReturnModelType, private events: Events, ) { - new Validator().validate(defaultMapPool, mapPoolSchema, { throwError: true }); + new Validator().validate(defaultMapPool, mapPoolSchema, { + throwError: true, + }); } async onModuleInit() { const mapCount = await this.mapModel.countDocuments(); if (mapCount === 0) { - this.logger.log('Map pool empty! Initializing it with the default one...'); + this.logger.log( + 'Map pool empty! Initializing it with the default one...', + ); await this.mapModel.insertMany(defaultMapPool.maps); } @@ -46,7 +49,7 @@ export class MapPoolService implements OnModuleInit { } async setMaps(maps: Map[]): Promise { - await this.mapModel.deleteMany({ }); + await this.mapModel.deleteMany({}); const ret = await this.mapModel.insertMany(maps); this.refreshMaps(); return ret; @@ -56,5 +59,4 @@ export class MapPoolService implements OnModuleInit { const maps = await this.getMaps(); this.events.mapPoolChange.next({ maps }); } - } diff --git a/src/queue/services/map-vote.service.spec.ts b/src/queue/services/map-vote.service.spec.ts index 71c1a84f2..7e80345bc 100644 --- a/src/queue/services/map-vote.service.spec.ts +++ b/src/queue/services/map-vote.service.spec.ts @@ -18,7 +18,7 @@ describe('MapVoteService', () => { let queueService: jest.Mocked; let events: Events; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { @@ -27,11 +27,7 @@ describe('MapVoteService', () => { typegooseTestingModule(mongod), TypegooseModule.forFeature([Map]), ], - providers: [ - MapVoteService, - QueueService, - Events, - ], + providers: [MapVoteService, QueueService, Events], }).compile(); service = module.get(MapVoteService); @@ -42,9 +38,9 @@ describe('MapVoteService', () => { beforeEach(async () => { await mapModel.insertMany([ - { name: 'cp_badlands' }, - { name: 'cp_process_final' }, - { name: 'cp_snakewater_final1' }, + { name: 'cp_badlands' }, + { name: 'cp_process_final' }, + { name: 'cp_snakewater_final1' }, ]); queueService.isInQueue.mockReturnValue(true); }); @@ -52,7 +48,7 @@ describe('MapVoteService', () => { beforeEach(async () => await service.onModuleInit()); afterEach(async () => { - await mapModel.deleteMany({ }); + await mapModel.deleteMany({}); }); it('should be defined', () => { @@ -60,17 +56,19 @@ describe('MapVoteService', () => { }); it('should reset all votes initially', () => { - expect(service.results.every(r => r.voteCount === 0)).toBe(true); + expect(service.results.every((r) => r.voteCount === 0)).toBe(true); }); describe('#voteForMap()', () => { it('should save the vote', () => { service.voteForMap('FAKE_ID', 'cp_badlands'); - expect(service.results).toEqual(expect.arrayContaining([ - { map: 'cp_badlands', voteCount: 1 }, - { map: 'cp_process_final', voteCount: 0 }, - { map: 'cp_snakewater_final1', voteCount: 0 }, - ])); + expect(service.results).toEqual( + expect.arrayContaining([ + { map: 'cp_badlands', voteCount: 1 }, + { map: 'cp_process_final', voteCount: 0 }, + { map: 'cp_snakewater_final1', voteCount: 0 }, + ]), + ); expect(service.voteCountForMap('cp_badlands')).toEqual(1); }); @@ -84,26 +82,34 @@ describe('MapVoteService', () => { }); it('should deny', () => { - expect(() => service.voteForMap('FAKE_ID', 'cp_badlands')).toThrowError(); + expect(() => + service.voteForMap('FAKE_ID', 'cp_badlands'), + ).toThrowError(); }); }); - it('should remove the player\'s vote when the player leaves the queue', () => { + it("should remove the player's vote when the player leaves the queue", () => { service.voteForMap('FAKE_PLAYER_ID', 'cp_badlands'); expect(service.voteCountForMap('cp_badlands')).toEqual(1); - events.playerLeavesQueue.next({ playerId: 'FAKE_PLAYER_ID', reason: 'manual' }); + events.playerLeavesQueue.next({ + playerId: 'FAKE_PLAYER_ID', + reason: 'manual', + }); expect(service.voteCountForMap('cp_badlands')).toEqual(0); }); - it('should emit the mapVotesChange event', async () => new Promise(resolve => { - events.mapVotesChange.subscribe(({ results }) => { - expect(results.length).toEqual(3); - expect(results.find(r => r.map === 'cp_badlands').voteCount).toEqual(1); - resolve(); - }); + it('should emit the mapVotesChange event', async () => + new Promise((resolve) => { + events.mapVotesChange.subscribe(({ results }) => { + expect(results.length).toEqual(3); + expect( + results.find((r) => r.map === 'cp_badlands').voteCount, + ).toEqual(1); + resolve(); + }); - service.voteForMap('FAKE_ID', 'cp_badlands'); - })); + service.voteForMap('FAKE_ID', 'cp_badlands'); + })); }); describe('#getWinner()', () => { @@ -118,16 +124,19 @@ describe('MapVoteService', () => { expect(await service.getWinner()).toMatch(/cp_badlands|cp_process_final/); }); - it('should eventually reset the vote', async () => new Promise(resolve => { - events.mapVotesChange.pipe(skip(1)).subscribe(({ results }) => { - expect(results.every(r => r.voteCount === 0)).toBe(true); - expect(service.mapOptions.every(m => m !== 'cp_badlands')).toBe(true); - resolve(); - }); + it('should eventually reset the vote', async () => + new Promise((resolve) => { + events.mapVotesChange.pipe(skip(1)).subscribe(({ results }) => { + expect(results.every((r) => r.voteCount === 0)).toBe(true); + expect(service.mapOptions.every((m) => m !== 'cp_badlands')).toBe( + true, + ); + resolve(); + }); - service.voteForMap('FAKE_ID_1', 'cp_badlands'); - service.getWinner(); - })); + service.voteForMap('FAKE_ID_1', 'cp_badlands'); + service.getWinner(); + })); describe('when a map is chosen', () => { beforeEach(async () => { @@ -160,7 +169,7 @@ describe('MapVoteService', () => { const maps = await mapModel.find(); events.mapPoolChange.next({ maps }); - await new Promise(resolve => setTimeout(resolve, 100)); - expect(service.results.every(r => r.voteCount === 0)).toBe(true); + await new Promise((resolve) => setTimeout(resolve, 100)); + expect(service.results.every((r) => r.voteCount === 0)).toBe(true); }); }); diff --git a/src/queue/services/map-vote.service.ts b/src/queue/services/map-vote.service.ts index d39d5eed9..11af829ef 100644 --- a/src/queue/services/map-vote.service.ts +++ b/src/queue/services/map-vote.service.ts @@ -16,7 +16,6 @@ interface MapVote { @Injectable() export class MapVoteService implements OnModuleInit { - private readonly logger = new Logger(MapVoteService.name); private readonly _results = new BehaviorSubject([]); @@ -34,18 +33,22 @@ export class MapVoteService implements OnModuleInit { @InjectModel(Map) private mapModel: ReturnModelType, private queueService: QueueService, private events: Events, - ) { } + ) {} async onModuleInit() { - this._results.subscribe(results => this.events.mapVotesChange.next({ results })); + this._results.subscribe((results) => + this.events.mapVotesChange.next({ results }), + ); - this.events.playerLeavesQueue.subscribe(({ playerId }) => this.resetPlayerVote(playerId)); + this.events.playerLeavesQueue.subscribe(({ playerId }) => + this.resetPlayerVote(playerId), + ); this.events.mapPoolChange.subscribe(() => this.scramble()); await this.scramble(); } voteCountForMap(map: string): number { - return this.votes.filter(v => v.map === map).length; + return this.votes.filter((v) => v.map === map).length; } voteForMap(playerId: string, map: string) { @@ -58,7 +61,7 @@ export class MapVoteService implements OnModuleInit { } this.votes = [ - ...this.votes.filter(v => v.playerId !== playerId), + ...this.votes.filter((v) => v.playerId !== playerId), { map, playerId }, ]; @@ -66,17 +69,23 @@ export class MapVoteService implements OnModuleInit { } playerVote(playerId: string): string { - return this.votes.find(v => v.playerId === playerId)?.map; + return this.votes.find((v) => v.playerId === playerId)?.map; } /** * Decides the winner and resets the vote. */ async getWinner() { - const maxVotes = maxBy(this.results, r => r.voteCount).voteCount; - const mapsWithMaxVotes = this.results.filter(m => m.voteCount === maxVotes); - const map = mapsWithMaxVotes[Math.floor(Math.random() * mapsWithMaxVotes.length)].map; - await this.mapModel.updateMany({ cooldown: { $gt: 0 } }, { $inc: { cooldown: -1 } }); + const maxVotes = maxBy(this.results, (r) => r.voteCount).voteCount; + const mapsWithMaxVotes = this.results.filter( + (m) => m.voteCount === maxVotes, + ); + const map = + mapsWithMaxVotes[Math.floor(Math.random() * mapsWithMaxVotes.length)].map; + await this.mapModel.updateMany( + { cooldown: { $gt: 0 } }, + { $inc: { cooldown: -1 } }, + ); await this.mapModel.updateOne({ name: map }, { cooldown: mapCooldown }); setImmediate(() => this.scramble()); return map; @@ -86,9 +95,11 @@ export class MapVoteService implements OnModuleInit { * Randomly maps that will be available for vote. */ async scramble() { - this.mapOptions = shuffle(await this.mapModel.find({ cooldown: { $lte: 0 } }).exec()) + this.mapOptions = shuffle( + await this.mapModel.find({ cooldown: { $lte: 0 } }).exec(), + ) .slice(0, this.mapVoteOptionCount) - .map(m => m.name); + .map((m) => m.name); this.logger.debug(`Map options: ${this.mapOptions.join(',')}`); this.votes = []; this._results.next(this.getResults()); @@ -96,13 +107,14 @@ export class MapVoteService implements OnModuleInit { } private resetPlayerVote(playerId: string) { - this.votes = [ ...this.votes.filter(v => v.playerId !== playerId) ]; + this.votes = [...this.votes.filter((v) => v.playerId !== playerId)]; this._results.next(this.getResults()); } private getResults() { - return this.mapOptions - .map(map => ({ map, voteCount: this.voteCountForMap(map) })); + return this.mapOptions.map((map) => ({ + map, + voteCount: this.voteCountForMap(map), + })); } - } diff --git a/src/queue/services/player-populator.service.spec.ts b/src/queue/services/player-populator.service.spec.ts index 826ea0fb2..37490d47e 100644 --- a/src/queue/services/player-populator.service.spec.ts +++ b/src/queue/services/player-populator.service.spec.ts @@ -18,19 +18,16 @@ describe('PlayerPopulatorService', () => { let playersService: PlayersService; let mockPlayer: DocumentType; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ Player ]), - ], - providers: [ - PlayerPopulatorService, - PlayersService, + TypegooseModule.forFeature([Player]), ], + providers: [PlayerPopulatorService, PlayersService], }).compile(); service = module.get(PlayerPopulatorService); @@ -54,15 +51,31 @@ describe('PlayerPopulatorService', () => { describe('#populatePlayer()', () => { describe('when the playerId is null', () => { it('should return the unchanged slot', async () => { - const slot: QueueSlot = { id: 3, gameClass: Tf2ClassName.soldier, ready: false, playerId: null }; - expect(await service.populatePlayer(slot)).toEqual({ ...slot, player: null }); + const slot: QueueSlot = { + id: 3, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: null, + }; + expect(await service.populatePlayer(slot)).toEqual({ + ...slot, + player: null, + }); }); }); describe('when the playerId is defined', () => { it('should populate the player', async () => { - const slot: QueueSlot = { id: 3, gameClass: Tf2ClassName.soldier, ready: false, playerId: mockPlayer.id }; - expect(await service.populatePlayer(slot)).toEqual({ ...slot, player: plainToClass(Player, mockPlayer.toObject()) }); + const slot: QueueSlot = { + id: 3, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: mockPlayer.id, + }; + expect(await service.populatePlayer(slot)).toEqual({ + ...slot, + player: plainToClass(Player, mockPlayer.toObject()), + }); }); }); }); @@ -70,13 +83,35 @@ describe('PlayerPopulatorService', () => { describe('#populatePlayers', () => { it('should populate players in the array', async () => { const slots: QueueSlot[] = [ - { id: 0, gameClass: Tf2ClassName.soldier, ready: false, playerId: null }, - { id: 1, gameClass: Tf2ClassName.soldier, ready: false, playerId: mockPlayer.id }, + { + id: 0, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: null, + }, + { + id: 1, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: mockPlayer.id, + }, ]; expect(await service.populatePlayers(slots)).toEqual([ - { id: 0, gameClass: Tf2ClassName.soldier, ready: false, playerId: null, player: null }, - { id: 1, gameClass: Tf2ClassName.soldier, ready: false, playerId: mockPlayer.id, player: plainToClass(Player, mockPlayer.toObject()) }, + { + id: 0, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: null, + player: null, + }, + { + id: 1, + gameClass: Tf2ClassName.soldier, + ready: false, + playerId: mockPlayer.id, + player: plainToClass(Player, mockPlayer.toObject()), + }, ]); }); }); diff --git a/src/queue/services/player-populator.service.ts b/src/queue/services/player-populator.service.ts index 819490534..f30cb78eb 100644 --- a/src/queue/services/player-populator.service.ts +++ b/src/queue/services/player-populator.service.ts @@ -4,17 +4,18 @@ import { QueueSlot } from '../queue-slot'; @Injectable() export class PlayerPopulatorService { - - constructor( - private playersService: PlayersService, - ) { } + constructor(private playersService: PlayersService) {} async populatePlayer(slot: QueueSlot) { - return { ...slot, player: slot.playerId ? (await this.playersService.getById(slot.playerId)) : null }; + return { + ...slot, + player: slot.playerId + ? await this.playersService.getById(slot.playerId) + : null, + }; } async populatePlayers(slots: QueueSlot[]) { - return Promise.all(slots.map(slot => this.populatePlayer(slot))); + return Promise.all(slots.map((slot) => this.populatePlayer(slot))); } - } diff --git a/src/queue/services/queue-announcements.service.ts b/src/queue/services/queue-announcements.service.ts index b87522202..ed2635f6a 100644 --- a/src/queue/services/queue-announcements.service.ts +++ b/src/queue/services/queue-announcements.service.ts @@ -5,15 +5,14 @@ import { SlotStatus } from '@/games/models/slot-status'; @Injectable() export class QueueAnnouncementsService { - constructor( @Inject(forwardRef(() => GamesService)) private gamesService: GamesService, - ) { } + ) {} async substituteRequests(): Promise { const games = await this.gamesService.getGamesWithSubstitutionRequests(); - return games.flatMap(game => { - return game.slots.flatMap(slot => { + return games.flatMap((game) => { + return game.slots.flatMap((slot) => { if (slot.status !== SlotStatus.waitingForSubstitute) { return []; } @@ -27,5 +26,4 @@ export class QueueAnnouncementsService { }); }); } - } diff --git a/src/queue/services/queue-config.service.ts b/src/queue/services/queue-config.service.ts index 96088cdd9..fce875534 100644 --- a/src/queue/services/queue-config.service.ts +++ b/src/queue/services/queue-config.service.ts @@ -5,14 +5,11 @@ import * as queueConfigSchema from '../queue-config.schema.json'; @Injectable() export class QueueConfigService { - queueConfig: QueueConfig; private readonly logger = new Logger(QueueConfigService.name); - constructor( - @Inject('QUEUE_CONFIG_JSON') queueConfigJson: string, - ) { + constructor(@Inject('QUEUE_CONFIG_JSON') queueConfigJson: string) { const config = JSON.parse(queueConfigJson); this.validateConfig(config); this.queueConfig = config as QueueConfig; @@ -21,5 +18,4 @@ export class QueueConfigService { private validateConfig(config: any) { new Validator().validate(config, queueConfigSchema, { throwError: true }); } - } diff --git a/src/queue/services/queue.service.spec.ts b/src/queue/services/queue.service.spec.ts index e9b271433..f3e7f8664 100644 --- a/src/queue/services/queue.service.spec.ts +++ b/src/queue/services/queue.service.spec.ts @@ -32,7 +32,7 @@ class QueueConfigServiceStub { }; } -const waitForImmediate = () => new Promise(resolve => setImmediate(resolve)); +const waitForImmediate = () => new Promise((resolve) => setImmediate(resolve)); describe('QueueService', () => { let service: QueueService; @@ -43,14 +43,14 @@ describe('QueueService', () => { let events: Events; let player: DocumentType; - beforeAll(() => mongod = new MongoMemoryServer()); + beforeAll(() => (mongod = new MongoMemoryServer())); afterAll(async () => await mongod.stop()); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ typegooseTestingModule(mongod), - TypegooseModule.forFeature([ Player, Game ]), + TypegooseModule.forFeature([Player, Game]), ], providers: [ QueueService, @@ -91,29 +91,32 @@ describe('QueueService', () => { it('should be empty initially', () => { expect(service.state).toEqual('waiting'); expect(service.slots.length).toBe(12); - expect(service.slots.every(s => s.playerId === null)).toBe(true); - expect(service.slots.every(s => s.ready === false)).toBe(true); + expect(service.slots.every((s) => s.playerId === null)).toBe(true); + expect(service.slots.every((s) => s.ready === false)).toBe(true); expect(service.playerCount).toEqual(0); expect(service.readyPlayerCount).toEqual(0); expect(service.requiredPlayerCount).toEqual(12); }); describe('#reset()', () => { - it('should emit the queueSlotsChange event', async () => new Promise(resolve => { - events.queueSlotsChange.subscribe(({ slots }) => { - expect(slots.length).toEqual(12); - expect(slots.every(s => s.playerId === null)).toBe(true); - resolve(); - }); + it('should emit the queueSlotsChange event', async () => + new Promise((resolve) => { + events.queueSlotsChange.subscribe(({ slots }) => { + expect(slots.length).toEqual(12); + expect(slots.every((s) => s.playerId === null)).toBe(true); + resolve(); + }); - service.reset(); - })); + service.reset(); + })); }); describe('#join()', () => { - it('should fail if the given player doesn\'t exist', async () => { + it("should fail if the given player doesn't exist", async () => { jest.spyOn(playersService, 'getById').mockResolvedValue(null); - await expect(service.join(0, new ObjectId().toString())).rejects.toThrowError('no such player'); + await expect( + service.join(0, new ObjectId().toString()), + ).rejects.toThrowError('no such player'); }); describe('when the player has not accepted rules', () => { @@ -123,33 +126,45 @@ describe('QueueService', () => { }); it('should fail', async () => { - await expect(service.join(0, player.id)).rejects.toThrowError('player has not accepted rules'); + await expect(service.join(0, player.id)).rejects.toThrowError( + 'player has not accepted rules', + ); }); }); describe('when the player is banned', () => { beforeEach(() => { - jest.spyOn(playerBansService, 'getPlayerActiveBans').mockResolvedValue([{ } as any]); + jest + .spyOn(playerBansService, 'getPlayerActiveBans') + .mockResolvedValue([{} as any]); }); it('should fail', async () => { - await expect(service.join(0, player.id)).rejects.toThrowError('player is banned'); + await expect(service.join(0, player.id)).rejects.toThrowError( + 'player is banned', + ); }); }); describe('when the player is playing a game', () => { beforeEach(() => { - jest.spyOn(gamesService, 'getPlayerActiveGame').mockResolvedValue({ number: 1, state: 'started' } as any); + jest + .spyOn(gamesService, 'getPlayerActiveGame') + .mockResolvedValue({ number: 1, state: 'started' } as any); }); it('should fail', async () => { - await expect(service.join(0, player.id)).rejects.toThrowError('player involved in a currently active game'); + await expect(service.join(0, player.id)).rejects.toThrowError( + 'player involved in a currently active game', + ); }); }); describe('when the player tries to join an invalid slot', () => { it('should fail', async () => { - await expect(service.join(1234567, player.id)).rejects.toThrowError('no such slot'); + await expect(service.join(1234567, player.id)).rejects.toThrowError( + 'no such slot', + ); }); }); @@ -166,13 +181,15 @@ describe('QueueService', () => { }); it('should fail when trying to take a slot that was already occupied', async () => { - await expect(service.join(0, player.id)).rejects.toThrowError('slot occupied'); + await expect(service.join(0, player.id)).rejects.toThrowError( + 'slot occupied', + ); }); }); it('should store add the player to the given slot', async () => { const slots = await service.join(0, player.id); - const slot = slots.find(s => s.playerId === player.id); + const slot = slots.find((s) => s.playerId === player.id); expect(slot).toBeDefined(); expect(slot.id).toBe(0); expect(service.playerCount).toBe(1); @@ -187,33 +204,39 @@ describe('QueueService', () => { const oldSlots = await service.join(0, player.id); const newSlots = await service.join(1, player.id); expect(newSlots.length).toEqual(2); - expect(newSlots.find(s => s.playerId === player.id)).toBeDefined(); + expect(newSlots.find((s) => s.playerId === player.id)).toBeDefined(); expect(oldSlots[0].playerId).toBeNull(); }); - it('should emit the playerJoinsQueue event', async () => new Promise(resolve => { - events.playerJoinsQueue.subscribe(({ playerId }) => { - expect(playerId).toEqual(player.id); - resolve(); - }); + it('should emit the playerJoinsQueue event', async () => + new Promise((resolve) => { + events.playerJoinsQueue.subscribe(({ playerId }) => { + expect(playerId).toEqual(player.id); + resolve(); + }); - service.join(0, player.id); - })); + service.join(0, player.id); + })); - it('should emit the queueSlotsChange event', async () => new Promise(resolve => { - events.queueSlotsChange.subscribe(({ slots }) => { - expect(slots).toEqual([ { gameClass: 'scout', id: 0, playerId: player.id, ready: false } ]); - resolve(); - }); + it('should emit the queueSlotsChange event', async () => + new Promise((resolve) => { + events.queueSlotsChange.subscribe(({ slots }) => { + expect(slots).toEqual([ + { gameClass: 'scout', id: 0, playerId: player.id, ready: false }, + ]); + resolve(); + }); - service.join(0, player.id); - })); + service.join(0, player.id); + })); describe('when the player joins as the last one', () => { beforeEach(async () => { for (let i = 0; i < 11; ++i) { // @ts-expect-error - const player = await playersService._createOne({ hasAcceptedRules: true }); + const player = await playersService._createOne({ + hasAcceptedRules: true, + }); await service.join(i, player.id); } }); @@ -237,15 +260,16 @@ describe('QueueService', () => { expect(slot.ready).toBe(false); }); - it('should emit the playerLeavesQueue event', async () => new Promise(resolve => { - events.playerLeavesQueue.subscribe(({ playerId, reason }) => { - expect(playerId).toEqual(player.id); - expect(reason).toEqual('manual'); - resolve(); - }); + it('should emit the playerLeavesQueue event', async () => + new Promise((resolve) => { + events.playerLeavesQueue.subscribe(({ playerId, reason }) => { + expect(playerId).toEqual(player.id); + expect(reason).toEqual('manual'); + resolve(); + }); - service.leave(player.id); - })); + service.leave(player.id); + })); describe('when the slots is already free', () => { beforeEach(() => { @@ -253,7 +277,9 @@ describe('QueueService', () => { }); it('should throw an error', () => { - expect(() => service.leave(player.id)).toThrowError('slot already free'); + expect(() => service.leave(player.id)).toThrowError( + 'slot already free', + ); }); }); }); @@ -271,15 +297,16 @@ describe('QueueService', () => { expect(service.playerCount).toBe(0); }); - it('should emit the playerLeavesQueue event', async () => new Promise(resolve => { - events.playerLeavesQueue.subscribe(({ playerId, reason }) => { - expect(playerId).toEqual(player.id); - expect(reason).toEqual('kicked'); - resolve(); - }); + it('should emit the playerLeavesQueue event', async () => + new Promise((resolve) => { + events.playerLeavesQueue.subscribe(({ playerId, reason }) => { + expect(playerId).toEqual(player.id); + expect(reason).toEqual('kicked'); + resolve(); + }); - service.kick(player.id); - })); + service.kick(player.id); + })); }); describe('#readyUp()', () => { @@ -289,7 +316,9 @@ describe('QueueService', () => { }); it('should fail', async () => { - expect(() => service.readyUp(player.id)).toThrowError('queue not ready'); + expect(() => service.readyUp(player.id)).toThrowError( + 'queue not ready', + ); }); }); }); @@ -302,7 +331,9 @@ describe('QueueService', () => { for (let i = 0; i < 12; ++i) { // @ts-expect-error - const player = await playersService._createOne({ hasAcceptedRules: true }); + const player = await playersService._createOne({ + hasAcceptedRules: true, + }); await service.join(i, player.id); players.push(player); } @@ -316,7 +347,7 @@ describe('QueueService', () => { describe('#readyUp()', () => { it('should be able to ready-up all players', () => { - players.forEach(player => { + players.forEach((player) => { const slot = service.readyUp(player.id); expect(slot.ready).toBe(true); }); @@ -326,15 +357,16 @@ describe('QueueService', () => { expect(() => service.readyUp(player.id)).toThrow(); }); - it('should emit the queueSlotsChange event', async () => new Promise(resolve => { - events.queueSlotsChange.subscribe(({ slots }) => { - expect(slots.length).toEqual(1); - expect(slots[0].ready).toBe(true); - resolve(); - }); + it('should emit the queueSlotsChange event', async () => + new Promise((resolve) => { + events.queueSlotsChange.subscribe(({ slots }) => { + expect(slots.length).toEqual(1); + expect(slots[0].ready).toBe(true); + resolve(); + }); - service.readyUp(players[0].id); - })); + service.readyUp(players[0].id); + })); }); it('should ready up joining players immediately', async () => { @@ -355,7 +387,7 @@ describe('QueueService', () => { describe('when all players leave', () => { beforeEach(async () => { - service.kick(...players.map(p => p.id)); + service.kick(...players.map((p) => p.id)); await waitForImmediate(); }); @@ -372,7 +404,7 @@ describe('QueueService', () => { describe('and after he disconnects', () => { beforeEach(() => { - events.playerDisconnects.next(({ playerId: player.id })); + events.playerDisconnects.next({ playerId: player.id }); }); it('should kick him from the queue', () => { diff --git a/src/queue/services/queue.service.ts b/src/queue/services/queue.service.ts index 1e16871d2..4d87dca67 100644 --- a/src/queue/services/queue.service.ts +++ b/src/queue/services/queue.service.ts @@ -1,4 +1,11 @@ -import { Injectable, Logger, Inject, forwardRef, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; +import { + Injectable, + Logger, + Inject, + forwardRef, + OnModuleInit, + OnModuleDestroy, +} from '@nestjs/common'; import { QueueSlot } from '@/queue/queue-slot'; import { PlayersService } from '@/players/services/players.service'; import { QueueConfigService } from './queue-config.service'; @@ -10,7 +17,6 @@ import { Events } from '@/events/events'; @Injectable() export class QueueService implements OnModuleInit, OnModuleDestroy { - slots: QueueSlot[] = []; state: QueueState = 'waiting'; @@ -22,27 +28,36 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { } get playerCount(): number { - return this.slots.filter(s => !!s.playerId).length; + return this.slots.filter((s) => !!s.playerId).length; } get readyPlayerCount() { - return this.slots.filter(s => s.ready).length; + return this.slots.filter((s) => s.ready).length; } constructor( - @Inject(forwardRef(() => PlayersService)) private playersService: PlayersService, + @Inject(forwardRef(() => PlayersService)) + private playersService: PlayersService, private queueConfigService: QueueConfigService, private playerBansService: PlayerBansService, @Inject(forwardRef(() => GamesService)) private gamesService: GamesService, private events: Events, - ) { } + ) {} onModuleInit() { this.resetSlots(); - this.events.queueSlotsChange.subscribe(() => setImmediate(() => this.maybeUpdateState())); - this.events.queueStateChange.subscribe(({ state }) => this.onStateChange(state)); - this.events.playerDisconnects.subscribe(({ playerId }) => this.kick(playerId)); - this.events.playerBanAdded.subscribe(({ ban }) => this.kick(ban.player.toString())); + this.events.queueSlotsChange.subscribe(() => + setImmediate(() => this.maybeUpdateState()), + ); + this.events.queueStateChange.subscribe(({ state }) => + this.onStateChange(state), + ); + this.events.playerDisconnects.subscribe(({ playerId }) => + this.kick(playerId), + ); + this.events.playerBanAdded.subscribe(({ ban }) => + this.kick(ban.player.toString()), + ); } onModuleDestroy() { @@ -50,15 +65,15 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { } getSlotById(id: number): QueueSlot { - return this.slots.find(s => s.id === id); + return this.slots.find((s) => s.id === id); } findSlotByPlayerId(playerId: string): QueueSlot { - return this.slots.find(s => s.playerId === playerId); + return this.slots.find((s) => s.playerId === playerId); } isInQueue(playerId: string): boolean { - return !!this.slots.find(s => s.playerId === playerId); + return !!this.slots.find((s) => s.playerId === playerId); } reset() { @@ -107,24 +122,29 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { } // remove player from any slot(s) he could be occupying - const oldSlots = this.slots.filter(s => s.playerId === playerId); - oldSlots.forEach(s => this.clearSlot(s)); + const oldSlots = this.slots.filter((s) => s.playerId === playerId); + oldSlots.forEach((s) => this.clearSlot(s)); targetSlot.playerId = playerId; - if (this.state === 'ready' || this.playerCount === this.requiredPlayerCount) { + if ( + this.state === 'ready' || + this.playerCount === this.requiredPlayerCount + ) { targetSlot.ready = true; } - this.logger.debug(`player ${player.name} joined the queue (slotId=${targetSlot.id}, gameClass=${targetSlot.gameClass})`); + this.logger.debug( + `player ${player.name} joined the queue (slotId=${targetSlot.id}, gameClass=${targetSlot.gameClass})`, + ); // is player joining instead of only changing slots? if (oldSlots.length === 0) { this.events.playerJoinsQueue.next({ playerId }); } - const slots = [ targetSlot, ...oldSlots ]; - this.events.queueSlotsChange.next({ slots }) + const slots = [targetSlot, ...oldSlots]; + this.events.queueSlotsChange.next({ slots }); return slots; } @@ -138,7 +158,7 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { this.clearSlot(slot); this.logger.debug(`slot ${slot.id} (gameClass=${slot.gameClass}) free`); this.events.playerLeavesQueue.next({ playerId, reason: 'manual' }); - this.events.queueSlotsChange.next({ slots: [ slot ] }); + this.events.queueSlotsChange.next({ slots: [slot] }); return slot; } else { throw new Error('slot already free'); @@ -157,7 +177,9 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { if (slot) { this.clearSlot(slot); this.events.playerLeavesQueue.next({ playerId, reason: 'kicked' }); - this.logger.debug(`slot ${slot.id} (gameClass=${slot.gameClass}) free (player was kicked)`); + this.logger.debug( + `slot ${slot.id} (gameClass=${slot.gameClass}) free (player was kicked)`, + ); updatedSlots.push(slot); } } @@ -173,8 +195,10 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { const slot = this.findSlotByPlayerId(playerId); if (slot) { slot.ready = true; - this.logger.debug(`slot ${slot.id} ready (${this.readyPlayerCount}/${this.requiredPlayerCount})`); - this.events.queueSlotsChange.next({ slots: [ slot ] }); + this.logger.debug( + `slot ${slot.id} ready (${this.readyPlayerCount}/${this.requiredPlayerCount})`, + ); + this.events.queueSlotsChange.next({ slots: [slot] }); return slot; } else { throw new Error('player is not in the queue'); @@ -224,18 +248,25 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { }; let lastId = 0; - this.slots = this.queueConfigService.queueConfig.classes.reduce((prev, curr) => { - const tmpSlots = []; - for (let i = 0; i < curr.count * this.queueConfigService.queueConfig.teamCount; ++i) { - tmpSlots.push({ - id: lastId++, - gameClass: curr.name, - ...defaultSlot, - }); - } + this.slots = this.queueConfigService.queueConfig.classes.reduce( + (prev, curr) => { + const tmpSlots = []; + for ( + let i = 0; + i < curr.count * this.queueConfigService.queueConfig.teamCount; + ++i + ) { + tmpSlots.push({ + id: lastId++, + gameClass: curr.name, + ...defaultSlot, + }); + } - return prev.concat(tmpSlots); - }, []); + return prev.concat(tmpSlots); + }, + [], + ); } private clearSlot(slot: QueueSlot) { @@ -259,13 +290,13 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { private kickUnreadyPlayers() { this.logger.debug('kicking players that are not ready'); - const slots = this.slots.filter(s => !s.ready); - this.kick(...slots.map(s => s.playerId)); + const slots = this.slots.filter((s) => !s.ready); + this.kick(...slots.map((s) => s.playerId)); } private unreadyQueue() { - const slots = this.slots.filter(s => !!s.playerId); - slots.forEach(s => s.ready = false); + const slots = this.slots.filter((s) => !!s.playerId); + slots.forEach((s) => (s.ready = false)); this.events.queueSlotsChange.next({ slots }); this.setState('waiting'); } @@ -274,5 +305,4 @@ export class QueueService implements OnModuleInit, OnModuleDestroy { this.state = state; this.events.queueStateChange.next({ state }); } - } diff --git a/src/shared/decorators/omit-props.decorator.ts b/src/shared/decorators/omit-props.decorator.ts index 1ab3d3c64..f9530b213 100644 --- a/src/shared/decorators/omit-props.decorator.ts +++ b/src/shared/decorators/omit-props.decorator.ts @@ -1,7 +1,8 @@ import { applyDecorators, SetMetadata, UseInterceptors } from '@nestjs/common'; import { OmitPropsInterceptor } from '../interceptors/omit-props.interceptor'; -export const OmitProps = (...props: string[]) => applyDecorators( - SetMetadata('omit-props', props), - UseInterceptors(OmitPropsInterceptor), -); +export const OmitProps = (...props: string[]) => + applyDecorators( + SetMetadata('omit-props', props), + UseInterceptors(OmitPropsInterceptor), + ); diff --git a/src/shared/filters/document-not-found.filter.spec.ts b/src/shared/filters/document-not-found.filter.spec.ts index c69ca652a..54d576073 100644 --- a/src/shared/filters/document-not-found.filter.spec.ts +++ b/src/shared/filters/document-not-found.filter.spec.ts @@ -26,8 +26,11 @@ describe('DocumentNotFoundFilter', () => { }; const filter = new DocumentNotFoundFilter(); - filter.catch(new mongoose.Error.DocumentNotFoundError({ }), host as any); + filter.catch(new mongoose.Error.DocumentNotFoundError({}), host as any); expect(response.status).toHaveBeenCalledWith(404); - expect(response.json).toHaveBeenCalledWith({ statusCode: 404, path: '/some/invalid/path' }); + expect(response.json).toHaveBeenCalledWith({ + statusCode: 404, + path: '/some/invalid/path', + }); }); }); diff --git a/src/shared/filters/document-not-found.filter.ts b/src/shared/filters/document-not-found.filter.ts index 84dea6cef..3e6cd9160 100644 --- a/src/shared/filters/document-not-found.filter.ts +++ b/src/shared/filters/document-not-found.filter.ts @@ -4,18 +4,14 @@ import { Request, Response } from 'express'; @Catch(mongoose.Error.DocumentNotFoundError) export class DocumentNotFoundFilter implements ExceptionFilter { - catch(exception: mongoose.Error.DocumentNotFoundError, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); - response - .status(404) - .json({ - statusCode: 404, - path: request.url, - }); + response.status(404).json({ + statusCode: 404, + path: request.url, + }); } - } diff --git a/src/shared/interceptors/omit-props.interceptor.ts b/src/shared/interceptors/omit-props.interceptor.ts index 9757d0959..4e7e3e50f 100644 --- a/src/shared/interceptors/omit-props.interceptor.ts +++ b/src/shared/interceptors/omit-props.interceptor.ts @@ -1,4 +1,9 @@ -import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { omit } from 'lodash'; import { Observable } from 'rxjs'; @@ -6,16 +11,13 @@ import { map } from 'rxjs/operators'; @Injectable() export class OmitPropsInterceptor implements NestInterceptor { - - constructor( - private reflector: Reflector, - ) { } + constructor(private reflector: Reflector) {} intercept(context: ExecutionContext, next: CallHandler): Observable { - const paths = this.reflector.get('omit-props', context.getHandler()); - return next.handle().pipe( - map(obj => omit(obj, paths)), + const paths = this.reflector.get( + 'omit-props', + context.getHandler(), ); + return next.handle().pipe(map((obj) => omit(obj, paths))); } - } diff --git a/src/shared/pipes/is-one-of.pipe.spec.ts b/src/shared/pipes/is-one-of.pipe.spec.ts index 84cae3a92..14ed56b17 100644 --- a/src/shared/pipes/is-one-of.pipe.spec.ts +++ b/src/shared/pipes/is-one-of.pipe.spec.ts @@ -6,12 +6,18 @@ describe('IsOneOfPipe', () => { expect(new IsOneOfPipe([])).toBeDefined(); }); - it('should return the value if it\'s allowed', () => { - expect(new IsOneOfPipe(['one', 'two']).transform('one', null)).toEqual('one'); + it("should return the value if it's allowed", () => { + expect(new IsOneOfPipe(['one', 'two']).transform('one', null)).toEqual( + 'one', + ); }); it('should throw an error if the value is not allowed', () => { - expect(() => new IsOneOfPipe(['one', 'two']).transform('three', { type: 'query', data: 'field' })) - .toThrow(BadRequestException); + expect(() => + new IsOneOfPipe(['one', 'two']).transform('three', { + type: 'query', + data: 'field', + }), + ).toThrow(BadRequestException); }); }); diff --git a/src/shared/pipes/is-one-of.pipe.ts b/src/shared/pipes/is-one-of.pipe.ts index b87272818..96aa13970 100644 --- a/src/shared/pipes/is-one-of.pipe.ts +++ b/src/shared/pipes/is-one-of.pipe.ts @@ -1,17 +1,21 @@ -import { ArgumentMetadata, Injectable, PipeTransform, BadRequestException } from '@nestjs/common'; +import { + ArgumentMetadata, + Injectable, + PipeTransform, + BadRequestException, +} from '@nestjs/common'; @Injectable() export class IsOneOfPipe implements PipeTransform { - - constructor( - private choices: string[], - ) { } + constructor(private choices: string[]) {} transform(value: string, metadata: ArgumentMetadata) { if (!this.choices.includes(value)) { - throw new BadRequestException(`${metadata.data} must be on of ${this.choices.join(', ')}`); + throw new BadRequestException( + `${metadata.data} must be on of ${this.choices.join(', ')}`, + ); } - + return value; } } diff --git a/src/shared/pipes/object-id-validation.pipe.spec.ts b/src/shared/pipes/object-id-validation.pipe.spec.ts index 53d281478..787d511a8 100644 --- a/src/shared/pipes/object-id-validation.pipe.spec.ts +++ b/src/shared/pipes/object-id-validation.pipe.spec.ts @@ -11,7 +11,9 @@ describe('ObjectIdValidationPipe', () => { expect(new ObjectIdValidationPipe().transform(id)).toEqual(id); }); - it('should deny invalid object id',() => { - expect(() => new ObjectIdValidationPipe().transform('some invalid id')).toThrow(); + it('should deny invalid object id', () => { + expect(() => + new ObjectIdValidationPipe().transform('some invalid id'), + ).toThrow(); }); }); diff --git a/src/shared/shared.module.ts b/src/shared/shared.module.ts index 60f29f572..0780eee10 100644 --- a/src/shared/shared.module.ts +++ b/src/shared/shared.module.ts @@ -1,4 +1,4 @@ import { Module } from '@nestjs/common'; -@Module({ }) -export class SharedModule { } +@Module({}) +export class SharedModule {} diff --git a/src/streams/streams.module.ts b/src/streams/streams.module.ts index 551823deb..c481fd210 100644 --- a/src/streams/streams.module.ts +++ b/src/streams/streams.module.ts @@ -1,18 +1,17 @@ import { DynamicModule, Module } from '@nestjs/common'; import { TwitchModule } from './twitch/twitch.module'; -const twitchModule = () => (process.env.TWITCH_CLIENT_ID && process.env.TWITCH_CLIENT_SECRET) ? [ TwitchModule ] : []; +const twitchModule = () => + process.env.TWITCH_CLIENT_ID && process.env.TWITCH_CLIENT_SECRET + ? [TwitchModule] + : []; -@Module({ }) +@Module({}) export class StreamsModule { - static configure(): DynamicModule { return { module: StreamsModule, - imports: [ - ...twitchModule(), - ], + imports: [...twitchModule()], }; } - } diff --git a/src/streams/twitch/controllers/twitch.controller.spec.ts b/src/streams/twitch/controllers/twitch.controller.spec.ts index 44963e7ce..32be7152a 100644 --- a/src/streams/twitch/controllers/twitch.controller.spec.ts +++ b/src/streams/twitch/controllers/twitch.controller.spec.ts @@ -15,16 +15,18 @@ class TwitchServiceStub { id: '1495594625', userName: 'H2P_Gucio', title: 'Bliżej niż dalej :) / 10 zgonów = gift sub', - thumbnailUrl: 'https://static-cdn.jtvnw.net/previews-ttv/live_user_h2p_gucio-{width}x{height}.jpg', - viewerCount: 5018 + thumbnailUrl: + 'https://static-cdn.jtvnw.net/previews-ttv/live_user_h2p_gucio-{width}x{height}.jpg', + viewerCount: 5018, }, { playerId: '5d44887bb963ff7e00c6b6bb', id: '1494755665', userName: 'xEmtek', title: 'SPEEDRUN do 1 rangi - ciąg dalszy ', - thumbnailUrl: 'https://static-cdn.jtvnw.net/previews-ttv/live_user_xemtek-{width}x{height}.jpg', - viewerCount: 703 + thumbnailUrl: + 'https://static-cdn.jtvnw.net/previews-ttv/live_user_xemtek-{width}x{height}.jpg', + viewerCount: 703, }, ]; fetchUserProfile(token: string) { @@ -38,18 +40,28 @@ class TwitchServiceStub { } class TwitchAuthServiceStub { - getOauthRedirectUrl(state: string) { return `FAKE_REDIRECT_URL?state=${state}`; } - fetchUserAccessToken(code: string) { return Promise.resolve('FAKE_TOKEN'); } + getOauthRedirectUrl(state: string) { + return `FAKE_REDIRECT_URL?state=${state}`; + } + fetchUserAccessToken(code: string) { + return Promise.resolve('FAKE_TOKEN'); + } } class PlayersServiceStub { - registerTwitchAccount(playerId: string, twitchUserId: string) { return Promise.resolve(); } + registerTwitchAccount(playerId: string, twitchUserId: string) { + return Promise.resolve(); + } removeTwitchTvProfile = jest.fn().mockResolvedValue({ id: 'FAKE_USER_ID' }); } class AuthServiceStub { - verifyToken(purpose, token) { return { id: 'FAKE_USER_ID' }; } - generateJwtToken(purpose, userId) { return Promise.resolve('FAKE_JWT'); } + verifyToken(purpose, token) { + return { id: 'FAKE_USER_ID' }; + } + generateJwtToken(purpose, userId) { + return Promise.resolve('FAKE_JWT'); + } } describe('Twitch Controller', () => { @@ -89,11 +101,15 @@ describe('Twitch Controller', () => { describe('if the jwt is incorrect', () => { beforeEach(() => { - jest.spyOn(authService, 'verifyToken').mockImplementation(() => { throw new JsonWebTokenError('FAKE_ERROR'); }); + jest.spyOn(authService, 'verifyToken').mockImplementation(() => { + throw new JsonWebTokenError('FAKE_ERROR'); + }); }); it('should return 400', async () => { - await expect(controller.authenticate('FAKE_TOKEN')).rejects.toThrow(BadRequestException); + await expect(controller.authenticate('FAKE_TOKEN')).rejects.toThrow( + BadRequestException, + ); }); }); }); @@ -112,9 +128,11 @@ describe('Twitch Controller', () => { }); describe('#disconnect()', () => { - it('should remove twitch.tv profile from the user\'s account', async () => { + it("should remove twitch.tv profile from the user's account", async () => { const ret = await controller.disconnect({ id: 'FAKE_USER_ID' } as Player); - expect(playersService.removeTwitchTvProfile).toHaveBeenCalledWith('FAKE_USER_ID'); + expect(playersService.removeTwitchTvProfile).toHaveBeenCalledWith( + 'FAKE_USER_ID', + ); expect(ret).toEqual({ id: 'FAKE_USER_ID' }); }); }); diff --git a/src/streams/twitch/controllers/twitch.controller.ts b/src/streams/twitch/controllers/twitch.controller.ts index 894625f79..22dce1411 100644 --- a/src/streams/twitch/controllers/twitch.controller.ts +++ b/src/streams/twitch/controllers/twitch.controller.ts @@ -1,4 +1,12 @@ -import { Controller, Get, Redirect, Query, BadRequestException, Logger, Put } from '@nestjs/common'; +import { + Controller, + Get, + Redirect, + Query, + BadRequestException, + Logger, + Put, +} from '@nestjs/common'; import { TwitchService } from '../services/twitch.service'; import { TwitchAuthService } from '../services/twitch-auth.service'; import { PlayersService } from '@/players/services/players.service'; @@ -12,7 +20,6 @@ import { Player } from '@/players/models/player'; @Controller('twitch') export class TwitchController { - private logger = new Logger(TwitchController.name); constructor( @@ -20,14 +27,17 @@ export class TwitchController { private twitchAuthService: TwitchAuthService, private playersService: PlayersService, private authService: AuthService, - ) { } + ) {} @Get('auth') @Redirect('https://id.twitch.tv/oauth2/authorize') async authenticate(@Query('token') token: string) { try { const { id } = this.authService.verifyToken(JwtTokenPurpose.auth, token); - const contextToken = await this.authService.generateJwtToken(JwtTokenPurpose.context, id); + const contextToken = await this.authService.generateJwtToken( + JwtTokenPurpose.context, + id, + ); return { url: this.twitchAuthService.getOauthRedirectUrl(contextToken) }; } catch (e) { this.logger.error(e); @@ -41,7 +51,10 @@ export class TwitchController { @Get('auth/return') @Redirect('/logged-in-with-twitch-tv.html') - async authenticationCallback(@Query('code') code: string, @Query('state') state: string) { + async authenticationCallback( + @Query('code') code: string, + @Query('state') state: string, + ) { const { id } = this.authService.verifyToken(JwtTokenPurpose.context, state); const token = await this.twitchAuthService.fetchUserAccessToken(code); const userProfile = await this.twitchService.fetchUserProfile(token); @@ -64,5 +77,4 @@ export class TwitchController { getStreams() { return this.twitchService.streams; } - } diff --git a/src/streams/twitch/gateways/twitch.gateway.spec.ts b/src/streams/twitch/gateways/twitch.gateway.spec.ts index dd15a8c2a..7811e9769 100644 --- a/src/streams/twitch/gateways/twitch.gateway.spec.ts +++ b/src/streams/twitch/gateways/twitch.gateway.spec.ts @@ -2,7 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TwitchGateway } from './twitch.gateway'; class SocketStub { - emit(ev: string, ...args: any[]) { return null; } + emit(ev: string, ...args: any[]) { + return null; + } } describe('TwitchGateway', () => { diff --git a/src/streams/twitch/gateways/twitch.gateway.ts b/src/streams/twitch/gateways/twitch.gateway.ts index 0bfd3da99..d14c2f05f 100644 --- a/src/streams/twitch/gateways/twitch.gateway.ts +++ b/src/streams/twitch/gateways/twitch.gateway.ts @@ -4,15 +4,13 @@ import { TwitchStream } from '../models/twitch-stream'; @WebSocketGateway() export class TwitchGateway implements OnGatewayInit { - private socket: Socket; emitStreamsUpdate(streams: TwitchStream[]) { - this.socket?.emit('twitch streams update', streams) + this.socket?.emit('twitch streams update', streams); } afterInit(socket: Socket) { this.socket = socket; } - } diff --git a/src/streams/twitch/services/twitch-auth.service.spec.ts b/src/streams/twitch/services/twitch-auth.service.spec.ts index 03f64c331..37ddbb04e 100644 --- a/src/streams/twitch/services/twitch-auth.service.spec.ts +++ b/src/streams/twitch/services/twitch-auth.service.spec.ts @@ -17,7 +17,9 @@ class HttpServiceStub { expires_in: 3600, }, }; - post(url: string) { return of(this.result); } + post(url: string) { + return of(this.result); + } } describe('TwitchAuthService', () => { @@ -43,7 +45,9 @@ describe('TwitchAuthService', () => { describe('#getOauthRedirectUrl()', () => { it('should provide the oauthRedirectUrl', () => { - expect(service.getOauthRedirectUrl('FAKE_STATE')).toEqual('https://id.twitch.tv/oauth2/authorize?client_id=FAKE_TWITCH_CLIENT_ID&redirect_uri=FAKE_API_URL/twitch/auth/return&response_type=code&scope=user_read&state=FAKE_STATE'); + expect(service.getOauthRedirectUrl('FAKE_STATE')).toEqual( + 'https://id.twitch.tv/oauth2/authorize?client_id=FAKE_TWITCH_CLIENT_ID&redirect_uri=FAKE_API_URL/twitch/auth/return&response_type=code&scope=user_read&state=FAKE_STATE', + ); }); }); diff --git a/src/streams/twitch/services/twitch-auth.service.ts b/src/streams/twitch/services/twitch-auth.service.ts index 8ee18c995..414865a01 100644 --- a/src/streams/twitch/services/twitch-auth.service.ts +++ b/src/streams/twitch/services/twitch-auth.service.ts @@ -23,7 +23,6 @@ const twitchOauth2TokenUrl = 'https://id.twitch.tv/oauth2/token'; @Injectable() export class TwitchAuthService { - private readonly redirectUri = `${this.environment.apiUrl}/twitch/auth/return`; private logger = new Logger(TwitchAuthService.name); private appAccessToken: string; @@ -32,58 +31,73 @@ export class TwitchAuthService { constructor( private environment: Environment, private httpService: HttpService, - ) { } + ) {} getOauthRedirectUrl(state: string) { // https://dev.twitch.tv/docs/authentication/getting-tokens-oauth - return `${twitchOauth2AuthorizeUrl}` + + return ( + `${twitchOauth2AuthorizeUrl}` + `?client_id=${this.environment.twitchClientId}` + `&redirect_uri=${this.redirectUri}` + `&response_type=code` + `&scope=user_read` + - `&state=${state}`; + `&state=${state}` + ); } async fetchUserAccessToken(code: string) { - return this.httpService.post( - `${twitchOauth2TokenUrl}` + - `?client_id=${this.environment.twitchClientId}` + - `&client_secret=${this.environment.twitchClientSecret}` + - `&code=${code}` + - `&grant_type=authorization_code` + - `&redirect_uri=${this.redirectUri}` - ).pipe( - map(response => response.data.access_token), - ).toPromise(); + return this.httpService + .post( + `${twitchOauth2TokenUrl}` + + `?client_id=${this.environment.twitchClientId}` + + `&client_secret=${this.environment.twitchClientSecret}` + + `&code=${code}` + + `&grant_type=authorization_code` + + `&redirect_uri=${this.redirectUri}`, + ) + .pipe(map((response) => response.data.access_token)) + .toPromise(); } async getAppAccessToken() { - if (this.appAccessToken && this.tokenExpirationDate && this.tokenExpirationDate > new Date()) { + if ( + this.appAccessToken && + this.tokenExpirationDate && + this.tokenExpirationDate > new Date() + ) { return this.appAccessToken; } const { accessToken, expiresIn } = await this.fetchAppAccessToken(); this.appAccessToken = accessToken; this.tokenExpirationDate = new Date(); - this.tokenExpirationDate.setSeconds(this.tokenExpirationDate.getSeconds() + expiresIn); + this.tokenExpirationDate.setSeconds( + this.tokenExpirationDate.getSeconds() + expiresIn, + ); this.logger.debug('app access token refreshed'); this.logger.debug(`the new token expires at ${this.tokenExpirationDate}`); return this.appAccessToken; } private fetchAppAccessToken() { - return this.httpService.post(twitchOauth2TokenUrl, { }, { - params: { - client_id: this.environment.twitchClientId, - client_secret: this.environment.twitchClientSecret, - grant_type: 'client_credentials', - }, - }).pipe( - map(response => ({ - accessToken: response.data.access_token, - expiresIn: response.data.expires_in, - })), - ).toPromise(); + return this.httpService + .post( + twitchOauth2TokenUrl, + {}, + { + params: { + client_id: this.environment.twitchClientId, + client_secret: this.environment.twitchClientSecret, + grant_type: 'client_credentials', + }, + }, + ) + .pipe( + map((response) => ({ + accessToken: response.data.access_token, + expiresIn: response.data.expires_in, + })), + ) + .toPromise(); } - } diff --git a/src/streams/twitch/services/twitch.service.spec.ts b/src/streams/twitch/services/twitch.service.spec.ts index dcc3bb212..da603ea70 100644 --- a/src/streams/twitch/services/twitch.service.spec.ts +++ b/src/streams/twitch/services/twitch.service.spec.ts @@ -17,12 +17,18 @@ class PlayersServiceStub { }, }; - getUsersWithTwitchTvAccount() { return Promise.resolve([ this.twitchUser ]); } - findByTwitchUserId(twitchUserId: string) { return Promise.resolve(this.twitchUser); } + getUsersWithTwitchTvAccount() { + return Promise.resolve([this.twitchUser]); + } + findByTwitchUserId(twitchUserId: string) { + return Promise.resolve(this.twitchUser); + } } class HttpServiceStub { - get(url: string, options: any) { return of(); } + get(url: string, options: any) { + return of(); + } } const environment = { @@ -30,16 +36,18 @@ const environment = { twitchClientId: 'FAKE_TWITCH_CLIENT_ID', }; -class TwitchGatewayStub { - -} +class TwitchGatewayStub {} class TwitchAuthServiceStub { - getAppAccessToken() { return Promise.resolve('FAKE_APP_ACCESS_TOKEN'); } + getAppAccessToken() { + return Promise.resolve('FAKE_APP_ACCESS_TOKEN'); + } } class PlayerBansServiceStub { - getPlayerActiveBans(playerId: string) { return Promise.resolve([]); } + getPlayerActiveBans(playerId: string) { + return Promise.resolve([]); + } } describe('TwitchService', () => { @@ -74,29 +82,35 @@ describe('TwitchService', () => { let spy; beforeEach(() => { - spy = jest.spyOn(httpService, 'get').mockReturnValue(of({ - data: { - 'data': [{ - 'id': '44322889', - 'login': 'dallas', - 'display_name': 'dallas', - 'type': 'staff', - 'broadcaster_type': '', - 'description': 'Just a gamer playing games and chatting. :)', - 'profile_image_url': 'https://static-cdn.jtvnw.net/jtv_user_pictures/dallas-profile_image-1a2c906ee2c35f12-300x300.png', - 'offline_image_url': 'https://static-cdn.jtvnw.net/jtv_user_pictures/dallas-channel_offline_image-1a2c906ee2c35f12-1920x1080.png', - 'view_count': 191836881, - 'email': 'login@provider.com' - }] - } - })); + spy = jest.spyOn(httpService, 'get').mockReturnValue( + of({ + data: { + data: [ + { + id: '44322889', + login: 'dallas', + display_name: 'dallas', + type: 'staff', + broadcaster_type: '', + description: 'Just a gamer playing games and chatting. :)', + profile_image_url: + 'https://static-cdn.jtvnw.net/jtv_user_pictures/dallas-profile_image-1a2c906ee2c35f12-300x300.png', + offline_image_url: + 'https://static-cdn.jtvnw.net/jtv_user_pictures/dallas-channel_offline_image-1a2c906ee2c35f12-1920x1080.png', + view_count: 191836881, + email: 'login@provider.com', + }, + ], + }, + }), + ); }); it('should query the correct endpoint', async () => { await service.fetchUserProfile('FAKE_ACCESS_TOKEN'); expect(spy).toHaveBeenCalledWith(expect.stringMatching(/\/users$/), { headers: { - 'Authorization': 'Bearer FAKE_ACCESS_TOKEN', + Authorization: 'Bearer FAKE_ACCESS_TOKEN', 'Client-ID': 'FAKE_TWITCH_CLIENT_ID', }, }); @@ -105,30 +119,31 @@ describe('TwitchService', () => { describe('#pollUsersStreams()', () => { beforeEach(() => { - jest.spyOn(httpService, 'get').mockReturnValue(of({ - data: { - 'data': [ - { - 'id': '26007494656', - 'user_id': '23161357', - 'user_name': 'LIRIK', - 'game_id': '417752', - 'type': 'live', - 'title': 'Hey Guys, It\'s Monday - Twitter: @Lirik', - 'viewer_count': 32575, - 'started_at': '2017-08-14T16:08:32Z', - 'language': 'en', - 'thumbnail_url': 'https://static-cdn.jtvnw.net/previews-ttv/live_user_lirik-{width}x{height}.jpg', - 'tag_ids': [ - '6ea6bca4-4712-4ab9-a906-e3336a9d8039' - ], + jest.spyOn(httpService, 'get').mockReturnValue( + of({ + data: { + data: [ + { + id: '26007494656', + user_id: '23161357', + user_name: 'LIRIK', + game_id: '417752', + type: 'live', + title: "Hey Guys, It's Monday - Twitter: @Lirik", + viewer_count: 32575, + started_at: '2017-08-14T16:08:32Z', + language: 'en', + thumbnail_url: + 'https://static-cdn.jtvnw.net/previews-ttv/live_user_lirik-{width}x{height}.jpg', + tag_ids: ['6ea6bca4-4712-4ab9-a906-e3336a9d8039'], + }, + ], + pagination: { + cursor: 'eyJiIjpudWxsLCJhIjp7Ik9mZnNldCI6MjB9fQ==', }, - ], - 'pagination': { - 'cursor': 'eyJiIjpudWxsLCJhIjp7Ik9mZnNldCI6MjB9fQ==' }, - }, - })); + }), + ); }); it('should refresh all streams', async () => { @@ -138,7 +153,9 @@ describe('TwitchService', () => { describe('when a user is banned', () => { beforeEach(() => { - jest.spyOn(playerBansService, 'getPlayerActiveBans').mockResolvedValue([{ } as any]); + jest + .spyOn(playerBansService, 'getPlayerActiveBans') + .mockResolvedValue([{} as any]); }); it('should not add his stream to the list of streams', async () => { diff --git a/src/streams/twitch/services/twitch.service.ts b/src/streams/twitch/services/twitch.service.ts index 9d5c92fb1..e435fb756 100644 --- a/src/streams/twitch/services/twitch.service.ts +++ b/src/streams/twitch/services/twitch.service.ts @@ -44,7 +44,6 @@ interface TwitchGetStreamsResponse { @Injectable() export class TwitchService implements OnModuleInit { - private logger = new Logger(TwitchService.name); private _streams = new BehaviorSubject([]); @@ -59,47 +58,60 @@ export class TwitchService implements OnModuleInit { private twitchGateway: TwitchGateway, private twitchAuthService: TwitchAuthService, private playerBansService: PlayerBansService, - ) { } + ) {} onModuleInit() { - this._streams.pipe( - distinctUntilChanged((x, y) => JSON.stringify(x) === JSON.stringify(y)), - ).subscribe(streams => this.twitchGateway.emitStreamsUpdate(streams)); + this._streams + .pipe( + distinctUntilChanged((x, y) => JSON.stringify(x) === JSON.stringify(y)), + ) + .subscribe((streams) => this.twitchGateway.emitStreamsUpdate(streams)); } async fetchUserProfile(accessToken: string) { // https://dev.twitch.tv/docs/api/reference#get-users - return this.httpService.get(`${twitchTvApiEndpoint}/users`, { - headers: { - 'Authorization': `Bearer ${accessToken}`, - 'Client-ID': this.environment.twitchClientId, - }, - }).pipe( - map(response => response.data.data[0]), - ).toPromise(); + return this.httpService + .get(`${twitchTvApiEndpoint}/users`, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Client-ID': this.environment.twitchClientId, + }, + }) + .pipe(map((response) => response.data.data[0])) + .toPromise(); } @Cron(CronExpression.EVERY_MINUTE) async pollUsersStreams() { const users = await this.playersService.getUsersWithTwitchTvAccount(); if (users.length > 0) { - const rawStreams = await this.fetchStreams(users.map(u => u.twitchTvUser.userId)); - const streams = (await Promise.all(rawStreams.map(async s => { - const player = await this.playersService.findByTwitchUserId(s.user_id); - const bans = await this.playerBansService.getPlayerActiveBans(player.id); - if (bans.length > 0) { - return null; - } else { - return { - playerId: player.id, - id: s.id, - userName: s.user_name, - title: s.title, - thumbnailUrl: s.thumbnail_url, - viewerCount: s.viewer_count, - }; - } - }))).filter(stream => !!stream); + const rawStreams = await this.fetchStreams( + users.map((u) => u.twitchTvUser.userId), + ); + const streams = ( + await Promise.all( + rawStreams.map(async (s) => { + const player = await this.playersService.findByTwitchUserId( + s.user_id, + ); + const bans = await this.playerBansService.getPlayerActiveBans( + player.id, + ); + if (bans.length > 0) { + return null; + } else { + return { + playerId: player.id, + id: s.id, + userName: s.user_name, + title: s.title, + thumbnailUrl: s.thumbnail_url, + viewerCount: s.viewer_count, + }; + } + }), + ) + ).filter((stream) => !!stream); this._streams.next(streams); this.logger.debug('streams refreshed'); } @@ -107,17 +119,17 @@ export class TwitchService implements OnModuleInit { private async fetchStreams(users: string[]) { // https://dev.twitch.tv/docs/api/reference#get-streams - return this.httpService.get(`${twitchTvApiEndpoint}/streams`, { - params: { - user_id: users, - }, - headers: { - 'Client-ID': this.environment.twitchClientId, - 'Authorization': `Bearer ${await this.twitchAuthService.getAppAccessToken()}`, - }, - }).pipe( - map(response => response.data.data), - ).toPromise(); + return this.httpService + .get(`${twitchTvApiEndpoint}/streams`, { + params: { + user_id: users, + }, + headers: { + 'Client-ID': this.environment.twitchClientId, + Authorization: `Bearer ${await this.twitchAuthService.getAppAccessToken()}`, + }, + }) + .pipe(map((response) => response.data.data)) + .toPromise(); } - } diff --git a/src/streams/twitch/twitch.module.ts b/src/streams/twitch/twitch.module.ts index cfaf1bd8f..3494fd9ce 100644 --- a/src/streams/twitch/twitch.module.ts +++ b/src/streams/twitch/twitch.module.ts @@ -7,22 +7,9 @@ import { AuthModule } from '@/auth/auth.module'; import { TwitchGateway } from './gateways/twitch.gateway'; @Module({ - imports: [ - HttpModule, - - PlayersModule, - AuthModule, - ], - providers: [ - TwitchService, - TwitchAuthService, - TwitchGateway, - ], - exports: [ - TwitchService, - ], - controllers: [ - TwitchController, - ], + imports: [HttpModule, PlayersModule, AuthModule], + providers: [TwitchService, TwitchAuthService, TwitchGateway], + exports: [TwitchService], + controllers: [TwitchController], }) -export class TwitchModule { } +export class TwitchModule {} diff --git a/src/utils/create-mongo-db-uri.ts b/src/utils/create-mongo-db-uri.ts index c401bbacf..84304eb27 100644 --- a/src/utils/create-mongo-db-uri.ts +++ b/src/utils/create-mongo-db-uri.ts @@ -15,5 +15,7 @@ export const createMongoDbUri = (props: CreateMongoDbUriProps) => { credentials = `${props.username}@`; } } - return `mongodb://${credentials}${props.host}:${props.port}/${props.database ?? ''}`; + return `mongodb://${credentials}${props.host}:${props.port}/${ + props.database ?? '' + }`; }; diff --git a/src/utils/mongoose-document.ts b/src/utils/mongoose-document.ts index d9d84f204..7633a8931 100644 --- a/src/utils/mongoose-document.ts +++ b/src/utils/mongoose-document.ts @@ -2,11 +2,9 @@ import { mongoose } from '@typegoose/typegoose'; import { Exclude } from 'class-transformer'; export abstract class MongooseDocument { - @Exclude({ toPlainOnly: true }) __v?: number; @Exclude({ toPlainOnly: true }) _id?: mongoose.Types.ObjectId; - } diff --git a/src/utils/remove-id.ts b/src/utils/remove-id.ts index c9337955e..1ddaa017e 100644 --- a/src/utils/remove-id.ts +++ b/src/utils/remove-id.ts @@ -4,8 +4,6 @@ import { Exclude } from 'class-transformer'; * Utility class to get rid of the _id property. */ export class RemoveId { - @Exclude() _id?: string; - } diff --git a/src/utils/remove-version-key-and-id.ts b/src/utils/remove-version-key-and-id.ts index fcf9fd5cc..5aaa7ff56 100644 --- a/src/utils/remove-version-key-and-id.ts +++ b/src/utils/remove-version-key-and-id.ts @@ -4,11 +4,9 @@ import { Exclude } from 'class-transformer'; * Utility class to get rid of the _id property and the version key. */ export class RemoveVersionKeyAndId { - @Exclude() _id?: string; @Exclude() __v?: number; - } diff --git a/src/utils/remove-version-key.ts b/src/utils/remove-version-key.ts index 94232790d..4741acccb 100644 --- a/src/utils/remove-version-key.ts +++ b/src/utils/remove-version-key.ts @@ -4,8 +4,6 @@ import { Exclude } from 'class-transformer'; * Utility class to get rid of the mongoose's version key from models. */ export class RemoveVersionKey { - @Exclude() __v?: number; - } diff --git a/src/utils/standard-schema-options.ts b/src/utils/standard-schema-options.ts index db4fc84e9..1efdf2cdc 100644 --- a/src/utils/standard-schema-options.ts +++ b/src/utils/standard-schema-options.ts @@ -4,7 +4,8 @@ import { Document } from 'mongoose'; type TransformFn = (doc: Document, ret: any) => any; function chain(...transformFunctions: TransformFn[]): TransformFn { - return (doc: Document, ret: any) => transformFunctions.forEach(fn => fn(doc, ret)); + return (doc: Document, ret: any) => + transformFunctions.forEach((fn) => fn(doc, ret)); } export function standardSchemaOptions(cls: any, ...transform: TransformFn[]) { diff --git a/src/utils/testing-typegoose-module.ts b/src/utils/testing-typegoose-module.ts index 6f3f1c53c..2f299c8e1 100644 --- a/src/utils/testing-typegoose-module.ts +++ b/src/utils/testing-typegoose-module.ts @@ -1,15 +1,16 @@ import { TypegooseModule } from 'nestjs-typegoose'; import { MongoMemoryServer } from 'mongodb-memory-server'; -export const typegooseTestingModule = (mongod?: MongoMemoryServer) => TypegooseModule.forRootAsync({ - useFactory: async () => { - mongod = mongod ?? new MongoMemoryServer(); - return { - uri: await mongod.getUri(), - useNewUrlParser: true, - useUnifiedTopology: true, - useFindAndModify: false, - useCreateIndex: true, - }; - }, -}); +export const typegooseTestingModule = (mongod?: MongoMemoryServer) => + TypegooseModule.forRootAsync({ + useFactory: async () => { + mongod = mongod ?? new MongoMemoryServer(); + return { + uri: await mongod.getUri(), + useNewUrlParser: true, + useUnifiedTopology: true, + useFindAndModify: false, + useCreateIndex: true, + }; + }, + });